selftests/mm: add check_after_split_folio_orders() helper

The helper gathers a folio order statistics of folios within a virtual
address range and checks it against a given order list. It aims to provide
a more precise folio order check instead of just checking the existence of
PMD folios.

The helper will be used the upcoming commit.

Link: https://lkml.kernel.org/r/20250818184622.1521620-5-ziy@nvidia.com
Signed-off-by: Zi Yan <ziy@nvidia.com>
Tested-by: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Barry Song <baohua@kernel.org>
Cc: David Hildenbrand <david@redhat.com>
Cc: Dev Jain <dev.jain@arm.com>
Cc: Donet Tom <donettom@linux.ibm.com>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Mariano Pache <npache@redhat.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: wang lian <lianux.mm@gmail.com>
Cc: Wei Yang <richard.weiyang@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
pull/1354/merge
Zi Yan 2025-08-18 14:46:21 -04:00 committed by Andrew Morton
parent bd66448f2a
commit fca418e59a
1 changed files with 152 additions and 0 deletions

View File

@ -98,6 +98,158 @@ fail:
return false;
}
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_after_split_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_after_split_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.
*
* NOTE: the function is used to check folio orders after a split is performed,
* so it assumes [vaddr_start, len) fully maps to after-split folios within that
* range.
*
* Return: 0 - no error, -1 - unhandled cases
*/
static int gather_after_split_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;
}
static int check_after_split_folio_orders(char *vaddr_start, size_t len,
int pagemap_fd, int kpageflags_fd, int orders[], int nr_orders)
{
int *vaddr_orders;
int status;
int i;
vaddr_orders = (int *)malloc(sizeof(int) * nr_orders);
if (!vaddr_orders)
ksft_exit_fail_msg("Cannot allocate memory for vaddr_orders");
memset(vaddr_orders, 0, sizeof(int) * nr_orders);
status = gather_after_split_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;
}
free(vaddr_orders);
return status;
}
static void write_file(const char *path, const char *buf, size_t buflen)
{
int fd;