As there is no longer a vmacache, find_vma() is more expensive and so avoid doing them Signed-off-by: Liam R. Howlett <Liam.Howlett@xxxxxxxxxx> --- mm/mmap.c | 115 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/mm/mmap.c b/mm/mmap.c index cc9d0c524d575..af426972c9dd2 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2585,44 +2585,6 @@ static void unmap_region(struct mm_struct *mm, tlb_finish_mmu(&tlb, start, end); } -/* - * Create a list of vma's touched by the unmap, removing them from the mm's - * vma list as we go.. - */ -static bool -detach_vmas_to_be_unmapped(struct mm_struct *mm, struct vm_area_struct *vma, - struct vm_area_struct *prev, unsigned long end) -{ - struct vm_area_struct **insertion_point; - struct vm_area_struct *tail_vma = NULL; - - insertion_point = (prev ? &prev->vm_next : &mm->mmap); - vma->vm_prev = NULL; - vma_mt_szero(mm, vma->vm_start, end); - do { - mm->map_count--; - tail_vma = vma; - vma = vma->vm_next; - } while (vma && vma->vm_start < end); - *insertion_point = vma; - if (vma) - vma->vm_prev = prev; - else - mm->highest_vm_end = prev ? vm_end_gap(prev) : 0; - tail_vma->vm_next = NULL; - - /* - * Do not downgrade mmap_lock if we are next to VM_GROWSDOWN or - * VM_GROWSUP VMA. Such VMAs can change their size under - * down_read(mmap_lock) and collide with the VMA we are about to unmap. - */ - if (vma && (vma->vm_flags & VM_GROWSDOWN)) - return false; - if (prev && (prev->vm_flags & VM_GROWSUP)) - return false; - return true; -} - /* * __split_vma() bypasses sysctl_max_map_count checking. We use this where it * has already been checked or doesn't make sense to fail. @@ -2702,12 +2664,16 @@ int split_vma(struct mm_struct *mm, struct vm_area_struct *vma, return __split_vma(mm, vma, addr, new_below); } -static inline void unlock_range(struct vm_area_struct *start, unsigned long limit) +static inline int unlock_range(struct vm_area_struct *start, + struct vm_area_struct **tail, unsigned long limit) { struct mm_struct *mm = start->vm_mm; struct vm_area_struct *tmp = start; + int count = 0; while (tmp && tmp->vm_start < limit) { + *tail = tmp; + count++; if (tmp->vm_flags & VM_LOCKED) { mm->locked_vm -= vma_pages(tmp); munlock_vma_pages_all(tmp); @@ -2715,6 +2681,8 @@ static inline void unlock_range(struct vm_area_struct *start, unsigned long limi tmp = tmp->vm_next; } + + return count; } /* Munmap is split into 2 main parts -- this part which finds * what needs doing, and the areas themselves, which do the @@ -2726,24 +2694,24 @@ int __do_munmap(struct mm_struct *mm, unsigned long start, size_t len, { unsigned long end; struct vm_area_struct *vma, *prev, *last; + MA_STATE(mas, &mm->mm_mt, start, start); if ((offset_in_page(start)) || start > TASK_SIZE || len > TASK_SIZE-start) return -EINVAL; - len = PAGE_ALIGN(len); - end = start + len; - if (len == 0) + end = start + PAGE_ALIGN(len); + if (end == start) return -EINVAL; /* arch_unmap() might do unmaps itself. */ arch_unmap(mm, start, end); /* Find the first overlapping VMA */ - vma = find_vma_intersection(mm, start, end); + vma = mas_find(&mas, end - 1); if (!vma) return 0; - prev = vma->vm_prev; + mas.last = end - 1; /* we have start < vma->vm_end */ /* @@ -2767,16 +2735,27 @@ int __do_munmap(struct mm_struct *mm, unsigned long start, size_t len, if (error) return error; prev = vma; + vma = vma_next(mm, prev); + mas.index = start; + mas_reset(&mas); + } else { + prev = vma->vm_prev; } + if (vma->vm_end >= end) + last = vma; + else + last = find_vma_intersection(mm, end - 1, end); + /* Does it split the last one? */ - last = find_vma(mm, end); - if (last && end > last->vm_start) { + if (last && end < last->vm_end) { int error = __split_vma(mm, last, end, 1); if (error) return error; + vma = vma_next(mm, prev); + mas_reset(&mas); } - vma = vma_next(mm, prev); + if (unlikely(uf)) { /* @@ -2789,22 +2768,46 @@ int __do_munmap(struct mm_struct *mm, unsigned long start, size_t len, * failure that it's not worth optimizing it for. */ int error = userfaultfd_unmap_prep(vma, start, end, uf); + if (error) return error; } /* - * unlock any mlock()ed ranges before detaching vmas + * unlock any mlock()ed ranges before detaching vmas, count the number + * of VMAs to be dropped, and return the tail entry of the affected + * area. */ - if (mm->locked_vm) - unlock_range(vma, end); + mm->map_count -= unlock_range(vma, &last, end); + /* Drop removed area from the tree */ + mas_store_gfp(&mas, NULL, GFP_KERNEL); + + /* Detach vmas from the MM linked list */ + vma->vm_prev = NULL; + if (prev) + prev->vm_next = last->vm_next; + else + mm->mmap = last->vm_next; - /* Detach vmas from the MM linked list and remove from the mm tree*/ - if (!detach_vmas_to_be_unmapped(mm, vma, prev, end)) - downgrade = false; + if (last->vm_next) { + last->vm_next->vm_prev = prev; + last->vm_next = NULL; + } else + mm->highest_vm_end = prev ? vm_end_gap(prev) : 0; - if (downgrade) - mmap_write_downgrade(mm); + /* + * Do not downgrade mmap_lock if we are next to VM_GROWSDOWN or + * VM_GROWSUP VMA. Such VMAs can change their size under + * down_read(mmap_lock) and collide with the VMA we are about to unmap. + */ + if (downgrade) { + if (last && (last->vm_flags & VM_GROWSDOWN)) + downgrade = false; + else if (prev && (prev->vm_flags & VM_GROWSUP)) + downgrade = false; + else + mmap_write_downgrade(mm); + } unmap_region(mm, vma, prev, start, end); @@ -3227,7 +3230,7 @@ void exit_mmap(struct mm_struct *mm) } if (mm->locked_vm) - unlock_range(mm->mmap, ULONG_MAX); + unlock_range(mm->mmap, &vma, ULONG_MAX); arch_exit_mmap(mm); -- 2.28.0