DF-4701 / fix.diff
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(), |