* [PATCH v7 0/3] scsi: ufs: core: Avoid redundant link recovery from the error handler during PM
[not found] <CGME20261106061544epcas2p4b1ebab9e2c0e041ee52ff724caf8a9e8@epcas2p4.samsung.com>
@ 2026-11-06 6:11 ` HOYOUNG SEO
[not found] ` <CGME20261106061545epcas2p2e16985621e8852e675e5a557857dca54@epcas2p2.samsung.com>
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: HOYOUNG SEO @ 2026-11-06 6:11 UTC (permalink / raw)
To: linux-scsi, linux-kernel, bvanassche, mkp, martin.petersen,
stanleyjhu, alim.akhtar, kwangwon.min, kwmad.kim, cpgs, h10.kim
Cc: HOYOUNG SEO
If a LINERESET is reported and the hibern8 exit fails during system
resume, both ufshcd_err_handler() and __ufshcd_wl_resume() call
ufshcd_link_recovery(), and the two recoveries are not serialized.
Patch 1 makes ufshcd_link_recovery() clear the saved errors like
ufshcd_reset_and_restore() does, so the error handler does not act on
errors that have already been recovered.
Patch 2 makes ufshcd_wl_poweroff() hold host_sem like
ufshcd_wl_suspend() does, so that the error handler waits for it, and
fixes the unbalanced release of host_sem if hibernation is aborted.
Patch 3 restricts the early link recovery in ufshcd_err_handler(), which
does not hold host_sem, to runtime PM. For system PM and poweroff the
error handler now waits for host_sem, and for shutdown it stops.
The early link recovery is kept for runtime suspend and runtime resume.
During runtime resume it can still run concurrently with
__ufshcd_wl_resume(). Removing it there needs a closer look at the
deadlock that it avoids.
Changes in v7:
- Patch 1: add Reviewed-by: Bart Van Assche.
- Patch 2 (was v6 patch 3): hold host_sem in ufshcd_wl_poweroff() until
ufshcd_wl_resume() releases it, instead of setting hba->shutting_down,
as suggested by Bart Van Assche. This fixes the failure of
ufs_mtk_system_suspend() and the state left behind when hibernation is
aborted, and the unbalanced release of host_sem. Add a Fixes: tag and
move the patch before the one that depends on it, so that the series
stays bisectable.
- Patch 3 (was v6 patch 2): use READ_ONCE() and WRITE_ONCE() for
hba->runtime_pm_in_progress, and describe the behavior change during
shutdown in the commit message.
Changes in v6:
- Go back to a single flag instead of the system PM flag of v5, and set
it in the runtime PM callbacks. The error handler no longer reads two
variables that are not updated atomically (Sashiko review of v5).
- Add a patch that sets hba->shutting_down in ufshcd_wl_poweroff()
(Sashiko review of v4).
Changes in v5:
- Patch 2: track system PM (hba->system_pm_in_progress) instead of
runtime PM, so that the early link recovery is only skipped where
host_sem is held.
Changes in v4 (as suggested by Bart Van Assche):
- Replace the is_sys_suspended check of v3 patch 2 with a flag set in
__ufshcd_wl_suspend() and __ufshcd_wl_resume(), which also covers a
system suspend failure.
- Add Fixes: tags.
- Do not serialize ufshcd_link_recovery() with a mutex: it calls
ufshcd_probe_hba(), and a device command timing out inside it ends up
in ufshcd_eh_timed_out(), which calls ufshcd_link_recovery() again.
Changes in v3:
- Patch 2: reword the commit message only, no code change. The error
handler does not always run a full recovery after a failed system
resume; it stops according to ufshcd_err_handling_should_stop() (e.g.
in UFSHCD_STATE_ERROR). Describe this behavior and refer to the commit
that added the early link recovery (Sashiko review).
- Patch 1: no change.
Changes in v2:
- Add patch 1. As pointed out by the Sashiko review, saved_err and
saved_uic_err are still set after the resume path recovered the link,
so with v1 alone ufshcd_err_handler() would run a redundant recovery
once host_sem is released.
- Patch 2: describe the race and the system/runtime PM difference in
the commit message, and add a comment.
v6: https://lore.kernel.org/linux-scsi/cover.1793931113.git.hy50.seo@samsung.com/
v5: https://lore.kernel.org/linux-scsi/cover.1793865172.git.hy50.seo@samsung.com/
v4: https://lore.kernel.org/linux-scsi/cover.1793851432.git.hy50.seo@samsung.com/
v3: https://lore.kernel.org/linux-scsi/cover.1792989286.git.hy50.seo@samsung.com/
v2: https://lore.kernel.org/linux-scsi/cover.1792755091.git.hy50.seo@samsung.com/
HOYOUNG SEO (3):
scsi: ufs: core: Clear saved errors in ufshcd_link_recovery()
scsi: ufs: core: Hold host_sem in ufshcd_wl_poweroff()
scsi: ufs: core: Limit early link recovery in the error handler to
runtime PM
drivers/ufs/core/ufshcd.c | 28 ++++++++++++++++++++++++++--
include/ufs/ufshcd.h | 2 ++
2 files changed, 28 insertions(+), 2 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v7 1/3] scsi: ufs: core: Clear saved errors in ufshcd_link_recovery()
[not found] ` <CGME20261106061545epcas2p2e16985621e8852e675e5a557857dca54@epcas2p2.samsung.com>
@ 2026-11-06 6:11 ` HOYOUNG SEO
0 siblings, 0 replies; 6+ messages in thread
From: HOYOUNG SEO @ 2026-11-06 6:11 UTC (permalink / raw)
To: linux-scsi, linux-kernel, bvanassche, mkp, martin.petersen,
stanleyjhu, alim.akhtar, kwangwon.min, kwmad.kim, cpgs, h10.kim
Cc: HOYOUNG SEO
ufshcd_link_recovery() resets both the device and the host and then
re-establishes the link. Unlike ufshcd_reset_and_restore(), however, it
does not clear hba->saved_err, hba->saved_uic_err and hba->force_reset,
so the errors latched before the recovery remain pending after the link
has already been recovered.
When ufshcd_err_handler() runs afterwards,
ufshcd_err_handling_should_stop() sees these stale errors. The error
handler then quiesces all queues and, depending on the latched errors
(e.g. a fatal error or UFSHCD_UIC_DL_PA_INIT_ERROR), performs another
full ufshcd_reset_and_restore() on a link that is already operational.
Cache and clear the saved errors before the reset, as
ufshcd_reset_and_restore() does, so that only errors raised during or
after the recovery are left to the error handler. Put them back if the
recovery fails.
Fixes: 35dabf4503b9 ("scsi: ufs: core: Use link recovery when h8 exit fails during runtime resume")
Signed-off-by: HOYOUNG SEO <hy50.seo@samsung.com>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
---
drivers/ufs/core/ufshcd.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index fd52deb72d21..3b5580ac390c 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -4695,10 +4695,20 @@ EXPORT_SYMBOL_GPL(ufshcd_uic_change_pwr_mode);
int ufshcd_link_recovery(struct ufs_hba *hba)
{
+ u32 saved_err, saved_uic_err;
int ret;
unsigned long flags;
spin_lock_irqsave(&hba->host->host_lock, flags);
+ /*
+ * The errors saved so far are handled by this full reset, so cache and
+ * clear them first, in case new errors are generated during recovery.
+ */
+ saved_err = hba->saved_err;
+ saved_uic_err = hba->saved_uic_err;
+ hba->saved_err = 0;
+ hba->saved_uic_err = 0;
+ hba->force_reset = false;
hba->ufshcd_state = UFSHCD_STATE_RESET;
ufshcd_set_eh_in_progress(hba);
spin_unlock_irqrestore(&hba->host->host_lock, flags);
@@ -4709,8 +4719,11 @@ int ufshcd_link_recovery(struct ufs_hba *hba)
ret = ufshcd_host_reset_and_restore(hba);
spin_lock_irqsave(&hba->host->host_lock, flags);
- if (ret)
+ if (ret) {
hba->ufshcd_state = UFSHCD_STATE_ERROR;
+ hba->saved_err |= saved_err;
+ hba->saved_uic_err |= saved_uic_err;
+ }
ufshcd_clear_eh_in_progress(hba);
spin_unlock_irqrestore(&hba->host->host_lock, flags);
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v7 2/3] scsi: ufs: core: Hold host_sem in ufshcd_wl_poweroff()
[not found] ` <CGME20261106061546epcas2p3b0d54ffe9fcea407f7d0e27ed579de0d@epcas2p3.samsung.com>
@ 2026-11-06 6:11 ` HOYOUNG SEO
2026-11-06 21:56 ` Bart Van Assche
0 siblings, 1 reply; 6+ messages in thread
From: HOYOUNG SEO @ 2026-11-06 6:11 UTC (permalink / raw)
To: linux-scsi, linux-kernel, bvanassche, mkp, martin.petersen,
stanleyjhu, alim.akhtar, kwangwon.min, kwmad.kim, cpgs, h10.kim
Cc: HOYOUNG SEO
ufshcd_wl_suspend() takes hba->host_sem and ufshcd_wl_resume() releases
it, which serializes the error handler against system suspend and
resume. ufshcd_wl_poweroff(), the hibernation poweroff callback, also
suspends the device with UFS_SHUTDOWN_PM but does not take host_sem. An
error handler that runs in the meantime can reset the link concurrently
with __ufshcd_wl_suspend().
It also leaves host_sem unbalanced: if hibernation is aborted after
ufshcd_wl_poweroff() has been called, ufshcd_wl_resume() is called as
the .restore callback and releases host_sem, which was not acquired.
Make ufshcd_wl_poweroff() behave like ufshcd_wl_suspend() and keep
holding host_sem. It is released by ufshcd_wl_resume() if hibernation is
aborted. Do not use hba->shutting_down here: it makes
ufs_mtk_system_suspend(), which is also the .poweroff callback of the
MediaTek host controller, fail with -EBUSY, and nothing clears it when
hibernation is aborted.
Fixes: b294ff3e3449 ("scsi: ufs: core: Enable power management for wlun")
Suggested-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: HOYOUNG SEO <hy50.seo@samsung.com>
---
drivers/ufs/core/ufshcd.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 3b5580ac390c..1c935a8d016d 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -11574,6 +11574,8 @@ static int ufshcd_wl_poweroff(struct device *dev)
struct scsi_device *sdev = to_scsi_device(dev);
struct ufs_hba *hba = shost_priv(sdev->host);
+ /* Released by ufshcd_wl_resume() if hibernation is aborted. */
+ down(&hba->host_sem);
__ufshcd_wl_suspend(hba, UFS_SHUTDOWN_PM);
return 0;
}
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v7 3/3] scsi: ufs: core: Limit early link recovery in the error handler to runtime PM
[not found] ` <CGME20261106061547epcas2p40541421c139d758cdc3633302634d29b@epcas2p4.samsung.com>
@ 2026-11-06 6:11 ` HOYOUNG SEO
2026-11-06 21:57 ` Bart Van Assche
0 siblings, 1 reply; 6+ messages in thread
From: HOYOUNG SEO @ 2026-11-06 6:11 UTC (permalink / raw)
To: linux-scsi, linux-kernel, bvanassche, mkp, martin.petersen,
stanleyjhu, alim.akhtar, kwangwon.min, kwmad.kim, cpgs, h10.kim
Cc: HOYOUNG SEO
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 PM operation is in progress.
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().
Since pm_op_in_progress does not tell which PM operation is running, add
hba->runtime_pm_in_progress. It is set in ufshcd_wl_runtime_suspend() and
ufshcd_wl_runtime_resume() around the PM operation, so it covers
pm_op_in_progress for runtime PM and is never set for the other PM
operations. The error handler tests only this flag. It is accessed
without locking, so use READ_ONCE() and WRITE_ONCE().
Fixes: e23ef4f22db3 ("scsi: ufs: core: Fix error handler host_sem issue")
Signed-off-by: HOYOUNG SEO <hy50.seo@samsung.com>
---
drivers/ufs/core/ufshcd.c | 11 ++++++++++-
include/ufs/ufshcd.h | 2 ++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 1c935a8d016d..3d748974e753 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 not do this for system PM, shutdown and poweroff. 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 (READ_ONCE(hba->runtime_pm_in_progress)) {
ufshcd_link_recovery(hba);
ufshcd_rpm_put(hba);
return;
@@ -10678,11 +10683,13 @@ static int ufshcd_wl_runtime_suspend(struct device *dev)
ktime_t start = ktime_get();
hba = shost_priv(sdev->host);
+ WRITE_ONCE(hba->runtime_pm_in_progress, true);
ret = __ufshcd_wl_suspend(hba, UFS_RUNTIME_PM);
if (ret)
dev_err(&sdev->sdev_gendev, "%s failed: %d\n", __func__, ret);
+ WRITE_ONCE(hba->runtime_pm_in_progress, false);
trace_ufshcd_wl_runtime_suspend(hba, ret,
ktime_to_us(ktime_sub(ktime_get(), start)),
hba->curr_dev_pwr_mode, hba->uic_link_state);
@@ -10698,11 +10705,13 @@ static int ufshcd_wl_runtime_resume(struct device *dev)
ktime_t start = ktime_get();
hba = shost_priv(sdev->host);
+ WRITE_ONCE(hba->runtime_pm_in_progress, true);
ret = __ufshcd_wl_resume(hba, UFS_RUNTIME_PM);
if (ret)
dev_err(&sdev->sdev_gendev, "%s failed: %d\n", __func__, ret);
+ WRITE_ONCE(hba->runtime_pm_in_progress, false);
trace_ufshcd_wl_runtime_resume(hba, ret,
ktime_to_us(ktime_sub(ktime_get(), start)),
hba->curr_dev_pwr_mode, hba->uic_link_state);
diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h
index be62165b785e..8b5bfc83fea2 100644
--- a/include/ufs/ufshcd.h
+++ b/include/ufs/ufshcd.h
@@ -974,6 +974,7 @@ enum ufshcd_mcq_opr {
* @spm_lvl: desired UFS power management level during system PM.
* @pm_lvl_min: minimum supported power management level.
* @pm_op_in_progress: whether or not a PM operation is in progress.
+ * @runtime_pm_in_progress: whether or not a runtime PM operation is in progress.
* @ahit: value of Auto-Hibernate Idle Timer register.
* @outstanding_tasks: Bits representing outstanding task requests
* @outstanding_lock: Protects @outstanding_reqs.
@@ -1129,6 +1130,7 @@ struct ufs_hba {
enum ufs_pm_level spm_lvl;
enum ufs_pm_level pm_lvl_min;
int pm_op_in_progress;
+ bool runtime_pm_in_progress;
/* Auto-Hibernate Idle Timer register value */
u32 ahit;
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v7 2/3] scsi: ufs: core: Hold host_sem in ufshcd_wl_poweroff()
2026-11-06 6:11 ` [PATCH v7 2/3] scsi: ufs: core: Hold host_sem in ufshcd_wl_poweroff() HOYOUNG SEO
@ 2026-11-06 21:56 ` Bart Van Assche
0 siblings, 0 replies; 6+ messages in thread
From: Bart Van Assche @ 2026-11-06 21:56 UTC (permalink / raw)
To: HOYOUNG SEO, linux-scsi, linux-kernel, mkp, martin.petersen,
stanleyjhu, alim.akhtar, kwangwon.min, kwmad.kim, cpgs, h10.kim
On 11/5/26 10:11 PM, HOYOUNG SEO wrote:
> @@ -11574,6 +11574,8 @@ static int ufshcd_wl_poweroff(struct device *dev)
> struct scsi_device *sdev = to_scsi_device(dev);
> struct ufs_hba *hba = shost_priv(sdev->host);
>
> + /* Released by ufshcd_wl_resume() if hibernation is aborted. */
> + down(&hba->host_sem);
> __ufshcd_wl_suspend(hba, UFS_SHUTDOWN_PM);
> return 0;
> }
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
As Sashiko pointed out, ufshcd_wl_poweroff() is still different from
ufshcd_wl_suspend() in other ways: it ignores the return value of
__ufshcd_wl_suspend(), it doesn't check pm_runtime_suspended(dev) and
it doesn't set hba->is_sys_suspended. This patch doesn't introduce any
of these issues, so I think they should be fixed separately. Please
consider a follow-up patch that makes ufshcd_wl_suspend() and
ufshcd_wl_poweroff() share a common helper function. Since returning an
error from the .poweroff callback aborts hibernation, that change should
be a separate patch with a commit message that explains the behavior
change.
Thanks,
Bart.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v7 3/3] scsi: ufs: core: Limit early link recovery in the error handler to runtime PM
2026-11-06 6:11 ` [PATCH v7 3/3] scsi: ufs: core: Limit early link recovery in the error handler to runtime PM HOYOUNG SEO
@ 2026-11-06 21:57 ` Bart Van Assche
0 siblings, 0 replies; 6+ messages in thread
From: Bart Van Assche @ 2026-11-06 21:57 UTC (permalink / raw)
To: HOYOUNG SEO, linux-scsi, linux-kernel, mkp, martin.petersen,
stanleyjhu, alim.akhtar, kwangwon.min, kwmad.kim, cpgs, h10.kim
On 11/5/26 10:11 PM, HOYOUNG SEO wrote:
> ufshcd_rpm_get_noresume(hba);
> - if (hba->pm_op_in_progress) {
> + if (READ_ONCE(hba->runtime_pm_in_progress)) {
> ufshcd_link_recovery(hba);
> ufshcd_rpm_put(hba);
> return;
Has it been considered to use the runtime PM status maintained by the
PM core instead of introducing a new member variable? While
ufshcd_wl_runtime_suspend() or ufshcd_wl_runtime_resume() is running,
hba->ufs_device_wlun->sdev_gendev.power.runtime_status is either
RPM_SUSPENDING or RPM_RESUMING. During system PM it is either
RPM_ACTIVE or RPM_SUSPENDED. Reading runtime_status without holding
power.lock is not more racy than reading the new flag.
Thanks,
Bart.
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-11-06 21:57 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <CGME20261106061544epcas2p4b1ebab9e2c0e041ee52ff724caf8a9e8@epcas2p4.samsung.com>
2026-11-06 6:11 ` [PATCH v7 0/3] scsi: ufs: core: Avoid redundant link recovery from the error handler during PM HOYOUNG SEO
[not found] ` <CGME20261106061545epcas2p2e16985621e8852e675e5a557857dca54@epcas2p2.samsung.com>
2026-11-06 6:11 ` [PATCH v7 1/3] scsi: ufs: core: Clear saved errors in ufshcd_link_recovery() HOYOUNG SEO
[not found] ` <CGME20261106061546epcas2p3b0d54ffe9fcea407f7d0e27ed579de0d@epcas2p3.samsung.com>
2026-11-06 6:11 ` [PATCH v7 2/3] scsi: ufs: core: Hold host_sem in ufshcd_wl_poweroff() HOYOUNG SEO
2026-11-06 21:56 ` Bart Van Assche
[not found] ` <CGME20261106061547epcas2p40541421c139d758cdc3633302634d29b@epcas2p4.samsung.com>
2026-11-06 6:11 ` [PATCH v7 3/3] scsi: ufs: core: Limit early link recovery in the error handler to runtime PM HOYOUNG SEO
2026-11-06 21:57 ` Bart Van Assche
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®