// 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. */ #define _GNU_SOURCE #include #include #include #include #include #include "kselftest.h" #include "vm_util.h" #include static int pagemap_fd; static int kpageflags_fd; static char *alloc_aligned(size_t size) { size_t len = size * 2; char *p, *aligned; p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (p == MAP_FAILED) ksft_exit_fail_perror("mmap()"); aligned = (char *)ALIGN((uintptr_t)p, size); if (aligned != p) munmap(p, aligned - p); if (aligned + size != p + len) munmap(aligned + size, p + len - aligned - size); return aligned; } static void check_order(int order) { struct thp_settings settings = *thp_current_settings(); size_t size = psize() << order; bool ok = true; char *p; int i; for (i = 0; i < NR_ORDERS; i++) settings.hugepages[i].enabled = THP_NEVER; if (order) settings.hugepages[order].enabled = THP_ALWAYS; thp_push_settings(&settings); p = alloc_aligned(size); *p = 1; if (!is_range_backed_by_order(p, size, order, pagemap_fd, kpageflags_fd)) { 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)) { 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)) { ksft_print_msg("order %d also reported as order 0\n", order); ok = false; } munmap(p, size); thp_pop_settings(); ksft_test_result(ok, "order %d classified\n", order); } int main(void) { struct thp_settings settings; unsigned long orders; int order; ksft_print_header(); 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"); ksft_set_plan(__builtin_popcountl(orders) + 1); thp_save_settings(); thp_read_settings(&settings); /* Base of the settings stack; the bottom entry is never popped */ thp_push_settings(&settings); check_order(0); for (order = 1; order < NR_ORDERS; order++) { if (!(orders & (1UL << order))) continue; check_order(order); } ksft_finished(); }