thp_underused() decides whether a large folio on the deferred split list is underused and should be split so that its zero-filled subpages can be reclaimed. Today it only ever runs on PMD-sized folios, so it assumes that the folio has HPAGE_PMD_NR pages and compares the zero-filled page count directly against khugepaged_max_ptes_none. A later patch will queue anonymous mTHP folios onto the deferred split list as well, at which point that assumption will no longer hold. Make thp_underused() folio-size-aware, and scale khugepaged_max_ptes_none proportionally to the number of pages in the folio (eg every folio tolerates the same fraction of zero-filled pages the user configured for a PMD-sized folio). khugepaged deliberately doesn't scale intermediate values when collapsing to mTHP orders (to avoid the memory footprint creeping up through repeated collapses), but if/when collapse later gains support for intermediate values, a folio that khugepaged collapses won't then be split right back by the shrinker (and vice versa) since both collapse and split would use the same threshold for a given folio size. There is no functional change for PMD-sized folios, as for those, nr_pages is HPAGE_PMD_NR. Suggested-by: Usama Arif Signed-off-by: Joanne Koong --- mm/huge_memory.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 14599d0fff01..3720c12bdb0d 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -4625,25 +4625,33 @@ static unsigned long deferred_split_count(struct shrinker *shrink, static bool thp_underused(struct folio *folio) { - int num_zero_pages = 0, num_filled_pages = 0; - int i; + const unsigned long nr_pages = folio_nr_pages(folio); + unsigned long num_zero_pages = 0, num_filled_pages = 0; + unsigned long max_ptes_none = khugepaged_max_ptes_none; + unsigned long i; - if (khugepaged_max_ptes_none == HPAGE_PMD_NR - 1) + if (max_ptes_none == HPAGE_PMD_NR - 1) return false; if (folio_contain_hwpoisoned_page(folio)) return false; - for (i = 0; i < folio_nr_pages(folio); i++) { + /* + * khugepaged_max_ptes_none is a number of pages out of HPAGE_PMD_NR. + * Scale it to this folio's size. + */ + max_ptes_none = max_ptes_none * nr_pages / HPAGE_PMD_NR; + + for (i = 0; i < nr_pages; i++) { if (pages_identical(folio_page(folio, i), ZERO_PAGE(0))) { - if (++num_zero_pages > khugepaged_max_ptes_none) + if (++num_zero_pages > max_ptes_none) return true; } else { /* * Another path for early exit once the number * of non-zero filled pages exceeds threshold. */ - if (++num_filled_pages >= HPAGE_PMD_NR - khugepaged_max_ptes_none) + if (++num_filled_pages >= nr_pages - max_ptes_none) return false; } } -- 2.52.0