From: Chris Wright Successive mlock/munlock calls can leave fragmented vmas because they can be split but not merged. Give mlock et. al. full vma merging support. While we're at it, move *pprev assignment above first split_vma in mprotect_fixup to keep it in step with mlock_fixup (which for mlockall ignores errors yet still needs a valid prev pointer). Signed-off-by: Chris Wright Acked-by: Hugh Dickins Signed-off-by: Andrew Morton --- 25-akpm/mm/mlock.c | 49 ++++++++++++++++++++++++++++++++++--------------- 25-akpm/mm/mprotect.c | 7 ++----- 2 files changed, 36 insertions(+), 20 deletions(-) diff -puN mm/mlock.c~allow-vma-merging-with-mlock-et-al mm/mlock.c --- 25/mm/mlock.c~allow-vma-merging-with-mlock-et-al 2005-02-28 16:44:35.000000000 -0800 +++ 25-akpm/mm/mlock.c 2005-02-28 16:44:35.000000000 -0800 @@ -7,18 +7,32 @@ #include #include +#include #include -static int mlock_fixup(struct vm_area_struct * vma, +static int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev, unsigned long start, unsigned long end, unsigned int newflags) { struct mm_struct * mm = vma->vm_mm; + pgoff_t pgoff; int pages; int ret = 0; - if (newflags == vma->vm_flags) + if (newflags == vma->vm_flags) { + *prev = vma; goto out; + } + + pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); + *prev = vma_merge(mm, *prev, start, end, newflags, vma->anon_vma, + vma->vm_file, pgoff, vma_policy(vma)); + if (*prev) { + vma = *prev; + goto success; + } + + *prev = vma; if (start != vma->vm_start) { ret = split_vma(mm, vma, start, 1); @@ -32,6 +46,7 @@ static int mlock_fixup(struct vm_area_st goto out; } +success: /* * vm_flags is protected by the mmap_sem held in write mode. * It's okay if try_to_unmap_one unmaps a page just after we @@ -59,7 +74,7 @@ out: static int do_mlock(unsigned long start, size_t len, int on) { unsigned long nstart, end, tmp; - struct vm_area_struct * vma, * next; + struct vm_area_struct * vma, * prev; int error; len = PAGE_ALIGN(len); @@ -68,10 +83,13 @@ static int do_mlock(unsigned long start, return -EINVAL; if (end == start) return 0; - vma = find_vma(current->mm, start); + vma = find_vma_prev(current->mm, start, &prev); if (!vma || vma->vm_start > start) return -ENOMEM; + if (start > vma->vm_start) + prev = vma; + for (nstart = start ; ; ) { unsigned int newflags; @@ -81,18 +99,19 @@ static int do_mlock(unsigned long start, if (!on) newflags &= ~VM_LOCKED; - if (vma->vm_end >= end) { - error = mlock_fixup(vma, nstart, end, newflags); - break; - } - tmp = vma->vm_end; - next = vma->vm_next; - error = mlock_fixup(vma, nstart, tmp, newflags); + if (tmp > end) + tmp = end; + error = mlock_fixup(vma, &prev, nstart, tmp, newflags); if (error) break; nstart = tmp; - vma = next; + if (nstart < prev->vm_end) + nstart = prev->vm_end; + if (nstart >= end) + break; + + vma = prev->vm_next; if (!vma || vma->vm_start != nstart) { error = -ENOMEM; break; @@ -141,7 +160,7 @@ asmlinkage long sys_munlock(unsigned lon static int do_mlockall(int flags) { - struct vm_area_struct * vma; + struct vm_area_struct * vma, * prev = NULL; unsigned int def_flags = 0; if (flags & MCL_FUTURE) @@ -150,7 +169,7 @@ static int do_mlockall(int flags) if (flags == MCL_FUTURE) goto out; - for (vma = current->mm->mmap; vma ; vma = vma->vm_next) { + for (vma = current->mm->mmap; vma ; vma = prev->vm_next) { unsigned int newflags; newflags = vma->vm_flags | VM_LOCKED; @@ -158,7 +177,7 @@ static int do_mlockall(int flags) newflags &= ~VM_LOCKED; /* Ignore errors */ - mlock_fixup(vma, vma->vm_start, vma->vm_end, newflags); + mlock_fixup(vma, &prev, vma->vm_start, vma->vm_end, newflags); } out: return 0; diff -puN mm/mprotect.c~allow-vma-merging-with-mlock-et-al mm/mprotect.c --- 25/mm/mprotect.c~allow-vma-merging-with-mlock-et-al 2005-02-28 16:44:35.000000000 -0800 +++ 25-akpm/mm/mprotect.c 2005-02-28 16:44:35.000000000 -0800 @@ -185,16 +185,13 @@ mprotect_fixup(struct vm_area_struct *vm goto success; } + *pprev = vma; + if (start != vma->vm_start) { error = split_vma(mm, vma, start, 1); if (error) goto fail; } - /* - * Unless it returns an error, this function always sets *pprev to - * the first vma for which vma->vm_end >= end. - */ - *pprev = vma; if (end != vma->vm_end) { error = split_vma(mm, vma, end, 0); _