From: Hugh Dickins 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 2004-04-03 03:00:18.332444600 -0800 +++ 25-akpm/include/linux/mm.h 2004-04-03 03:00:18.338443688 -0800 @@ -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 2004-04-03 03:00:18.334444296 -0800 +++ 25-akpm/mm/mmap.c 2004-04-03 03:00:18.339443536 -0800 @@ -1525,3 +1525,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 2004-04-03 03:00:18.335444144 -0800 +++ 25-akpm/mm/mremap.c 2004-04-03 03:00:18.340443384 -0800 @@ -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; _