mm/gup: allow VM_FAULT_RETRY for multiple times
[linux-2.6-microblaze.git] / mm / gup.c
index 4ae1a9b..efe9038 100644 (file)
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -868,7 +868,10 @@ static int faultin_page(struct task_struct *tsk, struct vm_area_struct *vma,
        if (*flags & FOLL_NOWAIT)
                fault_flags |= FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_RETRY_NOWAIT;
        if (*flags & FOLL_TRIED) {
-               VM_WARN_ON_ONCE(fault_flags & FAULT_FLAG_ALLOW_RETRY);
+               /*
+                * Note: FAULT_FLAG_ALLOW_RETRY and FAULT_FLAG_TRIED
+                * can co-exist
+                */
                fault_flags |= FAULT_FLAG_TRIED;
        }
 
@@ -1228,7 +1231,6 @@ retry:
                down_read(&mm->mmap_sem);
                if (!(fault_flags & FAULT_FLAG_TRIED)) {
                        *unlocked = true;
-                       fault_flags &= ~FAULT_FLAG_ALLOW_RETRY;
                        fault_flags |= FAULT_FLAG_TRIED;
                        goto retry;
                }
@@ -1312,17 +1314,30 @@ static __always_inline long __get_user_pages_locked(struct task_struct *tsk,
                if (likely(pages))
                        pages += ret;
                start += ret << PAGE_SHIFT;
+               lock_dropped = true;
 
+retry:
                /*
                 * Repeat on the address that fired VM_FAULT_RETRY
-                * without FAULT_FLAG_ALLOW_RETRY but with
-                * FAULT_FLAG_TRIED.
+                * with both FAULT_FLAG_ALLOW_RETRY and
+                * FAULT_FLAG_TRIED.  Note that GUP can be interrupted
+                * by fatal signals, so we need to check it before we
+                * start trying again otherwise it can loop forever.
                 */
+
+               if (fatal_signal_pending(current))
+                       break;
+
                *locked = 1;
-               lock_dropped = true;
                down_read(&mm->mmap_sem);
+
                ret = __get_user_pages(tsk, mm, start, 1, flags | FOLL_TRIED,
-                                      pages, NULL, NULL);
+                                      pages, NULL, locked);
+               if (!*locked) {
+                       /* Continue to retry until we succeeded */
+                       BUG_ON(ret != 0);
+                       goto retry;
+               }
                if (ret != 1) {
                        BUG_ON(ret > 1);
                        if (!pages_done)