[PATCH 11/16] selftests/mm: race-harness variant for permissive hole occupancy

From: Kiryl Shutsemau

Date: Sun Aug 02 2026 - 16:01:02 EST


From: "Kiryl Shutsemau (Meta)" <kas@xxxxxxxxxx>

The race harness runs with max_ptes_none=0: strict occupancy keeps
selection honest under racing MADV_DONTNEED and avoids doomed PMD-sized
allocations on 512M-PMD configs. That regime never exercises collapse of
partially populated windows -- every candidate it emits is fully occupied.

Collapsing a window that contains holes is a different path: the hole is
not copied from anywhere, it is zero-filled into the new folio, and the
slot has to be re-checked under the page table lock at install time
because a racing fault may have filled it in the meantime. None of that is
reached at max_ptes_none=0.

Add -z, which selects the other supported end of the occupancy scale
(HPAGE_PMD_NR - 1, scaled per order): selection then emits hole-heavy
windows and those paths take the brunt of the racing faults and zaps.

Also drop the stale claim that max_ptes_none sits "mid-range" from the
header comment; the harness has always pinned it to an end of the scale.

Assisted-by: Claude-Code:claude-opus-5
Signed-off-by: Kiryl Shutsemau (Meta) <kas@xxxxxxxxxx>
---
tools/testing/selftests/mm/khugepaged_race.c | 33 ++++++++++++++------
tools/testing/selftests/mm/run_vmtests.sh | 2 ++
2 files changed, 25 insertions(+), 10 deletions(-)

diff --git a/tools/testing/selftests/mm/khugepaged_race.c b/tools/testing/selftests/mm/khugepaged_race.c
index b586a114e4cd..2e36e242caa7 100644
--- a/tools/testing/selftests/mm/khugepaged_race.c
+++ b/tools/testing/selftests/mm/khugepaged_race.c
@@ -15,8 +15,12 @@
* madvise MADV_COLLAPSE in a loop — the legacy-PMD regression
* axis.
*
- * All anon THP orders are enabled (inherit) and max_ptes_none is set
- * mid-range, so the MADV_DONTNEED holes steer selection across orders.
+ * All anon THP orders are enabled (inherit). max_ptes_none is 0 by
+ * default — racing MADV_DONTNEED then steers selection across orders —
+ * or the permissive limit with -z, which floods the batch engine with
+ * hole and zeropage slots so the population paths (park-time zeropage
+ * clear, zero-filled copy, install-time pte_none() verify and abort)
+ * race the faulters directly.
*
* Correctness signals: every racing page must read as its pattern or
* zero (MADV_DONTNEED), never anything else — checked continuously by
@@ -196,7 +200,8 @@ static unsigned long now_ms(void)
static void usage(void)
{
fprintf(stderr,
- "Usage: khugepaged_race [-d seconds] [-m stepped|free|madvise] [-a areas]\n"
+ "Usage: khugepaged_race [-d seconds] [-m stepped|free|madvise] [-z] [-a areas]\n"
+ "\t-z: permissive max_ptes_none (hole-heavy windows)\n"
"\t-a: number of shared PMD-sized playground areas (default 3)\n");
exit(1);
}
@@ -219,11 +224,12 @@ int main(int argc, char **argv)
int duration_s = 10;
unsigned long thread_mask = ~0UL;
int nr_areas_arg = 0;
+ bool permissive_none = false;
unsigned long i;
int steps = 0;
int opt;

- while ((opt = getopt(argc, argv, "a:d:m:t:h")) != -1) {
+ while ((opt = getopt(argc, argv, "a:d:m:t:zh")) != -1) {
switch (opt) {
case 'a':
nr_areas_arg = atoi(optarg);
@@ -238,6 +244,9 @@ int main(int argc, char **argv)
/* debug: bitmask of racing threads to start */
thread_mask = strtoul(optarg, NULL, 0);
break;
+ case 'z':
+ permissive_none = true;
+ break;
default:
usage();
}
@@ -274,13 +283,17 @@ int main(int argc, char **argv)
strcmp(mode, "free") ? 1000 : 0;
settings.khugepaged.alloc_sleep_millisecs = 10;
/*
- * Strict occupancy: mTHP collapse only supports 0 or
- * HPAGE_PMD_NR - 1 and coerces anything else to 0 anyway, and 0
- * also keeps khugepaged from burning the whole step in doomed
- * PMD-sized allocations on 512M-PMD configs: under racing
- * MADV_DONTNEED a fully populated PMD area is rare.
+ * mTHP collapse only supports the two ends of the occupancy
+ * scale: 0 or HPAGE_PMD_NR - 1 (anything else coerces to 0).
+ * Strict is the default — it also keeps khugepaged from burning
+ * the whole step in doomed PMD-sized allocations on 512M-PMD
+ * configs, where a fully populated area is rare under racing
+ * MADV_DONTNEED. -z selects the permissive end: selection then
+ * emits hole-heavy windows and the engine's population paths
+ * take the brunt of the racing faults and zaps.
*/
- settings.khugepaged.max_ptes_none = 0;
+ settings.khugepaged.max_ptes_none = permissive_none ?
+ (hpage_pmd_size / page_size) - 1 : 0;
settings.khugepaged.pages_to_scan =
nr_areas * (hpage_pmd_size / page_size) * 8;
for (i = 0; i < NR_ORDERS; i++) {
diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index f61ec76d8e00..83a04b1e2520 100755
--- a/tools/testing/selftests/mm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
@@ -413,6 +413,8 @@ CATEGORY="thp" run_test ./khugepaged_race -d 5 -m free

CATEGORY="thp" run_test ./khugepaged_race -d 5 -m madvise

+CATEGORY="thp" run_test ./khugepaged_race -d 5 -m stepped -z
+
CATEGORY="thp" run_test ./khugepaged

CATEGORY="thp" run_test ./khugepaged -s 2
--
2.54.0