[PATCH 21/28] mm/mmap: Change __do_munmap() to use a ma_state

From: Liam R. Howlett
Date: Thu Dec 10 2020 - 13:16:20 EST


Signed-off-by: Liam R. Howlett <Liam.Howlett@xxxxxxxxxx>
---
mm/mmap.c | 99 ++++++++++++++++++++++++++-----------------------------
1 file changed, 47 insertions(+), 52 deletions(-)

diff --git a/mm/mmap.c b/mm/mmap.c
index 15616f105d051..34f337a5fc31d 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2562,44 +2562,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.
@@ -2679,12 +2641,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);
@@ -2692,6 +2658,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
@@ -2703,23 +2671,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;

+ mas.last = end - 1;
/* we have start < vma->vm_end */

/*
@@ -2744,6 +2713,8 @@ int __do_munmap(struct mm_struct *mm, unsigned long start, size_t len,
return error;
prev = vma;
vma = vma_next(mm, prev);
+ mas.index = start;
+ mas_reset(&mas);
} else {
prev = vma->vm_prev;
}
@@ -2759,6 +2730,7 @@ int __do_munmap(struct mm_struct *mm, unsigned long start, size_t len,
if (error)
return error;
vma = vma_next(mm, prev);
+ mas_reset(&mas);
}


@@ -2779,17 +2751,40 @@ int __do_munmap(struct mm_struct *mm, unsigned long start, size_t len,
}

/*
- * 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 and remove from the mm tree*/
- if (!detach_vmas_to_be_unmapped(mm, vma, prev, end))
- downgrade = false;
+ /* 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;

- if (downgrade)
- mmap_write_downgrade(mm);
+ 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;
+
+ /*
+ * 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);

@@ -3212,7 +3207,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