Merge branches 'pm-cpuidle', 'pm-sleep' and 'pm-powercap'
[linux-block.git] / mm / gup.c
index af8edadc05d1b87200050bd34ff3d58ec52abd52..1611e73b1121b1b9031356ccac6b765c60327ba1 100644 (file)
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -1206,6 +1206,22 @@ static long __get_user_pages(struct mm_struct *mm,
 
                /* first iteration or cross vma bound */
                if (!vma || start >= vma->vm_end) {
+                       /*
+                        * MADV_POPULATE_(READ|WRITE) wants to handle VMA
+                        * lookups+error reporting differently.
+                        */
+                       if (gup_flags & FOLL_MADV_POPULATE) {
+                               vma = vma_lookup(mm, start);
+                               if (!vma) {
+                                       ret = -ENOMEM;
+                                       goto out;
+                               }
+                               if (check_vma_flags(vma, gup_flags)) {
+                                       ret = -EINVAL;
+                                       goto out;
+                               }
+                               goto retry;
+                       }
                        vma = gup_vma_lookup(mm, start);
                        if (!vma && in_gate_area(mm, start)) {
                                ret = get_gate_page(mm, start & PAGE_MASK,
@@ -1685,35 +1701,35 @@ long populate_vma_page_range(struct vm_area_struct *vma,
 }
 
 /*
- * faultin_vma_page_range() - populate (prefault) page tables inside the
- *                           given VMA range readable/writable
+ * faultin_page_range() - populate (prefault) page tables inside the
+ *                       given range readable/writable
  *
  * This takes care of mlocking the pages, too, if VM_LOCKED is set.
  *
- * @vma: target vma
+ * @mm: the mm to populate page tables in
  * @start: start address
  * @end: end address
  * @write: whether to prefault readable or writable
  * @locked: whether the mmap_lock is still held
  *
- * Returns either number of processed pages in the vma, or a negative error
- * code on error (see __get_user_pages()).
+ * Returns either number of processed pages in the MM, or a negative error
+ * code on error (see __get_user_pages()). Note that this function reports
+ * errors related to VMAs, such as incompatible mappings, as expected by
+ * MADV_POPULATE_(READ|WRITE).
  *
- * vma->vm_mm->mmap_lock must be held. The range must be page-aligned and
- * covered by the VMA. If it's released, *@locked will be set to 0.
+ * The range must be page-aligned.
+ *
+ * mm->mmap_lock must be held. If it's released, *@locked will be set to 0.
  */
-long faultin_vma_page_range(struct vm_area_struct *vma, unsigned long start,
-                           unsigned long end, bool write, int *locked)
+long faultin_page_range(struct mm_struct *mm, unsigned long start,
+                       unsigned long end, bool write, int *locked)
 {
-       struct mm_struct *mm = vma->vm_mm;
        unsigned long nr_pages = (end - start) / PAGE_SIZE;
        int gup_flags;
        long ret;
 
        VM_BUG_ON(!PAGE_ALIGNED(start));
        VM_BUG_ON(!PAGE_ALIGNED(end));
-       VM_BUG_ON_VMA(start < vma->vm_start, vma);
-       VM_BUG_ON_VMA(end > vma->vm_end, vma);
        mmap_assert_locked(mm);
 
        /*
@@ -1725,19 +1741,13 @@ long faultin_vma_page_range(struct vm_area_struct *vma, unsigned long start,
         *                a poisoned page.
         * !FOLL_FORCE: Require proper access permissions.
         */
-       gup_flags = FOLL_TOUCH | FOLL_HWPOISON | FOLL_UNLOCKABLE;
+       gup_flags = FOLL_TOUCH | FOLL_HWPOISON | FOLL_UNLOCKABLE |
+                   FOLL_MADV_POPULATE;
        if (write)
                gup_flags |= FOLL_WRITE;
 
-       /*
-        * We want to report -EINVAL instead of -EFAULT for any permission
-        * problems or incompatible mappings.
-        */
-       if (check_vma_flags(vma, gup_flags))
-               return -EINVAL;
-
-       ret = __get_user_pages(mm, start, nr_pages, gup_flags,
-                              NULL, locked);
+       ret = __get_user_pages_locked(mm, start, nr_pages, NULL, locked,
+                                     gup_flags);
        lru_add_drain();
        return ret;
 }