[PATCH v8 3/3] scsi: ufs: core: Limit early link recovery in the error handler to runtime PM

From: HOYOUNG SEO

Date: Sun Nov 08 2026 - 23:59:08 EST


ufshcd_err_handler() recovers the link without taking hba->host_sem
whenever hba->pm_op_in_progress is set. That early path was added by
commit f966e02ae521 ("scsi: ufs: core: Fix runtime suspend error
deadlock") because the regular error handling would wait for a runtime
PM operation that cannot complete while the error handler is waiting.

pm_op_in_progress is also set during system suspend, system resume,
shutdown and poweroff, where the early path is not needed and runs
concurrently with the PM path itself. For example, if a LINERESET is
reported and the hibern8 exit fails in __ufshcd_wl_resume() during
system resume, both ufshcd_err_handler() and __ufshcd_wl_resume() call
ufshcd_link_recovery(), and the host and the device may be reset at the
same time.

Only take the early path while a runtime suspend or resume of the WLUN
is in progress. The runtime PM core sets the runtime status of the WLUN
to RPM_SUSPENDING or RPM_RESUMING while ufshcd_wl_runtime_suspend() or
ufshcd_wl_runtime_resume() runs, and it is RPM_ACTIVE or RPM_SUSPENDED
during system PM. Read it with READ_ONCE() without power.lock held in
the new helper ufshcd_rpm_transition_in_progress(), next to the other
ufshcd_rpm_*() helpers. The status is only available with CONFIG_PM, so
the helper returns false without it.

For the other PM operations the regular error handling is safe:

- the system PM callbacks hold host_sem from ufshcd_wl_suspend() until
ufshcd_wl_resume() returns, and ufshcd_wl_poweroff() holds it as
well, so the error handler waits for them;
- during shutdown, hba->shutting_down makes the error handler stop.

This changes the behavior during shutdown: errors that occur while the
final START STOP UNIT command is being processed are no longer handled
by the error handler. Recovery then only happens through
ufshcd_eh_timed_out().

Fixes: e23ef4f22db3 ("scsi: ufs: core: Fix error handler host_sem issue")
Signed-off-by: HOYOUNG SEO <hy50.seo@xxxxxxxxxxx>
---
drivers/ufs/core/ufshcd-priv.h | 13 +++++++++++++
drivers/ufs/core/ufshcd.c | 7 ++++++-
2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/drivers/ufs/core/ufshcd-priv.h b/drivers/ufs/core/ufshcd-priv.h
index e55c2a02c1f5..ae3eaf5ab017 100644
--- a/drivers/ufs/core/ufshcd-priv.h
+++ b/drivers/ufs/core/ufshcd-priv.h
@@ -413,6 +413,19 @@ static inline void ufshcd_rpm_put(struct ufs_hba *hba)
pm_runtime_put(&hba->ufs_device_wlun->sdev_gendev);
}

+/* Whether a runtime suspend or resume of the WLUN is in progress. */
+static inline bool ufshcd_rpm_transition_in_progress(struct ufs_hba *hba)
+{
+#ifdef CONFIG_PM
+ enum rpm_status status =
+ READ_ONCE(hba->ufs_device_wlun->sdev_gendev.power.runtime_status);
+
+ return status == RPM_SUSPENDING || status == RPM_RESUMING;
+#else
+ return false;
+#endif
+}
+
/**
* ufs_is_valid_unit_desc_lun - checks if the given LUN has a unit descriptor
* @dev_info: pointer of instance of struct ufs_dev_info
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 1c935a8d016d..a79951cfcb29 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -7091,9 +7091,14 @@ static void ufshcd_err_handler(struct work_struct *work)
* runtime resume. This avoids potential deadlocks that could
* happen if we tried to resume the device while a PM operation
* is already in progress.
+ *
+ * Do this only while a runtime PM operation is in progress. The
+ * system PM and poweroff callbacks hold host_sem, and
+ * shutting_down makes the regular path below stop, so it does
+ * not reset the link concurrently with them.
*/
ufshcd_rpm_get_noresume(hba);
- if (hba->pm_op_in_progress) {
+ if (ufshcd_rpm_transition_in_progress(hba)) {
ufshcd_link_recovery(hba);
ufshcd_rpm_put(hba);
return;
--
2.34.1