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

The new engine can collapse an mTHP inside a VMA smaller than a PMD.  The
old mechanism could not: its coverage was rooted at whole PMD-aligned
spans, so a VMA that could not hold one was passed over however many
mTHP-sized windows it held.  On arm64 with 64K pages, where a PMD is 512M,
that was every VMA below 512M.

Nothing covers that, so add two cases:

 - a VMA of exactly one window, the smallest thing that can be collapsed
   at all;
 - a VMA of several windows, which also walks from one window to the next
   inside a single VMA.

The second takes as many windows as fit in half a table.  At the order
just below the PMD order two windows are already a whole table, so there
is no room for several and the case skips.

Assisted-by: Claude-Code:claude-opus-5
Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>
---
 tools/testing/selftests/mm/khugepaged.c | 65 +++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/tools/testing/selftests/mm/khugepaged.c 
b/tools/testing/selftests/mm/khugepaged.c
index 172e7307eeee..85f138cfb9b2 100644
--- a/tools/testing/selftests/mm/khugepaged.c
+++ b/tools/testing/selftests/mm/khugepaged.c
@@ -1536,6 +1536,69 @@ static void collapse_order_mixed_sources(struct 
collapse_context *c,
        ksft_test_result_report(exit_status, "%s\n", __func__);
 }
 
+/*
+ * A VMA smaller than a PMD is a valid collapse target, so long as it holds a
+ * naturally aligned window of the target order.  This is the case khugepaged
+ * used to pass over entirely, its coverage being rooted at whole PMD-aligned
+ * spans, which on arm64 with 64K pages meant every VMA below 512M.
+ */
+static void __collapse_order_sub_pmd_vma(struct collapse_context *c,
+                                        struct mem_ops *ops, int nr_windows,
+                                        const char *name)
+{
+       size_t size = nr_windows * mthp_window_size();
+       void *p;
+
+       mthp_push_target_order();
+
+       p = mmap(BASE_ADDR, size, PROT_READ | PROT_WRITE,
+                MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+       if (p != BASE_ADDR)
+               ksft_exit_fail_msg("Failed to allocate VMA at %p\n", BASE_ADDR);
+
+       fill_memory(p, 0, size);
+       if (!window_not_collapsed(p, size))
+               ksft_exit_fail_msg("Unexpected large folio after fault\n");
+
+       madvise(p, size, MADV_HUGEPAGE);
+       ksft_print_msg("Collapse inside a sub-PMD VMA (%d windows)...",
+                      nr_windows);
+       if (!khugepaged_wait_full_pass())
+               fail("Timeout");
+       else if (window_collapsed(p, size))
+               success("OK");
+       else
+               fail("Fail");
+
+       validate_memory(p, 0, size);
+       munmap(p, size);
+       thp_pop_settings();
+       ksft_test_result_report(exit_status, "%s\n", name);
+}
+
+static void collapse_order_sub_pmd_vma(struct collapse_context *c,
+                                      struct mem_ops *ops)
+{
+       __collapse_order_sub_pmd_vma(c, ops, 1, __func__);
+}
+
+static void collapse_order_sub_pmd_range(struct collapse_context *c,
+                                        struct mem_ops *ops)
+{
+       size_t window = mthp_window_size();
+       int nr_windows = 16;
+
+       while (nr_windows > 1 && nr_windows * window > hpage_pmd_size / 2)
+               nr_windows /= 2;
+
+       if (nr_windows == 1) {
+               ksft_test_result_skip("%s: no room for multiple windows below 
the PMD\n",
+                                     __func__);
+               return;
+       }
+       __collapse_order_sub_pmd_vma(c, ops, nr_windows, __func__);
+}
+
 static void usage(void)
 {
        fprintf(stderr, "\nUsage: ./khugepaged [OPTIONS] <test type> 
[dir]\n\n");
@@ -1823,6 +1886,8 @@ int main(int argc, char **argv)
                TEST(collapse_order_partial_window, mthp_khugepaged_context, 
anon_ops);
                TEST(collapse_order_max_ptes_none, mthp_khugepaged_context, 
anon_ops);
                TEST(collapse_order_mixed_sources, mthp_khugepaged_context, 
anon_ops);
+               TEST(collapse_order_sub_pmd_vma, mthp_khugepaged_context, 
anon_ops);
+               TEST(collapse_order_sub_pmd_range, mthp_khugepaged_context, 
anon_ops);
        }
 
        TEST(collapse_full, madvise_context, anon_ops);
-- 
2.54.0


Reply via email to