[PATCH 2/4] selftests/mm: check sub-PMD ranges window by window in check_huge()
From: Kiryl Shutsemau
Date: Tue Sep 29 2026 - 09:19:53 EST
From: "Kiryl Shutsemau (Meta)" <kas@xxxxxxxxxx>
For orders below PMD, check_huge() counts folios of the requested order
in the range and compares the count with nr_hpages. The count does not
say where the folios are. A folio mapped at the wrong alignment, or
split across two windows, counts the same as one placed where khugepaged
would put it.
Count aligned windows backed by a single folio of that order instead.
is_range_backed_by_order() already does it for one window. Move it up
and use it. All callers pass PMD-aligned ranges, so the windows line
up. The PMD case is unchanged.
This is preparation for using check_huge() for the collapse_order cases
in khugepaged.c.
Assisted-by: LLM
Signed-off-by: Kiryl Shutsemau (Meta) <kas@xxxxxxxxxx>
---
tools/testing/selftests/mm/vm_util.c | 131 +++++++++++++--------------
1 file changed, 61 insertions(+), 70 deletions(-)
diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c
index e284e378d806..0a4b85e29e14 100644
--- a/tools/testing/selftests/mm/vm_util.c
+++ b/tools/testing/selftests/mm/vm_util.c
@@ -17,7 +17,6 @@
#define MAX_LINE_LENGTH 500
#define PAGEMAP_PATH "/proc/self/pagemap"
#define KPAGEFLAGS_PATH "/proc/kpageflags"
-#define MAX_NR_ORDERS 20
unsigned int __page_size;
unsigned int __page_shift;
@@ -397,34 +396,72 @@ char *__get_smap_entry(void *addr, const char *pattern, char *buf, size_t len)
return entry;
}
-static bool check_large_folios(int pagemap_fd, int kpageflags_fd,
- void *addr, size_t len, int nr_hpages,
- uint64_t hpage_size)
+/**
+ * is_range_backed_by_order() - check that a range is backed by @order folios
+ * @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
+ * backed by several smaller folios fails, and so does a folio mapped off
+ * its natural alignment.
+ *
+ * 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)
{
- int order = 0, pagesize = getpagesize();
- unsigned int nr_pages = hpage_size / pagesize;
- int orders[MAX_NR_ORDERS], status;
- bool ret = false;
+ const unsigned long nr_pages = 1UL << order;
+ const size_t folio_size = nr_pages * psize();
+ char *vaddr;
- if (!nr_pages)
- ksft_exit_fail_msg("invalid hugepage size\n");
+ if ((uintptr_t)start % folio_size || len % folio_size)
+ return false;
- order = 31 - __builtin_clz(nr_pages);
- if (!order || order >= MAX_NR_ORDERS)
- ksft_exit_fail_msg("invalid order\n");
+ for (vaddr = start; vaddr < start + len; vaddr += folio_size) {
+ const unsigned long pfn = pagemap_get_pfn(pagemap_fd, vaddr);
+ unsigned long i;
- memset(orders, 0, sizeof(int) * MAX_NR_ORDERS);
+ /* Not present, or a tail page */
+ if (pfn == -1UL || pfn % nr_pages)
+ return false;
- status = gather_folio_orders(addr, len, pagemap_fd,
- kpageflags_fd, orders, MAX_NR_ORDERS);
- if (status)
- goto out;
+ for (i = 1; i < nr_pages; i++) {
+ char *page = vaddr + i * psize();
- if (orders[order] == nr_hpages)
- ret = true;
+ if (pagemap_get_pfn(pagemap_fd, page) != pfn + i)
+ return false;
+ }
-out:
- return ret;
+ if (!is_backed_by_folio(vaddr, order, pagemap_fd, kpageflags_fd))
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * How many hpage_size-aligned windows of the range are one folio of that
+ * 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)
+{
+ 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))
+ nr_windows++;
+ }
+
+ return nr_windows;
}
enum check_huge_type {
@@ -471,8 +508,8 @@ static bool __check_huge(void *addr, size_t len, int nr_hpages,
kpageflags_fd = kpageflags_fd_get();
if (!check_pmd_mapping &&
- !check_large_folios(pagemap_fd, kpageflags_fd,
- addr, len, nr_hpages, hpage_size))
+ nr_hpages != count_windows_at_order(start, len, hpage_size,
+ pagemap_fd, kpageflags_fd))
return false;
for (; start < end; start += scan_mapping_size) {
@@ -608,52 +645,6 @@ bool is_backed_by_folio(char *vaddr, int order, int pagemap_fd,
ksft_exit_fail_msg("Failed to get folio info\n");
}
-/**
- * is_range_backed_by_order() - check that a range is backed by @order folios
- * @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
- * backed by several smaller folios fails, and so does a folio mapped off
- * its natural alignment.
- *
- * 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)
-{
- const unsigned long nr_pages = 1UL << order;
- const size_t folio_size = nr_pages * psize();
- char *vaddr;
-
- if ((uintptr_t)start % folio_size || len % folio_size)
- return false;
-
- for (vaddr = start; vaddr < start + len; vaddr += folio_size) {
- const unsigned long pfn = pagemap_get_pfn(pagemap_fd, vaddr);
- unsigned long i;
-
- /* Not present, or a tail page */
- if (pfn == -1UL || pfn % nr_pages)
- return false;
-
- for (i = 1; i < nr_pages; i++) {
- char *page = vaddr + i * psize();
-
- if (pagemap_get_pfn(pagemap_fd, page) != pfn + i)
- return false;
- }
-
- if (!is_backed_by_folio(vaddr, order, pagemap_fd, kpageflags_fd))
- return false;
- }
-
- return true;
-}
#define TRACEFS_ROOT "/sys/kernel/tracing"
--
2.54.0