summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Gwynne <dlg@cvs.openbsd.org>2009-12-26 09:12:56 +0000
committerDavid Gwynne <dlg@cvs.openbsd.org>2009-12-26 09:12:56 +0000
commit991bdbbe17f0440b449c7a74b1f5781828223e1b (patch)
treeda2b58caa1f53070d804dfe3319c52d8f742b444
parentf911a54d4cdbeb7dabc209d044d85387474b9054 (diff)
move st over to the new midlayer.
much testing and debugging from krw@ his diff was a nice christmas present
-rw-r--r--sys/scsi/st.c229
1 files changed, 158 insertions, 71 deletions
diff --git a/sys/scsi/st.c b/sys/scsi/st.c
index 555f8a7e911..81389185f9f 100644
--- a/sys/scsi/st.c
+++ b/sys/scsi/st.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: st.c,v 1.87 2009/10/13 19:33:19 pirofti Exp $ */
+/* $OpenBSD: st.c,v 1.88 2009/12/26 09:12:55 dlg Exp $ */
/* $NetBSD: st.c,v 1.71 1997/02/21 23:03:49 thorpej Exp $ */
/*
@@ -218,6 +218,8 @@ struct st_softc {
struct buf buf_queue; /* the queue of pending IO operations */
struct timeout sc_timeout;
+ struct mutex queue_mtx;
+ struct mutex start_mtx;
};
@@ -234,7 +236,10 @@ int st_mount_tape(dev_t, int);
void st_unmount(struct st_softc *, int, int);
int st_decide_mode(struct st_softc *, int);
void ststart(void *);
-void strestart(void *);
+struct buf *st_buf_dequeue(struct st_softc *);
+void st_buf_enqueue(struct st_softc *, struct buf *);
+void st_buf_requeue(struct st_softc *, struct buf *);
+void st_buf_done(struct scsi_xfer *);
int st_read(struct st_softc *, char *, int, int);
int st_read_block_limits(struct st_softc *, int);
int st_mode_sense(struct st_softc *, int);
@@ -281,6 +286,8 @@ struct scsi_device st_switch = {
#define ST_2FM_AT_EOD 0x0400 /* write 2 file marks at EOD */
#define ST_MOUNTED 0x0800 /* Device is presently mounted */
#define ST_DONTBUFFER 0x1000 /* Disable buffering/caching */
+#define ST_WAITING 0x2000
+#define ST_STARTING 0x4000
#define ST_PER_ACTION (ST_AT_FILEMARK | ST_EIO_PENDING | ST_BLANK_READ)
#define ST_PER_MOUNT (ST_INFO_VALID | ST_BLOCK_SET | ST_WRITTEN | \
@@ -333,8 +340,11 @@ stattach(struct device *parent, struct device *self, void *aux)
st_identify_drive(st, sa->sa_inqbuf);
printf("\n");
- timeout_set(&st->sc_timeout, strestart, st);
+ mtx_init(&st->queue_mtx, IPL_BIO);
+ mtx_init(&st->start_mtx, IPL_BIO);
+ timeout_set(&st->sc_timeout, ststart, st);
+
/*
* Set up the buf queue for this device
*/
@@ -831,7 +841,6 @@ ststrategy(struct buf *bp)
{
struct scsi_link *sc_link;
struct st_softc *st;
- struct buf *dp;
int error, s;
st = stlookup(STUNIT(bp->b_dev));
@@ -876,18 +885,13 @@ ststrategy(struct buf *bp)
bp->b_error = EIO;
goto bad;
}
- s = splbio();
/*
* Place it in the queue of activities for this tape
* at the end (a bit silly because we only have on user..
* (but it could fork()))
*/
- dp = &st->buf_queue;
- bp->b_actf = NULL;
- bp->b_actb = dp->b_actb;
- *dp->b_actb = bp;
- dp->b_actb = &bp->b_actf;
+ st_buf_enqueue(st, bp);
/*
* Tell the device to get going on the transfer if it's
@@ -896,7 +900,6 @@ ststrategy(struct buf *bp)
*/
ststart(st);
- splx(s);
device_unref(&st->sc_dev);
return;
bad:
@@ -912,6 +915,48 @@ done:
splx(s);
}
+void
+st_buf_enqueue(struct st_softc *st, struct buf *bp)
+{
+ struct buf *dp;
+
+ mtx_enter(&st->queue_mtx);
+ dp = &st->buf_queue;
+ bp->b_actf = NULL;
+ bp->b_actb = dp->b_actb;
+ *dp->b_actb = bp;
+ dp->b_actb = &bp->b_actf;
+ mtx_leave(&st->queue_mtx);
+}
+
+struct buf *
+st_buf_dequeue(struct st_softc *st)
+{
+ struct buf *bp;
+
+ mtx_enter(&st->queue_mtx);
+ bp = st->buf_queue.b_actf;
+ if (bp != NULL)
+ st->buf_queue.b_actf = bp->b_actf;
+ if (st->buf_queue.b_actf == NULL)
+ st->buf_queue.b_actb = &st->buf_queue.b_actf;
+ mtx_leave(&st->queue_mtx);
+
+ return (bp);
+}
+
+void
+st_buf_requeue(struct st_softc *st, struct buf *bp)
+{
+ mtx_enter(&st->queue_mtx);
+ bp->b_actf = st->buf_queue.b_actf;
+ st->buf_queue.b_actf = bp;
+ if (bp->b_actf == NULL)
+ st->buf_queue.b_actb = &bp->b_actf;
+ mtx_leave(&st->queue_mtx);
+}
+
+
/*
* ststart looks to see if there is a buf waiting for the device
* and that the device is not already busy. If both are true,
@@ -924,44 +969,40 @@ done:
* This routine is also called after other non-queued requests
* have been made of the scsi driver, to ensure that the queue
* continues to be drained.
- * ststart() is called at splbio from ststrategy, strestart and scsi_done()
+ * ststart() is called at splbio from ststrategy and scsi_done()
*/
void
ststart(void *v)
{
struct st_softc *st = v;
struct scsi_link *sc_link = st->sc_link;
- struct buf *bp, *dp;
- struct scsi_rw_tape cmd;
- int flags, error;
+ struct buf *bp;
+ struct scsi_rw_tape *cmd;
+ struct scsi_xfer *xs;
+ int s;
SC_DEBUG(sc_link, SDEV_DB2, ("ststart\n"));
if (st->flags & ST_DYING)
return;
- splassert(IPL_BIO);
-
/*
* 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 */
- if (sc_link->flags & SDEV_WAITING) {
- sc_link->flags &= ~SDEV_WAITING;
- wakeup((caddr_t)sc_link);
- return;
- }
- dp = &st->buf_queue;
- if ((bp = dp->b_actf) == NULL)
- return;
- if ((dp = bp->b_actf) != NULL)
- dp->b_actb = bp->b_actb;
- else
- st->buf_queue.b_actb = bp->b_actb;
- *bp->b_actb = dp;
+ mtx_enter(&st->start_mtx);
+ if (ISSET(st->flags, ST_STARTING)) {
+ mtx_leave(&st->start_mtx);
+ return;
+ }
+
+ SET(st->flags, ST_STARTING);
+ mtx_leave(&st->start_mtx);
+
+ CLR(st->flags, ST_WAITING);
+ while (!ISSET(st->flags, ST_WAITING) &&
+ (bp = st_buf_dequeue(st)) != NULL) {
/*
* if the device has been unmounted by the user
@@ -974,9 +1015,18 @@ ststart(void *v)
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
bp->b_error = EIO;
+ s = splbio();
biodone(bp);
+ splx(s);
continue;
}
+
+ xs = scsi_xs_get(sc_link, SCSI_NOSLEEP);
+ if (xs == NULL) {
+ st_buf_requeue(st, bp);
+ break;
+ }
+
/*
* only FIXEDBLOCK devices have pending operations
*/
@@ -997,7 +1047,9 @@ ststart(void *v)
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
bp->b_error = EIO;
+ s = splbio();
biodone(bp);
+ splx(s);
continue;
}
} else {
@@ -1005,7 +1057,9 @@ ststart(void *v)
bp->b_error = 0;
bp->b_flags &= ~B_ERROR;
st->flags &= ~ST_AT_FILEMARK;
+ s = splbio();
biodone(bp);
+ splx(s);
continue; /* seek more work */
}
}
@@ -1018,7 +1072,9 @@ ststart(void *v)
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
st->flags &= ~ST_EIO_PENDING;
+ s = splbio();
biodone(bp);
+ splx(s);
continue; /* seek more work */
}
}
@@ -1026,15 +1082,16 @@ ststart(void *v)
/*
* Fill out the scsi command
*/
- bzero(&cmd, sizeof(cmd));
+ cmd = (struct scsi_rw_tape *)xs->cmd;
+ bzero(cmd, sizeof(*cmd));
if ((bp->b_flags & B_READ) == B_WRITE) {
- cmd.opcode = WRITE;
+ cmd->opcode = WRITE;
st->flags &= ~ST_FM_WRITTEN;
st->flags |= ST_WRITTEN;
- flags = SCSI_DATA_OUT;
+ xs->flags |= SCSI_DATA_OUT;
} else {
- cmd.opcode = READ;
- flags = SCSI_DATA_IN;
+ cmd->opcode = READ;
+ xs->flags |= SCSI_DATA_IN;
}
/*
@@ -1042,51 +1099,83 @@ ststart(void *v)
* block count instead of the length.
*/
if (st->flags & ST_FIXEDBLOCKS) {
- cmd.byte2 |= SRW_FIXED;
- _lto3b(bp->b_bcount / st->blksize, cmd.len);
+ cmd->byte2 |= SRW_FIXED;
+ _lto3b(bp->b_bcount / st->blksize, cmd->len);
} else
- _lto3b(bp->b_bcount, cmd.len);
+ _lto3b(bp->b_bcount, cmd->len);
if (st->media_blkno != -1) {
/* Update block count now, errors will set it to -1. */
if (st->flags & ST_FIXEDBLOCKS)
- st->media_blkno += _3btol(cmd.len);
- else if (cmd.len != 0)
+ st->media_blkno += _3btol(cmd->len);
+ else if (cmd->len != 0)
st->media_blkno++;
}
+ xs->cmdlen = sizeof(*cmd);
+ xs->timeout = ST_IO_TIME;
+ xs->data = bp->b_data;
+ xs->datalen = bp->b_bcount;
+ xs->done = st_buf_done;
+ xs->cookie = bp;
+
/*
* go ask the adapter to do all this for us
*/
- error = scsi_scsi_cmd(sc_link, (struct scsi_generic *) &cmd,
- sizeof(cmd), (u_char *) bp->b_data, bp->b_bcount, 0,
- ST_IO_TIME, bp, flags | SCSI_NOSLEEP);
- switch (error) {
- case 0:
- timeout_del(&st->sc_timeout);
- break;
- case EAGAIN:
- /*
- * The device can't start another i/o. Try again later.
- */
- dp->b_actf = bp;
- timeout_add(&st->sc_timeout, 1);
- return;
- default:
- printf("%s: not queued\n", st->sc_dev.dv_xname);
- break;
- }
+ scsi_xs_exec(xs);
} /* go back and see if we can cram more work in.. */
+
+ mtx_enter(&st->start_mtx);
+ CLR(st->flags, ST_STARTING);
+ mtx_leave(&st->start_mtx);
}
void
-strestart(void *v)
+st_buf_done(struct scsi_xfer *xs)
{
- int s;
+ struct st_softc *st = xs->sc_link->device_softc;
+ struct buf *bp = xs->cookie;
- s = splbio();
- ststart(v);
- splx(s);
+ 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. */
+ st_buf_requeue(st, bp);
+ scsi_xs_put(xs);
+ SET(st->flags, ST_WAITING); /* break out of cdstart loop */
+ timeout_add(&st->sc_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);
+ ststart(st); /* restart io */
}
void
@@ -2115,16 +2204,14 @@ stdump(dev_t dev, daddr64_t blkno, caddr_t va, size_t size)
void
st_kill_buffers(struct st_softc *st)
{
- struct buf *dp, *bp;
+ struct buf *bp;
int s;
- s = splbio();
- for (dp = &st->buf_queue; (bp = dp->b_actf) != NULL; ) {
- dp->b_actf = bp->b_actf;
-
+ while ((bp = st_buf_dequeue(st)) != NULL) {
bp->b_error = ENXIO;
bp->b_flags |= B_ERROR;
+ s = splbio();
biodone(bp);
+ splx(s);
}
- splx(s);
}