[PATCH 01/16] selftests/mm: move is_backed_by_folio() into vm_util
From: Kiryl Shutsemau
Date: Sun Aug 02 2026 - 15:55:34 EST
From: "Kiryl Shutsemau (Meta)" <kas@xxxxxxxxxx>
The khugepaged selftest is about to gain mTHP collapse coverage, which
needs to verify that a VA range is backed by a folio of a given order.
split_huge_page_test.c already has the building block for that:
is_backed_by_folio() classifies the folio backing a page via
/proc/kpageflags compound head/tail flags.
Move it into vm_util so other tests can use it. No functional change.
Assisted-by: Claude-Code:claude-opus-5
Signed-off-by: Kiryl Shutsemau (Meta) <kas@xxxxxxxxxx>
---
.../selftests/mm/split_huge_page_test.c | 62 -------------------
tools/testing/selftests/mm/vm_util.c | 62 +++++++++++++++++++
tools/testing/selftests/mm/vm_util.h | 2 +
3 files changed, 64 insertions(+), 62 deletions(-)
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
index 32b991472f74..9b3885bd001b 100644
--- a/tools/testing/selftests/mm/split_huge_page_test.c
+++ b/tools/testing/selftests/mm/split_huge_page_test.c
@@ -42,68 +42,6 @@ const char *kpageflags_proc = "/proc/kpageflags";
int pagemap_fd;
int kpageflags_fd;
-static bool is_backed_by_folio(char *vaddr, int order, int pagemap_fd,
- int kpageflags_fd)
-{
- 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;
- unsigned long pfn_head;
- uint64_t pfn_flags;
- unsigned long pfn;
- unsigned long i;
-
- pfn = pagemap_get_pfn(pagemap_fd, vaddr);
-
- /* non present page */
- if (pfn == -1UL)
- return false;
-
- if (pageflags_get(pfn, kpageflags_fd, &pfn_flags))
- goto fail;
-
- /* check for order-0 pages */
- if (!order) {
- if (pfn_flags & (folio_head_flags | folio_tail_flags))
- return false;
- return true;
- }
-
- /* non THP folio */
- if (!(pfn_flags & KPF_THP))
- return false;
-
- pfn_head = pfn & ~(nr_pages - 1);
-
- if (pageflags_get(pfn_head, kpageflags_fd, &pfn_flags))
- goto fail;
-
- /* head PFN has no compound_head flag set */
- if ((pfn_flags & folio_head_flags) != folio_head_flags)
- return false;
-
- /* check all tail PFN flags */
- for (i = 1; i < nr_pages; i++) {
- if (pageflags_get(pfn_head + i, kpageflags_fd, &pfn_flags))
- goto fail;
- if ((pfn_flags & folio_tail_flags) != folio_tail_flags)
- return false;
- }
-
- /*
- * check the PFN after this folio, but if its flags cannot be obtained,
- * assume this folio has the expected order
- */
- if (pageflags_get(pfn_head + nr_pages, kpageflags_fd, &pfn_flags))
- return true;
-
- /* If we find another tail page, then the folio is larger. */
- return (pfn_flags & folio_tail_flags) != folio_tail_flags;
-fail:
- ksft_exit_fail_msg("Failed to get folio info\n");
- return false;
-}
-
static int vaddr_pageflags_get(char *vaddr, int pagemap_fd, int kpageflags_fd,
uint64_t *flags)
{
diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c
index ef1ea11981a7..343a15e25a9f 100644
--- a/tools/testing/selftests/mm/vm_util.c
+++ b/tools/testing/selftests/mm/vm_util.c
@@ -303,6 +303,68 @@ 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)
+{
+ 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;
+ unsigned long pfn_head;
+ uint64_t pfn_flags;
+ unsigned long pfn;
+ unsigned long i;
+
+ pfn = pagemap_get_pfn(pagemap_fd, vaddr);
+
+ /* non present page */
+ if (pfn == -1UL)
+ return false;
+
+ if (pageflags_get(pfn, kpageflags_fd, &pfn_flags))
+ goto fail;
+
+ /* check for order-0 pages */
+ if (!order) {
+ if (pfn_flags & (folio_head_flags | folio_tail_flags))
+ return false;
+ return true;
+ }
+
+ /* non THP folio */
+ if (!(pfn_flags & KPF_THP))
+ return false;
+
+ pfn_head = pfn & ~(nr_pages - 1);
+
+ if (pageflags_get(pfn_head, kpageflags_fd, &pfn_flags))
+ goto fail;
+
+ /* head PFN has no compound_head flag set */
+ if ((pfn_flags & folio_head_flags) != folio_head_flags)
+ return false;
+
+ /* check all tail PFN flags */
+ for (i = 1; i < nr_pages; i++) {
+ if (pageflags_get(pfn_head + i, kpageflags_fd, &pfn_flags))
+ goto fail;
+ if ((pfn_flags & folio_tail_flags) != folio_tail_flags)
+ return false;
+ }
+
+ /*
+ * check the PFN after this folio, but if its flags cannot be obtained,
+ * assume this folio has the expected order
+ */
+ if (pageflags_get(pfn_head + nr_pages, kpageflags_fd, &pfn_flags))
+ return true;
+
+ /* If we find another tail page, then the folio is larger. */
+ return (pfn_flags & folio_tail_flags) != folio_tail_flags;
+fail:
+ ksft_exit_fail_msg("Failed to get folio info\n");
+ return false;
+}
+
/* 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 7799154b67ee..5eb90e0d1d13 100644
--- a/tools/testing/selftests/mm/vm_util.h
+++ b/tools/testing/selftests/mm/vm_util.h
@@ -95,6 +95,8 @@ bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size);
bool check_huge_shmem(void *addr, 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);
+bool is_backed_by_folio(char *vaddr, 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