selftests/mm: pagemap_scan ioctl: add PFN ZERO test cases

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.

Link: https://lkml.kernel.org/r/20250707073321.106431-1-usama.anjum@collabora.com
Signed-off-by: Muhammad Usama Anjum <usama.anjum@collabora.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Muhammad Usama Anjum <usama.anjum@collabora.com>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
Muhammad Usama Anjum 2025-07-07 12:33:20 +05:00 committed by Andrew Morton
parent 0b473f9e6e
commit f73858d5ef
4 changed files with 95 additions and 29 deletions

View File

@ -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)
@ -1903,7 +1878,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() +

View File

@ -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 long 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();
}

View File

@ -532,3 +532,26 @@ void *sys_mremap(void *old_address, unsigned long old_size,
old_size, new_size, flags,
(unsigned long)new_address);
}
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;
}

View File

@ -44,6 +44,8 @@ static inline unsigned int pshift(void)
return __page_shift;
}
bool detect_huge_zeropage(void);
/*
* Plan 9 FS has bugs (at least on QEMU) where certain operations fail with
* ENOENT on unlinked files. See