|
@@ -1684,10 +1684,24 @@ int zap_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
|
|
spin_unlock(ptl);
|
|
spin_unlock(ptl);
|
|
tlb_remove_page_size(tlb, pmd_page(orig_pmd), HPAGE_PMD_SIZE);
|
|
tlb_remove_page_size(tlb, pmd_page(orig_pmd), HPAGE_PMD_SIZE);
|
|
} else {
|
|
} else {
|
|
- struct page *page = pmd_page(orig_pmd);
|
|
|
|
- page_remove_rmap(page, true);
|
|
|
|
- VM_BUG_ON_PAGE(page_mapcount(page) < 0, page);
|
|
|
|
- VM_BUG_ON_PAGE(!PageHead(page), page);
|
|
|
|
|
|
+ struct page *page = NULL;
|
|
|
|
+ int flush_needed = 1;
|
|
|
|
+
|
|
|
|
+ if (pmd_present(orig_pmd)) {
|
|
|
|
+ page = pmd_page(orig_pmd);
|
|
|
|
+ page_remove_rmap(page, true);
|
|
|
|
+ VM_BUG_ON_PAGE(page_mapcount(page) < 0, page);
|
|
|
|
+ VM_BUG_ON_PAGE(!PageHead(page), page);
|
|
|
|
+ } else if (thp_migration_supported()) {
|
|
|
|
+ swp_entry_t entry;
|
|
|
|
+
|
|
|
|
+ VM_BUG_ON(!is_pmd_migration_entry(orig_pmd));
|
|
|
|
+ entry = pmd_to_swp_entry(orig_pmd);
|
|
|
|
+ page = pfn_to_page(swp_offset(entry));
|
|
|
|
+ flush_needed = 0;
|
|
|
|
+ } else
|
|
|
|
+ WARN_ONCE(1, "Non present huge pmd without pmd migration enabled!");
|
|
|
|
+
|
|
if (PageAnon(page)) {
|
|
if (PageAnon(page)) {
|
|
zap_deposited_table(tlb->mm, pmd);
|
|
zap_deposited_table(tlb->mm, pmd);
|
|
add_mm_counter(tlb->mm, MM_ANONPAGES, -HPAGE_PMD_NR);
|
|
add_mm_counter(tlb->mm, MM_ANONPAGES, -HPAGE_PMD_NR);
|
|
@@ -1696,8 +1710,10 @@ int zap_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
|
|
zap_deposited_table(tlb->mm, pmd);
|
|
zap_deposited_table(tlb->mm, pmd);
|
|
add_mm_counter(tlb->mm, MM_FILEPAGES, -HPAGE_PMD_NR);
|
|
add_mm_counter(tlb->mm, MM_FILEPAGES, -HPAGE_PMD_NR);
|
|
}
|
|
}
|
|
|
|
+
|
|
spin_unlock(ptl);
|
|
spin_unlock(ptl);
|
|
- tlb_remove_page_size(tlb, page, HPAGE_PMD_SIZE);
|
|
|
|
|
|
+ if (flush_needed)
|
|
|
|
+ tlb_remove_page_size(tlb, page, HPAGE_PMD_SIZE);
|
|
}
|
|
}
|
|
return 1;
|
|
return 1;
|
|
}
|
|
}
|
|
@@ -2745,3 +2761,61 @@ static int __init split_huge_pages_debugfs(void)
|
|
}
|
|
}
|
|
late_initcall(split_huge_pages_debugfs);
|
|
late_initcall(split_huge_pages_debugfs);
|
|
#endif
|
|
#endif
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_ARCH_ENABLE_THP_MIGRATION
|
|
|
|
+void set_pmd_migration_entry(struct page_vma_mapped_walk *pvmw,
|
|
|
|
+ struct page *page)
|
|
|
|
+{
|
|
|
|
+ struct vm_area_struct *vma = pvmw->vma;
|
|
|
|
+ struct mm_struct *mm = vma->vm_mm;
|
|
|
|
+ unsigned long address = pvmw->address;
|
|
|
|
+ pmd_t pmdval;
|
|
|
|
+ swp_entry_t entry;
|
|
|
|
+
|
|
|
|
+ if (!(pvmw->pmd && !pvmw->pte))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ mmu_notifier_invalidate_range_start(mm, address,
|
|
|
|
+ address + HPAGE_PMD_SIZE);
|
|
|
|
+
|
|
|
|
+ flush_cache_range(vma, address, address + HPAGE_PMD_SIZE);
|
|
|
|
+ pmdval = *pvmw->pmd;
|
|
|
|
+ pmdp_invalidate(vma, address, pvmw->pmd);
|
|
|
|
+ if (pmd_dirty(pmdval))
|
|
|
|
+ set_page_dirty(page);
|
|
|
|
+ entry = make_migration_entry(page, pmd_write(pmdval));
|
|
|
|
+ pmdval = swp_entry_to_pmd(entry);
|
|
|
|
+ set_pmd_at(mm, address, pvmw->pmd, pmdval);
|
|
|
|
+ page_remove_rmap(page, true);
|
|
|
|
+ put_page(page);
|
|
|
|
+
|
|
|
|
+ mmu_notifier_invalidate_range_end(mm, address,
|
|
|
|
+ address + HPAGE_PMD_SIZE);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void remove_migration_pmd(struct page_vma_mapped_walk *pvmw, struct page *new)
|
|
|
|
+{
|
|
|
|
+ struct vm_area_struct *vma = pvmw->vma;
|
|
|
|
+ struct mm_struct *mm = vma->vm_mm;
|
|
|
|
+ unsigned long address = pvmw->address;
|
|
|
|
+ unsigned long mmun_start = address & HPAGE_PMD_MASK;
|
|
|
|
+ pmd_t pmde;
|
|
|
|
+ swp_entry_t entry;
|
|
|
|
+
|
|
|
|
+ if (!(pvmw->pmd && !pvmw->pte))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ entry = pmd_to_swp_entry(*pvmw->pmd);
|
|
|
|
+ get_page(new);
|
|
|
|
+ pmde = pmd_mkold(mk_huge_pmd(new, vma->vm_page_prot));
|
|
|
|
+ if (is_write_migration_entry(entry))
|
|
|
|
+ pmde = maybe_pmd_mkwrite(pmde, vma);
|
|
|
|
+
|
|
|
|
+ flush_cache_range(vma, mmun_start, mmun_start + HPAGE_PMD_SIZE);
|
|
|
|
+ page_add_anon_rmap(new, vma, mmun_start, true);
|
|
|
|
+ set_pmd_at(mm, mmun_start, pvmw->pmd, pmde);
|
|
|
|
+ if (vma->vm_flags & VM_LOCKED)
|
|
|
|
+ mlock_vma_page(new);
|
|
|
|
+ update_mmu_cache_pmd(vma, address, pvmw->pmd);
|
|
|
|
+}
|
|
|
|
+#endif
|