Add test cases to test the correctness of PFN ZERO flag of pagemap_scan
ioctl. Test with normal pages backed memory and huge pages backed
memory.

Cc: David Hildenbrand <[email protected]>
Signed-off-by: Muhammad Usama Anjum <[email protected]>
---
The bug has been fixed [1].

[1] https://lore.kernel.org/all/[email protected]
Changes since v1:
- Skip if madvise() fails
- Skip test if use_zero_page isn't set to 1
- Keep on using memalign()+free() to allocate huge pages

Changes sice v2:
- Move zero page detection code out to vm_util and use that
- Use mmap instead of memalign to allocate hugepage
---
 tools/testing/selftests/mm/cow.c           | 27 +-------
 tools/testing/selftests/mm/pagemap_ioctl.c | 72 +++++++++++++++++++++-
 tools/testing/selftests/mm/vm_util.c       | 23 +++++++
 tools/testing/selftests/mm/vm_util.h       |  2 +
 4 files changed, 95 insertions(+), 29 deletions(-)

diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/cow.c
index b6cfe0a4b7dfd..b26bbf6ec4617 100644
--- a/tools/testing/selftests/mm/cow.c
+++ b/tools/testing/selftests/mm/cow.c
@@ -72,31 +72,6 @@ static int detect_thp_sizes(size_t sizes[], int max)
        return count;
 }
 
-static void detect_huge_zeropage(void)
-{
-       int fd = open("/sys/kernel/mm/transparent_hugepage/use_zero_page",
-                     O_RDONLY);
-       size_t enabled = 0;
-       char buf[15];
-       int ret;
-
-       if (fd < 0)
-               return;
-
-       ret = pread(fd, buf, sizeof(buf), 0);
-       if (ret > 0 && ret < sizeof(buf)) {
-               buf[ret] = 0;
-
-               enabled = strtoul(buf, NULL, 10);
-               if (enabled == 1) {
-                       has_huge_zeropage = true;
-                       ksft_print_msg("[INFO] huge zeropage is enabled\n");
-               }
-       }
-
-       close(fd);
-}
-
 static bool range_is_swapped(void *addr, size_t size)
 {
        for (; size; addr += pagesize, size -= pagesize)
@@ -1791,7 +1766,7 @@ int main(int argc, char **argv)
        }
        nr_hugetlbsizes = detect_hugetlb_page_sizes(hugetlbsizes,
                                                    ARRAY_SIZE(hugetlbsizes));
-       detect_huge_zeropage();
+       has_huge_zeropage = detect_huge_zeropage();
 
        ksft_set_plan(ARRAY_SIZE(anon_test_cases) * tests_per_anon_test_case() +
                      ARRAY_SIZE(anon_thp_test_cases) * 
tests_per_anon_thp_test_case() +
diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c 
b/tools/testing/selftests/mm/pagemap_ioctl.c
index 57b4bba2b45f3..059c6d5f971e7 100644
--- a/tools/testing/selftests/mm/pagemap_ioctl.c
+++ b/tools/testing/selftests/mm/pagemap_ioctl.c
@@ -1,4 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
+
 #define _GNU_SOURCE
 #include <stdio.h>
 #include <fcntl.h>
@@ -34,8 +35,8 @@
 #define PAGEMAP "/proc/self/pagemap"
 int pagemap_fd;
 int uffd;
-unsigned int page_size;
-unsigned int hpage_size;
+size_t page_size;
+size_t hpage_size;
 const char *progname;
 
 #define LEN(region)    ((region.end - region.start)/page_size)
@@ -1480,6 +1481,68 @@ static void transact_test(int page_size)
                              extra_thread_faults);
 }
 
+void zeropfn_tests(void)
+{
+       unsigned long long mem_size;
+       struct page_region vec;
+       int i, ret;
+       char *mmap_mem, *mem;
+
+       /* Test with normal memory */
+       mem_size = 10 * page_size;
+       mem = mmap(NULL, mem_size, PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0);
+       if (mem == MAP_FAILED)
+               ksft_exit_fail_msg("error nomem\n");
+
+       /* Touch each page to ensure it's mapped */
+       for (i = 0; i < mem_size; i += page_size)
+               (void)((volatile char *)mem)[i];
+
+       ret = pagemap_ioctl(mem, mem_size, &vec, 1, 0,
+                           (mem_size / page_size), PAGE_IS_PFNZERO, 0, 0, 
PAGE_IS_PFNZERO);
+       if (ret < 0)
+               ksft_exit_fail_msg("error %d %d %s\n", ret, errno, 
strerror(errno));
+
+       ksft_test_result(ret == 1 && LEN(vec) == (mem_size / page_size),
+                        "%s all pages must have PFNZERO set\n", __func__);
+
+       munmap(mem, mem_size);
+
+       /* Test with huge page if user_zero_page is set to 1 */
+       if (!detect_huge_zeropage()) {
+               ksft_test_result_skip("%s use_zero_page not supported or set to 
1\n", __func__);
+               return;
+       }
+
+       mem_size = 2 * hpage_size;
+       mmap_mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE,
+                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+       if (mmap_mem == MAP_FAILED)
+               ksft_exit_fail_msg("error nomem\n");
+
+       /* We need a THP-aligned memory area. */
+       mem = (char *)(((uintptr_t)mmap_mem + hpage_size) & ~(hpage_size - 1));
+
+       ret = madvise(mem, hpage_size, MADV_HUGEPAGE);
+       if (!ret) {
+               char tmp = *mem;
+
+               asm volatile("" : "+r" (tmp));
+
+               ret = pagemap_ioctl(mem, hpage_size, &vec, 1, 0,
+                                   0, PAGE_IS_PFNZERO, 0, 0, PAGE_IS_PFNZERO);
+               if (ret < 0)
+                       ksft_exit_fail_msg("error %d %d %s\n", ret, errno, 
strerror(errno));
+
+               ksft_test_result(ret == 1 && LEN(vec) == (hpage_size / 
page_size),
+                                "%s all huge pages must have PFNZERO set\n", 
__func__);
+       } else {
+               ksft_test_result_skip("%s huge page not supported\n", __func__);
+       }
+
+       munmap(mmap_mem, mem_size);
+}
+
 int main(int __attribute__((unused)) argc, char *argv[])
 {
        int shmid, buf_size, fd, i, ret;
@@ -1494,7 +1557,7 @@ int main(int __attribute__((unused)) argc, char *argv[])
        if (init_uffd())
                ksft_exit_pass();
 
-       ksft_set_plan(115);
+       ksft_set_plan(117);
 
        page_size = getpagesize();
        hpage_size = read_pmd_pagesize();
@@ -1669,6 +1732,9 @@ int main(int __attribute__((unused)) argc, char *argv[])
        /* 16. Userfaultfd tests */
        userfaultfd_tests();
 
+       /* 17. ZEROPFN tests */
+       zeropfn_tests();
+
        close(pagemap_fd);
        ksft_exit_pass();
 }
diff --git a/tools/testing/selftests/mm/vm_util.c 
b/tools/testing/selftests/mm/vm_util.c
index a36734fb62f38..dde9e8ab4dc46 100644
--- a/tools/testing/selftests/mm/vm_util.c
+++ b/tools/testing/selftests/mm/vm_util.c
@@ -424,3 +424,26 @@ bool check_vmflag_io(void *addr)
                flags += flaglen;
        }
 }
+
+bool detect_huge_zeropage(void)
+{
+       int fd = open("/sys/kernel/mm/transparent_hugepage/use_zero_page",
+                     O_RDONLY);
+       bool enabled = 0;
+       char buf[15];
+       int ret;
+
+       if (fd < 0)
+               return 0;
+
+       ret = pread(fd, buf, sizeof(buf), 0);
+       if (ret > 0 && ret < sizeof(buf)) {
+               buf[ret] = 0;
+
+               if (strtoul(buf, NULL, 10) == 1)
+                       enabled = 1;
+       }
+
+       close(fd);
+       return enabled;
+}
diff --git a/tools/testing/selftests/mm/vm_util.h 
b/tools/testing/selftests/mm/vm_util.h
index 6effafdc4d8a2..ca4c1f78ce18c 100644
--- a/tools/testing/selftests/mm/vm_util.h
+++ b/tools/testing/selftests/mm/vm_util.h
@@ -74,6 +74,8 @@ int uffd_register_with_ioctls(int uffd, void *addr, uint64_t 
len,
 unsigned long get_free_hugepages(void);
 bool check_vmflag_io(void *addr);
 
+bool detect_huge_zeropage(void);
+
 /*
  * On ppc64 this will only work with radix 2M hugepage size
  */
-- 
2.43.0


Reply via email to