kern/93942: panic: ufs_dirbad: bad dir

Matthew Dillon dillon at apollo.backplane.com
Thu May 4 19:40:26 UTC 2006


The following reply was made to PR kern/93942; it has been noted by GNATS.

From: Matthew Dillon <dillon at apollo.backplane.com>
To: freebsd-gnats-submit at freebsd.org, freebsd-current at freebsd.org
Cc:  
Subject: Re: kern/93942: panic: ufs_dirbad: bad dir
Date: Thu, 4 May 2006 12:33:28 -0700 (PDT)

     I've found three additional issues which might be related to ufs_dirbad
     panics.  Again, unfortunately, no smoking gun.
 
     First, if B_NOCACHE gets set on a B_DIRTY buffer, the buffer can be
     lost without the data being written under certain conditions due
     to brelse() mechanics.  B_NOCACHE is typically set by softupdates 
     related code but can be set by other things as well (in particular,
     if a buffer is resized, and certain write/read combinations).  One
     might think that calling bwrite() after setting B_NOCACHE would be
     safe, but that is not necessarily true.  If a buffer is redirtied
     (B_DIRTY set) during the write, something which softupdates does all
     the time, B_NOCACHE almost certainly has to be cleared.  Of the three
     issues I found, this is the most likely cause.
 
     Second, vnode_pager_setsize() is being called too late in 
     ufs/ufs/ufs_lookup.c (line 733 in FreeBSD-current).  It is
     being called after the buffer has been instantiated.  This could
     create problems with the VMIO backing store for the buffer created
     by the UFS_BALLOC call.
 
     Third, vnode_pager_setsize() is being called too late in
     ufs/ufs/ufs_vnops.c (line 1557 in FreeBSD-current).  It is 
     being called after the buffer has been instantiated by UFS_BALLOC()
     in ufs_mkdir(), which could create problems with the buffer's VMIO
     backing store.
 
     --
 
     The M.O. of this corruption, after examining over a dozen kernel cores,
     makes me now believe that the corruption is occuring when the kernel
     attempts to append a full block to a directory.  The bitmaps are all
     good... it is if as though the directory block never got written and
     the data we are seeing is data that existed in tha block before the
     directory allocated it.  But, likewise, the issue has occured with
     different disk drivers so I think we can rule out a disk driver failure.
     The issue also seems to occur most often with large, 'busy' buffers
     (lots of directory operations going on).  Since no similar corruption
     has ever been reported for heavily used files, this supports the idea
     that it is *not* the disk driver.
 
     I believe that the data is getting written to the filesystem buffer
     representing the new block, but the buffer or its backing store
     is somehow getting thrown away without being written, or getting thrown
     away and then reinstantiated without being read.   The areas I 
     indicate in the above list are areas where data can potentially get
     thrown away or lost prior to a write.
 
 					-Matt
 					Matthew Dillon 
 					<dillon at backplane.com>
 
 
 (Patch against DragonFly, will not apply to FreeBSD directly, included for
 reference only):
 
 Index: kern/vfs_bio.c
 ===================================================================
 RCS file: /cvs/src/sys/kern/vfs_bio.c,v
 retrieving revision 1.53.2.1
 diff -u -r1.53.2.1 vfs_bio.c
 --- kern/vfs_bio.c	18 Apr 2006 17:12:25 -0000	1.53.2.1
 +++ kern/vfs_bio.c	24 Apr 2006 19:22:04 -0000
 @@ -972,6 +972,13 @@
  bdirty(struct buf *bp)
  {
  	KASSERT(bp->b_qindex == BQUEUE_NONE, ("bdirty: buffer %p still on queue %d", bp, bp->b_qindex));
 +	if (bp->b_flags & B_NOCACHE) {
 +		printf("bdirty: clearing B_NOCACHE on buf %p\n", bp);
 +		bp->b_flags &= ~B_NOCACHE;
 +	}
 +	if (bp->b_flags & B_INVAL) {
 +		printf("bdirty: warning, dirtying invalid buffer %p\n", bp);
 +	}
  	bp->b_flags &= ~(B_READ|B_RELBUF);
  
  	if ((bp->b_flags & B_DELWRI) == 0) {
 @@ -1096,6 +1103,11 @@
  
  	crit_enter();
  
 +	if ((bp->b_flags & (B_NOCACHE|B_DIRTY)) == (B_NOCACHE|B_DIRTY)) {
 +		printf("warning: buf %p marked dirty & B_NOCACHE, clearing B_NOCACHE\n", bp);
 +		bp->b_flags &= ~B_NOCACHE;
 +	}
 +
  	if (bp->b_flags & B_LOCKED)
  		bp->b_flags &= ~B_ERROR;
  
 Index: vfs/ufs/ufs_lookup.c
 ===================================================================
 RCS file: /cvs/src/sys/vfs/ufs/ufs_lookup.c,v
 retrieving revision 1.18
 diff -u -r1.18 ufs_lookup.c
 --- vfs/ufs/ufs_lookup.c	14 Sep 2005 01:13:48 -0000	1.18
 +++ vfs/ufs/ufs_lookup.c	24 Apr 2006 19:22:23 -0000
 @@ -716,6 +716,7 @@
  		 */
  		if (dp->i_offset & (DIRBLKSIZ - 1))
  			panic("ufs_direnter: newblk");
 +		vnode_pager_setsize(dvp, dp->i_offset + DIRBLKSIZ);
  		flags = B_CLRBUF;
  		if (!DOINGSOFTDEP(dvp) && !DOINGASYNC(dvp))
  			flags |= B_SYNC;
 @@ -727,7 +728,6 @@
  		}
  		dp->i_size = dp->i_offset + DIRBLKSIZ;
  		dp->i_flag |= IN_CHANGE | IN_UPDATE;
 -		vnode_pager_setsize(dvp, (u_long)dp->i_size);
  		dirp->d_reclen = DIRBLKSIZ;
  		blkoff = dp->i_offset &
  		    (VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_iosize - 1);
 Index: vfs/ufs/ufs_vnops.c
 ===================================================================
 RCS file: /cvs/src/sys/vfs/ufs/ufs_vnops.c,v
 retrieving revision 1.32
 diff -u -r1.32 ufs_vnops.c
 --- vfs/ufs/ufs_vnops.c	17 Sep 2005 07:43:12 -0000	1.32
 +++ vfs/ufs/ufs_vnops.c	24 Apr 2006 19:22:42 -0000
 @@ -1420,12 +1420,12 @@
  	dirtemplate = *dtp;
  	dirtemplate.dot_ino = ip->i_number;
  	dirtemplate.dotdot_ino = dp->i_number;
 +	vnode_pager_setsize(tvp, DIRBLKSIZ);
  	if ((error = VOP_BALLOC(tvp, (off_t)0, DIRBLKSIZ, cnp->cn_cred,
  	    B_CLRBUF, &bp)) != 0)
  		goto bad;
  	ip->i_size = DIRBLKSIZ;
  	ip->i_flag |= IN_CHANGE | IN_UPDATE;
 -	vnode_pager_setsize(tvp, (u_long)ip->i_size);
  	bcopy((caddr_t)&dirtemplate, (caddr_t)bp->b_data, sizeof dirtemplate);
  	if (DOINGSOFTDEP(tvp)) {
  		/*


More information about the freebsd-bugs mailing list