* [PATCH v3] scsi: core: pair EH runtime PM get and put
@ 2026-07-29 11:16 Hongjie Fang
2026-07-29 11:27 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Hongjie Fang @ 2026-07-29 11:16 UTC (permalink / raw)
To: James.Bottomley, martin.petersen, avri.altman, stern, peter.wang,
bvanassche
Cc: linux-scsi, linux-kernel
shost->eh_noresume is currently consulted twice in one error handling
iteration: once before scsi_autopm_get_host() and once again before
scsi_autopm_put_host().
That is racy when a PM-triggered error path flips shost->eh_noresume while
the SCSI EH thread is still running.
The problem flow looks like this:
PM path
ufshcd_set_dev_pwr_mode()
shost->eh_noresume = 1
ufshcd_execute_start_stop <-- trigger EH
...
shost->eh_noresume = 0
EH path
scsi_error_handler()
if (!shost->eh_noresume)
scsi_autopm_get_host() <-- skipped
...
if (!shost->eh_noresume)
scsi_autopm_put_host() <-- executed later
In that case one EH iteration can skip autoresume on entry and still drop
a runtime PM reference on exit. That leaves an unmatched runtime PM put
and can trigger a runtime PM usage count underflow.
Fix this by making eh_noresume a regular bool so it can be accessed with
READ_ONCE() and WRITE_ONCE(). Snapshot it once per EH iteration and use
that snapshot for both runtime PM get and put decisions.
Fixes: ae0751ffc77e ("[SCSI] add flag to skip the runtime PM calls on the host")
Signed-off-by: Hongjie Fang <hongjiefang@asrmicro.com>
---
v3:
- Use READ_ONCE() and WRITE_ONCE() for eh_noresume.
- Make eh_noresume a regular bool since READ_ONCE() cannot be used on
bitfields.
v2:
- Snapshot the shost->eh_noresume once per EH iteration.
drivers/scsi/scsi_error.c | 6 ++++--
drivers/ufs/core/ufshcd.c | 4 ++--
include/scsi/scsi_host.h | 6 +++---
3 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 147127fb4db9..72515fa6daf7 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -2342,6 +2342,7 @@ static void scsi_unjam_host(struct Scsi_Host *shost)
int scsi_error_handler(void *data)
{
struct Scsi_Host *shost = data;
+ bool eh_noresume;
/*
* We use TASK_INTERRUPTIBLE so that the thread is not
@@ -2383,7 +2384,8 @@ int scsi_error_handler(void *data)
* what we need to do to get it up and online again (if we can).
* If we fail, we end up taking the thing offline.
*/
- if (!shost->eh_noresume && scsi_autopm_get_host(shost) != 0) {
+ eh_noresume = READ_ONCE(shost->eh_noresume);
+ if (!eh_noresume && scsi_autopm_get_host(shost) != 0) {
SCSI_LOG_ERROR_RECOVERY(1,
shost_printk(KERN_ERR, shost,
"scsi_eh_%d: unable to autoresume\n",
@@ -2407,7 +2409,7 @@ int scsi_error_handler(void *data)
* which are still online.
*/
scsi_restart_operations(shost);
- if (!shost->eh_noresume)
+ if (!eh_noresume)
scsi_autopm_put_host(shost);
}
__set_current_state(TASK_RUNNING);
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index c3f08957d179..9e2ae37340c9 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -9995,7 +9995,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,
* we are functional while we are here, skip host resume in error
* handling context.
*/
- hba->host->eh_noresume = 1;
+ WRITE_ONCE(hba->host->eh_noresume, 1);
/*
* Current function would be generally called from the power management
@@ -10017,7 +10017,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,
}
scsi_device_put(sdp);
- hba->host->eh_noresume = 0;
+ WRITE_ONCE(hba->host->eh_noresume, 0);
return ret;
}
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index 7e2011830ba4..857f955f08cf 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -664,6 +664,9 @@ struct Scsi_Host {
/* Asynchronous scan in progress */
bool async_scan __guarded_by(&scan_mutex);
+ /* Don't resume host in EH */
+ bool eh_noresume;
+
unsigned active_mode:2;
/*
@@ -682,9 +685,6 @@ struct Scsi_Host {
/* Task mgmt function in progress */
unsigned tmf_in_progress:1;
- /* Don't resume host in EH */
- unsigned eh_noresume:1;
-
/* The controller does not support WRITE SAME */
unsigned no_write_same:1;
--
2.25.1
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH v3] scsi: core: pair EH runtime PM get and put
2026-07-29 11:16 [PATCH v3] scsi: core: pair EH runtime PM get and put Hongjie Fang
@ 2026-07-29 11:27 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-07-29 11:27 UTC (permalink / raw)
To: Hongjie Fang; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but the SCSI error handler thread enters an infinite CPU spin (livelock) if `scsi_autopm_get_host(shost)` persistently fails.
--
--- Patch [1]: [PATCH v3] scsi: core: pair EH runtime PM get and put ---
commit c29848e943092f2d5f259d4461009f3a5aa7ad12
Author: Hongjie Fang <hongjiefang@asrmicro.com>
scsi: core: pair EH runtime PM get and put
This commit modifies the SCSI error handler to snapshot the eh_noresume
flag using READ_ONCE() at the start of each EH iteration. This ensures
that runtime PM get and put operations remain balanced even if the flag
changes concurrently. It also changes eh_noresume from a bitfield to a
regular bool.
> diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
> index 453a2232452db..74b70801269bd 100644
> --- a/drivers/scsi/scsi_error.c
> +++ b/drivers/scsi/scsi_error.c
[ ... ]
> @@ -2403,7 +2404,8 @@ int scsi_error_handler(void *data)
> * what we need to do to get it up and online again (if we can).
> * If we fail, we end up taking the thing offline.
> */
> - if (!shost->eh_noresume && scsi_autopm_get_host(shost) != 0) {
> + eh_noresume = READ_ONCE(shost->eh_noresume);
> + if (!eh_noresume && scsi_autopm_get_host(shost) != 0) {
> SCSI_LOG_ERROR_RECOVERY(1,
> shost_printk(KERN_ERR, shost,
> "scsi_eh_%d: unable to autoresume\n",
> shost->host_no));
> continue;
^^^^^^^^
[Severity: High]
This isn't a bug introduced by this patch, but does this continue statement
cause an infinite CPU spin if scsi_autopm_get_host() persistently fails?
If the SCSI host encounters an error, shost->host_failed == scsi_host_busy(shost)
becomes true, and the error handler thread wakes up. If the PM resume returns
a non-zero error (like -ENODEV during hot-unplug), the code logs an error and
hits this continue statement.
This skips the scsi_unjam_host() call and the shost->host_failed = 0
assignment. On the next loop iteration in scsi_error_handler(), host_failed
still equals host_busy, so the sleep condition remains false. This causes
the thread to immediately loop and retry the PM resume, spinning endlessly
on a CPU core.
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729111614.2407559-1-hongjiefang@asrmicro.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-29 11:27 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-29 11:16 [PATCH v3] scsi: core: pair EH runtime PM get and put Hongjie Fang
2026-07-29 11:27 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.