kernel - Fix issue in UFS related to new nvtruncbuf() API use
authorMatthew Dillon <dillon@apollo.backplane.com>
Fri, 29 Jan 2010 18:55:34 +0000 (10:55 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Fri, 29 Jan 2010 18:55:34 +0000 (10:55 -0800)
* When a UFS truncation must downsize a block it must sometimes call
  FSYNC twice, the second time to flush out softdep block dependencies
  related to the original indirect block.

  UFS depends on the first FSYNC call to prevent the buffer cache buffer
  straddling the new file/directory EOF from becoming dirty.  However,
  nvtruncbuf() defeats this by re-dirtying the bp.

  The solution is to simply undirty the bp prior to the second FSYNC,
  which works fine since it will be written out later with a b*write()
  anyway.

* Fixes 'locking against myself' panic w/UFS.

Reported-by: Stathis Kamperis <ekamperi@gmail.com>
sys/vfs/ufs/ffs_inode.c

index aa9bd5a..41bb3dd 100644 (file)
@@ -261,9 +261,8 @@ ffs_truncate(struct vnode *vp, off_t length, int flags, struct ucred *cred)
                if (flags & IO_SYNC)
                        aflags |= B_SYNC;
                error = VOP_BALLOC(ovp, length - 1, 1, cred, aflags, &bp);
-               if (error) {
+               if (error)
                        return (error);
-               }
 
                /*
                 * When we are doing soft updates and the UFS_BALLOC
@@ -272,16 +271,28 @@ ffs_truncate(struct vnode *vp, off_t length, int flags, struct ucred *cred)
                 * we must flush out the block dependency with an FSYNC
                 * so that we do not get a soft updates inconsistency
                 * when we create the fragment below.
+                *
+                * nvtruncbuf() may have re-dirtied the underlying block
+                * as part of its truncation zeroing code.  To avoid a
+                * 'locking against myself' panic in the second fsync we
+                * can simply undirty the bp since the redirtying was
+                * related to areas of the buffer that we are going to
+                * throw away anyway, and we will b*write() the remainder
+                * anyway down below.
                 */
                if (DOINGSOFTDEP(ovp) && lbn < NDADDR &&
-                   fragroundup(fs, blkoff(fs, length)) < fs->fs_bsize &&
-                   (error = VOP_FSYNC(ovp, MNT_WAIT, 0)) != 0) {
+                   fragroundup(fs, blkoff(fs, length)) < fs->fs_bsize) {
+                       bundirty(bp);
+                       error = VOP_FSYNC(ovp, MNT_WAIT, 0);
+                       if (error) {
+                               bdwrite(bp);
                                return (error);
+                       }
                }
                oip->i_size = length;
                size = blksize(fs, oip, lbn);
 #if 0
-               /* vtruncbuf deals with this */
+               /* remove - nvtruncbuf deals with this */
                if (ovp->v_type != VDIR)
                        bzero((char *)bp->b_data + offset,
                            (uint)(size - offset));