diff options
Diffstat (limited to 'mm/migrate.c')
-rw-r--r-- | mm/migrate.c | 108 |
1 files changed, 44 insertions, 64 deletions
diff --git a/mm/migrate.c b/mm/migrate.c index 87f4d0f81819..2c63ac06791b 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -74,7 +74,7 @@ int migrate_prep_local(void) return 0; } -bool isolate_movable_page(struct page *page, isolate_mode_t mode) +int isolate_movable_page(struct page *page, isolate_mode_t mode) { struct address_space *mapping; @@ -125,14 +125,14 @@ bool isolate_movable_page(struct page *page, isolate_mode_t mode) __SetPageIsolated(page); unlock_page(page); - return true; + return 0; out_no_isolated: unlock_page(page); out_putpage: put_page(page); out: - return false; + return -EBUSY; } /* It should be called on page which is PG_movable */ @@ -193,82 +193,62 @@ void putback_movable_pages(struct list_head *l) /* * Restore a potential migration pte to a working pte entry */ -static int remove_migration_pte(struct page *new, struct vm_area_struct *vma, +static int remove_migration_pte(struct page *page, struct vm_area_struct *vma, unsigned long addr, void *old) { - struct mm_struct *mm = vma->vm_mm; + struct page_vma_mapped_walk pvmw = { + .page = old, + .vma = vma, + .address = addr, + .flags = PVMW_SYNC | PVMW_MIGRATION, + }; + struct page *new; + pte_t pte; swp_entry_t entry; - pmd_t *pmd; - pte_t *ptep, pte; - spinlock_t *ptl; - if (unlikely(PageHuge(new))) { - ptep = huge_pte_offset(mm, addr); - if (!ptep) - goto out; - ptl = huge_pte_lockptr(hstate_vma(vma), mm, ptep); - } else { - pmd = mm_find_pmd(mm, addr); - if (!pmd) - goto out; + VM_BUG_ON_PAGE(PageTail(page), page); + while (page_vma_mapped_walk(&pvmw)) { + new = page - pvmw.page->index + + linear_page_index(vma, pvmw.address); - ptep = pte_offset_map(pmd, addr); + get_page(new); + pte = pte_mkold(mk_pte(new, READ_ONCE(vma->vm_page_prot))); + if (pte_swp_soft_dirty(*pvmw.pte)) + pte = pte_mksoft_dirty(pte); /* - * Peek to check is_swap_pte() before taking ptlock? No, we - * can race mremap's move_ptes(), which skips anon_vma lock. + * Recheck VMA as permissions can change since migration started */ - - ptl = pte_lockptr(mm, pmd); - } - - spin_lock(ptl); - pte = *ptep; - if (!is_swap_pte(pte)) - goto unlock; - - entry = pte_to_swp_entry(pte); - - if (!is_migration_entry(entry) || - migration_entry_to_page(entry) != old) - goto unlock; - - get_page(new); - pte = pte_mkold(mk_pte(new, READ_ONCE(vma->vm_page_prot))); - if (pte_swp_soft_dirty(*ptep)) - pte = pte_mksoft_dirty(pte); - - /* Recheck VMA as permissions can change since migration started */ - if (is_write_migration_entry(entry)) - pte = maybe_mkwrite(pte, vma); + entry = pte_to_swp_entry(*pvmw.pte); + if (is_write_migration_entry(entry)) + pte = maybe_mkwrite(pte, vma); #ifdef CONFIG_HUGETLB_PAGE - if (PageHuge(new)) { - pte = pte_mkhuge(pte); - pte = arch_make_huge_pte(pte, vma, new, 0); - } + if (PageHuge(new)) { + pte = pte_mkhuge(pte); + pte = arch_make_huge_pte(pte, vma, new, 0); + } #endif - flush_dcache_page(new); - set_pte_at(mm, addr, ptep, pte); + flush_dcache_page(new); + set_pte_at(vma->vm_mm, pvmw.address, pvmw.pte, pte); - if (PageHuge(new)) { - if (PageAnon(new)) - hugepage_add_anon_rmap(new, vma, addr); + if (PageHuge(new)) { + if (PageAnon(new)) + hugepage_add_anon_rmap(new, vma, pvmw.address); + else + page_dup_rmap(new, true); + } else if (PageAnon(new)) + page_add_anon_rmap(new, vma, pvmw.address, false); else - page_dup_rmap(new, true); - } else if (PageAnon(new)) - page_add_anon_rmap(new, vma, addr, false); - else - page_add_file_rmap(new, false); + page_add_file_rmap(new, false); - if (vma->vm_flags & VM_LOCKED && !PageTransCompound(new)) - mlock_vma_page(new); + if (vma->vm_flags & VM_LOCKED && !PageTransCompound(new)) + mlock_vma_page(new); + + /* No need to invalidate - it was non-present before */ + update_mmu_cache(vma, pvmw.address, pvmw.pte); + } - /* No need to invalidate - it was non-present before */ - update_mmu_cache(vma, addr, ptep); -unlock: - pte_unmap_unlock(ptep, ptl); -out: return SWAP_AGAIN; } |