* [PATCH] drm/amdgpu: do not enter fs_reclaim under notifier_lock in lockdep training
@ 2026-06-18 5:52 Mikhail Gavrilov
[not found] ` <20260618061728.75BDF1F000E9@smtp.kernel.org>
2026-06-18 8:03 ` Christian König
0 siblings, 2 replies; 5+ messages in thread
From: Mikhail Gavrilov @ 2026-06-18 5:52 UTC (permalink / raw)
To: Alex Deucher, Christian König, Vitaly Prosyak
Cc: David Airlie, Simona Vetter, amd-gfx, dri-devel, linux-kernel,
Mikhail Gavrilov
amdgpu_lockdep_init() trains lockdep on the driver lock ordering by
taking a chain of dummy locks in order and calling fs_reclaim_acquire()
in the middle of it. The fs_reclaim_acquire()/fs_reclaim_release() pair
is placed while notifier_lock (amdgpu_notifier_lock_key) is held, which
teaches lockdep that it is legal to enter memory reclaim with the MMU
notifier lock held:
notifier_lock -> vram_lock -> reset_domain->sem -> reset_lock ->
fs_reclaim
notifier_lock is, however, acquired from the MMU notifier invalidate
callback amdgpu_hmm_invalidate_gfx(), which mm/ runs from inside memory
reclaim via __mmu_notifier_invalidate_range_start(). That establishes
the mandatory reverse ordering:
fs_reclaim -> mmu_notifier_invalidate_range_start -> notifier_lock
The two together form a cycle. It stays dormant until reclaim first
unmaps a page covered by an amdgpu userptr interval notifier, at which
point kswapd closes the loop and lockdep reports a false circular
locking dependency:
WARNING: possible circular locking dependency detected
kswapd0/268 is trying to acquire lock:
(&amdgpu_notifier_lock_key){+.+.}, at: amdgpu_hmm_invalidate_gfx
but task is already holding lock:
(mmu_notifier_invalidate_range_start){+.+.}, at: try_to_unmap_one
A lock that is taken inside an MMU notifier callback must never be held
across a reclaiming allocation, so the fs_reclaim annotation does not
belong inside the notifier_lock region. Drop it. The remaining chain
still teaches the intended lock nesting.
Fixes: 1d0f5838b126 ("drm/amdgpu: Add lockdep annotations for lock ordering validation")
Signed-off-by: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c | 8 --------
1 file changed, 8 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c
index d5d71fd7c70d..eeb3b5007f80 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c
@@ -150,12 +150,6 @@ int amdgpu_lockdep_init(void)
/* Level 6: Reset control lock */
mutex_lock(&reset_ctl.reset_lock);
- /*
- * Mark potential memory reclaim boundary.
- * GPU operations might trigger memory allocation/reclaim.
- */
- fs_reclaim_acquire(GFP_KERNEL);
-
/* Level 7: SRBM register access */
mutex_lock(&srbm_mutex);
@@ -175,8 +169,6 @@ int amdgpu_lockdep_init(void)
mutex_unlock(&grbm_idx_mutex);
mutex_unlock(&srbm_mutex);
- fs_reclaim_release(GFP_KERNEL);
-
mutex_unlock(&reset_ctl.reset_lock);
up_read(&reset_domain->sem);
mutex_unlock(&vram_lock);
--
2.54.0
^ permalink raw reply [flat|nested] 5+ messages in thread[parent not found: <20260618061728.75BDF1F000E9@smtp.kernel.org>]
* Re: [PATCH] drm/amdgpu: do not enter fs_reclaim under notifier_lock in lockdep training [not found] ` <20260618061728.75BDF1F000E9@smtp.kernel.org> @ 2026-06-18 6:44 ` Mikhail Gavrilov 0 siblings, 0 replies; 5+ messages in thread From: Mikhail Gavrilov @ 2026-06-18 6:44 UTC (permalink / raw) To: sashiko-reviews, Alex Deucher, Christian König, Vitaly Prosyak Cc: dri-devel, David Airlie, Simona Vetter, amd-gfx list, Linux List Kernel Mailing On Thu, Jun 18, 2026 at 11:17 AM <sashiko-bot@kernel.org> wrote: > > Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: > > Pre-existing issues: > - [High] Removing the `fs_reclaim` lockdep annotation silences a true-positive warning about a real ABBA deadlock between memory reclaim and GPU reset, rather than fixing the underlying bug. > -- > > commit 4896f663f62249014930226b2bc964c2419486d0 > Author: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com> > > drm/amdgpu: do not enter fs_reclaim under notifier_lock in lockdep training > > The commit removes the fs_reclaim_acquire() and fs_reclaim_release() calls > from amdgpu_lockdep_init() to silence a circular locking dependency warning, > claiming it is a false positive. > > > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c > > index d5d71fd7c70d0..eeb3b5007f80f 100644 > > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c > > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c > > @@ -150,12 +150,6 @@ int amdgpu_lockdep_init(void) > > /* Level 6: Reset control lock */ > > mutex_lock(&reset_ctl.reset_lock); > > > > - /* > > - * Mark potential memory reclaim boundary. > > - * GPU operations might trigger memory allocation/reclaim. > > - */ > > - fs_reclaim_acquire(GFP_KERNEL); > > - > > [Severity: High] > This is a pre-existing issue, but does removing this lockdep annotation > silence a true-positive warning about a real ABBA deadlock? > > While the circular dependency might not happen in the same thread, couldn't a > cross-thread deadlock occur between memory reclaim and GPU reset? > > For example, if Thread A executes the MMU notifier amdgpu_hmm_invalidate_gfx() > (e.g., during direct reclaim), it acquires adev->notifier_lock and blocks in > dma_resv_wait_timeout() waiting for a hardware fence: > > drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c:amdgpu_hmm_invalidate_gfx() { > ... > mutex_lock(&adev->notifier_lock); > ... > r = dma_resv_wait_timeout(vm_root->tbo.base.resv, > DMA_RESV_USAGE_BOOKKEEP, false, > MAX_SCHEDULE_TIMEOUT); > } > > If Thread B (the GPU reset thread) takes down_write(&adev->reset_domain->sem) > to reset the GPU and signal the fences, and performs GFP_KERNEL allocations, > it could be forced into direct memory reclaim (fs_reclaim). This would invoke > the MMU notifier, attempting to acquire adev->notifier_lock, forming a cycle. > > Would it be better to fix the underlying deadlock by wrapping the GPU reset > path with memalloc_noreclaim_save() or memalloc_noio_save() to prevent it > from entering memory reclaim, instead of removing the lockdep annotation? > > -- > Sashiko AI review · https://sashiko.dev/#/patchset/20260618055216.56191-1-mikhail.v.gavrilov@gmail.com?part=1 The reported splat is not the reset/reclaim deadlock described here. It is single-threaded: kswapd holds fs_reclaim and the mmu_notifier range, then takes notifier_lock in amdgpu_hmm_invalidate_gfx(). That is the normal, mandatory direction fs_reclaim -> mmu_notifier -> notifier_lock. The MMU notifier callback runs from inside reclaim, so notifier_lock is acquired below fs_reclaim and is never held across a reclaiming allocation. amdgpu_lockdep_init() asserts the opposite edge, notifier_lock -> fs_reclaim, by calling fs_reclaim_acquire() while notifier_lock is held. That edge does not occur at runtime, so the reported cycle is a false positive. Dropping the annotation removes the impossible edge and touches no real lock. On the cross-thread reset case: if the reset path really holds reset_domain->sem across a GFP_KERNEL allocation, lockdep learns reset_sem -> fs_reclaim from that real allocation, not from this annotation. The fs_reclaim_acquire() here adds nothing for that real edge; it only injects the impossible notifier_lock -> fs_reclaim one. And because it fires on the innocent kswapd path, it calls debug_locks_off() and disables lockdep for the rest of the boot, which would prevent detecting exactly that reset deadlock. Note memalloc_noreclaim_save() on the reset path would not silence this splat: the false edge lives in amdgpu_lockdep_init(), independent of the reset path. The splat reproduces with a userptr BO + MADV_PAGEOUT and is gone after this change; I verified both. If reset is confirmed to allocate under reset_domain->sem with reclaim, that is a real and separate issue and memalloc_noreclaim_save() there would be reasonable, but it is a different patch and does not change this one. -- Best Regards, Mike Gavrilov. ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] drm/amdgpu: do not enter fs_reclaim under notifier_lock in lockdep training 2026-06-18 5:52 [PATCH] drm/amdgpu: do not enter fs_reclaim under notifier_lock in lockdep training Mikhail Gavrilov [not found] ` <20260618061728.75BDF1F000E9@smtp.kernel.org> @ 2026-06-18 8:03 ` Christian König 2026-06-19 12:18 ` Mikhail Gavrilov 1 sibling, 1 reply; 5+ messages in thread From: Christian König @ 2026-06-18 8:03 UTC (permalink / raw) To: Mikhail Gavrilov, Alex Deucher, Vitaly Prosyak Cc: David Airlie, Simona Vetter, amd-gfx, dri-devel, linux-kernel On 6/18/26 07:52, Mikhail Gavrilov wrote: > amdgpu_lockdep_init() trains lockdep on the driver lock ordering by > taking a chain of dummy locks in order and calling fs_reclaim_acquire() > in the middle of it. The fs_reclaim_acquire()/fs_reclaim_release() pair > is placed while notifier_lock (amdgpu_notifier_lock_key) is held, which > teaches lockdep that it is legal to enter memory reclaim with the MMU > notifier lock held: > > notifier_lock -> vram_lock -> reset_domain->sem -> reset_lock -> > fs_reclaim > > notifier_lock is, however, acquired from the MMU notifier invalidate > callback amdgpu_hmm_invalidate_gfx(), which mm/ runs from inside memory > reclaim via __mmu_notifier_invalidate_range_start(). That establishes > the mandatory reverse ordering: > > fs_reclaim -> mmu_notifier_invalidate_range_start -> notifier_lock > > The two together form a cycle. It stays dormant until reclaim first > unmaps a page covered by an amdgpu userptr interval notifier, at which > point kswapd closes the loop and lockdep reports a false circular > locking dependency: > > WARNING: possible circular locking dependency detected > kswapd0/268 is trying to acquire lock: > (&amdgpu_notifier_lock_key){+.+.}, at: amdgpu_hmm_invalidate_gfx > but task is already holding lock: > (mmu_notifier_invalidate_range_start){+.+.}, at: try_to_unmap_one > > A lock that is taken inside an MMU notifier callback must never be held > across a reclaiming allocation, so the fs_reclaim annotation does not > belong inside the notifier_lock region. Drop it. The remaining chain > still teaches the intended lock nesting. > > Fixes: 1d0f5838b126 ("drm/amdgpu: Add lockdep annotations for lock ordering validation") > Signed-off-by: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com> > --- > drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c | 8 -------- > 1 file changed, 8 deletions(-) > > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c > index d5d71fd7c70d..eeb3b5007f80 100644 > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c > @@ -150,12 +150,6 @@ int amdgpu_lockdep_init(void) > /* Level 6: Reset control lock */ > mutex_lock(&reset_ctl.reset_lock); > > - /* > - * Mark potential memory reclaim boundary. > - * GPU operations might trigger memory allocation/reclaim. > - */ > - fs_reclaim_acquire(GFP_KERNEL); > - This shouldn't be removed, but instead moved outside the notifier lock. The notifier lock and vram_lock are also in incorrect order. @Vitaly can you take care of fixing that? Thanks in advance. Amdgpus VM eviction lock needs to be handled here as well, but has another ordering bug with fs_reclaim_acquire(). Patches to fix that are pending on the amdgpu mailing list, but I need to find time to work on them. Thanks for the report, Christian. > /* Level 7: SRBM register access */ > mutex_lock(&srbm_mutex); > > @@ -175,8 +169,6 @@ int amdgpu_lockdep_init(void) > mutex_unlock(&grbm_idx_mutex); > mutex_unlock(&srbm_mutex); > > - fs_reclaim_release(GFP_KERNEL); > - > mutex_unlock(&reset_ctl.reset_lock); > up_read(&reset_domain->sem); > mutex_unlock(&vram_lock); ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] drm/amdgpu: do not enter fs_reclaim under notifier_lock in lockdep training 2026-06-18 8:03 ` Christian König @ 2026-06-19 12:18 ` Mikhail Gavrilov 2026-06-22 1:55 ` vitaly prosyak 0 siblings, 1 reply; 5+ messages in thread From: Mikhail Gavrilov @ 2026-06-19 12:18 UTC (permalink / raw) To: Christian König, Alex Deucher, Vitaly Prosyak Cc: Mikhail Gavrilov, David Airlie, Simona Vetter, amd-gfx, dri-devel, linux-kernel 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; } ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] drm/amdgpu: do not enter fs_reclaim under notifier_lock in lockdep training 2026-06-19 12:18 ` Mikhail Gavrilov @ 2026-06-22 1:55 ` vitaly prosyak 0 siblings, 0 replies; 5+ messages in thread From: vitaly prosyak @ 2026-06-22 1:55 UTC (permalink / raw) To: Mikhail Gavrilov, Christian König, Alex Deucher, Vitaly Prosyak Cc: David Airlie, Simona Vetter, amd-gfx, dri-devel, linux-kernel Hi Mikhail, Thank you so much for providing the deterministic reproducer! This is extremely helpful - I really appreciate the detailed build notes and explanation of the lockdep cycle. Ill test this on my side this week and confirm the splat. Once we have the fix ready and tested, Ill be sure to credit you with Tested-by and acknowledge your help in identifying and reproducing the issue. Your assistance is much appreciated! Best regards, Vitaly On 2026-06-19 08:18, Mikhail Gavrilov wrote: > [You don't often get email from mikhail.v.gavrilov@gmail.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ] > > 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; > } ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-06-22 1:56 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-06-18 5:52 [PATCH] drm/amdgpu: do not enter fs_reclaim under notifier_lock in lockdep training 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
2026-06-22 1:55 ` vitaly prosyak
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®