Module Name: src
Committed By: hannken
Date: Wed Feb 16 19:43:50 UTC 2011
Modified Files:
src/sys/ufs/ffs: ffs_snapshot.c
Log Message:
Refine the scope of WAPBL transactions so we should no longer get
a "wapbl_flush: current transaction too big to flush" panic when
creating or removing snapshots on larger logging disks.
Adresses PR #44568 (WAPBL doens't play nice with snapshots).
To generate a diff of this commit:
cvs rdiff -u -r1.102 -r1.103 src/sys/ufs/ffs/ffs_snapshot.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/ufs/ffs/ffs_snapshot.c
diff -u src/sys/ufs/ffs/ffs_snapshot.c:1.102 src/sys/ufs/ffs/ffs_snapshot.c:1.103
--- src/sys/ufs/ffs/ffs_snapshot.c:1.102 Mon Dec 20 00:25:47 2010
+++ src/sys/ufs/ffs/ffs_snapshot.c Wed Feb 16 19:43:50 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: ffs_snapshot.c,v 1.102 2010/12/20 00:25:47 matt Exp $ */
+/* $NetBSD: ffs_snapshot.c,v 1.103 2011/02/16 19:43:50 hannken Exp $ */
/*
* Copyright 2000 Marshall Kirk McKusick. All Rights Reserved.
@@ -38,7 +38,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ffs_snapshot.c,v 1.102 2010/12/20 00:25:47 matt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ffs_snapshot.c,v 1.103 2011/02/16 19:43:50 hannken Exp $");
#if defined(_KERNEL_OPT)
#include "opt_ffs.h"
@@ -114,6 +114,7 @@
static int rwfsblk(struct vnode *, int, void *, daddr_t);
static int syncsnap(struct vnode *);
static int wrsnapblk(struct vnode *, void *, daddr_t);
+static int blocks_in_journal(struct fs *);
static inline bool is_active_snapshot(struct snap_info *, struct inode *);
static inline daddr_t db_get(struct inode *, int);
@@ -403,11 +404,12 @@
static int
snapshot_setup(struct mount *mp, struct vnode *vp)
{
- int error, i, len, loc;
+ int error, n, len, loc;
daddr_t blkno, numblks;
struct buf *ibp, *nbp;
struct fs *fs = VFSTOUFS(mp)->um_fs;
struct lwp *l = curlwp;
+ const int wbreak = blocks_in_journal(fs)/8;
/*
* Check mount, exclusive reference and owner.
@@ -452,13 +454,13 @@
error = UFS_WAPBL_BEGIN(mp);
if (error)
return error;
- for (blkno = NDADDR, i = 0; blkno < numblks; blkno += NINDIR(fs)) {
+ for (blkno = NDADDR, n = 0; blkno < numblks; blkno += NINDIR(fs)) {
error = ffs_balloc(vp, lblktosize(fs, (off_t)blkno),
fs->fs_bsize, l->l_cred, B_METAONLY, &ibp);
if (error)
goto out;
brelse(ibp, 0);
- if ((++i % 16) == 0) {
+ if (wbreak > 0 && (++n % wbreak) == 0) {
UFS_WAPBL_END(mp);
error = UFS_WAPBL_BEGIN(mp);
if (error)
@@ -561,7 +563,6 @@
snapshot_expunge(struct mount *mp, struct vnode *vp, struct fs *copy_fs,
daddr_t *snaplistsize, daddr_t **snaplist)
{
- bool has_wapbl = false;
int cg, error, len, loc;
daddr_t blkno, *blkp;
struct fs *fs = VFSTOUFS(mp)->um_fs;
@@ -593,10 +594,6 @@
*/
*snaplistsize = fs->fs_ncg + howmany(fs->fs_cssize, fs->fs_bsize) +
FSMAXSNAP + 1 /* superblock */ + 1 /* last block */ + 1 /* size */;
- error = UFS_WAPBL_BEGIN(mp);
- if (error)
- goto out;
- has_wapbl = true;
mutex_enter(&mntvnode_lock);
/*
* NOTE: not using the TAILQ_FOREACH here since in this loop vgone()
@@ -648,19 +645,30 @@
if (loc < NDADDR) {
len = fragroundup(fs, blkoff(fs, xp->i_size));
if (len > 0 && len < fs->fs_bsize) {
+ error = UFS_WAPBL_BEGIN(mp);
+ if (error) {
+ (void)vunmark(mvp);
+ goto out;
+ }
ffs_blkfree_snap(copy_fs, vp, db_get(xp, loc),
len, xp->i_number);
blkno = db_get(xp, loc);
db_assign(xp, loc, 0);
+ UFS_WAPBL_END(mp);
}
}
*snaplistsize += 1;
error = expunge(vp, xp, copy_fs, fullacct, BLK_NOCOPY);
if (blkno)
db_assign(xp, loc, blkno);
- if (!error)
- error = ffs_freefile_snap(copy_fs, vp, xp->i_number,
- xp->i_mode);
+ if (!error) {
+ error = UFS_WAPBL_BEGIN(mp);
+ if (!error) {
+ error = ffs_freefile_snap(copy_fs, vp,
+ xp->i_number, xp->i_mode);
+ UFS_WAPBL_END(mp);
+ }
+ }
if (error) {
(void)vunmark(mvp);
goto out;
@@ -688,8 +696,6 @@
(*snaplist)[0] = blkp - &(*snaplist)[0];
out:
- if (has_wapbl)
- UFS_WAPBL_END(mp);
if (mvp != NULL)
vnfree(mvp);
if (logvp != NULL)
@@ -711,16 +717,13 @@
snapshot_expunge_snap(struct mount *mp, struct vnode *vp,
struct fs *copy_fs, daddr_t snaplistsize)
{
- int error, i;
+ int error = 0, i;
daddr_t numblks, *snaplist = NULL;
struct fs *fs = VFSTOUFS(mp)->um_fs;
struct inode *ip = VTOI(vp), *xp;
struct lwp *l = curlwp;
struct snap_info *si = VFSTOUFS(mp)->um_snapinfo;
- error = UFS_WAPBL_BEGIN(mp);
- if (error)
- return error;
TAILQ_FOREACH(xp, &si->si_snapshots, i_nextsnap) {
if (xp == ip)
break;
@@ -729,7 +732,11 @@
break;
if (xp->i_nlink != 0)
continue;
+ error = UFS_WAPBL_BEGIN(mp);
+ if (error)
+ break;
error = ffs_freefile_snap(copy_fs, vp, xp->i_number, xp->i_mode);
+ UFS_WAPBL_END(mp);
if (error)
break;
}
@@ -761,12 +768,10 @@
snaplist[i] = ufs_rw64(snaplist[i], UFS_FSNEEDSWAP(fs));
error = vn_rdwr(UIO_WRITE, vp, (void *)snaplist,
snaplistsize * sizeof(daddr_t), lblktosize(fs, (off_t)numblks),
- UIO_SYSSPACE, IO_NODELOCKED | IO_JOURNALLOCKED | IO_UNIT,
- l->l_cred, NULL, NULL);
+ UIO_SYSSPACE, IO_NODELOCKED | IO_UNIT, l->l_cred, NULL, NULL);
for (i = 0; i < snaplistsize; i++)
snaplist[i] = ufs_rw64(snaplist[i], UFS_FSNEEDSWAP(fs));
out:
- UFS_WAPBL_END(mp);
if (error && snaplist != NULL) {
free(snaplist, M_UFSMNT);
ip->i_snapblklist = NULL;
@@ -859,13 +864,10 @@
static int
cgaccount(struct vnode *vp, int passno, int *redo)
{
- int cg, error;
+ int cg, error = 0;
struct buf *nbp;
struct fs *fs = VTOI(vp)->i_fs;
- error = UFS_WAPBL_BEGIN(vp->v_mount);
- if (error)
- return error;
if (redo != NULL)
*redo = 0;
if (passno == 1)
@@ -874,18 +876,24 @@
for (cg = 0; cg < fs->fs_ncg; cg++) {
if (passno == 2 && ACTIVECG_ISSET(fs, cg))
continue;
+
if (redo != NULL)
*redo += 1;
+ error = UFS_WAPBL_BEGIN(vp->v_mount);
+ if (error)
+ return error;
error = ffs_balloc(vp, lfragtosize(fs, cgtod(fs, cg)),
fs->fs_bsize, curlwp->l_cred, 0, &nbp);
- if (error)
+ if (error) {
+ UFS_WAPBL_END(vp->v_mount);
break;
+ }
error = cgaccount1(cg, vp, nbp->b_data, passno);
bawrite(nbp);
+ UFS_WAPBL_END(vp->v_mount);
if (error)
break;
}
- UFS_WAPBL_END(vp->v_mount);
return error;
}
@@ -992,8 +1000,14 @@
struct lwp *l = curlwp;
void *bap;
struct buf *bp;
+ struct mount *mp;
ns = UFS_FSNEEDSWAP(fs);
+ mp = snapvp->v_mount;
+
+ error = UFS_WAPBL_BEGIN(mp);
+ if (error)
+ return error;
/*
* Prepare to expunge the inode. If its inode block has not
* yet been copied, then allocate and fill the copy.
@@ -1011,8 +1025,10 @@
if (! error)
error = rwfsblk(snapvp, B_READ, bp->b_data, lbn);
}
- if (error)
+ if (error) {
+ UFS_WAPBL_END(mp);
return error;
+ }
/*
* Set a snapshot inode to be a zero length file, regular files
* or unlinked snapshots to be completely unallocated.
@@ -1039,6 +1055,7 @@
memset(&dip2->di_db[0], 0, (NDADDR + NIADDR) * sizeof(int64_t));
}
bdwrite(bp);
+ UFS_WAPBL_END(mp);
/*
* Now go through and expunge all the blocks in the file
* using the function requested.
@@ -1048,13 +1065,15 @@
bap = &cancelip->i_ffs1_db[0];
else
bap = &cancelip->i_ffs2_db[0];
- if ((error = (*acctfunc)(snapvp, bap, 0, NDADDR, fs, 0, expungetype)))
+ error = (*acctfunc)(snapvp, bap, 0, NDADDR, fs, 0, expungetype);
+ if (error)
return (error);
if (fs->fs_magic == FS_UFS1_MAGIC)
bap = &cancelip->i_ffs1_ib[0];
else
bap = &cancelip->i_ffs2_ib[0];
- if ((error = (*acctfunc)(snapvp, bap, 0, NIADDR, fs, -1, expungetype)))
+ error = (*acctfunc)(snapvp, bap, 0, NIADDR, fs, -1, expungetype);
+ if (error)
return (error);
blksperindir = 1;
lbn = -NDADDR;
@@ -1170,12 +1189,17 @@
{
struct inode *ip = VTOI(vp);
struct lwp *l = curlwp;
+ struct mount *mp = vp->v_mount;
daddr_t blkno;
daddr_t lbn;
struct buf *ibp;
- int error;
+ int error, n;
+ const int wbreak = blocks_in_journal(VFSTOUFS(mp)->um_fs)/8;
- for ( ; oldblkp < lastblkp; oldblkp++) {
+ error = UFS_WAPBL_BEGIN(mp);
+ if (error)
+ return error;
+ for ( n = 0; oldblkp < lastblkp; oldblkp++) {
blkno = idb_get(ip, bap, oldblkp);
if (blkno == 0 || blkno == BLK_NOCOPY || blkno == BLK_SNAP)
continue;
@@ -1187,7 +1211,7 @@
error = ffs_balloc(vp, lblktosize(fs, (off_t)lbn),
fs->fs_bsize, l->l_cred, B_METAONLY, &ibp);
if (error)
- return (error);
+ break;
blkno = idb_get(ip, ibp->b_data,
(lbn - NDADDR) % NINDIR(fs));
}
@@ -1211,8 +1235,15 @@
bdwrite(ibp);
}
}
+ if (wbreak > 0 && (++n % wbreak) == 0) {
+ UFS_WAPBL_END(mp);
+ error = UFS_WAPBL_BEGIN(mp);
+ if (error)
+ return error;
+ }
}
- return (0);
+ UFS_WAPBL_END(mp);
+ return error;
}
/*
@@ -1224,16 +1255,21 @@
{
daddr_t blkno;
struct inode *ip;
+ struct mount *mp = vp->v_mount;
ino_t inum;
- int acctit;
+ int acctit, error, n;
+ const int wbreak = blocks_in_journal(VFSTOUFS(mp)->um_fs)/8;
+ error = UFS_WAPBL_BEGIN(mp);
+ if (error)
+ return error;
ip = VTOI(vp);
inum = ip->i_number;
if (lblkno == -1)
acctit = 0;
else
acctit = 1;
- for ( ; oldblkp < lastblkp; oldblkp++, lblkno++) {
+ for ( n = 0; oldblkp < lastblkp; oldblkp++, lblkno++) {
blkno = idb_get(ip, bap, oldblkp);
if (blkno == 0 || blkno == BLK_NOCOPY)
continue;
@@ -1242,7 +1278,14 @@
if (blkno == BLK_SNAP)
blkno = blkstofrags(fs, lblkno);
ffs_blkfree_snap(fs, vp, blkno, fs->fs_bsize, inum);
+ if (wbreak > 0 && (++n % wbreak) == 0) {
+ UFS_WAPBL_END(mp);
+ error = UFS_WAPBL_BEGIN(mp);
+ if (error)
+ return error;
+ }
}
+ UFS_WAPBL_END(mp);
return (0);
}
#endif /* defined(FFS_NO_SNAPSHOT) */
@@ -1309,7 +1352,8 @@
struct snap_info *si;
struct lwp *l = curlwp;
daddr_t numblks, blkno, dblk;
- int error, loc, last;
+ int error, loc, last, n;
+ const int wbreak = blocks_in_journal(fs)/8;
si = VFSTOUFS(mp)->um_snapinfo;
/*
@@ -1357,7 +1401,7 @@
}
}
numblks = howmany(ip->i_size, fs->fs_bsize);
- for (blkno = NDADDR; blkno < numblks; blkno += NINDIR(fs)) {
+ for (blkno = NDADDR, n = 0; blkno < numblks; blkno += NINDIR(fs)) {
error = ffs_balloc(vp, lblktosize(fs, (off_t)blkno),
fs->fs_bsize, l->l_cred, B_METAONLY, &ibp);
if (error)
@@ -1367,6 +1411,12 @@
else
last = fs->fs_size - blkno;
for (loc = 0; loc < last; loc++) {
+ if (wbreak > 0 && (++n % wbreak) == 0) {
+ UFS_WAPBL_END(mp);
+ error = UFS_WAPBL_BEGIN(mp);
+ if (error)
+ panic("UFS_WAPBL_BEGIN failed");
+ }
dblk = idb_get(ip, ibp->b_data, loc);
if (dblk == BLK_NOCOPY || dblk == BLK_SNAP)
idb_assign(ip, ibp->b_data, loc, 0);
@@ -2141,6 +2191,33 @@
}
/*
+ * Number of blocks that fit into the journal or zero if not logging.
+ */
+static int
+blocks_in_journal(struct fs *fs)
+{
+ off_t bpj;
+
+ if ((fs->fs_flags & FS_DOWAPBL) == 0)
+ return 0;
+ bpj = 1;
+ if (fs->fs_journal_version == UFS_WAPBL_VERSION) {
+ switch (fs->fs_journal_location) {
+ case UFS_WAPBL_JOURNALLOC_END_PARTITION:
+ bpj = (off_t)fs->fs_journallocs[UFS_WAPBL_EPART_BLKSZ]*
+ fs->fs_journallocs[UFS_WAPBL_EPART_COUNT];
+ break;
+ case UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM:
+ bpj = (off_t)fs->fs_journallocs[UFS_WAPBL_INFS_BLKSZ]*
+ fs->fs_journallocs[UFS_WAPBL_INFS_COUNT];
+ break;
+ }
+ }
+ bpj /= fs->fs_bsize;
+ return (bpj > 0 ? bpj : 1);
+}
+
+/*
* Get/Put direct block from inode or buffer containing disk addresses. Take
* care for fs type (UFS1/UFS2) and byte swapping. These functions should go
* into a global include.