]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - mm/fremap.c
mm: merge populate and nopage into fault (fixes nonlinear)
[mirror_ubuntu-zesty-kernel.git] / mm / fremap.c
index 4e3f53dd5fd448d20a23296dfb398dd1d281fee0..01e51f01b84e8c1aa83519347d3b3ea04db570c4 100644 (file)
@@ -126,6 +126,25 @@ out:
        return err;
 }
 
+static int populate_range(struct mm_struct *mm, struct vm_area_struct *vma,
+                       unsigned long addr, unsigned long size, pgoff_t pgoff)
+{
+       int err;
+
+       do {
+               err = install_file_pte(mm, vma, addr, pgoff, vma->vm_page_prot);
+               if (err)
+                       return err;
+
+               size -= PAGE_SIZE;
+               addr += PAGE_SIZE;
+               pgoff++;
+       } while (size);
+
+        return 0;
+
+}
+
 /***
  * sys_remap_file_pages - remap arbitrary pages of a shared backing store
  *                        file within an existing vma.
@@ -183,41 +202,63 @@ asmlinkage long sys_remap_file_pages(unsigned long start, unsigned long size,
         * the single existing vma.  vm_private_data is used as a
         * swapout cursor in a VM_NONLINEAR vma.
         */
-       if (vma && (vma->vm_flags & VM_SHARED) &&
-               (!vma->vm_private_data || (vma->vm_flags & VM_NONLINEAR)) &&
-               vma->vm_ops && vma->vm_ops->populate &&
-                       end > start && start >= vma->vm_start &&
-                               end <= vma->vm_end) {
-
-               /* Must set VM_NONLINEAR before any pages are populated. */
-               if (pgoff != linear_page_index(vma, start) &&
-                   !(vma->vm_flags & VM_NONLINEAR)) {
-                       if (!has_write_lock) {
-                               up_read(&mm->mmap_sem);
-                               down_write(&mm->mmap_sem);
-                               has_write_lock = 1;
-                               goto retry;
+       if (!vma || !(vma->vm_flags & VM_SHARED))
+               goto out;
+
+       if (vma->vm_private_data && !(vma->vm_flags & VM_NONLINEAR))
+               goto out;
+
+       if ((!vma->vm_ops || !vma->vm_ops->populate) &&
+                                       !(vma->vm_flags & VM_CAN_NONLINEAR))
+               goto out;
+
+       if (end <= start || start < vma->vm_start || end > vma->vm_end)
+               goto out;
+
+       /* Must set VM_NONLINEAR before any pages are populated. */
+       if (!(vma->vm_flags & VM_NONLINEAR)) {
+               /* Don't need a nonlinear mapping, exit success */
+               if (pgoff == linear_page_index(vma, start)) {
+                       err = 0;
+                       goto out;
+               }
+
+               if (!has_write_lock) {
+                       up_read(&mm->mmap_sem);
+                       down_write(&mm->mmap_sem);
+                       has_write_lock = 1;
+                       goto retry;
+               }
+               mapping = vma->vm_file->f_mapping;
+               spin_lock(&mapping->i_mmap_lock);
+               flush_dcache_mmap_lock(mapping);
+               vma->vm_flags |= VM_NONLINEAR;
+               vma_prio_tree_remove(vma, &mapping->i_mmap);
+               vma_nonlinear_insert(vma, &mapping->i_mmap_nonlinear);
+               flush_dcache_mmap_unlock(mapping);
+               spin_unlock(&mapping->i_mmap_lock);
+       }
+
+       if (vma->vm_flags & VM_CAN_NONLINEAR) {
+               err = populate_range(mm, vma, start, size, pgoff);
+               if (!err && !(flags & MAP_NONBLOCK)) {
+                       if (unlikely(has_write_lock)) {
+                               downgrade_write(&mm->mmap_sem);
+                               has_write_lock = 0;
                        }
-                       mapping = vma->vm_file->f_mapping;
-                       spin_lock(&mapping->i_mmap_lock);
-                       flush_dcache_mmap_lock(mapping);
-                       vma->vm_flags |= VM_NONLINEAR;
-                       vma_prio_tree_remove(vma, &mapping->i_mmap);
-                       vma_nonlinear_insert(vma, &mapping->i_mmap_nonlinear);
-                       flush_dcache_mmap_unlock(mapping);
-                       spin_unlock(&mapping->i_mmap_lock);
+                       make_pages_present(start, start+size);
                }
+       } else
+               err = vma->vm_ops->populate(vma, start, size, vma->vm_page_prot,
+                                               pgoff, flags & MAP_NONBLOCK);
 
-               err = vma->vm_ops->populate(vma, start, size,
-                                           vma->vm_page_prot,
-                                           pgoff, flags & MAP_NONBLOCK);
+       /*
+        * We can't clear VM_NONLINEAR because we'd have to do
+        * it after ->populate completes, and that would prevent
+        * downgrading the lock.  (Locks can't be upgraded).
+        */
 
-               /*
-                * We can't clear VM_NONLINEAR because we'd have to do
-                * it after ->populate completes, and that would prevent
-                * downgrading the lock.  (Locks can't be upgraded).
-                */
-       }
+out:
        if (likely(!has_write_lock))
                up_read(&mm->mmap_sem);
        else