[PATCH v4 5/6] scsi: core: Protect host state changes with the host lock
Bart Van Assche <[email protected]> Fri, 31 Jul 2026 14:52:10 -0700
| Newsgroups | org.kernel.vger.linux-scsi |
|---|---|
| Message-ID | <970cc36fb627fae64b4957954483c74cf8e59c87.1785534721.git.bvanassche@acm.org> |
Some but not all SCSI host state changes are protected with the SCSI host lock. Annotate the SCSI host state with __guarded_by(host_lock), protect all SCSI host state changes with the SCSI host lock and use READ_ONCE() for all SCSI host state reads. This patch prevents that KCSAN complains about data races when accessing the SCSI host state. Reported-by: Jianzhou Zhao <[email protected]> Closes: https://lore.kernel.org/all/36d59d0e.6db0.19cdbeee01b.Coremail.lu= [email protected]/ Signed-off-by: Bart Van Assche <[email protected]> --- drivers/scsi/hosts.c | 16 +++++++++------- drivers/scsi/megaraid/megaraid_sas_base.c | 2 +- drivers/scsi/mpi3mr/mpi3mr_os.c | 2 +- drivers/scsi/mpt3sas/mpt3sas_scsih.c | 2 +- drivers/scsi/qla4xxx/ql4_os.c | 6 ++---- drivers/scsi/scsi_lib.c | 3 +-- drivers/scsi/scsi_sysfs.c | 7 ++++--- include/scsi/scsi_host.h | 23 ++++++++++++++++------- 8 files changed, 35 insertions(+), 26 deletions(-) diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index d512080268af..190b1932416b 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -145,7 +145,7 @@ int scsi_host_set_state(struct Scsi_Host *shost, enum= scsi_host_state state) } break; } - shost->shost_state =3D state; + WRITE_ONCE(shost->shost_state, state); return 0; =20 illegal: @@ -276,7 +276,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, s= truct device *dev, if (error) goto out_disable_runtime_pm; =20 - scsi_host_set_state(shost, SHOST_RUNNING); + scoped_guard(spinlock_irq, shost->host_lock) + scsi_host_set_state(shost, SHOST_RUNNING); get_device(shost->shost_gendev.parent); =20 device_enable_async_suspend(&shost->shost_dev); @@ -350,6 +351,7 @@ EXPORT_SYMBOL(scsi_add_host_with_dma); static void scsi_host_dev_release(struct device *dev) { struct Scsi_Host *shost =3D dev_to_shost(dev); + enum scsi_host_state host_state =3D scsi_get_host_state(shost); struct device *parent =3D dev->parent; =20 /* Wait for functions invoked through call_rcu(&scmd->rcu, ...) */ @@ -362,7 +364,7 @@ static void scsi_host_dev_release(struct device *dev) if (shost->work_q) destroy_workqueue(shost->work_q); =20 - if (shost->shost_state =3D=3D SHOST_CREATED) { + if (host_state =3D=3D SHOST_CREATED) { /* * Free the shost_dev device name and remove the proc host dir * here if scsi_host_{alloc,put}() have been called but neither @@ -378,7 +380,7 @@ static void scsi_host_dev_release(struct device *dev) =20 ida_free(&host_index_ida, shost->host_no); =20 - if (shost->shost_state !=3D SHOST_CREATED) + if (host_state !=3D SHOST_CREATED) put_device(parent); kfree(shost); } @@ -411,8 +413,8 @@ struct Scsi_Host *scsi_host_alloc(const struct scsi_h= ost_template *sht, int priv return NULL; =20 shost->host_lock =3D &shost->default_lock; - spin_lock_init(shost->host_lock); - shost->shost_state =3D SHOST_CREATED; + scoped_guard(spinlock_init, shost->host_lock) + shost->shost_state =3D SHOST_CREATED; INIT_LIST_HEAD(&shost->__devices); INIT_LIST_HEAD(&shost->__targets); INIT_LIST_HEAD(&shost->eh_abort_list); @@ -598,7 +600,7 @@ EXPORT_SYMBOL(scsi_host_lookup); **/ struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost) { - if ((shost->shost_state =3D=3D SHOST_DEL) || + if (scsi_get_host_state(shost) =3D=3D SHOST_DEL || !get_device(&shost->shost_gendev)) return NULL; return shost; diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/meg= araid/megaraid_sas_base.c index ecd365d78ae3..f0152b043e18 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -3072,7 +3072,7 @@ static int megasas_reset_bus_host(struct scsi_cmnd = *scmd) =20 scmd_printk(KERN_INFO, scmd, "SCSI host state: %d SCSI host busy: %d FW outstanding: %d\n", - scmd->device->host->shost_state, + scsi_get_host_state(scmd->device->host), scsi_host_busy(scmd->device->host), atomic_read(&instance->fw_outstanding)); /* diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr= _os.c index 402d1f35d214..f80a21ec161b 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_os.c +++ b/drivers/scsi/mpi3mr/mpi3mr_os.c @@ -5172,7 +5172,7 @@ static enum scsi_qc_status mpi3mr_qcmd(struct Scsi_= Host *shost, =20 /* Avoid error handling escalation when device is removed or blocked */ =20 - if (scmd->device->host->shost_state =3D=3D SHOST_RECOVERY && + if (scsi_get_host_state(scmd->device->host) =3D=3D SHOST_RECOVERY && scmd->cmnd[0] =3D=3D TEST_UNIT_READY && (stgt_priv_data->dev_removed || (dev_handle =3D=3D MPI3MR_INVALID_DEV_= HANDLE))) { scsi_build_sense(scmd, 0, UNIT_ATTENTION, 0x29, 0x07); diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/= mpt3sas_scsih.c index dea78688cc9b..0e12009a87f6 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -5472,7 +5472,7 @@ static enum scsi_qc_status scsih_qcmd(struct Scsi_H= ost *shost, * Avoid error handling escallation when device is disconnected */ if (handle =3D=3D MPT3SAS_INVALID_DEVICE_HANDLE || sas_device_priv_data= ->block) { - if (scmd->device->host->shost_state =3D=3D SHOST_RECOVERY && + if (scsi_get_host_state(scmd->device->host) =3D=3D SHOST_RECOVERY && scmd->cmnd[0] =3D=3D TEST_UNIT_READY) { scsi_build_sense(scmd, 0, UNIT_ATTENTION, 0x29, 0x07); scsi_done(scmd); diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.= c index d598ab4126f8..c9d9fc7c81fb 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -9411,11 +9411,9 @@ static int qla4xxx_eh_target_reset(struct scsi_cmn= d *cmd) * This routine finds that if reset host is called in EH * scenario or from some application like sg_reset **/ -static int qla4xxx_is_eh_active(struct Scsi_Host *shost) +static bool qla4xxx_is_eh_active(struct Scsi_Host *shost) { - if (shost->shost_state =3D=3D SHOST_RECOVERY) - return 1; - return 0; + return scsi_get_host_state(shost) =3D=3D SHOST_RECOVERY; } =20 /** diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 22e2e3223440..89d2e5a70e9b 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1661,10 +1661,9 @@ static enum scsi_qc_status scsi_dispatch_cmd(struc= t scsi_cmnd *cmd) goto done; } =20 - if (unlikely(host->shost_state =3D=3D SHOST_DEL)) { + if (unlikely(scsi_get_host_state(host) =3D=3D SHOST_DEL)) { cmd->result =3D (DID_NO_CONNECT << 16); goto done; - } =20 trace_scsi_dispatch_cmd_start(cmd); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index dfc3559e7e04..9480432f650b 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -214,8 +214,9 @@ store_shost_state(struct device *dev, struct device_a= ttribute *attr, if (!state) return -EINVAL; =20 - if (scsi_host_set_state(shost, state)) - return -EINVAL; + scoped_guard(spinlock_irq, shost->host_lock) + if (scsi_host_set_state(shost, state)) + return -EINVAL; return count; } =20 @@ -223,7 +224,7 @@ static ssize_t show_shost_state(struct device *dev, struct device_attribute *attr, char= *buf) { struct Scsi_Host *shost =3D class_to_shost(dev); - const char *name =3D scsi_host_state_name(shost->shost_state); + const char *name =3D scsi_host_state_name(scsi_get_host_state(shost)); =20 if (!name) return -EINVAL; diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 7e2011830ba4..69d432fd8e32 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -727,7 +727,7 @@ struct Scsi_Host { unsigned int irq; =09 =20 - enum scsi_host_state shost_state; + enum scsi_host_state shost_state __guarded_by(host_lock); =20 /* ldm bits */ struct device shost_gendev, shost_dev; @@ -785,11 +785,18 @@ static inline struct Scsi_Host *dev_to_shost(struct= device *dev) return container_of(dev, struct Scsi_Host, shost_gendev); } =20 +static inline enum scsi_host_state scsi_get_host_state(struct Scsi_Host = *shost) +{ + return context_unsafe(READ_ONCE(shost->shost_state)); +} + static inline int scsi_host_in_recovery(struct Scsi_Host *shost) { - return shost->shost_state =3D=3D SHOST_RECOVERY || - shost->shost_state =3D=3D SHOST_CANCEL_RECOVERY || - shost->shost_state =3D=3D SHOST_DEL_RECOVERY || + enum scsi_host_state state =3D scsi_get_host_state(shost); + + return state =3D=3D SHOST_RECOVERY || + state =3D=3D SHOST_CANCEL_RECOVERY || + state =3D=3D SHOST_DEL_RECOVERY || shost->tmf_in_progress; } =20 @@ -835,8 +842,9 @@ static inline struct device *scsi_get_device(struct S= csi_Host *shost) **/ static inline int scsi_host_scan_allowed(struct Scsi_Host *shost) { - return shost->shost_state =3D=3D SHOST_RUNNING || - shost->shost_state =3D=3D SHOST_RECOVERY; + enum scsi_host_state state =3D scsi_get_host_state(shost); + + return state =3D=3D SHOST_RUNNING || state =3D=3D SHOST_RECOVERY; } =20 extern void scsi_unblock_requests(struct Scsi_Host *); @@ -940,6 +948,7 @@ static inline unsigned char scsi_host_get_guard(struc= t Scsi_Host *shost) return shost->prot_guard_type; } =20 -extern int scsi_host_set_state(struct Scsi_Host *, enum scsi_host_state)= ; +int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state st= ate) + __must_hold(shost->host_lock); =20 #endif /* _SCSI_SCSI_HOST_H */