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;

Reply via email to