mm/munlock: page migration needs mlock pagevec drained
authorHugh Dickins <hughd@google.com>
Tue, 15 Feb 2022 02:38:47 +0000 (18:38 -0800)
committerMatthew Wilcox (Oracle) <willy@infradead.org>
Thu, 17 Feb 2022 16:59:40 +0000 (11:59 -0500)
Page migration of a VM_LOCKED page tends to fail, because when the old
page is unmapped, it is put on the mlock pagevec with raised refcount,
which then fails the freeze.

At first I thought this would be fixed by a local mlock_page_drain() at
the upper rmap_walk() level - which would have nicely batched all the
munlocks of that page; but tests show that the task can too easily move
to another cpu, leaving pagevec residue behind which fails the migration.

So try_to_migrate_one() drain the local pagevec after page_remove_rmap()
from a VM_LOCKED vma; and do the same in try_to_unmap_one(), whose
TTU_IGNORE_MLOCK users would want the same treatment; and do the same
in remove_migration_pte() - not important when successfully inserting
a new page, but necessary when hoping to retry after failure.

Any new pagevec runs the risk of adding a new way of stranding, and we
might discover other corners where mlock_page_drain() or lru_add_drain()
would now help.

Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
mm/migrate.c
mm/rmap.c

index f4bcf15..e7d0b68 100644 (file)
@@ -251,6 +251,8 @@ static bool remove_migration_pte(struct page *page, struct vm_area_struct *vma,
                                page_add_file_rmap(new, vma, false);
                        set_pte_at(vma->vm_mm, pvmw.address, pvmw.pte, pte);
                }
+               if (vma->vm_flags & VM_LOCKED)
+                       mlock_page_drain(smp_processor_id());
 
                /* No need to invalidate - it was non-present before */
                update_mmu_cache(vma, pvmw.address, pvmw.pte);
index 5442a5c..714bfdc 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -1656,6 +1656,8 @@ discard:
                 * See Documentation/vm/mmu_notifier.rst
                 */
                page_remove_rmap(subpage, vma, PageHuge(page));
+               if (vma->vm_flags & VM_LOCKED)
+                       mlock_page_drain(smp_processor_id());
                put_page(page);
        }
 
@@ -1930,6 +1932,8 @@ static bool try_to_migrate_one(struct page *page, struct vm_area_struct *vma,
                 * See Documentation/vm/mmu_notifier.rst
                 */
                page_remove_rmap(subpage, vma, PageHuge(page));
+               if (vma->vm_flags & VM_LOCKED)
+                       mlock_page_drain(smp_processor_id());
                put_page(page);
        }