]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - mm/mlock.c
ARM: dts: imx6ul-tx6ul: disable DS1339 which is not normally equipped on the TXUL...
[karo-tx-linux.git] / mm / mlock.c
index 25936680064fd433d4cf693c88c3195bdab4bb98..0147b57f9704398f8f75dbb6d9c24ca5efedc6ce 100644 (file)
@@ -82,6 +82,9 @@ void mlock_vma_page(struct page *page)
        /* Serialize with page migration */
        BUG_ON(!PageLocked(page));
 
+       VM_BUG_ON_PAGE(PageTail(page), page);
+       VM_BUG_ON_PAGE(PageCompound(page) && PageDoubleMap(page), page);
+
        if (!TestSetPageMlocked(page)) {
                mod_zone_page_state(page_zone(page), NR_MLOCK,
                                    hpage_nr_pages(page));
@@ -178,6 +181,8 @@ unsigned int munlock_vma_page(struct page *page)
        /* For try_to_munlock() and to serialize with page migration */
        BUG_ON(!PageLocked(page));
 
+       VM_BUG_ON_PAGE(PageTail(page), page);
+
        /*
         * Serialize with any parallel __split_huge_page_refcount() which
         * might otherwise copy PageMlocked to part of the tail pages before
@@ -422,7 +427,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;
@@ -444,7 +449,10 @@ void munlock_vma_pages_range(struct vm_area_struct *vma,
                                &page_mask);
 
                if (page && !IS_ERR(page)) {
-                       if (PageTransHuge(page)) {
+                       if (PageTransTail(page)) {
+                               VM_BUG_ON_PAGE(PageMlocked(page), page);
+                               put_page(page); /* follow_page_mask() */
+                       } else if (PageTransHuge(page)) {
                                lock_page(page);
                                /*
                                 * Any THP page found by follow_page_mask() may
@@ -477,8 +485,6 @@ void munlock_vma_pages_range(struct vm_area_struct *vma,
                                goto next;
                        }
                }
-               /* It's a bug to munlock in the middle of a THP page */
-               VM_BUG_ON((start >> PAGE_SHIFT) & page_mask);
                page_increm = 1 + page_mask;
                start += page_increm * PAGE_SIZE;
 next:
@@ -506,7 +512,8 @@ 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,
@@ -554,13 +561,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)
@@ -576,14 +584,11 @@ static int do_mlock(unsigned long start, size_t len, int on)
                prev = vma;
 
        for (nstart = start ; ; ) {
-               vm_flags_t newflags;
+               vm_flags_t newflags = vma->vm_flags & VM_LOCKED_CLEAR_MASK;
 
-               /* Here we know that  vma->vm_start <= nstart < vma->vm_end. */
-
-               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;
@@ -605,7 +610,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;
@@ -616,7 +621,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);
@@ -629,7 +634,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)
@@ -641,37 +646,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);
@@ -684,14 +727,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 */
@@ -704,11 +746,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;
 }
 
@@ -717,7 +759,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;
 }