X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/4645c316f1687860ea91a23109b295f29f5ce6f9..527a0d68f20b15c964d4fcc3b161fe4db7332ccd:/usr/src/sys/ufs/lfs/lfs_segment.c?ds=inline diff --git a/usr/src/sys/ufs/lfs/lfs_segment.c b/usr/src/sys/ufs/lfs/lfs_segment.c index 400fbbd326..ad524b26dc 100644 --- a/usr/src/sys/ufs/lfs/lfs_segment.c +++ b/usr/src/sys/ufs/lfs/lfs_segment.c @@ -4,7 +4,7 @@ * * %sccs.include.redist.c% * - * @(#)lfs_segment.c 7.26 (Berkeley) %G% + * @(#)lfs_segment.c 7.32 (Berkeley) %G% */ #include @@ -32,21 +32,7 @@ #include #include -/* In-memory description of a segment about to be written. */ -struct segment { - struct buf **bpp; /* pointer to buffer array */ - struct buf **cbpp; /* pointer to next available bp */ - struct buf *ibp; /* buffer pointer to inode page */ - struct finfo *fip; /* current fileinfo pointer */ - void *segsum; /* segment summary info */ - u_long ninodes; /* number of inodes in this segment */ - u_long seg_bytes_left; /* bytes left in segment */ - u_long sum_bytes_left; /* bytes left in summary block */ - u_long seg_number; /* number of this segment */ -#define SEGM_CKP 0x01 /* doing a checkpoint */ - u_long seg_flags; /* run-time flags for this segment */ -}; - +#define MAX_ACTIVE 10 /* * Determine if it's OK to start a partial in this segment, or if we need * to go on to a new segment. @@ -58,18 +44,17 @@ struct segment { void lfs_callback __P((struct buf *)); void lfs_gather __P((struct lfs *, struct segment *, struct vnode *, int (*) __P((struct lfs *, struct buf *)))); +int lfs_gatherblock __P((struct segment *, struct buf *, int *)); void lfs_initseg __P((struct lfs *, struct segment *)); void lfs_iset __P((struct inode *, daddr_t, time_t)); int lfs_match_data __P((struct lfs *, struct buf *)); int lfs_match_dindir __P((struct lfs *, struct buf *)); int lfs_match_indir __P((struct lfs *, struct buf *)); int lfs_match_tindir __P((struct lfs *, struct buf *)); -struct buf * - lfs_newbuf __P((struct lfs *, daddr_t, size_t)); void lfs_newseg __P((struct lfs *)); void lfs_shellsort __P((struct buf **, daddr_t *, register int)); -void lfs_updatemeta __P((struct lfs *, - struct segment *, struct vnode *, daddr_t *, struct buf **, int)); +void lfs_supercallback __P((struct buf *)); +void lfs_updatemeta __P((struct segment *)); void lfs_writefile __P((struct lfs *, struct segment *, struct vnode *)); int lfs_writeinode __P((struct lfs *, struct segment *, struct inode *)); int lfs_writeseg __P((struct lfs *, struct segment *)); @@ -95,9 +80,6 @@ lfs_vflush(vp) struct segment *sp; int error, s; -#ifdef VERBOSE - printf("lfs_vflush\n"); -#endif fs = VFSTOUFS(vp->v_mount)->um_lfs; lfs_seglock(fs); @@ -110,7 +92,7 @@ lfs_vflush(vp) sp->bpp = malloc(((LFS_SUMMARY_SIZE - sizeof(SEGSUM)) / sizeof(daddr_t) + 1) * sizeof(struct buf *), M_SEGMENT, M_WAITOK); sp->seg_flags = SEGM_CKP; - lfs_initseg(fs, sp); + sp->vp = NULL; /* * Keep a cumulative count of the outstanding I/O operations. If the @@ -124,11 +106,11 @@ lfs_vflush(vp) ip = VTOI(vp); do { + lfs_initseg(fs, sp); do { if (vp->v_dirtyblkhd != NULL) lfs_writefile(fs, sp, vp); } while (lfs_writeinode(fs, sp, ip)); - ip->i_flags &= ~(IMOD | IACC | IUPD | ICHG); } while (lfs_writeseg(fs, sp) && ip->i_number == LFS_IFILE_INUM); @@ -196,7 +178,6 @@ loop: for (vp = mp->mnt_mounth; vp; vp = vp->v_mountf) { if (vp->v_dirtyblkhd != NULL) lfs_writefile(fs, sp, vp); (void) lfs_writeinode(fs, sp, ip); - ip->i_flags &= ~(IMOD | IACC | IUPD | ICHG); } vp->v_flag &= ~VDIROP; vrele(vp); @@ -215,12 +196,27 @@ lfs_segwrite(mp, do_ckp) struct vnode *vp; SEGUSE *segusep; daddr_t ibno; - int error, i, s; + CLEANERINFO *cip; + int clean, error, i, s; -#ifdef VERBOSE - printf("lfs_segwrite\n"); -#endif fs = VFSTOUFS(mp)->um_lfs; + + /* + * If we have fewer than 2 clean segments, wait until cleaner + * writes. + */ + do { + LFS_CLEANERINFO(cip, fs, bp); + clean = cip->clean; + brelse(bp); + if (clean <= 2) { + printf ("segs clean: %d\n", clean); + wakeup(&lfs_allclean_wakeup); + if (error = tsleep(&fs->lfs_avail, PRIBIO + 1, + "lfs writer", 0)) + return (error); + } + } while (clean <= 2 ); lfs_seglock(fs); /* @@ -228,10 +224,12 @@ lfs_segwrite(mp, do_ckp) * the maximum possible number of buffers which can be described in a * single summary block. */ + do_ckp = do_ckp || fs->lfs_nactive > MAX_ACTIVE; sp = malloc(sizeof(struct segment), M_SEGMENT, M_WAITOK); sp->bpp = malloc(((LFS_SUMMARY_SIZE - sizeof(SEGSUM)) / sizeof(daddr_t) + 1) * sizeof(struct buf *), M_SEGMENT, M_WAITOK); sp->seg_flags = do_ckp ? SEGM_CKP : 0; + sp->vp = NULL; lfs_initseg(fs, sp); /* @@ -272,7 +270,7 @@ lfs_segwrite(mp, do_ckp) for (i = fs->lfs_sepb; i--; segusep++) segusep->su_flags &= ~SEGUSE_ACTIVE; - LFS_UBWRITE(bp); + error = VOP_BWRITE(bp); } if (do_ckp || fs->lfs_doifile) { @@ -282,7 +280,6 @@ lfs_segwrite(mp, do_ckp) if (vp->v_dirtyblkhd != NULL) lfs_writefile(fs, sp, vp); (void)lfs_writeinode(fs, sp, ip); - ip->i_flags &= ~(IMOD | IACC | IUPD | ICHG); vput(vp); /* * This should never happen because we just guaranteed @@ -312,6 +309,7 @@ lfs_segwrite(mp, do_ckp) return (error); } splx(s); + fs->lfs_nactive = 0; lfs_writesuper(fs, sp); } else splx(s); @@ -337,9 +335,6 @@ lfs_writefile(fs, sp, vp) struct finfo *fip; IFILE *ifp; -#ifdef VERBOSE - printf("lfs_writefile\n"); -#endif if (sp->seg_bytes_left < fs->lfs_bsize || sp->sum_bytes_left < sizeof(struct finfo)) { (void) lfs_writeseg(fs, sp); @@ -375,6 +370,7 @@ lfs_writefile(fs, sp, vp) sp->fip = (struct finfo *)((caddr_t)fip + sizeof(struct finfo) + sizeof(daddr_t) * (fip->fi_nblocks - 1)); + sp->start_lbp = &sp->fip->fi_blocks[0]; } else sp->sum_bytes_left += sizeof(struct finfo) - sizeof(daddr_t); } @@ -390,12 +386,12 @@ lfs_writeinode(fs, sp, ip) SEGUSE *sup; daddr_t daddr; ino_t ino; - int ndx; + int error, ndx; int redo_ifile = 0; -#ifdef VERBOSE - printf("lfs_writeinode\n"); -#endif + if (!(ip->i_flag & (IMOD | IACC | IUPD | ICHG))) + return; + /* Allocate a new inode block if necessary. */ if (sp->ibp == NULL) { /* Allocate a new segment if necessary. */ @@ -409,8 +405,10 @@ lfs_writeinode(fs, sp, ip) daddr = fs->lfs_offset; fs->lfs_offset += fsbtodb(fs, 1); sp->ibp = *sp->cbpp++ = - lfs_newbuf(fs, daddr, fs->lfs_bsize); - + lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp, daddr, + fs->lfs_bsize); + ++sp->start_bpp; + fs->lfs_avail -= fsbtodb(fs, 1); /* Set remaining space counters. */ sp->seg_bytes_left -= fs->lfs_bsize; sp->sum_bytes_left -= sizeof(daddr_t); @@ -420,10 +418,12 @@ lfs_writeinode(fs, sp, ip) } /* Update the inode times and copy the inode onto the inode page. */ + if (ip->i_flag & IMOD) + --fs->lfs_uinodes; ITIMES(ip, &time, &time); + ip->i_flag &= ~(IMOD | IACC | IUPD | ICHG); bp = sp->ibp; bp->b_un.b_dino[sp->ninodes % INOPB(fs)] = ip->i_din; - /* Increment inode count in segment summary block. */ ++((SEGSUM *)(sp->segsum))->ss_ninos; @@ -443,7 +443,7 @@ lfs_writeinode(fs, sp, ip) LFS_IENTRY(ifp, fs, ino, ibp); daddr = ifp->if_daddr; ifp->if_daddr = bp->b_blkno; - LFS_UBWRITE(ibp); + error = VOP_BWRITE(ibp); } /* @@ -451,7 +451,7 @@ lfs_writeinode(fs, sp, ip) * or if the last inode address is in the current partial segment. */ if (daddr != LFS_UNUSED_DADDR && - !(daddr >= fs->lfs_curseg && daddr <= ifp->if_daddr) ) { + !(daddr >= fs->lfs_lastpseg && daddr <= bp->b_blkno)) { LFS_SEGENTRY(sup, fs, datosn(fs, daddr), bp); #ifdef DIAGNOSTIC if (sup->su_nbytes < sizeof(struct dinode)) { @@ -462,12 +462,69 @@ lfs_writeinode(fs, sp, ip) } #endif sup->su_nbytes -= sizeof(struct dinode); - LFS_UBWRITE(bp); - redo_ifile = (ino == LFS_IFILE_INUM && !(bp->b_flags & B_GATHERED)); + redo_ifile = + (ino == LFS_IFILE_INUM && !(bp->b_flags & B_GATHERED)); + error = VOP_BWRITE(bp); } return (redo_ifile); } +int +lfs_gatherblock(sp, bp, sptr) + struct segment *sp; + struct buf *bp; + int *sptr; +{ + struct lfs *fs; + int version; + + /* + * If full, finish this segment. We may be doing I/O, so + * release and reacquire the splbio(). + */ +#ifdef DIAGNOSTIC + if (sp->vp == NULL) + panic ("lfs_gatherblock: Null vp in segment"); +#endif + fs = sp->fs; + if (sp->sum_bytes_left < sizeof(daddr_t) || + sp->seg_bytes_left < fs->lfs_bsize) { + if (sptr) + splx(*sptr); + lfs_updatemeta(sp); + + /* Add the current file to the segment summary. */ + ++((SEGSUM *)(sp->segsum))->ss_nfinfo; + + version = sp->fip->fi_version; + (void) lfs_writeseg(fs, sp); + lfs_initseg(fs, sp); + + sp->fip->fi_version = version; + sp->fip->fi_ino = VTOI(sp->vp)->i_number; + + sp->sum_bytes_left -= + sizeof(struct finfo) - sizeof(daddr_t); + + if (sptr) + *sptr = splbio(); + return(1); + } + + /* Insert into the buffer list, update the FINFO block. */ +if (bp->b_vp == sp->fs->lfs_ivnode && +((bp->b_lblkno == 0 && (bp->b_un.b_daddr[0] > 26 || bp->b_un.b_daddr[1] > 26)) || +(bp->b_lblkno > 2))) + printf ("Bad ifile block\n"); + bp->b_flags |= B_GATHERED; + *sp->cbpp++ = bp; + sp->fip->fi_blocks[sp->fip->fi_nblocks++] = bp->b_lblkno; + + sp->sum_bytes_left -= sizeof(daddr_t); + sp->seg_bytes_left -= bp->b_bufsize; + return(0); +} + void lfs_gather(fs, sp, vp, match) struct lfs *fs; @@ -475,25 +532,12 @@ lfs_gather(fs, sp, vp, match) struct vnode *vp; int (*match) __P((struct lfs *, struct buf *)); { - struct buf **bpp, *bp; -struct buf *lastbp; - struct finfo *fip; - struct inode *ip; - daddr_t *lbp, *start_lbp; - u_long version; + struct buf *bp; int s; -#ifdef VERBOSE - printf("lfs_gather\n"); -#endif - ip = VTOI(vp); - bpp = sp->cbpp; - fip = sp->fip; - start_lbp = lbp = &fip->fi_blocks[fip->fi_nblocks]; - -loop: s = splbio(); - lastbp = NULL; - for (bp = vp->v_dirtyblkhd; bp; lastbp = bp, bp = bp->b_blockf) { + sp->vp = vp; + s = splbio(); +loop: for (bp = vp->v_dirtyblkhd; bp; bp = bp->b_blockf) { if (bp->b_flags & B_BUSY || !match(fs, bp) || bp->b_flags & B_GATHERED) continue; @@ -503,85 +547,50 @@ loop: s = splbio(); if (!(bp->b_flags & B_LOCKED)) panic("lfs_gather: bp not B_LOCKED"); #endif - /* - * If full, finish this segment. We may be doing I/O, so - * release and reacquire the splbio(). - */ - if (sp->sum_bytes_left < sizeof(daddr_t) || - sp->seg_bytes_left < fs->lfs_bsize) { - splx(s); - lfs_updatemeta(fs, - sp, vp, start_lbp, bpp, lbp - start_lbp); - - /* Add the current file to the segment summary. */ - ++((SEGSUM *)(sp->segsum))->ss_nfinfo; - - version = fip->fi_version; - (void) lfs_writeseg(fs, sp); - lfs_initseg(fs, sp); - - fip = sp->fip; - fip->fi_version = version; - fip->fi_ino = ip->i_number; - start_lbp = lbp = fip->fi_blocks; - - sp->sum_bytes_left -= - sizeof(struct finfo) - sizeof(daddr_t); - - bpp = sp->cbpp; + if (lfs_gatherblock(sp, bp, &s)) goto loop; - } - - /* Insert into the buffer list, update the FINFO block. */ - bp->b_flags |= B_GATHERED; - *sp->cbpp++ = bp; - ++fip->fi_nblocks; - *lbp++ = bp->b_lblkno; - - sp->sum_bytes_left -= sizeof(daddr_t); - sp->seg_bytes_left -= bp->b_bufsize; } splx(s); - lfs_updatemeta(fs, sp, vp, start_lbp, bpp, lbp - start_lbp); + lfs_updatemeta(sp); + sp->vp = NULL; } + /* * Update the metadata that points to the blocks listed in the FINFO * array. */ void -lfs_updatemeta(fs, sp, vp, lbp, bpp, nblocks) - struct lfs *fs; +lfs_updatemeta(sp) struct segment *sp; - struct vnode *vp; - daddr_t *lbp; - struct buf **bpp; - int nblocks; { SEGUSE *sup; struct buf *bp; + struct lfs *fs; + struct vnode *vp; INDIR a[NIADDR], *ap; struct inode *ip; daddr_t daddr, lbn, off; - int db_per_fsb, error, i, num; + int db_per_fsb, error, i, nblocks, num; -#ifdef VERBOSE - printf("lfs_updatemeta\n"); -#endif - if (nblocks == 0) + vp = sp->vp; + nblocks = &sp->fip->fi_blocks[sp->fip->fi_nblocks] - sp->start_lbp; + if (vp == NULL || nblocks == 0) return; /* Sort the blocks. */ - lfs_shellsort(bpp, lbp, nblocks); + if (!(sp->seg_flags & SEGM_CLEAN)) + lfs_shellsort(sp->start_bpp, sp->start_lbp, nblocks); /* * Assign disk addresses, and update references to the logical * block and the segment usage information. */ + fs = sp->fs; db_per_fsb = fsbtodb(fs, 1); - for (i = nblocks; i--; ++bpp) { - lbn = *lbp++; - (*bpp)->b_blkno = off = fs->lfs_offset; + for (i = nblocks; i--; ++sp->start_bpp) { + lbn = *sp->start_lbp++; + (*sp->start_bpp)->b_blkno = off = fs->lfs_offset; fs->lfs_offset += db_per_fsb; if (error = lfs_bmaparray(vp, lbn, &daddr, a, &num)) @@ -604,6 +613,7 @@ lfs_updatemeta(fs, sp, vp, lbp, bpp, nblocks) * to get counted for the inode. */ if (bp->b_blkno == -1 && !(bp->b_flags & B_CACHE)) { +printf ("Updatemeta allocating indirect block: shouldn't happen\n"); ip->i_blocks += btodb(fs->lfs_bsize); fs->lfs_bfree -= btodb(fs->lfs_bsize); } @@ -623,7 +633,7 @@ lfs_updatemeta(fs, sp, vp, lbp, bpp, nblocks) } #endif sup->su_nbytes -= fs->lfs_bsize; - LFS_UBWRITE(bp); + error = VOP_BWRITE(bp); } } } @@ -641,9 +651,6 @@ lfs_initseg(fs, sp) struct buf *bp; daddr_t lbn, *lbnp; -#ifdef VERBOSE - printf("lfs_initseg\n"); -#endif /* Advance to the next segment. */ if (!LFS_PARTIAL_FITS(fs)) { /* Wake up any cleaning procs waiting on this file system. */ @@ -672,14 +679,16 @@ lfs_initseg(fs, sp) } fs->lfs_lastpseg = fs->lfs_offset; + sp->fs = fs; sp->ibp = NULL; sp->ninodes = 0; /* Get a new buffer for SEGSUM and enter it into the buffer list. */ sp->cbpp = sp->bpp; - *sp->cbpp = lfs_newbuf(fs, fs->lfs_offset, LFS_SUMMARY_SIZE); + *sp->cbpp = lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp, fs->lfs_offset, + LFS_SUMMARY_SIZE); sp->segsum = (*sp->cbpp)->b_un.b_addr; - ++sp->cbpp; + sp->start_bpp = ++sp->cbpp; fs->lfs_offset += LFS_SUMMARY_SIZE / DEV_BSIZE; /* Set point to SEGSUM, initialize it. */ @@ -690,6 +699,7 @@ lfs_initseg(fs, sp) /* Set pointer to first FINFO, initialize it. */ sp->fip = (struct finfo *)(sp->segsum + sizeof(SEGSUM)); sp->fip->fi_nblocks = 0; + sp->start_lbp = &sp->fip->fi_blocks[0]; sp->seg_bytes_left -= LFS_SUMMARY_SIZE; sp->sum_bytes_left = LFS_SUMMARY_SIZE - sizeof(SEGSUM); @@ -705,19 +715,19 @@ lfs_newseg(fs) CLEANERINFO *cip; SEGUSE *sup; struct buf *bp; - int curseg, isdirty, sn; + int curseg, error, isdirty, sn; -#ifdef VERBOSE - printf("lfs_newseg\n"); -#endif LFS_SEGENTRY(sup, fs, datosn(fs, fs->lfs_nextseg), bp); sup->su_flags |= SEGUSE_DIRTY; - LFS_UBWRITE(bp); + sup->su_nbytes = 0; + sup->su_nsums = 0; + sup->su_ninos = 0; + (void) VOP_BWRITE(bp); LFS_CLEANERINFO(cip, fs, bp); --cip->clean; ++cip->dirty; - LFS_UBWRITE(bp); + (void) VOP_BWRITE(bp); fs->lfs_lastseg = fs->lfs_curseg; fs->lfs_curseg = fs->lfs_nextseg; @@ -732,6 +742,7 @@ lfs_newseg(fs) break; } + ++fs->lfs_nactive; fs->lfs_nextseg = sntoda(fs, sn); } @@ -740,23 +751,25 @@ lfs_writeseg(fs, sp) struct lfs *fs; struct segment *sp; { + extern int locked_queue_count; struct buf **bpp, *bp, *cbp; SEGUSE *sup; SEGSUM *ssp; dev_t i_dev; size_t size; u_long *datap, *dp; - int ch_per_blk, do_again, i, nblocks, num, s; + int ch_per_blk, do_again, error, i, nblocks, num, s; int (*strategy)__P((struct vop_strategy_args *)); struct vop_strategy_args vop_strategy_a; u_short ninos; char *p; -#ifdef VERBOSE - printf("lfs_writeseg\n"); -#endif - /* Checkpoint always writes superblock, even if no data blocks. */ - if ((nblocks = sp->cbpp - sp->bpp) == 0 && !(sp->seg_flags & SEGM_CKP)) + /* + * If there are no buffers other than the segment summary to write + * and it is not a checkpoint, don't do anything. On a checkpoint, + * even if there aren't any buffers, you need to write the superblock. + */ + if ((nblocks = sp->cbpp - sp->bpp) == 1 && !(sp->seg_flags & SEGM_CKP)) return (0); /* @@ -772,7 +785,7 @@ lfs_writeseg(fs, sp) *dp++ = (*++bpp)->b_un.b_words[0]; ssp = (SEGSUM *)sp->segsum; ssp->ss_create = time.tv_sec; - ssp->ss_datasum = cksum(datap, nblocks * sizeof(u_long)); + ssp->ss_datasum = cksum(datap, (nblocks - 1) * sizeof(u_long)); ssp->ss_sumsum = cksum(&ssp->ss_datasum, LFS_SUMMARY_SIZE - sizeof(ssp->ss_sumsum)); free(datap, M_SEGMENT); @@ -782,13 +795,14 @@ lfs_writeseg(fs, sp) ninos = (ssp->ss_ninos + INOPB(fs) - 1) / INOPB(fs); sup->su_nbytes += nblocks - 1 - ninos << fs->lfs_bshift; sup->su_nbytes += ssp->ss_ninos * sizeof(struct dinode); + sup->su_nbytes += LFS_SUMMARY_SIZE; sup->su_lastmod = time.tv_sec; sup->su_flags |= SEGUSE_ACTIVE; sup->su_ninos += ninos; ++sup->su_nsums; - LFS_UBWRITE(bp); - fs->lfs_bfree -= (fsbtodb(fs, ninos) + LFS_SUMMARY_SIZE / DEV_BSIZE); do_again = !(bp->b_flags & B_GATHERED); + (void)VOP_BWRITE(bp); + fs->lfs_bfree -= (fsbtodb(fs, ninos) + LFS_SUMMARY_SIZE / DEV_BSIZE); i_dev = VTOI(fs->lfs_ivnode)->i_dev; strategy = VTOI(fs->lfs_ivnode)->i_devvp->v_op[VOFFSET(vop_strategy)]; @@ -817,29 +831,54 @@ lfs_writeseg(fs, sp) i -= num; size = num * fs->lfs_bsize; - cbp = lfs_newbuf(fs, (*bpp)->b_blkno, 0); + cbp = lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp, + (*bpp)->b_blkno, size); cbp->b_dev = i_dev; - cbp->b_flags = B_ASYNC | B_BUSY | B_CALL; - cbp->b_iodone = lfs_callback; - cbp->b_saveaddr = cbp->b_un.b_addr; - cbp->b_un.b_addr = malloc(size, M_SEGMENT, M_WAITOK); + cbp->b_flags |= B_ASYNC | B_BUSY; s = splbio(); ++fs->lfs_iocount; for (p = cbp->b_un.b_addr; num--;) { bp = *bpp++; - bcopy(bp->b_un.b_addr, p, bp->b_bcount); + /* + * Fake buffers from the cleaner are marked as B_INVAL. + * We need to copy the data from user space rather than + * from the buffer indicated. + * XXX == what do I do on an error? + */ + if (bp->b_flags & B_INVAL) { + if (copyin(bp->b_saveaddr, p, bp->b_bcount)) + panic("lfs_writeseg: copyin failed"); + } else + bcopy(bp->b_un.b_addr, p, bp->b_bcount); p += bp->b_bcount; - bp->b_flags &= ~(B_DONE | B_ERROR | B_READ | B_DELWRI | + if (bp->b_flags & B_LOCKED) + --locked_queue_count; + bp->b_flags &= ~(B_ERROR | B_READ | B_DELWRI | B_LOCKED | B_GATHERED); - if (!(bp->b_flags & (B_NOCACHE | B_INVAL))) { + if (bp->b_flags & B_CALL) { + /* if B_CALL, it was created with newbuf */ + brelvp(bp); + free(bp, M_SEGMENT); + } else { bremfree(bp); reassignbuf(bp, bp->b_vp); + brelse(bp); } - brelse(bp); } + ++cbp->b_vp->v_numoutput; splx(s); cbp->b_bcount = p - cbp->b_un.b_addr; + /* + * XXXX This is a gross and disgusting hack. Since these + * buffers are physically addressed, they hang off the + * device vnode (devvp). As a result, they have no way + * of getting to the LFS superblock or lfs structure to + * keep track of the number of I/O's pending. So, I am + * going to stuff the fs into the saveaddr field of + * the buffer (yuk). + */ + cbp->b_saveaddr = (caddr_t)fs; vop_strategy_a.a_desc = VDESC(vop_strategy); vop_strategy_a.a_bp = cbp; (strategy)(&vop_strategy_a); @@ -855,32 +894,35 @@ lfs_writesuper(fs, sp) struct buf *bp; dev_t i_dev; int (*strategy) __P((struct vop_strategy_args *)); + int s; struct vop_strategy_args vop_strategy_a; -#ifdef VERBOSE - printf("lfs_writesuper\n"); -#endif i_dev = VTOI(fs->lfs_ivnode)->i_dev; strategy = VTOI(fs->lfs_ivnode)->i_devvp->v_op[VOFFSET(vop_strategy)]; /* Checksum the superblock and copy it into a buffer. */ fs->lfs_cksum = cksum(fs, sizeof(struct lfs) - sizeof(fs->lfs_cksum)); - bp = lfs_newbuf(fs, fs->lfs_sboffs[0], LFS_SBPAD); + bp = lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp, fs->lfs_sboffs[0], + LFS_SBPAD); *bp->b_un.b_lfs = *fs; /* Write the first superblock (wait). */ bp->b_dev = i_dev; bp->b_flags |= B_BUSY; - bp->b_flags &= ~(B_DONE | B_ERROR | B_READ | B_DELWRI); + bp->b_flags &= ~(B_DONE | B_CALL | B_ERROR | B_READ | B_DELWRI); vop_strategy_a.a_desc = VDESC(vop_strategy); vop_strategy_a.a_bp = bp; + s = splbio(); + bp->b_vp->v_numoutput += 2; + splx(s); (strategy)(&vop_strategy_a); biowait(bp); /* Write the second superblock (don't wait). */ bp->b_blkno = bp->b_lblkno = fs->lfs_sboffs[1]; - bp->b_flags |= B_ASYNC | B_BUSY; + bp->b_flags |= B_CALL | B_ASYNC | B_BUSY; bp->b_flags &= ~(B_DONE | B_ERROR | B_READ | B_DELWRI); + bp->b_iodone = lfs_supercallback; (strategy)(&vop_strategy_a); } @@ -933,29 +975,27 @@ lfs_match_tindir(fs, bp) * Allocate a new buffer header. */ struct buf * -lfs_newbuf(fs, daddr, size) - struct lfs *fs; +lfs_newbuf(vp, daddr, size) + struct vnode *vp; daddr_t daddr; size_t size; { struct buf *bp; - -#ifdef VERBOSE - printf("lfs_newbuf\n"); -#endif - bp = getnewbuf(); - bremhash(bp); - bgetvp(fs->lfs_ivnode, bp); - bp->b_bcount = 0; + size_t nbytes; + + nbytes = roundup(size, DEV_BSIZE); + bp = malloc(sizeof(struct buf) + nbytes, M_SEGMENT, M_WAITOK); + bzero(bp, sizeof(struct buf) + nbytes); + bgetvp(vp, bp); + bp->b_un.b_addr = (caddr_t)(bp + 1); + bp->b_bufsize = size; + bp->b_bcount = size; bp->b_lblkno = daddr; bp->b_blkno = daddr; bp->b_error = 0; bp->b_resid = 0; - if (size) - allocbuf(bp, size); - bp->b_flags |= B_NOCACHE; - bp->b_saveaddr = NULL; - binshash(bp, &bfreelist[BQ_AGE]); + bp->b_iodone = lfs_callback; + bp->b_flags |= B_BUSY | B_CALL | B_NOCACHE; return (bp); } @@ -965,7 +1005,7 @@ lfs_callback(bp) { struct lfs *fs; - fs = VFSTOUFS(bp->b_vp->v_mount)->um_lfs; + fs = (struct lfs *)bp->b_saveaddr; #ifdef DIAGNOSTIC if (fs->lfs_iocount == 0) panic("lfs_callback: zero iocount\n"); @@ -973,12 +1013,16 @@ lfs_callback(bp) if (--fs->lfs_iocount == 0) wakeup(&fs->lfs_iocount); - if (bp->b_saveaddr) { - free(bp->b_un.b_addr, M_SEGMENT); - bp->b_un.b_addr = bp->b_saveaddr; - bp->b_saveaddr = NULL; - } - brelse(bp); + brelvp(bp); + free(bp, M_SEGMENT); +} + +void +lfs_supercallback(bp) + struct buf *bp; +{ + brelvp(bp); + free(bp, M_SEGMENT); } /* @@ -1019,3 +1063,4 @@ lfs_shellsort(bp_array, lb_array, nmemb) } else break; } +