From: Hugh Dickins <hugh@veritas.com>

Subtle point from Rajesh Venkatasubramanian: when mremap's move_vma fails and
so rewinds, before moving the file-based ptes back, we must move new_vma
before old vma in the i_mmap or i_mmap_shared list, so that when racing
against vmtruncate we cannot propagate pages to be truncated back from
new_vma into the just cleaned old_vma.


---

 25-akpm/include/linux/mm.h |    1 +
 25-akpm/mm/mmap.c          |   21 +++++++++++++++++++++
 25-akpm/mm/mremap.c        |    7 +++++++
 3 files changed, 29 insertions(+)

diff -puN include/linux/mm.h~mremap-vma_relink_file-fix include/linux/mm.h
--- 25/include/linux/mm.h~mremap-vma_relink_file-fix	Tue Mar 30 16:04:21 2004
+++ 25-akpm/include/linux/mm.h	Tue Mar 30 16:04:21 2004
@@ -543,6 +543,7 @@ extern void __vma_link_rb(struct mm_stru
 	struct rb_node **, struct rb_node *);
 extern struct vm_area_struct *copy_vma(struct vm_area_struct *,
 	unsigned long addr, unsigned long len, unsigned long pgoff);
+extern void vma_relink_file(struct vm_area_struct *, struct vm_area_struct *);
 extern void exit_mmap(struct mm_struct *);
 
 extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
diff -puN mm/mmap.c~mremap-vma_relink_file-fix mm/mmap.c
--- 25/mm/mmap.c~mremap-vma_relink_file-fix	Tue Mar 30 16:04:21 2004
+++ 25-akpm/mm/mmap.c	Tue Mar 30 16:04:21 2004
@@ -1524,3 +1524,24 @@ struct vm_area_struct *copy_vma(struct v
 	}
 	return new_vma;
 }
+
+/*
+ * Position vma after prev in shared file list:
+ * for mremap move error recovery racing against vmtruncate.
+ */
+void vma_relink_file(struct vm_area_struct *vma, struct vm_area_struct *prev)
+{
+	struct mm_struct *mm = vma->vm_mm;
+	struct address_space *mapping;
+
+	if (vma->vm_file) {
+		mapping = vma->vm_file->f_mapping;
+		if (mapping) {
+			down(&mapping->i_shared_sem);
+			spin_lock(&mm->page_table_lock);
+			list_move(&vma->shared, &prev->shared);
+			spin_unlock(&mm->page_table_lock);
+			up(&mapping->i_shared_sem);
+		}
+	}
+}
diff -puN mm/mremap.c~mremap-vma_relink_file-fix mm/mremap.c
--- 25/mm/mremap.c~mremap-vma_relink_file-fix	Tue Mar 30 16:04:21 2004
+++ 25-akpm/mm/mremap.c	Tue Mar 30 16:04:21 2004
@@ -187,7 +187,14 @@ static unsigned long move_vma(struct vm_
 		 * On error, move entries back from new area to old,
 		 * which will succeed since page tables still there,
 		 * and then proceed to unmap new area instead of old.
+		 *
+		 * Subtle point from Rajesh Venkatasubramanian: before
+		 * moving file-based ptes, move new_vma before old vma
+		 * in the i_mmap or i_mmap_shared list, so when racing
+		 * against vmtruncate we cannot propagate pages to be
+		 * truncated back from new_vma into just cleaned old.
 		 */
+		vma_relink_file(vma, new_vma);
 		move_page_tables(new_vma, old_addr, new_addr, moved_len);
 		vma = new_vma;
 		old_len = new_len;

_