Linux SCSI subsystem development
 help / color / mirror / Atom feed
* [PATCH v2] ufs: mcq: fix dev cmd abort path and add OCS poll after sq cleanup
       [not found] <CGME20261030093653epcas2p329f2bb351b051a118edad4211b4b3470@epcas2p3.samsung.com>
@ 2026-10-30  9:36 ` Kiwoong Kim
  2026-10-30  9:58   ` sashiko-bot
  0 siblings, 1 reply; 2+ messages in thread
From: Kiwoong Kim @ 2026-10-30  9:36 UTC (permalink / raw)
  To: linux-scsi, linux-kernel, alim.akhtar, avri.altman, jejb,
	martin.petersen, bvanassche, kwangwon.min, hy50.seo, cpgs,
	h10.kim
  Cc: stable, stanleyjhu, Kiwoong Kim

In MCQ mode, ufshcd_mcq_abort() falls through to ufshcd_try_to_abort_task()
for all timed-out commands. However, device management commands (query, NOP)
use reserved requests and are not SCSI I/O commands. The ABORT TASK task
management function is designed for SCSI I/O UPIUs and is not applicable to
device management UPIUs.

Fix this by checking ufshcd_is_scsi_cmd() in ufshcd_try_to_abort_task(). When
the request is a reserved (device management) command, skip the TM path and
call ufshcd_clear_cmd() directly instead.

Additionally, extend ufshcd_mcq_sq_cleanup() to poll the OCS field in the
UTRD of a device management command after a successful SQ_CUS poll. UFSHCI
does not specify the order between the OCS update and the SQ_CUS update, so
OCS may still be stale when cleanup returns. Without this poll, a late
response to a timed-out query can arrive while a retry is already in flight,
leaving the host with two query contexts and raising IS.UTPES.

The poll is limited to device management commands because
ufshcd_compl_one_cqe() copies the OCS from the CQE into the UTRD only for
them. For SCSI commands, the OCS in the UTRD stays
OCS_INVALID_COMMAND_STATUS, so polling it would always time out.

The poll waits until OCS is no longer OCS_INVALID_COMMAND_STATUS or
MCQ_POLL_US elapses. -ETIMEDOUT is returned only if OCS is still
OCS_INVALID_COMMAND_STATUS after the poll. Any other value, such as
OCS_ABORTED reported by the host controller for the cleaned-up request,
means the cleanup has completed.

--
Here is the timeline of an actual case for this that happened with kernel 6.18.
1. The host sent a query but the device didn't respond with it for a long time.

[ 7384.156142] [4:  kworker/u40:5:10671] exynos-ufs 38300000.ufs: ufshcd_mcq_sq_cleanup, hwq 0: cleanup return code (RTC) 0
[ 7384.156154] [4:  kworker/u40:5:10671] exynos-ufs 38300000.ufs: ufshcd_query_flag: Sending flag query for idn 14 failed, err = -11

2. So after the query was timed out, the driver did SQ cleanup.
Then the host updated CQ update and raised IS.CQES,
Unfortunately, complete() was called the driver's ISR afterward.

3. The driver started to retried the query. In the meantime, it set OCS to 0xF.
Then the driver didn't wait for completion due to the previous complete() call,
and just checked OCS.

[ 7384.156163] [4:  kworker/u40:5:10671] exynos-ufs 38300000.ufs: ufshcd_query_flag: Sending flag query for idn 14 failed, err = 15

5. The driver retried the query again, but the device didn't return anything,
so the query was also timed-out.

[ 7385.660108] [3:  kworker/u40:5:10671] exynos-ufs 38300000.ufs: ufshcd_query_flag: Sending flag query for idn 14 failed, err = -11

6. A few seconds later, the device returned the response of the 1st query,
but since the host was holding two query contexts and couldn't handle it,
it raised IS.UTPES.

Signed-off-by: Kiwoong Kim <kwmad.kim@samsung.com>

---
Changes since v1:
- Limit the OCS poll in ufshcd_mcq_sq_cleanup() to device management
  commands. ufshcd_compl_one_cqe() copies the OCS from the CQE into the
  UTRD only for them, so for SCSI commands the poll always ran for
  MCQ_POLL_US and then failed with -ETIMEDOUT (Sashiko).
- Return -ETIMEDOUT only if OCS is still OCS_INVALID_COMMAND_STATUS
  after the poll. OCS_ABORTED, which the host controller reports for
  the cleaned-up request, is no longer treated as a cleanup failure
  (Sashiko).
- Update the commit message to match.

Link: https://lore.kernel.org/linux-scsi/20261030083311.602909-1-kwmad.kim@samsung.com/
---
 drivers/ufs/core/ufs-mcq.c | 25 +++++++++++++++++++++++++
 drivers/ufs/core/ufshcd.c  |  4 ++++
 2 files changed, 29 insertions(+)

diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c
index 8106d55f4041..4f42b502d159 100644
--- a/drivers/ufs/core/ufs-mcq.c
+++ b/drivers/ufs/core/ufs-mcq.c
@@ -609,6 +609,31 @@ int ufshcd_mcq_sq_cleanup(struct ufs_hba *hba, int task_tag)
 			 __func__, id,
 			 FIELD_GET(SQ_ICU_ERR_CODE_MASK, readl(reg)));
 
+	/*
+	 * For device management commands, there is no order guaranteed between
+	 * OCS update and CUS update, which is why we need to wait here. SCSI
+	 * commands are excluded because the OCS in their UTRD is not updated
+	 * from the CQE.
+	 */
+	if (!err && blk_mq_is_reserved_rq(rq)) {
+		enum utp_ocs ocs;
+		ktime_t start = ktime_get();
+
+		do {
+			unsigned long flags;
+
+			spin_lock_irqsave(&hwq->cq_lock, flags);
+			ocs = READ_ONCE(lrbp->utr_descriptor_ptr->header.ocs);
+			spin_unlock_irqrestore(&hwq->cq_lock, flags);
+			if (ocs != OCS_INVALID_COMMAND_STATUS)
+				break;
+			usleep_range(20, 50);
+		} while (ktime_us_delta(ktime_get(), start) < MCQ_POLL_US);
+
+		if (ocs == OCS_INVALID_COMMAND_STATUS)
+			err = -ETIMEDOUT;
+	}
+
 	if (ufshcd_mcq_sq_start(hba, hwq))
 		err = -ETIMEDOUT;
 
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index bcc2512bf34b..7fc80926eeec 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -8046,6 +8046,9 @@ int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag)
 
 	lrbp = scsi_cmd_priv(cmd);
 
+	if (!ufshcd_is_scsi_cmd(cmd))
+		goto clear_cmd;
+
 	for (poll_cnt = 100; poll_cnt; poll_cnt--) {
 		err = ufshcd_issue_tm_cmd(hba, lrbp->lun, tag, UFS_QUERY_TASK,
 					  &resp);
@@ -8091,6 +8094,7 @@ int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag)
 		return err;
 	}
 
+clear_cmd:
 	err = ufshcd_clear_cmd(hba, tag);
 	if (err)
 		dev_err(hba->dev, "%s: Failed clearing cmd at tag %d, err %d\n",
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH v2] ufs: mcq: fix dev cmd abort path and add OCS poll after sq cleanup
  2026-10-30  9:36 ` [PATCH v2] ufs: mcq: fix dev cmd abort path and add OCS poll after sq cleanup Kiwoong Kim
@ 2026-10-30  9:58   ` sashiko-bot
  0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-10-30  9:58 UTC (permalink / raw)
  To: Kiwoong Kim, linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] ufs: mcq: re-read header.ocs after timeout in ufshcd_mcq_sq_cleanup()
--

commit 840d70c7b6d87e03c26e1ea5e9102ecbc05ff2f0
Author: Kiwoong Kim <kwmad.kim@samsung.com>

ufs: mcq: fix dev cmd abort path and add OCS poll after sq cleanup

This commit skips the task management path in ufshcd_try_to_abort_task()
for reserved device management commands and calls ufshcd_clear_cmd()
directly. It also extends ufshcd_mcq_sq_cleanup() to poll header.ocs for
device management commands after SQ_CUS is set.

Link: https://lore.kernel.org/linux-scsi/20261030083311.602909-1-kwmad.kim@samsung.com/

> diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c
> index 25e392f8ede2b..8c8b8e018fbdc 100644
> --- a/drivers/ufs/core/ufs-mcq.c
> +++ b/drivers/ufs/core/ufs-mcq.c
> @@ -613,6 +613,31 @@ int ufshcd_mcq_sq_cleanup(struct ufs_hba *hba, int task_tag)
>  			 __func__, id,
>  			 FIELD_GET(SQ_ICU_ERR_CODE_MASK, readl(reg)));
>  
> +	/*
> +	 * For device management commands, there is no order guaranteed between
> +	 * OCS update and CUS update, which is why we need to wait here. SCSI
> +	 * commands are excluded because the OCS in their UTRD is not updated
> +	 * from the CQE.
> +	 */
> +	if (!err && blk_mq_is_reserved_rq(rq)) {
> +		enum utp_ocs ocs;
> +		ktime_t start = ktime_get();
> +
> +		do {
> +			unsigned long flags;
> +
> +			spin_lock_irqsave(&hwq->cq_lock, flags);
> +			ocs = READ_ONCE(lrbp->utr_descriptor_ptr->header.ocs);
> +			spin_unlock_irqrestore(&hwq->cq_lock, flags);
> +			if (ocs != OCS_INVALID_COMMAND_STATUS)
> +				break;
> +			usleep_range(20, 50);
> +		} while (ktime_us_delta(ktime_get(), start) < MCQ_POLL_US);
> +
> +		if (ocs == OCS_INVALID_COMMAND_STATUS)
> +			err = -ETIMEDOUT;
> +	}

[Severity: Medium]
Can this loop in ufshcd_mcq_sq_cleanup() return a false -ETIMEDOUT if the
CQ interrupt updates lrbp->utr_descriptor_ptr->header.ocs while the task
is sleeping in usleep_range(20, 50)?

If the task wakes up after MCQ_POLL_US has elapsed, either on the final
polling iteration or due to scheduling delay on a busy CPU, the while
condition evaluates to false and exits the loop without re-reading
header.ocs.

The subsequent check then evaluates the stale pre-sleep ocs value and sets
err = -ETIMEDOUT, which causes ufshcd_mcq_abort() to fail and trigger an
unnecessary full SCSI host reset even though the command cleanup completed.

Should this re-read lrbp->utr_descriptor_ptr->header.ocs under hwq->cq_lock
after the loop exits (or use poll_timeout_us() from include/linux/iopoll.h)?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261030093645.790159-1-kwmad.kim@samsung.com?part=1

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-10-30  9:58 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <CGME20261030093653epcas2p329f2bb351b051a118edad4211b4b3470@epcas2p3.samsung.com>
2026-10-30  9:36 ` [PATCH v2] ufs: mcq: fix dev cmd abort path and add OCS poll after sq cleanup Kiwoong Kim
2026-10-30  9:58   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox