diff options
Diffstat (limited to 'sys/dev/mps')
-rw-r--r-- | sys/dev/mps/mps.c | 56 | ||||
-rw-r--r-- | sys/dev/mps/mps_pci.c | 1 | ||||
-rw-r--r-- | sys/dev/mps/mps_sas.c | 346 | ||||
-rw-r--r-- | sys/dev/mps/mps_user.c | 2 | ||||
-rw-r--r-- | sys/dev/mps/mpsvar.h | 16 |
5 files changed, 414 insertions, 7 deletions
diff --git a/sys/dev/mps/mps.c b/sys/dev/mps/mps.c index 404b12e..1fb37e2 100644 --- a/sys/dev/mps/mps.c +++ b/sys/dev/mps/mps.c @@ -1569,17 +1569,53 @@ mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) sc = cm->cm_sc; /* - * Set up DMA direction flags. Note no support for - * bi-directional transactions. + * In this case, just print out a warning and let the chip tell the + * user they did the wrong thing. + */ + if ((cm->cm_max_segs != 0) && (nsegs > cm->cm_max_segs)) { + mps_printf(sc, "%s: warning: busdma returned %d segments, " + "more than the %d allowed\n", __func__, nsegs, + cm->cm_max_segs); + } + + /* + * Set up DMA direction flags. Note that we don't support + * bi-directional transfers, with the exception of SMP passthrough. */ sflags = 0; - if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) { + if (cm->cm_flags & MPS_CM_FLAGS_SMP_PASS) { + /* + * We have to add a special case for SMP passthrough, there + * is no easy way to generically handle it. The first + * S/G element is used for the command (therefore the + * direction bit needs to be set). The second one is used + * for the reply. We'll leave it to the caller to make + * sure we only have two buffers. + */ + /* + * Even though the busdma man page says it doesn't make + * sense to have both direction flags, it does in this case. + * We have one s/g element being accessed in each direction. + */ + dir = BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD; + + /* + * Set the direction flag on the first buffer in the SMP + * passthrough request. We'll clear it for the second one. + */ + sflags |= MPI2_SGE_FLAGS_DIRECTION | + MPI2_SGE_FLAGS_END_OF_BUFFER; + } else 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++) { + if ((cm->cm_flags & MPS_CM_FLAGS_SMP_PASS) + && (i != 0)) { + sflags &= ~MPI2_SGE_FLAGS_DIRECTION; + } error = mps_add_dmaseg(cm, segs[i].ds_addr, segs[i].ds_len, sflags, nsegs - i); if (error != 0) { @@ -1595,6 +1631,13 @@ mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) return; } +static void +mps_data_cb2(void *arg, bus_dma_segment_t *segs, int nsegs, bus_size_t mapsize, + int error) +{ + mps_data_cb(arg, segs, nsegs, error); +} + /* * Note that the only error path here is from bus_dmamap_load(), which can * return EINPROGRESS if it is waiting for resources. @@ -1605,7 +1648,10 @@ mps_map_command(struct mps_softc *sc, struct mps_command *cm) MPI2_SGE_SIMPLE32 *sge; int error = 0; - if ((cm->cm_data != NULL) && (cm->cm_length != 0)) { + if (cm->cm_flags & MPS_CM_FLAGS_USE_UIO) { + error = bus_dmamap_load_uio(sc->buffer_dmat, cm->cm_dmamap, + &cm->cm_uio, mps_data_cb2, cm, 0); + } else if ((cm->cm_data != NULL) && (cm->cm_length != 0)) { error = bus_dmamap_load(sc->buffer_dmat, cm->cm_dmamap, cm->cm_data, cm->cm_length, mps_data_cb, cm, 0); } else { @@ -1619,7 +1665,7 @@ mps_map_command(struct mps_softc *sc, struct mps_command *cm) MPI2_SGE_FLAGS_SHIFT; sge->Address = 0; } - mps_enqueue_request(sc, cm); + mps_enqueue_request(sc, cm); } return (error); diff --git a/sys/dev/mps/mps_pci.c b/sys/dev/mps/mps_pci.c index 2b33ba8..2c57286 100644 --- a/sys/dev/mps/mps_pci.c +++ b/sys/dev/mps/mps_pci.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include <sys/conf.h> #include <sys/malloc.h> #include <sys/sysctl.h> +#include <sys/uio.h> #include <machine/bus.h> #include <machine/resource.h> diff --git a/sys/dev/mps/mps_sas.c b/sys/dev/mps/mps_sas.c index 5883e49..0e7ce03 100644 --- a/sys/dev/mps/mps_sas.c +++ b/sys/dev/mps/mps_sas.c @@ -41,6 +41,8 @@ __FBSDID("$FreeBSD$"); #include <sys/malloc.h> #include <sys/uio.h> #include <sys/sysctl.h> +#include <sys/sglist.h> +#include <sys/endian.h> #include <machine/bus.h> #include <machine/resource.h> @@ -55,6 +57,9 @@ __FBSDID("$FreeBSD$"); #include <cam/cam_periph.h> #include <cam/scsi/scsi_all.h> #include <cam/scsi/scsi_message.h> +#if __FreeBSD_version >= 900026 +#include <cam/scsi/smp_all.h> +#endif #include <dev/mps/mpi/mpi2_type.h> #include <dev/mps/mpi/mpi2.h> @@ -69,9 +74,11 @@ struct mpssas_target { uint16_t handle; uint8_t linkrate; uint64_t devname; + uint64_t sasaddr; uint32_t devinfo; uint16_t encl_handle; uint16_t encl_slot; + uint16_t parent_handle; int flags; #define MPSSAS_TARGET_INABORT (1 << 0) #define MPSSAS_TARGET_INRESET (1 << 1) @@ -144,6 +151,12 @@ static int mpssas_complete_tm_request(struct mps_softc *sc, struct mps_command *cm, int free_cm); static void mpssas_action_scsiio(struct mpssas_softc *, union ccb *); static void mpssas_scsiio_complete(struct mps_softc *, struct mps_command *); +#if __FreeBSD_version >= 900026 +static void mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm); +static void mpssas_send_smpcmd(struct mpssas_softc *sassc, union ccb *ccb, + uint64_t sasaddr); +static void mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb); +#endif /* __FreeBSD_version >= 900026 */ static void mpssas_resetdev(struct mpssas_softc *, struct mps_command *); static void mpssas_action_resetdev(struct mpssas_softc *, union ccb *); static void mpssas_resetdev_complete(struct mps_softc *, struct mps_command *); @@ -312,6 +325,8 @@ mpssas_probe_device_complete(struct mps_softc *sc, probe->target.devinfo = buf->DeviceInfo; probe->target.encl_handle = buf->EnclosureHandle; probe->target.encl_slot = buf->Slot; + probe->target.sasaddr = mps_to_u64(&buf->SASAddress); + probe->target.parent_handle = buf->ParentDevHandle; if (buf->DeviceInfo & MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH) { params->page_address = @@ -916,6 +931,11 @@ mpssas_action(struct cam_sim *sim, union ccb *ccb) case XPT_SCSI_IO: mpssas_action_scsiio(sassc, ccb); return; +#if __FreeBSD_version >= 900026 + case XPT_SMP_IO: + mpssas_action_smpio(sassc, ccb); + return; +#endif /* __FreeBSD_version >= 900026 */ default: ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; break; @@ -1361,6 +1381,9 @@ mpssas_action_scsiio(struct mpssas_softc *sassc, union ccb *ccb) bcopy(csio->cdb_io.cdb_bytes, &req->CDB.CDB32[0],csio->cdb_len); req->IoFlags = csio->cdb_len; + /* + * XXX need to handle S/G lists and physical addresses here. + */ cm->cm_data = csio->data_ptr; cm->cm_length = csio->dxfer_len; cm->cm_sge = &req->SGL; @@ -1525,6 +1548,329 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm) xpt_done(ccb); } +#if __FreeBSD_version >= 900026 +static void +mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm) +{ + MPI2_SMP_PASSTHROUGH_REPLY *rpl; + MPI2_SMP_PASSTHROUGH_REQUEST *req; + uint64_t sasaddr; + union ccb *ccb; + + ccb = cm->cm_complete_data; + rpl = (MPI2_SMP_PASSTHROUGH_REPLY *)cm->cm_reply; + if (rpl == NULL) { + mps_dprint(sc, MPS_INFO, "%s: NULL cm_reply!\n", __func__); + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + goto bailout; + } + + req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req; + sasaddr = le32toh(req->SASAddress.Low); + sasaddr |= ((uint64_t)(le32toh(req->SASAddress.High))) << 32; + + if ((rpl->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS || + rpl->SASStatus != MPI2_SASSTATUS_SUCCESS) { + mps_dprint(sc, MPS_INFO, "%s: IOCStatus %04x SASStatus %02x\n", + __func__, rpl->IOCStatus, rpl->SASStatus); + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + goto bailout; + } + + mps_dprint(sc, MPS_INFO, "%s: SMP request to SAS address " + "%#jx completed successfully\n", __func__, + (uintmax_t)sasaddr); + + if (ccb->smpio.smp_response[2] == SMP_FR_ACCEPTED) + ccb->ccb_h.status = CAM_REQ_CMP; + else + ccb->ccb_h.status = CAM_SMP_STATUS_ERROR; + +bailout: + /* + * We sync in both directions because we had DMAs in the S/G list + * in both directions. + */ + bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap); + mps_free_command(sc, cm); + xpt_done(ccb); +} + +static void +mpssas_send_smpcmd(struct mpssas_softc *sassc, union ccb *ccb, uint64_t sasaddr) +{ + struct mps_command *cm; + uint8_t *request, *response; + MPI2_SMP_PASSTHROUGH_REQUEST *req; + struct mps_softc *sc; + struct sglist *sg; + int error; + + sc = sassc->sc; + sg = NULL; + error = 0; + + /* + * XXX We don't yet support physical addresses here. + */ + if (ccb->ccb_h.flags & (CAM_DATA_PHYS|CAM_SG_LIST_PHYS)) { + mps_printf(sc, "%s: physical addresses not supported\n", + __func__); + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + return; + } + + /* + * If the user wants to send an S/G list, check to make sure they + * have single buffers. + */ + if (ccb->ccb_h.flags & CAM_SCATTER_VALID) { + /* + * The chip does not support more than one buffer for the + * request or response. + */ + if ((ccb->smpio.smp_request_sglist_cnt > 1) + || (ccb->smpio.smp_response_sglist_cnt > 1)) { + mps_printf(sc, "%s: multiple request or response " + "buffer segments not supported for SMP\n", + __func__); + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + return; + } + + /* + * The CAM_SCATTER_VALID flag was originally implemented + * for the XPT_SCSI_IO CCB, which only has one data pointer. + * We have two. So, just take that flag to mean that we + * might have S/G lists, and look at the S/G segment count + * to figure out whether that is the case for each individual + * buffer. + */ + if (ccb->smpio.smp_request_sglist_cnt != 0) { + bus_dma_segment_t *req_sg; + + req_sg = (bus_dma_segment_t *)ccb->smpio.smp_request; + request = (uint8_t *)req_sg[0].ds_addr; + } else + request = ccb->smpio.smp_request; + + if (ccb->smpio.smp_response_sglist_cnt != 0) { + bus_dma_segment_t *rsp_sg; + + rsp_sg = (bus_dma_segment_t *)ccb->smpio.smp_response; + response = (uint8_t *)rsp_sg[0].ds_addr; + } else + response = ccb->smpio.smp_response; + } else { + request = ccb->smpio.smp_request; + response = ccb->smpio.smp_response; + } + + cm = mps_alloc_command(sc); + if (cm == NULL) { + mps_printf(sc, "%s: cannot allocate command\n", __func__); + ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_done(ccb); + return; + } + + req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req; + bzero(req, sizeof(*req)); + req->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; + + /* Allow the chip to use any route to this SAS address. */ + req->PhysicalPort = 0xff; + + req->RequestDataLength = ccb->smpio.smp_request_len; + req->SGLFlags = + MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE | MPI2_SGLFLAGS_SGL_TYPE_MPI; + + mps_dprint(sc, MPS_INFO, "%s: sending SMP request to SAS " + "address %#jx\n", __func__, (uintmax_t)sasaddr); + + mpi_init_sge(cm, req, &req->SGL); + + /* + * Set up a uio to pass into mps_map_command(). This allows us to + * do one map command, and one busdma call in there. + */ + cm->cm_uio.uio_iov = cm->cm_iovec; + cm->cm_uio.uio_iovcnt = 2; + cm->cm_uio.uio_segflg = UIO_SYSSPACE; + + /* + * The read/write flag isn't used by busdma, but set it just in + * case. This isn't exactly accurate, either, since we're going in + * both directions. + */ + cm->cm_uio.uio_rw = UIO_WRITE; + + cm->cm_iovec[0].iov_base = request; + cm->cm_iovec[0].iov_len = req->RequestDataLength; + cm->cm_iovec[1].iov_base = response; + cm->cm_iovec[1].iov_len = ccb->smpio.smp_response_len; + + cm->cm_uio.uio_resid = cm->cm_iovec[0].iov_len + + cm->cm_iovec[1].iov_len; + + /* + * Trigger a warning message in mps_data_cb() for the user if we + * wind up exceeding two S/G segments. The chip expects one + * segment for the request and another for the response. + */ + cm->cm_max_segs = 2; + + cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; + cm->cm_complete = mpssas_smpio_complete; + cm->cm_complete_data = ccb; + + /* + * Tell the mapping code that we're using a uio, and that this is + * an SMP passthrough request. There is a little special-case + * logic there (in mps_data_cb()) to handle the bidirectional + * transfer. + */ + cm->cm_flags |= MPS_CM_FLAGS_USE_UIO | MPS_CM_FLAGS_SMP_PASS | + MPS_CM_FLAGS_DATAIN | MPS_CM_FLAGS_DATAOUT; + + /* The chip data format is little endian. */ + req->SASAddress.High = htole32(sasaddr >> 32); + req->SASAddress.Low = htole32(sasaddr); + + /* + * XXX Note that we don't have a timeout/abort mechanism here. + * From the manual, it looks like task management requests only + * work for SCSI IO and SATA passthrough requests. We may need to + * have a mechanism to retry requests in the event of a chip reset + * at least. Hopefully the chip will insure that any errors short + * of that are relayed back to the driver. + */ + error = mps_map_command(sc, cm); + if ((error != 0) && (error != EINPROGRESS)) { + mps_printf(sc, "%s: error %d returned from mps_map_command()\n", + __func__, error); + goto bailout_error; + } + + return; + +bailout_error: + mps_free_command(sc, cm); + ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_done(ccb); + return; + +} + +static void +mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb) +{ + struct mps_softc *sc; + struct mpssas_target *targ; + uint64_t sasaddr = 0; + + sc = sassc->sc; + + /* + * Make sure the target exists. + */ + targ = &sassc->targets[ccb->ccb_h.target_id]; + if (targ->handle == 0x0) { + mps_printf(sc, "%s: target %d does not exist!\n", __func__, + ccb->ccb_h.target_id); + ccb->ccb_h.status = CAM_SEL_TIMEOUT; + xpt_done(ccb); + return; + } + + /* + * If this device has an embedded SMP target, we'll talk to it + * directly. + * figure out what the expander's address is. + */ + if ((targ->devinfo & MPI2_SAS_DEVICE_INFO_SMP_TARGET) != 0) + sasaddr = targ->sasaddr; + + /* + * If we don't have a SAS address for the expander yet, try + * grabbing it from the page 0x83 information cached in the + * transport layer for this target. LSI expanders report the + * expander SAS address as the port-associated SAS address in + * Inquiry VPD page 0x83. Maxim expanders don't report it in page + * 0x83. + * + * XXX KDM disable this for now, but leave it commented out so that + * it is obvious that this is another possible way to get the SAS + * address. + * + * The parent handle method below is a little more reliable, and + * the other benefit is that it works for devices other than SES + * devices. So you can send a SMP request to a da(4) device and it + * will get routed to the expander that device is attached to. + * (Assuming the da(4) device doesn't contain an SMP target...) + */ +#if 0 + if (sasaddr == 0) + sasaddr = xpt_path_sas_addr(ccb->ccb_h.path); +#endif + + /* + * If we still don't have a SAS address for the expander, look for + * the parent device of this device, which is probably the expander. + */ + if (sasaddr == 0) { + struct mpssas_target *parent_target; + + if (targ->parent_handle == 0x0) { + mps_printf(sc, "%s: handle %d does not have a valid " + "parent handle!\n", __func__, targ->handle); + ccb->ccb_h.status = CAM_REQ_INVALID; + goto bailout; + } + parent_target = mpssas_find_target(sassc, 0, + targ->parent_handle); + + if (parent_target == NULL) { + mps_printf(sc, "%s: handle %d does not have a valid " + "parent target!\n", __func__, targ->handle); + ccb->ccb_h.status = CAM_REQ_INVALID; + goto bailout; + } + + if ((parent_target->devinfo & + MPI2_SAS_DEVICE_INFO_SMP_TARGET) == 0) { + mps_printf(sc, "%s: handle %d parent %d does not " + "have an SMP target!\n", __func__, + targ->handle, parent_target->handle); + ccb->ccb_h.status = CAM_REQ_INVALID; + goto bailout; + + } + + sasaddr = parent_target->sasaddr; + } + + if (sasaddr == 0) { + mps_printf(sc, "%s: unable to find SAS address for handle %d\n", + __func__, targ->handle); + ccb->ccb_h.status = CAM_REQ_INVALID; + goto bailout; + } + mpssas_send_smpcmd(sassc, ccb, sasaddr); + + return; + +bailout: + xpt_done(ccb); + +} + +#endif /* __FreeBSD_version >= 900026 */ + static void mpssas_action_resetdev(struct mpssas_softc *sassc, union ccb *ccb) { diff --git a/sys/dev/mps/mps_user.c b/sys/dev/mps/mps_user.c index ede4c02..7ca90c1 100644 --- a/sys/dev/mps/mps_user.c +++ b/sys/dev/mps/mps_user.c @@ -322,7 +322,7 @@ mps_user_write_cfg_page(struct mps_softc *sc, return (0); } -static void +void mpi_init_sge(struct mps_command *cm, void *req, void *sge) { int off, space; diff --git a/sys/dev/mps/mpsvar.h b/sys/dev/mps/mpsvar.h index db91030..f578347 100644 --- a/sys/dev/mps/mpsvar.h +++ b/sys/dev/mps/mpsvar.h @@ -60,11 +60,19 @@ struct mps_chain { uint32_t chain_busaddr; }; +/* + * This needs to be at least 2 to support SMP passthrough. + */ +#define MPS_IOVEC_COUNT 2 + struct mps_command { TAILQ_ENTRY(mps_command) cm_link; struct mps_softc *cm_sc; void *cm_data; u_int cm_length; + struct uio cm_uio; + struct iovec cm_iovec[MPS_IOVEC_COUNT]; + u_int cm_max_segs; u_int cm_sglsize; MPI2_SGE_IO_UNION *cm_sge; uint8_t *cm_req; @@ -82,6 +90,8 @@ struct mps_command { #define MPS_CM_FLAGS_DATAIN (1 << 4) #define MPS_CM_FLAGS_WAKEUP (1 << 5) #define MPS_CM_FLAGS_ACTIVE (1 << 6) +#define MPS_CM_FLAGS_USE_UIO (1 << 7) +#define MPS_CM_FLAGS_SMP_PASS (1 << 8) u_int cm_state; #define MPS_CM_STATE_FREE 0 #define MPS_CM_STATE_BUSY 1 @@ -238,12 +248,15 @@ mps_free_command(struct mps_softc *sc, struct mps_command *cm) { struct mps_chain *chain, *chain_temp; - if (cm->cm_reply != NULL) + if (cm->cm_reply != NULL) { mps_free_reply(sc, cm->cm_reply_data); + cm->cm_reply = NULL; + } cm->cm_flags = 0; cm->cm_complete = NULL; cm->cm_complete_data = NULL; cm->cm_targ = 0; + cm->cm_max_segs = 0; cm->cm_state = MPS_CM_STATE_FREE; TAILQ_FOREACH_SAFE(chain, &cm->cm_chain_list, chain_link, chain_temp) { TAILQ_REMOVE(&cm->cm_chain_list, chain, chain_link); @@ -368,6 +381,7 @@ int mps_map_command(struct mps_softc *sc, struct mps_command *cm); int mps_read_config_page(struct mps_softc *, struct mps_config_params *); int mps_write_config_page(struct mps_softc *, struct mps_config_params *); void mps_memaddr_cb(void *, bus_dma_segment_t *, int , int ); +void mpi_init_sge(struct mps_command *cm, void *req, void *sge); int mps_attach_user(struct mps_softc *); void mps_detach_user(struct mps_softc *); |