[PATCH] tests/amdgpu: Add lockdep false positive reproducer test

<[email protected]>
Newsgroups org.freedesktop.lists.igt-dev
Message-ID <[email protected]>
From: Vitaly Prosyak <[email protected]>

Add notifier-reclaim-splat subtest to amd_lockdep that reproduces
the lockdep false positive circular dependency warning reported by
Mikhail Gavrilov.

The test is based on Mikhail Gavrilov's standalone reproducer code.
It creates a 64MB GPU userptr memory buffer, then repeatedly forces
the kernel to reclaim it by issuing madvise(MADV_PAGEOUT). This
simulates memory pressure and triggers the MMU notifier callback
path (amdgpu_hmm_invalidate_gfx) from within the reclaim context.

The test exposes the lockdep false positive where amdgpu_lockdep_init()
incorrectly taught lockdep that fs_reclaim can be acquired while holding
notifier_lock, when the runtime path actually takes notifier_lock under
fs_reclaim (the opposite direction).

Test behavior:
  Before kernel fix: Lockdep splat on first reclaim attempt (iteration 1/8)
  After kernel fix:  All 8 iterations complete without warnings

The test monitors dmesg for lockdep violations and reports them to the
user with detailed explanation of the kernel bug and required fix.

Run with:
  sudo ./build/tests/amdgpu/amd_lockdep --run-subtest notifier-reclaim-splat

Requires:
  - Kernel built with CONFIG_PROVE_LOCKING=y (lockdep enabled)
  - Fresh boot (lockdep disables itself after first splat)
  - AMD GPU with amdgpu driver

The corresponding kernel fix is:
  drm/amdgpu: Fix lockdep false positive in amdgpu_lockdep_init

v2: Credit Mikhail Gavrilov properly:
    - Fix author name: Michael -> Mikhail Gavrilov in Based-on-patch-by
    - Add note about corresponding kernel fix in commit message

Based-on-patch-by: Mikhail Gavrilov <[email protected]>
Cc: Christian König <[email protected]>
Cc: Alex Deucher <[email protected]>
Cc: Jesse Zhang <[email protected]>
Cc: Mikhail Gavrilov <[email protected]>
Signed-off-by: Vitaly Prosyak <[email protected]>
---
 tests/amdgpu/amd_lockdep.c | 123 +++++++++++++++++++++++++++++++++++++
 1 file changed, 123 insertions(+)

diff --git a/tests/amdgpu/amd_lockdep.c b/tests/amdgpu/amd_lockdep.c
index 48d7ccafa..8edae507c 100644
--- a/tests/amdgpu/amd_lockdep.c
+++ b/tests/amdgpu/amd_lockdep.c
@@ -20,6 +20,7 @@
  *   concurrent-mmap-and-evict    - mmap/munmap while VRAM eviction runs
  *   concurrent-userptr-and-reset - USERPTR invalidation during reset
  *   stress-all-paths             - Combined stress of all paths
+ *   notifier-reclaim-splat       - userptr + MADV_PAGEOUT (Michael Gavrilov)
  */
 
 #include <errno.h>
@@ -42,6 +43,10 @@
 #define STRESS_ITERATIONS 100
 #define THREAD_RUNTIME_SEC 2
 
+/* For notifier-reclaim-splat test */
+#define USERPTR_BUF_SIZE (64ull * 1024 * 1024) /* 64 MiB */
+#define PAGEOUT_ITERATIONS 8
+
 /* kmsg patterns that indicate lockdep violations */
 static const char *lockdep_violation_patterns[] = {
 	"circular locking dependency",
@@ -478,6 +483,120 @@ static void test_stress_all_paths(int fd, amdgpu_device_handle device)
 	close(kmsg_fd);
 }
 
+/* For notifier-reclaim-splat test */
+#define USERPTR_BUF_SIZE (64ull * 1024 * 1024) /* 64 MiB */
+#define PAGEOUT_ITERATIONS 8
+
+/*
+ * test_notifier_reclaim_splat - Michael Gavrilov reproducer
+ *
+ * Triggers the false positive lockdep splat by:
+ * 1. Creating anonymous memory buffer (64MB)
+ * 2. Registering it as AMDGPU userptr (installs MMU notifier)
+ * 3. Forcing memory reclaim with madvise(MADV_PAGEOUT)
+ * 4. Reclaim path calls MMU notifier which takes notifier_lock under fs_reclaim
+ * 5. Lockdep sees circular dependency with the false edge from amdgpu_lockdep_init
+ *
+ * Expected behavior:
+ * - BEFORE kernel fix: lockdep splat in dmesg (test FAILS)
+ * - AFTER kernel fix: no lockdep splat (test PASSES)
+ */
+static void test_notifier_reclaim_splat(int fd)
+{
+	void *buf;
+	struct drm_amdgpu_gem_userptr userptr = {0};
+	int ret;
+	int kmsg_fd;
+	bool violation_found = false;
+	unsigned long taints = 0;
+
+	igt_info("Creating %llu MiB anonymous buffer\n", USERPTR_BUF_SIZE / (1024 * 1024));
+
+	buf = mmap(NULL, USERPTR_BUF_SIZE, PROT_READ | PROT_WRITE,
+	           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	igt_assert(buf != MAP_FAILED);
+
+	/* Fault all pages in */
+	igt_info("Faulting in all pages\n");
+	memset(buf, 0xa5, USERPTR_BUF_SIZE);
+
+	/* Open kmsg to monitor for lockdep violations */
+	kmsg_fd = kmsg_open();
+	igt_assert(kmsg_fd >= 0);
+
+	/* Register as userptr - this installs MMU notifier callback */
+	igt_info("Registering as userptr BO - installs MMU notifier\n");
+	userptr.addr = (uint64_t)(uintptr_t)buf;
+	userptr.size = USERPTR_BUF_SIZE;
+	userptr.flags = AMDGPU_GEM_USERPTR_ANONONLY |
+	                AMDGPU_GEM_USERPTR_REGISTER |   /* Install mmu_interval_notifier */
+	                AMDGPU_GEM_USERPTR_VALIDATE;
+
+	ret = igt_ioctl(fd, DRM_IOCTL_AMDGPU_GEM_USERPTR, &userptr);
+	igt_assert_eq(ret, 0);
+
+	igt_info("Forcing reclaim with MADV_PAGEOUT - triggers MMU notifier under fs_reclaim\n");
+
+	/* Force reclaim - this triggers the lockdep splat on vulnerable kernel */
+	for (int i = 0; i < PAGEOUT_ITERATIONS; i++) {
+		igt_info("  Iteration %d/%d: madvise MADV_PAGEOUT\n", i + 1, PAGEOUT_ITERATIONS);
+
+		ret = madvise(buf, USERPTR_BUF_SIZE, MADV_PAGEOUT);
+		igt_assert_eq(ret, 0);
+
+		usleep(50000);  /* 50ms - give kernel time to process */
+
+		/* Check for lockdep violations */
+		if (kmsg_has_lockdep_violation(kmsg_fd)) {
+			violation_found = true;
+			igt_warn("LOCKDEP VIOLATION DETECTED during iteration %d\n", i + 1);
+			break;
+		}
+
+		/* Re-fault pages for next iteration */
+		memset(buf, 0xa5, USERPTR_BUF_SIZE);
+	}
+
+	close(kmsg_fd);
+
+	if (violation_found) {
+		igt_info("\n");
+		igt_info("================================================================\n");
+		igt_info("LOCKDEP SPLAT DETECTED - This is EXPECTED before kernel fix\n");
+		igt_info("================================================================\n");
+		igt_info("\n");
+		igt_info("The kernel has a FALSE POSITIVE lockdep bug:\n");
+		igt_info("  File: drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c\n");
+		igt_info("  Function: amdgpu_lockdep_init() around line 176\n");
+		igt_info("  Problem: fs_reclaim_acquire called while holding notifier_lock\n");
+		igt_info("\n");
+		igt_info("This teaches lockdep a FALSE edge: notifier_lock -> fs_reclaim\n");
+		igt_info("Runtime reality: fs_reclaim -> notifier_lock (opposite direction)\n");
+		igt_info("\n");
+		igt_info("FIX: Move fs_reclaim_acquire to BEFORE all mutex acquisitions\n");
+		igt_info("     in amdgpu_lockdep_init\n");
+		igt_info("\n");
+		igt_info("See: /home/vprosyak/Documents/IGT/LOCKDEP_ISSUE/\n");
+		igt_info("================================================================\n");
+		igt_info("\n");
+	} else {
+		igt_info("\n");
+		igt_info("================================================================\n");
+		igt_info("NO LOCKDEP VIOLATION - Kernel fix appears to be working!\n");
+		igt_info("================================================================\n");
+		igt_info("\n");
+	}
+
+	/* Cleanup */
+	munmap(buf, USERPTR_BUF_SIZE);
+
+	/* Check kernel taint flags */
+	igt_kernel_tainted(&taints);
+	igt_assert_f(!(taints & (1ul << TAINT_WARN)),
+	             "Kernel is tainted - lockdep violation detected\n");
+}
+
+
 int igt_main()
 {
 	amdgpu_device_handle device;
@@ -522,6 +641,10 @@ int igt_main()
 		test_stress_all_paths(fd, device);
 	}
 
+	igt_describe("Michael Gavrilov reproducer: userptr + MADV_PAGEOUT triggers fs_reclaim cycle");
+	igt_subtest("notifier-reclaim-splat") {
+		test_notifier_reclaim_splat(fd);
+	}
 	igt_fixture() {
 		amdgpu_device_deinitialize(device);
 		drm_close_driver(fd);
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.