All of lore.kernel.org
 help / color / mirror / Atom feed
From: Niklas Cassel <cassel@kernel.org>
To: Damien Le Moal <dlemoal@kernel.org>
Cc: linux-ide@vger.kernel.org, linux-scsi@vger.kernel.org,
	"Martin K . Petersen" <martin.petersen@oracle.com>,
	Igor Pylypiv <ipylypiv@google.com>,
	John Garry <john.g.garry@oracle.com>,
	Jason Yan <yanaijie@huawei.com>
Subject: Re: [PATCH v3 1/2] ata: libata-scsi: terminate deferred commands on time out
Date: Mon, 13 Jul 2026 12:51:05 +0200	[thread overview]
Message-ID: <alTDGdJMA-XZHeS-@fedora> (raw)
In-Reply-To: <20260713041252.463401-2-dlemoal@kernel.org>

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

  reply	other threads:[~2026-07-13 10:51 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=alTDGdJMA-XZHeS-@fedora \
    --to=cassel@kernel.org \
    --cc=dlemoal@kernel.org \
    --cc=ipylypiv@google.com \
    --cc=john.g.garry@oracle.com \
    --cc=linux-ide@vger.kernel.org \
    --cc=linux-scsi@vger.kernel.org \
    --cc=martin.petersen@oracle.com \
    --cc=yanaijie@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.