[PATCH v3 07/18] selftests/mm: add folio-order check for address ranges
From: Kiryl Shutsemau
Date: Wed Aug 12 2026 - 09:28:21 EST
From: "Kiryl Shutsemau (Meta)" <kas@xxxxxxxxxx>
An mTHP collapse test has to ask whether a range came out as folios of one
particular order, placed where a collapse would place them. Nothing
answers that today: is_backed_by_folio() classifies the folio behind a
single page, and check_huge_anon() reads smaps AnonHugePages, which only
accounts PMD mappings.
Add is_range_backed_by_folio_orders(). For every order-aligned window of
the range it requires a present head PFN at its natural alignment and a
contiguous PFN run across the window. A window stitched together from
several folios, or one holding a folio shifted off its natural position,
fails -- which is what makes the helper usable for "this window collapsed
and that one did not".
Assisted-by: Claude-Code:claude-opus-5
Tested-by: Muhammad Usama Anjum <usama.anjum@xxxxxxx>
Signed-off-by: Kiryl Shutsemau (Meta) <kas@xxxxxxxxxx>
---
tools/testing/selftests/mm/vm_util.c | 42 ++++++++++++++++++++++++++++
tools/testing/selftests/mm/vm_util.h | 2 ++
2 files changed, 44 insertions(+)
diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c
index 5db1a7774f49..c9bd6c92fa41 100644
--- a/tools/testing/selftests/mm/vm_util.c
+++ b/tools/testing/selftests/mm/vm_util.c
@@ -556,6 +556,48 @@ bool is_backed_by_folio(char *vaddr, int order, int pagemap_fd,
return false;
}
+/*
+ * Check whether every order-@order window of [start, len) maps exactly one
+ * folio of that order, head to tail. The address range must be naturally
+ * aligned, each window's PFN run must be contiguous, and a window's first
+ * PFN must be the folio head.
+ *
+ * This is the check "did this range collapse into order-@order folios": a
+ * window assembled from parts of several folios, or mapping a folio shifted
+ * from its natural position, fails.
+ */
+bool is_range_backed_by_folio_orders(char *start, size_t len, int order,
+ int pagemap_fd, int kpageflags_fd)
+{
+ const unsigned long nr_pages = 1UL << order;
+ const size_t window = nr_pages * psize();
+ char *vaddr;
+
+ if ((uintptr_t)start % window || len % window)
+ return false;
+
+ for (vaddr = start; vaddr < start + len; vaddr += window) {
+ unsigned long pfn = pagemap_get_pfn(pagemap_fd, vaddr);
+ unsigned long i;
+
+ /* Not present, or not mapping the folio head. */
+ if (pfn == -1UL || pfn % nr_pages)
+ return false;
+
+ for (i = 1; i < nr_pages; i++) {
+ if (pagemap_get_pfn(pagemap_fd, vaddr + i * psize()) !=
+ pfn + i)
+ return false;
+ }
+
+ if (!is_backed_by_folio(vaddr, order, pagemap_fd,
+ kpageflags_fd))
+ return false;
+ }
+
+ return true;
+}
+
/* If `ioctls' non-NULL, the allowed ioctls will be returned into the var */
int uffd_register_with_ioctls(int uffd, void *addr, uint64_t len,
bool miss, bool wp, bool minor, uint64_t *ioctls)
diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
index 56a28ce7d029..39dfb18dc10c 100644
--- a/tools/testing/selftests/mm/vm_util.h
+++ b/tools/testing/selftests/mm/vm_util.h
@@ -99,6 +99,8 @@ 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_folio_orders(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