Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src - Change vcache_reclaim() to always call VOP_INACTIVE() befo...



details:   https://anonhg.NetBSD.org/src/rev/c6bdcddd915d
branches:  trunk
changeset: 349233:c6bdcddd915d
user:      hannken <hannken%NetBSD.org@localhost>
date:      Thu Dec 01 14:49:03 2016 +0000

description:
- Change vcache_reclaim() to always call VOP_INACTIVE() before VOP_RECLAIM().
  When called from vrecycle() or vgone() there is a window where the refcount
  is greater than zero and another thread could get and release a reference
  that would miss VOP_INACTIVE() as the refcount doesn't drop to zero.

  Adjust test fs/puffs/t_basic:  test VOP_INACTIVE count being greater zero.

- Make vrecycle() more robust by checking v_usecount first and preventing
  further references across vn_lock().  Fixes a deadlock where one thread
  starts unmount, second thread locks a directory and allocates a vnode
  and first thread tries to vrecycle() the directory.
  First thread holds vfs_busy and wants vnode, second thread holds vnode
  and wants vfs_busy.

- With these fixes in place change cleanvnode() to use vget()/vrecycle()
  to reclaim the vnode.

diffstat:

 sys/kern/vfs_vnode.c     |  72 +++++++++++++++++++++++------------------------
 tests/fs/puffs/t_basic.c |   6 ++--
 2 files changed, 38 insertions(+), 40 deletions(-)

diffs (164 lines):

diff -r a1ce52ee0c9c -r c6bdcddd915d sys/kern/vfs_vnode.c
--- a/sys/kern/vfs_vnode.c      Thu Dec 01 14:29:15 2016 +0000
+++ b/sys/kern/vfs_vnode.c      Thu Dec 01 14:49:03 2016 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: vfs_vnode.c,v 1.59 2016/11/03 11:04:21 hannken Exp $   */
+/*     $NetBSD: vfs_vnode.c,v 1.60 2016/12/01 14:49:03 hannken Exp $   */
 
 /*-
  * Copyright (c) 1997-2011 The NetBSD Foundation, Inc.
@@ -156,7 +156,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vfs_vnode.c,v 1.59 2016/11/03 11:04:21 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_vnode.c,v 1.60 2016/12/01 14:49:03 hannken Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -444,16 +444,11 @@
                KASSERT(vp->v_usecount == 0);
                KASSERT(vp->v_freelisthd == listhd);
 
-               if (vn_lock(vp, LK_EXCLUSIVE | LK_NOWAIT) != 0)
+               if (!mutex_tryenter(vp->v_interlock))
                        continue;
-               if (!mutex_tryenter(vp->v_interlock)) {
-                       VOP_UNLOCK(vp);
-                       continue;
-               }
                mp = vp->v_mount;
                if (fstrans_start_nowait(mp, FSTRANS_SHARED) != 0) {
                        mutex_exit(vp->v_interlock);
-                       VOP_UNLOCK(vp);
                        continue;
                }
                break;
@@ -468,21 +463,12 @@
                return EBUSY;
        }
 
-       /* Remove it from the freelist. */
-       TAILQ_REMOVE(listhd, vp, v_freelist);
-       vp->v_freelisthd = NULL;
        mutex_exit(&vnode_free_list_lock);
 
-       KASSERT(vp->v_usecount == 0);
-
-       /*
-        * The vnode is still associated with a file system, so we must
-        * clean it out before freeing it.  We need to add a reference
-        * before doing this.
-        */
-       vp->v_usecount = 1;
-       vcache_reclaim(vp);
-       vrelel(vp, 0);
+       if (vget(vp, 0, true /* wait */) == 0) {
+               if (!vrecycle(vp))
+                       vrele(vp);
+       }
        fstrans_done(mp);
 
        return 0;
@@ -949,19 +935,37 @@
 bool
 vrecycle(vnode_t *vp)
 {
-
-       if (vn_lock(vp, LK_EXCLUSIVE) != 0)
-               return false;
+       int error __diagused;
 
        mutex_enter(vp->v_interlock);
 
+       /* Make sure we hold the last reference. */
+       VSTATE_WAIT_STABLE(vp);
        if (vp->v_usecount != 1) {
                mutex_exit(vp->v_interlock);
-               VOP_UNLOCK(vp);
                return false;
        }
+
+       /* If the vnode is already clean we're done. */
+       if (VSTATE_GET(vp) != VS_ACTIVE) {
+               VSTATE_ASSERT(vp, VS_RECLAIMED);
+               vrelel(vp, 0);
+               return true;
+       }
+
+       /* Prevent further references until the vnode is locked. */
+       VSTATE_CHANGE(vp, VS_ACTIVE, VS_BLOCKED);
+       mutex_exit(vp->v_interlock);
+
+       error = vn_lock(vp, LK_EXCLUSIVE);
+       KASSERT(error == 0);
+
+       mutex_enter(vp->v_interlock);
+       VSTATE_CHANGE(vp, VS_BLOCKED, VS_ACTIVE);
+
        vcache_reclaim(vp);
        vrelel(vp, 0);
+
        return true;
 }
 
@@ -1488,8 +1492,7 @@
        /*
         * Clean out any cached data associated with the vnode.
         * If purging an active vnode, it must be closed and
-        * deactivated before being reclaimed. Note that the
-        * VOP_INACTIVE will unlock the vnode.
+        * deactivated before being reclaimed.
         */
        error = vinvalbuf(vp, V_SAVE, NOCRED, l, 0, 0);
        if (error != 0) {
@@ -1502,17 +1505,12 @@
        if (active && (vp->v_type == VBLK || vp->v_type == VCHR)) {
                 spec_node_revoke(vp);
        }
-       if (active) {
-               VOP_INACTIVE(vp, &recycle);
-       } else {
-               /*
-                * Any other processes trying to obtain this lock must first
-                * wait for VS_RECLAIMED, then call the new lock operation.
-                */
-               VOP_UNLOCK(vp);
-       }
 
-       /* Disassociate the underlying file system from the vnode. */
+       /*
+        * Disassociate the underlying file system from the vnode.
+        * Note that the VOP_INACTIVE will unlock the vnode.
+        */
+       VOP_INACTIVE(vp, &recycle);
        if (VOP_RECLAIM(vp)) {
                vnpanic(vp, "%s: cannot reclaim", __func__);
        }
diff -r a1ce52ee0c9c -r c6bdcddd915d tests/fs/puffs/t_basic.c
--- a/tests/fs/puffs/t_basic.c  Thu Dec 01 14:29:15 2016 +0000
+++ b/tests/fs/puffs/t_basic.c  Thu Dec 01 14:49:03 2016 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: t_basic.c,v 1.12 2013/10/19 17:45:00 christos Exp $    */
+/*     $NetBSD: t_basic.c,v 1.13 2016/12/01 14:49:04 hannken Exp $     */
 
 #include <sys/types.h>
 #include <sys/mount.h>
@@ -286,7 +286,7 @@
        rump_sys_close(fd);
        syncbar(FSTEST_MNTNAME);
 
-       ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], 1);
+       ATF_REQUIRE(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE] > 0);
        ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 1);
 
        FSTEST_EXIT();
@@ -383,7 +383,7 @@
        syncbar(FSTEST_MNTNAME);
 
        ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_RECLAIM], 1);
-       ATF_REQUIRE_EQ(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE], ianow+1);
+       ATF_REQUIRE(pargs->pta_vn_toserv_ops[PUFFS_VN_INACTIVE] > ianow);
 
        ATF_REQUIRE_STREQ(buf, MAGICSTR);
 



Home | Main Index | Thread Index | Old Index