Merge branch 'kconfig' of git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuild
[cascardo/linux.git] / mm / rmap.c
index f5b5c1f..b577fbb 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -1304,6 +1304,10 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
        int ret = SWAP_AGAIN;
        enum ttu_flags flags = (enum ttu_flags)arg;
 
+       /* munlock has nothing to gain from examining un-locked vmas */
+       if ((flags & TTU_MUNLOCK) && !(vma->vm_flags & VM_LOCKED))
+               goto out;
+
        pte = page_check_address(page, mm, address, &ptl, 0);
        if (!pte)
                goto out;
@@ -1314,9 +1318,12 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
         * skipped over this mm) then we should reactivate it.
         */
        if (!(flags & TTU_IGNORE_MLOCK)) {
-               if (vma->vm_flags & VM_LOCKED)
-                       goto out_mlock;
-
+               if (vma->vm_flags & VM_LOCKED) {
+                       /* Holding pte lock, we do *not* need mmap_sem here */
+                       mlock_vma_page(page);
+                       ret = SWAP_MLOCK;
+                       goto out_unmap;
+               }
                if (flags & TTU_MUNLOCK)
                        goto out_unmap;
        }
@@ -1352,7 +1359,9 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
        update_hiwater_rss(mm);
 
        if (PageHWPoison(page) && !(flags & TTU_IGNORE_HWPOISON)) {
-               if (!PageHuge(page)) {
+               if (PageHuge(page)) {
+                       hugetlb_count_sub(1 << compound_order(page), mm);
+               } else {
                        if (PageAnon(page))
                                dec_mm_counter(mm, MM_ANONPAGES);
                        else
@@ -1370,47 +1379,44 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
                        dec_mm_counter(mm, MM_ANONPAGES);
                else
                        dec_mm_counter(mm, MM_FILEPAGES);
+       } else if (IS_ENABLED(CONFIG_MIGRATION) && (flags & TTU_MIGRATION)) {
+               swp_entry_t entry;
+               pte_t swp_pte;
+               /*
+                * Store the pfn of the page in a special migration
+                * pte. do_swap_page() will wait until the migration
+                * pte is removed and then restart fault handling.
+                */
+               entry = make_migration_entry(page, pte_write(pteval));
+               swp_pte = swp_entry_to_pte(entry);
+               if (pte_soft_dirty(pteval))
+                       swp_pte = pte_swp_mksoft_dirty(swp_pte);
+               set_pte_at(mm, address, pte, swp_pte);
        } else if (PageAnon(page)) {
                swp_entry_t entry = { .val = page_private(page) };
                pte_t swp_pte;
-
-               if (PageSwapCache(page)) {
-                       /*
-                        * Store the swap location in the pte.
-                        * See handle_pte_fault() ...
-                        */
-                       if (swap_duplicate(entry) < 0) {
-                               set_pte_at(mm, address, pte, pteval);
-                               ret = SWAP_FAIL;
-                               goto out_unmap;
-                       }
-                       if (list_empty(&mm->mmlist)) {
-                               spin_lock(&mmlist_lock);
-                               if (list_empty(&mm->mmlist))
-                                       list_add(&mm->mmlist, &init_mm.mmlist);
-                               spin_unlock(&mmlist_lock);
-                       }
-                       dec_mm_counter(mm, MM_ANONPAGES);
-                       inc_mm_counter(mm, MM_SWAPENTS);
-               } else if (IS_ENABLED(CONFIG_MIGRATION)) {
-                       /*
-                        * Store the pfn of the page in a special migration
-                        * pte. do_swap_page() will wait until the migration
-                        * pte is removed and then restart fault handling.
-                        */
-                       BUG_ON(!(flags & TTU_MIGRATION));
-                       entry = make_migration_entry(page, pte_write(pteval));
+               /*
+                * Store the swap location in the pte.
+                * See handle_pte_fault() ...
+                */
+               VM_BUG_ON_PAGE(!PageSwapCache(page), page);
+               if (swap_duplicate(entry) < 0) {
+                       set_pte_at(mm, address, pte, pteval);
+                       ret = SWAP_FAIL;
+                       goto out_unmap;
+               }
+               if (list_empty(&mm->mmlist)) {
+                       spin_lock(&mmlist_lock);
+                       if (list_empty(&mm->mmlist))
+                               list_add(&mm->mmlist, &init_mm.mmlist);
+                       spin_unlock(&mmlist_lock);
                }
+               dec_mm_counter(mm, MM_ANONPAGES);
+               inc_mm_counter(mm, MM_SWAPENTS);
                swp_pte = swp_entry_to_pte(entry);
                if (pte_soft_dirty(pteval))
                        swp_pte = pte_swp_mksoft_dirty(swp_pte);
                set_pte_at(mm, address, pte, swp_pte);
-       } else if (IS_ENABLED(CONFIG_MIGRATION) &&
-                  (flags & TTU_MIGRATION)) {
-               /* Establish migration entry for a file page */
-               swp_entry_t entry;
-               entry = make_migration_entry(page, pte_write(pteval));
-               set_pte_at(mm, address, pte, swp_entry_to_pte(entry));
        } else
                dec_mm_counter(mm, MM_FILEPAGES);
 
@@ -1419,31 +1425,10 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
 
 out_unmap:
        pte_unmap_unlock(pte, ptl);
-       if (ret != SWAP_FAIL && !(flags & TTU_MUNLOCK))
+       if (ret != SWAP_FAIL && ret != SWAP_MLOCK && !(flags & TTU_MUNLOCK))
                mmu_notifier_invalidate_page(mm, address);
 out:
        return ret;
-
-out_mlock:
-       pte_unmap_unlock(pte, ptl);
-
-
-       /*
-        * We need mmap_sem locking, Otherwise VM_LOCKED check makes
-        * unstable result and race. Plus, We can't wait here because
-        * we now hold anon_vma->rwsem or mapping->i_mmap_rwsem.
-        * if trylock failed, the page remain in evictable lru and later
-        * vmscan could retry to move the page to unevictable lru if the
-        * page is actually mlocked.
-        */
-       if (down_read_trylock(&vma->vm_mm->mmap_sem)) {
-               if (vma->vm_flags & VM_LOCKED) {
-                       mlock_vma_page(page);
-                       ret = SWAP_MLOCK;
-               }
-               up_read(&vma->vm_mm->mmap_sem);
-       }
-       return ret;
 }
 
 bool is_vma_temporary_stack(struct vm_area_struct *vma)
@@ -1607,6 +1592,8 @@ static int rmap_walk_anon(struct page *page, struct rmap_walk_control *rwc)
                struct vm_area_struct *vma = avc->vma;
                unsigned long address = vma_address(page, vma);
 
+               cond_resched();
+
                if (rwc->invalid_vma && rwc->invalid_vma(vma, rwc->arg))
                        continue;
 
@@ -1656,6 +1643,8 @@ static int rmap_walk_file(struct page *page, struct rmap_walk_control *rwc)
        vma_interval_tree_foreach(vma, &mapping->i_mmap, pgoff, pgoff) {
                unsigned long address = vma_address(page, vma);
 
+               cond_resched();
+
                if (rwc->invalid_vma && rwc->invalid_vma(vma, rwc->arg))
                        continue;