* [PATCH] scsi: ufs: core: Prevent MMIO access and drain in-flight commands during shutdown
@ 2026-09-03 3:13 Stanley Jhu
2026-09-18 13:45 ` [PATCH v2] " Stanley Jhu
0 siblings, 1 reply; 2+ messages in thread
From: Stanley Jhu @ 2026-09-03 3:13 UTC (permalink / raw)
To: Martin K . Petersen, James E . J . Bottomley, linux-scsi
Cc: Bart Van Assche, Seunghwan Baek, Alim Akhtar, Avri Altman,
Peter Wang, Can Guo, Bean Huo, linux-kernel, stable, Stanley Jhu
Commit 19a198b67767 ("scsi: ufs: core: Put the normal LU into
SDEV_OFFLINE when ufs device wl-lun suspend") transitioned normal
logical units into SDEV_OFFLINE during ufshcd_wl_shutdown() to prevent
requeue deadlocks. However, setting SDEV_OFFLINE does not wait for
in-flight or currently dispatching requests to drain.
If an I/O request races past scsi_queue_rq() right as or before
SDEV_OFFLINE is set, or if an asynchronous command execution suffers
scheduling delays, it can arrive in ufshcd_queuecommand() or issue MMIO
writes after ufshcd_wl_shutdown() has powered off the UFS controller
(is_powered = false, regulators disabled, clocks gated). Writing to MMIO
registers of a power-gated or clock-gated controller triggers fatal
hardware bus errors, system hangs, or kernel panics.
Fix this with a dual-layer defense:
1. In ufshcd_queuecommand(), check if hba->shutting_down is set. Reject
any non-WLUN / non-PM requests immediately with DID_NO_CONNECT before
any MMIO register access.
Note that checking !hba->is_powered is unnecessary here because
hba->shutting_down is asserted prior to disabling clocks and
regulators in ufshcd_wl_shutdown(), and module removal drains and
destroys all request queues via scsi_remove_host() before is_powered
is cleared.
2. In ufshcd_wl_shutdown(), invoke ufshcd_wait_for_pending_cmds() after
taking regular LUNs offline to drain all existing hardware transfer
and task management requests before putting the device into powerdown
mode and powering down the host controller. Warn if draining times
out after 1 second.
Fixes: 19a198b67767 ("scsi: ufs: core: Put the normal LU into SDEV_OFFLINE when ufs device wl-lun suspend")
Cc: stable@vger.kernel.org
Signed-off-by: Stanley Jhu <stanleyjhu@google.com>
---
drivers/ufs/core/ufshcd.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 2ba244cf40ac..6d78e34a19b2 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -3105,6 +3105,25 @@ static enum scsi_qc_status ufshcd_queuecommand(struct Scsi_Host *host,
int err = 0;
struct ufs_hw_queue *hwq = NULL;
+ /*
+ * During host shutdown, fail any incoming regular I/O commands
+ * immediately. This prevents stray requests that bypassed SCSI queue
+ * offline checks from writing to MMIO doorbells after the controller
+ * is power-gated (causing fatal bus errors / panics).
+ *
+ * Note: Checking !hba->is_powered is not needed here because:
+ * 1. During shutdown, hba->shutting_down is set prior to cutting
+ * controller power, so shutting_down alone fully covers the
+ * unpowered window.
+ * 2. During module removal, scsi_remove_host() freezes and destroys
+ * all request queues before hba->is_powered is set to false in
+ * ufshcd_hba_exit().
+ */
+ if (unlikely(READ_ONCE(hba->shutting_down))) {
+ if (!is_device_wlun(cmd->device) ||
+ !(scsi_cmd_to_rq(cmd)->rq_flags & RQF_PM)) {
+ set_host_byte(cmd, DID_NO_CONNECT);
+ scsi_done(cmd);
+ return 0;
+ }
+ }
+
switch (hba->ufshcd_state) {
case UFSHCD_STATE_OPERATIONAL:
break;
@@ -10931,6 +10950,14 @@ static void ufshcd_wl_shutdown(struct scsi_device *sdev)
scsi_device_set_state(sdev, SDEV_OFFLINE);
mutex_unlock(&sdev->state_mutex);
}
+
+ /*
+ * Drain all in-flight transfer and task management requests before
+ * putting the device into low power and turning off controller power.
+ */
+ if (ufshcd_wait_for_pending_cmds(hba, USEC_PER_SEC))
+ dev_warn(hba->dev,
+ "timed out waiting for in-flight commands during shutdown\n");
+
__ufshcd_wl_suspend(hba, UFS_SHUTDOWN_PM);
/*
--
2.43.0
^ permalink raw reply [flat|nested] 2+ messages in thread* [PATCH v2] scsi: ufs: core: Prevent MMIO access and drain in-flight commands during shutdown
2026-09-03 3:13 [PATCH] scsi: ufs: core: Prevent MMIO access and drain in-flight commands during shutdown Stanley Jhu
@ 2026-09-18 13:45 ` Stanley Jhu
0 siblings, 0 replies; 2+ messages in thread
From: Stanley Jhu @ 2026-09-18 13:45 UTC (permalink / raw)
To: mkp, James.Bottomley, linux-scsi
Cc: bvanassche, sh8267.baek, alim.akhtar, avri.altman, peter.wang,
can.guo, beanhuo, linux-kernel, stable
Commit 19a198b67767 ("scsi: ufs: core: Set SDEV_OFFLINE when UFS is
shut down") replaced scsi_device_quiesce() with
scsi_device_set_state(sdev, SDEV_OFFLINE) in ufshcd_wl_shutdown() to
avoid unbounded blk_mq_freeze_queue() deadlocks when a reboot occurs
early during boot. However, unlike scsi_device_quiesce(), setting
SDEV_OFFLINE is a state write only and does not wait for in-flight or
currently dispatching requests to drain.
As a result, ufshcd_wl_shutdown() immediately sends START STOP UNIT
(PowerDown) and powers off the UFS controller (is_powered = false,
regulators disabled, clocks gated) while regular LUNs may still have
commands outstanding or threads preempted between scsi_queue_rq() and
ufshcd_queuecommand(). Writing to MMIO registers of a power-gated or
clock-gated controller triggers hardware bus errors, system hangs, or
kernel panics.
Close this window without reintroducing the unbounded freeze deadlock:
1. In ufshcd_wl_shutdown(), invoke ufshcd_wait_for_pending_cmds() with a
bounded 1-second timeout after marking regular LUNs SDEV_OFFLINE so
in-flight transfer and task management requests drain before the
device enters powerdown mode and controller clocks/regulators are
disabled.
2. In ufshcd_queuecommand(), check hba->shutting_down and reject any
stray non-WLUN / non-PM requests with DID_NO_CONNECT before touching
MMIO registers, covering threads preempted across the bounded drain
window.
Fixes: 19a198b67767 ("scsi: ufs: core: Set SDEV_OFFLINE when UFS is shut down")
Cc: stable@vger.kernel.org
Reviewed-by: Peter Wang <peter.wang@mediatek.com>
Signed-off-by: Stanley Jhu <stanleyjhu@google.com>
---
Changes since v1:
- Correct the commit subject in the Fixes: tag and clarify why a bounded
drain is used instead of restoring scsi_device_quiesce().
- Collect Reviewed-by from Peter Wang (sent off-list due to mail gateway
headers). No code changes.
drivers/ufs/core/ufshcd.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 2ba244cf40ac..6d78e34a19b2 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -3105,6 +3105,25 @@ static enum scsi_qc_status ufshcd_queuecommand(struct Scsi_Host *host,
int err = 0;
struct ufs_hw_queue *hwq = NULL;
+ /*
+ * During host shutdown, fail any incoming regular I/O commands
+ * immediately. This prevents stray requests that bypassed SCSI queue
+ * offline checks from writing to MMIO doorbells after the controller
+ * is power-gated (causing fatal bus errors / panics).
+ *
+ * Note: Checking !hba->is_powered is not needed here because:
+ * 1. During shutdown, hba->shutting_down is set prior to cutting
+ * controller power, so shutting_down alone fully covers the
+ * unpowered window.
+ * 2. During module removal, scsi_remove_host() freezes and destroys
+ * all request queues before hba->is_powered is set to false in
+ * ufshcd_hba_exit().
+ */
+ if (unlikely(READ_ONCE(hba->shutting_down))) {
+ if (!is_device_wlun(cmd->device) ||
+ !(scsi_cmd_to_rq(cmd)->rq_flags & RQF_PM)) {
+ set_host_byte(cmd, DID_NO_CONNECT);
+ scsi_done(cmd);
+ return 0;
+ }
+ }
+
switch (hba->ufshcd_state) {
case UFSHCD_STATE_OPERATIONAL:
break;
@@ -10931,6 +10950,14 @@ static void ufshcd_wl_shutdown(struct scsi_device *sdev)
scsi_device_set_state(sdev, SDEV_OFFLINE);
mutex_unlock(&sdev->state_mutex);
}
+
+ /*
+ * Drain all in-flight transfer and task management requests before
+ * putting the device into low power and turning off controller power.
+ */
+ if (ufshcd_wait_for_pending_cmds(hba, USEC_PER_SEC))
+ dev_warn(hba->dev,
+ "timed out waiting for in-flight commands during shutdown\n");
+
__ufshcd_wl_suspend(hba, UFS_SHUTDOWN_PM);
/*
--
2.55.0.1082.g2b9226bbc0-goog
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-18 13:45 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-03 3:13 [PATCH] scsi: ufs: core: Prevent MMIO access and drain in-flight commands during shutdown Stanley Jhu
2026-09-18 13:45 ` [PATCH v2] " Stanley Jhu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®