check_pmd_state() would report a PMD swap entry as SCAN_NO_PTE_TABLE - it is not present and, on x86, is also pmd_bad(). The range is already a THP; it just happens to be on disk. Return SCAN_PMD_MAPPED next to the existing migration-entry case and before the pmd_present() and pmd_bad() tests. This does change MADV_COLLAPSE. Today a swapped-out THP is HPAGE_PMD_NR PTE swap entries and MADV_COLLAPSE, which ignores khugepaged_max_ptes_swap, swaps them all in. With a PMD swap entry it reports SCAN_PMD_MAPPED, which madvise_collapse() counts as success, so the call returns 0 without making the range resident. It still faults back in as a whole THP on first touch; only the point at which the I/O happens moves. Signed-off-by: Usama Arif --- mm/khugepaged.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 0d8cd44b14c57..bbbf96c616295 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -1052,6 +1052,12 @@ static inline enum scan_result check_pmd_state(pmd_t *pmd) */ if (pmd_is_migration_entry(pmde)) return SCAN_PMD_MAPPED; + /* + * A PMD-mapped THP that has been swapped out is still a THP from + * khugepaged's perspective; treat it like a present huge PMD. + */ + if (pmd_is_swap_entry(pmde)) + return SCAN_PMD_MAPPED; if (!pmd_present(pmde)) return SCAN_NO_PTE_TABLE; if (pmd_trans_huge(pmde)) -- 2.53.0-Meta