* [PATCH 1/4] selftests/mm: open pagemap and kpageflags once for check_huge()
2026-09-29 13:15 [PATCH 0/4] selftests/mm: check large folio results through check_huge() only Kiryl Shutsemau
@ 2026-09-29 13:15 ` Kiryl Shutsemau
2026-09-29 15:28 ` Zi Yan
2026-09-29 13:15 ` [PATCH 2/4] selftests/mm: check sub-PMD ranges window by window in check_huge() Kiryl Shutsemau
` (2 subsequent siblings)
3 siblings, 1 reply; 7+ messages in thread
From: Kiryl Shutsemau @ 2026-09-29 13:15 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes, Mike Rapoport,
Zi Yan, Baolin Wang, Shuah Khan
Cc: Kiryl Shutsemau (Meta),
Barry Song, Dev Jain, Lance Yang, Liam R . Howlett, Michal Hocko,
Nico Pache, Ryan Roberts, Suren Baghdasaryan, Usama Arif,
Vlastimil Babka, Yeoreum Yun, linux-mm, linux-kselftest,
linux-kernel, kernel-team
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
check_huge_anon() and check_huge_file() open /proc/self/pagemap and
/proc/kpageflags on every call and read the PMD size from sysfs on top.
The khugepaged test calls them hundreds of times.
Open both once and keep the descriptors. A pagemap descriptor is tied
to the mm of the process that opened it, and the khugepaged fork() cases
call check_huge() from the child, so reopen it when getpid() changes.
Cache the PMD size in read_pmd_pagesize() as well.
Assisted-by: LLM
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
tools/testing/selftests/mm/vm_util.c | 68 ++++++++++++++++++++--------
1 file changed, 50 insertions(+), 18 deletions(-)
diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c
index 762c31f7dfab..e284e378d806 100644
--- a/tools/testing/selftests/mm/vm_util.c
+++ b/tools/testing/selftests/mm/vm_util.c
@@ -22,6 +22,43 @@
unsigned int __page_size;
unsigned int __page_shift;
+/*
+ * A pagemap file reads the mm of the process that opened it, so a child
+ * inherits a descriptor that describes its parent. Open a new one whenever
+ * the pid changed since the last open.
+ */
+static int pagemap_fd_get(void)
+{
+ static int fd = -1;
+ static pid_t pid;
+
+ if (fd >= 0 && pid == getpid())
+ return fd;
+
+ if (fd >= 0)
+ close(fd);
+
+ fd = open(PAGEMAP_PATH, O_RDONLY);
+ if (fd < 0)
+ ksft_exit_fail_msg("open pagemap fail\n");
+ pid = getpid();
+
+ return fd;
+}
+
+static int kpageflags_fd_get(void)
+{
+ static int fd = -1;
+
+ if (fd < 0) {
+ fd = open(KPAGEFLAGS_PATH, O_RDONLY);
+ if (fd < 0)
+ ksft_exit_fail_msg("open kpageflags fail\n");
+ }
+
+ return fd;
+}
+
uint64_t pagemap_get_entry(int fd, char *start)
{
const unsigned long pfn = (unsigned long)start / getpagesize();
@@ -161,10 +198,14 @@ bool check_for_pattern(FILE *fp, const char *pattern, char *buf, size_t len)
uint64_t read_pmd_pagesize(void)
{
+ static uint64_t pmd_pagesize;
int fd;
char buf[20];
ssize_t num_read;
+ if (pmd_pagesize)
+ return pmd_pagesize;
+
fd = open(PMD_SIZE_FILE_PATH, O_RDONLY);
if (fd == -1)
return 0;
@@ -177,7 +218,8 @@ uint64_t read_pmd_pagesize(void)
buf[num_read] = '\0';
close(fd);
- return strtoul(buf, NULL, 10);
+ pmd_pagesize = strtoul(buf, NULL, 10);
+ return pmd_pagesize;
}
unsigned long rss_anon(void)
@@ -407,7 +449,6 @@ 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)
{
- bool ret = false;
int pagemap_fd, kpageflags_fd;
int nr_pmd_mappings = 0;
uint64_t pmd_pagesize, scan_mapping_size;
@@ -426,42 +467,33 @@ static bool __check_huge(void *addr, size_t len, int nr_hpages,
/* Some mTHP tests check a partially populated PMD-sized range. */
allow_nonpresent = (uint64_t)nr_hpages * hpage_size < len;
- pagemap_fd = open(PAGEMAP_PATH, O_RDONLY);
- if (pagemap_fd < 0)
- ksft_exit_fail_msg("open pagemap fail\n");
-
- kpageflags_fd = open(KPAGEFLAGS_PATH, O_RDONLY);
- if (kpageflags_fd < 0)
- ksft_exit_fail_msg("open kpageflags fail\n");
+ pagemap_fd = pagemap_fd_get();
+ kpageflags_fd = kpageflags_fd_get();
if (!check_pmd_mapping &&
!check_large_folios(pagemap_fd, kpageflags_fd,
addr, len, nr_hpages, hpage_size))
- goto out;
+ return false;
for (; start < end; start += scan_mapping_size) {
categories = pagemap_scan_get_categories(pagemap_fd, start);
pfn = pagemap_get_pfn(pagemap_fd, start);
if (pfn == -1UL) {
if (!allow_nonpresent)
- goto out;
+ return false;
else
continue;
}
if (check_pmd_mapping && (categories & PAGE_IS_HUGE))
nr_pmd_mappings++;
if (!check_huge_type(categories, type))
- goto out;
+ return false;
}
if (check_pmd_mapping && (nr_pmd_mappings != nr_hpages))
- goto out;
- ret = true;
+ return false;
-out:
- close(pagemap_fd);
- close(kpageflags_fd);
- return ret;
+ return true;
}
bool check_huge_anon(void *addr, size_t len, int nr_hpages, uint64_t hpage_size)
--
2.54.0
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH 1/4] selftests/mm: open pagemap and kpageflags once for check_huge()
2026-09-29 13:15 ` [PATCH 1/4] selftests/mm: open pagemap and kpageflags once for check_huge() Kiryl Shutsemau
@ 2026-09-29 15:28 ` Zi Yan
2026-10-01 11:41 ` David Hildenbrand (Arm)
0 siblings, 1 reply; 7+ messages in thread
From: Zi Yan @ 2026-09-29 15:28 UTC (permalink / raw)
To: Kiryl Shutsemau
Cc: Andrew Morton, David Hildenbrand, Lorenzo Stoakes, Mike Rapoport,
Baolin Wang, Shuah Khan, Kiryl Shutsemau (Meta),
Barry Song, Dev Jain, Lance Yang, Liam R . Howlett, Michal Hocko,
Nico Pache, Ryan Roberts, Suren Baghdasaryan, Usama Arif,
Vlastimil Babka, Yeoreum Yun, linux-mm, linux-kselftest,
linux-kernel, kernel-team
On 29 Sep 2026, at 9:15, Kiryl Shutsemau wrote:
> From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
>
> check_huge_anon() and check_huge_file() open /proc/self/pagemap and
> /proc/kpageflags on every call and read the PMD size from sysfs on top.
> The khugepaged test calls them hundreds of times.
>
> Open both once and keep the descriptors. A pagemap descriptor is tied
> to the mm of the process that opened it, and the khugepaged fork() cases
> call check_huge() from the child, so reopen it when getpid() changes.
>
> Cache the PMD size in read_pmd_pagesize() as well.
This part might be overlapping with Yeoreum’s another series[1].
[1] https://lore.kernel.org/all/20260921-fix_hpage_macro-v4-2-cdea4fc6ce3f@arm.com/
>
> Assisted-by: LLM
> Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
> ---
> tools/testing/selftests/mm/vm_util.c | 68 ++++++++++++++++++++--------
> 1 file changed, 50 insertions(+), 18 deletions(-)
>
Best Regards,
Yan, Zi
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/4] selftests/mm: open pagemap and kpageflags once for check_huge()
2026-09-29 15:28 ` Zi Yan
@ 2026-10-01 11:41 ` David Hildenbrand (Arm)
0 siblings, 0 replies; 7+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-01 11:41 UTC (permalink / raw)
To: Zi Yan, Kiryl Shutsemau
Cc: Andrew Morton, Lorenzo Stoakes, Mike Rapoport, Baolin Wang,
Shuah Khan, Kiryl Shutsemau (Meta),
Barry Song, Dev Jain, Lance Yang, Liam R . Howlett, Michal Hocko,
Nico Pache, Ryan Roberts, Suren Baghdasaryan, Usama Arif,
Vlastimil Babka, Yeoreum Yun, linux-mm, linux-kselftest,
linux-kernel, kernel-team
On 9/29/26 17:28, Zi Yan wrote:
> On 29 Sep 2026, at 9:15, Kiryl Shutsemau wrote:
>
>> From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
>>
>> check_huge_anon() and check_huge_file() open /proc/self/pagemap and
>> /proc/kpageflags on every call and read the PMD size from sysfs on top.
>> The khugepaged test calls them hundreds of times.
>>
>> Open both once and keep the descriptors. A pagemap descriptor is tied
>> to the mm of the process that opened it, and the khugepaged fork() cases
>> call check_huge() from the child, so reopen it when getpid() changes.
>>
>> Cache the PMD size in read_pmd_pagesize() as well.
>
> This part might be overlapping with Yeoreum’s another series[1].
>
> [1] https://lore.kernel.org/all/20260921-fix_hpage_macro-v4-2-cdea4fc6ce3f@arm.com/
Yes, let's provide feedback there instead.
--
Cheers,
David
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 2/4] selftests/mm: check sub-PMD ranges window by window in check_huge()
2026-09-29 13:15 [PATCH 0/4] selftests/mm: check large folio results through check_huge() only Kiryl Shutsemau
2026-09-29 13:15 ` [PATCH 1/4] selftests/mm: open pagemap and kpageflags once for check_huge() Kiryl Shutsemau
@ 2026-09-29 13:15 ` Kiryl Shutsemau
2026-09-29 13:15 ` [PATCH 3/4] selftests/mm: check collapse results through check_huge() only Kiryl Shutsemau
2026-09-29 13:15 ` [PATCH 4/4] selftests/mm: check split results by position, drop gather_folio_orders() Kiryl Shutsemau
3 siblings, 0 replies; 7+ messages in thread
From: Kiryl Shutsemau @ 2026-09-29 13:15 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes, Mike Rapoport,
Zi Yan, Baolin Wang, Shuah Khan
Cc: Kiryl Shutsemau (Meta),
Barry Song, Dev Jain, Lance Yang, Liam R . Howlett, Michal Hocko,
Nico Pache, Ryan Roberts, Suren Baghdasaryan, Usama Arif,
Vlastimil Babka, Yeoreum Yun, linux-mm, linux-kselftest,
linux-kernel, kernel-team
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
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@kernel.org>
---
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
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH 3/4] selftests/mm: check collapse results through check_huge() only
2026-09-29 13:15 [PATCH 0/4] selftests/mm: check large folio results through check_huge() only Kiryl Shutsemau
2026-09-29 13:15 ` [PATCH 1/4] selftests/mm: open pagemap and kpageflags once for check_huge() Kiryl Shutsemau
2026-09-29 13:15 ` [PATCH 2/4] selftests/mm: check sub-PMD ranges window by window in check_huge() Kiryl Shutsemau
@ 2026-09-29 13:15 ` Kiryl Shutsemau
2026-09-29 13:15 ` [PATCH 4/4] selftests/mm: check split results by position, drop gather_folio_orders() Kiryl Shutsemau
3 siblings, 0 replies; 7+ messages in thread
From: Kiryl Shutsemau @ 2026-09-29 13:15 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes, Mike Rapoport,
Zi Yan, Baolin Wang, Shuah Khan
Cc: Kiryl Shutsemau (Meta),
Barry Song, Dev Jain, Lance Yang, Liam R . Howlett, Michal Hocko,
Nico Pache, Ryan Roberts, Suren Baghdasaryan, Usama Arif,
Vlastimil Babka, Yeoreum Yun, linux-mm, linux-kselftest,
linux-kernel, kernel-team
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
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) <kas@kernel.org>
---
.../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
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH 4/4] selftests/mm: check split results by position, drop gather_folio_orders()
2026-09-29 13:15 [PATCH 0/4] selftests/mm: check large folio results through check_huge() only Kiryl Shutsemau
` (2 preceding siblings ...)
2026-09-29 13:15 ` [PATCH 3/4] selftests/mm: check collapse results through check_huge() only Kiryl Shutsemau
@ 2026-09-29 13:15 ` Kiryl Shutsemau
3 siblings, 0 replies; 7+ messages in thread
From: Kiryl Shutsemau @ 2026-09-29 13:15 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes, Mike Rapoport,
Zi Yan, Baolin Wang, Shuah Khan
Cc: Kiryl Shutsemau (Meta),
Barry Song, Dev Jain, Lance Yang, Liam R . Howlett, Michal Hocko,
Nico Pache, Ryan Roberts, Suren Baghdasaryan, Usama Arif,
Vlastimil Babka, Yeoreum Yun, linux-mm, linux-kselftest,
linux-kernel, kernel-team
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
split_huge_page_test.c checks a split by counting folios of each order
in the range. The count does not say where the folios are, so a split
at the wrong offset passes as long as the per-order totals match.
The layout after a split is known. A uniform split leaves one folio of
the target order per window. A non-uniform split halves the folio and
then the half containing the offset, down to the target order, and each
folio it leaves behind sits where the offset determines. Check these
positions with check_huge_anon() and check_huge_file(). Unlike the
count, this requires every page to be present. That is fine, the test
has just read the whole range.
This removes the last user of gather_folio_orders(). Drop it. Make
is_backed_by_folio() use the cached descriptors as well, so the test
does not need its own.
Assisted-by: LLM
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
.../selftests/mm/split_huge_page_test.c | 104 ++++++--------
tools/testing/selftests/mm/vm_util.c | 128 +-----------------
tools/testing/selftests/mm/vm_util.h | 5 +-
3 files changed, 44 insertions(+), 193 deletions(-)
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
index ef4058662b91..1539ce2c0954 100644
--- a/tools/testing/selftests/mm/split_huge_page_test.c
+++ b/tools/testing/selftests/mm/split_huge_page_test.c
@@ -26,7 +26,6 @@ uint64_t pagesize;
unsigned int pageshift;
uint64_t pmd_pagesize;
unsigned int pmd_order;
-int *expected_orders;
#define SPLIT_DEBUGFS "/sys/kernel/debug/split_huge_pages"
#define SMAP_PATH "/proc/self/smaps"
@@ -36,38 +35,40 @@ int *expected_orders;
#define PID_FMT_OFFSET "%d,0x%lx,0x%lx,%d,%d"
#define PATH_FMT "%s,0x%lx,0x%lx,%d"
-const char *pagemap_proc = "/proc/self/pagemap";
-const char *kpageflags_proc = "/proc/kpageflags";
-int pagemap_fd;
-int kpageflags_fd;
-
-static int check_after_split_folio_orders(char *vaddr_start, size_t len,
- int pagemap_fd, int kpageflags_fd, int orders[], int nr_orders)
+/*
+ * A non-uniform split halves the folio, then halves the half holding
+ * @offset, down to @order. Every half left behind is one folio, and the
+ * last two halves are both of @order. Check that each PMD-sized part of
+ * the range came out that way.
+ */
+static bool check_split_at_offset(char *addr, size_t len, int order, int offset)
{
- int *vaddr_orders;
- int status;
- int i;
+ char *part;
- vaddr_orders = (int *)malloc(sizeof(int) * nr_orders);
+ for (part = addr; part < addr + len; part += pmd_pagesize) {
+ size_t off = (size_t)offset * pagesize;
+ char *lo = part;
+ int cur;
- if (!vaddr_orders)
- ksft_exit_fail_msg("Cannot allocate memory for vaddr_orders");
+ for (cur = pmd_order - 1; cur >= order; cur--) {
+ size_t half = pagesize << cur;
+ char *left;
- memset(vaddr_orders, 0, sizeof(int) * nr_orders);
- status = gather_folio_orders(vaddr_start, len, pagemap_fd,
- kpageflags_fd, vaddr_orders, nr_orders);
- if (status)
- ksft_exit_fail_msg("gather folio info failed\n");
-
- for (i = 0; i < nr_orders; i++)
- if (vaddr_orders[i] != orders[i]) {
- ksft_print_msg("order %d: expected: %d got %d\n", i,
- orders[i], vaddr_orders[i]);
- status = -1;
+ if (off < half) {
+ left = lo + half;
+ } else {
+ left = lo;
+ lo += half;
+ off -= half;
+ }
+ if (!check_huge_file(left, half, 1, half))
+ return false;
}
+ if (!check_huge_file(lo, pagesize << order, 1, pagesize << order))
+ return false;
+ }
- free(vaddr_orders);
- return status;
+ return true;
}
static void write_debugfs(const char *fmt, ...)
@@ -191,12 +192,8 @@ static void split_pmd_thp_to_order(int order)
if (one_page[i] != (char)i)
ksft_exit_fail_msg("%ld byte corrupted\n", i);
- memset(expected_orders, 0, sizeof(int) * (pmd_order + 1));
- expected_orders[order] = 4 << (pmd_order - order);
-
- if (check_after_split_folio_orders(one_page, len, pagemap_fd,
- kpageflags_fd, expected_orders,
- (pmd_order + 1)))
+ if (!check_huge_anon(one_page, len, len / (pagesize << order),
+ pagesize << order))
ksft_exit_fail_msg("Unexpected THP split\n");
if (!check_huge_anon(one_page, 4 * pmd_pagesize, 0, pmd_pagesize))
@@ -264,8 +261,7 @@ static void split_pte_mapped_thp(void)
* check_huge_anon() cannot be used as it checks for PMD mappings.
*/
for (i = 0; i < nr_thps; i++) {
- if (is_backed_by_folio(page_area + i * pagesize, pmd_order,
- pagemap_fd, kpageflags_fd))
+ if (is_backed_by_folio(page_area + i * pagesize, pmd_order))
continue;
ksft_test_result_fail("THP %zu missing after mremap\n", i);
goto out;
@@ -285,8 +281,7 @@ static void split_pte_mapped_thp(void)
/* Split failed? */
for (i = 0; i < nr_thps; i++) {
- if (is_backed_by_folio(page_area + i * pagesize, 0,
- pagemap_fd, kpageflags_fd))
+ if (is_backed_by_folio(page_area + i * pagesize, 0))
continue;
ksft_test_result_fail("THP %zu not split\n", i);
}
@@ -518,6 +513,7 @@ static void split_thp_in_pagecache_to_order_at(size_t fd_size,
size_t i;
char testfile[INPUT_MAX];
int err = 0;
+ bool ok;
err = snprintf(testfile, INPUT_MAX, "%s/test", fs_loc);
@@ -530,7 +526,6 @@ static void split_thp_in_pagecache_to_order_at(size_t fd_size,
err = 0;
- memset(expected_orders, 0, sizeof(int) * (pmd_order + 1));
/*
* use [split_addr, split_addr + pagesize) range to split THPs, since
* the debugfs function always split a range with pagesize step and
@@ -541,18 +536,10 @@ static void split_thp_in_pagecache_to_order_at(size_t fd_size,
for (split_addr = addr; split_addr < addr + fd_size; split_addr += pmd_pagesize)
write_debugfs(PID_FMT, getpid(), (uint64_t)split_addr,
(uint64_t)split_addr + pagesize, order);
-
- expected_orders[order] = fd_size / (pagesize << order);
} else {
- int times = fd_size / pmd_pagesize;
-
for (split_addr = addr; split_addr < addr + fd_size; split_addr += pmd_pagesize)
write_debugfs(PID_FMT_OFFSET, getpid(), (uint64_t)split_addr,
(uint64_t)split_addr + pagesize, order, offset);
-
- for (i = order + 1; i < pmd_order; i++)
- expected_orders[i] = times;
- expected_orders[order] = 2 * times;
}
for (i = 0; i < fd_size; i++)
@@ -562,9 +549,12 @@ static void split_thp_in_pagecache_to_order_at(size_t fd_size,
goto out;
}
- if (check_after_split_folio_orders(addr, fd_size, pagemap_fd,
- kpageflags_fd, expected_orders,
- (pmd_order + 1))) {
+ if (offset == -1)
+ ok = check_huge_file(addr, fd_size, fd_size / (pagesize << order),
+ pagesize << order);
+ else
+ ok = check_split_at_offset(addr, fd_size, order, offset);
+ if (!ok) {
ksft_print_msg("Unexpected THP split\n");
err = 1;
goto out;
@@ -625,21 +615,9 @@ int main(int argc, char **argv)
nr_pages = pmd_pagesize / pagesize;
pmd_order = sz2ord(pmd_pagesize, pagesize);
- expected_orders = (int *)malloc(sizeof(int) * (pmd_order + 1));
- if (!expected_orders)
- ksft_exit_fail_msg("Fail to allocate memory: %s\n", strerror(errno));
-
tests = 2 + (pmd_order - 1) + (2 * pmd_order) + (pmd_order - 1) * 4 + 2;
ksft_set_plan(tests);
- pagemap_fd = open(pagemap_proc, O_RDONLY);
- if (pagemap_fd == -1)
- ksft_exit_fail_msg("read pagemap: %s\n", strerror(errno));
-
- kpageflags_fd = open(kpageflags_proc, O_RDONLY);
- if (kpageflags_fd == -1)
- ksft_exit_fail_msg("read kpageflags: %s\n", strerror(errno));
-
fd_size = 2 * pmd_pagesize;
split_pmd_zero_pages();
@@ -664,9 +642,5 @@ int main(int argc, char **argv)
split_thp_in_pagecache_to_order_at(fd_size, fs_loc, i, offset);
cleanup_thp_fs(fs_loc, created_tmp);
- close(pagemap_fd);
- close(kpageflags_fd);
- free(expected_orders);
-
ksft_finished();
}
diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c
index 4c0d0e6f2553..abf047a2c09e 100644
--- a/tools/testing/selftests/mm/vm_util.c
+++ b/tools/testing/selftests/mm/vm_util.c
@@ -242,125 +242,6 @@ unsigned long rss_anon(void)
return rss_anon;
}
-static int vaddr_pageflags_get(char *vaddr, int pagemap_fd, int kpageflags_fd,
- uint64_t *flags)
-{
- unsigned long pfn;
-
- pfn = pagemap_get_pfn(pagemap_fd, vaddr);
-
- /* non-present PFN */
- if (pfn == -1UL)
- return 1;
-
- if (pageflags_get(pfn, kpageflags_fd, flags))
- return -1;
-
- return 0;
-}
-
-/*
- * gather_folio_orders - scan through [vaddr_start, len) and record
- * folio orders
- *
- * @vaddr_start: start vaddr
- * @len: range length
- * @pagemap_fd: file descriptor to /proc/<pid>/pagemap
- * @kpageflags_fd: file descriptor to /proc/kpageflags
- * @orders: output folio order array
- * @nr_orders: folio order array size
- *
- * gather_folio_orders() scan through [vaddr_start, len) and check
- * all folios within the range and record their orders. All order-0 pages will
- * be recorded. Non-present vaddr is skipped.
- *
- * Return: 0 - no error, -1 - unhandled cases
- */
-int gather_folio_orders(char *vaddr_start, size_t len,
- int pagemap_fd, int kpageflags_fd, int orders[], int nr_orders)
-{
- uint64_t page_flags = 0;
- int cur_order = -1;
- char *vaddr;
-
- if (pagemap_fd == -1 || kpageflags_fd == -1)
- return -1;
- if (!orders)
- return -1;
- if (nr_orders <= 0)
- return -1;
-
- for (vaddr = vaddr_start; vaddr < vaddr_start + len;) {
- char *next_folio_vaddr;
- int status;
-
- status = vaddr_pageflags_get(vaddr, pagemap_fd, kpageflags_fd,
- &page_flags);
- if (status < 0)
- return -1;
-
- /* skip non present vaddr */
- if (status == 1) {
- vaddr += psize();
- continue;
- }
-
- /* all order-0 pages with possible false postive (non folio) */
- if (!(page_flags & (KPF_COMPOUND_HEAD | KPF_COMPOUND_TAIL))) {
- orders[0]++;
- vaddr += psize();
- continue;
- }
-
- /* skip non thp compound pages */
- if (!(page_flags & KPF_THP)) {
- vaddr += psize();
- continue;
- }
-
- /* vpn points to part of a THP at this point */
- if (page_flags & KPF_COMPOUND_HEAD)
- cur_order = 1;
- else {
- vaddr += psize();
- continue;
- }
-
- next_folio_vaddr = vaddr + (1UL << (cur_order + pshift()));
-
- if (next_folio_vaddr >= vaddr_start + len)
- break;
-
- while ((status = vaddr_pageflags_get(next_folio_vaddr,
- pagemap_fd, kpageflags_fd,
- &page_flags)) >= 0) {
- /*
- * non present vaddr, next compound head page, or
- * order-0 page
- */
- if (status == 1 ||
- (page_flags & KPF_COMPOUND_HEAD) ||
- !(page_flags & (KPF_COMPOUND_HEAD | KPF_COMPOUND_TAIL))) {
- if (cur_order < nr_orders) {
- orders[cur_order]++;
- cur_order = -1;
- vaddr = next_folio_vaddr;
- }
- break;
- }
-
- cur_order++;
- next_folio_vaddr = vaddr + (1UL << (cur_order + pshift()));
- }
-
- if (status < 0)
- return status;
- }
- if (cur_order > 0 && cur_order < nr_orders)
- orders[cur_order]++;
- return 0;
-}
-
char *__get_smap_entry(void *addr, const char *pattern, char *buf, size_t len)
{
int ret;
@@ -414,7 +295,6 @@ 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)
@@ -435,7 +315,7 @@ static bool is_range_backed_by_order(char *start, size_t len, int order)
return false;
}
- if (!is_backed_by_folio(vaddr, order, pagemap_fd, kpageflags_fd))
+ if (!is_backed_by_folio(vaddr, order))
return false;
}
@@ -579,12 +459,13 @@ 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)
+bool is_backed_by_folio(char *vaddr, int order)
{
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;
+ const int pagemap_fd = pagemap_fd_get();
+ const int kpageflags_fd = kpageflags_fd_get();
unsigned long pfn_head;
uint64_t pfn_flags;
unsigned long pfn;
@@ -640,7 +521,6 @@ bool is_backed_by_folio(char *vaddr, int order, int pagemap_fd,
ksft_exit_fail_msg("Failed to get folio info\n");
}
-
#define TRACEFS_ROOT "/sys/kernel/tracing"
/*
diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
index 2bf64d5b42aa..581c1b8445a0 100644
--- a/tools/testing/selftests/mm/vm_util.h
+++ b/tools/testing/selftests/mm/vm_util.h
@@ -98,10 +98,7 @@ bool check_huge_anon(void *addr, size_t len, int nr_hpages, uint64_t hpage_size)
bool check_huge_file(void *addr, size_t len, 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);
-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_backed_by_folio(char *vaddr, int order);
int uffd_register(int uffd, void *addr, uint64_t len,
bool miss, bool wp, bool minor);
--
2.54.0
^ permalink raw reply [flat|nested] 7+ messages in thread