Commit 6213074b authored by Chris Wright's avatar Chris Wright Committed by Linus Torvalds

[PATCH] allow vma merging with mlock et. al.

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: default avatarChris Wright <chrisw@osdl.org>
Acked-by: default avatarHugh Dickins <hugh@veritas.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent afb7359a
...@@ -7,18 +7,32 @@ ...@@ -7,18 +7,32 @@
#include <linux/mman.h> #include <linux/mman.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/mempolicy.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
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) unsigned long start, unsigned long end, unsigned int newflags)
{ {
struct mm_struct * mm = vma->vm_mm; struct mm_struct * mm = vma->vm_mm;
pgoff_t pgoff;
int pages; int pages;
int ret = 0; int ret = 0;
if (newflags == vma->vm_flags) if (newflags == vma->vm_flags) {
*prev = vma;
goto out; 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) { if (start != vma->vm_start) {
ret = split_vma(mm, vma, start, 1); ret = split_vma(mm, vma, start, 1);
...@@ -32,6 +46,7 @@ static int mlock_fixup(struct vm_area_struct * vma, ...@@ -32,6 +46,7 @@ static int mlock_fixup(struct vm_area_struct * vma,
goto out; goto out;
} }
success:
/* /*
* vm_flags is protected by the mmap_sem held in write mode. * 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 * It's okay if try_to_unmap_one unmaps a page just after we
...@@ -59,7 +74,7 @@ static int mlock_fixup(struct vm_area_struct * vma, ...@@ -59,7 +74,7 @@ static int mlock_fixup(struct vm_area_struct * vma,
static int do_mlock(unsigned long start, size_t len, int on) static int do_mlock(unsigned long start, size_t len, int on)
{ {
unsigned long nstart, end, tmp; unsigned long nstart, end, tmp;
struct vm_area_struct * vma, * next; struct vm_area_struct * vma, * prev;
int error; int error;
len = PAGE_ALIGN(len); len = PAGE_ALIGN(len);
...@@ -68,10 +83,13 @@ static int do_mlock(unsigned long start, size_t len, int on) ...@@ -68,10 +83,13 @@ static int do_mlock(unsigned long start, size_t len, int on)
return -EINVAL; return -EINVAL;
if (end == start) if (end == start)
return 0; return 0;
vma = find_vma(current->mm, start); vma = find_vma_prev(current->mm, start, &prev);
if (!vma || vma->vm_start > start) if (!vma || vma->vm_start > start)
return -ENOMEM; return -ENOMEM;
if (start > vma->vm_start)
prev = vma;
for (nstart = start ; ; ) { for (nstart = start ; ; ) {
unsigned int newflags; unsigned int newflags;
...@@ -81,18 +99,19 @@ static int do_mlock(unsigned long start, size_t len, int on) ...@@ -81,18 +99,19 @@ static int do_mlock(unsigned long start, size_t len, int on)
if (!on) if (!on)
newflags &= ~VM_LOCKED; newflags &= ~VM_LOCKED;
if (vma->vm_end >= end) {
error = mlock_fixup(vma, nstart, end, newflags);
break;
}
tmp = vma->vm_end; tmp = vma->vm_end;
next = vma->vm_next; if (tmp > end)
error = mlock_fixup(vma, nstart, tmp, newflags); tmp = end;
error = mlock_fixup(vma, &prev, nstart, tmp, newflags);
if (error) if (error)
break; break;
nstart = tmp; 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) { if (!vma || vma->vm_start != nstart) {
error = -ENOMEM; error = -ENOMEM;
break; break;
...@@ -141,7 +160,7 @@ asmlinkage long sys_munlock(unsigned long start, size_t len) ...@@ -141,7 +160,7 @@ asmlinkage long sys_munlock(unsigned long start, size_t len)
static int do_mlockall(int flags) static int do_mlockall(int flags)
{ {
struct vm_area_struct * vma; struct vm_area_struct * vma, * prev = NULL;
unsigned int def_flags = 0; unsigned int def_flags = 0;
if (flags & MCL_FUTURE) if (flags & MCL_FUTURE)
...@@ -150,7 +169,7 @@ static int do_mlockall(int flags) ...@@ -150,7 +169,7 @@ static int do_mlockall(int flags)
if (flags == MCL_FUTURE) if (flags == MCL_FUTURE)
goto out; 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; unsigned int newflags;
newflags = vma->vm_flags | VM_LOCKED; newflags = vma->vm_flags | VM_LOCKED;
...@@ -158,7 +177,7 @@ static int do_mlockall(int flags) ...@@ -158,7 +177,7 @@ static int do_mlockall(int flags)
newflags &= ~VM_LOCKED; newflags &= ~VM_LOCKED;
/* Ignore errors */ /* Ignore errors */
mlock_fixup(vma, vma->vm_start, vma->vm_end, newflags); mlock_fixup(vma, &prev, vma->vm_start, vma->vm_end, newflags);
} }
out: out:
return 0; return 0;
......
...@@ -188,16 +188,13 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, ...@@ -188,16 +188,13 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev,
goto success; goto success;
} }
*pprev = vma;
if (start != vma->vm_start) { if (start != vma->vm_start) {
error = split_vma(mm, vma, start, 1); error = split_vma(mm, vma, start, 1);
if (error) if (error)
goto fail; 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) { if (end != vma->vm_end) {
error = split_vma(mm, vma, end, 0); error = split_vma(mm, vma, end, 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment