mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>
To: "Christian König" <christian.koenig@amd.com>,
	"Alex Deucher" <alexander.deucher@amd.com>,
	"Vitaly Prosyak" <vitaly.prosyak@amd.com>
Cc: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>,
	David Airlie <airlied@gmail.com>, Simona Vetter <simona@ffwll.ch>,
	amd-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH] drm/amdgpu: do not enter fs_reclaim under notifier_lock in lockdep training
Date: Fri, 19 Jun 2026 17:18:18 +0500	[thread overview]
Message-ID: <20260619121819.189293-1-mikhail.v.gavrilov@gmail.com> (raw)
In-Reply-To: <cbc54f68-b0bd-40ad-b5e7-ed8157128417@amd.com>

Makes sense, thanks. I won't respin this one then.

Vitaly, for the reorder, here is a deterministic reproducer so you can confirm
the splat on your side without a round-trip. It arms an mmu_interval_notifier
via GEM_USERPTR over anonymous memory, then forces reclaim of that exact range
with madvise(MADV_PAGEOUT), so amdgpu_hmm_invalidate_gfx() takes notifier_lock
under fs_reclaim in the calling thread. Needs CONFIG_PROVE_LOCKING and a fresh
boot; build/run notes are in the header. Happy to give Tested-by once you post.

// SPDX-License-Identifier: MIT
/*
 * amdgpu-notifier-reclaim-repro.c
 *
 * Deterministic reproducer for the false circular-locking-dependency splat
 * produced by drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c.
 *
 * amdgpu_lockdep_init() (run at module load) calls fs_reclaim_acquire()
 * while holding the dummy notifier_lock, teaching lockdep that it is legal
 * to enter reclaim with the MMU-notifier lock held. The real notifier lock
 * is taken in amdgpu_hmm_invalidate_gfx(), which mm/ calls from inside
 * reclaim, so the reverse edge fs_reclaim -> mmu_notifier -> notifier_lock
 * is mandatory. The cycle is closed the first time reclaim unmaps a page
 * covered by an amdgpu userptr interval notifier.
 *
 * This program installs such a notifier (GEM_USERPTR) over anonymous memory
 * and then forces synchronous reclaim of that exact range with
 * MADV_PAGEOUT, which runs try_to_unmap()->invalidate_range_start() with
 * fs_reclaim held in the calling thread, closing the loop on demand.
 *
 * Requirements:
 *   - kernel built with CONFIG_PROVE_LOCKING (lockdep)
 *   - amdgpu loaded; run from a FRESH boot (the first lockdep splat of any
 *     kind calls debug_locks_off() and silences all later reports)
 *
 * Build:  cc -O2 -o repro amdgpu-notifier-reclaim-repro.c
 * Run:    ./repro                 # picks the first amdgpu render node
 *         ./repro /dev/dri/renderD129
 * Watch:  sudo dmesg -w
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

/* --- minimal amdgpu uapi (self-contained, no libdrm needed) ----------- */
#ifndef DRM_IOCTL_BASE
#define DRM_IOCTL_BASE 'd'
#endif
#define DRM_COMMAND_BASE 0x40
#define DRM_AMDGPU_GEM_USERPTR 0x11

struct drm_amdgpu_gem_userptr {
	uint64_t addr;
	uint64_t size;
	uint32_t flags;
	uint32_t handle;
};

#define DRM_IOCTL_AMDGPU_GEM_USERPTR \
	_IOWR(DRM_IOCTL_BASE, DRM_COMMAND_BASE + DRM_AMDGPU_GEM_USERPTR, \
	      struct drm_amdgpu_gem_userptr)

#define AMDGPU_GEM_USERPTR_READONLY (1 << 0)
#define AMDGPU_GEM_USERPTR_ANONONLY (1 << 1)
#define AMDGPU_GEM_USERPTR_VALIDATE (1 << 2)
#define AMDGPU_GEM_USERPTR_REGISTER (1 << 3)

#ifndef MADV_PAGEOUT
#define MADV_PAGEOUT 21
#endif

/* ------------------------------------------------------------ */

#define BUF_SIZE (64ull * 1024 * 1024) /* 64 MiB, page aligned by mmap */

static int open_amdgpu_render(const char *forced)
{
	if (forced) {
		int fd = open(forced, O_RDWR | O_CLOEXEC);
		if (fd < 0)
			perror(forced);
		return fd;
	}

	/* try renderD128..renderD143 and keep the first that accepts GEM_USERPTR */
	for (int i = 128; i < 144; i++) {
		char path[64];
		snprintf(path, sizeof(path), "/dev/dri/renderD%d", i);
		int fd = open(path, O_RDWR | O_CLOEXEC);
		if (fd < 0)
			continue;

		/* probe: a zero-size userptr returns -EINVAL on amdgpu but
		 * -ENOTTY/-ENODEV on a non-amdgpu driver, which lets us tell
		 * the nodes apart without pulling in libdrm version ioctls. */
		struct drm_amdgpu_gem_userptr probe = { 0 };
		errno = 0;
		ioctl(fd, DRM_IOCTL_AMDGPU_GEM_USERPTR, &probe);
		if (errno == ENOTTY || errno == ENODEV || errno == EOPNOTSUPP) {
			close(fd);
			continue;
		}
		fprintf(stderr, "using %s\n", path);
		return fd;
	}
	fprintf(stderr, "no amdgpu render node found under /dev/dri\n");
	return -1;
}

int main(int argc, char **argv)
{
	int fd = open_amdgpu_render(argc > 1 ? argv[1] : NULL);
	if (fd < 0)
		return 1;

	/* anonymous, private, page-aligned region for the userptr */
	void *buf = mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE,
			 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
	if (buf == MAP_FAILED) {
		perror("mmap");
		return 1;
	}
	memset(buf, 0xa5, BUF_SIZE); /* fault every page in */

	/* REGISTER installs the mmu_interval_notifier (amdgpu_hmm_register());
	 * VALIDATE additionally faults the pages via hmm_range_fault() and
	 * binds them into GTT. ANONONLY matches our MAP_ANONYMOUS region. */
	struct drm_amdgpu_gem_userptr up = {
		.addr  = (uint64_t)(uintptr_t)buf,
		.size  = BUF_SIZE,
		.flags = AMDGPU_GEM_USERPTR_ANONONLY |
			 AMDGPU_GEM_USERPTR_REGISTER |
			 AMDGPU_GEM_USERPTR_VALIDATE,
	};
	if (ioctl(fd, DRM_IOCTL_AMDGPU_GEM_USERPTR, &up)) {
		perror("GEM_USERPTR (VALIDATE)");
		/* retry without VALIDATE: the notifier is still registered, the
		 * pages are present from the memset, MADV_PAGEOUT still works */
		up.flags = AMDGPU_GEM_USERPTR_ANONONLY |
			   AMDGPU_GEM_USERPTR_REGISTER;
		if (ioctl(fd, DRM_IOCTL_AMDGPU_GEM_USERPTR, &up)) {
			perror("GEM_USERPTR (REGISTER)");
			return 1;
		}
	}
	fprintf(stderr, "userptr handle=%u, interval notifier armed over %p..%p\n",
		up.handle, buf, (char *)buf + BUF_SIZE);

	/* Force synchronous reclaim of the notifier-covered range. MADV_PAGEOUT
	 * runs shrink_folio_list()->try_to_unmap()->invalidate_range_start()
	 * with fs_reclaim held in THIS thread, so amdgpu_hmm_invalidate_gfx()
	 * takes notifier_lock under fs_reclaim and lockdep closes the cycle.
	 *
	 * A few iterations (re-touching in between) cover the race where pages
	 * are already paged out on the first pass. */
	for (int it = 0; it < 8; it++) {
		if (madvise(buf, BUF_SIZE, MADV_PAGEOUT))
			perror("madvise(MADV_PAGEOUT)");
		usleep(50 * 1000);
		memset(buf, 0xa5, BUF_SIZE); /* fault back in for the next pass */
	}

	fprintf(stderr,
		"done: check dmesg for "
		"\"possible circular locking dependency\" / amdgpu_hmm_invalidate_gfx\n");

	munmap(buf, BUF_SIZE);
	close(fd);
	return 0;
}

  reply	other threads:[~2026-06-19 12:18 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-18  5:52 Mikhail Gavrilov
     [not found] ` <20260618061728.75BDF1F000E9@smtp.kernel.org>
2026-06-18  6:44   ` Mikhail Gavrilov
2026-06-18  8:03 ` Christian König
2026-06-19 12:18   ` Mikhail Gavrilov [this message]
2026-06-22  1:55     ` vitaly prosyak

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260619121819.189293-1-mikhail.v.gavrilov@gmail.com \
    --to=mikhail.v.gavrilov@gmail.com \
    --cc=airlied@gmail.com \
    --cc=alexander.deucher@amd.com \
    --cc=amd-gfx@lists.freedesktop.org \
    --cc=christian.koenig@amd.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=simona@ffwll.ch \
    --cc=vitaly.prosyak@amd.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®