Re: [PATCH v4 2/2] scsi: libsas: terminate deferred commands on time out
John Garry <[email protected]> Wed, 15 Jul 2026 18:37:18 +0100
| Newsgroups | org.kernel.vger.linux-ide,org.kernel.vger.linux-scsi |
|---|---|
| Organization | Oracle Corporation |
| Message-ID | <[email protected]> |
On 15/07/2026 09:58, 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 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_scsi_retry_deferred_qc() > for SATA devices. > > Co-developed-by: Igor Pylypiv <[email protected]> > Signed-off-by: Igor Pylypiv <[email protected]> > Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") > Cc: [email protected] > Signed-off-by: Damien Le Moal <[email protected]> > --- > drivers/ata/libata-scsi.c | 4 ++-- > drivers/scsi/libsas/sas_scsi_host.c | 17 +++++++++++++++++ > include/linux/libata.h | 1 + > include/scsi/libsas.h | 2 ++ > 4 files changed, 22 insertions(+), 2 deletions(-) > > diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c > index 1870669b8e05..878527cc2b0e 100644 > --- a/drivers/ata/libata-scsi.c > +++ b/drivers/ata/libata-scsi.c > @@ -1708,8 +1708,7 @@ static void ata_scsi_schedule_deferred_qc(struct ata_link *link) > queue_work(system_highpri_wq, &link->deferred_qc_work); > } > > -static void ata_scsi_retry_deferred_qc(struct ata_port *ap, > - struct scsi_cmnd *scmd) > +void ata_scsi_retry_deferred_qc(struct ata_port *ap, struct scsi_cmnd *scmd) > { > unsigned long flags; > > @@ -1717,6 +1716,7 @@ static void ata_scsi_retry_deferred_qc(struct ata_port *ap, > ata_eh_retry_deferred_qc(ap, scmd); > spin_unlock_irqrestore(ap->lock, flags); > } > +EXPORT_SYMBOL_GPL(ata_scsi_retry_deferred_qc); > > enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *scmd) > { > diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c > index c83282733ec4..ca497cfc1353 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) Can cmd ever be NULL? AFAICS, the only caller is scsi_timeout() and cmd can never be NULL there. > + dev = cmd_to_domain_dev(cmd); > + if (dev && dev_is_sata(dev)) furthermore, I don't think that dev can ever be NULL (even from cmd_to_domain_dev()) > + ata_scsi_retry_deferred_qc(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 51178acd68d7..18f76c3db4a4 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); > +void ata_scsi_retry_deferred_qc(struct ata_port *ap, struct scsi_cmnd *scmd); > 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); > 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, \ >