From: "Kiryl Shutsemau (Meta)" <[email protected]>

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) <[email protected]>
---
 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


Reply via email to