]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
mm/mmap: Change __do_munmap() to avoid unnecessary lookups.
authorLiam R. Howlett <Liam.Howlett@Oracle.com>
Thu, 19 Nov 2020 17:57:23 +0000 (12:57 -0500)
committerLiam R. Howlett <Liam.Howlett@Oracle.com>
Thu, 14 Jan 2021 01:33:55 +0000 (20:33 -0500)
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@Oracle.com>
mm/mmap.c

index c71167fc0cc7c316c3fcb2de277cb51ee5f8a189..be58dec13807800bec753cf8fabf213e5c013240 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2604,44 +2604,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.
@@ -2721,12 +2683,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);
@@ -2734,6 +2700,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
@@ -2745,24 +2713,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  */
 
        /*
@@ -2786,16 +2754,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)) {
                /*
@@ -2808,22 +2787,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);
 
@@ -3247,7 +3250,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);