ami(4) has three things which need protection at runtime: the ccb
free list, the queue of pending and active ccbs, and the hardware
mailboxes. the ccb free list is already protected by mutexes after
my last commit. this diff moves the pending/active ccb and mailbox
protection to mutexes.
since you only ever dequeue pending io when the mbox is free and
then place the io on the active queue, these are all now protected
by a single mutex.
other than that the biggest change in this code is how polled
commands are done. instead of managing the ccbs movement through
the queues and on and off the hardware manually, this now just
shoves the io onto the chip before all currently pending ios, and
then runs the interrupt handler periodically until the ccb is marked
as dequeued. to do this it swaps the ccb completion handler out so
that the only change to the ccb on completion by the isr is to
change the ccbs state. most ccb handlers free the ccb via the isr.
this works for me, but i would definitely appreciate testing.
once this is in i can trivially cut ami over to iopools.
dlg
Index: ami.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/ami.c,v
retrieving revision 1.207
diff -u -p -r1.207 ami.c
--- ami.c 21 Jun 2010 11:43:38 -0000 1.207
+++ ami.c 21 Jun 2010 12:57:06 -0000
@@ -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 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 *, st
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,
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 am
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, struc
* 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;
- }
-
- 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);
- }
+ timeout = MAX(30000, timeout); /* timeout */
- 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
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
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, str
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 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
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
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
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)
Index: amivar.h
===================================================================
RCS file: /cvs/src/sys/dev/ic/amivar.h,v
retrieving revision 1.56
diff -u -p -r1.56 amivar.h
--- amivar.h 21 Jun 2010 11:43:38 -0000 1.56
+++ amivar.h 21 Jun 2010 12:57:06 -0000
@@ -107,14 +107,16 @@ struct ami_softc {
bus_space_handle_t sc_ioh;
bus_dma_tag_t sc_dmat;
+ struct ami_ccb *sc_ccbs;
+ struct ami_ccb_list sc_ccb_freeq;
+ struct mutex sc_ccb_freeq_mtx;
+
struct ami_mem *sc_mbox_am;
volatile struct ami_iocmd *sc_mbox;
paddr_t sc_mbox_pa;
- struct ami_ccb *sc_ccbs;
- struct ami_ccb_list sc_ccb_freeq;
- struct mutex sc_ccb_freeq_mtx;
struct ami_ccb_list sc_ccb_preq, sc_ccb_runq;
+ struct mutex sc_cmd_mtx;
struct ami_mem *sc_ccbmem_am;