diff options
Diffstat (limited to 'sys/scsi/ss.c')
-rw-r--r-- | sys/scsi/ss.c | 186 |
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); } /* |