Only a huge PMD entry mapping an anonymous folio is split. Everything else is unmapped and comes back on the next fault. Put that in unmap_huge_pmd_entry(). Old and new differ only for a device private entry in a non-anonymous VMA, which nothing in the tree can produce. Signed-off-by: Yin Tirui --- mm/huge_memory.c | 84 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 90d84f761619..aefd62827139 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -3148,6 +3148,52 @@ static bool huge_zero_pmd_can_split(struct vm_area_struct *vma, pmd_t pmdval) return is_huge_zero_pmd(pmdval) && vma_is_anonymous(vma); } +/** + * unmap_huge_pmd_entry() - Unmap a huge PMD entry rather than splitting it. + * @vma: The VMA @pmd belongs to. + * @haddr: The PMD-aligned address @pmd maps. + * @pmd: Pointer to the huge PMD entry. + * @folio: The folio @pmd describes, or NULL if it describes none. + * @is_present: Is @pmd a present entry rather than a softleaf entry? + * + * Only anonymous folios are rebuilt at PTE level when a huge PMD entry is + * split. Everything else is unmapped here and faulted back in on the next + * access. + */ +static void unmap_huge_pmd_entry(struct vm_area_struct *vma, + unsigned long haddr, pmd_t *pmd, struct folio *folio, + bool is_present) +{ + struct mm_struct *mm = vma->vm_mm; + pmd_t old_pmd; + + old_pmd = pmdp_huge_clear_flush(vma, haddr, pmd); + /* + * We are going to unmap this huge page. So + * just go ahead and zap it + */ + if (arch_needs_pgtable_deposit()) + zap_deposited_table(mm, pmd); + + if (!folio) + return; + + if (is_present) { + struct page *page = pmd_page(old_pmd); + + if (!folio_test_dirty(folio) && pmd_dirty(old_pmd)) + folio_mark_dirty(folio); + if (!folio_test_referenced(folio) && pmd_young(old_pmd)) + folio_set_referenced(folio); + folio_remove_rmap_pmd(folio, page, vma); + } + + add_mm_counter(mm, mm_counter_file(folio), -HPAGE_PMD_NR); + + if (is_present) + folio_put(folio); +} + static void __split_huge_pmd_locked(struct vm_area_struct *vma, pmd_t *pmd, unsigned long haddr, bool freeze) { @@ -3187,34 +3233,16 @@ static void __split_huge_pmd_locked(struct vm_area_struct *vma, pmd_t *pmd, } if (!vma_is_anonymous(vma)) { - old_pmd = pmdp_huge_clear_flush(vma, haddr, pmd); - /* - * We are going to unmap this huge page. So - * just go ahead and zap it - */ - if (arch_needs_pgtable_deposit()) - zap_deposited_table(mm, pmd); - if (vma_is_special_huge(vma)) - return; - if (unlikely(pmd_is_migration_entry(old_pmd))) { - const softleaf_t old_entry = softleaf_from_pmd(old_pmd); - - folio = softleaf_to_folio(old_entry); - } else if (is_huge_zero_pmd(old_pmd)) { - return; - } else { - page = pmd_page(old_pmd); - folio = page_folio(page); - if (!folio_test_dirty(folio) && pmd_dirty(old_pmd)) - folio_mark_dirty(folio); - if (!folio_test_referenced(folio) && pmd_young(old_pmd)) - folio_set_referenced(folio); - folio_remove_rmap_pmd(folio, page, vma); - add_mm_counter(mm, mm_counter_file(folio), -HPAGE_PMD_NR); - folio_put(folio); - return; - } - add_mm_counter(mm, mm_counter_file(folio), -HPAGE_PMD_NR); + const bool is_present = pmd_present(old_pmd); + + if (vma_is_special_huge(vma) || is_huge_zero_pmd(old_pmd)) + folio = NULL; + else if (is_present) + folio = page_folio(pmd_page(old_pmd)); + else + folio = softleaf_to_folio(softleaf_from_pmd(old_pmd)); + + unmap_huge_pmd_entry(vma, haddr, pmd, folio, is_present); return; } -- 2.34.1