summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Gwynne <dlg@cvs.openbsd.org>2010-01-03 06:15:31 +0000
committerDavid Gwynne <dlg@cvs.openbsd.org>2010-01-03 06:15:31 +0000
commit7bbb0b2fdb44c5b4fa3d1131e02b42d1028f34e5 (patch)
treead0cbc997c73d9981b662ee29f290ecdcf416d86
parent46e72594047f32bf470f6ca20938367c79baa8cc (diff)
switch mpi from using splbio to protect itself over to mutexes.
mpi only needs two mutexes, one for the list of free ccbs, and another to protect the reply doorbell. the latter is necessary to allow polling for command completion to work in smp systems. tested on sas and fc hbas. this diff was written over 2 years ago now with surprisingly few tweaks to handle changes that have occurred since then.
-rw-r--r--sys/dev/ic/mpi.c142
-rw-r--r--sys/dev/ic/mpivar.h7
2 files changed, 83 insertions, 66 deletions
diff --git a/sys/dev/ic/mpi.c b/sys/dev/ic/mpi.c
index 78b9dbc8eee..91f7c931a3b 100644
--- a/sys/dev/ic/mpi.c
+++ b/sys/dev/ic/mpi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mpi.c,v 1.124 2009/12/10 00:20:38 chl Exp $ */
+/* $OpenBSD: mpi.c,v 1.125 2010/01/03 06:15:30 dlg Exp $ */
/*
* Copyright (c) 2005, 2006, 2009 David Gwynne <dlg@openbsd.org>
@@ -27,6 +27,7 @@
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
+#include <sys/mutex.h>
#include <sys/rwlock.h>
#include <sys/sensors.h>
@@ -93,9 +94,9 @@ int mpi_alloc_replies(struct mpi_softc *);
void mpi_push_replies(struct mpi_softc *);
void mpi_start(struct mpi_softc *, struct mpi_ccb *);
-int mpi_complete(struct mpi_softc *, struct mpi_ccb *, int);
int mpi_poll(struct mpi_softc *, struct mpi_ccb *, int);
-int mpi_reply(struct mpi_softc *, u_int32_t);
+u_int32_t mpi_pop_mtx_reply(struct mpi_softc *);
+struct mpi_ccb *mpi_reply(struct mpi_softc *, u_int32_t);
int mpi_cfg_spi_port(struct mpi_softc *);
void mpi_squash_ppr(struct mpi_softc *);
@@ -200,6 +201,8 @@ mpi_attach(struct mpi_softc *sc)
struct scsibus_attach_args saa;
struct mpi_ccb *ccb;
+ mtx_init(&sc->sc_reply_mtx, IPL_BIO);
+
printf("\n");
/* disable interrupts */
@@ -815,22 +818,34 @@ mpi_detach(struct mpi_softc *sc)
}
+u_int32_t
+mpi_pop_mtx_reply(struct mpi_softc *sc)
+{
+ u_int32_t reg;
+
+ mtx_enter(&sc->sc_reply_mtx);
+ reg = mpi_pop_reply(sc);
+ mtx_leave(&sc->sc_reply_mtx);
+
+ return (reg);
+}
+
int
mpi_intr(void *arg)
{
struct mpi_softc *sc = arg;
+ struct mpi_ccb *ccb = NULL;
u_int32_t reg;
- int rv = 0;
- while ((reg = mpi_pop_reply(sc)) != 0xffffffff) {
- mpi_reply(sc, reg);
- rv = 1;
+ while ((reg = mpi_pop_mtx_reply(sc)) != 0xffffffff) {
+ ccb = mpi_reply(sc, reg);
+ ccb->ccb_done(ccb);
}
- return (rv);
+ return ((ccb == NULL) ? 0 : 1);
}
-int
+struct mpi_ccb *
mpi_reply(struct mpi_softc *sc, u_int32_t reg)
{
struct mpi_ccb *ccb;
@@ -882,9 +897,7 @@ mpi_reply(struct mpi_softc *sc, u_int32_t reg)
ccb->ccb_state = MPI_CCB_READY;
ccb->ccb_rcb = rcb;
- ccb->ccb_done(ccb);
-
- return (id);
+ return (ccb);
}
struct mpi_dmamem *
@@ -955,6 +968,7 @@ mpi_alloc_ccbs(struct mpi_softc *sc)
int i;
TAILQ_INIT(&sc->sc_ccb_free);
+ mtx_init(&sc->sc_ccb_mtx, IPL_BIO);
sc->sc_ccbs = malloc(sizeof(struct mpi_ccb) * sc->sc_maxcmds,
M_DEVBUF, M_WAITOK | M_CANFAIL | M_ZERO);
@@ -1018,17 +1032,15 @@ mpi_get_ccb(struct mpi_softc *sc)
{
struct mpi_ccb *ccb;
+ mtx_enter(&sc->sc_ccb_mtx);
ccb = TAILQ_FIRST(&sc->sc_ccb_free);
- if (ccb == NULL) {
- DNPRINTF(MPI_D_CCB, "%s: mpi_get_ccb == NULL\n", DEVNAME(sc));
- return (NULL);
+ if (ccb != NULL) {
+ TAILQ_REMOVE(&sc->sc_ccb_free, ccb, ccb_link);
+ ccb->ccb_state = MPI_CCB_READY;
}
+ mtx_leave(&sc->sc_ccb_mtx);
- TAILQ_REMOVE(&sc->sc_ccb_free, ccb, ccb_link);
-
- ccb->ccb_state = MPI_CCB_READY;
-
- DNPRINTF(MPI_D_CCB, "%s: mpi_get_ccb %#x\n", DEVNAME(sc), ccb);
+ DNPRINTF(MPI_D_CCB, "%s: mpi_get_ccb %p\n", DEVNAME(sc), ccb);
return (ccb);
}
@@ -1036,13 +1048,15 @@ mpi_get_ccb(struct mpi_softc *sc)
void
mpi_put_ccb(struct mpi_softc *sc, struct mpi_ccb *ccb)
{
- DNPRINTF(MPI_D_CCB, "%s: mpi_put_ccb %#x\n", DEVNAME(sc), ccb);
+ DNPRINTF(MPI_D_CCB, "%s: mpi_put_ccb %p\n", DEVNAME(sc), ccb);
ccb->ccb_state = MPI_CCB_FREE;
ccb->ccb_xs = NULL;
ccb->ccb_done = NULL;
bzero(ccb->ccb_cmd, MPI_REQUEST_SIZE);
+ mtx_enter(&sc->sc_ccb_mtx);
TAILQ_INSERT_TAIL(&sc->sc_ccb_free, ccb, ccb_link);
+ mtx_leave(&sc->sc_ccb_mtx);
}
int
@@ -1099,45 +1113,56 @@ mpi_start(struct mpi_softc *sc, struct mpi_ccb *ccb)
}
int
-mpi_complete(struct mpi_softc *sc, struct mpi_ccb *ccb, int timeout)
+mpi_poll(struct mpi_softc *sc, struct mpi_ccb *ccb, int timeout)
{
+ struct mpi_ccb_list completed =
+ TAILQ_HEAD_INITIALIZER(completed);
+ struct mpi_ccb *nccb = NULL;
u_int32_t reg;
- int id = -1;
+ int rv = 0;
- DNPRINTF(MPI_D_INTR, "%s: mpi_complete timeout %d\n", DEVNAME(sc),
+ DNPRINTF(MPI_D_INTR, "%s: mpi_poll timeout %d\n", DEVNAME(sc),
timeout);
+ mtx_enter(&sc->sc_reply_mtx);
+ mpi_start(sc, ccb);
do {
reg = mpi_pop_reply(sc);
if (reg == 0xffffffff) {
- if (timeout-- == 0)
- return (1);
+ if (timeout-- == 0) {
+ printf("%s: timeout\n", DEVNAME(sc));
+ rv = 1;
+ goto timeout;
+ }
delay(1000);
continue;
}
- id = mpi_reply(sc, reg);
+ nccb = mpi_reply(sc, reg);
- } while (ccb->ccb_id != id);
+ /*
+ * The ccb used for event notification can come out of the
+ * reply doorbell multiple times. Avoid enqueuing it here for
+ * completion outside the reply mutex since that can screw the
+ * completed TAILQ up. We know it will not cause new ccbs to be
+ * generated and issued, so it is safe to complete here.
+ */
+ if (nccb == sc->sc_evt_ccb)
+ nccb->ccb_done(nccb);
+ else
+ TAILQ_INSERT_TAIL(&completed, nccb, ccb_link);
- return (0);
-}
-
-int
-mpi_poll(struct mpi_softc *sc, struct mpi_ccb *ccb, int timeout)
-{
- int error;
- int s;
+ } while (ccb != nccb);
+ mtx_leave(&sc->sc_reply_mtx);
- DNPRINTF(MPI_D_CMD, "%s: mpi_poll\n", DEVNAME(sc));
-
- s = splbio();
- mpi_start(sc, ccb);
- error = mpi_complete(sc, ccb, timeout);
- splx(s);
+timeout:
+ while ((nccb = TAILQ_FIRST(&completed)) != NULL) {
+ TAILQ_REMOVE(&completed, nccb, ccb_link);
+ nccb->ccb_done(nccb);
+ }
- return (error);
+ return (rv);
}
int
@@ -1167,9 +1192,7 @@ mpi_scsi_cmd(struct scsi_xfer *xs)
return (COMPLETE);
}
- s = splbio();
ccb = mpi_get_ccb(sc);
- splx(s);
if (ccb == NULL)
return (NO_CCB);
@@ -1225,8 +1248,8 @@ mpi_scsi_cmd(struct scsi_xfer *xs)
if (mpi_load_xs(ccb) != 0) {
xs->error = XS_DRIVER_STUFFUP;
xs->flags |= ITSDONE;
- s = splbio();
mpi_put_ccb(sc, ccb);
+ s = splbio();
scsi_done(xs);
splx(s);
return (COMPLETE);
@@ -1245,9 +1268,7 @@ mpi_scsi_cmd(struct scsi_xfer *xs)
return (COMPLETE);
}
- s = splbio();
mpi_start(sc, ccb);
- splx(s);
return (SUCCESSFULLY_QUEUED);
}
@@ -1259,6 +1280,7 @@ mpi_scsi_cmd_done(struct mpi_ccb *ccb)
struct mpi_ccb_bundle *mcb = ccb->ccb_cmd;
bus_dmamap_t dmap = ccb->ccb_dmamap;
struct mpi_msg_scsi_io_error *sie;
+ int s;
if (xs->datalen != 0) {
bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize,
@@ -1275,9 +1297,11 @@ mpi_scsi_cmd_done(struct mpi_ccb *ccb)
if (ccb->ccb_rcb == NULL) {
/* no scsi error, we're ok so drop out early */
- xs->status = SCSI_OK;
mpi_put_ccb(sc, ccb);
+ xs->status = SCSI_OK;
+ s = splbio();
scsi_done(xs);
+ splx(s);
return;
}
@@ -1363,7 +1387,9 @@ mpi_scsi_cmd_done(struct mpi_ccb *ccb)
mpi_push_reply(sc, ccb->ccb_rcb->rcb_reply_dva);
mpi_put_ccb(sc, ccb);
+ s = splbio();
scsi_done(xs);
+ splx(s);
}
void
@@ -1981,13 +2007,11 @@ mpi_portfacts(struct mpi_softc *sc)
struct mpi_ccb *ccb;
struct mpi_msg_portfacts_request *pfq;
volatile struct mpi_msg_portfacts_reply *pfp;
- int s, rv = 1;
+ int rv = 1;
DNPRINTF(MPI_D_MISC, "%s: mpi_portfacts\n", DEVNAME(sc));
- s = splbio();
ccb = mpi_get_ccb(sc);
- splx(s);
if (ccb == NULL) {
DNPRINTF(MPI_D_MISC, "%s: mpi_portfacts ccb_get\n",
DEVNAME(sc));
@@ -2095,11 +2119,8 @@ mpi_eventnotify(struct mpi_softc *sc)
{
struct mpi_ccb *ccb;
struct mpi_msg_event_request *enq;
- int s;
- s = splbio();
ccb = mpi_get_ccb(sc);
- splx(s);
if (ccb == NULL) {
DNPRINTF(MPI_D_MISC, "%s: mpi_eventnotify ccb_get\n",
DEVNAME(sc));
@@ -2114,6 +2135,7 @@ mpi_eventnotify(struct mpi_softc *sc)
enq->event_switch = MPI_EVENT_SWITCH_ON;
enq->msg_context = htole32(ccb->ccb_id);
+ sc->sc_evt_ccb = ccb;
mpi_start(sc, ccb);
return (0);
}
@@ -2256,13 +2278,10 @@ mpi_portenable(struct mpi_softc *sc)
{
struct mpi_ccb *ccb;
struct mpi_msg_portenable_request *peq;
- int s;
DNPRINTF(MPI_D_MISC, "%s: mpi_portenable\n", DEVNAME(sc));
- s = splbio();
ccb = mpi_get_ccb(sc);
- splx(s);
if (ccb == NULL) {
DNPRINTF(MPI_D_MISC, "%s: mpi_portenable ccb_get\n",
DEVNAME(sc));
@@ -2303,7 +2322,6 @@ mpi_fwupload(struct mpi_softc *sc)
} __packed *bundle;
struct mpi_msg_fwupload_reply *upp;
u_int64_t addr;
- int s;
int rv = 0;
if (sc->sc_fw_len == 0)
@@ -2318,9 +2336,7 @@ mpi_fwupload(struct mpi_softc *sc)
return (1);
}
- s = splbio();
ccb = mpi_get_ccb(sc);
- splx(s);
if (ccb == NULL) {
DNPRINTF(MPI_D_MISC, "%s: mpi_fwupload ccb_get\n",
DEVNAME(sc));
@@ -2462,9 +2478,7 @@ mpi_req_cfg_header(struct mpi_softc *sc, u_int8_t type, u_int8_t number,
"address: 0x%08x flags: 0x%b\n", DEVNAME(sc), type, number,
address, flags, MPI_PG_FMT);
- s = splbio();
ccb = mpi_get_ccb(sc);
- splx(s);
if (ccb == NULL) {
DNPRINTF(MPI_D_MISC, "%s: mpi_cfg_header ccb_get\n",
DEVNAME(sc));
@@ -2572,9 +2586,7 @@ mpi_req_cfg_page(struct mpi_softc *sc, u_int32_t address, int flags,
len < page_length * 4)
return (1);
- s = splbio();
ccb = mpi_get_ccb(sc);
- splx(s);
if (ccb == NULL) {
DNPRINTF(MPI_D_MISC, "%s: mpi_cfg_page ccb_get\n", DEVNAME(sc));
return (1);
diff --git a/sys/dev/ic/mpivar.h b/sys/dev/ic/mpivar.h
index 8db118095e8..4d4f2d8cd33 100644
--- a/sys/dev/ic/mpivar.h
+++ b/sys/dev/ic/mpivar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: mpivar.h,v 1.25 2009/11/02 23:20:41 marco Exp $ */
+/* $OpenBSD: mpivar.h,v 1.26 2010/01/03 06:15:30 dlg Exp $ */
/*
* Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
@@ -111,6 +111,8 @@ struct mpi_softc {
bus_size_t sc_ios;
bus_dma_tag_t sc_dmat;
+ struct mutex sc_reply_mtx;
+
u_int8_t sc_porttype;
int sc_maxcmds;
int sc_maxchdepth;
@@ -125,11 +127,14 @@ struct mpi_softc {
struct mpi_dmamem *sc_requests;
struct mpi_ccb *sc_ccbs;
struct mpi_ccb_list sc_ccb_free;
+ struct mutex sc_ccb_mtx;
struct mpi_dmamem *sc_replies;
struct mpi_rcb *sc_rcbs;
int sc_repq;
+ struct mpi_ccb *sc_evt_ccb;
+
size_t sc_fw_len;
struct mpi_dmamem *sc_fw;