Subject: Re: panic: dequeued wrong buf in -current
To: Andreas Wrede <andreas@planix.com>
From: Manuel Bouyer <bouyer@antioche.eu.org>
List: tech-kern
Date: 09/07/2004 23:12:00
--M9NhX3UHpAaciwkO
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Tue, Sep 07, 2004 at 12:12:42AM +0200, Manuel Bouyer wrote:
> Juergen Hannken-Illjes has reported in private mail a similar problem,
> on sparc64 without raidframe or ccd involved.
> He started looking at this, and it appears that sdstart() is called twice,
> once of the calls being interrupted. I followed the call graph and I don't
> know where it could happen.
> 
> Both you and Juergen use the esiop driver, and this driver can call
> scsipi_done() from esiop_scsipi_request(). This can likely cause sdstart() to
> call itself. Other HBA drivers may do this as well.
> 
> A workaround would be to add a lock in sdstart() to avoid such recursion,
> but this will have an impact on performances, as we loose opportunities to
> keep the disk busy.
> A better way would be to allow sdstart() to be reentrant. 
> Basically we need to deqeue the buf before calling the HBA's adapter request.
> 1) add a struct scsipi_xfer * argument to scsipi_command(): if this pointer is
>    not null it would use this xfer, otherwise it would try to allocate one
>    as it does now.
> 2) make scsipi_command() dequeue the buf itself. We can't do this for every
>    command with a buf, so this needs a new flag, or something
> 3) always dequeue the buf, and use a local FIFO queue when we're out of
>    ressources.
> 
> I prefer 1) myself, as it can allow a more flexible error recovery procedure
> on resources shortage in other cases too.  However, it's quite intrusive as
> all scsipi_command() calls needs to be touched (which means almost all
> files in sys/dev/scsipi) As we want to get this pulled up to 2.0, 2) may be
> better. 

2) isn't doable without adding an extra argument to scsipi_command() either
(we need a pointer to the buf queue), so I've gone with 1).
I'm now running the attached patch on i386 with atapi devices, and sparc
with scsi drives (controller is a esp@sbus).

I noone object I'll commit this in a couple of days, and request a pullup to
2.0 of all commits related to dealing with scsipi_xfer shortage.

Andreas, can you please give this patch a try ?

-- 
Manuel Bouyer <bouyer@antioche.eu.org>
     NetBSD: 26 ans d'experience feront toujours la difference
--

--M9NhX3UHpAaciwkO
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=diff

Index: atapi_base.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/atapi_base.c,v
retrieving revision 1.21
diff -u -r1.21 atapi_base.c
--- atapi_base.c	27 Aug 2004 20:37:28 -0000	1.21
+++ atapi_base.c	7 Sep 2004 20:28:28 -0000
@@ -213,11 +213,11 @@
  * to associate with the transfer, we need that too.
  */
 int
-atapi_scsipi_cmd(struct scsipi_periph *periph,
+atapi_scsipi_cmd(struct scsipi_periph *periph, struct scsipi_xfer *_xs,
     struct scsipi_generic *scsipi_cmd, int cmdlen, void *data, size_t datalen,
     int retries, int timeout, struct buf *bp, int flags)
 {
-	struct scsipi_xfer *xs;
+	struct scsipi_xfer *xs = _xs;
 	int error;
 
 	SC_DEBUG(periph, SCSIPI_DB2, ("atapi_cmd\n"));
@@ -226,11 +226,12 @@
 	if (bp != NULL && (flags & XS_CTL_ASYNC) == 0)
 		panic("atapi_scsipi_cmd: buffer without async");
 #endif
-
-	if ((xs = scsipi_make_xs(periph, scsipi_cmd, cmdlen, data,
-	    datalen, retries, timeout, bp, flags)) == NULL) {
-		/* let the caller deal with this */
-		return (ENOMEM);
+	if (xs == NULL) {
+		if ((xs = scsipi_make_xs(periph, scsipi_cmd, cmdlen, data,
+		    datalen, retries, timeout, bp, flags)) == NULL) {
+			/* let the caller deal with this */
+			return (ENOMEM);
+		}
 	}
 
 	xs->cmdlen = (periph->periph_cap & PERIPH_CAP_CMD16) ? 16 : 12;
Index: atapiconf.h
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/atapiconf.h,v
retrieving revision 1.17
diff -u -r1.17 atapiconf.h
--- atapiconf.h	21 Aug 2004 17:41:18 -0000	1.17
+++ atapiconf.h	7 Sep 2004 20:28:30 -0000
@@ -57,7 +57,8 @@
 int	atapiprint(void *, const char *);
 void	atapi_print_addr(struct scsipi_periph *);
 int	atapi_interpret_sense(struct scsipi_xfer *);
-int	atapi_scsipi_cmd(struct scsipi_periph *, struct scsipi_generic *,
-	    int, void *, size_t, int, int, struct buf *, int);
+int	atapi_scsipi_cmd(struct scsipi_periph *, struct scsipi_xfer *xs,
+	    struct scsipi_generic *, int, void *, size_t,
+	    int, int, struct buf *, int);
 
 #endif /* _DEV_SCSIPI_ATAPICONF_H */
Index: cd.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/cd.c,v
retrieving revision 1.204
diff -u -r1.204 cd.c
--- cd.c	6 Sep 2004 20:38:14 -0000	1.204
+++ cd.c	7 Sep 2004 20:28:34 -0000
@@ -88,6 +88,7 @@
 					/* from there */
 #include <dev/scsipi/scsi_disk.h>	/* rw comes from there */
 #include <dev/scsipi/scsipiconf.h>
+#include <dev/scsipi/scsipi_base.h>
 #include <dev/scsipi/cdvar.h>
 
 #define	CDUNIT(z)			DISKUNIT(z)
@@ -772,6 +773,7 @@
 	struct scsipi_rw_big cmd_big;
 	struct scsi_rw cmd_small;
 	struct scsipi_generic *cmdp;
+	struct scsipi_xfer *xs;
 	int flags, nblks, cmdlen, error;
 
 	SC_DEBUG(periph, SCSIPI_DB2, ("cdstart "));
@@ -866,15 +868,10 @@
 		 * Call the routine that chats with the adapter.
 		 * Note: we cannot sleep as we may be an interrupt
 		 */
-		error = scsipi_command(periph, cmdp, cmdlen,
+		xs = scsipi_make_xs(periph, cmdp, cmdlen,
 		    (u_char *)bp->b_data, bp->b_bcount,
 		    CDRETRIES, 30000, bp, flags);
-		if (__predict_false(error)) {
-			disk_unbusy(&cd->sc_dk, 0, 0);
-			printf("%s: not queued, error %d\n",
-			    cd->sc_dev.dv_xname, error);
-		}
-		if (__predict_false(error == ENOMEM)) {
+		if (__predict_false(xs == NULL)) {
 			/*
 			 * out of memory. Keep this buffer in the queue, and
 			 * retry later.
@@ -883,12 +880,28 @@
 			    periph);
 			return;
 		}
+		/*
+		 * need to dequeue de buffer before queuing the command,
+		 * because cdstart may be called recursively from the
+		 * HBA driver
+		 */
 #ifdef DIAGNOSTIC
 		if (BUFQ_GET(&cd->buf_queue) != bp)
 			panic("cdstart(): dequeued wrong buf");
 #else
 		BUFQ_GET(&cd->buf_queue);
 #endif
+		error = scsipi_command(periph, xs, cmdp, cmdlen,
+		    (u_char *)bp->b_data, bp->b_bcount,
+		    CDRETRIES, 30000, bp, flags);
+		if (__predict_false(error)) {
+			disk_unbusy(&cd->sc_dk, 0, 0);
+			printf("%s: not queued, error %d\n",
+			    cd->sc_dev.dv_xname, error);
+			bp->b_flags |= B_ERROR;
+			bp->b_error = ENOMEM;
+			biodone(bp);
+		}
 	}
 }
 
@@ -1619,7 +1632,7 @@
 	 * If the command works, interpret the result as a 4 byte
 	 * number of blocks and a blocksize
 	 */
-	if (scsipi_command(cd->sc_periph,
+	if (scsipi_command(cd->sc_periph, NULL,
 	    (struct scsipi_generic *)&scsipi_cmd, sizeof(scsipi_cmd),
 	    (u_char *)&rdcap, sizeof(rdcap), CDRETRIES, 30000, NULL,
 	    flags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK) != 0)
@@ -1657,7 +1670,7 @@
 	scsipi_cmd.opcode = PLAY;
 	_lto4b(blkno, scsipi_cmd.blk_addr);
 	_lto2b(nblks, scsipi_cmd.xfer_len);
-	return (scsipi_command(cd->sc_periph,
+	return (scsipi_command(cd->sc_periph, NULL,
 	    (struct scsipi_generic *)&scsipi_cmd, sizeof(scsipi_cmd),
 	    0, 0, CDRETRIES, 30000, NULL, 0));
 }
@@ -1713,7 +1726,7 @@
 	scsipi_cmd.end_m = endm;
 	scsipi_cmd.end_s = ends;
 	scsipi_cmd.end_f = endf;
-	return (scsipi_command(cd->sc_periph,
+	return (scsipi_command(cd->sc_periph, NULL,
 	    (struct scsipi_generic *)&scsipi_cmd, sizeof(scsipi_cmd),
 	    0, 0, CDRETRIES, 30000, NULL, 0));
 }
@@ -1729,7 +1742,7 @@
 	memset(&scsipi_cmd, 0, sizeof(scsipi_cmd));
 	scsipi_cmd.opcode = PAUSE;
 	scsipi_cmd.resume = go & 0xff;
-	return (scsipi_command(cd->sc_periph,
+	return (scsipi_command(cd->sc_periph, NULL,
 	    (struct scsipi_generic *)&scsipi_cmd, sizeof(scsipi_cmd),
 	    0, 0, CDRETRIES, 30000, NULL, 0));
 }
@@ -1741,7 +1754,7 @@
 cd_reset(struct cd_softc *cd)
 {
 
-	return (scsipi_command(cd->sc_periph, 0, 0, 0, 0,
+	return (scsipi_command(cd->sc_periph, NULL, 0, 0, 0, 0,
 	    CDRETRIES, 30000, NULL, XS_CTL_RESET));
 }
 
@@ -1762,7 +1775,7 @@
 	scsipi_cmd.subchan_format = format;
 	scsipi_cmd.track = track;
 	_lto2b(len, scsipi_cmd.data_len);
-	return (scsipi_command(cd->sc_periph,
+	return (scsipi_command(cd->sc_periph, NULL,
 	    (struct scsipi_generic *)&scsipi_cmd,
 	    sizeof(struct scsipi_read_subchannel), (u_char *)data, len,
 	    CDRETRIES, 30000, NULL, flags | XS_CTL_DATA_IN | XS_CTL_SILENT));
@@ -1792,7 +1805,7 @@
 	scsipi_cmd.from_track = start;
 	_lto2b(ntoc, scsipi_cmd.data_len);
 	scsipi_cmd.control = control;
-	return (scsipi_command(cd->sc_periph,
+	return (scsipi_command(cd->sc_periph, NULL,
 	    (struct scsipi_generic *)&scsipi_cmd,
 	    sizeof(struct scsipi_read_toc), (u_char *)data, len, CDRETRIES,
 	    30000, NULL, flags | XS_CTL_DATA_IN));
@@ -1867,7 +1880,7 @@
 		cmd.opcode = GPCMD_REPORT_KEY;
 		cmd.bytes[8] = 8;
 		cmd.bytes[9] = 0 | (0 << 6);
-		error = scsipi_command(cd->sc_periph, &cmd, 12, buf, 8,
+		error = scsipi_command(cd->sc_periph, NULL, &cmd, 12, buf, 8,
 		    CDRETRIES, 30000, NULL,
 		    XS_CTL_DATA_IN|XS_CTL_DATA_ONSTACK);
 		if (error)
@@ -1879,7 +1892,7 @@
 		cmd.opcode = GPCMD_REPORT_KEY;
 		cmd.bytes[8] = 16;
 		cmd.bytes[9] = 1 | (a->lsc.agid << 6);
-		error = scsipi_command(cd->sc_periph, &cmd, 12, buf, 16,
+		error = scsipi_command(cd->sc_periph, NULL, &cmd, 12, buf, 16,
 		    CDRETRIES, 30000, NULL,
 		    XS_CTL_DATA_IN|XS_CTL_DATA_ONSTACK);
 		if (error)
@@ -1891,7 +1904,7 @@
 		cmd.opcode = GPCMD_REPORT_KEY;
 		cmd.bytes[8] = 12;
 		cmd.bytes[9] = 2 | (a->lsk.agid << 6);
-		error = scsipi_command(cd->sc_periph, &cmd, 12, buf, 12,
+		error = scsipi_command(cd->sc_periph, NULL, &cmd, 12, buf, 12,
 		    CDRETRIES, 30000, NULL,
 		    XS_CTL_DATA_IN|XS_CTL_DATA_ONSTACK);
 		if (error)
@@ -1904,7 +1917,7 @@
 		_lto4b(a->lstk.lba, &cmd.bytes[1]);
 		cmd.bytes[8] = 12;
 		cmd.bytes[9] = 4 | (a->lstk.agid << 6);
-		error = scsipi_command(cd->sc_periph, &cmd, 12, buf, 12,
+		error = scsipi_command(cd->sc_periph, NULL, &cmd, 12, buf, 12,
 		    CDRETRIES, 30000, NULL,
 		    XS_CTL_DATA_IN|XS_CTL_DATA_ONSTACK);
 		if (error)
@@ -1919,7 +1932,7 @@
 		cmd.opcode = GPCMD_REPORT_KEY;
 		cmd.bytes[8] = 8;
 		cmd.bytes[9] = 5 | (a->lsasf.agid << 6);
-		error = scsipi_command(cd->sc_periph, &cmd, 12, buf, 8,
+		error = scsipi_command(cd->sc_periph, NULL, &cmd, 12, buf, 8,
 		    CDRETRIES, 30000, NULL,
 		    XS_CTL_DATA_IN|XS_CTL_DATA_ONSTACK);
 		if (error)
@@ -1933,7 +1946,7 @@
 		cmd.bytes[9] = 1 | (a->hsc.agid << 6);
 		buf[1] = 14;
 		dvd_copy_challenge(&buf[4], a->hsc.chal);
-		error = scsipi_command(cd->sc_periph, &cmd, 12, buf, 16,
+		error = scsipi_command(cd->sc_periph, NULL, &cmd, 12, buf, 16,
 		    CDRETRIES, 30000, NULL,
 		    XS_CTL_DATA_OUT|XS_CTL_DATA_ONSTACK);
 		if (error)
@@ -1947,7 +1960,7 @@
 		cmd.bytes[9] = 3 | (a->hsk.agid << 6);
 		buf[1] = 10;
 		dvd_copy_key(&buf[4], a->hsk.key);
-		error = scsipi_command(cd->sc_periph, &cmd, 12, buf, 12,
+		error = scsipi_command(cd->sc_periph, NULL, &cmd, 12, buf, 12,
 		    CDRETRIES, 30000, NULL,
 		    XS_CTL_DATA_OUT|XS_CTL_DATA_ONSTACK);
 		if (error) {
@@ -1960,7 +1973,7 @@
 	case DVD_INVALIDATE_AGID:
 		cmd.opcode = GPCMD_REPORT_KEY;
 		cmd.bytes[9] = 0x3f | (a->lsa.agid << 6);
-		error = scsipi_command(cd->sc_periph, &cmd, 12, buf, 16,
+		error = scsipi_command(cd->sc_periph, NULL, &cmd, 12, buf, 16,
 		    CDRETRIES, 30000, NULL, 0);
 		if (error)
 			return (error);
@@ -1970,7 +1983,7 @@
 		cmd.opcode = GPCMD_REPORT_KEY;
 		cmd.bytes[8] = 8;
 		cmd.bytes[9] = 8 | (0 << 6);
-		error = scsipi_command(cd->sc_periph, &cmd, 12, buf, 8,
+		error = scsipi_command(cd->sc_periph, NULL, &cmd, 12, buf, 8,
 		    CDRETRIES, 30000, NULL,
 		    XS_CTL_DATA_IN|XS_CTL_DATA_ONSTACK);
 		if (error)
@@ -1988,7 +2001,7 @@
 		cmd.bytes[9] = 6 | (0 << 6);
 		buf[1] = 6;
 		buf[4] = a->hrpcs.pdrc;
-		error = scsipi_command(cd->sc_periph, &cmd, 12, buf, 8,
+		error = scsipi_command(cd->sc_periph, NULL, &cmd, 12, buf, 8,
 		    CDRETRIES, 30000, NULL,
 		    XS_CTL_DATA_OUT|XS_CTL_DATA_ONSTACK);
 		if (error)
@@ -2016,7 +2029,7 @@
 	_lto2b(sizeof(buf), &cmd.bytes[7]);
 
 	cmd.bytes[5] = s->physical.layer_num;
-	error = scsipi_command(cd->sc_periph, &cmd, 12, buf, sizeof(buf),
+	error = scsipi_command(cd->sc_periph, NULL, &cmd, 12, buf, sizeof(buf),
 	    CDRETRIES, 30000, NULL, XS_CTL_DATA_IN|XS_CTL_DATA_ONSTACK);
 	if (error)
 		return (error);
@@ -2054,7 +2067,7 @@
 	_lto2b(sizeof(buf), &cmd.bytes[7]);
 
 	cmd.bytes[5] = s->copyright.layer_num;
-	error = scsipi_command(cd->sc_periph, &cmd, 12, buf, sizeof(buf),
+	error = scsipi_command(cd->sc_periph, NULL, &cmd, 12, buf, sizeof(buf),
 	    CDRETRIES, 30000, NULL, XS_CTL_DATA_IN|XS_CTL_DATA_ONSTACK);
 	if (error)
 		return (error);
@@ -2079,7 +2092,7 @@
 	_lto2b(4 + 2048, &cmd.bytes[7]);
 
 	cmd.bytes[9] = s->disckey.agid << 6;
-	error = scsipi_command(cd->sc_periph, &cmd, 12, buf, 4 + 2048,
+	error = scsipi_command(cd->sc_periph, NULL, &cmd, 12, buf, 4 + 2048,
 	    CDRETRIES, 30000, NULL, XS_CTL_DATA_IN|XS_CTL_DATA_ONSTACK);
 	if (error == 0)
 		memcpy(s->disckey.value, &buf[4], 2048);
@@ -2100,7 +2113,7 @@
 	cmd.bytes[6] = s->type;
 	_lto2b(sizeof(buf), &cmd.bytes[7]);
 
-	error = scsipi_command(cd->sc_periph, &cmd, 12, buf, sizeof(buf),
+	error = scsipi_command(cd->sc_periph, NULL, &cmd, 12, buf, sizeof(buf),
 	    CDRETRIES, 30000, NULL, XS_CTL_DATA_IN|XS_CTL_DATA_ONSTACK);
 	if (error)
 		return (error);
@@ -2126,7 +2139,7 @@
 	cmd.bytes[6] = s->type;
 	_lto2b(4 + 2048, &cmd.bytes[7]);
 
-	error = scsipi_command(cd->sc_periph, &cmd, 12, buf, 4 + 2048,
+	error = scsipi_command(cd->sc_periph, NULL, &cmd, 12, buf, 4 + 2048,
 	    CDRETRIES, 30000, NULL, XS_CTL_DATA_IN|XS_CTL_DATA_ONSTACK);
 	if (error == 0) {
 		s->manufact.len = _2btol(&buf[0]);
@@ -2402,7 +2415,7 @@
 	scsipi_cmd.options = args->options;    /* ioctl uses MMC values */
 	scsipi_cmd.slot = args->slot;
 
-	return (scsipi_command(cd->sc_periph,
+	return (scsipi_command(cd->sc_periph, NULL,
 	    (struct scsipi_generic *)&scsipi_cmd, sizeof(scsipi_cmd),
 	    0, 0, CDRETRIES, 200000, NULL, 0));
 }
Index: ch.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/ch.c,v
retrieving revision 1.61
diff -u -r1.61 ch.c
--- ch.c	21 Aug 2004 17:40:25 -0000	1.61
+++ ch.c	7 Sep 2004 20:28:36 -0000
@@ -615,7 +615,7 @@
 	/*
 	 * Send command to changer.
 	 */
-	return (scsipi_command(sc->sc_periph,
+	return (scsipi_command(sc->sc_periph, NULL,
 	    (struct scsipi_generic *)&cmd, sizeof(cmd), NULL, 0, CHRETRIES,
 	    100000, NULL, 0));
 }
@@ -670,7 +670,7 @@
 	/*
 	 * Send command to changer.
 	 */
-	return (scsipi_command(sc->sc_periph,
+	return (scsipi_command(sc->sc_periph, NULL,
 	    (struct scsipi_generic *)&cmd, sizeof(cmd), NULL, 0, CHRETRIES,
 	    100000, NULL, 0));
 }
@@ -707,7 +707,7 @@
 	/*
 	 * Send command to changer.
 	 */
-	return (scsipi_command(sc->sc_periph,
+	return (scsipi_command(sc->sc_periph, NULL,
 	    (struct scsipi_generic *)&cmd, sizeof(cmd), NULL, 0, CHRETRIES,
 	    100000, NULL, 0));
 }
@@ -1047,7 +1047,7 @@
 	/*
 	 * Send command to changer.
 	 */
-	return (scsipi_command(sc->sc_periph,
+	return (scsipi_command(sc->sc_periph, NULL,
 	    (struct scsipi_generic *)&cmd, sizeof(cmd),
 	    (u_char *)data, datalen, CHRETRIES, 100000, NULL,
 	    scsiflags | XS_CTL_DATA_IN));
@@ -1113,7 +1113,7 @@
 	/*
 	 * Send command to changer.
 	 */
-	return (scsipi_command(sc->sc_periph,
+	return (scsipi_command(sc->sc_periph, NULL,
 	    (struct scsipi_generic *)&cmd, sizeof(cmd),
 	    (u_char *)data, datalen, CHRETRIES, 100000, NULL,
 	    datalen ? XS_CTL_DATA_OUT | XS_CTL_DATA_ONSTACK : 0));
@@ -1150,7 +1150,7 @@
 	tmo *= 5 * 60 * 1000;
 	tmo += (10 * 60 * 1000);
 
-	return (scsipi_command(sc->sc_periph,
+	return (scsipi_command(sc->sc_periph, NULL,
 	    (struct scsipi_generic *)&cmd, sizeof(cmd),
 	    NULL, 0, CHRETRIES, tmo, NULL, XS_CTL_IGNORE_ILLEGAL_REQUEST));
 }
Index: if_se.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/if_se.c,v
retrieving revision 1.46
diff -u -r1.46 if_se.c
--- if_se.c	23 Apr 2004 21:52:17 -0000	1.46
+++ if_se.c	7 Sep 2004 20:28:38 -0000
@@ -387,7 +387,7 @@
 	int error;
 	int s = splbio();
 
-	error = scsipi_command(periph, scsipi_cmd, cmdlen, data_addr,
+	error = scsipi_command(periph, NULL, scsipi_cmd, cmdlen, data_addr,
 	    datalen, retries, timeout, bp, flags);
 	splx(s);
 	return (error);
Index: scsi_base.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/scsi_base.c,v
retrieving revision 1.80
diff -u -r1.80 scsi_base.c
--- scsi_base.c	27 Aug 2004 20:37:28 -0000	1.80
+++ scsi_base.c	7 Sep 2004 20:28:38 -0000
@@ -67,7 +67,7 @@
 	scsipi_cmd.opcode = SCSI_CHANGE_DEFINITION;
 	scsipi_cmd.how = SC_SCSI_2;
 
-	return (scsipi_command(periph,
+	return (scsipi_command(periph, NULL,
 	    (struct scsipi_generic *) &scsipi_cmd, sizeof(scsipi_cmd),
 	    0, 0, SCSIPIRETRIES, 100000, NULL, flags));
 }
@@ -79,11 +79,11 @@
  * to associate with the transfer, we need that too.
  */
 int
-scsi_scsipi_cmd(struct scsipi_periph *periph, struct scsipi_generic *scsipi_cmd,
-    int cmdlen, void *data, size_t datalen, int retries, int timeout,
-    struct buf *bp, int flags)
+scsi_scsipi_cmd(struct scsipi_periph *periph, struct scsipi_xfer *_xs,
+    struct scsipi_generic *scsipi_cmd, int cmdlen, void *data, size_t datalen,
+    int retries, int timeout, struct buf *bp, int flags)
 {
-	struct scsipi_xfer *xs;
+	struct scsipi_xfer *xs = _xs;
 	int error;
 
 	SC_DEBUG(periph, SCSIPI_DB2, ("scsi_scsipi_cmd\n"));
@@ -93,10 +93,12 @@
 		panic("scsi_scsipi_cmd: buffer without async");
 #endif
 
-	if ((xs = scsipi_make_xs(periph, scsipi_cmd, cmdlen, data,
-	    datalen, retries, timeout, bp, flags)) == NULL) {
-		/* let the caller deal with this */
-		return (ENOMEM);
+	if (xs == NULL) {
+		if ((xs = scsipi_make_xs(periph, scsipi_cmd, cmdlen, data,
+		    datalen, retries, timeout, bp, flags)) == NULL) {
+			/* let the caller deal with this */
+			return (ENOMEM);
+		}
 	}
 
 	/*
Index: scsiconf.h
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/scsiconf.h,v
retrieving revision 1.51
diff -u -r1.51 scsiconf.h
--- scsiconf.h	21 Aug 2004 20:40:36 -0000	1.51
+++ scsiconf.h	7 Sep 2004 20:28:38 -0000
@@ -76,7 +76,8 @@
 void	scsi_kill_pending(struct scsipi_periph *);
 void	scsi_print_addr(struct scsipi_periph *);
 int	scsi_probe_bus(struct scsibus_softc *, int, int);
-int	scsi_scsipi_cmd(struct scsipi_periph *, struct scsipi_generic *,
-	    int, void *, size_t, int, int, struct buf *, int);
+int	scsi_scsipi_cmd(struct scsipi_periph *, struct scsipi_xfer *,
+	    struct scsipi_generic *, int, void *, size_t,
+	    int, int, struct buf *, int);
 
 #endif /* _DEV_SCSIPI_SCSICONF_H_ */
Index: scsipi_base.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/scsipi_base.c,v
retrieving revision 1.111
diff -u -r1.111 scsipi_base.c
--- scsipi_base.c	2 Sep 2004 12:39:56 -0000	1.111
+++ scsipi_base.c	7 Sep 2004 20:28:42 -0000
@@ -1040,7 +1040,7 @@
 	 * If the command works, interpret the result as a 4 byte
 	 * number of blocks
 	 */
-	if (scsipi_command(periph, (struct scsipi_generic *)&scsipi_cmd,
+	if (scsipi_command(periph, NULL, (struct scsipi_generic *)&scsipi_cmd,
 	    sizeof(scsipi_cmd), (u_char *)&rdcap, sizeof(rdcap),
 	    SCSIPIRETRIES, 20000, NULL,
 	    flags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK | XS_CTL_SILENT) != 0)
@@ -1072,7 +1072,7 @@
 	else
 		retries = SCSIPIRETRIES;
 
-	return (scsipi_command(periph,
+	return (scsipi_command(periph, NULL,
 	    (struct scsipi_generic *)&scsipi_cmd, sizeof(scsipi_cmd),
 	    0, 0, retries, 10000, NULL, flags));
 }
@@ -1110,13 +1110,13 @@
 	 * - mycroft, 2003/10/16
 	 */
 	scsipi_cmd.length = SCSIPI_INQUIRY_LENGTH_SCSI2;
-	error = scsipi_command(periph,
+	error = scsipi_command(periph, NULL,
 	    (struct scsipi_generic *) &scsipi_cmd, sizeof(scsipi_cmd),
 	    (u_char *) inqbuf, SCSIPI_INQUIRY_LENGTH_SCSI2,
 	    retries, 10000, NULL, XS_CTL_DATA_IN | flags);
 	if (!error && inqbuf->additional_length > SCSIPI_INQUIRY_LENGTH_SCSI2 - 4) {
 		scsipi_cmd.length = SCSIPI_INQUIRY_LENGTH_SCSI3;
-		error = scsipi_command(periph,
+		error = scsipi_command(periph, NULL,
 		    (struct scsipi_generic *) &scsipi_cmd, sizeof(scsipi_cmd),
 		    (u_char *) inqbuf, SCSIPI_INQUIRY_LENGTH_SCSI3,
 		    retries, 10000, NULL, XS_CTL_DATA_IN | flags);
@@ -1180,7 +1180,7 @@
 	scsipi_cmd.opcode = PREVENT_ALLOW;
 	scsipi_cmd.how = type;
 
-	return (scsipi_command(periph,
+	return (scsipi_command(periph, NULL,
 	    (struct scsipi_generic *) &scsipi_cmd, sizeof(scsipi_cmd),
 	    0, 0, SCSIPIRETRIES, 5000, NULL, flags));
 }
@@ -1200,7 +1200,7 @@
 	scsipi_cmd.byte2 = 0x00;
 	scsipi_cmd.how = type;
 
-	return (scsipi_command(periph,
+	return (scsipi_command(periph, NULL,
 	    (struct scsipi_generic *) &scsipi_cmd, sizeof(scsipi_cmd),
 	    0, 0, SCSIPIRETRIES, (type & SSS_START) ? 60000 : 10000,
 	    NULL, flags));
@@ -1224,8 +1224,9 @@
 	scsipi_cmd.byte2 = byte2;
 	scsipi_cmd.page = page;
 	scsipi_cmd.length = len & 0xff;
-	error = scsipi_command(periph, (struct scsipi_generic *)&scsipi_cmd,
-	    sizeof(scsipi_cmd), (void *)data, len, retries, timeout, NULL,
+	error = scsipi_command(periph, NULL,
+	    (struct scsipi_generic *)&scsipi_cmd, sizeof(scsipi_cmd),
+	    (void *)data, len, retries, timeout, NULL,
 	    flags | XS_CTL_DATA_IN);
 	SC_DEBUG(periph, SCSIPI_DB2,
 	    ("scsipi_mode_sense: error=%d\n", error));
@@ -1245,8 +1246,9 @@
 	scsipi_cmd.byte2 = byte2;
 	scsipi_cmd.page = page;
 	_lto2b(len, scsipi_cmd.length);
-	error = scsipi_command(periph, (struct scsipi_generic *)&scsipi_cmd,
-	    sizeof(scsipi_cmd), (void *)data, len, retries, timeout, NULL,
+	error = scsipi_command(periph, NULL,
+	    (struct scsipi_generic *)&scsipi_cmd, sizeof(scsipi_cmd),
+	    (void *)data, len, retries, timeout, NULL,
 	    flags | XS_CTL_DATA_IN);
 	SC_DEBUG(periph, SCSIPI_DB2,
 	    ("scsipi_mode_sense_big: error=%d\n", error));
@@ -1265,8 +1267,9 @@
 	scsipi_cmd.opcode = MODE_SELECT;
 	scsipi_cmd.byte2 = byte2;
 	scsipi_cmd.length = len & 0xff;
-	error = scsipi_command(periph, (struct scsipi_generic *)&scsipi_cmd,
-	    sizeof(scsipi_cmd), (void *)data, len, retries, timeout, NULL,
+	error = scsipi_command(periph, NULL,
+	    (struct scsipi_generic *)&scsipi_cmd, sizeof(scsipi_cmd),
+	    (void *)data, len, retries, timeout, NULL,
 	    flags | XS_CTL_DATA_OUT);
 	SC_DEBUG(periph, SCSIPI_DB2,
 	    ("scsipi_mode_select: error=%d\n", error));
@@ -1285,8 +1288,9 @@
 	scsipi_cmd.opcode = MODE_SELECT_BIG;
 	scsipi_cmd.byte2 = byte2;
 	_lto2b(len, scsipi_cmd.length);
-	error = scsipi_command(periph, (struct scsipi_generic *)&scsipi_cmd,
-	    sizeof(scsipi_cmd), (void *)data, len, retries, timeout, NULL,
+	error = scsipi_command(periph, NULL,
+	    (struct scsipi_generic *)&scsipi_cmd, sizeof(scsipi_cmd),
+	    (void *)data, len, retries, timeout, NULL,
 	    flags | XS_CTL_DATA_OUT);
 	SC_DEBUG(periph, SCSIPI_DB2,
 	    ("scsipi_mode_select: error=%d\n", error));
@@ -1690,7 +1694,7 @@
 	cmd.opcode = REQUEST_SENSE;
 	cmd.length = sizeof(struct scsipi_sense_data);
 
-	error = scsipi_command(periph,
+	error = scsipi_command(periph, NULL,
 	    (struct scsipi_generic *) &cmd, sizeof(cmd),
 	    (u_char*)&xs->sense.scsi_sense, sizeof(struct scsipi_sense_data),
 	    0, 1000, NULL, flags);
Index: scsipi_ioctl.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/scsipi_ioctl.c,v
retrieving revision 1.47
diff -u -r1.47 scsipi_ioctl.c
--- scsipi_ioctl.c	21 Aug 2004 21:29:39 -0000	1.47
+++ scsipi_ioctl.c	7 Sep 2004 20:28:43 -0000
@@ -278,7 +278,7 @@
 	if (screq->flags & SCCMD_ESCAPE)
 		flags |= XS_CTL_ESCAPE;
 
-	error = scsipi_command(periph,
+	error = scsipi_command(periph, NULL,
 	    (struct scsipi_generic *)screq->cmd, screq->cmdlen,
 	    (u_char *)bp->b_data, screq->datalen,
 	    0, /* user must do the retries *//* ignored */
Index: scsipiconf.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/scsipiconf.c,v
retrieving revision 1.23
diff -u -r1.23 scsipiconf.c
--- scsipiconf.c	21 Aug 2004 21:30:58 -0000	1.23
+++ scsipiconf.c	7 Sep 2004 20:28:44 -0000
@@ -71,9 +71,9 @@
 #define	STRVIS_ISWHITE(x) ((x) == ' ' || (x) == '\0' || (x) == (u_char)'\377')
 
 int
-scsipi_command(struct scsipi_periph *periph, struct scsipi_generic *cmd,
-    int cmdlen, u_char *data_addr, int datalen, int retries, int timeout,
-    struct buf *bp, int flags)
+scsipi_command(struct scsipi_periph *periph, struct scsipi_xfer *xs,
+    struct scsipi_generic *cmd, int cmdlen, u_char *data_addr, int datalen,
+    int retries, int timeout, struct buf *bp, int flags)
 {
 	int error;
  
@@ -86,7 +86,7 @@
 		PHOLD(curlwp);
 	}
 	error = (*periph->periph_channel->chan_bustype->bustype_cmd)(periph,
-	    cmd, cmdlen, data_addr, datalen, retries, timeout, bp, flags);
+	    xs, cmd, cmdlen, data_addr, datalen, retries, timeout, bp, flags);
 	if ((flags & XS_CTL_DATA_ONSTACK) != 0)
 		PRELE(curlwp);
 	return (error);
Index: scsipiconf.h
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/scsipiconf.h,v
retrieving revision 1.86
diff -u -r1.86 scsipiconf.h
--- scsipiconf.h	21 Aug 2004 21:30:29 -0000	1.86
+++ scsipiconf.h	7 Sep 2004 20:28:46 -0000
@@ -237,7 +237,7 @@
 struct scsipi_bustype {
 	int	bustype_type;		/* symbolic name of type */
 	
-	int	(*bustype_cmd)(struct scsipi_periph *,
+	int	(*bustype_cmd)(struct scsipi_periph *, struct scsipi_xfer *,
 		    struct scsipi_generic *, int, void *, size_t, int,
 		    int, struct buf *, int);
 	int	(*bustype_interpret_sense)(struct scsipi_xfer *);
@@ -626,7 +626,7 @@
 
 #ifdef _KERNEL
 void	scsipi_init(void);
-int	scsipi_command(struct scsipi_periph *,
+int	scsipi_command(struct scsipi_periph *, struct scsipi_xfer *xs,
 	    struct scsipi_generic *, int, u_char *, int,
 	    int, int, struct buf *, int);
 void	scsipi_create_completion_thread(void *);
Index: sd.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/sd.c,v
retrieving revision 1.222
diff -u -r1.222 sd.c
--- sd.c	6 Sep 2004 20:38:14 -0000	1.222
+++ sd.c	7 Sep 2004 20:28:49 -0000
@@ -85,6 +85,7 @@
 #include <dev/scsipi/scsipi_disk.h>
 #include <dev/scsipi/scsi_disk.h>
 #include <dev/scsipi/scsiconf.h>
+#include <dev/scsipi/scsipi_base.h>
 #include <dev/scsipi/sdvar.h>
 
 #define	SDUNIT(dev)			DISKUNIT(dev)
@@ -795,6 +796,7 @@
 	struct scsipi_rw_big cmd_big;
 	struct scsi_rw cmd_small;
 	struct scsipi_generic *cmdp;
+	struct scsipi_xfer *xs;
 	int nblks, cmdlen, error, flags;
 
 	SC_DEBUG(periph, SCSIPI_DB2, ("sdstart "));
@@ -899,15 +901,10 @@
 		 * Call the routine that chats with the adapter.
 		 * Note: we cannot sleep as we may be an interrupt
 		 */
-		error = scsipi_command(periph, cmdp, cmdlen,
+		xs = scsipi_make_xs(periph, cmdp, cmdlen,
 		    (u_char *)bp->b_data, bp->b_bcount,
 		    SDRETRIES, SD_IO_TIMEOUT, bp, flags);
-		if (__predict_false(error)) {
-			disk_unbusy(&sd->sc_dk, 0, 0);
-			printf("%s: not queued, error %d\n",
-			    sd->sc_dev.dv_xname, error);
-		}
-		if (__predict_false(error == ENOMEM)) {
+		if (__predict_false(xs == NULL)) {
 			/*
 			 * out of memory. Keep this buffer in the queue, and
 			 * retry later.
@@ -916,13 +913,28 @@
 			    periph);
 			return;
 		}
+		/*
+		 * need to dequeue de buffer before queuing the command,
+		 * because cdstart may be called recursively from the
+		 * HBA driver
+		 */
 #ifdef DIAGNOSTIC
 		if (BUFQ_GET(&sd->buf_queue) != bp)
 			panic("sdstart(): dequeued wrong buf");
 #else
 		BUFQ_GET(&sd->buf_queue);
 #endif
-
+		error = scsipi_command(periph, xs, cmdp, cmdlen,
+		    (u_char *)bp->b_data, bp->b_bcount,
+		    SDRETRIES, SD_IO_TIMEOUT, bp, flags);
+		if (__predict_false(error)) {
+			disk_unbusy(&sd->sc_dk, 0, 0);
+			printf("%s: not queued, error %d\n",
+			    sd->sc_dev.dv_xname, error);
+			bp->b_flags |= B_ERROR;
+			bp->b_error = ENOMEM;
+			biodone(bp);
+		}
 	}
 }
 
@@ -1665,8 +1677,9 @@
 		cmd.opcode = READ_FORMAT_CAPACITIES;
 		_lto2b(sizeof(data), cmd.length);
 
-		error = scsipi_command(sd->sc_periph, (void *)&cmd, sizeof(cmd),
-		    (void *)&data, sizeof(data), SDRETRIES, 20000, NULL,
+		error = scsipi_command(sd->sc_periph, NULL,
+		    (void *)&cmd, sizeof(cmd), (void *)&data, sizeof(data),
+		    SDRETRIES, 20000, NULL,
 		    flags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK);
 		if (error == EFTYPE) {
 			/* Medium Format Corrupted, handle as not formatted */
@@ -1953,7 +1966,7 @@
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.opcode = SCSI_SYNCHRONIZE_CACHE;
 
-	return (scsipi_command(periph, (void *)&cmd, sizeof(cmd), 0, 0,
+	return (scsipi_command(periph, NULL, (void *)&cmd, sizeof(cmd), 0, 0,
 	    SDRETRIES, 100000, NULL, flags | XS_CTL_IGNORE_ILLEGAL_REQUEST));
 }
 
Index: ses.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/ses.c,v
retrieving revision 1.23
diff -u -r1.23 ses.c
--- ses.c	21 Aug 2004 22:02:31 -0000	1.23
+++ ses.c	7 Sep 2004 20:28:52 -0000
@@ -500,7 +500,7 @@
 #ifndef	SCSIDEBUG
 	flg |= XS_CTL_SILENT;
 #endif
-	error = scsipi_command(ssc->sc_periph, &sgen, cdbl,
+	error = scsipi_command(ssc->sc_periph, NULL, &sgen, cdbl,
 	    (u_char *) dptr, dl, SCSIPIRETRIES, 30000, NULL, flg);
 
 	if (error == 0 && dptr)
Index: ss_mustek.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/ss_mustek.c,v
retrieving revision 1.20
diff -u -r1.20 ss_mustek.c
--- ss_mustek.c	27 Aug 2004 20:37:29 -0000	1.20
+++ ss_mustek.c	7 Sep 2004 20:28:54 -0000
@@ -66,6 +66,7 @@
 #include <dev/scsipi/scsipi_all.h>
 #include <dev/scsipi/scsi_scanner.h>
 #include <dev/scsipi/scsiconf.h>
+#include <dev/scsipi/scsipi_base.h>
 #include <dev/scsipi/ssvar.h>
 #include <dev/scsipi/ss_mustek.h>
 
@@ -320,7 +321,7 @@
 
 	/* send the set window command to the scanner */
 	SC_DEBUG(periph, SCSIPI_DB1, ("mustek_set_parms: set_window\n"));
-	error = scsipi_command(periph,
+	error = scsipi_command(periph, NULL,
 	    (struct scsipi_generic *) &window_cmd,
 	    sizeof(window_cmd), (u_char *) &window_data, sizeof(window_data),
 	    MUSTEK_RETRIES, 5000, NULL, XS_CTL_DATA_OUT | XS_CTL_DATA_ONSTACK);
@@ -360,7 +361,7 @@
 
 	SC_DEBUG(periph, SCSIPI_DB1, ("mustek_trigger_scanner: mode_select\n"));
 	/* send the command to the scanner */
-	error = scsipi_command(periph,
+	error = scsipi_command(periph, NULL,
 	    (struct scsipi_generic *) &mode_cmd,
 	    sizeof(mode_cmd), (u_char *) &mode_data, sizeof(mode_data),
 	    MUSTEK_RETRIES, 5000, NULL, XS_CTL_DATA_OUT | XS_CTL_DATA_ONSTACK);
@@ -398,7 +399,7 @@
 
 	/* send the command to the scanner */
 	SC_DEBUG(periph, SCSIPI_DB1, ("mustek_trigger_scanner: start_scan\n"));
-	error = scsipi_command(periph,
+	error = scsipi_command(periph, NULL,
 	    (struct scsipi_generic *) &start_scan_cmd,
 	    sizeof(start_scan_cmd), NULL, 0,
 	    MUSTEK_RETRIES, 5000, NULL, 0);
@@ -441,7 +442,7 @@
 		/* send the command to the scanner */
 		SC_DEBUG(periph, SCSIPI_DB1,
 		    ("mustek_rewind_scanner: stop_scan\n"));
-		error = scsipi_command(periph,
+		error = scsipi_command(periph, NULL,
 		    (struct scsipi_generic *) &cmd,
 		    sizeof(cmd), NULL, 0, MUSTEK_RETRIES, 5000, NULL, 0);
 		if (error)
@@ -460,6 +461,7 @@
 mustek_read(struct ss_softc *ss, struct buf *bp)
 {
 	struct mustek_read_cmd cmd;
+	struct scsipi_xfer *xs;
 	struct scsipi_periph *periph = ss->sc_periph;
 	u_long lines_to_read;
 	int error;
@@ -479,22 +481,33 @@
 	/*
 	 * go ask the adapter to do all this for us
 	 */
-	error = scsipi_command(periph,
+	xs = scsipi_make_xs(periph,
+	    (struct scsipi_generic *) &cmd, sizeof(cmd),
+	    (u_char *) bp->b_data, bp->b_bcount,
+	    MUSTEK_RETRIES, 10000, bp,
+	    XS_CTL_NOSLEEP | XS_CTL_ASYNC | XS_CTL_DATA_IN);
+	if (xs == NULL) {
+		/*
+		 * out of memory. Keep this buffer in the queue, and
+		 * retry later.
+		 */
+		callout_reset(&ss->sc_callout, hz / 2, ssrestart,
+		    periph);
+		return(0);
+	}
+#ifdef DIAGNOSTIC
+	if (BUFQ_GET(&ss->buf_queue) != bp)
+		panic("ssstart(): dequeued wrong buf");
+#else
+	BUFQ_GET(&ss->buf_queue);
+#endif
+	error = scsipi_command(periph, xs,
 	    (struct scsipi_generic *) &cmd, sizeof(cmd),
 	    (u_char *) bp->b_data, bp->b_bcount, MUSTEK_RETRIES, 10000, bp,
 	    XS_CTL_NOSLEEP | XS_CTL_ASYNC | XS_CTL_DATA_IN);
 	if (error) {
 		printf("%s: not queued, error %d\n", ss->sc_dev.dv_xname,
 		    error);
-		if (error == ENOMEM) {
-			/*
-			 * out of memory. Keep this buffer in the queue, and
-			 * retry later.
-			 */
-			callout_reset(&ss->sc_callout, hz / 2, ssrestart,
-			    periph);
-			return(0);
-		}
 	} else {
 		ss->sio.scan_lines -= lines_to_read;
 		if (ss->sio.scan_lines < 0)
@@ -503,13 +516,6 @@
 		if (ss->sio.scan_window_size < 0)
 			ss->sio.scan_window_size = 0;
 	}
-#ifdef DIAGNOSTIC
-	if (BUFQ_GET(&ss->buf_queue) != bp)
-		panic("ssstart(): dequeued wrong buf");
-#else
-	BUFQ_GET(&ss->buf_queue);
-#endif
-
 	return (0);
 }
 
@@ -534,7 +540,7 @@
 
 	while (1) {
 		SC_DEBUG(periph, SCSIPI_DB1, ("mustek_get_status: stat_cmd\n"));
-		error = scsipi_command(periph,
+		error = scsipi_command(periph, NULL,
 		    (struct scsipi_generic *) &cmd, sizeof(cmd),
 		    (u_char *) &data, sizeof(data), MUSTEK_RETRIES,
 		    5000, NULL, XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK);
Index: ss_scanjet.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/ss_scanjet.c,v
retrieving revision 1.31
diff -u -r1.31 ss_scanjet.c
--- ss_scanjet.c	27 Aug 2004 20:37:29 -0000	1.31
+++ ss_scanjet.c	7 Sep 2004 20:28:55 -0000
@@ -54,6 +54,7 @@
 #include <dev/scsipi/scsipi_all.h>
 #include <dev/scsipi/scsi_scanner.h>
 #include <dev/scsipi/scsiconf.h>
+#include <dev/scsipi/scsipi_base.h>
 #include <dev/scsipi/ssvar.h>
 
 #define SCANJET_RETRIES 4
@@ -260,6 +261,7 @@
 scanjet_read(struct ss_softc *ss, struct buf *bp)
 {
 	struct scsi_rw_scanner cmd;
+	struct scsipi_xfer *xs;
 	struct scsipi_periph *periph = ss->sc_periph;
 	int error;
 
@@ -278,34 +280,38 @@
 	/*
 	 * go ask the adapter to do all this for us
 	 */
-	error = scsipi_command(periph,
+	xs = scsipi_make_xs(periph,
+	    (struct scsipi_generic *) &cmd, sizeof(cmd),
+	    (u_char *) bp->b_data, bp->b_bcount,
+	    SCANJET_RETRIES, 100000, bp,
+	    XS_CTL_NOSLEEP | XS_CTL_ASYNC | XS_CTL_DATA_IN);
+	if (xs == NULL) {
+		/*
+		 * out of memory. Keep this buffer in the queue, and
+		 * retry later.
+		 */
+		callout_reset(&ss->sc_callout, hz / 2, ssrestart,
+		    periph);
+		return(0);
+	}
+#ifdef DIAGNOSTIC
+	if (BUFQ_GET(&ss->buf_queue) != bp)
+		panic("ssstart(): dequeued wrong buf");
+#else
+	BUFQ_GET(&ss->buf_queue);
+#endif
+	error = scsipi_command(periph, xs,
 	    (struct scsipi_generic *) &cmd, sizeof(cmd),
 	    (u_char *) bp->b_data, bp->b_bcount, SCANJET_RETRIES, 100000, bp,
 	    XS_CTL_NOSLEEP | XS_CTL_ASYNC | XS_CTL_DATA_IN);
 	if (error) {
 		printf("%s: not queued, error %d\n", ss->sc_dev.dv_xname,
 		    error);
-		if (error == ENOMEM) {
-			/*
-			 * out of memory. Keep this buffer in the queue, and
-			 * retry later.
-			 */
-			callout_reset(&ss->sc_callout, hz / 2, ssrestart,
-			    periph);
-			return(0);
-		}
 	} else {
 		ss->sio.scan_window_size -= bp->b_bcount;
 		if (ss->sio.scan_window_size < 0)
 			ss->sio.scan_window_size = 0;
 	}
-#ifdef DIAGNOSTIC
-	if (BUFQ_GET(&ss->buf_queue) != bp)
-		panic("ssstart(): dequeued wrong buf");
-#else
-	BUFQ_GET(&ss->buf_queue);
-#endif
-
 	return (0);
 }
 
@@ -326,7 +332,7 @@
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.opcode = WRITE;
 	_lto3b(size, cmd.len);
-	return (scsipi_command(ss->sc_periph,
+	return (scsipi_command(ss->sc_periph, NULL,
 	    (struct scsipi_generic *) &cmd,
 	    sizeof(cmd), (u_char *) buf, size, 0, 100000, NULL,
 	    flags | XS_CTL_DATA_OUT | XS_CTL_DATA_ONSTACK));
@@ -349,7 +355,7 @@
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.opcode = READ;
 	_lto3b(size, cmd.len);
-	return (scsipi_command(ss->sc_periph,
+	return (scsipi_command(ss->sc_periph, NULL,
 	    (struct scsipi_generic *) &cmd,
 	    sizeof(cmd), (u_char *) buf, size, 0, 100000, NULL,
 	    flags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK));
Index: st.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/st.c,v
retrieving revision 1.165
diff -u -r1.165 st.c
--- st.c	27 Aug 2004 20:37:29 -0000	1.165
+++ st.c	7 Sep 2004 20:28:58 -0000
@@ -79,6 +79,7 @@
 #include <dev/scsipi/scsi_all.h>
 #include <dev/scsipi/scsi_tape.h>
 #include <dev/scsipi/stvar.h>
+#include <dev/scsipi/scsipi_base.h>
 
 /* Defines for device specific stuff */
 #define DEF_FIXED_BSIZE  512
@@ -1162,6 +1163,7 @@
 	struct st_softc *st = (void *)periph->periph_dev;
 	struct buf *bp;
 	struct scsi_rw_tape cmd;
+	struct scsipi_xfer *xs;
 	int flags, error;
 
 	SC_DEBUG(periph, SCSIPI_DB2, ("ststart "));
@@ -1281,15 +1283,11 @@
 		/*
 		 * go ask the adapter to do all this for us
 		 */
-		error = scsipi_command(periph,
+		xs = scsipi_make_xs(periph,
 		    (struct scsipi_generic *)&cmd, sizeof(cmd),
 		    (u_char *)bp->b_data, bp->b_bcount,
 		    0, ST_IO_TIME, bp, flags);
-		if (__predict_false(error)) {
-			printf("%s: not queued, error %d\n",
-			    st->sc_dev.dv_xname, error);
-		}
-		if (__predict_false(error == ENOMEM)) {
+		if (__predict_false(xs == NULL)) {
 			/*
 			 * out of memory. Keep this buffer in the queue, and
 			 * retry later.
@@ -1298,12 +1296,28 @@
 			    periph);
 			return;
 		}
+		/*
+		 * need to dequeue de buffer before queuing the command,
+		 * because cdstart may be called recursively from the
+		 * HBA driver
+		 */
 #ifdef DIAGNOSTIC
 		if (BUFQ_GET(&st->buf_queue) != bp)
 			panic("ststart(): dequeued wrong buf");
 #else
 		BUFQ_GET(&st->buf_queue);
 #endif
+		error = scsipi_command(periph, xs,
+		    (struct scsipi_generic *)&cmd, sizeof(cmd),
+		    (u_char *)bp->b_data, bp->b_bcount,
+		    0, ST_IO_TIME, bp, flags);
+		if (__predict_false(error)) {
+			printf("%s: not queued, error %d\n",
+			    st->sc_dev.dv_xname, error);
+			bp->b_flags |= B_ERROR;
+			bp->b_error = ENOMEM;
+			biodone(bp);
+		}
 	} /* go back and see if we can cram more work in.. */
 }
 
@@ -1636,7 +1650,7 @@
 		    cmd.len);
 	} else
 		_lto3b(size, cmd.len);
-	return (scsipi_command(st->sc_periph,
+	return (scsipi_command(st->sc_periph, NULL,
 	    (struct scsipi_generic *)&cmd, sizeof(cmd),
 	    (u_char *)buf, size, 0, ST_IO_TIME, NULL, flags | XS_CTL_DATA_IN));
 }
@@ -1672,7 +1686,7 @@
 	if ((st->quirks & ST_Q_ERASE_NOIMM) == 0)
 		cmd.byte2 |= SE_IMMED;
 
-	return (scsipi_command(st->sc_periph,
+	return (scsipi_command(st->sc_periph, NULL,
 	    (struct scsipi_generic *)&cmd, sizeof(cmd),
 	    0, 0, ST_RETRIES, tmo, NULL, flags));
 }
@@ -1758,7 +1772,7 @@
 
 	st->flags &= ~ST_POSUPDATED;
 	st->last_ctl_resid = 0;
-	error = scsipi_command(st->sc_periph,
+	error = scsipi_command(st->sc_periph, NULL,
 	    (struct scsipi_generic *)&cmd, sizeof(cmd),
 	    0, 0, 0, ST_SPC_TIME, NULL, flags);
 
@@ -1828,7 +1842,7 @@
 		_lto3b(number, cmd.number);
 
 	/* XXX WE NEED TO BE ABLE TO GET A RESIDIUAL XXX */
-	error = scsipi_command(st->sc_periph,
+	error = scsipi_command(st->sc_periph, NULL,
 	    (struct scsipi_generic *)&cmd, sizeof(cmd),
 	    0, 0, 0, ST_IO_TIME * 4, NULL, flags);
 	if (error == 0 && st->fileno != -1) {
@@ -1902,7 +1916,7 @@
 		cmd.byte2 = SR_IMMED;
 	cmd.how = type;
 
-	error = scsipi_command(st->sc_periph,
+	error = scsipi_command(st->sc_periph, NULL,
 	    (struct scsipi_generic *)&cmd, sizeof(cmd),
 	    0, 0, ST_RETRIES, ST_SPC_TIME, NULL, flags);
 	if (error) {
@@ -1940,7 +1954,7 @@
 	cmd.opcode = REWIND;
 	cmd.byte2 = immediate;
 
-	error = scsipi_command(st->sc_periph,
+	error = scsipi_command(st->sc_periph, NULL,
 	    (struct scsipi_generic *)&cmd, sizeof(cmd), 0, 0, ST_RETRIES,
 	    immediate ? ST_CTL_TIME: ST_SPC_TIME, NULL, flags);
 	if (error) {
@@ -1991,7 +2005,7 @@
 	if (hard)
 		cmd.byte1 = 1;
 
-	error = scsipi_command(st->sc_periph,
+	error = scsipi_command(st->sc_periph, NULL,
 	    (struct scsipi_generic *)&cmd, sizeof(cmd), (u_char *)&posdata,
 	    sizeof(posdata), ST_RETRIES, ST_CTL_TIME, NULL,
 	    XS_CTL_SILENT | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK);
@@ -2032,7 +2046,7 @@
 	if (hard)
 		cmd.byte2 = 1 << 2;
 	_lto4b(*blkptr, cmd.blkaddr);
-	error = scsipi_command(st->sc_periph,
+	error = scsipi_command(st->sc_periph, NULL,
 		(struct scsipi_generic *)&cmd, sizeof(cmd),
 		NULL, 0, ST_RETRIES, ST_SPC_TIME, NULL, 0);
 	/*
Index: st_scsi.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/st_scsi.c,v
retrieving revision 1.11
diff -u -r1.11 st_scsi.c
--- st_scsi.c	21 Aug 2004 22:16:07 -0000	1.11
+++ st_scsi.c	7 Sep 2004 20:28:58 -0000
@@ -155,7 +155,7 @@
 	/*
 	 * do the command, update the global values
 	 */
-	error = scsipi_command(periph, (struct scsipi_generic *)&cmd,
+	error = scsipi_command(periph, NULL, (struct scsipi_generic *)&cmd,
 	    sizeof(cmd), (u_char *)&block_limits, sizeof(block_limits),
 	    ST_RETRIES, ST_CTL_TIME, NULL,
 	    flags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK);

--M9NhX3UHpAaciwkO--