Skip site navigation (1)Skip section navigation (2)
Date:       Tue, 18 Apr 2000 14:18:24 -0400
From:      Dave Chapeskie <dchapes@borderware.com>
To:        Adrian Chadd <adrian@FreeBSD.ORG>
Cc:        Matthew Dillon <dillon@apollo.backplane.com>, freebsd-fs@FreeBSD.ORG
Subject:   Re: vnode_free_list corruption
Message-ID:  <00Apr18.141542edt.117123@gateway.borderware.com>
In-Reply-To: <20000418174608.C71428@ewok.creative.net.au>; from Adrian Chadd on Tue, Apr 18, 2000 at 05:46:11PM %2B0800
References:  <00Apr14.141908edt.117140@gateway.borderware.com> <20000415023148.F34852@ewok.creative.net.au> <200004141835.LAA71253@apollo.backplane.com> <20000418042733.I59015@ewok.creative.net.au> <00Apr17.185117edt.117127@gateway.borderware.com> <20000418174608.C71428@ewok.creative.net.au>

next in thread | previous in thread | raw e-mail | index | archive | help

--BOKacYhQ+x31HxR3
Content-Type: text/plain; charset=us-ascii

On Tue, Apr 18, 2000 at 05:46:11PM +0800, Adrian Chadd wrote:
> Yes, but from my take of the code, if a vnode reaches VDOOMED, its been
> earmarked for recycling and is in the process of being flushed. If
> the vnode is being used in the FS code somewhere (or anywhere for that
> matter :) it shouldn't ever be considered for recycling as it should be
> vref()'ed or at the least vhold()'ed.

I see it the other way around, getnewvnode is perfectly within it's
rights to call vgonel() on a VOP_LOCKED vnode from what I can see (see
code comment below).  I think the problem is that vhold and vbusy need
to be checking for VXLOCK and returning an error just the way vget does.
If that's the case it seems silly to have both vhold and vget.


The vclean() call (from vgonel called by getnewvnode) must be blocking.
It's the only place between getnewvnode()'s setting VDOOMED and it's
later clearing of the flags (assuming VXLOCK isn't already set) where it
can block.  There is a comment in vclean() which says:

	/*
	 * Even if the count is zero, the VOP_INACTIVE routine may still
	 * have the object locked while it cleans it out. The VOP_LOCK
	 * ensures that the VOP_INACTIVE routine is done with its work.
	 * For active vnodes, it ensures that no other activity can
	 * occur while the underlying object is being cleaned out.
	 */
	VOP_LOCK(vp, LK_DRAIN | LK_INTERLOCK, p);

Alternatively it may sometimes be blocking in the vinvalbuf() call.

I just repeated the problem again with some extra kernel debugging.  One
of the 'head' processes calls getnewvnode and it blocks with a wait
message of "inode", I think that means its blocked in the VOP_LOCK call
waiting for the VOP_LOCK.


vprint() from the getnewvnode call looks like this:

getnewvnode: 0xcd2e7200: type VREG, usecount 0, writecount 0,
		refcount 0, flags (VFREE)
	tag VT_UFS, ino 27201, on dev 0x20015 (0, 131093)
		lock type inode: EXCL (count 1) by pid 611
getnewvnode: pid 1455 recycling VOP_ISLOCKED vnode!


The vprint() from vbusy looks like this:

vbusy: 0xcd2e7200: type VREG, usecount 0, writecount 0,
		refcount 1, flags (VXLOCK|VDOOMED|VFREE)
	tag VT_UFS, ino 27201, on dev 0x20015 (0, 131093)
		lock type inode: EXCL (count 1) by pid 611
panic: vbusy on VDOOMED vnode


pid 611 is 'rm', pid 1455 is 'head' with a wait message of "inode".

So it looks like my panic call in vbusy should be checking for VXLOCK
instead of VDOOMED (since the later is only set/checked from within
getnewvnode it's better not to make other parts of the system know about
it).


> Right, I'll drop MAXMEM down and try to starve the system further, and
> see what happens.

Also make sure softupdates is off since it could easily be changing the
timing (all my test were done with it off).

Also try the attached patch in order to make the problem easier to
replicate.  It makes getnewvnode _try_ and find a VOP_LOCKED vnode to
recycle.  It still only picks vnodes that might otherwise have been
selected (if they were closer to the front of the inactive list).  It of
course slows things down since it often walks the complete free list but
that shouldn't matter for the purposes of this test.

With similar changes in my kernel it can still takes a couple of minutes
for the vbusy panic to occur (although I can do it with fewer instances
of my test scripts running).  You'll probably notice that getnewvnode
does successfully recycle several VOP_LOCKED vnodes before vbusy() gets
called on one.

-- 
Dave Chapeskie
Senior Software Engineer
Borderware Technologies Inc.
Mississauga, Ontario, Canada

--BOKacYhQ+x31HxR3
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="test.diff"

diff -u -r1.253 vfs_subr.c
--- kern/vfs_subr.c	2000/03/20 11:28:45	1.253
+++ kern/vfs_subr.c	2000/04/18 18:12:58
@@ -112,6 +112,14 @@
 SYSCTL_INT(_vfs, OID_AUTO, reassignbufsortbad, CTLFLAG_RW, &reassignbufsortbad, 0, "");
 static int reassignbufmethod = 1;
 SYSCTL_INT(_vfs, OID_AUTO, reassignbufmethod, CTLFLAG_RW, &reassignbufmethod, 0, "");
+#ifdef DDB
+static int skip_vop_locked = 0;
+SYSCTL_INT(_debug, OID_AUTO, skip_vop_locked, CTLFLAG_RW, &skip_vop_locked, 0,
+		"move VOP_LOCKED vnodes to the back of the free list");
+static int find_vop_locked = 1;
+SYSCTL_INT(_debug, OID_AUTO, find_vop_locked, CTLFLAG_RW, &find_vop_locked, 0,
+		"try and cause problems by looking for VOP_LOCKED vnodes to recycle");
+#endif
 
 #ifdef ENABLE_VFS_IOOPT
 int vfs_ioopt = 0;
@@ -453,6 +461,9 @@
 	struct vnode *vp, *tvp, *nvp;
 	vm_object_t object;
 	TAILQ_HEAD(freelst, vnode) vnode_tmp_list;
+#ifdef DDB
+	struct vnode *non_locked = NULL;
+#endif
 
 	/*
 	 * We take the least recently used vnode from the freelist
@@ -507,7 +518,35 @@
 				/* Don't recycle if active in the namecache */
 				simple_unlock(&vp->v_interlock);
 				continue;
+			} else if (VOP_ISLOCKED(vp)) {
+#ifdef DDB
+				vprint("getnewvnode", vp);
+				if (!skip_vop_locked) {
+					printf (getnewvnode: "pid %ld recycling"
+					    " VOP_ISLOCKED vnode!\n",
+					    curproc ? curproc->p_pid : 0);
+					break;
+				}
+				printf ("getnewvnode: pushing VOP_ISLOCKED"
+				    " vnode to end of list\n");
+#endif
+				TAILQ_REMOVE(&vnode_free_list, vp, v_freelist);
+				TAILQ_INSERT_TAIL(&vnode_tmp_list, vp, v_freelist);
+				continue;
 			} else {
+#ifdef DDB
+				if (!skip_vop_locked && find_vop_locked) {
+					/*
+					 * To illistrate a problem look for
+					 * VOP_LOCKED vnodes to recycle,
+					 * but remember the first non-locked
+					 * vnode
+					 */
+					if (non_locked == NULL)
+						non_locked = vp;
+					continue;
+				} else
+#endif
 				break;
 			}
 		}
@@ -520,6 +559,11 @@
 		simple_unlock(&tvp->v_interlock);
 	}
 
+#ifdef DDB
+	/* If there are no locked vnodes, use the first non-locked one */
+	if (vp == NULL && non_locked != NULL)
+		vp = non_locked;
+#endif
 	if (vp) {
 		vp->v_flag |= VDOOMED;
 		TAILQ_REMOVE(&vnode_free_list, vp, v_freelist);
@@ -2613,6 +2657,13 @@
 	int s;
 
 	s = splbio();
+	if (vp->v_flag & VDOOMED|VXLOCK) {
+#ifdef DIAGNOSTIC
+		vprint ("vbusy", vp);
+		printf ("vbusy by pid %ld\n", curproc ? curproc->p_pid : 0);
+#endif
+		panic ("vbusy on VDOOMED or VXLOCKed vnode");
+	}
 	simple_lock(&vnode_free_list_slock);
 	if (vp->v_flag & VTBFREE) {
 		TAILQ_REMOVE(&vnode_tobefree_list, vp, v_freelist);

--BOKacYhQ+x31HxR3--


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




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?00Apr18.141542edt.117123>