Message ID | 1594696064-1409-1-git-send-email-linmiaohe@huawei.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | mm: mmap: Merge vma after call_mmap() if possible | expand |
On Tue, 14 Jul 2020 11:07:44 +0800 linmiaohe <linmiaohe@huawei.com> wrote: > The vm_flags may be changed after call_mmap() because drivers may set some > flags for their own purpose. As a result, we failed to merge the adjacent > vma due to the different vm_flags as userspace can't pass in the same one. > Try to merge vma after call_mmap() to fix this issue. Which drivers do this?
On Tue, 14 Jul 2020 11:07:44 +0800 linmiaohe <linmiaohe@huawei.com> wrote: > From: Miaohe Lin <linmiaohe@huawei.com> > > The vm_flags may be changed after call_mmap() because drivers may set some > flags for their own purpose. As a result, we failed to merge the adjacent > vma due to the different vm_flags as userspace can't pass in the same one. > Try to merge vma after call_mmap() to fix this issue. > > Signed-off-by: Hongxiang Lou <louhongxiang@huawei.com> > Signed-off-by: Miaohe Lin <linmiaohe@huawei.com> > --- > mm/mmap.c | 17 ++++++++++++++++- > 1 file changed, 16 insertions(+), 1 deletion(-) > > diff --git a/mm/mmap.c b/mm/mmap.c > index 59a4682ebf3f..9568117471f8 100644 > --- a/mm/mmap.c > +++ b/mm/mmap.c > @@ -1689,7 +1689,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr, > struct list_head *uf) > { > struct mm_struct *mm = current->mm; > - struct vm_area_struct *vma, *prev; > + struct vm_area_struct *vma, *prev, *merge; > int error; > struct rb_node **rb_link, *rb_parent; > unsigned long charged = 0; > @@ -1773,6 +1773,20 @@ unsigned long mmap_region(struct file *file, unsigned long addr, > if (error) > goto unmap_and_free_vma; > > + /* If vm_flags changed after call_mmap(), we should try merge vma again > + * as we may succeed this time. > + */ > + if (unlikely(vm_flags != vma->vm_flags && prev)) { > + merge = vma_merge(mm, prev, vma->vm_start, vma->vm_end, vma->vm_flags, > + NULL, vma->vm_file, vma->vm_pgoff, NULL, NULL_VM_UFFD_CTX); > + if (merge) { > + fput(file); > + vm_area_free(vma); > + vma = merge; > + goto unmap_writable; Shouldn't we update local variable `vm_flags' here, to pick up the change? And possibly `addr'? > + } > + } > + > /* Can addr have changed?? > * > * Answer: Yes, several device drivers can do it in their > @@ -1795,6 +1809,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr, > vma_link(mm, vma, prev, rb_link, rb_parent); > /* Once vma denies write, undo our temporary denial count */ > if (file) { > +unmap_writable: > if (vm_flags & VM_SHARED) > mapping_unmap_writable(file->f_mapping); > if (vm_flags & VM_DENYWRITE)
diff --git a/mm/mmap.c b/mm/mmap.c index 59a4682ebf3f..9568117471f8 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1689,7 +1689,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr, struct list_head *uf) { struct mm_struct *mm = current->mm; - struct vm_area_struct *vma, *prev; + struct vm_area_struct *vma, *prev, *merge; int error; struct rb_node **rb_link, *rb_parent; unsigned long charged = 0; @@ -1773,6 +1773,20 @@ unsigned long mmap_region(struct file *file, unsigned long addr, if (error) goto unmap_and_free_vma; + /* If vm_flags changed after call_mmap(), we should try merge vma again + * as we may succeed this time. + */ + if (unlikely(vm_flags != vma->vm_flags && prev)) { + merge = vma_merge(mm, prev, vma->vm_start, vma->vm_end, vma->vm_flags, + NULL, vma->vm_file, vma->vm_pgoff, NULL, NULL_VM_UFFD_CTX); + if (merge) { + fput(file); + vm_area_free(vma); + vma = merge; + goto unmap_writable; + } + } + /* Can addr have changed?? * * Answer: Yes, several device drivers can do it in their @@ -1795,6 +1809,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr, vma_link(mm, vma, prev, rb_link, rb_parent); /* Once vma denies write, undo our temporary denial count */ if (file) { +unmap_writable: if (vm_flags & VM_SHARED) mapping_unmap_writable(file->f_mapping); if (vm_flags & VM_DENYWRITE)