Merge branch 'sched-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[sfrench/cifs-2.6.git] / mm / mempolicy.c
index e08c94170ae4f66393b158525642992a4c242231..067cf7d3daf5af6126f90e481a154392e724559c 100644 (file)
@@ -410,7 +410,9 @@ struct queue_pages {
        struct list_head *pagelist;
        unsigned long flags;
        nodemask_t *nmask;
-       struct vm_area_struct *prev;
+       unsigned long start;
+       unsigned long end;
+       struct vm_area_struct *first;
 };
 
 /*
@@ -618,6 +620,22 @@ static int queue_pages_test_walk(unsigned long start, unsigned long end,
        unsigned long endvma = vma->vm_end;
        unsigned long flags = qp->flags;
 
+       /* range check first */
+       VM_BUG_ON((vma->vm_start > start) || (vma->vm_end < end));
+
+       if (!qp->first) {
+               qp->first = vma;
+               if (!(flags & MPOL_MF_DISCONTIG_OK) &&
+                       (qp->start < vma->vm_start))
+                       /* hole at head side of range */
+                       return -EFAULT;
+       }
+       if (!(flags & MPOL_MF_DISCONTIG_OK) &&
+               ((vma->vm_end < qp->end) &&
+               (!vma->vm_next || vma->vm_end < vma->vm_next->vm_start)))
+               /* hole at middle or tail of range */
+               return -EFAULT;
+
        /*
         * Need check MPOL_MF_STRICT to return -EIO if possible
         * regardless of vma_migratable
@@ -628,17 +646,6 @@ static int queue_pages_test_walk(unsigned long start, unsigned long end,
 
        if (endvma > end)
                endvma = end;
-       if (vma->vm_start > start)
-               start = vma->vm_start;
-
-       if (!(flags & MPOL_MF_DISCONTIG_OK)) {
-               if (!vma->vm_next && vma->vm_end < end)
-                       return -EFAULT;
-               if (qp->prev && qp->prev->vm_end < vma->vm_start)
-                       return -EFAULT;
-       }
-
-       qp->prev = vma;
 
        if (flags & MPOL_MF_LAZY) {
                /* Similar to task_numa_work, skip inaccessible VMAs */
@@ -681,14 +688,23 @@ queue_pages_range(struct mm_struct *mm, unsigned long start, unsigned long end,
                nodemask_t *nodes, unsigned long flags,
                struct list_head *pagelist)
 {
+       int err;
        struct queue_pages qp = {
                .pagelist = pagelist,
                .flags = flags,
                .nmask = nodes,
-               .prev = NULL,
+               .start = start,
+               .end = end,
+               .first = NULL,
        };
 
-       return walk_page_range(mm, start, end, &queue_pages_walk_ops, &qp);
+       err = walk_page_range(mm, start, end, &queue_pages_walk_ops, &qp);
+
+       if (!qp.first)
+               /* whole range in hole */
+               err = -EFAULT;
+
+       return err;
 }
 
 /*
@@ -740,8 +756,7 @@ static int mbind_range(struct mm_struct *mm, unsigned long start,
        unsigned long vmend;
 
        vma = find_vma(mm, start);
-       if (!vma || vma->vm_start > start)
-               return -EFAULT;
+       VM_BUG_ON(!vma);
 
        prev = vma->vm_prev;
        if (start > vma->vm_start)