summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/sys/conf.h10
-rw-r--r--sys/sys/linedisc.h10
-rw-r--r--sys/ufs/ffs/ffs_snapshot.c326
-rw-r--r--sys/ufs/ufs/inode.h2
4 files changed, 204 insertions, 144 deletions
diff --git a/sys/sys/conf.h b/sys/sys/conf.h
index 713ebf5..1be8506 100644
--- a/sys/sys/conf.h
+++ b/sys/sys/conf.h
@@ -72,8 +72,6 @@ struct cdev {
LIST_HEAD(, cdev) si_children;
LIST_ENTRY(cdev) si_siblings;
dev_t si_parent;
- struct snaphead si_snapshots;
- int (*si_copyonwrite)(struct vnode *, struct buf *);
u_int si_inode;
char si_name[SPECNAMELEN + 1];
void *si_drv1, *si_drv2;
@@ -92,6 +90,10 @@ struct cdev {
struct mount *__sid_mountpoint;
int __sid_bsize_phys; /* min physical block size */
int __sid_bsize_best; /* optimal block size */
+ struct snaphead __sid_snapshots;
+ daddr_t __sid_snaplistsize; /* size of snapblklist. */
+ daddr_t *__sid_snapblklist; /* known snapshot blocks. */
+ int (*__sid_copyonwrite)(struct vnode *, struct buf *);
} __si_disk;
} __si_u;
};
@@ -101,6 +103,10 @@ struct cdev {
#define si_mountpoint __si_u.__si_disk.__sid_mountpoint
#define si_bsize_phys __si_u.__si_disk.__sid_bsize_phys
#define si_bsize_best __si_u.__si_disk.__sid_bsize_best
+#define si_snapshots __si_u.__si_disk.__sid_snapshots
+#define si_snaplistsize __si_u.__si_disk.__sid_snaplistsize
+#define si_snapblklist __si_u.__si_disk.__sid_snapblklist
+#define si_copyonwrite __si_u.__si_disk.__sid_copyonwrite
/*
* Special device management
diff --git a/sys/sys/linedisc.h b/sys/sys/linedisc.h
index 713ebf5..1be8506 100644
--- a/sys/sys/linedisc.h
+++ b/sys/sys/linedisc.h
@@ -72,8 +72,6 @@ struct cdev {
LIST_HEAD(, cdev) si_children;
LIST_ENTRY(cdev) si_siblings;
dev_t si_parent;
- struct snaphead si_snapshots;
- int (*si_copyonwrite)(struct vnode *, struct buf *);
u_int si_inode;
char si_name[SPECNAMELEN + 1];
void *si_drv1, *si_drv2;
@@ -92,6 +90,10 @@ struct cdev {
struct mount *__sid_mountpoint;
int __sid_bsize_phys; /* min physical block size */
int __sid_bsize_best; /* optimal block size */
+ struct snaphead __sid_snapshots;
+ daddr_t __sid_snaplistsize; /* size of snapblklist. */
+ daddr_t *__sid_snapblklist; /* known snapshot blocks. */
+ int (*__sid_copyonwrite)(struct vnode *, struct buf *);
} __si_disk;
} __si_u;
};
@@ -101,6 +103,10 @@ struct cdev {
#define si_mountpoint __si_u.__si_disk.__sid_mountpoint
#define si_bsize_phys __si_u.__si_disk.__sid_bsize_phys
#define si_bsize_best __si_u.__si_disk.__sid_bsize_best
+#define si_snapshots __si_u.__si_disk.__sid_snapshots
+#define si_snaplistsize __si_u.__si_disk.__sid_snaplistsize
+#define si_snapblklist __si_u.__si_disk.__sid_snapblklist
+#define si_copyonwrite __si_u.__si_disk.__sid_copyonwrite
/*
* Special device management
diff --git a/sys/ufs/ffs/ffs_snapshot.c b/sys/ufs/ffs/ffs_snapshot.c
index ff7947b..4e03eb9 100644
--- a/sys/ufs/ffs/ffs_snapshot.c
+++ b/sys/ufs/ffs/ffs_snapshot.c
@@ -125,10 +125,10 @@ ffs_snapshot(mp, snapfile)
int flag = mp->mnt_flag;
struct timespec starttime = {0, 0}, endtime;
char saved_nice = 0;
- long redo = 0;
+ long redo = 0, snaplistsize;
int32_t *lp;
void *space;
- daddr_t *listhd;
+ daddr_t *snapblklist;
struct fs *copy_fs = NULL, *fs = VFSTOUFS(mp)->um_fs;
struct snaphead *snaphead;
struct thread *td = curthread;
@@ -532,16 +532,18 @@ out1:
}
/*
* Allocate the space for the list of preallocated snapshot blocks.
+ * The i_offset field is borrowed to pass the value of snapblklist
+ * down into the expunge functions.
*/
- ip->i_snaplistsize = fragstoblks(fs, dbtofsb(fs, DIP(ip,i_blocks))) + 1;
- MALLOC(listhd, daddr_t *, ip->i_snaplistsize * sizeof(daddr_t),
+ 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 = listhd;
- *ip->i_snapblklist++ = ip->i_snaplistsize;
+ ((daddr_t *)(ip->i_offset)) = &snapblklist[1];
/*
* Expunge the blocks used by the snapshots from the set of
* blocks marked as used in the snapshot bitmaps. Also, collect
- * the list of allocated blocks in i_snapblklist.
+ * the list of allocated blocks in i_offset.
*/
if (ip->i_ump->um_fstype == UFS1)
error = expunge_ufs1(vp, ip, copy_fs, mapacct_ufs1, BLK_SNAP);
@@ -549,20 +551,19 @@ out1:
error = expunge_ufs2(vp, ip, copy_fs, mapacct_ufs2, BLK_SNAP);
if (error) {
fs->fs_snapinum[snaploc] = 0;
- FREE(listhd, M_UFSMNT);
+ FREE(snapblklist, M_UFSMNT);
goto done;
}
+ snaplistsize = ((daddr_t *)(ip->i_offset)) - snapblklist;
+ snapblklist[0] = snaplistsize;
+ ip->i_offset = 0;
/*
* Write out the list of allocated blocks to the end of the snapshot.
*/
- if (ip->i_snapblklist - listhd != ip->i_snaplistsize)
- printf("Snaplist mismatch, got %jd should be %jd\n",
- (intmax_t)(ip->i_snapblklist - listhd),
- (intmax_t)ip->i_snaplistsize);
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
- aiov.iov_base = (void *)listhd;
- aiov.iov_len = ip->i_snaplistsize * sizeof(daddr_t);
+ aiov.iov_base = (void *)snapblklist;
+ aiov.iov_len = snaplistsize * sizeof(daddr_t);
auio.uio_resid = aiov.iov_len;;
auio.uio_offset = ip->i_size;
auio.uio_segflg = UIO_SYSSPACE;
@@ -570,10 +571,9 @@ out1:
auio.uio_td = td;
if ((error = VOP_WRITE(vp, &auio, IO_UNIT, td->td_ucred)) != 0) {
fs->fs_snapinum[snaploc] = 0;
- FREE(listhd, M_UFSMNT);
+ FREE(snapblklist, M_UFSMNT);
goto done;
}
- ip->i_snapblklist = listhd;
/*
* Write the superblock and its summary information
* to the snapshot.
@@ -586,14 +586,22 @@ out1:
if (error) {
brelse(nbp);
fs->fs_snapinum[snaploc] = 0;
- FREE(listhd, M_UFSMNT);
- ip->i_snapblklist = NULL;
+ FREE(snapblklist, M_UFSMNT);
goto done;
}
bcopy(space, nbp->b_data, fs->fs_bsize);
space = (char *)space + fs->fs_bsize;
bawrite(nbp);
}
+ /*
+ * As this is the newest list, it is the most inclusive, so
+ * should replace the previous list.
+ */
+ VI_LOCK(devvp);
+ FREE(devvp->v_rdev->si_snapblklist, M_UFSMNT);
+ devvp->v_rdev->si_snapblklist = snapblklist;
+ devvp->v_rdev->si_snaplistsize = snaplistsize;
+ VI_UNLOCK(devvp);
done:
free(copy_fs->fs_csp, M_UFSMNT);
bawrite(sbp);
@@ -747,25 +755,6 @@ expunge_ufs1(snapvp, cancelip, fs, acctfunc, expungetype)
struct thread *td = curthread;
struct buf *bp;
- numblks = howmany(cancelip->i_size, fs->fs_bsize);
- if ((error = (*acctfunc)(snapvp, &cancelip->i_din1->di_db[0],
- &cancelip->i_din1->di_ib[NIADDR], fs, 0, expungetype)))
- return (error);
- blksperindir = 1;
- lbn = -NDADDR;
- len = numblks - NDADDR;
- rlbn = NDADDR;
- for (i = 0; len > 0 && i < NIADDR; i++) {
- error = indiracct_ufs1(snapvp, ITOV(cancelip), i,
- cancelip->i_din1->di_ib[i], lbn, rlbn, len,
- blksperindir, fs, acctfunc, expungetype);
- if (error)
- return (error);
- blksperindir *= NINDIR(fs);
- lbn -= blksperindir + 1;
- len -= blksperindir;
- rlbn += blksperindir;
- }
/*
* Prepare to expunge the inode. If its inode block has not
* yet been copied, then allocate and fill the copy.
@@ -809,6 +798,32 @@ expunge_ufs1(snapvp, cancelip, fs, acctfunc, expungetype)
dip->di_flags &= ~SF_SNAPSHOT;
bzero(&dip->di_db[0], (NDADDR + NIADDR) * sizeof(ufs1_daddr_t));
bdwrite(bp);
+ /*
+ * Now go through and expunge all the blocks in the file
+ * using the function requested.
+ */
+ numblks = howmany(cancelip->i_size, fs->fs_bsize);
+ if ((error = (*acctfunc)(snapvp, &cancelip->i_din1->di_db[0],
+ &cancelip->i_din1->di_db[NDADDR], fs, 0, expungetype)))
+ return (error);
+ if ((error = (*acctfunc)(snapvp, &cancelip->i_din1->di_ib[0],
+ &cancelip->i_din1->di_ib[NIADDR], fs, -1, expungetype)))
+ return (error);
+ blksperindir = 1;
+ lbn = -NDADDR;
+ len = numblks - NDADDR;
+ rlbn = NDADDR;
+ for (i = 0; len > 0 && i < NIADDR; i++) {
+ error = indiracct_ufs1(snapvp, ITOV(cancelip), i,
+ cancelip->i_din1->di_ib[i], lbn, rlbn, len,
+ blksperindir, fs, acctfunc, expungetype);
+ if (error)
+ return (error);
+ blksperindir *= NINDIR(fs);
+ lbn -= blksperindir + 1;
+ len -= blksperindir;
+ rlbn += blksperindir;
+ }
return (0);
}
@@ -862,7 +877,8 @@ indiracct_ufs1(snapvp, cancelvp, level, blkno, lbn, rlbn, remblks,
MALLOC(bap, ufs1_daddr_t *, fs->fs_bsize, M_DEVBUF, M_WAITOK);
bcopy(bp->b_data, (caddr_t)bap, fs->fs_bsize);
bqrelse(bp);
- error = (*acctfunc)(snapvp, &bap[0], &bap[last], fs, rlbn, expungetype);
+ error = (*acctfunc)(snapvp, &bap[0], &bap[last], fs,
+ level == 0 ? rlbn : -1, expungetype);
if (error || level == 0)
goto out;
/*
@@ -970,6 +986,12 @@ mapacct_ufs1(vp, oldblkp, lastblkp, fs, lblkno, expungetype)
struct inode *ip;
ino_t inum;
+ /*
+ * We only care about the leaf block numbers, not the
+ * meta-block numbers.
+ */
+ if (lblkno == -1)
+ return (0);
ip = VTOI(vp);
inum = ip->i_number;
for ( ; oldblkp < lastblkp; oldblkp++, lblkno++) {
@@ -977,7 +999,7 @@ mapacct_ufs1(vp, oldblkp, lastblkp, fs, lblkno, expungetype)
if (blkno == 0 || blkno == BLK_NOCOPY)
continue;
if (expungetype == BLK_SNAP && blkno != BLK_SNAP)
- *ip->i_snapblklist++ = lblkno;
+ *((daddr_t *)(ip->i_offset))++ = lblkno;
if (blkno == BLK_SNAP)
blkno = blkstofrags(fs, lblkno);
ffs_blkfree(fs, vp, blkno, fs->fs_bsize, inum);
@@ -1009,25 +1031,6 @@ expunge_ufs2(snapvp, cancelip, fs, acctfunc, expungetype)
struct thread *td = curthread;
struct buf *bp;
- numblks = howmany(cancelip->i_size, fs->fs_bsize);
- if ((error = (*acctfunc)(snapvp, &cancelip->i_din2->di_db[0],
- &cancelip->i_din2->di_ib[NIADDR], fs, 0, expungetype)))
- return (error);
- blksperindir = 1;
- lbn = -NDADDR;
- len = numblks - NDADDR;
- rlbn = NDADDR;
- for (i = 0; len > 0 && i < NIADDR; i++) {
- error = indiracct_ufs2(snapvp, ITOV(cancelip), i,
- cancelip->i_din2->di_ib[i], lbn, rlbn, len,
- blksperindir, fs, acctfunc, expungetype);
- if (error)
- return (error);
- blksperindir *= NINDIR(fs);
- lbn -= blksperindir + 1;
- len -= blksperindir;
- rlbn += blksperindir;
- }
/*
* Prepare to expunge the inode. If its inode block has not
* yet been copied, then allocate and fill the copy.
@@ -1071,6 +1074,32 @@ expunge_ufs2(snapvp, cancelip, fs, acctfunc, expungetype)
dip->di_flags &= ~SF_SNAPSHOT;
bzero(&dip->di_db[0], (NDADDR + NIADDR) * sizeof(ufs2_daddr_t));
bdwrite(bp);
+ /*
+ * Now go through and expunge all the blocks in the file
+ * using the function requested.
+ */
+ numblks = howmany(cancelip->i_size, fs->fs_bsize);
+ if ((error = (*acctfunc)(snapvp, &cancelip->i_din2->di_db[0],
+ &cancelip->i_din2->di_db[NDADDR], fs, 0, expungetype)))
+ return (error);
+ if ((error = (*acctfunc)(snapvp, &cancelip->i_din2->di_ib[0],
+ &cancelip->i_din2->di_ib[NIADDR], fs, -1, expungetype)))
+ return (error);
+ blksperindir = 1;
+ lbn = -NDADDR;
+ len = numblks - NDADDR;
+ rlbn = NDADDR;
+ for (i = 0; len > 0 && i < NIADDR; i++) {
+ error = indiracct_ufs2(snapvp, ITOV(cancelip), i,
+ cancelip->i_din2->di_ib[i], lbn, rlbn, len,
+ blksperindir, fs, acctfunc, expungetype);
+ if (error)
+ return (error);
+ blksperindir *= NINDIR(fs);
+ lbn -= blksperindir + 1;
+ len -= blksperindir;
+ rlbn += blksperindir;
+ }
return (0);
}
@@ -1124,7 +1153,8 @@ indiracct_ufs2(snapvp, cancelvp, level, blkno, lbn, rlbn, remblks,
MALLOC(bap, ufs2_daddr_t *, fs->fs_bsize, M_DEVBUF, M_WAITOK);
bcopy(bp->b_data, (caddr_t)bap, fs->fs_bsize);
bqrelse(bp);
- error = (*acctfunc)(snapvp, &bap[0], &bap[last], fs, rlbn, expungetype);
+ error = (*acctfunc)(snapvp, &bap[0], &bap[last], fs,
+ level == 0 ? rlbn : -1, expungetype);
if (error || level == 0)
goto out;
/*
@@ -1232,6 +1262,12 @@ mapacct_ufs2(vp, oldblkp, lastblkp, fs, lblkno, expungetype)
struct inode *ip;
ino_t inum;
+ /*
+ * We only care about the leaf block numbers, not the
+ * meta-block numbers.
+ */
+ if (lblkno == -1)
+ return (0);
ip = VTOI(vp);
inum = ip->i_number;
for ( ; oldblkp < lastblkp; oldblkp++, lblkno++) {
@@ -1239,7 +1275,7 @@ mapacct_ufs2(vp, oldblkp, lastblkp, fs, lblkno, expungetype)
if (blkno == 0 || blkno == BLK_NOCOPY)
continue;
if (expungetype == BLK_SNAP && blkno != BLK_SNAP)
- *ip->i_snapblklist++ = lblkno;
+ *((daddr_t *)(ip->i_offset))++ = lblkno;
if (blkno == BLK_SNAP)
blkno = blkstofrags(fs, lblkno);
ffs_blkfree(fs, vp, blkno, fs->fs_bsize, inum);
@@ -1265,11 +1301,11 @@ ffs_snapgone(ip)
TAILQ_FOREACH(xp, &ip->i_devvp->v_rdev->si_snapshots, i_nextsnap)
if (xp == ip)
break;
- if (xp == 0)
+ if (xp != NULL)
+ vrele(ITOV(ip));
+ else if (snapdebug)
printf("ffs_snapgone: lost snapshot vnode %d\n",
ip->i_number);
- else
- vrele(ITOV(ip));
/*
* Delete snapshot inode from superblock. Keep list dense.
*/
@@ -1300,7 +1336,7 @@ ffs_snapremove(vp)
struct buf *ibp;
struct fs *fs;
struct thread *td = curthread;
- ufs2_daddr_t numblks, blkno, dblk;
+ ufs2_daddr_t numblks, blkno, dblk, *snapblklist;
int error, loc, last;
ip = VTOI(vp);
@@ -1326,22 +1362,19 @@ ffs_snapremove(vp)
if (TAILQ_FIRST(&devvp->v_rdev->si_snapshots) != 0) {
VI_UNLOCK(devvp);
} else {
+ snapblklist = devvp->v_rdev->si_snapblklist;
+ devvp->v_rdev->si_snapblklist = 0;
+ devvp->v_rdev->si_snaplistsize = 0;
devvp->v_rdev->si_copyonwrite = 0;
devvp->v_vflag &= ~VV_COPYONWRITE;
lockmgr(lkp, LK_DRAIN|LK_INTERLOCK, VI_MTX(devvp), td);
lockmgr(lkp, LK_RELEASE, NULL, td);
lockdestroy(lkp);
FREE(lkp, M_UFSMNT);
+ FREE(snapblklist, M_UFSMNT);
}
}
/*
- * Get rid of its hints list.
- */
- if (ip->i_snapblklist != NULL) {
- FREE(ip->i_snapblklist, M_UFSMNT);
- ip->i_snapblklist = NULL;
- }
- /*
* Clear all BLK_NOCOPY fields. Pass any block claims to other
* snapshots that want them (see ffs_snapblkfree below).
*/
@@ -1448,18 +1481,15 @@ retry:
blkno = DIP(ip, i_db[lbn]);
} else {
if (snapshot_locked == 0 &&
- lockmgr(&vp->v_lock,
+ lockmgr(vp->v_vnlock,
LK_INTERLOCK | LK_EXCLUSIVE | LK_SLEEPFAIL,
VI_MTX(devvp), td) != 0)
goto retry;
+ snapshot_locked = 1;
td->td_proc->p_flag |= P_COWINPROGRESS;
error = UFS_BALLOC(vp, lblktosize(fs, (off_t)lbn),
fs->fs_bsize, KERNCRED, BA_METAONLY, &ibp);
td->td_proc->p_flag &= ~P_COWINPROGRESS;
- if (snapshot_locked == 0) {
- VI_LOCK(devvp);
- lockmgr(&vp->v_lock, LK_RELEASE, NULL, td);
- }
if (error)
break;
indiroff = (lbn - NDADDR) % NINDIR(fs);
@@ -1642,8 +1672,9 @@ ffs_snapshot_mount(mp)
struct inode *ip, *xp;
struct uio auio;
struct iovec aiov;
- void *listhd;
+ void *snapblklist;
char *reason;
+ daddr_t snaplistsize;
int error, snaploc, loc;
/*
@@ -1654,10 +1685,11 @@ ffs_snapshot_mount(mp)
/*
* Process each snapshot listed in the superblock.
*/
+ vp = NULL;
snaphead = &devvp->v_rdev->si_snapshots;
for (snaploc = 0; snaploc < FSMAXSNAP; snaploc++) {
if (fs->fs_snapinum[snaploc] == 0)
- return;
+ break;
if ((error = VFS_VGET(mp, fs->fs_snapinum[snaploc],
LK_EXCLUSIVE, &vp)) != 0){
printf("ffs_snapshot_mount: vget failed %d\n", error);
@@ -1676,6 +1708,7 @@ ffs_snapshot_mount(mp)
printf("ffs_snapshot_mount: %s inode %d\n",
reason, fs->fs_snapinum[snaploc]);
vput(vp);
+ vp = NULL;
for (loc = snaploc + 1; loc < FSMAXSNAP; loc++) {
if (fs->fs_snapinum[loc] == 0)
break;
@@ -1686,36 +1719,6 @@ ffs_snapshot_mount(mp)
continue;
}
/*
- * Allocate the space for the block hints list.
- */
- auio.uio_iov = &aiov;
- auio.uio_iovcnt = 1;
- aiov.iov_base = (void *)&ip->i_snaplistsize;
- aiov.iov_len = sizeof(ip->i_snaplistsize);
- auio.uio_resid = aiov.iov_len;
- auio.uio_offset =
- lblktosize(fs, howmany(fs->fs_size, fs->fs_frag));
- auio.uio_segflg = UIO_SYSSPACE;
- auio.uio_rw = UIO_READ;
- auio.uio_td = td;
- if ((error = VOP_READ(vp, &auio, IO_UNIT, td->td_ucred)) != 0) {
- printf("ffs_snapshot_mount: read_1 failed %d\n", error);
- continue;
- }
- MALLOC(listhd, void *, ip->i_snaplistsize * sizeof(daddr_t),
- M_UFSMNT, M_WAITOK);
- auio.uio_iovcnt = 1;
- aiov.iov_base = listhd;
- aiov.iov_len = ip->i_snaplistsize * sizeof (daddr_t);
- auio.uio_resid = aiov.iov_len;
- auio.uio_offset -= sizeof(ip->i_snaplistsize);
- if ((error = VOP_READ(vp, &auio, IO_UNIT, td->td_ucred)) != 0) {
- printf("ffs_snapshot_mount: read_2 failed %d\n", error);
- FREE(listhd, M_UFSMNT);
- continue;
- }
- ip->i_snapblklist = (daddr_t *)listhd;
- /*
* If there already exist snapshots on this filesystem, grab a
* reference to their shared lock. If this is the first snapshot
* on this filesystem, we need to allocate a lock for the
@@ -1751,12 +1754,55 @@ ffs_snapshot_mount(mp)
else
TAILQ_INSERT_TAIL(snaphead, ip, i_nextsnap);
vp->v_vflag |= VV_SYSTEM;
- devvp->v_rdev->si_copyonwrite = ffs_copyonwrite;
- ASSERT_VOP_LOCKED(devvp, "ffs_snapshot_mount");
- devvp->v_vflag |= VV_COPYONWRITE;
VI_UNLOCK(devvp);
VOP_UNLOCK(vp, 0, td);
}
+ /*
+ * No usable snapshots found.
+ */
+ if (vp == NULL)
+ return;
+ /*
+ * Allocate the space for the block hints list. We always want to
+ * use the list from the newest snapshot.
+ */
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ aiov.iov_base = (void *)&snaplistsize;
+ aiov.iov_len = sizeof(snaplistsize);
+ auio.uio_resid = aiov.iov_len;
+ auio.uio_offset =
+ lblktosize(fs, howmany(fs->fs_size, fs->fs_frag));
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_READ;
+ auio.uio_td = td;
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+ if ((error = VOP_READ(vp, &auio, IO_UNIT, td->td_ucred)) != 0) {
+ printf("ffs_snapshot_mount: read_1 failed %d\n", error);
+ VOP_UNLOCK(vp, 0, td);
+ return;
+ }
+ MALLOC(snapblklist, void *, snaplistsize * sizeof(daddr_t),
+ M_UFSMNT, M_WAITOK);
+ auio.uio_iovcnt = 1;
+ aiov.iov_base = snapblklist;
+ aiov.iov_len = snaplistsize * sizeof (daddr_t);
+ auio.uio_resid = aiov.iov_len;
+ auio.uio_offset -= sizeof(snaplistsize);
+ if ((error = VOP_READ(vp, &auio, IO_UNIT, td->td_ucred)) != 0) {
+ printf("ffs_snapshot_mount: read_2 failed %d\n", error);
+ VOP_UNLOCK(vp, 0, td);
+ FREE(snapblklist, M_UFSMNT);
+ return;
+ }
+ VOP_UNLOCK(vp, 0, td);
+ VI_LOCK(devvp);
+ ASSERT_VOP_LOCKED(devvp, "ffs_snapshot_mount");
+ devvp->v_rdev->si_snaplistsize = snaplistsize;
+ devvp->v_rdev->si_snapblklist = (daddr_t *)snapblklist;
+ devvp->v_rdev->si_copyonwrite = ffs_copyonwrite;
+ devvp->v_vflag |= VV_COPYONWRITE;
+ VI_UNLOCK(devvp);
}
/*
@@ -1778,10 +1824,6 @@ ffs_snapshot_unmount(mp)
lkp = vp->v_vnlock;
vp->v_vnlock = &vp->v_lock;
TAILQ_REMOVE(snaphead, xp, i_nextsnap);
- if (xp->i_snapblklist != NULL) {
- FREE(xp->i_snapblklist, M_UFSMNT);
- xp->i_snapblklist = NULL;
- }
xp->i_nextsnap.tqe_prev = 0;
if (xp->i_effnlink > 0) {
VI_UNLOCK(devvp);
@@ -1789,6 +1831,11 @@ ffs_snapshot_unmount(mp)
VI_LOCK(devvp);
}
}
+ if (devvp->v_rdev->si_snapblklist != NULL) {
+ FREE(devvp->v_rdev->si_snapblklist, M_UFSMNT);
+ devvp->v_rdev->si_snapblklist = NULL;
+ devvp->v_rdev->si_snaplistsize = 0;
+ }
if (lkp != NULL) {
lockdestroy(lkp);
FREE(lkp, M_UFSMNT);
@@ -1814,16 +1861,39 @@ ffs_copyonwrite(devvp, bp)
struct fs *fs;
struct inode *ip;
struct vnode *vp = 0;
- ufs2_daddr_t lbn, blkno;
+ ufs2_daddr_t lbn, blkno, *snapblklist;
int lower, upper, mid, indiroff, snapshot_locked = 0, error = 0;
if (td->td_proc->p_flag & P_COWINPROGRESS)
panic("ffs_copyonwrite: recursive call");
+ /*
+ * First check to see if it is in the preallocated list.
+ * By doing this check we avoid several potential deadlocks.
+ */
VI_LOCK(devvp);
snaphead = &devvp->v_rdev->si_snapshots;
ip = TAILQ_FIRST(snaphead);
fs = ip->i_fs;
lbn = fragstoblks(fs, dbtofsb(fs, bp->b_blkno));
+ snapblklist = devvp->v_rdev->si_snapblklist;
+ upper = devvp->v_rdev->si_snaplistsize - 1;
+ lower = 1;
+ while (lower <= upper) {
+ mid = (lower + upper) / 2;
+ if (snapblklist[mid] == lbn)
+ break;
+ if (snapblklist[mid] < lbn)
+ lower = mid + 1;
+ else
+ upper = mid - 1;
+ }
+ if (lower <= upper) {
+ VI_UNLOCK(devvp);
+ return (0);
+ }
+ /*
+ * Not in the precomputed list, so check the snapshots.
+ */
retry:
TAILQ_FOREACH(ip, snaphead, i_nextsnap) {
vp = ITOV(ip);
@@ -1836,23 +1906,6 @@ retry:
if (bp->b_vp == vp)
continue;
/*
- * First check to see if it is in the preallocated list.
- * By doing this check we avoid several potential deadlocks.
- */
- lower = 1;
- upper = ip->i_snaplistsize - 1;
- while (lower <= upper) {
- mid = (lower + upper) / 2;
- if (ip->i_snapblklist[mid] == lbn)
- break;
- if (ip->i_snapblklist[mid] < lbn)
- lower = mid + 1;
- else
- upper = mid - 1;
- }
- if (lower <= upper)
- continue;
- /*
* Check to see if block needs to be copied. We do not have
* to hold the snapshot lock while doing this lookup as it
* will never require any additional allocations for the
@@ -1862,20 +1915,17 @@ retry:
blkno = DIP(ip, i_db[lbn]);
} else {
if (snapshot_locked == 0 &&
- lockmgr(&vp->v_lock,
+ lockmgr(vp->v_vnlock,
LK_INTERLOCK | LK_EXCLUSIVE | LK_SLEEPFAIL,
VI_MTX(devvp), td) != 0) {
VI_LOCK(devvp);
goto retry;
}
+ snapshot_locked = 1;
td->td_proc->p_flag |= P_COWINPROGRESS;
error = UFS_BALLOC(vp, lblktosize(fs, (off_t)lbn),
fs->fs_bsize, KERNCRED, BA_METAONLY, &ibp);
td->td_proc->p_flag &= ~P_COWINPROGRESS;
- if (snapshot_locked == 0) {
- VI_LOCK(devvp);
- lockmgr(&vp->v_lock, LK_RELEASE, NULL, td);
- }
if (error)
break;
indiroff = (lbn - NDADDR) % NINDIR(fs);
diff --git a/sys/ufs/ufs/inode.h b/sys/ufs/ufs/inode.h
index af11a4c..2a59e63 100644
--- a/sys/ufs/ufs/inode.h
+++ b/sys/ufs/ufs/inode.h
@@ -63,8 +63,6 @@
struct inode {
LIST_ENTRY(inode) i_hash;/* Hash chain. */
TAILQ_ENTRY(inode) i_nextsnap; /* snapshot file list. */
- daddr_t i_snaplistsize; /* size of snapblklist. */
- daddr_t *i_snapblklist; /* list of known snapshot blocks. */
struct vnode *i_vnode;/* Vnode associated with this inode. */
struct ufsmount *i_ump;/* Ufsmount point associated with this inode. */
struct vnode *i_devvp;/* Vnode for block I/O. */
OpenPOWER on IntegriCloud