mm/madvise: reduce code duplication in error handling paths
[linux-2.6-block.git] / mm / madvise.c
index 968df3aa069fda3ca88121769ddad04e7390fe6e..68ab988ad4333b43ae6102d147e94891097c0ae6 100644 (file)
 #include <linux/userfaultfd_k.h>
 #include <linux/hugetlb.h>
 #include <linux/falloc.h>
+#include <linux/fadvise.h>
 #include <linux/sched.h>
 #include <linux/ksm.h>
 #include <linux/fs.h>
 #include <linux/file.h>
 #include <linux/blkdev.h>
 #include <linux/backing-dev.h>
+#include <linux/pagewalk.h>
 #include <linux/swap.h>
 #include <linux/swapops.h>
 #include <linux/shmem_fs.h>
@@ -105,28 +107,14 @@ static long madvise_behavior(struct vm_area_struct *vma,
        case MADV_MERGEABLE:
        case MADV_UNMERGEABLE:
                error = ksm_madvise(vma, start, end, behavior, &new_flags);
-               if (error) {
-                       /*
-                        * madvise() returns EAGAIN if kernel resources, such as
-                        * slab, are temporarily unavailable.
-                        */
-                       if (error == -ENOMEM)
-                               error = -EAGAIN;
-                       goto out;
-               }
+               if (error)
+                       goto out_convert_errno;
                break;
        case MADV_HUGEPAGE:
        case MADV_NOHUGEPAGE:
                error = hugepage_madvise(vma, &new_flags, behavior);
-               if (error) {
-                       /*
-                        * madvise() returns EAGAIN if kernel resources, such as
-                        * slab, are temporarily unavailable.
-                        */
-                       if (error == -ENOMEM)
-                               error = -EAGAIN;
-                       goto out;
-               }
+               if (error)
+                       goto out_convert_errno;
                break;
        }
 
@@ -152,15 +140,8 @@ static long madvise_behavior(struct vm_area_struct *vma,
                        goto out;
                }
                error = __split_vma(mm, vma, start, 1);
-               if (error) {
-                       /*
-                        * madvise() returns EAGAIN if kernel resources, such as
-                        * slab, are temporarily unavailable.
-                        */
-                       if (error == -ENOMEM)
-                               error = -EAGAIN;
-                       goto out;
-               }
+               if (error)
+                       goto out_convert_errno;
        }
 
        if (end != vma->vm_end) {
@@ -169,15 +150,8 @@ static long madvise_behavior(struct vm_area_struct *vma,
                        goto out;
                }
                error = __split_vma(mm, vma, end, 0);
-               if (error) {
-                       /*
-                        * madvise() returns EAGAIN if kernel resources, such as
-                        * slab, are temporarily unavailable.
-                        */
-                       if (error == -ENOMEM)
-                               error = -EAGAIN;
-                       goto out;
-               }
+               if (error)
+                       goto out_convert_errno;
        }
 
 success:
@@ -185,6 +159,14 @@ success:
         * vm_flags is protected by the mmap_sem held in write mode.
         */
        vma->vm_flags = new_flags;
+
+out_convert_errno:
+       /*
+        * madvise() returns EAGAIN if kernel resources, such as
+        * slab, are temporarily unavailable.
+        */
+       if (error == -ENOMEM)
+               error = -EAGAIN;
 out:
        return error;
 }
@@ -225,19 +207,9 @@ static int swapin_walk_pmd_entry(pmd_t *pmd, unsigned long start,
        return 0;
 }
 
-static void force_swapin_readahead(struct vm_area_struct *vma,
-               unsigned long start, unsigned long end)
-{
-       struct mm_walk walk = {
-               .mm = vma->vm_mm,
-               .pmd_entry = swapin_walk_pmd_entry,
-               .private = vma,
-       };
-
-       walk_page_range(start, end, &walk);
-
-       lru_add_drain();        /* Push any new pages onto the LRU now */
-}
+static const struct mm_walk_ops swapin_walk_ops = {
+       .pmd_entry              = swapin_walk_pmd_entry,
+};
 
 static void force_shm_swapin_readahead(struct vm_area_struct *vma,
                unsigned long start, unsigned long end,
@@ -275,11 +247,13 @@ static long madvise_willneed(struct vm_area_struct *vma,
                             unsigned long start, unsigned long end)
 {
        struct file *file = vma->vm_file;
+       loff_t offset;
 
        *prev = vma;
 #ifdef CONFIG_SWAP
        if (!file) {
-               force_swapin_readahead(vma, start, end);
+               walk_page_range(vma->vm_mm, start, end, &swapin_walk_ops, vma);
+               lru_add_drain(); /* Push any new pages onto the LRU now */
                return 0;
        }
 
@@ -298,12 +272,20 @@ static long madvise_willneed(struct vm_area_struct *vma,
                return 0;
        }
 
-       start = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
-       if (end > vma->vm_end)
-               end = vma->vm_end;
-       end = ((end - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
-
-       force_page_cache_readahead(file->f_mapping, file, start, end - start);
+       /*
+        * Filesystem's fadvise may need to take various locks.  We need to
+        * explicitly grab a reference because the vma (and hence the
+        * vma's reference to the file) can go away as soon as we drop
+        * mmap_sem.
+        */
+       *prev = NULL;   /* tell sys_madvise we drop mmap_sem */
+       get_file(file);
+       up_read(&current->mm->mmap_sem);
+       offset = (loff_t)(start - vma->vm_start)
+                       + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
+       vfs_fadvise(file, offset, end - start, POSIX_FADV_WILLNEED);
+       fput(file);
+       down_read(&current->mm->mmap_sem);
        return 0;
 }
 
@@ -440,20 +422,9 @@ next:
        return 0;
 }
 
-static void madvise_free_page_range(struct mmu_gather *tlb,
-                            struct vm_area_struct *vma,
-                            unsigned long addr, unsigned long end)
-{
-       struct mm_walk free_walk = {
-               .pmd_entry = madvise_free_pte_range,
-               .mm = vma->vm_mm,
-               .private = tlb,
-       };
-
-       tlb_start_vma(tlb, vma);
-       walk_page_range(addr, end, &free_walk);
-       tlb_end_vma(tlb, vma);
-}
+static const struct mm_walk_ops madvise_free_walk_ops = {
+       .pmd_entry              = madvise_free_pte_range,
+};
 
 static int madvise_free_single_vma(struct vm_area_struct *vma,
                        unsigned long start_addr, unsigned long end_addr)
@@ -480,7 +451,10 @@ static int madvise_free_single_vma(struct vm_area_struct *vma,
        update_hiwater_rss(mm);
 
        mmu_notifier_invalidate_range_start(&range);
-       madvise_free_page_range(&tlb, vma, range.start, range.end);
+       tlb_start_vma(&tlb, vma);
+       walk_page_range(vma->vm_mm, range.start, range.end,
+                       &madvise_free_walk_ops, &tlb);
+       tlb_end_vma(&tlb, vma);
        mmu_notifier_invalidate_range_end(&range);
        tlb_finish_mmu(&tlb, range.start, range.end);