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
parent
bd66448f2a
commit
fca418e59a
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue