From: "Kiryl Shutsemau (Meta)" <[email protected]> khugepaged.c checks collapse results in two ways. Generic cases go through the mem_ops check_huge() callback. collapse_order_* cases call is_range_backed_by_order() on descriptors of their own. Baolin Wang found that confusing. The split only existed because check_huge() counted folios instead of windows.
Now that check_huge() checks windows, the two give the same answer. Use the callback for the collapse_order_* cases and drop the private descriptors. Convert khugepaged_sync_check and folio_order_check the same way. The latter still checks that each order is detected as that order, not as the one below and not as order 0, only now through check_huge_anon(). At the PMD order that means the PMD-mapping check rather than the page-flag walk. is_range_backed_by_order() has no users outside vm_util.c left. Make it static and let it use the cached descriptors. Assisted-by: LLM Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]> --- .../testing/selftests/mm/folio_order_check.c | 28 +++-------- tools/testing/selftests/mm/khugepaged.c | 47 ++++++++----------- .../selftests/mm/khugepaged_sync_check.c | 7 +-- tools/testing/selftests/mm/vm_util.c | 19 +++----- tools/testing/selftests/mm/vm_util.h | 2 - 5 files changed, 34 insertions(+), 69 deletions(-) diff --git a/tools/testing/selftests/mm/folio_order_check.c b/tools/testing/selftests/mm/folio_order_check.c index 5eafbcc1b4f3..aa0586047620 100644 --- a/tools/testing/selftests/mm/folio_order_check.c +++ b/tools/testing/selftests/mm/folio_order_check.c @@ -1,13 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Self-check for the vm_util folio-order helpers, is_backed_by_folio() and - * is_range_backed_by_order(), which the khugepaged mTHP cases use to detect - * collapse results. For every anon THP order the kernel supports, fault - * memory in with only that order enabled and require the helpers to report - * exactly that order. + * Self-check for check_huge_anon(), which the khugepaged mTHP cases use to + * tell collapse results. For every anon THP order the kernel supports, + * fault memory in with only that order enabled and require the check to + * report exactly that order: not the order below it, and not order 0. */ #define _GNU_SOURCE -#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> @@ -17,9 +15,6 @@ #include "vm_util.h" #include <mm/hugepage_settings.h> -static int pagemap_fd; -static int kpageflags_fd; - static char *alloc_aligned(size_t size) { size_t len = size * 2; @@ -56,22 +51,20 @@ static void check_order(int order) p = alloc_aligned(size); *p = 1; - if (!is_range_backed_by_order(p, size, order, pagemap_fd, kpageflags_fd)) { + if (!check_huge_anon(p, size, 1, size)) { ksft_print_msg("order %d not detected after fault\n", order); ok = false; } /* A lower order must be rejected: the folio is larger */ - if (order && is_range_backed_by_order(p, size, order - 1, - pagemap_fd, kpageflags_fd)) { + if (order && check_huge_anon(p, size, 2, size / 2)) { ksft_print_msg("order %d also reported as order %d\n", order, order - 1); ok = false; } /* A large folio must not pass as order 0 */ - if (order && is_range_backed_by_order(p, size, 0, - pagemap_fd, kpageflags_fd)) { + if (order && check_huge_anon(p, size, 1 << order, psize())) { ksft_print_msg("order %d also reported as order 0\n", order); ok = false; } @@ -93,13 +86,6 @@ int main(void) if (!thp_available()) ksft_exit_skip("Transparent Hugepages not available\n"); - pagemap_fd = open("/proc/self/pagemap", O_RDONLY); - if (pagemap_fd < 0) - ksft_exit_fail_perror("open(/proc/self/pagemap)"); - kpageflags_fd = open("/proc/kpageflags", O_RDONLY); - if (kpageflags_fd < 0) - ksft_exit_skip("open(/proc/kpageflags) requires root\n"); - orders = thp_supported_orders(); if (!orders) ksft_exit_skip("No supported THP orders\n"); diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c index 61bd69d604d2..a5b682eca888 100644 --- a/tools/testing/selftests/mm/khugepaged.c +++ b/tools/testing/selftests/mm/khugepaged.c @@ -33,8 +33,6 @@ static int collapse_order; static bool collapse_order_set; static int collapse_orders[NR_ORDERS]; static int nr_collapse_orders; -static int pagemap_fd = -1; -static int kpageflags_fd = -1; #define PID_SMAPS "/proc/self/smaps" #define TEST_FILE "collapse_test_file" @@ -1330,19 +1328,20 @@ static void mthp_push_target_order(void) thp_push_settings(&settings); } -static bool all_windows_at_order(void *p, size_t len) +static bool all_windows_at_order(struct mem_ops *ops, void *p, size_t len) { - return is_range_backed_by_order(p, len, collapse_order, - pagemap_fd, kpageflags_fd); + size_t window = mthp_window_size(); + + return ops->check_huge(p, len, len / window, window); } -static bool any_window_at_order(void *p, size_t len) +static bool any_window_at_order(struct mem_ops *ops, void *p, size_t len) { size_t window = mthp_window_size(); char *addr = p; for (; len >= window; addr += window, len -= window) { - if (all_windows_at_order(addr, window)) + if (ops->check_huge(addr, window, 1, window)) return true; } return false; @@ -1358,7 +1357,7 @@ static void collapse_order_single_window(struct collapse_context *c, p = ops->setup_area(1); ops->fault(p, window, 2 * window); - if (any_window_at_order(p, hpage_pmd_size)) + if (any_window_at_order(ops, p, hpage_pmd_size)) ksft_exit_fail_msg("Unexpected large folio after fault\n"); if (madvise(p, hpage_pmd_size, MADV_HUGEPAGE)) @@ -1366,9 +1365,9 @@ static void collapse_order_single_window(struct collapse_context *c, ksft_print_msg("Collapse one fully populated window..."); if (!khugepaged_full_pass(MTHP_PASS_TIMEOUT_S)) fail("Timeout"); - else if (all_windows_at_order(p + window, window) && - !any_window_at_order(p, window) && - !any_window_at_order(p + 2 * window, + else if (all_windows_at_order(ops, p + window, window) && + !any_window_at_order(ops, p, window) && + !any_window_at_order(ops, p + 2 * window, hpage_pmd_size - 2 * window)) success("OK"); else @@ -1389,7 +1388,7 @@ static void collapse_order_partial_window(struct collapse_context *c, p = ops->setup_area(1); ops->fault(p, 0, page_size); - if (any_window_at_order(p, hpage_pmd_size)) + if (any_window_at_order(ops, p, hpage_pmd_size)) ksft_exit_fail_msg("Unexpected large folio after fault\n"); if (madvise(p, hpage_pmd_size, MADV_HUGEPAGE)) @@ -1397,7 +1396,7 @@ static void collapse_order_partial_window(struct collapse_context *c, ksft_print_msg("Collapse window with single PTE entry present..."); if (!khugepaged_full_pass(MTHP_PASS_TIMEOUT_S)) fail("Timeout"); - else if (all_windows_at_order(p, mthp_window_size())) + else if (all_windows_at_order(ops, p, mthp_window_size())) success("OK"); else fail("Fail"); @@ -1422,7 +1421,7 @@ static void collapse_order_max_ptes_none(struct collapse_context *c, p = ops->setup_area(1); ops->fault(p, 0, 2 * window - page_size); - if (any_window_at_order(p, hpage_pmd_size)) + if (any_window_at_order(ops, p, hpage_pmd_size)) ksft_exit_fail_msg("Unexpected large folio after fault\n"); if (madvise(p, hpage_pmd_size, MADV_HUGEPAGE)) @@ -1430,8 +1429,8 @@ static void collapse_order_max_ptes_none(struct collapse_context *c, ksft_print_msg("Collapse full window, not the one missing a page..."); if (!khugepaged_full_pass(MTHP_PASS_TIMEOUT_S)) fail("Timeout"); - else if (all_windows_at_order(p, window) && - !any_window_at_order(p + window, window)) + else if (all_windows_at_order(ops, p, window) && + !any_window_at_order(ops, p + window, window)) success("OK"); else fail("Fail"); @@ -1447,6 +1446,7 @@ static void collapse_order_mixed_sources(struct collapse_context *c, struct mem_ops *ops) { int source_order = anon_order ? anon_order : MIN_MTHP_ORDER; + size_t source_size = page_size << source_order; struct thp_settings settings; void *p; @@ -1470,8 +1470,8 @@ static void collapse_order_mixed_sources(struct collapse_context *c, * The allocator can fall back to smaller folios under fragmentation; * having nothing to collapse from is not a failure. */ - if (!is_range_backed_by_order(p, hpage_pmd_size, source_order, - pagemap_fd, kpageflags_fd)) { + if (!ops->check_huge(p, hpage_pmd_size, hpage_pmd_size / source_size, + source_size)) { ksft_print_msg("No order-%d sources to collapse...", source_order); skip("Skip"); ops->cleanup_area(p, hpage_pmd_size); @@ -1486,7 +1486,7 @@ static void collapse_order_mixed_sources(struct collapse_context *c, source_order); if (!khugepaged_full_pass(MTHP_PASS_TIMEOUT_S)) fail("Timeout"); - else if (all_windows_at_order(p, hpage_pmd_size)) + else if (all_windows_at_order(ops, p, hpage_pmd_size)) success("OK"); else fail("Fail"); @@ -1715,15 +1715,6 @@ int main(int argc, char **argv) } } - if (mthp_khugepaged_context) { - pagemap_fd = open("/proc/self/pagemap", O_RDONLY); - if (pagemap_fd < 0) - ksft_exit_fail_perror("open(/proc/self/pagemap)"); - kpageflags_fd = open("/proc/kpageflags", O_RDONLY); - if (kpageflags_fd < 0) - ksft_exit_fail_perror("open(/proc/kpageflags)"); - } - setbuf(stdout, NULL); /* diff --git a/tools/testing/selftests/mm/khugepaged_sync_check.c b/tools/testing/selftests/mm/khugepaged_sync_check.c index 28a9b1ff5d44..40cba121a987 100644 --- a/tools/testing/selftests/mm/khugepaged_sync_check.c +++ b/tools/testing/selftests/mm/khugepaged_sync_check.c @@ -26,7 +26,6 @@ #define PASS_TIMEOUT_S 30 static int pagemap_fd; -static int kpageflags_fd; static int trace_events_fd = -1; static unsigned long hpage_pmd_size; @@ -116,8 +115,7 @@ static void one_step(int iteration) if (!passed) ksft_exit_fail_msg("khugepaged did not complete a full pass\n"); - collapsed = is_range_backed_by_order(p, window, TARGET_ORDER, - pagemap_fd, kpageflags_fd); + collapsed = check_huge_anon(p, window, 1, window); attributed = count_attributed(pfns, nr_pages, TARGET_ORDER); ksft_test_result(collapsed && attributed == 1, @@ -146,9 +144,6 @@ int main(void) pagemap_fd = open("/proc/self/pagemap", O_RDONLY); if (pagemap_fd < 0) ksft_exit_fail_perror("open(/proc/self/pagemap)"); - kpageflags_fd = open("/proc/kpageflags", O_RDONLY); - if (kpageflags_fd < 0) - ksft_exit_skip("open(/proc/kpageflags) requires root\n"); trace_events_fd = tracing_events_open("huge_memory"); if (trace_events_fd < 0) ksft_exit_skip("huge_memory events require tracefs and root\n"); diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c index 0a4b85e29e14..4c0d0e6f2553 100644 --- a/tools/testing/selftests/mm/vm_util.c +++ b/tools/testing/selftests/mm/vm_util.c @@ -401,8 +401,6 @@ char *__get_smap_entry(void *addr, const char *pattern, char *buf, size_t len) * @start: start of the range, a multiple of the folio size * @len: length of the range in bytes, a multiple of the folio size * @order: the folio order to check for - * @pagemap_fd: open /proc/<pid>/pagemap of the range's owner - * @kpageflags_fd: open /proc/kpageflags * * Every folio-sized, folio-aligned part of the range must map one folio of * @order, head to tail, with the head at the start of the part. A part @@ -411,11 +409,12 @@ char *__get_smap_entry(void *addr, const char *pattern, char *buf, size_t len) * * Returns: true if the whole range is backed that way, false otherwise. */ -bool is_range_backed_by_order(char *start, size_t len, int order, - int pagemap_fd, int kpageflags_fd) +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) @@ -448,16 +447,14 @@ bool is_range_backed_by_order(char *start, size_t len, int order, * size each, with the folio's head at the window start. A folio mapped off * its alignment or split across two windows counts for neither. */ -static int count_windows_at_order(char *start, size_t len, uint64_t hpage_size, - int pagemap_fd, int kpageflags_fd) +static int count_windows_at_order(char *start, size_t len, uint64_t hpage_size) { const int order = sz2ord(hpage_size, psize()); int nr_windows = 0; char *addr; for (addr = start; addr + hpage_size <= start + len; addr += hpage_size) { - if (is_range_backed_by_order(addr, hpage_size, order, - pagemap_fd, kpageflags_fd)) + if (is_range_backed_by_order(addr, hpage_size, order)) nr_windows++; } @@ -486,7 +483,7 @@ static bool check_huge_type(uint64_t categories, enum check_huge_type type) static bool __check_huge(void *addr, size_t len, int nr_hpages, uint64_t hpage_size, enum check_huge_type type) { - int pagemap_fd, kpageflags_fd; + int pagemap_fd; int nr_pmd_mappings = 0; uint64_t pmd_pagesize, scan_mapping_size; uint64_t categories; @@ -505,11 +502,9 @@ static bool __check_huge(void *addr, size_t len, int nr_hpages, allow_nonpresent = (uint64_t)nr_hpages * hpage_size < len; pagemap_fd = pagemap_fd_get(); - kpageflags_fd = kpageflags_fd_get(); if (!check_pmd_mapping && - nr_hpages != count_windows_at_order(start, len, hpage_size, - pagemap_fd, kpageflags_fd)) + nr_hpages != count_windows_at_order(start, len, hpage_size)) return false; for (; start < end; start += scan_mapping_size) { diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h index b5d59729d432..2bf64d5b42aa 100644 --- a/tools/testing/selftests/mm/vm_util.h +++ b/tools/testing/selftests/mm/vm_util.h @@ -102,8 +102,6 @@ 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_range_backed_by_order(char *start, size_t len, int order, - int pagemap_fd, int kpageflags_fd); int uffd_register(int uffd, void *addr, uint64_t len, bool miss, bool wp, bool minor); -- 2.54.0

