From: Vitaly Prosyak <[email protected]>
Move fs_reclaim_acquire() to before all lock acquisitions to eliminate
false positive circular locking dependency warning.
This is a 7.2-cycle regression fix suitable for stable backport.
PROBLEM:
amdgpu_lockdep_init() trains lockdep by acquiring locks in sequence
while calling fs_reclaim_acquire() in the middle. The fs_reclaim call
was placed while notifier_lock was held, teaching lockdep:
notifier_lock -> fs_reclaim
However, at runtime, MMU notifier callbacks run from inside memory
reclaim, establishing the opposite dependency:
fs_reclaim -> mmu_notifier -> notifier_lock
These form a false cycle that lockdep reports when reclaim unmaps a
page covered by an amdgpu userptr notifier.
LOCKDEP WARNING EXAMPLE (BEFORE FIX):
WARNING: possible circular locking dependency detected
6.19.0+ #48 Not tainted
amd_lockdep/3762 is trying to acquire lock:
ffff8881756f3c30 (&amdgpu_notifier_lock_key), at: amdgpu_hmm_invalidate_gfx
but task is already holding lock:
ffffffff92e4b5c0 (mmu_notifier_invalidate_range_start), at: try_to_unmap_one
Chain exists of:
&amdgpu_notifier_lock_key --> fs_reclaim -->
mmu_notifier_invalidate_range_start
Possible unsafe locking scenario:
CPU0 CPU1
---- ----
lock(mmu_notifier_invalidate_range_start);
lock(fs_reclaim);
lock(mmu_notifier_invalidate_range_start);
lock(&amdgpu_notifier_lock_key);
*** DEADLOCK ***
The dependency chain shows:
-> #5 (fs_reclaim):
amdgpu_lockdep_init+0x61e/0x730 [amdgpu] <- FALSE EDGE
-> #0 (&amdgpu_notifier_lock_key):
amdgpu_hmm_invalidate_gfx+0x77/0x110 [amdgpu] <- RUNTIME PATH
SOLUTION:
Move fs_reclaim_acquire/release to BEFORE all lock acquisitions.
The acquire/release pair does not create a static lockdep edge by itself
(no locks are held between acquire and release), but it registers the
fs_reclaim lock class. The actual fs_reclaim -> notifier_lock ordering
is established at runtime when memory reclaim invokes MMU notifiers that
take notifier_lock. By moving the fs_reclaim registration before any
lock acquisition, we avoid teaching lockdep the false reverse dependency.
VERIFICATION (AFTER FIX):
Test reproducer (based on Mikhail Gavrilov's standalone test case)
creates a 64MB GPU userptr memory buffer, then repeatedly forces the
kernel to reclaim it by moving pages to disk (simulating memory
pressure). Each reclaim cycle triggers the MMU notifier callback path.
The test is available as an IGT subtest:
tests/amdgpu/amd_lockdep.c::notifier-reclaim-splat
Results:
Before fix: Lockdep false positive warning on first reclaim attempt
After fix: Completed 8 reclaim cycles with no lockdep warnings
dmesg after fix shows only test success messages, no circular dependency
warnings. The fix is minimal (relocate 2 lines) with no runtime overhead,
only corrects lockdep's static analysis model.
v2: Address Mikhail Gavrilov's review feedback:
- Fix author name: Michael -> Mikhail Gavrilov in all trailers
- Add Fixes: tag to link regression to original commit
- Add Tested-by: Mikhail Gavrilov (tested on RX 7900 XTX)
- Rewrite fs_reclaim comment to be technically accurate about what
fs_reclaim_acquire/release actually does (registers lock class,
doesn't create static edge)
- Add note that this is a 7.2-cycle regression for stable routing
Fixes: 1d0f5838b126 ("drm/amdgpu: Add lockdep annotations for lock ordering
validation")
Reported-by: Mikhail Gavrilov <[email protected]>
Analyzed-by: Mikhail Gavrilov <[email protected]>
Test-case-by: Mikhail Gavrilov <[email protected]>
Tested-by: Mikhail Gavrilov <[email protected]>
Suggested-by: Christian König <[email protected]>
Tested-by: Vitaly Prosyak <[email protected]>
Cc: Christian König <[email protected]>
Cc: Alex Deucher <[email protected]>
Cc: [email protected]
Signed-off-by: Vitaly Prosyak <[email protected]>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c
b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c
index b251350b1fb2..718411f724f3 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c
@@ -150,6 +150,18 @@ int amdgpu_lockdep_init(void)
* Take locks in the correct order to train lockdep.
* This establishes the dependency chain.
*/
+ /*
+ * Train fs_reclaim FIRST, before taking any locks.
+ * This teaches lockdep that fs_reclaim is the OUTERMOST context.
+ * When memory reclaim later calls MMU notifiers (which take
notifier_lock),
+ * lockdep will see: fs_reclaim -> notifier_lock (correct runtime edge).
+ *
+ * Bug fix: Previously fs_reclaim_acquire() was called while holding
+ * notifier_lock, teaching lockdep the FALSE edge: notifier_lock ->
fs_reclaim.
+ * This caused false positive circular dependency warnings.
+ */
+ fs_reclaim_acquire(GFP_KERNEL);
+ fs_reclaim_release(GFP_KERNEL);
/* Level 1: Global userq scheduler mutex (outermost) */
mutex_lock(&locks->userq_sch_mutex);
@@ -168,11 +180,6 @@ int amdgpu_lockdep_init(void)
/* Level 7: DRM file list mutex */
mutex_lock(&locks->filelist_mutex);
- /*
- * Mark potential memory reclaim boundary.
- * GPU operations might trigger memory allocation/reclaim.
- */
- fs_reclaim_acquire(GFP_KERNEL);
/* Level 8: SRBM register access */
mutex_lock(&locks->srbm_mutex);
@@ -190,7 +197,6 @@ int amdgpu_lockdep_init(void)
spin_unlock_irqrestore(&locks->mmio_idx_lock, flags);
mutex_unlock(&locks->grbm_idx_mutex);
mutex_unlock(&locks->srbm_mutex);
- fs_reclaim_release(GFP_KERNEL);
mutex_unlock(&locks->filelist_mutex);
mutex_unlock(&locks->reset_lock);
--
2.54.0