aboutsummaryrefslogtreecommitdiff
path: root/sys/ufs/ffs/ffs_softdep.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/ufs/ffs/ffs_softdep.c')
-rw-r--r--sys/ufs/ffs/ffs_softdep.c521
1 files changed, 403 insertions, 118 deletions
diff --git a/sys/ufs/ffs/ffs_softdep.c b/sys/ufs/ffs/ffs_softdep.c
index f03615099af9..631c82a34855 100644
--- a/sys/ufs/ffs/ffs_softdep.c
+++ b/sys/ufs/ffs/ffs_softdep.c
@@ -157,6 +157,7 @@ static void clear_inodedeps(struct thread *);
static int flush_pagedep_deps(struct vnode *, struct mount *,
struct diraddhd *);
static int flush_inodedep_deps(struct fs *, ino_t);
+static int flush_deplist(struct allocdirectlst *, int, int *);
static int handle_written_filepage(struct pagedep *, struct buf *);
static void diradd_inode_written(struct diradd *, struct inodedep *);
static int handle_written_inodeblock(struct inodedep *, struct buf *);
@@ -181,7 +182,7 @@ static void free_allocdirect(struct allocdirectlst *,
static int check_inode_unwritten(struct inodedep *);
static int free_inodedep(struct inodedep *);
static void handle_workitem_freeblocks(struct freeblks *, int);
-static void merge_inode_lists(struct inodedep *);
+static void merge_inode_lists(struct allocdirectlst *,struct allocdirectlst *);
static void setup_allocindir_phase2(struct buf *, struct inode *,
struct allocindir *);
static struct allocindir *newallocindir(struct inode *, int, ufs2_daddr_t,
@@ -1041,12 +1042,15 @@ top:
inodedep->id_nlinkdelta = 0;
inodedep->id_savedino1 = NULL;
inodedep->id_savedsize = -1;
+ inodedep->id_savedextsize = -1;
inodedep->id_buf = NULL;
LIST_INIT(&inodedep->id_pendinghd);
LIST_INIT(&inodedep->id_inowait);
LIST_INIT(&inodedep->id_bufwait);
TAILQ_INIT(&inodedep->id_inoupdt);
TAILQ_INIT(&inodedep->id_newinoupdt);
+ TAILQ_INIT(&inodedep->id_extupdt);
+ TAILQ_INIT(&inodedep->id_newextupdt);
ACQUIRE_LOCK(&lk);
LIST_INSERT_HEAD(inodedephd, inodedep, id_hash);
sema_release(&inodedep_in_progress);
@@ -1566,6 +1570,103 @@ handle_workitem_freefrag(freefrag)
}
/*
+ * Set up a dependency structure for an external attributes data block.
+ * This routine follows much of the structure of softdep_setup_allocdirect.
+ * See the description of softdep_setup_allocdirect above for details.
+ */
+void
+softdep_setup_allocext(ip, lbn, newblkno, oldblkno, newsize, oldsize, bp)
+ struct inode *ip;
+ ufs_lbn_t lbn;
+ ufs2_daddr_t newblkno;
+ ufs2_daddr_t oldblkno;
+ long newsize;
+ long oldsize;
+ struct buf *bp;
+{
+ struct allocdirect *adp, *oldadp;
+ struct allocdirectlst *adphead;
+ struct bmsafemap *bmsafemap;
+ struct inodedep *inodedep;
+ struct newblk *newblk;
+
+ MALLOC(adp, struct allocdirect *, sizeof(struct allocdirect),
+ M_ALLOCDIRECT, M_SOFTDEP_FLAGS|M_ZERO);
+ adp->ad_list.wk_type = D_ALLOCDIRECT;
+ adp->ad_lbn = lbn;
+ adp->ad_newblkno = newblkno;
+ adp->ad_oldblkno = oldblkno;
+ adp->ad_newsize = newsize;
+ adp->ad_oldsize = oldsize;
+ adp->ad_state = ATTACHED | EXTDATA;
+ LIST_INIT(&adp->ad_newdirblk);
+ if (newblkno == oldblkno)
+ adp->ad_freefrag = NULL;
+ else
+ adp->ad_freefrag = newfreefrag(ip, oldblkno, oldsize);
+
+ if (newblk_lookup(ip->i_fs, newblkno, 0, &newblk) == 0)
+ panic("softdep_setup_allocext: lost block");
+
+ ACQUIRE_LOCK(&lk);
+ inodedep_lookup(ip->i_fs, ip->i_number, DEPALLOC | NODELAY, &inodedep);
+ adp->ad_inodedep = inodedep;
+
+ if (newblk->nb_state == DEPCOMPLETE) {
+ adp->ad_state |= DEPCOMPLETE;
+ adp->ad_buf = NULL;
+ } else {
+ bmsafemap = newblk->nb_bmsafemap;
+ adp->ad_buf = bmsafemap->sm_buf;
+ LIST_REMOVE(newblk, nb_deps);
+ LIST_INSERT_HEAD(&bmsafemap->sm_allocdirecthd, adp, ad_deps);
+ }
+ LIST_REMOVE(newblk, nb_hash);
+ FREE(newblk, M_NEWBLK);
+
+ WORKLIST_INSERT(&bp->b_dep, &adp->ad_list);
+ if (lbn >= NXADDR) {
+ FREE_LOCK(&lk);
+ panic("softdep_setup_allocext: lbn %d > NXADDR", lbn);
+ }
+ /*
+ * The list of allocdirects must be kept in sorted and ascending
+ * order so that the rollback routines can quickly determine the
+ * first uncommitted block (the size of the file stored on disk
+ * ends at the end of the lowest committed fragment, or if there
+ * are no fragments, at the end of the highest committed block).
+ * Since files generally grow, the typical case is that the new
+ * block is to be added at the end of the list. We speed this
+ * special case by checking against the last allocdirect in the
+ * list before laboriously traversing the list looking for the
+ * insertion point.
+ */
+ adphead = &inodedep->id_newextupdt;
+ oldadp = TAILQ_LAST(adphead, allocdirectlst);
+ if (oldadp == NULL || oldadp->ad_lbn <= lbn) {
+ /* insert at end of list */
+ TAILQ_INSERT_TAIL(adphead, adp, ad_next);
+ if (oldadp != NULL && oldadp->ad_lbn == lbn)
+ allocdirect_merge(adphead, adp, oldadp);
+ FREE_LOCK(&lk);
+ return;
+ }
+ TAILQ_FOREACH(oldadp, adphead, ad_next) {
+ if (oldadp->ad_lbn >= lbn)
+ break;
+ }
+ if (oldadp == NULL) {
+ FREE_LOCK(&lk);
+ panic("softdep_setup_allocext: lost entry");
+ }
+ /* insert in middle of list */
+ TAILQ_INSERT_BEFORE(oldadp, adp, ad_next);
+ if (oldadp->ad_lbn == lbn)
+ allocdirect_merge(adphead, adp, oldadp);
+ FREE_LOCK(&lk);
+}
+
+/*
* Indirect block allocation dependencies.
*
* The same dependencies that exist for a direct block also exist when
@@ -1769,7 +1870,8 @@ setup_allocindir_phase2(bp, ip, aip)
LIST_INIT(&newindirdep->ir_deplisthd);
LIST_INIT(&newindirdep->ir_donehd);
if (bp->b_blkno == bp->b_lblkno) {
- ufs_bmaparray(bp->b_vp, bp->b_lblkno, &blkno, NULL, NULL);
+ ufs_bmaparray(bp->b_vp, bp->b_lblkno, &blkno, bp,
+ NULL, NULL);
bp->b_blkno = blkno;
}
newindirdep->ir_savebp =
@@ -1809,9 +1911,10 @@ setup_allocindir_phase2(bp, ip, aip)
* can release it.
*/
void
-softdep_setup_freeblocks(ip, length)
+softdep_setup_freeblocks(ip, length, flags)
struct inode *ip; /* The inode whose length is to be reduced */
off_t length; /* The new length for the file */
+ int flags; /* IO_EXT and/or IO_NORMAL */
{
struct freeblks *freeblks;
struct inodedep *inodedep;
@@ -1819,6 +1922,7 @@ softdep_setup_freeblocks(ip, length)
struct vnode *vp;
struct buf *bp;
struct fs *fs;
+ ufs2_daddr_t extblocks, datablocks;
int i, delay, error;
fs = ip->i_fs;
@@ -1831,27 +1935,46 @@ softdep_setup_freeblocks(ip, length)
freeblks->fb_previousinum = ip->i_number;
freeblks->fb_devvp = ip->i_devvp;
freeblks->fb_mnt = ITOV(ip)->v_mount;
- freeblks->fb_oldsize = ip->i_size;
- freeblks->fb_newsize = length;
- freeblks->fb_chkcnt = DIP(ip, i_blocks);
- for (i = 0; i < NDADDR; i++) {
- freeblks->fb_dblks[i] = DIP(ip, i_db[i]);
- DIP(ip, i_db[i]) = 0;
- }
- for (i = 0; i < NIADDR; i++) {
- freeblks->fb_iblks[i] = DIP(ip, i_ib[i]);
- DIP(ip, i_ib[i]) = 0;
- }
- DIP(ip, i_blocks) = 0;
- ip->i_size = 0;
- DIP(ip, i_size) = 0;
- /*
- * If the file was removed, then the space being freed was
- * accounted for then (see softdep_filereleased()). If the
- * file is merely being truncated, then we account for it now.
- */
- if ((ip->i_flag & IN_SPACECOUNTED) == 0)
- fs->fs_pendingblocks += freeblks->fb_chkcnt;
+ extblocks = 0;
+ if (fs->fs_magic == FS_UFS2_MAGIC)
+ extblocks = btodb(fragroundup(fs, ip->i_din2->di_extsize));
+ datablocks = DIP(ip, i_blocks) - extblocks;
+ if ((flags & IO_NORMAL) == 0) {
+ freeblks->fb_oldsize = 0;
+ freeblks->fb_chkcnt = 0;
+ } else {
+ freeblks->fb_oldsize = ip->i_size;
+ ip->i_size = 0;
+ DIP(ip, i_size) = 0;
+ freeblks->fb_chkcnt = datablocks;
+ for (i = 0; i < NDADDR; i++) {
+ freeblks->fb_dblks[i] = DIP(ip, i_db[i]);
+ DIP(ip, i_db[i]) = 0;
+ }
+ for (i = 0; i < NIADDR; i++) {
+ freeblks->fb_iblks[i] = DIP(ip, i_ib[i]);
+ DIP(ip, i_ib[i]) = 0;
+ }
+ /*
+ * If the file was removed, then the space being freed was
+ * accounted for then (see softdep_filereleased()). If the
+ * file is merely being truncated, then we account for it now.
+ */
+ if ((ip->i_flag & IN_SPACECOUNTED) == 0)
+ fs->fs_pendingblocks += datablocks;
+ }
+ if ((flags & IO_EXT) == 0) {
+ freeblks->fb_oldextsize = 0;
+ } else {
+ freeblks->fb_oldextsize = ip->i_din2->di_extsize;
+ ip->i_din2->di_extsize = 0;
+ freeblks->fb_chkcnt += extblocks;
+ for (i = 0; i < NXADDR; i++) {
+ freeblks->fb_eblks[i] = ip->i_din2->di_extb[i];
+ ip->i_din2->di_extb[i] = 0;
+ }
+ }
+ DIP(ip, i_blocks) -= freeblks->fb_chkcnt;
/*
* Push the zero'ed inode to to its disk buffer so that we are free
* to delete its dependencies below. Once the dependencies are gone
@@ -1897,9 +2020,18 @@ softdep_setup_freeblocks(ip, length)
* If we still have a bitmap dependency, then the inode has never
* been written to disk, so we can free any fragments without delay.
*/
- merge_inode_lists(inodedep);
- while ((adp = TAILQ_FIRST(&inodedep->id_inoupdt)) != 0)
- free_allocdirect(&inodedep->id_inoupdt, adp, delay);
+ if (flags & IO_NORMAL) {
+ merge_inode_lists(&inodedep->id_newinoupdt,
+ &inodedep->id_inoupdt);
+ while ((adp = TAILQ_FIRST(&inodedep->id_inoupdt)) != 0)
+ free_allocdirect(&inodedep->id_inoupdt, adp, delay);
+ }
+ if (flags & IO_EXT) {
+ merge_inode_lists(&inodedep->id_newextupdt,
+ &inodedep->id_extupdt);
+ while ((adp = TAILQ_FIRST(&inodedep->id_extupdt)) != 0)
+ free_allocdirect(&inodedep->id_extupdt, adp, delay);
+ }
FREE_LOCK(&lk);
bdwrite(bp);
/*
@@ -1911,14 +2043,21 @@ softdep_setup_freeblocks(ip, length)
vp = ITOV(ip);
ACQUIRE_LOCK(&lk);
drain_output(vp, 1);
- while (getdirtybuf(&TAILQ_FIRST(&vp->v_dirtyblkhd), MNT_WAIT)) {
- bp = TAILQ_FIRST(&vp->v_dirtyblkhd);
+restart:
+ TAILQ_FOREACH(bp, &vp->v_dirtyblkhd, b_vnbufs) {
+ if (((flags & IO_EXT) == 0 && (bp->b_xflags & BX_ALTDATA)) ||
+ ((flags & IO_NORMAL) == 0 &&
+ (bp->b_xflags & BX_ALTDATA) == 0))
+ continue;
+ if (getdirtybuf(&bp, MNT_WAIT) == 0)
+ goto restart;
(void) inodedep_lookup(fs, ip->i_number, 0, &inodedep);
deallocate_dependencies(bp, inodedep);
bp->b_flags |= B_INVAL | B_NOCACHE;
FREE_LOCK(&lk);
brelse(bp);
ACQUIRE_LOCK(&lk);
+ goto restart;
}
if (inodedep_lookup(fs, ip->i_number, 0, &inodedep) != 0)
(void) free_inodedep(inodedep);
@@ -2216,6 +2355,8 @@ check_inode_unwritten(inodedep)
LIST_FIRST(&inodedep->id_inowait) != NULL ||
TAILQ_FIRST(&inodedep->id_inoupdt) != NULL ||
TAILQ_FIRST(&inodedep->id_newinoupdt) != NULL ||
+ TAILQ_FIRST(&inodedep->id_extupdt) != NULL ||
+ TAILQ_FIRST(&inodedep->id_newextupdt) != NULL ||
inodedep->id_nlinkdelta != 0)
return (0);
inodedep->id_state |= ALLCOMPLETE;
@@ -2249,6 +2390,8 @@ free_inodedep(inodedep)
LIST_FIRST(&inodedep->id_inowait) != NULL ||
TAILQ_FIRST(&inodedep->id_inoupdt) != NULL ||
TAILQ_FIRST(&inodedep->id_newinoupdt) != NULL ||
+ TAILQ_FIRST(&inodedep->id_extupdt) != NULL ||
+ TAILQ_FIRST(&inodedep->id_newextupdt) != NULL ||
inodedep->id_nlinkdelta != 0 || inodedep->id_savedino1 != NULL)
return (0);
LIST_REMOVE(inodedep, id_hash);
@@ -2288,30 +2431,48 @@ handle_workitem_freeblocks(freeblks, flags)
nblocks = btodb(fs->fs_bsize);
blocksreleased = 0;
/*
- * Indirect blocks first.
+ * Release all extended attribute blocks or frags.
*/
- for (level = (NIADDR - 1); level >= 0; level--) {
- if ((bn = freeblks->fb_iblks[level]) == 0)
- continue;
- if ((error = indir_trunc(freeblks, fsbtodb(fs, bn), level,
- baselbns[level], &blocksreleased)) == 0)
- allerror = error;
- ffs_blkfree(fs, freeblks->fb_devvp, bn, fs->fs_bsize,
- freeblks->fb_previousinum);
- fs->fs_pendingblocks -= nblocks;
- blocksreleased += nblocks;
+ if (freeblks->fb_oldextsize > 0) {
+ for (i = (NXADDR - 1); i >= 0; i--) {
+ if ((bn = freeblks->fb_eblks[i]) == 0)
+ continue;
+ bsize = sblksize(fs, freeblks->fb_oldextsize, i);
+ ffs_blkfree(fs, freeblks->fb_devvp, bn, bsize,
+ freeblks->fb_previousinum);
+ blocksreleased += btodb(bsize);
+ }
}
/*
- * All direct blocks or frags.
+ * Release all data blocks or frags.
*/
- for (i = (NDADDR - 1); i >= 0; i--) {
- if ((bn = freeblks->fb_dblks[i]) == 0)
- continue;
- bsize = sblksize(fs, freeblks->fb_oldsize, i);
- ffs_blkfree(fs, freeblks->fb_devvp, bn, bsize,
- freeblks->fb_previousinum);
- fs->fs_pendingblocks -= btodb(bsize);
- blocksreleased += btodb(bsize);
+ if (freeblks->fb_oldsize > 0) {
+ /*
+ * Indirect blocks first.
+ */
+ for (level = (NIADDR - 1); level >= 0; level--) {
+ if ((bn = freeblks->fb_iblks[level]) == 0)
+ continue;
+ if ((error = indir_trunc(freeblks, fsbtodb(fs, bn),
+ level, baselbns[level], &blocksreleased)) == 0)
+ allerror = error;
+ ffs_blkfree(fs, freeblks->fb_devvp, bn, fs->fs_bsize,
+ freeblks->fb_previousinum);
+ fs->fs_pendingblocks -= nblocks;
+ blocksreleased += nblocks;
+ }
+ /*
+ * All direct blocks or frags.
+ */
+ for (i = (NDADDR - 1); i >= 0; i--) {
+ if ((bn = freeblks->fb_dblks[i]) == 0)
+ continue;
+ bsize = sblksize(fs, freeblks->fb_oldsize, i);
+ ffs_blkfree(fs, freeblks->fb_devvp, bn, bsize,
+ freeblks->fb_previousinum);
+ fs->fs_pendingblocks -= btodb(bsize);
+ blocksreleased += btodb(bsize);
+ }
}
/*
* If we still have not finished background cleanup, then check
@@ -3049,6 +3210,8 @@ softdep_releasefile(ip)
struct inode *ip; /* inode with the zero effective link count */
{
struct inodedep *inodedep;
+ struct fs *fs;
+ int extblocks;
if (ip->i_effnlink > 0)
panic("softdep_filerelease: file still referenced");
@@ -3073,7 +3236,11 @@ softdep_releasefile(ip)
if ((inodedep_lookup(ip->i_fs, ip->i_number, 0, &inodedep)))
inodedep->id_state |= SPACECOUNTED;
FREE_LOCK(&lk);
- ip->i_fs->fs_pendingblocks += DIP(ip, i_blocks);
+ fs = ip->i_fs;
+ extblocks = 0;
+ if (fs->fs_magic == FS_UFS2_MAGIC)
+ extblocks = btodb(fragroundup(fs, ip->i_din2->di_extsize));
+ ip->i_fs->fs_pendingblocks += DIP(ip, i_blocks) - extblocks;
ip->i_fs->fs_pendinginodes += 1;
ip->i_flag |= IN_SPACECOUNTED;
}
@@ -3404,6 +3571,7 @@ initiate_write_inodeblock_ufs1(inodedep, bp)
* If no dependencies, then there is nothing to roll back.
*/
inodedep->id_savedsize = dp->di_size;
+ inodedep->id_savedextsize = 0;
if (TAILQ_FIRST(&inodedep->id_inoupdt) == NULL)
return;
/*
@@ -3556,12 +3724,81 @@ initiate_write_inodeblock_ufs2(inodedep, bp)
* If no dependencies, then there is nothing to roll back.
*/
inodedep->id_savedsize = dp->di_size;
- if (TAILQ_FIRST(&inodedep->id_inoupdt) == NULL)
+ inodedep->id_savedextsize = dp->di_extsize;
+ if (TAILQ_FIRST(&inodedep->id_inoupdt) == NULL &&
+ TAILQ_FIRST(&inodedep->id_extupdt) == NULL)
return;
/*
- * Set the dependencies to busy.
+ * Set the ext data dependencies to busy.
*/
ACQUIRE_LOCK(&lk);
+ for (deplist = 0, adp = TAILQ_FIRST(&inodedep->id_extupdt); adp;
+ adp = TAILQ_NEXT(adp, ad_next)) {
+#ifdef DIAGNOSTIC
+ if (deplist != 0 && prevlbn >= adp->ad_lbn) {
+ FREE_LOCK(&lk);
+ panic("softdep_write_inodeblock: lbn order");
+ }
+ prevlbn = adp->ad_lbn;
+ if (dp->di_extb[adp->ad_lbn] != adp->ad_newblkno) {
+ FREE_LOCK(&lk);
+ panic("%s: direct pointer #%jd mismatch %jd != %jd",
+ "softdep_write_inodeblock",
+ (intmax_t)adp->ad_lbn,
+ (intmax_t)dp->di_extb[adp->ad_lbn],
+ (intmax_t)adp->ad_newblkno);
+ }
+ deplist |= 1 << adp->ad_lbn;
+ if ((adp->ad_state & ATTACHED) == 0) {
+ FREE_LOCK(&lk);
+ panic("softdep_write_inodeblock: Unknown state 0x%x",
+ adp->ad_state);
+ }
+#endif /* DIAGNOSTIC */
+ adp->ad_state &= ~ATTACHED;
+ adp->ad_state |= UNDONE;
+ }
+ /*
+ * The on-disk inode cannot claim to be any larger than the last
+ * fragment that has been written. Otherwise, the on-disk inode
+ * might have fragments that were not the last block in the ext
+ * data which would corrupt the filesystem.
+ */
+ for (lastadp = NULL, adp = TAILQ_FIRST(&inodedep->id_extupdt); adp;
+ lastadp = adp, adp = TAILQ_NEXT(adp, ad_next)) {
+ dp->di_extb[adp->ad_lbn] = adp->ad_oldblkno;
+ /* keep going until hitting a rollback to a frag */
+ if (adp->ad_oldsize == 0 || adp->ad_oldsize == fs->fs_bsize)
+ continue;
+ dp->di_extsize = fs->fs_bsize * adp->ad_lbn + adp->ad_oldsize;
+ for (i = adp->ad_lbn + 1; i < NXADDR; i++) {
+#ifdef DIAGNOSTIC
+ if (dp->di_extb[i] != 0 && (deplist & (1 << i)) == 0) {
+ FREE_LOCK(&lk);
+ panic("softdep_write_inodeblock: lost dep1");
+ }
+#endif /* DIAGNOSTIC */
+ dp->di_extb[i] = 0;
+ }
+ lastadp = NULL;
+ break;
+ }
+ /*
+ * If we have zero'ed out the last allocated block of the ext
+ * data, roll back the size to the last currently allocated block.
+ * We know that this last allocated block is a full-sized as
+ * we already checked for fragments in the loop above.
+ */
+ if (lastadp != NULL &&
+ dp->di_extsize <= (lastadp->ad_lbn + 1) * fs->fs_bsize) {
+ for (i = lastadp->ad_lbn; i >= 0; i--)
+ if (dp->di_extb[i] != 0)
+ break;
+ dp->di_extsize = (i + 1) * fs->fs_bsize;
+ }
+ /*
+ * Set the file data dependencies to busy.
+ */
for (deplist = 0, adp = TAILQ_FIRST(&inodedep->id_inoupdt); adp;
adp = TAILQ_NEXT(adp, ad_next)) {
#ifdef DIAGNOSTIC
@@ -3617,7 +3854,7 @@ initiate_write_inodeblock_ufs2(inodedep, bp)
#ifdef DIAGNOSTIC
if (dp->di_db[i] != 0 && (deplist & (1 << i)) == 0) {
FREE_LOCK(&lk);
- panic("softdep_write_inodeblock: lost dep1");
+ panic("softdep_write_inodeblock: lost dep2");
}
#endif /* DIAGNOSTIC */
dp->di_db[i] = 0;
@@ -3627,7 +3864,7 @@ initiate_write_inodeblock_ufs2(inodedep, bp)
if (dp->di_ib[i] != 0 &&
(deplist & ((1 << NDADDR) << i)) == 0) {
FREE_LOCK(&lk);
- panic("softdep_write_inodeblock: lost dep2");
+ panic("softdep_write_inodeblock: lost dep3");
}
#endif /* DIAGNOSTIC */
dp->di_ib[i] = 0;
@@ -3805,6 +4042,7 @@ static void
handle_allocdirect_partdone(adp)
struct allocdirect *adp; /* the completed allocdirect */
{
+ struct allocdirectlst *listhead;
struct allocdirect *listadp;
struct inodedep *inodedep;
long bsize, delay;
@@ -3822,11 +4060,16 @@ handle_allocdirect_partdone(adp)
* which would corrupt the filesystem. Thus, we cannot free any
* allocdirects after one whose ad_oldblkno claims a fragment as
* these blocks must be rolled back to zero before writing the inode.
- * We check the currently active set of allocdirects in id_inoupdt.
+ * We check the currently active set of allocdirects in id_inoupdt
+ * or id_extupdt as appropriate.
*/
inodedep = adp->ad_inodedep;
bsize = inodedep->id_fs->fs_bsize;
- TAILQ_FOREACH(listadp, &inodedep->id_inoupdt, ad_next) {
+ if (adp->ad_state & EXTDATA)
+ listhead = &inodedep->id_extupdt;
+ else
+ listhead = &inodedep->id_inoupdt;
+ TAILQ_FOREACH(listadp, listhead, ad_next) {
/* found our block */
if (listadp == adp)
break;
@@ -3845,7 +4088,11 @@ handle_allocdirect_partdone(adp)
*/
if (listadp == NULL) {
#ifdef DEBUG
- TAILQ_FOREACH(listadp, &inodedep->id_newinoupdt, ad_next)
+ if (adp->ad_state & EXTDATA)
+ listhead = &inodedep->id_newextupdt;
+ else
+ listhead = &inodedep->id_newinoupdt;
+ TAILQ_FOREACH(listadp, listhead, ad_next)
/* found our block */
if (listadp == adp)
break;
@@ -3868,7 +4115,7 @@ handle_allocdirect_partdone(adp)
listadp = TAILQ_NEXT(adp, ad_next);
if ((adp->ad_state & ALLCOMPLETE) != ALLCOMPLETE)
return;
- free_allocdirect(&inodedep->id_inoupdt, adp, delay);
+ free_allocdirect(listhead, adp, delay);
}
}
@@ -4023,12 +4270,31 @@ handle_written_inodeblock(inodedep, bp)
adp->ad_state |= ATTACHED;
hadchanges = 1;
}
+ for (adp = TAILQ_FIRST(&inodedep->id_extupdt); adp; adp = nextadp) {
+ nextadp = TAILQ_NEXT(adp, ad_next);
+ if (adp->ad_state & ATTACHED) {
+ lk.lkt_held = NOHOLDER;
+ panic("handle_written_inodeblock: new entry");
+ }
+ if (dp2->di_extb[adp->ad_lbn] != adp->ad_oldblkno) {
+ lk.lkt_held = NOHOLDER;
+ panic("%s: direct pointers #%jd %s %jd != %jd",
+ "handle_written_inodeblock",
+ (intmax_t)adp->ad_lbn, "mismatch",
+ (intmax_t)dp2->di_extb[adp->ad_lbn],
+ (intmax_t)adp->ad_oldblkno);
+ }
+ dp2->di_extb[adp->ad_lbn] = adp->ad_newblkno;
+ adp->ad_state &= ~UNDONE;
+ adp->ad_state |= ATTACHED;
+ hadchanges = 1;
+ }
if (hadchanges && (bp->b_flags & B_DELWRI) == 0)
stat_direct_blk_ptrs++;
/*
* Reset the file size to its most up-to-date value.
*/
- if (inodedep->id_savedsize == -1) {
+ if (inodedep->id_savedsize == -1 || inodedep->id_savedextsize == -1) {
lk.lkt_held = NOHOLDER;
panic("handle_written_inodeblock: bad size");
}
@@ -4042,8 +4308,13 @@ handle_written_inodeblock(inodedep, bp)
dp2->di_size = inodedep->id_savedsize;
hadchanges = 1;
}
+ if (dp2->di_extsize != inodedep->id_savedextsize) {
+ dp2->di_extsize = inodedep->id_savedextsize;
+ hadchanges = 1;
+ }
}
inodedep->id_savedsize = -1;
+ inodedep->id_savedextsize = -1;
/*
* If there were any rollbacks in the inode block, then it must be
* marked dirty so that its will eventually get written back in
@@ -4056,6 +4327,8 @@ handle_written_inodeblock(inodedep, bp)
*/
if ((adp = TAILQ_FIRST(&inodedep->id_inoupdt)) != NULL)
handle_allocdirect_partdone(adp);
+ if ((adp = TAILQ_FIRST(&inodedep->id_extupdt)) != NULL)
+ handle_allocdirect_partdone(adp);
/*
* Process deallocations that were held pending until the
* inode had been written to disk. Freeing of the inode
@@ -4119,7 +4392,9 @@ handle_written_inodeblock(inodedep, bp)
/*
* If no outstanding dependencies, free it.
*/
- if (free_inodedep(inodedep) || TAILQ_FIRST(&inodedep->id_inoupdt) == 0)
+ if (free_inodedep(inodedep) ||
+ (TAILQ_FIRST(&inodedep->id_inoupdt) == 0 &&
+ TAILQ_FIRST(&inodedep->id_extupdt) == 0))
return (0);
return (hadchanges);
}
@@ -4358,9 +4633,12 @@ softdep_update_inodeblock(ip, bp, waitfor)
* the in-memory copy of the inode. Once merged process any
* allocdirects that are completed by the merger.
*/
- merge_inode_lists(inodedep);
+ merge_inode_lists(&inodedep->id_newinoupdt, &inodedep->id_inoupdt);
if (TAILQ_FIRST(&inodedep->id_inoupdt) != NULL)
handle_allocdirect_partdone(TAILQ_FIRST(&inodedep->id_inoupdt));
+ merge_inode_lists(&inodedep->id_newextupdt, &inodedep->id_extupdt);
+ if (TAILQ_FIRST(&inodedep->id_extupdt) != NULL)
+ handle_allocdirect_partdone(TAILQ_FIRST(&inodedep->id_extupdt));
/*
* Now that the inode has been pushed into the buffer, the
* operations dependent on the inode being written to disk
@@ -4392,34 +4670,35 @@ softdep_update_inodeblock(ip, bp, waitfor)
}
/*
- * Merge the new inode dependency list (id_newinoupdt) into the old
- * inode dependency list (id_inoupdt). This routine must be called
- * with splbio interrupts blocked.
+ * Merge the a new inode dependency list (such as id_newinoupdt) into an
+ * old inode dependency list (such as id_inoupdt). This routine must be
+ * called with splbio interrupts blocked.
*/
static void
-merge_inode_lists(inodedep)
- struct inodedep *inodedep;
+merge_inode_lists(newlisthead, oldlisthead)
+ struct allocdirectlst *newlisthead;
+ struct allocdirectlst *oldlisthead;
{
struct allocdirect *listadp, *newadp;
- newadp = TAILQ_FIRST(&inodedep->id_newinoupdt);
- for (listadp = TAILQ_FIRST(&inodedep->id_inoupdt); listadp && newadp;) {
+ newadp = TAILQ_FIRST(newlisthead);
+ for (listadp = TAILQ_FIRST(oldlisthead); listadp && newadp;) {
if (listadp->ad_lbn < newadp->ad_lbn) {
listadp = TAILQ_NEXT(listadp, ad_next);
continue;
}
- TAILQ_REMOVE(&inodedep->id_newinoupdt, newadp, ad_next);
+ TAILQ_REMOVE(newlisthead, newadp, ad_next);
TAILQ_INSERT_BEFORE(listadp, newadp, ad_next);
if (listadp->ad_lbn == newadp->ad_lbn) {
- allocdirect_merge(&inodedep->id_inoupdt, newadp,
+ allocdirect_merge(oldlisthead, newadp,
listadp);
listadp = newadp;
}
- newadp = TAILQ_FIRST(&inodedep->id_newinoupdt);
+ newadp = TAILQ_FIRST(newlisthead);
}
- while ((newadp = TAILQ_FIRST(&inodedep->id_newinoupdt)) != NULL) {
- TAILQ_REMOVE(&inodedep->id_newinoupdt, newadp, ad_next);
- TAILQ_INSERT_TAIL(&inodedep->id_inoupdt, newadp, ad_next);
+ while ((newadp = TAILQ_FIRST(newlisthead)) != NULL) {
+ TAILQ_REMOVE(newlisthead, newadp, ad_next);
+ TAILQ_INSERT_TAIL(oldlisthead, newadp, ad_next);
}
}
@@ -4454,6 +4733,8 @@ softdep_fsync(vp)
}
if (LIST_FIRST(&inodedep->id_inowait) != NULL ||
LIST_FIRST(&inodedep->id_bufwait) != NULL ||
+ TAILQ_FIRST(&inodedep->id_extupdt) != NULL ||
+ TAILQ_FIRST(&inodedep->id_newextupdt) != NULL ||
TAILQ_FIRST(&inodedep->id_inoupdt) != NULL ||
TAILQ_FIRST(&inodedep->id_newinoupdt) != NULL) {
FREE_LOCK(&lk);
@@ -4877,9 +5158,7 @@ flush_inodedep_deps(fs, ino)
ino_t ino;
{
struct inodedep *inodedep;
- struct allocdirect *adp;
int error, waitfor;
- struct buf *bp;
/*
* This work is done in two passes. The first pass grabs most
@@ -4894,52 +5173,17 @@ flush_inodedep_deps(fs, ino)
* We give a brief window at the top of the loop to allow
* any pending I/O to complete.
*/
- for (waitfor = MNT_NOWAIT; ; ) {
+ for (error = 0, waitfor = MNT_NOWAIT; ; ) {
+ if (error)
+ return (error);
FREE_LOCK(&lk);
ACQUIRE_LOCK(&lk);
if (inodedep_lookup(fs, ino, 0, &inodedep) == 0)
return (0);
- TAILQ_FOREACH(adp, &inodedep->id_inoupdt, ad_next) {
- if (adp->ad_state & DEPCOMPLETE)
- continue;
- bp = adp->ad_buf;
- if (getdirtybuf(&bp, waitfor) == 0) {
- if (waitfor == MNT_NOWAIT)
- continue;
- break;
- }
- FREE_LOCK(&lk);
- if (waitfor == MNT_NOWAIT) {
- bawrite(bp);
- } else if ((error = BUF_WRITE(bp)) != 0) {
- ACQUIRE_LOCK(&lk);
- return (error);
- }
- ACQUIRE_LOCK(&lk);
- break;
- }
- if (adp != NULL)
- continue;
- TAILQ_FOREACH(adp, &inodedep->id_newinoupdt, ad_next) {
- if (adp->ad_state & DEPCOMPLETE)
- continue;
- bp = adp->ad_buf;
- if (getdirtybuf(&bp, waitfor) == 0) {
- if (waitfor == MNT_NOWAIT)
- continue;
- break;
- }
- FREE_LOCK(&lk);
- if (waitfor == MNT_NOWAIT) {
- bawrite(bp);
- } else if ((error = BUF_WRITE(bp)) != 0) {
- ACQUIRE_LOCK(&lk);
- return (error);
- }
- ACQUIRE_LOCK(&lk);
- break;
- }
- if (adp != NULL)
+ if (flush_deplist(&inodedep->id_inoupdt, waitfor, &error) ||
+ flush_deplist(&inodedep->id_newinoupdt, waitfor, &error) ||
+ flush_deplist(&inodedep->id_extupdt, waitfor, &error) ||
+ flush_deplist(&inodedep->id_newextupdt, waitfor, &error))
continue;
/*
* If pass2, we are done, otherwise do pass 2.
@@ -4957,6 +5201,41 @@ flush_inodedep_deps(fs, ino)
}
/*
+ * Flush an inode dependency list.
+ * Called with splbio blocked.
+ */
+static int
+flush_deplist(listhead, waitfor, errorp)
+ struct allocdirectlst *listhead;
+ int waitfor;
+ int *errorp;
+{
+ struct allocdirect *adp;
+ struct buf *bp;
+
+ TAILQ_FOREACH(adp, listhead, ad_next) {
+ if (adp->ad_state & DEPCOMPLETE)
+ continue;
+ bp = adp->ad_buf;
+ if (getdirtybuf(&bp, waitfor) == 0) {
+ if (waitfor == MNT_NOWAIT)
+ continue;
+ return (1);
+ }
+ FREE_LOCK(&lk);
+ if (waitfor == MNT_NOWAIT) {
+ bawrite(bp);
+ } else if ((*errorp = BUF_WRITE(bp)) != 0) {
+ ACQUIRE_LOCK(&lk);
+ return (1);
+ }
+ ACQUIRE_LOCK(&lk);
+ return (1);
+ }
+ return (0);
+}
+
+/*
* Eliminate a pagedep dependency by flushing out all its diradd dependencies.
* Called with splbio blocked.
*/
@@ -5406,6 +5685,12 @@ softdep_count_dependencies(bp, wantcount)
if (!wantcount)
goto out;
}
+ if (TAILQ_FIRST(&inodedep->id_extupdt)) {
+ /* direct block pointer dependency */
+ retval += 1;
+ if (!wantcount)
+ goto out;
+ }
continue;
case D_INDIRDEP: