net/ncsi: Allow to extend NCSI request properties
[cascardo/linux.git] / mm / mmap.c
index 234edff..ca9d91b 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -25,6 +25,7 @@
 #include <linux/personality.h>
 #include <linux/security.h>
 #include <linux/hugetlb.h>
+#include <linux/shmem_fs.h>
 #include <linux/profile.h>
 #include <linux/export.h>
 #include <linux/mount.h>
@@ -620,7 +621,6 @@ int vma_adjust(struct vm_area_struct *vma, unsigned long start,
 {
        struct mm_struct *mm = vma->vm_mm;
        struct vm_area_struct *next = vma->vm_next;
-       struct vm_area_struct *importer = NULL;
        struct address_space *mapping = NULL;
        struct rb_root *root = NULL;
        struct anon_vma *anon_vma = NULL;
@@ -630,17 +630,25 @@ int vma_adjust(struct vm_area_struct *vma, unsigned long start,
        int remove_next = 0;
 
        if (next && !insert) {
-               struct vm_area_struct *exporter = NULL;
+               struct vm_area_struct *exporter = NULL, *importer = NULL;
 
                if (end >= next->vm_end) {
                        /*
                         * vma expands, overlapping all the next, and
                         * perhaps the one after too (mprotect case 6).
                         */
-again:                 remove_next = 1 + (end > next->vm_end);
+                       remove_next = 1 + (end > next->vm_end);
                        end = next->vm_end;
                        exporter = next;
                        importer = vma;
+
+                       /*
+                        * If next doesn't have anon_vma, import from vma after
+                        * next, if the vma overlaps with it.
+                        */
+                       if (remove_next == 2 && next && !next->anon_vma)
+                               exporter = next->vm_next;
+
                } else if (end > next->vm_start) {
                        /*
                         * vma expands, overlapping part of the next:
@@ -674,6 +682,8 @@ again:                      remove_next = 1 + (end > next->vm_end);
                                return error;
                }
        }
+again:
+       vma_adjust_trans_huge(vma, start, end, adjust_next);
 
        if (file) {
                mapping = file->f_mapping;
@@ -695,8 +705,6 @@ again:                      remove_next = 1 + (end > next->vm_end);
                }
        }
 
-       vma_adjust_trans_huge(vma, start, end, adjust_next);
-
        anon_vma = vma->anon_vma;
        if (!anon_vma && adjust_next)
                anon_vma = next->anon_vma;
@@ -795,8 +803,11 @@ again:                     remove_next = 1 + (end > next->vm_end);
                 * up the code too much to do both in one go.
                 */
                next = vma->vm_next;
-               if (remove_next == 2)
+               if (remove_next == 2) {
+                       remove_next = 1;
+                       end = next->vm_end;
                        goto again;
+               }
                else if (next)
                        vma_gap_update(next);
                else
@@ -1897,8 +1908,19 @@ get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
                return -ENOMEM;
 
        get_area = current->mm->get_unmapped_area;
-       if (file && file->f_op->get_unmapped_area)
-               get_area = file->f_op->get_unmapped_area;
+       if (file) {
+               if (file->f_op->get_unmapped_area)
+                       get_area = file->f_op->get_unmapped_area;
+       } else if (flags & MAP_SHARED) {
+               /*
+                * mmap_region() will call shmem_zero_setup() to create a file,
+                * so use shmem's get_unmapped_area in case it can be huge.
+                * do_mmap_pgoff() will clear pgoff, so match alignment.
+                */
+               pgoff = 0;
+               get_area = shmem_get_unmapped_area;
+       }
+
        addr = get_area(file, addr, len, pgoff, flags);
        if (IS_ERR_VALUE(addr))
                return addr;
@@ -2591,6 +2613,12 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
                /* drop PG_Mlocked flag for over-mapped range */
                for (tmp = vma; tmp->vm_start >= start + size;
                                tmp = tmp->vm_next) {
+                       /*
+                        * Split pmd and munlock page on the border
+                        * of the range.
+                        */
+                       vma_adjust_trans_huge(tmp, start, start + size, 0);
+
                        munlock_vma_pages_range(tmp,
                                        max(tmp->vm_start, start),
                                        min(tmp->vm_end, start + size));
@@ -2625,16 +2653,18 @@ static inline void verify_mm_writelocked(struct mm_struct *mm)
  *  anonymous maps.  eventually we may be able to do some
  *  brk-specific accounting here.
  */
-static int do_brk(unsigned long addr, unsigned long len)
+static int do_brk(unsigned long addr, unsigned long request)
 {
        struct mm_struct *mm = current->mm;
        struct vm_area_struct *vma, *prev;
-       unsigned long flags;
+       unsigned long flags, len;
        struct rb_node **rb_link, *rb_parent;
        pgoff_t pgoff = addr >> PAGE_SHIFT;
        int error;
 
-       len = PAGE_ALIGN(len);
+       len = PAGE_ALIGN(request);
+       if (len < request)
+               return -ENOMEM;
        if (!len)
                return 0;