]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
selftests/mm: add check_after_split_folio_orders() helper
authorZi Yan <ziy@nvidia.com>
Mon, 18 Aug 2025 18:46:21 +0000 (14:46 -0400)
committerAndrew Morton <akpm@linux-foundation.org>
Fri, 12 Sep 2025 00:25:12 +0000 (17:25 -0700)
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>
tools/testing/selftests/mm/split_huge_page_test.c

index 7be06dae663ed6ebe593dd2d3c04041b7d0deb04..cd90e8ed8dd151d35eb38f39a0083aa46f34278f 100644 (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;