summaryrefslogtreecommitdiffstats
path: root/sys/dev/mps
diff options
context:
space:
mode:
authormdf <mdf@FreeBSD.org>2010-10-14 16:44:05 +0000
committermdf <mdf@FreeBSD.org>2010-10-14 16:44:05 +0000
commitc3e059acee4dcdaa1d1d01608e2ffe3b56c8a3e5 (patch)
tree2c6a51fbf510342eeff6cfbb2cf8499b5630c700 /sys/dev/mps
parent7ff23ffda7218293b8d478431a47d5bc98d3620f (diff)
downloadFreeBSD-src-c3e059acee4dcdaa1d1d01608e2ffe3b56c8a3e5.zip
FreeBSD-src-c3e059acee4dcdaa1d1d01608e2ffe3b56c8a3e5.tar.gz
Re-work the internals of adding items to the driver's scatter-gather
list. Use the new internals to simplify adding transaction context elements, and in future diffs, more complicated SGLs.
Diffstat (limited to 'sys/dev/mps')
-rw-r--r--sys/dev/mps/mps.c251
-rw-r--r--sys/dev/mps/mps_user.c49
-rw-r--r--sys/dev/mps/mpsvar.h3
3 files changed, 213 insertions, 90 deletions
diff --git a/sys/dev/mps/mps.c b/sys/dev/mps/mps.c
index b70e18f..f872611 100644
--- a/sys/dev/mps/mps.c
+++ b/sys/dev/mps/mps.c
@@ -380,7 +380,7 @@ mps_request_sync(struct mps_softc *sc, void *req, MPI2_DEFAULT_REPLY *reply,
return (0);
}
-static void
+void
mps_enqueue_request(struct mps_softc *sc, struct mps_command *cm)
{
@@ -1374,33 +1374,88 @@ mps_deregister_events(struct mps_softc *sc, struct mps_event_handle *handle)
return (mps_update_events(sc, NULL, NULL));
}
-static void
-mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+/*
+ * Add a chain element as the next SGE for the specified command.
+ * Reset cm_sge and cm_sgesize to indicate all the available space.
+ */
+static int
+mps_add_chain(struct mps_command *cm)
{
- MPI2_SGE_SIMPLE64 *sge;
MPI2_SGE_CHAIN32 *sgc;
- struct mps_softc *sc;
- struct mps_command *cm;
struct mps_chain *chain;
- u_int i, segsleft, sglspace, dir, flags, sflags;
+ int space;
- cm = (struct mps_command *)arg;
- sc = cm->cm_sc;
+ if (cm->cm_sglsize < MPS_SGC_SIZE)
+ panic("MPS: Need SGE Error Code\n");
- segsleft = nsegs;
- sglspace = cm->cm_sglsize;
- sge = (MPI2_SGE_SIMPLE64 *)&cm->cm_sge->MpiSimple;
+ chain = mps_alloc_chain(cm->cm_sc);
+ if (chain == NULL)
+ return (ENOBUFS);
+
+ space = (int)cm->cm_sc->facts->IOCRequestFrameSize * 4;
/*
- * Set up DMA direction flags. Note no support for
- * bi-directional transactions.
+ * Note: a double-linked list is used to make it easier to
+ * walk for debugging.
*/
- sflags = MPI2_SGE_FLAGS_ADDRESS_SIZE;
- if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) {
- sflags |= MPI2_SGE_FLAGS_DIRECTION;
- dir = BUS_DMASYNC_PREWRITE;
- } else
- dir = BUS_DMASYNC_PREREAD;
+ TAILQ_INSERT_TAIL(&cm->cm_chain_list, chain, chain_link);
+
+ sgc = (MPI2_SGE_CHAIN32 *)&cm->cm_sge->MpiChain;
+ sgc->Length = space;
+ sgc->NextChainOffset = 0;
+ sgc->Flags = MPI2_SGE_FLAGS_CHAIN_ELEMENT;
+ sgc->Address = chain->chain_busaddr;
+
+ cm->cm_sge = (MPI2_SGE_IO_UNION *)&chain->chain->MpiSimple;
+ cm->cm_sglsize = space;
+ return (0);
+}
+
+/*
+ * Add one scatter-gather element (chain, simple, transaction context)
+ * to the scatter-gather list for a command. Maintain cm_sglsize and
+ * cm_sge as the remaining size and pointer to the next SGE to fill
+ * in, respectively.
+ */
+int
+mps_push_sge(struct mps_command *cm, void *sgep, size_t len, int segsleft)
+{
+ MPI2_SGE_TRANSACTION_UNION *tc = sgep;
+ MPI2_SGE_SIMPLE64 *sge = sgep;
+ int error, type;
+
+ type = (tc->Flags & MPI2_SGE_FLAGS_ELEMENT_MASK);
+
+#ifdef INVARIANTS
+ switch (type) {
+ case MPI2_SGE_FLAGS_TRANSACTION_ELEMENT: {
+ if (len != tc->DetailsLength + 4)
+ panic("TC %p length %u or %zu?", tc,
+ tc->DetailsLength + 4, len);
+ }
+ break;
+ case MPI2_SGE_FLAGS_CHAIN_ELEMENT:
+ /* Driver only uses 32-bit chain elements */
+ if (len != MPS_SGC_SIZE)
+ panic("CHAIN %p length %u or %zu?", sgep,
+ MPS_SGC_SIZE, len);
+ break;
+ case MPI2_SGE_FLAGS_SIMPLE_ELEMENT:
+ /* Driver only uses 64-bit SGE simple elements */
+ sge = sgep;
+ if (len != MPS_SGE64_SIZE)
+ panic("SGE simple %p length %u or %zu?", sge,
+ MPS_SGE64_SIZE, len);
+ if (((sge->FlagsLength >> MPI2_SGE_FLAGS_SHIFT) &
+ MPI2_SGE_FLAGS_ADDRESS_SIZE) == 0)
+ panic("SGE simple %p flags %02x not marked 64-bit?",
+ sge, sge->FlagsLength >> MPI2_SGE_FLAGS_SHIFT);
+
+ break;
+ default:
+ panic("Unexpected SGE %p, flags %02x", tc, tc->Flags);
+ }
+#endif
/*
* case 1: 1 more segment, enough room for it
@@ -1408,70 +1463,128 @@ mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
* case 3: >=2 more segments, only enough room for 1 and a chain
* case 4: >=1 more segment, enough room for only a chain
* case 5: >=1 more segment, no room for anything (error)
- */
+ */
- for (i = 0; i < nsegs; i++) {
+ /*
+ * There should be room for at least a chain element, or this
+ * code is buggy. Case (5).
+ */
+ if (cm->cm_sglsize < MPS_SGC_SIZE)
+ panic("MPS: Need SGE Error Code\n");
- /* Case 5 Error. This should never happen. */
- if (sglspace < MPS_SGC_SIZE) {
- panic("MPS: Need SGE Error Code\n");
+ if (segsleft >= 2 &&
+ cm->cm_sglsize < len + MPS_SGC_SIZE + MPS_SGE64_SIZE) {
+ /*
+ * There are 2 or more segments left to add, and only
+ * enough room for 1 and a chain. Case (3).
+ *
+ * Mark as last element in this chain if necessary.
+ */
+ if (type == MPI2_SGE_FLAGS_SIMPLE_ELEMENT) {
+ sge->FlagsLength |=
+ (MPI2_SGE_FLAGS_LAST_ELEMENT << MPI2_SGE_FLAGS_SHIFT);
}
/*
- * Case 4, Fill in a chain element, allocate a chain,
- * fill in one SGE element, continue.
+ * Add the item then a chain. Do the chain now,
+ * rather than on the next iteration, to simplify
+ * understanding the code.
*/
- if ((sglspace >= MPS_SGC_SIZE) && (sglspace < MPS_SGE64_SIZE)) {
- chain = mps_alloc_chain(sc);
- if (chain == NULL) {
- /* Resource shortage, roll back! */
- mps_printf(sc, "out of chain frames\n");
- return;
- }
+ cm->cm_sglsize -= len;
+ bcopy(sgep, cm->cm_sge, len);
+ cm->cm_sge = (MPI2_SGE_IO_UNION *)((uintptr_t)cm->cm_sge + len);
+ return (mps_add_chain(cm));
+ }
- /*
- * Note: a double-linked list is used to make it
- * easier to walk for debugging.
- */
- TAILQ_INSERT_TAIL(&cm->cm_chain_list, chain,chain_link);
+ if (segsleft >= 1 && cm->cm_sglsize < len + MPS_SGC_SIZE) {
+ /*
+ * 1 or more segment, enough room for only a chain.
+ * Hope the previous element wasn't a Simple entry
+ * that needed to be marked with
+ * MPI2_SGE_FLAGS_LAST_ELEMENT. Case (4).
+ */
+ if ((error = mps_add_chain(cm)) != 0)
+ return (error);
+ }
- sgc = (MPI2_SGE_CHAIN32 *)sge;
- sgc->Length = 128;
- sgc->NextChainOffset = 0;
- sgc->Flags = MPI2_SGE_FLAGS_CHAIN_ELEMENT;
- sgc->Address = chain->chain_busaddr;
+#ifdef INVARIANTS
+ /* Case 1: 1 more segment, enough room for it. */
+ if (segsleft == 1 && cm->cm_sglsize < len)
+ panic("1 seg left and no room? %u versus %zu",
+ cm->cm_sglsize, len);
- sge = (MPI2_SGE_SIMPLE64 *)&chain->chain->MpiSimple;
- sglspace = 128;
- }
+ /* Case 2: 2 more segments, enough room for both */
+ if (segsleft == 2 && cm->cm_sglsize < len + MPS_SGE64_SIZE)
+ panic("2 segs left and no room? %u versus %zu",
+ cm->cm_sglsize, len);
+#endif
- flags = MPI2_SGE_FLAGS_SIMPLE_ELEMENT;
- sge->FlagsLength = segs[i].ds_len |
- ((sflags | flags) << MPI2_SGE_FLAGS_SHIFT);
- mps_from_u64(segs[i].ds_addr, &sge->Address);
+ if (segsleft == 1 && type == MPI2_SGE_FLAGS_SIMPLE_ELEMENT) {
+ /*
+ * Last element of the last segment of the entire
+ * buffer.
+ */
+ sge->FlagsLength |= ((MPI2_SGE_FLAGS_LAST_ELEMENT |
+ MPI2_SGE_FLAGS_END_OF_BUFFER |
+ MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT);
+ }
- /* Case 1, Fill in one SGE element and break */
- if (segsleft == 1)
- break;
+ cm->cm_sglsize -= len;
+ bcopy(sgep, cm->cm_sge, len);
+ cm->cm_sge = (MPI2_SGE_IO_UNION *)((uintptr_t)cm->cm_sge + len);
+ return (0);
+}
- sglspace -= MPS_SGE64_SIZE;
- segsleft--;
+/*
+ * Add one dma segment to the scatter-gather list for a command.
+ */
+int
+mps_add_dmaseg(struct mps_command *cm, vm_paddr_t pa, size_t len, u_int flags,
+ int segsleft)
+{
+ MPI2_SGE_SIMPLE64 sge;
- /* Case 3, prepare for a chain on the next loop */
- if ((segsleft > 0) && (sglspace < MPS_SGE64_SIZE))
- sge->FlagsLength |=
- (MPI2_SGE_FLAGS_LAST_ELEMENT <<
- MPI2_SGE_FLAGS_SHIFT);
+ /*
+ * This driver always uses 64-bit address elements for
+ * simplicity.
+ */
+ flags |= MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_ADDRESS_SIZE;
+ sge.FlagsLength = len | (flags << MPI2_SGE_FLAGS_SHIFT);
+ mps_from_u64(pa, &sge.Address);
- /* Advance to the next element to be filled in. */
- sge++;
- }
+ return (mps_push_sge(cm, &sge, sizeof sge, segsleft));
+}
- /* Last element of the last segment of the entire buffer */
- flags = MPI2_SGE_FLAGS_LAST_ELEMENT |
- MPI2_SGE_FLAGS_END_OF_BUFFER |
- MPI2_SGE_FLAGS_END_OF_LIST;
- sge->FlagsLength |= (flags << MPI2_SGE_FLAGS_SHIFT);
+static void
+mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ struct mps_softc *sc;
+ struct mps_command *cm;
+ u_int i, dir, sflags;
+
+ cm = (struct mps_command *)arg;
+ sc = cm->cm_sc;
+
+ /*
+ * Set up DMA direction flags. Note no support for
+ * bi-directional transactions.
+ */
+ sflags = 0;
+ if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) {
+ sflags |= MPI2_SGE_FLAGS_DIRECTION;
+ dir = BUS_DMASYNC_PREWRITE;
+ } else
+ dir = BUS_DMASYNC_PREREAD;
+
+ for (i = 0; i < nsegs; i++) {
+ error = mps_add_dmaseg(cm, segs[i].ds_addr, segs[i].ds_len,
+ sflags, nsegs - i);
+ if (error != 0) {
+ /* Resource shortage, roll back! */
+ mps_printf(sc, "out of chain frames\n");
+ return;
+ }
+ }
bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, dir);
mps_enqueue_request(sc, cm);
diff --git a/sys/dev/mps/mps_user.c b/sys/dev/mps/mps_user.c
index 0a90f90..b6621e3 100644
--- a/sys/dev/mps/mps_user.c
+++ b/sys/dev/mps/mps_user.c
@@ -322,6 +322,21 @@ mps_user_write_cfg_page(struct mps_softc *sc,
return (0);
}
+static void
+mpi_init_sge(struct mps_command *cm, void *req, void *sge)
+{
+ int off, space;
+
+ space = (int)cm->cm_sc->facts->IOCRequestFrameSize * 4;
+ off = (uintptr_t)sge - (uintptr_t)req;
+
+ KASSERT(off < space, ("bad pointers %p %p, off %d, space %d",
+ req, sge, off, space));
+
+ cm->cm_sge = sge;
+ cm->cm_sglsize = space - off;
+}
+
/*
* Prepare the mps_command for an IOC_FACTS request.
*/
@@ -374,8 +389,7 @@ mpi_pre_fw_download(struct mps_command *cm, struct mps_usr_command *cmd)
if (cmd->rpl_len != sizeof *rpl)
return (EINVAL);
- cm->cm_sge = (MPI2_SGE_IO_UNION *)&req->SGL;
- cm->cm_sglsize = sizeof req->SGL;
+ mpi_init_sge(cm, req, &req->SGL);
return (0);
}
@@ -387,45 +401,41 @@ mpi_pre_fw_upload(struct mps_command *cm, struct mps_usr_command *cmd)
{
MPI2_FW_UPLOAD_REQUEST *req = (void *)cm->cm_req;
MPI2_FW_UPLOAD_REPLY *rpl;
- MPI2_FW_UPLOAD_TCSGE *tc;
+ MPI2_FW_UPLOAD_TCSGE tc;
/*
* This code assumes there is room in the request's SGL for
* the TransactionContext plus at least a SGL chain element.
*/
- CTASSERT(sizeof req->SGL >= sizeof *tc + MPS_SGC_SIZE);
+ CTASSERT(sizeof req->SGL >= sizeof tc + MPS_SGC_SIZE);
if (cmd->req_len != sizeof *req)
return (EINVAL);
if (cmd->rpl_len != sizeof *rpl)
return (EINVAL);
- cm->cm_sglsize = sizeof req->SGL;
+ mpi_init_sge(cm, req, &req->SGL);
if (cmd->len == 0) {
/* Perhaps just asking what the size of the fw is? */
- cm->cm_sge = (MPI2_SGE_IO_UNION *)&req->SGL;
return (0);
}
- tc = (void *)&req->SGL;
- bzero(tc, sizeof *tc);
+ bzero(&tc, sizeof tc);
/*
* The value of the first two elements is specified in the
* Fusion-MPT Message Passing Interface document.
*/
- tc->ContextSize = 0;
- tc->DetailsLength = 12;
+ tc.ContextSize = 0;
+ tc.DetailsLength = 12;
/*
* XXX Is there any reason to fetch a partial image? I.e. to
* set ImageOffset to something other than 0?
*/
- tc->ImageOffset = 0;
- tc->ImageSize = cmd->len;
- cm->cm_sge = (MPI2_SGE_IO_UNION *)(tc + 1);
- cm->cm_sglsize -= sizeof *tc;
+ tc.ImageOffset = 0;
+ tc.ImageSize = cmd->len;
- return (0);
+ return (mps_push_sge(cm, &tc, sizeof tc, 0));
}
/*
@@ -442,8 +452,7 @@ mpi_pre_sata_passthrough(struct mps_command *cm, struct mps_usr_command *cmd)
if (cmd->rpl_len != sizeof *rpl)
return (EINVAL);
- cm->cm_sge = (MPI2_SGE_IO_UNION *)&req->SGL;
- cm->cm_sglsize = sizeof req->SGL;
+ mpi_init_sge(cm, req, &req->SGL);
return (0);
}
@@ -461,8 +470,7 @@ mpi_pre_smp_passthrough(struct mps_command *cm, struct mps_usr_command *cmd)
if (cmd->rpl_len != sizeof *rpl)
return (EINVAL);
- cm->cm_sge = (MPI2_SGE_IO_UNION *)&req->SGL;
- cm->cm_sglsize = sizeof req->SGL;
+ mpi_init_sge(cm, req, &req->SGL);
return (0);
}
@@ -480,8 +488,7 @@ mpi_pre_config(struct mps_command *cm, struct mps_usr_command *cmd)
if (cmd->rpl_len != sizeof *rpl)
return (EINVAL);
- cm->cm_sge = (MPI2_SGE_IO_UNION *)&req->PageBufferSGE;
- cm->cm_sglsize = sizeof req->PageBufferSGE;
+ mpi_init_sge(cm, req, &req->PageBufferSGE);
return (0);
}
diff --git a/sys/dev/mps/mpsvar.h b/sys/dev/mps/mpsvar.h
index cf27e5d..db91030 100644
--- a/sys/dev/mps/mpsvar.h
+++ b/sys/dev/mps/mpsvar.h
@@ -359,6 +359,9 @@ int mps_register_events(struct mps_softc *, uint8_t *, mps_evt_callback_t *,
int mps_update_events(struct mps_softc *, struct mps_event_handle *, uint8_t *);
int mps_deregister_events(struct mps_softc *, struct mps_event_handle *);
int mps_request_polled(struct mps_softc *sc, struct mps_command *cm);
+void mps_enqueue_request(struct mps_softc *, struct mps_command *);
+int mps_push_sge(struct mps_command *, void *, size_t, int);
+int mps_add_dmaseg(struct mps_command *, vm_paddr_t, size_t, u_int, int);
int mps_attach_sas(struct mps_softc *sc);
int mps_detach_sas(struct mps_softc *sc);
int mps_map_command(struct mps_softc *sc, struct mps_command *cm);
OpenPOWER on IntegriCloud