Fix DF-4253: SATA-ID timeout recovery frees in-flight commands and can reset the wrong target. Inside mprsas_add_device() (mpr_sas_lsi.c:992-1026) the recovery scan matches ANY command carrying MPR_CM_FLAGS_SATA_ID_TIMEOUT -- including commands belonging to a different target -- sends TARGET_RESET via the CURRENTLY-ADDED target, and the out: loop immediately mpr_free_command()s the flagged command: even when mprsas_alloc_tm() failed (no abort of any kind sent) the firmware may still complete that SMID later; the freed SMID can be handed to a new request (verified live with the real mpr_alloc_command(): the freed command is returned immediately) and a late reply is then matched to the new command by the SMID-indexed dispatch in mpr_intr_locked(). targ->tm is also overwritten without a NULL check (the alloc-failure branch even assigns NULL over it), orphaning any outstanding TM. Fix: (a) only take the targ->tm slot when free; (b) release the flagged command only when a Target Reset was actually sent in this call. Replica-verified live with the REAL mprsas_alloc_tm and the REAL header-inline mpr_free_command/mpr_alloc_command. Note: the wrong-target selection itself (abort-by-handle would be correct) is a deeper design issue; this fix closes the memory-safety half (free-while-in-flight + TM orphaning). --- a/sys/dev/raid/mpr/mpr_sas_lsi.c +++ b/sys/dev/raid/mpr/mpr_sas_lsi.c @@ -810,6 +810,7 @@ struct mprsas_lun *lun; u8 is_SATA_SSD = 0; struct mpr_command *cm; + int reset_sent = 0; sassc = sc->sassc; mprsas_startup_increment(sassc); @@ -995,13 +996,15 @@ targ->timeouts++; cm->cm_state = MPR_CM_STATE_TIMEDOUT; - if ((targ->tm = mprsas_alloc_tm(sc)) != NULL) { + if ((targ->tm == NULL) && + ((targ->tm = mprsas_alloc_tm(sc)) != NULL)) { mpr_dprint(sc, MPR_INFO, "%s: sending Target " "Reset for stuck SATA identify command " "(cm = %p)\n", __func__, cm); targ->tm->cm_targ = targ; mprsas_send_reset(sc, targ->tm, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET); + reset_sent = 1; } else { mpr_dprint(sc, MPR_ERROR, "Failed to allocate " "tm for Target Reset after SATA ID command " @@ -1016,12 +1019,17 @@ } out: /* - * Free the commands that may not have been freed from the SATA ID call + * Free the commands that may not have been freed from the SATA ID call. + * Only release a timed-out command back to the pool once a Target + * Reset has actually been sent to abort it; otherwise the firmware + * may still complete the command later, after its SMID has been + * reused for a new request. */ for (i = 1; i < sc->num_reqs; i++) { cm = &sc->commands[i]; if (cm->cm_flags & MPR_CM_FLAGS_SATA_ID_TIMEOUT) { - mpr_free_command(sc, cm); + if (reset_sent) + mpr_free_command(sc, cm); } } mprsas_startup_decrement(sassc);