kernel - Fix lost lock in devfs
authorMatthew Dillon <dillon@apollo.backplane.com>
Thu, 1 Dec 2011 23:22:44 +0000 (15:22 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Thu, 1 Dec 2011 23:22:44 +0000 (15:22 -0800)
* vn_lock() will always fail if a vnode is undergoing reclamation, do
  not try to unlock/relock in the spec_close code in that case.

* Refactor the error test in devfs_allocv() in case vget() returns
  an error other than ENOENT in the future.

sys/vfs/devfs/devfs_core.c
sys/vfs/devfs/devfs_vnops.c

index 8fd9992..8e4d9b8 100644 (file)
@@ -295,10 +295,14 @@ devfs_allocv(struct vnode **vpp, struct devfs_node *node)
 try_again:
        while ((vp = node->v_node) != NULL) {
                error = vget(vp, LK_EXCLUSIVE);
-               if (error != ENOENT) {
+               if (error == 0) {
                        *vpp = vp;
                        goto out;
                }
+               if (error != ENOENT) {
+                       *vpp = NULL;
+                       goto out;
+               }
        }
 
        if ((error = getnewvnode(VT_DEVFS, node->mp, vpp, 0, 0)) != 0)
index dcc88fd..04638fb 100644 (file)
@@ -1062,10 +1062,11 @@ devfs_spec_close(struct vop_close_args *ap)
                        node->flags |= DEVFS_INVISIBLE;
 
                /*
-                * Unlock around dev_dclose()
+                * Unlock around dev_dclose(), unless the vnode is
+                * undergoing a vgone/reclaim (during umount).
                 */
                needrelock = 0;
-               if (vn_islocked(vp)) {
+               if ((vp->v_flag & VRECLAIMED) == 0 && vn_islocked(vp)) {
                        needrelock = 1;
                        vn_unlock(vp);
                }
@@ -1073,12 +1074,20 @@ devfs_spec_close(struct vop_close_args *ap)
                /*
                 * WARNING!  If the device destroys itself the devfs node
                 *           can disappear here.
+                *
+                * WARNING!  vn_lock() will fail if the vp is in a VRECLAIM,
+                *           which can occur during umount.
                 */
                error = dev_dclose(dev, ap->a_fflag, S_IFCHR);
                /* node is now stale */
 
-               if (needrelock)
-                       vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+               if (needrelock) {
+                       if (vn_lock(vp, LK_EXCLUSIVE | LK_RETRY) != 0) {
+                               panic("devfs_spec_close: vnode %p "
+                                     "unexpectedly could not be relocked",
+                                     vp);
+                       }
+               }
        } else {
                error = 0;
        }