Skip site navigation (1)Skip section navigation (2)
Date:       Fri, 14 Apr 2000 14:20:37 -0400
From:      Dave Chapeskie <dchapes@borderware.com>
To:        freebsd-hackers@FreeBSD.ORG, freebsd-fs@FreeBSD.ORG
Subject:   vnode_free_list corruption [patch]
Message-ID:  <00Apr14.141908edt.117140@gateway.borderware.com>

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

--GvXjxJ+pjyke8COw
Content-Type: text/plain; charset=us-ascii

Greetings.

I've been seeing a rash of "free vnode isn't" panics lately.  Some
machines were panicing several times a day.  Along with this we saw
occasional "object inconsistent state: RPC: %d, RC: %d" messages.

I was able to replicate the problem by running multiple (~8 on a
pentium 200 system with 32 MB of RAM) copies of each of the attached
simple shell scripts (with all output redirected to /dev/null).  It
would often panic within 10-20 minutes.


I tracked the problem down to a race between getnewvnode() recycling a
vnode and vhold().  I found that vhold() was calling vbusy() for a vnode
with the VDOOMED flag set.

This is bad since getnewvnode() removes the vnode from the free list
before setting this flag so vbusy() is calling TAILQ_REMOVE for a vnode
that is not on the free list.  This can easily result in corruption of
the free list pointers causing future getnewvnode() calls to find active
vnodes that it thinks are on the free list.

I added a panic in vbusy() if VDOOMED is set and this hit quite often
during my tests.  Typically the call chain looked something like:

ffs_truncate
ffs_indirtrunc
getblk
bgetvp
vhold
vbusy
panic

With ffs_truncate often being called due to rename(2) or unlink(2).


I managed to solve the problem here by adding a VOP_ISLOCKED(vp) check
to getnewvnode() and skipping such vnodes instead of trying to recycle
them.  From my searches of the mailing lists it appears I'm not the
first one to think of this but apparently this isn't guaranteed to work
for all files system types.  I just know it works for the FFS problems I
was seeing.


At a minimum I'd highly recommend that someone commit a panic to vbusy()
for vnodes with VDOOMED set since letting it continue if that flag is
set can and does result in the corruption of the vnode_free_list.  I'd
also recommend the addition of the VOP_ISLOCKED() check to getnewvnode()
even if it doesn't work for all file system types it will help in some
(most?) cases.  A patch for CURRENT is attached.

-- 
Dave Chapeskie
Senior Software Engineer
Borderware Technologies Inc.

--GvXjxJ+pjyke8COw
Content-Type: application/x-sh
Content-Disposition: attachment; filename="cp_mv_loop.sh"

#!/bin/sh
cd /var/log
while true; do
	mkdir copy; cp * copy
	mkdir copy/2; mv copy/* copy/2
	rm -rf copy
done

--GvXjxJ+pjyke8COw
Content-Type: application/x-sh
Content-Disposition: attachment; filename="find_loop.sh"

#!/bin/sh
FS="/tmp /var /usr"
while true; do
	for f in $FS; do
		find -x $f -type f -print | xargs -n 4 head -1
	done
done

--GvXjxJ+pjyke8COw
Content-Type: application/x-sh
Content-Disposition: attachment; filename="ps_loop.sh"

#!/bin/sh
while true; do
	ps -aux; ps -ajx
done

--GvXjxJ+pjyke8COw
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="vfs_subr.c.diff"

diff -u -t -r1.253 vfs_subr.c
--- kern/vfs_subr.c	2000/03/20 11:28:45	1.253
+++ kern/vfs_subr.c	2000/04/14 18:01:52
@@ -467,6 +467,8 @@
 
         for (vp = TAILQ_FIRST(&vnode_tobefree_list); vp; vp = nvp) {
                 nvp = TAILQ_NEXT(vp, v_freelist);
+                if (!simple_lock_try(&vp->v_interlock))
+                        continue;
                 TAILQ_REMOVE(&vnode_tobefree_list, vp, v_freelist);
                 if (vp->v_flag & VAGE) {
                         TAILQ_INSERT_HEAD(&vnode_free_list, vp, v_freelist);
@@ -478,6 +480,7 @@
                 if (vp->v_usecount)
                         panic("tobe free vnode isn't");
                 freevnodes++;
+                simple_unlock(&tvp->v_interlock);
         }
 
         if (wantfreevnodes && freevnodes < wantfreevnodes) {
@@ -507,6 +510,10 @@
                                 /* Don't recycle if active in the namecache */
                                 simple_unlock(&vp->v_interlock);
                                 continue;
+                        } else if (VOP_ISLOCKED(vp))
+                                TAILQ_REMOVE(&vnode_free_list, vp, v_freelist);
+                                TAILQ_INSERT_TAIL(&vnode_tmp_list, vp, v_freelist);
+                                continue;
                         } else {
                                 break;
                         }
@@ -2613,6 +2620,10 @@
         int s;
 
         s = splbio();
+        if (vp->v_flag & VDOOMED) {
+                vprint("vbusy", vp);
+                panic("vbusy on VDOOMED vnode");
+        }
         simple_lock(&vnode_free_list_slock);
         if (vp->v_flag & VTBFREE) {
                 TAILQ_REMOVE(&vnode_tobefree_list, vp, v_freelist);

--GvXjxJ+pjyke8COw--


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




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?00Apr14.141908edt.117140>