* [PATCH v2] scsi: ufs: mcq: Reap aborted CQEs before returning from abort
@ 2026-10-30 1:13 Jeuk Kim
2026-10-30 1:51 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Jeuk Kim @ 2026-10-30 1:13 UTC (permalink / raw)
To: bvanassche
Cc: James.Bottomley, alim.akhtar, avri.altman, beanhuo, can.guo,
cw9316.lee, hyenc.jeong, j-young.choi, jeuk20.kim, john.garry,
linux-kernel, linux-scsi, mkp, peter.wang, ping.gao,
rafael.j.wysocki, stanleyjhu, vamshigajjela, stable
From: Jeuk Kim <jeuk20.kim@samsung.com>
ufshcd_mcq_abort() can return before the OCS_ABORTED CQE is reaped.
If the command is retried or reused, the late CQE can unmap its new
DMA buffers or access a freed SG table, triggering a KASAN
null-ptr-deref in dma_direct_unmap_sg().
It can also overwrite cmd->result and complete a resubmitted command
because SCMD_STATE_COMPLETE is cleared on resubmission.
Track driver completion for all command types and check it under cq_lock
in the shared SQ cleanup path, covering abort and LU reset. CUS alone
does not establish that software has consumed the CQE. If SQ cleanup
times out, leave DMA mapped and let the SCSI core escalate recovery.
Release uncompleted commands in the existing host-reset cleanup path
after ufshcd_hba_stop(), including commands with SCMD_STATE_COMPLETE set.
Reproduced with QEMU UFS in MCQ mode using fault injection and a
test-only delay in OCS_ABORTED CQE processing.
Fixes: 53755903b935 ("scsi: ufs: mcq: Delete ufshcd_release_scsi_cmd() in ufshcd_mcq_abort()")
Cc: stable@vger.kernel.org
Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com>
---
Changes in v2:
- Track completion separately and wait in shared SQ cleanup for abort/LU reset.
- Keep DMA mapped on timeout and let SCSI EH escalate recovery.
- Release timed-out commands during host-reset cleanup; fix flags and metadata.
drivers/ufs/core/ufs-mcq.c | 28 ++++++++++++++++++++
drivers/ufs/core/ufshcd.c | 52 ++++++++++++++++++++++++++------------
include/ufs/ufshcd.h | 6 +++++
3 files changed, 70 insertions(+), 16 deletions(-)
diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c
index df293b66350d..9ca74947bfd0 100644
--- a/drivers/ufs/core/ufs-mcq.c
+++ b/drivers/ufs/core/ufs-mcq.c
@@ -554,6 +554,19 @@ static int ufshcd_mcq_sq_start(struct ufs_hba *hba, struct ufs_hw_queue *hwq)
return err;
}
+static bool ufshcd_mcq_cmd_completed(struct ufs_hba *hba,
+ struct ufs_hw_queue *hwq,
+ struct scsi_cmnd *cmd)
+{
+ struct ufshcd_lrb *lrbp = scsi_cmd_priv(cmd);
+
+ ufshcd_mcq_poll_cqe_lock(hba, hwq);
+
+ /* A completion may have started after the poll dropped cq_lock. */
+ guard(spinlock_irqsave)(&hwq->cq_lock);
+ return test_bit(UFSHCD_LRB_COMPLETED, &lrbp->flags);
+}
+
/**
* ufshcd_mcq_sq_cleanup - Clean up submission queue resources
* associated with the pending command.
@@ -570,6 +583,7 @@ int ufshcd_mcq_sq_cleanup(struct ufs_hba *hba, int task_tag)
struct ufs_hw_queue *hwq;
void __iomem *reg, *opr_sqd_base;
u32 nexus, id, val;
+ bool completed;
int err;
if (hba->quirks & UFSHCD_QUIRK_MCQ_BROKEN_RTC)
@@ -616,6 +630,20 @@ int ufshcd_mcq_sq_cleanup(struct ufs_hba *hba, int task_tag)
if (ufshcd_mcq_sq_start(hba, hwq))
err = -ETIMEDOUT;
+ if (err)
+ return err;
+
+ /*
+ * CUS reports hardware cleanup, not software completion. Reap the
+ * command's CQE before any caller can allow the command to be reused.
+ * On timeout, leave its DMA mapping intact for error recovery.
+ */
+ err = read_poll_timeout(ufshcd_mcq_cmd_completed, completed, completed,
+ 20, MCQ_POLL_US, false, hba, hwq, cmd);
+ if (err)
+ dev_err(hba->dev, "%s: no completion for tag %d\n",
+ __func__, task_tag);
+
return err;
}
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index cc75384c6502..3f2b99cfd87f 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -2426,6 +2426,8 @@ static inline void ufshcd_send_command(struct ufs_hba *hba,
const int tag = scsi_cmd_to_rq(cmd)->tag;
unsigned long flags;
+ lrbp->flags = 0;
+
if (hba->monitor.enabled) {
lrbp->issue_time_stamp = ktime_get();
lrbp->issue_time_stamp_local_clock = local_clock();
@@ -3098,11 +3100,15 @@ static void ufshcd_map_queues(struct Scsi_Host *shost)
}
/*
- * The only purpose of this function is to make the SCSI core skip the memset()
- * call for the private command data.
+ * Initialize the completion state and make the SCSI core preserve the private
+ * command data across dispatch attempts.
*/
static int ufshcd_init_cmd_priv(struct Scsi_Host *host, struct scsi_cmnd *cmd)
{
+ struct ufshcd_lrb *lrbp = scsi_cmd_priv(cmd);
+
+ /* The SCSI core may complete a request before calling queuecommand(). */
+ lrbp->flags = BIT(UFSHCD_LRB_COMPLETED);
return 0;
}
@@ -6014,6 +6020,7 @@ void ufshcd_compl_one_cqe(struct ufs_hba *hba, int task_tag,
(struct utp_upiu_req *)lrbp->ucd_rsp_ptr);
cmd->result = 0;
}
+ set_bit(UFSHCD_LRB_COMPLETED, &lrbp->flags);
/* Do not touch lrbp after scsi_done() has been called. */
scsi_done(cmd);
}
@@ -6091,6 +6098,7 @@ static int ufshcd_poll(struct Scsi_Host *shost, unsigned int queue_num)
static bool ufshcd_mcq_force_compl_one(struct request *rq, void *priv)
{
struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq);
+ struct ufshcd_lrb *lrbp = scsi_cmd_priv(cmd);
struct scsi_device *sdev = rq->q->queuedata;
struct Scsi_Host *shost = sdev->host;
struct ufs_hba *hba = shost_priv(shost);
@@ -6101,13 +6109,18 @@ static bool ufshcd_mcq_force_compl_one(struct request *rq, void *priv)
/*
* For those cmds of which the cqes are not present in the cq, complete
- * them explicitly.
+ * them explicitly after the controller has stopped. Timed-out commands
+ * still need their driver resources released, but SCSI EH owns their
+ * result and completion.
*/
scoped_guard(spinlock_irqsave, &hwq->cq_lock) {
- if (!test_bit(SCMD_STATE_COMPLETE, &cmd->state)) {
- set_host_byte(cmd, DID_REQUEUE);
+ if (!test_bit(UFSHCD_LRB_COMPLETED, &lrbp->flags)) {
ufshcd_release_scsi_cmd(hba, cmd);
- scsi_done(cmd);
+ set_bit(UFSHCD_LRB_COMPLETED, &lrbp->flags);
+ if (!test_bit(SCMD_STATE_COMPLETE, &cmd->state)) {
+ set_host_byte(cmd, DID_REQUEUE);
+ scsi_done(cmd);
+ }
}
}
@@ -7934,31 +7947,34 @@ int ufshcd_advanced_rpmb_req_handler(struct ufs_hba *hba, struct utp_upiu_req *r
return err ? : result;
}
+struct ufshcd_lu_reset_info {
+ u64 lun;
+ int err;
+};
+
static bool ufshcd_clear_lu_cmds(struct request *req, void *priv)
{
struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(req);
struct scsi_device *sdev = cmd->device;
struct Scsi_Host *shost = sdev->host;
struct ufs_hba *hba = shost_priv(shost);
- const u64 lun = *(u64 *)priv;
+ struct ufshcd_lu_reset_info *reset = priv;
const u32 tag = req->tag;
+ int err;
- if (blk_mq_is_reserved_rq(req) || sdev->lun != lun)
+ if (blk_mq_is_reserved_rq(req) || sdev->lun != reset->lun)
return true;
- if (ufshcd_clear_cmd(hba, tag) < 0) {
+ err = ufshcd_clear_cmd(hba, tag);
+ if (err) {
+ reset->err = err;
dev_err(hba->dev, "%s: failed to clear request %d\n", __func__,
tag);
return true;
}
- if (hba->mcq_enabled) {
- struct ufs_hw_queue *hwq = ufshcd_mcq_req_to_hwq(hba, req);
-
- if (hwq)
- ufshcd_mcq_poll_cqe_lock(hba, hwq);
+ if (hba->mcq_enabled)
return true;
- }
ufshcd_compl_one_cqe(hba, tag, NULL);
return true;
@@ -7972,6 +7988,7 @@ static bool ufshcd_clear_lu_cmds(struct request *req, void *priv)
*/
static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
{
+ struct ufshcd_lu_reset_info reset = { .lun = cmd->device->lun };
struct Scsi_Host *host;
struct ufs_hba *hba;
int err;
@@ -7989,7 +8006,8 @@ static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
/* clear the commands that were pending for corresponding LUN */
blk_mq_tagset_busy_iter(&hba->host->tag_set,
ufshcd_clear_lu_cmds,
- &cmd->device->lun);
+ &reset);
+ err = reset.err;
}
hba->req_abort_count = 0;
@@ -9617,6 +9635,7 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
static enum scsi_timeout_action ufshcd_eh_timed_out(struct scsi_cmnd *scmd)
{
+ struct ufshcd_lrb *lrbp = scsi_cmd_priv(scmd);
struct ufs_hba *hba = shost_priv(scmd->device->host);
if (!hba->pm_op_in_progress) {
@@ -9653,6 +9672,7 @@ static enum scsi_timeout_action ufshcd_eh_timed_out(struct scsi_cmnd *scmd)
set_host_byte(scmd, DID_TIME_OUT);
}
+ set_bit(UFSHCD_LRB_COMPLETED, &lrbp->flags);
scsi_done(scmd);
}
diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h
index be62165b785e..69e49d25f6ce 100644
--- a/include/ufs/ufshcd.h
+++ b/include/ufs/ufshcd.h
@@ -151,6 +151,10 @@ struct ufs_pm_lvl_states {
enum uic_link_state link_state;
};
+/* Bits in ufshcd_lrb::flags. */
+/* No driver resources remain; also set before the first dispatch. */
+#define UFSHCD_LRB_COMPLETED 0
+
/**
* struct ufshcd_lrb - local reference block
* @utr_descriptor_ptr: UTRD address of the command
@@ -166,6 +170,7 @@ struct ufs_pm_lvl_states {
* @lun: LUN of the command
* @intr_cmd: Interrupt command (doesn't participate in interrupt aggregation)
* @req_abort_skip: skip request abort task flag
+ * @flags: per-command flags (UFSHCD_LRB_*)
* @issue_time_stamp: time stamp for debug purposes (CLOCK_MONOTONIC)
* @issue_time_stamp_local_clock: time stamp for debug purposes (local_clock)
* @compl_time_stamp: time stamp for statistics (CLOCK_MONOTONIC)
@@ -190,6 +195,7 @@ struct ufshcd_lrb {
u8 lun; /* UPIU LUN id field is only 8-bit wide */
bool intr_cmd;
bool req_abort_skip;
+ unsigned long flags;
ktime_t issue_time_stamp;
u64 issue_time_stamp_local_clock;
ktime_t compl_time_stamp;
--
2.43.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH v2] scsi: ufs: mcq: Reap aborted CQEs before returning from abort
2026-10-30 1:13 [PATCH v2] scsi: ufs: mcq: Reap aborted CQEs before returning from abort Jeuk Kim
@ 2026-10-30 1:51 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-10-30 1:51 UTC (permalink / raw)
To: Jeuk Kim; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] scsi: ufs: unguarded completion and missing outstanding_reqs clear in ufshcd_clear_lu_cmds()
- [High] scsi: ufs: missing UFSHCD_LRB_COMPLETED check in ufshcd_eh_timed_out()
- [Medium] scsi: ufs: ufshcd_compl_one_cqe() clobbers cmd->result of timed-out commands
--
commit 54407428c294792b0ec3e920d76f745b0a9af3bc
Author: Jeuk Kim <jeuk20.kim@samsung.com>
scsi: ufs: mcq: Reap aborted CQEs before returning from abort
This commit tracks per-command driver completion via UFSHCD_LRB_COMPLETED and
waits in ufshcd_mcq_sq_cleanup() for the CQE of an aborted or reset command to
be reaped before returning. If SQ cleanup times out, DMA mappings are kept
intact until host-reset cleanup releases uncompleted commands after stopping
the HBA.
> diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
> index cc75384c65021..3f2b99cfd87fc 100644
> --- a/drivers/ufs/core/ufshcd.c
> +++ b/drivers/ufs/core/ufshcd.c
[ ... ]
> @@ -6014,6 +6020,7 @@ void ufshcd_compl_one_cqe(struct ufs_hba *hba, int task_tag,
> (struct utp_upiu_req *)lrbp->ucd_rsp_ptr);
> cmd->result = 0;
> }
> + set_bit(UFSHCD_LRB_COMPLETED, &lrbp->flags);
> /* Do not touch lrbp after scsi_done() has been called. */
> scsi_done(cmd);
> }
[Severity: Medium]
When a command has already timed out and SCMD_STATE_COMPLETE is set on
cmd->state, should ufshcd_compl_one_cqe() avoid overwriting cmd->result (just
as ufshcd_mcq_force_compl_one() below guards set_host_byte(cmd, DID_REQUEUE)
with !test_bit(SCMD_STATE_COMPLETE, &cmd->state))?
When a SCSI command times out, scsi_timeout() sets SCMD_STATE_COMPLETE on
scmd->state and schedules scmd_eh_abort_handler(). If ufshcd_abort() returns
FAILED (for example when lrbp->req_abort_skip is set, the command is found in
the SQ, UFS_QUERY_TASK or UFS_ABORT_TASK fails, or ufshcd_mcq_sq_cleanup()
times out) or eh_deadline has expired, scmd_eh_abort_handler() jumps to out:
before calling set_host_byte(scmd, DID_TIME_OUT) and queues the command to
eh_cmd_q with scmd->result == 0 (or with DID_TIME_OUT already set if a
retried command timed out a second time).
When SCSI EH then runs ufshcd_eh_device_reset_handler() ->
ufshcd_clear_lu_cmds() -> ufshcd_mcq_sq_cleanup(), or drains CQEs during host
reset via ufshcd_mcq_compl_all_cqes_lock(), or processes a late CQE interrupt
after an SQ cleanup timeout, ufshcd_compl_one_cqe() runs while
SCMD_STATE_COMPLETE is already set and unconditionally executes:
drivers/ufs/core/ufshcd.c:ufshcd_compl_one_cqe() {
if (ufshcd_is_scsi_cmd(cmd)) {
...
cmd->result = ufshcd_transfer_rsp_status(hba, cmd, cqe);
ufshcd_release_scsi_cmd(hba, cmd);
} else {
...
cmd->result = 0;
}
}
For an OCS_ABORTED CQE, ufshcd_transfer_rsp_status() returns DID_REQUEUE << 16,
setting scmd->result to a non-zero value. Later in scsi_eh_flush_done_q():
drivers/scsi/scsi_error.c:scsi_eh_flush_done_q() {
if (!scmd->result &&
!(scmd->flags & SCMD_FORCE_EH_SUCCESS))
scmd->result |= (DID_TIME_OUT << 16);
...
scsi_finish_command(scmd);
}
Because scmd->result is now DID_REQUEUE << 16 instead of 0, !scmd->result is
false and DID_TIME_OUT is not set when finishing the command. Can this cause
passthrough/SG_IO requests and scsi_execute_cmd() callers to observe
DID_REQUEUE instead of DID_TIME_OUT, and prevent scsi_noretry_cmd() and
sd_eh_action() from matching DID_TIME_OUT?
[ ... ]
> @@ -7934,31 +7947,34 @@ int ufshcd_advanced_rpmb_req_handler(struct ufs_hba *hba, struct utp_upiu_req *r
> return err ? : result;
> }
>
> +struct ufshcd_lu_reset_info {
> + u64 lun;
> + int err;
> +};
> +
> static bool ufshcd_clear_lu_cmds(struct request *req, void *priv)
> {
> struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(req);
> struct scsi_device *sdev = cmd->device;
> struct Scsi_Host *shost = sdev->host;
> struct ufs_hba *hba = shost_priv(shost);
> - const u64 lun = *(u64 *)priv;
> + struct ufshcd_lu_reset_info *reset = priv;
> const u32 tag = req->tag;
> + int err;
>
> - if (blk_mq_is_reserved_rq(req) || sdev->lun != lun)
> + if (blk_mq_is_reserved_rq(req) || sdev->lun != reset->lun)
> return true;
>
> - if (ufshcd_clear_cmd(hba, tag) < 0) {
> + err = ufshcd_clear_cmd(hba, tag);
> + if (err) {
> + reset->err = err;
> dev_err(hba->dev, "%s: failed to clear request %d\n", __func__,
> tag);
> return true;
> }
>
> - if (hba->mcq_enabled) {
> - struct ufs_hw_queue *hwq = ufshcd_mcq_req_to_hwq(hba, req);
> -
> - if (hwq)
> - ufshcd_mcq_poll_cqe_lock(hba, hwq);
> + if (hba->mcq_enabled)
> return true;
> - }
>
> ufshcd_compl_one_cqe(hba, tag, NULL);
> return true;
> @@ -7972,6 +7988,7 @@ static bool ufshcd_clear_lu_cmds(struct request *req, void *priv)
> */
> static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
> {
> + struct ufshcd_lu_reset_info reset = { .lun = cmd->device->lun };
> struct Scsi_Host *host;
> struct ufs_hba *hba;
> int err;
> @@ -7989,7 +8006,8 @@ static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
> /* clear the commands that were pending for corresponding LUN */
> blk_mq_tagset_busy_iter(&hba->host->tag_set,
> ufshcd_clear_lu_cmds,
> - &cmd->device->lun);
> + &reset);
> + err = reset.err;
> }
[Severity: High]
What happens in ufshcd_clear_lu_cmds() when blk_mq_tagset_busy_iter() visits a
request on reset.lun that has already completed in the driver?
blk_mq_tagset_busy_iter() visits every request where blk_mq_request_started()
(req->state != MQ_RQ_IDLE) is true. A command on reset.lun that already
completed via ufshcd_compl_one_cqe() (setting UFSHCD_LRB_COMPLETED and calling
ufshcd_release_scsi_cmd()) remains started in MQ_RQ_IN_FLIGHT if it completed
after scsi_timeout() set SCMD_STATE_COMPLETE (since scsi_done_internal()
returns early before blk_mq_complete_request(), and ufshcd_abort() then fails
and queues the command to eh_cmd_q), or in MQ_RQ_COMPLETE if it was queued to
eh_cmd_q via scsi_complete() -> scsi_eh_scmd_add().
Because ufshcd_clear_lu_cmds() does not check UFSHCD_LRB_COMPLETED before
calling ufshcd_clear_cmd(hba, tag):
1. In MCQ mode, now that reset.err is propagated to
ufshcd_eh_device_reset_handler(), visiting an already-completed command on
a host with UFSHCD_QUIRK_MCQ_BROKEN_RTC causes ufshcd_mcq_sq_cleanup() to
return -ETIMEDOUT and fails the LU reset even when no command is pending in
hardware. On hosts without that quirk, ufshcd_mcq_sq_cleanup() still stops
the hardware SQ and issues SQ_ICU for a tag that has already completed.
2. In SDB mode (!hba->mcq_enabled), ufshcd_clear_lu_cmds() neither checks
UFSHCD_LRB_COMPLETED / hba->outstanding_reqs nor clears tag in
hba->outstanding_reqs. For an already-completed command on reset.lun,
ufshcd_clear_cmd() sees the doorbell bit is 0 and returns 0, and
ufshcd_clear_lu_cmds() calls ufshcd_compl_one_cqe(hba, tag, NULL) a second
time, triggering a double scsi_dma_unmap() and underflowing
hba->clk_gating.active_reqs and hba->clk_scaling.active_reqs. For a command
that was still pending in hba->outstanding_reqs, ufshcd_clear_lu_cmds()
clears the doorbell bit and calls ufshcd_compl_one_cqe(hba, tag, NULL)
while leaving tag set in hba->outstanding_reqs, so a subsequent
ufshcd_poll() call sees (~tr_doorbell & hba->outstanding_reqs) and completes
the same tag a second time via __ufshcd_transfer_req_compl().
Should ufshcd_clear_lu_cmds() check whether the command is already completed
(and clear hba->outstanding_reqs under hba->outstanding_lock in SDB mode)
before cleaning up and completing the tag?
[ ... ]
> @@ -9653,6 +9672,7 @@ static enum scsi_timeout_action ufshcd_eh_timed_out(struct scsi_cmnd *scmd)
> set_host_byte(scmd, DID_TIME_OUT);
> }
>
> + set_bit(UFSHCD_LRB_COMPLETED, &lrbp->flags);
> scsi_done(scmd);
> }
[Severity: High]
Can ufshcd_eh_timed_out() call ufshcd_release_scsi_cmd(hba, scmd) a second
time here because it only checks !test_bit(SCMD_STATE_COMPLETE, &scmd->state)
instead of checking UFSHCD_LRB_COMPLETED (under hwq->cq_lock in MCQ mode)?
When hba->pm_op_in_progress is true, ufshcd_eh_timed_out() runs
ufshcd_link_recovery(hba) -> ufshcd_host_reset_and_restore() before this
block. That can leave SCMD_STATE_COMPLETE clear while UFSHCD_LRB_COMPLETED is
already set in two cases:
1. At the start of ufshcd_host_reset_and_restore(), ufshcd_complete_requests()
calls ufshcd_mcq_force_compl_one() (or __ufshcd_transfer_req_compl() in SDB
mode), which releases scmd via ufshcd_release_scsi_cmd(), sets
UFSHCD_LRB_COMPLETED, and completes scmd with DID_REQUEUE via
scsi_done(scmd) before ufshcd_hba_enable(), ufshcd_device_init(), and
ufshcd_probe_hba() finish. Because the host is not in SHOST_RECOVERY,
DID_REQUEUE causes scsi_complete() -> scsi_queue_insert() to requeue the
request (or wakes blk_execute_rq() in scsi_execute_cmd() to retry). When
scsi_queue_rq() runs while ufshcd_host_reset_and_restore() still has
hba->ufshcd_state == UFSHCD_STATE_RESET, scsi_queue_rq() executes
clear_bit(SCMD_STATE_COMPLETE, &cmd->state) and ufshcd_queuecommand()
returns SCSI_MLQUEUE_HOST_BUSY, leaving SCMD_STATE_COMPLETE == 0 while
UFSHCD_LRB_COMPLETED == 1.
2. If scmd already completed in ufshcd_compl_one_cqe() (which called
ufshcd_release_scsi_cmd() and set UFSHCD_LRB_COMPLETED), but
scsi_done_internal() returned early at blk_should_fake_timeout() before
setting SCMD_STATE_COMPLETE, ufshcd_mcq_force_compl_one() during
ufshcd_link_recovery() now sees UFSHCD_LRB_COMPLETED is set and skips scmd
without setting SCMD_STATE_COMPLETE.
In both cases, when ufshcd_link_recovery(hba) returns, ufshcd_eh_timed_out()
sees !test_bit(SCMD_STATE_COMPLETE, &scmd->state) is true and calls
ufshcd_release_scsi_cmd(hba, scmd) and scsi_done(scmd) a second time,
resulting in a double scsi_dma_unmap() and active_reqs underflow.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261030011321.1868029-1-jeuk20.kim@gmail.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-30 1:51 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-30 1:13 [PATCH v2] scsi: ufs: mcq: Reap aborted CQEs before returning from abort Jeuk Kim
2026-10-30 1:51 ` sashiko-bot
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.