These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / mm / mlock.c
index 6fd2cf1..d6006b1 100644 (file)
@@ -172,7 +172,7 @@ static void __munlock_isolation_failed(struct page *page)
  */
 unsigned int munlock_vma_page(struct page *page)
 {
-       unsigned int nr_pages;
+       int nr_pages;
        struct zone *zone = page_zone(page);
 
        /* For try_to_munlock() and to serialize with page migration */
@@ -422,7 +422,7 @@ static unsigned long __munlock_pagevec_fill(struct pagevec *pvec,
 void munlock_vma_pages_range(struct vm_area_struct *vma,
                             unsigned long start, unsigned long end)
 {
-       vma->vm_flags &= ~VM_LOCKED;
+       vma->vm_flags &= VM_LOCKED_CLEAR_MASK;
 
        while (start < end) {
                struct page *page = NULL;
@@ -506,11 +506,13 @@ static int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev,
 
        if (newflags == vma->vm_flags || (vma->vm_flags & VM_SPECIAL) ||
            is_vm_hugetlb_page(vma) || vma == get_gate_vma(current->mm))
-               goto out;       /* don't set VM_LOCKED,  don't count */
+               /* don't set VM_LOCKED or VM_LOCKONFAULT and don't count */
+               goto out;
 
        pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
        *prev = vma_merge(mm, *prev, start, end, newflags, vma->anon_vma,
-                         vma->vm_file, pgoff, vma_policy(vma));
+                         vma->vm_file, pgoff, vma_policy(vma),
+                         vma->vm_userfaultfd_ctx);
        if (*prev) {
                vma = *prev;
                goto success;
@@ -553,13 +555,14 @@ out:
        return ret;
 }
 
-static int do_mlock(unsigned long start, size_t len, int on)
+static int apply_vma_lock_flags(unsigned long start, size_t len,
+                               vm_flags_t flags)
 {
        unsigned long nstart, end, tmp;
        struct vm_area_struct * vma, * prev;
        int error;
 
-       VM_BUG_ON(start & ~PAGE_MASK);
+       VM_BUG_ON(offset_in_page(start));
        VM_BUG_ON(len != PAGE_ALIGN(len));
        end = start + len;
        if (end < start)
@@ -575,14 +578,11 @@ static int do_mlock(unsigned long start, size_t len, int on)
                prev = vma;
 
        for (nstart = start ; ; ) {
-               vm_flags_t newflags;
-
-               /* Here we know that  vma->vm_start <= nstart < vma->vm_end. */
+               vm_flags_t newflags = vma->vm_flags & VM_LOCKED_CLEAR_MASK;
 
-               newflags = vma->vm_flags & ~VM_LOCKED;
-               if (on)
-                       newflags |= VM_LOCKED;
+               newflags |= flags;
 
+               /* Here we know that  vma->vm_start <= nstart < vma->vm_end. */
                tmp = vma->vm_end;
                if (tmp > end)
                        tmp = end;
@@ -604,7 +604,7 @@ static int do_mlock(unsigned long start, size_t len, int on)
        return error;
 }
 
-SYSCALL_DEFINE2(mlock, unsigned long, start, size_t, len)
+static int do_mlock(unsigned long start, size_t len, vm_flags_t flags)
 {
        unsigned long locked;
        unsigned long lock_limit;
@@ -615,7 +615,7 @@ SYSCALL_DEFINE2(mlock, unsigned long, start, size_t, len)
 
        lru_add_drain_all();    /* flush pagevec */
 
-       len = PAGE_ALIGN(len + (start & ~PAGE_MASK));
+       len = PAGE_ALIGN(len + (offset_in_page(start)));
        start &= PAGE_MASK;
 
        lock_limit = rlimit(RLIMIT_MEMLOCK);
@@ -628,7 +628,7 @@ SYSCALL_DEFINE2(mlock, unsigned long, start, size_t, len)
 
        /* check against resource limits */
        if ((locked <= lock_limit) || capable(CAP_IPC_LOCK))
-               error = do_mlock(start, len, 1);
+               error = apply_vma_lock_flags(start, len, flags);
 
        up_write(&current->mm->mmap_sem);
        if (error)
@@ -640,37 +640,75 @@ SYSCALL_DEFINE2(mlock, unsigned long, start, size_t, len)
        return 0;
 }
 
+SYSCALL_DEFINE2(mlock, unsigned long, start, size_t, len)
+{
+       return do_mlock(start, len, VM_LOCKED);
+}
+
+SYSCALL_DEFINE3(mlock2, unsigned long, start, size_t, len, int, flags)
+{
+       vm_flags_t vm_flags = VM_LOCKED;
+
+       if (flags & ~MLOCK_ONFAULT)
+               return -EINVAL;
+
+       if (flags & MLOCK_ONFAULT)
+               vm_flags |= VM_LOCKONFAULT;
+
+       return do_mlock(start, len, vm_flags);
+}
+
 SYSCALL_DEFINE2(munlock, unsigned long, start, size_t, len)
 {
        int ret;
 
-       len = PAGE_ALIGN(len + (start & ~PAGE_MASK));
+       len = PAGE_ALIGN(len + (offset_in_page(start)));
        start &= PAGE_MASK;
 
        down_write(&current->mm->mmap_sem);
-       ret = do_mlock(start, len, 0);
+       ret = apply_vma_lock_flags(start, len, 0);
        up_write(&current->mm->mmap_sem);
 
        return ret;
 }
 
-static int do_mlockall(int flags)
+/*
+ * Take the MCL_* flags passed into mlockall (or 0 if called from munlockall)
+ * and translate into the appropriate modifications to mm->def_flags and/or the
+ * flags for all current VMAs.
+ *
+ * There are a couple of subtleties with this.  If mlockall() is called multiple
+ * times with different flags, the values do not necessarily stack.  If mlockall
+ * is called once including the MCL_FUTURE flag and then a second time without
+ * it, VM_LOCKED and VM_LOCKONFAULT will be cleared from mm->def_flags.
+ */
+static int apply_mlockall_flags(int flags)
 {
        struct vm_area_struct * vma, * prev = NULL;
+       vm_flags_t to_add = 0;
 
-       if (flags & MCL_FUTURE)
+       current->mm->def_flags &= VM_LOCKED_CLEAR_MASK;
+       if (flags & MCL_FUTURE) {
                current->mm->def_flags |= VM_LOCKED;
-       else
-               current->mm->def_flags &= ~VM_LOCKED;
-       if (flags == MCL_FUTURE)
-               goto out;
+
+               if (flags & MCL_ONFAULT)
+                       current->mm->def_flags |= VM_LOCKONFAULT;
+
+               if (!(flags & MCL_CURRENT))
+                       goto out;
+       }
+
+       if (flags & MCL_CURRENT) {
+               to_add |= VM_LOCKED;
+               if (flags & MCL_ONFAULT)
+                       to_add |= VM_LOCKONFAULT;
+       }
 
        for (vma = current->mm->mmap; vma ; vma = prev->vm_next) {
                vm_flags_t newflags;
 
-               newflags = vma->vm_flags & ~VM_LOCKED;
-               if (flags & MCL_CURRENT)
-                       newflags |= VM_LOCKED;
+               newflags = vma->vm_flags & VM_LOCKED_CLEAR_MASK;
+               newflags |= to_add;
 
                /* Ignore errors */
                mlock_fixup(vma, &prev, vma->vm_start, vma->vm_end, newflags);
@@ -683,14 +721,13 @@ out:
 SYSCALL_DEFINE1(mlockall, int, flags)
 {
        unsigned long lock_limit;
-       int ret = -EINVAL;
+       int ret;
 
-       if (!flags || (flags & ~(MCL_CURRENT | MCL_FUTURE)))
-               goto out;
+       if (!flags || (flags & ~(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT)))
+               return -EINVAL;
 
-       ret = -EPERM;
        if (!can_do_mlock())
-               goto out;
+               return -EPERM;
 
        if (flags & MCL_CURRENT)
                lru_add_drain_all();    /* flush pagevec */
@@ -703,11 +740,11 @@ SYSCALL_DEFINE1(mlockall, int, flags)
 
        if (!(flags & MCL_CURRENT) || (current->mm->total_vm <= lock_limit) ||
            capable(CAP_IPC_LOCK))
-               ret = do_mlockall(flags);
+               ret = apply_mlockall_flags(flags);
        up_write(&current->mm->mmap_sem);
        if (!ret && (flags & MCL_CURRENT))
                mm_populate(0, TASK_SIZE);
-out:
+
        return ret;
 }
 
@@ -716,7 +753,7 @@ SYSCALL_DEFINE0(munlockall)
        int ret;
 
        down_write(&current->mm->mmap_sem);
-       ret = do_mlockall(0);
+       ret = apply_mlockall_flags(0);
        up_write(&current->mm->mmap_sem);
        return ret;
 }