summaryrefslogtreecommitdiff
path: root/sys/scsi/ss.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/scsi/ss.c')
-rw-r--r--sys/scsi/ss.c186
1 files changed, 135 insertions, 51 deletions
diff --git a/sys/scsi/ss.c b/sys/scsi/ss.c
index 40a8cedffa7..e1c09979613 100644
--- a/sys/scsi/ss.c
+++ b/sys/scsi/ss.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ss.c,v 1.64 2010/01/01 06:30:27 dlg Exp $ */
+/* $OpenBSD: ss.c,v 1.65 2010/01/02 23:28:51 dlg Exp $ */
/* $NetBSD: ss.c,v 1.10 1996/05/05 19:52:55 christos Exp $ */
/*
@@ -113,6 +113,7 @@ struct quirkdata ss_gen_quirks = {
void ssstrategy(struct buf *);
void ssstart(void *);
+void ssdone(struct scsi_xfer *);
void ssminphys(struct buf *);
void ss_identify_scanner(struct ss_softc *, struct scsi_inquiry_data*);
@@ -353,6 +354,11 @@ ssattach(parent, self, aux)
/* XXX fill in the rest of the scan_io struct by calling the
compute_sizes routine */
+ mtx_init(&ss->queue_mtx, IPL_BIO);
+ mtx_init(&ss->start_mtx, IPL_BIO);
+
+ timeout_set(&ss->timeout, ssstart, ss);
+
/*
* Set up the buf queue for this device
*/
@@ -559,7 +565,6 @@ ssstrategy(bp)
struct buf *bp;
{
struct ss_softc *ss = ss_cd.cd_devs[SSUNIT(bp->b_dev)];
- struct buf *dp;
int s;
SC_DEBUG(ss->sc_link, SDEV_DB2, ("ssstrategy: %ld bytes @ blk %d\n",
@@ -574,18 +579,12 @@ ssstrategy(bp)
if (bp->b_bcount == 0)
goto done;
- s = splbio();
-
/*
* Place it in the queue of activities for this scanner
* at the end (a bit silly because we only have on user..)
* (but it could fork() or dup())
*/
- dp = &ss->buf_queue;
- bp->b_actf = NULL;
- bp->b_actb = dp->b_actb;
- *dp->b_actb = bp;
- dp->b_actb = &bp->b_actf;
+ ss_buf_enqueue(ss, bp);
/*
* Tell the device to get going on the transfer if it's
@@ -594,7 +593,6 @@ ssstrategy(bp)
*/
ssstart(ss);
- splx(s);
return;
done:
@@ -607,6 +605,48 @@ done:
splx(s);
}
+void
+ss_buf_enqueue(struct ss_softc *ss, struct buf *bp)
+{
+ struct buf *dp;
+
+ mtx_enter(&ss->queue_mtx);
+ dp = &ss->buf_queue;
+ bp->b_actf = NULL;
+ bp->b_actb = dp->b_actb;
+ *dp->b_actb = bp;
+ dp->b_actb = &bp->b_actf;
+ mtx_leave(&ss->queue_mtx);
+}
+
+struct buf *
+ss_buf_dequeue(struct ss_softc *ss)
+{
+ struct buf *bp;
+
+ mtx_enter(&ss->queue_mtx);
+ bp = ss->buf_queue.b_actf;
+ if (bp != NULL)
+ ss->buf_queue.b_actf = bp->b_actf;
+ if (ss->buf_queue.b_actf == NULL)
+ ss->buf_queue.b_actb = &ss->buf_queue.b_actf;
+ mtx_leave(&ss->queue_mtx);
+
+ return (bp);
+}
+
+void
+ss_buf_requeue(struct ss_softc *ss, struct buf *bp)
+{
+ mtx_enter(&ss->queue_mtx);
+ bp->b_actf = ss->buf_queue.b_actf;
+ ss->buf_queue.b_actf = bp;
+ if (bp->b_actf == NULL)
+ ss->buf_queue.b_actb = &bp->b_actf;
+ mtx_leave(&ss->queue_mtx);
+}
+
+
/*
* ssstart looks to see if there is a buf waiting for the device
* and that the device is not already busy. If both are true,
@@ -627,55 +667,99 @@ ssstart(v)
{
struct ss_softc *ss = v;
struct scsi_link *sc_link = ss->sc_link;
- struct buf *bp, *dp;
- struct scsi_r_scanner read_cmd;
- int flags;
+ struct scsi_xfer *xs;
+ struct buf *bp;
+ struct scsi_r_scanner *cdb;
SC_DEBUG(sc_link, SDEV_DB2, ("ssstart\n"));
- /*
- * See if there is a buf to do and we are not already
- * doing one
- */
- while (sc_link->openings > 0) {
- /* if a special awaits, let it proceed first */
- mtx_enter(&sc_link->mtx);
- if (ISSET(sc_link->state, SDEV_S_WAITING)) {
- atomic_clearbits_int(&sc_link->state, SDEV_S_WAITING);
- wakeup((caddr_t)sc_link);
- mtx_leave(&sc_link->mtx);
- return;
- }
- mtx_leave(&sc_link->mtx);
- /*
- * See if there is a buf with work for us to do..
- */
- dp = &ss->buf_queue;
- if ((bp = dp->b_actf) == NULL)
- return;
- if ((dp = bp->b_actf) != NULL)
- dp->b_actb = bp->b_actb;
- else
- ss->buf_queue.b_actb = bp->b_actb;
- *bp->b_actb = dp;
+ mtx_enter(&ss->start_mtx);
+ if (ISSET(ss->flags, SSF_STARTING)) {
+ mtx_leave(&ss->start_mtx);
+ return;
+ }
+
+ SET(ss->flags, SSF_STARTING);
+ mtx_leave(&ss->start_mtx);
+
+ CLR(ss->flags, SSF_WAITING);
+ while (!ISSET(ss->flags, SSF_WAITING) &&
+ (bp = ss_buf_dequeue(ss)) != NULL) {
+
+ xs = scsi_xs_get(sc_link, SCSI_NOSLEEP);
+ if (xs == NULL)
+ break;
if (ss->special.read) {
- (ss->special.read)(ss, bp);
+ (ss->special.read)(ss, xs, bp);
} else {
- /* generic scsi2 scanner read */
- bzero(&read_cmd, sizeof(read_cmd));
- read_cmd.opcode = READ_BIG;
- _lto3b(bp->b_bcount, read_cmd.len);
- flags = SCSI_DATA_IN;
- /*
- * go ask the adapter to do all this for us
- */
- if (scsi_scsi_cmd(sc_link, (struct scsi_generic *)
- &read_cmd, sizeof(read_cmd), (u_char *) bp->b_data,
- bp->b_bcount, 0, 100000, bp, flags | SCSI_NOSLEEP))
- printf("%s: not queued\n", ss->sc_dev.dv_xname);
+ cdb = (struct scsi_r_scanner *)xs->cmd;
+ xs->cmdlen = sizeof(*cdb);
+
+ cdb->opcode = READ_BIG;
+ _lto3b(bp->b_bcount, cdb->len);
+
+ xs->data = bp->b_data;
+ xs->datalen = bp->b_bcount;
+ xs->flags |= SCSI_DATA_IN;
+ xs->retries = 0;
+ xs->timeout = 100000;
+ xs->done = ssdone;
+ xs->cookie = bp;
+
+ scsi_xs_exec(xs);
}
}
+ mtx_enter(&ss->start_mtx);
+ CLR(ss->flags, SSF_STARTING);
+ mtx_leave(&ss->start_mtx);
+}
+
+void
+ssdone(struct scsi_xfer *xs)
+{
+ struct ss_softc *ss = xs->sc_link->device_softc;
+ struct buf *bp = xs->cookie;
+
+ splassert(IPL_BIO);
+
+ switch (xs->error) {
+ case XS_NOERROR:
+ bp->b_error = 0;
+ bp->b_resid = xs->resid;
+ break;
+
+ case XS_NO_CCB:
+ /* The adapter is busy, requeue the buf and try it later. */
+ ss_buf_requeue(ss, bp);
+ scsi_xs_put(xs);
+ SET(ss->flags, SSF_WAITING); /* break out of cdstart loop */
+ timeout_add(&ss->timeout, 1);
+ return;
+
+ case XS_SENSE:
+ case XS_SHORTSENSE:
+ if (scsi_interpret_sense(xs) != ERESTART)
+ xs->retries = 0;
+
+ /* FALLTHROUGH */
+ case XS_BUSY:
+ case XS_TIMEOUT:
+ if (xs->retries--) {
+ scsi_xs_exec(xs);
+ return;
+ }
+
+ /* FALLTHROUGH */
+ default:
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ bp->b_resid = bp->b_bcount;
+ break;
+ }
+
+ biodone(bp);
+ scsi_xs_put(xs);
}
/*