151188Sbostic /* 251188Sbostic * Copyright (c) 1991 Regents of the University of California. 351188Sbostic * All rights reserved. 451188Sbostic * 551188Sbostic * %sccs.include.redist.c% 651188Sbostic * 7*52995Sbostic * @(#)lfs_segment.c 7.15 (Berkeley) 03/18/92 851188Sbostic */ 951188Sbostic 1051490Sbostic #include <sys/param.h> 1151490Sbostic #include <sys/systm.h> 1251490Sbostic #include <sys/namei.h> 1352085Sbostic #include <sys/kernel.h> 1451490Sbostic #include <sys/resourcevar.h> 1551490Sbostic #include <sys/file.h> 1651490Sbostic #include <sys/stat.h> 1751490Sbostic #include <sys/buf.h> 1851490Sbostic #include <sys/proc.h> 1951490Sbostic #include <sys/conf.h> 2051490Sbostic #include <sys/vnode.h> 2151490Sbostic #include <sys/specdev.h> 2251490Sbostic #include <sys/fifo.h> 2351490Sbostic #include <sys/malloc.h> 2451490Sbostic #include <sys/mount.h> 2551188Sbostic 2651499Sbostic #include <ufs/ufs/quota.h> 2751499Sbostic #include <ufs/ufs/inode.h> 2851499Sbostic #include <ufs/ufs/dir.h> 2951499Sbostic #include <ufs/ufs/ufsmount.h> 3051490Sbostic 3151499Sbostic #include <ufs/lfs/lfs.h> 3251499Sbostic #include <ufs/lfs/lfs_extern.h> 3351490Sbostic 3451860Sbostic /* In-memory description of a segment about to be written. */ 3551860Sbostic struct segment { 3652085Sbostic struct buf **bpp; /* pointer to buffer array */ 3752085Sbostic struct buf **cbpp; /* pointer to next available bp */ 3852085Sbostic struct buf *ibp; /* buffer pointer to inode page */ 3952085Sbostic struct finfo *fip; /* current fileinfo pointer */ 4051860Sbostic void *segsum; /* segment summary info */ 4151860Sbostic u_long ninodes; /* number of inodes in this segment */ 4251860Sbostic u_long seg_bytes_left; /* bytes left in segment */ 4351860Sbostic u_long sum_bytes_left; /* bytes left in summary block */ 4451860Sbostic u_long seg_number; /* number of this segment */ 4551860Sbostic #define SEGM_CKP 0x01 /* doing a checkpoint */ 4651860Sbostic u_long seg_flags; /* run-time flags for this segment */ 4751860Sbostic }; 4851860Sbostic 4951188Sbostic /* 5051860Sbostic * Determine if it's OK to start a partial in this segment, or if we need 5151860Sbostic * to go on to a new segment. 5251301Sbostic */ 5351860Sbostic #define LFS_PARTIAL_FITS(fs) \ 5451860Sbostic ((fs)->lfs_dbpseg - ((fs)->lfs_offset - (fs)->lfs_curseg) > \ 5551860Sbostic 1 << (fs)->lfs_fsbtodb) 5651188Sbostic 5752085Sbostic int lfs_callback __P((struct buf *)); 5852085Sbostic void lfs_gather __P((struct lfs *, struct segment *, 5952085Sbostic struct vnode *, int (*) __P((struct lfs *, struct buf *)))); 6052085Sbostic void lfs_initseg __P((struct lfs *, struct segment *)); 6152085Sbostic void lfs_iset __P((struct inode *, daddr_t, time_t)); 6252085Sbostic int lfs_match_data __P((struct lfs *, struct buf *)); 6352085Sbostic int lfs_match_dindir __P((struct lfs *, struct buf *)); 6452085Sbostic int lfs_match_indir __P((struct lfs *, struct buf *)); 6552085Sbostic int lfs_match_tindir __P((struct lfs *, struct buf *)); 6652085Sbostic struct buf * 6752688Sbostic lfs_newbuf __P((struct lfs *, daddr_t, size_t)); 6852077Sbostic void lfs_newseg __P((struct lfs *)); 6952085Sbostic void lfs_shellsort __P((struct buf **, daddr_t *, register int)); 7052077Sbostic void lfs_updatemeta __P((struct lfs *, 7152085Sbostic struct segment *, struct vnode *, daddr_t *, struct buf **, int)); 7252085Sbostic void lfs_writefile __P((struct lfs *, struct segment *, struct vnode *)); 7352085Sbostic void lfs_writeinode __P((struct lfs *, struct segment *, struct inode *)); 7452085Sbostic void lfs_writeseg __P((struct lfs *, struct segment *)); 7552085Sbostic void lfs_writesuper __P((struct lfs *, struct segment *)); 7651188Sbostic 7751860Sbostic int lfs_allclean_wakeup; /* Cleaner wakeup address. */ 7851860Sbostic 7952328Sbostic /* 8052328Sbostic * Ifile and meta data blocks are not marked busy, so segment writes MUST be 8152328Sbostic * single threaded. Currently, there are two paths into lfs_segwrite, sync() 8252328Sbostic * and getnewbuf(). They both mark the file system busy. Lfs_vflush() 8352328Sbostic * explicitly marks the file system busy. So lfs_segwrite is safe. I think. 8452328Sbostic */ 8552328Sbostic 8651188Sbostic int 8752328Sbostic lfs_vflush(vp) 8852328Sbostic struct vnode *vp; 8952328Sbostic { 9052328Sbostic struct inode *ip; 9152328Sbostic struct lfs *fs; 9252328Sbostic struct mount *mp; 9352328Sbostic struct segment *sp; 9452328Sbostic int error, s; 9552328Sbostic 9652328Sbostic #ifdef VERBOSE 9752328Sbostic printf("lfs_vflush\n"); 9852328Sbostic #endif 9952328Sbostic mp = vp->v_mount; 10052328Sbostic fs = VFSTOUFS(mp)->um_lfs; 10152328Sbostic 10252328Sbostic /* 10352328Sbostic * XXX 10452328Sbostic * check flags? 10552328Sbostic * mp->mnt_flag & (MNT_MLOCK|MNT_RDONLY|MNT_MPBUSY) || 10652328Sbostic */ 10752328Sbostic if (vfs_busy(mp)) 10852328Sbostic return (0); 10952328Sbostic 11052328Sbostic /* 11152328Sbostic * Allocate a segment structure and enough space to hold pointers to 11252328Sbostic * the maximum possible number of buffers which can be described in a 11352328Sbostic * single summary block. 11452328Sbostic */ 11552328Sbostic sp = malloc(sizeof(struct segment), M_SEGMENT, M_WAITOK); 11652328Sbostic sp->bpp = malloc(((LFS_SUMMARY_SIZE - sizeof(SEGSUM)) / 11752328Sbostic sizeof(daddr_t) + 1) * sizeof(struct buf *), M_SEGMENT, M_WAITOK); 11852328Sbostic sp->seg_flags = SEGM_CKP; 11952328Sbostic lfs_initseg(fs, sp); 12052328Sbostic 12152328Sbostic /* 12252328Sbostic * Keep a cumulative count of the outstanding I/O operations. If the 12352328Sbostic * disk drive catches up with us it could go to zero before we finish, 12452328Sbostic * so we artificially increment it by one until we've scheduled all of 12552328Sbostic * the writes we intend to do. 12652328Sbostic */ 12752328Sbostic s = splbio(); 12852688Sbostic ++fs->lfs_iocount; 12952328Sbostic splx(s); 13052328Sbostic 13152328Sbostic if (vp->v_dirtyblkhd != NULL) 13252328Sbostic lfs_writefile(fs, sp, vp); 13352328Sbostic ip = VTOI(vp); 13452328Sbostic lfs_writeinode(fs, sp, ip); 13552328Sbostic ip->i_flags &= ~(IMOD | IACC | IUPD | ICHG); 13652328Sbostic 13752328Sbostic lfs_writeseg(fs, sp); 13852328Sbostic 13952328Sbostic /* 14052328Sbostic * If the I/O count is non-zero, sleep until it reaches zero. At the 14152328Sbostic * moment, the user's process hangs around so we can sleep. 14252328Sbostic */ 14352328Sbostic s = splbio(); 14452328Sbostic if (--fs->lfs_iocount && (error = 145*52995Sbostic tsleep(&fs->lfs_iocount, PRIBIO + 1, "lfs vflush", 0))) { 146*52995Sbostic free(sp->bpp, M_SEGMENT); 147*52995Sbostic free(sp, M_SEGMENT); 14852328Sbostic return (error); 149*52995Sbostic } 15052328Sbostic splx(s); 15152328Sbostic vfs_unbusy(mp); 15252328Sbostic 153*52995Sbostic /* 154*52995Sbostic * XXX 155*52995Sbostic * Should be writing a checkpoint? 156*52995Sbostic */ 15752328Sbostic free(sp->bpp, M_SEGMENT); 15852328Sbostic free(sp, M_SEGMENT); 15952328Sbostic 16052328Sbostic return (0); 16152328Sbostic } 16252328Sbostic 16352328Sbostic int 16451215Sbostic lfs_segwrite(mp, do_ckp) 16552085Sbostic struct mount *mp; 16651860Sbostic int do_ckp; /* Do a checkpoint. */ 16751188Sbostic { 16852085Sbostic struct inode *ip; 16951499Sbostic struct lfs *fs; 17052085Sbostic struct segment *sp; 17152085Sbostic struct vnode *vp; 17252328Sbostic int error, islocked, s; 17351188Sbostic 17451860Sbostic #ifdef VERBOSE 17551860Sbostic printf("lfs_segwrite\n"); 17651860Sbostic #endif 17752328Sbostic fs = VFSTOUFS(mp)->um_lfs; 17852085Sbostic 17951860Sbostic /* 18052328Sbostic * Allocate a segment structure and enough space to hold pointers to 18152328Sbostic * the maximum possible number of buffers which can be described in a 18252328Sbostic * single summary block. 18352328Sbostic */ 18452328Sbostic sp = malloc(sizeof(struct segment), M_SEGMENT, M_WAITOK); 18552328Sbostic sp->bpp = malloc(((LFS_SUMMARY_SIZE - sizeof(SEGSUM)) / 18652328Sbostic sizeof(daddr_t) + 1) * sizeof(struct buf *), M_SEGMENT, M_WAITOK); 18752328Sbostic sp->seg_flags = do_ckp ? SEGM_CKP : 0; 18852328Sbostic lfs_initseg(fs, sp); 18952328Sbostic 19052328Sbostic /* 19152688Sbostic * Keep a cumulative count of the outstanding I/O operations. If the 19252688Sbostic * disk drive catches up with us it could go to zero before we finish, 19352688Sbostic * so we artificially increment it by one until we've scheduled all of 19452688Sbostic * the writes we intend to do. If not a checkpoint, we never do the 19552688Sbostic * final decrement, avoiding the wakeup in the callback routine. 19651860Sbostic */ 19752688Sbostic s = splbio(); 19852688Sbostic ++fs->lfs_iocount; 19952688Sbostic splx(s); 20051342Sbostic 20152328Sbostic loop: for (vp = mp->mnt_mounth; vp; vp = vp->v_mountf) { 20251188Sbostic /* 20351188Sbostic * If the vnode that we are about to sync is no longer 20451188Sbostic * associated with this mount point, start over. 20551188Sbostic */ 20651188Sbostic if (vp->v_mount != mp) 20751188Sbostic goto loop; 20851860Sbostic 20952328Sbostic islocked = VOP_ISLOCKED(vp); 21051860Sbostic 21152077Sbostic /* 21252077Sbostic * XXX 21352077Sbostic * This is wrong, I think -- we should just wait until we 21452077Sbostic * get the vnode and go on. Probably going to reschedule 21552077Sbostic * all of the writes we already scheduled... 21652077Sbostic */ 21752328Sbostic if (islocked) 21852328Sbostic VREF(vp); 21952328Sbostic else if (vget(vp)) 22052085Sbostic { 22152085Sbostic printf("lfs_segment: failed to get vnode (tell Keith)!\n"); 22251188Sbostic goto loop; 22352085Sbostic } 22452328Sbostic /* 22552328Sbostic * Write the inode/file if dirty and it's not the 22652328Sbostic * the IFILE. 22752328Sbostic */ 22852328Sbostic ip = VTOI(vp); 22952328Sbostic if ((ip->i_flag & (IMOD | IACC | IUPD | ICHG) || 23052328Sbostic vp->v_dirtyblkhd != NULL) && 23152328Sbostic ip->i_number != LFS_IFILE_INUM) { 23252328Sbostic if (vp->v_dirtyblkhd != NULL) 23352328Sbostic lfs_writefile(fs, sp, vp); 23452328Sbostic lfs_writeinode(fs, sp, ip); 23552328Sbostic ip->i_flags &= ~(IMOD | IACC | IUPD | ICHG); 23652328Sbostic } 23752328Sbostic if (islocked) 23852328Sbostic vrele(vp); 23952328Sbostic else 24052328Sbostic vput(vp); 24151188Sbostic } 24251860Sbostic if (do_ckp) { 24352077Sbostic vp = fs->lfs_ivnode; 24452077Sbostic while (vget(vp)); 24552077Sbostic ip = VTOI(vp); 24652077Sbostic if (vp->v_dirtyblkhd != NULL) 24752077Sbostic lfs_writefile(fs, sp, vp); 24852077Sbostic lfs_writeinode(fs, sp, ip); 24952077Sbostic ip->i_flags &= ~(IMOD | IACC | IUPD | ICHG); 25052077Sbostic vput(vp); 25151860Sbostic } 25251342Sbostic lfs_writeseg(fs, sp); 25351342Sbostic 25451215Sbostic /* 25551860Sbostic * If the I/O count is non-zero, sleep until it reaches zero. At the 25651860Sbostic * moment, the user's process hangs around so we can sleep. 25751215Sbostic */ 25852688Sbostic s = splbio(); 25952688Sbostic --fs->lfs_iocount; 26051860Sbostic if (do_ckp) { 26152688Sbostic if (fs->lfs_iocount && (error = 262*52995Sbostic tsleep(&fs->lfs_iocount, PRIBIO + 1, "lfs sync", 0))) { 263*52995Sbostic free(sp->bpp, M_SEGMENT); 264*52995Sbostic free(sp, M_SEGMENT); 26551915Sbostic return (error); 266*52995Sbostic } 26751860Sbostic splx(s); 26851860Sbostic lfs_writesuper(fs, sp); 26952688Sbostic } else 27052688Sbostic splx(s); 27151215Sbostic 27251927Sbostic free(sp->bpp, M_SEGMENT); 27351927Sbostic free(sp, M_SEGMENT); 27451215Sbostic 27551860Sbostic return (0); 27651188Sbostic } 27751188Sbostic 27851860Sbostic /* 27951860Sbostic * Write the dirty blocks associated with a vnode. 28051860Sbostic */ 28152077Sbostic void 28251860Sbostic lfs_writefile(fs, sp, vp) 28351499Sbostic struct lfs *fs; 28452085Sbostic struct segment *sp; 28552085Sbostic struct vnode *vp; 28651188Sbostic { 28751860Sbostic struct buf *bp; 28852085Sbostic struct finfo *fip; 28951860Sbostic IFILE *ifp; 29051188Sbostic 29151860Sbostic #ifdef VERBOSE 29251860Sbostic printf("lfs_writefile\n"); 29351860Sbostic #endif 29452085Sbostic if (sp->seg_bytes_left < fs->lfs_bsize || 29552085Sbostic sp->sum_bytes_left < sizeof(struct finfo)) { 29652085Sbostic lfs_writeseg(fs, sp); 29752085Sbostic lfs_initseg(fs, sp); 29852085Sbostic } 29952085Sbostic sp->sum_bytes_left -= sizeof(struct finfo) - sizeof(daddr_t); 30051215Sbostic 30152085Sbostic fip = sp->fip; 30252085Sbostic fip->fi_nblocks = 0; 30352085Sbostic fip->fi_ino = VTOI(vp)->i_number; 30452085Sbostic LFS_IENTRY(ifp, fs, fip->fi_ino, bp); 30552085Sbostic fip->fi_version = ifp->if_version; 30652085Sbostic brelse(bp); 30751188Sbostic 30852085Sbostic /* 30952085Sbostic * It may not be necessary to write the meta-data blocks at this point, 31052085Sbostic * as the roll-forward recovery code should be able to reconstruct the 31152085Sbostic * list. 31252085Sbostic */ 31352085Sbostic lfs_gather(fs, sp, vp, lfs_match_data); 31452085Sbostic lfs_gather(fs, sp, vp, lfs_match_indir); 31552085Sbostic lfs_gather(fs, sp, vp, lfs_match_dindir); 31651860Sbostic #ifdef TRIPLE 31752085Sbostic lfs_gather(fs, sp, vp, lfs_match_tindir); 31851860Sbostic #endif 31951342Sbostic 32052085Sbostic fip = sp->fip; 32151860Sbostic #ifdef META 32252085Sbostic printf("lfs_writefile: adding %d blocks\n", fip->fi_nblocks); 32351860Sbostic #endif 32452085Sbostic if (fip->fi_nblocks != 0) { 32552085Sbostic ++((SEGSUM *)(sp->segsum))->ss_nfinfo; 32652085Sbostic sp->fip = 32752085Sbostic (struct finfo *)((caddr_t)fip + sizeof(struct finfo) + 32852085Sbostic sizeof(daddr_t) * (fip->fi_nblocks - 1)); 32952682Sstaelin } else 33052682Sstaelin sp->sum_bytes_left += sizeof(struct finfo) - sizeof(daddr_t); 33151215Sbostic } 33251215Sbostic 33352077Sbostic void 33451915Sbostic lfs_writeinode(fs, sp, ip) 33551915Sbostic struct lfs *fs; 33652085Sbostic struct segment *sp; 33752085Sbostic struct inode *ip; 33851915Sbostic { 33952085Sbostic struct buf *bp, *ibp; 34052077Sbostic IFILE *ifp; 34152682Sstaelin SEGUSE *sup; 34252682Sstaelin daddr_t daddr; 34352077Sbostic ino_t ino; 34451915Sbostic int ndx; 34551915Sbostic 34651915Sbostic #ifdef VERBOSE 34751915Sbostic printf("lfs_writeinode\n"); 34851915Sbostic #endif 34951915Sbostic /* Allocate a new inode block if necessary. */ 35051915Sbostic if (sp->ibp == NULL) { 35151915Sbostic /* Allocate a new segment if necessary. */ 35251915Sbostic if (sp->seg_bytes_left < fs->lfs_bsize || 35351915Sbostic sp->sum_bytes_left < sizeof(daddr_t)) { 35451915Sbostic lfs_writeseg(fs, sp); 35551915Sbostic lfs_initseg(fs, sp); 35651915Sbostic } 35751915Sbostic 35851915Sbostic /* Get next inode block. */ 35952682Sstaelin daddr = fs->lfs_offset; 36051915Sbostic fs->lfs_offset += fsbtodb(fs, 1); 36151915Sbostic sp->ibp = *sp->cbpp++ = 36252688Sbostic lfs_newbuf(fs, daddr, fs->lfs_bsize); 36351915Sbostic 36452688Sbostic /* Set remaining space counters. */ 36551915Sbostic sp->seg_bytes_left -= fs->lfs_bsize; 36651915Sbostic sp->sum_bytes_left -= sizeof(daddr_t); 36752077Sbostic ndx = LFS_SUMMARY_SIZE / sizeof(daddr_t) - 36851915Sbostic sp->ninodes / INOPB(fs) - 1; 36952682Sstaelin ((daddr_t *)(sp->segsum))[ndx] = daddr; 37051915Sbostic } 37151915Sbostic 37252085Sbostic /* Update the inode times and copy the inode onto the inode page. */ 37352077Sbostic ITIMES(ip, &time, &time); 37451915Sbostic bp = sp->ibp; 37552085Sbostic bp->b_un.b_dino[sp->ninodes % INOPB(fs)] = ip->i_din; 37651915Sbostic 37751915Sbostic /* Increment inode count in segment summary block. */ 37851915Sbostic ++((SEGSUM *)(sp->segsum))->ss_ninos; 37951915Sbostic 38051915Sbostic /* If this page is full, set flag to allocate a new page. */ 38151915Sbostic if (++sp->ninodes % INOPB(fs) == 0) 38251915Sbostic sp->ibp = NULL; 38351915Sbostic 38451915Sbostic /* 38552077Sbostic * If updating the ifile, update the super-block. Update the disk 38652077Sbostic * address and access times for this inode in the ifile. 38751915Sbostic */ 38852077Sbostic ino = ip->i_number; 38952077Sbostic if (ino == LFS_IFILE_INUM) 39051915Sbostic fs->lfs_idaddr = bp->b_blkno; 39152077Sbostic 39252077Sbostic LFS_IENTRY(ifp, fs, ino, ibp); 39352682Sstaelin daddr = ifp->if_daddr; 39452077Sbostic ifp->if_daddr = bp->b_blkno; 39552085Sbostic LFS_UBWRITE(ibp); 39652682Sstaelin 39752682Sstaelin if (daddr != LFS_UNUSED_DADDR) { 39852682Sstaelin LFS_SEGENTRY(sup, fs, datosn(fs, daddr), bp); 39952682Sstaelin #ifdef DIAGNOSTIC 40052682Sstaelin if (sup->su_nbytes < sizeof(struct dinode)) 40152819Sbostic /* XXX -- Change to a panic. */ 40252819Sbostic printf("lfs: negative bytes (segment %d)\n", 40352682Sstaelin datosn(fs, daddr)); 40452682Sstaelin #endif 40552682Sstaelin sup->su_nbytes -= sizeof(struct dinode); 40652682Sstaelin LFS_UBWRITE(bp); 40752682Sstaelin } 40851915Sbostic } 40951915Sbostic 41052077Sbostic void 41151215Sbostic lfs_gather(fs, sp, vp, match) 41251499Sbostic struct lfs *fs; 41352085Sbostic struct segment *sp; 41452085Sbostic struct vnode *vp; 41552085Sbostic int (*match) __P((struct lfs *, struct buf *)); 41651215Sbostic { 41752085Sbostic struct buf **bpp, *bp, *nbp; 41852085Sbostic struct finfo *fip; 41952085Sbostic struct inode *ip; 42051215Sbostic daddr_t *lbp, *start_lbp; 42151342Sbostic u_long version; 42251342Sbostic int s; 42351215Sbostic 42451860Sbostic #ifdef VERBOSE 42551860Sbostic printf("lfs_gather\n"); 42651860Sbostic #endif 42751215Sbostic ip = VTOI(vp); 42851215Sbostic bpp = sp->cbpp; 42951215Sbostic fip = sp->fip; 43051215Sbostic start_lbp = lbp = &fip->fi_blocks[fip->fi_nblocks]; 43151215Sbostic 43251215Sbostic s = splbio(); 43351215Sbostic for (bp = vp->v_dirtyblkhd; bp; bp = nbp) { 43451215Sbostic nbp = bp->b_blockf; 43551915Sbostic /* 43651915Sbostic * XXX 43752682Sstaelin * Should sleep on any BUSY buffer if doing an fsync? 43851915Sbostic */ 43952328Sbostic if (bp->b_flags & B_BUSY || !match(fs, bp)) 44051215Sbostic continue; 44151342Sbostic #ifdef DIAGNOSTIC 44251860Sbostic if (!(bp->b_flags & B_DELWRI)) 44351915Sbostic panic("lfs_gather: bp not B_DELWRI"); 44451860Sbostic if (!(bp->b_flags & B_LOCKED)) 44551915Sbostic panic("lfs_gather: bp not B_LOCKED"); 44651342Sbostic #endif 44751860Sbostic /* 44851860Sbostic * If full, finish this segment. We may be doing I/O, so 44951860Sbostic * release and reacquire the splbio(). 45051860Sbostic */ 45151342Sbostic if (sp->sum_bytes_left < sizeof(daddr_t) || 45251215Sbostic sp->seg_bytes_left < fs->lfs_bsize) { 45351215Sbostic splx(s); 45451342Sbostic lfs_updatemeta(fs, 45551860Sbostic sp, vp, start_lbp, bpp, lbp - start_lbp); 45651215Sbostic 45751342Sbostic /* Add the current file to the segment summary. */ 45851342Sbostic ++((SEGSUM *)(sp->segsum))->ss_nfinfo; 45951215Sbostic 46051342Sbostic version = fip->fi_version; 46151860Sbostic lfs_writeseg(fs, sp); 46251915Sbostic lfs_initseg(fs, sp); 46351342Sbostic 46451215Sbostic fip = sp->fip; 46551342Sbostic fip->fi_version = version; 46651215Sbostic fip->fi_ino = ip->i_number; 46751342Sbostic start_lbp = lbp = fip->fi_blocks; 46851342Sbostic 46952682Sstaelin sp->sum_bytes_left -= 47052682Sstaelin sizeof(struct finfo) - sizeof(daddr_t); 47152682Sstaelin 47251215Sbostic bpp = sp->cbpp; 47351215Sbostic s = splbio(); 47451215Sbostic } 47552682Sstaelin 47652682Sstaelin /* Insert into the buffer list, update the FINFO block. */ 47752682Sstaelin *sp->cbpp++ = bp; 47852682Sstaelin ++fip->fi_nblocks; 47952682Sstaelin *lbp++ = bp->b_lblkno; 48052682Sstaelin 48152682Sstaelin sp->sum_bytes_left -= sizeof(daddr_t); 48252682Sstaelin sp->seg_bytes_left -= bp->b_bufsize; 48351188Sbostic } 48451215Sbostic splx(s); 48551860Sbostic lfs_updatemeta(fs, sp, vp, start_lbp, bpp, lbp - start_lbp); 48651188Sbostic } 48751188Sbostic 48851342Sbostic /* 48951342Sbostic * Update the metadata that points to the blocks listed in the FINFO 49051188Sbostic * array. 49151188Sbostic */ 49252077Sbostic void 49351860Sbostic lfs_updatemeta(fs, sp, vp, lbp, bpp, nblocks) 49451499Sbostic struct lfs *fs; 49552085Sbostic struct segment *sp; 49652085Sbostic struct vnode *vp; 49751215Sbostic daddr_t *lbp; 49852085Sbostic struct buf **bpp; 49951215Sbostic int nblocks; 50051188Sbostic { 50151915Sbostic SEGUSE *sup; 50252085Sbostic struct buf *bp; 50351860Sbostic INDIR a[NIADDR], *ap; 50452085Sbostic struct inode *ip; 50551915Sbostic daddr_t daddr, lbn, off; 50651860Sbostic int db_per_fsb, error, i, num; 50751188Sbostic 50851860Sbostic #ifdef VERBOSE 50951860Sbostic printf("lfs_updatemeta\n"); 51051860Sbostic #endif 51151342Sbostic if (nblocks == 0) 51251215Sbostic return; 51351215Sbostic 51451915Sbostic /* Sort the blocks. */ 51552077Sbostic lfs_shellsort(bpp, lbp, nblocks); 51651215Sbostic 51751915Sbostic /* 51851915Sbostic * Assign disk addresses, and update references to the logical 51951915Sbostic * block and the segment usage information. 52051915Sbostic */ 52151860Sbostic db_per_fsb = fsbtodb(fs, 1); 52251915Sbostic for (i = nblocks; i--; ++bpp) { 52351915Sbostic lbn = *lbp++; 52451915Sbostic (*bpp)->b_blkno = off = fs->lfs_offset; 52551860Sbostic fs->lfs_offset += db_per_fsb; 52651215Sbostic 52751860Sbostic if (error = lfs_bmaparray(vp, lbn, &daddr, a, &num)) 52852085Sbostic panic("lfs_updatemeta: lfs_bmaparray %d", error); 52951860Sbostic ip = VTOI(vp); 53051860Sbostic switch (num) { 53151860Sbostic case 0: 53251915Sbostic ip->i_db[lbn] = off; 53351860Sbostic break; 53451860Sbostic case 1: 53551915Sbostic ip->i_ib[a[0].in_off] = off; 53651860Sbostic break; 53751860Sbostic default: 53851860Sbostic ap = &a[num - 1]; 53951860Sbostic if (bread(vp, ap->in_lbn, fs->lfs_bsize, NOCRED, &bp)) 54051860Sbostic panic("lfs_updatemeta: bread bno %d", 54151860Sbostic ap->in_lbn); 54251915Sbostic bp->b_un.b_daddr[ap->in_off] = off; 54351342Sbostic lfs_bwrite(bp); 54451188Sbostic } 54551915Sbostic 54651915Sbostic /* Update segment usage information. */ 54751915Sbostic if (daddr != UNASSIGNED) { 54851915Sbostic LFS_SEGENTRY(sup, fs, datosn(fs, daddr), bp); 54951915Sbostic #ifdef DIAGNOSTIC 55051915Sbostic if (sup->su_nbytes < fs->lfs_bsize) 55152819Sbostic /* XXX -- Change to a panic. */ 55252819Sbostic printf("lfs: negative bytes (segment %d)\n", 55351915Sbostic datosn(fs, daddr)); 55451915Sbostic #endif 55551915Sbostic sup->su_nbytes -= fs->lfs_bsize; 55652085Sbostic LFS_UBWRITE(bp); 55751915Sbostic } 55851188Sbostic } 55951188Sbostic } 56051188Sbostic 56151915Sbostic /* 56251915Sbostic * Start a new segment. 56351915Sbostic */ 56452077Sbostic void 56551915Sbostic lfs_initseg(fs, sp) 56651499Sbostic struct lfs *fs; 56752085Sbostic struct segment *sp; 56851188Sbostic { 56951915Sbostic SEGUSE *sup; 57051915Sbostic SEGSUM *ssp; 57151915Sbostic struct buf *bp; 57251915Sbostic daddr_t lbn, *lbnp; 57351215Sbostic 57451860Sbostic #ifdef VERBOSE 57551915Sbostic printf("lfs_initseg\n"); 57651860Sbostic #endif 57751915Sbostic /* Advance to the next segment. */ 57851927Sbostic if (!LFS_PARTIAL_FITS(fs)) { 57952682Sstaelin /* Wake up any cleaning procs waiting on this file system. */ 58052688Sbostic wakeup(&fs->lfs_nextseg); 58152688Sbostic wakeup(&lfs_allclean_wakeup); 58252682Sstaelin 58351927Sbostic lfs_newseg(fs); 58451927Sbostic fs->lfs_offset = fs->lfs_curseg; 58551915Sbostic sp->seg_number = datosn(fs, fs->lfs_curseg); 58651915Sbostic sp->seg_bytes_left = fs->lfs_dbpseg * DEV_BSIZE; 58751915Sbostic 58851915Sbostic /* 58951927Sbostic * If the segment contains a superblock, update the offset 59051927Sbostic * and summary address to skip over it. 59151915Sbostic */ 59252077Sbostic LFS_SEGENTRY(sup, fs, sp->seg_number, bp); 59351927Sbostic if (sup->su_flags & SEGUSE_SUPERBLOCK) { 59451915Sbostic fs->lfs_offset += LFS_SBPAD / DEV_BSIZE; 59551915Sbostic sp->seg_bytes_left -= LFS_SBPAD; 59651215Sbostic } 59752085Sbostic brelse(bp); 59851915Sbostic } else { 59951915Sbostic sp->seg_number = datosn(fs, fs->lfs_curseg); 60051915Sbostic sp->seg_bytes_left = (fs->lfs_dbpseg - 60151915Sbostic (fs->lfs_offset - fs->lfs_curseg)) * DEV_BSIZE; 60251915Sbostic } 60351342Sbostic 60451915Sbostic sp->ibp = NULL; 60551915Sbostic sp->ninodes = 0; 60651342Sbostic 60751915Sbostic /* Get a new buffer for SEGSUM and enter it into the buffer list. */ 60851915Sbostic sp->cbpp = sp->bpp; 60952688Sbostic *sp->cbpp = lfs_newbuf(fs, fs->lfs_offset, LFS_SUMMARY_SIZE); 61051915Sbostic sp->segsum = (*sp->cbpp)->b_un.b_addr; 61151915Sbostic ++sp->cbpp; 61251915Sbostic fs->lfs_offset += LFS_SUMMARY_SIZE / DEV_BSIZE; 61351342Sbostic 61451915Sbostic /* Set point to SEGSUM, initialize it. */ 61551915Sbostic ssp = sp->segsum; 61651915Sbostic ssp->ss_next = fs->lfs_nextseg; 61751915Sbostic ssp->ss_nfinfo = ssp->ss_ninos = 0; 61851342Sbostic 61951915Sbostic /* Set pointer to first FINFO, initialize it. */ 62052085Sbostic sp->fip = (struct finfo *)(sp->segsum + sizeof(SEGSUM)); 62151915Sbostic sp->fip->fi_nblocks = 0; 62251342Sbostic 62351915Sbostic sp->seg_bytes_left -= LFS_SUMMARY_SIZE; 62451915Sbostic sp->sum_bytes_left = LFS_SUMMARY_SIZE - sizeof(SEGSUM); 62551915Sbostic } 62651342Sbostic 62751915Sbostic /* 62851915Sbostic * Return the next segment to write. 62951915Sbostic */ 63052077Sbostic void 63151915Sbostic lfs_newseg(fs) 63251915Sbostic struct lfs *fs; 63351915Sbostic { 63451927Sbostic CLEANERINFO *cip; 63551915Sbostic SEGUSE *sup; 63651915Sbostic struct buf *bp; 63751927Sbostic int curseg, isdirty, sn; 63851915Sbostic 63951915Sbostic #ifdef VERBOSE 64051915Sbostic printf("lfs_newseg\n"); 64151915Sbostic #endif 64251927Sbostic /* 64351927Sbostic * Turn off the active bit for the current segment, turn on the 64451927Sbostic * active and dirty bits for the next segment, update the cleaner 64551927Sbostic * info. Set the current segment to the next segment, get a new 64651927Sbostic * next segment. 64751927Sbostic */ 64851927Sbostic LFS_SEGENTRY(sup, fs, datosn(fs, fs->lfs_curseg), bp); 64951927Sbostic sup->su_flags &= ~SEGUSE_ACTIVE; 65052085Sbostic LFS_UBWRITE(bp); 65151927Sbostic 65251927Sbostic LFS_SEGENTRY(sup, fs, datosn(fs, fs->lfs_nextseg), bp); 65351927Sbostic sup->su_flags |= SEGUSE_ACTIVE | SEGUSE_DIRTY; 65452085Sbostic LFS_UBWRITE(bp); 65551927Sbostic 65651927Sbostic LFS_CLEANERINFO(cip, fs, bp); 65751927Sbostic --cip->clean; 65851927Sbostic ++cip->dirty; 65952085Sbostic LFS_UBWRITE(bp); 66051927Sbostic 66151927Sbostic fs->lfs_lastseg = fs->lfs_curseg; 66251927Sbostic fs->lfs_curseg = fs->lfs_nextseg; 66351927Sbostic for (sn = curseg = datosn(fs, fs->lfs_curseg);;) { 66451915Sbostic sn = (sn + 1) % fs->lfs_nseg; 66551927Sbostic if (sn == curseg) 66651915Sbostic panic("lfs_nextseg: no clean segments"); 66751915Sbostic LFS_SEGENTRY(sup, fs, sn, bp); 66851915Sbostic isdirty = sup->su_flags & SEGUSE_DIRTY; 66952085Sbostic brelse(bp); 67051915Sbostic if (!isdirty) 67151915Sbostic break; 67251915Sbostic } 67351927Sbostic fs->lfs_nextseg = sntoda(fs, sn); 67451188Sbostic } 67551188Sbostic 67652077Sbostic void 67751188Sbostic lfs_writeseg(fs, sp) 67851499Sbostic struct lfs *fs; 67952085Sbostic struct segment *sp; 68051188Sbostic { 68152688Sbostic struct buf **bpp, *bp, *cbp; 68251188Sbostic SEGUSE *sup; 68352085Sbostic SEGSUM *ssp; 68451860Sbostic dev_t i_dev; 68551860Sbostic u_long *datap, *dp; 68652688Sbostic size_t size; 68752688Sbostic int ch_per_blk, i, nblocks, num, s, (*strategy)__P((struct buf *)); 68852688Sbostic char *p; 68951188Sbostic 69051860Sbostic #ifdef VERBOSE 69151860Sbostic printf("lfs_writeseg\n"); 69251860Sbostic #endif 69352085Sbostic if ((nblocks = sp->cbpp - sp->bpp) == 0) 69452085Sbostic return; 69552085Sbostic 69651188Sbostic /* 69752085Sbostic * Compute checksum across data and then across summary; the first 69852085Sbostic * block (the summary block) is skipped. Set the create time here 69952085Sbostic * so that it's guaranteed to be later than the inode mod times. 70051860Sbostic * 70151860Sbostic * XXX 70251860Sbostic * Fix this to do it inline, instead of malloc/copy. 70351188Sbostic */ 70451860Sbostic datap = dp = malloc(nblocks * sizeof(u_long), M_SEGMENT, M_WAITOK); 70551915Sbostic for (bpp = sp->bpp, i = nblocks - 1; i--;) 70651915Sbostic *dp++ = (*++bpp)->b_un.b_words[0]; 70752085Sbostic ssp = (SEGSUM *)sp->segsum; 70852103Sbostic ssp->ss_create = time.tv_sec; 70952085Sbostic ssp->ss_datasum = cksum(datap, nblocks * sizeof(u_long)); 71052085Sbostic ssp->ss_sumsum = 71152085Sbostic cksum(&ssp->ss_datasum, LFS_SUMMARY_SIZE - sizeof(ssp->ss_sumsum)); 71251927Sbostic free(datap, M_SEGMENT); 71351188Sbostic 71451860Sbostic i_dev = VTOI(fs->lfs_ivnode)->i_dev; 71551860Sbostic strategy = VTOI(fs->lfs_ivnode)->i_devvp->v_op->vop_strategy; 71651301Sbostic 71752688Sbostic /* 71852688Sbostic * When we simply write the blocks we lose a rotation for every block 71952688Sbostic * written. To avoid this problem, we allocate memory in chunks, copy 72052688Sbostic * the buffers into the chunk and write the chunk. 56K was chosen as 72152688Sbostic * some driver/controllers can't handle unsigned 16 bit transfers. 72252688Sbostic * When the data is copied to the chunk, turn off the the B_LOCKED bit 72352688Sbostic * and brelse the buffer (which will move them to the LRU list). Add 72452688Sbostic * the B_CALL flag to the buffer header so we can count I/O's for the 72552688Sbostic * checkpoints and so we can release the allocated memory. 72652688Sbostic * 72752688Sbostic * XXX 72852688Sbostic * This should be removed if the new virtual memory system allows us to 72952688Sbostic * easily make the buffers contiguous in kernel memory and if that's 73052688Sbostic * fast enough. 73152688Sbostic */ 73252688Sbostic #define LFS_CHUNKSIZE (56 * 1024) 73352688Sbostic ch_per_blk = LFS_CHUNKSIZE / fs->lfs_bsize; 73452688Sbostic for (bpp = sp->bpp, i = nblocks; i;) { 73552688Sbostic num = ch_per_blk; 73652688Sbostic if (num > i) 73752688Sbostic num = i; 73852688Sbostic i -= num; 73952688Sbostic size = num * fs->lfs_bsize; 74052688Sbostic 74152688Sbostic cbp = lfs_newbuf(fs, (*bpp)->b_blkno, 0); 74252688Sbostic cbp->b_dev = i_dev; 74352688Sbostic cbp->b_flags = B_ASYNC | B_BUSY | B_CALL; 74452688Sbostic cbp->b_iodone = lfs_callback; 74552688Sbostic cbp->b_saveaddr = cbp->b_un.b_addr; 74652688Sbostic cbp->b_un.b_addr = malloc(size, M_SEGMENT, M_WAITOK); 74752688Sbostic 74852688Sbostic s = splbio(); 74952688Sbostic ++fs->lfs_iocount; 75052688Sbostic for (p = cbp->b_un.b_addr; num--;) { 75152688Sbostic bp = *bpp++; 75252688Sbostic bcopy(bp->b_un.b_addr, p, bp->b_bcount); 75352688Sbostic p += bp->b_bcount; 75452688Sbostic bp->b_flags &= 75552688Sbostic ~(B_DONE | B_ERROR | B_READ | B_DELWRI | B_LOCKED); 75652688Sbostic if (!(bp->b_flags & B_NOCACHE)) { 75752688Sbostic bremfree(bp); 75852688Sbostic reassignbuf(bp, bp->b_vp); 75952688Sbostic } 76052688Sbostic brelse(bp); 76151860Sbostic } 76252688Sbostic splx(s); 76352688Sbostic cbp->b_bcount = p - cbp->b_un.b_addr; 76452688Sbostic (strategy)(cbp); 76551860Sbostic } 76652077Sbostic 76752682Sstaelin /* Update the segment usage information. */ 76852682Sstaelin LFS_SEGENTRY(sup, fs, sp->seg_number, bp); 76952682Sstaelin sup->su_nbytes += nblocks - 1 - 77052682Sstaelin (ssp->ss_ninos + INOPB(fs) - 1) / INOPB(fs) << fs->lfs_bshift; 77152682Sstaelin sup->su_nbytes += ssp->ss_ninos * sizeof(struct dinode); 77252682Sstaelin sup->su_lastmod = time.tv_sec; 77352682Sstaelin LFS_UBWRITE(bp); 77451188Sbostic } 77551188Sbostic 77652077Sbostic void 77751860Sbostic lfs_writesuper(fs, sp) 77851499Sbostic struct lfs *fs; 77952085Sbostic struct segment *sp; 78051301Sbostic { 78152085Sbostic struct buf *bp; 78251860Sbostic dev_t i_dev; 78352085Sbostic int (*strategy) __P((struct buf *)); 78451301Sbostic 78551860Sbostic #ifdef VERBOSE 78651860Sbostic printf("lfs_writesuper\n"); 78751860Sbostic #endif 78851860Sbostic i_dev = VTOI(fs->lfs_ivnode)->i_dev; 78951860Sbostic strategy = VTOI(fs->lfs_ivnode)->i_devvp->v_op->vop_strategy; 79051356Sbostic 79151342Sbostic /* Checksum the superblock and copy it into a buffer. */ 79251499Sbostic fs->lfs_cksum = cksum(fs, sizeof(struct lfs) - sizeof(fs->lfs_cksum)); 79352688Sbostic bp = lfs_newbuf(fs, fs->lfs_sboffs[0], LFS_SBPAD); 79451860Sbostic *bp->b_un.b_lfs = *fs; 79551215Sbostic 79651356Sbostic /* Write the first superblock (wait). */ 79751860Sbostic bp->b_dev = i_dev; 79851915Sbostic bp->b_flags |= B_BUSY; 79951860Sbostic bp->b_flags &= ~(B_DONE | B_ERROR | B_READ | B_DELWRI); 80051342Sbostic (strategy)(bp); 80151215Sbostic biowait(bp); 80251342Sbostic 80351356Sbostic /* Write the second superblock (don't wait). */ 80451215Sbostic bp->b_blkno = bp->b_lblkno = fs->lfs_sboffs[1]; 80551915Sbostic bp->b_flags |= B_ASYNC | B_BUSY; 80651860Sbostic bp->b_flags &= ~(B_DONE | B_ERROR | B_READ | B_DELWRI); 80751342Sbostic (strategy)(bp); 80851215Sbostic } 80951215Sbostic 81051342Sbostic /* 81151342Sbostic * Logical block number match routines used when traversing the dirty block 81251342Sbostic * chain. 81351342Sbostic */ 81452077Sbostic int 81552077Sbostic lfs_match_data(fs, bp) 81651860Sbostic struct lfs *fs; 81752085Sbostic struct buf *bp; 81851215Sbostic { 81951342Sbostic return (bp->b_lblkno >= 0); 82051215Sbostic } 82151215Sbostic 82252077Sbostic int 82352077Sbostic lfs_match_indir(fs, bp) 82451860Sbostic struct lfs *fs; 82552085Sbostic struct buf *bp; 82651215Sbostic { 82751860Sbostic int lbn; 82851860Sbostic 82951860Sbostic lbn = bp->b_lblkno; 83051860Sbostic return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 0); 83151215Sbostic } 83251215Sbostic 83352077Sbostic int 83452077Sbostic lfs_match_dindir(fs, bp) 83551860Sbostic struct lfs *fs; 83652085Sbostic struct buf *bp; 83751215Sbostic { 83851860Sbostic int lbn; 83951860Sbostic 84051860Sbostic lbn = bp->b_lblkno; 84151860Sbostic return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 1); 84251215Sbostic } 84351215Sbostic 84452077Sbostic int 84552077Sbostic lfs_match_tindir(fs, bp) 84651499Sbostic struct lfs *fs; 84752085Sbostic struct buf *bp; 84851342Sbostic { 84951860Sbostic int lbn; 85051342Sbostic 85151860Sbostic lbn = bp->b_lblkno; 85251860Sbostic return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 2); 85351860Sbostic } 85451342Sbostic 85551860Sbostic /* 85651860Sbostic * Allocate a new buffer header. 85751860Sbostic */ 85852085Sbostic struct buf * 85952688Sbostic lfs_newbuf(fs, daddr, size) 86051860Sbostic struct lfs *fs; 86151860Sbostic daddr_t daddr; 86251860Sbostic size_t size; 86351860Sbostic { 86452085Sbostic struct buf *bp; 86551342Sbostic 86651860Sbostic #ifdef VERBOSE 86751860Sbostic printf("lfs_newbuf\n"); 86851860Sbostic #endif 86951860Sbostic bp = getnewbuf(); 87051860Sbostic bremhash(bp); 87151860Sbostic bgetvp(fs->lfs_ivnode, bp); 87251860Sbostic bp->b_bcount = 0; 87351860Sbostic bp->b_lblkno = daddr; 87451860Sbostic bp->b_blkno = daddr; 87551860Sbostic bp->b_error = 0; 87651860Sbostic bp->b_resid = 0; 87752688Sbostic if (size) 87852688Sbostic allocbuf(bp, size); 87951860Sbostic bp->b_flags |= B_NOCACHE; 88052688Sbostic bp->b_saveaddr = NULL; 88151915Sbostic binshash(bp, &bfreelist[BQ_AGE]); 88251860Sbostic return (bp); 88351860Sbostic } 88451342Sbostic 88552077Sbostic int /* XXX should be void */ 88651860Sbostic lfs_callback(bp) 88752085Sbostic struct buf *bp; 88851860Sbostic { 88951860Sbostic struct lfs *fs; 89051342Sbostic 89151860Sbostic fs = VFSTOUFS(bp->b_vp->v_mount)->um_lfs; 89251860Sbostic #ifdef DIAGNOSTIC 89351860Sbostic if (fs->lfs_iocount == 0) 89451860Sbostic panic("lfs_callback: zero iocount\n"); 89551860Sbostic #endif 89651860Sbostic if (--fs->lfs_iocount == 0) 89752688Sbostic wakeup(&fs->lfs_iocount); 89851915Sbostic 89952688Sbostic if (bp->b_saveaddr) { 90052688Sbostic free(bp->b_un.b_addr, M_SEGMENT); 90152688Sbostic bp->b_un.b_addr = bp->b_saveaddr; 90252819Sbostic bp->b_saveaddr = NULL; 90352688Sbostic } 90451860Sbostic brelse(bp); 90551860Sbostic } 90651342Sbostic 90751215Sbostic /* 90851188Sbostic * Shellsort (diminishing increment sort) from Data Structures and 90951188Sbostic * Algorithms, Aho, Hopcraft and Ullman, 1983 Edition, page 290; 91051188Sbostic * see also Knuth Vol. 3, page 84. The increments are selected from 91151188Sbostic * formula (8), page 95. Roughly O(N^3/2). 91251188Sbostic */ 91351188Sbostic /* 91451188Sbostic * This is our own private copy of shellsort because we want to sort 91551188Sbostic * two parallel arrays (the array of buffer pointers and the array of 91651188Sbostic * logical block numbers) simultaneously. Note that we cast the array 91751188Sbostic * of logical block numbers to a unsigned in this routine so that the 91851188Sbostic * negative block numbers (meta data blocks) sort AFTER the data blocks. 91951188Sbostic */ 92052077Sbostic void 92152077Sbostic lfs_shellsort(bp_array, lb_array, nmemb) 92252085Sbostic struct buf **bp_array; 92351215Sbostic daddr_t *lb_array; 92451188Sbostic register int nmemb; 92551188Sbostic { 92651188Sbostic static int __rsshell_increments[] = { 4, 1, 0 }; 92751188Sbostic register int incr, *incrp, t1, t2; 92852085Sbostic struct buf *bp_temp; 92951188Sbostic u_long lb_temp; 93051188Sbostic 93151188Sbostic for (incrp = __rsshell_increments; incr = *incrp++;) 93251188Sbostic for (t1 = incr; t1 < nmemb; ++t1) 93351188Sbostic for (t2 = t1 - incr; t2 >= 0;) 93451188Sbostic if (lb_array[t2] > lb_array[t2 + incr]) { 93551188Sbostic lb_temp = lb_array[t2]; 93651188Sbostic lb_array[t2] = lb_array[t2 + incr]; 93751188Sbostic lb_array[t2 + incr] = lb_temp; 93851188Sbostic bp_temp = bp_array[t2]; 93951188Sbostic bp_array[t2] = bp_array[t2 + incr]; 94051188Sbostic bp_array[t2 + incr] = bp_temp; 94151188Sbostic t2 -= incr; 94251188Sbostic } else 94351188Sbostic break; 94451188Sbostic } 945