diff options
Diffstat (limited to 'sys/ufs/ffs/ffs_inode.c')
-rw-r--r-- | sys/ufs/ffs/ffs_inode.c | 23 |
1 files changed, 19 insertions, 4 deletions
diff --git a/sys/ufs/ffs/ffs_inode.c b/sys/ufs/ffs/ffs_inode.c index 9d0144a16a98..6175ed83a4ff 100644 --- a/sys/ufs/ffs/ffs_inode.c +++ b/sys/ufs/ffs/ffs_inode.c @@ -106,6 +106,7 @@ ffs_update(vp, waitfor) flags = 0; if (IS_SNAPSHOT(ip)) flags = GB_LOCK_NOWAIT; +loop: error = breadn_flags(ip->i_devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), (int) fs->fs_bsize, 0, 0, 0, NOCRED, flags, &bp); @@ -115,13 +116,27 @@ ffs_update(vp, waitfor) return (error); } KASSERT((IS_SNAPSHOT(ip)), ("EBUSY from non-snapshot")); - vref(vp); /* Protect against ffs_snapgone() */ + /* + * Wait for our inode block to become available. + * + * Hold a reference to the vnode to protect against + * ffs_snapgone(). Since we hold a reference, it can only + * get reclaimed (VI_DOOMED flag) in a forcible downgrade + * or unmount. For an unmount, the entire filesystem will be + * gone, so we cannot attempt to touch anything associated + * with it while the vnode is unlocked; all we can do is + * pause briefly and try again. If when we relock the vnode + * we discover that it has been reclaimed, updating it is no + * longer necessary and we can just return an error. + */ + vref(vp); VOP_UNLOCK(vp, 0); - (void) bread(ip->i_devvp, - fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), - (int) fs->fs_bsize, NOCRED, &bp); + pause("ffsupd", 1); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); vrele(vp); + if ((vp->v_iflag & VI_DOOMED) != 0) + return (ENOENT); + goto loop; } if (DOINGSOFTDEP(vp)) softdep_update_inodeblock(ip, bp, waitfor); |