From: "Kiryl Shutsemau (Meta)" wait_for_scan() gives khugepaged a fixed three seconds, whatever a huge page costs to build. collapse_full() asks for four of them, which is 8M at a 2M PMD and 2G at a 512M PMD -- arm64 with 64K base pages -- and three seconds does not cover copying 2G. The escape hatch does not help either: it wants two full khugepaged passes inside the same three seconds, and one pass over a 512M PMD takes about that long by itself. So collapse_full fails there on a collapse that works. Measured with a probe that faults 4 x 512M, marks it MADV_HUGEPAGE and polls: all four PMDs collapse, with collapse_alloc=4 at the PMD size and no allocation failures. Raising only this budget makes the test pass, and it does not turn into a "Fail" -- which is what khugepaged completing two passes without collapsing would produce. Scale the budget with the memory to be collapsed: keep three seconds as the floor and add a second per 64M. That leaves a 2M PMD at exactly the three seconds it has now, and gives 35s at a 512M PMD, where the collapse measures under 3s. Keying it on nr_hpages * hpage_pmd_size rather than the PMD size alone matters because the callers ask for one or four; scaling linearly on PMD size alone would ask for 768s, which is not a budget so much as a hang. This also brings the helper in line with khugepaged_full_pass(), which already allows 30s and is why the order-parameterized cases pass at a 512M PMD while this one did not. arm64/64K: khugepaged all:anon 21 pass/1 fail -> 22 pass/0 fail. x86-64 unchanged, 129 ok. Assisted-by: Claude-Code:claude-opus-5 Signed-off-by: Kiryl Shutsemau (Meta) --- tools/testing/selftests/mm/khugepaged.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c index 21a8fb24dc43..9213ce1658d0 100644 --- a/tools/testing/selftests/mm/khugepaged.c +++ b/tools/testing/selftests/mm/khugepaged.c @@ -573,8 +573,17 @@ static void madvise_collapse(const char *msg, char *p, int nr_hpages, static bool wait_for_scan(const char *msg, char *p, int nr_hpages, struct mem_ops *ops) { + /* + * The budget has to cover khugepaged copying nr_hpages * + * hpage_pmd_size, plus two of its passes over the mm. Three seconds + * does that at a 2M PMD, but the same test moves 2G at a 512M PMD + * (arm64 with 64K base pages) and 3s is then marginal: it fails on a + * collapse that completes correctly, just not inside the budget. + * Allow a further second per 64M to collapse. + */ + const unsigned long bytes = (unsigned long)nr_hpages * hpage_pmd_size; + int timeout = 6 + 2 * (bytes / (64UL << 20)); int full_scans; - int timeout = 6; /* 3 seconds */ /* Sanity check */ if (!ops->check_huge(p, 0)) -- 2.54.0