From: "Kiryl Shutsemau (Meta)" 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) --- 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