* [PATCH v3 0/2] fixup handling of timeouts with deferred QCs @ 2026-07-13 4:12 Damien Le Moal 2026-07-13 4:12 ` [PATCH v3 1/2] ata: libata-scsi: terminate deferred commands on time out Damien Le Moal 2026-07-13 4:12 ` [PATCH v3 2/2] scsi: libsas: " Damien Le Moal 0 siblings, 2 replies; 8+ messages in thread From: Damien Le Moal @ 2026-07-13 4:12 UTC (permalink / raw) To: linux-ide, Niklas Cassel, linux-scsi, Martin K . Petersen Cc: Igor Pylypiv, John Garry, Jason Yan This patch series fixes libata and libsas to correctly handles deferred queued commands in case of a timeout error, to avoid excessive delays in waking up the scsi EH task. Igor, My apologies for the churn, but please retest ! Also, I added your Signed-off-by on patch 2 since half of it is yours. Martin, Once reviewed, I or you can take both patches ? Changes from v2: - Modified patch 1 to avoid the problem reported by Sashiko that requeued deferred QCs may be re-ssued immediately by the block layer, thus potentially keeping the device busy. The modification now relies on libata-EH to perform the requeue instead of immediately doing it from the eh_timed_out operation. - Modified patch 2 to use the new helper function defined in patch 1. Changes from v1: - Modified patch 1 to ignore timed out deferred QCs in ata_scsi_requeue_deferred_qc() to let ata_scsi_cmd_error_handler() correctly handle this case. Damien Le Moal (2): ata: libata-scsi: terminate deferred commands on time out scsi: libsas: terminate deferred commands on time out drivers/ata/libata-eh.c | 36 ++++++++++++++++++++++++++++- drivers/ata/libata-scsi.c | 22 ++++++++++++++++++ drivers/scsi/libsas/sas_scsi_host.c | 17 ++++++++++++++ include/linux/libata.h | 4 ++++ include/scsi/libsas.h | 2 ++ 5 files changed, 80 insertions(+), 1 deletion(-) -- 2.55.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 1/2] ata: libata-scsi: terminate deferred commands on time out 2026-07-13 4:12 [PATCH v3 0/2] fixup handling of timeouts with deferred QCs Damien Le Moal @ 2026-07-13 4:12 ` Damien Le Moal 2026-07-13 10:51 ` Niklas Cassel 2026-07-13 18:07 ` Igor Pylypiv 2026-07-13 4:12 ` [PATCH v3 2/2] scsi: libsas: " Damien Le Moal 1 sibling, 2 replies; 8+ messages in thread From: Damien Le Moal @ 2026-07-13 4:12 UTC (permalink / raw) To: linux-ide, Niklas Cassel, linux-scsi, Martin K . Petersen Cc: Igor Pylypiv, John Garry, Jason Yan If a command timeout occurs while we have a deferred non-NCQ command waiting to be issued, the SCSI EH task is not immediately woken up as the waiting deferred command is never issued nor completed, thus leaving this command to always be counted as "busy" for the SCSI host. This results in the test "shost->host_failed != scsi_host_busy(shost))" in the function scsi_error_handler() to always be true, keeping the EH task sleeping. Eventually, when the deferred command also times out, the SCSI EH task is woken up and the timeout processing occurs. Avoid this unnecessary SCSI EH task wake-up additional time using the eh_timed_out SCSI host template operation. The function ata_scsi_eh_timed_out() is introduced to implement this operation. This function calls the new helper ata_eh_schedule_deferred_qc_retry() to schedule a retry through libata EH of all differed queued command, except for a differed queued commands that timed out as that case is handled in ata_scsi_cmd_error_handler(). Since ata_scsi_eh_timed_out() does not directly handles the timeout itself and eventual re-issuing of deferred commands, this function returns SCSI_EH_NOT_HANDLED to have scsi_timeout() continue with the regular timeout handling, using scsi_abort_command() and scsi_eh_scmd_add(), thus preventing the wkae-up delay for SCSI EH task. Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal <dlemoal@kernel.org> --- drivers/ata/libata-eh.c | 35 ++++++++++++++++++++++++++++++++++- drivers/ata/libata-scsi.c | 22 ++++++++++++++++++++++ drivers/ata/libata.h | 2 ++ include/linux/libata.h | 2 ++ 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 05df7ea6954a..8e9d57c6f039 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -546,6 +546,31 @@ static void ata_eh_unload(struct ata_port *ap) spin_unlock_irqrestore(ap->lock, flags); } +void ata_eh_schedule_deferred_qc_retry(struct ata_port *ap, + struct scsi_cmnd *scmd) +{ + struct ata_queued_cmd *qc; + struct ata_link *link; + unsigned long flags; + + /* + * Trigger EH for retrying any deferred qc that is not the queued + * command for scmd. + */ + spin_lock_irqsave(ap->lock, flags); + ata_for_each_link(link, ap, PMP_FIRST) { + qc = link->deferred_qc; + if (!qc || qc->scsicmd == scmd) + continue; + + link->deferred_qc = NULL; + cancel_work(&link->deferred_qc_work); + qc->flags |= ATA_QCFLAG_RETRY; + ata_qc_schedule_eh(qc); + } + spin_unlock_irqrestore(ap->lock, flags); +} + /** * ata_scsi_error - SCSI layer error handler callback * @host: SCSI host on which error occurred @@ -1214,9 +1239,17 @@ static void __ata_eh_qc_complete(struct ata_queued_cmd *qc) struct scsi_cmnd *scmd = qc->scsicmd; unsigned long flags; + + /* + * If we are retrying a deferred QC after a timeout, it is not active + * and all we need to do is to complete it directly. + */ spin_lock_irqsave(ap->lock, flags); qc->scsidone = ata_eh_scsidone; - __ata_qc_complete(qc); + if ((qc->flags & ATA_QCFLAG_RETRY) && !(qc->flags & ATA_QCFLAG_ACTIVE)) + qc->complete_fn(qc); + else + __ata_qc_complete(qc); WARN_ON(ata_tag_valid(qc->tag)); spin_unlock_irqrestore(ap->lock, flags); diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 5868526301a2..89ef2eef72a0 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1730,6 +1730,28 @@ static void ata_scsi_schedule_deferred_qc(struct ata_link *link) queue_work(system_highpri_wq, &link->deferred_qc_work); } +enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *scmd) +{ + struct ata_port *ap = ata_shost_to_port(scmd->device->host); + + /* + * ata_scsi_cmd_error_handler() takes care of timed-out deferred queued + * commands. However, if we had any other command time out and we have + * deferred queued commands, we must let scsi_timeout() handle them + * with scsi_eh_scmd_add() so that we do not unnecessarilly delay + * starting the SCSI EH task. So schedule all deferred queued commands + * for retry through EH. + */ + ata_eh_schedule_deferred_qc_retry(ap, scmd); + + /* + * Let scsi_timeout() know that it must continue with handling the + * timeout as we in fact did not do much here. + */ + return SCSI_EH_NOT_HANDLED; +} +EXPORT_SYMBOL_GPL(ata_scsi_eh_timed_out); + static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) { struct ata_link *link = qc->dev->link; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 700627596ce1..e1fdecd93ccf 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -198,6 +198,8 @@ extern void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev, unsigned int action); extern void ata_eh_done(struct ata_link *link, struct ata_device *dev, unsigned int action); +void ata_eh_schedule_deferred_qc_retry(struct ata_port *ap, + struct scsi_cmnd *scmd); extern void ata_eh_autopsy(struct ata_port *ap); const char *ata_get_cmd_name(u8 command); extern void ata_eh_report(struct ata_port *ap); diff --git a/include/linux/libata.h b/include/linux/libata.h index 96e626d6a7ca..327da43d7496 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1153,6 +1153,7 @@ extern int ata_scsi_ioctl(struct scsi_device *dev, unsigned int cmd, #endif extern enum scsi_qc_status ata_scsi_queuecmd(struct Scsi_Host *h, struct scsi_cmnd *cmd); +enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *cmd); #if IS_REACHABLE(CONFIG_ATA) bool ata_scsi_dma_need_drain(struct request *rq); #else @@ -1464,6 +1465,7 @@ extern const struct attribute_group *ata_common_sdev_groups[]; .ioctl = ata_scsi_ioctl, \ ATA_SCSI_COMPAT_IOCTL \ .queuecommand = ata_scsi_queuecmd, \ + .eh_timed_out = ata_scsi_eh_timed_out, \ .dma_need_drain = ata_scsi_dma_need_drain, \ .this_id = ATA_SHT_THIS_ID, \ .emulated = ATA_SHT_EMULATED, \ -- 2.55.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v3 1/2] ata: libata-scsi: terminate deferred commands on time out 2026-07-13 4:12 ` [PATCH v3 1/2] ata: libata-scsi: terminate deferred commands on time out Damien Le Moal @ 2026-07-13 10:51 ` Niklas Cassel 2026-07-14 5:41 ` Damien Le Moal 2026-07-13 18:07 ` Igor Pylypiv 1 sibling, 1 reply; 8+ messages in thread From: Niklas Cassel @ 2026-07-13 10:51 UTC (permalink / raw) To: Damien Le Moal Cc: linux-ide, linux-scsi, Martin K . Petersen, Igor Pylypiv, John Garry, Jason Yan On Mon, Jul 13, 2026 at 01:12:51PM +0900, Damien Le Moal wrote: > If a command timeout occurs while we have a deferred non-NCQ command > waiting to be issued, the SCSI EH task is not immediately woken up as the > waiting deferred command is never issued nor completed, thus leaving this > command to always be counted as "busy" for the SCSI host. This results in > the test "shost->host_failed != scsi_host_busy(shost))" in the function > scsi_error_handler() to always be true, keeping the EH task sleeping. > Eventually, when the deferred command also times out, the SCSI EH task > is woken up and the timeout processing occurs. > > Avoid this unnecessary SCSI EH task wake-up additional time using the > eh_timed_out SCSI host template operation. The function > ata_scsi_eh_timed_out() is introduced to implement this operation. This > function calls the new helper ata_eh_schedule_deferred_qc_retry() to > schedule a retry through libata EH of all differed queued command, except s/differed/deferred/ > for a differed queued commands that timed out as that case is handled in s/differed/deferred/ > ata_scsi_cmd_error_handler(). > > Since ata_scsi_eh_timed_out() does not directly handles the timeout itself > and eventual re-issuing of deferred commands, this function returns > SCSI_EH_NOT_HANDLED to have scsi_timeout() continue with the regular > timeout handling, using scsi_abort_command() and scsi_eh_scmd_add(), thus > preventing the wkae-up delay for SCSI EH task. s/wkae-up/wake-up/ > > Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") > Cc: stable@vger.kernel.org > Signed-off-by: Damien Le Moal <dlemoal@kernel.org> > --- > drivers/ata/libata-eh.c | 35 ++++++++++++++++++++++++++++++++++- > drivers/ata/libata-scsi.c | 22 ++++++++++++++++++++++ > drivers/ata/libata.h | 2 ++ > include/linux/libata.h | 2 ++ > 4 files changed, 60 insertions(+), 1 deletion(-) > > diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c > index 05df7ea6954a..8e9d57c6f039 100644 > --- a/drivers/ata/libata-eh.c > +++ b/drivers/ata/libata-eh.c > @@ -546,6 +546,31 @@ static void ata_eh_unload(struct ata_port *ap) > spin_unlock_irqrestore(ap->lock, flags); > } > > +void ata_eh_schedule_deferred_qc_retry(struct ata_port *ap, > + struct scsi_cmnd *scmd) > +{ > + struct ata_queued_cmd *qc; > + struct ata_link *link; > + unsigned long flags; > + > + /* > + * Trigger EH for retrying any deferred qc that is not the queued > + * command for scmd. > + */ > + spin_lock_irqsave(ap->lock, flags); > + ata_for_each_link(link, ap, PMP_FIRST) { > + qc = link->deferred_qc; > + if (!qc || qc->scsicmd == scmd) > + continue; > + > + link->deferred_qc = NULL; > + cancel_work(&link->deferred_qc_work); > + qc->flags |= ATA_QCFLAG_RETRY; > + ata_qc_schedule_eh(qc); > + } > + spin_unlock_irqrestore(ap->lock, flags); I was expecting you to modify ata_scsi_requeue_deferred_qc() to also requeue commands using this method, so we only have one of the functions. Would it work if you modify ata_scsi_requeue_deferred_qc() to essentially call this function instead? (They seem to have different locking requirements, so possibly create a __ata_scsi_requeue_deferred_qc() that has a lockdep_assert_held(ap->lock) and let ata_eh_schedule_deferred_qc_retry() take the lock and call __ata_scsi_requeue_deferred_qc(). Perhaps we can come up with better names, but you get the point.) Because, as far as I understood the Sashiko comment: """ [Severity: Critical] This is a pre-existing issue, but won't the explicit invocation of this requeue logic on every command timeout make a regression more likely? When ata_scsi_requeue_deferred_qc() completes a deferred command using ata_scsi_qc_done() with DID_REQUEUE, it unconditionally calls ata_qc_free(), which clears the active flag. """ I don't see how this patch will address the pre-existing issue Sashiko warned about. I think that we need to modify ata_scsi_requeue_deferred_qc() to also requeue commands via EH. Possibly it works because ata_scsi_requeue_deferred_qc() is currently always called by EH. But I think it would be simpler if we just have one function that always requeues/retries via EH, regardless if we already are in EH context or not. > +} > + > /** > * ata_scsi_error - SCSI layer error handler callback > * @host: SCSI host on which error occurred > @@ -1214,9 +1239,17 @@ static void __ata_eh_qc_complete(struct ata_queued_cmd *qc) > struct scsi_cmnd *scmd = qc->scsicmd; > unsigned long flags; > > + > + /* > + * If we are retrying a deferred QC after a timeout, it is not active > + * and all we need to do is to complete it directly. > + */ > spin_lock_irqsave(ap->lock, flags); > qc->scsidone = ata_eh_scsidone; > - __ata_qc_complete(qc); > + if ((qc->flags & ATA_QCFLAG_RETRY) && !(qc->flags & ATA_QCFLAG_ACTIVE)) > + qc->complete_fn(qc); > + else > + __ata_qc_complete(qc); I thought that we had to call scsi_eh_finish_cmd() for a command that was on the SCSI list of failed commands. See e.g. how ata_scsi_cmd_error_handler() currently calls scsi_eh_finish_cmd(). What am I missing? Does the code still work if you simply unconditionally call __ata_qc_complete() here? (Even if it is slightly less efficient, I would prefer a single/unified path. This is only if we have a NCQ error or timeout, so I don't think it is super critical to prioritize efficiency over maintainability here.) Kind regards, Niklas ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 1/2] ata: libata-scsi: terminate deferred commands on time out 2026-07-13 10:51 ` Niklas Cassel @ 2026-07-14 5:41 ` Damien Le Moal 0 siblings, 0 replies; 8+ messages in thread From: Damien Le Moal @ 2026-07-14 5:41 UTC (permalink / raw) To: Niklas Cassel Cc: linux-ide, linux-scsi, Martin K . Petersen, Igor Pylypiv, John Garry, Jason Yan On 7/13/26 19:51, Niklas Cassel wrote: >> +void ata_eh_schedule_deferred_qc_retry(struct ata_port *ap, >> + struct scsi_cmnd *scmd) >> +{ >> + struct ata_queued_cmd *qc; >> + struct ata_link *link; >> + unsigned long flags; >> + >> + /* >> + * Trigger EH for retrying any deferred qc that is not the queued >> + * command for scmd. >> + */ >> + spin_lock_irqsave(ap->lock, flags); >> + ata_for_each_link(link, ap, PMP_FIRST) { >> + qc = link->deferred_qc; >> + if (!qc || qc->scsicmd == scmd) >> + continue; >> + >> + link->deferred_qc = NULL; >> + cancel_work(&link->deferred_qc_work); >> + qc->flags |= ATA_QCFLAG_RETRY; >> + ata_qc_schedule_eh(qc); >> + } >> + spin_unlock_irqrestore(ap->lock, flags); > > I was expecting you to modify ata_scsi_requeue_deferred_qc() to also > requeue commands using this method, so we only have one of the functions. > > Would it work if you modify ata_scsi_requeue_deferred_qc() to essentially > call this function instead? Yes, good point. I will do more tests but looks OK so far to do the retry through EH. Initially, I did not want to do that to not trigger EH too much, but as you pointed out, ata_scsi_requeue_deferred_qc() is called only if EH is already scheduled or already running. So there is no point in not using it. > """ > [Severity: Critical] > This is a pre-existing issue, but won't the explicit invocation of this > requeue logic on every command timeout make a regression more likely? > > When ata_scsi_requeue_deferred_qc() completes a deferred command using > ata_scsi_qc_done() with DID_REQUEUE, it unconditionally calls > ata_qc_free(), which clears the active flag. > """ When I read this comment from Sashiko, it seems to me that it refers to the race between command normal completion and timeout timer triggering. Both may happen at nearly the same time, and one may "win" depending on which is processed first. Though if it is the timeout, we still endup doing a normal completion if there was one. The code comment: int ata_scsi_cmd_error_handler() { ... /* Normal completion occurred after * SCSI timeout but before this point. * Successfully complete it. */ scmd->retries = scmd->allowed; scsi_eh_finish_cmd(scmd, &ap->eh_done_q); Which incidentally is the code path we take when retrying deferred QCs. > I don't see how this patch will address the pre-existing issue Sashiko > warned about. It does not. > I think that we need to modify ata_scsi_requeue_deferred_qc() to > also requeue commands via EH. Done. Testing now. > Possibly it works because ata_scsi_requeue_deferred_qc() is currently > always called by EH. But I think it would be simpler if we just have > one function that always requeues/retries via EH, regardless if we > already are in EH context or not. Yes. Agree. > > >> +} >> + >> /** >> * ata_scsi_error - SCSI layer error handler callback >> * @host: SCSI host on which error occurred >> @@ -1214,9 +1239,17 @@ static void __ata_eh_qc_complete(struct ata_queued_cmd *qc) >> struct scsi_cmnd *scmd = qc->scsicmd; >> unsigned long flags; >> >> + >> + /* >> + * If we are retrying a deferred QC after a timeout, it is not active >> + * and all we need to do is to complete it directly. >> + */ >> spin_lock_irqsave(ap->lock, flags); >> qc->scsidone = ata_eh_scsidone; >> - __ata_qc_complete(qc); >> + if ((qc->flags & ATA_QCFLAG_RETRY) && !(qc->flags & ATA_QCFLAG_ACTIVE)) >> + qc->complete_fn(qc); >> + else >> + __ata_qc_complete(qc); > > I thought that we had to call scsi_eh_finish_cmd() for a command that was > on the SCSI list of failed commands. The new use of the eh_timed_out operation puts them in shost->eh_cmd_q with scsi_eh_scmd_add() because we do not have a abort handler for ATA. commands in shost->eh_cmd_q go into the local eh_work_q of ata_scsi_cmd_error_handler() and for all deferred QCs, we endup calling scsi_eh_finish_cmd(), which finally move the commands into the done_q that ata_eh_finish() uses. So this is all good, because we always have scsi_eh_scmd_add() to "unbusy" the host so that we do not block SCSI EH task wakeup. > See e.g. how ata_scsi_cmd_error_handler() currently calls scsi_eh_finish_cmd(). > > What am I missing? Nothing. > Does the code still work if you simply unconditionally call > __ata_qc_complete() here? You get a WARN_ON() because the QC does *not* have ATA_QCFLAG_ACTIVE flag set because for deferred QCs, we have not yet issued them. Hence the little change above to avoid the warn splat. We could avoid it with a ATA_QCFLAG_DEFERRED flag... > (Even if it is slightly less efficient, I would prefer a single/unified path. > This is only if we have a NCQ error or timeout, so I don't think it is super > critical to prioritize efficiency over maintainability here.) This is not about efficiency. It is to be consistent with the ATA_QCFLAG_ACTIVE flag being set or not. -- Damien Le Moal Western Digital Research ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 1/2] ata: libata-scsi: terminate deferred commands on time out 2026-07-13 4:12 ` [PATCH v3 1/2] ata: libata-scsi: terminate deferred commands on time out Damien Le Moal 2026-07-13 10:51 ` Niklas Cassel @ 2026-07-13 18:07 ` Igor Pylypiv 2026-07-13 23:41 ` Damien Le Moal 1 sibling, 1 reply; 8+ messages in thread From: Igor Pylypiv @ 2026-07-13 18:07 UTC (permalink / raw) To: Damien Le Moal Cc: linux-ide, Niklas Cassel, linux-scsi, Martin K . Petersen, John Garry, Jason Yan On Mon, Jul 13, 2026 at 01:12:51PM +0900, Damien Le Moal wrote: > If a command timeout occurs while we have a deferred non-NCQ command > waiting to be issued, the SCSI EH task is not immediately woken up as the > waiting deferred command is never issued nor completed, thus leaving this > command to always be counted as "busy" for the SCSI host. This results in > the test "shost->host_failed != scsi_host_busy(shost))" in the function > scsi_error_handler() to always be true, keeping the EH task sleeping. > Eventually, when the deferred command also times out, the SCSI EH task > is woken up and the timeout processing occurs. > > Avoid this unnecessary SCSI EH task wake-up additional time using the > eh_timed_out SCSI host template operation. The function > ata_scsi_eh_timed_out() is introduced to implement this operation. This > function calls the new helper ata_eh_schedule_deferred_qc_retry() to > schedule a retry through libata EH of all differed queued command, except > for a differed queued commands that timed out as that case is handled in > ata_scsi_cmd_error_handler(). > > Since ata_scsi_eh_timed_out() does not directly handles the timeout itself > and eventual re-issuing of deferred commands, this function returns > SCSI_EH_NOT_HANDLED to have scsi_timeout() continue with the regular > timeout handling, using scsi_abort_command() and scsi_eh_scmd_add(), thus > preventing the wkae-up delay for SCSI EH task. > > Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") > Cc: stable@vger.kernel.org > Signed-off-by: Damien Le Moal <dlemoal@kernel.org> > --- > drivers/ata/libata-eh.c | 35 ++++++++++++++++++++++++++++++++++- > drivers/ata/libata-scsi.c | 22 ++++++++++++++++++++++ > drivers/ata/libata.h | 2 ++ > include/linux/libata.h | 2 ++ > 4 files changed, 60 insertions(+), 1 deletion(-) > > diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c > index 05df7ea6954a..8e9d57c6f039 100644 > --- a/drivers/ata/libata-eh.c > +++ b/drivers/ata/libata-eh.c > @@ -546,6 +546,31 @@ static void ata_eh_unload(struct ata_port *ap) > spin_unlock_irqrestore(ap->lock, flags); > } > > +void ata_eh_schedule_deferred_qc_retry(struct ata_port *ap, > + struct scsi_cmnd *scmd) > +{ > + struct ata_queued_cmd *qc; > + struct ata_link *link; > + unsigned long flags; > + > + /* > + * Trigger EH for retrying any deferred qc that is not the queued > + * command for scmd. > + */ > + spin_lock_irqsave(ap->lock, flags); > + ata_for_each_link(link, ap, PMP_FIRST) { > + qc = link->deferred_qc; > + if (!qc || qc->scsicmd == scmd) > + continue; > + > + link->deferred_qc = NULL; > + cancel_work(&link->deferred_qc_work); > + qc->flags |= ATA_QCFLAG_RETRY; Gemini pointed out an issue: ata_scsi_cmd_error_handler() would skip this command because ATA_QCFLAG_ACTIVE flag is not set and qc != qc->dev->link->deferred_qc because ata_eh_schedule_deferred_qc_retry() set link->deferred_qc to NULL. https://github.com/torvalds/linux/blob/master/drivers/ata/libata-eh.c#L655-L657 > + ata_qc_schedule_eh(qc); > + } > + spin_unlock_irqrestore(ap->lock, flags); > +} > + > /** > * ata_scsi_error - SCSI layer error handler callback > * @host: SCSI host on which error occurred > @@ -1214,9 +1239,17 @@ static void __ata_eh_qc_complete(struct ata_queued_cmd *qc) > struct scsi_cmnd *scmd = qc->scsicmd; > unsigned long flags; > > + > + /* > + * If we are retrying a deferred QC after a timeout, it is not active > + * and all we need to do is to complete it directly. > + */ > spin_lock_irqsave(ap->lock, flags); > qc->scsidone = ata_eh_scsidone; > - __ata_qc_complete(qc); > + if ((qc->flags & ATA_QCFLAG_RETRY) && !(qc->flags & ATA_QCFLAG_ACTIVE)) > + qc->complete_fn(qc); > + else > + __ata_qc_complete(qc); > WARN_ON(ata_tag_valid(qc->tag)); > spin_unlock_irqrestore(ap->lock, flags); > > diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c > index 5868526301a2..89ef2eef72a0 100644 > --- a/drivers/ata/libata-scsi.c > +++ b/drivers/ata/libata-scsi.c > @@ -1730,6 +1730,28 @@ static void ata_scsi_schedule_deferred_qc(struct ata_link *link) > queue_work(system_highpri_wq, &link->deferred_qc_work); > } > > +enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *scmd) > +{ > + struct ata_port *ap = ata_shost_to_port(scmd->device->host); > + > + /* > + * ata_scsi_cmd_error_handler() takes care of timed-out deferred queued > + * commands. However, if we had any other command time out and we have > + * deferred queued commands, we must let scsi_timeout() handle them > + * with scsi_eh_scmd_add() so that we do not unnecessarilly delay > + * starting the SCSI EH task. So schedule all deferred queued commands > + * for retry through EH. > + */ > + ata_eh_schedule_deferred_qc_retry(ap, scmd); > + > + /* > + * Let scsi_timeout() know that it must continue with handling the > + * timeout as we in fact did not do much here. > + */ > + return SCSI_EH_NOT_HANDLED; > +} > +EXPORT_SYMBOL_GPL(ata_scsi_eh_timed_out); > + > static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) > { > struct ata_link *link = qc->dev->link; > diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h > index 700627596ce1..e1fdecd93ccf 100644 > --- a/drivers/ata/libata.h > +++ b/drivers/ata/libata.h > @@ -198,6 +198,8 @@ extern void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev, > unsigned int action); > extern void ata_eh_done(struct ata_link *link, struct ata_device *dev, > unsigned int action); > +void ata_eh_schedule_deferred_qc_retry(struct ata_port *ap, > + struct scsi_cmnd *scmd); > extern void ata_eh_autopsy(struct ata_port *ap); > const char *ata_get_cmd_name(u8 command); > extern void ata_eh_report(struct ata_port *ap); > diff --git a/include/linux/libata.h b/include/linux/libata.h > index 96e626d6a7ca..327da43d7496 100644 > --- a/include/linux/libata.h > +++ b/include/linux/libata.h > @@ -1153,6 +1153,7 @@ extern int ata_scsi_ioctl(struct scsi_device *dev, unsigned int cmd, > #endif > extern enum scsi_qc_status ata_scsi_queuecmd(struct Scsi_Host *h, > struct scsi_cmnd *cmd); > +enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *cmd); > #if IS_REACHABLE(CONFIG_ATA) > bool ata_scsi_dma_need_drain(struct request *rq); > #else > @@ -1464,6 +1465,7 @@ extern const struct attribute_group *ata_common_sdev_groups[]; > .ioctl = ata_scsi_ioctl, \ > ATA_SCSI_COMPAT_IOCTL \ > .queuecommand = ata_scsi_queuecmd, \ > + .eh_timed_out = ata_scsi_eh_timed_out, \ > .dma_need_drain = ata_scsi_dma_need_drain, \ > .this_id = ATA_SHT_THIS_ID, \ > .emulated = ATA_SHT_EMULATED, \ > -- > 2.55.0 > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 1/2] ata: libata-scsi: terminate deferred commands on time out 2026-07-13 18:07 ` Igor Pylypiv @ 2026-07-13 23:41 ` Damien Le Moal 0 siblings, 0 replies; 8+ messages in thread From: Damien Le Moal @ 2026-07-13 23:41 UTC (permalink / raw) To: Igor Pylypiv Cc: linux-ide, Niklas Cassel, linux-scsi, Martin K . Petersen, John Garry, Jason Yan On 7/14/26 03:07, Igor Pylypiv wrote: > On Mon, Jul 13, 2026 at 01:12:51PM +0900, Damien Le Moal wrote: >> If a command timeout occurs while we have a deferred non-NCQ command >> waiting to be issued, the SCSI EH task is not immediately woken up as the >> waiting deferred command is never issued nor completed, thus leaving this >> command to always be counted as "busy" for the SCSI host. This results in >> the test "shost->host_failed != scsi_host_busy(shost))" in the function >> scsi_error_handler() to always be true, keeping the EH task sleeping. >> Eventually, when the deferred command also times out, the SCSI EH task >> is woken up and the timeout processing occurs. >> >> Avoid this unnecessary SCSI EH task wake-up additional time using the >> eh_timed_out SCSI host template operation. The function >> ata_scsi_eh_timed_out() is introduced to implement this operation. This >> function calls the new helper ata_eh_schedule_deferred_qc_retry() to >> schedule a retry through libata EH of all differed queued command, except >> for a differed queued commands that timed out as that case is handled in >> ata_scsi_cmd_error_handler(). >> >> Since ata_scsi_eh_timed_out() does not directly handles the timeout itself >> and eventual re-issuing of deferred commands, this function returns >> SCSI_EH_NOT_HANDLED to have scsi_timeout() continue with the regular >> timeout handling, using scsi_abort_command() and scsi_eh_scmd_add(), thus >> preventing the wkae-up delay for SCSI EH task. >> >> Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") >> Cc: stable@vger.kernel.org >> Signed-off-by: Damien Le Moal <dlemoal@kernel.org> >> --- >> drivers/ata/libata-eh.c | 35 ++++++++++++++++++++++++++++++++++- >> drivers/ata/libata-scsi.c | 22 ++++++++++++++++++++++ >> drivers/ata/libata.h | 2 ++ >> include/linux/libata.h | 2 ++ >> 4 files changed, 60 insertions(+), 1 deletion(-) >> >> diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c >> index 05df7ea6954a..8e9d57c6f039 100644 >> --- a/drivers/ata/libata-eh.c >> +++ b/drivers/ata/libata-eh.c >> @@ -546,6 +546,31 @@ static void ata_eh_unload(struct ata_port *ap) >> spin_unlock_irqrestore(ap->lock, flags); >> } >> >> +void ata_eh_schedule_deferred_qc_retry(struct ata_port *ap, >> + struct scsi_cmnd *scmd) >> +{ >> + struct ata_queued_cmd *qc; >> + struct ata_link *link; >> + unsigned long flags; >> + >> + /* >> + * Trigger EH for retrying any deferred qc that is not the queued >> + * command for scmd. >> + */ >> + spin_lock_irqsave(ap->lock, flags); >> + ata_for_each_link(link, ap, PMP_FIRST) { >> + qc = link->deferred_qc; >> + if (!qc || qc->scsicmd == scmd) >> + continue; >> + >> + link->deferred_qc = NULL; >> + cancel_work(&link->deferred_qc_work); >> + qc->flags |= ATA_QCFLAG_RETRY; > > > Gemini pointed out an issue: > > ata_scsi_cmd_error_handler() would skip this command because > ATA_QCFLAG_ACTIVE flag is not set and qc != qc->dev->link->deferred_qc > because ata_eh_schedule_deferred_qc_retry() set link->deferred_qc to NULL. > > https://github.com/torvalds/linux/blob/master/drivers/ata/libata-eh.c#L655-L657 And I think that is fine, since there was no errors with the deferred QCs. In this case, it falls into the last else after the loop, which does: scmd->retries = scmd->allowed; scsi_eh_finish_cmd(scmd, &ap->eh_done_q); and after that, ata_eh_finish() will retry the QCs because they are tagged with ATA_QCFLAG_EH and ATA_QCFLAG_RETRY. I tested with AHCI, and checked that. If anything, the retries count should be checked because I think this path messes it up. -- Damien Le Moal Western Digital Research ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 2/2] scsi: libsas: terminate deferred commands on time out 2026-07-13 4:12 [PATCH v3 0/2] fixup handling of timeouts with deferred QCs Damien Le Moal 2026-07-13 4:12 ` [PATCH v3 1/2] ata: libata-scsi: terminate deferred commands on time out Damien Le Moal @ 2026-07-13 4:12 ` Damien Le Moal 2026-07-13 4:27 ` sashiko-bot 1 sibling, 1 reply; 8+ messages in thread From: Damien Le Moal @ 2026-07-13 4:12 UTC (permalink / raw) To: linux-ide, Niklas Cassel, linux-scsi, Martin K . Petersen Cc: Igor Pylypiv, John Garry, Jason Yan If a command timeout occurs while we have a deferred non-NCQ command waiting to be issued, the SCSI EH task is not immediately woken up as the waiting deferred command is never issued nor completed, thus leaving this command to always be counted as "busy" for the SCSI host. This results in the test "shost->host_failed != scsi_host_busy(shost))" in the function scsi_error_handler() to always be true, keeping the EH task sleeping. Eventually, when the deferred command also times out, the SCSI EH task is woken up and the timeout processing occurs. Avoid this unnecessary additional SCSI EH trigger wait time with the same method as implemented in libata-scsi, using the eh_timed_out SCSI host template operation. The function sas_eh_timed_out() implements this operation and executes the function ata_eh_schedule_deferred_qc_retry() for SATA devices. Co-developed-by: Igor Pylypiv <ipylypiv@google.com> Signed-off-by: Igor Pylypiv <ipylypiv@google.com> Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal <dlemoal@kernel.org> --- drivers/ata/libata-eh.c | 1 + drivers/ata/libata.h | 2 -- drivers/scsi/libsas/sas_scsi_host.c | 17 +++++++++++++++++ include/linux/libata.h | 2 ++ include/scsi/libsas.h | 2 ++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 8e9d57c6f039..ff6d9f94ebed 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -570,6 +570,7 @@ void ata_eh_schedule_deferred_qc_retry(struct ata_port *ap, } spin_unlock_irqrestore(ap->lock, flags); } +EXPORT_SYMBOL_GPL(ata_eh_schedule_deferred_qc_retry); /** * ata_scsi_error - SCSI layer error handler callback diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index e1fdecd93ccf..700627596ce1 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -198,8 +198,6 @@ extern void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev, unsigned int action); extern void ata_eh_done(struct ata_link *link, struct ata_device *dev, unsigned int action); -void ata_eh_schedule_deferred_qc_retry(struct ata_port *ap, - struct scsi_cmnd *scmd); extern void ata_eh_autopsy(struct ata_port *ap); const char *ata_get_cmd_name(u8 command); extern void ata_eh_report(struct ata_port *ap); diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index c83282733ec4..6b62522bd0b2 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -502,6 +502,23 @@ int sas_eh_target_reset_handler(struct scsi_cmnd *cmd) } EXPORT_SYMBOL_GPL(sas_eh_target_reset_handler); +/* + * Handle deferred QCs in case of a command timeout. + * See ata_scsi_eh_timed_out() for details. + */ +enum scsi_timeout_action sas_eh_timed_out(struct scsi_cmnd *cmd) +{ + struct domain_device *dev = NULL; + + if (cmd) + dev = cmd_to_domain_dev(cmd); + if (dev && dev_is_sata(dev)) + ata_eh_schedule_deferred_qc_retry(dev->sata_dev.ap, cmd); + + return SCSI_EH_NOT_HANDLED; +} +EXPORT_SYMBOL_GPL(sas_eh_timed_out); + /* Try to reset a device */ static int try_to_reset_cmd_device(struct scsi_cmnd *cmd) { diff --git a/include/linux/libata.h b/include/linux/libata.h index 327da43d7496..2ea7bfbdd867 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1261,6 +1261,8 @@ extern int sata_link_hardreset(struct ata_link *link, extern int sata_link_resume(struct ata_link *link, const unsigned int *params, unsigned long deadline); extern void ata_eh_analyze_ncq_error(struct ata_link *link); +void ata_eh_schedule_deferred_qc_retry(struct ata_port *ap, + struct scsi_cmnd *scmd); #else static inline const unsigned int * sata_ehc_deb_timing(struct ata_eh_context *ehc) diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 163f23c92b41..c7017ae76c61 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -705,6 +705,7 @@ void sas_task_abort(struct sas_task *); int sas_eh_abort_handler(struct scsi_cmnd *cmd); int sas_eh_device_reset_handler(struct scsi_cmnd *cmd); int sas_eh_target_reset_handler(struct scsi_cmnd *cmd); +enum scsi_timeout_action sas_eh_timed_out(struct scsi_cmnd *cmd); extern void sas_target_destroy(struct scsi_target *); extern int sas_sdev_init(struct scsi_device *); @@ -743,6 +744,7 @@ void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event, .this_id = -1, \ .eh_device_reset_handler = sas_eh_device_reset_handler, \ .eh_target_reset_handler = sas_eh_target_reset_handler, \ + .eh_timed_out = sas_eh_timed_out, \ .target_destroy = sas_target_destroy, \ .ioctl = sas_ioctl, \ -- 2.55.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v3 2/2] scsi: libsas: terminate deferred commands on time out 2026-07-13 4:12 ` [PATCH v3 2/2] scsi: libsas: " Damien Le Moal @ 2026-07-13 4:27 ` sashiko-bot 0 siblings, 0 replies; 8+ messages in thread From: sashiko-bot @ 2026-07-13 4:27 UTC (permalink / raw) To: Damien Le Moal; +Cc: linux-ide, linux-scsi, dlemoal, cassel Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] Missing function declaration for `ata_eh_schedule_deferred_qc_retry()` when `CONFIG_SATA_HOST` is disabled, leading to a build failure. -- --- Patch [2]: [PATCH v3 2/2] scsi: libsas: terminate deferred commands on time out --- commit 3af8343f458da773cfa43a85c2e86949acf28cc1 Author: Damien Le Moal <dlemoal@kernel.org> scsi: libsas: terminate deferred commands on time out If a command timeout occurs while we have a deferred non-NCQ command waiting to be issued, the SCSI EH task is not immediately woken up. This patch avoids this unnecessary wait by using the eh_timed_out SCSI host template operation. > diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c > index c83282733ec4c..6b62522bd0b2e 100644 > --- a/drivers/scsi/libsas/sas_scsi_host.c > +++ b/drivers/scsi/libsas/sas_scsi_host.c [ ... ] > @@ -502,6 +502,23 @@ int sas_eh_target_reset_handler(struct scsi_cmnd *cmd) > } > EXPORT_SYMBOL_GPL(sas_eh_target_reset_handler); > > +/* > + * Handle deferred QCs in case of a command timeout. > + * See ata_scsi_eh_timed_out() for details. > + */ > +enum scsi_timeout_action sas_eh_timed_out(struct scsi_cmnd *cmd) > +{ > + struct domain_device *dev = NULL; > + > + if (cmd) > + dev = cmd_to_domain_dev(cmd); > + if (dev && dev_is_sata(dev)) > + ata_eh_schedule_deferred_qc_retry(dev->sata_dev.ap, cmd); > + > + return SCSI_EH_NOT_HANDLED; > +} > +EXPORT_SYMBOL_GPL(sas_eh_timed_out); > + [ ... ] > diff --git a/include/linux/libata.h b/include/linux/libata.h > index 327da43d74967..2ea7bfbdd867b 100644 > --- a/include/linux/libata.h > +++ b/include/linux/libata.h > @@ -1261,6 +1261,8 @@ extern int sata_link_hardreset(struct ata_link *link, > extern int sata_link_resume(struct ata_link *link, const unsigned int *params, > unsigned long deadline); > extern void ata_eh_analyze_ncq_error(struct ata_link *link); > +void ata_eh_schedule_deferred_qc_retry(struct ata_port *ap, > + struct scsi_cmnd *scmd); > #else [Severity: Low] Will this result in a build failure when CONFIG_SATA_HOST is disabled? The declaration for ata_eh_schedule_deferred_qc_retry() is placed inside this conditional block. When CONFIG_SATA_HOST is disabled, this declaration is omitted, but the function is still called unconditionally by sas_eh_timed_out() in sas_scsi_host.c (as well as ata_scsi_eh_timed_out() in libata-scsi.c). Since modern compilers treat implicit function declarations as errors, could this break the build under these configurations? > static inline const unsigned int * > sata_ehc_deb_timing(struct ata_eh_context *ehc) -- Sashiko AI review · https://sashiko.dev/#/patchset/20260713041252.463401-1-dlemoal@kernel.org?part=2 ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-07-14 5:41 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-13 4:12 [PATCH v3 0/2] fixup handling of timeouts with deferred QCs Damien Le Moal 2026-07-13 4:12 ` [PATCH v3 1/2] ata: libata-scsi: terminate deferred commands on time out Damien Le Moal 2026-07-13 10:51 ` Niklas Cassel 2026-07-14 5:41 ` Damien Le Moal 2026-07-13 18:07 ` Igor Pylypiv 2026-07-13 23:41 ` Damien Le Moal 2026-07-13 4:12 ` [PATCH v3 2/2] scsi: libsas: " Damien Le Moal 2026-07-13 4:27 ` sashiko-bot
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox