151188Sbostic /* 251188Sbostic * Copyright (c) 1991 Regents of the University of California. 351188Sbostic * All rights reserved. 451188Sbostic * 551188Sbostic * %sccs.include.redist.c% 651188Sbostic * 7*53574Sheideman * @(#)lfs_segment.c 7.19 (Berkeley) 05/15/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 5753347Sbostic void 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 = 14552995Sbostic tsleep(&fs->lfs_iocount, PRIBIO + 1, "lfs vflush", 0))) { 14652995Sbostic free(sp->bpp, M_SEGMENT); 14752995Sbostic free(sp, M_SEGMENT); 14852328Sbostic return (error); 14952995Sbostic } 15052328Sbostic splx(s); 15152328Sbostic vfs_unbusy(mp); 15252328Sbostic 15352995Sbostic /* 15452995Sbostic * XXX 15552995Sbostic * Should be writing a checkpoint? 15652995Sbostic */ 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 { 16853530Sheideman USES_VOP_ISLOCKED; 16952085Sbostic struct inode *ip; 17051499Sbostic struct lfs *fs; 17152085Sbostic struct segment *sp; 17252085Sbostic struct vnode *vp; 17352328Sbostic int error, islocked, s; 17451188Sbostic 17551860Sbostic #ifdef VERBOSE 17651860Sbostic printf("lfs_segwrite\n"); 17751860Sbostic #endif 17852328Sbostic fs = VFSTOUFS(mp)->um_lfs; 17952085Sbostic 18051860Sbostic /* 18152328Sbostic * Allocate a segment structure and enough space to hold pointers to 18252328Sbostic * the maximum possible number of buffers which can be described in a 18352328Sbostic * single summary block. 18452328Sbostic */ 18552328Sbostic sp = malloc(sizeof(struct segment), M_SEGMENT, M_WAITOK); 18652328Sbostic sp->bpp = malloc(((LFS_SUMMARY_SIZE - sizeof(SEGSUM)) / 18752328Sbostic sizeof(daddr_t) + 1) * sizeof(struct buf *), M_SEGMENT, M_WAITOK); 18852328Sbostic sp->seg_flags = do_ckp ? SEGM_CKP : 0; 18952328Sbostic lfs_initseg(fs, sp); 19052328Sbostic 19152328Sbostic /* 19252688Sbostic * Keep a cumulative count of the outstanding I/O operations. If the 19352688Sbostic * disk drive catches up with us it could go to zero before we finish, 19452688Sbostic * so we artificially increment it by one until we've scheduled all of 19552688Sbostic * the writes we intend to do. If not a checkpoint, we never do the 19652688Sbostic * final decrement, avoiding the wakeup in the callback routine. 19751860Sbostic */ 19852688Sbostic s = splbio(); 19952688Sbostic ++fs->lfs_iocount; 20052688Sbostic splx(s); 20151342Sbostic 20252328Sbostic loop: for (vp = mp->mnt_mounth; vp; vp = vp->v_mountf) { 20351188Sbostic /* 20451188Sbostic * If the vnode that we are about to sync is no longer 20551188Sbostic * associated with this mount point, start over. 20651188Sbostic */ 20751188Sbostic if (vp->v_mount != mp) 20851188Sbostic goto loop; 20951860Sbostic 21052328Sbostic islocked = VOP_ISLOCKED(vp); 21151860Sbostic 21252077Sbostic /* 21352077Sbostic * XXX 21452077Sbostic * This is wrong, I think -- we should just wait until we 21552077Sbostic * get the vnode and go on. Probably going to reschedule 21652077Sbostic * all of the writes we already scheduled... 21752077Sbostic */ 21852328Sbostic if (islocked) 21952328Sbostic VREF(vp); 22052328Sbostic else if (vget(vp)) 22152085Sbostic { 22252085Sbostic printf("lfs_segment: failed to get vnode (tell Keith)!\n"); 22351188Sbostic goto loop; 22452085Sbostic } 22552328Sbostic /* 22652328Sbostic * Write the inode/file if dirty and it's not the 22752328Sbostic * the IFILE. 22852328Sbostic */ 22952328Sbostic ip = VTOI(vp); 23052328Sbostic if ((ip->i_flag & (IMOD | IACC | IUPD | ICHG) || 23152328Sbostic vp->v_dirtyblkhd != NULL) && 23252328Sbostic ip->i_number != LFS_IFILE_INUM) { 23352328Sbostic if (vp->v_dirtyblkhd != NULL) 23452328Sbostic lfs_writefile(fs, sp, vp); 23552328Sbostic lfs_writeinode(fs, sp, ip); 23652328Sbostic ip->i_flags &= ~(IMOD | IACC | IUPD | ICHG); 23752328Sbostic } 23852328Sbostic if (islocked) 23952328Sbostic vrele(vp); 24052328Sbostic else 24152328Sbostic vput(vp); 24251188Sbostic } 24351860Sbostic if (do_ckp) { 24452077Sbostic vp = fs->lfs_ivnode; 24552077Sbostic while (vget(vp)); 24652077Sbostic ip = VTOI(vp); 24752077Sbostic if (vp->v_dirtyblkhd != NULL) 24852077Sbostic lfs_writefile(fs, sp, vp); 24952077Sbostic lfs_writeinode(fs, sp, ip); 25052077Sbostic ip->i_flags &= ~(IMOD | IACC | IUPD | ICHG); 25152077Sbostic vput(vp); 25251860Sbostic } 25351342Sbostic lfs_writeseg(fs, sp); 25451342Sbostic 25551215Sbostic /* 25651860Sbostic * If the I/O count is non-zero, sleep until it reaches zero. At the 25751860Sbostic * moment, the user's process hangs around so we can sleep. 25851215Sbostic */ 25952688Sbostic s = splbio(); 26052688Sbostic --fs->lfs_iocount; 26151860Sbostic if (do_ckp) { 26252688Sbostic if (fs->lfs_iocount && (error = 26352995Sbostic tsleep(&fs->lfs_iocount, PRIBIO + 1, "lfs sync", 0))) { 26452995Sbostic free(sp->bpp, M_SEGMENT); 26552995Sbostic free(sp, M_SEGMENT); 26651915Sbostic return (error); 26752995Sbostic } 26851860Sbostic splx(s); 26951860Sbostic lfs_writesuper(fs, sp); 27052688Sbostic } else 27152688Sbostic splx(s); 27251215Sbostic 27351927Sbostic free(sp->bpp, M_SEGMENT); 27451927Sbostic free(sp, M_SEGMENT); 27551215Sbostic 27651860Sbostic return (0); 27751188Sbostic } 27851188Sbostic 27951860Sbostic /* 28051860Sbostic * Write the dirty blocks associated with a vnode. 28151860Sbostic */ 28252077Sbostic void 28351860Sbostic lfs_writefile(fs, sp, vp) 28451499Sbostic struct lfs *fs; 28552085Sbostic struct segment *sp; 28652085Sbostic struct vnode *vp; 28751188Sbostic { 28851860Sbostic struct buf *bp; 28952085Sbostic struct finfo *fip; 29051860Sbostic IFILE *ifp; 29151188Sbostic 29251860Sbostic #ifdef VERBOSE 29351860Sbostic printf("lfs_writefile\n"); 29451860Sbostic #endif 29552085Sbostic if (sp->seg_bytes_left < fs->lfs_bsize || 29652085Sbostic sp->sum_bytes_left < sizeof(struct finfo)) { 29752085Sbostic lfs_writeseg(fs, sp); 29852085Sbostic lfs_initseg(fs, sp); 29952085Sbostic } 30052085Sbostic sp->sum_bytes_left -= sizeof(struct finfo) - sizeof(daddr_t); 30151215Sbostic 30252085Sbostic fip = sp->fip; 30352085Sbostic fip->fi_nblocks = 0; 30452085Sbostic fip->fi_ino = VTOI(vp)->i_number; 30552085Sbostic LFS_IENTRY(ifp, fs, fip->fi_ino, bp); 30652085Sbostic fip->fi_version = ifp->if_version; 30752085Sbostic brelse(bp); 30851188Sbostic 30952085Sbostic /* 31052085Sbostic * It may not be necessary to write the meta-data blocks at this point, 31152085Sbostic * as the roll-forward recovery code should be able to reconstruct the 31252085Sbostic * list. 31352085Sbostic */ 31452085Sbostic lfs_gather(fs, sp, vp, lfs_match_data); 31552085Sbostic lfs_gather(fs, sp, vp, lfs_match_indir); 31652085Sbostic lfs_gather(fs, sp, vp, lfs_match_dindir); 31751860Sbostic #ifdef TRIPLE 31852085Sbostic lfs_gather(fs, sp, vp, lfs_match_tindir); 31951860Sbostic #endif 32051342Sbostic 32152085Sbostic fip = sp->fip; 32251860Sbostic #ifdef META 32352085Sbostic printf("lfs_writefile: adding %d blocks\n", fip->fi_nblocks); 32451860Sbostic #endif 32552085Sbostic if (fip->fi_nblocks != 0) { 32652085Sbostic ++((SEGSUM *)(sp->segsum))->ss_nfinfo; 32752085Sbostic sp->fip = 32852085Sbostic (struct finfo *)((caddr_t)fip + sizeof(struct finfo) + 32952085Sbostic sizeof(daddr_t) * (fip->fi_nblocks - 1)); 33052682Sstaelin } else 33152682Sstaelin sp->sum_bytes_left += sizeof(struct finfo) - sizeof(daddr_t); 33251215Sbostic } 33351215Sbostic 33452077Sbostic void 33551915Sbostic lfs_writeinode(fs, sp, ip) 33651915Sbostic struct lfs *fs; 33752085Sbostic struct segment *sp; 33852085Sbostic struct inode *ip; 33951915Sbostic { 34052085Sbostic struct buf *bp, *ibp; 34152077Sbostic IFILE *ifp; 34252682Sstaelin SEGUSE *sup; 34352682Sstaelin daddr_t daddr; 34452077Sbostic ino_t ino; 34551915Sbostic int ndx; 34651915Sbostic 34751915Sbostic #ifdef VERBOSE 34851915Sbostic printf("lfs_writeinode\n"); 34951915Sbostic #endif 35051915Sbostic /* Allocate a new inode block if necessary. */ 35151915Sbostic if (sp->ibp == NULL) { 35251915Sbostic /* Allocate a new segment if necessary. */ 35351915Sbostic if (sp->seg_bytes_left < fs->lfs_bsize || 35451915Sbostic sp->sum_bytes_left < sizeof(daddr_t)) { 35551915Sbostic lfs_writeseg(fs, sp); 35651915Sbostic lfs_initseg(fs, sp); 35751915Sbostic } 35851915Sbostic 35951915Sbostic /* Get next inode block. */ 36052682Sstaelin daddr = fs->lfs_offset; 36151915Sbostic fs->lfs_offset += fsbtodb(fs, 1); 36251915Sbostic sp->ibp = *sp->cbpp++ = 36352688Sbostic lfs_newbuf(fs, daddr, fs->lfs_bsize); 36451915Sbostic 36552688Sbostic /* Set remaining space counters. */ 36651915Sbostic sp->seg_bytes_left -= fs->lfs_bsize; 36751915Sbostic sp->sum_bytes_left -= sizeof(daddr_t); 36852077Sbostic ndx = LFS_SUMMARY_SIZE / sizeof(daddr_t) - 36951915Sbostic sp->ninodes / INOPB(fs) - 1; 37052682Sstaelin ((daddr_t *)(sp->segsum))[ndx] = daddr; 37151915Sbostic } 37251915Sbostic 37352085Sbostic /* Update the inode times and copy the inode onto the inode page. */ 37452077Sbostic ITIMES(ip, &time, &time); 37551915Sbostic bp = sp->ibp; 37652085Sbostic bp->b_un.b_dino[sp->ninodes % INOPB(fs)] = ip->i_din; 37751915Sbostic 37851915Sbostic /* Increment inode count in segment summary block. */ 37951915Sbostic ++((SEGSUM *)(sp->segsum))->ss_ninos; 38051915Sbostic 38151915Sbostic /* If this page is full, set flag to allocate a new page. */ 38251915Sbostic if (++sp->ninodes % INOPB(fs) == 0) 38351915Sbostic sp->ibp = NULL; 38451915Sbostic 38551915Sbostic /* 38652077Sbostic * If updating the ifile, update the super-block. Update the disk 38752077Sbostic * address and access times for this inode in the ifile. 38851915Sbostic */ 38952077Sbostic ino = ip->i_number; 39052077Sbostic if (ino == LFS_IFILE_INUM) 39151915Sbostic fs->lfs_idaddr = bp->b_blkno; 39252077Sbostic 39352077Sbostic LFS_IENTRY(ifp, fs, ino, ibp); 39452682Sstaelin daddr = ifp->if_daddr; 39552077Sbostic ifp->if_daddr = bp->b_blkno; 39652085Sbostic LFS_UBWRITE(ibp); 39752682Sstaelin 39852682Sstaelin if (daddr != LFS_UNUSED_DADDR) { 39952682Sstaelin LFS_SEGENTRY(sup, fs, datosn(fs, daddr), bp); 40052682Sstaelin #ifdef DIAGNOSTIC 40152682Sstaelin if (sup->su_nbytes < sizeof(struct dinode)) 40252819Sbostic /* XXX -- Change to a panic. */ 40352819Sbostic printf("lfs: negative bytes (segment %d)\n", 40452682Sstaelin datosn(fs, daddr)); 40552682Sstaelin #endif 40652682Sstaelin sup->su_nbytes -= sizeof(struct dinode); 40752682Sstaelin LFS_UBWRITE(bp); 40852682Sstaelin } 40951915Sbostic } 41051915Sbostic 41152077Sbostic void 41251215Sbostic lfs_gather(fs, sp, vp, match) 41351499Sbostic struct lfs *fs; 41452085Sbostic struct segment *sp; 41552085Sbostic struct vnode *vp; 41652085Sbostic int (*match) __P((struct lfs *, struct buf *)); 41751215Sbostic { 41852085Sbostic struct buf **bpp, *bp, *nbp; 41952085Sbostic struct finfo *fip; 42052085Sbostic struct inode *ip; 42151215Sbostic daddr_t *lbp, *start_lbp; 42251342Sbostic u_long version; 42351342Sbostic int s; 42451215Sbostic 42551860Sbostic #ifdef VERBOSE 42651860Sbostic printf("lfs_gather\n"); 42751860Sbostic #endif 42851215Sbostic ip = VTOI(vp); 42951215Sbostic bpp = sp->cbpp; 43051215Sbostic fip = sp->fip; 43151215Sbostic start_lbp = lbp = &fip->fi_blocks[fip->fi_nblocks]; 43251215Sbostic 43353145Sstaelin loop: s = splbio(); 43451215Sbostic for (bp = vp->v_dirtyblkhd; bp; bp = nbp) { 43551215Sbostic nbp = bp->b_blockf; 43651915Sbostic /* 43751915Sbostic * XXX 43852682Sstaelin * Should sleep on any BUSY buffer if doing an fsync? 43951915Sbostic */ 44052328Sbostic if (bp->b_flags & B_BUSY || !match(fs, bp)) 44151215Sbostic continue; 44251342Sbostic #ifdef DIAGNOSTIC 44351860Sbostic if (!(bp->b_flags & B_DELWRI)) 44451915Sbostic panic("lfs_gather: bp not B_DELWRI"); 44551860Sbostic if (!(bp->b_flags & B_LOCKED)) 44651915Sbostic panic("lfs_gather: bp not B_LOCKED"); 44751342Sbostic #endif 44851860Sbostic /* 44951860Sbostic * If full, finish this segment. We may be doing I/O, so 45051860Sbostic * release and reacquire the splbio(). 45151860Sbostic */ 45251342Sbostic if (sp->sum_bytes_left < sizeof(daddr_t) || 45351215Sbostic sp->seg_bytes_left < fs->lfs_bsize) { 45451215Sbostic splx(s); 45551342Sbostic lfs_updatemeta(fs, 45651860Sbostic sp, vp, start_lbp, bpp, lbp - start_lbp); 45751215Sbostic 45851342Sbostic /* Add the current file to the segment summary. */ 45951342Sbostic ++((SEGSUM *)(sp->segsum))->ss_nfinfo; 46051215Sbostic 46151342Sbostic version = fip->fi_version; 46251860Sbostic lfs_writeseg(fs, sp); 46351915Sbostic lfs_initseg(fs, sp); 46451342Sbostic 46551215Sbostic fip = sp->fip; 46651342Sbostic fip->fi_version = version; 46751215Sbostic fip->fi_ino = ip->i_number; 46851342Sbostic start_lbp = lbp = fip->fi_blocks; 46951342Sbostic 47052682Sstaelin sp->sum_bytes_left -= 47152682Sstaelin sizeof(struct finfo) - sizeof(daddr_t); 47252682Sstaelin 47351215Sbostic bpp = sp->cbpp; 47453145Sstaelin goto loop; 47551215Sbostic } 47652682Sstaelin 47752682Sstaelin /* Insert into the buffer list, update the FINFO block. */ 47852682Sstaelin *sp->cbpp++ = bp; 47952682Sstaelin ++fip->fi_nblocks; 48052682Sstaelin *lbp++ = bp->b_lblkno; 48152682Sstaelin 48252682Sstaelin sp->sum_bytes_left -= sizeof(daddr_t); 48352682Sstaelin sp->seg_bytes_left -= bp->b_bufsize; 48451188Sbostic } 48551215Sbostic splx(s); 48651860Sbostic lfs_updatemeta(fs, sp, vp, start_lbp, bpp, lbp - start_lbp); 48751188Sbostic } 48851188Sbostic 48951342Sbostic /* 49051342Sbostic * Update the metadata that points to the blocks listed in the FINFO 49151188Sbostic * array. 49251188Sbostic */ 49352077Sbostic void 49451860Sbostic lfs_updatemeta(fs, sp, vp, lbp, bpp, nblocks) 49551499Sbostic struct lfs *fs; 49652085Sbostic struct segment *sp; 49752085Sbostic struct vnode *vp; 49851215Sbostic daddr_t *lbp; 49952085Sbostic struct buf **bpp; 50051215Sbostic int nblocks; 50151188Sbostic { 50253530Sheideman USES_VOP_BWRITE; 50351915Sbostic SEGUSE *sup; 50452085Sbostic struct buf *bp; 50551860Sbostic INDIR a[NIADDR], *ap; 50652085Sbostic struct inode *ip; 50751915Sbostic daddr_t daddr, lbn, off; 50851860Sbostic int db_per_fsb, error, i, num; 50951188Sbostic 51051860Sbostic #ifdef VERBOSE 51151860Sbostic printf("lfs_updatemeta\n"); 51251860Sbostic #endif 51351342Sbostic if (nblocks == 0) 51451215Sbostic return; 51551215Sbostic 51651915Sbostic /* Sort the blocks. */ 51752077Sbostic lfs_shellsort(bpp, lbp, nblocks); 51851215Sbostic 51951915Sbostic /* 52051915Sbostic * Assign disk addresses, and update references to the logical 52151915Sbostic * block and the segment usage information. 52251915Sbostic */ 52351860Sbostic db_per_fsb = fsbtodb(fs, 1); 52451915Sbostic for (i = nblocks; i--; ++bpp) { 52551915Sbostic lbn = *lbp++; 52651915Sbostic (*bpp)->b_blkno = off = fs->lfs_offset; 52751860Sbostic fs->lfs_offset += db_per_fsb; 52851215Sbostic 52951860Sbostic if (error = lfs_bmaparray(vp, lbn, &daddr, a, &num)) 53052085Sbostic panic("lfs_updatemeta: lfs_bmaparray %d", error); 53151860Sbostic ip = VTOI(vp); 53251860Sbostic switch (num) { 53351860Sbostic case 0: 53451915Sbostic ip->i_db[lbn] = off; 53551860Sbostic break; 53651860Sbostic case 1: 53751915Sbostic ip->i_ib[a[0].in_off] = off; 53851860Sbostic break; 53951860Sbostic default: 54051860Sbostic ap = &a[num - 1]; 54151860Sbostic if (bread(vp, ap->in_lbn, fs->lfs_bsize, NOCRED, &bp)) 54251860Sbostic panic("lfs_updatemeta: bread bno %d", 54351860Sbostic ap->in_lbn); 54451915Sbostic bp->b_un.b_daddr[ap->in_off] = off; 54553530Sheideman VOP_BWRITE(bp); 54651188Sbostic } 54751915Sbostic 54851915Sbostic /* Update segment usage information. */ 54951915Sbostic if (daddr != UNASSIGNED) { 55051915Sbostic LFS_SEGENTRY(sup, fs, datosn(fs, daddr), bp); 55151915Sbostic #ifdef DIAGNOSTIC 55251915Sbostic if (sup->su_nbytes < fs->lfs_bsize) 55352819Sbostic /* XXX -- Change to a panic. */ 55452819Sbostic printf("lfs: negative bytes (segment %d)\n", 55551915Sbostic datosn(fs, daddr)); 55651915Sbostic #endif 55751915Sbostic sup->su_nbytes -= fs->lfs_bsize; 55852085Sbostic LFS_UBWRITE(bp); 55951915Sbostic } 56051188Sbostic } 56151188Sbostic } 56251188Sbostic 56351915Sbostic /* 56451915Sbostic * Start a new segment. 56551915Sbostic */ 56652077Sbostic void 56751915Sbostic lfs_initseg(fs, sp) 56851499Sbostic struct lfs *fs; 56952085Sbostic struct segment *sp; 57051188Sbostic { 57151915Sbostic SEGUSE *sup; 57251915Sbostic SEGSUM *ssp; 57351915Sbostic struct buf *bp; 57451915Sbostic daddr_t lbn, *lbnp; 57551215Sbostic 57651860Sbostic #ifdef VERBOSE 57751915Sbostic printf("lfs_initseg\n"); 57851860Sbostic #endif 57951915Sbostic /* Advance to the next segment. */ 58051927Sbostic if (!LFS_PARTIAL_FITS(fs)) { 58152682Sstaelin /* Wake up any cleaning procs waiting on this file system. */ 58252688Sbostic wakeup(&fs->lfs_nextseg); 58352688Sbostic wakeup(&lfs_allclean_wakeup); 58452682Sstaelin 58551927Sbostic lfs_newseg(fs); 58651927Sbostic fs->lfs_offset = fs->lfs_curseg; 58751915Sbostic sp->seg_number = datosn(fs, fs->lfs_curseg); 58851915Sbostic sp->seg_bytes_left = fs->lfs_dbpseg * DEV_BSIZE; 58951915Sbostic 59051915Sbostic /* 59151927Sbostic * If the segment contains a superblock, update the offset 59251927Sbostic * and summary address to skip over it. 59351915Sbostic */ 59452077Sbostic LFS_SEGENTRY(sup, fs, sp->seg_number, bp); 59551927Sbostic if (sup->su_flags & SEGUSE_SUPERBLOCK) { 59651915Sbostic fs->lfs_offset += LFS_SBPAD / DEV_BSIZE; 59751915Sbostic sp->seg_bytes_left -= LFS_SBPAD; 59851215Sbostic } 59952085Sbostic brelse(bp); 60051915Sbostic } else { 60151915Sbostic sp->seg_number = datosn(fs, fs->lfs_curseg); 60251915Sbostic sp->seg_bytes_left = (fs->lfs_dbpseg - 60351915Sbostic (fs->lfs_offset - fs->lfs_curseg)) * DEV_BSIZE; 60451915Sbostic } 60551342Sbostic 60651915Sbostic sp->ibp = NULL; 60751915Sbostic sp->ninodes = 0; 60851342Sbostic 60951915Sbostic /* Get a new buffer for SEGSUM and enter it into the buffer list. */ 61051915Sbostic sp->cbpp = sp->bpp; 61152688Sbostic *sp->cbpp = lfs_newbuf(fs, fs->lfs_offset, LFS_SUMMARY_SIZE); 61251915Sbostic sp->segsum = (*sp->cbpp)->b_un.b_addr; 61351915Sbostic ++sp->cbpp; 61451915Sbostic fs->lfs_offset += LFS_SUMMARY_SIZE / DEV_BSIZE; 61551342Sbostic 61651915Sbostic /* Set point to SEGSUM, initialize it. */ 61751915Sbostic ssp = sp->segsum; 61851915Sbostic ssp->ss_next = fs->lfs_nextseg; 61951915Sbostic ssp->ss_nfinfo = ssp->ss_ninos = 0; 62051342Sbostic 62151915Sbostic /* Set pointer to first FINFO, initialize it. */ 62252085Sbostic sp->fip = (struct finfo *)(sp->segsum + sizeof(SEGSUM)); 62351915Sbostic sp->fip->fi_nblocks = 0; 62451342Sbostic 62551915Sbostic sp->seg_bytes_left -= LFS_SUMMARY_SIZE; 62651915Sbostic sp->sum_bytes_left = LFS_SUMMARY_SIZE - sizeof(SEGSUM); 62751915Sbostic } 62851342Sbostic 62951915Sbostic /* 63051915Sbostic * Return the next segment to write. 63151915Sbostic */ 63252077Sbostic void 63351915Sbostic lfs_newseg(fs) 63451915Sbostic struct lfs *fs; 63551915Sbostic { 63651927Sbostic CLEANERINFO *cip; 63751915Sbostic SEGUSE *sup; 63851915Sbostic struct buf *bp; 63951927Sbostic int curseg, isdirty, sn; 64051915Sbostic 64151915Sbostic #ifdef VERBOSE 64251915Sbostic printf("lfs_newseg\n"); 64351915Sbostic #endif 64451927Sbostic /* 64551927Sbostic * Turn off the active bit for the current segment, turn on the 64651927Sbostic * active and dirty bits for the next segment, update the cleaner 64751927Sbostic * info. Set the current segment to the next segment, get a new 64851927Sbostic * next segment. 64951927Sbostic */ 65051927Sbostic LFS_SEGENTRY(sup, fs, datosn(fs, fs->lfs_curseg), bp); 65151927Sbostic sup->su_flags &= ~SEGUSE_ACTIVE; 65252085Sbostic LFS_UBWRITE(bp); 65351927Sbostic 65451927Sbostic LFS_SEGENTRY(sup, fs, datosn(fs, fs->lfs_nextseg), bp); 65551927Sbostic sup->su_flags |= SEGUSE_ACTIVE | SEGUSE_DIRTY; 65652085Sbostic LFS_UBWRITE(bp); 65751927Sbostic 65851927Sbostic LFS_CLEANERINFO(cip, fs, bp); 65951927Sbostic --cip->clean; 66051927Sbostic ++cip->dirty; 66152085Sbostic LFS_UBWRITE(bp); 66251927Sbostic 66351927Sbostic fs->lfs_lastseg = fs->lfs_curseg; 66451927Sbostic fs->lfs_curseg = fs->lfs_nextseg; 66551927Sbostic for (sn = curseg = datosn(fs, fs->lfs_curseg);;) { 66651915Sbostic sn = (sn + 1) % fs->lfs_nseg; 66751927Sbostic if (sn == curseg) 66851915Sbostic panic("lfs_nextseg: no clean segments"); 66951915Sbostic LFS_SEGENTRY(sup, fs, sn, bp); 67051915Sbostic isdirty = sup->su_flags & SEGUSE_DIRTY; 67152085Sbostic brelse(bp); 67251915Sbostic if (!isdirty) 67351915Sbostic break; 67451915Sbostic } 67551927Sbostic fs->lfs_nextseg = sntoda(fs, sn); 67651188Sbostic } 67751188Sbostic 67852077Sbostic void 67951188Sbostic lfs_writeseg(fs, sp) 68051499Sbostic struct lfs *fs; 68152085Sbostic struct segment *sp; 68251188Sbostic { 683*53574Sheideman USES_VOP_STRATEGY; 68452688Sbostic struct buf **bpp, *bp, *cbp; 68551188Sbostic SEGUSE *sup; 68652085Sbostic SEGSUM *ssp; 68751860Sbostic dev_t i_dev; 68851860Sbostic u_long *datap, *dp; 68952688Sbostic size_t size; 690*53574Sheideman int ch_per_blk, i, nblocks, num, s, (*strategy)__P((struct vop_strategy_args *)); 69152688Sbostic char *p; 69251188Sbostic 69351860Sbostic #ifdef VERBOSE 69451860Sbostic printf("lfs_writeseg\n"); 69551860Sbostic #endif 69652085Sbostic if ((nblocks = sp->cbpp - sp->bpp) == 0) 69752085Sbostic return; 69852085Sbostic 69951188Sbostic /* 70052085Sbostic * Compute checksum across data and then across summary; the first 70152085Sbostic * block (the summary block) is skipped. Set the create time here 70252085Sbostic * so that it's guaranteed to be later than the inode mod times. 70351860Sbostic * 70451860Sbostic * XXX 70551860Sbostic * Fix this to do it inline, instead of malloc/copy. 70651188Sbostic */ 70751860Sbostic datap = dp = malloc(nblocks * sizeof(u_long), M_SEGMENT, M_WAITOK); 70851915Sbostic for (bpp = sp->bpp, i = nblocks - 1; i--;) 70951915Sbostic *dp++ = (*++bpp)->b_un.b_words[0]; 71052085Sbostic ssp = (SEGSUM *)sp->segsum; 71152103Sbostic ssp->ss_create = time.tv_sec; 71252085Sbostic ssp->ss_datasum = cksum(datap, nblocks * sizeof(u_long)); 71352085Sbostic ssp->ss_sumsum = 71452085Sbostic cksum(&ssp->ss_datasum, LFS_SUMMARY_SIZE - sizeof(ssp->ss_sumsum)); 71551927Sbostic free(datap, M_SEGMENT); 71651188Sbostic 71751860Sbostic i_dev = VTOI(fs->lfs_ivnode)->i_dev; 718*53574Sheideman strategy = VTOI(fs->lfs_ivnode)->i_devvp->v_op[VOFFSET(vop_strategy)]; 71951301Sbostic 72052688Sbostic /* 72152688Sbostic * When we simply write the blocks we lose a rotation for every block 72252688Sbostic * written. To avoid this problem, we allocate memory in chunks, copy 72352688Sbostic * the buffers into the chunk and write the chunk. 56K was chosen as 72452688Sbostic * some driver/controllers can't handle unsigned 16 bit transfers. 72552688Sbostic * When the data is copied to the chunk, turn off the the B_LOCKED bit 72652688Sbostic * and brelse the buffer (which will move them to the LRU list). Add 72752688Sbostic * the B_CALL flag to the buffer header so we can count I/O's for the 72852688Sbostic * checkpoints and so we can release the allocated memory. 72952688Sbostic * 73052688Sbostic * XXX 73152688Sbostic * This should be removed if the new virtual memory system allows us to 73252688Sbostic * easily make the buffers contiguous in kernel memory and if that's 73352688Sbostic * fast enough. 73452688Sbostic */ 73552688Sbostic #define LFS_CHUNKSIZE (56 * 1024) 73652688Sbostic ch_per_blk = LFS_CHUNKSIZE / fs->lfs_bsize; 73752688Sbostic for (bpp = sp->bpp, i = nblocks; i;) { 73852688Sbostic num = ch_per_blk; 73952688Sbostic if (num > i) 74052688Sbostic num = i; 74152688Sbostic i -= num; 74252688Sbostic size = num * fs->lfs_bsize; 74352688Sbostic 74452688Sbostic cbp = lfs_newbuf(fs, (*bpp)->b_blkno, 0); 74552688Sbostic cbp->b_dev = i_dev; 74652688Sbostic cbp->b_flags = B_ASYNC | B_BUSY | B_CALL; 74752688Sbostic cbp->b_iodone = lfs_callback; 74852688Sbostic cbp->b_saveaddr = cbp->b_un.b_addr; 74952688Sbostic cbp->b_un.b_addr = malloc(size, M_SEGMENT, M_WAITOK); 75052688Sbostic 75152688Sbostic s = splbio(); 75252688Sbostic ++fs->lfs_iocount; 75352688Sbostic for (p = cbp->b_un.b_addr; num--;) { 75452688Sbostic bp = *bpp++; 75552688Sbostic bcopy(bp->b_un.b_addr, p, bp->b_bcount); 75652688Sbostic p += bp->b_bcount; 75752688Sbostic bp->b_flags &= 75852688Sbostic ~(B_DONE | B_ERROR | B_READ | B_DELWRI | B_LOCKED); 75952688Sbostic if (!(bp->b_flags & B_NOCACHE)) { 76052688Sbostic bremfree(bp); 76152688Sbostic reassignbuf(bp, bp->b_vp); 76252688Sbostic } 76352688Sbostic brelse(bp); 76451860Sbostic } 76552688Sbostic splx(s); 76652688Sbostic cbp->b_bcount = p - cbp->b_un.b_addr; 767*53574Sheideman vop_strategy_a.a_desc = VDESC(vop_strategy); 768*53574Sheideman vop_strategy_a.a_bp = cbp; 769*53574Sheideman (strategy)(&vop_strategy_a); 77051860Sbostic } 77152077Sbostic 77252682Sstaelin /* Update the segment usage information. */ 77352682Sstaelin LFS_SEGENTRY(sup, fs, sp->seg_number, bp); 77452682Sstaelin sup->su_nbytes += nblocks - 1 - 77552682Sstaelin (ssp->ss_ninos + INOPB(fs) - 1) / INOPB(fs) << fs->lfs_bshift; 77652682Sstaelin sup->su_nbytes += ssp->ss_ninos * sizeof(struct dinode); 77752682Sstaelin sup->su_lastmod = time.tv_sec; 77852682Sstaelin LFS_UBWRITE(bp); 77951188Sbostic } 78051188Sbostic 78152077Sbostic void 78251860Sbostic lfs_writesuper(fs, sp) 78351499Sbostic struct lfs *fs; 78452085Sbostic struct segment *sp; 78551301Sbostic { 786*53574Sheideman USES_VOP_STRATEGY; 78752085Sbostic struct buf *bp; 78851860Sbostic dev_t i_dev; 789*53574Sheideman int (*strategy) __P((struct vop_strategy_args *)); 79051301Sbostic 79151860Sbostic #ifdef VERBOSE 79251860Sbostic printf("lfs_writesuper\n"); 79351860Sbostic #endif 79451860Sbostic i_dev = VTOI(fs->lfs_ivnode)->i_dev; 795*53574Sheideman strategy = VTOI(fs->lfs_ivnode)->i_devvp->v_op[VOFFSET(vop_strategy)]; 79651356Sbostic 79751342Sbostic /* Checksum the superblock and copy it into a buffer. */ 79851499Sbostic fs->lfs_cksum = cksum(fs, sizeof(struct lfs) - sizeof(fs->lfs_cksum)); 79952688Sbostic bp = lfs_newbuf(fs, fs->lfs_sboffs[0], LFS_SBPAD); 80051860Sbostic *bp->b_un.b_lfs = *fs; 80151215Sbostic 80251356Sbostic /* Write the first superblock (wait). */ 80351860Sbostic bp->b_dev = i_dev; 80451915Sbostic bp->b_flags |= B_BUSY; 80551860Sbostic bp->b_flags &= ~(B_DONE | B_ERROR | B_READ | B_DELWRI); 806*53574Sheideman vop_strategy_a.a_desc = VDESC(vop_strategy); 807*53574Sheideman vop_strategy_a.a_bp = bp; 808*53574Sheideman (strategy)(&vop_strategy_a); 80951215Sbostic biowait(bp); 81051342Sbostic 81151356Sbostic /* Write the second superblock (don't wait). */ 81251215Sbostic bp->b_blkno = bp->b_lblkno = fs->lfs_sboffs[1]; 81351915Sbostic bp->b_flags |= B_ASYNC | B_BUSY; 81451860Sbostic bp->b_flags &= ~(B_DONE | B_ERROR | B_READ | B_DELWRI); 815*53574Sheideman (strategy)(&vop_strategy_a); 81651215Sbostic } 81751215Sbostic 81851342Sbostic /* 81951342Sbostic * Logical block number match routines used when traversing the dirty block 82051342Sbostic * chain. 82151342Sbostic */ 82252077Sbostic int 82352077Sbostic lfs_match_data(fs, bp) 82451860Sbostic struct lfs *fs; 82552085Sbostic struct buf *bp; 82651215Sbostic { 82751342Sbostic return (bp->b_lblkno >= 0); 82851215Sbostic } 82951215Sbostic 83052077Sbostic int 83152077Sbostic lfs_match_indir(fs, bp) 83251860Sbostic struct lfs *fs; 83352085Sbostic struct buf *bp; 83451215Sbostic { 83551860Sbostic int lbn; 83651860Sbostic 83751860Sbostic lbn = bp->b_lblkno; 83851860Sbostic return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 0); 83951215Sbostic } 84051215Sbostic 84152077Sbostic int 84252077Sbostic lfs_match_dindir(fs, bp) 84351860Sbostic struct lfs *fs; 84452085Sbostic struct buf *bp; 84551215Sbostic { 84651860Sbostic int lbn; 84751860Sbostic 84851860Sbostic lbn = bp->b_lblkno; 84951860Sbostic return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 1); 85051215Sbostic } 85151215Sbostic 85252077Sbostic int 85352077Sbostic lfs_match_tindir(fs, bp) 85451499Sbostic struct lfs *fs; 85552085Sbostic struct buf *bp; 85651342Sbostic { 85751860Sbostic int lbn; 85851342Sbostic 85951860Sbostic lbn = bp->b_lblkno; 86051860Sbostic return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 2); 86151860Sbostic } 86251342Sbostic 86351860Sbostic /* 86451860Sbostic * Allocate a new buffer header. 86551860Sbostic */ 86652085Sbostic struct buf * 86752688Sbostic lfs_newbuf(fs, daddr, size) 86851860Sbostic struct lfs *fs; 86951860Sbostic daddr_t daddr; 87051860Sbostic size_t size; 87151860Sbostic { 87252085Sbostic struct buf *bp; 87351342Sbostic 87451860Sbostic #ifdef VERBOSE 87551860Sbostic printf("lfs_newbuf\n"); 87651860Sbostic #endif 87751860Sbostic bp = getnewbuf(); 87851860Sbostic bremhash(bp); 87951860Sbostic bgetvp(fs->lfs_ivnode, bp); 88051860Sbostic bp->b_bcount = 0; 88151860Sbostic bp->b_lblkno = daddr; 88251860Sbostic bp->b_blkno = daddr; 88351860Sbostic bp->b_error = 0; 88451860Sbostic bp->b_resid = 0; 88552688Sbostic if (size) 88652688Sbostic allocbuf(bp, size); 88751860Sbostic bp->b_flags |= B_NOCACHE; 88852688Sbostic bp->b_saveaddr = NULL; 88951915Sbostic binshash(bp, &bfreelist[BQ_AGE]); 89051860Sbostic return (bp); 89151860Sbostic } 89251342Sbostic 89353347Sbostic void 89451860Sbostic lfs_callback(bp) 89552085Sbostic struct buf *bp; 89651860Sbostic { 89751860Sbostic struct lfs *fs; 89851342Sbostic 89951860Sbostic fs = VFSTOUFS(bp->b_vp->v_mount)->um_lfs; 90051860Sbostic #ifdef DIAGNOSTIC 90151860Sbostic if (fs->lfs_iocount == 0) 90251860Sbostic panic("lfs_callback: zero iocount\n"); 90351860Sbostic #endif 90451860Sbostic if (--fs->lfs_iocount == 0) 90552688Sbostic wakeup(&fs->lfs_iocount); 90651915Sbostic 90752688Sbostic if (bp->b_saveaddr) { 90852688Sbostic free(bp->b_un.b_addr, M_SEGMENT); 90952688Sbostic bp->b_un.b_addr = bp->b_saveaddr; 91052819Sbostic bp->b_saveaddr = NULL; 91152688Sbostic } 91251860Sbostic brelse(bp); 91351860Sbostic } 91451342Sbostic 91551215Sbostic /* 91651188Sbostic * Shellsort (diminishing increment sort) from Data Structures and 91751188Sbostic * Algorithms, Aho, Hopcraft and Ullman, 1983 Edition, page 290; 91851188Sbostic * see also Knuth Vol. 3, page 84. The increments are selected from 91951188Sbostic * formula (8), page 95. Roughly O(N^3/2). 92051188Sbostic */ 92151188Sbostic /* 92251188Sbostic * This is our own private copy of shellsort because we want to sort 92351188Sbostic * two parallel arrays (the array of buffer pointers and the array of 92451188Sbostic * logical block numbers) simultaneously. Note that we cast the array 92551188Sbostic * of logical block numbers to a unsigned in this routine so that the 92651188Sbostic * negative block numbers (meta data blocks) sort AFTER the data blocks. 92751188Sbostic */ 92852077Sbostic void 92952077Sbostic lfs_shellsort(bp_array, lb_array, nmemb) 93052085Sbostic struct buf **bp_array; 93151215Sbostic daddr_t *lb_array; 93251188Sbostic register int nmemb; 93351188Sbostic { 93451188Sbostic static int __rsshell_increments[] = { 4, 1, 0 }; 93551188Sbostic register int incr, *incrp, t1, t2; 93652085Sbostic struct buf *bp_temp; 93751188Sbostic u_long lb_temp; 93851188Sbostic 93951188Sbostic for (incrp = __rsshell_increments; incr = *incrp++;) 94051188Sbostic for (t1 = incr; t1 < nmemb; ++t1) 94151188Sbostic for (t2 = t1 - incr; t2 >= 0;) 94251188Sbostic if (lb_array[t2] > lb_array[t2 + incr]) { 94351188Sbostic lb_temp = lb_array[t2]; 94451188Sbostic lb_array[t2] = lb_array[t2 + incr]; 94551188Sbostic lb_array[t2 + incr] = lb_temp; 94651188Sbostic bp_temp = bp_array[t2]; 94751188Sbostic bp_array[t2] = bp_array[t2 + incr]; 94851188Sbostic bp_array[t2 + incr] = bp_temp; 94951188Sbostic t2 -= incr; 95051188Sbostic } else 95151188Sbostic break; 95251188Sbostic } 953