On Sat, Aug 15, 2026 at 02:58:53AM +0100, Kiryl Shutsemau wrote:
> From: "Kiryl Shutsemau (Meta)" <[email protected]>
>
> The mthp_khugepaged context runs the generic cases at a sub-PMD order,
> which answers how many folios of that order a range ends up with.  It
> cannot say which window they are in, so "the populated window collapsed
> and its neighbour did not" and "one window collapsed twice" look alike.
>
> Add four cases that check each aligned window on its own, with the
> folio-order helpers in vm_util:
>
> - collapse_order_single_window(): only the populated window collapses;
> - collapse_order_partial_window(): the default max_ptes_none lets a window
>   with one present PTE collapse;
> - collapse_order_max_ptes_none(): with max_ptes_none=0 a full window
>   collapses and one missing a page does not;
> - collapse_order_mixed_sources(): sources that are already large folios of
>   a smaller order collapse to the target.
>
> Each case faults its region before MADV_HUGEPAGE with only the target
> order enabled, so the sources are order 0 and the result can only come
> from khugepaged.  They wait for a full pass rather than for the result to
> appear: without a completed pass, "not collapsed" and "not scanned yet"
> are the same thing.
>
> Assisted-by: Claude-Code:claude-opus-5
> Tested-by: Muhammad Usama Anjum <[email protected]>
> Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>

Same comments on comments as other patches - far too dense, read like a
discussion and not a terse description of something the code doesn't make
clear.

Please write comments yourself, LLMs are terrible at it (I mean I feel the
same goes for code also).

> ---
>  tools/testing/selftests/mm/khugepaged.c | 230 ++++++++++++++++++++++++
>  1 file changed, 230 insertions(+)
>
> diff --git a/tools/testing/selftests/mm/khugepaged.c 
> b/tools/testing/selftests/mm/khugepaged.c
> index 0008862e7cbc..0489967d6ee0 100644
> --- a/tools/testing/selftests/mm/khugepaged.c
> +++ b/tools/testing/selftests/mm/khugepaged.c
> @@ -31,6 +31,8 @@ static unsigned long page_size;
>  static int hpage_pmd_nr;
>  static int anon_order;
>  static int collapse_order;
> +static int pagemap_fd = -1;
> +static int kpageflags_fd = -1;
>
>  #define PID_SMAPS "/proc/self/smaps"
>  #define TEST_FILE "collapse_test_file"
> @@ -1227,6 +1229,216 @@ static void madvise_retracted_page_tables(struct 
> collapse_context *c,
>       ksft_test_result_report(exit_status, "%s\n", __func__);
>  }
>
> +/* Smallest order khugepaged will consider for mTHP collapse. */
> +#define MIN_MTHP_ORDER 2
> +
> +/*
> + * Order-parameterized collapse cases for the mthp_khugepaged context.  What
> + * they add over the generic cases run under that context is per-window
> + * detection: which aligned window collapsed, and which of its neighbours did
> + * not.  check_huge() answers how many folios of the order the range holds,
> + * which cannot tell one window from another.
> + *
> + * The region is faulted before MADV_HUGEPAGE, and the target order is only
> + * enabled for madvise, so the sources are always order 0 and the collapse
> + * product can only have come from khugepaged.
> + */
> +static size_t mthp_window_size(void)
> +{
> +     return page_size << collapse_order;
> +}
> +
> +static void mthp_push_target_order(void)
> +{
> +     struct thp_settings settings = *thp_current_settings();
> +     int i;
> +
> +     /*
> +      * The target order, for madvise only, and nothing else enabled: the
> +      * cases fault their region before MADV_HUGEPAGE, so the sources are
> +      * order 0 whatever -s asked the fault path for.  That matters for the
> +      * cases built around a hole -- a large source folio would fill it in
> +      * and the window would collapse after all.
> +      * collapse_order_mixed_sources enables the source order it wants on
> +      * top of this.
> +      */
> +     settings.thp_enabled = THP_NEVER;
> +     for (i = 0; i < NR_ORDERS; i++)
> +             settings.hugepages[i].enabled = THP_NEVER;
> +     settings.hugepages[collapse_order].enabled = THP_MADVISE;
> +     thp_push_settings(&settings);
> +}
> +
> +static bool window_collapsed(void *p, size_t len)
> +{
> +     return is_range_backed_by_folio_orders(p, len, collapse_order,
> +                                            pagemap_fd, kpageflags_fd);
> +}
> +
> +/* No aligned window in [p, p + len) is backed at the target order. */
> +static bool window_not_collapsed(void *p, size_t len)
> +{
> +     size_t window = mthp_window_size();
> +     char *addr = p;
> +
> +     for (; len >= window; addr += window, len -= window) {
> +             if (window_collapsed(addr, window))
> +                     return false;
> +     }
> +     return true;
> +}
> +
> +static bool khugepaged_wait_full_pass(void)
> +{
> +     /* Wait up to 30 seconds for the pass to complete. */
> +     return khugepaged_full_pass(30);
> +}
> +
> +static void collapse_order_single_window(struct collapse_context *c,
> +                                      struct mem_ops *ops)
> +{
> +     size_t window = mthp_window_size();
> +     void *p;
> +
> +     mthp_push_target_order();
> +
> +     p = ops->setup_area(1);
> +     ops->fault(p, window, 2 * window);
> +     if (!window_not_collapsed(p, hpage_pmd_size))
> +             ksft_exit_fail_msg("Unexpected large folio after fault\n");
> +
> +     madvise(p, hpage_pmd_size, MADV_HUGEPAGE);
> +     ksft_print_msg("Collapse one fully populated window...");
> +     if (!khugepaged_wait_full_pass())
> +             fail("Timeout");
> +     else if (window_collapsed(p + window, window) &&
> +              window_not_collapsed(p, window) &&
> +              window_not_collapsed(p + 2 * window,
> +                                   hpage_pmd_size - 2 * window))
> +             success("OK");
> +     else
> +             fail("Fail");
> +
> +     validate_memory(p, window, 2 * window);
> +     ops->cleanup_area(p, hpage_pmd_size);
> +     thp_pop_settings();
> +     ksft_test_result_report(exit_status, "%s\n", __func__);
> +}
> +
> +static void collapse_order_partial_window(struct collapse_context *c,
> +                                       struct mem_ops *ops)
> +{
> +     void *p;
> +
> +     mthp_push_target_order();
> +
> +     p = ops->setup_area(1);
> +     ops->fault(p, 0, page_size);
> +     if (!window_not_collapsed(p, hpage_pmd_size))
> +             ksft_exit_fail_msg("Unexpected large folio after fault\n");
> +
> +     madvise(p, hpage_pmd_size, MADV_HUGEPAGE);
> +     ksft_print_msg("Collapse window with single PTE entry present...");
> +     if (!khugepaged_wait_full_pass())
> +             fail("Timeout");
> +     else if (window_collapsed(p, mthp_window_size()))
> +             success("OK");
> +     else
> +             fail("Fail");
> +
> +     validate_memory(p, 0, page_size);
> +     ops->cleanup_area(p, hpage_pmd_size);
> +     thp_pop_settings();
> +     ksft_test_result_report(exit_status, "%s\n", __func__);
> +}
> +
> +static void collapse_order_max_ptes_none(struct collapse_context *c,
> +                                      struct mem_ops *ops)
> +{
> +     struct thp_settings settings;
> +     size_t window = mthp_window_size();
> +     void *p;
> +
> +     mthp_push_target_order();
> +     settings = *thp_current_settings();
> +     settings.khugepaged.max_ptes_none = 0;
> +     thp_push_settings(&settings);
> +
> +     p = ops->setup_area(1);
> +     ops->fault(p, 0, 2 * window - page_size);
> +     if (!window_not_collapsed(p, hpage_pmd_size))
> +             ksft_exit_fail_msg("Unexpected large folio after fault\n");
> +
> +     madvise(p, hpage_pmd_size, MADV_HUGEPAGE);
> +     ksft_print_msg("Collapse full window, not the one missing a page...");
> +     if (!khugepaged_wait_full_pass())
> +             fail("Timeout");
> +     else if (window_collapsed(p, window) &&
> +              window_not_collapsed(p + window, window))
> +             success("OK");
> +     else
> +             fail("Fail");
> +
> +     validate_memory(p, 0, 2 * window - page_size);
> +     ops->cleanup_area(p, hpage_pmd_size);
> +     thp_pop_settings();
> +     thp_pop_settings();
> +     ksft_test_result_report(exit_status, "%s\n", __func__);
> +}
> +
> +static void collapse_order_mixed_sources(struct collapse_context *c,
> +                                      struct mem_ops *ops)
> +{
> +     struct thp_settings settings;
> +     void *p;
> +
> +     if (collapse_order <= MIN_MTHP_ORDER) {
> +             ksft_test_result_skip("%s: no source order below target\n",
> +                                   __func__);
> +             return;
> +     }
> +
> +     mthp_push_target_order();
> +
> +     /* Fault the whole region as order-MIN_MTHP_ORDER folios. */
> +     settings = *thp_current_settings();
> +     settings.hugepages[MIN_MTHP_ORDER].enabled = THP_ALWAYS;
> +     thp_push_settings(&settings);
> +     p = ops->setup_area(1);
> +     ops->fault(p, 0, hpage_pmd_size);
> +     thp_pop_settings();
> +
> +     /*
> +      * The order is enabled, but the allocator can still fall back under
> +      * fragmentation.  That leaves nothing to collapse from, which is the
> +      * machine's answer rather than a reason to end the run.
> +      */
> +     if (!is_range_backed_by_folio_orders(p, hpage_pmd_size, MIN_MTHP_ORDER,
> +                                          pagemap_fd, kpageflags_fd)) {
> +             ksft_print_msg("No order-%d sources to collapse...",
> +                            MIN_MTHP_ORDER);
> +             skip("Skip");
> +             ops->cleanup_area(p, hpage_pmd_size);
> +             thp_pop_settings();
> +             ksft_test_result_report(exit_status, "%s\n", __func__);
> +             return;
> +     }
> +
> +     madvise(p, hpage_pmd_size, MADV_HUGEPAGE);
> +     ksft_print_msg("Collapse region backed by smaller large folios...");
> +     if (!khugepaged_wait_full_pass())
> +             fail("Timeout");
> +     else if (window_collapsed(p, hpage_pmd_size))
> +             success("OK");
> +     else
> +             fail("Fail");
> +
> +     validate_memory(p, 0, hpage_pmd_size);
> +     ops->cleanup_area(p, hpage_pmd_size);
> +     thp_pop_settings();
> +     ksft_test_result_report(exit_status, "%s\n", __func__);
> +}
> +
>  static void usage(void)
>  {
>       fprintf(stderr, "\nUsage: ./khugepaged [OPTIONS] <test type> 
> [dir]\n\n");
> @@ -1395,6 +1607,20 @@ int main(int argc, char **argv)
>
>       parse_test_type(argc, argv);
>
> +     if (mthp_khugepaged_context &&
> +         !(thp_supported_orders() & (1UL << collapse_order)))
> +             ksft_exit_skip("Order %d is not a supported anon THP order\n",
> +                            collapse_order);
> +
> +     if (mthp_khugepaged_context) {
> +             pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
> +             if (pagemap_fd < 0)
> +                     ksft_exit_fail_perror("open(/proc/self/pagemap)");
> +             kpageflags_fd = open("/proc/kpageflags", O_RDONLY);
> +             if (kpageflags_fd < 0)
> +                     ksft_exit_fail_perror("open(/proc/kpageflags)");
> +     }
> +
>       setbuf(stdout, NULL);
>
>       /*
> @@ -1450,6 +1676,10 @@ int main(int argc, char **argv)
>       TEST(collapse_empty, madvise_context, anon_ops);
>
>       TEST(collapse_single_mthp, mthp_khugepaged_context, anon_ops);
> +     TEST(collapse_order_single_window, mthp_khugepaged_context, anon_ops);
> +     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_single_pte_entry, khugepaged_context, anon_ops);
>       TEST(collapse_single_pte_entry, khugepaged_context, read_only_file_ops);
> --
> 2.54.0
>

--
Cheers, Lorenzo

Reply via email to