fix for DF-4178: twe_immediate_request timeout path releases a request that is still posted to the controller and still linked on the busy queue. Two hunks: 1. twe_immediate_request: if the 5-second poll times out, unmap, remove the request from the busy queue and mark it TWE_CMD_ERROR before returning, so the caller's twe_release_request() cannot double-link the request (busy + free) and a later completion cannot process a reused request. 2. twe_done: if a response arrives for a request that is not busy (the "completion event for nonbusy command" case - e.g. a late completion of a timed-out, already-released request), consume the response and skip all processing instead of falling through to twe_remove_busy() on a request that is no longer on the busy queue (TAILQ corruption). Residual (documented, not fixed here): after a timeout the caller still frees the 512-byte parameter buffer that the controller may late-write; fully closing that requires aborting the command on the controller (TWE_OP_ABORT) and waiting for its completion before freeing. --- a/sys/dev/raid/twe/twe.c +++ b/sys/dev/raid/twe/twe.c @@ -1007,6 +1007,19 @@ if (usetmp && (tr->tr_data != NULL)) bcopy(sc->twe_immediate, tr->tr_data, tr->tr_length); + /* + * Timed out. The command may still be posted to the controller and + * linked on the busy queue; take it off the queue and mark it failed + * so that the caller may safely release the request. If the + * controller completes the command after we have given up, twe_done() + * will observe a nonbusy request and drop the stale response instead + * of corrupting the queue linkage. + */ + if (tr->tr_status == TWE_CMD_BUSY) { + twe_unmap_request(tr); + twe_remove_busy(tr); + tr->tr_status = TWE_CMD_ERROR; + } return(tr->tr_status != TWE_CMD_COMPLETE); } @@ -1200,8 +1213,12 @@ #ifdef TWE_DEBUG cmd = TWE_FIND_COMMAND(tr); #endif - if (tr->tr_status != TWE_CMD_BUSY) + if (tr->tr_status != TWE_CMD_BUSY) { twe_printf(sc, "completion event for nonbusy command\n"); + /* stale response for a request that timed out and was + * released/reused: consume it, never touch the request */ + continue; + } tr->tr_status = TWE_CMD_COMPLETE; debug(3, "completed request id %d with status %d", cmd->generic.request_id, cmd->generic.status);