|
@@ -177,6 +177,37 @@ out:
|
|
|
return SWAP_AGAIN;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Congratulations to trinity for discovering this bug.
|
|
|
+ * mm/fremap.c's remap_file_pages() accepts any range within a single vma to
|
|
|
+ * convert that vma to VM_NONLINEAR; and generic_file_remap_pages() will then
|
|
|
+ * replace the specified range by file ptes throughout (maybe populated after).
|
|
|
+ * If page migration finds a page within that range, while it's still located
|
|
|
+ * by vma_interval_tree rather than lost to i_mmap_nonlinear list, no problem:
|
|
|
+ * zap_pte() clears the temporary migration entry before mmap_sem is dropped.
|
|
|
+ * But if the migrating page is in a part of the vma outside the range to be
|
|
|
+ * remapped, then it will not be cleared, and remove_migration_ptes() needs to
|
|
|
+ * deal with it. Fortunately, this part of the vma is of course still linear,
|
|
|
+ * so we just need to use linear location on the nonlinear list.
|
|
|
+ */
|
|
|
+static int remove_linear_migration_ptes_from_nonlinear(struct page *page,
|
|
|
+ struct address_space *mapping, void *arg)
|
|
|
+{
|
|
|
+ struct vm_area_struct *vma;
|
|
|
+ /* hugetlbfs does not support remap_pages, so no huge pgoff worries */
|
|
|
+ pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
|
|
|
+ unsigned long addr;
|
|
|
+
|
|
|
+ list_for_each_entry(vma,
|
|
|
+ &mapping->i_mmap_nonlinear, shared.nonlinear) {
|
|
|
+
|
|
|
+ addr = vma->vm_start + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
|
|
|
+ if (addr >= vma->vm_start && addr < vma->vm_end)
|
|
|
+ remove_migration_pte(page, vma, addr, arg);
|
|
|
+ }
|
|
|
+ return SWAP_AGAIN;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Get rid of all migration entries and replace them by
|
|
|
* references to the indicated page.
|
|
@@ -186,6 +217,7 @@ static void remove_migration_ptes(struct page *old, struct page *new)
|
|
|
struct rmap_walk_control rwc = {
|
|
|
.rmap_one = remove_migration_pte,
|
|
|
.arg = old,
|
|
|
+ .file_nonlinear = remove_linear_migration_ptes_from_nonlinear,
|
|
|
};
|
|
|
|
|
|
rmap_walk(new, &rwc);
|