summaryrefslogtreecommitdiffstats
path: root/sys/dev/mps/mps.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/mps/mps.c')
-rw-r--r--sys/dev/mps/mps.c142
1 files changed, 120 insertions, 22 deletions
diff --git a/sys/dev/mps/mps.c b/sys/dev/mps/mps.c
index 61cbaa6..593ba7e 100644
--- a/sys/dev/mps/mps.c
+++ b/sys/dev/mps/mps.c
@@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/uio.h>
#include <sys/sysctl.h>
+#include <sys/endian.h>
#include <machine/bus.h>
#include <machine/resource.h>
@@ -61,6 +62,7 @@ static void mps_startup(void *arg);
static void mps_startup_complete(struct mps_softc *sc, struct mps_command *cm);
static int mps_send_iocinit(struct mps_softc *sc);
static int mps_attach_log(struct mps_softc *sc);
+static __inline void mps_complete_command(struct mps_command *cm);
static void mps_dispatch_event(struct mps_softc *sc, uintptr_t data, MPI2_EVENT_NOTIFICATION_REPLY *reply);
static void mps_config_complete(struct mps_softc *sc, struct mps_command *cm);
static void mps_periodic(void *);
@@ -386,6 +388,15 @@ mps_enqueue_request(struct mps_softc *sc, struct mps_command *cm)
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+ if (sc->mps_flags & MPS_FLAGS_ATTACH_DONE)
+ mtx_assert(&sc->mps_mtx, MA_OWNED);
+
+ if ((cm->cm_desc.Default.SMID < 1)
+ || (cm->cm_desc.Default.SMID >= sc->num_reqs)) {
+ mps_printf(sc, "%s: invalid SMID %d, desc %#x %#x\n",
+ __func__, cm->cm_desc.Default.SMID,
+ cm->cm_desc.Words.High, cm->cm_desc.Words.Low);
+ }
mps_regwrite(sc, MPI2_REQUEST_DESCRIPTOR_POST_LOW_OFFSET,
cm->cm_desc.Words.Low);
mps_regwrite(sc, MPI2_REQUEST_DESCRIPTOR_POST_HIGH_OFFSET,
@@ -607,9 +618,16 @@ mps_alloc_queues(struct mps_softc *sc)
static int
mps_alloc_replies(struct mps_softc *sc)
{
- int rsize;
+ int rsize, num_replies;
+
+ /*
+ * sc->num_replies should be one less than sc->fqdepth. We need to
+ * allocate space for sc->fqdepth replies, but only sc->num_replies
+ * replies can be used at once.
+ */
+ num_replies = max(sc->fqdepth, sc->num_replies);
- rsize = sc->facts->ReplyFrameSize * sc->num_replies * 4;
+ rsize = sc->facts->ReplyFrameSize * num_replies * 4;
if (bus_dma_tag_create( sc->mps_parent_dmat, /* parent */
4, 0, /* algnmnt, boundary */
BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
@@ -724,6 +742,7 @@ mps_alloc_requests(struct mps_softc *sc)
chain->chain_busaddr = sc->chain_busaddr +
i * sc->facts->IOCRequestFrameSize * 4;
mps_free_chain(sc, chain);
+ sc->chain_free_lowwater++;
}
/* XXX Need to pick a more precise value */
@@ -782,11 +801,19 @@ mps_init_queues(struct mps_softc *sc)
memset((uint8_t *)sc->post_queue, 0xff, sc->pqdepth * 8);
+ /*
+ * According to the spec, we need to use one less reply than we
+ * have space for on the queue. So sc->num_replies (the number we
+ * use) should be less than sc->fqdepth (allocated size).
+ */
if (sc->num_replies >= sc->fqdepth)
return (EINVAL);
- for (i = 0; i < sc->num_replies; i++)
- sc->free_queue[i] = sc->reply_busaddr + i * sc->facts->ReplyFrameSize * 4;
+ /*
+ * Initialize all of the free queue entries.
+ */
+ for (i = 0; i < sc->fqdepth; i++)
+ sc->free_queue[i] = sc->reply_busaddr + (i * sc->facts->ReplyFrameSize * 4);
sc->replyfreeindex = sc->num_replies;
return (0);
@@ -830,15 +857,35 @@ mps_attach(struct mps_softc *sc)
if (sc->sysctl_tree == NULL)
return (ENOMEM);
- SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ SYSCTL_ADD_UINT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
OID_AUTO, "debug_level", CTLFLAG_RW, &sc->mps_debug, 0,
"mps debug level");
- SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ SYSCTL_ADD_UINT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
OID_AUTO, "allow_multiple_tm_cmds", CTLFLAG_RW,
&sc->allow_multiple_tm_cmds, 0,
"allow multiple simultaneous task management cmds");
+ SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ OID_AUTO, "io_cmds_active", CTLFLAG_RD,
+ &sc->io_cmds_active, 0, "number of currently active commands");
+
+ SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ OID_AUTO, "io_cmds_highwater", CTLFLAG_RD,
+ &sc->io_cmds_highwater, 0, "maximum active commands seen");
+
+ SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ OID_AUTO, "chain_free", CTLFLAG_RD,
+ &sc->chain_free, 0, "number of free chain elements");
+
+ SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ OID_AUTO, "chain_free_lowwater", CTLFLAG_RD,
+ &sc->chain_free_lowwater, 0,"lowest number of free chain elements");
+
+ SYSCTL_ADD_UQUAD(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ OID_AUTO, "chain_alloc_fail", CTLFLAG_RD,
+ &sc->chain_alloc_fail, "chain allocation failures");
+
if ((error = mps_transition_ready(sc)) != 0)
return (error);
@@ -879,9 +926,12 @@ mps_attach(struct mps_softc *sc)
sc->num_reqs = MIN(MPS_REQ_FRAMES, sc->facts->RequestCredit);
sc->num_replies = MIN(MPS_REPLY_FRAMES + MPS_EVT_REPLY_FRAMES,
sc->facts->MaxReplyDescriptorPostQueueDepth) - 1;
+ mps_dprint(sc, MPS_INFO, "num_reqs %d, num_replies %d\n", sc->num_reqs,
+ sc->num_replies);
TAILQ_INIT(&sc->req_list);
TAILQ_INIT(&sc->chain_list);
TAILQ_INIT(&sc->tm_list);
+ TAILQ_INIT(&sc->io_list);
if (((error = mps_alloc_queues(sc)) != 0) ||
((error = mps_alloc_replies(sc)) != 0) ||
@@ -907,7 +957,6 @@ mps_attach(struct mps_softc *sc)
* replies.
*/
sc->replypostindex = 0;
- sc->replycurindex = 0;
mps_regwrite(sc, MPI2_REPLY_FREE_HOST_INDEX_OFFSET, sc->replyfreeindex);
mps_regwrite(sc, MPI2_REPLY_POST_HOST_INDEX_OFFSET, 0);
@@ -952,6 +1001,8 @@ mps_attach(struct mps_softc *sc)
error = EINVAL;
}
+ sc->mps_flags |= MPS_FLAGS_ATTACH_DONE;
+
return (error);
}
@@ -1151,6 +1202,22 @@ mps_free(struct mps_softc *sc)
return (0);
}
+static __inline void
+mps_complete_command(struct mps_command *cm)
+{
+ if (cm->cm_flags & MPS_CM_FLAGS_POLLED)
+ cm->cm_flags |= MPS_CM_FLAGS_COMPLETE;
+
+ if (cm->cm_complete != NULL)
+ cm->cm_complete(cm->cm_sc, cm);
+
+ if (cm->cm_flags & MPS_CM_FLAGS_WAKEUP) {
+ mps_dprint(cm->cm_sc, MPS_TRACE, "%s: waking up %p\n",
+ __func__, cm);
+ wakeup(cm);
+ }
+}
+
void
mps_intr(void *data)
{
@@ -1211,7 +1278,8 @@ mps_intr_locked(void *data)
desc = &sc->post_queue[pq];
flags = desc->Default.ReplyFlags &
MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
- if (flags == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
+ if ((flags == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
+ || (desc->Words.High == 0xffffffff))
break;
switch (flags) {
@@ -1224,9 +1292,36 @@ mps_intr_locked(void *data)
uint32_t baddr;
uint8_t *reply;
+ /*
+ * Re-compose the reply address from the address
+ * sent back from the chip. The ReplyFrameAddress
+ * is the lower 32 bits of the physical address of
+ * particular reply frame. Convert that address to
+ * host format, and then use that to provide the
+ * offset against the virtual address base
+ * (sc->reply_frames).
+ */
+ baddr = le32toh(desc->AddressReply.ReplyFrameAddress);
reply = sc->reply_frames +
- sc->replycurindex * sc->facts->ReplyFrameSize * 4;
- baddr = desc->AddressReply.ReplyFrameAddress;
+ (baddr - ((uint32_t)sc->reply_busaddr));
+ /*
+ * Make sure the reply we got back is in a valid
+ * range. If not, go ahead and panic here, since
+ * we'll probably panic as soon as we deference the
+ * reply pointer anyway.
+ */
+ if ((reply < sc->reply_frames)
+ || (reply > (sc->reply_frames +
+ (sc->fqdepth * sc->facts->ReplyFrameSize * 4)))) {
+ printf("%s: WARNING: reply %p out of range!\n",
+ __func__, reply);
+ printf("%s: reply_frames %p, fqdepth %d, "
+ "frame size %d\n", __func__,
+ sc->reply_frames, sc->fqdepth,
+ sc->facts->ReplyFrameSize * 4);
+ printf("%s: baddr %#x,\n", __func__, baddr);
+ panic("Reply address out of range");
+ }
if (desc->AddressReply.SMID == 0) {
mps_dispatch_event(sc, baddr,
(MPI2_EVENT_NOTIFICATION_REPLY *) reply);
@@ -1236,8 +1331,6 @@ mps_intr_locked(void *data)
cm->cm_reply_data =
desc->AddressReply.ReplyFrameAddress;
}
- if (++sc->replycurindex >= sc->fqdepth)
- sc->replycurindex = 0;
break;
}
case MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS:
@@ -1251,16 +1344,8 @@ mps_intr_locked(void *data)
break;
}
- if (cm != NULL) {
- if (cm->cm_flags & MPS_CM_FLAGS_POLLED)
- cm->cm_flags |= MPS_CM_FLAGS_COMPLETE;
-
- if (cm->cm_complete != NULL)
- cm->cm_complete(sc, cm);
-
- if (cm->cm_flags & MPS_CM_FLAGS_WAKEUP)
- wakeup(cm);
- }
+ if (cm != NULL)
+ mps_complete_command(cm);
desc->Words.Low = 0xffffffff;
desc->Words.High = 0xffffffff;
@@ -1621,6 +1706,8 @@ mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
if (error != 0) {
/* Resource shortage, roll back! */
mps_printf(sc, "out of chain frames\n");
+ cm->cm_flags |= MPS_CM_FLAGS_CHAIN_FAILED;
+ mps_complete_command(cm);
return;
}
}
@@ -1760,6 +1847,15 @@ mps_config_complete(struct mps_softc *sc, struct mps_command *cm)
bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap);
}
+ /*
+ * XXX KDM need to do more error recovery? This results in the
+ * device in question not getting probed.
+ */
+ if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+ params->status = MPI2_IOCSTATUS_BUSY;
+ goto bailout;
+ }
+
reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
params->status = reply->IOCStatus;
if (params->hdr.Ext.ExtPageType != 0) {
@@ -1772,6 +1868,8 @@ mps_config_complete(struct mps_softc *sc, struct mps_command *cm)
params->hdr.Struct.PageVersion = reply->Header.PageVersion;
}
+bailout:
+
mps_free_command(sc, cm);
if (params->callback != NULL)
params->callback(sc, params);
OpenPOWER on IntegriCloud