mm: allow GUP to fail instead of waiting on a page
authorGleb Natapov <gleb@redhat.com>
Tue, 22 Mar 2011 23:30:51 +0000 (16:30 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 23 Mar 2011 00:44:02 +0000 (17:44 -0700)
GUP user may want to try to acquire a reference to a page if it is already
in memory, but not if IO, to bring it in, is needed.  For example KVM may
tell vcpu to schedule another guest process if current one is trying to
access swapped out page.  Meanwhile, the page will be swapped in and the
guest process, that depends on it, will be able to run again.

This patch adds FAULT_FLAG_RETRY_NOWAIT (suggested by Linus) and
FOLL_NOWAIT follow_page flags.  FAULT_FLAG_RETRY_NOWAIT, when used in
conjunction with VM_FAULT_ALLOW_RETRY, indicates to handle_mm_fault that
it shouldn't drop mmap_sem and wait on a page, but return VM_FAULT_RETRY
instead.

[akpm@linux-foundation.org: improve FOLL_NOWAIT comment]
Signed-off-by: Gleb Natapov <gleb@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Hugh Dickins <hughd@google.com>
Acked-by: Rik van Riel <riel@redhat.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Avi Kivity <avi@redhat.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
include/linux/mm.h
mm/filemap.c
mm/memory.c

index 1f82adc8535256e7d87658e9b11afc8fb89a768c..901435e3a9a94200c7b70d554f5bf9e5a02b1e6e 100644 (file)
@@ -151,6 +151,7 @@ extern pgprot_t protection_map[16];
 #define FAULT_FLAG_NONLINEAR   0x02    /* Fault was via a nonlinear mapping */
 #define FAULT_FLAG_MKWRITE     0x04    /* Fault was mkwrite of existing pte */
 #define FAULT_FLAG_ALLOW_RETRY 0x08    /* Retry fault if blocking */
+#define FAULT_FLAG_RETRY_NOWAIT        0x10    /* Don't drop mmap_sem and wait when retrying */
 
 /*
  * This interface is used by x86 PAT code to identify a pfn mapping that is
@@ -1545,6 +1546,8 @@ struct page *follow_page(struct vm_area_struct *, unsigned long address,
 #define FOLL_GET       0x04    /* do get_page on page */
 #define FOLL_DUMP      0x08    /* give error on hole if it would be zero */
 #define FOLL_FORCE     0x10    /* get_user_pages read/write w/o permission */
+#define FOLL_NOWAIT    0x20    /* if a disk transfer is needed, start the IO
+                                * and return without waiting upon it */
 #define FOLL_MLOCK     0x40    /* mark page as mlocked */
 #define FOLL_SPLIT     0x80    /* don't return transhuge pages, split them */
 #define FOLL_HWPOISON  0x100   /* check page is hwpoisoned */
index 83a45d35468b961340fb19a958eee77d0e2e2297..312b6eb7843098834dc447d2d1bceb90e1dba170 100644 (file)
@@ -621,8 +621,10 @@ int __lock_page_or_retry(struct page *page, struct mm_struct *mm,
                __lock_page(page);
                return 1;
        } else {
-               up_read(&mm->mmap_sem);
-               wait_on_page_locked(page);
+               if (!(flags & FAULT_FLAG_RETRY_NOWAIT)) {
+                       up_read(&mm->mmap_sem);
+                       wait_on_page_locked(page);
+               }
                return 0;
        }
 }
index e48945ab362b87904d3a541e27902dc0d492cecb..615be5127ce1f2895da610978c7d73ec9789308d 100644 (file)
@@ -1569,6 +1569,8 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
                                        fault_flags |= FAULT_FLAG_WRITE;
                                if (nonblocking)
                                        fault_flags |= FAULT_FLAG_ALLOW_RETRY;
+                               if (foll_flags & FOLL_NOWAIT)
+                                       fault_flags |= (FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_RETRY_NOWAIT);
 
                                ret = handle_mm_fault(mm, vma, start,
                                                        fault_flags);
@@ -1595,7 +1597,8 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
                                        tsk->min_flt++;
 
                                if (ret & VM_FAULT_RETRY) {
-                                       *nonblocking = 0;
+                                       if (nonblocking)
+                                               *nonblocking = 0;
                                        return i;
                                }