⬢ DragonFlyBSD Kernel Audit
DF-4701 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/raid/twa/tw_cl_init.c b/sys/dev/raid/twa/tw_cl_init.c
--- a/sys/dev/raid/twa/tw_cl_init.c
+++ b/sys/dev/raid/twa/tw_cl_init.c
@@ -570,7 +570,18 @@
 
 	tw_cli_disable_interrupts(ctlr);
 
-	/* Let the controller know that we are going down. */
+	/*
+	 * Let the controller know that we are going down.  If this handshake
+	 * fails (e.g. wedged firmware), the controller is nonetheless being
+	 * torn down: ctlr->active is already FALSE, the OSL has already torn
+	 * down interrupts and cancelled the watchdog, and we destroy the CL
+	 * locks below regardless.  Propagating the error makes twa_detach()
+	 * abort *after* all that, leaving a still-attached, half-shut-down
+	 * device whose I/O entry paths have no ctlr->active gate -- every
+	 * subsequent (unprivileged) disk I/O is then submitted to a dead
+	 * controller until reboot.  The failure is logged as an event; make
+	 * detach continue so the teardown completes.
+	 */
 	if ((error = tw_cli_init_connection(ctlr, TWA_SHUTDOWN_MESSAGE_CREDITS,
 			0, 0, 0, 0, 0, TW_CL_NULL, TW_CL_NULL, TW_CL_NULL,
 			TW_CL_NULL, TW_CL_NULL)))
@@ -588,7 +599,12 @@
 	tw_osl_destroy_lock(ctlr_handle, ctlr->io_lock);
 
 ret:
-	return(error);
+	/*
+	 * The CL context is torn down (or destined to be): never report the
+	 * handshake error, since the OSL treats non-zero as "device still
+	 * fully usable" and aborts the detach in the worst possible state.
+	 */
+	return(TW_OSL_ESUCCESS);
 }
 
 
diff --git a/sys/dev/raid/twa/tw_cl_io.c b/sys/dev/raid/twa/tw_cl_io.c
--- a/sys/dev/raid/twa/tw_cl_io.c
+++ b/sys/dev/raid/twa/tw_cl_io.c
@@ -81,6 +81,15 @@
 	ctlr = (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
 
 	/*
+	 * Refuse work on a controller that has been shut down.  ctlr->active
+	 * is cleared by tw_cl_shutdown_ctlr() before the device goes away;
+	 * without this gate, requests keep being submitted to a dead firmware
+	 * queue (CAM timeout storms) if a detach/shutdown interleaves with I/O.
+	 */
+	if (!(ctlr->active))
+		return(TW_OSL_EBUSY);
+
+	/*
 	 * If working with a firmware version that does not support multiple
 	 * luns, and this request is directed at a non-zero lun, error it
 	 * back right away.
@@ -276,6 +285,10 @@
 
 	ctlr = (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
 
+	/* Refuse passthru on a shut-down controller (see tw_cl_start_io). */
+	if (!(ctlr->active))
+		return(TW_OSL_EBUSY);
+
 	if ((req = tw_cli_get_request(ctlr
 		)) == TW_CL_NULL) {
 		tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),