[PARISC] head.S: section mismatch fixes
[sfrench/cifs-2.6.git] / mm / madvise.c
index 77916e9fc52bc466abb08d55457b8c79b0e05854..93ee375b38e7edc7b414b7a2b6891411a45d0b0c 100644 (file)
 #include <linux/syscalls.h>
 #include <linux/mempolicy.h>
 #include <linux/hugetlb.h>
+#include <linux/sched.h>
+
+/*
+ * Any behaviour which results in changes to the vma->vm_flags needs to
+ * take mmap_sem for writing. Others, which simply traverse vmas, need
+ * to only take it for reading.
+ */
+static int madvise_need_mmap_write(int behavior)
+{
+       switch (behavior) {
+       case MADV_REMOVE:
+       case MADV_WILLNEED:
+       case MADV_DONTNEED:
+               return 0;
+       default:
+               /* be safe, default to 1. list exceptions explicitly */
+               return 1;
+       }
+}
 
 /*
  * We can potentially split a vm area into separate
@@ -159,9 +178,10 @@ static long madvise_remove(struct vm_area_struct *vma,
                                unsigned long start, unsigned long end)
 {
        struct address_space *mapping;
-        loff_t offset, endoff;
+       loff_t offset, endoff;
+       int error;
 
-       *prev = vma;
+       *prev = NULL;   /* tell sys_madvise we drop mmap_sem */
 
        if (vma->vm_flags & (VM_LOCKED|VM_NONLINEAR|VM_HUGETLB))
                return -EINVAL;
@@ -180,7 +200,12 @@ static long madvise_remove(struct vm_area_struct *vma,
                        + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
        endoff = (loff_t)(end - vma->vm_start - 1)
                        + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
-       return  vmtruncate_range(mapping->host, offset, endoff);
+
+       /* vmtruncate_range needs to take i_mutex and i_alloc_sem */
+       up_read(&current->mm->mmap_sem);
+       error = vmtruncate_range(mapping->host, offset, endoff);
+       down_read(&current->mm->mmap_sem);
+       return error;
 }
 
 static long
@@ -262,9 +287,14 @@ asmlinkage long sys_madvise(unsigned long start, size_t len_in, int behavior)
        struct vm_area_struct * vma, *prev;
        int unmapped_error = 0;
        int error = -EINVAL;
+       int write;
        size_t len;
 
-       down_write(&current->mm->mmap_sem);
+       write = madvise_need_mmap_write(behavior);
+       if (write)
+               down_write(&current->mm->mmap_sem);
+       else
+               down_read(&current->mm->mmap_sem);
 
        if (start & ~PAGE_MASK)
                goto out;
@@ -315,14 +345,21 @@ asmlinkage long sys_madvise(unsigned long start, size_t len_in, int behavior)
                if (error)
                        goto out;
                start = tmp;
-               if (start < prev->vm_end)
+               if (prev && start < prev->vm_end)
                        start = prev->vm_end;
                error = unmapped_error;
                if (start >= end)
                        goto out;
-               vma = prev->vm_next;
+               if (prev)
+                       vma = prev->vm_next;
+               else    /* madvise_remove dropped mmap_sem */
+                       vma = find_vma(current->mm, start);
        }
 out:
-       up_write(&current->mm->mmap_sem);
+       if (write)
+               up_write(&current->mm->mmap_sem);
+       else
+               up_read(&current->mm->mmap_sem);
+
        return error;
 }