summaryrefslogtreecommitdiffstats
path: root/sys/dev/mps/mps_sas_lsi.c
diff options
context:
space:
mode:
authorsmh <smh@FreeBSD.org>2014-07-30 17:58:17 +0000
committersmh <smh@FreeBSD.org>2014-07-30 17:58:17 +0000
commit9af66cbda234ef1061db94471badefecc48db86c (patch)
treeb8ed301988692b33c742cb962d23d36fcf8dc7a1 /sys/dev/mps/mps_sas_lsi.c
parentf3c5b17432cd19dfe1409bcb499d632c2b98e3a4 (diff)
downloadFreeBSD-src-9af66cbda234ef1061db94471badefecc48db86c.zip
FreeBSD-src-9af66cbda234ef1061db94471badefecc48db86c.tar.gz
Bring in LSI's phase16 - phase18 changes
* Implements Start Stop Unit for SATA direct-attach devices in IR mode to avoid data corruption. * Use CAM_DEV_NOT_THERE instead of CAM_SEL_TIMEOUT and CAM_TID_INVALID Obtained from: LSI MFC after: 2 weeks
Diffstat (limited to 'sys/dev/mps/mps_sas_lsi.c')
-rw-r--r--sys/dev/mps/mps_sas_lsi.c144
1 files changed, 141 insertions, 3 deletions
diff --git a/sys/dev/mps/mps_sas_lsi.c b/sys/dev/mps/mps_sas_lsi.c
index c219be14..5836329 100644
--- a/sys/dev/mps/mps_sas_lsi.c
+++ b/sys/dev/mps/mps_sas_lsi.c
@@ -120,6 +120,9 @@ int mpssas_get_sas_address_for_sata_disk(struct mps_softc *sc,
u64 *sas_address, u16 handle, u32 device_info);
static int mpssas_volume_add(struct mps_softc *sc,
u16 handle);
+static void mpssas_SSU_to_SATA_devices(struct mps_softc *sc);
+static void mpssas_stop_unit_done(struct cam_periph *periph,
+ union ccb *done_ccb);
void
mpssas_evt_handler(struct mps_softc *sc, uintptr_t data,
@@ -910,6 +913,138 @@ out:
}
/**
+ * mpssas_SSU_to_SATA_devices
+ * @sc: per adapter object
+ *
+ * Looks through the target list and issues a StartStopUnit SCSI command to each
+ * SATA direct-access device. This helps to ensure that data corruption is
+ * avoided when the system is being shut down. This must be called after the IR
+ * System Shutdown RAID Action is sent if in IR mode.
+ *
+ * Return nothing.
+ */
+static void
+mpssas_SSU_to_SATA_devices(struct mps_softc *sc)
+{
+ struct mpssas_softc *sassc = sc->sassc;
+ union ccb *ccb;
+ path_id_t pathid = cam_sim_path(sassc->sim);
+ target_id_t targetid;
+ struct mpssas_target *target;
+ struct mpssas_lun *lun;
+ char path_str[64];
+ struct timeval cur_time, start_time;
+
+ /*
+ * For each LUN of each target, issue a StartStopUnit command to stop
+ * the device.
+ */
+ sc->SSU_started = TRUE;
+ sc->SSU_refcount = 0;
+ for (targetid = 0; targetid < sc->facts->MaxTargets; targetid++) {
+ target = &sassc->targets[targetid];
+ if (target->handle == 0x0) {
+ continue;
+ }
+
+ SLIST_FOREACH(lun, &target->luns, lun_link) {
+ ccb = xpt_alloc_ccb_nowait();
+ if (ccb == NULL) {
+ mps_dprint(sc, MPS_FAULT, "Unable to alloc CCB "
+ "to stop unit.\n");
+ return;
+ }
+
+ /*
+ * The stop_at_shutdown flag will be set if this LUN is
+ * a SATA direct-access end device.
+ */
+ if (lun->stop_at_shutdown) {
+ if (xpt_create_path(&ccb->ccb_h.path,
+ xpt_periph, pathid, targetid,
+ lun->lun_id) != CAM_REQ_CMP) {
+ mps_dprint(sc, MPS_FAULT, "Unable to "
+ "create LUN path to stop unit.\n");
+ xpt_free_ccb(ccb);
+ return;
+ }
+ xpt_path_string(ccb->ccb_h.path, path_str,
+ sizeof(path_str));
+
+ mps_dprint(sc, MPS_INFO, "Sending StopUnit: "
+ "path %s handle %d\n", path_str,
+ target->handle);
+
+ /*
+ * Issue a START STOP UNIT command for the LUN.
+ * Increment the SSU counter to be used to
+ * count the number of required replies.
+ */
+ mps_dprint(sc, MPS_INFO, "Incrementing SSU "
+ "count\n");
+ sc->SSU_refcount++;
+ ccb->ccb_h.target_id =
+ xpt_path_target_id(ccb->ccb_h.path);
+ ccb->ccb_h.target_lun = lun->lun_id;
+ ccb->ccb_h.ppriv_ptr1 = sassc;
+ scsi_start_stop(&ccb->csio,
+ /*retries*/0,
+ mpssas_stop_unit_done,
+ MSG_SIMPLE_Q_TAG,
+ /*start*/FALSE,
+ /*load/eject*/0,
+ /*immediate*/FALSE,
+ MPS_SENSE_LEN,
+ /*timeout*/10000);
+ xpt_action(ccb);
+ }
+ }
+ }
+
+ /*
+ * Wait until all of the SSU commands have completed or time has
+ * expired (60 seconds). pause for 100ms each time through. If any
+ * command times out, the target will be reset in the SCSI command
+ * timeout routine.
+ */
+ getmicrotime(&start_time);
+ while (sc->SSU_refcount) {
+ pause("mpswait", hz/10);
+
+ getmicrotime(&cur_time);
+ if ((cur_time.tv_sec - start_time.tv_sec) > 60) {
+ mps_dprint(sc, MPS_FAULT, "Time has expired waiting "
+ "for SSU commands to complete.\n");
+ break;
+ }
+ }
+}
+
+static void
+mpssas_stop_unit_done(struct cam_periph *periph, union ccb *done_ccb)
+{
+ struct mpssas_softc *sassc;
+ char path_str[64];
+
+ sassc = (struct mpssas_softc *)done_ccb->ccb_h.ppriv_ptr1;
+
+ xpt_path_string(done_ccb->ccb_h.path, path_str, sizeof(path_str));
+ mps_dprint(sassc->sc, MPS_INFO, "Completing stop unit for %s\n",
+ path_str);
+
+ if (done_ccb == NULL)
+ return;
+
+ /*
+ * Nothing more to do except free the CCB and path. If the command
+ * timed out, an abort reset, then target reset will be issued during
+ * the SCSI Command process.
+ */
+ xpt_free_path(done_ccb->ccb_h.path);
+ xpt_free_ccb(done_ccb);
+}
+
+/**
* mpssas_ir_shutdown - IR shutdown notification
* @sc: per adapter object
*
@@ -933,7 +1068,7 @@ mpssas_ir_shutdown(struct mps_softc *sc)
/* is IR firmware build loaded? */
if (!sc->ir_firmware)
- return;
+ goto out;
/* are there any volumes? Look at IR target IDs. */
// TODO-later, this should be looked up in the RAID config structure
@@ -958,11 +1093,11 @@ mpssas_ir_shutdown(struct mps_softc *sc)
}
if (!found_volume)
- return;
+ goto out;
if ((cm = mps_alloc_command(sc)) == NULL) {
printf("%s: command alloc failed\n", __func__);
- return;
+ goto out;
}
action = (MPI2_RAID_ACTION_REQUEST *)cm->cm_req;
@@ -978,4 +1113,7 @@ mpssas_ir_shutdown(struct mps_softc *sc)
*/
if (cm)
mps_free_command(sc, cm);
+
+out:
+ mpssas_SSU_to_SATA_devices(sc);
}
OpenPOWER on IntegriCloud