Pull ec into release branch
[pandora-kernel.git] / mm / madvise.c
index 20e075d..603c525 100644 (file)
@@ -22,16 +22,23 @@ static long madvise_behavior(struct vm_area_struct * vma,
        struct mm_struct * mm = vma->vm_mm;
        int error = 0;
        pgoff_t pgoff;
-       int new_flags = vma->vm_flags & ~VM_READHINTMASK;
+       int new_flags = vma->vm_flags;
 
        switch (behavior) {
+       case MADV_NORMAL:
+               new_flags = new_flags & ~VM_RAND_READ & ~VM_SEQ_READ;
+               break;
        case MADV_SEQUENTIAL:
-               new_flags |= VM_SEQ_READ;
+               new_flags = (new_flags & ~VM_RAND_READ) | VM_SEQ_READ;
                break;
        case MADV_RANDOM:
-               new_flags |= VM_RAND_READ;
+               new_flags = (new_flags & ~VM_SEQ_READ) | VM_RAND_READ;
                break;
-       default:
+       case MADV_DONTFORK:
+               new_flags |= VM_DONTCOPY;
+               break;
+       case MADV_DOFORK:
+               new_flags &= ~VM_DONTCOPY;
                break;
        }
 
@@ -126,7 +133,7 @@ static long madvise_dontneed(struct vm_area_struct * vma,
                             unsigned long start, unsigned long end)
 {
        *prev = vma;
-       if ((vma->vm_flags & VM_LOCKED) || is_vm_hugetlb_page(vma))
+       if (vma->vm_flags & (VM_LOCKED|VM_HUGETLB|VM_PFNMAP))
                return -EINVAL;
 
        if (unlikely(vma->vm_flags & VM_NONLINEAR)) {
@@ -140,6 +147,48 @@ static long madvise_dontneed(struct vm_area_struct * vma,
        return 0;
 }
 
+/*
+ * Application wants to free up the pages and associated backing store.
+ * This is effectively punching a hole into the middle of a file.
+ *
+ * NOTE: Currently, only shmfs/tmpfs is supported for this operation.
+ * Other filesystems return -ENOSYS.
+ */
+static long madvise_remove(struct vm_area_struct *vma,
+                               struct vm_area_struct **prev,
+                               unsigned long start, unsigned long end)
+{
+       struct address_space *mapping;
+       loff_t offset, endoff;
+       int error;
+
+       *prev = NULL;   /* tell sys_madvise we drop mmap_sem */
+
+       if (vma->vm_flags & (VM_LOCKED|VM_NONLINEAR|VM_HUGETLB))
+               return -EINVAL;
+
+       if (!vma->vm_file || !vma->vm_file->f_mapping
+               || !vma->vm_file->f_mapping->host) {
+                       return -EINVAL;
+       }
+
+       if ((vma->vm_flags & (VM_SHARED|VM_WRITE)) != (VM_SHARED|VM_WRITE))
+               return -EACCES;
+
+       mapping = vma->vm_file->f_mapping;
+
+       offset = (loff_t)(start - vma->vm_start)
+                       + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
+       endoff = (loff_t)(end - vma->vm_start - 1)
+                       + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
+
+       /* vmtruncate_range needs to take i_mutex and i_alloc_sem */
+       up_write(&current->mm->mmap_sem);
+       error = vmtruncate_range(mapping->host, offset, endoff);
+       down_write(&current->mm->mmap_sem);
+       return error;
+}
+
 static long
 madvise_vma(struct vm_area_struct *vma, struct vm_area_struct **prev,
                unsigned long start, unsigned long end, int behavior)
@@ -147,11 +196,20 @@ madvise_vma(struct vm_area_struct *vma, struct vm_area_struct **prev,
        long error;
 
        switch (behavior) {
+       case MADV_DOFORK:
+               if (vma->vm_flags & VM_IO) {
+                       error = -EINVAL;
+                       break;
+               }
+       case MADV_DONTFORK:
        case MADV_NORMAL:
        case MADV_SEQUENTIAL:
        case MADV_RANDOM:
                error = madvise_behavior(vma, prev, start, end, behavior);
                break;
+       case MADV_REMOVE:
+               error = madvise_remove(vma, prev, start, end);
+               break;
 
        case MADV_WILLNEED:
                error = madvise_willneed(vma, prev, start, end);
@@ -190,6 +248,8 @@ madvise_vma(struct vm_area_struct *vma, struct vm_area_struct **prev,
  *             some pages ahead.
  *  MADV_DONTNEED - the application is finished with the given range,
  *             so the kernel can free resources associated with it.
+ *  MADV_REMOVE - the application wants to free up the given range of
+ *             pages and associated backing store.
  *
  * return values:
  *  zero    - success
@@ -261,12 +321,15 @@ 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);