From: "Kiryl Shutsemau (Meta)" <[email protected]> The page cache caps folio order at MAX_PAGECACHE_ORDER, which sits below the PMD order where a PMD is 512M -- arm64 with 64K base pages. A PMD-sized page cache folio is then impossible, so MADV_COLLAPSE answers -EINVAL and khugepaged passes over the range. The shmem cases ask for one anyway, so four of them fail and the run bails out in the middle.
The cap is one global, so it rules out every file mapping, not just shmem: shmem_huge_global_enabled() drops the PMD order from what it allows, and file_thp_enabled() refuses a regular file whose mapping cannot hold a PMD folio. Skip both mem types when the PMD order has no per-order shmem_enabled control, which is the readable form of the cap: that control is created for the orders in THP_ORDERS_ALL_FILE_DEFAULT. A run left with nothing to collapse into skips outright. Anonymous collapse is unaffected: its orders are not capped this way. Assisted-by: Claude-Code:claude-opus-5 Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]> --- tools/testing/selftests/mm/khugepaged.c | 31 +++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c index 0f828bfee31f..9b1da0dd0f8e 100644 --- a/tools/testing/selftests/mm/khugepaged.c +++ b/tools/testing/selftests/mm/khugepaged.c @@ -1424,6 +1424,37 @@ int main(int argc, char **argv) setbuf(stdout, NULL); + /* + * The page cache caps folio order at MAX_PAGECACHE_ORDER, which + * xas_split_alloc() puts below the PMD order on arm64 with 64K pages. + * A PMD-sized page cache folio is then impossible, so the kernel + * refuses these collapses by design and there is nothing to test. + * + * The cap is one global, so it rules out every file mapping, not just + * shmem: shmem_huge_global_enabled() drops the PMD order from what it + * allows, and file_thp_enabled() refuses a regular file whose mapping + * cannot hold a PMD folio. + * + * The per-order shmem_enabled control below is what makes the cap + * readable: it is created for the orders in THP_ORDERS_ALL_FILE_DEFAULT, + * which is the cap and nothing else, so whether the PMD order has one + * answers for a regular file as much as for shmem. + */ + if (!(thp_shmem_supported_orders() & (1UL << hpage_pmd_order))) { + if (shmem_ops) { + ksft_print_msg("no PMD-order page cache folio: skipping shmem\n"); + shmem_ops = NULL; + } + if (read_only_file_ops) { + ksft_print_msg("no PMD-order page cache folio: skipping file\n"); + read_only_file_ops = NULL; + read_write_file_read_ops = NULL; + read_write_file_write_ops = NULL; + } + if (!anon_ops && !shmem_ops && !read_only_file_ops) + ksft_exit_skip("Nothing left to collapse into\n"); + } + default_settings.khugepaged.max_ptes_none = hpage_pmd_nr - 1; default_settings.khugepaged.max_ptes_swap = hpage_pmd_nr / 8; default_settings.khugepaged.max_ptes_shared = hpage_pmd_nr / 2; -- 2.54.0

