summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/ufs/ffs/ffs_snapshot.c50
1 files changed, 43 insertions, 7 deletions
diff --git a/sys/ufs/ffs/ffs_snapshot.c b/sys/ufs/ffs/ffs_snapshot.c
index 3bfc1b9..afbea57 100644
--- a/sys/ufs/ffs/ffs_snapshot.c
+++ b/sys/ufs/ffs/ffs_snapshot.c
@@ -119,16 +119,15 @@ ffs_snapshot(mp, snapfile)
struct mount *mp;
char *snapfile;
{
- ufs2_daddr_t numblks, blkno;
+ ufs2_daddr_t numblks, blkno, *blkp, *snapblklist;
int error, cg, snaploc;
int i, size, len, loc;
int flag = mp->mnt_flag;
struct timespec starttime = {0, 0}, endtime;
char saved_nice = 0;
- long redo = 0, snaplistsize;
+ long redo = 0, snaplistsize = 0;
int32_t *lp;
void *space;
- daddr_t *snapblklist;
struct fs *copy_fs = NULL, *fs = VFSTOUFS(mp)->um_fs;
struct snaphead *snaphead;
struct thread *td = curthread;
@@ -394,7 +393,11 @@ restart:
* spec_strategy about writing on a suspended filesystem.
* Note that we skip unlinked snapshot files as they will
* be handled separately below.
+ *
+ * We also calculate the needed size for the snapshot list.
*/
+ snaplistsize = fs->fs_ncg + howmany(fs->fs_cssize, fs->fs_bsize) +
+ FSMAXSNAP + 1 /* superblock */ + 1 /* last block */ + 1 /* size */;
mp->mnt_kern_flag &= ~MNTK_SUSPENDED;
mtx_lock(&mntvnode_mtx);
loop:
@@ -438,6 +441,7 @@ loop:
DIP(xp, i_db[loc]) = 0;
}
}
+ snaplistsize += 1;
if (xp->i_ump->um_fstype == UFS1)
error = expunge_ufs1(vp, xp, copy_fs, fullacct_ufs1,
BLK_NOCOPY);
@@ -487,6 +491,36 @@ loop:
transferlockers(&vp->v_lock, vp->v_vnlock);
lockmgr(&vp->v_lock, LK_RELEASE, NULL, td);
/*
+ * If this is the first snapshot on this filesystem, then we need
+ * to allocate the space for the list of preallocated snapshot blocks.
+ * This list will be refined below, but this preliminary one will
+ * keep us out of deadlock until the full one is ready.
+ */
+ if (xp == NULL) {
+ MALLOC(snapblklist, daddr_t *, snaplistsize * sizeof(daddr_t),
+ M_UFSMNT, M_WAITOK);
+ blkp = &snapblklist[1];
+ *blkp++ = lblkno(fs, fs->fs_sblockloc);
+ blkno = fragstoblks(fs, fs->fs_csaddr);
+ for (cg = 0; cg < fs->fs_ncg; cg++) {
+ if (fragstoblks(fs, cgtod(fs, cg) > blkno))
+ break;
+ *blkp++ = fragstoblks(fs, cgtod(fs, cg));
+ }
+ len = howmany(fs->fs_cssize, fs->fs_bsize);
+ for (loc = 0; loc < len; loc++)
+ *blkp++ = blkno + loc;
+ for (; cg < fs->fs_ncg; cg++)
+ *blkp++ = fragstoblks(fs, cgtod(fs, cg));
+ snapblklist[0] = blkp - snapblklist;
+ VI_LOCK(devvp);
+ if (devvp->v_rdev->si_snapblklist != NULL)
+ panic("ffs_snapshot: non-empty list");
+ devvp->v_rdev->si_snapblklist = snapblklist;
+ devvp->v_rdev->si_snaplistsize = blkp - snapblklist;
+ VI_UNLOCK(devvp);
+ }
+ /*
* Record snapshot inode. Since this is the newest snapshot,
* it must be placed at the end of the list.
*/
@@ -535,10 +569,8 @@ out1:
}
}
/*
- * Allocate the space for the list of preallocated snapshot blocks.
+ * Allocate space for the full list of preallocated snapshot blocks.
*/
- snaplistsize = fs->fs_ncg + howmany(fs->fs_cssize, fs->fs_bsize) +
- FSMAXSNAP + 1 /* superblock */ + 1 /* last block */ + 1 /* size */;
MALLOC(snapblklist, daddr_t *, snaplistsize * sizeof(daddr_t),
M_UFSMNT, M_WAITOK);
ip->i_snapblklist = &snapblklist[1];
@@ -556,6 +588,8 @@ out1:
FREE(snapblklist, M_UFSMNT);
goto done;
}
+ if (snaplistsize < ip->i_snapblklist - snapblklist)
+ panic("ffs_snapshot: list too small");
snaplistsize = ip->i_snapblklist - snapblklist;
snapblklist[0] = snaplistsize;
ip->i_snapblklist = 0;
@@ -600,9 +634,11 @@ out1:
* should replace the previous list.
*/
VI_LOCK(devvp);
- FREE(devvp->v_rdev->si_snapblklist, M_UFSMNT);
+ space = devvp->v_rdev->si_snapblklist;
devvp->v_rdev->si_snapblklist = snapblklist;
devvp->v_rdev->si_snaplistsize = snaplistsize;
+ if (space != NULL)
+ FREE(space, M_UFSMNT);
VI_UNLOCK(devvp);
done:
free(copy_fs->fs_csp, M_UFSMNT);
OpenPOWER on IntegriCloud