[prev in list] [next in list] [prev in thread] [next in thread] 

List:       freebsd-scsi
Subject:    LSI mps phase16 -> phase 18 driver changes patch
From:       "Steven Hartland" <steven () multiplay ! co ! uk>
Date:       2014-04-01 13:40:24
Message-ID: 84D0D5ADFC374E3AB7EFB51DC941411A () multiplay ! co ! uk
[Download RAW message or body]

Hi guys attached is a patch which updates our mps with the changes
LSI have implemented between phase 16 (our last sync up) and
phase 18 (current latest LSI release) drivers.

The main change seems to be the addition Start Stop Unit for
SATA direct-attach devices in IR mode to avoid data corruption.

Any objections to committing this to HEAD?

    Regards
    Steve
["mps-phase16-phase18.patch" (application/octet-stream)]

Import LSI phase16 - phase18 changes
* Implements Start Stop Unit for SATA direct-attach devices in IR mode to avoid
  data corruption.
--- sys/dev/mps/mps_sas.c.orig	2014-04-01 11:05:42.538799904 +0000
+++ sys/dev/mps/mps_sas.c	2014-04-01 11:36:50.202671306 +0000
@@ -188,6 +188,16 @@
 }
 
 void
+mpssas_release_simq_reinit(struct mpssas_softc *sassc)
+{
+	if (sassc->flags & MPSSAS_QUEUE_FROZEN) {
+		sassc->flags &= ~MPSSAS_QUEUE_FROZEN;
+		xpt_release_simq(sassc->sim, 1);
+		mps_dprint(sassc->sc, MPS_INFO, "Unfreezing SIM queue\n");
+	}
+}
+
+void
 mpssas_startup_decrement(struct mpssas_softc *sassc)
 {
 	MPS_FUNCTRACE(sassc->sc);
@@ -995,7 +1005,7 @@
 
 		targ = &sassc->targets[cts->ccb_h.target_id];
 		if (targ->handle == 0x0) {
-			cts->ccb_h.status = CAM_SEL_TIMEOUT;
+			cts->ccb_h.status = CAM_DEV_NOT_THERE;
 			break;
 		}
 
@@ -1112,6 +1122,14 @@
 			wakeup(cm);
 			completed = 1;
 		}
+
+		if (cm->cm_sc->io_cmds_active != 0) {
+			cm->cm_sc->io_cmds_active--;
+		} else {
+			mps_dprint(cm->cm_sc, MPS_INFO, "Warning: "
+			    "io_cmds_active is out of sync - resynching to "
+			    "0\n");
+		}
 		
 		if ((completed == 0) && (cm->cm_state != MPS_CM_STATE_FREE)) {
 			/* this should never happen, but if it does, log */
@@ -1643,14 +1661,14 @@
 	if (targ->handle == 0x0) {
 		mps_dprint(sc, MPS_ERROR, "%s NULL handle for target %u\n", 
 		    __func__, csio->ccb_h.target_id);
-		csio->ccb_h.status = CAM_SEL_TIMEOUT;
+		csio->ccb_h.status = CAM_DEV_NOT_THERE;
 		xpt_done(ccb);
 		return;
 	}
 	if (targ->flags & MPS_TARGET_FLAGS_RAID_COMPONENT) {
 		mps_dprint(sc, MPS_ERROR, "%s Raid component no SCSI IO "
 		    "supported %u\n", __func__, csio->ccb_h.target_id);
-		csio->ccb_h.status = CAM_TID_INVALID;
+		csio->ccb_h.status = CAM_DEV_NOT_THERE;
 		xpt_done(ccb);
 		return;
 	}
@@ -1681,13 +1699,16 @@
 
 	if ((sc->mps_flags & MPS_FLAGS_SHUTDOWN) != 0) {
 		mps_dprint(sc, MPS_INFO, "%s shutting down\n", __func__);
-		csio->ccb_h.status = CAM_TID_INVALID;
+		csio->ccb_h.status = CAM_DEV_NOT_THERE;
 		xpt_done(ccb);
 		return;
 	}
 
 	cm = mps_alloc_command(sc);
-	if (cm == NULL) {
+	if (cm == NULL || (sc->mps_flags & MPS_FLAGS_DIAGRESET)) {
+		if (cm != NULL) {
+			mps_free_command(sc, cm);
+		}
 		if ((sassc->flags & MPSSAS_QUEUE_FROZEN) == 0) {
 			xpt_freeze_simq(sassc->sim, 1);
 			sassc->flags |= MPSSAS_QUEUE_FROZEN;
@@ -2166,6 +2187,18 @@
 		}
 	}
 
+	/*
+	 * If this is a Start Stop Unit command and it was issued by the driver
+	 * during shutdown, decrement the refcount to account for all of the
+	 * commands that were sent.  All SSU commands should be completed before
+	 * shutdown completes, meaning SSU_refcount will be 0 after SSU_started
+	 * is TRUE.
+	 */
+	if (sc->SSU_started && (csio->cdb_io.cdb_bytes[0] == START_STOP_UNIT)) {
+		mps_dprint(sc, MPS_INFO, "Decrementing SSU count.\n");
+		sc->SSU_refcount--;
+	}
+
 	/* Take the fast path to completion */
 	if (cm->cm_reply == NULL) {
 		if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) {
@@ -2990,7 +3023,7 @@
 			mps_dprint(sc, MPS_ERROR,
 				   "%s: handle %d does not have a valid "
 				   "parent handle!\n", __func__, targ->handle);
-			ccb->ccb_h.status = CAM_REQ_INVALID;
+			ccb->ccb_h.status = CAM_DEV_NOT_THERE;
 			goto bailout;
 		}
 #ifdef OLD_MPS_PROBE
@@ -3001,7 +3034,7 @@
 			mps_dprint(sc, MPS_ERROR,
 				   "%s: handle %d does not have a valid "
 				   "parent target!\n", __func__, targ->handle);
-			ccb->ccb_h.status = CAM_REQ_INVALID;
+			ccb->ccb_h.status = CAM_DEV_NOT_THERE;
 			goto bailout;
 		}
 
@@ -3011,7 +3044,7 @@
 				   "%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;
+			ccb->ccb_h.status = CAM_DEV_NOT_THERE;
 			goto bailout;
 
 		}
@@ -3024,7 +3057,7 @@
 				   "%s: handle %d parent %d does not "
 				   "have an SMP target!\n", __func__,
 				   targ->handle, targ->parent_handle);
-			ccb->ccb_h.status = CAM_REQ_INVALID;
+			ccb->ccb_h.status = CAM_DEV_NOT_THERE;
 			goto bailout;
 
 		}
@@ -3033,7 +3066,7 @@
 				   "%s: handle %d parent handle %d does "
 				   "not have a valid SAS address!\n",
 				   __func__, targ->handle, targ->parent_handle);
-			ccb->ccb_h.status = CAM_REQ_INVALID;
+			ccb->ccb_h.status = CAM_DEV_NOT_THERE;
 			goto bailout;
 		}
 
@@ -3046,7 +3079,7 @@
 		mps_dprint(sc, MPS_INFO,
 			   "%s: unable to find SAS address for handle %d\n",
 			   __func__, targ->handle);
-		ccb->ccb_h.status = CAM_REQ_INVALID;
+		ccb->ccb_h.status = CAM_DEV_NOT_THERE;
 		goto bailout;
 	}
 	mpssas_send_smpcmd(sassc, ccb, sasaddr);
@@ -3348,6 +3381,20 @@
 	}
 
 	xpt_path_string(local_path, path_str, sizeof(path_str));
+
+	/*
+	 * If this is a SATA direct-access end device,
+	 * mark it so that a SCSI StartStopUnit command
+	 * will be sent to it when the driver is being
+	 * shutdown.
+	 */
+	if ((cgd.inq_data.device == T_DIRECT) && 
+		(target->devinfo & MPI2_SAS_DEVICE_INFO_SATA_DEVICE) &&
+		((target->devinfo & MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) ==
+		MPI2_SAS_DEVICE_INFO_END_DEVICE)) {
+		lun->stop_at_shutdown = TRUE;
+	}
+
 	mps_dprint(sc, MPS_INFO, "Sending read cap: path %s handle %d\n",
 	    path_str, target->handle);
 
--- sys/dev/mps/mps_sas_lsi.c.orig	2014-03-26 17:49:01.000000000 +0000
+++ sys/dev/mps/mps_sas_lsi.c	2014-04-01 11:06:42.158795951 +0000
@@ -120,6 +120,9 @@
     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 @@
 }
 
 /**
+ * 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 @@
 
 	/* 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 @@
 	}
 
 	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 @@
 	 */
 	if (cm)
 		mps_free_command(sc, cm);
+
+out:
+	mpssas_SSU_to_SATA_devices(sc);
 }
--- sys/dev/mps/mps_sas.h.orig	2014-03-26 17:49:01.000000000 +0000
+++ sys/dev/mps/mps_sas.h	2014-04-01 11:06:42.157795822 +0000
@@ -35,6 +35,7 @@
 	lun_id_t	lun_id;
 	uint8_t		eedp_formatted;
 	uint32_t	eedp_block_size;
+	uint8_t		stop_at_shutdown;
 };
 
 struct mpssas_target {
@@ -154,6 +155,7 @@
 void mpssas_discovery_end(struct mpssas_softc *sassc);
 void mpssas_startup_increment(struct mpssas_softc *sassc);
 void mpssas_startup_decrement(struct mpssas_softc *sassc);
+void mpssas_release_simq_reinit(struct mpssas_softc *sassc);
 
 struct mps_command * mpssas_alloc_tm(struct mps_softc *sc);
 void mpssas_free_tm(struct mps_softc *sc, struct mps_command *tm);
--- sys/dev/mps/mps.c.orig	2014-04-01 11:05:42.598799869 +0000
+++ sys/dev/mps/mps.c	2014-04-01 11:51:15.083611560 +0000
@@ -141,6 +141,7 @@
 {
 	uint32_t reg;
 	int i, error, tries = 0;
+	uint8_t first_wait_done = FALSE;
 
 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
 
@@ -183,15 +184,32 @@
 
 	/* Wait up to 300 seconds in 50ms intervals */
 	error = ETIMEDOUT;
-	for (i = 0; i < 60000; i++) {
-		/* wait 50 msec */
-		if (mtx_owned(&sc->mps_mtx) && sleep_flag == CAN_SLEEP)
-			msleep(&sc->msleep_fake_chan, &sc->mps_mtx, 0,
-			    "mpsdiag", hz/20);
-		else if (sleep_flag == CAN_SLEEP)
-			pause("mpsdiag", hz/20);
-		else
-			DELAY(50 * 1000);
+	for (i = 0; i < 6000; i++) {
+		/*
+		 * Wait 50 msec. If this is the first time through, wait 256
+		 * msec to satisfy Diag Reset timing requirements.
+		 */
+		if (first_wait_done) {
+			if (mtx_owned(&sc->mps_mtx) && sleep_flag == CAN_SLEEP)
+				msleep(&sc->msleep_fake_chan, &sc->mps_mtx, 0,
+				    "mpsdiag", hz/20);
+			else if (sleep_flag == CAN_SLEEP)
+				pause("mpsdiag", hz/20);
+			else
+				DELAY(50 * 1000);
+		} else {
+			DELAY(256 * 1000);
+			first_wait_done = TRUE;
+		}
+		/*
+		 * Check for the RESET_ADAPTER bit to be cleared first, then
+		 * wait for the RESET state to be cleared, which takes a little
+		 * longer.
+		 */
+		reg = mps_regread(sc, MPI2_HOST_DIAGNOSTIC_OFFSET);
+		if (reg & MPI2_DIAG_RESET_ADAPTER) {
+			continue;
+		}
 		reg = mps_regread(sc, MPI2_DOORBELL_OFFSET);
 		if ((reg & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_RESET) {
 			error = 0;
@@ -237,7 +255,7 @@
 	sleep_flags = (sc->mps_flags & MPS_FLAGS_ATTACH_DONE)
 					? CAN_SLEEP:NO_SLEEP;
 	error = 0;
-	while (tries++ < 5) {
+	while (tries++ < 1200) {
 		reg = mps_regread(sc, MPI2_DOORBELL_OFFSET);
 		mps_dprint(sc, MPS_INIT, "Doorbell= 0x%x\n", reg);
 
@@ -679,6 +697,9 @@
 mps_reinit(struct mps_softc *sc)
 {
 	int error;
+	struct mpssas_softc *sassc;
+
+	sassc = sc->sassc;
 
 	MPS_FUNCTRACE(sc);
 
@@ -759,6 +780,8 @@
 	mps_dprint(sc, MPS_INFO, "%s finished sc %p post %u free %u\n", 
 	    __func__, sc, sc->replypostindex, sc->replyfreeindex);
 
+	mpssas_release_simq_reinit(sassc);
+
 	return 0;
 }
 
@@ -2533,6 +2556,7 @@
 mps_request_polled(struct mps_softc *sc, struct mps_command *cm)
 {
 	int error, timeout = 0, rc;
+	struct timeval cur_time, start_time;
 
 	error = 0;
 
@@ -2540,22 +2564,33 @@
 	cm->cm_complete = NULL;
 	mps_map_command(sc, cm);
 
+	getmicrotime(&start_time);
 	while ((cm->cm_flags & MPS_CM_FLAGS_COMPLETE) == 0) {
 		mps_intr_locked(sc);
 
-		DELAY(50 * 1000);
-		if (timeout++ > 1000) {
+		if (mtx_owned(&sc->mps_mtx))
+			msleep(&sc->msleep_fake_chan, &sc->mps_mtx, 0,
+			    "mpspoll", hz/20);
+		else
+			pause("mpsdiag", hz/20);
+
+		/*
+		 * Check for real-time timeout and fail if more than 60 seconds.
+		 */
+		getmicrotime(&cur_time);
+		timeout = cur_time.tv_sec - start_time.tv_sec;
+		if (timeout > 60) {
 			mps_dprint(sc, MPS_FAULT, "polling failed\n");
 			error = ETIMEDOUT;
 			break;
 		}
 	}
-	
+
 	if (error) {
 		mps_dprint(sc, MPS_FAULT, "Calling Reinit from %s\n", __func__);
 		rc = mps_reinit(sc);
-		mps_dprint(sc, MPS_FAULT, "Reinit %s\n", 
-				(rc == 0) ? "success" : "failed");
+		mps_dprint(sc, MPS_FAULT, "Reinit %s\n", (rc == 0) ? "success" :
+		    "failed");
 	}
 
 	return (error);
--- sys/dev/mps/mpsvar.h.orig	2014-03-26 17:49:01.000000000 +0000
+++ sys/dev/mps/mpsvar.h	2014-04-01 11:30:54.245695971 +0000
@@ -32,7 +32,7 @@
 #ifndef _MPSVAR_H
 #define _MPSVAR_H
 
-#define MPS_DRIVER_VERSION	"16.00.00.00-fbsd"
+#define MPS_DRIVER_VERSION	"18.00.00.00-fbsd"
 
 #define MPS_DB_MAX_WAIT		2500
 
@@ -417,6 +417,10 @@
 
 	char				exclude_ids[80];
 	struct timeval			lastfail;
+
+	/* StartStopUnit command handling at shutdown */
+	uint32_t			SSU_refcount;
+	uint8_t				SSU_started;
 };
 
 struct mps_config_params {


_______________________________________________
freebsd-scsi@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-scsi
To unsubscribe, send any mail to "freebsd-scsi-unsubscribe@freebsd.org"

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic