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.

Signed-off-by: Zi Yan <z...@nvidia.com>
---
 .../selftests/mm/split_huge_page_test.c       | 150 ++++++++++++++++++
 1 file changed, 150 insertions(+)

diff --git a/tools/testing/selftests/mm/split_huge_page_test.c 
b/tools/testing/selftests/mm/split_huge_page_test.c
index 80f718ca21c7..d2467072e752 100644
--- a/tools/testing/selftests/mm/split_huge_page_test.c
+++ b/tools/testing/selftests/mm/split_huge_page_test.c
@@ -102,6 +102,156 @@ static int is_backed_by_folio(char *vaddr, int order, int 
pagemap_fd,
        return -1;
 }
 
+static int get_page_flags(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 (get_pfn_flags(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 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 = get_page_flags(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 = get_page_flags(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;
-- 
2.50.1


Reply via email to