diff options
author | David Gwynne <dlg@cvs.openbsd.org> | 2009-12-26 09:12:56 +0000 |
---|---|---|
committer | David Gwynne <dlg@cvs.openbsd.org> | 2009-12-26 09:12:56 +0000 |
commit | 991bdbbe17f0440b449c7a74b1f5781828223e1b (patch) | |
tree | da2b58caa1f53070d804dfe3319c52d8f742b444 | |
parent | f911a54d4cdbeb7dabc209d044d85387474b9054 (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.c | 229 |
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); } |