kernel - Fix devfs deadlock
authorMatthew Dillon <dillon@apollo.backplane.com>
Tue, 14 May 2013 17:44:36 +0000 (10:44 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Tue, 14 May 2013 17:46:39 +0000 (10:46 -0700)
* Fix a devfs deadlock against getnewvnode() due to the devfs master
  lock being held.

* Most evident on 32-bit builds because 64-bit builds don't recycle vnodes
  as quickly.

Reported-by: marino
Analysis-by: vsrinivas
sys/vfs/devfs/devfs_core.c

index 1610847..2ae4fd5 100644 (file)
@@ -292,9 +292,19 @@ devfs_allocv(struct vnode **vpp, struct devfs_node *node)
 
        KKASSERT(node);
 
 
        KKASSERT(node);
 
+       /*
+        * devfs master lock must not be held across a vget() call, we have
+        * to hold our ad-hoc vp to avoid a free race from destroying the
+        * contents of the structure.  The vget() will interlock recycles
+        * for us.
+        */
 try_again:
        while ((vp = node->v_node) != NULL) {
 try_again:
        while ((vp = node->v_node) != NULL) {
+               vhold(vp);
+               lockmgr(&devfs_lock, LK_RELEASE);
                error = vget(vp, LK_EXCLUSIVE);
                error = vget(vp, LK_EXCLUSIVE);
+               vdrop(vp);
+               lockmgr(&devfs_lock, LK_EXCLUSIVE);
                if (error == 0) {
                        *vpp = vp;
                        goto out;
                if (error == 0) {
                        *vpp = vp;
                        goto out;
@@ -305,8 +315,15 @@ try_again:
                }
        }
 
                }
        }
 
-       if ((error = getnewvnode(VT_DEVFS, node->mp, vpp, 0, 0)) != 0)
+       /*
+        * devfs master lock must not be held across a getnewvnode() call.
+        */
+       lockmgr(&devfs_lock, LK_RELEASE);
+       if ((error = getnewvnode(VT_DEVFS, node->mp, vpp, 0, 0)) != 0) {
+               lockmgr(&devfs_lock, LK_EXCLUSIVE);
                goto out;
                goto out;
+       }
+       lockmgr(&devfs_lock, LK_EXCLUSIVE);
 
        vp = *vpp;
 
 
        vp = *vpp;