aboutsummaryrefslogtreecommitdiff
path: root/sys/ufs/ffs/ffs_inode.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/ufs/ffs/ffs_inode.c')
-rw-r--r--sys/ufs/ffs/ffs_inode.c23
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);