Fix the deadlock between uvn_io() and uvn_flush() by restarting the fault.
authormpi <mpi@openbsd.org>
Tue, 12 Oct 2021 07:38:22 +0000 (07:38 +0000)
committermpi <mpi@openbsd.org>
Tue, 12 Oct 2021 07:38:22 +0000 (07:38 +0000)
Do not allow a faulting thread to sleep on a contended vnode lock to prevent
lock ordering issues with upcoming per-uobj lock.

Also reduce the sleep value for VM_PAGER_AGAIN from 1sec to 5nsec to not add
visible slowdown when starting a multi-threaded application with threads that
fault on the same vnode (chromium, firefox, etc).

Tested by anton@, tb@, robert@ and gnezdo@

ok anton@, tb@

Reported-by: syzbot+e63407b35dff08dbee02@syzkaller.appspotmail.com
sys/uvm/uvm_fault.c
sys/uvm/uvm_pager.h
sys/uvm/uvm_vnode.c

index 6660d05..c90d9b3 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: uvm_fault.c,v 1.120 2021/03/26 13:40:05 mpi Exp $     */
+/*     $OpenBSD: uvm_fault.c,v 1.121 2021/10/12 07:38:22 mpi Exp $     */
 /*     $NetBSD: uvm_fault.c,v 1.51 2000/08/06 00:22:53 thorpej Exp $   */
 
 /*
@@ -1260,7 +1260,7 @@ uvm_fault_lower(struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
 
                        if (result == VM_PAGER_AGAIN) {
                                tsleep_nsec(&nowake, PVM, "fltagain2",
-                                   SEC_TO_NSEC(1));
+                                   MSEC_TO_NSEC(5));
                                return ERESTART;
                        }
 
index ceafdbe..3e49465 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: uvm_pager.h,v 1.32 2021/03/12 14:15:49 jsg Exp $      */
+/*     $OpenBSD: uvm_pager.h,v 1.33 2021/10/12 07:38:22 mpi Exp $      */
 /*     $NetBSD: uvm_pager.h,v 1.20 2000/11/27 08:40:05 chs Exp $       */
 
 /*
@@ -111,6 +111,7 @@ struct uvm_pagerops {
 #define PGO_LOCKED     0x040   /* fault data structures are locked [get] */
 #define PGO_PDFREECLUST        0x080   /* daemon's free cluster flag [uvm_pager_put] */
 #define PGO_REALLOCSWAP        0x100   /* reallocate swap area [pager_dropcluster] */
+#define PGO_NOWAIT     0x200   /* do not wait for inode lock */
 
 /* page we are not interested in getting */
 #define PGO_DONTCARE ((struct vm_page *) -1L)  /* [get only] */
index 9c70fb4..5fc969c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: uvm_vnode.c,v 1.115 2021/10/12 07:37:42 mpi Exp $     */
+/*     $OpenBSD: uvm_vnode.c,v 1.116 2021/10/12 07:38:22 mpi Exp $     */
 /*     $NetBSD: uvm_vnode.c,v 1.36 2000/11/24 20:34:01 chs Exp $       */
 
 /*
@@ -1060,7 +1060,7 @@ uvn_get(struct uvm_object *uobj, voff_t offset, struct vm_page **pps,
                 * I/O to fill it with valid data.
                 */
                result = uvn_io((struct uvm_vnode *) uobj, &ptmp, 1,
-                   PGO_SYNCIO, UIO_READ);
+                   PGO_SYNCIO|PGO_NOWAIT, UIO_READ);
 
                /*
                 * I/O done.  because we used syncio the result can not be
@@ -1120,6 +1120,7 @@ uvn_io(struct uvm_vnode *uvn, vm_page_t *pps, int npages, int flags, int rw)
        int waitf, result, mapinflags;
        size_t got, wanted;
        int netunlocked = 0;
+       int lkflags = (flags & PGO_NOWAIT) ? LK_NOWAIT : 0;
 
        /* init values */
        waitf = (flags & PGO_SYNCIO) ? M_WAITOK : M_NOWAIT;
@@ -1199,8 +1200,7 @@ uvn_io(struct uvm_vnode *uvn, vm_page_t *pps, int npages, int flags, int rw)
         */
        result = 0;
        if ((uvn->u_flags & UVM_VNODE_VNISLOCKED) == 0)
-               result = vn_lock(vn, LK_EXCLUSIVE | LK_RECURSEFAIL);
-
+               result = vn_lock(vn, LK_EXCLUSIVE | LK_RECURSEFAIL | lkflags);
        if (result == 0) {
                /* NOTE: vnode now locked! */
                if (rw == UIO_READ)
@@ -1247,11 +1247,14 @@ uvn_io(struct uvm_vnode *uvn, vm_page_t *pps, int npages, int flags, int rw)
 
        if (result == 0) {
                return VM_PAGER_OK;
+       } else if (result == EBUSY) {
+               KASSERT(flags & PGO_NOWAIT);
+               return VM_PAGER_AGAIN;
        } else {
                while (rebooting)
                        tsleep_nsec(&rebooting, PVM, "uvndead", INFSLP);
                return VM_PAGER_ERROR;
-       }
+       }
 }
 
 /*