userfaultfd: release page in error path to avoid BUG_ON
[linux-2.6-microblaze.git] / mm / userfaultfd.c
index 512576e..e14b382 100644 (file)
@@ -56,7 +56,6 @@ static int mcopy_atomic_pte(struct mm_struct *dst_mm,
                            struct page **pagep,
                            bool wp_copy)
 {
-       struct mem_cgroup *memcg;
        pte_t _dst_pte, *dst_pte;
        spinlock_t *ptl;
        void *page_kaddr;
@@ -77,7 +76,7 @@ static int mcopy_atomic_pte(struct mm_struct *dst_mm,
                                     PAGE_SIZE);
                kunmap_atomic(page_kaddr);
 
-               /* fallback to copy_from_user outside mmap_sem */
+               /* fallback to copy_from_user outside mmap_lock */
                if (unlikely(ret)) {
                        ret = -ENOENT;
                        *pagep = page;
@@ -97,7 +96,7 @@ static int mcopy_atomic_pte(struct mm_struct *dst_mm,
        __SetPageUptodate(page);
 
        ret = -ENOMEM;
-       if (mem_cgroup_try_charge(page, dst_mm, GFP_KERNEL, &memcg, false))
+       if (mem_cgroup_charge(page, dst_mm, GFP_KERNEL))
                goto out_release;
 
        _dst_pte = pte_mkdirty(mk_pte(page, dst_vma->vm_page_prot));
@@ -124,8 +123,7 @@ static int mcopy_atomic_pte(struct mm_struct *dst_mm,
 
        inc_mm_counter(dst_mm, MM_ANONPAGES);
        page_add_new_anon_rmap(page, dst_vma, dst_addr, false);
-       mem_cgroup_commit_charge(page, memcg, false, false);
-       lru_cache_add_active_or_unevictable(page, dst_vma);
+       lru_cache_add_inactive_or_unevictable(page, dst_vma);
 
        set_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte);
 
@@ -138,7 +136,6 @@ out:
        return ret;
 out_release_uncharge_unlock:
        pte_unmap_unlock(dst_pte, ptl);
-       mem_cgroup_cancel_charge(page, memcg, false);
 out_release:
        put_page(page);
        goto out;
@@ -203,14 +200,14 @@ static pmd_t *mm_alloc_pmd(struct mm_struct *mm, unsigned long address)
 #ifdef CONFIG_HUGETLB_PAGE
 /*
  * __mcopy_atomic processing for HUGETLB vmas.  Note that this routine is
- * called with mmap_sem held, it will release mmap_sem before returning.
+ * called with mmap_lock held, it will release mmap_lock before returning.
  */
 static __always_inline ssize_t __mcopy_atomic_hugetlb(struct mm_struct *dst_mm,
                                              struct vm_area_struct *dst_vma,
                                              unsigned long dst_start,
                                              unsigned long src_start,
                                              unsigned long len,
-                                             bool zeropage)
+                                             enum mcopy_atomic_mode mode)
 {
        int vm_alloc_shared = dst_vma->vm_flags & VM_SHARED;
        int vm_shared = dst_vma->vm_flags & VM_SHARED;
@@ -230,8 +227,8 @@ static __always_inline ssize_t __mcopy_atomic_hugetlb(struct mm_struct *dst_mm,
         * by THP.  Since we can not reliably insert a zero page, this
         * feature is not supported.
         */
-       if (zeropage) {
-               up_read(&dst_mm->mmap_sem);
+       if (mode == MCOPY_ATOMIC_ZEROPAGE) {
+               mmap_read_unlock(dst_mm);
                return -EINVAL;
        }
 
@@ -250,7 +247,7 @@ static __always_inline ssize_t __mcopy_atomic_hugetlb(struct mm_struct *dst_mm,
 
 retry:
        /*
-        * On routine entry dst_vma is set.  If we had to drop mmap_sem and
+        * On routine entry dst_vma is set.  If we had to drop mmap_lock and
         * retry, dst_vma will be set to NULL and we must lookup again.
         */
        if (!dst_vma) {
@@ -276,8 +273,6 @@ retry:
        }
 
        while (src_addr < src_start + len) {
-               pte_t dst_pteval;
-
                BUG_ON(dst_addr >= dst_start + len);
 
                /*
@@ -293,23 +288,23 @@ retry:
                mutex_lock(&hugetlb_fault_mutex_table[hash]);
 
                err = -ENOMEM;
-               dst_pte = huge_pte_alloc(dst_mm, dst_addr, vma_hpagesize);
+               dst_pte = huge_pte_alloc(dst_mm, dst_vma, dst_addr, vma_hpagesize);
                if (!dst_pte) {
                        mutex_unlock(&hugetlb_fault_mutex_table[hash]);
                        i_mmap_unlock_read(mapping);
                        goto out_unlock;
                }
 
-               err = -EEXIST;
-               dst_pteval = huge_ptep_get(dst_pte);
-               if (!huge_pte_none(dst_pteval)) {
+               if (mode != MCOPY_ATOMIC_CONTINUE &&
+                   !huge_pte_none(huge_ptep_get(dst_pte))) {
+                       err = -EEXIST;
                        mutex_unlock(&hugetlb_fault_mutex_table[hash]);
                        i_mmap_unlock_read(mapping);
                        goto out_unlock;
                }
 
                err = hugetlb_mcopy_atomic_pte(dst_mm, dst_pte, dst_vma,
-                                               dst_addr, src_addr, &page);
+                                              dst_addr, src_addr, mode, &page);
 
                mutex_unlock(&hugetlb_fault_mutex_table[hash]);
                i_mmap_unlock_read(mapping);
@@ -318,7 +313,7 @@ retry:
                cond_resched();
 
                if (unlikely(err == -ENOENT)) {
-                       up_read(&dst_mm->mmap_sem);
+                       mmap_read_unlock(dst_mm);
                        BUG_ON(!page);
 
                        err = copy_huge_page_from_user(page,
@@ -329,7 +324,7 @@ retry:
                                err = -EFAULT;
                                goto out;
                        }
-                       down_read(&dst_mm->mmap_sem);
+                       mmap_read_lock(dst_mm);
 
                        dst_vma = NULL;
                        goto retry;
@@ -349,7 +344,7 @@ retry:
        }
 
 out_unlock:
-       up_read(&dst_mm->mmap_sem);
+       mmap_read_unlock(dst_mm);
 out:
        if (page) {
                /*
@@ -360,7 +355,7 @@ out:
                 * private and shared mappings.  See the routine
                 * restore_reserve_on_error for details.  Unfortunately, we
                 * can not call restore_reserve_on_error now as it would
-                * require holding mmap_sem.
+                * require holding mmap_lock.
                 *
                 * If a reservation for the page existed in the reservation
                 * map of a private mapping, the map was modified to indicate
@@ -411,7 +406,7 @@ extern ssize_t __mcopy_atomic_hugetlb(struct mm_struct *dst_mm,
                                      unsigned long dst_start,
                                      unsigned long src_start,
                                      unsigned long len,
-                                     bool zeropage);
+                                     enum mcopy_atomic_mode mode);
 #endif /* CONFIG_HUGETLB_PAGE */
 
 static __always_inline ssize_t mfill_atomic_pte(struct mm_struct *dst_mm,
@@ -461,7 +456,7 @@ static __always_inline ssize_t __mcopy_atomic(struct mm_struct *dst_mm,
                                              unsigned long dst_start,
                                              unsigned long src_start,
                                              unsigned long len,
-                                             bool zeropage,
+                                             enum mcopy_atomic_mode mcopy_mode,
                                              bool *mmap_changing,
                                              __u64 mode)
 {
@@ -472,6 +467,7 @@ static __always_inline ssize_t __mcopy_atomic(struct mm_struct *dst_mm,
        long copied;
        struct page *page;
        bool wp_copy;
+       bool zeropage = (mcopy_mode == MCOPY_ATOMIC_ZEROPAGE);
 
        /*
         * Sanitize the command parameters:
@@ -488,7 +484,7 @@ static __always_inline ssize_t __mcopy_atomic(struct mm_struct *dst_mm,
        copied = 0;
        page = NULL;
 retry:
-       down_read(&dst_mm->mmap_sem);
+       mmap_read_lock(dst_mm);
 
        /*
         * If memory mappings are changing because of non-cooperative
@@ -530,10 +526,12 @@ retry:
         */
        if (is_vm_hugetlb_page(dst_vma))
                return  __mcopy_atomic_hugetlb(dst_mm, dst_vma, dst_start,
-                                               src_start, len, zeropage);
+                                               src_start, len, mcopy_mode);
 
        if (!vma_is_anonymous(dst_vma) && !vma_is_shmem(dst_vma))
                goto out_unlock;
+       if (mcopy_mode == MCOPY_ATOMIC_CONTINUE)
+               goto out_unlock;
 
        /*
         * Ensure the dst_vma has a anon_vma or this page
@@ -586,7 +584,7 @@ retry:
                if (unlikely(err == -ENOENT)) {
                        void *page_kaddr;
 
-                       up_read(&dst_mm->mmap_sem);
+                       mmap_read_unlock(dst_mm);
                        BUG_ON(!page);
 
                        page_kaddr = kmap(page);
@@ -615,7 +613,7 @@ retry:
        }
 
 out_unlock:
-       up_read(&dst_mm->mmap_sem);
+       mmap_read_unlock(dst_mm);
 out:
        if (page)
                put_page(page);
@@ -629,14 +627,22 @@ ssize_t mcopy_atomic(struct mm_struct *dst_mm, unsigned long dst_start,
                     unsigned long src_start, unsigned long len,
                     bool *mmap_changing, __u64 mode)
 {
-       return __mcopy_atomic(dst_mm, dst_start, src_start, len, false,
-                             mmap_changing, mode);
+       return __mcopy_atomic(dst_mm, dst_start, src_start, len,
+                             MCOPY_ATOMIC_NORMAL, mmap_changing, mode);
 }
 
 ssize_t mfill_zeropage(struct mm_struct *dst_mm, unsigned long start,
                       unsigned long len, bool *mmap_changing)
 {
-       return __mcopy_atomic(dst_mm, start, 0, len, true, mmap_changing, 0);
+       return __mcopy_atomic(dst_mm, start, 0, len, MCOPY_ATOMIC_ZEROPAGE,
+                             mmap_changing, 0);
+}
+
+ssize_t mcopy_continue(struct mm_struct *dst_mm, unsigned long start,
+                      unsigned long len, bool *mmap_changing)
+{
+       return __mcopy_atomic(dst_mm, start, 0, len, MCOPY_ATOMIC_CONTINUE,
+                             mmap_changing, 0);
 }
 
 int mwriteprotect_range(struct mm_struct *dst_mm, unsigned long start,
@@ -655,7 +661,7 @@ int mwriteprotect_range(struct mm_struct *dst_mm, unsigned long start,
        /* Does the address range wrap, or is the span zero-sized? */
        BUG_ON(start + len <= start);
 
-       down_read(&dst_mm->mmap_sem);
+       mmap_read_lock(dst_mm);
 
        /*
         * If memory mappings are changing because of non-cooperative
@@ -689,6 +695,6 @@ int mwriteprotect_range(struct mm_struct *dst_mm, unsigned long start,
 
        err = 0;
 out_unlock:
-       up_read(&dst_mm->mmap_sem);
+       mmap_read_unlock(dst_mm);
        return err;
 }