diff options
author | mckusick <mckusick@FreeBSD.org> | 2000-07-24 05:28:33 +0000 |
---|---|---|
committer | mckusick <mckusick@FreeBSD.org> | 2000-07-24 05:28:33 +0000 |
commit | acc66855bf5786e46e7a1f2c9805ca96cc90c681 (patch) | |
tree | 293bc9453d98bf984dd4fb4392b47ad92614d53f /sys/ufs/ffs/ffs_snapshot.c | |
parent | 35aeef29b58a4f4acf20b4a13f1326e85affab9e (diff) | |
download | FreeBSD-src-acc66855bf5786e46e7a1f2c9805ca96cc90c681.zip FreeBSD-src-acc66855bf5786e46e7a1f2c9805ca96cc90c681.tar.gz |
This patch corrects the first round of panics and hangs reported
with the new snapshot code.
Update addaliasu to correctly implement the semantics of the old
checkalias function. When a device vnode first comes into existence,
check to see if an anonymous vnode for the same device was created
at boot time by bdevvp(). If so, adopt the bdevvp vnode rather than
creating a new vnode for the device. This corrects a problem which
caused the kernel to panic when taking a snapshot of the root
filesystem.
Change the calling convention of vn_write_suspend_wait() to be the
same as vn_start_write().
Split out softdep_flushworklist() from softdep_flushfiles() so that
it can be used to clear the work queue when suspending filesystem
operations.
Access to buffers becomes recursive so that snapshots can recursively
traverse their indirect blocks using ffs_copyonwrite() when checking
for the need for copy on write when flushing one of their own indirect
blocks. This eliminates a deadlock between the syncer daemon and a
process taking a snapshot.
Ensure that softdep_process_worklist() can never block because of a
snapshot being taken. This eliminates a problem with buffer starvation.
Cleanup change in ffs_sync() which did not synchronously wait when
MNT_WAIT was specified. The result was an unclean filesystem panic
when doing forcible unmount with heavy filesystem I/O in progress.
Return a zero'ed block when reading a block that was not in use at
the time that a snapshot was taken. Normally, these blocks should
never be read. However, the readahead code will occationally read
them which can cause unexpected behavior.
Clean up the debugging code that ensures that no blocks be written
on a filesystem while it is suspended. Snapshots must explicitly
label the blocks that they are writing during the suspension so that
they do not cause a `write on suspended filesystem' panic.
Reorganize ffs_copyonwrite() to eliminate a deadlock and also to
prevent a race condition that would permit the same block to be
copied twice. This change eliminates an unexpected soft updates
inconsistency in fsck caused by the double allocation.
Use bqrelse rather than brelse for buffers that will be needed
soon again by the snapshot code. This improves snapshot performance.
Diffstat (limited to 'sys/ufs/ffs/ffs_snapshot.c')
-rw-r--r-- | sys/ufs/ffs/ffs_snapshot.c | 67 |
1 files changed, 46 insertions, 21 deletions
diff --git a/sys/ufs/ffs/ffs_snapshot.c b/sys/ufs/ffs/ffs_snapshot.c index d749abe..af03143 100644 --- a/sys/ufs/ffs/ffs_snapshot.c +++ b/sys/ufs/ffs/ffs_snapshot.c @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ffs_snapshot.c 8.10 (McKusick) 7/11/00 + * @(#)ffs_snapshot.c 8.11 (McKusick) 7/23/00 * $FreeBSD$ */ @@ -290,6 +290,7 @@ restart: if (fs->fs_cgsize < fs->fs_bsize) bzero(&nbp->b_data[fs->fs_cgsize], fs->fs_bsize - fs->fs_cgsize); + nbp->b_flags |= B_VALIDSUSPWRT; bawrite(nbp); base = cg * fs->fs_fpg / fs->fs_frag; if (base + len > numblks) @@ -311,6 +312,7 @@ restart: indiroff = (base + loc - NDADDR) % NINDIR(fs); for ( ; loc < len; loc++, indiroff++) { if (indiroff >= NINDIR(fs)) { + ibp->b_flags |= B_VALIDSUSPWRT; bawrite(ibp); error = VOP_BALLOC(vp, lblktosize(fs, (off_t)(base + loc)), @@ -325,7 +327,8 @@ restart: continue; ((ufs_daddr_t *)(ibp->b_data))[indiroff] = BLK_NOCOPY; } - brelse(bp); + bqrelse(bp); + ibp->b_flags |= B_VALIDSUSPWRT; bdwrite(ibp); } /* @@ -340,6 +343,7 @@ restart: if (fs->fs_sbsize < fs->fs_bsize) bzero(&nbp->b_data[fs->fs_sbsize], fs->fs_bsize - fs->fs_sbsize); + nbp->b_flags |= B_VALIDSUSPWRT; bawrite(nbp); blkno = fragstoblks(fs, fs->fs_csaddr); len = howmany(fs->fs_cssize, fs->fs_bsize) - 1; @@ -354,6 +358,7 @@ restart: size = fs->fs_cssize % fs->fs_bsize; } bcopy(fs->fs_csp[loc], nbp->b_data, size); + nbp->b_flags |= B_VALIDSUSPWRT; bawrite(nbp); } /* @@ -366,6 +371,7 @@ restart: if (error) goto out1; readblock(nbp, inoblks[loc]); + nbp->b_flags |= B_VALIDSUSPWRT; bdwrite(nbp); } /* @@ -410,6 +416,7 @@ restart: dip->di_blocks = 0; dip->di_flags &= ~(SF_IMMUTABLE | SF_SNAPSHOT); bzero(&dip->di_db[0], (NDADDR + NIADDR) * sizeof(ufs_daddr_t)); + nbp->b_flags |= B_VALIDSUSPWRT; bdwrite(nbp); } /* @@ -422,7 +429,7 @@ restart: if (error) goto out1; copyblkno = fragstoblks(fs, dbtofsb(fs, ibp->b_blkno)); - brelse(ibp); + bqrelse(ibp); error = VOP_BALLOC(vp, lblktosize(fs, (off_t)copyblkno), fs->fs_bsize, p->p_ucred, 0, &nbp); if (error) @@ -434,7 +441,8 @@ restart: goto out1; } bcopy(ibp->b_data, nbp->b_data, fs->fs_bsize); - brelse(ibp); + bqrelse(ibp); + nbp->b_flags |= B_VALIDSUSPWRT; bawrite(nbp); } /* @@ -518,7 +526,7 @@ indiracct(snapvp, cancelvp, level, blkno, lbn, rlbn, remblks, blksperindir) } else { MALLOC(bap, ufs_daddr_t *, fs->fs_bsize, M_DEVBUF, M_WAITOK); bcopy(bp->b_data, (caddr_t)bap, fs->fs_bsize); - brelse(bp); + bqrelse(bp); } error = snapacct(snapvp, &bap[0], &bap[last]); if (error || level == 0) @@ -539,7 +547,7 @@ indiracct(snapvp, cancelvp, level, blkno, lbn, rlbn, remblks, blksperindir) } out: if (snapvp != cancelvp) - brelse(bp); + bqrelse(bp); else FREE(bap, M_DEVBUF); return (error); @@ -578,8 +586,10 @@ snapacct(vp, oldblkp, lastblkp) if (*blkp != 0) panic("snapacct: bad block"); *blkp = BLK_SNAP; - if (lbn >= NDADDR) + if (lbn >= NDADDR) { + ibp->b_flags |= B_VALIDSUSPWRT; bdwrite(ibp); + } } return (0); } @@ -732,7 +742,7 @@ ffs_snapblkfree(freeip, bno, size) default: case BLK_NOCOPY: if (lbn >= NDADDR) - brelse(ibp); + bqrelse(ibp); continue; /* * No previous snapshot claimed the block, so it will be @@ -787,7 +797,7 @@ ffs_snapblkfree(freeip, bno, size) return (1); } if (lbn >= NDADDR) - brelse(ibp); + bqrelse(ibp); /* * Allocate the block into which to do the copy. Note that this * allocation will never require any additional allocations for @@ -933,40 +943,57 @@ ffs_copyonwrite(ap) if (bp->b_vp == vp) continue; /* - * Check to see if block needs to be copied. + * Check to see if block needs to be copied. We have to + * be able to do the VOP_BALLOC without blocking, otherwise + * we may get in a deadlock with another process also + * trying to allocate. If we find outselves unable to + * get the buffer lock, we unlock the snapshot vnode, + * sleep briefly, and try again. */ +retry: + vn_lock(vp, LK_SHARED | LK_RETRY, p); if (lbn < NDADDR) { blkno = ip->i_db[lbn]; } else { - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); p->p_flag |= P_COWINPROGRESS; error = VOP_BALLOC(vp, lblktosize(fs, (off_t)lbn), - fs->fs_bsize, KERNCRED, B_METAONLY, &ibp); + fs->fs_bsize, KERNCRED, B_METAONLY | B_NOWAIT, &ibp); p->p_flag &= ~P_COWINPROGRESS; - VOP_UNLOCK(vp, 0, p); - if (error) - break; + if (error) { + VOP_UNLOCK(vp, 0, p); + if (error != EWOULDBLOCK) + break; + tsleep(vp, p->p_usrpri, "nap", 1); + goto retry; + } indiroff = (lbn - NDADDR) % NINDIR(fs); blkno = ((ufs_daddr_t *)(ibp->b_data))[indiroff]; - brelse(ibp); + bqrelse(ibp); } #ifdef DIAGNOSTIC if (blkno == BLK_SNAP && bp->b_lblkno >= 0) panic("ffs_copyonwrite: bad copy block"); #endif - if (blkno != 0) + if (blkno != 0) { + VOP_UNLOCK(vp, 0, p); continue; + } /* * Allocate the block into which to do the copy. Note that this * allocation will never require any additional allocations for * the snapshot inode. */ - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); p->p_flag |= P_COWINPROGRESS; error = VOP_BALLOC(vp, lblktosize(fs, (off_t)lbn), - fs->fs_bsize, KERNCRED, 0, &cbp); + fs->fs_bsize, KERNCRED, B_NOWAIT, &cbp); p->p_flag &= ~P_COWINPROGRESS; VOP_UNLOCK(vp, 0, p); + if (error) { + if (error != EWOULDBLOCK) + break; + tsleep(vp, p->p_usrpri, "nap", 1); + goto retry; + } #ifdef DEBUG if (snapdebug) { printf("Copyonwrite: snapino %d lbn %d for ", @@ -979,8 +1006,6 @@ ffs_copyonwrite(ap) cbp->b_blkno); } #endif - if (error) - break; /* * If we have already read the old block contents, then * simply copy them to the new block. |