Re: [PATCH v2] scsi: core: pair EH runtime PM get and put

Alan Stern <[email protected]>
Newsgroups org.kernel.vger.linux-scsi,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
On Mon, Jul 27, 2026 at 01:56:15PM +0800, Hongjie Fang wrote:
> 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 snapshotting shost->eh_noresume once per EH iteration and
> using 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 <[email protected]>
> ---
>  drivers/scsi/scsi_error.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
> index 147127fb4db9..a216f56044d9 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 = shost->eh_noresume;

You should be aware that simply reading a variable does not snapshot its 
value unless the read is protected by a lock or something else.  In this 
case, the compiler is allowed to eliminate the eh_noresume variable and 
use shost->eh_noresume instead, both here and below, under the 
assumption that shost->eh_noresume doesn't change in the meantime.

To truly snapshot the the value in a way that's immune to changes from 
other threads, you have to use READ_ONCE():

		eh_noresume = READ_ONCE(shost->eh_noresume);

The compiler is then not allowed to assume that shost->eh_noresume 
remains unchanged later on.  This is all explained (along with a _lot_ 
of other material -- search for "READ_ONCE") in 
Documentation/memory-barriers.txt.

Alan Stern

> +		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);
> -- 
> 2.25.1
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.