]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
selftests/mm: move uffd pagemap test to unit test
authorPeter Xu <peterx@redhat.com>
Wed, 12 Apr 2023 16:43:52 +0000 (12:43 -0400)
committerAndrew Morton <akpm@linux-foundation.org>
Tue, 18 Apr 2023 23:30:07 +0000 (16:30 -0700)
Move it over and make it split into two tests, one for pagemap and one for
the new WP_UNPOPULATED (to be a separate one).

The thp pagemap test wasn't really working (with MADV_HUGEPAGE).  Let's
just drop it (since it never really worked anyway..) and leave that for
later.

Link: https://lkml.kernel.org/r/20230412164352.328733-1-peterx@redhat.com
Signed-off-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Mike Rapoport (IBM) <rppt@kernel.org>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Dmitry Safonov <0x7f454c46@gmail.com>
Cc: Mike Kravetz <mike.kravetz@oracle.com>
Cc: Zach O'Keefe <zokeefe@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
tools/testing/selftests/mm/uffd-stress.c
tools/testing/selftests/mm/uffd-unit-tests.c

index 747d588c0d69896feaefff138ce1372be2c4c555..61d025d87bf2571837c69fb0c9dddd924eb4a605 100644 (file)
@@ -670,157 +670,6 @@ static int userfaultfd_minor_test(void)
        return args.missing_faults != 0 || args.minor_faults != nr_pages;
 }
 
-static int pagemap_open(void)
-{
-       int fd = open("/proc/self/pagemap", O_RDONLY);
-
-       if (fd < 0)
-               err("open pagemap");
-
-       return fd;
-}
-
-/* This macro let __LINE__ works in err() */
-#define  pagemap_check_wp(value, wp) do {                              \
-               if (!!(value & PM_UFFD_WP) != wp)                       \
-                       err("pagemap uffd-wp bit error: 0x%"PRIx64, value); \
-       } while (0)
-
-static int pagemap_test_fork(bool present)
-{
-       pid_t child = fork();
-       uint64_t value;
-       int fd, result;
-
-       if (!child) {
-               /* Open the pagemap fd of the child itself */
-               fd = pagemap_open();
-               value = pagemap_get_entry(fd, area_dst);
-               /*
-                * After fork() uffd-wp bit should be gone as long as we're
-                * without UFFD_FEATURE_EVENT_FORK
-                */
-               pagemap_check_wp(value, false);
-               /* Succeed */
-               exit(0);
-       }
-       waitpid(child, &result, 0);
-       return result;
-}
-
-static void userfaultfd_wp_unpopulated_test(int pagemap_fd)
-{
-       uint64_t value;
-
-       /* Test applying pte marker to anon unpopulated */
-       wp_range(uffd, (uint64_t)area_dst, page_size, true);
-       value = pagemap_get_entry(pagemap_fd, area_dst);
-       pagemap_check_wp(value, true);
-
-       /* Test unprotect on anon pte marker */
-       wp_range(uffd, (uint64_t)area_dst, page_size, false);
-       value = pagemap_get_entry(pagemap_fd, area_dst);
-       pagemap_check_wp(value, false);
-
-       /* Test zap on anon marker */
-       wp_range(uffd, (uint64_t)area_dst, page_size, true);
-       if (madvise(area_dst, page_size, MADV_DONTNEED))
-               err("madvise(MADV_DONTNEED) failed");
-       value = pagemap_get_entry(pagemap_fd, area_dst);
-       pagemap_check_wp(value, false);
-
-       /* Test fault in after marker removed */
-       *area_dst = 1;
-       value = pagemap_get_entry(pagemap_fd, area_dst);
-       pagemap_check_wp(value, false);
-       /* Drop it to make pte none again */
-       if (madvise(area_dst, page_size, MADV_DONTNEED))
-               err("madvise(MADV_DONTNEED) failed");
-
-       /* Test read-zero-page upon pte marker */
-       wp_range(uffd, (uint64_t)area_dst, page_size, true);
-       *(volatile char *)area_dst;
-       /* Drop it to make pte none again */
-       if (madvise(area_dst, page_size, MADV_DONTNEED))
-               err("madvise(MADV_DONTNEED) failed");
-}
-
-static void userfaultfd_pagemap_test(unsigned int test_pgsize)
-{
-       int pagemap_fd;
-       uint64_t value;
-
-       /* Pagemap tests uffd-wp only */
-       if (!test_uffdio_wp)
-               return;
-
-       /* Not enough memory to test this page size */
-       if (test_pgsize > nr_pages * page_size)
-               return;
-
-       printf("testing uffd-wp with pagemap (pgsize=%u): ", test_pgsize);
-       /* Flush so it doesn't flush twice in parent/child later */
-       fflush(stdout);
-
-       uffd_test_ctx_init(UFFD_FEATURE_WP_UNPOPULATED);
-
-       if (test_pgsize > page_size) {
-               /* This is a thp test */
-               if (madvise(area_dst, nr_pages * page_size, MADV_HUGEPAGE))
-                       err("madvise(MADV_HUGEPAGE) failed");
-       } else if (test_pgsize == page_size) {
-               /* This is normal page test; force no thp */
-               if (madvise(area_dst, nr_pages * page_size, MADV_NOHUGEPAGE))
-                       err("madvise(MADV_NOHUGEPAGE) failed");
-       }
-
-       if (uffd_register(uffd, area_dst, nr_pages * page_size,
-                         false, true, false))
-               err("register failed");
-
-       pagemap_fd = pagemap_open();
-
-       /* Smoke test WP_UNPOPULATED first when it's still empty */
-       if (test_pgsize == page_size)
-               userfaultfd_wp_unpopulated_test(pagemap_fd);
-
-       /* Touch the page */
-       *area_dst = 1;
-       wp_range(uffd, (uint64_t)area_dst, test_pgsize, true);
-       value = pagemap_get_entry(pagemap_fd, area_dst);
-       pagemap_check_wp(value, true);
-       /* Make sure uffd-wp bit dropped when fork */
-       if (pagemap_test_fork(true))
-               err("Detected stall uffd-wp bit in child");
-
-       /* Exclusive required or PAGEOUT won't work */
-       if (!(value & PM_MMAP_EXCLUSIVE))
-               err("multiple mapping detected: 0x%"PRIx64, value);
-
-       if (madvise(area_dst, test_pgsize, MADV_PAGEOUT))
-               err("madvise(MADV_PAGEOUT) failed");
-
-       /* Uffd-wp should persist even swapped out */
-       value = pagemap_get_entry(pagemap_fd, area_dst);
-       pagemap_check_wp(value, true);
-       /* Make sure uffd-wp bit dropped when fork */
-       if (pagemap_test_fork(false))
-               err("Detected stall uffd-wp bit in child");
-
-       /* Unprotect; this tests swap pte modifications */
-       wp_range(uffd, (uint64_t)area_dst, page_size, false);
-       value = pagemap_get_entry(pagemap_fd, area_dst);
-       pagemap_check_wp(value, false);
-
-       /* Fault in the page from disk */
-       *area_dst = 2;
-       value = pagemap_get_entry(pagemap_fd, area_dst);
-       pagemap_check_wp(value, false);
-
-       close(pagemap_fd);
-       printf("done\n");
-}
-
 static int userfaultfd_stress(void)
 {
        void *area;
@@ -932,21 +781,6 @@ static int userfaultfd_stress(void)
                uffd_stats_report(args, nr_cpus);
        }
 
-       if (test_type == TEST_ANON) {
-               /*
-                * shmem/hugetlb won't be able to run since they have different
-                * behavior on fork() (file-backed memory normally drops ptes
-                * directly when fork), meanwhile the pagemap test will verify
-                * pgtable entry of fork()ed child.
-                */
-               userfaultfd_pagemap_test(page_size);
-               /*
-                * Hard-code for x86_64 for now for 2M THP, as x86_64 is
-                * currently the only one that supports uffd-wp
-                */
-               userfaultfd_pagemap_test(page_size * 512);
-       }
-
        return userfaultfd_zeropage_test() || userfaultfd_sig_test()
                || userfaultfd_events_test() || userfaultfd_minor_test();
 }
index 936b24a6f468ecfddf5a7378fcbc0711d2fafa52..4690c95a942042156a1042810b592bff72dfee90 100644 (file)
@@ -197,7 +197,152 @@ static bool uffd_feature_supported(uffd_test_case_t *test)
            test->uffd_feature_required;
 }
 
+static int pagemap_open(void)
+{
+       int fd = open("/proc/self/pagemap", O_RDONLY);
+
+       if (fd < 0)
+               err("open pagemap");
+
+       return fd;
+}
+
+/* This macro let __LINE__ works in err() */
+#define  pagemap_check_wp(value, wp) do {                              \
+               if (!!(value & PM_UFFD_WP) != wp)                       \
+                       err("pagemap uffd-wp bit error: 0x%"PRIx64, value); \
+       } while (0)
+
+static int pagemap_test_fork(bool present)
+{
+       pid_t child = fork();
+       uint64_t value;
+       int fd, result;
+
+       if (!child) {
+               /* Open the pagemap fd of the child itself */
+               fd = pagemap_open();
+               value = pagemap_get_entry(fd, area_dst);
+               /*
+                * After fork() uffd-wp bit should be gone as long as we're
+                * without UFFD_FEATURE_EVENT_FORK
+                */
+               pagemap_check_wp(value, false);
+               /* Succeed */
+               exit(0);
+       }
+       waitpid(child, &result, 0);
+       return result;
+}
+
+static void uffd_wp_unpopulated_test(void)
+{
+       uint64_t value;
+       int pagemap_fd;
+
+       if (uffd_register(uffd, area_dst, nr_pages * page_size,
+                         false, true, false))
+               err("register failed");
+
+       pagemap_fd = pagemap_open();
+
+       /* Test applying pte marker to anon unpopulated */
+       wp_range(uffd, (uint64_t)area_dst, page_size, true);
+       value = pagemap_get_entry(pagemap_fd, area_dst);
+       pagemap_check_wp(value, true);
+
+       /* Test unprotect on anon pte marker */
+       wp_range(uffd, (uint64_t)area_dst, page_size, false);
+       value = pagemap_get_entry(pagemap_fd, area_dst);
+       pagemap_check_wp(value, false);
+
+       /* Test zap on anon marker */
+       wp_range(uffd, (uint64_t)area_dst, page_size, true);
+       if (madvise(area_dst, page_size, MADV_DONTNEED))
+               err("madvise(MADV_DONTNEED) failed");
+       value = pagemap_get_entry(pagemap_fd, area_dst);
+       pagemap_check_wp(value, false);
+
+       /* Test fault in after marker removed */
+       *area_dst = 1;
+       value = pagemap_get_entry(pagemap_fd, area_dst);
+       pagemap_check_wp(value, false);
+       /* Drop it to make pte none again */
+       if (madvise(area_dst, page_size, MADV_DONTNEED))
+               err("madvise(MADV_DONTNEED) failed");
+
+       /* Test read-zero-page upon pte marker */
+       wp_range(uffd, (uint64_t)area_dst, page_size, true);
+       *(volatile char *)area_dst;
+       /* Drop it to make pte none again */
+       if (madvise(area_dst, page_size, MADV_DONTNEED))
+               err("madvise(MADV_DONTNEED) failed");
+
+       uffd_test_pass();
+}
+
+static void uffd_pagemap_test(void)
+{
+       int pagemap_fd;
+       uint64_t value;
+
+       if (uffd_register(uffd, area_dst, nr_pages * page_size,
+                         false, true, false))
+               err("register failed");
+
+       pagemap_fd = pagemap_open();
+
+       /* Touch the page */
+       *area_dst = 1;
+       wp_range(uffd, (uint64_t)area_dst, page_size, true);
+       value = pagemap_get_entry(pagemap_fd, area_dst);
+       pagemap_check_wp(value, true);
+       /* Make sure uffd-wp bit dropped when fork */
+       if (pagemap_test_fork(true))
+               err("Detected stall uffd-wp bit in child");
+
+       /* Exclusive required or PAGEOUT won't work */
+       if (!(value & PM_MMAP_EXCLUSIVE))
+               err("multiple mapping detected: 0x%"PRIx64, value);
+
+       if (madvise(area_dst, page_size, MADV_PAGEOUT))
+               err("madvise(MADV_PAGEOUT) failed");
+
+       /* Uffd-wp should persist even swapped out */
+       value = pagemap_get_entry(pagemap_fd, area_dst);
+       pagemap_check_wp(value, true);
+       /* Make sure uffd-wp bit dropped when fork */
+       if (pagemap_test_fork(false))
+               err("Detected stall uffd-wp bit in child");
+
+       /* Unprotect; this tests swap pte modifications */
+       wp_range(uffd, (uint64_t)area_dst, page_size, false);
+       value = pagemap_get_entry(pagemap_fd, area_dst);
+       pagemap_check_wp(value, false);
+
+       /* Fault in the page from disk */
+       *area_dst = 2;
+       value = pagemap_get_entry(pagemap_fd, area_dst);
+       pagemap_check_wp(value, false);
+
+       close(pagemap_fd);
+       uffd_test_pass();
+}
+
 uffd_test_case_t uffd_tests[] = {
+       {
+               .name = "pagemap",
+               .uffd_fn = uffd_pagemap_test,
+               .mem_targets = MEM_ANON,
+               .uffd_feature_required = UFFD_FEATURE_PAGEFAULT_FLAG_WP,
+       },
+       {
+               .name = "wp-unpopulated",
+               .uffd_fn = uffd_wp_unpopulated_test,
+               .mem_targets = MEM_ANON,
+               .uffd_feature_required =
+               UFFD_FEATURE_PAGEFAULT_FLAG_WP | UFFD_FEATURE_WP_UNPOPULATED,
+       },
 };
 
 int main(int argc, char *argv[])