From 431e735e9bb6b7fd8e2ea9d83f0627bb5c5687ee Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 11 Aug 2026 14:39:51 -0700 Subject: [PATCH 01/26] scsi: qla2xxx: Fix size_t format specifier in qla29xx_process_rd_image() After commit c3930ec119cb ("scsi: qla2xxx: Add FC operational firmware load for 29xx"), there is a warning due to an incorrect format specifier for a 'size_t' variable when building for 32-bit platforms, for which 'size_t' is 'unsigned int': drivers/scsi/qla2xxx/qla_init.c: In function 'qla29xx_process_rd_image': drivers/scsi/qla2xxx/qla_init.c:9272:74: error: format '%lx' expects argument of type 'long unsigned int', but argument 6 has type 'size_t' {aka 'unsigned int'} [-Werror=format=] 9272 | "TIM section too large (0x%x bytes, ring 0x%lx bytes).\n", | ~~^ | | | long unsigned int | %x 9273 | section_size, 9274 | req->length * qla_req_entry_size(ha)); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | size_t {aka unsigned int} cc1: all warnings being treated as errors Use '%zx', the proper 'size_t' format specifier, to clear up the warning. Fixes: c3930ec119cb ("scsi: qla2xxx: Add FC operational firmware load for 29xx") Signed-off-by: Nathan Chancellor Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260811-scsi-qla2xxxx-qla_init-wformat-v1-1-50760021914f@kernel.org Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/qla2xxx/qla_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 2b9a9c672ec6..900cd141928e 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -9269,7 +9269,7 @@ static int qla29xx_process_rd_image(struct scsi_qla_host *vha, if (section == TIM) { if (section_size > req->length * qla_req_entry_size(ha)) { ql_log(ql_log_fatal, vha, 0x0098, - "TIM section too large (0x%x bytes, ring 0x%lx bytes).\n", + "TIM section too large (0x%x bytes, ring 0x%zx bytes).\n", section_size, req->length * qla_req_entry_size(ha)); return QLA_FUNCTION_FAILED; From b9f679dfe629004b593f018df33b330d799bcee4 Mon Sep 17 00:00:00 2001 From: Chandrakanth Patil Date: Sat, 8 Aug 2026 20:40:10 +0530 Subject: [PATCH 02/26] scsi: mpt3sas: Avoid freeing unallocated PCIe SGL buffers _base_release_memory_pools() unconditionally frees every ioc->pcie_sg_lookup[] entry, including ones the setup loop never allocated after a partial failure, causing a "bad dma" warning on debug kernels or a NULL pointer dereference otherwise. Fixes: dbec4c9040ed ("scsi: mpt3sas: lockless command submission") Reported-by: Laurence Oberman Signed-off-by: Chandrakanth Patil Link: https://patch.msgid.link/20260808151010.185603-1-chandrakanth.patil@broadcom.com Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/mpt3sas/mpt3sas_base.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 791a3c5fbf44..fed7aeffec58 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -5870,6 +5870,8 @@ _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc) if (ioc->pcie_sgl_dma_pool) { for (i = 0; i < ioc->scsiio_depth; i++) { + if (!ioc->pcie_sg_lookup[i].pcie_sgl) + continue; dma_pool_free(ioc->pcie_sgl_dma_pool, ioc->pcie_sg_lookup[i].pcie_sgl, ioc->pcie_sg_lookup[i].pcie_sgl_dma); From 4c2128c1a399318859d9eea0b74613d90f4bcb66 Mon Sep 17 00:00:00 2001 From: Xingui Yang Date: Tue, 11 Aug 2026 12:03:33 +0800 Subject: [PATCH 03/26] scsi: libsas: Add sas_ex_phy_to_dev() helper Add sas_ex_phy_to_dev() to return any device type attached to an expander phy, and refactor sas_ex_to_ata() to use it. No functional changes intended. Signed-off-by: Xingui Yang Reviewed-by: John Garry Link: https://patch.msgid.link/20260811040334.4184911-2-yangxingui@huawei.com Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/libsas/sas_expander.c | 13 +++++++++---- drivers/scsi/libsas/sas_internal.h | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index f471ab464a78..a5c5327cd0dd 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -345,11 +345,10 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, SAS_ADDR(phy->attached_sas_addr), type); } -/* check if we have an existing attached ata device on this expander phy */ -struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) +/* Return the domain device attached to an expander phy */ +struct domain_device *sas_ex_phy_to_dev(struct domain_device *ex_dev, int phy_id) { struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy_id]; - struct domain_device *dev; struct sas_rphy *rphy; if (!ex_phy->port) @@ -359,7 +358,13 @@ struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) if (!rphy) return NULL; - dev = sas_find_dev_by_rphy(rphy); + return sas_find_dev_by_rphy(rphy); +} + +/* Check if we have an existing attached ata device on this expander phy */ +struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) +{ + struct domain_device *dev = sas_ex_phy_to_dev(ex_dev, phy_id); if (dev && dev_is_sata(dev)) return dev; diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 7dce0f587149..f5c75ab10dc4 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -91,6 +91,7 @@ int sas_smp_get_phy_events(struct sas_phy *phy); void sas_device_set_phy(struct domain_device *dev, struct sas_port *port); struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); +struct domain_device *sas_ex_phy_to_dev(struct domain_device *ex_dev, int phy_id); struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id); int sas_ex_phy_discover(struct domain_device *dev, int single); int sas_get_report_phy_sata(struct domain_device *dev, int phy_id, From db441dcb8c1452279956b7ea4e394b5ce8ce77f1 Mon Sep 17 00:00:00 2001 From: Xingui Yang Date: Tue, 11 Aug 2026 12:03:34 +0800 Subject: [PATCH 04/26] scsi: libsas: Add linkrate and sas_addr change detection in rediscover Introduce sas_dev_is_flutter() and sas_rediscover_ex_phy() to improve flutter and device replace detection during rediscovery. sas_dev_is_flutter() calls sas_ex_phy_discover() before looking up the child device via sas_ex_phy_to_dev(), ensuring the PHY state is always updated and avoiding use-after-free since the child device pointer is obtained after the sleeping SMP request completes. Add validation for linkrate and sas_addr changes. When the SAS address changes, phy->attached_sas_addr is restored to the original address before returning false, so sas_unregister_devs_sas_addr() can properly match and unregister the old device. The sas_addr check is ordered before the linkrate check to avoid skipping the restoration when both change simultaneously. sas_rediscover_ex_phy() uses the async discovery pattern (sas_discover_event) instead of the synchronous sas_discover_new() to ensure proper ordering between device unregistration and rediscovery, avoiding sysfs_warn_dup() errors. Signed-off-by: Xingui Yang Suggested-by: John Garry Link: https://patch.msgid.link/20260811040334.4184911-3-yangxingui@huawei.com Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/libsas/sas_expander.c | 85 ++++++++++++++++++++++++------ 1 file changed, 70 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index a5c5327cd0dd..811c9eb4fef1 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -1963,6 +1963,72 @@ static bool dev_type_flutter(enum sas_device_type new, enum sas_device_type old) return false; } +static void sas_rediscover_ex_phy(struct domain_device *dev, int phy_id, + bool last) +{ + struct expander_device *ex = &dev->ex_dev; + struct ex_phy *phy = &ex->ex_phy[phy_id]; + + phy->phy_change_count = -1; + ex->ex_change_count = -1; + sas_unregister_devs_sas_addr(dev, phy_id, last); + sas_discover_event(dev->port, DISCE_REVALIDATE_DOMAIN); +} + +static bool sas_dev_is_flutter(struct domain_device *dev, int phy_id, + u8 *sas_addr, enum sas_device_type type) +{ + struct expander_device *ex = &dev->ex_dev; + struct ex_phy *phy = &ex->ex_phy[phy_id]; + struct domain_device *child_dev; + char *action = ""; + int res; + + if (SAS_ADDR(sas_addr) != SAS_ADDR(phy->attached_sas_addr) || + !dev_type_flutter(type, phy->attached_dev_type)) + return false; + + res = sas_ex_phy_discover(dev, phy_id); + if (res) + return false; + + child_dev = sas_ex_phy_to_dev(dev, phy_id); + if (!child_dev) + goto out; + + if (dev_is_sata(child_dev) && + phy->attached_dev_type == SAS_SATA_PENDING) { + action = ", needs recovery"; + goto out; + } + + if (SAS_ADDR(child_dev->sas_addr) != SAS_ADDR(phy->attached_sas_addr)) { + pr_info("ex %016llx phy%02d sas_addr changed from %016llx to %016llx\n", + SAS_ADDR(dev->sas_addr), phy_id, + SAS_ADDR(child_dev->sas_addr), + SAS_ADDR(phy->attached_sas_addr)); + /* + * Device unregistering relies on address matching. Restore + * attached_sas_addr back to the original address so that the old + * device can be unregistered later + */ + memcpy(phy->attached_sas_addr, child_dev->sas_addr, SAS_ADDR_SIZE); + return false; + } + + if (child_dev->linkrate != phy->linkrate) { + pr_info("ex %016llx phy%02d linkrate changed from %d to %d\n", + SAS_ADDR(dev->sas_addr), phy_id, + child_dev->linkrate, phy->linkrate); + return false; + } + +out: + pr_debug("ex %016llx phy%02d broadcast flutter%s\n", + SAS_ADDR(dev->sas_addr), phy_id, action); + return true; +} + static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last, int sibling) { @@ -2016,27 +2082,16 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id, if (res == 0) sas_set_ex_phy(dev, phy_id, disc_resp); goto out_free_resp; - } else if (SAS_ADDR(sas_addr) == SAS_ADDR(phy->attached_sas_addr) && - dev_type_flutter(type, phy->attached_dev_type)) { - struct domain_device *ata_dev = sas_ex_to_ata(dev, phy_id); - char *action = ""; - - sas_ex_phy_discover(dev, phy_id); - - if (ata_dev && phy->attached_dev_type == SAS_SATA_PENDING) - action = ", needs recovery"; - pr_debug("ex %016llx phy%02d broadcast flutter%s\n", - SAS_ADDR(dev->sas_addr), phy_id, action); - goto out_free_resp; } + if (sas_dev_is_flutter(dev, phy_id, sas_addr, type)) + goto out_free_resp; + /* we always have to delete the old device when we went here */ pr_info("ex %016llx phy%02d replace %016llx\n", SAS_ADDR(dev->sas_addr), phy_id, SAS_ADDR(phy->attached_sas_addr)); - sas_unregister_devs_sas_addr(dev, phy_id, last); - - res = sas_discover_new(dev, phy_id); + sas_rediscover_ex_phy(dev, phy_id, last); out_free_resp: kfree(disc_resp); return res; From 720d8b2f2457f4962ca353dd90395e2660d9a10d Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 7 Aug 2026 15:49:46 -0700 Subject: [PATCH 05/26] scsi: ufs: core: Set task state before io_schedule_timeout() Set the task state to TASK_UNINTERRUPTIBLE before calling io_schedule_timeout() in ufshcd_wait_for_pending_cmds(). Without setting the task state, io_schedule_timeout() returns immediately because the task state remains TASK_RUNNING. This results in a busy loop that wastes CPU cycles. Fixes: 2000bc309703 ("scsi: ufs: core: Reduce the clock scaling latency") Reviewed-by: Peter Wang Reported-by: Sashiko Signed-off-by: Bart Van Assche Link: https://patch.msgid.link/8fe4526ce272811b28e99048b42358dd8f7c48af.1786142946.git.bvanassche@acm.org Signed-off-by: Martin K. Petersen (Oracle) --- drivers/ufs/core/ufshcd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index a51e071916cf..2c5d8e0e696e 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -1317,6 +1317,7 @@ static int ufshcd_wait_for_pending_cmds(struct ufs_hba *hba, break; } + __set_current_state(TASK_UNINTERRUPTIBLE); io_schedule_timeout(msecs_to_jiffies(20)); if (ktime_to_us(ktime_sub(ktime_get(), start)) > wait_timeout_us) { From ff5d552022b35f7475bdc538173b3ceb95d55d85 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 7 Aug 2026 15:49:47 -0700 Subject: [PATCH 06/26] scsi: ufs: core: Enable context analysis Annotate functions that modify the state of a synchronization object. Remove the struct semaphore annotations because lock context annotations are not supported for semaphores. Reviewed-by: Peter Wang Signed-off-by: Bart Van Assche Link: https://patch.msgid.link/3c975386a5bcb939f8a2a0d47fd621f234321a9e.1786142946.git.bvanassche@acm.org Signed-off-by: Martin K. Petersen (Oracle) --- drivers/ufs/core/Makefile | 2 ++ drivers/ufs/core/ufs-debugfs.c | 8 ++++++-- drivers/ufs/core/ufshcd.c | 14 ++++++++++++++ drivers/ufs/host/Makefile | 2 ++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/ufs/core/Makefile b/drivers/ufs/core/Makefile index ce7d16d2cf35..67ab9ffbdf5d 100644 --- a/drivers/ufs/core/Makefile +++ b/drivers/ufs/core/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 +CONTEXT_ANALYSIS := y + obj-$(CONFIG_SCSI_UFSHCD) += ufshcd-core.o ufshcd-core-y += ufshcd.o ufs-sysfs.o ufs-mcq.o ufs-txeq.o ufshcd-core-$(CONFIG_RPMB) += ufs-rpmb.o diff --git a/drivers/ufs/core/ufs-debugfs.c b/drivers/ufs/core/ufs-debugfs.c index be527209540d..ed1de70e2ec1 100644 --- a/drivers/ufs/core/ufs-debugfs.c +++ b/drivers/ufs/core/ufs-debugfs.c @@ -65,8 +65,10 @@ static int ee_usr_mask_get(void *data, u64 *val) return 0; } +token_context_lock(ufs_debugfs); + static int ufs_debugfs_get_user_access(struct ufs_hba *hba) -__acquires(&hba->host_sem) + __cond_acquires(0, ufs_debugfs) { down(&hba->host_sem); if (!ufshcd_is_user_access_allowed(hba)) { @@ -74,14 +76,16 @@ __acquires(&hba->host_sem) return -EBUSY; } ufshcd_rpm_get_sync(hba); + __acquire(ufs_debugfs); return 0; } static void ufs_debugfs_put_user_access(struct ufs_hba *hba) -__releases(&hba->host_sem) + __releases(ufs_debugfs) { ufshcd_rpm_put_sync(hba); up(&hba->host_sem); + __release(ufs_debugfs); } static int ee_usr_mask_set(void *data, u64 val) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 2c5d8e0e696e..67745d2796d0 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -1354,6 +1354,8 @@ static int ufshcd_wait_for_pending_cmds(struct ufs_hba *hba, * On failure, all acquired locks are released and the tagset is unquiesced. */ int ufshcd_pause_command_processing(struct ufs_hba *hba, u64 timeout_us) + __cond_acquires(0, &hba->host->scan_mutex) + __cond_acquires(0, &hba->clk_scaling_lock) { int ret = 0; @@ -1378,6 +1380,8 @@ int ufshcd_pause_command_processing(struct ufs_hba *hba, u64 timeout_us) * This function resumes command submissions. */ void ufshcd_resume_command_processing(struct ufs_hba *hba) + __releases(&hba->clk_scaling_lock) + __releases(&hba->host->scan_mutex) { up_write(&hba->clk_scaling_lock); blk_mq_unquiesce_tagset(&hba->host->tag_set); @@ -1447,6 +1451,9 @@ static int ufshcd_scale_gear(struct ufs_hba *hba, u32 target_gear, bool scale_up * Return: 0 upon success; -EBUSY upon timeout. */ static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba, u64 timeout_us) + __cond_acquires(0, &hba->host->scan_mutex) + __cond_acquires(0, &hba->wb_mutex) + __cond_acquires(0, &hba->clk_scaling_lock) { int ret = 0; /* @@ -1476,6 +1483,9 @@ static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba, u64 timeout_us) } static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba, int err) + __releases(&hba->clk_scaling_lock) + __releases(&hba->wb_mutex) + __releases(&hba->host->scan_mutex) { up_write(&hba->clk_scaling_lock); mutex_unlock(&hba->wb_mutex); @@ -3305,6 +3315,8 @@ ufshcd_dev_cmd_completion(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) } static void ufshcd_dev_man_lock(struct ufs_hba *hba) + __acquires(&hba->dev_cmd.lock) + __acquires_shared(&hba->clk_scaling_lock) { ufshcd_hold(hba); mutex_lock(&hba->dev_cmd.lock); @@ -3312,6 +3324,8 @@ static void ufshcd_dev_man_lock(struct ufs_hba *hba) } static void ufshcd_dev_man_unlock(struct ufs_hba *hba) + __releases_shared(&hba->clk_scaling_lock) + __releases(&hba->dev_cmd.lock) { up_read(&hba->clk_scaling_lock); mutex_unlock(&hba->dev_cmd.lock); diff --git a/drivers/ufs/host/Makefile b/drivers/ufs/host/Makefile index 65d8bb23ab7b..7d8db67eb23c 100644 --- a/drivers/ufs/host/Makefile +++ b/drivers/ufs/host/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 +CONTEXT_ANALYSIS := y + obj-$(CONFIG_SCSI_UFS_DWC_TC_PCI) += tc-dwc-g210-pci.o ufshcd-dwc.o tc-dwc-g210.o obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o ufshcd-dwc.o tc-dwc-g210.o obj-$(CONFIG_SCSI_UFS_CDNS_PLATFORM) += cdns-pltfrm.o From e70647b25a641b9f5602558839182d036b512de5 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 7 Aug 2026 15:49:48 -0700 Subject: [PATCH 07/26] scsi: core: Pass the SCSI host pointer directly to scanning functions In the functions scsi_probe_and_add_lun(), scsi_sequential_lun_scan(), scsi_report_lun_scan() and __scsi_scan_target() the SCSI host pointer is derived from the SCSI target pointer. Pass the SCSI host pointer directly. This patch prepares for enabling context analysis. With this patch applied, context annotations can refer to the SCSI host pointer directly, e.g. __must_hold(&shost->scan_mutex). Without this patch, the following annotation would have to be used: __must_hold(&dev_to_shost(starget->dev.parent)->scan_mutex) Additionally, in code that locks shost->scan_mutex, the following would have to be added to help the compiler understand that shost == dev_to_shost(starget->dev.parent): __assume_ctx_lock(&dev_to_shost(starget->dev.parent)->scan_mutex); __assume_ctx_lock() statements should be avoided if there is a good alternative. Hence this patch. No functionality has been changed. Reviewed-by: John Garry Signed-off-by: Bart Van Assche Link: https://patch.msgid.link/49d2fc5fae5cb5dca2536818155581c73f39c883.1786142946.git.bvanassche@acm.org Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/scsi_scan.c | 47 ++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index e27da038603a..d7e0e9393194 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1169,6 +1169,7 @@ static unsigned char *scsi_inq_str(unsigned char *buf, unsigned char *inq, /** * scsi_probe_and_add_lun - probe a LUN, if a LUN is found add it + * @shost: SCSI host pointer * @starget: pointer to target device structure * @lun: LUN of target device * @bflagsp: store bflags here if not NULL @@ -1188,7 +1189,8 @@ static unsigned char *scsi_inq_str(unsigned char *buf, unsigned char *inq, * attached at the LUN * - SCSI_SCAN_LUN_PRESENT: a new scsi_device was allocated and initialized **/ -static int scsi_probe_and_add_lun(struct scsi_target *starget, +static int scsi_probe_and_add_lun(struct Scsi_Host *shost, + struct scsi_target *starget, u64 lun, blist_flags_t *bflagsp, struct scsi_device **sdevp, enum scsi_scan_mode rescan, @@ -1198,7 +1200,6 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, unsigned char *result; blist_flags_t bflags; int res = SCSI_SCAN_NO_RESPONSE, result_len = 256; - struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); /* * The rescan flag is used as an optimization, the first scan of a @@ -1334,6 +1335,7 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, /** * scsi_sequential_lun_scan - sequentially scan a SCSI target + * @shost: SCSI host pointer * @starget: pointer to target structure to scan * @bflags: black/white list flag for LUN 0 * @scsi_level: Which version of the standard does this device adhere to @@ -1346,13 +1348,13 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, * * Modifies sdevscan->lun. **/ -static void scsi_sequential_lun_scan(struct scsi_target *starget, +static void scsi_sequential_lun_scan(struct Scsi_Host *shost, + struct scsi_target *starget, blist_flags_t bflags, int scsi_level, enum scsi_scan_mode rescan) { uint max_dev_lun; u64 sparse_lun, lun; - struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); SCSI_LOG_SCAN_BUS(3, starget_printk(KERN_INFO, starget, "scsi scan: Sequential scan\n")); @@ -1412,14 +1414,15 @@ static void scsi_sequential_lun_scan(struct scsi_target *starget, * sparse_lun. */ for (lun = 1; lun < max_dev_lun; ++lun) - if ((scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan, - NULL) != SCSI_SCAN_LUN_PRESENT) && + if (scsi_probe_and_add_lun(shost, starget, lun, NULL, NULL, + rescan, NULL) != SCSI_SCAN_LUN_PRESENT && !sparse_lun) return; } /** * scsi_report_lun_scan - Scan using SCSI REPORT LUN results + * @shost: SCSI host pointer * @starget: which target * @bflags: Zero or a mix of BLIST_NOLUN, BLIST_REPORTLUN2, or BLIST_NOREPORTLUN * @rescan: nonzero if we can skip code only needed on first scan @@ -1438,8 +1441,9 @@ static void scsi_sequential_lun_scan(struct scsi_target *starget, * 0: scan completed (or no memory, so further scanning is futile) * 1: could not scan with REPORT LUN **/ -static int scsi_report_lun_scan(struct scsi_target *starget, blist_flags_t bflags, - enum scsi_scan_mode rescan) +static int scsi_report_lun_scan(struct Scsi_Host *shost, + struct scsi_target *starget, blist_flags_t bflags, + enum scsi_scan_mode rescan) { unsigned char scsi_cmd[MAX_COMMAND_SIZE]; unsigned int length; @@ -1448,7 +1452,6 @@ static int scsi_report_lun_scan(struct scsi_target *starget, blist_flags_t bflag int result; struct scsi_lun *lunp, *lun_data; struct scsi_device *sdev; - struct Scsi_Host *shost = dev_to_shost(&starget->dev); struct scsi_failure failure_defs[] = { { .sense = UNIT_ATTENTION, @@ -1594,7 +1597,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, blist_flags_t bflag } else { int res; - res = scsi_probe_and_add_lun(starget, + res = scsi_probe_and_add_lun(shost, starget, lun, NULL, NULL, rescan, NULL); if (res == SCSI_SCAN_NO_RESPONSE) { /* @@ -1641,7 +1644,7 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, scsi_complete_async_scans(); if (scsi_host_scan_allowed(shost) && scsi_autopm_get_host(shost) == 0) { - scsi_probe_and_add_lun(starget, lun, NULL, &sdev, + scsi_probe_and_add_lun(shost, starget, lun, NULL, &sdev, SCSI_SCAN_RESCAN, hostdata); scsi_autopm_put_host(shost); } @@ -1763,10 +1766,10 @@ int scsi_rescan_device(struct scsi_device *sdev) } EXPORT_SYMBOL(scsi_rescan_device); -static void __scsi_scan_target(struct device *parent, unsigned int channel, - unsigned int id, u64 lun, enum scsi_scan_mode rescan) +static void __scsi_scan_target(struct Scsi_Host *shost, struct device *parent, + unsigned int channel, unsigned int id, u64 lun, + enum scsi_scan_mode rescan) { - struct Scsi_Host *shost = dev_to_shost(parent); blist_flags_t bflags = 0; int res; struct scsi_target *starget; @@ -1786,7 +1789,8 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel, /* * Scan for a specific host/chan/id/lun. */ - scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan, NULL); + scsi_probe_and_add_lun(shost, starget, lun, NULL, NULL, rescan, + NULL); goto out_reap; } @@ -1794,14 +1798,15 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel, * Scan LUN 0, if there is some response, scan further. Ideally, we * would not configure LUN 0 until all LUNs are scanned. */ - res = scsi_probe_and_add_lun(starget, 0, &bflags, NULL, rescan, NULL); + res = scsi_probe_and_add_lun(shost, starget, 0, &bflags, NULL, rescan, + NULL); if (res == SCSI_SCAN_LUN_PRESENT || res == SCSI_SCAN_TARGET_PRESENT) { - if (scsi_report_lun_scan(starget, bflags, rescan) != 0) + if (scsi_report_lun_scan(shost, starget, bflags, rescan) != 0) /* * The REPORT LUN did not scan the target, * do a sequential scan. */ - scsi_sequential_lun_scan(starget, bflags, + scsi_sequential_lun_scan(shost, starget, bflags, starget->scsi_level, rescan); } @@ -1851,7 +1856,7 @@ void scsi_scan_target(struct device *parent, unsigned int channel, scsi_complete_async_scans(); if (scsi_host_scan_allowed(shost) && scsi_autopm_get_host(shost) == 0) { - __scsi_scan_target(parent, channel, id, lun, rescan); + __scsi_scan_target(shost, parent, channel, id, lun, rescan); scsi_autopm_put_host(shost); } mutex_unlock(&shost->scan_mutex); @@ -1882,11 +1887,11 @@ static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel, order_id = shost->max_id - id - 1; else order_id = id; - __scsi_scan_target(&shost->shost_gendev, channel, + __scsi_scan_target(shost, &shost->shost_gendev, channel, order_id, lun, rescan); } else - __scsi_scan_target(&shost->shost_gendev, channel, + __scsi_scan_target(shost, &shost->shost_gendev, channel, id, lun, rescan); } From 09982efcc07e739c7e4ac6089e3e521132388b53 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 7 Aug 2026 15:49:49 -0700 Subject: [PATCH 08/26] scsi: core: Add lock context annotations Document which functions expect that shost->scan_mutex is held. Reviewed-by: John Garry Signed-off-by: Bart Van Assche Link: https://patch.msgid.link/ad5ca37acf8c933a12830c0811c293af54c87573.1786142946.git.bvanassche@acm.org Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/scsi_scan.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index d7e0e9393194..3b82e80e807a 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1195,6 +1195,7 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *shost, struct scsi_device **sdevp, enum scsi_scan_mode rescan, void *hostdata) + __must_hold(&shost->scan_mutex) { struct scsi_device *sdev; unsigned char *result; @@ -1352,6 +1353,7 @@ static void scsi_sequential_lun_scan(struct Scsi_Host *shost, struct scsi_target *starget, blist_flags_t bflags, int scsi_level, enum scsi_scan_mode rescan) + __must_hold(&shost->scan_mutex) { uint max_dev_lun; u64 sparse_lun, lun; @@ -1444,6 +1446,7 @@ static void scsi_sequential_lun_scan(struct Scsi_Host *shost, static int scsi_report_lun_scan(struct Scsi_Host *shost, struct scsi_target *starget, blist_flags_t bflags, enum scsi_scan_mode rescan) + __must_hold(&shost->scan_mutex) { unsigned char scsi_cmd[MAX_COMMAND_SIZE]; unsigned int length; @@ -1769,6 +1772,7 @@ EXPORT_SYMBOL(scsi_rescan_device); static void __scsi_scan_target(struct Scsi_Host *shost, struct device *parent, unsigned int channel, unsigned int id, u64 lun, enum scsi_scan_mode rescan) + __must_hold(&shost->scan_mutex) { blist_flags_t bflags = 0; int res; @@ -1866,6 +1870,7 @@ EXPORT_SYMBOL(scsi_scan_target); static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel, unsigned int id, u64 lun, enum scsi_scan_mode rescan) + __must_hold(&shost->scan_mutex) { uint order_id; From 4c461ee2b2a5a7c327fe092b41de1fcc002adc01 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 7 Aug 2026 15:49:50 -0700 Subject: [PATCH 09/26] scsi: core: Protect host state changes with the host lock 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) and protect all SCSI host state changes with the SCSI host lock. This patch prevents that KCSAN complains about data races when accessing the SCSI host state. Reported-by: Jianzhou Zhao Closes: https://lore.kernel.org/all/36d59d0e.6db0.19cdbeee01b.Coremail.luckd0g@163.com/ Signed-off-by: Bart Van Assche Reviewed-by: John Garry Link: https://patch.msgid.link/681e4a5260c182feb5fc1d96f0d43c62c21dc6c9.1786142946.git.bvanassche@acm.org Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/hosts.c | 19 ++++++++++-------- drivers/scsi/leapraid/leapraid_func.c | 2 +- drivers/scsi/leapraid/leapraid_os.c | 2 +- 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 | 24 ++++++++++++++++------- 10 files changed, 40 insertions(+), 29 deletions(-) diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index d512080268af..9610dd7aa85f 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -73,8 +73,9 @@ static struct class shost_class = { * transition is illegal. **/ int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state) + __must_hold(shost->host_lock) { - enum scsi_host_state oldstate = shost->shost_state; + enum scsi_host_state oldstate = READ_ONCE(shost->shost_state); if (state == oldstate) return 0; @@ -145,7 +146,7 @@ int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state) } break; } - shost->shost_state = state; + WRITE_ONCE(shost->shost_state, state); return 0; illegal: @@ -276,7 +277,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, if (error) goto out_disable_runtime_pm; - 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); device_enable_async_suspend(&shost->shost_dev); @@ -350,6 +352,7 @@ EXPORT_SYMBOL(scsi_add_host_with_dma); static void scsi_host_dev_release(struct device *dev) { struct Scsi_Host *shost = dev_to_shost(dev); + enum scsi_host_state state = scsi_get_host_state(shost); struct device *parent = dev->parent; /* Wait for functions invoked through call_rcu(&scmd->rcu, ...) */ @@ -362,7 +365,7 @@ static void scsi_host_dev_release(struct device *dev) if (shost->work_q) destroy_workqueue(shost->work_q); - if (shost->shost_state == SHOST_CREATED) { + if (state == 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 +381,7 @@ static void scsi_host_dev_release(struct device *dev) ida_free(&host_index_ida, shost->host_no); - if (shost->shost_state != SHOST_CREATED) + if (state != SHOST_CREATED) put_device(parent); kfree(shost); } @@ -411,8 +414,8 @@ struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int priv return NULL; shost->host_lock = &shost->default_lock; - spin_lock_init(shost->host_lock); - shost->shost_state = SHOST_CREATED; + scoped_guard(spinlock_init, shost->host_lock) + shost->shost_state = SHOST_CREATED; INIT_LIST_HEAD(&shost->__devices); INIT_LIST_HEAD(&shost->__targets); INIT_LIST_HEAD(&shost->eh_abort_list); @@ -598,7 +601,7 @@ EXPORT_SYMBOL(scsi_host_lookup); **/ struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost) { - if ((shost->shost_state == SHOST_DEL) || + if (scsi_get_host_state(shost) == SHOST_DEL || !get_device(&shost->shost_gendev)) return NULL; return shost; diff --git a/drivers/scsi/leapraid/leapraid_func.c b/drivers/scsi/leapraid/leapraid_func.c index 3e1aeceab994..089d0810bd13 100644 --- a/drivers/scsi/leapraid/leapraid_func.c +++ b/drivers/scsi/leapraid/leapraid_func.c @@ -37,7 +37,7 @@ static noinline bool leapraid_shost_in_recovery(struct Scsi_Host *shost) { enum scsi_host_state state; - state = READ_ONCE(shost->shost_state); + state = scsi_get_host_state(shost); return state == SHOST_RECOVERY || state == SHOST_CANCEL_RECOVERY || state == SHOST_DEL_RECOVERY || diff --git a/drivers/scsi/leapraid/leapraid_os.c b/drivers/scsi/leapraid/leapraid_os.c index 2f3d4ac7490c..a8e1c9f33896 100644 --- a/drivers/scsi/leapraid/leapraid_os.c +++ b/drivers/scsi/leapraid/leapraid_os.c @@ -808,7 +808,7 @@ static bool leapraid_should_queuecommand(struct leapraid_adapter *adapter, goto no_connect; if (sdev_priv->block && - scmd->device->host->shost_state == SHOST_RECOVERY && + scsi_get_host_state(scmd->device->host) == SHOST_RECOVERY && scmd->cmnd[0] == TEST_UNIT_READY) { scsi_build_sense(scmd, 0, UNIT_ATTENTION, LEAPRAID_SCSI_ASC_POWER_ON_RESET, diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/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) 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, /* Avoid error handling escalation when device is removed or blocked */ - if (scmd->device->host->shost_state == SHOST_RECOVERY && + if (scsi_get_host_state(scmd->device->host) == SHOST_RECOVERY && scmd->cmnd[0] == TEST_UNIT_READY && (stgt_priv_data->dev_removed || (dev_handle == 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_Host *shost, * Avoid error handling escallation when device is disconnected */ if (handle == MPT3SAS_INVALID_DEVICE_HANDLE || sas_device_priv_data->block) { - if (scmd->device->host->shost_state == SHOST_RECOVERY && + if (scsi_get_host_state(scmd->device->host) == SHOST_RECOVERY && scmd->cmnd[0] == 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_cmnd *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 == SHOST_RECOVERY) - return 1; - return 0; + return scsi_get_host_state(shost) == SHOST_RECOVERY; } /** diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 686cef2406b3..2e07b686090f 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1663,10 +1663,9 @@ static enum scsi_qc_status scsi_dispatch_cmd(struct scsi_cmnd *cmd) goto done; } - if (unlikely(host->shost_state == SHOST_DEL)) { + if (unlikely(scsi_get_host_state(host) == SHOST_DEL)) { cmd->result = (DID_NO_CONNECT << 16); goto done; - } 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_attribute *attr, if (!state) return -EINVAL; - 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; } @@ -223,7 +224,7 @@ static ssize_t show_shost_state(struct device *dev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(dev); - const char *name = scsi_host_state_name(shost->shost_state); + const char *name = scsi_host_state_name(scsi_get_host_state(shost)); if (!name) return -EINVAL; diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 7e2011830ba4..c9754771bf29 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -2,6 +2,7 @@ #ifndef _SCSI_SCSI_HOST_H #define _SCSI_SCSI_HOST_H +#include #include #include #include @@ -727,7 +728,7 @@ struct Scsi_Host { unsigned int irq; - enum scsi_host_state shost_state; + enum scsi_host_state shost_state __guarded_by(host_lock); /* ldm bits */ struct device shost_gendev, shost_dev; @@ -785,11 +786,18 @@ static inline struct Scsi_Host *dev_to_shost(struct device *dev) return container_of(dev, struct Scsi_Host, shost_gendev); } +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 == SHOST_RECOVERY || - shost->shost_state == SHOST_CANCEL_RECOVERY || - shost->shost_state == SHOST_DEL_RECOVERY || + enum scsi_host_state state = scsi_get_host_state(shost); + + return state == SHOST_RECOVERY || + state == SHOST_CANCEL_RECOVERY || + state == SHOST_DEL_RECOVERY || shost->tmf_in_progress; } @@ -835,8 +843,9 @@ static inline struct device *scsi_get_device(struct Scsi_Host *shost) **/ static inline int scsi_host_scan_allowed(struct Scsi_Host *shost) { - return shost->shost_state == SHOST_RUNNING || - shost->shost_state == SHOST_RECOVERY; + enum scsi_host_state state = scsi_get_host_state(shost); + + return state == SHOST_RUNNING || state == SHOST_RECOVERY; } extern void scsi_unblock_requests(struct Scsi_Host *); @@ -940,6 +949,7 @@ static inline unsigned char scsi_host_get_guard(struct Scsi_Host *shost) return shost->prot_guard_type; } -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 state) + __must_hold(shost->host_lock); #endif /* _SCSI_SCSI_HOST_H */ From fb0fc67db96292a65539e63f536c6f586490c78b Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 7 Aug 2026 15:49:51 -0700 Subject: [PATCH 10/26] scsi: core: Enable context analysis Enable context analysis for those SCSI core files that build without triggering any context analysis warnings. Signed-off-by: Bart Van Assche Reviewed-by: John Garry Link: https://patch.msgid.link/2576d2f7e3530b721b5050ac6d25c413037d7e7e.1786142946.git.bvanassche@acm.org Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/Makefile | 22 ++++++++++++++++++++++ drivers/scsi/device_handler/Makefile | 3 +++ 2 files changed, 25 insertions(+) diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 098f35219e7d..72eb395ccf1d 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -14,6 +14,28 @@ # satisfy certain initialization assumptions in the SCSI layer. # *!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*! +CONTEXT_ANALYSIS_constants.o := y +CONTEXT_ANALYSIS_scsi.o := y +CONTEXT_ANALYSIS_scsi_common.o := y +CONTEXT_ANALYSIS_scsi_devinfo.o := y +CONTEXT_ANALYSIS_scsi_dh.o := y +CONTEXT_ANALYSIS_scsi_error.o := y +CONTEXT_ANALYSIS_scsi_ioctl.o := y +CONTEXT_ANALYSIS_scsi_lib.o := y +CONTEXT_ANALYSIS_scsi_lib_dma.o := y +CONTEXT_ANALYSIS_scsi_logging.o := y +CONTEXT_ANALYSIS_scsi_netlink.o := y +CONTEXT_ANALYSIS_scsi_pm.o := y +CONTEXT_ANALYSIS_scsi_proc.o := y +CONTEXT_ANALYSIS_scsi_scan.o := y +CONTEXT_ANALYSIS_scsi_sysfs.o := y +CONTEXT_ANALYSIS_scsi_trace.o := y +CONTEXT_ANALYSIS_scsicam.o := y +CONTEXT_ANALYSIS_sd.o := y +CONTEXT_ANALYSIS_sd_dif.o := y +CONTEXT_ANALYSIS_sd_zbc.o := y +CONTEXT_ANALYSIS_sr.o := y +CONTEXT_ANALYSIS_sr_ioctl.o := y CFLAGS_aha152x.o = -DAHA152X_STAT -DAUTOCONF diff --git a/drivers/scsi/device_handler/Makefile b/drivers/scsi/device_handler/Makefile index 0a603aefd2bb..5aa282a63e24 100644 --- a/drivers/scsi/device_handler/Makefile +++ b/drivers/scsi/device_handler/Makefile @@ -2,6 +2,9 @@ # # SCSI Device Handler # + +CONTEXT_ANALYSIS := y + obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o obj-$(CONFIG_SCSI_DH_HP_SW) += scsi_dh_hp_sw.o obj-$(CONFIG_SCSI_DH_EMC) += scsi_dh_emc.o From 5a03dbfd670092a6960de433005aae1734c18f1f Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Wed, 29 Jul 2026 09:46:13 -0500 Subject: [PATCH 11/26] scsi: lpfc: Replace strlcat() with seq_buf in lpfc_info() In preparation for removing the strlcat() API[1], replace its uses in lpfc_info(). The function accumulates a variable number of optional fragments, which is what seq_buf is for. The intermediate tmp buffer and the per fragment overflow checks become unnecessary. seq_buf is memory safe by construction and silently truncates in the same way as the replaced pattern. The old code passed phba->ModelDesc as the format string of the first scnprintf() call. The model description comes from adapter VPD data. seq_buf_printf() takes a format string, so the replacement prints it through "%s". A model description containing conversion specifiers is no longer interpreted. Link: https://github.com/KSPP/linux/issues/370 [1] Signed-off-by: Ian Bridges Link: https://patch.msgid.link/20260729144617.1388646-2-icb@fastmail.org Reviewed-by: Nigel Kirkland Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/lpfc/lpfc_scsi.c | 49 +++++++++++------------------------ 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index f2cab134af7f..8a795c65e3c3 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -21,6 +21,7 @@ * included with this package. * *******************************************************************/ #include +#include #include #include #include @@ -5103,57 +5104,37 @@ lpfc_info(struct Scsi_Host *host) struct lpfc_hba *phba = vport->phba; int link_speed = 0; static char lpfcinfobuf[384]; - char tmp[384] = {0}; + struct seq_buf s; memset(lpfcinfobuf, 0, sizeof(lpfcinfobuf)); + seq_buf_init(&s, lpfcinfobuf, sizeof(lpfcinfobuf)); if (phba && phba->pcidev){ /* Model Description */ - scnprintf(tmp, sizeof(tmp), phba->ModelDesc); - if (strlcat(lpfcinfobuf, tmp, sizeof(lpfcinfobuf)) >= - sizeof(lpfcinfobuf)) - goto buffer_done; + seq_buf_printf(&s, "%s", phba->ModelDesc); /* PCI Info */ - scnprintf(tmp, sizeof(tmp), - " on PCI bus %02x device %02x irq %d", - phba->pcidev->bus->number, phba->pcidev->devfn, - phba->pcidev->irq); - if (strlcat(lpfcinfobuf, tmp, sizeof(lpfcinfobuf)) >= - sizeof(lpfcinfobuf)) - goto buffer_done; + seq_buf_printf(&s, " on PCI bus %02x device %02x irq %d", + phba->pcidev->bus->number, phba->pcidev->devfn, + phba->pcidev->irq); /* Port Number */ - if (phba->Port[0]) { - scnprintf(tmp, sizeof(tmp), " port %s", phba->Port); - if (strlcat(lpfcinfobuf, tmp, sizeof(lpfcinfobuf)) >= - sizeof(lpfcinfobuf)) - goto buffer_done; - } + if (phba->Port[0]) + seq_buf_printf(&s, " port %s", phba->Port); /* Link Speed */ link_speed = lpfc_sli_port_speed_get(phba); - if (link_speed != 0) { - scnprintf(tmp, sizeof(tmp), - " Logical Link Speed: %d Mbps", link_speed); - if (strlcat(lpfcinfobuf, tmp, sizeof(lpfcinfobuf)) >= - sizeof(lpfcinfobuf)) - goto buffer_done; - } + if (link_speed != 0) + seq_buf_printf(&s, " Logical Link Speed: %d Mbps", + link_speed); /* Support for BSG ioctls */ - scnprintf(tmp, sizeof(tmp), " BSG"); - if (strlcat(lpfcinfobuf, tmp, sizeof(lpfcinfobuf)) >= - sizeof(lpfcinfobuf)) - goto buffer_done; + seq_buf_printf(&s, " BSG"); /* PCI resettable */ - if (!lpfc_check_pci_resettable(phba)) { - scnprintf(tmp, sizeof(tmp), " PCI resettable"); - strlcat(lpfcinfobuf, tmp, sizeof(lpfcinfobuf)); - } + if (!lpfc_check_pci_resettable(phba)) + seq_buf_printf(&s, " PCI resettable"); } -buffer_done: return lpfcinfobuf; } From 07f46a9f8964b91d56951ef7f74c551af0eddeac Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Wed, 29 Jul 2026 09:46:14 -0500 Subject: [PATCH 12/26] scsi: lpfc: Replace strlcat() with scnprintf() in lpfc_vport_symbolic_node_name() In preparation for removing the strlcat() API[1], replace its uses in lpfc_vport_symbolic_node_name(). The function builds five unconditional fragments, so one scnprintf() call composes the whole string. The intermediate tmp buffer and the per fragment overflow checks become unnecessary. scnprintf() truncates at the buffer size and returns the number of bytes it wrote, which equals the length that the removed strnlen() call computed. The old code capped every fragment at MAXHOSTNAMELEN bytes before appending it, independently of the room left in the destination. The replacement formats each fragment directly into the destination, so a fragment longer than MAXHOSTNAMELEN is no longer truncated when the destination has room for it. Link: https://github.com/KSPP/linux/issues/370 [1] Signed-off-by: Ian Bridges Link: https://patch.msgid.link/20260729144617.1388646-3-icb@fastmail.org Reviewed-by: Nigel Kirkland Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/lpfc/lpfc_ct.c | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index c7853e7fe071..0734ab3be3e3 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -1823,34 +1823,15 @@ lpfc_vport_symbolic_node_name(struct lpfc_vport *vport, char *symbol, size_t size) { char fwrev[FW_REV_STR_SIZE] = {0}; - char tmp[MAXHOSTNAMELEN] = {0}; - - memset(symbol, 0, size); - - scnprintf(tmp, sizeof(tmp), "Emulex %s", vport->phba->ModelName); - if (strlcat(symbol, tmp, size) >= size) - goto buffer_done; lpfc_decode_firmware_rev(vport->phba, fwrev, 0); - scnprintf(tmp, sizeof(tmp), " FV%s", fwrev); - if (strlcat(symbol, tmp, size) >= size) - goto buffer_done; - - scnprintf(tmp, sizeof(tmp), " DV%s", lpfc_release_version); - if (strlcat(symbol, tmp, size) >= size) - goto buffer_done; - - scnprintf(tmp, sizeof(tmp), " HN:%s", vport->phba->os_host_name); - if (strlcat(symbol, tmp, size) >= size) - goto buffer_done; + memset(symbol, 0, size); /* Note :- OS name is "Linux" */ - scnprintf(tmp, sizeof(tmp), " OS:%s", init_utsname()->sysname); - strlcat(symbol, tmp, size); - -buffer_done: - return strnlen(symbol, size); - + return scnprintf(symbol, size, "Emulex %s FV%s DV%s HN:%s OS:%s", + vport->phba->ModelName, fwrev, + lpfc_release_version, vport->phba->os_host_name, + init_utsname()->sysname); } static uint32_t From 22d4cbf6f7509f0aab58d60d488a0082916a2fb0 Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Wed, 29 Jul 2026 09:46:15 -0500 Subject: [PATCH 13/26] scsi: lpfc: Replace strlcat() with seq_buf in lpfc_rx_monitor_report() In preparation for removing the strlcat() API[1], replace its use in lpfc_rx_monitor_report(). The function accumulates one line per ring entry, which is what seq_buf is for. seq_buf tracks the write position, so the per entry strlen() rescans of the destination are gone. Each record is still formatted into the tmp buffer. seq_buf_puts() appends it only when it fits whole, so the output keeps ending at the last complete record. The loop still stops on overflow without consuming the current entry, and the returned count and the ring head keep their old meaning. The produced bytes are unchanged. Link: https://github.com/KSPP/linux/issues/370 [1] Signed-off-by: Ian Bridges Link: https://patch.msgid.link/20260729144617.1388646-4-icb@fastmail.org Reviewed-by: Nigel Kirkland Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/lpfc/lpfc_sli.c | 43 ++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 62a30a92b792..cfa4371169f1 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -8109,17 +8110,18 @@ u32 lpfc_rx_monitor_report(struct lpfc_hba *phba, u32 cnt = 0; char tmp[DBG_LOG_STR_SZ] = {0}; bool log_to_kmsg = (!buf || !buf_len) ? true : false; + struct seq_buf s; if (!log_to_kmsg) { /* clear the buffer to be sure */ memset(buf, 0, buf_len); - scnprintf(buf, buf_len, "\t%-16s%-16s%-16s%-16s%-8s%-8s%-8s" - "%-8s%-8s%-8s%-16s\n", - "MaxBPI", "Tot_Data_CMF", - "Tot_Data_Cmd", "Tot_Data_Cmpl", - "Lat(us)", "Avg_IO", "Max_IO", "Bsy", - "IO_cnt", "Info", "BWutil(ms)"); + seq_buf_init(&s, buf, buf_len); + seq_buf_printf(&s, "\t%-16s%-16s%-16s%-16s%-8s%-8s%-8s%-8s%-8s%-8s%-16s\n", + "MaxBPI", "Tot_Data_CMF", + "Tot_Data_Cmd", "Tot_Data_Cmpl", + "Lat(us)", "Avg_IO", "Max_IO", "Bsy", + "IO_cnt", "Info", "BWutil(ms)"); } /* Needs to be _irq because record is called from timer interrupt @@ -8131,24 +8133,27 @@ u32 lpfc_rx_monitor_report(struct lpfc_hba *phba, /* Read out this entry's data. */ if (!log_to_kmsg) { - /* If !log_to_kmsg, then store to buf. */ + /* + * Drop a record whole if it does not fit, without + * consuming its ring entry. + */ scnprintf(tmp, sizeof(tmp), - "%03d:\t%-16llu%-16llu%-16llu%-16llu%-8llu" - "%-8llu%-8llu%-8u%-8u%-8u%u(%u)\n", - *head_idx, entry->max_bytes_per_interval, - entry->cmf_bytes, entry->total_bytes, - entry->rcv_bytes, entry->avg_io_latency, - entry->avg_io_size, entry->max_read_cnt, + "%03d:\t%-16llu%-16llu%-16llu%-16llu%-8llu%-8llu%-8llu%-8u%-8u%-8u%u(%u)\n", + *head_idx, + entry->max_bytes_per_interval, + entry->cmf_bytes, + entry->total_bytes, + entry->rcv_bytes, + entry->avg_io_latency, + entry->avg_io_size, + entry->max_read_cnt, entry->cmf_busy, entry->io_cnt, - entry->cmf_info, entry->timer_utilization, + entry->cmf_info, + entry->timer_utilization, entry->timer_interval); - /* Check for buffer overflow */ - if ((strlen(buf) + strlen(tmp)) >= buf_len) + if (seq_buf_puts(&s, tmp) < 0) break; - - /* Append entry's data to buffer */ - strlcat(buf, tmp, buf_len); } else { lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT, "4410 %02u: MBPI %llu Xmit %llu " From 4832a60e0a7818400afffa392b74776d084ddf4a Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Wed, 29 Jul 2026 09:46:16 -0500 Subject: [PATCH 14/26] scsi: lpfc: Replace strlcat() with seq_buf in the debugfs dump helpers In preparation for removing the strlcat() API[1], replace its uses in lpfc_debugfs_multixripools_data(), lpfc_debugfs_scsistat_data() and lpfc_debugfs_hdwqstat_data(). Each helper accumulates a variable number of lines into the debugfs buffer, which is what seq_buf is for. The intermediate tmp buffers and the per fragment overflow checks become unnecessary. Once a seq_buf overflows, later writes to it do nothing, so dropping the early exits does not change the produced bytes. Each loop that appends keeps one seq_buf_has_overflowed() exit, so a full buffer stops the iteration. lpfc_debugfs_multixripools_data() and lpfc_debugfs_hdwqstat_data() append to whatever the buffer already holds, so their seq_buf is anchored at the current end of the string. All three helpers keep returning strnlen() because seq_buf_used() reports the full buffer size after an overflow. Link: https://github.com/KSPP/linux/issues/370 [1] Signed-off-by: Ian Bridges Link: https://patch.msgid.link/20260729144617.1388646-5-icb@fastmail.org Reviewed-by: Nigel Kirkland Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/lpfc/lpfc_debugfs.c | 198 +++++++++++++------------------ 1 file changed, 85 insertions(+), 113 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 052023fc1733..30e3cc050aaa 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -462,7 +463,8 @@ lpfc_debugfs_multixripools_data(struct lpfc_hba *phba, char *buf, int size) struct lpfc_pvt_pool *pvt_pool; struct lpfc_pbl_pool *pbl_pool; u32 txcmplq_cnt; - char tmp[LPFC_DEBUG_OUT_LINE_SZ] = {0}; + size_t used; + struct seq_buf s; if (phba->sli_rev != LPFC_SLI_REV4) return 0; @@ -475,6 +477,9 @@ lpfc_debugfs_multixripools_data(struct lpfc_hba *phba, char *buf, int size) return i; } + used = strnlen(buf, size); + seq_buf_init(&s, buf + used, size - used); + /* * Pbl: Current number of free XRIs in public pool * Pvt: Current number of free XRIs in private pool @@ -484,10 +489,8 @@ lpfc_debugfs_multixripools_data(struct lpfc_hba *phba, char *buf, int size) * pbl_empty: Incremented by 1 when all pbl_pool are empty during * IO submission */ - scnprintf(tmp, sizeof(tmp), - "HWQ: Pbl Pvt Busy HWM | pvt_empty pbl_empty "); - if (strlcat(buf, tmp, size) >= size) - return strnlen(buf, size); + seq_buf_printf(&s, + "HWQ: Pbl Pvt Busy HWM | pvt_empty pbl_empty "); #ifdef LPFC_MXP_STAT /* @@ -501,25 +504,17 @@ lpfc_debugfs_multixripools_data(struct lpfc_hba *phba, char *buf, int size) * othPbl_hit: Incremented by 1 if successfully get a batch of XRI from * other pbl_pool */ - scnprintf(tmp, sizeof(tmp), - "MAXH above_lmt below_lmt locPbl_hit othPbl_hit"); - if (strlcat(buf, tmp, size) >= size) - return strnlen(buf, size); + seq_buf_printf(&s, "MAXH above_lmt below_lmt locPbl_hit othPbl_hit"); /* * sPbl: snapshot of Pbl 15 sec after stat gets cleared * sPvt: snapshot of Pvt 15 sec after stat gets cleared * sBusy: snapshot of Busy 15 sec after stat gets cleared */ - scnprintf(tmp, sizeof(tmp), - " | sPbl sPvt sBusy"); - if (strlcat(buf, tmp, size) >= size) - return strnlen(buf, size); + seq_buf_printf(&s, " | sPbl sPvt sBusy"); #endif - scnprintf(tmp, sizeof(tmp), "\n"); - if (strlcat(buf, tmp, size) >= size) - return strnlen(buf, size); + seq_buf_printf(&s, "\n"); hwq_count = phba->cfg_hdw_queue; for (i = 0; i < hwq_count; i++) { @@ -531,36 +526,32 @@ lpfc_debugfs_multixripools_data(struct lpfc_hba *phba, char *buf, int size) pvt_pool = &multixri_pool->pvt_pool; txcmplq_cnt = qp->io_wq->pring->txcmplq_cnt; - scnprintf(tmp, sizeof(tmp), - "%03d: %4d %4d %4d %4d | %10d %10d ", - i, pbl_pool->count, pvt_pool->count, - txcmplq_cnt, pvt_pool->high_watermark, - qp->empty_io_bufs, multixri_pool->pbl_empty_count); - if (strlcat(buf, tmp, size) >= size) - break; + seq_buf_printf(&s, + "%03d: %4d %4d %4d %4d | %10d %10d ", + i, pbl_pool->count, pvt_pool->count, + txcmplq_cnt, pvt_pool->high_watermark, + qp->empty_io_bufs, + multixri_pool->pbl_empty_count); #ifdef LPFC_MXP_STAT - scnprintf(tmp, sizeof(tmp), - "%4d %10d %10d %10d %10d", - multixri_pool->stat_max_hwm, - multixri_pool->above_limit_count, - multixri_pool->below_limit_count, - multixri_pool->local_pbl_hit_count, - multixri_pool->other_pbl_hit_count); - if (strlcat(buf, tmp, size) >= size) - break; + seq_buf_printf(&s, + "%4d %10d %10d %10d %10d", + multixri_pool->stat_max_hwm, + multixri_pool->above_limit_count, + multixri_pool->below_limit_count, + multixri_pool->local_pbl_hit_count, + multixri_pool->other_pbl_hit_count); - scnprintf(tmp, sizeof(tmp), - " | %4d %4d %5d", - multixri_pool->stat_pbl_count, - multixri_pool->stat_pvt_count, - multixri_pool->stat_busy_count); - if (strlcat(buf, tmp, size) >= size) - break; + seq_buf_printf(&s, + " | %4d %4d %5d", + multixri_pool->stat_pbl_count, + multixri_pool->stat_pvt_count, + multixri_pool->stat_busy_count); #endif - scnprintf(tmp, sizeof(tmp), "\n"); - if (strlcat(buf, tmp, size) >= size) + seq_buf_printf(&s, "\n"); + + if (seq_buf_has_overflowed(&s)) break; } return strnlen(buf, size); @@ -1261,13 +1252,14 @@ lpfc_debugfs_scsistat_data(struct lpfc_vport *vport, char *buf, int size) u64 data1, data2, data3; u64 tot, totin, totout; int i; - char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0}; + struct seq_buf s; if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_FCP) || (phba->sli_rev != LPFC_SLI_REV4)) return 0; - scnprintf(buf, size, "SCSI HDWQ Statistics\n"); + seq_buf_init(&s, buf, size); + seq_buf_printf(&s, "SCSI HDWQ Statistics\n"); totin = 0; totout = 0; @@ -1280,21 +1272,18 @@ lpfc_debugfs_scsistat_data(struct lpfc_vport *vport, char *buf, int size) data3 = cstat->control_requests; totout += (data1 + data2 + data3); - scnprintf(tmp, sizeof(tmp), "HDWQ (%d): Rd %016llx Wr %016llx " - "IO %016llx ", i, data1, data2, data3); - if (strlcat(buf, tmp, size) >= size) - goto buffer_done; + seq_buf_printf(&s, "HDWQ (%d): Rd %016llx Wr %016llx IO %016llx ", + i, data1, data2, data3); - scnprintf(tmp, sizeof(tmp), "Cmpl %016llx OutIO %016llx\n", - tot, ((data1 + data2 + data3) - tot)); - if (strlcat(buf, tmp, size) >= size) - goto buffer_done; + seq_buf_printf(&s, "Cmpl %016llx OutIO %016llx\n", + tot, ((data1 + data2 + data3) - tot)); + + if (seq_buf_has_overflowed(&s)) + break; } - scnprintf(tmp, sizeof(tmp), "Total FCP Cmpl %016llx Issue %016llx " - "OutIO %016llx\n", totin, totout, totout - totin); - strlcat(buf, tmp, size); + seq_buf_printf(&s, "Total FCP Cmpl %016llx Issue %016llx OutIO %016llx\n", + totin, totout, totout - totin); -buffer_done: len = strnlen(buf, size); return len; @@ -1704,28 +1693,23 @@ lpfc_debugfs_hdwqstat_data(struct lpfc_vport *vport, char *buf, int size) uint32_t tot_xmt; uint32_t tot_rcv; uint32_t tot_cmpl; - char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0}; + size_t used = strnlen(buf, size); + struct seq_buf s; - scnprintf(tmp, sizeof(tmp), "HDWQ Stats:\n\n"); - if (strlcat(buf, tmp, size) >= size) - goto buffer_done; + seq_buf_init(&s, buf + used, size - used); - scnprintf(tmp, sizeof(tmp), "(NVME Accounting: %s) ", - (phba->hdwqstat_on & - (LPFC_CHECK_NVME_IO | LPFC_CHECK_NVMET_IO) ? - "Enabled" : "Disabled")); - if (strlcat(buf, tmp, size) >= size) - goto buffer_done; + seq_buf_printf(&s, "HDWQ Stats:\n\n"); - scnprintf(tmp, sizeof(tmp), "(SCSI Accounting: %s) ", - (phba->hdwqstat_on & LPFC_CHECK_SCSI_IO ? - "Enabled" : "Disabled")); - if (strlcat(buf, tmp, size) >= size) - goto buffer_done; + seq_buf_printf(&s, "(NVME Accounting: %s) ", + (phba->hdwqstat_on & + (LPFC_CHECK_NVME_IO | LPFC_CHECK_NVMET_IO) ? + "Enabled" : "Disabled")); - scnprintf(tmp, sizeof(tmp), "\n\n"); - if (strlcat(buf, tmp, size) >= size) - goto buffer_done; + seq_buf_printf(&s, "(SCSI Accounting: %s) ", + (phba->hdwqstat_on & LPFC_CHECK_SCSI_IO ? + "Enabled" : "Disabled")); + + seq_buf_printf(&s, "\n\n"); for (i = 0; i < phba->cfg_hdw_queue; i++) { tot_rcv = 0; @@ -1744,62 +1728,50 @@ lpfc_debugfs_hdwqstat_data(struct lpfc_vport *vport, char *buf, int size) !c_stat->rcv_io) continue; - if (!tot_xmt && !tot_cmpl && !tot_rcv) { - /* Print HDWQ string only the first time */ - scnprintf(tmp, sizeof(tmp), "[HDWQ %d]:\t", i); - if (strlcat(buf, tmp, size) >= size) - goto buffer_done; - } + /* Print HDWQ string only the first time */ + if (!tot_xmt && !tot_cmpl && !tot_rcv) + seq_buf_printf(&s, "[HDWQ %d]:\t", i); tot_xmt += c_stat->xmt_io; tot_cmpl += c_stat->cmpl_io; if (phba->nvmet_support) tot_rcv += c_stat->rcv_io; - scnprintf(tmp, sizeof(tmp), "| [CPU %d]: ", j); - if (strlcat(buf, tmp, size) >= size) - goto buffer_done; + seq_buf_printf(&s, "| [CPU %d]: ", j); - if (phba->nvmet_support) { - scnprintf(tmp, sizeof(tmp), - "XMT 0x%x CMPL 0x%x RCV 0x%x |", - c_stat->xmt_io, c_stat->cmpl_io, - c_stat->rcv_io); - if (strlcat(buf, tmp, size) >= size) - goto buffer_done; - } else { - scnprintf(tmp, sizeof(tmp), - "XMT 0x%x CMPL 0x%x |", - c_stat->xmt_io, c_stat->cmpl_io); - if (strlcat(buf, tmp, size) >= size) - goto buffer_done; - } + if (phba->nvmet_support) + seq_buf_printf(&s, + "XMT 0x%x CMPL 0x%x RCV 0x%x |", + c_stat->xmt_io, c_stat->cmpl_io, + c_stat->rcv_io); + else + seq_buf_printf(&s, + "XMT 0x%x CMPL 0x%x |", + c_stat->xmt_io, c_stat->cmpl_io); + + if (seq_buf_has_overflowed(&s)) + break; } + if (seq_buf_has_overflowed(&s)) + break; + /* Check if nothing to display */ if (!tot_xmt && !tot_cmpl && !tot_rcv) continue; - scnprintf(tmp, sizeof(tmp), "\t->\t[HDWQ Total: "); - if (strlcat(buf, tmp, size) >= size) - goto buffer_done; + seq_buf_printf(&s, "\t->\t[HDWQ Total: "); - if (phba->nvmet_support) { - scnprintf(tmp, sizeof(tmp), - "XMT 0x%x CMPL 0x%x RCV 0x%x]\n\n", - tot_xmt, tot_cmpl, tot_rcv); - if (strlcat(buf, tmp, size) >= size) - goto buffer_done; - } else { - scnprintf(tmp, sizeof(tmp), - "XMT 0x%x CMPL 0x%x]\n\n", - tot_xmt, tot_cmpl); - if (strlcat(buf, tmp, size) >= size) - goto buffer_done; - } + if (phba->nvmet_support) + seq_buf_printf(&s, + "XMT 0x%x CMPL 0x%x RCV 0x%x]\n\n", + tot_xmt, tot_cmpl, tot_rcv); + else + seq_buf_printf(&s, + "XMT 0x%x CMPL 0x%x]\n\n", + tot_xmt, tot_cmpl); } -buffer_done: len = strnlen(buf, size); return len; } From 36b6dcb2b7463b290d63c83d82bec8065a1a93de Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Wed, 29 Jul 2026 09:46:17 -0500 Subject: [PATCH 15/26] scsi: lpfc: Replace strlcat() with sysfs_emit_at() in the sysfs show functions In preparation for removing the strlcat() API[1], replace its uses in lpfc_cmf_info_show(), lpfc_nvme_info_show() and lpfc_scsi_stat_show(). The three functions build sysfs attribute output, and sysfs_emit_at() is the designated helper for that. The single write paths become sysfs_emit(), the offset zero form of the same helper. Each intermediate tmp buffer and its per fragment overflow check become unnecessary. Once the page is full, sysfs_emit_at() writes nothing more, so dropping the early exits does not change the produced bytes. Each loop that appends keeps one exit, so a full page stops the iteration. In lpfc_nvme_info_show() the exit also releases the fc_nodes_list_lock as it did before. The unlock_buf_done label loses its last user and is removed. The old code capped every fragment at LPFC_MAX_INFO_TMP_LEN or LPFC_MAX_SCSI_INFO_TMP_LEN bytes before appending it. The replacement formats each fragment directly into the page, so a fragment longer than its old tmp buffer is no longer truncated when the page has room for it. Both macros lose their last user and are removed. The running length that sysfs_emit_at() maintains equals the length that the removed strnlen() calls computed, so the "Could be more info" overflow markers keep their trigger condition. Link: https://github.com/KSPP/linux/issues/370 [1] Signed-off-by: Ian Bridges Link: https://patch.msgid.link/20260729144617.1388646-6-icb@fastmail.org Reviewed-by: Nigel Kirkland Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/lpfc/lpfc_attr.c | 533 ++++++++++++++-------------------- drivers/scsi/lpfc/lpfc_scsi.h | 3 - 2 files changed, 219 insertions(+), 317 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index f4e8164b94ab..bb5c84f0b787 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -57,7 +57,6 @@ #define LPFC_MIN_DEVLOSS_TMO 1 #define LPFC_MAX_DEVLOSS_TMO 255 -#define LPFC_MAX_INFO_TMP_LEN 100 #define LPFC_INFO_MORE_STR "\nCould be more info...\n" /* * Write key size should be multiple of 4. If write key is changed @@ -126,133 +125,121 @@ lpfc_cmf_info_show(struct device *dev, struct device_attribute *attr, int len = 0; int cpu; u64 rcv, total; - char tmp[LPFC_MAX_INFO_TMP_LEN] = {0}; if (phba->cgn_i) cp = (struct lpfc_cgn_info *)phba->cgn_i->virt; - scnprintf(tmp, sizeof(tmp), - "Congestion Mgmt Info: E2Eattr %d Ver %d " - "CMF %d cnt %d\n", - phba->sli4_hba.pc_sli4_params.mi_cap, - cp ? cp->cgn_info_version : 0, - phba->sli4_hba.pc_sli4_params.cmf, phba->cmf_timer_cnt); - - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len = strnlen(buf, PAGE_SIZE); + len += sysfs_emit_at(buf, len, + "Congestion Mgmt Info: E2Eattr %d Ver %d CMF %d cnt %d\n", + phba->sli4_hba.pc_sli4_params.mi_cap, + cp ? cp->cgn_info_version : 0, + phba->sli4_hba.pc_sli4_params.cmf, + phba->cmf_timer_cnt); if (!phba->sli4_hba.pc_sli4_params.cmf) goto buffer_done; switch (phba->cgn_init_reg_signal) { case EDC_CG_SIG_WARN_ONLY: - scnprintf(tmp, sizeof(tmp), - "Register: Init: Signal:WARN "); + len += sysfs_emit_at(buf, len, + "Register: Init: Signal:WARN "); break; case EDC_CG_SIG_WARN_ALARM: - scnprintf(tmp, sizeof(tmp), - "Register: Init: Signal:WARN|ALARM "); + len += sysfs_emit_at(buf, len, + "Register: Init: Signal:WARN|ALARM "); break; default: - scnprintf(tmp, sizeof(tmp), - "Register: Init: Signal:NONE "); + len += sysfs_emit_at(buf, len, + "Register: Init: Signal:NONE "); break; } - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; switch (phba->cgn_init_reg_fpin) { case LPFC_CGN_FPIN_WARN: - scnprintf(tmp, sizeof(tmp), - "FPIN:WARN\n"); + len += sysfs_emit_at(buf, len, "FPIN:WARN\n"); break; case LPFC_CGN_FPIN_ALARM: - scnprintf(tmp, sizeof(tmp), - "FPIN:ALARM\n"); + len += sysfs_emit_at(buf, len, "FPIN:ALARM\n"); break; case LPFC_CGN_FPIN_BOTH: - scnprintf(tmp, sizeof(tmp), - "FPIN:WARN|ALARM\n"); + len += sysfs_emit_at(buf, len, "FPIN:WARN|ALARM\n"); break; default: - scnprintf(tmp, sizeof(tmp), - "FPIN:NONE\n"); + len += sysfs_emit_at(buf, len, "FPIN:NONE\n"); break; } - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; switch (phba->cgn_reg_signal) { case EDC_CG_SIG_WARN_ONLY: - scnprintf(tmp, sizeof(tmp), - " Current: Signal:WARN "); + len += sysfs_emit_at(buf, len, + " Current: Signal:WARN "); break; case EDC_CG_SIG_WARN_ALARM: - scnprintf(tmp, sizeof(tmp), - " Current: Signal:WARN|ALARM "); + len += sysfs_emit_at(buf, len, + " Current: Signal:WARN|ALARM "); break; default: - scnprintf(tmp, sizeof(tmp), - " Current: Signal:NONE "); + len += sysfs_emit_at(buf, len, + " Current: Signal:NONE "); break; } - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; switch (phba->cgn_reg_fpin) { case LPFC_CGN_FPIN_WARN: - scnprintf(tmp, sizeof(tmp), - "FPIN:WARN ACQEcnt:%d\n", phba->cgn_acqe_cnt); + len += sysfs_emit_at(buf, len, + "FPIN:WARN ACQEcnt:%d\n", + phba->cgn_acqe_cnt); break; case LPFC_CGN_FPIN_ALARM: - scnprintf(tmp, sizeof(tmp), - "FPIN:ALARM ACQEcnt:%d\n", phba->cgn_acqe_cnt); + len += sysfs_emit_at(buf, len, + "FPIN:ALARM ACQEcnt:%d\n", + phba->cgn_acqe_cnt); break; case LPFC_CGN_FPIN_BOTH: - scnprintf(tmp, sizeof(tmp), - "FPIN:WARN|ALARM ACQEcnt:%d\n", phba->cgn_acqe_cnt); + len += sysfs_emit_at(buf, len, + "FPIN:WARN|ALARM ACQEcnt:%d\n", + phba->cgn_acqe_cnt); break; default: - scnprintf(tmp, sizeof(tmp), - "FPIN:NONE ACQEcnt:%d\n", phba->cgn_acqe_cnt); + len += sysfs_emit_at(buf, len, + "FPIN:NONE ACQEcnt:%d\n", + phba->cgn_acqe_cnt); break; } - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; if (phba->cmf_active_mode != phba->cgn_p.cgn_param_mode) { switch (phba->cmf_active_mode) { case LPFC_CFG_OFF: - scnprintf(tmp, sizeof(tmp), "Active: Mode:Off\n"); + len += sysfs_emit_at(buf, len, "Active: Mode:Off\n"); break; case LPFC_CFG_MANAGED: - scnprintf(tmp, sizeof(tmp), "Active: Mode:Managed\n"); + len += sysfs_emit_at(buf, len, + "Active: Mode:Managed\n"); break; case LPFC_CFG_MONITOR: - scnprintf(tmp, sizeof(tmp), "Active: Mode:Monitor\n"); + len += sysfs_emit_at(buf, len, + "Active: Mode:Monitor\n"); break; default: - scnprintf(tmp, sizeof(tmp), "Active: Mode:Unknown\n"); + len += sysfs_emit_at(buf, len, + "Active: Mode:Unknown\n"); } - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; } switch (phba->cgn_p.cgn_param_mode) { case LPFC_CFG_OFF: - scnprintf(tmp, sizeof(tmp), "Config: Mode:Off "); + len += sysfs_emit_at(buf, len, "Config: Mode:Off "); break; case LPFC_CFG_MANAGED: - scnprintf(tmp, sizeof(tmp), "Config: Mode:Managed "); + len += sysfs_emit_at(buf, len, "Config: Mode:Managed "); break; case LPFC_CFG_MONITOR: - scnprintf(tmp, sizeof(tmp), "Config: Mode:Monitor "); + len += sysfs_emit_at(buf, len, "Config: Mode:Monitor "); break; default: - scnprintf(tmp, sizeof(tmp), "Config: Mode:Unknown "); + len += sysfs_emit_at(buf, len, "Config: Mode:Unknown "); } - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; total = 0; rcv = 0; @@ -262,24 +249,18 @@ lpfc_cmf_info_show(struct device *dev, struct device_attribute *attr, rcv += atomic64_read(&cgs->rcv_bytes); } - scnprintf(tmp, sizeof(tmp), - "IObusy:%d Info:%d Bytes: Rcv:x%llx Total:x%llx\n", - atomic_read(&phba->cmf_busy), - phba->cmf_active_info, rcv, total); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "IObusy:%d Info:%d Bytes: Rcv:x%llx Total:x%llx\n", + atomic_read(&phba->cmf_busy), + phba->cmf_active_info, rcv, total); - scnprintf(tmp, sizeof(tmp), - "Port_speed:%d Link_byte_cnt:%ld " - "Max_byte_per_interval:%ld\n", - lpfc_sli_port_speed_get(phba), - (unsigned long)phba->cmf_link_byte_count, - (unsigned long)phba->cmf_max_bytes_per_interval); - strlcat(buf, tmp, PAGE_SIZE); + len += sysfs_emit_at(buf, len, + "Port_speed:%d Link_byte_cnt:%ld Max_byte_per_interval:%ld\n", + lpfc_sli_port_speed_get(phba), + (unsigned long)phba->cmf_link_byte_count, + (unsigned long)phba->cmf_max_bytes_per_interval); buffer_done: - len = strnlen(buf, PAGE_SIZE); - if (unlikely(len >= (PAGE_SIZE - 1))) { lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT, "6312 Catching potential buffer " @@ -480,17 +461,19 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, char *statep; int i; int len = 0; - char tmp[LPFC_MAX_INFO_TMP_LEN] = {0}; if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) { - len = scnprintf(buf, PAGE_SIZE, "NVME Disabled\n"); + len = sysfs_emit(buf, "NVME Disabled\n"); return len; } + + len = strnlen(buf, PAGE_SIZE); + if (phba->nvmet_support) { if (!phba->targetport) { - len = scnprintf(buf, PAGE_SIZE, - "NVME Target: x%llx is not allocated\n", - wwn_to_u64(vport->fc_portname.u.wwn)); + len = sysfs_emit(buf, + "NVME Target: x%llx is not allocated\n", + wwn_to_u64(vport->fc_portname.u.wwn)); return len; } /* Port state is only one of two values for now. */ @@ -498,167 +481,131 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, statep = "REGISTERED"; else statep = "INIT"; - scnprintf(tmp, sizeof(tmp), - "NVME Target Enabled State %s\n", - statep); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "NVME Target Enabled State %s\n", + statep); - scnprintf(tmp, sizeof(tmp), - "%s%d WWPN x%llx WWNN x%llx DID x%06x\n", - "NVME Target: lpfc", - phba->brd_no, - wwn_to_u64(vport->fc_portname.u.wwn), - wwn_to_u64(vport->fc_nodename.u.wwn), - phba->targetport->port_id); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "%s%d WWPN x%llx WWNN x%llx DID x%06x\n", + "NVME Target: lpfc", + phba->brd_no, + wwn_to_u64(vport->fc_portname.u.wwn), + wwn_to_u64(vport->fc_nodename.u.wwn), + phba->targetport->port_id); - if (strlcat(buf, "\nNVME Target: Statistics\n", PAGE_SIZE) - >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, "\nNVME Target: Statistics\n"); tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; - scnprintf(tmp, sizeof(tmp), - "LS: Rcv %08x Drop %08x Abort %08x\n", - atomic_read(&tgtp->rcv_ls_req_in), - atomic_read(&tgtp->rcv_ls_req_drop), - atomic_read(&tgtp->xmt_ls_abort)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "LS: Rcv %08x Drop %08x Abort %08x\n", + atomic_read(&tgtp->rcv_ls_req_in), + atomic_read(&tgtp->rcv_ls_req_drop), + atomic_read(&tgtp->xmt_ls_abort)); if (atomic_read(&tgtp->rcv_ls_req_in) != atomic_read(&tgtp->rcv_ls_req_out)) { - scnprintf(tmp, sizeof(tmp), - "Rcv LS: in %08x != out %08x\n", - atomic_read(&tgtp->rcv_ls_req_in), - atomic_read(&tgtp->rcv_ls_req_out)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "Rcv LS: in %08x != out %08x\n", + atomic_read(&tgtp->rcv_ls_req_in), + atomic_read(&tgtp->rcv_ls_req_out)); } - scnprintf(tmp, sizeof(tmp), - "LS: Xmt %08x Drop %08x Cmpl %08x\n", - atomic_read(&tgtp->xmt_ls_rsp), - atomic_read(&tgtp->xmt_ls_drop), - atomic_read(&tgtp->xmt_ls_rsp_cmpl)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "LS: Xmt %08x Drop %08x Cmpl %08x\n", + atomic_read(&tgtp->xmt_ls_rsp), + atomic_read(&tgtp->xmt_ls_drop), + atomic_read(&tgtp->xmt_ls_rsp_cmpl)); - scnprintf(tmp, sizeof(tmp), - "LS: RSP Abort %08x xb %08x Err %08x\n", - atomic_read(&tgtp->xmt_ls_rsp_aborted), - atomic_read(&tgtp->xmt_ls_rsp_xb_set), - atomic_read(&tgtp->xmt_ls_rsp_error)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "LS: RSP Abort %08x xb %08x Err %08x\n", + atomic_read(&tgtp->xmt_ls_rsp_aborted), + atomic_read(&tgtp->xmt_ls_rsp_xb_set), + atomic_read(&tgtp->xmt_ls_rsp_error)); - scnprintf(tmp, sizeof(tmp), - "FCP: Rcv %08x Defer %08x Release %08x " - "Drop %08x\n", - atomic_read(&tgtp->rcv_fcp_cmd_in), - atomic_read(&tgtp->rcv_fcp_cmd_defer), - atomic_read(&tgtp->xmt_fcp_release), - atomic_read(&tgtp->rcv_fcp_cmd_drop)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "FCP: Rcv %08x Defer %08x Release %08x Drop %08x\n", + atomic_read(&tgtp->rcv_fcp_cmd_in), + atomic_read(&tgtp->rcv_fcp_cmd_defer), + atomic_read(&tgtp->xmt_fcp_release), + atomic_read(&tgtp->rcv_fcp_cmd_drop)); if (atomic_read(&tgtp->rcv_fcp_cmd_in) != atomic_read(&tgtp->rcv_fcp_cmd_out)) { - scnprintf(tmp, sizeof(tmp), - "Rcv FCP: in %08x != out %08x\n", - atomic_read(&tgtp->rcv_fcp_cmd_in), - atomic_read(&tgtp->rcv_fcp_cmd_out)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "Rcv FCP: in %08x != out %08x\n", + atomic_read(&tgtp->rcv_fcp_cmd_in), + atomic_read(&tgtp->rcv_fcp_cmd_out)); } - scnprintf(tmp, sizeof(tmp), - "FCP Rsp: RD %08x rsp %08x WR %08x rsp %08x " - "drop %08x\n", - atomic_read(&tgtp->xmt_fcp_read), - atomic_read(&tgtp->xmt_fcp_read_rsp), - atomic_read(&tgtp->xmt_fcp_write), - atomic_read(&tgtp->xmt_fcp_rsp), - atomic_read(&tgtp->xmt_fcp_drop)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "FCP Rsp: RD %08x rsp %08x WR %08x rsp %08x drop %08x\n", + atomic_read(&tgtp->xmt_fcp_read), + atomic_read(&tgtp->xmt_fcp_read_rsp), + atomic_read(&tgtp->xmt_fcp_write), + atomic_read(&tgtp->xmt_fcp_rsp), + atomic_read(&tgtp->xmt_fcp_drop)); - scnprintf(tmp, sizeof(tmp), - "FCP Rsp Cmpl: %08x err %08x drop %08x\n", - atomic_read(&tgtp->xmt_fcp_rsp_cmpl), - atomic_read(&tgtp->xmt_fcp_rsp_error), - atomic_read(&tgtp->xmt_fcp_rsp_drop)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "FCP Rsp Cmpl: %08x err %08x drop %08x\n", + atomic_read(&tgtp->xmt_fcp_rsp_cmpl), + atomic_read(&tgtp->xmt_fcp_rsp_error), + atomic_read(&tgtp->xmt_fcp_rsp_drop)); - scnprintf(tmp, sizeof(tmp), - "FCP Rsp Abort: %08x xb %08x xricqe %08x\n", - atomic_read(&tgtp->xmt_fcp_rsp_aborted), - atomic_read(&tgtp->xmt_fcp_rsp_xb_set), - atomic_read(&tgtp->xmt_fcp_xri_abort_cqe)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "FCP Rsp Abort: %08x xb %08x xricqe %08x\n", + atomic_read(&tgtp->xmt_fcp_rsp_aborted), + atomic_read(&tgtp->xmt_fcp_rsp_xb_set), + atomic_read(&tgtp->xmt_fcp_xri_abort_cqe)); - scnprintf(tmp, sizeof(tmp), - "ABORT: Xmt %08x Cmpl %08x\n", - atomic_read(&tgtp->xmt_fcp_abort), - atomic_read(&tgtp->xmt_fcp_abort_cmpl)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "ABORT: Xmt %08x Cmpl %08x\n", + atomic_read(&tgtp->xmt_fcp_abort), + atomic_read(&tgtp->xmt_fcp_abort_cmpl)); - scnprintf(tmp, sizeof(tmp), - "ABORT: Sol %08x Usol %08x Err %08x Cmpl %08x\n", - atomic_read(&tgtp->xmt_abort_sol), - atomic_read(&tgtp->xmt_abort_unsol), - atomic_read(&tgtp->xmt_abort_rsp), - atomic_read(&tgtp->xmt_abort_rsp_error)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "ABORT: Sol %08x Usol %08x Err %08x Cmpl %08x\n", + atomic_read(&tgtp->xmt_abort_sol), + atomic_read(&tgtp->xmt_abort_unsol), + atomic_read(&tgtp->xmt_abort_rsp), + atomic_read(&tgtp->xmt_abort_rsp_error)); - scnprintf(tmp, sizeof(tmp), - "DELAY: ctx %08x fod %08x wqfull %08x\n", - atomic_read(&tgtp->defer_ctx), - atomic_read(&tgtp->defer_fod), - atomic_read(&tgtp->defer_wqfull)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "DELAY: ctx %08x fod %08x wqfull %08x\n", + atomic_read(&tgtp->defer_ctx), + atomic_read(&tgtp->defer_fod), + atomic_read(&tgtp->defer_wqfull)); /* Calculate outstanding IOs */ tot = atomic_read(&tgtp->rcv_fcp_cmd_drop); tot += atomic_read(&tgtp->xmt_fcp_release); tot = atomic_read(&tgtp->rcv_fcp_cmd_in) - tot; - scnprintf(tmp, sizeof(tmp), - "IO_CTX: %08x WAIT: cur %08x tot %08x\n" - "CTX Outstanding %08llx\n\n", - phba->sli4_hba.nvmet_xri_cnt, - phba->sli4_hba.nvmet_io_wait_cnt, - phba->sli4_hba.nvmet_io_wait_total, - tot); - strlcat(buf, tmp, PAGE_SIZE); + len += sysfs_emit_at(buf, len, + "IO_CTX: %08x WAIT: cur %08x tot %08x\n" + "CTX Outstanding %08llx\n\n", + phba->sli4_hba.nvmet_xri_cnt, + phba->sli4_hba.nvmet_io_wait_cnt, + phba->sli4_hba.nvmet_io_wait_total, + tot); goto buffer_done; } localport = vport->localport; if (!localport) { - len = scnprintf(buf, PAGE_SIZE, - "NVME Initiator x%llx is not allocated\n", - wwn_to_u64(vport->fc_portname.u.wwn)); + len = sysfs_emit(buf, + "NVME Initiator x%llx is not allocated\n", + wwn_to_u64(vport->fc_portname.u.wwn)); return len; } lport = (struct lpfc_nvme_lport *)localport->private; - if (strlcat(buf, "\nNVME Initiator Enabled\n", PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, "\nNVME Initiator Enabled\n"); - scnprintf(tmp, sizeof(tmp), - "XRI Dist lpfc%d Total %d IO %d ELS %d\n", - phba->brd_no, - phba->sli4_hba.max_cfg_param.max_xri, - phba->sli4_hba.io_xri_max, - lpfc_sli4_get_els_iocb_cnt(phba)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "XRI Dist lpfc%d Total %d IO %d ELS %d\n", + phba->brd_no, + phba->sli4_hba.max_cfg_param.max_xri, + phba->sli4_hba.io_xri_max, + lpfc_sli4_get_els_iocb_cnt(phba)); /* Port state is only one of two values for now. */ if (localport->port_id) @@ -666,15 +613,13 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, else statep = "UNKNOWN "; - scnprintf(tmp, sizeof(tmp), - "%s%d WWPN x%llx WWNN x%llx DID x%06x %s\n", - "NVME LPORT lpfc", - phba->brd_no, - wwn_to_u64(vport->fc_portname.u.wwn), - wwn_to_u64(vport->fc_nodename.u.wwn), - localport->port_id, statep); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "%s%d WWPN x%llx WWNN x%llx DID x%06x %s\n", + "NVME LPORT lpfc", + phba->brd_no, + wwn_to_u64(vport->fc_portname.u.wwn), + wwn_to_u64(vport->fc_nodename.u.wwn), + localport->port_id, statep); spin_lock_irqsave(&vport->fc_nodes_list_lock, iflags); @@ -702,77 +647,55 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, } /* Tab in to show lport ownership. */ - if (strlcat(buf, "NVME RPORT ", PAGE_SIZE) >= PAGE_SIZE) - goto unlock_buf_done; - if (phba->brd_no >= 10) { - if (strlcat(buf, " ", PAGE_SIZE) >= PAGE_SIZE) - goto unlock_buf_done; - } + len += sysfs_emit_at(buf, len, "NVME RPORT "); + if (phba->brd_no >= 10) + len += sysfs_emit_at(buf, len, " "); - scnprintf(tmp, sizeof(tmp), "WWPN x%llx ", - nrport->port_name); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto unlock_buf_done; + len += sysfs_emit_at(buf, len, "WWPN x%llx ", + nrport->port_name); - scnprintf(tmp, sizeof(tmp), "WWNN x%llx ", - nrport->node_name); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto unlock_buf_done; + len += sysfs_emit_at(buf, len, "WWNN x%llx ", + nrport->node_name); - scnprintf(tmp, sizeof(tmp), "DID x%06x ", - nrport->port_id); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto unlock_buf_done; + len += sysfs_emit_at(buf, len, "DID x%06x ", + nrport->port_id); /* An NVME rport can have multiple roles. */ - if (nrport->port_role & FC_PORT_ROLE_NVME_INITIATOR) { - if (strlcat(buf, "INITIATOR ", PAGE_SIZE) >= PAGE_SIZE) - goto unlock_buf_done; - } - if (nrport->port_role & FC_PORT_ROLE_NVME_TARGET) { - if (strlcat(buf, "TARGET ", PAGE_SIZE) >= PAGE_SIZE) - goto unlock_buf_done; - } - if (nrport->port_role & FC_PORT_ROLE_NVME_DISCOVERY) { - if (strlcat(buf, "DISCSRVC ", PAGE_SIZE) >= PAGE_SIZE) - goto unlock_buf_done; - } + if (nrport->port_role & FC_PORT_ROLE_NVME_INITIATOR) + len += sysfs_emit_at(buf, len, "INITIATOR "); + if (nrport->port_role & FC_PORT_ROLE_NVME_TARGET) + len += sysfs_emit_at(buf, len, "TARGET "); + if (nrport->port_role & FC_PORT_ROLE_NVME_DISCOVERY) + len += sysfs_emit_at(buf, len, "DISCSRVC "); if (nrport->port_role & ~(FC_PORT_ROLE_NVME_INITIATOR | FC_PORT_ROLE_NVME_TARGET | - FC_PORT_ROLE_NVME_DISCOVERY)) { - scnprintf(tmp, sizeof(tmp), "UNKNOWN ROLE x%x", - nrport->port_role); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto unlock_buf_done; - } + FC_PORT_ROLE_NVME_DISCOVERY)) + len += sysfs_emit_at(buf, len, "UNKNOWN ROLE x%x", + nrport->port_role); - scnprintf(tmp, sizeof(tmp), "%s\n", statep); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto unlock_buf_done; + len += sysfs_emit_at(buf, len, "%s\n", statep); + + if (len >= PAGE_SIZE - 1) + break; } spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags); if (!lport) goto buffer_done; - if (strlcat(buf, "\nNVME Statistics\n", PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, "\nNVME Statistics\n"); - scnprintf(tmp, sizeof(tmp), - "LS: Xmt %010x Cmpl %010x Abort %08x\n", - atomic_read(&lport->fc4NvmeLsRequests), - atomic_read(&lport->fc4NvmeLsCmpls), - atomic_read(&lport->xmt_ls_abort)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "LS: Xmt %010x Cmpl %010x Abort %08x\n", + atomic_read(&lport->fc4NvmeLsRequests), + atomic_read(&lport->fc4NvmeLsCmpls), + atomic_read(&lport->xmt_ls_abort)); - scnprintf(tmp, sizeof(tmp), - "LS XMIT: Err %08x CMPL: xb %08x Err %08x\n", - atomic_read(&lport->xmt_ls_err), - atomic_read(&lport->cmpl_ls_xb), - atomic_read(&lport->cmpl_ls_err)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "LS XMIT: Err %08x CMPL: xb %08x Err %08x\n", + atomic_read(&lport->xmt_ls_err), + atomic_read(&lport->cmpl_ls_xb), + atomic_read(&lport->cmpl_ls_err)); totin = 0; totout = 0; @@ -785,40 +708,25 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, data3 = cstat->control_requests; totout += (data1 + data2 + data3); } - scnprintf(tmp, sizeof(tmp), - "Total FCP Cmpl %016llx Issue %016llx " - "OutIO %016llx\n", - totin, totout, totout - totin); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "Total FCP Cmpl %016llx Issue %016llx OutIO %016llx\n", + totin, totout, totout - totin); - scnprintf(tmp, sizeof(tmp), - "\tabort %08x noxri %08x nondlp %08x qdepth %08x " - "wqerr %08x err %08x\n", - atomic_read(&lport->xmt_fcp_abort), - atomic_read(&lport->xmt_fcp_noxri), - atomic_read(&lport->xmt_fcp_bad_ndlp), - atomic_read(&lport->xmt_fcp_qdepth), - atomic_read(&lport->xmt_fcp_wqerr), - atomic_read(&lport->xmt_fcp_err)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "\tabort %08x noxri %08x nondlp %08x qdepth %08x wqerr %08x err %08x\n", + atomic_read(&lport->xmt_fcp_abort), + atomic_read(&lport->xmt_fcp_noxri), + atomic_read(&lport->xmt_fcp_bad_ndlp), + atomic_read(&lport->xmt_fcp_qdepth), + atomic_read(&lport->xmt_fcp_wqerr), + atomic_read(&lport->xmt_fcp_err)); - scnprintf(tmp, sizeof(tmp), - "FCP CMPL: xb %08x Err %08x\n", - atomic_read(&lport->cmpl_fcp_xb), - atomic_read(&lport->cmpl_fcp_err)); - strlcat(buf, tmp, PAGE_SIZE); - - /* host_lock is already unlocked. */ - goto buffer_done; - - unlock_buf_done: - spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags); + len += sysfs_emit_at(buf, len, + "FCP CMPL: xb %08x Err %08x\n", + atomic_read(&lport->cmpl_fcp_xb), + atomic_read(&lport->cmpl_fcp_err)); buffer_done: - len = strnlen(buf, PAGE_SIZE); - if (unlikely(len >= (PAGE_SIZE - 1))) { lpfc_printf_log(phba, KERN_INFO, LOG_NVME, "6314 Catching potential buffer " @@ -844,13 +752,12 @@ lpfc_scsi_stat_show(struct device *dev, struct device_attribute *attr, u64 data1, data2, data3; u64 tot, totin, totout; int i; - char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0}; if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_FCP) || (phba->sli_rev != LPFC_SLI_REV4)) return 0; - scnprintf(buf, PAGE_SIZE, "SCSI HDWQ Statistics\n"); + len = sysfs_emit(buf, "SCSI HDWQ Statistics\n"); totin = 0; totout = 0; @@ -863,22 +770,20 @@ lpfc_scsi_stat_show(struct device *dev, struct device_attribute *attr, data3 = cstat->control_requests; totout += (data1 + data2 + data3); - scnprintf(tmp, sizeof(tmp), "HDWQ (%d): Rd %016llx Wr %016llx " - "IO %016llx ", i, data1, data2, data3); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "HDWQ (%d): Rd %016llx Wr %016llx IO %016llx ", + i, data1, data2, data3); - scnprintf(tmp, sizeof(tmp), "Cmpl %016llx OutIO %016llx\n", - tot, ((data1 + data2 + data3) - tot)); - if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) - goto buffer_done; + len += sysfs_emit_at(buf, len, + "Cmpl %016llx OutIO %016llx\n", + tot, ((data1 + data2 + data3) - tot)); + + if (len >= PAGE_SIZE - 1) + break; } - scnprintf(tmp, sizeof(tmp), "Total FCP Cmpl %016llx Issue %016llx " - "OutIO %016llx\n", totin, totout, totout - totin); - strlcat(buf, tmp, PAGE_SIZE); - -buffer_done: - len = strnlen(buf, PAGE_SIZE); + len += sysfs_emit_at(buf, len, + "Total FCP Cmpl %016llx Issue %016llx OutIO %016llx\n", + totin, totout, totout - totin); return len; } diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h index a05d203e4777..3e0937cf3ff3 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.h +++ b/drivers/scsi/lpfc/lpfc_scsi.h @@ -158,6 +158,3 @@ struct fcp_cmnd32 { #define TXRDY_PAYLOAD_LEN 12 -/* For sysfs/debugfs tmp string max len */ -#define LPFC_MAX_SCSI_INFO_TMP_LEN 79 - From d34a88f53a7ab0a4d84fbff137c9077701e95b06 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 14 Aug 2026 16:20:08 +0000 Subject: [PATCH 16/26] scsi: core: Enable context analysis for hosts.o Enable compiler-based context analysis for drivers/scsi/hosts.c by setting CONTEXT_ANALYSIS_hosts.o := y in drivers/scsi/Makefile. The SCSI host management code in hosts.c now has the necessary lock context annotations (such as __must_hold(shost->host_lock) on scsi_host_set_state) and conforms to compile-time lock checking rules. It builds cleanly without triggering any context analysis warnings. Enable context analysis for hosts.o so that lock correctness and context safety invariants for SCSI host operations are verified at compile time when CONFIG_WARN_CONTEXT_ANALYSIS is enabled. Fixes: fb0fc67db962 ("scsi: core: Enable context analysis") Reported-by: John Garry Signed-off-by: Bart Van Assche Reviewed-by: John Garry Link: https://patch.msgid.link/3e1c3c0ca9307e2581cf4b96cf3fcdae35202255.1786724393.git.bvanassche@acm.org Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 72eb395ccf1d..533623382eca 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -15,6 +15,7 @@ # *!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*! CONTEXT_ANALYSIS_constants.o := y +CONTEXT_ANALYSIS_hosts.o := y CONTEXT_ANALYSIS_scsi.o := y CONTEXT_ANALYSIS_scsi_common.o := y CONTEXT_ANALYSIS_scsi_devinfo.o := y From b79b88b655a84187aed12d773566dabf5ab72ed6 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 17 Aug 2026 12:04:55 -0700 Subject: [PATCH 17/26] scsi: ibmvfc: Fix use of uninitialized rport in ibmvfc_do_work() After commit 696d1cc2aaa2 ("scsi: ibmvfc: process NVMe/FC rports in work thread"), clang warns (or errors with CONFIG_WERROR=y / W=e): drivers/scsi/ibmvscsi/ibmvfc-core.c:6154:15: error: variable 'rport' is uninitialized when used here [-Werror,-Wuninitialized] 6154 | } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) { | ^~~~~ The check for rport is unnecessary in this block, it was accidentally included from copying and pasting. Remove it to clear up the warning. Fixes: 696d1cc2aaa2 ("scsi: ibmvfc: process NVMe/FC rports in work thread") Suggested-by: Tyrel Datwyler Link: https://lore.kernel.org/6ccbe8c5-beb6-483f-bfa4-c2d3819ad5f2@linux.ibm.com/ Signed-off-by: Nathan Chancellor Acked-by: Tyrel Datwyler Link: https://patch.msgid.link/20260817-ibmvscsi-rport-wuninitialized-v1-1-0fdfb27a5f01@kernel.org Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/ibmvscsi/ibmvfc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c index 93b9f699c2e5..b3bc3ce872d6 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c @@ -6151,7 +6151,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) timer_delete_sync(&tgt->timer); kref_put(&tgt->kref, ibmvfc_release_tgt); return; - } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) { + } else if (tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) { tgt_dbg(tgt, "Deleting NVMe rport with outstanding I/O\n"); nvme_rport = tgt->nvme_remote_port; ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT); From ff9365a4c9991cca68fd48c7729808f2fdae2036 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 13 Aug 2026 10:08:27 +0300 Subject: [PATCH 18/26] scsi: qla2xxx: Fix an error code in qla_get_tmf() Negative -EIO was intended instead of positive EIO. The caller, doesn't care so this doesn't affect runtime. It's just a cleanup. Signed-off-by: Dan Carpenter Link: https://patch.msgid.link/an1taxANE_4_vzJT@stanley.mountain Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/qla2xxx/qla_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 900cd141928e..bb0cc71de37b 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -2302,7 +2302,7 @@ int qla_get_tmf(struct tmf_arg *arg) if (TMF_NOT_READY(fcport)) { ql_log(ql_log_warn, vha, 0x802c, "Unable to acquire TM resource due to disruption.\n"); - rc = EIO; + rc = -EIO; break; } if (ha->active_tmf < MAX_ACTIVE_TMF && From 11e48f5201fd86ffa038809dd9c41144d43ee2e4 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 13 Aug 2026 10:09:53 +0300 Subject: [PATCH 19/26] scsi: qla2xxx: Fix an loop timeout test This loop timeout with "retries" set to -1, not 0. Fix the test for failure. Fixes: 7ec0effd30bb ("[SCSI] qla2xxx: Add support for ISP8044.") Signed-off-by: Dan Carpenter Link: https://patch.msgid.link/an1twcxTYSFkkUTA@stanley.mountain Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/qla2xxx/qla_nx2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_nx2.c b/drivers/scsi/qla2xxx/qla_nx2.c index 41ff6fbdb933..04d7ab6ad037 100644 --- a/drivers/scsi/qla2xxx/qla_nx2.c +++ b/drivers/scsi/qla2xxx/qla_nx2.c @@ -3507,7 +3507,7 @@ qla8044_poll_flash_status_reg(struct scsi_qla_host *vha) msleep(QLA8044_FLASH_STATUS_REG_POLL_DELAY); } - if (!retries) + if (retries == -1) ret_val = QLA_FUNCTION_FAILED; return ret_val; From 0ec418204f23f0a1dfff79d5f6721080ec006042 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 13 Aug 2026 10:09:32 +0300 Subject: [PATCH 20/26] scsi: lpfc: Remove unnnecessary NULL check The "evt_dat" variale is non-NULL at this point so there is no need to check. Delete the check and pull the code in a tab. Signed-off-by: Dan Carpenter Reviewed-by: Paul Ely Link: https://patch.msgid.link/an1trOAUeQmYEus_@stanley.mountain Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/lpfc/lpfc_bsg.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index c95165905483..7354ae9ba8e5 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -1329,10 +1329,8 @@ lpfc_bsg_hba_get_event(struct bsg_job *job) else bsg_reply->reply_payload_rcv_len = 0; - if (evt_dat) { - kfree(evt_dat->data); - kfree(evt_dat); - } + kfree(evt_dat->data); + kfree(evt_dat); spin_lock_irqsave(&phba->ct_ev_lock, flags); lpfc_bsg_event_unref(evt); From 970f69b6bf71562df5afcefb89c77b4e971d68de Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Fri, 14 Aug 2026 11:38:44 +0800 Subject: [PATCH 21/26] scsi: leapraid: Balance host references for firmware log VMAs leapraid_fw_mmap() keeps the Scsi_Host reference obtained while looking up the adapter for the lifetime of the initial VMA. The VMA close callback drops that reference. The open callback is also invoked when a VMA is duplicated or split, but it only increments mmap_refcnt. Since every corresponding close callback drops a host reference, cloning the mapping can release the host while another VMA still refers to the adapter. Take a host device reference for every VMA open and release the lookup reference once the initial mapping has acquired its own reference. Use get_device() because a VMA can be cloned after the host enters SHOST_DEL; an existing VMA still pins the host at that point and open cannot fail. Fixes: 5597088c9e79 ("scsi: leapraid: Add new SCSI driver") Signed-off-by: Linmao Li Reviewed-by: Dongdong Hao Link: https://patch.msgid.link/20260814033845.2971706-2-lilinmao@kylinos.cn Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/leapraid/leapraid_app.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/leapraid/leapraid_app.c b/drivers/scsi/leapraid/leapraid_app.c index 742f19c07fcb..841027ce2501 100644 --- a/drivers/scsi/leapraid/leapraid_app.c +++ b/drivers/scsi/leapraid/leapraid_app.c @@ -697,6 +697,7 @@ static void leapraid_fw_mmap_open(struct vm_area_struct *vma) if (!adapter) return; + get_device(&adapter->shost->shost_gendev); atomic_inc(&adapter->fw_log_desc.mmap_refcnt); } @@ -767,7 +768,6 @@ static int leapraid_fw_mmap(struct file *filp, struct vm_area_struct *vma) vma->vm_private_data = adapter; vma->vm_ops = &leapraid_fw_mmap_vm_ops; leapraid_fw_mmap_open(vma); - adapter = NULL; rc = 0; out_put: From 00b7c8d4ce441aa9ac704840332ba4738e1c6d51 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Fri, 14 Aug 2026 11:38:45 +0800 Subject: [PATCH 22/26] scsi: leapraid: Serialize firmware log mmap with teardown leapraid_fw_log_exit() waits for mmap_refcnt to reach zero before it frees the firmware log buffer. leapraid_fw_mmap() checks host_removing, but it does not increment mmap_refcnt until after dma_mmap_coherent() succeeds and the VMA open callback runs. Removal can set host_removing and observe a zero mmap_refcnt between the check and the VMA open. It can then free the coherent buffer while the mmap path is still establishing a userspace mapping of it. Claim a temporary mmap reference while looking up the adapter under leapraid_adapter_lock. Removal deletes the adapter from the same locked list after setting host_removing, so a mapping is either rejected or included in the count that removal waits for. Drop the temporary reference on the common exit path, after a successful VMA open has acquired the reference covering the VMA lifetime. Fixes: 5597088c9e79 ("scsi: leapraid: Add new SCSI driver") Signed-off-by: Linmao Li Reviewed-by: Dongdong Hao Link: https://patch.msgid.link/20260814033845.2971706-3-lilinmao@kylinos.cn Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/leapraid/leapraid_app.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/leapraid/leapraid_app.c b/drivers/scsi/leapraid/leapraid_app.c index 841027ce2501..84eef50947ae 100644 --- a/drivers/scsi/leapraid/leapraid_app.c +++ b/drivers/scsi/leapraid/leapraid_app.c @@ -171,7 +171,8 @@ static int leapraid_ctl_validate_sge_offset(struct leapraid_adapter *adapter, return 0; } -static struct leapraid_adapter *leapraid_ctl_lookup_adapter(int adapter_id) +static struct leapraid_adapter *leapraid_ctl_lookup_adapter(int adapter_id, + bool track_mmap) { struct leapraid_adapter *adapter; struct Scsi_Host *shost; @@ -184,6 +185,8 @@ static struct leapraid_adapter *leapraid_ctl_lookup_adapter(int adapter_id) shost = adapter->shost; if (!shost || !scsi_host_get(shost)) break; + if (track_mmap) + atomic_inc(&adapter->fw_log_desc.mmap_refcnt); spin_unlock(&leapraid_adapter_lock); return adapter; } @@ -589,7 +592,7 @@ static int leapraid_ctl_ioctl_main(struct file *file, unsigned int cmd, return -EFAULT; } - adapter = leapraid_ctl_lookup_adapter(ioctl_header.adapter_id); + adapter = leapraid_ctl_lookup_adapter(ioctl_header.adapter_id, false); if (!adapter) return -EFAULT; @@ -728,7 +731,7 @@ static int leapraid_fw_mmap(struct file *filp, struct vm_area_struct *vma) length = vma->vm_end - vma->vm_start; - adapter = leapraid_ctl_lookup_adapter(adapter_id); + adapter = leapraid_ctl_lookup_adapter(adapter_id, true); if (!adapter) { pr_err("%s: No adapter found!\n", __func__); return -EINVAL; @@ -771,6 +774,9 @@ static int leapraid_fw_mmap(struct file *filp, struct vm_area_struct *vma) rc = 0; out_put: + if (adapter && + atomic_dec_and_test(&adapter->fw_log_desc.mmap_refcnt)) + wake_up(&adapter->fw_log_desc.mmap_waitq); leapraid_ctl_put_adapter(adapter); return rc; } From 46f861d300e87283f81faf9ee377d1e73682903d Mon Sep 17 00:00:00 2001 From: Dongdong Hao Date: Fri, 14 Aug 2026 17:05:26 +0800 Subject: [PATCH 23/26] scsi: leapraid: Standardize NCQ priority sysfs attributes Replace the earlier LeapRAID ncq_cmd_prio_enable attribute with the standard sas_ncq_prio_supported and sas_ncq_prio_enable names documented in Documentation/ABI/testing/sysfs-block-device, and rename the per-device NCQ priority state to match. The earlier ncq_cmd_prio_enable name has not yet been established as part of a released userspace ABI, so no compatibility alias is needed. For LeapRAID, sas_ncq_prio_enable is backed by the driver's per-device NCQ priority state and controls whether RT-class I/O requests are issued with command priority on supported SATA devices. Update leapraid.rst to describe the standard attribute names and paths, and clean up the surrounding RST text for consistency with kernel documentation style. Also switch the capability check from open-coded VPD page 0x89 parsing to sas_ata_ncq_prio_supported(), use kstrtobool() for the enable path, and expose the NCQ priority attributes only for SATA devices using LeapRAID's target-private SAS device state. Reviewed-by: Damien Le Moal Signed-off-by: Dongdong Hao Reviewed-by: Hannes Reinecke Link: https://patch.msgid.link/20260814090526.395704-1-doubled@leap-io-kernel.com Signed-off-by: Martin K. Petersen (Oracle) --- Documentation/scsi/leapraid.rst | 14 +++-- drivers/scsi/leapraid/leapraid_func.h | 4 +- drivers/scsi/leapraid/leapraid_os.c | 87 +++++++++++++++++---------- 3 files changed, 68 insertions(+), 37 deletions(-) diff --git a/Documentation/scsi/leapraid.rst b/Documentation/scsi/leapraid.rst index 99930ce2b8d0..d36ff627d9f0 100644 --- a/Documentation/scsi/leapraid.rst +++ b/Documentation/scsi/leapraid.rst @@ -22,6 +22,7 @@ Supported devices Features ======== + - PCIe Gen4 x8 host interface - Support for SAS and SATA devices - RAID levels: 0, 1, 10, 5, 50, 6, 60 @@ -50,16 +51,20 @@ LeapRAID specific disk attributes :: - /sys/class/scsi_disk/host:bus:target:lun/device/sas_device_handle - /sys/class/scsi_disk/host:bus:target:lun/device/ncq_cmd_prio_enable + /sys/block//device/sas_device_handle + /sys/block//device/sas_ncq_prio_supported + /sys/block//device/sas_ncq_prio_enable The read-only attribute "sas_device_handle" represents the disk's device handle, which is a unique identifier maintained by the firmware. -This attribute "ncq_cmd_prio_enable" controls NCQ command priority. A value +The read-only attribute "sas_ncq_prio_supported" reports whether a SATA +device supports NCQ command priority. + +The attribute "sas_ncq_prio_enable" controls NCQ command priority. A value of 0 disables NCQ priority handling for RT-priority I/O. Writing 1 enables NCQ priority handling when the device reports support for the feature through -VPD page 0x89. Unsupported devices keep the effective state at 0. +VPD page 0x89. Writes to unsupported devices fail with an error. LeapRAID module parameters ========================== @@ -100,6 +105,7 @@ in io_uring poll mode. The default value is 0. File Location ============= + The driver source is located at: ``drivers/scsi/leapraid/`` diff --git a/drivers/scsi/leapraid/leapraid_func.h b/drivers/scsi/leapraid/leapraid_func.h index 923596211aa1..4c0b9ca728d8 100644 --- a/drivers/scsi/leapraid/leapraid_func.h +++ b/drivers/scsi/leapraid/leapraid_func.h @@ -1064,7 +1064,7 @@ struct leapraid_starget_priv { * @starget_priv: Associated target private data. * @lun: Logical Unit Number. * @flg: Flags. - * @ncq_cmd_prio_enable: Enables NCQ command priority for RT I/O. + * @ncq_prio_enable: Enables NCQ command priority for RT I/O. * @block: Block flag. * @deleted: Deletion flag. * @sep: SEP flag. @@ -1073,7 +1073,7 @@ struct leapraid_sdev_priv { struct leapraid_starget_priv *starget_priv; unsigned int lun; u32 flg; - u8 ncq_cmd_prio_enable; + u8 ncq_prio_enable; u8 block; u8 deleted; u8 sep; diff --git a/drivers/scsi/leapraid/leapraid_os.c b/drivers/scsi/leapraid/leapraid_os.c index a8e1c9f33896..ee3242779dfd 100644 --- a/drivers/scsi/leapraid/leapraid_os.c +++ b/drivers/scsi/leapraid/leapraid_os.c @@ -859,7 +859,7 @@ static u32 build_scsiio_req_control(struct scsi_cmnd *scmd, control |= LEAPRAID_SCSIIO_CTRL_SIMPLEQ; - if (sdev_priv->ncq_cmd_prio_enable && + if (sdev_priv->ncq_prio_enable && (IOPRIO_PRIO_CLASS(req_get_ioprio(scsi_cmd_to_rq(scmd))) == IOPRIO_CLASS_RT)) control |= LEAPRAID_SCSIIO_CTRL_CMDPRI; @@ -1854,7 +1854,16 @@ static ssize_t sas_device_handle_show(struct device *dev, sas_device_priv_data->starget_priv->hdl); } -static ssize_t ncq_cmd_prio_enable_show(struct device *dev, +static ssize_t sas_ncq_prio_supported_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + + return sysfs_emit(buf, "%d\n", sas_ata_ncq_prio_supported(sdev)); +} + +static ssize_t sas_ncq_prio_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1867,19 +1876,16 @@ static ssize_t ncq_cmd_prio_enable_show(struct device *dev, return -EINVAL; } - return sysfs_emit(buf, "%d\n", - sas_device_priv_data->ncq_cmd_prio_enable); + return sysfs_emit(buf, "%d\n", sas_device_priv_data->ncq_prio_enable); } -static ssize_t ncq_cmd_prio_enable_store(struct device *dev, +static ssize_t sas_ncq_prio_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct scsi_device *sdev = to_scsi_device(dev); struct leapraid_sdev_priv *sas_device_priv_data = sdev->hostdata; - struct scsi_vpd *vpd_pg89; - int ncq_cmd_prio_enable; - bool ncq_supported; + bool enable; if (!sas_device_priv_data) { dev_err(&sdev->sdev_gendev, @@ -1887,44 +1893,63 @@ static ssize_t ncq_cmd_prio_enable_store(struct device *dev, return -EINVAL; } - if (kstrtoint(buf, 0, &ncq_cmd_prio_enable)) + if (kstrtobool(buf, &enable)) return -EINVAL; - if (ncq_cmd_prio_enable != 0 && ncq_cmd_prio_enable != 1) { - dev_err(&sdev->sdev_gendev, - "%s: Invalid NCQ cmd prio %d (0/1 only)\n", - __func__, ncq_cmd_prio_enable); + if (!sas_ata_ncq_prio_supported(sdev)) return -EINVAL; - } - rcu_read_lock(); - vpd_pg89 = rcu_dereference(sdev->vpd_pg89); - if (!vpd_pg89 || vpd_pg89->len < LEAPRAID_VPD_PG89_MIN_LEN) { - rcu_read_unlock(); - return -EINVAL; - } - - ncq_supported = (vpd_pg89->data[LEAPRAID_VPD_PG89_NCQ_BYTE_IDX] >> - LEAPRAID_VPD_PG89_NCQ_BIT_SHIFT) & - LEAPRAID_VPD_PG89_NCQ_BIT_MASK; - rcu_read_unlock(); - if (ncq_supported) - sas_device_priv_data->ncq_cmd_prio_enable = - ncq_cmd_prio_enable; + sas_device_priv_data->ncq_prio_enable = enable; return count; } static DEVICE_ATTR_RO(sas_device_handle); +static DEVICE_ATTR_RO(sas_ncq_prio_supported); +static DEVICE_ATTR_RW(sas_ncq_prio_enable); -static DEVICE_ATTR_RW(ncq_cmd_prio_enable); +static bool leapraid_sdev_is_sata(struct scsi_device *sdev) +{ + struct scsi_target *starget = sdev->sdev_target; + struct leapraid_starget_priv *starget_priv = starget->hostdata; + struct leapraid_sas_dev *sas_dev; + + if (!starget_priv) + return false; + + sas_dev = starget_priv->sas_dev; + return sas_dev && (sas_dev->dev_info & LEAPRAID_DEVTYP_SATA_DEV); +} static struct attribute *leapraid_sdev_attrs[] = { &dev_attr_sas_device_handle.attr, - &dev_attr_ncq_cmd_prio_enable.attr, + &dev_attr_sas_ncq_prio_supported.attr, + &dev_attr_sas_ncq_prio_enable.attr, NULL, }; -ATTRIBUTE_GROUPS(leapraid_sdev); +static umode_t leapraid_sdev_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int i) +{ + struct device *dev = kobj_to_dev(kobj); + struct scsi_device *sdev = to_scsi_device(dev); + + if (attr == &dev_attr_sas_ncq_prio_supported.attr || + attr == &dev_attr_sas_ncq_prio_enable.attr) + if (!leapraid_sdev_is_sata(sdev)) + return 0; + + return attr->mode; +} + +static const struct attribute_group leapraid_sdev_attr_group = { + .attrs = leapraid_sdev_attrs, + .is_visible = leapraid_sdev_attr_is_visible, +}; + +static const struct attribute_group *leapraid_sdev_groups[] = { + &leapraid_sdev_attr_group, + NULL, +}; static struct scsi_host_template leapraid_driver_template = { .module = THIS_MODULE, From 764587d7d76846716a568dea7ae48be1c50ecc18 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Tue, 18 Aug 2026 11:00:24 +1000 Subject: [PATCH 24/26] scsi: MAINTAINERS: Leave the cumana_1 and oak drivers to the RISCPC maintainers The NCR5380 entry in MAINTAINERS includes drivers/scsi/arm/cumana_1.c and drivers/scsi/arm/oak.c. However, those two files are also covered by the drivers/scsi/arm/ pathname in the ARM/RISCPC entry. The latter entry is more effective than the former because, AIUI, neither Michael nor I have access to the necessary hardware. IMHO, such access is a pre-requisite for the 'maintainer' role for device drivers. To work on these particular drivers would require an old GCC compiler, having support for -march=armv3m, which is a problem for contributors. Cc: Michael Schmitz Cc: Russell King Cc: Arnd Bergmann Signed-off-by: Finn Thain Acked-by: Michael Schmitz Link: https://patch.msgid.link/935b08c0fb292888c06c2233570331f2ccadcd53.1787014824.git.fthain@linux-m68k.org Signed-off-by: Martin K. Petersen (Oracle) --- MAINTAINERS | 2 -- 1 file changed, 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index a8a58f059c49..95fd1a961cd5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18576,8 +18576,6 @@ L: linux-scsi@vger.kernel.org S: Maintained F: Documentation/scsi/g_NCR5380.rst F: drivers/scsi/NCR5380.* -F: drivers/scsi/arm/cumana_1.c -F: drivers/scsi/arm/oak.c F: drivers/scsi/atari_scsi.* F: drivers/scsi/dmx3191d.c F: drivers/scsi/g_NCR5380.* From df125bd16280b19835bfa2eef6504b505790f1f6 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen (Oracle)" Date: Sun, 23 Aug 2026 22:08:18 -0400 Subject: [PATCH 25/26] scsi: MAINTAINERS: Update my email address Use my kernel.org address for Linux development. Signed-off-by: Martin K. Petersen (Oracle) --- .mailmap | 3 +++ MAINTAINERS | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.mailmap b/.mailmap index 23eb9a4b04f4..e484232b32a1 100644 --- a/.mailmap +++ b/.mailmap @@ -553,6 +553,9 @@ Mark Brown Mark Starovoytov Markus Schneider-Pargmann Mark Yao +Martin K. Petersen +Martin K. Petersen +Martin K. Petersen Martin Kepplinger-Novakovic Martyna Szapar-Mudlaw Mathieu Othacehe diff --git a/MAINTAINERS b/MAINTAINERS index 95fd1a961cd5..9c768040658d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24291,7 +24291,7 @@ F: include/scsi/sg.h SCSI SUBSYSTEM M: "James E.J. Bottomley" -M: "Martin K. Petersen" +M: "Martin K. Petersen" L: linux-scsi@vger.kernel.org S: Maintained Q: https://patchwork.kernel.org/project/linux-scsi/list/ @@ -24321,7 +24321,7 @@ F: drivers/target/target_core_user.c F: include/uapi/linux/target_core_user.h SCSI TARGET SUBSYSTEM -M: "Martin K. Petersen" +M: "Martin K. Petersen" L: linux-scsi@vger.kernel.org L: target-devel@vger.kernel.org S: Supported From 12e67eb89eb2b9516685c744d3f7de0a2d1bd701 Mon Sep 17 00:00:00 2001 From: Chen Changcheng Date: Mon, 27 Jul 2026 15:34:38 +0800 Subject: [PATCH 26/26] scsi: snic: Fix SCSI host leak on workqueue allocation failure In snic_add_host(), if scsi_add_host() succeeds but alloc_ordered_workqueue() fails, the function returns -ENOMEM with shost->work_q left as NULL. The caller's error path then calls snic_del_host(), which returns early when !shost->work_q without calling scsi_remove_host(). The Scsi_Host remains registered in sysfs as a zombie device even after the probe has failed. This causes: - The leaked host remains visible in /sys/class/scsi_host/ after probe failure, with state "running". - Subsequent SCSI host numbering is permanently shifted (the leaked host ID from ida_alloc() is never reclaimed). - Memory leak: the Scsi_Host allocation can never be freed because device_add() took a reference that can only be released by device_del() inside scsi_remove_host(). Fix by adding scsi_remove_host() in the workqueue allocation failure path inside snic_add_host(), undoing the successful scsi_add_host() before returning the error. This is cleaner than modifying snic_del_host() because snic_del_host() is called from a shared error label that also serves paths where snic_add_host() was never invoked. Reproducer (requires no real SNIC hardware): - Build CONFIG_SCSI_SNIC=y (built-in) - Add snic.test_mode=1 snic.inject_wq_fail=1 to kernel cmdline - Boot with a PCI device matching the snic driver (e.g. QEMU edu device, PCI ID 0x1234:0x11e8, temporarily added to the driver's PCI ID table) Before the fix: # /sys/class/scsi_host/ contains a zombie host0: $ cat /sys/class/scsi_host/host0/proc_name snic_scsi $ cat /sys/class/scsi_host/host0/state running # ata_piix gets host1, host2 (host0 stuck): scsi host1: ata_piix scsi host2: ata_piix After the fix: # host0 is properly freed and reused by ata_piix: scsi host0: ata_piix scsi host1: ata_piix # No zombie host in /sys/class/scsi_host/ Signed-off-by: Chen Changcheng Acked-by: Narsimhulu Musini Link: https://patch.msgid.link/20260727073438.209673-1-chenchangcheng@kylinos.cn Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/snic/snic_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/snic/snic_main.c b/drivers/scsi/snic/snic_main.c index 82953e6a0915..cd638b4a4d7b 100644 --- a/drivers/scsi/snic/snic_main.c +++ b/drivers/scsi/snic/snic_main.c @@ -305,6 +305,7 @@ snic_add_host(struct Scsi_Host *shost, struct pci_dev *pdev) if (!shost->work_q) { SNIC_HOST_ERR(shost, "Failed to Create ScsiHost wq.\n"); + scsi_remove_host(shost); ret = -ENOMEM; }