diff options
author | David Gwynne <dlg@cvs.openbsd.org> | 2010-06-23 03:46:26 +0000 |
---|---|---|
committer | David Gwynne <dlg@cvs.openbsd.org> | 2010-06-23 03:46:26 +0000 |
commit | 0e499d22151d61ae999b6cfa20c3ee6e165fb3c5 (patch) | |
tree | cbdc31d29b0d1c49a1322add778910940981c98e /sys/dev/ic/ami.c | |
parent | f7a3ca0727d1eb79523002500ee1e3d56c9ddd38 (diff) |
switch ami over to using mutexes instead of splbio to protect itself. this
protects the ccb runqueues and the mailbox with a single mutex.
the biggest change as a consequence of this is a rewrite of the polled
command code. it now forces the polled command onto the chip ahead of the
pending runqueue, and simply runs the interrupt handler until the ccb
completes.
tested by sthen@
Diffstat (limited to 'sys/dev/ic/ami.c')
-rw-r--r-- | sys/dev/ic/ami.c | 271 |
1 files changed, 89 insertions, 182 deletions
diff --git a/sys/dev/ic/ami.c b/sys/dev/ic/ami.c index 614df98ce38..3038dc6ae72 100644 --- a/sys/dev/ic/ami.c +++ b/sys/dev/ic/ami.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ami.c,v 1.207 2010/06/21 11:43:38 dlg Exp $ */ +/* $OpenBSD: ami.c,v 1.208 2010/06/23 03:46:25 dlg Exp $ */ /* * Copyright (c) 2001 Michael Shalayeff @@ -114,8 +114,6 @@ struct scsi_device ami_raw_dev = { NULL, NULL, NULL, NULL }; -void ami_remove_runq(struct ami_ccb *); -void ami_insert_runq(struct ami_ccb *); struct ami_ccb *ami_get_ccb(struct ami_softc *); void ami_put_ccb(struct ami_softc *, struct ami_ccb *); @@ -131,7 +129,6 @@ int ami_alloc_ccbs(struct ami_softc *, int); int ami_poll(struct ami_softc *, struct ami_ccb *); void ami_start(struct ami_softc *, struct ami_ccb *); void ami_complete(struct ami_softc *, struct ami_ccb *, int); -int ami_done(struct ami_softc *, int, int); void ami_runqueue_tick(void *); void ami_runqueue(struct ami_softc *); @@ -141,7 +138,6 @@ void ami_done_xs(struct ami_softc *, struct ami_ccb *); void ami_done_pt(struct ami_softc *, struct ami_ccb *); void ami_done_flush(struct ami_softc *, struct ami_ccb *); void ami_done_sysflush(struct ami_softc *, struct ami_ccb *); -void ami_stimeout(void *); void ami_done_dummy(struct ami_softc *, struct ami_ccb *); void ami_done_ioctl(struct ami_softc *, struct ami_ccb *); @@ -180,24 +176,6 @@ void ami_refresh_sensors(void *); #define DEVNAME(_s) ((_s)->sc_dev.dv_xname) -void -ami_remove_runq(struct ami_ccb *ccb) -{ - splassert(IPL_BIO); - - TAILQ_REMOVE(&ccb->ccb_sc->sc_ccb_runq, ccb, ccb_link); - if (ccb->ccb_sc->sc_drainio && TAILQ_EMPTY(&ccb->ccb_sc->sc_ccb_runq)) - wakeup(ccb->ccb_sc); -} - -void -ami_insert_runq(struct ami_ccb *ccb) -{ - splassert(IPL_BIO); - - TAILQ_INSERT_TAIL(&ccb->ccb_sc->sc_ccb_runq, ccb, ccb_link); -} - struct ami_ccb * ami_get_ccb(struct ami_softc *sc) { @@ -404,7 +382,8 @@ ami_attach(struct ami_softc *sc) struct ami_fc_prodinfo *pi; const char *p; paddr_t pa; - int s; + + mtx_init(&sc->sc_cmd_mtx, IPL_BIO); am = ami_allocmem(sc, NBPG); if (am == NULL) { @@ -431,8 +410,6 @@ ami_attach(struct ami_softc *sc) (sc->sc_init)(sc); - s = splbio(); - /* try FC inquiry first */ cmd->acc_cmd = AMI_FCOP; cmd->acc_io.aio_channel = AMI_FC_EINQ3; @@ -479,7 +456,6 @@ ami_attach(struct ami_softc *sc) cmd->acc_io.aio_param = 0; cmd->acc_io.aio_data = pa; if (ami_poll(sc, &iccb) != 0) { - splx(s); printf(": cannot do inquiry\n"); goto free_mbox; } @@ -525,8 +501,6 @@ ami_attach(struct ami_softc *sc) sc->sc_link.openings = sc->sc_maxcmds; } - splx(s); - ami_freemem(sc, am); if (ami_alloc_ccbs(sc, AMI_MAXCMDS + 1) != 0) { @@ -962,76 +936,66 @@ ami_schwartz_poll(struct ami_softc *sc, struct ami_iocmd *mbox) void ami_start_xs(struct ami_softc *sc, struct ami_ccb *ccb, struct scsi_xfer *xs) { - timeout_set(&xs->stimeout, ami_stimeout, ccb); - - if (xs->flags & SCSI_POLL) { + if (xs->flags & SCSI_POLL) ami_complete(sc, ccb, xs->timeout); - return; - } - - /* XXX way wrong, this timeout needs to be set later */ - timeout_add_sec(&xs->stimeout, 61); - ami_start(sc, ccb); + else + ami_start(sc, ccb); } void ami_start(struct ami_softc *sc, struct ami_ccb *ccb) { - int s; - - s = splbio(); + mtx_enter(&sc->sc_cmd_mtx); ccb->ccb_state = AMI_CCB_PREQUEUED; TAILQ_INSERT_TAIL(&sc->sc_ccb_preq, ccb, ccb_link); + mtx_leave(&sc->sc_cmd_mtx); + ami_runqueue(sc); - splx(s); } void ami_runqueue_tick(void *arg) { - struct ami_softc *sc = arg; - int s; - - s = splbio(); - ami_runqueue(sc); - splx(s); + ami_runqueue(arg); } void ami_runqueue(struct ami_softc *sc) { struct ami_ccb *ccb; + int add = 0; - splassert(IPL_BIO); - - if (sc->sc_drainio) - return; + mtx_enter(&sc->sc_cmd_mtx); + if (!sc->sc_drainio) { + while ((ccb = TAILQ_FIRST(&sc->sc_ccb_preq)) != NULL) { + if (sc->sc_exec(sc, &ccb->ccb_cmd) != 0) { + add = 1; + break; + } - while ((ccb = TAILQ_FIRST(&sc->sc_ccb_preq)) != NULL) { - if (sc->sc_exec(sc, &ccb->ccb_cmd) != 0) { - timeout_add(&sc->sc_run_tmo, 1); - break; + TAILQ_REMOVE(&sc->sc_ccb_preq, ccb, ccb_link); + ccb->ccb_state = AMI_CCB_QUEUED; + TAILQ_INSERT_TAIL(&sc->sc_ccb_runq, ccb, ccb_link); } - - TAILQ_REMOVE(&sc->sc_ccb_preq, ccb, ccb_link); - ccb->ccb_state = AMI_CCB_QUEUED; - ami_insert_runq(ccb); } + mtx_leave(&sc->sc_cmd_mtx); + + if (add) + timeout_add(&sc->sc_run_tmo, 1); } int ami_poll(struct ami_softc *sc, struct ami_ccb *ccb) { int error; - int s; - s = splbio(); + mtx_enter(&sc->sc_cmd_mtx); error = sc->sc_poll(sc, &ccb->ccb_cmd); if (error == -1) ccb->ccb_flags |= AMI_CCB_F_ERR; + mtx_leave(&sc->sc_cmd_mtx); ccb->ccb_done(sc, ccb); - splx(s); return (error); } @@ -1039,27 +1003,36 @@ ami_poll(struct ami_softc *sc, struct ami_ccb *ccb) void ami_complete(struct ami_softc *sc, struct ami_ccb *ccb, int timeout) { - struct ami_iocmd mbox; - int i = 0, j, done = 0; - int s, ready; + void (*done)(struct ami_softc *, struct ami_ccb *); + int ready; + int i; + int s; - s = splbio(); + done = ccb->ccb_done; + ccb->ccb_done = ami_done_dummy; /* * since exec will return if the mbox is busy we have to busy wait * ourselves. once its in, jam it into the runq. */ + mtx_enter(&sc->sc_cmd_mtx); while (i < AMI_MAX_BUSYWAIT) { if (sc->sc_exec(sc, &ccb->ccb_cmd) == 0) { ccb->ccb_state = AMI_CCB_QUEUED; - ami_insert_runq(ccb); + TAILQ_INSERT_TAIL(&sc->sc_ccb_runq, ccb, ccb_link); break; } DELAY(1000); i++; } - if (ccb->ccb_state != AMI_CCB_QUEUED) - goto err; + ready = (ccb->ccb_state == AMI_CCB_QUEUED); + mtx_leave(&sc->sc_cmd_mtx); + + if (!ready) { + ccb->ccb_flags |= AMI_CCB_F_ERR; + ccb->ccb_state = AMI_CCB_READY; + goto done; + } /* * Override timeout for PERC3. The first command triggers a chip @@ -1068,97 +1041,27 @@ ami_complete(struct ami_softc *sc, struct ami_ccb *ccb, int timeout) * firmware to be up again. After the first two commands the * timeouts are as expected. */ - i = 0; - while (i < 30000 /* timeout */) { - if (sc->sc_done(sc, &mbox) != 0) { - for (j = 0; j < mbox.acc_nstat; j++) { - ready = mbox.acc_cmplidl[j]; - ami_done(sc, ready, mbox.acc_status); - if (ready == ccb->ccb_cmd.acc_id) - done = 1; - } - if (done) - break; - } + timeout = MAX(30000, timeout); /* timeout */ - DELAY(1000); - i++; - } - if (!done) { - printf("%s: timeout ccb %d\n", DEVNAME(sc), - ccb->ccb_cmd.acc_id); - ami_remove_runq(ccb); - goto err; - } - - /* start the runqueue again */ - ami_runqueue(sc); - - splx(s); - - return; - -err: - ccb->ccb_flags |= AMI_CCB_F_ERR; - ccb->ccb_state = AMI_CCB_READY; - ccb->ccb_done(sc, ccb); - splx(s); -} - -void -ami_stimeout(void *v) -{ - struct ami_ccb *ccb = v; - struct ami_softc *sc = ccb->ccb_sc; - struct ami_iocmd *cmd = &ccb->ccb_cmd; - int s; - - s = splbio(); - switch (ccb->ccb_state) { - case AMI_CCB_PREQUEUED: - /* command never ran, cleanup is easy */ - TAILQ_REMOVE(&sc->sc_ccb_preq, ccb, ccb_link); - ccb->ccb_flags |= AMI_CCB_F_ERR; - ccb->ccb_done(sc, ccb); - break; - - case AMI_CCB_QUEUED: - /* - * ccb has taken more than a minute to finish. we can't take - * it off the hardware in case it finishes later, but we can - * warn the user to look at what is happening. - */ - AMI_DPRINTF(AMI_D_CMD, ("%s: stimeout ccb %d, check volume " - "state\n", DEVNAME(sc), cmd->acc_id)); - break; - - default: - panic("%s: ami_stimeout(%d) botch", DEVNAME(sc), cmd->acc_id); - } - - splx(s); -} - -int -ami_done(struct ami_softc *sc, int idx, int status) -{ - struct ami_ccb *ccb = &sc->sc_ccbs[idx - 1]; - - AMI_DPRINTF(AMI_D_CMD, ("done(%d) ", ccb->ccb_cmd.acc_id)); + while (ccb->ccb_state == AMI_CCB_QUEUED) { + s = splbio(); /* interrupt handlers are called at their IPL */ + ready = ami_intr(sc); + splx(s); + + if (ready == 0) { + if (timeout-- == 0) { + /* XXX */ + printf("%s: timeout\n", DEVNAME(sc)); + return; + } - if (ccb->ccb_state != AMI_CCB_QUEUED) { - printf("%s: unqueued ccb %d ready, state = %d\n", - DEVNAME(sc), idx, ccb->ccb_state); - return (1); + delay(1000); + continue; + } } - ccb->ccb_state = AMI_CCB_READY; - ccb->ccb_status = status; - ami_remove_runq(ccb); - - ccb->ccb_done(sc, ccb); - - return (0); +done: + done(sc, ccb); } void @@ -1182,7 +1085,6 @@ ami_done_pt(struct ami_softc *sc, struct ami_ccb *ccb) bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap); } - timeout_del(&xs->stimeout); xs->resid = 0; if (ccb->ccb_flags & AMI_CCB_F_ERR) @@ -1220,7 +1122,6 @@ ami_done_xs(struct ami_softc *sc, struct ami_ccb *ccb) bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap); } - timeout_del(&xs->stimeout); xs->resid = 0; if (ccb->ccb_flags & AMI_CCB_F_ERR) @@ -1236,7 +1137,6 @@ ami_done_flush(struct ami_softc *sc, struct ami_ccb *ccb) struct scsi_xfer *xs = ccb->ccb_xs; struct ami_iocmd *cmd = &ccb->ccb_cmd; - timeout_del(&xs->stimeout); if (ccb->ccb_flags & AMI_CCB_F_ERR) { xs->error = XS_DRIVER_STUFFUP; xs->resid = 0; @@ -1258,7 +1158,6 @@ ami_done_sysflush(struct ami_softc *sc, struct ami_ccb *ccb) { struct scsi_xfer *xs = ccb->ccb_xs; - timeout_del(&xs->stimeout); xs->resid = 0; if (ccb->ccb_flags & AMI_CCB_F_ERR) xs->error = XS_DRIVER_STUFFUP; @@ -1627,28 +1526,36 @@ ami_scsi_cmd(struct scsi_xfer *xs) int ami_intr(void *v) { - struct ami_softc *sc = v; struct ami_iocmd mbox; + struct ami_softc *sc = v; + struct ami_ccb *ccb; int i, rv = 0, ready; - splassert(IPL_BIO); - - if (TAILQ_EMPTY(&sc->sc_ccb_runq)) - return (0); - - AMI_DPRINTF(AMI_D_INTR, ("intr ")); - - while ((sc->sc_done)(sc, &mbox)) { + mtx_enter(&sc->sc_cmd_mtx); + while (!TAILQ_EMPTY(&sc->sc_ccb_runq) && sc->sc_done(sc, &mbox)) { AMI_DPRINTF(AMI_D_CMD, ("got#%d ", mbox.acc_nstat)); for (i = 0; i < mbox.acc_nstat; i++ ) { - ready = mbox.acc_cmplidl[i]; + ready = mbox.acc_cmplidl[i] - 1; AMI_DPRINTF(AMI_D_CMD, ("ready=%d ", ready)); - if (!ami_done(sc, ready, mbox.acc_status)) - rv |= 1; + + ccb = &sc->sc_ccbs[ready]; + ccb->ccb_status = mbox.acc_status; + ccb->ccb_state = AMI_CCB_READY; + TAILQ_REMOVE(&ccb->ccb_sc->sc_ccb_runq, ccb, ccb_link); + + mtx_leave(&sc->sc_cmd_mtx); + ccb->ccb_done(sc, ccb); + mtx_enter(&sc->sc_cmd_mtx); + + rv = 1; } } + ready = (sc->sc_drainio && TAILQ_EMPTY(&sc->sc_ccb_runq)); + mtx_leave(&sc->sc_cmd_mtx); - if (rv) + if (ready) + wakeup(sc); + else if (rv) ami_runqueue(sc); AMI_DPRINTF(AMI_D_INTR, ("exit ")); @@ -1856,7 +1763,7 @@ ami_mgmt(struct ami_softc *sc, u_int8_t opcode, u_int8_t par1, u_int8_t par2, struct ami_iocmd *cmd; struct ami_mem *am = NULL; char *idata = NULL; - int s, error = 0; + int error = 0; rw_enter_write(&sc->sc_lock); @@ -1900,17 +1807,17 @@ ami_mgmt(struct ami_softc *sc, u_int8_t opcode, u_int8_t par1, u_int8_t par2, if (opcode != AMI_CHSTATE) { ami_start(sc, ccb); - s = splbio(); + mtx_enter(&sc->sc_cmd_mtx); while (ccb->ccb_state != AMI_CCB_READY) - tsleep(ccb, PRIBIO,"ami_mgmt", 0); - splx(s); + msleep(ccb, &sc->sc_cmd_mtx, PRIBIO,"ami_mgmt", 0); + mtx_leave(&sc->sc_cmd_mtx); } else { /* change state must be run with id 0xfe and MUST be polled */ - s = splbio(); + mtx_enter(&sc->sc_cmd_mtx); sc->sc_drainio = 1; while (!TAILQ_EMPTY(&sc->sc_ccb_runq)) { - if (tsleep(sc, PRIBIO, "ami_mgmt_drain", hz * 60) == - EWOULDBLOCK) { + if (msleep(sc, &sc->sc_cmd_mtx, PRIBIO, + "amimgmt", hz * 60) == EWOULDBLOCK) { printf("%s: drain io timeout\n", DEVNAME(sc)); ccb->ccb_flags |= AMI_CCB_F_ERR; goto restartio; @@ -1924,8 +1831,8 @@ ami_mgmt(struct ami_softc *sc, u_int8_t opcode, u_int8_t par1, u_int8_t par2, restartio: /* restart io */ sc->sc_drainio = 0; + mtx_leave(&sc->sc_cmd_mtx); ami_runqueue(sc); - splx(s); } if (ccb->ccb_flags & AMI_CCB_F_ERR) |