* [PATCH 1/3] ufs: core: Simplify timeout handling for START STOP UNIT commands
2026-08-12 18:22 [PATCH 0/3] ufs: core: Improve error handling Bart Van Assche
@ 2026-08-12 18:22 ` Bart Van Assche
2026-08-12 18:40 ` sashiko-bot
2026-08-12 18:22 ` [PATCH 2/3] ufs: core: Fix a race condition triggered by ufshcd_eh_timed_out() Bart Van Assche
2026-08-12 18:22 ` [PATCH 3/3] ufs: core: Do not forcibly complete SCSI commands Bart Van Assche
2 siblings, 1 reply; 7+ messages in thread
From: Bart Van Assche @ 2026-08-12 18:22 UTC (permalink / raw)
To: Martin K . Petersen
Cc: linux-scsi, Bart Van Assche, James E.J. Bottomley, Peter Wang,
Bean Huo, Avri Altman, Can Guo, Hyeoncheol Jeong
The set_host_byte(cmd, DID_REQUEUE) code has been copied from
ufshcd_mcq_force_compl_one(). That completion status is useful for SCSI
commands that should be retried but not for START STOP UNIT commands,
the only type of SCSI command for which ufshcd_eh_timed_out() calls
set_host_byte(). The SCMD_FAIL_IF_RECOVERING flag is set for START STOP
UNIT commands. This causes scsi_queue_rq() to fail SCSI commands. Simplify
timeout handling for START STOP UNIT commands by skipping command
resubmission and by failing START STOP UNIT commands directly. See also
commit 01d5e237b339 ("scsi: ufs: core: Handle PM commands timeout before
SCSI EH").
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
drivers/ufs/core/ufshcd.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index a51e071916cf..73f1e8817f44 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -9604,13 +9604,9 @@ static enum scsi_timeout_action ufshcd_eh_timed_out(struct scsi_cmnd *scmd)
spin_unlock_irqrestore(&hba->outstanding_lock, flags);
}
- if (ufshcd_is_scsi_cmd(scmd)) {
- set_host_byte(scmd, DID_REQUEUE);
+ set_host_byte(scmd, DID_TIME_OUT);
+ if (ufshcd_is_scsi_cmd(scmd))
ufshcd_release_scsi_cmd(hba, scmd);
- } else {
- set_host_byte(scmd, DID_TIME_OUT);
- }
-
scsi_done(scmd);
}
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 2/3] ufs: core: Fix a race condition triggered by ufshcd_eh_timed_out()
2026-08-12 18:22 [PATCH 0/3] ufs: core: Improve error handling Bart Van Assche
2026-08-12 18:22 ` [PATCH 1/3] ufs: core: Simplify timeout handling for START STOP UNIT commands Bart Van Assche
@ 2026-08-12 18:22 ` Bart Van Assche
2026-08-12 18:42 ` sashiko-bot
2026-08-12 18:22 ` [PATCH 3/3] ufs: core: Do not forcibly complete SCSI commands Bart Van Assche
2 siblings, 1 reply; 7+ messages in thread
From: Bart Van Assche @ 2026-08-12 18:22 UTC (permalink / raw)
To: Martin K . Petersen
Cc: linux-scsi, Bart Van Assche, James E.J. Bottomley, Peter Wang,
Avri Altman, Bean Huo, Can Guo, Hyeoncheol Jeong, Adrian Hunter
If a START STOP UNIT command times out, the ufshcd_link_recovery() call
in ufshcd_eh_timed_out() may call scsi_done() for that command via the
force-completion mechanism. This may cause ufshcd_set_dev_pwr_mode() to
return before link recovery has finished. Fix this race condition by
moving the ufshcd_link_recovery() call into ufshcd_set_dev_pwr_mode().
Fixes: 7029e2151a7c ("scsi: ufs: Fix a deadlock between PM and the SCSI error handler")
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
drivers/ufs/core/ufshcd.c | 36 ++++++++++++------------------------
1 file changed, 12 insertions(+), 24 deletions(-)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 73f1e8817f44..0618712be32c 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -9582,34 +9582,18 @@ static enum scsi_timeout_action ufshcd_eh_timed_out(struct scsi_cmnd *scmd)
return SCSI_EH_NOT_HANDLED;
}
- /*
- * Handle the timeout directly to prevent a deadlock between
- * ufshcd_set_dev_pwr_mode() and ufshcd_err_handler().
- */
- ufshcd_link_recovery(hba);
- dev_info(hba->dev, "%s() finished; outstanding_tasks = %#lx.\n",
- __func__, hba->outstanding_tasks);
-
- /*
- * ufshcd_link_recovery() may already have completed @scmd, e.g. via
- * the existing MCQ force-completion path.
- */
- if (!test_bit(SCMD_STATE_COMPLETE, &scmd->state)) {
- if (!hba->mcq_enabled) {
- unsigned long flags;
- struct request *rq = scsi_cmd_to_rq(scmd);
+ if (!hba->mcq_enabled) {
+ struct request *rq = scsi_cmd_to_rq(scmd);
- spin_lock_irqsave(&hba->outstanding_lock, flags);
+ scoped_guard(spinlock_irqsave, &hba->outstanding_lock)
__clear_bit(rq->tag, &hba->outstanding_reqs);
- spin_unlock_irqrestore(&hba->outstanding_lock, flags);
- }
-
- set_host_byte(scmd, DID_TIME_OUT);
- if (ufshcd_is_scsi_cmd(scmd))
- ufshcd_release_scsi_cmd(hba, scmd);
- scsi_done(scmd);
}
+ set_host_byte(scmd, DID_TIME_OUT);
+ if (ufshcd_is_scsi_cmd(scmd))
+ ufshcd_release_scsi_cmd(hba, scmd);
+ scsi_done(scmd);
+
return SCSI_EH_DONE;
}
@@ -10123,9 +10107,13 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,
sdev_printk(KERN_WARNING, sdp,
"START_STOP failed for power mode: %d, result %x\n",
pwr_mode, ret);
+
+ ufshcd_link_recovery(hba);
+
if (ret > 0) {
if (scsi_sense_valid(&sshdr))
scsi_print_sense_hdr(sdp, NULL, &sshdr);
+
ret = -EIO;
}
} else {
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 3/3] ufs: core: Do not forcibly complete SCSI commands
2026-08-12 18:22 [PATCH 0/3] ufs: core: Improve error handling Bart Van Assche
2026-08-12 18:22 ` [PATCH 1/3] ufs: core: Simplify timeout handling for START STOP UNIT commands Bart Van Assche
2026-08-12 18:22 ` [PATCH 2/3] ufs: core: Fix a race condition triggered by ufshcd_eh_timed_out() Bart Van Assche
@ 2026-08-12 18:22 ` Bart Van Assche
2026-08-12 18:38 ` sashiko-bot
2 siblings, 1 reply; 7+ messages in thread
From: Bart Van Assche @ 2026-08-12 18:22 UTC (permalink / raw)
To: Martin K . Petersen
Cc: linux-scsi, Bart Van Assche, James E.J. Bottomley, Peter Wang,
vamshi gajjela, Alok Tiwari, Hyeoncheol Jeong, Chanwoo Lee,
Bean Huo, Can Guo, Rafael J. Wysocki, Avri Altman, Stanley Jhu,
Bao D. Nguyen
The SCSI core error handler is responsible for deciding whether to abort or
to requeue SCSI commands. A SCSI LLD shouldn't interfere with this decision.
Hence, do not forcibly complete SCSI commands from inside the UFS error
handler. This patch makes the MCQ behavior consistent with the behavior for
legacy mode.
Fixes: ab248643d3d6 ("scsi: ufs: core: Add error handling for MCQ mode")
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
drivers/ufs/core/ufs-mcq.c | 26 ------------------
drivers/ufs/core/ufshcd-priv.h | 2 --
drivers/ufs/core/ufshcd.c | 48 +++++-----------------------------
3 files changed, 7 insertions(+), 69 deletions(-)
diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c
index 8106d55f4041..55ed72d15ada 100644
--- a/drivers/ufs/core/ufs-mcq.c
+++ b/drivers/ufs/core/ufs-mcq.c
@@ -328,32 +328,6 @@ static void ufshcd_mcq_process_cqe(struct ufs_hba *hba,
}
}
-/*
- * This function is called from the UFS error handler with the UFS host
- * controller disabled (HCE = 0). Reading host controller registers, e.g. the
- * CQ tail pointer (CQTPy), may not be safe with the host controller disabled.
- * Hence, iterate over all completion queue entries. This won't result in
- * double completions because ufshcd_mcq_process_cqe() clears a CQE after it
- * has been processed.
- */
-void ufshcd_mcq_compl_all_cqes_lock(struct ufs_hba *hba,
- struct ufs_hw_queue *hwq)
-{
- unsigned long flags;
- u32 entries = hwq->max_entries;
-
- spin_lock_irqsave(&hwq->cq_lock, flags);
- while (entries > 0) {
- ufshcd_mcq_process_cqe(hba, hwq);
- ufshcd_mcq_inc_cq_head_slot(hwq);
- entries--;
- }
-
- ufshcd_mcq_update_cq_tail_slot(hwq);
- hwq->cq_head_slot = hwq->cq_tail_slot;
- spin_unlock_irqrestore(&hwq->cq_lock, flags);
-}
-
unsigned long ufshcd_mcq_poll_cqe_lock(struct ufs_hba *hba,
struct ufs_hw_queue *hwq)
{
diff --git a/drivers/ufs/core/ufshcd-priv.h b/drivers/ufs/core/ufshcd-priv.h
index e55c2a02c1f5..8ddc19143abf 100644
--- a/drivers/ufs/core/ufshcd-priv.h
+++ b/drivers/ufs/core/ufshcd-priv.h
@@ -73,8 +73,6 @@ int ufshcd_get_hba_mac(struct ufs_hba *hba);
int ufshcd_mcq_memory_alloc(struct ufs_hba *hba);
struct ufs_hw_queue *ufshcd_mcq_req_to_hwq(struct ufs_hba *hba,
struct request *req);
-void ufshcd_mcq_compl_all_cqes_lock(struct ufs_hba *hba,
- struct ufs_hw_queue *hwq);
bool ufshcd_cmd_inflight(struct scsi_cmnd *cmd);
int ufshcd_mcq_sq_cleanup(struct ufs_hba *hba, int task_tag);
int ufshcd_mcq_abort(struct scsi_cmnd *cmd);
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 0618712be32c..963784f8c0a6 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -6018,34 +6018,6 @@ static int ufshcd_poll(struct Scsi_Host *shost, unsigned int queue_num)
return completed_reqs != 0;
}
-static bool ufshcd_mcq_force_compl_one(struct request *rq, void *priv)
-{
- struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq);
- struct scsi_device *sdev = rq->q->queuedata;
- struct Scsi_Host *shost = sdev->host;
- struct ufs_hba *hba = shost_priv(shost);
- struct ufs_hw_queue *hwq = ufshcd_mcq_req_to_hwq(hba, rq);
-
- if (blk_mq_is_reserved_rq(rq) || !hwq)
- return true;
-
- ufshcd_mcq_compl_all_cqes_lock(hba, hwq);
-
- /*
- * For those cmds of which the cqes are not present in the cq, complete
- * them explicitly.
- */
- scoped_guard(spinlock_irqsave, &hwq->cq_lock) {
- if (!test_bit(SCMD_STATE_COMPLETE, &cmd->state)) {
- set_host_byte(cmd, DID_REQUEUE);
- ufshcd_release_scsi_cmd(hba, cmd);
- scsi_done(cmd);
- }
- }
-
- return true;
-}
-
static bool ufshcd_mcq_compl_one(struct request *rq, void *priv)
{
struct scsi_device *sdev = rq->q->queuedata;
@@ -6066,16 +6038,10 @@ static bool ufshcd_mcq_compl_one(struct request *rq, void *priv)
* the scsi command.
*
* @hba: per adapter instance
- * @force_compl: This flag is set to true when invoked
- * from ufshcd_host_reset_and_restore() in which case it requires special
- * handling because the host controller has been reset by ufshcd_hba_stop().
*/
-static void ufshcd_mcq_compl_pending_transfer(struct ufs_hba *hba,
- bool force_compl)
+static void ufshcd_mcq_compl_pending_transfer(struct ufs_hba *hba)
{
- blk_mq_tagset_busy_iter(&hba->host->tag_set,
- force_compl ? ufshcd_mcq_force_compl_one :
- ufshcd_mcq_compl_one,
+ blk_mq_tagset_busy_iter(&hba->host->tag_set, ufshcd_mcq_compl_one,
NULL);
}
@@ -6632,10 +6598,10 @@ static void ufshcd_exception_event_handler(struct work_struct *work)
}
/* Complete requests that have door-bell cleared */
-static void ufshcd_complete_requests(struct ufs_hba *hba, bool force_compl)
+static void ufshcd_complete_requests(struct ufs_hba *hba)
{
if (hba->mcq_enabled)
- ufshcd_mcq_compl_pending_transfer(hba, force_compl);
+ ufshcd_mcq_compl_pending_transfer(hba);
else
ufshcd_transfer_req_compl(hba);
@@ -6936,7 +6902,7 @@ static bool ufshcd_abort_all(struct ufs_hba *hba)
out:
/* Complete the requests that are cleared by s/w */
- ufshcd_complete_requests(hba, false);
+ ufshcd_complete_requests(hba);
return ret != 0;
}
@@ -6998,7 +6964,7 @@ static void ufshcd_err_handler(struct work_struct *work)
spin_unlock_irqrestore(hba->host->host_lock, flags);
/* Complete requests that have door-bell cleared by h/w */
- ufshcd_complete_requests(hba, false);
+ ufshcd_complete_requests(hba);
spin_lock_irqsave(hba->host->host_lock, flags);
again:
needs_restore = false;
@@ -8229,7 +8195,7 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba *hba)
*/
ufshcd_hba_stop(hba);
hba->silence_err_logs = true;
- ufshcd_complete_requests(hba, true);
+ ufshcd_complete_requests(hba);
hba->silence_err_logs = false;
/* scale up clocks to max frequency before full reinitialization */
^ permalink raw reply related [flat|nested] 7+ messages in thread