]> git.ipfire.org Git - thirdparty/kernel/stable.git/blobdiff - tools/testing/selftests/mm/userfaultfd.c
selftests/mm: test UFFDIO_ZEROPAGE only when !hugetlb
[thirdparty/kernel/stable.git] / tools / testing / selftests / mm / userfaultfd.c
index 7f22844ed7040e6b9bfcea2982f715bfb9c5e3ba..d724f1c788474af29bac37389c81fc1744fdca92 100644 (file)
@@ -585,6 +585,8 @@ static void continue_range(int ufd, __u64 start, __u64 len)
        req.range.start = start;
        req.range.len = len;
        req.mode = 0;
+       if (test_uffdio_wp)
+               req.mode |= UFFDIO_CONTINUE_MODE_WP;
 
        if (ioctl(ufd, UFFDIO_CONTINUE, &req))
                err("UFFDIO_CONTINUE failed for address 0x%" PRIx64,
@@ -1116,7 +1118,7 @@ static int __uffdio_zeropage(int ufd, unsigned long offset, bool retry)
 {
        struct uffdio_zeropage uffdio_zeropage;
        int ret;
-       bool has_zeropage = get_expected_ioctls(0) & (1 << _UFFDIO_ZEROPAGE);
+       bool has_zeropage = !(test_type == TEST_HUGETLB);
        __s64 res;
 
        if (offset >= nr_pages * page_size)
@@ -1332,6 +1334,8 @@ static int userfaultfd_minor_test(void)
        uffdio_register.range.start = (unsigned long)area_dst_alias;
        uffdio_register.range.len = nr_pages * page_size;
        uffdio_register.mode = UFFDIO_REGISTER_MODE_MINOR;
+       if (test_uffdio_wp)
+               uffdio_register.mode |= UFFDIO_REGISTER_MODE_WP;
        if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register))
                err("register failure");
 
@@ -1385,14 +1389,6 @@ static int userfaultfd_minor_test(void)
        return stats.missing_faults != 0 || stats.minor_faults != nr_pages;
 }
 
-#define BIT_ULL(nr)                   (1ULL << (nr))
-#define PM_SOFT_DIRTY                 BIT_ULL(55)
-#define PM_MMAP_EXCLUSIVE             BIT_ULL(56)
-#define PM_UFFD_WP                    BIT_ULL(57)
-#define PM_FILE                       BIT_ULL(61)
-#define PM_SWAP                       BIT_ULL(62)
-#define PM_PRESENT                    BIT_ULL(63)
-
 static int pagemap_open(void)
 {
        int fd = open("/proc/self/pagemap", O_RDONLY);
@@ -1403,19 +1399,6 @@ static int pagemap_open(void)
        return fd;
 }
 
-static uint64_t pagemap_read_vaddr(int fd, void *vaddr)
-{
-       uint64_t value;
-       int ret;
-
-       ret = pread(fd, &value, sizeof(uint64_t),
-                   ((uint64_t)vaddr >> 12) * sizeof(uint64_t));
-       if (ret != sizeof(uint64_t))
-               err("pread() on pagemap failed");
-
-       return value;
-}
-
 /* This macro let __LINE__ works in err() */
 #define  pagemap_check_wp(value, wp) do {                              \
                if (!!(value & PM_UFFD_WP) != wp)                       \
@@ -1431,7 +1414,7 @@ static int pagemap_test_fork(bool present)
        if (!child) {
                /* Open the pagemap fd of the child itself */
                fd = pagemap_open();
-               value = pagemap_read_vaddr(fd, area_dst);
+               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
@@ -1444,6 +1427,43 @@ static int pagemap_test_fork(bool present)
        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)
 {
        struct uffdio_register uffdio_register;
@@ -1462,7 +1482,7 @@ static void userfaultfd_pagemap_test(unsigned int test_pgsize)
        /* Flush so it doesn't flush twice in parent/child later */
        fflush(stdout);
 
-       uffd_test_ctx_init(0);
+       uffd_test_ctx_init(UFFD_FEATURE_WP_UNPOPULATED);
 
        if (test_pgsize > page_size) {
                /* This is a thp test */
@@ -1482,10 +1502,14 @@ static void userfaultfd_pagemap_test(unsigned int test_pgsize)
 
        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_read_vaddr(pagemap_fd, area_dst);
+       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))
@@ -1499,7 +1523,7 @@ static void userfaultfd_pagemap_test(unsigned int test_pgsize)
                err("madvise(MADV_PAGEOUT) failed");
 
        /* Uffd-wp should persist even swapped out */
-       value = pagemap_read_vaddr(pagemap_fd, area_dst);
+       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))
@@ -1507,12 +1531,12 @@ static void userfaultfd_pagemap_test(unsigned int test_pgsize)
 
        /* Unprotect; this tests swap pte modifications */
        wp_range(uffd, (uint64_t)area_dst, page_size, false);
-       value = pagemap_read_vaddr(pagemap_fd, area_dst);
+       value = pagemap_get_entry(pagemap_fd, area_dst);
        pagemap_check_wp(value, false);
 
        /* Fault in the page from disk */
        *area_dst = 2;
-       value = pagemap_read_vaddr(pagemap_fd, area_dst);
+       value = pagemap_get_entry(pagemap_fd, area_dst);
        pagemap_check_wp(value, false);
 
        close(pagemap_fd);
@@ -1526,7 +1550,7 @@ static int userfaultfd_stress(void)
        struct uffdio_register uffdio_register;
        struct uffd_stats uffd_stats[nr_cpus];
 
-       uffd_test_ctx_init(0);
+       uffd_test_ctx_init(UFFD_FEATURE_WP_UNPOPULATED);
 
        if (posix_memalign(&area, page_size, page_size))
                err("out of memory");
@@ -1658,30 +1682,6 @@ static int userfaultfd_stress(void)
                || userfaultfd_events_test() || userfaultfd_minor_test();
 }
 
-/*
- * Copied from mlock2-tests.c
- */
-unsigned long default_huge_page_size(void)
-{
-       unsigned long hps = 0;
-       char *line = NULL;
-       size_t linelen = 0;
-       FILE *f = fopen("/proc/meminfo", "r");
-
-       if (!f)
-               return 0;
-       while (getline(&line, &linelen, f) > 0) {
-               if (sscanf(line, "Hugepagesize:       %lu kB", &hps) == 1) {
-                       hps <<= 10;
-                       break;
-               }
-       }
-
-       free(line);
-       fclose(f);
-       return hps;
-}
-
 static void set_test_type(const char *type)
 {
        if (!strcmp(type, "anon")) {