summaryrefslogtreecommitdiffstats
path: root/sys/dev/mps/mps_sas.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/mps/mps_sas.c')
-rw-r--r--sys/dev/mps/mps_sas.c192
1 files changed, 182 insertions, 10 deletions
diff --git a/sys/dev/mps/mps_sas.c b/sys/dev/mps/mps_sas.c
index 0e7ce03..1c58bb2 100644
--- a/sys/dev/mps/mps_sas.c
+++ b/sys/dev/mps/mps_sas.c
@@ -121,6 +121,7 @@ struct mpssas_devprobe {
MALLOC_DEFINE(M_MPSSAS, "MPSSAS", "MPS SAS memory");
+static __inline int mpssas_set_lun(uint8_t *lun, u_int ccblun);
static struct mpssas_target * mpssas_alloc_target(struct mpssas_softc *,
struct mpssas_target *);
static struct mpssas_target * mpssas_find_target(struct mpssas_softc *, int,
@@ -163,6 +164,43 @@ static void mpssas_resetdev_complete(struct mps_softc *, struct mps_command *);
static void mpssas_freeze_device(struct mpssas_softc *, struct mpssas_target *);
static void mpssas_unfreeze_device(struct mpssas_softc *, struct mpssas_target *) __unused;
+/*
+ * Abstracted so that the driver can be backwards and forwards compatible
+ * with future versions of CAM that will provide this functionality.
+ */
+#define MPS_SET_LUN(lun, ccblun) \
+ mpssas_set_lun(lun, ccblun)
+
+static __inline int
+mpssas_set_lun(uint8_t *lun, u_int ccblun)
+{
+ uint64_t *newlun;
+
+ newlun = (uint64_t *)lun;
+ *newlun = 0;
+ if (ccblun <= 0xff) {
+ /* Peripheral device address method, LUN is 0 to 255 */
+ lun[1] = ccblun;
+ } else if (ccblun <= 0x3fff) {
+ /* Flat space address method, LUN is <= 16383 */
+ scsi_ulto2b(ccblun, lun);
+ lun[0] |= 0x40;
+ } else if (ccblun <= 0xffffff) {
+ /* Extended flat space address method, LUN is <= 16777215 */
+ scsi_ulto3b(ccblun, &lun[1]);
+ /* Extended Flat space address method */
+ lun[0] = 0xc0;
+ /* Length = 1, i.e. LUN is 3 bytes long */
+ lun[0] |= 0x10;
+ /* Extended Address Method */
+ lun[0] |= 0x02;
+ } else {
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
static struct mpssas_target *
mpssas_alloc_target(struct mpssas_softc *sassc, struct mpssas_target *probe)
{
@@ -448,7 +486,10 @@ mpssas_prepare_remove(struct mpssas_softc *sassc, MPI2_EVENT_SAS_TOPO_PHY_ENTRY
return;
}
+ mps_dprint(sc, MPS_INFO, "Preparing to remove target %d\n", targ->tid);
+
req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
+ memset(req, 0, sizeof(*req));
req->DevHandle = targ->handle;
req->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
@@ -469,6 +510,7 @@ mpssas_remove_device(struct mps_softc *sc, struct mps_command *cm)
MPI2_SCSI_TASK_MANAGE_REPLY *reply;
MPI2_SAS_IOUNIT_CONTROL_REQUEST *req;
struct mpssas_target *targ;
+ struct mps_command *next_cm;
uint16_t handle;
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
@@ -478,6 +520,18 @@ mpssas_remove_device(struct mps_softc *sc, struct mps_command *cm)
mpssas_complete_tm_request(sc, cm, /*free_cm*/ 0);
+ /*
+ * Currently there should be no way we can hit this case. It only
+ * happens when we have a failure to allocate chain frames, and
+ * task management commands don't have S/G lists.
+ */
+ if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+ mps_printf(sc, "%s: cm_flags = %#x for remove of handle %#04x! "
+ "This should not happen!\n", __func__, cm->cm_flags,
+ handle);
+ return;
+ }
+
if (reply->IOCStatus != MPI2_IOCSTATUS_SUCCESS) {
mps_printf(sc, "Failure 0x%x reseting device 0x%04x\n",
reply->IOCStatus, handle);
@@ -485,11 +539,13 @@ mpssas_remove_device(struct mps_softc *sc, struct mps_command *cm)
return;
}
- mps_printf(sc, "Reset aborted %d commands\n", reply->TerminationCount);
+ mps_dprint(sc, MPS_INFO, "Reset aborted %u commands\n",
+ reply->TerminationCount);
mps_free_reply(sc, cm->cm_reply_data);
/* Reuse the existing command */
req = (MPI2_SAS_IOUNIT_CONTROL_REQUEST *)cm->cm_req;
+ memset(req, 0, sizeof(*req));
req->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
req->Operation = MPI2_SAS_OP_REMOVE_DEVICE;
req->DevHandle = handle;
@@ -501,6 +557,17 @@ mpssas_remove_device(struct mps_softc *sc, struct mps_command *cm)
mps_map_command(sc, cm);
mps_dprint(sc, MPS_INFO, "clearing target handle 0x%04x\n", handle);
+ TAILQ_FOREACH_SAFE(cm, &sc->io_list, cm_link, next_cm) {
+ union ccb *ccb;
+
+ if (cm->cm_targ->handle != handle)
+ continue;
+
+ mps_dprint(sc, MPS_INFO, "Completing missed command %p\n", cm);
+ ccb = cm->cm_complete_data;
+ ccb->ccb_h.status = CAM_DEV_NOT_THERE;
+ mpssas_scsiio_complete(sc, cm);
+ }
targ = mpssas_find_target(sc->sassc, 0, handle);
if (targ != NULL) {
targ->handle = 0x0;
@@ -1045,6 +1112,17 @@ mpssas_abort_complete(struct mps_softc *sc, struct mps_command *cm)
req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
+ /*
+ * Currently there should be no way we can hit this case. It only
+ * happens when we have a failure to allocate chain frames, and
+ * task management commands don't have S/G lists.
+ */
+ if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+ mps_printf(sc, "%s: cm_flags = %#x for abort on handle %#04x! "
+ "This should not happen!\n", __func__, cm->cm_flags,
+ req->DevHandle);
+ }
+
mps_printf(sc, "%s: abort request on handle %#04x SMID %d "
"complete\n", __func__, req->DevHandle, req->TaskMID);
@@ -1161,7 +1239,8 @@ mpssas_tm_complete(struct mps_softc *sc, struct mps_command *cm, int error)
resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply;
- resp->ResponseCode = error;
+ if (resp != NULL)
+ resp->ResponseCode = error;
/*
* Call the callback for this command, it will be
@@ -1311,6 +1390,7 @@ mpssas_action_scsiio(struct mpssas_softc *sassc, union ccb *ccb)
}
req = (MPI2_SCSI_IO_REQUEST *)cm->cm_req;
+ bzero(req, sizeof(*req));
req->DevHandle = targ->handle;
req->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
req->MsgFlags = 0;
@@ -1366,14 +1446,12 @@ mpssas_action_scsiio(struct mpssas_softc *sassc, union ccb *ccb)
break;
}
- /* XXX Need to handle multi-level LUNs */
- if (csio->ccb_h.target_lun > 255) {
+ if (MPS_SET_LUN(req->LUN, csio->ccb_h.target_lun) != 0) {
mps_free_command(sc, cm);
ccb->ccb_h.status = CAM_LUN_INVALID;
xpt_done(ccb);
return;
}
- req->LUN[1] = csio->ccb_h.target_lun;
if (csio->ccb_h.flags & CAM_CDB_POINTER)
bcopy(csio->cdb_io.cdb_ptr, &req->CDB.CDB32[0], csio->cdb_len);
@@ -1394,6 +1472,11 @@ mpssas_action_scsiio(struct mpssas_softc *sassc, union ccb *ccb)
cm->cm_complete_data = ccb;
cm->cm_targ = targ;
+ sc->io_cmds_active++;
+ if (sc->io_cmds_active > sc->io_cmds_highwater)
+ sc->io_cmds_highwater = sc->io_cmds_active;
+
+ TAILQ_INSERT_TAIL(&sc->io_list, cm, cm_link);
callout_reset(&cm->cm_callout, (ccb->ccb_h.timeout * hz) / 1000,
mpssas_scsiio_timeout, cm);
@@ -1413,11 +1496,18 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm)
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
callout_stop(&cm->cm_callout);
+ TAILQ_REMOVE(&sc->io_list, cm, cm_link);
+ sc->io_cmds_active--;
sassc = sc->sassc;
ccb = cm->cm_complete_data;
rep = (MPI2_SCSI_IO_REPLY *)cm->cm_reply;
+ /*
+ * XXX KDM if the chain allocation fails, does it matter if we do
+ * the sync and unload here? It is simpler to do it in every case,
+ * assuming it doesn't cause problems.
+ */
if (cm->cm_data != NULL) {
if (cm->cm_flags & MPS_CM_FLAGS_DATAIN)
dir = BUS_DMASYNC_POSTREAD;
@@ -1427,15 +1517,51 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm)
bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap);
}
- if (sassc->flags & MPSSAS_QUEUE_FROZEN) {
- ccb->ccb_h.flags |= CAM_RELEASE_SIMQ;
- sassc->flags &= ~MPSSAS_QUEUE_FROZEN;
+ if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+ /*
+ * We ran into an error after we tried to map the command,
+ * so we're getting a callback without queueing the command
+ * to the hardware. So we set the status here, and it will
+ * be retained below. We'll go through the "fast path",
+ * because there can be no reply when we haven't actually
+ * gone out to the hardware.
+ */
+ ccb->ccb_h.status |= CAM_REQUEUE_REQ;
+
+ /*
+ * Currently the only error included in the mask is
+ * MPS_CM_FLAGS_CHAIN_FAILED, which means we're out of
+ * chain frames. We need to freeze the queue until we get
+ * a command that completed without this error, which will
+ * hopefully have some chain frames attached that we can
+ * use. If we wanted to get smarter about it, we would
+ * only unfreeze the queue in this condition when we're
+ * sure that we're getting some chain frames back. That's
+ * probably unnecessary.
+ */
+ if ((sassc->flags & MPSSAS_QUEUE_FROZEN) == 0) {
+ xpt_freeze_simq(sassc->sim, 1);
+ sassc->flags |= MPSSAS_QUEUE_FROZEN;
+ mps_printf(sc, "Error sending command, freezing "
+ "SIM queue\n");
+ }
}
/* Take the fast path to completion */
if (cm->cm_reply == NULL) {
- ccb->ccb_h.status = CAM_REQ_CMP;
- ccb->csio.scsi_status = SCSI_STATUS_OK;
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) {
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ ccb->csio.scsi_status = SCSI_STATUS_OK;
+
+ if (sassc->flags & MPSSAS_QUEUE_FROZEN) {
+ ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
+ sassc->flags &= ~MPSSAS_QUEUE_FROZEN;
+ mps_printf(sc, "Unfreezing SIM queue\n");
+ }
+ } else {
+ ccb->ccb_h.status |= CAM_DEV_QFRZN;
+ xpt_freeze_devq(ccb->ccb_h.path, /*count*/ 1);
+ }
mps_free_command(sc, cm);
xpt_done(ccb);
return;
@@ -1490,7 +1616,16 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm)
break;
case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
+#if 0
ccb->ccb_h.status = CAM_REQ_ABORTED;
+#endif
+ mps_printf(sc, "(%d:%d:%d) terminated ioc %x scsi %x state %x "
+ "xfer %u\n", xpt_path_path_id(ccb->ccb_h.path),
+ xpt_path_target_id(ccb->ccb_h.path),
+ xpt_path_lun_id(ccb->ccb_h.path),
+ rep->IOCStatus, rep->SCSIStatus, rep->SCSIState,
+ rep->TransferCount);
+ ccb->ccb_h.status = CAM_REQUEUE_REQ;
break;
case MPI2_IOCSTATUS_INVALID_SGL:
mps_print_scsiio_cmd(sc, cm);
@@ -1544,6 +1679,15 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm)
if (rep->SCSIState & MPI2_SCSI_STATE_RESPONSE_INFO_VALID)
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ if (sassc->flags & MPSSAS_QUEUE_FROZEN) {
+ ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
+ sassc->flags &= ~MPSSAS_QUEUE_FROZEN;
+ mps_printf(sc, "Command completed, unfreezing SIM queue\n");
+ }
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ ccb->ccb_h.status |= CAM_DEV_QFRZN;
+ xpt_freeze_devq(ccb->ccb_h.path, /*count*/ 1);
+ }
mps_free_command(sc, cm);
xpt_done(ccb);
}
@@ -1558,6 +1702,20 @@ mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm)
union ccb *ccb;
ccb = cm->cm_complete_data;
+
+ /*
+ * Currently there should be no way we can hit this case. It only
+ * happens when we have a failure to allocate chain frames, and SMP
+ * commands require two S/G elements only. That should be handled
+ * in the standard request size.
+ */
+ if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+ mps_printf(sc, "%s: cm_flags = %#x on SMP request!\n",
+ __func__, cm->cm_flags);
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ goto bailout;
+ }
+
rpl = (MPI2_SMP_PASSTHROUGH_REPLY *)cm->cm_reply;
if (rpl == NULL) {
mps_dprint(sc, MPS_INFO, "%s: NULL cm_reply!\n", __func__);
@@ -1937,6 +2095,19 @@ mpssas_resetdev_complete(struct mps_softc *sc, struct mps_command *cm)
resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply;
ccb = cm->cm_complete_data;
+ if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+ MPI2_SCSI_TASK_MANAGE_REQUEST *req;
+
+ req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
+
+ mps_printf(sc, "%s: cm_flags = %#x for reset of handle %#04x! "
+ "This should not happen!\n", __func__, cm->cm_flags,
+ req->DevHandle);
+
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ goto bailout;
+ }
+
printf("resetdev complete IOCStatus= 0x%x ResponseCode= 0x%x\n",
resp->IOCStatus, resp->ResponseCode);
@@ -1945,6 +2116,7 @@ mpssas_resetdev_complete(struct mps_softc *sc, struct mps_command *cm)
else
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+bailout:
mpssas_complete_tm_request(sc, cm, /*free_cm*/ 1);
xpt_done(ccb);
OpenPOWER on IntegriCloud