]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/vfs/vfs_bio.c
xnu-344.49.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_bio.c
index 3e9b0a09badfe201681aefdc7aa49df9039afe53..4ca0b0e67fa79e1e04caf755822b05abf8740246 100644 (file)
@@ -1,21 +1,24 @@
 /*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
  * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
  * 
  * @APPLE_LICENSE_HEADER_END@
  */
@@ -70,7 +73,6 @@
  *     Leffler, et al.: The Design and Implementation of the 4.3BSD
  *             UNIX Operating System (Addison Welley, 1989)
  */
-#define ZALLOC_METADATA 1
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <kern/zalloc.h>
 
 #include <sys/kdebug.h>
+#include <machine/spl.h>
 
-extern void bufqinc(int q);
-extern void bufqdec(int q);
-extern void bufq_balance_thread_init();
+static __inline__ void bufqinc(int q);
+static __inline__ void bufqdec(int q);
 
-extern void reassignbuf(struct buf *, struct vnode *);
 static struct buf *getnewbuf(int slpflag, int slptimeo, int *queue);
+static int bcleanbuf(struct buf *bp);
+extern void vwakeup();
 
-extern int niobuf;             /* The number of IO buffer headers for cluster IO */
+extern int niobuf;     /* The number of IO buffer headers for cluster IO */
 int blaundrycnt;
 
+/* zone allocated buffer headers */
+static zone_t buf_hdr_zone;
+static int buf_hdr_count;
+
 #if TRACE
 struct proc *traceproc;
 int    tracewhich, tracebuf[TRCSIZ];
@@ -120,6 +127,9 @@ u_long      bufhash;
 /* Definitions for the buffer stats. */
 struct bufstats bufstats;
 
+/* Number of delayed write buffers */
+int nbdwrite = 0;
+
 /*
  * Insq/Remq for the buffer hash lists.
  */
@@ -131,8 +141,8 @@ struct bufstats bufstats;
 
 TAILQ_HEAD(ioqueue, buf) iobufqueue;
 TAILQ_HEAD(bqueues, buf) bufqueues[BQUEUES];
-int needbuffer;
-int need_iobuffer;
+static int needbuffer;
+static int need_iobuffer;
 
 /*
  * Insq/Remq for the buffer free lists.
@@ -159,8 +169,21 @@ int need_iobuffer;
        (bp)->b_hash.le_next = (struct buf *)0; \
        (bp)->b_hash.le_prev = (struct buf **)0xdeadbeef;
 
+/*
+ * Insq/Remq for the vnode usage lists.
+ */
+#define        bufinsvn(bp, dp)        LIST_INSERT_HEAD(dp, bp, b_vnbufs)
+#define        bufremvn(bp) {                                                  \
+       LIST_REMOVE(bp, b_vnbufs);                                      \
+       (bp)->b_vnbufs.le_next = NOLIST;                                \
+}
+
 simple_lock_data_t bufhashlist_slock;          /* lock on buffer hash list */
 
+/* number of per vnode, "in flight" buffer writes */
+#define        BUFWRITE_THROTTLE       9
+
+
 /*
  * Time in seconds before a buffer on a list is 
  * considered as a stale buffer 
@@ -173,8 +196,8 @@ int lru_is_stale = LRU_IS_STALE;
 int age_is_stale = AGE_IS_STALE;
 int meta_is_stale = META_IS_STALE;
 
-#if 1
-void
+/* LIST_INSERT_HEAD() with assertions */
+static __inline__ void
 blistenterhead(struct bufhashhdr * head, struct buf * bp)
 {
        if ((bp->b_hash.le_next = (head)->lh_first) != NULL)
@@ -183,45 +206,35 @@ blistenterhead(struct bufhashhdr * head, struct buf * bp)
        bp->b_hash.le_prev = &(head)->lh_first;
        if (bp->b_hash.le_prev == (struct buf **)0xdeadbeef) 
                panic("blistenterhead: le_prev is deadbeef");
-
 }
-#endif
 
-#if 1
-void 
+static __inline__ void 
 binshash(struct buf *bp, struct bufhashhdr *dp)
 {
-int s;
-
-struct buf *nbp;
+       struct buf *nbp;
 
        simple_lock(&bufhashlist_slock);
-#if 0  
-       if(incore(bp->b_vp, bp->b_lblkno)) {
-               panic("adding to queue already existing element");
-       }
+
+#if 0
+       if((bad = incore(bp->b_vp, bp->b_lblkno)))
+               panic("binshash: already incore bp 0x%x, bad 0x%x\n", bp, bad);
 #endif /* 0 */
+
        BHASHENTCHECK(bp);
-       
+
        nbp = dp->lh_first;
        for(; nbp != NULL; nbp = nbp->b_hash.le_next) {
                if(nbp == bp) 
                        panic("buf already in hashlist");
        }
 
-#if 0
-       LIST_INSERT_HEAD(dp, bp, b_hash);
-#else
        blistenterhead(dp, bp);
-#endif
        simple_unlock(&bufhashlist_slock);
 }
 
-void 
+static __inline__ void 
 bremhash(struct buf *bp) 
 {
-       int s;
-
        simple_lock(&bufhashlist_slock);
        if (bp->b_hash.le_prev == (struct buf **)0xdeadbeef) 
                panic("bremhash le_prev is deadbeef");
@@ -234,9 +247,6 @@ bremhash(struct buf *bp)
        simple_unlock(&bufhashlist_slock);
 }
 
-#endif /* 1 */
-
-
 /*
  * Remove a buffer from the free list it's on
  */
@@ -268,6 +278,82 @@ bremfree(bp)
        bp->b_timestamp = 0; 
 }
 
+/*
+ * Associate a buffer with a vnode.
+ */
+static void
+bgetvp(vp, bp)
+       register struct vnode *vp;
+       register struct buf *bp;
+{
+
+       if (bp->b_vp != vp)
+               panic("bgetvp: not free");
+       VHOLD(vp);
+       bp->b_vp = vp;
+       if (vp->v_type == VBLK || vp->v_type == VCHR)
+               bp->b_dev = vp->v_rdev;
+       else
+               bp->b_dev = NODEV;
+       /*
+        * Insert onto list for new vnode.
+        */
+       bufinsvn(bp, &vp->v_cleanblkhd);
+}
+
+/*
+ * Disassociate a buffer from a vnode.
+ */
+static void
+brelvp(bp)
+       register struct buf *bp;
+{
+       struct vnode *vp;
+
+       if (bp->b_vp == (struct vnode *) 0)
+               panic("brelvp: NULL vp");
+       /*
+        * Delete from old vnode list, if on one.
+        */
+       if (bp->b_vnbufs.le_next != NOLIST)
+               bufremvn(bp);
+       vp = bp->b_vp;
+       bp->b_vp = (struct vnode *) 0;
+       HOLDRELE(vp);
+}
+
+/*
+ * Reassign a buffer from one vnode to another.
+ * Used to assign file specific control information
+ * (indirect blocks) to the vnode to which they belong.
+ */
+void
+reassignbuf(bp, newvp)
+       register struct buf *bp;
+       register struct vnode *newvp;
+{
+       register struct buflists *listheadp;
+
+       if (newvp == NULL) {
+               printf("reassignbuf: NULL");
+               return;
+       }
+       /*
+        * Delete from old vnode list, if on one.
+        */
+       if (bp->b_vnbufs.le_next != NOLIST)
+               bufremvn(bp);
+       /*
+        * If dirty, put on list of dirty buffers;
+        * otherwise insert onto list of clean buffers.
+        */
+       if (ISSET(bp->b_flags, B_DELWRI))
+               listheadp = &newvp->v_dirtyblkhd;
+       else
+               listheadp = &newvp->v_cleanblkhd;
+       bufinsvn(bp, listheadp);
+}
+
 static __inline__ void
 bufhdrinit(struct buf *bp)
 {
@@ -284,7 +370,7 @@ bufhdrinit(struct buf *bp)
 /*
  * Initialize buffers and hash links for buffers.
  */
-void
+__private_extern__ void
 bufinit()
 {
        register struct buf *bp;
@@ -340,13 +426,15 @@ bufinit()
        bcleanbuf_thread_init();
 
 #if 0  /* notyet */
+       {
+       static void bufq_balance_thread_init();
        /* create a thread to do dynamic buffer queue balancing */
        bufq_balance_thread_init();
-#endif /* XXX */
+       }
+#endif /* notyet */
 }
 
-/* __inline  */
-struct buf *
+static struct buf *
 bio_doread(vp, blkno, size, cred, async, queuetype)
        struct vnode *vp;
        daddr_t blkno;
@@ -375,6 +463,7 @@ bio_doread(vp, blkno, size, cred, async, queuetype)
                         */
                        bp->b_rcred = crdup(cred);
                }
+
                VOP_STRATEGY(bp);
 
                trace(TR_BREADMISS, pack(vp, size), blkno);
@@ -492,16 +581,16 @@ bwrite(bp)
 {
        int rv, sync, wasdelayed;
        struct proc     *p = current_proc();
-       upl_t  upl;
-       upl_page_info_t *pl;
-       void * object;
-       kern_return_t kret;
        struct vnode *vp = bp->b_vp;
 
        /* Remember buffer type, to switch on it later. */
        sync = !ISSET(bp->b_flags, B_ASYNC);
        wasdelayed = ISSET(bp->b_flags, B_DELWRI);
        CLR(bp->b_flags, (B_READ | B_DONE | B_ERROR | B_DELWRI));
+       if (wasdelayed) {
+               nbdwrite--;
+               wakeup((caddr_t)&nbdwrite);
+       }
 
        if (!sync) {
                /*
@@ -517,7 +606,7 @@ bwrite(bp)
                        p->p_stats->p_ru.ru_oublock++;          /* XXX */
        }
 
-       trace(TR_BWRITE, pack(vp, bp->b_bcount), bp->b_lblkno);
+       trace(TR_BUFWRITE, pack(vp, bp->b_bcount), bp->b_lblkno);
 
        /* Initiate disk write.  Make sure the appropriate party is charged. */
        SET(bp->b_flags, B_WRITEINPROG);
@@ -543,7 +632,12 @@ bwrite(bp)
                        p->p_stats->p_ru.ru_oublock++;          /* XXX */
 
                /* Release the buffer. */
-               brelse(bp);
+               // XXXdbg - only if the unused bit is set
+               if (!ISSET(bp->b_flags, B_NORELSE)) {
+                   brelse(bp);
+               } else {
+                   CLR(bp->b_flags, B_NORELSE);
+               }
 
                return (rv);
        } else {
@@ -570,15 +664,20 @@ vn_bwrite(ap)
  * written in the order that the writes are requested.
  *
  * Described in Leffler, et al. (pp. 208-213).
+ *
+ * Note: With the abilitty to allocate additional buffer
+ * headers, we can get in to the situation where "too" many 
+ * bdwrite()s can create situation where the kernel can create
+ * buffers faster than the disks can service. Doing a bawrite() in
+ * cases were we have "too many" outstanding bdwrite()s avoids that.
  */
-void
-bdwrite(bp)
+__private_extern__ int
+bdwrite_internal(bp, return_error)
        struct buf *bp;
+       int return_error;
 {
        struct proc *p = current_proc();
-       kern_return_t kret;
-       upl_t upl;
-       upl_page_info_t *pl;
+       struct vnode *vp = bp->b_vp;
 
        /*
         * If the block hasn't been seen before:
@@ -590,33 +689,117 @@ bdwrite(bp)
                SET(bp->b_flags, B_DELWRI);
                if (p && p->p_stats) 
                        p->p_stats->p_ru.ru_oublock++;          /* XXX */
-
-               reassignbuf(bp, bp->b_vp);
+               nbdwrite ++;
+               reassignbuf(bp, vp);
        }
 
-
        /* If this is a tape block, write it the block now. */
        if (ISSET(bp->b_flags, B_TAPE)) {
                /* bwrite(bp); */
-        VOP_BWRITE(bp);
-               return;
+               VOP_BWRITE(bp);
+               return (0);
+       }
+
+       /*
+        * If the vnode has "too many" write operations in progress
+        * wait for them to finish the IO
+        */
+       while (vp->v_numoutput >= BUFWRITE_THROTTLE) {
+               vp->v_flag |= VTHROTTLED;
+               (void)tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "bdwrite", 0);
        }
 
+       /*
+        * If we have too many delayed write buffers, 
+        * more than we can "safely" handle, just fall back to
+        * doing the async write
+        */
+       if (nbdwrite < 0)
+               panic("bdwrite: Negative nbdwrite");
+
+       // can't do a bawrite() if the LOCKED bit is set because the
+       // buffer is part of a transaction and can't go to disk until
+       // the LOCKED bit is cleared.
+       if (!ISSET(bp->b_flags, B_LOCKED) && nbdwrite > ((nbuf/4)*3)) {
+               if (return_error)
+                       return (EAGAIN);
+               else
+                       bawrite(bp);
+               return (0);
+       }
+        
        /* Otherwise, the "write" is done, so mark and release the buffer. */
        SET(bp->b_flags, B_DONE);
        brelse(bp);
+       return (0);
+}
+
+void
+bdwrite(bp)
+       struct buf *bp;
+{
+       (void) bdwrite_internal(bp, 0);
 }
 
 /*
  * Asynchronous block write; just an asynchronous bwrite().
+ *
+ * Note: With the abilitty to allocate additional buffer
+ * headers, we can get in to the situation where "too" many 
+ * bawrite()s can create situation where the kernel can create
+ * buffers faster than the disks can service.
+ * We limit the number of "in flight" writes a vnode can have to
+ * avoid this.
  */
-void
-bawrite(bp)
+static int
+bawrite_internal(bp, throttle)
        struct buf *bp;
+       int throttle;
 {
+       struct vnode *vp = bp->b_vp;
+
+       if (vp) {
+               /*
+                * If the vnode has "too many" write operations in progress
+                * wait for them to finish the IO
+                */
+               while (vp->v_numoutput >= BUFWRITE_THROTTLE) {
+                       if (throttle) {
+                               vp->v_flag |= VTHROTTLED;
+                               (void)tsleep((caddr_t)&vp->v_numoutput,
+                                               PRIBIO + 1, "bawrite", 0);
+                       } else
+                               return (EWOULDBLOCK);
+               }
+       }
 
        SET(bp->b_flags, B_ASYNC);
        VOP_BWRITE(bp);
+       return (0);
+}
+
+void
+bawrite(bp)
+       struct buf *bp;
+{
+       (void) bawrite_internal(bp, 1);
+}
+
+/* 
+ *     bwillwrite:
+ * 
+ *     Called prior to the locking of any vnodes when we are expecting to
+ *     write.  We do not want to starve the buffer cache with too many
+ *     dirty buffers so we block here.  By blocking prior to the locking
+ *     of any vnodes we attempt to avoid the situation where a locked vnode
+ *     prevents the various system daemons from flushing related buffers.
+ */ 
+void 
+bwillwrite(void) 
+{
+       /* XXX To be implemented later */
 }
 
 /*
@@ -632,10 +815,32 @@ brelse(bp)
        long whichq;
 
        KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 388)) | DBG_FUNC_START,
-                    bp->b_lblkno * PAGE_SIZE, bp, bp->b_data, bp->b_flags, 0);
+                    bp->b_lblkno * PAGE_SIZE, (int)bp, (int)bp->b_data,
+                    bp->b_flags, 0);
 
        trace(TR_BRELSE, pack(bp->b_vp, bp->b_bufsize), bp->b_lblkno);
 
+       // if we're invalidating a buffer that has the B_CALL bit
+       // set then call the b_iodone function so it gets cleaned
+       // up properly.
+       //
+       if (ISSET(bp->b_flags, B_META) && ISSET(bp->b_flags, B_INVAL)) {
+               if (ISSET(bp->b_flags, B_CALL) && !ISSET(bp->b_flags, B_DELWRI)) {
+                       panic("brelse: CALL flag set but not DELWRI! bp 0x%x\n", bp);
+               }
+               if (ISSET(bp->b_flags, B_CALL)) {       /* if necessary, call out */
+                       void    (*iodone_func)(struct buf *) = bp->b_iodone;
+
+                       CLR(bp->b_flags, B_CALL);       /* but note callout done */
+                       bp->b_iodone = NULL;
+
+                       if (iodone_func == NULL) {
+                               panic("brelse: bp @ 0x%x has NULL b_iodone!\n", bp);
+                       }
+                       (*iodone_func)(bp);
+               }
+       }
+       
        /* IO is done. Cleanup the UPL state */
        if (!ISSET(bp->b_flags, B_META)
                && UBCINFOEXISTS(bp->b_vp) && bp->b_bufsize) {
@@ -674,7 +879,9 @@ brelse(bp)
                                        upl_flags = 0;
                                ubc_upl_abort(upl, upl_flags);
                        } else {
-                           if (ISSET(bp->b_flags, (B_DELWRI | B_WASDIRTY)))
+                           if (ISSET(bp->b_flags, B_NEEDCOMMIT))
+                                   upl_flags = UPL_COMMIT_CLEAR_DIRTY ;
+                           else if (ISSET(bp->b_flags, B_DELWRI | B_WASDIRTY))
                                        upl_flags = UPL_COMMIT_SET_DIRTY ;
                                else
                                    upl_flags = UPL_COMMIT_CLEAR_DIRTY ;
@@ -725,9 +932,15 @@ brelse(bp)
                 */
                if (bp->b_vp)
                        brelvp(bp);
-               CLR(bp->b_flags, B_DELWRI);
+               if (ISSET(bp->b_flags, B_DELWRI)) {
+                       CLR(bp->b_flags, B_DELWRI);
+                       nbdwrite--;
+                       wakeup((caddr_t)&nbdwrite);
+               }
                if (bp->b_bufsize <= 0)
                        whichq = BQ_EMPTY;      /* no data */
+               else if (ISSET(bp->b_flags, B_META))
+                       whichq = BQ_META;               /* meta-data */
                else
                        whichq = BQ_AGE;        /* invalid data */
 
@@ -758,7 +971,7 @@ brelse(bp)
        splx(s);
 
        KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 388)) | DBG_FUNC_END,
-                    bp, bp->b_data, bp->b_flags, 0, 0);
+                    (int)bp, (int)bp->b_data, bp->b_flags, 0, 0);
 }
 
 /*
@@ -774,24 +987,21 @@ incore(vp, blkno)
        daddr_t blkno;
 {
        struct buf *bp;
-       int bufseen = 0;
 
        bp = BUFHASH(vp, blkno)->lh_first;
 
        /* Search hash chain */
-       for (; bp != NULL; bp = bp->b_hash.le_next, bufseen++) {
+       for (; bp != NULL; bp = bp->b_hash.le_next) {
                if (bp->b_lblkno == blkno && bp->b_vp == vp &&
                    !ISSET(bp->b_flags, B_INVAL))
                        return (bp);
-       if(bufseen >= nbuf) 
-               panic("walked more than nbuf in incore");
-               
        }
 
        return (0);
 }
 
-/* XXX FIXME -- Update the comment to reflect the UBC changes -- */
+
+/* XXX FIXME -- Update the comment to reflect the UBC changes (please) -- */
 /*
  * Get a block of requested size that is associated with
  * a given vnode and block offset. If it is found in the
@@ -819,7 +1029,7 @@ getblk(vp, blkno, size, slpflag, slptimeo, operation)
 start:
 
        s = splbio();
-       if (bp = incore(vp, blkno)) {
+       if ((bp = incore(vp, blkno))) {
                /* Found in the Buffer Cache */
                if (ISSET(bp->b_flags, B_BUSY)) {
                        /* but is busy */
@@ -889,8 +1099,11 @@ start:
                                        SET(bp->b_flags, B_PAGELIST);
                                        bp->b_pagelist = upl;
 
-                                       if ( !upl_valid_page(pl, 0))
-                                               panic("getblk: incore buffer without valid page");
+                                       if (!upl_valid_page(pl, 0)) {
+                                               if (vp->v_tag != VT_NFS)
+                                                       panic("getblk: incore buffer without valid page");
+                                               CLR(bp->b_flags, B_CACHE);
+                                       }
 
                                        if (upl_dirty_page(pl, 0))
                                                SET(bp->b_flags, B_WASDIRTY);
@@ -898,11 +1111,11 @@ start:
                                                CLR(bp->b_flags, B_WASDIRTY);
 
                                        kret = ubc_upl_map(upl, (vm_address_t *)&(bp->b_data));
-                                       if (kret != KERN_SUCCESS) {
+                                       if (kret != KERN_SUCCESS)
                                                panic("getblk: ubc_upl_map() failed with (%d)",
                                                                  kret);
-                                       }
-                                       if (bp->b_data == 0) panic("ubc_upl_map mapped 0");
+                                       if (bp->b_data == 0)
+                                               panic("ubc_upl_map mapped 0");
                                }
                                break;
 
@@ -911,7 +1124,7 @@ start:
                                 * VM is not involved in IO for the meta data
                                 * buffer already has valid data 
                                 */
-                       if(bp->b_data == 0)
+                               if(bp->b_data == 0)
                                        panic("bp->b_data null incore buf=%x", bp);
                                break;
 
@@ -942,6 +1155,10 @@ start:
                        brelse(bp);
                        goto start;
                }
+               /*
+                * NOTE: YOU CAN NOT BLOCK UNTIL binshash() HAS BEEN
+                *       CALLED!  BE CAREFUL.
+                */
 
                /*
                 * if it is meta, the queue may be set to other 
@@ -953,36 +1170,29 @@ start:
                        SET(bp->b_flags, B_META);
                        queue = BQ_META;
                }
+
+               bp->b_blkno = bp->b_lblkno = blkno;
+               bp->b_vp = vp;
+
                /*
                 * Insert in the hash so that incore() can find it 
                 */
                binshash(bp, BUFHASH(vp, blkno)); 
 
+               s = splbio();
+               bgetvp(vp, bp);
+               splx(s);
+
                allocbuf(bp, size);
 
                switch (operation) {
                case BLK_META:
                        /* buffer data is invalid */
 
-#if !ZALLOC_METADATA
-                       if (bp->b_data)
-                               panic("bp->b_data is not nul; %x",bp);
-                       kret = kmem_alloc(kernel_map,
-                                               &bp->b_data, bp->b_bufsize);
-                       if (kret != KERN_SUCCESS)
-                               panic("getblk: kmem_alloc() returned %d", kret);
-#endif /* ZALLOC_METADATA */
-
                        if(bp->b_data == 0)
                                panic("bp->b_data is null %x",bp);
 
-                       bp->b_blkno = bp->b_lblkno = blkno;
-                       s = splbio();
-                       bgetvp(vp, bp);
                        bufstats.bufs_miss++;
-                       splx(s);
-                       if (bp->b_data == 0)
-                               panic("b_data is 0: 2");
 
                        /* wakeup the buffer */ 
                        CLR(bp->b_flags, B_WANTED);
@@ -1007,7 +1217,6 @@ start:
 #ifdef  UBC_DEBUG
                        upl_ubc_alias_set(upl, bp, 4);
 #endif /* UBC_DEBUG */
-                       bp->b_blkno = bp->b_lblkno = blkno;
                        bp->b_pagelist = upl;
 
                        SET(bp->b_flags, B_PAGELIST);
@@ -1061,8 +1270,9 @@ start:
                                                bp->b_dirtyend = 0;
                                        }
                                }
-                               if (error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL)) {
-                                       panic("VOP_BMAP failed in getblk");
+                               error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL);
+                               if(error) {
+                                       panic("getblk: VOP_BMAP failed");
                                        /*NOTREACHED*/
                                        /*
                                         * XXX:  We probably should invalidate the VM Page
@@ -1082,11 +1292,8 @@ start:
                                panic("getblk: ubc_upl_map() "
                                      "failed with (%d)", kret);
                        }
-                       if (bp->b_data == 0) panic("kernel_upl_map mapped 0");
-
-                       s = splbio();
-                       bgetvp(vp, bp);
-                       splx(s);
+                       if (bp->b_data == 0)
+                               panic("kernel_upl_map mapped 0");
 
                        break;
 
@@ -1105,14 +1312,12 @@ start:
                panic("getblk: bp->b_addr is null");
 
        if (bp->b_bufsize & 0xfff) {
-#if ZALLOC_METADATA
                if (ISSET(bp->b_flags, B_META) && (bp->b_bufsize & 0x1ff))
-#endif /* ZALLOC_METADATA */
                        panic("getblk: bp->b_bufsize = %d", bp->b_bufsize);
        }
 
        KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 386)) | DBG_FUNC_END,
-                    bp, bp->b_data, bp->b_flags, 3, 0);
+                    (int)bp, (int)bp->b_data, bp->b_flags, 3, 0);
 
        return (bp);
 }
@@ -1126,34 +1331,16 @@ geteblk(size)
 {
        struct buf *bp;
     int queue = BQ_EMPTY;
-#if !ZALLOC_METADATA
-       kern_return_t kret;
-       vm_size_t desired_size = roundup(size, CLBYTES);
-
-       if (desired_size > MAXBSIZE)
-               panic("geteblk: buffer larger than MAXBSIZE requested");
-#endif /* ZALLOC_METADATA */
 
        while ((bp = getnewbuf(0, 0, &queue)) == 0)
                ;
-#if ZALLOC_METADATA
        SET(bp->b_flags, (B_META|B_INVAL));
-#else
-       SET(bp->b_flags, B_INVAL);
-#endif /* ZALLOC_METADATA */
 
 #if DIAGNOSTIC
        assert(queue == BQ_EMPTY);
 #endif /* DIAGNOSTIC */
        /* XXX need to implement logic to deal with other queues */
 
-#if !ZALLOC_METADATA
-       /* Empty buffer - allocate pages */
-       kret = kmem_alloc_aligned(kernel_map, &bp->b_data, desired_size);
-       if (kret != KERN_SUCCESS)
-               panic("geteblk: kmem_alloc_aligned returned %d", kret);
-#endif /* ZALLOC_METADATA */
-
        binshash(bp, &invalhash);
        allocbuf(bp, size);
        bufstats.bufs_eblk++;
@@ -1161,7 +1348,6 @@ geteblk(size)
        return (bp);
 }
 
-#if ZALLOC_METADATA
 /*
  * Zones for the meta data buffers
  */
@@ -1179,18 +1365,10 @@ struct meta_zone_entry {
 struct meta_zone_entry meta_zones[] = {
        {NULL, (MINMETA * 1), 128 * (MINMETA * 1), "buf.512" },
        {NULL, (MINMETA * 2),  64 * (MINMETA * 2), "buf.1024" },
-       {NULL, (MINMETA * 3),  16 * (MINMETA * 3), "buf.1536" },
        {NULL, (MINMETA * 4),  16 * (MINMETA * 4), "buf.2048" },
-       {NULL, (MINMETA * 5),  16 * (MINMETA * 5), "buf.2560" },
-       {NULL, (MINMETA * 6),  16 * (MINMETA * 6), "buf.3072" },
-       {NULL, (MINMETA * 7),  16 * (MINMETA * 7), "buf.3584" },
        {NULL, (MINMETA * 8), 512 * (MINMETA * 8), "buf.4096" },
        {NULL, 0, 0, "" } /* End */
 };
-#endif /* ZALLOC_METADATA */
-
-zone_t buf_hdr_zone;
-int buf_hdr_count;
 
 /*
  * Initialize the meta data zones
@@ -1198,7 +1376,6 @@ int buf_hdr_count;
 static void
 bufzoneinit(void)
 {
-#if ZALLOC_METADATA
        int i;
 
        for (i = 0; meta_zones[i].mz_size != 0; i++) {
@@ -1208,23 +1385,24 @@ bufzoneinit(void)
                                        PAGE_SIZE,
                                        meta_zones[i].mz_name);
        }
-#endif /* ZALLOC_METADATA */
        buf_hdr_zone = zinit(sizeof(struct buf), 32, PAGE_SIZE, "buf headers");
 }
 
-#if ZALLOC_METADATA
-static zone_t
+static __inline__ zone_t
 getbufzone(size_t size)
 {
        int i;
 
-       if (size % 512)
+       if ((size % 512) || (size < MINMETA) || (size > MAXMETA))
                panic("getbufzone: incorect size = %d", size);
 
-       i = (size / 512) - 1;
+    for (i = 0; meta_zones[i].mz_size != 0; i++) {
+               if (meta_zones[i].mz_size >= size)
+                       break;
+       }
+
        return (meta_zones[i].mz_zone);
 }
-#endif /* ZALLOC_METADATA */
 
 /*
  * With UBC, there is no need to expand / shrink the file data 
@@ -1252,7 +1430,6 @@ allocbuf(bp, size)
        if (desired_size > MAXBSIZE)
                panic("allocbuf: buffer larger than MAXBSIZE requested");
 
-#if ZALLOC_METADATA
        if (ISSET(bp->b_flags, B_META)) {
                kern_return_t kret;
                zone_t zprev, z;
@@ -1312,11 +1489,11 @@ allocbuf(bp, size)
        }
 
        if (ISSET(bp->b_flags, B_META) && (bp->b_data == 0))
-               panic("allocbuf: bp->b_data is NULL");
-#endif /* ZALLOC_METADATA */
+               panic("allocbuf: bp->b_data is NULL, buf @ 0x%x", bp);
 
-               bp->b_bufsize = desired_size;
-               bp->b_bcount = size;
+       bp->b_bufsize = desired_size;
+       bp->b_bcount = size;
+       return (0);
 }
 
 /*
@@ -1348,7 +1525,6 @@ getnewbuf(slpflag, slptimeo, queue)
        register struct buf *meta_bp;
        register int age_time, lru_time, bp_time, meta_time;
        int s;
-       struct ucred *cred;
        int req = *queue; /* save it for restarts */
 
 start:
@@ -1372,8 +1548,11 @@ start:
        lru_bp = bufqueues[BQ_LRU].tqh_first;
        meta_bp = bufqueues[BQ_META].tqh_first;
 
-       if (!age_bp && !lru_bp && !meta_bp) { /* Unavailble on AGE or LRU */
-               /* Try the empty list first */
+       if (!age_bp && !lru_bp && !meta_bp) {
+               /*
+                * Unavailble on AGE or LRU or META queues
+                * Try the empty list first
+                */
                bp = bufqueues[BQ_EMPTY].tqh_first;
                if (bp) {
                        *queue = BQ_EMPTY;
@@ -1462,11 +1641,15 @@ start:
                panic("getnewbuf: null bp");
 
 found:
+       if (ISSET(bp->b_flags, B_LOCKED)) {
+           panic("getnewbuf: bp @ 0x%x is LOCKED! (flags 0x%x)\n", bp, bp->b_flags);
+       }
+       
        if (bp->b_hash.le_prev == (struct buf **)0xdeadbeef) 
-               panic("getnewbuf: le_prev is deadbeef");
+               panic("getnewbuf: le_prev is deadbeef, buf @ 0x%x", bp);
 
        if(ISSET(bp->b_flags, B_BUSY))
-               panic("getnewbuf reusing BUSY buf");
+               panic("getnewbuf reusing BUSY buf @ 0x%x", bp);
 
        /* Clean it */
        if (bcleanbuf(bp)) {
@@ -1478,8 +1661,10 @@ found:
        splx(s);
        return (bp); 
 }
+
 #include <mach/mach_types.h>
 #include <mach/memory_object_types.h>
+#include <kern/sched_prim.h>
 
 /* 
  * Clean a buffer.
@@ -1487,11 +1672,12 @@ found:
  * Returns 1 if issued a bawrite() to indicate 
  * that the buffer is not ready.
  */
-int
+static int
 bcleanbuf(struct buf *bp)
 {
        int s;
        struct ucred *cred;
+       int     hdralloc = 0;
 
        s = splbio();
 
@@ -1501,6 +1687,10 @@ bcleanbuf(struct buf *bp)
        /* Buffer is no longer on free lists. */
        SET(bp->b_flags, B_BUSY);
 
+       /* Check whether the buffer header was "allocated" */
+       if (ISSET(bp->b_flags, B_HDRALLOC))
+               hdralloc = 1;
+
        if (bp->b_hash.le_prev == (struct buf **)0xdeadbeef) 
                panic("bcleanbuf: le_prev is deadbeef");
 
@@ -1513,6 +1703,8 @@ bcleanbuf(struct buf *bp)
                binstailfree(bp, &bufqueues[BQ_LAUNDRY], BQ_LAUNDRY);
                blaundrycnt++;
                wakeup(&blaundrycnt);
+               /* and give it a chance to run */
+               (void)thread_block(THREAD_CONTINUE_NULL);
                return (1);
        }
 
@@ -1524,7 +1716,6 @@ bcleanbuf(struct buf *bp)
        splx(s);
 
        if (ISSET(bp->b_flags, B_META)) {
-#if ZALLOC_METADATA
                vm_offset_t elem = (vm_offset_t)bp->b_data;
                if (elem == 0)
                        panic("bcleanbuf: NULL bp->b_data B_META buffer");
@@ -1543,12 +1734,6 @@ bcleanbuf(struct buf *bp)
                        bp->b_data = (caddr_t)0xdeadbeef;
                        kmem_free(kernel_map, elem, bp->b_bufsize); 
                }
-#else
-          if (bp->b_data == 0)
-                  panic("bcleanbuf: bp->b_data == NULL for B_META buffer");
-
-          kmem_free(kernel_map, bp->b_data, bp->b_bufsize); 
-#endif /* ZALLOC_METADATA */
        }
 
        trace(TR_BRELSE, pack(bp->b_vp, bp->b_bufsize), bp->b_lblkno);
@@ -1560,6 +1745,8 @@ bcleanbuf(struct buf *bp)
        bp->b_bufsize = 0;
        bp->b_data = 0;
        bp->b_flags = B_BUSY;
+       if (hdralloc)
+               SET(bp->b_flags, B_HDRALLOC);
        bp->b_dev = NODEV;
        bp->b_blkno = bp->b_lblkno = 0;
        bp->b_iodone = 0;
@@ -1593,10 +1780,7 @@ int
 biowait(bp)
        struct buf *bp;
 {
-       upl_t           upl;
-       upl_page_info_t *pl;
        int s;
-       kern_return_t kret;
 
        s = splbio();
        while (!ISSET(bp->b_flags, B_DONE))
@@ -1634,12 +1818,12 @@ biodone(bp)
        struct buf *bp;
 {
        boolean_t       funnel_state;
-       int s;
+       struct vnode *vp;
 
        funnel_state = thread_funnel_set(kernel_flock, TRUE);
 
        KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 387)) | DBG_FUNC_START,
-                    bp, bp->b_data, bp->b_flags, 0, 0);
+                    (int)bp, (int)bp->b_data, bp->b_flags, 0, 0);
 
        if (ISSET(bp->b_flags, B_DONE))
                panic("biodone already");
@@ -1652,10 +1836,44 @@ biodone(bp)
 
        if (!ISSET(bp->b_flags, B_READ) && !ISSET(bp->b_flags, B_RAW))
                vwakeup(bp);     /* wake up reader */
+           
+        if (kdebug_enable) {
+            int    code = DKIO_DONE;
+
+            if (bp->b_flags & B_READ)
+                code |= DKIO_READ;
+            if (bp->b_flags & B_ASYNC)
+                code |= DKIO_ASYNC;
+
+            if (bp->b_flags & B_META)
+                code |= DKIO_META;
+            else if (bp->b_flags & (B_PGIN | B_PAGEOUT))
+                code |= DKIO_PAGING;
+
+            KERNEL_DEBUG_CONSTANT(FSDBG_CODE(DBG_DKRW, code) | DBG_FUNC_NONE,
+                                    bp, bp->b_vp, bp->b_resid, bp->b_error, 0);
+        }
+        
+       /* Wakeup the throttled write operations as needed */
+       vp = bp->b_vp;
+       if (vp
+               && (vp->v_flag & VTHROTTLED)
+               && (vp->v_numoutput <= (BUFWRITE_THROTTLE / 3))) {
+               vp->v_flag &= ~VTHROTTLED;
+               wakeup((caddr_t)&vp->v_numoutput);
+       }
 
        if (ISSET(bp->b_flags, B_CALL)) {       /* if necessary, call out */
+               void    (*iodone_func)(struct buf *) = bp->b_iodone;
+
                CLR(bp->b_flags, B_CALL);       /* but note callout done */
-               (*bp->b_iodone)(bp);
+               bp->b_iodone = NULL;
+
+               if (iodone_func == NULL) {
+                       panic("biodone: bp @ 0x%x has NULL b_iodone!\n", bp);                   
+               } else { 
+                       (*iodone_func)(bp);
+               }
        } else if (ISSET(bp->b_flags, B_ASYNC)) /* if async, release it */
                brelse(bp);
        else {                                  /* or just wakeup the buffer */ 
@@ -1664,7 +1882,7 @@ biodone(bp)
        }
 
        KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 387)) | DBG_FUNC_END,
-                    bp, bp->b_data, bp->b_flags, 0, 0);
+                    (int)bp, (int)bp->b_data, bp->b_flags, 0, 0);
 
        thread_funnel_set(kernel_flock, funnel_state);
 }
@@ -1699,7 +1917,7 @@ count_busy_buffers()
        return (nbusy);
 }
 
-#if 1 /*DIAGNOSTIC */
+#if DIAGNOSTIC
 /*
  * Print out statistics on the current allocation of the buffer pool.
  * Can be enabled to print out on every ``sync'' by setting "syncprt"
@@ -1734,9 +1952,9 @@ vfs_bufstats()
 }
 #endif /* DIAGNOSTIC */
 
-#define        NRESERVEDIOBUFS 16
+#define        NRESERVEDIOBUFS 64
 
-struct buf *
+__private_extern__ struct buf *
 alloc_io_buf(vp, priv)
        struct vnode *vp;
        int priv;
@@ -1764,6 +1982,7 @@ alloc_io_buf(vp, priv)
        /* clear out various fields */
        bp->b_flags = B_BUSY;
        bp->b_blkno = bp->b_lblkno = 0;
+
        bp->b_iodone = 0;
        bp->b_error = 0;
        bp->b_resid = 0;
@@ -1783,7 +2002,7 @@ alloc_io_buf(vp, priv)
        return (bp);
 }
 
-void
+__private_extern__ void
 free_io_buf(bp)
        struct buf *bp;
 {
@@ -1805,8 +2024,7 @@ free_io_buf(bp)
        splx(s);
 }
 
-
-/* not hookedup yet */
+/* disabled for now */
 
 /* XXX move this to a separate file */
 /*
@@ -1897,19 +2115,18 @@ struct bufqlim {
 
 long bufqscanwait = 0;
 
-extern void bufqscan_thread();
-extern int balancebufq(int q);
-extern int btrimempty(int n);
-extern int initbufqscan(void);
-extern int nextbufq(int q);
-extern void buqlimprt(int all);
+static void bufqscan_thread();
+static int balancebufq(int q);
+static int btrimempty(int n);
+static __inline__ int initbufqscan(void);
+static __inline__ int nextbufq(int q);
+static void buqlimprt(int all);
 
-void
+static void
 bufq_balance_thread_init()
 {
 
        if (bufqscanwait++ == 0) {
-               int i;
 
                /* Initalize globals */
                MAXNBUF = (mem_size / PAGE_SIZE);
@@ -1968,7 +2185,7 @@ bufq_balance_thread_init()
 }
 
 /* The workloop for the buffer balancing thread */
-void
+static void
 bufqscan_thread()
 {
        boolean_t       funnel_state;
@@ -1980,13 +2197,14 @@ bufqscan_thread()
                do {
                        int q;  /* buffer queue to process */
                
-                       for (q = initbufqscan(); q; ) {
+                       q = initbufqscan();
+                       for (; q; ) {
                                moretodo |= balancebufq(q);
                                q = nextbufq(q);
                        }
                } while (moretodo);
 
-#if 1 || DIAGNOSTIC
+#if DIAGNOSTIC
                vfs_bufstats();
                buqlimprt(0);
 #endif
@@ -1998,7 +2216,7 @@ bufqscan_thread()
 }
 
 /* Seed for the buffer queue balancing */
-int
+static __inline__ int
 initbufqscan()
 {
        /* Start with AGE queue */
@@ -2006,7 +2224,7 @@ initbufqscan()
 }
 
 /* Pick next buffer queue to balance */
-int
+static __inline__ int
 nextbufq(int q)
 {
        int order[] = { BQ_AGE, BQ_LRU, BQ_META, BQ_EMPTY, 0 };
@@ -2017,7 +2235,7 @@ nextbufq(int q)
 }
 
 /* function to balance the buffer queues */
-int
+static int
 balancebufq(int q)
 {
        int moretodo = 0;
@@ -2073,7 +2291,7 @@ out:
        return (moretodo);              
 }
 
-int
+static int
 btrimempty(int n)
 {
        /*
@@ -2084,7 +2302,7 @@ btrimempty(int n)
         return (0);
 }
 
-void
+static __inline__ void
 bufqinc(int q)
 {
        if ((q < 0) || (q >= BQUEUES))
@@ -2094,7 +2312,7 @@ bufqinc(int q)
        return;
 }
 
-void
+static __inline__ void
 bufqdec(int q)
 {
        if ((q < 0) || (q >= BQUEUES))
@@ -2104,7 +2322,7 @@ bufqdec(int q)
        return;
 }
 
-void
+static void
 buqlimprt(int all)
 {
        int i;
@@ -2114,16 +2332,16 @@ buqlimprt(int all)
        if (all)
                for (i = 0; i < BQUEUES; i++) {
                        printf("%s : ", bname[i]);
-                       printf("min = %d, ", (long)bufqlim[i].bl_nlow);
-                       printf("cur = %d, ", (long)bufqlim[i].bl_num);
-                       printf("max = %d, ", (long)bufqlim[i].bl_nlhigh);
-                       printf("target = %d, ", (long)bufqlim[i].bl_target);
-                       printf("stale after %d seconds\n", bufqlim[i].bl_stale);
+                       printf("min = %ld, ", (long)bufqlim[i].bl_nlow);
+                       printf("cur = %ld, ", (long)bufqlim[i].bl_num);
+                       printf("max = %ld, ", (long)bufqlim[i].bl_nlhigh);
+                       printf("target = %ld, ", (long)bufqlim[i].bl_target);
+                       printf("stale after %ld seconds\n", bufqlim[i].bl_stale);
                }
        else
                for (i = 0; i < BQUEUES; i++) {
                        printf("%s : ", bname[i]);
-                       printf("cur = %d, ", (long)bufqlim[i].bl_num);
+                       printf("cur = %ld, ", (long)bufqlim[i].bl_num);
                }
 }
 
@@ -2147,6 +2365,8 @@ bcleanbuf_thread()
 {
        boolean_t       funnel_state;
        struct buf *bp;
+       int error = 0;
+       int loopcnt = 0;
 
        funnel_state = thread_funnel_set(kernel_flock, TRUE);
 
@@ -2158,10 +2378,93 @@ doit:
        bremfree(bp);
        blaundrycnt--;
        /* do the IO */
-       bawrite(bp);
+       error = bawrite_internal(bp, 0);
+       if (error) {
+               binstailfree(bp, &bufqueues[BQ_LAUNDRY], BQ_LAUNDRY);
+               blaundrycnt++;
+               if (loopcnt > 10) {
+                       (void)tsleep((void *)&blaundrycnt, PRIBIO, "blaundry", 1);
+                       loopcnt = 0;
+               } else {
+                       (void)thread_block(THREAD_CONTINUE_NULL);
+                       loopcnt++;
+               }
+       }
        /* start again */
        goto doit;
 
        (void) thread_funnel_set(kernel_flock, funnel_state);
 }
 
+
+static int
+bp_cmp(void *a, void *b)
+{
+    struct buf *bp_a = *(struct buf **)a,
+               *bp_b = *(struct buf **)b;
+    daddr_t res;
+
+    // don't have to worry about negative block
+    // numbers so this is ok to do.
+    //
+    res = (bp_a->b_blkno - bp_b->b_blkno);
+
+    return (int)res;
+}
+
+#define NFLUSH 32
+
+int
+bflushq(int whichq, struct mount *mp)
+{
+       struct buf *bp, *next;
+       int         i, buf_count, s;
+       int         counter=0, total_writes=0;
+       static struct buf *flush_table[NFLUSH];
+
+       if (whichq < 0 || whichq >= BQUEUES) {
+           return;
+       }
+
+
+  restart:
+       bp = TAILQ_FIRST(&bufqueues[whichq]);
+       for(buf_count=0; bp; bp=next) {
+           next = bp->b_freelist.tqe_next;
+                       
+           if (bp->b_vp == NULL || bp->b_vp->v_mount != mp) {
+               continue;
+           }
+
+           if ((bp->b_flags & B_DELWRI) && (bp->b_flags & B_BUSY) == 0) {
+               if (whichq != BQ_LOCKED && (bp->b_flags & B_LOCKED)) {
+                   panic("bflushq: bp @ 0x%x is locked!\n", bp);
+               }
+               
+               bremfree(bp);
+               bp->b_flags |= B_BUSY;
+               flush_table[buf_count] = bp;
+               buf_count++;
+               total_writes++;
+
+               if (buf_count >= NFLUSH) {
+                   qsort(flush_table, buf_count, sizeof(struct buf *), bp_cmp);
+
+                   for(i=0; i < buf_count; i++) {
+                       bawrite(flush_table[i]);
+                   }
+
+                   goto restart;
+               }
+           }
+       }
+
+       if (buf_count > 0) {
+           qsort(flush_table, buf_count, sizeof(struct buf *), bp_cmp);
+           for(i=0; i < buf_count; i++) {
+               bawrite(flush_table[i]);
+           }
+       }
+
+       return total_writes;
+}