summaryrefslogtreecommitdiffstats
path: root/sys/ufs/ffs/ffs_snapshot.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/ufs/ffs/ffs_snapshot.c')
-rw-r--r--sys/ufs/ffs/ffs_snapshot.c83
1 files changed, 66 insertions, 17 deletions
diff --git a/sys/ufs/ffs/ffs_snapshot.c b/sys/ufs/ffs/ffs_snapshot.c
index 2b09162..0e1e68f 100644
--- a/sys/ufs/ffs/ffs_snapshot.c
+++ b/sys/ufs/ffs/ffs_snapshot.c
@@ -198,10 +198,14 @@ restart:
}
/*
* Allocate shadow blocks to copy all of the other snapshot inodes
- * so that we will be able to expunge them from this snapshot.
+ * so that we will be able to expunge them from this snapshot. Also
+ * include a copy of ourselves so that we do not deadlock trying
+ * to copyonwrite ourselves when VOP_FSYNC'ing below.
*/
- for (loc = 0, inoblkcnt = 0; loc < snaploc; loc++) {
+ fs->fs_snapinum[snaploc] = ip->i_number;
+ for (loc = snaploc, inoblkcnt = 0; loc >= 0; loc--) {
blkno = fragstoblks(fs, ino_to_fsba(fs, fs->fs_snapinum[loc]));
+ fs->fs_snapinum[snaploc] = 0;
for (i = 0; i < inoblkcnt; i++)
if (inoblks[i] == blkno)
break;
@@ -652,14 +656,14 @@ ffs_snapremove(vp)
ip = VTOI(vp);
fs = ip->i_fs;
/*
- * Delete from incore list.
+ * If active, delete from incore list (this snapshot may
+ * already have been in the process of being deleted, so
+ * would not have been active).
+ *
* Clear copy-on-write flag if last snapshot.
*/
- devvp = ip->i_devvp;
- if (ip->i_nextsnap.tqe_prev == 0) {
- printf("ffs_snapremove: lost snapshot vnode %d\n",
- ip->i_number);
- } else {
+ if (ip->i_nextsnap.tqe_prev != 0) {
+ devvp = ip->i_devvp;
TAILQ_REMOVE(&devvp->v_rdev->si_snapshots, ip, i_nextsnap);
ip->i_nextsnap.tqe_prev = 0;
if (TAILQ_FIRST(&devvp->v_rdev->si_snapshots) == 0) {
@@ -832,9 +836,10 @@ ffs_snapblkfree(freeip, bno, size)
error = VOP_BALLOC(vp, lblktosize(fs, (off_t)lbn),
fs->fs_bsize, KERNCRED, 0, &cbp);
p->p_flag &= ~P_COWINPROGRESS;
- VOP_UNLOCK(vp, 0, p);
- if (error)
+ if (error) {
+ VOP_UNLOCK(vp, 0, p);
break;
+ }
#ifdef DEBUG
if (snapdebug)
printf("%s%d lbn %d for inum %d size %ld to blkno %d\n",
@@ -843,22 +848,44 @@ ffs_snapblkfree(freeip, bno, size)
#endif
/*
* If we have already read the old block contents, then
- * simply copy them to the new block.
+ * simply copy them to the new block. Note that we need
+ * to synchronously write snapshots that have not been
+ * unlinked, and hence will be visible after a crash,
+ * to ensure their integrity.
*/
if (savedcbp != 0) {
bcopy(savedcbp->b_data, cbp->b_data, fs->fs_bsize);
bawrite(cbp);
+ if (ip->i_effnlink > 0)
+ (void) VOP_FSYNC(vp, KERNCRED, MNT_WAIT, p);
+ VOP_UNLOCK(vp, 0, p);
continue;
}
/*
* Otherwise, read the old block contents into the buffer.
*/
- if ((error = readblock(cbp, lbn)) != 0)
+ if ((error = readblock(cbp, lbn)) != 0) {
+ bzero(cbp->b_data, fs->fs_bsize);
+ bawrite(cbp);
+ if (ip->i_effnlink > 0)
+ (void) VOP_FSYNC(vp, KERNCRED, MNT_WAIT, p);
+ VOP_UNLOCK(vp, 0, p);
break;
+ }
savedcbp = cbp;
}
- if (savedcbp)
+ /*
+ * Note that we need to synchronously write snapshots that
+ * have not been unlinked, and hence will be visible after
+ * a crash, to ensure their integrity.
+ */
+ if (savedcbp) {
+ vp = savedcbp->b_vp;
bawrite(savedcbp);
+ if (VTOI(vp)->i_effnlink > 0)
+ (void) VOP_FSYNC(vp, KERNCRED, MNT_WAIT, p);
+ VOP_UNLOCK(vp, 0, p);
+ }
/*
* If we have been unable to allocate a block in which to do
* the copy, then return non-zero so that the fragment will
@@ -1014,8 +1041,8 @@ retry:
error = VOP_BALLOC(vp, lblktosize(fs, (off_t)lbn),
fs->fs_bsize, KERNCRED, B_NOWAIT, &cbp);
p->p_flag &= ~P_COWINPROGRESS;
- VOP_UNLOCK(vp, 0, p);
if (error) {
+ VOP_UNLOCK(vp, 0, p);
if (error != EWOULDBLOCK)
break;
tsleep(vp, p->p_pri.pri_user, "nap", 1);
@@ -1035,22 +1062,44 @@ retry:
#endif
/*
* If we have already read the old block contents, then
- * simply copy them to the new block.
+ * simply copy them to the new block. Note that we need
+ * to synchronously write snapshots that have not been
+ * unlinked, and hence will be visible after a crash,
+ * to ensure their integrity.
*/
if (savedcbp != 0) {
bcopy(savedcbp->b_data, cbp->b_data, fs->fs_bsize);
bawrite(cbp);
+ if (ip->i_effnlink > 0)
+ (void) VOP_FSYNC(vp, KERNCRED, MNT_WAIT, p);
+ VOP_UNLOCK(vp, 0, p);
continue;
}
/*
* Otherwise, read the old block contents into the buffer.
*/
- if ((error = readblock(cbp, lbn)) != 0)
+ if ((error = readblock(cbp, lbn)) != 0) {
+ bzero(cbp->b_data, fs->fs_bsize);
+ bawrite(cbp);
+ if (ip->i_effnlink > 0)
+ (void) VOP_FSYNC(vp, KERNCRED, MNT_WAIT, p);
+ VOP_UNLOCK(vp, 0, p);
break;
+ }
savedcbp = cbp;
}
- if (savedcbp)
+ /*
+ * Note that we need to synchronously write snapshots that
+ * have not been unlinked, and hence will be visible after
+ * a crash, to ensure their integrity.
+ */
+ if (savedcbp) {
+ vp = savedcbp->b_vp;
bawrite(savedcbp);
+ if (VTOI(vp)->i_effnlink > 0)
+ (void) VOP_FSYNC(vp, KERNCRED, MNT_WAIT, p);
+ VOP_UNLOCK(vp, 0, p);
+ }
return (error);
}
OpenPOWER on IntegriCloud