From: "Kiryl Shutsemau (Meta)" <[email protected]> split_huge_page_test.c checks a split by counting folios of each order in the range. The count does not say where the folios are, so a split at the wrong offset passes as long as the per-order totals match.
The layout after a split is known. A uniform split leaves one folio of the target order per window. A non-uniform split halves the folio and then the half containing the offset, down to the target order, and each folio it leaves behind sits where the offset determines. Check these positions with check_huge_anon() and check_huge_file(). Unlike the count, this requires every page to be present. That is fine, the test has just read the whole range. This removes the last user of gather_folio_orders(). Drop it. Make is_backed_by_folio() use the cached descriptors as well, so the test does not need its own. Assisted-by: LLM Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]> --- .../selftests/mm/split_huge_page_test.c | 104 ++++++-------- tools/testing/selftests/mm/vm_util.c | 128 +----------------- tools/testing/selftests/mm/vm_util.h | 5 +- 3 files changed, 44 insertions(+), 193 deletions(-) diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index ef4058662b91..1539ce2c0954 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -26,7 +26,6 @@ uint64_t pagesize; unsigned int pageshift; uint64_t pmd_pagesize; unsigned int pmd_order; -int *expected_orders; #define SPLIT_DEBUGFS "/sys/kernel/debug/split_huge_pages" #define SMAP_PATH "/proc/self/smaps" @@ -36,38 +35,40 @@ int *expected_orders; #define PID_FMT_OFFSET "%d,0x%lx,0x%lx,%d,%d" #define PATH_FMT "%s,0x%lx,0x%lx,%d" -const char *pagemap_proc = "/proc/self/pagemap"; -const char *kpageflags_proc = "/proc/kpageflags"; -int pagemap_fd; -int kpageflags_fd; - -static int check_after_split_folio_orders(char *vaddr_start, size_t len, - int pagemap_fd, int kpageflags_fd, int orders[], int nr_orders) +/* + * A non-uniform split halves the folio, then halves the half holding + * @offset, down to @order. Every half left behind is one folio, and the + * last two halves are both of @order. Check that each PMD-sized part of + * the range came out that way. + */ +static bool check_split_at_offset(char *addr, size_t len, int order, int offset) { - int *vaddr_orders; - int status; - int i; + char *part; - vaddr_orders = (int *)malloc(sizeof(int) * nr_orders); + for (part = addr; part < addr + len; part += pmd_pagesize) { + size_t off = (size_t)offset * pagesize; + char *lo = part; + int cur; - if (!vaddr_orders) - ksft_exit_fail_msg("Cannot allocate memory for vaddr_orders"); + for (cur = pmd_order - 1; cur >= order; cur--) { + size_t half = pagesize << cur; + char *left; - memset(vaddr_orders, 0, sizeof(int) * nr_orders); - status = gather_folio_orders(vaddr_start, len, pagemap_fd, - kpageflags_fd, vaddr_orders, nr_orders); - if (status) - ksft_exit_fail_msg("gather folio info failed\n"); - - for (i = 0; i < nr_orders; i++) - if (vaddr_orders[i] != orders[i]) { - ksft_print_msg("order %d: expected: %d got %d\n", i, - orders[i], vaddr_orders[i]); - status = -1; + if (off < half) { + left = lo + half; + } else { + left = lo; + lo += half; + off -= half; + } + if (!check_huge_file(left, half, 1, half)) + return false; } + if (!check_huge_file(lo, pagesize << order, 1, pagesize << order)) + return false; + } - free(vaddr_orders); - return status; + return true; } static void write_debugfs(const char *fmt, ...) @@ -191,12 +192,8 @@ static void split_pmd_thp_to_order(int order) if (one_page[i] != (char)i) ksft_exit_fail_msg("%ld byte corrupted\n", i); - memset(expected_orders, 0, sizeof(int) * (pmd_order + 1)); - expected_orders[order] = 4 << (pmd_order - order); - - if (check_after_split_folio_orders(one_page, len, pagemap_fd, - kpageflags_fd, expected_orders, - (pmd_order + 1))) + if (!check_huge_anon(one_page, len, len / (pagesize << order), + pagesize << order)) ksft_exit_fail_msg("Unexpected THP split\n"); if (!check_huge_anon(one_page, 4 * pmd_pagesize, 0, pmd_pagesize)) @@ -264,8 +261,7 @@ static void split_pte_mapped_thp(void) * check_huge_anon() cannot be used as it checks for PMD mappings. */ for (i = 0; i < nr_thps; i++) { - if (is_backed_by_folio(page_area + i * pagesize, pmd_order, - pagemap_fd, kpageflags_fd)) + if (is_backed_by_folio(page_area + i * pagesize, pmd_order)) continue; ksft_test_result_fail("THP %zu missing after mremap\n", i); goto out; @@ -285,8 +281,7 @@ static void split_pte_mapped_thp(void) /* Split failed? */ for (i = 0; i < nr_thps; i++) { - if (is_backed_by_folio(page_area + i * pagesize, 0, - pagemap_fd, kpageflags_fd)) + if (is_backed_by_folio(page_area + i * pagesize, 0)) continue; ksft_test_result_fail("THP %zu not split\n", i); } @@ -518,6 +513,7 @@ static void split_thp_in_pagecache_to_order_at(size_t fd_size, size_t i; char testfile[INPUT_MAX]; int err = 0; + bool ok; err = snprintf(testfile, INPUT_MAX, "%s/test", fs_loc); @@ -530,7 +526,6 @@ static void split_thp_in_pagecache_to_order_at(size_t fd_size, err = 0; - memset(expected_orders, 0, sizeof(int) * (pmd_order + 1)); /* * use [split_addr, split_addr + pagesize) range to split THPs, since * the debugfs function always split a range with pagesize step and @@ -541,18 +536,10 @@ static void split_thp_in_pagecache_to_order_at(size_t fd_size, for (split_addr = addr; split_addr < addr + fd_size; split_addr += pmd_pagesize) write_debugfs(PID_FMT, getpid(), (uint64_t)split_addr, (uint64_t)split_addr + pagesize, order); - - expected_orders[order] = fd_size / (pagesize << order); } else { - int times = fd_size / pmd_pagesize; - for (split_addr = addr; split_addr < addr + fd_size; split_addr += pmd_pagesize) write_debugfs(PID_FMT_OFFSET, getpid(), (uint64_t)split_addr, (uint64_t)split_addr + pagesize, order, offset); - - for (i = order + 1; i < pmd_order; i++) - expected_orders[i] = times; - expected_orders[order] = 2 * times; } for (i = 0; i < fd_size; i++) @@ -562,9 +549,12 @@ static void split_thp_in_pagecache_to_order_at(size_t fd_size, goto out; } - if (check_after_split_folio_orders(addr, fd_size, pagemap_fd, - kpageflags_fd, expected_orders, - (pmd_order + 1))) { + if (offset == -1) + ok = check_huge_file(addr, fd_size, fd_size / (pagesize << order), + pagesize << order); + else + ok = check_split_at_offset(addr, fd_size, order, offset); + if (!ok) { ksft_print_msg("Unexpected THP split\n"); err = 1; goto out; @@ -625,21 +615,9 @@ int main(int argc, char **argv) nr_pages = pmd_pagesize / pagesize; pmd_order = sz2ord(pmd_pagesize, pagesize); - expected_orders = (int *)malloc(sizeof(int) * (pmd_order + 1)); - if (!expected_orders) - ksft_exit_fail_msg("Fail to allocate memory: %s\n", strerror(errno)); - tests = 2 + (pmd_order - 1) + (2 * pmd_order) + (pmd_order - 1) * 4 + 2; ksft_set_plan(tests); - pagemap_fd = open(pagemap_proc, O_RDONLY); - if (pagemap_fd == -1) - ksft_exit_fail_msg("read pagemap: %s\n", strerror(errno)); - - kpageflags_fd = open(kpageflags_proc, O_RDONLY); - if (kpageflags_fd == -1) - ksft_exit_fail_msg("read kpageflags: %s\n", strerror(errno)); - fd_size = 2 * pmd_pagesize; split_pmd_zero_pages(); @@ -664,9 +642,5 @@ int main(int argc, char **argv) split_thp_in_pagecache_to_order_at(fd_size, fs_loc, i, offset); cleanup_thp_fs(fs_loc, created_tmp); - close(pagemap_fd); - close(kpageflags_fd); - free(expected_orders); - ksft_finished(); } diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c index 4c0d0e6f2553..abf047a2c09e 100644 --- a/tools/testing/selftests/mm/vm_util.c +++ b/tools/testing/selftests/mm/vm_util.c @@ -242,125 +242,6 @@ unsigned long rss_anon(void) return rss_anon; } -static int vaddr_pageflags_get(char *vaddr, int pagemap_fd, int kpageflags_fd, - uint64_t *flags) -{ - unsigned long pfn; - - pfn = pagemap_get_pfn(pagemap_fd, vaddr); - - /* non-present PFN */ - if (pfn == -1UL) - return 1; - - if (pageflags_get(pfn, kpageflags_fd, flags)) - return -1; - - return 0; -} - -/* - * gather_folio_orders - scan through [vaddr_start, len) and record - * folio orders - * - * @vaddr_start: start vaddr - * @len: range length - * @pagemap_fd: file descriptor to /proc/<pid>/pagemap - * @kpageflags_fd: file descriptor to /proc/kpageflags - * @orders: output folio order array - * @nr_orders: folio order array size - * - * gather_folio_orders() scan through [vaddr_start, len) and check - * all folios within the range and record their orders. All order-0 pages will - * be recorded. Non-present vaddr is skipped. - * - * Return: 0 - no error, -1 - unhandled cases - */ -int gather_folio_orders(char *vaddr_start, size_t len, - int pagemap_fd, int kpageflags_fd, int orders[], int nr_orders) -{ - uint64_t page_flags = 0; - int cur_order = -1; - char *vaddr; - - if (pagemap_fd == -1 || kpageflags_fd == -1) - return -1; - if (!orders) - return -1; - if (nr_orders <= 0) - return -1; - - for (vaddr = vaddr_start; vaddr < vaddr_start + len;) { - char *next_folio_vaddr; - int status; - - status = vaddr_pageflags_get(vaddr, pagemap_fd, kpageflags_fd, - &page_flags); - if (status < 0) - return -1; - - /* skip non present vaddr */ - if (status == 1) { - vaddr += psize(); - continue; - } - - /* all order-0 pages with possible false postive (non folio) */ - if (!(page_flags & (KPF_COMPOUND_HEAD | KPF_COMPOUND_TAIL))) { - orders[0]++; - vaddr += psize(); - continue; - } - - /* skip non thp compound pages */ - if (!(page_flags & KPF_THP)) { - vaddr += psize(); - continue; - } - - /* vpn points to part of a THP at this point */ - if (page_flags & KPF_COMPOUND_HEAD) - cur_order = 1; - else { - vaddr += psize(); - continue; - } - - next_folio_vaddr = vaddr + (1UL << (cur_order + pshift())); - - if (next_folio_vaddr >= vaddr_start + len) - break; - - while ((status = vaddr_pageflags_get(next_folio_vaddr, - pagemap_fd, kpageflags_fd, - &page_flags)) >= 0) { - /* - * non present vaddr, next compound head page, or - * order-0 page - */ - if (status == 1 || - (page_flags & KPF_COMPOUND_HEAD) || - !(page_flags & (KPF_COMPOUND_HEAD | KPF_COMPOUND_TAIL))) { - if (cur_order < nr_orders) { - orders[cur_order]++; - cur_order = -1; - vaddr = next_folio_vaddr; - } - break; - } - - cur_order++; - next_folio_vaddr = vaddr + (1UL << (cur_order + pshift())); - } - - if (status < 0) - return status; - } - if (cur_order > 0 && cur_order < nr_orders) - orders[cur_order]++; - return 0; -} - char *__get_smap_entry(void *addr, const char *pattern, char *buf, size_t len) { int ret; @@ -414,7 +295,6 @@ static bool is_range_backed_by_order(char *start, size_t len, int order) const unsigned long nr_pages = 1UL << order; const size_t folio_size = nr_pages * psize(); const int pagemap_fd = pagemap_fd_get(); - const int kpageflags_fd = kpageflags_fd_get(); char *vaddr; if ((uintptr_t)start % folio_size || len % folio_size) @@ -435,7 +315,7 @@ static bool is_range_backed_by_order(char *start, size_t len, int order) return false; } - if (!is_backed_by_folio(vaddr, order, pagemap_fd, kpageflags_fd)) + if (!is_backed_by_folio(vaddr, order)) return false; } @@ -579,12 +459,13 @@ int pageflags_get(unsigned long pfn, int kpageflags_fd, uint64_t *flags) return 0; } -bool is_backed_by_folio(char *vaddr, int order, int pagemap_fd, - int kpageflags_fd) +bool is_backed_by_folio(char *vaddr, int order) { const uint64_t folio_head_flags = KPF_THP | KPF_COMPOUND_HEAD; const uint64_t folio_tail_flags = KPF_THP | KPF_COMPOUND_TAIL; const unsigned long nr_pages = 1UL << order; + const int pagemap_fd = pagemap_fd_get(); + const int kpageflags_fd = kpageflags_fd_get(); unsigned long pfn_head; uint64_t pfn_flags; unsigned long pfn; @@ -640,7 +521,6 @@ bool is_backed_by_folio(char *vaddr, int order, int pagemap_fd, ksft_exit_fail_msg("Failed to get folio info\n"); } - #define TRACEFS_ROOT "/sys/kernel/tracing" /* diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h index 2bf64d5b42aa..581c1b8445a0 100644 --- a/tools/testing/selftests/mm/vm_util.h +++ b/tools/testing/selftests/mm/vm_util.h @@ -98,10 +98,7 @@ bool check_huge_anon(void *addr, size_t len, int nr_hpages, uint64_t hpage_size) bool check_huge_file(void *addr, size_t len, int nr_hpages, uint64_t hpage_size); int64_t allocate_transhuge(void *ptr, int pagemap_fd); int pageflags_get(unsigned long pfn, int kpageflags_fd, uint64_t *flags); -int gather_folio_orders(char *vaddr_start, size_t len, - int pagemap_fd, int kpageflags_fd, int orders[], int nr_orders); -bool is_backed_by_folio(char *vaddr, int order, int pagemap_fd, - int kpageflags_fd); +bool is_backed_by_folio(char *vaddr, int order); int uffd_register(int uffd, void *addr, uint64_t len, bool miss, bool wp, bool minor); -- 2.54.0

