[PATCH 13/16] selftests/mm: parameterize the mixed-source collapse case by source order

Kiryl Shutsemau <[email protected]> Sun, 2 Aug 2026 20:52:45 +0100
Newsgroups org.kernel.vger.linux-kselftest,org.kernel.vger.linux-kernel,org.kvack.linux-mm
Message-ID <[email protected]>
From: "Kiryl Shutsemau (Meta)" <[email protected]>

collapse_order_mixed_sources faults its region as order-2 folios and
collapses them to the -o target, covering collapse of sources that are
already large folios of an order below the target. But it only ever tests
one source order, and order 2 sits below the contpte threshold on both
arm64 page-size configs -- so a source-side contpte unfold is never
exercised deterministically by this suite.

Let -s name the source order when combined with -o (it was rejected
before): the mixed-source case then faults at order @anon_order instead of
the fixed order 2, keeping order 2 as the default when -s is absent. -s
stays constrained to a supported mTHP order strictly below the target; the
other order-parameterized cases keep their order-0 sources (the source
order is enabled locally, not globally), so -s under -o is a knob on the
mixed-source case alone.

This makes e.g. -s 5 -o 7 on arm64/64K collapse contpte-mapped sources
into a larger mTHP, covering the source-unfold path directly.

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

diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c
index b074b005b62f..21a8fb24dc43 100644
--- a/tools/testing/selftests/mm/khugepaged.c
+++ b/tools/testing/selftests/mm/khugepaged.c
@@ -1363,28 +1363,32 @@ static void collapse_order_mixed_sources(struct collapse_context *c,
 					 struct mem_ops *ops)
 {
 	struct thp_settings settings = *thp_current_settings();
+	int source_order = anon_order ? anon_order : MIN_MTHP_ORDER;
 	void *p;
 
-	if (anon_target_order <= MIN_MTHP_ORDER) {
+	/* Sources must be a supported mTHP order strictly below the target. */
+	if (source_order >= anon_target_order ||
+	    !(thp_supported_orders() & (1UL << source_order))) {
 		ksft_test_result_skip("%s: no source order below target\n",
 				      __func__);
 		return;
 	}
 
-	/* Fault the whole region as order-MIN_MTHP_ORDER folios. */
-	settings.hugepages[MIN_MTHP_ORDER].enabled = THP_ALWAYS;
+	/* Fault the whole region as order-@source_order folios. */
+	settings.hugepages[source_order].enabled = THP_ALWAYS;
 	thp_push_settings(&settings);
 	p = ops->setup_area(1);
 	ops->fault(p, 0, hpage_pmd_size);
 	thp_pop_settings();
 
-	if (!is_range_backed_by_folio_orders(p, hpage_pmd_size, MIN_MTHP_ORDER,
+	if (!is_range_backed_by_folio_orders(p, hpage_pmd_size, source_order,
 					     pagemap_fd, kpageflags_fd))
 		ksft_exit_fail_msg("Region not backed by order-%d folios after fault\n",
-				   MIN_MTHP_ORDER);
+				   source_order);
 
 	madvise(p, hpage_pmd_size, MADV_HUGEPAGE);
-	ksft_print_msg("Collapse region backed by smaller large folios...");
+	ksft_print_msg("Collapse region backed by order-%d sources...",
+		       source_order);
 	if (!khugepaged_wait_full_pass())
 		fail("Timeout");
 	else if (range_collapsed(p, hpage_pmd_size))
@@ -1414,7 +1418,9 @@ static void usage(void)
 	fprintf(stderr,	"\t\t    Defaults to 0. Use this size for anon or shmem allocations.\n");
 	fprintf(stderr,	"\t\t-o: collapse target order for khugepaged:anon.\n");
 	fprintf(stderr,	"\t\t    Runs the order-parameterized collapse cases instead\n");
-	fprintf(stderr,	"\t\t    of the PMD cases. Cannot be combined with -s.\n");
+	fprintf(stderr,	"\t\t    of the PMD cases.\n");
+	fprintf(stderr,	"\t\t    With -s, -s names the mTHP source order for the\n");
+	fprintf(stderr,	"\t\t    mixed-source case (source order below the target).\n");
 	exit(1);
 }
 
@@ -1438,7 +1444,15 @@ static void parse_test_type(int argc, char **argv)
 		}
 	}
 
-	if (anon_target_order && anon_order)
+	/*
+	 * -s and -o compose: -s then names the mTHP source order for the
+	 * mixed-source case, which needs a source strictly below the
+	 * target (and at or above the smallest mTHP order). Alone, -s is
+	 * the source order for the default PMD suite; alone, -o is the
+	 * collapse target for the order-parameterized suite.
+	 */
+	if (anon_target_order && anon_order &&
+	    (anon_order < MIN_MTHP_ORDER || anon_order >= anon_target_order))
 		usage();
 
 	argv += optind;
@@ -1573,9 +1587,17 @@ int main(int argc, char **argv)
 	default_settings.khugepaged.max_ptes_shared = hpage_pmd_nr / 2;
 	default_settings.khugepaged.pages_to_scan = hpage_pmd_nr * 8;
 	default_settings.hugepages[hpage_pmd_order].enabled = THP_INHERIT;
-	default_settings.hugepages[anon_order].enabled = THP_ALWAYS;
 	default_settings.shmem_hugepages[hpage_pmd_order].enabled = SHMEM_INHERIT;
-	default_settings.shmem_hugepages[anon_order].enabled = SHMEM_ALWAYS;
+	/*
+	 * Under -o the order-parameterized cases want order-0 sources by
+	 * default; the mixed-source case enables its own (possibly -s
+	 * selected) source order locally. Enabling it globally here would
+	 * make every case fault that order.
+	 */
+	if (!anon_target_order) {
+		default_settings.hugepages[anon_order].enabled = THP_ALWAYS;
+		default_settings.shmem_hugepages[anon_order].enabled = SHMEM_ALWAYS;
+	}
 
 	if (anon_target_order) {
 		/*
-- 
2.54.0