BSD 4_4_Lite1 release
[unix-history] / usr / src / sys / ufs / lfs / lfs_segment.c
index 9bb3c64..249d59d 100644 (file)
 /*
 /*
- * Copyright (c) 1991 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  *
- * %sccs.include.redist.c%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
  *
  *
- *     @(#)lfs_segment.c       5.6 (Berkeley) %G%
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)lfs_segment.c       8.5 (Berkeley) 1/4/94
  */
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/namei.h>
  */
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/namei.h>
-#include <sys/resourcevar.h>
 #include <sys/kernel.h>
 #include <sys/kernel.h>
+#include <sys/resourcevar.h>
 #include <sys/file.h>
 #include <sys/stat.h>
 #include <sys/buf.h>
 #include <sys/proc.h>
 #include <sys/conf.h>
 #include <sys/vnode.h>
 #include <sys/file.h>
 #include <sys/stat.h>
 #include <sys/buf.h>
 #include <sys/proc.h>
 #include <sys/conf.h>
 #include <sys/vnode.h>
-#include <sys/specdev.h>
-#include <sys/fifo.h>
 #include <sys/malloc.h>
 #include <sys/mount.h>
 
 #include <sys/malloc.h>
 #include <sys/mount.h>
 
-#include <ufs/quota.h>
-#include <ufs/inode.h>
-#include <ufs/dir.h>
-#include <ufs/ufsmount.h>
+#include <miscfs/specfs/specdev.h>
+#include <miscfs/fifofs/fifo.h>
 
 
-#include <lfs/lfs.h>
-#include <lfs/lfs_extern.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
 
 
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+extern int count_lock_queue __P((void));
+
+#define MAX_ACTIVE     10
 /*
 /*
- * Add a check so that if the segment is empty, you don't write it.
- *
- * Change lfs_ialloc to allocate a new page of inodes if you have to.
- *
- * Need to keep vnode v_numoutput up to date for pending writes?  Could
- * actually fire off the datablock writes before you finish.  This would give
- * them a chance to get started earlier.
+ * Determine if it's OK to start a partial in this segment, or if we need
+ * to go on to a new segment.
  */
  */
-
-static int      lfs_biocallback __P((BUF *));
-static void     lfs_endsum __P((LFS *, SEGMENT *, int));
-static SEGMENT *lfs_gather
-                   __P((LFS *, SEGMENT *, VNODE *, int (*) __P((BUF *))));
-static BUF     *lfs_newbuf __P((LFS *, daddr_t, size_t));
-static SEGMENT *lfs_newseg __P((LFS *));
-static SEGMENT *lfs_newsum __P((LFS *, SEGMENT *));
-static daddr_t  lfs_nextseg __P((LFS *));
-static void     lfs_updatemeta __P((LFS *, SEGMENT *, INODE *, daddr_t *,
-                    BUF **, int));
-static SEGMENT *lfs_writeckp __P((LFS *, SEGMENT *));
-static SEGMENT *lfs_writefile __P((LFS *, SEGMENT *, VNODE *, int));
-static SEGMENT *lfs_writeinode __P((LFS *, SEGMENT *, INODE *));
-static void     lfs_writeseg __P((LFS *, SEGMENT *));
-static void     lfs_writesum __P((LFS *));
-static void     lfs_writesuper __P((LFS *));
-static int      match_data __P((BUF *));
-static int      match_dindir __P((BUF *));
-static int      match_indir __P((BUF *));
-static daddr_t  next __P((LFS *, SEGMENT *, int *));
-static void     shellsort __P((BUF **, daddr_t *, register int));
+#define        LFS_PARTIAL_FITS(fs) \
+       ((fs)->lfs_dbpseg - ((fs)->lfs_offset - (fs)->lfs_curseg) > \
+       1 << (fs)->lfs_fsbtodb)
+
+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_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 *));
+void    lfs_newseg __P((struct lfs *));
+void    lfs_shellsort __P((struct buf **, daddr_t *, register int));
+void    lfs_supercallback __P((struct buf *));
+void    lfs_updatemeta __P((struct segment *));
+int     lfs_vref __P((struct vnode *));
+void    lfs_vunref __P((struct vnode *));
+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 *));
+void    lfs_writesuper __P((struct lfs *));
+void    lfs_writevnodes __P((struct lfs *fs, struct mount *mp,
+           struct segment *sp, int dirops));
+
+int    lfs_allclean_wakeup;            /* Cleaner wakeup address. */
+
+/* Statistics Counters */
+#define DOSTATS
+struct lfs_stats lfs_stats;
+
+/* op values to lfs_writevnodes */
+#define        VN_REG  0
+#define        VN_DIROP        1
+#define        VN_EMPTY        2
 
 /*
 
 /*
- * XXX -- when we add fragments in here, we will need to allocate a larger
- * buffer pointer array (sp->bpp).
+ * Ifile and meta data blocks are not marked busy, so segment writes MUST be
+ * single threaded.  Currently, there are two paths into lfs_segwrite, sync()
+ * and getnewbuf().  They both mark the file system busy.  Lfs_vflush()
+ * explicitly marks the file system busy.  So lfs_segwrite is safe.  I think.
  */
  */
+
 int
 int
-lfs_segwrite(mp, do_ckp)
-       MOUNT *mp;
-       int do_ckp;                     /* do a checkpoint too */
+lfs_vflush(vp)
+       struct vnode *vp;
 {
 {
-       INODE *ip;
-       LFS *fs;
-       VNODE *vp;
-       SEGMENT *sp;
-       int s;
+       struct inode *ip;
+       struct lfs *fs;
+       struct segment *sp;
 
 
-       fs = VFSTOUFS(mp)->um_lfs;
+       fs = VFSTOUFS(vp->v_mount)->um_lfs;
+       if (fs->lfs_nactive > MAX_ACTIVE)
+               return(lfs_segwrite(vp->v_mount, SEGM_SYNC|SEGM_CKP));
+       lfs_seglock(fs, SEGM_SYNC);
+       sp = fs->lfs_sp;
 
 
-#ifdef DIAGNOSTIC
-       if (fs->lfs_seglist != NULL)
-               panic("lfs_segwrite: seglist not NULL");
+
+       ip = VTOI(vp);
+       if (vp->v_dirtyblkhd.lh_first == NULL)
+               lfs_writevnodes(fs, vp->v_mount, sp, VN_EMPTY);
+
+       do {
+               do {
+                       if (vp->v_dirtyblkhd.lh_first != NULL)
+                               lfs_writefile(fs, sp, vp);
+               } while (lfs_writeinode(fs, sp, ip));
+
+       } while (lfs_writeseg(fs, sp) && ip->i_number == LFS_IFILE_INUM);
+
+#ifdef DOSTATS
+       ++lfs_stats.nwrites;
+       if (sp->seg_flags & SEGM_SYNC)
+               ++lfs_stats.nsync_writes;
+       if (sp->seg_flags & SEGM_CKP)
+               ++lfs_stats.ncheckpoints;
 #endif
 #endif
+       lfs_segunlock(fs);
+       return (0);
+}
 
 
-       /*
-        * LFS requires that the summary blocks be written after the rest of
-        * the segment, and that the super blocks (on checkpoint) be written
-        * last of all.  We keep a cumulative count of the outstanding blocks
-        * from all of the segments, and write these blocks when this count
-        * goes to zero.  If the disk drive catches up with us it could go
-        * to zero before we finish, so we artificially increment it by one
-        * until we've scheduled all of the writes we intend to do.  At the
-        * moment, the user's process hangs around so we can sleep; this should
-        * probably be redone using a kernel thread.
-        */
-       s = splbio();
-       fs->lfs_iocount = 1;
-       splx(s);
+void
+lfs_writevnodes(fs, mp, sp, op)
+       struct lfs *fs;
+       struct mount *mp;
+       struct segment *sp;
+       int op;
+{
+       struct inode *ip;
+       struct vnode *vp;
 
 
-       sp = lfs_newseg(fs);
 loop:
 loop:
-       for (vp = mp->mnt_mounth; vp; vp = vp->v_mountf) {
+       for (vp = mp->mnt_vnodelist.lh_first;
+            vp != NULL;
+            vp = vp->v_mntvnodes.le_next) {
                /*
                 * If the vnode that we are about to sync is no longer
                 * associated with this mount point, start over.
                 */
                if (vp->v_mount != mp)
                        goto loop;
                /*
                 * If the vnode that we are about to sync is no longer
                 * associated with this mount point, start over.
                 */
                if (vp->v_mount != mp)
                        goto loop;
-               if (VOP_ISLOCKED(vp))
+
+               /* XXX ignore dirops for now
+               if (op == VN_DIROP && !(vp->v_flag & VDIROP) ||
+                   op != VN_DIROP && (vp->v_flag & VDIROP))
                        continue;
                        continue;
-               ip = VTOI(vp);
-               if (ip->i_number == LFS_IFILE_INUM)
+               */
+
+               if (op == VN_EMPTY && vp->v_dirtyblkhd.lh_first)
                        continue;
                        continue;
-               if ((ip->i_flag & (IMOD | IACC | IUPD | ICHG)) == 0 &&
-                   vp->v_dirtyblkhd == NULL)
+
+               if (vp->v_type == VNON)
                        continue;
                        continue;
-               if (vget(vp))
-                       goto loop;
-               sp = lfs_writefile(fs, sp, vp, do_ckp);
-               vput(vp);
+
+               if (lfs_vref(vp))
+                       continue;
+
+               /*
+                * Write the inode/file if dirty and it's not the
+                * the IFILE.
+                */
+               ip = VTOI(vp);
+               if ((ip->i_flag &
+                   (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE) ||
+                   vp->v_dirtyblkhd.lh_first != NULL) &&
+                   ip->i_number != LFS_IFILE_INUM) {
+                       if (vp->v_dirtyblkhd.lh_first != NULL)
+                               lfs_writefile(fs, sp, vp);
+                       (void) lfs_writeinode(fs, sp, ip);
+               }
+               vp->v_flag &= ~VDIROP;
+               lfs_vunref(vp);
        }
        }
-       if (do_ckp)
-               sp = lfs_writeckp(fs, sp);
-       lfs_writeseg(fs, sp);
+}
 
 
-       s = splbio();
-       if (--fs->lfs_iocount)
-               sleep(&fs->lfs_iocount, PRIBIO + 1);
-       splx(s);
-       lfs_writesum(fs);
+int
+lfs_segwrite(mp, flags)
+       struct mount *mp;
+       int flags;                      /* Do a checkpoint. */
+{
+       struct buf *bp;
+       struct inode *ip;
+       struct lfs *fs;
+       struct segment *sp;
+       struct vnode *vp;
+       SEGUSE *segusep;
+       daddr_t ibno;
+       CLEANERINFO *cip;
+       int clean, do_ckp, error, i;
+
+       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 );
+
+       /*
+        * Allocate a segment structure and enough space to hold pointers to
+        * the maximum possible number of buffers which can be described in a
+        * single summary block.
+        */
+       do_ckp = flags & SEGM_CKP || fs->lfs_nactive > MAX_ACTIVE;
+       lfs_seglock(fs, flags | (do_ckp ? SEGM_CKP : 0));
+       sp = fs->lfs_sp;
+
+       lfs_writevnodes(fs, mp, sp, VN_REG);
+
+       /* XXX ignore ordering of dirops for now */
+       /* XXX
+       fs->lfs_writer = 1;
+       if (fs->lfs_dirops && (error =
+           tsleep(&fs->lfs_writer, PRIBIO + 1, "lfs writer", 0))) {
+               free(sp->bpp, M_SEGMENT);
+               free(sp, M_SEGMENT); 
+               fs->lfs_writer = 0;
+               return (error);
+       }
+
+       lfs_writevnodes(fs, mp, sp, VN_DIROP);
+       */
+
+       /*
+        * If we are doing a checkpoint, mark everything since the
+        * last checkpoint as no longer ACTIVE.
+        */
        if (do_ckp)
        if (do_ckp)
-               lfs_writesuper(fs);
+               for (ibno = fs->lfs_cleansz + fs->lfs_segtabsz;
+                    --ibno >= fs->lfs_cleansz; ) {
+                       if (bread(fs->lfs_ivnode, ibno, fs->lfs_bsize,
+                           NOCRED, &bp))
+
+                               panic("lfs: ifile read");
+                       segusep = (SEGUSE *)bp->b_data;
+                       for (i = fs->lfs_sepb; i--; segusep++)
+                               segusep->su_flags &= ~SEGUSE_ACTIVE;
+                               
+                       error = VOP_BWRITE(bp);
+               }
+
+       if (do_ckp || fs->lfs_doifile) {
+redo:
+               vp = fs->lfs_ivnode;
+               while (vget(vp, 1));
+               ip = VTOI(vp);
+               if (vp->v_dirtyblkhd.lh_first != NULL)
+                       lfs_writefile(fs, sp, vp);
+               (void)lfs_writeinode(fs, sp, ip);
+               vput(vp);
+               if (lfs_writeseg(fs, sp) && do_ckp)
+                       goto redo;
+       } else
+               (void) lfs_writeseg(fs, sp);
+
+       /*
+        * If the I/O count is non-zero, sleep until it reaches zero.  At the
+        * moment, the user's process hangs around so we can sleep.
+        */
+       /* XXX ignore dirops for now
+       fs->lfs_writer = 0;
+       fs->lfs_doifile = 0;
+       wakeup(&fs->lfs_dirops);
+       */
+
+#ifdef DOSTATS
+       ++lfs_stats.nwrites;
+       if (sp->seg_flags & SEGM_SYNC)
+               ++lfs_stats.nsync_writes;
+       if (sp->seg_flags & SEGM_CKP)
+               ++lfs_stats.ncheckpoints;
+#endif
+       lfs_segunlock(fs);
        return (0);
 }
 
        return (0);
 }
 
-static int                                     /* XXX should be void */
-lfs_biocallback(bp)
-       BUF *bp;
+/*
+ * Write the dirty blocks associated with a vnode.
+ */
+void
+lfs_writefile(fs, sp, vp)
+       struct lfs *fs;
+       struct segment *sp;
+       struct vnode *vp;
 {
 {
-       LFS *fs;
+       struct buf *bp;
+       struct finfo *fip;
+       IFILE *ifp;
 
 
-       /*
-        * XXX
-        * Reset the flags (probably wrong).  If the contents of the buffer
-        * are valid, move back onto the clean list.
-        */
-       bp->b_flags &= ~(B_READ | B_DONE | B_ERROR | B_DELWRI);
-       fs = VFSTOUFS(bp->b_vp->v_mount)->um_lfs;
-       if (bp->b_flags & B_NOCACHE)
-               bp->b_vp = NULL;
-       else
-               reassignbuf(bp, bp->b_vp);
+       if (sp->seg_bytes_left < fs->lfs_bsize ||
+           sp->sum_bytes_left < sizeof(struct finfo))
+               (void) lfs_writeseg(fs, sp);
+
+       sp->sum_bytes_left -= sizeof(struct finfo) - sizeof(daddr_t);
+       ++((SEGSUM *)(sp->segsum))->ss_nfinfo;
+
+       fip = sp->fip;
+       fip->fi_nblocks = 0;
+       fip->fi_ino = VTOI(vp)->i_number;
+       LFS_IENTRY(ifp, fs, fip->fi_ino, bp);
+       fip->fi_version = ifp->if_version;
        brelse(bp);
 
        brelse(bp);
 
-#ifdef SEGWRITE
-printf("callback: buffer: %x iocount %d\n", bp, fs->lfs_iocount);
+       /*
+        * It may not be necessary to write the meta-data blocks at this point,
+        * as the roll-forward recovery code should be able to reconstruct the
+        * list.
+        */
+       lfs_gather(fs, sp, vp, lfs_match_data);
+       lfs_gather(fs, sp, vp, lfs_match_indir);
+       lfs_gather(fs, sp, vp, lfs_match_dindir);
+#ifdef TRIPLE
+       lfs_gather(fs, sp, vp, lfs_match_tindir);
 #endif
 #endif
-       if (fs->lfs_iocount == 0)
-               panic("lfs_biocallback: zero iocount\n");
 
 
-       if (--fs->lfs_iocount == 0)
-               wakeup(&fs->lfs_iocount);
+       fip = sp->fip;
+       if (fip->fi_nblocks != 0) {
+               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);
+               --((SEGSUM *)(sp->segsum))->ss_nfinfo;
+       }
 }
 
 }
 
-/* Finish up a summary block. */
-static void
-lfs_endsum(fs, sp, calc_next)
-       LFS *fs;
-       SEGMENT *sp;
-       int calc_next;
+int
+lfs_writeinode(fs, sp, ip)
+       struct lfs *fs;
+       struct segment *sp;
+       struct inode *ip;
 {
 {
-       SEGSUM *ssp;
-       int nsums_per_blk;
+       struct buf *bp, *ibp;
+       IFILE *ifp;
+       SEGUSE *sup;
+       daddr_t daddr;
+       ino_t ino;
+       int error, i, ndx;
+       int redo_ifile = 0;
 
 
-       if (sp->sbp == NULL)
-               return;
+       if (!(ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)))
+               return(0);
 
 
-       ssp = sp->segsum;
+       /* Allocate a new inode block if necessary. */
+       if (sp->ibp == NULL) {
+               /* Allocate a new segment if necessary. */
+               if (sp->seg_bytes_left < fs->lfs_bsize ||
+                   sp->sum_bytes_left < sizeof(daddr_t))
+                       (void) lfs_writeseg(fs, sp);
+
+               /* Get next inode block. */
+               daddr = fs->lfs_offset;
+               fs->lfs_offset += fsbtodb(fs, 1);
+               sp->ibp = *sp->cbpp++ =
+                   lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp, daddr,
+                   fs->lfs_bsize);
+               /* Zero out inode numbers */
+               for (i = 0; i < INOPB(fs); ++i)
+                       ((struct dinode *)sp->ibp->b_data)[i].di_inumber = 0;
+               ++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);
+               ndx = LFS_SUMMARY_SIZE / sizeof(daddr_t) -
+                   sp->ninodes / INOPB(fs) - 1;
+               ((daddr_t *)(sp->segsum))[ndx] = daddr;
+       }
+
+       /* Update the inode times and copy the inode onto the inode page. */
+       if (ip->i_flag & IN_MODIFIED)
+               --fs->lfs_uinodes;
+       ITIMES(ip, &time, &time);
+       ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
+       bp = sp->ibp;
+       ((struct dinode *)bp->b_data)[sp->ninodes % INOPB(fs)] = ip->i_din;
+       /* Increment inode count in segment summary block. */
+       ++((SEGSUM *)(sp->segsum))->ss_ninos;
+
+       /* If this page is full, set flag to allocate a new page. */
+       if (++sp->ninodes % INOPB(fs) == 0)
+               sp->ibp = NULL;
 
        /*
 
        /*
-        * Compute the address of the next summary block if calc_next is set,
-        * otherwise end the chain.  If the summary block is full, close it
-        * by setting sp->sbp to NULL, so lfs_newsum will allocate a new one.
-        * Calculate the checksum last.
+        * If updating the ifile, update the super-block.  Update the disk
+        * address and access times for this inode in the ifile.
         */
         */
-       nsums_per_blk = fs->lfs_bsize / LFS_SUMMARY_SIZE;
-       if (sp->nsums % nsums_per_blk == 0) {
-               ssp->ss_nextsum =
-                   calc_next ? next(fs, sp, NULL) +
-                   (nsums_per_blk - 1) * LFS_SUMMARY_SIZE / DEV_BSIZE :
-                   (daddr_t)-1;
-               sp->sbp = NULL;
-       } else
-               ssp->ss_nextsum = calc_next ?
-                   sp->sum_addr - LFS_SUMMARY_SIZE / DEV_BSIZE : (daddr_t)-1;
+       ino = ip->i_number;
+       if (ino == LFS_IFILE_INUM) {
+               daddr = fs->lfs_idaddr;
+               fs->lfs_idaddr = bp->b_blkno;
+       } else {
+               LFS_IENTRY(ifp, fs, ino, ibp);
+               daddr = ifp->if_daddr;
+               ifp->if_daddr = bp->b_blkno;
+               error = VOP_BWRITE(ibp);
+       }
 
 
-       ssp->ss_cksum =
-           cksum(&ssp->ss_cksum, LFS_SUMMARY_SIZE - sizeof(ssp->ss_cksum));
+       /*
+        * No need to update segment usage if there was no former inode address
+        * or if the last inode address is in the current partial segment.
+        */
+       if (daddr != LFS_UNUSED_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)) {
+                       /* XXX -- Change to a panic. */
+                       printf("lfs: negative bytes (segment %d)\n",
+                           datosn(fs, daddr));
+                       panic("negative bytes");
+               }
+#endif
+               sup->su_nbytes -= sizeof(struct dinode);
+               redo_ifile =
+                   (ino == LFS_IFILE_INUM && !(bp->b_flags & B_GATHERED));
+               error = VOP_BWRITE(bp);
+       }
+       return (redo_ifile);
 }
 
 }
 
-static SEGMENT *
-lfs_gather(fs, sp, vp, match)
-       LFS *fs;
-       SEGMENT *sp;
-       VNODE *vp;
-       int (*match) __P((BUF *));
+int
+lfs_gatherblock(sp, bp, sptr)
+       struct segment *sp;
+       struct buf *bp;
+       int *sptr;
 {
 {
-       BUF **bpp, *bp, *nbp;
-       FINFO *fip;
-       INODE *ip;
-       daddr_t *lbp, *start_lbp;
-       u_long version;
-       int s;
-
-       ip = VTOI(vp);
-       bpp = sp->cbpp;
-       fip = sp->fip;
-       start_lbp = lbp = &fip->fi_blocks[fip->fi_nblocks];
+       struct lfs *fs;
+       int version;
 
 
-       s = splbio();
-       for (bp = vp->v_dirtyblkhd; bp; bp = nbp) {
-               nbp = bp->b_blockf;
-               if (bp->b_flags & B_BUSY)
-                       continue;
+       /*
+        * If full, finish this segment.  We may be doing I/O, so
+        * release and reacquire the splbio().
+        */
 #ifdef DIAGNOSTIC
 #ifdef DIAGNOSTIC
-               if ((bp->b_flags & B_DELWRI) == 0)
-                       panic("lfs_gather: not dirty");
+       if (sp->vp == NULL)
+               panic ("lfs_gatherblock: Null vp in segment");
 #endif
 #endif
-               if (!match(bp))
-                       continue;
-
-               /* Remove the buffer from the free lists, prepare it for I/O. */
-               bremfree(bp);
-               bp->b_flags |= B_BUSY | B_CALL;
-               bp->b_iodone = lfs_biocallback;
-               bp->b_dev = VTOI(fs->lfs_ivnode)->i_dev;
+       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);
+
+               version = sp->fip->fi_version;
+               (void) lfs_writeseg(fs, sp);
+
+               sp->fip->fi_version = version;
+               sp->fip->fi_ino = VTOI(sp->vp)->i_number;
+               /* Add the current file to the segment summary. */
+               ++((SEGSUM *)(sp->segsum))->ss_nfinfo;
+               sp->sum_bytes_left -= 
+                   sizeof(struct finfo) - sizeof(daddr_t);
 
 
-               /* Insert into the buffer list, update the FINFO block. */
-               *sp->cbpp++ = bp;
-               ++fip->fi_nblocks;
-               *lbp++ = bp->b_lblkno;
+               if (sptr)
+                       *sptr = splbio();
+               return(1);
+       }
 
 
-               sp->sum_bytes_left -= sizeof(daddr_t);
-               sp->seg_bytes_left -= bp->b_bufsize;
+       /* Insert into the buffer list, update the FINFO block. */
+       bp->b_flags |= B_GATHERED;
+       *sp->cbpp++ = bp;
+       sp->fip->fi_blocks[sp->fip->fi_nblocks++] = bp->b_lblkno;
 
 
-               /*
-                * Allocate a new summary block (and, possibly, a new segment)
-                * if necessary.  In this case we sort the blocks we've done
-                * so far and assign disk addresses so we can start the new
-                * block correctly.  We may be doing I/O, so we need to release
-                * the splbio() before anything else.
-                */
-               if (sp->sum_bytes_left < sizeof(daddr_t) ||
-                   sp->seg_bytes_left < fs->lfs_bsize) {
-                       splx(s);
-                       lfs_updatemeta(fs,
-                           sp, ip, start_lbp, bpp, lbp - start_lbp);
-
-                       /* Add the current file to the segment summary. */
-                       ++((SEGSUM *)(sp->segsum))->ss_nfinfo;
-
-                       version = fip->fi_version;
-                       if (sp->seg_bytes_left < fs->lfs_bsize) {
-                               lfs_writeseg(fs, sp);
-                               sp = lfs_newseg(fs);
-                       } else if (sp->sum_bytes_left < sizeof(daddr_t))
-                               sp = lfs_newsum(fs, sp);
-
-                       /* A new FINFO either way. */
-                       fip = sp->fip;
-                       fip->fi_version = version;
-                       fip->fi_ino = ip->i_number;
-                       start_lbp = lbp = fip->fi_blocks;
-
-                       bpp = sp->cbpp;
-                       s = splbio();
-               }
-       }
-       splx(s);
-       lfs_updatemeta(fs, sp, ip, start_lbp, bpp, lbp - start_lbp);
-       return (sp);
+       sp->sum_bytes_left -= sizeof(daddr_t);
+       sp->seg_bytes_left -= fs->lfs_bsize;
+       return(0);
 }
 
 }
 
-/*
- * Allocate a new buffer header.
- */
-static BUF *
-lfs_newbuf(fs, daddr, size)
-       LFS *fs;
-       daddr_t daddr;
-       size_t size;
+void
+lfs_gather(fs, sp, vp, match)
+       struct lfs *fs;
+       struct segment *sp;
+       struct vnode *vp;
+       int (*match) __P((struct lfs *, struct buf *));
 {
 {
-       BUF *bp;
+       struct buf *bp;
+       int s;
 
 
-       bp = getnewbuf();
-       bremhash(bp);
-       bp->b_vp = fs->lfs_ivnode;
-       bp->b_bcount = 0;
-       bp->b_blkno = bp->b_lblkno = daddr;
-       bp->b_error = 0;
-       bp->b_resid = 0;
-       bp->b_flags |= B_DELWRI | B_NOCACHE;
-       bp->b_iodone = lfs_biocallback;
-       bp->b_dev = VTOI(fs->lfs_ivnode)->i_dev;
-       allocbuf(bp, size);
-       return (bp);
+       sp->vp = vp;
+       s = splbio();
+loop:  for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = bp->b_vnbufs.le_next) {
+               if (bp->b_flags & B_BUSY || !match(fs, bp) ||
+                   bp->b_flags & B_GATHERED)
+                       continue;
+#ifdef DIAGNOSTIC
+               if (!(bp->b_flags & B_DELWRI))
+                       panic("lfs_gather: bp not B_DELWRI");
+               if (!(bp->b_flags & B_LOCKED))
+                       panic("lfs_gather: bp not B_LOCKED");
+#endif
+               if (lfs_gatherblock(sp, bp, &s))
+                       goto loop;
+       }
+       splx(s);
+       lfs_updatemeta(sp);
+       sp->vp = NULL;
 }
 
 }
 
+
 /*
 /*
- * Start a new segment.
+ * Update the metadata that points to the blocks listed in the FINFO
+ * array.
  */
  */
-static SEGMENT *
-lfs_newseg(fs)
-       LFS *fs;
+void
+lfs_updatemeta(sp)
+       struct segment *sp;
 {
 {
-       FINFO *fip;
-       SEGMENT *sp;
        SEGUSE *sup;
        SEGUSE *sup;
-       SEGSUM *ssp;
-       daddr_t lbn, *lbnp;
-
-       sp = malloc(sizeof(SEGMENT), M_SEGMENT, M_WAITOK);
-       sp->nextp = NULL;
-       sp->cbpp = sp->bpp =
-           malloc(fs->lfs_ssize * sizeof(BUF *), M_SEGMENT, M_WAITOK);
-       sp->ibp = sp->sbp = NULL;
-       sp->seg_bytes_left = (fs->lfs_segmask + 1);
-       sp->saddr = fs->lfs_nextseg;
-       sp->sum_addr = sp->saddr + sp->seg_bytes_left / DEV_BSIZE;
-       sp->ninodes = 0;
-       sp->nsums = 0;
-       sp->seg_number =
-           (sp->saddr - fs->lfs_sboffs[0]) / fsbtodb(fs, fs->lfs_ssize);
-
-       /* Advance to the next segment. */
-       fs->lfs_nextseg = lfs_nextseg(fs);
+       struct buf *bp;
+       struct lfs *fs;
+       struct vnode *vp;
+       struct indir a[NIADDR + 2], *ap;
+       struct inode *ip;
+       daddr_t daddr, lbn, off;
+       int db_per_fsb, error, i, nblocks, num;
+
+       vp = sp->vp;
+       nblocks = &sp->fip->fi_blocks[sp->fip->fi_nblocks] - sp->start_lbp;
+       if (vp == NULL || nblocks == 0) 
+               return;
 
 
-       /* Initialize the summary block. */
-       sp = lfs_newsum(fs, sp);
+       /* Sort the blocks. */
+       if (!(sp->seg_flags & SEGM_CLEAN))
+               lfs_shellsort(sp->start_bpp, sp->start_lbp, nblocks);
 
        /*
 
        /*
-        * If su_nbytes non-zero after the segment was cleaned, the segment
-        * contains a super-block.  Add segment summary information to not
-        * allocate over it.
+        * Assign disk addresses, and update references to the logical
+        * block and the segment usage information.
         */
         */
-       sup = fs->lfs_segtab + sp->seg_number;
-       if (sup->su_nbytes != 0) {
-               ssp = (SEGSUM *)sp->segsum;
-               ++ssp->ss_nfinfo;
-               fip = sp->fip;
-               fip->fi_nblocks = LFS_SBPAD >> fs->lfs_bshift;
-               fip->fi_version = 1;
-               fip->fi_ino = LFS_UNUSED_INUM;
-               lbnp = fip->fi_blocks;
-               for (lbn = 0; lbn < fip->fi_nblocks; ++lbn)
-                       *lbnp++ = lbn;
-               sp->saddr += fsbtodb(fs, fip->fi_nblocks);
-               sp->seg_bytes_left -= sup->su_nbytes;
-               sp->sum_bytes_left -=
-                   sizeof(FINFO) + (fip->fi_nblocks - 1) * sizeof(daddr_t);
-               sp->fip = (FINFO *)lbnp;
+       fs = sp->fs;
+       db_per_fsb = fsbtodb(fs, 1);
+       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 = ufs_bmaparray(vp, lbn, &daddr, a, &num, NULL))
+                       panic("lfs_updatemeta: ufs_bmaparray %d", error);
+               ip = VTOI(vp);
+               switch (num) {
+               case 0:
+                       ip->i_db[lbn] = off;
+                       break;
+               case 1:
+                       ip->i_ib[a[0].in_off] = off;
+                       break;
+               default:
+                       ap = &a[num - 1];
+                       if (bread(vp, ap->in_lbn, fs->lfs_bsize, NOCRED, &bp))
+                               panic("lfs_updatemeta: bread bno %d",
+                                   ap->in_lbn);
+                       /*
+                        * Bread may create a new indirect block which needs
+                        * 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);
+                       }
+                       ((daddr_t *)bp->b_data)[ap->in_off] = off;
+                       VOP_BWRITE(bp);
+               }
+
+               /* Update segment usage information. */
+               if (daddr != UNASSIGNED &&
+                   !(daddr >= fs->lfs_lastpseg && daddr <= off)) {
+                       LFS_SEGENTRY(sup, fs, datosn(fs, daddr), bp);
+#ifdef DIAGNOSTIC
+                       if (sup->su_nbytes < fs->lfs_bsize) {
+                               /* XXX -- Change to a panic. */
+                               printf("lfs: negative bytes (segment %d)\n",
+                                   datosn(fs, daddr));
+                               panic ("Negative Bytes");
+                       }
+#endif
+                       sup->su_nbytes -= fs->lfs_bsize;
+                       error = VOP_BWRITE(bp);
+               }
        }
        }
-       return (sp);
 }
 
 }
 
-static SEGMENT *
-lfs_newsum(fs, sp)
-       LFS *fs;
-       SEGMENT *sp;
+/*
+ * Start a new segment.
+ */
+int
+lfs_initseg(fs)
+       struct lfs *fs;
 {
 {
+       struct segment *sp;
+       SEGUSE *sup;
        SEGSUM *ssp;
        SEGSUM *ssp;
-       int nblocks;
-
-       lfs_endsum(fs, sp, 1);
+       struct buf *bp;
+       int repeat;
 
 
-       /* Allocate a new buffer if necessary. */
-       if (sp->sbp == NULL) {
-               /* Allocate a new segment if necessary. */
-               if (sp->seg_bytes_left < fs->lfs_bsize) {
-                       lfs_writeseg(fs, sp);
-                       sp = lfs_newseg(fs);
-               }
+       sp = fs->lfs_sp;
 
 
-               /* Get the next summary block. */
-               sp->sum_addr = next(fs, sp, &nblocks);
-
-               /*
-                * Get a new buffer and enter into the buffer list from
-                * the top of the list.
-                */
-               sp->sbp = sp->bpp[fs->lfs_ssize - (nblocks + 1)] =
-                   lfs_newbuf(fs, sp->sum_addr, fs->lfs_bsize);
+       repeat = 0;
+       /* Advance to the next segment. */
+       if (!LFS_PARTIAL_FITS(fs)) {
+               /* Wake up any cleaning procs waiting on this file system. */
+               wakeup(&lfs_allclean_wakeup);
 
 
-               sp->seg_bytes_left -= fs->lfs_bsize;
+               lfs_newseg(fs);
+               repeat = 1;
+               fs->lfs_offset = fs->lfs_curseg;
+               sp->seg_number = datosn(fs, fs->lfs_curseg);
+               sp->seg_bytes_left = fs->lfs_dbpseg * DEV_BSIZE;
 
                /*
 
                /*
-                * Do a callback for all but the very last summary block in
-                * the segment, for which we wait.
+                * If the segment contains a superblock, update the offset
+                * and summary address to skip over it.
                 */
                 */
-               if (sp->nsums != 0)
-                       sp->sbp->b_flags |= B_CALL;
-               /*
-                * Fill in the block from the end.  The summary block is filled
-                * in from the end to the beginning so that the last summary
-                * is the last thing written, verifying the entire block.  This
-                * should go away when fragments are available.
-                */
-               sp->segsum =
-                   sp->sbp->b_un.b_addr + fs->lfs_bsize - LFS_SUMMARY_SIZE;
-               sp->sum_addr += (fs->lfs_bsize - LFS_SUMMARY_SIZE) / DEV_BSIZE;
-
-#ifdef SEGWRITE
-               printf("alloc summary: bp %x, lblkno %x, bp index %d\n",
-                   sp->sbp, sp->sbp->b_lblkno, fs->lfs_ssize - nblocks);
-#endif
+               LFS_SEGENTRY(sup, fs, sp->seg_number, bp);
+               if (sup->su_flags & SEGUSE_SUPERBLOCK) {
+                       fs->lfs_offset += LFS_SBPAD / DEV_BSIZE;
+                       sp->seg_bytes_left -= LFS_SBPAD;
+               }
+               brelse(bp);
        } else {
        } else {
-               sp->segsum -= LFS_SUMMARY_SIZE;
-               sp->sum_addr -= LFS_SUMMARY_SIZE / DEV_BSIZE;
+               sp->seg_number = datosn(fs, fs->lfs_curseg);
+               sp->seg_bytes_left = (fs->lfs_dbpseg -
+                   (fs->lfs_offset - fs->lfs_curseg)) * DEV_BSIZE;
        }
        }
-       ++sp->nsums;
+       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(VTOI(fs->lfs_ivnode)->i_devvp, fs->lfs_offset,
+            LFS_SUMMARY_SIZE);
+       sp->segsum = (*sp->cbpp)->b_data;
+       bzero(sp->segsum, LFS_SUMMARY_SIZE);
+       sp->start_bpp = ++sp->cbpp;
+       fs->lfs_offset += LFS_SUMMARY_SIZE / DEV_BSIZE;
 
        /* Set point to SEGSUM, initialize it. */
        ssp = sp->segsum;
        ssp->ss_next = fs->lfs_nextseg;
 
        /* Set point to SEGSUM, initialize it. */
        ssp = sp->segsum;
        ssp->ss_next = fs->lfs_nextseg;
-       ssp->ss_prev = fs->lfs_lastseg;
-       ssp->ss_nextsum = (daddr_t)-1;
-       ssp->ss_create = time.tv_sec;
        ssp->ss_nfinfo = ssp->ss_ninos = 0;
 
        /* Set pointer to first FINFO, initialize it. */
        ssp->ss_nfinfo = ssp->ss_ninos = 0;
 
        /* Set pointer to first FINFO, initialize it. */
-       sp->fip = (FINFO *)(sp->segsum + sizeof(SEGSUM));
+       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);
        sp->sum_bytes_left = LFS_SUMMARY_SIZE - sizeof(SEGSUM);
-       return (sp);
-}
-
-#define seginc(fs, sn)         /* increment segment number */ \
-       (((sn) + 1) % (fs)->lfs_nseg)
-/*
- * Return the next segment to write.
- */
-static daddr_t
-lfs_nextseg(fs)
-       LFS *fs;
-{
-       int segnum, sn;
-
-       segnum = sn = datosn(fs, fs->lfs_nextseg);
-       while ((sn = seginc(fs, sn)) != segnum &&
-           fs->lfs_segtab[sn].su_flags & SEGUSE_DIRTY);
 
 
-       if (sn == segnum)
-               panic("lfs_nextseg: file system full");         /* XXX */
-       return (sntoda(fs, sn));
+       return(repeat);
 }
 
 /*
 }
 
 /*
- * Update the metadata that points to the blocks listed in the FINFO
- * array.
+ * Return the next segment to write.
  */
  */
-static void
-lfs_updatemeta(fs, sp, ip, lbp, bpp, nblocks)
-       LFS *fs;
-       SEGMENT *sp;
-       INODE *ip;
-       daddr_t *lbp;
-       BUF **bpp;
-       int nblocks;
+void
+lfs_newseg(fs)
+       struct lfs *fs;
 {
 {
-       SEGUSE *segup;
-       BUF **lbpp, *bp;
-       daddr_t daddr, iblkno;
-       int db_per_fsb, error, i;
-       long lbn;
-
-       if (nblocks == 0)
-               return;
-
-       /* Sort the blocks and add disk addresses */
-       shellsort(bpp, lbp, nblocks);
-
-       db_per_fsb = 1 << fs->lfs_fsbtodb;
-       for (lbpp = bpp, i = 0; i < nblocks; ++i, ++lbpp) {
-               (*lbpp)->b_blkno = sp->saddr;
-               sp->saddr += db_per_fsb;
+       CLEANERINFO *cip;
+       SEGUSE *sup;
+       struct buf *bp;
+       int curseg, isdirty, sn;
+
+        LFS_SEGENTRY(sup, fs, datosn(fs, fs->lfs_nextseg), bp);
+        sup->su_flags |= SEGUSE_DIRTY | SEGUSE_ACTIVE;
+       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;
+       (void) VOP_BWRITE(bp);
+
+       fs->lfs_lastseg = fs->lfs_curseg;
+       fs->lfs_curseg = fs->lfs_nextseg;
+       for (sn = curseg = datosn(fs, fs->lfs_curseg);;) {
+               sn = (sn + 1) % fs->lfs_nseg;
+               if (sn == curseg)
+                       panic("lfs_nextseg: no clean segments");
+               LFS_SEGENTRY(sup, fs, sn, bp);
+               isdirty = sup->su_flags & SEGUSE_DIRTY;
+               brelse(bp);
+               if (!isdirty)
+                       break;
        }
 
        }
 
-       for (lbpp = bpp, i = 0; i < nblocks; ++i, ++lbpp) {
-               lbn = lbp[i];
-               if (error = lfs_bmap(ip, lbn, &daddr))
-                       panic("lfs_updatemeta: lfs_bmap");
-
-               /* Update in-core copy of old segment usage information. */
-               if (daddr != UNASSIGNED) {
-                       segup = fs->lfs_segtab + datosn(fs, daddr);
-                       segup->su_lastmod = time.tv_sec;
-#ifdef DIAGNOSTIC
-                       if (segup->su_nbytes < fs->lfs_bsize)
-                               panic("lfs: negative bytes (segment %d)\n",
-                                   segup - fs->lfs_segtab);
-#endif
-                       segup->su_nbytes -= fs->lfs_bsize;
-               }
-
-               /*
-                * Now change whomever points to lbn.  We could start with the
-                * smallest (most negative) block number in these if clauses,
-                * but we assume that indirect blocks are least common, and
-                * handle them separately.  The test for < 0 is correct and
-                * minimizes the path in the common case.
-                */
-#define        BREAD(bno) \
-       if (error = bread(ITOV(ip), (bno), fs->lfs_bsize, NOCRED, &bp)) \
-               panic("lfs_updatemeta: bread");
-
-               if (lbn < 0)
-                       if (lbn < -NIADDR) {
-#ifdef META
-                               printf("meta: update indirect block %d\n",
-                                   D_INDIR);
+       ++fs->lfs_nactive;
+       fs->lfs_nextseg = sntoda(fs, sn);
+#ifdef DOSTATS
+       ++lfs_stats.segsused;
 #endif
 #endif
-                               BREAD(D_INDIR);
-                               bp->b_un.b_daddr[-lbn % NINDIR(fs)] =
-                                   (*lbpp)->b_blkno;
-                               lfs_bwrite(bp);
-                       } else {
-                               ip->i_ib[-lbn-1] = (*lbpp)->b_blkno;
-               } else if (lbn < NDADDR) {
-                       ip->i_db[lbn] = (*lbpp)->b_blkno;
-               } else if ((lbn -= NDADDR) < NINDIR(fs)) {
-#ifdef META
-                       printf("meta: update indirect block %d\n", S_INDIR);
-#endif
-                       BREAD(S_INDIR);
-                       bp->b_un.b_daddr[lbn] = (*lbpp)->b_blkno;
-                       lfs_bwrite(bp);
-               } else if ((lbn =
-                   (lbn - NINDIR(fs)) / NINDIR(fs)) < NINDIR(fs)) {
-                       iblkno = -(lbn + NIADDR + 1);
-#ifdef META
-                       printf("meta: update indirect block %d\n", iblkno);
-#endif
-                       BREAD(iblkno);
-                       bp->b_un.b_daddr[lbn % NINDIR(fs)] = (*lbpp)->b_blkno;
-                       lfs_bwrite(bp);
-               } else
-                       panic("lfs_updatemeta: logical block number too large");
-       }
 }
 
 }
 
-static SEGMENT *
-lfs_writeckp(fs, sp)
-       LFS *fs;
-       SEGMENT *sp;
+int
+lfs_writeseg(fs, sp)
+       struct lfs *fs;
+       struct segment *sp;
 {
 {
-       BUF *bp;
-       FINFO *fip;
-       INODE *ip;
+       extern int locked_queue_count;
+       struct buf **bpp, *bp, *cbp;
        SEGUSE *sup;
        SEGUSE *sup;
-       void *xp;
-       daddr_t *lbp;
-       int bytes_needed, i;
-
-       /*
-        * This will write the dirty ifile blocks, but not the segusage
-        * table nor the ifile inode.
-        */
-       sp = lfs_writefile(fs, sp, fs->lfs_ivnode, 1);
+       SEGSUM *ssp;
+       dev_t i_dev;
+       size_t size;
+       u_long *datap, *dp;
+       int ch_per_blk, do_again, i, nblocks, num, s;
+       int (*strategy)__P((struct vop_strategy_args *));
+       struct vop_strategy_args vop_strategy_a;
+       u_short ninos;
+       char *p;
 
        /*
 
        /*
-        * If the segment usage table and the ifile inode won't fit in this
-        * segment, put them in the next one.
+        * 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.
         */
         */
-       bytes_needed = fs->lfs_segtabsz << fs->lfs_bshift;
-       if (sp->ninodes % INOPB(fs) == 0)
-               bytes_needed += fs->lfs_bsize;
+       if ((nblocks = sp->cbpp - sp->bpp) == 1)
+               return (0);
 
 
-       if (sp->seg_bytes_left < bytes_needed) {
-               lfs_writeseg(fs, sp);
-               sp = lfs_newseg(fs);
-               ++((SEGSUM *)(sp->segsum))->ss_nfinfo;
-       } else if (sp->sum_bytes_left < fs->lfs_segtabsz * sizeof(daddr_t)) {
-               sp = lfs_newsum(fs, sp);
-               ++((SEGSUM *)(sp->segsum))->ss_nfinfo;
-       }
+       ssp = (SEGSUM *)sp->segsum;
 
 
-#ifdef DEBUG
-       if (sp->seg_bytes_left < bytes_needed)
-               panic("lfs_writeckp: unable to write checkpoint");
-#endif
-       /*
-        * Update the segment usage information and the ifile inode
-        * and write it out.
-        */
-       sup = fs->lfs_segtab + sp->seg_number;
-       sup->su_nbytes =
-           (fs->lfs_segmask + 1) - sp->seg_bytes_left + bytes_needed;
+       /* Update the segment usage information. */
+       LFS_SEGENTRY(sup, fs, sp->seg_number, bp);
+       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_lastmod = time.tv_sec;
-       sup->su_flags = SEGUSE_DIRTY;
-
+       sup->su_ninos += ninos;
+       ++sup->su_nsums;
+       do_again = !(bp->b_flags & B_GATHERED);
+       (void)VOP_BWRITE(bp);
        /*
        /*
-        * Get buffers for the segusage table and write it out.  Don't
-        * bother updating the FINFO pointer, it's not used after this.
+        * Compute checksum across data and then across summary; the first
+        * block (the summary block) is skipped.  Set the create time here
+        * so that it's guaranteed to be later than the inode mod times.
+        *
+        * XXX
+        * Fix this to do it inline, instead of malloc/copy.
         */
         */
-       ip = VTOI(fs->lfs_ivnode);
-       fip = sp->fip;
-       lbp = &fip->fi_blocks[fip->fi_nblocks];
-       for (xp = fs->lfs_segtab, i = 0; i < fs->lfs_segtabsz;
-           xp += fs->lfs_bsize, ++i, ++lbp) {
-               *sp->cbpp++ = bp = lfs_newbuf(fs, sp->saddr, fs->lfs_bsize);
-               bp->b_flags |= B_CALL;
-               bcopy(xp, bp->b_un.b_addr, fs->lfs_bsize);
-               ip->i_db[i] = sp->saddr;
-               sp->saddr += (1 << fs->lfs_fsbtodb);
-               *lbp = i;
-               ++fip->fi_nblocks;
+       datap = dp = malloc(nblocks * sizeof(u_long), M_SEGMENT, M_WAITOK);
+       for (bpp = sp->bpp, i = nblocks - 1; i--;) {
+               if ((*++bpp)->b_flags & B_INVAL) {
+                       if (copyin((*bpp)->b_saveaddr, dp++, sizeof(u_long)))
+                               panic("lfs_writeseg: copyin failed");
+               } else
+                       *dp++ = ((u_long *)(*bpp)->b_data)[0];
        }
        }
-       return (lfs_writeinode(fs, sp, VTOI(fs->lfs_ivnode)));
-}
-
-/*
- * Write the dirty blocks associated with a vnode.
- */
-static SEGMENT *
-lfs_writefile(fs, sp, vp, do_ckp)
-       LFS *fs;
-       SEGMENT *sp;
-       VNODE *vp;
-       int do_ckp;
-{
-       FINFO *fip;
-       ino_t inum;
-
-       inum = VTOI(vp)->i_number;
-
-       if (vp->v_dirtyblkhd != NULL) {
-               if (sp->seg_bytes_left < fs->lfs_bsize) {
-                       lfs_writeseg(fs, sp);
-                       sp = lfs_newseg(fs);
-               } else if (sp->sum_bytes_left < sizeof(FINFO))
-                       sp = lfs_newsum(fs, sp);
-               sp->sum_bytes_left -= sizeof(FINFO) - sizeof(daddr_t);
-
-               fip = sp->fip;
-               fip->fi_nblocks = 0;
-               fip->fi_version =
-                   inum == LFS_IFILE_INUM ? 1 : lfs_getversion(fs, inum);
-               fip->fi_ino = inum;
-
-               sp = lfs_gather(fs, sp, vp, match_data);
-               if (do_ckp) {
-                       sp = lfs_gather(fs, sp, vp, match_indir);
-                       sp = lfs_gather(fs, sp, vp, match_dindir);
-               }
-
-               fip = sp->fip;
-
-#ifdef META
-               printf("lfs_writefile: adding %d blocks\n", fip->fi_nblocks);
+       ssp->ss_create = time.tv_sec;
+       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);
+#ifdef DIAGNOSTIC
+       if (fs->lfs_bfree < fsbtodb(fs, ninos) + LFS_SUMMARY_SIZE / DEV_BSIZE)
+               panic("lfs_writeseg: No diskspace for summary");
 #endif
 #endif
-               /*
-                * If this is the ifile, always update the file count as we'll
-                * be adding the segment usage information even if we didn't
-                * write any blocks.  Also, don't update the FINFO pointer for
-                * the ifile as the segment usage information hasn't yet been
-                * added.
-                */
-               if (inum == LFS_IFILE_INUM)
-                       ++((SEGSUM *)(sp->segsum))->ss_nfinfo;
-               else if (fip->fi_nblocks != 0) {
-                       ++((SEGSUM *)(sp->segsum))->ss_nfinfo;
-                       sp->fip = (FINFO *)((caddr_t)fip + sizeof(FINFO) +
-                           sizeof(daddr_t) * (fip->fi_nblocks - 1));
-               }
-       }
-
-       /* If this isn't the ifile, update the inode. */
-       if (inum != LFS_IFILE_INUM)
-               sp = lfs_writeinode(fs, sp, VTOI(vp));
-       return (sp);
-}
+       fs->lfs_bfree -= (fsbtodb(fs, ninos) + LFS_SUMMARY_SIZE / DEV_BSIZE);
 
 
-static SEGMENT *
-lfs_writeinode(fs, sp, ip)
-       LFS *fs;
-       SEGMENT *sp;
-       INODE *ip;
-{
-       BUF *bp;
-       daddr_t next_addr;
-       int nblocks;
+       i_dev = VTOI(fs->lfs_ivnode)->i_dev;
+       strategy = VTOI(fs->lfs_ivnode)->i_devvp->v_op[VOFFSET(vop_strategy)];
 
 
-       /* Allocate a new inode block if necessary. */
-       if (sp->ibp == NULL) {
-               /* Allocate a new segment if necessary. */
-               if (sp->seg_bytes_left < fs->lfs_bsize) {
-                       lfs_writeseg(fs, sp);
-                       sp = lfs_newseg(fs);
+       /*
+        * When we simply write the blocks we lose a rotation for every block
+        * written.  To avoid this problem, we allocate memory in chunks, copy
+        * the buffers into the chunk and write the chunk.  MAXPHYS is the
+        * largest size I/O devices can handle.
+        * When the data is copied to the chunk, turn off the the B_LOCKED bit
+        * and brelse the buffer (which will move them to the LRU list).  Add
+        * the B_CALL flag to the buffer header so we can count I/O's for the
+        * checkpoints and so we can release the allocated memory.
+        *
+        * XXX
+        * This should be removed if the new virtual memory system allows us to
+        * easily make the buffers contiguous in kernel memory and if that's
+        * fast enough.
+        */
+       ch_per_blk = MAXPHYS / fs->lfs_bsize;
+       for (bpp = sp->bpp, i = nblocks; i;) {
+               num = ch_per_blk;
+               if (num > i)
+                       num = i;
+               i -= num;
+               size = num * fs->lfs_bsize;
+
+               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;
+
+               s = splbio();
+               ++fs->lfs_iocount;
+               for (p = cbp->b_data; num--;) {
+                       bp = *bpp++;
+                       /*
+                        * 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_data, p, bp->b_bcount);
+                       p += bp->b_bcount;
+                       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_CALL) {
+                               /* if B_CALL, it was created with newbuf */
+                               brelvp(bp);
+                               if (!(bp->b_flags & B_INVAL))
+                                       free(bp->b_data, M_SEGMENT);
+                               free(bp, M_SEGMENT);
+                       } else {
+                               bremfree(bp);
+                               bp->b_flags |= B_DONE;
+                               reassignbuf(bp, bp->b_vp);
+                               brelse(bp);
+                       }
                }
                }
-
-               /* Get next inode block. */
-               next_addr = next(fs, sp, &nblocks);
-
+               ++cbp->b_vp->v_numoutput;
+               splx(s);
+               cbp->b_bcount = p - (char *)cbp->b_data;
                /*
                /*
-                * Get a new buffer and enter into the buffer list from
-                * the top of the list.
+                * 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).
                 */
                 */
-               sp->ibp = sp->bpp[fs->lfs_ssize - (nblocks + 1)] =
-                   lfs_newbuf(fs, next_addr, fs->lfs_bsize);
-               sp->ibp->b_flags |= B_CALL;
-
-               /* Set remaining space counter. */
-               sp->seg_bytes_left -= fs->lfs_bsize;
+               cbp->b_saveaddr = (caddr_t)fs;
+               vop_strategy_a.a_desc = VDESC(vop_strategy);
+               vop_strategy_a.a_bp = cbp;
+               (strategy)(&vop_strategy_a);
        }
        }
-
-       /* Copy the new inode onto the inode page. */
-       bp = sp->ibp;
-       bcopy(&ip->i_din,
-           bp->b_un.b_dino + (sp->ninodes % INOPB(fs)), sizeof(DINODE));
-
-       /* Increment inode count in segment summary block. */
-       ++((SEGSUM *)(sp->segsum))->ss_ninos;
-
-       /* If this page is full, set flag to allocate a new page. */
-       if (++sp->ninodes % INOPB(fs) == 0)
-               sp->ibp = NULL;
-
        /*
        /*
-        * If updating the ifile, update the super-block; otherwise, update
-        * the ifile itself.  In either case, turn of inode update flags.
+        * XXX
+        * Vinvalbuf can move locked buffers off the locked queue
+        * and we have no way of knowing about this.  So, after
+        * doing a big write, we recalculate how many bufers are
+        * really still left on the locked queue.
         */
         */
-       if (ip->i_number == LFS_IFILE_INUM)
-               fs->lfs_idaddr = bp->b_blkno;
-       else
-               lfs_iset(ip, bp->b_blkno, ip->i_atime);
-       ip->i_flags &= ~(IMOD | IACC | IUPD | ICHG);
-       return (sp);
+       locked_queue_count = count_lock_queue();
+       wakeup(&locked_queue_count);
+#ifdef DOSTATS
+       ++lfs_stats.psegwrites;
+       lfs_stats.blocktot += nblocks - 1;
+       if (fs->lfs_sp->seg_flags & SEGM_SYNC)
+               ++lfs_stats.psyncwrites;
+       if (fs->lfs_sp->seg_flags & SEGM_CLEAN) {
+               ++lfs_stats.pcleanwrites;
+               lfs_stats.cleanblocks += nblocks - 1;
+       }
+#endif
+       return (lfs_initseg(fs) || do_again);
 }
 
 }
 
-static void
-lfs_writeseg(fs, sp)
-       LFS *fs;
-       SEGMENT *sp;
+void
+lfs_writesuper(fs)
+       struct lfs *fs;
 {
 {
-       BUF **bpp;
-       SEGUSE *sup;
-       int i, nblocks, s, (*strategy) __P((BUF *));
-       void *pmeta;
-
-       /* Update superblock segment address. */
-       fs->lfs_lastseg = sntoda(fs, sp->seg_number);
-
-       /* Finish up any summary block. */
-       lfs_endsum(fs, sp, 0);
-
-       /*
-        * Copy inode and summary block buffer pointers down so they are
-        * contiguous with the page buffer pointers.
-        */
-       (void)next(fs, sp, &nblocks);
-       pmeta = (sp->bpp + fs->lfs_ssize) - nblocks;
-       if (pmeta != sp->cbpp)
-               bcopy(pmeta, sp->cbpp, sizeof(BUF *) * nblocks);
-       sp->cbpp += nblocks;
-       nblocks = sp->cbpp - sp->bpp;
-
-       sup = fs->lfs_segtab + sp->seg_number;
-       sup->su_nbytes = nblocks << fs->lfs_bshift;
-       sup->su_lastmod = time.tv_sec;
-       sup->su_flags = SEGUSE_DIRTY;
-
-       /*
-        * Since we need to guarantee that the summary block gets written last,
-        * we issue the writes in two sets.  The first n-1 buffers first, and
-        * then, after they've completed, the summary buffer.  Only when that
-        * final write completes is the segment valid.
-        */
-       --nblocks;                      /* Don't count last summary block. */
+       struct buf *bp;
+       dev_t i_dev;
+       int (*strategy) __P((struct vop_strategy_args *));
+       int s;
+       struct vop_strategy_args vop_strategy_a;
 
 
-       sp->nextp = fs->lfs_seglist;
-       fs->lfs_seglist = sp;
+       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(VTOI(fs->lfs_ivnode)->i_devvp, fs->lfs_sboffs[0],
+           LFS_SBPAD);
+       *(struct lfs *)bp->b_data = *fs;
+
+       /* XXX Toggle between first two superblocks; for now just write first */
+       bp->b_dev = i_dev;
+       bp->b_flags |= B_BUSY | B_CALL | B_ASYNC;
+       bp->b_flags &= ~(B_DONE | B_ERROR | B_READ | B_DELWRI);
+       bp->b_iodone = lfs_supercallback;
+       vop_strategy_a.a_desc = VDESC(vop_strategy);
+       vop_strategy_a.a_bp = bp;
        s = splbio();
        s = splbio();
-       fs->lfs_iocount += nblocks;
+       ++bp->b_vp->v_numoutput;
        splx(s);
        splx(s);
-
-       strategy =
-           VFSTOUFS(fs->lfs_ivnode->v_mount)->um_devvp->v_op->vop_strategy;
-       for (bpp = sp->bpp, i = 0; i < nblocks; ++i, ++bpp)
-               (strategy)(*bpp);
+       (strategy)(&vop_strategy_a);
 }
 
 }
 
-static void
-lfs_writesum(fs)
-       LFS *fs;
+/*
+ * Logical block number match routines used when traversing the dirty block
+ * chain.
+ */
+int
+lfs_match_data(fs, bp)
+       struct lfs *fs;
+       struct buf *bp;
 {
 {
-       BUF *bp;
-       SEGMENT *next_sp, *sp;
-       int (*strategy) __P((BUF *));
-
-       strategy =
-           VFSTOUFS(fs->lfs_ivnode->v_mount)->um_devvp->v_op->vop_strategy;
-       for (sp = fs->lfs_seglist; sp; sp = next_sp) {
-               bp = *(sp->cbpp - 1);
-               (strategy)(bp);
-               biowait(bp);
-               bp->b_vp = NULL;                /* No associated vnode. */
-               brelse(bp);
-
-               next_sp = sp->nextp;
-               free(sp->bpp, M_SEGMENT);
-               free(sp, M_SEGMENT);
-       }
-       /* Segment list is done. */
-       fs->lfs_seglist = NULL;
+       return (bp->b_lblkno >= 0);
 }
 
 }
 
-static void
-lfs_writesuper(fs)
-       LFS *fs;
+int
+lfs_match_indir(fs, bp)
+       struct lfs *fs;
+       struct buf *bp;
 {
 {
-       BUF *bp;
-       int (*strategy) __P((BUF *));
-
-       strategy =
-           VFSTOUFS(fs->lfs_ivnode->v_mount)->um_devvp->v_op->vop_strategy;
+       int lbn;
 
 
-       /* Checksum the superblock and copy it into a buffer. */
-       fs->lfs_cksum = cksum(fs, sizeof(LFS) - sizeof(fs->lfs_cksum));
-       bp = lfs_newbuf(fs, fs->lfs_sboffs[0], LFS_SBPAD);
-       bcopy(fs, bp->b_un.b_lfs, sizeof(LFS));
-
-       /* Write the first superblock (wait). */
-       (strategy)(bp);
-       biowait(bp);
-
-       /* Write the second superblock (don't wait). */
-       bp->b_flags &= ~B_DONE;
-       bp->b_flags |= B_ASYNC;
-       bp->b_vp = NULL;                        /* No associated vnode. */
-       bp->b_blkno = bp->b_lblkno = fs->lfs_sboffs[1];
-       (strategy)(bp);
+       lbn = bp->b_lblkno;
+       return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 0);
 }
 
 }
 
-/*
- * Logical block number match routines used when traversing the dirty block
- * chain.
- */
-static int
-match_data(bp)
-       BUF *bp;
+int
+lfs_match_dindir(fs, bp)
+       struct lfs *fs;
+       struct buf *bp;
 {
 {
-       return (bp->b_lblkno >= 0);
+       int lbn;
+
+       lbn = bp->b_lblkno;
+       return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 1);
 }
 
 }
 
-static int
-match_dindir(bp)
-       BUF *bp;
+int
+lfs_match_tindir(fs, bp)
+       struct lfs *fs;
+       struct buf *bp;
 {
 {
-       return (bp->b_lblkno == D_INDIR);
+       int lbn;
+
+       lbn = bp->b_lblkno;
+       return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 2);
 }
 
 /*
 }
 
 /*
- * These are single indirect blocks.  There are three types:
- *
- * the one in the inode (lblkno == S_INDIR, or -1).
- * the ones that hang off of the double indirect in the inode (D_INDIR);
- *    these all have addresses in the range -2NINDIR to -(3NINDIR-1).
- * the ones that hang off of the double indirect that hangs off of the
- *    triple indirect.  These all have addresses < -(NINDIR^2).
- *
- * Since we currently don't support triple indirect blocks, this gets
- * simpler, and we just look for block numbers less than -NIADDR.
+ * Allocate a new buffer header.
  */
  */
-static int
-match_indir(bp)
-       BUF *bp;
+struct buf *
+lfs_newbuf(vp, daddr, size)
+       struct vnode *vp;
+       daddr_t daddr;
+       size_t size;
 {
 {
-       return (bp->b_lblkno == S_INDIR || bp->b_lblkno < -NIADDR);
+       struct buf *bp;
+       size_t nbytes;
+
+       nbytes = roundup(size, DEV_BSIZE);
+       bp = malloc(sizeof(struct buf), M_SEGMENT, M_WAITOK);
+       bzero(bp, sizeof(struct buf));
+       if (nbytes)
+               bp->b_data = malloc(nbytes, M_SEGMENT, M_WAITOK);
+       bgetvp(vp, bp);
+       bp->b_bufsize = size;
+       bp->b_bcount = size;
+       bp->b_lblkno = daddr;
+       bp->b_blkno = daddr;
+       bp->b_error = 0;
+       bp->b_resid = 0;
+       bp->b_iodone = lfs_callback;
+       bp->b_flags |= B_BUSY | B_CALL | B_NOCACHE;
+       return (bp);
 }
 
 }
 
-/* Get the next inode/summary block. */
-static daddr_t
-next(fs, sp, nbp)
-       LFS *fs;
-       SEGMENT *sp;
-       int *nbp;
+void
+lfs_callback(bp)
+       struct buf *bp;
 {
 {
-       int nblocks, nino_blocks, nseg_blocks, sums_per_block;
-
-       /* Fs blocks allocated to summary blocks. */
-       sums_per_block = fs->lfs_bsize / LFS_SUMMARY_SIZE;
-       nseg_blocks = (sp->nsums + sums_per_block - 1) / sums_per_block;
-
-       /* Fs blocks allocated to inodes. */
-       nino_blocks = (sp->ninodes + INOPB(fs) - 1) / INOPB(fs);
+       struct lfs *fs;
 
 
-       /* Total number of fs blocks allocated. */
-       nblocks = nseg_blocks + nino_blocks;
+       fs = (struct lfs *)bp->b_saveaddr;
+#ifdef DIAGNOSTIC
+       if (fs->lfs_iocount == 0)
+               panic("lfs_callback: zero iocount\n");
+#endif
+       if (--fs->lfs_iocount == 0)
+               wakeup(&fs->lfs_iocount);
 
 
-       if (nbp)
-               *nbp = nblocks;
+       brelvp(bp);
+       free(bp->b_data, M_SEGMENT);
+       free(bp, M_SEGMENT);
+}
 
 
-       /*
-        * The disk address of the new inode/summary block is the address of
-        * the start of the segment after this one minus the number of blocks
-        * that we've already used.
-        */
-       return (sntoda(fs, sp->seg_number + 1) - fsbtodb(fs, nblocks + 1));
+void
+lfs_supercallback(bp)
+       struct buf *bp;
+{
+       brelvp(bp);
+       free(bp->b_data, M_SEGMENT);
+       free(bp, M_SEGMENT);
 }
 
 /*
 }
 
 /*
@@ -925,15 +1056,15 @@ next(fs, sp, nbp)
  * of logical block numbers to a unsigned in this routine so that the
  * negative block numbers (meta data blocks) sort AFTER the data blocks.
  */
  * of logical block numbers to a unsigned in this routine so that the
  * negative block numbers (meta data blocks) sort AFTER the data blocks.
  */
-static void
-shellsort(bp_array, lb_array, nmemb)
-       BUF **bp_array;
+void
+lfs_shellsort(bp_array, lb_array, nmemb)
+       struct buf **bp_array;
        daddr_t *lb_array;
        register int nmemb;
 {
        static int __rsshell_increments[] = { 4, 1, 0 };
        register int incr, *incrp, t1, t2;
        daddr_t *lb_array;
        register int nmemb;
 {
        static int __rsshell_increments[] = { 4, 1, 0 };
        register int incr, *incrp, t1, t2;
-       BUF *bp_temp;
+       struct buf *bp_temp;
        u_long lb_temp;
 
        for (incrp = __rsshell_increments; incr = *incrp++;)
        u_long lb_temp;
 
        for (incrp = __rsshell_increments; incr = *incrp++;)
@@ -950,3 +1081,31 @@ shellsort(bp_array, lb_array, nmemb)
                                } else
                                        break;
 }
                                } else
                                        break;
 }
+
+/*
+ * Check VXLOCK.  Return 1 if the vnode is locked.  Otherwise, vget it.
+ */
+lfs_vref(vp)
+       register struct vnode *vp;
+{
+
+       if (vp->v_flag & VXLOCK)
+               return(1);
+       return (vget(vp, 0));
+}
+
+void
+lfs_vunref(vp)
+       register struct vnode *vp;
+{
+       extern int lfs_no_inactive;
+
+       /*
+        * This is vrele except that we do not want to VOP_INACTIVE
+        * this vnode. Rather than inline vrele here, we use a global
+        * flag to tell lfs_inactive not to run. Yes, its gross.
+        */
+       lfs_no_inactive = 1;
+       vrele(vp);
+       lfs_no_inactive = 0;
+}