Index: sys/sys/param.h
===================================================================
RCS file: /cvsroot/src/sys/sys/param.h,v
retrieving revision 1.482
diff -u -r1.482 param.h
--- sys/sys/param.h     12 Jul 2015 08:11:28 -0000      1.482
+++ sys/sys/param.h     16 Aug 2015 17:34:02 -0000
@@ -63,7 +63,7 @@
 *     2.99.9          (299000900)
 */

-#define        __NetBSD_Version__      799002000       /* NetBSD 7.99.20 */
+#define        __NetBSD_Version__      799002100       /* NetBSD 7.99.21 */

#define __NetBSD_Prereq__(M,m,p) (((((M) * 100000000) + \
    (m) * 1000000) + (p) * 100) <= __NetBSD_Version__)
Index: sys/sys/disk.h
===================================================================
RCS file: /cvsroot/src/sys/sys/disk.h,v
retrieving revision 1.64
diff -u -r1.64 disk.h
--- sys/sys/disk.h      2 May 2015 08:00:08 -0000       1.64
+++ sys/sys/disk.h      16 Aug 2015 17:34:02 -0000
@@ -476,10 +476,11 @@
       void    (*d_minphys)(struct buf *);
       int     (*d_open)(dev_t, int, int, struct lwp *);
       int     (*d_close)(dev_t, int, int, struct lwp *);
-       void    (*d_diskstart)(device_t);
+       int     (*d_diskstart)(device_t, struct buf *);
       void    (*d_iosize)(device_t, int *);
       int     (*d_dumpblocks)(device_t, void *, daddr_t, int);
       int     (*d_lastclose)(device_t);
+       int     (*d_discard)(device_t, off_t, off_t);
};
#endif

Index: sys/dev/dksubr.c
===================================================================
RCS file: /cvsroot/src/sys/dev/dksubr.c,v
retrieving revision 1.70
diff -u -r1.70 dksubr.c
--- sys/dev/dksubr.c    16 Aug 2015 17:28:28 -0000      1.70
+++ sys/dev/dksubr.c    16 Aug 2015 17:34:02 -0000
@@ -73,6 +73,7 @@
       (MAKEDISKDEV(major((dev)), DISKUNIT((dev)), RAW_PART))

static void    dk_makedisklabel(struct dk_softc *);
+static int     dk_translate(struct dk_softc *, struct buf *);

void
dk_init(struct dk_softc *dksc, device_t dev, int dtype)
@@ -89,6 +90,7 @@
void
dk_attach(struct dk_softc *dksc)
{
+       mutex_init(&dksc->sc_iolock, MUTEX_DEFAULT, IPL_VM);
       dksc->sc_flags |= DKF_INITED;
#ifdef DIAGNOSTIC
       dksc->sc_flags |= DKF_WARNLABEL | DKF_LABELSANITY;
@@ -99,6 +101,7 @@
dk_detach(struct dk_softc *dksc)
{
       dksc->sc_flags &= ~DKF_INITED;
+       mutex_destroy(&dksc->sc_iolock);
}

/* ARGSUSED */
@@ -199,11 +202,10 @@
       return 0;
}

-void
-dk_strategy(struct dk_softc *dksc, struct buf *bp)
+static int
+dk_translate(struct dk_softc *dksc, struct buf *bp)
{
-       const struct dkdriver *dkd = dksc->sc_dkdev.dk_driver;
-       int     s, part;
+       int     part;
       int     wlabel;
       daddr_t blkno;
       struct disklabel *lp;
@@ -211,16 +213,6 @@
       uint64_t numsecs;
       unsigned secsize;

-       DPRINTF_FOLLOW(("dk_strategy(%s, %p, %p)\n",
-           dksc->sc_xname, dksc, bp));
-
-       if (!(dksc->sc_flags & DKF_INITED)) {
-               DPRINTF_FOLLOW(("dk_strategy: not inited\n"));
-               bp->b_error  = ENXIO;
-               biodone(bp);
-               return;
-       }
-
       lp = dksc->sc_dkdev.dk_label;
       dk = &dksc->sc_dkdev;

@@ -234,29 +226,20 @@
        * The transfer must be a whole number of blocks and the offset must
        * not be negative.
        */
-       if ((bp->b_bcount % secsize) != 0 || bp->b_blkno < 0) {
-               bp->b_error = EINVAL;
-               biodone(bp);
-               return;
-       }
+       if ((bp->b_bcount % secsize) != 0 || bp->b_blkno < 0)
+               return EINVAL;

       /* If there is nothing to do, then we are done */
-       if (bp->b_bcount == 0) {
-               biodone(bp);
-               return;
-       }
+       if (bp->b_bcount == 0)
+               return 0;

       wlabel = dksc->sc_flags & (DKF_WLABEL|DKF_LABELLING);
       if (part == RAW_PART) {
-               if (bounds_check_with_mediasize(bp, DEV_BSIZE, numsecs) <= 0) {
-                       biodone(bp);
-                       return;
-               }
+               if (bounds_check_with_mediasize(bp, DEV_BSIZE, numsecs) <= 0)
+                       return bp->b_error;
       } else {
-               if (bounds_check_with_label(&dksc->sc_dkdev, bp, wlabel) <= 0) {
-                       biodone(bp);
-                       return;
-               }
+               if (bounds_check_with_label(&dksc->sc_dkdev, bp, wlabel) <= 0)
+                       return bp->b_error;
       }

       /*
@@ -272,15 +255,72 @@
               blkno += lp->d_partitions[DISKPART(bp->b_dev)].p_offset;
       bp->b_rawblkno = blkno;

+       return -1;
+}
+
+void
+dk_strategy(struct dk_softc *dksc, struct buf *bp)
+{
+       int error;
+
+       DPRINTF_FOLLOW(("dk_strategy(%s, %p, %p)\n",
+           dksc->sc_xname, dksc, bp));
+
+       if (!(dksc->sc_flags & DKF_INITED)) {
+               DPRINTF_FOLLOW(("dk_strategy: not inited\n"));
+               bp->b_error  = ENXIO;
+               biodone(bp);
+               return;
+       }
+
+       error = dk_translate(dksc, bp);
+       if (error >= 0) {
+               biodone(bp);
+               return;
+       }
+
       /*
-        * Start the unit by calling the start routine
-        * provided by the individual driver.
+        * Queue buffer and start unit
        */
-       s = splbio();
-       bufq_put(dksc->sc_bufq, bp);
-       dkd->d_diskstart(dksc->sc_dev);
-       splx(s);
-       return;
+       dk_start(dksc, bp);
+}
+
+void
+dk_start(struct dk_softc *dksc, struct buf *bp)
+{
+       const struct dkdriver *dkd = dksc->sc_dkdev.dk_driver;
+       int error;
+       struct buf *qbp __diagused;
+
+       mutex_enter(&dksc->sc_iolock);
+
+       if (bp != NULL)
+               bufq_put(dksc->sc_bufq, bp);
+
+       while ((bp = bufq_peek(dksc->sc_bufq)) != NULL) {
+
+               disk_busy(&dksc->sc_dkdev);
+               error = dkd->d_diskstart(dksc->sc_dev, bp);
+               if (error == EAGAIN) {
+                       disk_unbusy(&dksc->sc_dkdev, 0, (bp->b_flags & B_READ));
+                       break;
+               }
+
+#ifdef DIAGNOSTIC
+               qbp = bufq_get(dksc->sc_bufq);
+               KASSERT(bp == qbp);
+#else
+               (void) bufq_get(dksc->sc_bufq);
+#endif
+
+               if (error != 0) {
+                       bp->b_error = error;
+                       bp->b_resid = bp->b_bcount;
+                       dk_done(dksc, bp);
+               }
+       }
+
+       mutex_exit(&dksc->sc_iolock);
}

void
@@ -296,14 +336,54 @@
               printf("\n");
       }

+       mutex_enter(&dksc->sc_iolock);
       disk_unbusy(dk, bp->b_bcount - bp->b_resid, (bp->b_flags & B_READ));
+       mutex_exit(&dksc->sc_iolock);
+
#ifdef notyet
       rnd_add_uint(&dksc->sc_rnd_source, bp->b_rawblkno);
#endif
+
       biodone(bp);
}

int
+dk_discard(struct dk_softc *dksc, dev_t dev, off_t pos, off_t len)
+{
+       const struct dkdriver *dkd = dksc->sc_dkdev.dk_driver;
+       unsigned secsize = dksc->sc_dkdev.dk_geom.dg_secsize;
+       struct buf tmp, *bp = &tmp;
+       int error;
+
+       DPRINTF_FOLLOW(("dk_discard(%s, %p, 0x"PRIx64", %jd, %jd)\n",
+           dksc->sc_xname, dksc, (intmax_t)pos, (intmax_t)len));
+
+       if (!(dksc->sc_flags & DKF_INITED)) {
+               DPRINTF_FOLLOW(("dk_discard: not inited\n"));
+               return ENXIO;
+       }
+
+       if (secsize == 0 || (pos % secsize) != 0)
+               return EINVAL;
+
+       /* enough data to please the bounds checking code */
+       bp->b_dev = dev;
+       bp->b_blkno = (daddr_t)(pos / secsize);
+       bp->b_bcount = len;
+       bp->b_flags = B_WRITE;
+
+       error = dk_translate(dksc, bp);
+       if (error >= 0)
+               return error;
+
+       error = dkd->d_discard(dksc->sc_dev,
+               (off_t)bp->b_rawblkno * secsize,
+               (off_t)bp->b_bcount);
+
+       return error;
+}
+
+int
dk_size(struct dk_softc *dksc, dev_t dev)
{
       const struct dkdriver *dkd = dksc->sc_dkdev.dk_driver;
@@ -462,12 +542,11 @@
       case DIOCGSTRATEGY:
           {
               struct disk_strategy *dks = (void *)data;
-               int s;

-               s = splbio();
+               mutex_enter(&dksc->sc_iolock);
               strlcpy(dks->dks_name, bufq_getstrategyname(dksc->sc_bufq),
                   sizeof(dks->dks_name));
-               splx(s);
+               mutex_exit(&dksc->sc_iolock);
               dks->dks_paramlen = 0;

               return 0;
@@ -478,7 +557,6 @@
               struct disk_strategy *dks = (void *)data;
               struct bufq_state *new;
               struct bufq_state *old;
-               int s;

               if (dks->dks_param != NULL) {
                       return EINVAL;
@@ -489,11 +567,11 @@
               if (error) {
                       return error;
               }
-               s = splbio();
+               mutex_enter(&dksc->sc_iolock);
               old = dksc->sc_bufq;
               bufq_move(new, old);
               dksc->sc_bufq = new;
-               splx(s);
+               mutex_exit(&dksc->sc_iolock);
               bufq_free(old);

               return 0;
Index: sys/dev/dkvar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/dkvar.h,v
retrieving revision 1.20
diff -u -r1.20 dkvar.h
--- sys/dev/dkvar.h     2 May 2015 08:00:08 -0000       1.20
+++ sys/dev/dkvar.h     16 Aug 2015 17:34:02 -0000
@@ -44,6 +44,7 @@
#define DK_XNAME_SIZE 8
       char                     sc_xname[DK_XNAME_SIZE]; /* external name */
       struct disk              sc_dkdev;      /* generic disk info */
+       kmutex_t                 sc_iolock;     /* protects buffer queue */
       struct bufq_state       *sc_bufq;       /* buffer queue */
       int                      sc_dtype;      /* disk type */
};
@@ -85,6 +86,8 @@
int    dk_close(struct dk_softc *, dev_t,
                int, int, struct lwp *);
void   dk_strategy(struct dk_softc *, struct buf *);
+int    dk_discard(struct dk_softc *, dev_t, off_t, off_t);
+void   dk_start(struct dk_softc *, struct buf *);
void   dk_done(struct dk_softc *, struct buf *);
int    dk_size(struct dk_softc *, dev_t);
int    dk_ioctl(struct dk_softc *, dev_t,
Index: sys/dev/ld.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ld.c,v
retrieving revision 1.88
diff -u -r1.88 ld.c
--- sys/dev/ld.c        16 Aug 2015 17:32:31 -0000      1.88
+++ sys/dev/ld.c        16 Aug 2015 17:34:03 -0000
@@ -63,7 +63,7 @@
static void    ldminphys(struct buf *bp);
static bool    ld_suspend(device_t, const pmf_qual_t *);
static bool    ld_shutdown(device_t, int);
-static void    ld_start(device_t);
+static int     ld_diskstart(device_t, struct buf *bp);
static void    ld_iosize(device_t, int *);
static int     ld_dumpblocks(device_t, void *, daddr_t, int);
static void    ld_fake_geometry(struct ld_softc *);
@@ -90,7 +90,7 @@
       .d_dump = lddump,
       .d_psize = ldsize,
       .d_discard = nodiscard,
-       .d_flag = D_DISK
+       .d_flag = D_DISK | D_MPSAFE
};

const struct cdevsw ld_cdevsw = {
@@ -105,7 +105,7 @@
       .d_mmap = nommap,
       .d_kqfilter = nokqfilter,
       .d_discard = nodiscard,
-       .d_flag = D_DISK
+       .d_flag = D_DISK | D_MPSAFE
};

static struct  dkdriver lddkdriver = {
@@ -114,7 +114,7 @@
       .d_strategy = ldstrategy,
       .d_iosize = ld_iosize,
       .d_minphys  = ldminphys,
-       .d_diskstart = ld_start,
+       .d_diskstart = ld_diskstart,
       .d_dumpblocks = ld_dumpblocks,
       .d_lastclose = ld_lastclose
};
@@ -404,55 +404,28 @@
       return dk_strategy(dksc, bp);
}

-static void
-ld_start(device_t dev)
+static int
+ld_diskstart(device_t dev, struct buf *bp)
{
       struct ld_softc *sc = device_private(dev);
-       struct dk_softc *dksc = &sc->sc_dksc;
-       struct buf *bp;
       int error;

+       if (sc->sc_queuecnt >= sc->sc_maxqueuecnt)
+               return EAGAIN;
+
       mutex_enter(&sc->sc_mutex);

-       while (sc->sc_queuecnt < sc->sc_maxqueuecnt) {
-               /* See if there is work to do. */
-               if ((bp = bufq_peek(dksc->sc_bufq)) == NULL)
-                       break;
-
-               disk_busy(&dksc->sc_dkdev);
-               sc->sc_queuecnt++;
-
-               if (__predict_true((error = (*sc->sc_start)(sc, bp)) == 0)) {
-                       /*
-                        * The back-end is running the job; remove it from
-                        * the queue.
-                        */
-                       (void) bufq_get(dksc->sc_bufq);
-               } else  {
-                       disk_unbusy(&dksc->sc_dkdev, 0, (bp->b_flags & B_READ));
-                       sc->sc_queuecnt--;
-                       if (error == EAGAIN) {
-                               /*
-                                * Temporary resource shortage in the
-                                * back-end; just defer the job until
-                                * later.
-                                *
-                                * XXX We might consider a watchdog timer
-                                * XXX to make sure we are kicked into action.
-                                */
-                               break;
-                       } else {
-                               (void) bufq_get(dksc->sc_bufq);
-                               bp->b_error = error;
-                               bp->b_resid = bp->b_bcount;
-                               mutex_exit(&sc->sc_mutex);
-                               biodone(bp);
-                               mutex_enter(&sc->sc_mutex);
-                       }
-               }
+       if (sc->sc_queuecnt >= sc->sc_maxqueuecnt)
+               error = EAGAIN;
+       else {
+               error = (*sc->sc_start)(sc, bp);
+               if (error == 0)
+                       sc->sc_queuecnt++;
       }

       mutex_exit(&sc->sc_mutex);
+
+       return error;
}

void
@@ -469,7 +442,7 @@
                       cv_broadcast(&sc->sc_drain);
               }
               mutex_exit(&sc->sc_mutex);
-               ld_start(dksc->sc_dev);
+               dk_start(dksc, NULL);
       } else
               mutex_exit(&sc->sc_mutex);
}
Index: sys/dev/cgd.c
===================================================================
RCS file: /cvsroot/src/sys/dev/cgd.c,v
retrieving revision 1.98
diff -u -r1.98 cgd.c
--- sys/dev/cgd.c       2 May 2015 08:00:08 -0000       1.98
+++ sys/dev/cgd.c       16 Aug 2015 17:34:03 -0000
@@ -104,7 +104,7 @@

/* Internal Functions */

-static void    cgd_start(device_t);
+static int     cgd_diskstart(device_t, struct buf *);
static void    cgdiodone(struct buf *);

static int     cgd_ioctl_set(struct cgd_softc *, void *, struct lwp *);
@@ -121,7 +121,7 @@
        .d_close = cgdclose,
        .d_strategy = cgdstrategy,
        .d_iosize = NULL,
-        .d_diskstart = cgd_start,
+        .d_diskstart = cgd_diskstart,
        .d_dumpblocks = NULL,
        .d_lastclose = NULL
};
@@ -379,79 +379,65 @@
       }
}

-static void
-cgd_start(device_t dev)
+static int
+cgd_diskstart(device_t dev, struct buf *bp)
{
       struct  cgd_softc *cs = device_private(dev);
       struct  dk_softc *dksc = &cs->sc_dksc;
-       struct  buf *bp, *nbp;
-#ifdef DIAGNOSTIC
-       struct  buf *qbp;
-#endif
+       struct  buf *nbp;
       void *  addr;
       void *  newaddr;
       daddr_t bn;
       struct  vnode *vp;

-       while ((bp = bufq_peek(dksc->sc_bufq)) != NULL) {
+       DPRINTF_FOLLOW(("cgd_diskstart(%p, %p)\n", dksc, bp));

-               DPRINTF_FOLLOW(("cgd_start(%p, %p)\n", dksc, bp));
-               disk_busy(&dksc->sc_dkdev);
+       bn = bp->b_rawblkno;

-               bn = bp->b_rawblkno;
+       /*
+        * We attempt to allocate all of our resources up front, so that
+        * we can fail quickly if they are unavailable.
+        */
+       nbp = getiobuf(cs->sc_tvn, false);
+       if (nbp == NULL)
+               return EAGAIN;

-               /*
-                * We attempt to allocate all of our resources up front, so that
-                * we can fail quickly if they are unavailable.
-                */
-               nbp = getiobuf(cs->sc_tvn, false);
-               if (nbp == NULL) {
-                       disk_unbusy(&dksc->sc_dkdev, 0, (bp->b_flags & B_READ));
-                       break;
+       /*
+        * If we are writing, then we need to encrypt the outgoing
+        * block into a new block of memory.
+        */
+       newaddr = addr = bp->b_data;
+       if ((bp->b_flags & B_READ) == 0) {
+               newaddr = cgd_getdata(dksc, bp->b_bcount);
+               if (!newaddr) {
+                       putiobuf(nbp);
+                       return EAGAIN;
               }
+               cgd_cipher(cs, newaddr, addr, bp->b_bcount, bn,
+                   DEV_BSIZE, CGD_CIPHER_ENCRYPT);
+       }

-               /*
-                * If we are writing, then we need to encrypt the outgoing
-                * block into a new block of memory.
-                */
-               newaddr = addr = bp->b_data;
-               if ((bp->b_flags & B_READ) == 0) {
-                       newaddr = cgd_getdata(dksc, bp->b_bcount);
-                       if (!newaddr) {
-                               putiobuf(nbp);
-                               disk_unbusy(&dksc->sc_dkdev, 0, (bp->b_flags & B_READ));
-                               break;
-                       }
-                       cgd_cipher(cs, newaddr, addr, bp->b_bcount, bn,
-                           DEV_BSIZE, CGD_CIPHER_ENCRYPT);
-               }
-               /* we now have all needed resources to process this buf */
-#ifdef DIAGNOSTIC
-               qbp = bufq_get(dksc->sc_bufq);
-               KASSERT(bp == qbp);
-#else
-               (void)bufq_get(dksc->sc_bufq);
-#endif
-               nbp->b_data = newaddr;
-               nbp->b_flags = bp->b_flags;
-               nbp->b_oflags = bp->b_oflags;
-               nbp->b_cflags = bp->b_cflags;
-               nbp->b_iodone = cgdiodone;
-               nbp->b_proc = bp->b_proc;
-               nbp->b_blkno = bn;
-               nbp->b_bcount = bp->b_bcount;
-               nbp->b_private = bp;
-
-               BIO_COPYPRIO(nbp, bp);
-
-               if ((nbp->b_flags & B_READ) == 0) {
-                       vp = nbp->b_vp;
-                       mutex_enter(vp->v_interlock);
-                       vp->v_numoutput++;
-                       mutex_exit(vp->v_interlock);
-               }
-               VOP_STRATEGY(cs->sc_tvn, nbp);
+       nbp->b_data = newaddr;
+       nbp->b_flags = bp->b_flags;
+       nbp->b_oflags = bp->b_oflags;
+       nbp->b_cflags = bp->b_cflags;
+       nbp->b_iodone = cgdiodone;
+       nbp->b_proc = bp->b_proc;
+       nbp->b_blkno = bn;
+       nbp->b_bcount = bp->b_bcount;
+       nbp->b_private = bp;
+
+       BIO_COPYPRIO(nbp, bp);
+
+       if ((nbp->b_flags & B_READ) == 0) {
+               vp = nbp->b_vp;
+               mutex_enter(vp->v_interlock);
+               vp->v_numoutput++;
+               mutex_exit(vp->v_interlock);
       }
+       VOP_STRATEGY(cs->sc_tvn, nbp);
+
+       return 0;
}

static void
@@ -460,7 +446,6 @@
       struct  buf *obp = nbp->b_private;
       struct  cgd_softc *cs = getcgd_softc(obp->b_dev);
       struct  dk_softc *dksc = &cs->sc_dksc;
-       int s;

       KDASSERT(cs);

@@ -492,16 +477,7 @@

       putiobuf(nbp);

-       /* Request is complete for whatever reason */
-       obp->b_resid = 0;
-       if (obp->b_error != 0)
-               obp->b_resid = obp->b_bcount;
-       s = splbio();
-       disk_unbusy(&dksc->sc_dkdev, obp->b_bcount - obp->b_resid,
-           (obp->b_flags & B_READ));
-       biodone(obp);
-       cgd_start(dksc->sc_dev);
-       splx(s);
+       dk_done(dksc, obp);
}

/* XXX: we should probably put these into dksubr.c, mostly */
Index: sys/arch/xen/xen/xbd_xenbus.c
===================================================================
RCS file: /cvsroot/src/sys/arch/xen/xen/xbd_xenbus.c,v
retrieving revision 1.71
diff -u -r1.71 xbd_xenbus.c
--- sys/arch/xen/xen/xbd_xenbus.c       2 May 2015 08:00:08 -0000       1.71
+++ sys/arch/xen/xen/xbd_xenbus.c       16 Aug 2015 17:34:03 -0000
@@ -40,7 +40,7 @@
 * - initiate request: xbdread/write/open/ioctl/..
 * - depending on operation, it is handled directly by disk(9) subsystem or
 *   goes through physio(9) first.
- * - the request is ultimately processed by xbdstart() that prepares the
+ * - the request is ultimately processed by xbd_diskstart() that prepares the
 *   xbd requests, post them in the ring I/O queue, then signal the backend.
 *
 * When a response is available in the queue, the backend signals the frontend
@@ -168,7 +168,7 @@
static bool xbd_xenbus_resume(device_t, const pmf_qual_t *);

static int  xbd_handler(void *);
-static void xbdstart(device_t);
+static int  xbd_diskstart(device_t, struct buf *);
static void xbd_backend_changed(void *, XenbusState);
static void xbd_connect(struct xbd_xenbus_softc *);

@@ -223,7 +223,7 @@
       .d_minphys = xbdminphys,
       .d_open = xbdopen,
       .d_close = xbdclose,
-       .d_diskstart = xbdstart,
+       .d_diskstart = xbd_diskstart,
};

static int
@@ -697,7 +697,7 @@
                       bp->b_resid = bp->b_bcount;
                       goto next;
               }
-               /* b_resid was set in xbdstart */
+               /* b_resid was set in dk_start */
next:
               if (bp->b_data != xbdreq->req_data)
                       xbd_unmap_align(xbdreq);
@@ -720,7 +720,7 @@
       if (sc->sc_xbdreq_wait)
               wakeup(&sc->sc_xbdreq_wait);
       else
-               xbdstart(sc->sc_dksc.sc_dev);
+               dk_start(&sc->sc_dksc, NULL);
       return 1;
}

@@ -918,156 +918,111 @@
       return dk_dump(&sc->sc_dksc, dev, blkno, va, size);
}

-static void
-xbdstart(device_t self)
+static int
+xbd_diskstart(device_t self, struct buf *bp)
{
       struct xbd_xenbus_softc *sc = device_private(self);
-       struct dk_softc *dksc = &sc->sc_dksc;
-       struct buf *bp;
-#ifdef DIAGNOSTIC
-       struct  buf *qbp;
-#endif
       struct xbd_req *xbdreq;
       blkif_request_t *req;
       size_t bcount, off;
       paddr_t ma;
       vaddr_t va;
       int nsects, nbytes, seg;
-       int notify;
-
-       while ((bp = bufq_peek(dksc->sc_bufq)) != NULL) {
+       int notify, error = 0;

-               DPRINTF(("xbdstart(%p): b_bcount = %ld\n",
-                   bp, (long)bp->b_bcount));
+       DPRINTF(("xbd_diskstart(%p): b_bcount = %ld\n",
+           bp, (long)bp->b_bcount));

-               if (sc->sc_shutdown != BLKIF_SHUTDOWN_RUN) {
-                       bp->b_error = EIO;
-                       goto err;
-               }
+       if (sc->sc_shutdown != BLKIF_SHUTDOWN_RUN) {
+               error = EIO;
+               goto err;
+       }

-               if (bp->b_rawblkno < 0 || bp->b_rawblkno > sc->sc_xbdsize) {
-                       /* invalid block number */
-                       bp->b_error = EINVAL;
-                       goto err;
-               }
+       if (bp->b_rawblkno < 0 || bp->b_rawblkno > sc->sc_xbdsize) {
+               /* invalid block number */
+               error = EINVAL;
+               goto err;
+       }

-               if (bp->b_rawblkno == sc->sc_xbdsize) {
-                       /* at end of disk; return short read */
-                       bp->b_resid = bp->b_bcount;
-#ifdef DIAGNOSTIC
-                       qbp = bufq_get(dksc->sc_bufq);
-                       KASSERT(bp == qbp);
-#else
-                       (void)bufq_get(dksc->sc_bufq);
-#endif
-                       biodone(bp);
-                       continue;
-               }
+       if (__predict_false(
+           sc->sc_backend_status == BLKIF_STATE_SUSPENDED)) {
+               /* device is suspended, do not consume buffer */
+               DPRINTF(("%s: (xbd_diskstart) device suspended\n",
+                   sc->sc_dksc.sc_xname));
+               error = EAGAIN;
+               goto out;
+       }

-               if (__predict_false(
-                   sc->sc_backend_status == BLKIF_STATE_SUSPENDED)) {
-                       /* device is suspended, do not consume buffer */
-                       DPRINTF(("%s: (xbdstart) device suspended\n",
-                           device_xname(sc->sc_dksc.sc_dev)));
-                       goto out;
-               }
+       if (RING_FULL(&sc->sc_ring) || sc->sc_xbdreq_wait) {
+               DPRINTF(("xbd_diskstart: ring_full\n"));
+               error = EAGAIN;
+               goto out;
+       }

-               if (RING_FULL(&sc->sc_ring) || sc->sc_xbdreq_wait) {
-                       DPRINTF(("xbdstart: ring_full\n"));
-                       goto out;
-               }
+       xbdreq = SLIST_FIRST(&sc->sc_xbdreq_head);
+       if (__predict_false(xbdreq == NULL)) {
+               DPRINTF(("xbd_diskstart: no req\n"));
+               error = EAGAIN;
+               goto out;
+       }

-               xbdreq = SLIST_FIRST(&sc->sc_xbdreq_head);
-               if (__predict_false(xbdreq == NULL)) {
-                       DPRINTF(("xbdstart: no req\n"));
+       xbdreq->req_bp = bp;
+       xbdreq->req_data = bp->b_data;
+       if ((vaddr_t)bp->b_data & (XEN_BSIZE - 1)) {
+               if (__predict_false(xbd_map_align(xbdreq) != 0)) {
+                       DPRINTF(("xbd_diskstart: no align\n"));
+                       error = EAGAIN;
                       goto out;
               }
+       }

-               xbdreq->req_bp = bp;
-               xbdreq->req_data = bp->b_data;
-               if ((vaddr_t)bp->b_data & (XEN_BSIZE - 1)) {
-                       if (__predict_false(xbd_map_align(xbdreq) != 0)) {
-                               DPRINTF(("xbdstart: no align\n"));
-                               goto out;
-                       }
-               }
-               /* now we're sure we'll send this buf */
-#ifdef DIAGNOSTIC
-               qbp = bufq_get(dksc->sc_bufq);
-               KASSERT(bp == qbp);
-#else
-               (void)bufq_get(dksc->sc_bufq);
-#endif
-               disk_busy(&dksc->sc_dkdev);
-
-               SLIST_REMOVE_HEAD(&sc->sc_xbdreq_head, req_next);
-               req = RING_GET_REQUEST(&sc->sc_ring, sc->sc_ring.req_prod_pvt);
-               req->id = xbdreq->req_id;
-               req->operation =
-                   bp->b_flags & B_READ ? BLKIF_OP_READ : BLKIF_OP_WRITE;
-               req->sector_number = bp->b_rawblkno;
-               req->handle = sc->sc_handle;
-
-               va = (vaddr_t)xbdreq->req_data & ~PAGE_MASK;
-               off = (vaddr_t)xbdreq->req_data & PAGE_MASK;
-               if (bp->b_rawblkno + bp->b_bcount / DEV_BSIZE >=
-                   sc->sc_xbdsize) {
-                       bcount = (sc->sc_xbdsize - bp->b_rawblkno) * DEV_BSIZE;
-                       bp->b_resid = bp->b_bcount - bcount;
-               } else {
-                       bcount = bp->b_bcount;
-                       bp->b_resid = 0;
-               }
-               if (bcount > XBD_MAX_XFER) {
-                       bp->b_resid += bcount - XBD_MAX_XFER;
-                       bcount = XBD_MAX_XFER;
-               }
-               for (seg = 0; bcount > 0;) {
-                       pmap_extract_ma(pmap_kernel(), va, &ma);
-                       KASSERT((ma & (XEN_BSIZE - 1)) == 0);
-                       if (bcount > PAGE_SIZE - off)
-                               nbytes = PAGE_SIZE - off;
-                       else
-                               nbytes = bcount;
-                       nsects = nbytes >> XEN_BSHIFT;
-                       req->seg[seg].first_sect = off >> XEN_BSHIFT;
-                       req->seg[seg].last_sect =
-                           (off >> XEN_BSHIFT) + nsects - 1;
-                       KASSERT(req->seg[seg].first_sect <=
-                           req->seg[seg].last_sect);
-                       KASSERT(req->seg[seg].last_sect < 8);
-                       if (__predict_false(xengnt_grant_access(
-                           sc->sc_xbusd->xbusd_otherend_id, ma,
-                           (bp->b_flags & B_READ) == 0,
-                           &xbdreq->req_gntref[seg])))
-                               panic("xbdstart: xengnt_grant_access"); /* XXX XXX !!! */
-                       req->seg[seg].gref = xbdreq->req_gntref[seg];
-                       seg++;
-                       KASSERT(seg <= BLKIF_MAX_SEGMENTS_PER_REQUEST);
-                       va += PAGE_SIZE;
-                       off = 0;
-                       bcount -= nbytes;
-               }
-               xbdreq->req_nr_segments = req->nr_segments = seg;
-               sc->sc_ring.req_prod_pvt++;
+       SLIST_REMOVE_HEAD(&sc->sc_xbdreq_head, req_next);
+       req = RING_GET_REQUEST(&sc->sc_ring, sc->sc_ring.req_prod_pvt);
+       req->id = xbdreq->req_id;
+       req->operation =
+           bp->b_flags & B_READ ? BLKIF_OP_READ : BLKIF_OP_WRITE;
+       req->sector_number = bp->b_rawblkno;
+       req->handle = sc->sc_handle;
+
+       va = (vaddr_t)xbdreq->req_data & ~PAGE_MASK;
+       off = (vaddr_t)xbdreq->req_data & PAGE_MASK;
+       bcount = bp->b_bcount;
+       bp->b_resid = 0;
+       for (seg = 0; bcount > 0;) {
+               pmap_extract_ma(pmap_kernel(), va, &ma);
+               KASSERT((ma & (XEN_BSIZE - 1)) == 0);
+               if (bcount > PAGE_SIZE - off)
+                       nbytes = PAGE_SIZE - off;
+               else
+                       nbytes = bcount;
+               nsects = nbytes >> XEN_BSHIFT;
+               req->seg[seg].first_sect = off >> XEN_BSHIFT;
+               req->seg[seg].last_sect =
+                   (off >> XEN_BSHIFT) + nsects - 1;
+               KASSERT(req->seg[seg].first_sect <=
+                   req->seg[seg].last_sect);
+               KASSERT(req->seg[seg].last_sect < 8);
+               if (__predict_false(xengnt_grant_access(
+                   sc->sc_xbusd->xbusd_otherend_id, ma,
+                   (bp->b_flags & B_READ) == 0,
+                   &xbdreq->req_gntref[seg])))
+                       panic("xbd_diskstart: xengnt_grant_access"); /* XXX XXX !!! */
+               req->seg[seg].gref = xbdreq->req_gntref[seg];
+               seg++;
+               KASSERT(seg <= BLKIF_MAX_SEGMENTS_PER_REQUEST);
+               va += PAGE_SIZE;
+               off = 0;
+               bcount -= nbytes;
       }
+       xbdreq->req_nr_segments = req->nr_segments = seg;
+       sc->sc_ring.req_prod_pvt++;

out:
       RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&sc->sc_ring, notify);
       if (notify)
               hypervisor_notify_via_evtchn(sc->sc_evtchn);
-       return;
-
err:
-#ifdef DIAGNOSTIC
-       qbp = bufq_get(dksc->sc_bufq);
-       KASSERT(bp == qbp);
-#else
-       (void)bufq_get(dksc->sc_bufq);
-#endif
-       bp->b_resid = bp->b_bcount;
-       biodone(bp);
-       return;
+       return error;
}

static int