Linux SCSI subsystem development
 help / color / mirror / Atom feed
From: Stanley Jhu <stanleyjhu@google.com>
To: "Martin K . Petersen" <mkp@kernel.org>,
	 "James E . J . Bottomley"
	<James.Bottomley@HansenPartnership.com>,
	linux-scsi@vger.kernel.org
Cc: Bart Van Assche <bvanassche@acm.org>,
	Seunghwan Baek <sh8267.baek@samsung.com>,
	 Alim Akhtar <alim.akhtar@samsung.com>,
	Avri Altman <avri.altman@sandisk.com>,
	 Peter Wang <peter.wang@mediatek.com>,
	Can Guo <can.guo@oss.qualcomm.com>,
	 Bean Huo <beanhuo@micron.com>,
	linux-kernel@vger.kernel.org, stable@vger.kernel.org,
	 Stanley Jhu <stanleyjhu@google.com>
Subject: [PATCH] scsi: ufs: core: Prevent MMIO access and drain in-flight commands during shutdown
Date: Thu,  3 Sep 2026 11:13:44 +0800	[thread overview]
Message-ID: <20260903031344.3740524-1-stanleyjhu@google.com> (raw)

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

                 reply	other threads:[~2026-09-03  3:13 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=20260903031344.3740524-1-stanleyjhu@google.com \
    --to=stanleyjhu@google.com \
    --cc=James.Bottomley@HansenPartnership.com \
    --cc=alim.akhtar@samsung.com \
    --cc=avri.altman@sandisk.com \
    --cc=beanhuo@micron.com \
    --cc=bvanassche@acm.org \
    --cc=can.guo@oss.qualcomm.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-scsi@vger.kernel.org \
    --cc=mkp@kernel.org \
    --cc=peter.wang@mediatek.com \
    --cc=sh8267.baek@samsung.com \
    --cc=stable@vger.kernel.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox