Unlike for PMD-sized folios, an anonymous mTHP folio doesn't get added to the deferred split list at fault or collapse time. As a result, a fully mapped mTHP folio that is mostly zero-filled doesn't get split by the deferred split shrinker when the system is under memory pressure. At Meta we would like to deploy 2M THP=always on arm64 with 64k base pages, as 2M gives the contpte benefits while the PMD size (512M) is too big to use. Without underused splitting this causes memory regressions, as the unused parts of those folios can never be broken down and reclaimed. Add anonymous mTHP folios to the deferred split list from map_anon_folio_pte_nopf(), mirroring what map_anon_folio_pmd_nopf() already does for PMD-sized folios. This covers both the fault path and the khugepaged mTHP collapse path. If there is memory pressure, a zero-filled mTHP can then be split with its zero pages remapped to the shared zero page and reclaimed. The preceding patch bounds what folios can get added to the deferred split list. Nothing gets added at the default khugepaged/max_ptes_none, and in cases where it is lowered, only folios with more pages than it are added, so orders that could never be underused are left alone and systems that enable only small mTHP orders are unaffected. For underused splitting to happen, khugepaged/max_ptes_none has to be set below the folio's page count. To minimize overhead on the common order-0 fault path, the deferred_split_folio() call is guarded by an inline folio_test_large() check. Suggested-by: Usama Arif Signed-off-by: Joanne Koong --- mm/memory.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/memory.c b/mm/memory.c index 8b0c2c735d3d..1fe76f72868d 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -5406,6 +5406,8 @@ void map_anon_folio_pte_nopf(struct folio *folio, pte_t *pte, folio_add_lru_vma(folio, vma); set_ptes(vma->vm_mm, addr, pte, entry, nr_pages); update_mmu_cache_range(NULL, vma, addr, pte, nr_pages); + if (folio_test_large(folio)) + deferred_split_folio(folio, false); } static void map_anon_folio_pte_pf(struct folio *folio, pte_t *pte, -- 2.52.0