Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 11 Sep 2002 12:24:58 +0100
From:      Ian Dowse <iedowse@maths.tcd.ie>
To:        Don Lewis <dl-freebsd@catspoiler.org>
Cc:        current@FreeBSD.ORG, jeff@FreeBSD.ORG
Subject:   Re: nfs_inactive() bug? -> panic: lockmgr: locking against myself 
Message-ID:   <200209111224.aa37675@salmon.maths.tcd.ie>
In-Reply-To: Your message of "Tue, 10 Sep 2002 23:58:02 PDT." <200209110658.g8B6w2wr097059@gw.catspoiler.org> 

next in thread | previous in thread | raw e-mail | index | archive | help
In message <200209110658.g8B6w2wr097059@gw.catspoiler.org>, Don Lewis writes:
>
>A potentially better solution just occurred to me.  It looks like it
>would be better if vrele() waited to decrement v_usecount until *after*
>the call to VOP_INACTIVE() (and after the call to VI_LOCK()).  If that
>were done, nfs_inactive() wouldn't need to muck with v_usecount at all.

I've looked at this before; I think some filesystems (ufs anyway)
depend on v_usecount being 0 when VOP_INACTIVE() is called. The
patch I have had lying around for quite a while is below. It adds
a vnode flag to avoid recursion into the last-reference handling
code in vrele/vput, which is the real problem.

It also guarantees that a vnode will not be recycled during
VOP_INACTIVE(), so the nfs code no longer needs to hold an extra
reference in the first place. The flag manipulation code got a bit
messy after Jeff's vnode flag splitting work, so the patch could
probably be improved.

Whatever way this is done, we should try to avoid adding more hacks
to the nfs_inactive() code anyway.

Ian

Index: sys/vnode.h
===================================================================
RCS file: /home/iedowse/CVS/src/sys/sys/vnode.h,v
retrieving revision 1.206
diff -u -r1.206 vnode.h
--- sys/vnode.h	1 Sep 2002 20:37:21 -0000	1.206
+++ sys/vnode.h	11 Sep 2002 11:06:46 -0000
@@ -220,6 +220,7 @@
 #define	VI_DOOMED	0x0080	/* This vnode is being recycled */
 #define	VI_FREE		0x0100	/* This vnode is on the freelist */
 #define	VI_OBJDIRTY	0x0400	/* object might be dirty */
+#define	VI_INACTIVE	0x0800	/* VOP_INACTIVE is in progress */
 /*
  * XXX VI_ONWORKLST could be replaced with a check for NULL list elements
  * in v_synclist.
@@ -377,14 +378,14 @@
 
 /* Requires interlock */
 #define	VSHOULDFREE(vp)	\
-	(!((vp)->v_iflag & (VI_FREE|VI_DOOMED)) && \
+	(!((vp)->v_iflag & (VI_FREE|VI_DOOMED|VI_INACTIVE)) && \
 	 !(vp)->v_holdcnt && !(vp)->v_usecount && \
 	 (!(vp)->v_object || \
 	  !((vp)->v_object->ref_count || (vp)->v_object->resident_page_count)))
 
 /* Requires interlock */
 #define VMIGHTFREE(vp) \
-	(!((vp)->v_iflag & (VI_FREE|VI_DOOMED|VI_XLOCK)) &&	\
+	(!((vp)->v_iflag & (VI_FREE|VI_DOOMED|VI_XLOCK|VI_INACTIVE)) &&	\
 	 LIST_EMPTY(&(vp)->v_cache_src) && !(vp)->v_usecount)
 
 /* Requires interlock */
Index: nfsclient/nfs_node.c
===================================================================
RCS file: /home/iedowse/CVS/src/sys/nfsclient/nfs_node.c,v
retrieving revision 1.55
diff -u -r1.55 nfs_node.c
--- nfsclient/nfs_node.c	11 Jul 2002 17:54:58 -0000	1.55
+++ nfsclient/nfs_node.c	11 Sep 2002 11:06:46 -0000
@@ -289,21 +289,7 @@
 	} else
 		sp = NULL;
 	if (sp) {
-		/*
-		 * We need a reference to keep the vnode from being
-		 * recycled by getnewvnode while we do the I/O
-		 * associated with discarding the buffers unless we
-		 * are being forcibly unmounted in which case we already
-		 * have our own reference.
-		 */
-		if (ap->a_vp->v_usecount > 0)
-			(void) nfs_vinvalbuf(ap->a_vp, 0, sp->s_cred, td, 1);
-		else if (vget(ap->a_vp, 0, td))
-			panic("nfs_inactive: lost vnode");
-		else {
-			(void) nfs_vinvalbuf(ap->a_vp, 0, sp->s_cred, td, 1);
-			vrele(ap->a_vp);
-		}
+		(void)nfs_vinvalbuf(ap->a_vp, 0, sp->s_cred, td, 1);
 		/*
 		 * Remove the silly file that was rename'd earlier
 		 */
Index: kern/vfs_subr.c
===================================================================
RCS file: /home/iedowse/CVS/src/sys/kern/vfs_subr.c,v
retrieving revision 1.401
diff -u -r1.401 vfs_subr.c
--- kern/vfs_subr.c	5 Sep 2002 20:46:19 -0000	1.401
+++ kern/vfs_subr.c	11 Sep 2002 11:06:46 -0000
@@ -829,7 +829,8 @@
 		for (count = 0; count < freevnodes; count++) {
 			vp = TAILQ_FIRST(&vnode_free_list);
 
-			KASSERT(vp->v_usecount == 0, 
+			KASSERT(vp->v_usecount == 0 &&
+			    (vp->v_iflag & VI_INACTIVE) == 0,
 			    ("getnewvnode: free vnode isn't"));
 
 			TAILQ_REMOVE(&vnode_free_list, vp, v_freelist);
@@ -1980,8 +1981,8 @@
 	KASSERT(vp->v_writecount < vp->v_usecount || vp->v_usecount < 1,
 	    ("vrele: missed vn_close"));
 
-	if (vp->v_usecount > 1) {
-
+	if (vp->v_usecount > 1 ||
+	    ((vp->v_iflag & VI_INACTIVE) && vp->v_usecount == 1)) {
 		vp->v_usecount--;
 		VI_UNLOCK(vp);
 
@@ -1991,13 +1992,20 @@
 	if (vp->v_usecount == 1) {
 		vp->v_usecount--;
 		/*
-		 * We must call VOP_INACTIVE with the node locked.
-		 * If we are doing a vput, the node is already locked,
-		 * but, in the case of vrele, we must explicitly lock
-		 * the vnode before calling VOP_INACTIVE.
+		 * We must call VOP_INACTIVE with the node locked. Mark
+		 * as VI_INACTIVE to avoid recursion.
 		 */
-		if (vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK, td) == 0)
+		if (vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK, td) == 0) {
+			VI_LOCK(vp);
+			vp->v_iflag |= VI_INACTIVE;
+			VI_UNLOCK(vp);
 			VOP_INACTIVE(vp, td);
+			VI_LOCK(vp);
+			KASSERT(vp->v_iflag & VI_INACTIVE,
+			    ("vrele: lost VI_INACTIVE"));
+			vp->v_iflag &= ~VI_INACTIVE;
+			VI_UNLOCK(vp);
+		}
 		VI_LOCK(vp);
 		if (VSHOULDFREE(vp))
 			vfree(vp);
@@ -2033,7 +2041,8 @@
 	KASSERT(vp->v_writecount < vp->v_usecount || vp->v_usecount < 1,
 	    ("vput: missed vn_close"));
 
-	if (vp->v_usecount > 1) {
+	if (vp->v_usecount > 1 ||
+	    ((vp->v_iflag & VI_INACTIVE) && vp->v_usecount == 1)) {
 		vp->v_usecount--;
 		VOP_UNLOCK(vp, LK_INTERLOCK, td);
 		return;
@@ -2042,13 +2051,16 @@
 	if (vp->v_usecount == 1) {
 		vp->v_usecount--;
 		/*
-		 * We must call VOP_INACTIVE with the node locked.
-		 * If we are doing a vput, the node is already locked,
-		 * so we just need to release the vnode mutex.
+		 * We must call VOP_INACTIVE with the node locked, so
+		 * we just need to release the vnode mutex. Mark as
+		 * as VI_INACTIVE to avoid recursion.
 		 */
+		vp->v_iflag |= VI_INACTIVE;
 		VI_UNLOCK(vp);
 		VOP_INACTIVE(vp, td);
 		VI_LOCK(vp);
+		KASSERT(vp->v_iflag & VI_INACTIVE, ("vput: lost VI_INACTIVE"));
+		vp->v_iflag &= ~VI_INACTIVE;
 		if (VSHOULDFREE(vp))
 			vfree(vp);
 		else
@@ -2331,12 +2343,16 @@
 	 * deactivated before being reclaimed. Note that the
 	 * VOP_INACTIVE will unlock the vnode.
 	 */
-	if (active) {
-		if (flags & DOCLOSE)
-			VOP_CLOSE(vp, FNONBLOCK, NOCRED, td);
+	if (active && (flags & DOCLOSE))
+		VOP_CLOSE(vp, FNONBLOCK, NOCRED, td);
+	if (active && (vp->v_iflag & VI_INACTIVE) == 0) {
+		vp->v_iflag |= VI_INACTIVE;
 		if (vn_lock(vp, LK_EXCLUSIVE | LK_NOWAIT, td) != 0)
 			panic("vclean: cannot relock.");
 		VOP_INACTIVE(vp, td);
+		KASSERT(vp->v_iflag & VI_INACTIVE,
+		    ("vclean: lost VI_INACTIVE"));
+		vp->v_iflag &= ~VI_INACTIVE;
 	}
 
 	/*



To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-current" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi? <200209111224.aa37675>