selftests/mm: add PAGEMAP_SCAN guard region test
authorAndrei Vagin <avagin@gmail.com>
Mon, 24 Mar 2025 06:53:28 +0000 (06:53 +0000)
committerAndrew Morton <akpm@linux-foundation.org>
Mon, 12 May 2025 00:48:17 +0000 (17:48 -0700)
Add a selftest to verify the PAGEMAP_SCAN ioctl correctly reports guard
regions using the newly introduced PAGE_IS_GUARD flag.

Link: https://lkml.kernel.org/r/20250324065328.107678-4-avagin@google.com
Signed-off-by: Andrei Vagin <avagin@gmail.com>
Reviewed-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
tools/testing/selftests/mm/guard-regions.c

index eba43ead13ae82aa0936c62424216256196d4921..0cd9d236649dc43abc97d903e25056bc06a4b174 100644 (file)
@@ -8,6 +8,7 @@
 #include <fcntl.h>
 #include <linux/limits.h>
 #include <linux/userfaultfd.h>
+#include <linux/fs.h>
 #include <setjmp.h>
 #include <signal.h>
 #include <stdbool.h>
@@ -2075,4 +2076,60 @@ TEST_F(guard_regions, pagemap)
        ASSERT_EQ(munmap(ptr, 10 * page_size), 0);
 }
 
+/*
+ * Assert that PAGEMAP_SCAN correctly reports guard region ranges.
+ */
+TEST_F(guard_regions, pagemap_scan)
+{
+       const unsigned long page_size = self->page_size;
+       struct page_region pm_regs[10];
+       struct pm_scan_arg pm_scan_args = {
+               .size = sizeof(struct pm_scan_arg),
+               .category_anyof_mask = PAGE_IS_GUARD,
+               .return_mask = PAGE_IS_GUARD,
+               .vec = (long)&pm_regs,
+               .vec_len = ARRAY_SIZE(pm_regs),
+       };
+       int proc_fd, i;
+       char *ptr;
+
+       proc_fd = open("/proc/self/pagemap", O_RDONLY);
+       ASSERT_NE(proc_fd, -1);
+
+       ptr = mmap_(self, variant, NULL, 10 * page_size,
+                   PROT_READ | PROT_WRITE, 0, 0);
+       ASSERT_NE(ptr, MAP_FAILED);
+
+       pm_scan_args.start = (long)ptr;
+       pm_scan_args.end = (long)ptr + 10 * page_size;
+       ASSERT_EQ(ioctl(proc_fd, PAGEMAP_SCAN, &pm_scan_args), 0);
+       ASSERT_EQ(pm_scan_args.walk_end, (long)ptr + 10 * page_size);
+
+       /* Install a guard region in every other page. */
+       for (i = 0; i < 10; i += 2) {
+               char *ptr_p = &ptr[i * page_size];
+
+               ASSERT_EQ(syscall(__NR_madvise, ptr_p, page_size, MADV_GUARD_INSTALL), 0);
+       }
+
+       /*
+        * Assert ioctl() returns the count of located regions, where each
+        * region spans every other page within the range of 10 pages.
+        */
+       ASSERT_EQ(ioctl(proc_fd, PAGEMAP_SCAN, &pm_scan_args), 5);
+       ASSERT_EQ(pm_scan_args.walk_end, (long)ptr + 10 * page_size);
+
+       /* Re-read from pagemap, and assert guard regions are detected. */
+       for (i = 0; i < 5; i++) {
+               long ptr_p = (long)&ptr[2 * i * page_size];
+
+               ASSERT_EQ(pm_regs[i].start, ptr_p);
+               ASSERT_EQ(pm_regs[i].end, ptr_p + page_size);
+               ASSERT_EQ(pm_regs[i].categories, PAGE_IS_GUARD);
+       }
+
+       ASSERT_EQ(close(proc_fd), 0);
+       ASSERT_EQ(munmap(ptr, 10 * page_size), 0);
+}
+
 TEST_HARNESS_MAIN