Linux SCSI subsystem development
 help / color / mirror / Atom feed
* [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support
@ 2026-07-07  5:53 Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 01/88] scsi: qla2xxx: Add 29xx series PCI device ID support Nilesh Javali
                   ` (87 more replies)
  0 siblings, 88 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Add support for the QLA29xx generation of Marvell QLogic
Fibre Channel HBAs (ISP2091/ISP2291/ISP2099/ISP2299).
The 29xx family shares much of its architecture with the
existing 27xx/28xx adapters but introduces 128-byte request
and response ring entries (up from 64 bytes), requiring
extended IOCB definitions and updated ring management
throughout the driver.

The key hardware change is the wider IOCB format: every
request and response queue entry is now 128 bytes.
This propagates into every code path that builds, submits,
or processes IOCBs -- command submission, status completion,
marker, CT pass-through, ELS, logio, task management,
abort, ABTS, VP control, and NVMe.

The series is organised as follows:

Patches 01-08: Foundation and flash/firmware infrastructure
  PCI device ID registration, ISP-flags wiring, flash read/write
  interface, NVRAM configuration, queue initialisation, FC operational
  firmware load, removal of a redundant VPD flash read in the sysfs
  read path, and BSG passthrough (flash block I/O, MPI firmware
  load/dump).

Patches 09-11: 128-byte IOCB infrastructure
  New qla_fw29.h header with extended structure definitions, status
  continuation and marker IOCBs, and IO-path updates that select the
  correct IOCB size via the entry-size helpers.

Patches 12-24: Sysfs, mailbox commands, and core enablement
  Sysfs attribute gating for unsupported 29xx features, mailbox command
  enablement (get_fw_version, execute_fw, get_adapter_id,
  init_firmware, get_firmware_state, serdes, ELS, echo_test,
  data rate), shutdown path, ring-slot helpers, and memory allocation
  updates.

Patches 25-39: Response-path IOCB handling and final wiring
  Status continuation, status entry, CT pass-through, PUREX, ELS,
  logio, task management, abort, ABTS, VP control/config/report-ID,
  LS4 pass-through, and BSG feature gating adjustments.

Patches 40-55: bug fixes uncovered during review of the earlier
  postings -- queue teardown NULL dma_free and bitmap locking,
  endianness/bitfield cleanups, 64-bit FPM word counters, 64G/128G
  port speed setting and reporting, an edif NULL deref, Name Server
  logout detection on FWI2 adapters, VP index bounds, NVMe abort and
  LS-reject locking, a dport diagnostics info leak, a BSG job leak,
  and an unbounded FRU image count.

Patches 56-87: additional robustness and bug fixes found during
  continued review -- MSI-X derived queue-count clamping, firmware
  dump data-capture improvement, flash-version read serialisation,
  use-after-free fixes (qpair work on teardown, cs84xx on host
  teardown, FCE trace during firmware dump), firmware-state and
  mailbox hygiene, FCE trace enable parsing, QLAFX00 ring-slot init,
  error-path pointer clearing, response-queue over-consumption,
  soft-lockup polling and OOB sense-data guards, response-IRQ
  quiescing and completion-path SRB validation, NPIV vport count
  clamping and report-ID-acquisition locking/refcount fixes, NVMe
  abort/LS-reject/unsolicited-context correctness and locking, a
  coherent DMA buffer for D_Port diagnostics, and BSG passthrough
  hardening (zero-initialised stack buffers, request_len validation,
  SFP DMA zeroing, and I2C length bounds).

Patch 88: bump the driver version to 12.00.00.2607b1.

The series applies on top of the scsi tree's 7.1/scsi-queue branch.

Changes in v3:
  - Folded several standalone helper and refactor commits into the
    feature commits they support, so every 29xx enablement commit is
    self-contained and bisectable: the entry-size helper conversion,
    the marker IOCB refactor, the NVMe IOCB build-path unification,
    the NVMe ring-advance conversion, and the duplicate flash-memo
    block removal are no longer separate patches.
  - Reworked flash-version handling: dropped the standalone
    get_flash_version patch in favour of removing the redundant VPD
    flash read in the sysfs read path.
  - Added a large batch of additional robustness and bug fixes
    uncovered during continued review (patches 56-87), covering the
    completion/response path, NPIV and report-ID acquisition, NVMe
    unsolicited-context handling, and BSG passthrough hardening.
  - Bumped the driver version to 12.00.00.2607b1.
  - Added Reviewed-by: Hannes Reinecke <hare@kernel.org> to the
    reviewed 29xx enablement patches (patches 01-55).

Changes in v2:
  - Folded the standalone fix-ups posted in v1 into the feature commits
    they corrected, so the 29xx enablement commits are now individually
    correct and bisectable (no "fix the previous patch" commits in the
    middle of the series).
  - Corrected several Fixes: tags to reference the actual introducing
    commits after the above reorganisation.
  - Widened the ELS vp_index path to 16 bits to match the 29xx 9-bit
    hardware field, folded into the ELS enablement commit.

Thanks,
Nilesh

Anil Gurumurthy (3):
  scsi: qla2xxx: Add 128-byte IOCB definitions for 29xx
  scsi: qla2xxx: Add extended status continuation and marker IOCBs
  scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx

Manish Rangankar (10):
  scsi: qla2xxx: Add 29xx series PCI device ID support
  scsi: qla2xxx: Add flash read/write interface for 29xx
  scsi: qla2xxx: Add NVRAM config support for 29xx adapters
  scsi: qla2xxx: Add 29xx support in queue initialisation path
  scsi: qla2xxx: Add FC operational firmware load for 29xx
  scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path
  scsi: qla2xxx: Add flash block read/write BSG support for 29xx
  scsi: qla2xxx: Add BSG MPI firmware load/dump for 29xx
  scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series
  scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support

Nilesh Javali (74):
  scsi: qla2xxx: Skip image-set-valid attribute for 29xx
  scsi: qla2xxx: Skip unsupported sysfs attributes for 29xx
  scsi: qla2xxx: Enable get_fw_version mailbox for 29xx
  scsi: qla2xxx: Extend execute_fw mailbox to include 29xx
  scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx
  scsi: qla2xxx: Enable init_firmware mailbox for 29xx
  scsi: qla2xxx: Enable get_firmware_state for 29xx
  scsi: qla2xxx: Enable serdes, resource count and FCE trace for 29xx
  scsi: qla2xxx: Enable set_els_cmds and echo_test for 29xx
  scsi: qla2xxx: Add support for QLA29XX in data rate functions
  scsi: qla2xxx: Enable qla2x00_shutdown for 29xx
  scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs
  scsi: qla2xxx: Add support for QLA29XX in memory allocation
  scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters
  scsi: qla2xxx: Update handling of status entries for 29xx series
  scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling for 29xx series
  scsi: qla2xxx: Enhance purex_entry handling for 29xx series
  scsi: qla2xxx: Update handling of ELS IOCBs for 29xx series
  scsi: qla2xxx: Add size check for ELS status entry layout on 29xx
  scsi: qla2xxx: Add 29xx extended logio IOCB support
  scsi: qla2xxx: Enhance task management IOCB handling for 29xx series
  scsi: qla2xxx: Add abort command handling for 29xx series
  scsi: qla2xxx: Enhance ABTS processing for 29xx series
  scsi: qla2xxx: Update VP control IOCB handling for 29xx series
  scsi: qla2xxx: Add build-time size check for VP config IOCB layout
  scsi: qla2xxx: Add size check for extended VP report ID entry
  scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking
  scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors
  scsi: qla2xxx: Fix endianness annotations in vp_rpt_id_entry
    structures
  scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats
  scsi: qla2xxx: Add 64G/128G port speed setting support
  scsi: qla2xxx: Fix 64G link speed reporting in get_data_rate
  scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check
  scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters
  scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size
  scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config()
  scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition()
  scsi: qla2xxx: Initialize NVMe abort_work once at submission
  scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject
  scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak
  scsi: qla2xxx: Fix BSG job leak on validate flash image error path
  scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions()
  scsi: qla2xxx: Clamp MSI-X derived queue counts to avoid truncation
  scsi: qla2xxx: Serialize flash version read in reset handler
  scsi: qla2xxx: Fix use-after-free of qpair work on queue teardown
  scsi: qla2xxx: Clarify MPI optrom address/length units
  scsi: qla2xxx: Fix cs84xx use-after-free on host teardown
  scsi: qla2xxx: Don't query firmware state while chip is down
  scsi: qla2xxx: Zero mailbox struct in qla2x00_get_firmware_state()
  scsi: qla2xxx: Fix FCE trace enable parsing in debugfs
  scsi: qla2xxx: Fix FCE trace use-after-free during firmware dump
  scsi: qla2xxx: Use memset_io() to clear QLAFX00 request ring slot
  scsi: qla2xxx: Null out freed pointers in qla2x00_mem_alloc() error
    path
  scsi: qla2xxx: Fix response queue over-consumption in
    __qla_consume_iocb()
  scsi: qla2xxx: Fix soft lockup polling continuation IOCB signature
  scsi: qla2xxx: Bound rsp_info_len to avoid OOB sense-data read
  scsi: qla2xxx: Avoid req_q_map double-read in qla2x00_error_entry()
  scsi: qla2xxx: Quiesce response IRQ before freeing request queue
  scsi: qla2xxx: Reject non-SCSI SRB on status IOCB fast path
  scsi: qla2xxx: Clamp max_npiv_vports to VP_CTRL bitmap capacity
  scsi: qla2xxx: Avoid double completion in async IOCB timeout
  scsi: qla2xxx: Skip vport under deletion in report ID acquisition
  scsi: qla2xxx: Drop vport reference under lock in report ID
    acquisition
  scsi: qla2xxx: Hold vport_slock for host map update in report ID
    acquisition
  scsi: qla2xxx: Fix NVMe abort reference leak on repeated abort
  scsi: qla2xxx: Skip NVMe LS reject IOCB when FW not started
  scsi: qla2xxx: Unlink NVMe unsol ctx before freeing on LS reject error
  scsi: qla2xxx: Serialize NVMe unsol ctx list with a per-fcport lock
  scsi: qla2xxx: Use coherent DMA buffer for D_Port diagnostics
  scsi: qla2xxx: Zero-init bsg stack buffers to avoid info leak
  scsi: qla2xxx: Validate BSG request_len before reading vendor_cmd[]
  scsi: qla2xxx: Zero SFP DMA buffer in FRU/I2C bsg handlers
  scsi: qla2xxx: Bound i2c->length in I2C bsg handlers
  scsi: qla2xxx: Update version to 12.00.00.2607b1

Quinn Tran (1):
  scsi: qla2xxx: Improve firmware dump data capture

 drivers/scsi/qla2xxx/qla_attr.c    |   63 +-
 drivers/scsi/qla2xxx/qla_bsg.c     |  608 +++++++++++--
 drivers/scsi/qla2xxx/qla_bsg.h     |   34 +
 drivers/scsi/qla2xxx/qla_dbg.c     |   37 +-
 drivers/scsi/qla2xxx/qla_def.h     |  145 +++-
 drivers/scsi/qla2xxx/qla_dfs.c     |    8 +-
 drivers/scsi/qla2xxx/qla_edif.c    |  101 ++-
 drivers/scsi/qla2xxx/qla_fw.h      |  144 ++-
 drivers/scsi/qla2xxx/qla_fw29.h    |  830 ++++++++++++++++++
 drivers/scsi/qla2xxx/qla_gbl.h     |   40 +-
 drivers/scsi/qla2xxx/qla_gs.c      |  176 +++-
 drivers/scsi/qla2xxx/qla_init.c    |  919 +++++++++++++++++---
 drivers/scsi/qla2xxx/qla_inline.h  |  295 ++++++-
 drivers/scsi/qla2xxx/qla_iocb.c    | 1303 ++++++++++++++++++++++------
 drivers/scsi/qla2xxx/qla_isr.c     |  755 ++++++++++------
 drivers/scsi/qla2xxx/qla_mbx.c     |  499 ++++++++---
 drivers/scsi/qla2xxx/qla_mid.c     |   78 +-
 drivers/scsi/qla2xxx/qla_nvme.c    |  315 +++++--
 drivers/scsi/qla2xxx/qla_nvme.h    |    4 +-
 drivers/scsi/qla2xxx/qla_nx.c      |    2 +-
 drivers/scsi/qla2xxx/qla_os.c      |  310 +++++--
 drivers/scsi/qla2xxx/qla_sup.c     |  784 ++++++++++++++++-
 drivers/scsi/qla2xxx/qla_target.c  |   34 +-
 drivers/scsi/qla2xxx/qla_tmpl.c    |   48 +-
 drivers/scsi/qla2xxx/qla_version.h |   10 +-
 25 files changed, 6388 insertions(+), 1154 deletions(-)
 create mode 100644 drivers/scsi/qla2xxx/qla_fw29.h


base-commit: 1801c8284d34d7927f1a158226e427c195936746
prerequisite-patch-id: 9cdf671a5c422facf4cee5626701f9135d076122
prerequisite-patch-id: 807b23211a696e9f032ae85b037aafeb08501768
prerequisite-patch-id: 1145e5762d000b371299b981054a7baabbbede6e
prerequisite-patch-id: ad6f2e8fdf93cc1341470516aee8ccdee0d99cfb
prerequisite-patch-id: 6df17c866b242df13f50eba6cd81f26d4dc0656c
prerequisite-patch-id: 05d09642756af9c90cd8568d63496c676ace66f0
prerequisite-patch-id: 85252984f56a31690f027b735594caf003738e27
prerequisite-patch-id: 86b571d585d2dd9f783220df2bb1baeab0c7dc2b
prerequisite-patch-id: 44f65619d39d4e5f1dd56a898ee6156238a6b3b7
prerequisite-patch-id: 412cfca3d3695325b90a70a56f998e26d34cf5bd
prerequisite-patch-id: 368f0039bdb590ec70f83327ed6c82028342367f
prerequisite-patch-id: 0af9e8e1b40955fb0f513f973db167ff469dd45f
-- 
2.47.3


^ permalink raw reply	[flat|nested] 159+ messages in thread

* [PATCH v3 01/88] scsi: qla2xxx: Add 29xx series PCI device ID support
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:21   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 02/88] scsi: qla2xxx: Add flash read/write interface for 29xx Nilesh Javali
                   ` (86 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

From: Manish Rangankar <mrangankar@marvell.com>

The QLA29xx is a new generation FC HBA that shares much of its
architecture with the 27xx/28xx family.  Register the new PCI
device IDs, wire up IS_QLA29XX() capability checks in the probe
and ISP-flags paths, and extend speed-capability logic so the
driver correctly recognises and initialises 29xx adapters.

Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_def.h  | 33 +++++++++++----
 drivers/scsi/qla2xxx/qla_init.c |  2 +-
 drivers/scsi/qla2xxx/qla_isr.c  |  5 ++-
 drivers/scsi/qla2xxx/qla_os.c   | 74 ++++++++++++++++++++++++++-------
 4 files changed, 88 insertions(+), 26 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 5593ad7fad27..6337a056b149 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -3528,7 +3528,6 @@ struct isp_operations {
 #define QLA_MIDX_DEFAULT	0
 #define QLA_MIDX_RSP_Q		1
 #define QLA_PCI_MSIX_CONTROL	0xa2
-#define QLA_83XX_PCI_MSIX_CONTROL	0x92
 
 struct scsi_qla_host;
 
@@ -4287,6 +4286,10 @@ struct qla_hw_data {
 #define PCI_DEVICE_ID_QLOGIC_ISP2089	0x2089
 #define PCI_DEVICE_ID_QLOGIC_ISP2281	0x2281
 #define PCI_DEVICE_ID_QLOGIC_ISP2289	0x2289
+#define PCI_DEVICE_ID_QLOGIC_ISP2099	0x2099
+#define PCI_DEVICE_ID_QLOGIC_ISP2299	0x2299
+#define PCI_DEVICE_ID_QLOGIC_ISP2091	0x2091
+#define PCI_DEVICE_ID_QLOGIC_ISP2291	0x2291
 
 	uint32_t	isp_type;
 #define DT_ISP2100                      BIT_0
@@ -4316,7 +4319,11 @@ struct qla_hw_data {
 #define DT_ISP2089			BIT_24
 #define DT_ISP2281			BIT_25
 #define DT_ISP2289			BIT_26
-#define DT_ISP_LAST			(DT_ISP2289 << 1)
+#define DT_ISP2299			BIT_27
+#define DT_ISP2099			BIT_28
+#define DT_ISP2091			BIT_29
+#define DT_ISP2291			BIT_30
+#define DT_ISP_LAST			((uint32_t)DT_ISP2291 << 1)
 
 	uint32_t	device_type;
 #define DT_T10_PI                       BIT_25
@@ -4353,6 +4360,10 @@ struct qla_hw_data {
 #define IS_QLA2261(ha)	(DT_MASK(ha) & DT_ISP2261)
 #define IS_QLA2081(ha)	(DT_MASK(ha) & DT_ISP2081)
 #define IS_QLA2281(ha)	(DT_MASK(ha) & DT_ISP2281)
+#define IS_QLA2299(ha)	(DT_MASK(ha) & DT_ISP2299)
+#define IS_QLA2099(ha)	(DT_MASK(ha) & DT_ISP2099)
+#define IS_QLA2091(ha)	(DT_MASK(ha) & DT_ISP2091)
+#define IS_QLA2291(ha)	(DT_MASK(ha) & DT_ISP2291)
 
 #define IS_QLA23XX(ha)  (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \
 			IS_QLA6312(ha) || IS_QLA6322(ha))
@@ -4363,6 +4374,9 @@ struct qla_hw_data {
 #define IS_QLA84XX(ha)  (IS_QLA8432(ha))
 #define IS_QLA27XX(ha)  (IS_QLA2071(ha) || IS_QLA2271(ha) || IS_QLA2261(ha))
 #define IS_QLA28XX(ha)	(IS_QLA2081(ha) || IS_QLA2281(ha))
+#define IS_QLA29XX(ha)	(IS_QLA2099(ha) || IS_QLA2299(ha) || \
+			 IS_QLA2091(ha) || IS_QLA2291(ha))
+
 #define IS_QLA24XX_TYPE(ha)     (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \
 				IS_QLA84XX(ha))
 #define IS_CNA_CAPABLE(ha)	(IS_QLA81XX(ha) || IS_QLA82XX(ha) || \
@@ -4372,9 +4386,10 @@ struct qla_hw_data {
 				IS_QLA25XX(ha) || IS_QLA81XX(ha) || \
 				IS_QLA82XX(ha) || IS_QLA83XX(ha) || \
 				IS_QLA8044(ha) || IS_QLA27XX(ha) || \
-				IS_QLA28XX(ha))
+				IS_QLA28XX(ha) || IS_QLA29XX(ha))
 #define IS_MSIX_NACK_CAPABLE(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha) || \
-				IS_QLA27XX(ha) || IS_QLA28XX(ha))
+				IS_QLA27XX(ha) || IS_QLA28XX(ha) || \
+				IS_QLA29XX(ha))
 #define IS_NOPOLLING_TYPE(ha)	(IS_QLA81XX(ha) && (ha)->flags.msix_enabled)
 #define IS_FAC_REQUIRED(ha)	(IS_QLA81XX(ha) || IS_QLA83XX(ha) || \
 				IS_QLA27XX(ha) || IS_QLA28XX(ha))
@@ -4390,9 +4405,9 @@ struct qla_hw_data {
 #define HAS_EXTENDED_IDS(ha)    ((ha)->device_type & DT_EXTENDED_IDS)
 #define IS_CT6_SUPPORTED(ha)	((ha)->device_type & DT_CT6_SUPPORTED)
 #define IS_MQUE_CAPABLE(ha)	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
-				 IS_QLA28XX(ha))
+				 IS_QLA28XX(ha) || IS_QLA29XX(ha))
 #define IS_BIDI_CAPABLE(ha) \
-    (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+    (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 /* Bit 21 of fw_attributes decides the MCTP capabilities */
 #define IS_MCTP_CAPABLE(ha)	(IS_QLA2031(ha) && \
 				((ha)->fw_attributes_ext[0] & BIT_0))
@@ -4408,12 +4423,12 @@ struct qla_hw_data {
 	(QLA_NVME_IOS(_sp) && QLA_ABTS_FW_ENABLED(_sp->fcport->vha->hw))
 
 #define IS_PI_UNINIT_CAPABLE(ha)	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
-					 IS_QLA28XX(ha))
+					 IS_QLA28XX(ha) || IS_QLA29XX(ha))
 #define IS_PI_IPGUARD_CAPABLE(ha)	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
-					 IS_QLA28XX(ha))
+					 IS_QLA28XX(ha) || IS_QLA29XX(ha))
 #define IS_PI_DIFB_DIX0_CAPABLE(ha)	(0)
 #define IS_PI_SPLIT_DET_CAPABLE_HBA(ha)	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
-					IS_QLA28XX(ha))
+					IS_QLA28XX(ha) || IS_QLA29XX(ha))
 #define IS_PI_SPLIT_DET_CAPABLE(ha)	(IS_PI_SPLIT_DET_CAPABLE_HBA(ha) && \
     (((ha)->fw_attributes_h << 16 | (ha)->fw_attributes) & BIT_22))
 #define IS_ATIO_MSIX_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index e746c9274cde..e23e7ac48ae2 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -2773,7 +2773,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha)
 	ha->isp_ops->reset_chip(vha);
 
 	/* Check for secure flash support */
-	if (IS_QLA28XX(ha)) {
+	if (IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		if (rd_reg_word(&reg->mailbox12) & BIT_0)
 			ha->flags.secure_adapter = 1;
 		ql_log(ql_log_info, vha, 0xffff, "Secure Adapter: %s\n",
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 33776330956c..c47c38e099ff 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -4663,7 +4663,8 @@ qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp)
 	/* If possible, enable MSI-X. */
 	if (ql2xenablemsix == 0 || (!IS_QLA2432(ha) && !IS_QLA2532(ha) &&
 	    !IS_QLA8432(ha) && !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha) &&
-	    !IS_QLAFX00(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha)))
+	    !IS_QLAFX00(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha) &&
+	    !IS_QLA29XX(ha)))
 		goto skip_msi;
 
 	if (ql2xenablemsix == 2)
@@ -4702,7 +4703,7 @@ qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp)
 
 	if (!IS_QLA24XX(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha) &&
 	    !IS_QLA8001(ha) && !IS_P3P_TYPE(ha) && !IS_QLAFX00(ha) &&
-	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		goto skip_msi;
 
 	ret = pci_alloc_irq_vectors(ha->pdev, 1, 1, PCI_IRQ_MSI);
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 72b1c28e4dae..c91d2b8bd08e 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -429,7 +429,8 @@ static void qla_init_base_qpair(struct scsi_qla_host *vha, struct req_que *req,
 	qla_cpu_update(rsp->qpair, raw_smp_processor_id());
 	ha->base_qpair->pdev = ha->pdev;
 
-	if (IS_QLA27XX(ha) || IS_QLA83XX(ha) || IS_QLA28XX(ha))
+	if (IS_QLA27XX(ha) || IS_QLA83XX(ha) || IS_QLA28XX(ha) ||
+	    IS_QLA29XX(ha))
 		ha->base_qpair->reqq_start_iocbs = qla_83xx_start_iocbs;
 }
 
@@ -2153,8 +2154,6 @@ qla2x00_iospace_config(struct qla_hw_data *ha)
 static int
 qla83xx_iospace_config(struct qla_hw_data *ha)
 {
-	uint16_t msix;
-
 	if (pci_request_selected_regions(ha->pdev, ha->bars,
 	    QLA2XXX_DRIVER_NAME)) {
 		ql_log_pci(ql_log_fatal, ha->pdev, 0x0117,
@@ -2203,10 +2202,18 @@ qla83xx_iospace_config(struct qla_hw_data *ha)
 	ha->msixbase = ioremap(pci_resource_start(ha->pdev, 2),
 			pci_resource_len(ha->pdev, 2));
 	if (ha->msixbase) {
+		int msix_cnt;
+
 		/* Read MSIX vector size of the board */
-		pci_read_config_word(ha->pdev,
-		    QLA_83XX_PCI_MSIX_CONTROL, &msix);
-		ha->msix_count = (msix & PCI_MSIX_FLAGS_QSIZE)  + 1;
+		msix_cnt = pci_msix_vec_count(ha->pdev);
+		if (msix_cnt <= 0) {
+			ql_log_pci(ql_log_warn, ha->pdev, 0x0120,
+				   "Failed to read MSI-X count (%d), falling back to base vectors.\n",
+				   msix_cnt);
+			goto mqiobase_exit;
+		}
+		ha->msix_count = msix_cnt;
+
 		/*
 		 * By default, driver uses at least two msix vectors
 		 * (default & rspq)
@@ -2796,6 +2803,20 @@ qla2x00_set_isp_flags(struct qla_hw_data *ha)
 		ha->device_type |= DT_T10_PI;
 		ha->fw_srisc_address = RISC_START_ADDRESS_2400;
 		break;
+	case PCI_DEVICE_ID_QLOGIC_ISP2099:
+	case PCI_DEVICE_ID_QLOGIC_ISP2299:
+	case PCI_DEVICE_ID_QLOGIC_ISP2091:
+	case PCI_DEVICE_ID_QLOGIC_ISP2291:
+		ha->isp_type |= DT_ISP2299;
+		ha->isp_type |= DT_ISP2099;
+		ha->isp_type |= DT_ISP2091;
+		ha->isp_type |= DT_ISP2291;
+		ha->device_type |= DT_ZIO_SUPPORTED;
+		ha->device_type |= DT_FWI2;
+		ha->device_type |= DT_IIDMA;
+		ha->device_type |= DT_T10_PI;
+		ha->fw_srisc_address = RISC_START_ADDRESS_2400;
+		break;
 	}
 
 	if (IS_QLA82XX(ha))
@@ -2803,8 +2824,8 @@ qla2x00_set_isp_flags(struct qla_hw_data *ha)
 	else {
 		/* Get adapter physical port no from interrupt pin register. */
 		pci_read_config_byte(ha->pdev, PCI_INTERRUPT_PIN, &ha->port_no);
-		if (IS_QLA25XX(ha) || IS_QLA2031(ha) ||
-		    IS_QLA27XX(ha) || IS_QLA28XX(ha))
+		if (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) ||
+		    IS_QLA28XX(ha) || IS_QLA29XX(ha))
 			ha->port_no--;
 		else
 			ha->port_no = !(ha->port_no & 1);
@@ -2936,7 +2957,11 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2081 ||
 	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2281 ||
 	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2089 ||
-	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2289) {
+	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2289 ||
+	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2099 ||
+	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2299 ||
+	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2091 ||
+	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2291) {
 		bars = pci_select_bars(pdev, IORESOURCE_MEM);
 		mem_only = 1;
 		ql_dbg_pci(ql_dbg_init, pdev, 0x0007,
@@ -2998,7 +3023,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	/* Set EEH reset type to fundamental if required by hba */
 	if (IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha) ||
-	    IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	    IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+	    IS_QLA29XX(ha))
 		pdev->needs_freset = 1;
 
 	ha->prev_topology = 0;
@@ -3195,6 +3221,22 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 		ha->flash_data_off = FARX_ACCESS_FLASH_DATA_28XX;
 		ha->nvram_conf_off = ~0;
 		ha->nvram_data_off = ~0;
+	} else if (IS_QLA29XX(ha)) {
+		ha->portnum = PCI_FUNC(ha->pdev->devfn);
+		ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
+		ha->mbx_count = MAILBOX_REGISTER_COUNT;
+		req_length = REQUEST_ENTRY_CNT_83XX;
+		rsp_length = RESPONSE_ENTRY_CNT_83XX;
+		ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
+		ha->init_cb_size = sizeof(struct mid_init_cb_81xx);
+		ha->gid_list_info_size = 8;
+		ha->optrom_size = OPTROM_SIZE_28XX;
+		ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
+		ha->isp_ops = &qla27xx_isp_ops;
+		ha->flash_conf_off = ~0;
+		ha->flash_data_off = ~0;
+		ha->nvram_conf_off = ~0;
+		ha->nvram_data_off = ~0;
 	}
 
 	ql_dbg_pci(ql_dbg_init, pdev, 0x001e,
@@ -3373,7 +3415,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 	rsp->rsp_q_in = &ha->iobase->isp24.rsp_q_in;
 	rsp->rsp_q_out = &ha->iobase->isp24.rsp_q_out;
 	if (ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha) ||
-	    IS_QLA28XX(ha)) {
+	    IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		req->req_q_in = &ha->mqiobase->isp25mq.req_q_in;
 		req->req_q_out = &ha->mqiobase->isp25mq.req_q_out;
 		rsp->rsp_q_in = &ha->mqiobase->isp25mq.rsp_q_in;
@@ -3898,7 +3940,7 @@ qla2x00_remove_one(struct pci_dev *pdev)
 		return;
 
 	if (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) ||
-	    IS_QLA28XX(ha)) {
+	    IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		if (ha->flags.fw_started)
 			qla2x00_abort_isp_cleanup(base_vha);
 	} else if (!IS_QLAFX00(ha)) {
@@ -4387,7 +4429,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 
 	/* Get consistent memory allocated for EX-INIT-CB. */
 	if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) ||
-	    IS_QLA28XX(ha)) {
+	    IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		ha->ex_init_cb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
 		    &ha->ex_init_cb_dma);
 		if (!ha->ex_init_cb)
@@ -4397,7 +4439,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 	}
 
 	/* Get consistent memory allocated for Special Features-CB. */
-	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+	if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		ha->sf_init_cb = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL,
 						&ha->sf_init_cb_dma);
 		if (!ha->sf_init_cb)
@@ -8155,6 +8197,10 @@ static const struct pci_device_id qla2xxx_pci_tbl[] = {
 	{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2281) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2089) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2289) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2099) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2299) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2091) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2291) },
 	{ 0 },
 };
 MODULE_DEVICE_TABLE(pci, qla2xxx_pci_tbl);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 02/88] scsi: qla2xxx: Add flash read/write interface for 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 01/88] scsi: qla2xxx: Add 29xx series PCI device ID support Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  8:24   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 03/88] scsi: qla2xxx: Add NVRAM config support for 29xx adapters Nilesh Javali
                   ` (85 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

From: Manish Rangankar <mrangankar@marvell.com>

The 29xx series uses a different flash access mechanism than
earlier adapters.  Add the mailbox wrappers and qla_sup helpers
needed for flash read and write operations, including the
necessary hooks in isp_ops so that the existing flash
infrastructure can drive the new hardware.

Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_def.h |  20 +
 drivers/scsi/qla2xxx/qla_fw.h  |  54 +++
 drivers/scsi/qla2xxx/qla_gbl.h |  15 +-
 drivers/scsi/qla2xxx/qla_mbx.c | 144 +++++++
 drivers/scsi/qla2xxx/qla_os.c  |  18 +-
 drivers/scsi/qla2xxx/qla_sup.c | 662 ++++++++++++++++++++++++++++++++-
 6 files changed, 900 insertions(+), 13 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 6337a056b149..b5ad6ed3d5d1 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -1278,6 +1278,7 @@ static inline bool qla2xxx_is_valid_mbs(unsigned int mbs)
 #define MBC_LOAD_RISC_RAM		9	/* Load RAM command. */
 #define MBC_DUMP_RISC_RAM		0xa	/* Dump RAM command. */
 #define MBC_SECURE_FLASH_UPDATE		0xa	/* Secure Flash Update(28xx) */
+#define MBC_RD_WR_FLASH			0xa	/* Read/write Dword/block Flash(29xx) */
 #define MBC_LOAD_RISC_RAM_EXTENDED	0xb	/* Load RAM extended. */
 #define MBC_DUMP_RISC_RAM_EXTENDED	0xc	/* Dump RAM extended. */
 #define MBC_WRITE_RAM_WORD_EXTENDED	0xd	/* Write RAM word extended */
@@ -4170,6 +4171,7 @@ struct qla_hw_data {
 #define EEH_FLUSH_RDY  1
 #define EEH_FLUSH_DONE 2
 		uint32_t	secure_mcu:1;
+		uint32_t	valid_flt:1;
 	} flags;
 
 	uint16_t max_exchg;
@@ -4498,6 +4500,8 @@ struct qla_hw_data {
 
 	struct qla_flt_header *flt;
 	dma_addr_t	flt_dma;
+	struct qla_flash_layout *flt_data;
+	uint32_t	fw_dump_tmplt_len;
 
 #define XGMAC_DATA_SIZE	4096
 	void		*xgmac_data;
@@ -4722,6 +4726,9 @@ struct qla_hw_data {
 	uint32_t	fdt_protect_sec_cmd;
 	uint32_t	fdt_wrt_sts_reg_cmd;
 
+#define QLA_SEGMENT_LENGTH      0x25000
+	uint32_t        flt_segment_length;
+
 	struct {
 		uint32_t	flt_region_flt;
 		uint32_t	flt_region_fdt;
@@ -5348,6 +5355,14 @@ static inline bool qla_vha_mark_busy(scsi_qla_host_t *vha)
 /*
  * Flash support definitions
  */
+#define check_and_set_mbc_bits(bopt, dopt, bit_to_check, bit_to_set) {	\
+	if (bopt & bit_to_check)			\
+		dopt |= bit_to_set;			\
+}
+
+#define SET_FW_BIT(__opts, bit) ((__opts) |= (bit))
+#define CLEAR_FW_BIT(__opts, bit) ((__opts) &= ~(bit))
+
 #define OPTROM_SIZE_2300	0x20000
 #define OPTROM_SIZE_2322	0x100000
 #define OPTROM_SIZE_24XX	0x100000
@@ -5612,4 +5627,9 @@ struct ql_vnd_tgt_stats_resp {
 	(!_fcport || IS_SESSION_DELETED(_fcport) || atomic_read(&_fcport->state) != FCS_ONLINE || \
 	!_fcport->vha->hw->flags.fw_started)
 
+#define is_flash_read(_opt)	\
+	(!(_opt & BIT_9) && !(_opt & BIT_6))
+
+#define is_flash_write(_opt)	\
+	(!(_opt & BIT_9) && (_opt & BIT_6))
 #endif
diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index f307beed9d29..d27d09964a24 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -1695,6 +1695,10 @@ struct qla_flt_location {
 #define FLT_REG_NVME_PARAMS_PRI_28XX	0x14E
 #define FLT_REG_NVME_PARAMS_SEC_28XX	0x179
 
+/* 29xx */
+#define FLT_REG_MINI_FLT		0x201
+#define FLT_REG_FW_DUMP_TMPLT		0x1A0
+
 struct qla_flt_region {
 	__le16	code;
 	uint8_t attribute;
@@ -1716,6 +1720,56 @@ struct qla_flt_header {
 #define FLT_MAX_REGIONS		0xFF
 #define FLT_REGIONS_SIZE	(FLT_REGION_SIZE * FLT_MAX_REGIONS)
 
+/* 29xx */
+#define FLT_HDR_VERSION		0x2
+
+struct qla_flt_region_header {
+	__le32	signature;
+	__le32	version;
+	__le32	length;
+	__le32	checksum;
+	__le16	region_count;
+	__le16	region_size;
+	__le32	segment_size;
+	__le32	res3;
+	__le32	res4;
+	__le32	res5;
+	__le32	res6;
+	__le32	res7;
+	__le32	res8;
+	__le32	res9;
+	__le32	res10;
+	__le32	res11;
+	__le32	res12;
+};
+
+struct qla_flt_region_data {
+	__le16	region_code;
+	__le16	reserved;
+	__le32	attribute;
+	__le32	image_length;
+	__le32	mbi_offset;
+	__le32	version;
+	__le32	card_type;
+	__le32	chip_revision;
+	__le32	res4;
+	__le32	res5;
+	__le32	res6;
+	__le32	res7;
+	__le32	res8;
+	__le32	res9;
+	__le32	res10;
+	__le32	res11;
+	__le32	res12;
+};
+
+struct qla_flash_layout {
+	struct qla_flt_region_header flt_header;
+	struct qla_flt_region_data region[];
+};
+
+#define FLT_DATA_MAX_REGIONS	0xFF
+
 /* Flash NPIV Configuration Table ********************************************/
 
 struct qla_npiv_header {
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 9e328c235e39..a032dd047a8f 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -580,6 +580,12 @@ extern int qla2xxx_read_remote_register(scsi_qla_host_t *, uint32_t,
 extern int qla2xxx_write_remote_register(scsi_qla_host_t *, uint32_t,
     uint32_t);
 void qla_no_op_mb(struct scsi_qla_host *vha);
+extern int qla29xx_flash_block_read(scsi_qla_host_t *vha, dma_addr_t req_dma,
+				    uint32_t flash_addr, uint32_t flash_size,
+				    uint16_t reg_code, uint16_t opt);
+extern int qla29xx_flash_block_write(scsi_qla_host_t *vha, dma_addr_t req_dma,
+				     uint32_t flash_addr, uint32_t flash_size,
+				     uint16_t reg_code, uint16_t opt);
 
 /*
  * Global Function Prototypes in qla_isr.c source file.
@@ -682,7 +688,14 @@ struct purex_item *qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha,
 	void **pkt, struct rsp_que **rsp, bool is_purls, bool byte_order);
 int qla_mailbox_passthru(scsi_qla_host_t *vha, uint16_t *mbx_in,
 			 uint16_t *mbx_out);
-
+void *qla29xx_read_optrom_data(struct scsi_qla_host *vha,
+				       uint16_t reg_code, uint16_t opts,
+				       void *buf, uint32_t offset,
+				       uint32_t length);
+extern int qla29xx_write_optrom_data(struct scsi_qla_host *vha,
+				     uint16_t reg_code, uint16_t opts,
+				     void *buf, uint32_t offset,
+				     uint32_t length);
 /*
  * Global Function Prototypes in qla_dbg.c source file.
  */
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 44e310f1a370..2d052f870b2b 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -7254,3 +7254,147 @@ int qla_mpipt_validate_fw(scsi_qla_host_t *vha, u16 img_idx, uint16_t *state)
 
 	return rval;
 }
+
+int qla29xx_flash_block_read(scsi_qla_host_t *vha, dma_addr_t req_dma,
+			     uint32_t flash_addr, uint32_t flash_size,
+			     uint16_t reg_code, uint16_t opt)
+{
+	mbx_cmd_t mc;
+	mbx_cmd_t *mcp = &mc;
+	int rval = 0;
+
+	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1067,
+			"Entered %s options 0x%x.\n", __func__, opt);
+
+	memset(mcp->mb, 0, sizeof(mcp->mb));
+
+	if (!is_flash_read(opt)) {
+		ql_log(ql_log_info, vha, 0x1068,
+				"%s: Invalid flash option 0x%x.\n", __func__, opt);
+		return -EINVAL;
+	}
+
+	mcp->mb[0] = MBC_RD_WR_FLASH;
+
+	/* mailbox option field :
+	 *      TIM img or Img  : BIT_15 (1/0)
+	 *       Last seg img   : BIT_11 (1)
+	 *      First seg img   : BIT_10  (1)
+	 *      dword or block  : BIT_9  (1/0)
+	 *     flash MBR update : BIT_8  (1/0)
+	 * Secure or non-secure : BIT_7  (1/0)
+	 *        Write or Read : BIT_6  (1/0)
+	 *       Last block img : BIT_5  (1)
+	 *      First block img : BIT_4  (1)
+	 *         Request type : (BIT3 - BIT_0)
+	 *                        - Normal
+	 *                        - Normal, Force Sema
+	 *                        - Initialize
+	 *                        - Initialize, Force sema
+	 *                        - Abort secured update
+	 */
+	mcp->mb[1] = opt;
+	mcp->mb[2] = reg_code;
+
+	mcp->mb[3] = MSW(flash_size);
+	mcp->mb[4] = LSW(flash_size);
+
+	mcp->mb[5] = MSW(req_dma);
+	mcp->mb[6] = LSW(req_dma);
+	mcp->mb[7] = MSW(MSD(req_dma));
+	mcp->mb[8] = LSW(MSD(req_dma));
+
+	mcp->mb[9] = MSW(flash_addr);
+	mcp->mb[10] = LSW(flash_addr);
+
+	mcp->out_mb =
+		MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+	mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+	mcp->tov = MBX_TOV_SECONDS;
+	mcp->flags = 0;
+
+	rval = qla2x00_mailbox_command(vha, mcp);
+	if (rval != QLA_SUCCESS) {
+		ql_dbg(ql_dbg_mbx, vha, 0x103f,
+				"Failed=%x mb=(0x%x,0x%x,0x%x,0x%x).\n",
+				rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3]);
+	} else {
+		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1043,
+				"Done %s mb=(0x%x,0x%x,0x%x).\n", __func__,
+				mcp->mb[0], mcp->mb[1],  mcp->mb[2]);
+	}
+
+	return rval;
+}
+
+int qla29xx_flash_block_write(scsi_qla_host_t *vha, dma_addr_t req_dma,
+			      uint32_t flash_addr, uint32_t flash_size,
+			      uint16_t reg_code, uint16_t opt)
+{
+	mbx_cmd_t mc;
+	mbx_cmd_t *mcp = &mc;
+	int rval = 0;
+
+	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1069,
+			"Entered %s options 0x%x.\n", __func__, opt);
+
+	memset(mcp->mb, 0, sizeof(mcp->mb));
+
+	if (!is_flash_write(opt)) {
+		ql_log(ql_log_info, vha, 0x106a,
+				"%s: Invalid flash option 0x%x.\n", __func__, opt);
+		return -EINVAL;
+	}
+
+	mcp->mb[0] = MBC_RD_WR_FLASH;
+
+	/* mailbox option field :
+	 *      TIM img or Img  : BIT_15 (1/0)
+	 *       Last seg img   : BIT_11 (1)
+	 *      First seg img   : BIT_10  (1)
+	 *      dword or block  : BIT_9  (1/0)
+	 *     flash MBR update : BIT_8  (1/0)
+	 * Secure or non-secure : BIT_7  (1/0)
+	 *        Write or Read : BIT_6  (1/0)
+	 *       Last block img : BIT_5  (1)
+	 *      First block img : BIT_4  (1)
+	 *         Request type : (BIT3 - BIT_0)
+	 *                        - Normal
+	 *                        - Normal, Force Sema
+	 *                        - Initialize
+	 *                        - Initialize, Force sema
+	 *                        - Abort secured update
+	 */
+	mcp->mb[1] = opt;
+	mcp->mb[2] = reg_code;
+
+	mcp->mb[3] = MSW(flash_size);
+	mcp->mb[4] = LSW(flash_size);
+
+	mcp->mb[5] = MSW(req_dma);
+	mcp->mb[6] = LSW(req_dma);
+	mcp->mb[7] = MSW(MSD(req_dma));
+	mcp->mb[8] = LSW(MSD(req_dma));
+
+	mcp->mb[9] = MSW(flash_addr);
+	mcp->mb[10] = LSW(flash_addr);
+
+	mcp->out_mb =
+		MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+	mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+	mcp->tov = MBX_TOV_SECONDS;
+	mcp->flags = 0;
+
+	rval = qla2x00_mailbox_command(vha, mcp);
+	if (rval != QLA_SUCCESS) {
+		ql_dbg(ql_dbg_mbx, vha, 0x110a,
+				"Failed=%x mb=(0x%x,0x%x,0x%x,0x%x).\n",
+				rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3]);
+	} else {
+		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110b,
+				"Done %s mb=(0x%x,0x%x,0x%x).\n", __func__,
+				mcp->mb[0], mcp->mb[1],  mcp->mb[2]);
+	}
+
+	return rval;
+}
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index c91d2b8bd08e..ebb97329e6ea 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -3237,6 +3237,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 		ha->flash_data_off = ~0;
 		ha->nvram_conf_off = ~0;
 		ha->nvram_data_off = ~0;
+		ha->flt_segment_length = QLA_SEGMENT_LENGTH;
 	}
 
 	ql_dbg_pci(ql_dbg_init, pdev, 0x001e,
@@ -4488,6 +4489,14 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 		goto fail_flt_buffer;
 	}
 
+	ha->flt_data = vzalloc(sizeof(struct qla_flash_layout) +
+			(sizeof(struct qla_flt_region_data) * FLT_MAX_REGIONS));
+	if (!ha->flt_data) {
+		ql_dbg_pci(ql_dbg_init, ha->pdev, 0x001a,
+			   "Unable to allocate memory for mini FLT data.\n");
+		goto fail_flt;
+	}
+
 	/* allocate the purex dma pool */
 	ha->purex_dma_pool = dma_pool_create(name, &ha->pdev->dev,
 	    ELS_MAX_PAYLOAD, 8, 0);
@@ -4495,7 +4504,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 	if (!ha->purex_dma_pool) {
 		ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011b,
 		    "Unable to allocate purex_dma_pool.\n");
-		goto fail_flt;
+		goto fail_flt_data;
 	}
 
 	ha->elsrej.size = sizeof(struct fc_els_ls_rjt) + 16;
@@ -4528,6 +4537,9 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 			  ha->elsrej.c, ha->elsrej.cdma);
 fail_elsrej:
 	dma_pool_destroy(ha->purex_dma_pool);
+fail_flt_data:
+	vfree(ha->flt_data);
+	ha->flt_data = NULL;
 fail_flt:
 	dma_free_coherent(&ha->pdev->dev, sizeof(struct qla_flt_header) + FLT_REGIONS_SIZE,
 	    ha->flt, ha->flt_dma);
@@ -4963,6 +4975,10 @@ qla2x00_mem_free(struct qla_hw_data *ha)
 	ha->flt = NULL;
 	ha->flt_dma = 0;
 
+	if (ha->flt_data)
+		vfree(ha->flt_data);
+	ha->flt_data = NULL;
+
 	if (ha->ms_iocb)
 		dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
 	ha->ms_iocb = NULL;
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
index b6c36a8a2d60..6b9938d1a750 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -10,6 +10,539 @@
 #include <linux/vmalloc.h>
 #include <linux/uaccess.h>
 
+/**
+ * qla29xx_get_flt_layout - Retrieve the flash layout table (FLT) for QLA29xx.
+ * @vha: Pointer to SCSI QLogic host structure.
+ *
+ * This function reads and validates the FLT structure from the flash memory.
+ * It extracts region information and updates the hardware data structure.
+ */
+static void
+qla29xx_get_flt_layout(scsi_qla_host_t *vha)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_flash_layout *flt_layout = ha->flt_data;
+	struct qla_flt_region_header *flt_header = &flt_layout->flt_header;
+	struct qla_flt_region_data *region = &flt_layout->region[0];
+	uint32_t flt_options = 0;
+	uint32_t flt_size;
+	uint32_t cnt, chksum;
+	uint16_t reg_cnt, i;
+	__le32 *wptr;
+	void *buf = NULL;
+
+	flt_size = sizeof(struct qla_flt_region_header);
+	wptr = (__force __le32 *)ha->flt_data;
+
+	buf = qla29xx_read_optrom_data(vha, FLT_REG_MINI_FLT, flt_options,
+				       ha->flt_data, 0, flt_size);
+	if (!buf) {
+		ql_log(ql_log_warn, vha, 0x007b,
+		    "Failed to read FLT information.\n");
+		goto exit_flt;
+	}
+
+	ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x0111,
+	       "Contents of Flash Layout (0x%x):\n", flt_size);
+	ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0112,
+		       ha->flt_data, flt_size);
+
+	/* check flt version checksum and other info */
+	if ((le32_to_cpu(*wptr) == 0xffffffff) ||
+	    flt_header->version != cpu_to_le32(FLT_HDR_VERSION)) {
+		ql_log(ql_log_warn, vha, 0x007c,
+		    "Unsupported FLT detected: version=0x%x length=0x%x signature=0x%x region_count=0x%x checksum=0x%x.\n",
+		    le32_to_cpu(flt_header->version),
+		    le32_to_cpu(flt_header->length),
+		    le32_to_cpu(flt_header->signature),
+		    le16_to_cpu(flt_header->region_count),
+		    le32_to_cpu(flt_header->checksum));
+		goto exit_flt;
+	}
+
+	reg_cnt = le16_to_cpu(flt_header->region_count);
+	if (reg_cnt > FLT_MAX_REGIONS)
+		goto exit_flt;
+
+	if (le16_to_cpu(flt_header->region_size) !=
+	    sizeof(struct qla_flt_region_data)) {
+		ql_log(ql_log_warn, vha, 0x0082,
+		    "Unsupported FLT region size 0x%x (expected 0x%zx).\n",
+		    le16_to_cpu(flt_header->region_size),
+		    sizeof(struct qla_flt_region_data));
+		goto exit_flt;
+	}
+
+	flt_size = sizeof(struct qla_flash_layout) +
+		   (reg_cnt * le16_to_cpu(flt_header->region_size));
+	if (flt_size > sizeof(struct qla_flash_layout) +
+	    sizeof(struct qla_flt_region_data) * FLT_DATA_MAX_REGIONS) {
+		ql_log(ql_log_warn, vha, 0x007d,
+		    "FLT size 0x%x exceeds buffer, region_size=0x%x.\n",
+		    flt_size, le16_to_cpu(flt_header->region_size));
+		goto exit_flt;
+	}
+
+	buf = qla29xx_read_optrom_data(vha, FLT_REG_MINI_FLT, flt_options,
+				       ha->flt_data, 0, flt_size);
+	if (!buf) {
+		ql_log(ql_log_warn, vha, 0x007b,
+		    "Failed to read FLT information.\n");
+		goto exit_flt;
+	}
+
+	cnt = le32_to_cpu(flt_header->length) / sizeof(*wptr);
+	if (cnt > flt_size / sizeof(*wptr)) {
+		ql_log(ql_log_warn, vha, 0x007e,
+		    "FLT length 0x%x exceeds read size 0x%x, rejecting.\n",
+		    le32_to_cpu(flt_header->length), flt_size);
+		goto exit_flt;
+	}
+	for (chksum = 0; cnt--; wptr++)
+		chksum += le32_to_cpu(*wptr);
+	if (chksum) {
+		ql_log(ql_log_fatal, vha, 0x007f,
+		    "Inconsistent FLT detected: version=0x%x length=0x%x signature=0x%x checksum=0x%x.\n",
+		    le32_to_cpu(flt_header->version),
+		    le32_to_cpu(flt_header->length),
+		    le32_to_cpu(flt_header->signature),
+		    le32_to_cpu(flt_header->checksum));
+		goto exit_flt;
+	}
+
+	ql_dbg(ql_dbg_init, vha, 0x007f,
+		"FLT detected: version=0x%08x length=0x%08x signature=0x%08x region_count=0x%04x region_len=0x%04x segment_len=0x%08x checksum=0x%08x.\n",
+		le32_to_cpu(flt_header->version),
+		le32_to_cpu(flt_header->length),
+		le32_to_cpu(flt_header->signature),
+		le16_to_cpu(flt_header->region_count),
+		le16_to_cpu(flt_header->region_size),
+		le32_to_cpu(flt_header->segment_size),
+		le32_to_cpu(flt_header->checksum));
+
+	for (i = 0; reg_cnt; i++, reg_cnt--) {
+		region = &flt_layout->region[i];
+		ql_dbg(ql_dbg_init, vha, 0x0080,
+		    "FLT[%03x]: len=0x%08x version=0x%08x attr=0x%02x.\n",
+		    le16_to_cpu(region->region_code),
+		    le32_to_cpu(region->image_length),
+		    le32_to_cpu(region->version),
+		    le32_to_cpu(region->attribute));
+		if (le16_to_cpu(region->region_code) == FLT_REG_FW_DUMP_TMPLT) {
+			ql_dbg(ql_dbg_init, vha, 0x0080,
+				"%s: %d: Found FW dump template", __func__, __LINE__);
+			ha->fw_dump_tmplt_len = le32_to_cpu(region->image_length);
+		}
+	}
+
+	if (le32_to_cpu(flt_header->segment_size) >= sizeof(uint32_t)) {
+		ha->flt_segment_length = le32_to_cpu(flt_header->segment_size);
+	} else {
+		ql_log(ql_log_warn, vha, 0x0081,
+		    "FLT segment size 0x%x too small, using default 0x%x.\n",
+		    le32_to_cpu(flt_header->segment_size), QLA_SEGMENT_LENGTH);
+		ha->flt_segment_length = QLA_SEGMENT_LENGTH;
+	}
+	ha->flags.valid_flt = true;
+	return;
+
+exit_flt:
+	ha->flt_segment_length = QLA_SEGMENT_LENGTH;
+}
+
+/**
+ * qla29xx_get_fdt_info - Retrieve flash descriptor table (FDT) information.
+ * @vha: Pointer to SCSI QLogic host structure.
+ *
+ * This function reads and validates the FDT structure from the flash memory.
+ * It extracts manufacturer and device-specific information and updates
+ * the hardware data structure.
+ */
+static void
+qla29xx_get_fdt_info(scsi_qla_host_t *vha)
+{
+#define FLASH_BLK_SIZE_4K	0x1000
+#define FLASH_BLK_SIZE_32K	0x8000
+#define FLASH_BLK_SIZE_64K	0x10000
+	struct qla_hw_data *ha = vha->hw;
+	struct req_que *req = ha->req_q_map[0];
+	uint16_t cnt, chksum;
+	__le16 *wptr = (__force __le16 *)req->ring;
+	struct qla_fdt_layout *fdt = (struct qla_fdt_layout *)req->ring;
+	uint32_t fdt_options = 0;
+	void *buf = NULL;
+
+	buf = qla29xx_read_optrom_data(vha, FLT_REG_FDT, fdt_options,
+					fdt, 0, sizeof(*fdt));
+	if (!buf) {
+		ql_log(ql_log_warn, vha, 0x0047,
+		    "Failed to read FLT information.\n");
+		return;
+	}
+
+	if (le16_to_cpu(*wptr) == 0xffff)
+		return;
+	if (memcmp(fdt->sig, "QLID", 4))
+		return;
+
+	for (cnt = 0, chksum = 0; cnt < sizeof(*fdt) >> 1; cnt++, wptr++)
+		chksum += le16_to_cpu(*wptr);
+	if (chksum) {
+		ql_dbg(ql_dbg_init, vha, 0x004c,
+			"Inconsistent FDT detected: checksum=0x%x id=%c version0x%x.\n",
+			chksum, fdt->sig[0], le16_to_cpu(fdt->version));
+		ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0113,
+		    fdt, sizeof(*fdt));
+		return;
+	}
+}
+
+/**
+ * qla29xx_get_flash_region - Retrieve flash region information for QLA29xx adapters.
+ * @vha: Pointer to SCSI QLogic host structure.
+ * @code: Region code to identify the flash region.
+ * @region: Pointer to store the retrieved flash region information.
+ *
+ * This function retrieves the flash region information for QLA29xx adapters
+ * based on the specified region code.
+ *
+ * Returns QLA_SUCCESS on success or QLA_FUNCTION_FAILED on failure.
+ */
+static int
+qla29xx_get_flash_region(struct scsi_qla_host *vha, uint32_t code,
+			 struct qla_flt_region_data *region)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_flash_layout *flt = ha->flt_data;
+	struct qla_flt_region_header *flt_hdr = &flt->flt_header;
+	struct qla_flt_region_data *flt_reg = &flt->region[0];
+	uint16_t cnt;
+	int rval = QLA_FUNCTION_FAILED;
+
+	if (!ha->flt_data || !ha->flags.valid_flt)
+		return QLA_FUNCTION_FAILED;
+
+	cnt = le16_to_cpu(flt_hdr->region_count);
+	if (cnt > FLT_MAX_REGIONS)
+		return QLA_FUNCTION_FAILED;
+	/*
+	 * flt_reg++ strides by sizeof(struct qla_flt_region_data). This is
+	 * safe because valid_flt is set only after qla29xx_get_flt_layout()
+	 * has verified the firmware region_size equals that struct size.
+	 */
+	for (; cnt; cnt--, flt_reg++) {
+		if (le16_to_cpu(flt_reg->region_code) == code) {
+			memcpy((uint8_t *)region, flt_reg,
+			    sizeof(struct qla_flt_region_data));
+			rval = QLA_SUCCESS;
+			break;
+		}
+	}
+
+	return rval;
+}
+
+/**
+ * set_segment_bits - Set segment-related bits in the options field.
+ * @options: Pointer to the options field.
+ * @segment_index: Index of the current segment.
+ * @total: Total number of segments.
+ *
+ * This function sets the appropriate bits in the options field to indicate
+ * whether the current segment is the first, last, or a single segment.
+ */
+static void set_segment_bits(uint16_t *options, int segment_index, int total)
+{
+	/* - Single segment complete image.
+	 * - 1st Segment of an image.
+	 * - Last segment of an image.
+	 */
+	if (total == 1)
+		*options |= (1 << 10) | (1 << 11);
+	else if (segment_index == 0)
+		*options |= (1 << 10);
+	else if (segment_index == total - 1)
+		*options |= (1 << 11);
+}
+
+/**
+ * set_chunk_bits - Set chunk-related bits in the options field.
+ * @options: Pointer to the options field.
+ * @count: Index of the current chunk.
+ * @total: Total number of chunks.
+ *
+ * This function sets the appropriate bits in the options field to indicate
+ * whether the current chunk is the first, last, or a single chunk.
+ */
+static void set_chunk_bits(uint16_t *options, int count, int total)
+{
+	/* - Single chunk complete segment
+	 * - 1st chunk of a segment
+	 * - Last chunk of a segment
+	 */
+	if (total == 1)
+		*options |= (1 << 4) | (1 << 5);
+	else if (count == 0)
+		*options |= (1 << 4);
+	else if (count == total - 1)
+		*options |= (1 << 5);
+}
+
+/**
+ * qla29xx_write_optrom_data - Write data to the optrom for QLA29xx adapters.
+ * @vha: Pointer to SCSI QLogic host structure.
+ * @reg_code: Region code to write to.
+ * @opts: Options for the write operation.
+ * @buf: Buffer containing the data to write.
+ * @offset: Offset within the region to start writing.
+ * @length: Length of data to write.
+ *
+ * This function writes data to the specified optrom region for QLA29xx adapters.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int
+qla29xx_write_optrom_data(struct scsi_qla_host *vha, uint16_t reg_code,
+			 uint16_t opts, void *buf, uint32_t offset,
+			 uint32_t length)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_flt_region_data region;
+	dma_addr_t optrom_dma;
+	uint32_t faddr, left, burst;
+	uint32_t img_len, seg_dlen;
+	uint32_t region_len, region_dlen;
+	void *optrom;
+	uint8_t *pbuf;
+	int rval = -EINVAL;
+	uint16_t total_segments, segment_index = 0;
+	uint16_t chunk_index = 0, chunk_count = 0;
+
+	memset(&region, 0, sizeof(region));
+
+	optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
+				    &optrom_dma, GFP_KERNEL);
+	if (!optrom) {
+		ql_log(ql_log_warn, vha, 0x0090,
+		    "Unable to allocate memory for optrom burst read (%x KB).\n",
+		    OPTROM_BURST_SIZE / 1024);
+		return -ENOMEM;
+	}
+
+	if (ha->flags.valid_flt && length == 0) {
+		/* Get image length and segment length from FLT */
+		rval = qla29xx_get_flash_region(vha, reg_code, &region);
+		if (rval != QLA_SUCCESS) {
+			ql_log(ql_log_warn, vha, 0x0092,
+				"Invalid address %x - not a region start address\n",
+				reg_code);
+			goto free_buf;
+		}
+		img_len = le32_to_cpu(region.image_length);
+	} else {
+		img_len = length;
+	}
+
+	seg_dlen = ha->flt_segment_length >> 2;
+	region_len = (length > 0) ? length : img_len;
+	region_dlen = (region_len >> 2);
+	total_segments = (region_dlen + seg_dlen - 1) / seg_dlen;
+
+	faddr = offset >> 2;
+	left = region_dlen;
+	burst = OPTROM_BURST_DWORDS;
+	pbuf = buf;
+
+	while (region_dlen > 0) {
+		uint32_t segment_size, total_chunks;
+		uint16_t options = 0;
+
+		segment_size = (region_dlen > seg_dlen) ? seg_dlen : region_dlen;
+		total_chunks = (segment_size + OPTROM_BURST_DWORDS - 1) /
+				OPTROM_BURST_DWORDS;
+
+		burst = OPTROM_BURST_DWORDS;
+		if (burst > left)
+			burst = left;
+		if (burst > segment_size - chunk_index * OPTROM_BURST_DWORDS)
+			burst = segment_size - chunk_index * OPTROM_BURST_DWORDS;
+
+		set_segment_bits(&options, segment_index, total_segments);
+		set_chunk_bits(&options, chunk_index, total_chunks);
+
+		/* flash block write operations */
+		SET_FW_BIT(options, BIT_6);
+		CLEAR_FW_BIT(options, BIT_9);
+
+		check_and_set_mbc_bits(opts, options, BIT_15, BIT_15);
+		check_and_set_mbc_bits(opts, options, BIT_7, BIT_7);
+
+		if (segment_index == total_segments - 1 &&
+		    chunk_index == total_chunks - 1)
+			check_and_set_mbc_bits(opts, options, BIT_8, BIT_8);
+
+		memcpy(optrom, pbuf, burst * 4);
+
+		/* faddr is offset relative to region code */
+		rval = qla29xx_flash_block_write(vha, optrom_dma,
+				faddr, burst, reg_code, options);
+		if (rval) {
+			ql_log(ql_log_warn, vha, 0x0095,
+			    "Unable to burst-write optrom segment (%x/%x/%llx).\n",
+			    rval, faddr, (unsigned long long)optrom_dma);
+
+			dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
+			    optrom, optrom_dma);
+			goto exit_write;
+		}
+
+		left -= burst;
+		faddr += burst;
+		pbuf += burst * 4;
+		chunk_index++;
+		chunk_count++;
+		if (chunk_index >= total_chunks) {
+			chunk_index = 0;
+			segment_index++;
+			region_dlen -= segment_size;
+		}
+	}
+
+free_buf:
+	dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom,
+	    optrom_dma);
+	return rval;
+
+exit_write:
+	return rval;
+}
+
+/**
+ * qla29xx_read_optrom_data - Read data from the optrom for QLA29xx adapters.
+ * @vha: Pointer to SCSI QLogic host structure.
+ * @reg_code: Region code to read from.
+ * @opts: Options for the read operation.
+ * @buf: Buffer to store the read data.
+ * @offset: Offset within the region to start reading.
+ * @length: Length of data to read.
+ *
+ * This function reads data from the specified optrom region for QLA29xx adapters.
+ *
+ * Returns a pointer to the buffer on success or NULL on failure.
+ */
+void *
+qla29xx_read_optrom_data(struct scsi_qla_host *vha, uint16_t reg_code,
+			 uint16_t opts, void *buf, uint32_t offset,
+			 uint32_t length)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_flt_region_data region;
+	dma_addr_t optrom_dma;
+	uint32_t faddr, left, burst;
+	uint32_t img_len, seg_dlen;
+	uint32_t region_len, region_dlen;
+	void *optrom;
+	uint8_t *pbuf;
+	uint16_t total_segments, segment_index = 0;
+	uint16_t chunk_index = 0, chunk_count = 0;
+	int rval;
+
+	memset(&region, 0, sizeof(region));
+
+	optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
+				    &optrom_dma, GFP_KERNEL);
+	if (!optrom) {
+		ql_log(ql_log_warn, vha, 0x0093,
+		    "Unable to allocate memory for optrom burst read (%x KB).\n",
+		    OPTROM_BURST_SIZE / 1024);
+		return NULL;
+	}
+
+	if (ha->flags.valid_flt && length == 0) {
+		/* Get image length and segment length from FLT */
+		rval = qla29xx_get_flash_region(vha, reg_code, &region);
+		if (rval != QLA_SUCCESS) {
+			ql_log(ql_log_warn, vha, 0x7033,
+				"Invalid address %x - not a region start address\n",
+				reg_code);
+			goto free_buf;
+		}
+		img_len = le32_to_cpu(region.image_length);
+	} else {
+		img_len = length;
+	}
+
+	seg_dlen = ha->flt_segment_length >> 2;
+	region_len = (length > 0) ? length : img_len;
+	region_dlen = (region_len >> 2);
+	total_segments = (region_dlen + seg_dlen - 1) / seg_dlen;
+
+	faddr = offset >> 2;
+	left = region_dlen;
+	burst = OPTROM_BURST_DWORDS;
+	pbuf = buf;
+
+	ql_log(ql_log_info, vha, 0x0096,
+	       "Reg[0x%x]: options=0x%x length=0x%x offset=0x%x segments=%u\n",
+		reg_code, opts, region_len, offset, total_segments);
+
+	while (region_dlen > 0) {
+		uint32_t segment_size, total_chunks;
+		uint16_t options = 0;
+
+		segment_size = (region_dlen > seg_dlen) ? seg_dlen : region_dlen;
+		total_chunks = (segment_size + OPTROM_BURST_DWORDS - 1) /
+				OPTROM_BURST_DWORDS;
+
+		burst = OPTROM_BURST_DWORDS;
+		if (burst > left)
+			burst = left;
+		if (burst > segment_size - chunk_index * OPTROM_BURST_DWORDS)
+			burst = segment_size - chunk_index * OPTROM_BURST_DWORDS;
+
+		set_segment_bits(&options, segment_index, total_segments);
+		set_chunk_bits(&options, chunk_index, total_chunks);
+
+		/* flash block read operations */
+		CLEAR_FW_BIT(options, BIT_9);
+		CLEAR_FW_BIT(options, BIT_6);
+
+		options |= opts;
+
+		/* faddr is offset relative to region code */
+		rval = qla29xx_flash_block_read(vha, optrom_dma,
+				faddr, burst, reg_code, options);
+		if (rval) {
+			ql_log(ql_log_warn, vha, 0x0097,
+			    "Unable to burst-read optrom segment (%x/%x/%llx).\n",
+			    rval, faddr, (unsigned long long)optrom_dma);
+			goto free_buf;
+		}
+
+		memcpy(pbuf, optrom, burst * 4);
+
+		left -= burst;
+		faddr += burst;
+		pbuf += burst * 4;
+		chunk_index++;
+		chunk_count++;
+		if (chunk_index >= total_chunks) {
+			chunk_index = 0;
+			segment_index++;
+			region_dlen -= segment_size;
+		}
+	}
+
+	dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom,
+	    optrom_dma);
+	return buf;
+
+free_buf:
+	dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom,
+	    optrom_dma);
+	return NULL;
+}
+
 /*
  * NVRAM support routines
  */
@@ -1117,10 +1650,18 @@ qla2xxx_get_flash_info(scsi_qla_host_t *vha)
 	uint32_t flt_addr;
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) &&
-	    !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha) &&
-	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
-		return QLA_SUCCESS;
+	if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && !IS_CNA_CAPABLE(ha) &&
+	    !IS_QLA2031(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha) &&
+	    !IS_QLA29XX(ha))
+		goto done;
+
+	if (IS_QLA29XX(ha)) {
+		mutex_lock(&ha->optrom_mutex);
+		qla29xx_get_flt_layout(vha);
+		qla29xx_get_fdt_info(vha);
+		mutex_unlock(&ha->optrom_mutex);
+		goto done;
+	}
 
 	if (IS_QLA28XX(ha) && !qla28xx_validate_mcu_signature(vha))
 		ha->flags.secure_mcu = 1;
@@ -1132,7 +1673,7 @@ qla2xxx_get_flash_info(scsi_qla_host_t *vha)
 	qla2xxx_get_flt_info(vha, flt_addr);
 	qla2xxx_get_fdt_info(vha);
 	qla2xxx_get_idc_param(vha);
-
+done:
 	return QLA_SUCCESS;
 }
 
@@ -3448,6 +3989,86 @@ qla82xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
 	return ret;
 }
 
+/*
+ * Read the boot BIOS/FCODE/EFI version for 29xx adapters.
+ *
+ * 29xx reads flash through an FLT region code plus a region-relative
+ * offset.  The firmware only honours such a read at the region base
+ * (offset 0) and rejects a non-zero offset with MBS_COMMAND_ERROR.  The
+ * PCI expansion ROM header and the PCI data structure it points to both
+ * live within the first dwords of FLT_REG_BOOT_CODE, so read one bounded
+ * block at offset 0 and parse both structures from memory instead of
+ * issuing a second read at the PCI-data-structure offset.
+ */
+#define QLA29XX_BOOT_PROBE_LEN	0x100
+
+static void
+qla29xx_get_boot_version(scsi_qla_host_t *vha, void *mbuf)
+{
+	struct qla_hw_data *ha = vha->hw;
+	uint8_t *bcode = mbuf;
+	uint32_t pcids;
+	uint8_t code_type;
+
+	if (!qla29xx_read_optrom_data(vha, FLT_REG_BOOT_CODE, 0, mbuf, 0,
+				      QLA29XX_BOOT_PROBE_LEN)) {
+		ql_log(ql_log_info, vha, 0x018e,
+		    "Unable to read PCI Data Structure.\n");
+		return;
+	}
+
+	/* Verify PCI expansion ROM header. */
+	if (memcmp(bcode, "\x55\xaa", 2)) {
+		ql_log(ql_log_info, vha, 0x0059,
+		    "No matching ROM signature.\n");
+		return;
+	}
+
+	/* Locate the PCI data structure within the buffer. */
+	pcids = (bcode[0x19] << 8) | bcode[0x18];
+	if (pcids + 0x16 > QLA29XX_BOOT_PROBE_LEN)
+		return;
+
+	bcode += pcids;
+
+	/* Validate signature of PCI data structure. */
+	if (memcmp(bcode, "PCIR", 4)) {
+		ql_log(ql_log_info, vha, 0x005a,
+		    "PCI data struct not found pcir_adr=%x.\n", pcids);
+		return;
+	}
+
+	code_type = bcode[0x14];
+	switch (code_type) {
+	case ROM_CODE_TYPE_BIOS:
+		/* Intel x86, PC-AT compatible. */
+		ha->bios_revision[0] = bcode[0x12];
+		ha->bios_revision[1] = bcode[0x13];
+		ql_dbg(ql_dbg_init, vha, 0x005b, "Read BIOS %d.%d.\n",
+		    ha->bios_revision[1], ha->bios_revision[0]);
+		break;
+	case ROM_CODE_TYPE_FCODE:
+		/* Open Firmware standard for PCI (FCode). */
+		ha->fcode_revision[0] = bcode[0x12];
+		ha->fcode_revision[1] = bcode[0x13];
+		ql_dbg(ql_dbg_init, vha, 0x005c, "Read FCODE %d.%d.\n",
+		    ha->fcode_revision[1], ha->fcode_revision[0]);
+		break;
+	case ROM_CODE_TYPE_EFI:
+		/* Extensible Firmware Interface (EFI). */
+		ha->efi_revision[0] = bcode[0x12];
+		ha->efi_revision[1] = bcode[0x13];
+		ql_dbg(ql_dbg_init, vha, 0x005d, "Read EFI %d.%d.\n",
+		    ha->efi_revision[1], ha->efi_revision[0]);
+		break;
+	default:
+		ql_log(ql_log_warn, vha, 0x005e,
+		    "Unrecognized code type %x at pcids %x.\n",
+		    code_type, pcids);
+		break;
+	}
+}
+
 int
 qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
 {
@@ -3472,12 +4093,31 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
 	memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
 	memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
 
+	/* ISP29xx: get FW version from FLT region metadata */
+	if (IS_QLA29XX(ha)) {
+		struct qla_flt_region_data region;
+
+		ret = qla29xx_get_flash_region(vha, FLT_REG_FW, &region);
+		if (ret != QLA_SUCCESS) {
+			ql_log(ql_log_warn, vha, 0x7033,
+					"Invalid region %x\n", FLT_REG_FW);
+			return ret;
+		}
+
+		ha->fw_revision[0] = (le32_to_cpu(region.version) >> 16) & 0xff;
+		ha->fw_revision[1] = (le32_to_cpu(region.version) >> 8) & 0xff;
+		ha->fw_revision[2] = le32_to_cpu(region.version) & 0xff;
+
+		/* BIOS/FCODE/EFI version from the boot-code region. */
+		qla29xx_get_boot_version(vha, mbuf);
+		return ret;
+	}
+
 	pcihdr = ha->flt_region_boot << 2;
 	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
 		qla27xx_get_active_image(vha, &active_regions);
-		if (active_regions.global == QLA27XX_SECONDARY_IMAGE) {
+		if (active_regions.global == QLA27XX_SECONDARY_IMAGE)
 			pcihdr = ha->flt_region_boot_sec << 2;
-		}
 	}
 
 	do {
@@ -3486,7 +4126,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
 		if (ret) {
 			ql_log(ql_log_info, vha, 0x017d,
 			    "Unable to read PCI EXP Rom Header(%x).\n", ret);
-			return QLA_FUNCTION_FAILED;
+			break;
 		}
 
 		bcode = mbuf + (pcihdr % 4);
@@ -3494,7 +4134,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
 			/* No signature */
 			ql_log(ql_log_fatal, vha, 0x0059,
 			    "No matching ROM signature.\n");
-			return QLA_FUNCTION_FAILED;
+			break;
 		}
 
 		/* Locate PCI data structure. */
@@ -3504,7 +4144,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
 		if (ret) {
 			ql_log(ql_log_info, vha, 0x018e,
 			    "Unable to read PCI Data Structure (%x).\n", ret);
-			return QLA_FUNCTION_FAILED;
+			break;
 		}
 
 		bcode = mbuf + (pcihdr % 4);
@@ -3515,7 +4155,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
 			ql_log(ql_log_fatal, vha, 0x005a,
 			    "PCI data struct not found pcir_adr=%x.\n", pcids);
 			ql_dump_buffer(ql_dbg_init, vha, 0x0059, dcode, 32);
-			return QLA_FUNCTION_FAILED;
+			break;
 		}
 
 		/* Read version */
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 03/88] scsi: qla2xxx: Add NVRAM config support for 29xx adapters
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 01/88] scsi: qla2xxx: Add 29xx series PCI device ID support Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 02/88] scsi: qla2xxx: Add flash read/write interface for 29xx Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 04/88] scsi: qla2xxx: Add 29xx support in queue initialisation path Nilesh Javali
                   ` (84 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

From: Manish Rangankar <mrangankar@marvell.com>

Extend the NVRAM read and configuration-apply paths to handle the
29xx series.  The 29xx NVRAM layout is similar to the 81xx family,
so reuse the existing nvram_81xx parsing while adding
29xx-specific fields and init-sequence integration.

Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_def.h  |   1 +
 drivers/scsi/qla2xxx/qla_fw.h   |  42 ++++++++++-
 drivers/scsi/qla2xxx/qla_init.c | 123 +++++++++++++++++++++++++-------
 drivers/scsi/qla2xxx/qla_os.c   |   2 +
 4 files changed, 141 insertions(+), 27 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index b5ad6ed3d5d1..89ddf332cdbd 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -4469,6 +4469,7 @@ struct qla_hw_data {
 	uint16_t	vpd_size;
 	uint16_t	vpd_base;
 	void		*vpd;
+	struct qla_flash_memo_block *fiv;
 
 	uint16_t	loop_reset_delay;
 	uint8_t		retry_count;
diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index d27d09964a24..42f6125b3cfb 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -1676,6 +1676,8 @@ struct qla_flt_location {
 #define FLT_REG_VPD_SEC_27XX_2	0xD8
 #define FLT_REG_VPD_SEC_27XX_3	0xDA
 #define FLT_REG_NVME_PARAMS_27XX	0x21
+#define FLT_REG_FMB_PRI		0xDF
+#define FLT_REG_FMB_SEC		0x124
 
 /* 28xx */
 #define FLT_REG_AUX_IMG_PRI_28XX	0x125
@@ -2110,7 +2112,7 @@ struct nvram_81xx {
 	 * BIT 7    = SCM Disabled if BIT is set (1)
 	 * BIT 8-15 = Unused
 	 */
-	uint16_t enhanced_features;
+	__le16	enhanced_features;
 
 	uint16_t reserved_24[4];
 
@@ -2338,4 +2340,42 @@ struct qla_fcp_prio_cfg {
 
 #define NVRAM_DUAL_FCP_NVME_FLAG_OFFSET	0x196
 
+struct qla_fmb_version {
+	uint8_t major;
+	uint8_t minor;
+	uint8_t sub;
+	uint8_t build;
+};
+
+struct qla_fmb_upd_time {
+	__le16   year;
+	uint8_t  month;
+	uint8_t  day;
+
+	uint8_t  hour;
+	uint8_t  minute;
+	uint8_t  second;
+	uint8_t  reserved;
+};
+
+struct qla_flash_memo_block {
+	__le32   signature;	/* "FMBS" */
+#define QLFC_FMB_SIG	cpu_to_le32(0x53424D46)
+	__le32   length;
+	__le32   version;
+#define QLFC_FMB_VERSION 3
+	__le32   checksum;
+	struct qla_fmb_version ffv_ver;
+	struct qla_fmb_version mbi_ver;
+	struct {
+		__le16   year;
+		uint8_t  month;
+		uint8_t  day;
+		uint8_t  reserve[4];
+	} bld_time;
+	uint8_t tool_id[4];
+	struct qla_fmb_upd_time upd_time;
+	struct qla_fmb_version  tool_version;
+};
+
 #endif
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index e23e7ac48ae2..f5d230fc3642 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -4194,11 +4194,11 @@ qla24xx_detect_sfp(scsi_qla_host_t *vha)
 	used_nvram = 0;
 	ha->flags.lr_detected = 0;
 	if (IS_BPM_RANGE_CAPABLE(ha) &&
-	    (nv->enhanced_features & NEF_LR_DIST_ENABLE)) {
+	    (le16_to_cpu(nv->enhanced_features) & NEF_LR_DIST_ENABLE)) {
 		used_nvram = 1;
 		ha->flags.lr_detected = 1;
 		ha->lr_distance =
-		    (nv->enhanced_features >> LR_DIST_NV_POS)
+		    (le16_to_cpu(nv->enhanced_features) >> LR_DIST_NV_POS)
 		     & LR_DIST_NV_MASK;
 	}
 
@@ -9306,34 +9306,97 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
 	if (IS_P3P_TYPE(ha) || IS_QLA8031(ha))
 		ha->vpd_size = FA_VPD_SIZE_82XX;
 
-	if (IS_QLA28XX(ha) || IS_QLA27XX(ha))
-		qla28xx_get_aux_images(vha, &active_regions);
-
 	/* Get VPD data into cache */
 	ha->vpd = ha->nvram + VPD_OFFSET;
 
-	faddr = ha->flt_region_vpd;
-	if (IS_QLA28XX(ha)) {
-		if (active_regions.aux.vpd_nvram == QLA27XX_SECONDARY_IMAGE)
-			faddr = ha->flt_region_vpd_sec;
+	if (IS_QLA29XX(ha)) {
+		uint16_t fw_options = 0, r_code;
+		uint32_t vpd_r[] = {FLT_REG_VPD_0, FLT_REG_VPD_1,
+				    FLT_REG_VPD_2, FLT_REG_VPD_3};
+		uint32_t nvram_r[] = {FLT_REG_NVRAM_0, FLT_REG_NVRAM_1,
+				      FLT_REG_NVRAM_2, FLT_REG_NVRAM_3};
+		void *buf;
+
+		BUILD_BUG_ON((VPD_OFFSET + FA_NVRAM_VPD_SIZE +
+			      sizeof(struct qla_flash_memo_block)) >
+			     MAX_NVRAM_SIZE);
+
+		ha->fiv = (struct qla_flash_memo_block *)
+			((char *)ha->vpd + ha->vpd_size);
+
+		buf = qla29xx_read_optrom_data(vha, FLT_REG_FMB_PRI,
+					       fw_options, ha->fiv, 0,
+					       sizeof(struct qla_flash_memo_block));
+		if (!buf) {
+			ql_log(ql_log_info, vha, 0x01be,
+			    "Unable to read Flash Image Version.\n");
+		} else if (ha->fiv->signature != QLFC_FMB_SIG) {
+			ql_log(ql_log_warn, vha, 0x01bf,
+			    "Invalid FMB signature %#x, expected %#x.\n",
+			    le32_to_cpu(ha->fiv->signature),
+			    le32_to_cpu(QLFC_FMB_SIG));
+			ha->fiv = NULL;
+		} else {
+			ql_log(ql_log_info, vha, 0x0024,
+			    "Flash Image Version %u.%02u.%02u\n",
+			    ha->fiv->mbi_ver.major,
+			    ha->fiv->mbi_ver.minor,
+			    ha->fiv->mbi_ver.sub);
+		}
+
+		if (ha->port_no >= ARRAY_SIZE(vpd_r)) {
+			ql_log(ql_log_warn, vha, 0x002e,
+			    "Invalid port number %u, skipping VPD/NVRAM read.\n",
+			    ha->port_no);
+			goto out_29xx;
+		}
+
+		r_code = vpd_r[ha->port_no];
+		buf = qla29xx_read_optrom_data(vha, r_code, fw_options,
+					       ha->vpd, 0, ha->vpd_size);
+		if (!buf)
+			ql_log(ql_log_info, vha, 0x002d,
+			    "Unable to read VPD info.\n");
+
+		r_code = nvram_r[ha->port_no];
+		buf = qla29xx_read_optrom_data(vha, r_code, fw_options,
+					       ha->nvram, 0, ha->nvram_size);
+		if (!buf)
+			ql_log(ql_log_info, vha, 0x0013,
+			    "Unable to read nvram config info.\n");
+out_29xx:
+	} else {
+		if (IS_QLA28XX(ha) || IS_QLA27XX(ha))
+			qla28xx_get_aux_images(vha, &active_regions);
+
+		faddr = ha->flt_region_vpd;
+		if (IS_QLA28XX(ha)) {
+			if (active_regions.aux.vpd_nvram ==
+			    QLA27XX_SECONDARY_IMAGE)
+				faddr = ha->flt_region_vpd_sec;
+			ql_dbg(ql_dbg_init, vha, 0x0110,
+			    "Loading %s nvram image.\n",
+			    active_regions.aux.vpd_nvram ==
+			    QLA27XX_PRIMARY_IMAGE ?
+			    "primary" : "secondary");
+		}
+		ha->isp_ops->read_optrom(vha, ha->vpd, faddr << 2,
+					 ha->vpd_size);
+
+		/* Get NVRAM data into cache and calculate checksum. */
+		faddr = ha->flt_region_nvram;
+		if (IS_QLA28XX(ha)) {
+			if (active_regions.aux.vpd_nvram ==
+			    QLA27XX_SECONDARY_IMAGE)
+				faddr = ha->flt_region_nvram_sec;
+		}
 		ql_dbg(ql_dbg_init, vha, 0x0110,
 		    "Loading %s nvram image.\n",
 		    active_regions.aux.vpd_nvram == QLA27XX_PRIMARY_IMAGE ?
 		    "primary" : "secondary");
+		ha->isp_ops->read_optrom(vha, ha->nvram, faddr << 2,
+					 ha->nvram_size);
 	}
-	ha->isp_ops->read_optrom(vha, ha->vpd, faddr << 2, ha->vpd_size);
-
-	/* Get NVRAM data into cache and calculate checksum. */
-	faddr = ha->flt_region_nvram;
-	if (IS_QLA28XX(ha)) {
-		if (active_regions.aux.vpd_nvram == QLA27XX_SECONDARY_IMAGE)
-			faddr = ha->flt_region_nvram_sec;
-	}
-	ql_dbg(ql_dbg_init, vha, 0x0110,
-	    "Loading %s nvram image.\n",
-	    active_regions.aux.vpd_nvram == QLA27XX_PRIMARY_IMAGE ?
-	    "primary" : "secondary");
-	ha->isp_ops->read_optrom(vha, ha->nvram, faddr << 2, ha->nvram_size);
 
 	dptr = (__force __le32 *)nv;
 	for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++, dptr++)
@@ -9386,7 +9449,10 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
 		nv->login_timeout = cpu_to_le16(0);
 		nv->firmware_options_1 =
 		    cpu_to_le32(BIT_14|BIT_13|BIT_2|BIT_1);
-		nv->firmware_options_2 = cpu_to_le32(2 << 4);
+		if (IS_QLA29XX(ha))
+			nv->firmware_options_2 = cpu_to_le32(BIT_4);
+		else
+			nv->firmware_options_2 = cpu_to_le32(BIT_5);
 		nv->firmware_options_2 |= cpu_to_le32(BIT_12);
 		nv->firmware_options_3 = cpu_to_le32(2 << 13);
 		nv->host_p = cpu_to_le32(BIT_11|BIT_10);
@@ -9468,9 +9534,13 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
 		icb->node_name[0] &= 0xF0;
 	}
 
-	if (IS_QLA28XX(ha) || IS_QLA27XX(ha)) {
-		if ((nv->enhanced_features & BIT_7) == 0)
+	/* SCM Enabled in NVRAM */
+	if (IS_QLA29XX(ha) || IS_QLA28XX(ha) || IS_QLA27XX(ha)) {
+		if ((le16_to_cpu(nv->enhanced_features) & BIT_7) == 0) {
+			ql_log(ql_log_info, vha, 0x0062,
+			       "USCM enabled in NVRAM\n");
 			ha->flags.scm_supported_a = 1;
+		}
 	}
 
 	/* Set host adapter parameters. */
@@ -9546,7 +9616,8 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
 
 	/* if not running MSI-X we need handshaking on interrupts */
 	if (!vha->hw->flags.msix_enabled &&
-	    (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)))
+	    (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+	     IS_QLA29XX(ha)))
 		icb->firmware_options_2 |= cpu_to_le32(BIT_22);
 
 	/* Enable ZIO. */
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index ebb97329e6ea..c3eb941f71ad 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -4620,6 +4620,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 fail_free_nvram:
 	kfree(ha->nvram);
 	ha->nvram = NULL;
+	ha->fiv = NULL;
 fail_free_ctx_mempool:
 	mempool_destroy(ha->ctx_mempool);
 	ha->ctx_mempool = NULL;
@@ -5085,6 +5086,7 @@ qla2x00_mem_free(struct qla_hw_data *ha)
 	ha->optrom_buffer = NULL;
 	kfree(ha->nvram);
 	ha->nvram = NULL;
+	ha->fiv = NULL;
 	kfree(ha->npiv_info);
 	ha->npiv_info = NULL;
 	kfree(ha->swl);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 04/88] scsi: qla2xxx: Add 29xx support in queue initialisation path
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (2 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 03/88] scsi: qla2xxx: Add NVRAM config support for 29xx adapters Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:31   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 05/88] scsi: qla2xxx: Add FC operational firmware load for 29xx Nilesh Javali
                   ` (83 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

From: Manish Rangankar <mrangankar@marvell.com>

Extend the queue initialisation and multi-queue management mailbox
commands to include IS_QLA29XX() checks, following the same mailbox
interface as 27xx/28xx.

Unlike earlier adapters that use 64-byte request/response ring entries
(request_t / response_t), 29xx uses 128-byte entries.  Add struct
request_ext and struct response_ext, which extend the legacy 64-byte
layout with a 64-byte reserved area.  The first 64 bytes are
layout-compatible with the legacy structures, so common header
accesses remain valid.

The enlarged entry stride doubles the DMA ring memory allocated for
both request and response queues on 29xx, and all ring pointer
arithmetic must account for the wider entries (handled by later
patches in this series).

Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_def.h | 30 ++++++++++++++++++++++++++++++
 drivers/scsi/qla2xxx/qla_mbx.c | 20 +++++++++++++-------
 drivers/scsi/qla2xxx/qla_mid.c | 25 +++++++++++++++++++++----
 3 files changed, 64 insertions(+), 11 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 89ddf332cdbd..1ec7ee578e0c 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -1989,6 +1989,18 @@ typedef struct {
 #define RESPONSE_PROCESSED	0xDEADDEAD	/* Signature */
 } response_t;
 
+struct response_ext {
+	uint8_t		entry_type;		/* Entry type. */
+	uint8_t		entry_count;		/* Entry count. */
+	uint8_t		sys_define;		/* System defined. */
+	uint8_t		entry_status;		/* Entry Status. */
+	uint32_t	handle;			/* System defined handle */
+	uint8_t		data[52];
+	uint32_t	signature;
+	uint8_t		reserved[64];
+#define RESPONSE_PROCESSED	0xDEADDEAD	/* Signature */
+};
+
 /*
  * ISP queue - ATIO queue entry definition.
  */
@@ -2067,6 +2079,24 @@ typedef struct {
 	struct dsd64 dsd[2];
 } cmd_a64_entry_t, request_t;
 
+struct request_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+	uint32_t handle;		/* System handle. */
+	target_id_t target;		/* SCSI ID */
+	__le16  lun;			/* SCSI LUN */
+	__le16  control_flags;		/* Control flags. */
+	uint16_t reserved_1;
+	__le16  timeout;		/* Command timeout. */
+	__le16  dseg_count;		/* Data segment count. */
+	uint8_t scsi_cdb[MAX_CMDSZ];	/* SCSI command words. */
+	uint32_t byte_count;		/* Total byte count. */
+	struct dsd64 dsd[2];
+	uint8_t reserved[64];
+};
+
 /*
  * ISP queue - continuation entry structure definition.
  */
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 2d052f870b2b..2b232b225c90 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -4584,7 +4584,8 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
 	mcp->mb[12] = req->qos;
 	mcp->mb[11] = req->vp_idx;
 	mcp->mb[13] = req->rid;
-	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+	    IS_QLA29XX(ha))
 		mcp->mb[15] = 0;
 
 	mcp->mb[4] = req->id;
@@ -4599,9 +4600,10 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
 	mcp->tov = MBX_TOV_SECONDS * 2;
 
 	if (IS_QLA81XX(ha) || IS_QLA83XX(ha) || IS_QLA27XX(ha) ||
-	    IS_QLA28XX(ha))
+	    IS_QLA28XX(ha) || IS_QLA29XX(ha))
 		mcp->in_mb |= MBX_1;
-	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+	    IS_QLA29XX(ha)) {
 		mcp->out_mb |= MBX_15;
 		/* debug q create issue in SR-IOV */
 		mcp->in_mb |= MBX_9 | MBX_8 | MBX_7;
@@ -4610,7 +4612,8 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 	if (!(req->options & BIT_0)) {
 		wrt_reg_dword(req->req_q_in, 0);
-		if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+		if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha) &&
+		    !IS_QLA29XX(ha))
 			wrt_reg_dword(req->req_q_out, 0);
 	}
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -4654,7 +4657,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
 	mcp->mb[5] = rsp->length;
 	mcp->mb[14] = rsp->msix->entry;
 	mcp->mb[13] = rsp->rid;
-	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+	    IS_QLA29XX(ha))
 		mcp->mb[15] = 0;
 
 	mcp->mb[4] = rsp->id;
@@ -4671,7 +4675,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
 	if (IS_QLA81XX(ha)) {
 		mcp->out_mb |= MBX_12|MBX_11|MBX_10;
 		mcp->in_mb |= MBX_1;
-	} else if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+	} else if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+		   IS_QLA29XX(ha)) {
 		mcp->out_mb |= MBX_15|MBX_12|MBX_11|MBX_10;
 		mcp->in_mb |= MBX_1;
 		/* debug q create issue in SR-IOV */
@@ -4681,7 +4686,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 	if (!(rsp->options & BIT_0)) {
 		wrt_reg_dword(rsp->rsp_q_out, 0);
-		if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+		if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha) &&
+		    !IS_QLA29XX(ha))
 			wrt_reg_dword(rsp->rsp_q_in, 0);
 	}
 
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index c563133f751e..e75b7ae22bc5 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -574,9 +574,13 @@ qla25xx_free_req_que(struct scsi_qla_host *vha, struct req_que *req)
 {
 	struct qla_hw_data *ha = vha->hw;
 	uint16_t que_id = req->id;
+	uint16_t reqsz;
+
+	reqsz = IS_QLA29XX(ha) ? sizeof(struct request_ext) :
+				 sizeof(request_t);
 
 	dma_free_coherent(&ha->pdev->dev, (req->length + 1) *
-		sizeof(request_t), req->ring, req->dma);
+			  reqsz, req->ring, req->dma);
 	req->ring = NULL;
 	req->dma = 0;
 	if (que_id) {
@@ -594,6 +598,10 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
 {
 	struct qla_hw_data *ha = vha->hw;
 	uint16_t que_id = rsp->id;
+	uint16_t rspsz;
+
+	rspsz = IS_QLA29XX(ha) ? sizeof(struct response_ext) :
+				 sizeof(response_t);
 
 	if (rsp->msix && rsp->msix->have_irq) {
 		free_irq(rsp->msix->vector, rsp->msix->handle);
@@ -601,8 +609,9 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
 		rsp->msix->in_use = 0;
 		rsp->msix->handle = NULL;
 	}
+
 	dma_free_coherent(&ha->pdev->dev, (rsp->length + 1) *
-		sizeof(response_t), rsp->ring, rsp->dma);
+			  rspsz, rsp->ring, rsp->dma);
 	rsp->ring = NULL;
 	rsp->dma = 0;
 	if (que_id) {
@@ -706,6 +715,7 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
 	uint16_t que_id = 0;
 	device_reg_t *reg;
 	uint32_t cnt;
+	uint16_t reqsz;
 
 	req = kzalloc_obj(struct req_que);
 	if (req == NULL) {
@@ -714,9 +724,12 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
 		goto failed;
 	}
 
+	reqsz = IS_QLA29XX(ha) ? sizeof(struct request_ext) :
+				 sizeof(request_t);
+
 	req->length = REQUEST_ENTRY_CNT_24XX;
 	req->ring = dma_alloc_coherent(&ha->pdev->dev,
-			(req->length + 1) * sizeof(request_t),
+			(req->length + 1) * reqsz,
 			&req->dma, GFP_KERNEL);
 	if (req->ring == NULL) {
 		ql_log(ql_log_fatal, base_vha, 0x00da,
@@ -833,6 +846,7 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
 	struct scsi_qla_host *vha = pci_get_drvdata(ha->pdev);
 	uint16_t que_id = 0;
 	device_reg_t *reg;
+	uint16_t rspsz;
 
 	rsp = kzalloc_obj(struct rsp_que);
 	if (rsp == NULL) {
@@ -841,9 +855,12 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
 		goto failed;
 	}
 
+	rspsz = IS_QLA29XX(ha) ? sizeof(struct response_ext) :
+				 sizeof(response_t);
+
 	rsp->length = RESPONSE_ENTRY_CNT_MQ;
 	rsp->ring = dma_alloc_coherent(&ha->pdev->dev,
-			(rsp->length + 1) * sizeof(response_t),
+			(rsp->length + 1) * rspsz,
 			&rsp->dma, GFP_KERNEL);
 	if (rsp->ring == NULL) {
 		ql_log(ql_log_warn, base_vha, 0x00e1,
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 05/88] scsi: qla2xxx: Add FC operational firmware load for 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (3 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 04/88] scsi: qla2xxx: Add 29xx support in queue initialisation path Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:17   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 06/88] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path Nilesh Javali
                   ` (82 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

From: Manish Rangankar <mrangankar@marvell.com>

Add support to load the 29xx FC operational firmware from the
filesystem and to set up the corresponding firmware dump
template.  This follows the same request_firmware / segment-load
pattern used by earlier adapters.

Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_fw.h   |  36 +++
 drivers/scsi/qla2xxx/qla_gbl.h  |   3 +-
 drivers/scsi/qla2xxx/qla_init.c | 544 +++++++++++++++++++++++++++++++-
 drivers/scsi/qla2xxx/qla_mbx.c  |  19 +-
 drivers/scsi/qla2xxx/qla_nx.c   |   2 +-
 drivers/scsi/qla2xxx/qla_os.c   |  47 ++-
 drivers/scsi/qla2xxx/qla_sup.c  |   4 +-
 7 files changed, 641 insertions(+), 14 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index 42f6125b3cfb..4d6f8b1a36d1 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -2378,4 +2378,40 @@ struct qla_flash_memo_block {
 	struct qla_fmb_version  tool_version;
 };
 
+#define TIM_DEST_ADDR	0xffffffff
+#define CHUNK_SIZE	0x10000
+
+#define TIM	0
+#define ARR1	1
+#define ARR2	2
+#define ARR3	3
+#define ARR4	4
+
+#define LD_FL_HEADER_SIGNATURE	0x46434F50
+#define LD_FL_HEADER_VERSION	0x01
+#define LD_FL_HEADER_SIZE	(0x14 * 4)
+
+struct fcop_header {
+	uint32_t signature;
+	uint32_t header_length;
+	uint32_t header_version;
+	uint32_t segment_size;
+	uint32_t tim_length;
+	uint32_t fc_major_version;
+	uint32_t fc_minor_version;
+	uint32_t fc_subminor_version;
+	uint32_t array1_length;
+	uint32_t array1_destination_addr;
+	uint32_t array2_length;
+	uint32_t array2_destination_addr;
+	uint32_t array3_length;
+	uint32_t array4_length;
+	uint32_t attribute;
+	uint32_t extended_attribute;
+	uint32_t reserved0;
+	uint32_t reserved1;
+	uint32_t reserved2;
+	uint32_t image_checksum;
+};
+
 #endif
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index a032dd047a8f..ca5a80b9c3d2 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -38,6 +38,7 @@ extern void qla24xx_update_fw_options(scsi_qla_host_t *);
 extern int qla2x00_load_risc(struct scsi_qla_host *, uint32_t *);
 extern int qla24xx_load_risc(scsi_qla_host_t *, uint32_t *);
 extern int qla81xx_load_risc(scsi_qla_host_t *, uint32_t *);
+extern int qla29xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr);
 
 extern int qla2x00_perform_loop_resync(scsi_qla_host_t *);
 extern int qla2x00_loop_resync(scsi_qla_host_t *);
@@ -336,7 +337,7 @@ void qla2x00_els_dcmd2_iocb_timeout(void *data);
  * Global Function Prototypes in qla_mbx.c source file.
  */
 extern int
-qla2x00_load_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t);
+qla2x00_load_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t, int);
 
 extern int
 qla2x00_dump_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t);
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index f5d230fc3642..79b8026cb33d 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -3868,7 +3868,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
 			eft_size = EFT_SIZE;
 	}
 
-	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+	if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		struct fwdt *fwdt = ha->fwdt;
 		uint j;
 
@@ -3937,7 +3937,8 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
 				    "Allocated (%d KB) for firmware dump.\n",
 				    dump_size / 1024);
 
-				if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+				if (IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+				    IS_QLA29XX(ha)) {
 					ha->mpi_fw_dump = (char *)fw_dump +
 						ha->fwdt[1].dump_size;
 					mutex_unlock(&ha->optrom_mutex);
@@ -8669,7 +8670,8 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr,
 			for (i = 0; i < dlen; i++)
 				dcode[i] = swab32(dcode[i]);
 
-			rval = qla2x00_load_ram(vha, req->dma, risc_addr, dlen);
+			rval = qla2x00_load_ram(vha, req->dma, risc_addr,
+			    dlen, 0);
 			if (rval) {
 				ql_log(ql_log_fatal, vha, 0x008f,
 				    "-> Failed load firmware fragment %u.\n",
@@ -8839,7 +8841,7 @@ qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
 				wcode[i] = swab16((__force u32)fwcode[i]);
 
 			rval = qla2x00_load_ram(vha, req->dma, risc_addr,
-			    wlen);
+			    wlen, 0);
 			if (rval) {
 				ql_log(ql_log_fatal, vha, 0x008a,
 				    "Failed to load segment %d of firmware.\n",
@@ -8929,7 +8931,8 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr)
 			for (i = 0; i < dlen; i++)
 				dcode[i] = swab32((__force u32)fwcode[i]);
 
-			rval = qla2x00_load_ram(vha, req->dma, risc_addr, dlen);
+			rval = qla2x00_load_ram(vha, req->dma, risc_addr,
+			    dlen, 0);
 			if (rval) {
 				ql_log(ql_log_fatal, vha, 0x0098,
 				    "-> Failed load firmware fragment %u.\n",
@@ -9128,6 +9131,537 @@ qla81xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
 	return rval;
 }
 
+static int qla29xx_validate_firmware_image(scsi_qla_host_t *vha,
+					   __be32 *dword)
+{
+	if (be32_to_cpu(dword[0]) != LD_FL_HEADER_SIGNATURE) {
+		ql_dbg(ql_dbg_init, vha, 0x0093,
+		       "Firmware image Signature does not match (0x%x).\n",
+		       be32_to_cpu(dword[0]));
+		return QLA_FUNCTION_FAILED;
+	}
+	if (be32_to_cpu(dword[2]) != LD_FL_HEADER_VERSION) {
+		ql_dbg(ql_dbg_init, vha, 0x0093,
+		       "Firmware image Version does not match (0x%x).\n",
+		       be32_to_cpu(dword[2]));
+		return QLA_FUNCTION_FAILED;
+	}
+	return QLA_SUCCESS;
+}
+
+static int qla29xx_process_rd_image(struct scsi_qla_host *vha,
+				    struct fcop_header *header,
+				    __be32 *fwcode, int section,
+				    uint32_t risc_addr, uint32_t section_size)
+{
+	int rval = QLA_SUCCESS;
+	uint32_t *dcode = NULL;
+	struct qla_hw_data *ha = vha->hw;
+	struct req_que *req = ha->req_q_map[0];
+	int num_segments = 0, segment = 0;
+	int chunks_per_segment = 0, chunk = 0;
+	int is_first_chunk = 0;
+	int is_last_chunk = 0;
+	int is_first_segment = 0;
+	int is_last_segment = 0;
+	int opt = 0;
+	uint32_t size = 0;
+	uint32_t size_remainder = 0;
+	uint32_t seg_size_remainder = 0;
+	int i = 0;
+
+	dcode = (uint32_t *)req->ring;
+
+	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",
+			    section_size,
+			    req->length * qla_req_entry_size(ha));
+			return QLA_FUNCTION_FAILED;
+		}
+
+		opt = BIT_15 | BIT_2 | BIT_1 | BIT_0;
+		for (i = 0; i < (section_size >> 2); i++)
+			dcode[i] = swab32((__force u32)fwcode[i]);
+
+		ql_dbg(ql_dbg_init, vha, 0x0098,
+		       "TIM : process_rd_image [opt 0x%x]\n", opt);
+		rval = qla2x00_load_ram(vha, req->dma, risc_addr,
+					section_size >> 2, opt);
+		if (rval) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			       "-> Failed load TIM\n");
+			return QLA_FUNCTION_FAILED;
+		}
+	} else {
+		size_remainder = section_size;
+		num_segments += (section_size % header->segment_size == 0) ?
+			(section_size / header->segment_size) :
+			(section_size / header->segment_size) + 1;
+		chunks_per_segment =
+			(header->segment_size % CHUNK_SIZE == 0) ?
+			(header->segment_size / CHUNK_SIZE) :
+			(header->segment_size / CHUNK_SIZE) + 1;
+
+		ql_dbg(ql_dbg_init, vha, 0x0098,
+		       "num seg 0x%x chunk per seg 0x%x\n",
+		       num_segments, chunks_per_segment);
+
+		for (segment = 0; segment < num_segments; segment++) {
+			for (chunk = 0; chunk < chunks_per_segment; chunk++) {
+				is_first_chunk = (chunk == 0);
+				if (chunk == 0) {
+					if (size_remainder >=
+					    header->segment_size)
+						seg_size_remainder =
+							header->segment_size;
+					else
+						seg_size_remainder =
+							size_remainder;
+				}
+
+				is_first_segment =
+					(section == ARR1 && segment == 0);
+				is_last_segment =
+					(section == ARR2 &&
+					 (segment == (num_segments - 1)));
+				is_last_chunk =
+					(chunk == (chunks_per_segment - 1) ||
+					 size_remainder <= CHUNK_SIZE);
+
+				if (seg_size_remainder < CHUNK_SIZE)
+					size = seg_size_remainder % CHUNK_SIZE;
+				else
+					size = CHUNK_SIZE;
+
+				ql_dbg(ql_dbg_init, vha, 0x0098,
+				       "[%d]chunk 0x%x segment 0x%x first_segment 0x%x first_chunk 0x%x last_segment 0x%x last_chunk 0x%x\n",
+				       __LINE__, chunk, segment,
+				       is_first_segment, is_first_chunk,
+				       is_last_segment, is_last_chunk);
+
+				opt = BIT_2;
+				if (is_first_chunk)
+					opt |= BIT_0;
+				if (is_last_chunk)
+					opt |= BIT_1;
+				if (is_first_segment)
+					opt |= BIT_3;
+				if (is_last_segment)
+					opt |= BIT_4;
+
+				memcpy((char *)dcode, (char *)fwcode, size);
+
+			ql_dbg(ql_dbg_init, vha, 0x0098,
+			       "ARR[0x%x] : opt 0x%x\n", size, opt);
+				rval = qla2x00_load_ram(vha, req->dma,
+							risc_addr,
+							size >> 2, opt);
+				if (rval) {
+					ql_log(ql_log_fatal, vha, 0x0098,
+					       "-> Failed load ram\n");
+					return QLA_FUNCTION_FAILED;
+				}
+
+				fwcode += size >> 2;
+				size_remainder -= size;
+				seg_size_remainder -= size;
+				if (size_remainder == 0)
+					break;
+			}
+			risc_addr += header->segment_size >> 2;
+		}
+	}
+	return rval;
+}
+
+static int
+qla29xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr)
+{
+	int rval;
+	struct fcop_header *header;
+	uint32_t *dcode = NULL;
+	struct fw_blob *blob;
+	__be32 *fwcode, *array1_addr, *array3_addr;
+	uint templates;
+	uint32_t array_size, risc_attr = 0;
+	ulong i;
+	uint j;
+	ulong dlen, array3_offset;
+	size_t max_dwords;
+	struct qla_hw_data *ha = vha->hw;
+	struct fwdt *fwdt = ha->fwdt;
+
+	header = vmalloc(sizeof(*header));
+	if (!header)
+		return QLA_FUNCTION_FAILED;
+
+	ql_dbg(ql_dbg_init, vha, 0x0090,
+	       "-> FW: Loading via request-firmware.\n");
+
+	blob = qla2x00_request_firmware(vha);
+	if (!blob) {
+		ql_log(ql_log_warn, vha, 0x0092,
+		       "-> Firmware file not found.\n");
+		vfree(header);
+		return QLA_FUNCTION_FAILED;
+	}
+
+	fwcode = (__force __be32 *)blob->fw->data;
+
+	if (blob->fw->size < sizeof(*header)) {
+		ql_log(ql_log_fatal, vha, 0x0093,
+		       "Firmware image too small (%zu bytes, need %zu).\n",
+		       blob->fw->size, sizeof(*header));
+		vfree(header);
+		return QLA_FUNCTION_FAILED;
+	}
+
+	rval = qla29xx_validate_firmware_image(vha, fwcode);
+	if (rval != QLA_SUCCESS) {
+		ql_log(ql_log_fatal, vha, 0x0093,
+		       "Unable to verify integrity of firmware image\n");
+		vfree(header);
+		return QLA_FUNCTION_FAILED;
+	}
+
+	/* byte-swap big-endian firmware header to CPU endian */
+	for (i = 0; i < sizeof(*header) / sizeof(__be32); i++)
+		((uint32_t *)header)[i] = be32_to_cpu(fwcode[i]);
+
+	/*
+	 * Section lengths are dword counts from the firmware image. Reject
+	 * values that would overflow the u32 fields on conversion to bytes
+	 * (<< 2) or that exceed the image, before using them below.
+	 */
+	max_dwords = blob->fw->size >> 2;
+	if (header->header_length > max_dwords ||
+	    header->tim_length > max_dwords ||
+	    header->array1_length > max_dwords ||
+	    header->array2_length > max_dwords ||
+	    header->array3_length > max_dwords ||
+	    header->array4_length > max_dwords) {
+		ql_log(ql_log_fatal, vha, 0x0098,
+		       "FW image has invalid section length.\n");
+		goto img_corrupt;
+	}
+
+	header->header_length <<= 2;
+	header->tim_length <<= 2;
+	header->array1_length <<= 2;
+	header->array2_length <<= 2;
+	header->array3_length <<= 2;
+	header->array4_length <<= 2;
+
+	if (!header->segment_size) {
+		ql_log(ql_log_fatal, vha, 0x0098,
+		       "FW image has zero segment_size.\n");
+		goto img_corrupt;
+	}
+
+	{
+		size_t fw_size = blob->fw->size;
+		size_t offset;
+
+		/* TIM */
+		offset = header->header_length;
+		if (offset + header->tim_length > fw_size) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			       "FW image too small for TIM (%#zx > %#zx).\n",
+			       offset + header->tim_length, fw_size);
+			goto img_corrupt;
+		}
+		rval = qla29xx_process_rd_image(vha, header,
+			(__be32 *)((char *)fwcode + offset),
+			TIM, TIM_DEST_ADDR, header->tim_length);
+		if (rval) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			       "-> Failed load TIM\n");
+			goto img_corrupt;
+		}
+
+		/* Array 1 */
+		offset += header->tim_length;
+		if (offset + header->array1_length > fw_size) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			       "FW image too small for Arr1 (%#zx > %#zx).\n",
+			       offset + header->array1_length, fw_size);
+			goto img_corrupt;
+		}
+		rval = qla29xx_process_rd_image(vha, header,
+			(__be32 *)((char *)fwcode + offset),
+			ARR1, header->array1_destination_addr,
+			header->array1_length);
+		if (rval) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			       "-> Failed load Arr1\n");
+			goto img_corrupt;
+		}
+
+		/* Array 2 */
+		offset += header->array1_length;
+		if (offset + header->array2_length > fw_size) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			       "FW image too small for Arr2 (%#zx > %#zx).\n",
+			       offset + header->array2_length, fw_size);
+			goto img_corrupt;
+		}
+		rval = qla29xx_process_rd_image(vha, header,
+			(__be32 *)((char *)fwcode + offset),
+			ARR2, header->array2_destination_addr,
+			header->array2_length);
+		if (rval) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			       "-> Failed load Arr2\n");
+			goto img_corrupt;
+		}
+
+		/* Array 3: FW_DUMP template */
+		if (offset + 10 * sizeof(__be32) > fw_size) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			       "FW image too small for risc_attr.\n");
+			goto img_corrupt;
+		}
+		array1_addr = (__be32 *)((char *)fwcode +
+				header->header_length +
+				header->tim_length);
+		risc_attr = be32_to_cpu(array1_addr[9]);
+	}
+	templates = (risc_attr & BIT_9) ? 2 : 1;
+	ql_dbg(ql_dbg_init, vha, 0x0160,
+	       "-> templates = %u\n", templates);
+
+	array3_offset = header->header_length + header->tim_length +
+		header->array1_length + header->array2_length;
+	if (array3_offset > blob->fw->size) {
+		ql_log(ql_log_fatal, vha, 0x0098,
+		       "FW image too small for Arr3 (%#lx > %#zx).\n",
+		       array3_offset, blob->fw->size);
+		goto img_corrupt;
+	}
+
+	array3_addr = (__be32 *)(((char *)fwcode) + array3_offset);
+	for (j = 0; j < templates; j++, fwdt++) {
+		vfree(fwdt->template);
+		fwdt->template = NULL;
+		fwdt->length = 0;
+
+		if ((char *)&array3_addr[7] >
+		    (char *)blob->fw->data + blob->fw->size) {
+			ql_log(ql_log_warn, vha, 0x0169,
+			       "-> fwdt%u template header exceeds firmware blob.\n",
+			       j);
+			goto failed;
+		}
+
+		array_size = be32_to_cpu(array3_addr[2]);
+		ql_dbg(ql_dbg_init, vha, 0x0161,
+		       "-> fwdt%u template array at %p (%#x dwords)\n",
+		       j, array3_addr, array_size);
+
+		if (!array_size || !~array_size || array_size <= 8) {
+			ql_dbg(ql_dbg_init, vha, 0x0162,
+			       "-> fwdt%u failed to read array\n", j);
+			goto failed;
+		}
+
+		array3_addr += 7;
+		array_size -= 8;
+
+		if ((char *)&array3_addr[array_size] >
+		    (char *)blob->fw->data + blob->fw->size) {
+			ql_log(ql_log_warn, vha, 0x0163,
+			    "-> fwdt%u template array exceeds firmware blob.\n",
+			    j);
+			goto failed;
+		}
+
+		ql_dbg(ql_dbg_init, vha, 0x0163,
+		       "-> fwdt%u template allocate template %#x words...\n",
+		       j, array_size);
+		fwdt->template = vmalloc_array(array_size, sizeof(*dcode));
+		if (!fwdt->template) {
+			ql_log(ql_log_warn, vha, 0x0164,
+			       "-> fwdt%u failed allocate template.\n", j);
+			goto failed;
+		}
+
+		dcode = fwdt->template;
+		for (i = 0; i < array_size; i++)
+			dcode[i] = (__force u32)array3_addr[i];
+
+		if (!qla27xx_fwdt_template_valid(dcode)) {
+			ql_log(ql_log_warn, vha, 0x0165,
+			       "-> fwdt%u failed template validate\n", j);
+			goto failed;
+		}
+
+		dlen = qla27xx_fwdt_template_size(dcode);
+		ql_dbg(ql_dbg_init, vha, 0x0166,
+		       "-> fwdt%u template size %#lx bytes (%#lx words)\n",
+		       j, dlen, dlen / sizeof(*dcode));
+		if (dlen > array_size * sizeof(*dcode)) {
+			ql_log(ql_log_warn, vha, 0x0167,
+			       "-> fwdt%u template exceeds array (%-lu bytes)\n",
+			       j, dlen - array_size * sizeof(*dcode));
+			goto failed;
+		}
+
+		fwdt->length = dlen;
+		ql_dbg(ql_dbg_init, vha, 0x0168,
+		       "-> fwdt%u loaded template ok\n", j);
+
+		array3_addr += array_size + 1;
+	}
+	vfree(header);
+	return QLA_SUCCESS;
+
+failed:
+	vfree(fwdt->template);
+	fwdt->template = NULL;
+	fwdt->length = 0;
+img_corrupt:
+	vfree(header);
+	return QLA_FUNCTION_FAILED;
+}
+
+static int
+qla29xx_load_fw_template(scsi_qla_host_t *vha)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct fwdt *fwdt = ha->fwdt;
+	uint templates = 1;
+	void *fw_dump_tmplt;
+	uint32_t *dcode;
+	void *buf = NULL;
+	uint32_t len;
+	uint32_t template_size;
+	uint j = 0;
+	int rval = 0;
+
+	ql_dbg(ql_dbg_init, vha, 0x01bf,
+	       "-> templates = %u fw_dump_tmplt_len = 0x%x\n",
+	       templates, ha->fw_dump_tmplt_len);
+
+	if (!ha->fw_dump_tmplt_len) {
+		ql_dbg(ql_dbg_init, vha, 0x01bf,
+		       "no fw dump template available\n");
+		return QLA_SUCCESS;
+	}
+
+	fw_dump_tmplt = kzalloc(ha->fw_dump_tmplt_len, GFP_KERNEL);
+	if (!fw_dump_tmplt) {
+		ql_log(ql_log_info, vha, 0x0013,
+		       "Unable to allocate memory\n");
+		return QLA_FUNCTION_FAILED;
+	}
+
+	buf = qla29xx_read_optrom_data(vha, FLT_REG_FW_DUMP_TMPLT, 0,
+				       fw_dump_tmplt, 0,
+				       ha->fw_dump_tmplt_len);
+	if (!buf) {
+		ql_log(ql_log_info, vha, 0x0013,
+		       "Unable to read fw dump temp info.\n");
+		goto free_fw_dump;
+	}
+
+	ql_dump_buffer(ql_dbg_init, vha, 0x006b,
+		       (char *)fw_dump_tmplt, 1024);
+
+	for (j = 0; j < templates; j++, fwdt++) {
+		vfree(fwdt->template);
+		fwdt->template = NULL;
+		fwdt->length = 0;
+
+		template_size = le32_to_cpu(((__le32 *)fw_dump_tmplt)[2]);
+
+		ql_dbg(ql_dbg_init, vha, 0x0161,
+		       "-> fwdt%u template array at %p (0x%x bytes)\n",
+		       j, fw_dump_tmplt, template_size);
+
+		if (!template_size || !~template_size ||
+		    template_size > ha->fw_dump_tmplt_len) {
+			ql_dbg(ql_dbg_init, vha, 0x0162,
+			       "-> fwdt%u failed to read array\n", j);
+			goto failed;
+		}
+
+		fwdt->template = vmalloc(template_size);
+		if (!fwdt->template) {
+			ql_log(ql_log_warn, vha, 0x0164,
+			       "-> fwdt%u failed allocate template.\n", j);
+			goto failed;
+		}
+
+		dcode = fwdt->template;
+		memcpy((char *)dcode, (char *)fw_dump_tmplt, template_size);
+
+		if (!qla27xx_fwdt_template_valid(dcode)) {
+			ql_log(ql_log_warn, vha, 0x0165,
+			       "-> fwdt%u failed template validate (rval %x)\n",
+			       j, rval);
+			goto failed;
+		}
+
+		len = qla27xx_fwdt_template_size(dcode);
+		if (len > template_size) {
+			ql_log(ql_log_warn, vha, 0x0167,
+			       "-> fwdt%u template exceeds array (%-u bytes)\n",
+			       j, len - template_size);
+			goto failed;
+		}
+
+		fwdt->length = len;
+	}
+
+	kfree(fw_dump_tmplt);
+	return QLA_SUCCESS;
+
+failed:
+	if (fwdt->template) {
+		vfree(fwdt->template);
+		fwdt->template = NULL;
+		fwdt->length = 0;
+	}
+free_fw_dump:
+	kfree(fw_dump_tmplt);
+	return QLA_FUNCTION_FAILED;
+}
+
+int
+qla29xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
+{
+	int rval;
+
+	*srisc_addr = 0x100000;
+
+	if (ql2xfwloadbin == 2) {
+		rval = qla29xx_load_risc_blob(vha, srisc_addr);
+		if (rval != QLA_SUCCESS) {
+			ql_log(ql_log_fatal, vha, 0x019e,
+			       "Failed to load firmware from file.\n");
+			return QLA_FUNCTION_FAILED;
+		}
+	} else {
+		rval = qla28xx_load_flash_firmware(vha);
+		if (rval != QLA_SUCCESS) {
+			ql_log(ql_log_fatal, vha, 0x019e,
+			       "Failed to load flash firmware.\n");
+			return QLA_FUNCTION_FAILED;
+		}
+
+		rval = qla29xx_load_fw_template(vha);
+		if (rval != QLA_SUCCESS) {
+			ql_log(ql_log_warn, vha, 0x01bd,
+			       "failed to read firmware template\n");
+			return QLA_FUNCTION_FAILED;
+		}
+	}
+
+	return rval;
+}
+
 void
 qla2x00_try_to_stop_firmware(scsi_qla_host_t *vha)
 {
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 2b232b225c90..d49dacf6d576 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -618,7 +618,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
 
 int
 qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t risc_addr,
-    uint32_t risc_code_size)
+		 uint32_t risc_code_size, int opt)
 {
 	int rval;
 	struct qla_hw_data *ha = vha->hw;
@@ -651,7 +651,14 @@ qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t risc_addr,
 		mcp->out_mb |= MBX_4;
 	}
 
-	mcp->in_mb = MBX_1|MBX_0;
+	if (IS_QLA29XX(ha)) {
+		mcp->mb[9] = opt;
+		mcp->out_mb |= MBX_9;
+		mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+	} else {
+		mcp->in_mb = MBX_1|MBX_0;
+	}
+
 	mcp->tov = MBX_TOV_SECONDS;
 	mcp->flags = 0;
 	rval = qla2x00_mailbox_command(vha, mcp);
@@ -846,15 +853,19 @@ qla28xx_load_flash_firmware(scsi_qla_host_t *vha)
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	if (!IS_QLA28XX(ha))
+	if (!IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return rval;
 
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x11a6,
 	       "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_LOAD_FLASH_FIRMWARE;
+	mcp->mb[1] = 0;
 	mcp->out_mb = MBX_2 | MBX_1 | MBX_0;
-	mcp->in_mb = MBX_0;
+	if (IS_QLA29XX(ha))
+		mcp->in_mb = MBX_1 | MBX_0;
+	else
+		mcp->in_mb = MBX_0;
 	mcp->tov = MBX_TOV_SECONDS;
 	mcp->flags = 0;
 	rval = qla2x00_mailbox_command(vha, mcp);
diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c
index 298c060c1292..2366cca51bce 100644
--- a/drivers/scsi/qla2xxx/qla_nx.c
+++ b/drivers/scsi/qla2xxx/qla_nx.c
@@ -2663,7 +2663,7 @@ qla82xx_write_flash_data(struct scsi_qla_host *vha, __le32 *dwptr,
 
 			ret = qla2x00_load_ram(vha, optrom_dma,
 			    (ha->flash_data_off | faddr),
-			    OPTROM_BURST_DWORDS);
+			    OPTROM_BURST_DWORDS, 0);
 			if (ret != QLA_SUCCESS) {
 				ql_log(ql_log_warn, vha, 0xb01e,
 				    "Unable to burst-write optrom segment "
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index c3eb941f71ad..6560e58ad87c 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -2640,6 +2640,45 @@ static struct isp_operations qla27xx_isp_ops = {
 	.initialize_adapter	= qla2x00_initialize_adapter,
 };
 
+static struct isp_operations qla29xx_isp_ops = {
+	.pci_config		= qla25xx_pci_config,
+	.reset_chip		= qla24xx_reset_chip,
+	.chip_diag		= qla24xx_chip_diag,
+	.config_rings		= qla24xx_config_rings,
+	.reset_adapter		= qla24xx_reset_adapter,
+	.nvram_config		= qla81xx_nvram_config,
+	.update_fw_options	= qla24xx_update_fw_options,
+	.load_risc		= qla29xx_load_risc,
+	.pci_info_str		= qla24xx_pci_info_str,
+	.fw_version_str		= qla24xx_fw_version_str,
+	.intr_handler		= qla24xx_intr_handler,
+	.enable_intrs		= qla24xx_enable_intrs,
+	.disable_intrs		= qla24xx_disable_intrs,
+	.abort_command		= qla24xx_abort_command,
+	.target_reset		= qla24xx_abort_target,
+	.lun_reset		= qla24xx_lun_reset,
+	.fabric_login		= qla24xx_login_fabric,
+	.fabric_logout		= qla24xx_fabric_logout,
+	.calc_req_entries	= NULL,
+	.build_iocbs		= NULL,
+	.prep_ms_iocb		= qla24xx_prep_ms_iocb,
+	.prep_ms_fdmi_iocb	= qla24xx_prep_ms_fdmi_iocb,
+	.read_nvram		= NULL,
+	.write_nvram		= NULL,
+	.fw_dump		= qla27xx_fwdump,
+	.mpi_fw_dump		= qla27xx_mpi_fwdump,
+	.beacon_on		= qla24xx_beacon_on,
+	.beacon_off		= qla24xx_beacon_off,
+	.beacon_blink		= qla83xx_beacon_blink,
+	.read_optrom		= qla25xx_read_optrom_data,
+	.write_optrom		= qla24xx_write_optrom_data,
+	.get_flash_version	= qla24xx_get_flash_version,
+	.start_scsi_mq		= qla2xxx_dif_start_scsi_mq,
+	.abort_isp		= qla2x00_abort_isp,
+	.iospace_config		= qla83xx_iospace_config,
+	.initialize_adapter	= qla2x00_initialize_adapter,
+};
+
 static inline void
 qla2x00_set_isp_flags(struct qla_hw_data *ha)
 {
@@ -3232,7 +3271,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 		ha->gid_list_info_size = 8;
 		ha->optrom_size = OPTROM_SIZE_28XX;
 		ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
-		ha->isp_ops = &qla27xx_isp_ops;
+		ha->isp_ops = &qla29xx_isp_ops;
 		ha->flash_conf_off = ~0;
 		ha->flash_data_off = ~0;
 		ha->nvram_conf_off = ~0;
@@ -7676,6 +7715,7 @@ qla2x00_timer(struct timer_list *t)
 #define FW_ISP8031	9
 #define FW_ISP27XX	10
 #define FW_ISP28XX	11
+#define FW_ISP29XX	12
 
 #define FW_FILE_ISP21XX	"ql2100_fw.bin"
 #define FW_FILE_ISP22XX	"ql2200_fw.bin"
@@ -7689,6 +7729,7 @@ qla2x00_timer(struct timer_list *t)
 #define FW_FILE_ISP8031	"ql8300_fw.bin"
 #define FW_FILE_ISP27XX	"ql2700_fw.bin"
 #define FW_FILE_ISP28XX	"ql2800_fw.bin"
+#define FW_FILE_ISP29XX	"ql2900_fw.bin"
 
 
 static DEFINE_MUTEX(qla_fw_lock);
@@ -7706,6 +7747,7 @@ static struct fw_blob qla_fw_blobs[] = {
 	{ .name = FW_FILE_ISP8031, },
 	{ .name = FW_FILE_ISP27XX, },
 	{ .name = FW_FILE_ISP28XX, },
+	{ .name = FW_FILE_ISP29XX, },
 	{ .name = NULL, },
 };
 
@@ -7739,6 +7781,8 @@ qla2x00_request_firmware(scsi_qla_host_t *vha)
 		blob = &qla_fw_blobs[FW_ISP27XX];
 	} else if (IS_QLA28XX(ha)) {
 		blob = &qla_fw_blobs[FW_ISP28XX];
+	} else if (IS_QLA29XX(ha)) {
+		blob = &qla_fw_blobs[FW_ISP29XX];
 	} else {
 		return NULL;
 	}
@@ -8445,3 +8489,4 @@ MODULE_FIRMWARE(FW_FILE_ISP2300);
 MODULE_FIRMWARE(FW_FILE_ISP2322);
 MODULE_FIRMWARE(FW_FILE_ISP24XX);
 MODULE_FIRMWARE(FW_FILE_ISP25XX);
+MODULE_FIRMWARE(FW_FILE_ISP29XX);
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
index 6b9938d1a750..381d09145378 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -1915,7 +1915,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, __le32 *dwptr, uint32_t faddr,
 			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
 			    "Write burst (%#lx dwords)...\n", dburst);
 			ret = qla2x00_load_ram(vha, optrom_dma,
-			    flash_data_addr(ha, faddr), dburst);
+			    flash_data_addr(ha, faddr), dburst, 0);
 			if (!ret) {
 				liter += dburst - 1;
 				faddr += dburst - 1;
@@ -3515,7 +3515,7 @@ qla28xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
 		ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
 		    "Write burst (%#lx dwords)...\n", dburst);
 		rval = qla2x00_load_ram(vha, optrom_dma,
-		    flash_data_addr(ha, faddr), dburst);
+		    flash_data_addr(ha, faddr), dburst, 0);
 		if (rval != QLA_SUCCESS) {
 			ql_log(ql_log_warn, vha, 0x7097,
 			    "Failed burst write at %x (%p/%#llx)...\n",
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 06/88] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (4 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 05/88] scsi: qla2xxx: Add FC operational firmware load for 29xx Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:13   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 07/88] scsi: qla2xxx: Add flash block read/write BSG support for 29xx Nilesh Javali
                   ` (81 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

From: Manish Rangankar <mrangankar@marvell.com>

qla2x00_sysfs_read_vpd() called ha->isp_ops->read_optrom() a second
time after releasing optrom_mutex. The repeated read is redundant and,
unlike the first, runs without optrom_mutex held, exposing flash access
to concurrent optrom operations. Drop the duplicate call.

Fixes: 5fa8774c7f38 ("scsi: qla2xxx: Add 28xx flash primary/secondary status/image mechanism")
Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_attr.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 6a05ce195aa0..800751ab562a 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -580,7 +580,6 @@ qla2x00_sysfs_read_vpd(struct file *filp, struct kobject *kobj,
 	ha->isp_ops->read_optrom(vha, ha->vpd, faddr, ha->vpd_size);
 	mutex_unlock(&ha->optrom_mutex);
 
-	ha->isp_ops->read_optrom(vha, ha->vpd, faddr, ha->vpd_size);
 skip:
 	return memory_read_from_buffer(buf, count, &off, ha->vpd, ha->vpd_size);
 }
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 07/88] scsi: qla2xxx: Add flash block read/write BSG support for 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (5 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 06/88] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:21   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 08/88] scsi: qla2xxx: Add BSG MPI firmware load/dump " Nilesh Javali
                   ` (80 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

From: Manish Rangankar <mrangankar@marvell.com>

Introduce QL_VND_READ_FLASH_BLOCK and QL_VND_WRITE_FLASH_BLOCK
BSG vendor commands so that userspace tools can perform flash
block-level operations on 29xx adapters via the isp_ops
interface.

Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_bsg.c | 298 ++++++++++++++++++++++++++++-----
 drivers/scsi/qla2xxx/qla_bsg.h |  16 ++
 drivers/scsi/qla2xxx/qla_def.h |   7 +
 drivers/scsi/qla2xxx/qla_os.c  |  24 ++-
 4 files changed, 304 insertions(+), 41 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 5e910b5ca670..ff7421f3535d 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -1407,23 +1407,14 @@ qla24xx_iidma(struct bsg_job *bsg_job)
 
 static int
 qla2x00_optrom_setup(struct bsg_job *bsg_job, scsi_qla_host_t *vha,
-	uint8_t is_update)
+	uint32_t start, uint8_t is_update)
 {
-	struct fc_bsg_request *bsg_request = bsg_job->request;
-	uint32_t start = 0;
 	int valid = 0;
 	struct qla_hw_data *ha = vha->hw;
 
 	if (unlikely(pci_channel_offline(ha->pdev)))
 		return -EINVAL;
 
-	start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
-	if (start > ha->optrom_size) {
-		ql_log(ql_log_warn, vha, 0x7055,
-		    "start %d > optrom_size %d.\n", start, ha->optrom_size);
-		return -EINVAL;
-	}
-
 	if (ha->optrom_state != QLA_SWAITING) {
 		ql_log(ql_log_info, vha, 0x7056,
 		    "optrom_state %d.\n", ha->optrom_state);
@@ -1431,42 +1422,79 @@ qla2x00_optrom_setup(struct bsg_job *bsg_job, scsi_qla_host_t *vha,
 	}
 
 	ha->optrom_region_start = start;
-	ql_dbg(ql_dbg_user, vha, 0x7057, "is_update=%d.\n", is_update);
-	if (is_update) {
-		if (ha->optrom_size == OPTROM_SIZE_2300 && start == 0)
-			valid = 1;
-		else if (start == (ha->flt_region_boot * 4) ||
-		    start == (ha->flt_region_fw * 4))
-			valid = 1;
-		else if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) ||
-		    IS_CNA_CAPABLE(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) ||
-		    IS_QLA28XX(ha))
-			valid = 1;
-		if (!valid) {
-			ql_log(ql_log_warn, vha, 0x7058,
-			    "Invalid start region 0x%x/0x%x.\n", start,
-			    bsg_job->request_payload.payload_len);
+
+	if (IS_QLA29XX(ha)) {
+		if (start > ha->optrom_size) {
+			ql_log(ql_log_warn, vha, 0x7055,
+			    "start %d > optrom_size %d.\n", start,
+			    ha->optrom_size);
 			return -EINVAL;
 		}
 
-		ha->optrom_region_size = start +
-		    bsg_job->request_payload.payload_len > ha->optrom_size ?
-		    ha->optrom_size - start :
-		    bsg_job->request_payload.payload_len;
-		ha->optrom_state = QLA_SWRITING;
+		if (is_update) {
+			ha->optrom_region_size = start +
+			    bsg_job->request_payload.payload_len >
+			    ha->optrom_size ?
+			    ha->optrom_size - start :
+			    bsg_job->request_payload.payload_len;
+			ha->optrom_state = QLA_SWRITING;
+		} else {
+			ha->optrom_region_size = start +
+			    bsg_job->reply_payload.payload_len >
+			    ha->optrom_size ?
+			    ha->optrom_size - start :
+			    bsg_job->reply_payload.payload_len;
+			ha->optrom_state = QLA_SREADING;
+		}
 	} else {
-		ha->optrom_region_size = start +
-		    bsg_job->reply_payload.payload_len > ha->optrom_size ?
-		    ha->optrom_size - start :
-		    bsg_job->reply_payload.payload_len;
-		ha->optrom_state = QLA_SREADING;
+		if (start > ha->optrom_size) {
+			ql_log(ql_log_warn, vha, 0x7055,
+			    "start %d > optrom_size %d.\n", start,
+			    ha->optrom_size);
+			return -EINVAL;
+		}
+
+		ql_dbg(ql_dbg_user, vha, 0x7057,
+		    "is_update=%d.\n", is_update);
+		if (is_update) {
+			if (ha->optrom_size == OPTROM_SIZE_2300 && start == 0)
+				valid = 1;
+			else if (start == (ha->flt_region_boot * 4) ||
+			    start == (ha->flt_region_fw * 4))
+				valid = 1;
+			else if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) ||
+			    IS_CNA_CAPABLE(ha) || IS_QLA2031(ha) ||
+			    IS_QLA27XX(ha) || IS_QLA28XX(ha))
+				valid = 1;
+			if (!valid) {
+				ql_log(ql_log_warn, vha, 0x7058,
+				    "Invalid start region 0x%x/0x%x.\n",
+				    start,
+				    bsg_job->request_payload.payload_len);
+				return -EINVAL;
+			}
+
+			ha->optrom_region_size = start +
+			    bsg_job->request_payload.payload_len >
+			    ha->optrom_size ?
+			    ha->optrom_size - start :
+			    bsg_job->request_payload.payload_len;
+			ha->optrom_state = QLA_SWRITING;
+		} else {
+			ha->optrom_region_size = start +
+			    bsg_job->reply_payload.payload_len >
+			    ha->optrom_size ?
+			    ha->optrom_size - start :
+			    bsg_job->reply_payload.payload_len;
+			ha->optrom_state = QLA_SREADING;
+		}
 	}
 
 	ha->optrom_buffer = vzalloc(ha->optrom_region_size);
 	if (!ha->optrom_buffer) {
 		ql_log(ql_log_warn, vha, 0x7059,
-		    "Read: Unable to allocate memory for optrom retrieval "
-		    "(%x)\n", ha->optrom_region_size);
+		    "%s: Unable to allocate memory for optrom retrieval (%x)\n",
+		    __func__, ha->optrom_region_size);
 
 		ha->optrom_state = QLA_SWAITING;
 		return -ENOMEM;
@@ -1478,17 +1506,25 @@ qla2x00_optrom_setup(struct bsg_job *bsg_job, scsi_qla_host_t *vha,
 static int
 qla2x00_read_optrom(struct bsg_job *bsg_job)
 {
+	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
+	uint32_t start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
 	int rval = 0;
 
 	if (ha->flags.nic_core_reset_hdlr_active)
 		return -EBUSY;
 
+	if (IS_QLA29XX(ha)) {
+		ql_log(ql_log_warn, vha, 0x7070,
+		    "Legacy optrom read not supported on 29xx.\n");
+		return -EINVAL;
+	}
+
 	mutex_lock(&ha->optrom_mutex);
-	rval = qla2x00_optrom_setup(bsg_job, vha, 0);
+	rval = qla2x00_optrom_setup(bsg_job, vha, start, 0);
 	if (rval) {
 		mutex_unlock(&ha->optrom_mutex);
 		return rval;
@@ -1515,14 +1551,16 @@ qla2x00_read_optrom(struct bsg_job *bsg_job)
 static int
 qla2x00_update_optrom(struct bsg_job *bsg_job)
 {
+	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
+	uint32_t start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
 	int rval = 0;
 
 	mutex_lock(&ha->optrom_mutex);
-	rval = qla2x00_optrom_setup(bsg_job, vha, 1);
+	rval = qla2x00_optrom_setup(bsg_job, vha, start, 1);
 	if (rval) {
 		mutex_unlock(&ha->optrom_mutex);
 		return rval;
@@ -1554,6 +1592,182 @@ qla2x00_update_optrom(struct bsg_job *bsg_job)
 	return rval;
 }
 
+/**
+ * qla29xx_bsg_flash_block_read - Read flash block for QLA29XX.
+ * @bsg_job: BSG job structure.
+ *
+ * Returns 0 on success, error code on failure.
+ */
+static int qla29xx_bsg_flash_block_read(struct bsg_job *bsg_job)
+{
+	struct fc_bsg_request *bsg_req = bsg_job->request;
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
+	scsi_qla_host_t *vha = shost_priv(host);
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_block_rw *brcmd;
+	void *buf;
+	uint16_t opts = 0;
+	int rval = 0;
+
+	if (bsg_job->request_len < sizeof(struct fc_bsg_request) +
+	    2 * sizeof(uint32_t) + sizeof(struct qla_block_rw))
+		return -EINVAL;
+
+	brcmd =
+	(struct qla_block_rw *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2];
+
+	ql_log(ql_log_info, vha, 0x7062,
+	       "%s: region 0x%x options 0x%x rw_length 0x%x offset 0x%x chunk_length 0x%x\n",
+		__func__, brcmd->region, brcmd->options, brcmd->rw_length,
+		brcmd->region_offset, brcmd->chunk_length);
+
+	if (brcmd->region > U16_MAX) {
+		ql_log(ql_log_warn, vha, 0x7066,
+		       "%s: invalid region 0x%x\n", __func__, brcmd->region);
+		return -EINVAL;
+	}
+
+	mutex_lock(&ha->optrom_mutex);
+	rval = qla2x00_optrom_setup(bsg_job, vha, brcmd->region_offset, 0);
+	if (rval) {
+		mutex_unlock(&ha->optrom_mutex);
+		return rval;
+	}
+
+	check_and_set_mbc_bits(brcmd->options, opts, QLA_IS_TIM, BIT_15);
+	check_and_set_mbc_bits(brcmd->options, opts, QLA_IS_SECURE, BIT_7);
+	check_and_set_mbc_bits(brcmd->options, opts, QLA_UPDATE_MBR, BIT_8);
+
+	if (!ha->isp_ops->read_optrom_region) {
+		vfree(ha->optrom_buffer);
+		ha->optrom_buffer = NULL;
+		ha->optrom_state = QLA_SWAITING;
+		mutex_unlock(&ha->optrom_mutex);
+		return -EINVAL;
+	}
+
+	buf = ha->isp_ops->read_optrom_region(vha, brcmd->region, opts,
+				ha->optrom_buffer, ha->optrom_region_start,
+				ha->optrom_region_size);
+	if (!buf) {
+		ql_log(ql_log_warn, vha, 0x7063,
+			"%s failed to read flash region 0x%x\n",
+			__func__, brcmd->region);
+		bsg_reply->result = -EINVAL;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+							EXT_STATUS_MAILBOX;
+		bsg_reply->reply_payload_rcv_len = 0;
+	} else {
+		bsg_reply->result = DID_OK;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+			EXT_STATUS_OK;
+
+		ql_dump_buffer(ql_dbg_user + ql_dbg_verbose, vha, 0x72a6,
+			       ha->optrom_buffer, ha->optrom_region_size);
+
+		sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+				    bsg_job->reply_payload.sg_cnt,
+				    ha->optrom_buffer,
+				    ha->optrom_region_size);
+
+		bsg_reply->reply_payload_rcv_len = ha->optrom_region_size;
+	}
+	vfree(ha->optrom_buffer);
+	ha->optrom_buffer = NULL;
+	ha->optrom_state = QLA_SWAITING;
+	mutex_unlock(&ha->optrom_mutex);
+	bsg_job_done(bsg_job, bsg_reply->result,
+		     bsg_reply->reply_payload_rcv_len);
+
+	return rval;
+}
+
+/**
+ * qla29xx_bsg_flash_block_write - Write flash block for QLA29XX.
+ * @bsg_job: BSG job structure.
+ *
+ * Returns 0 on success, error code on failure.
+ */
+static int qla29xx_bsg_flash_block_write(struct bsg_job *bsg_job)
+{
+	struct fc_bsg_request *bsg_req = bsg_job->request;
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
+	scsi_qla_host_t *vha = shost_priv(host);
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_block_rw *bwcmd;
+	uint16_t opts = 0;
+	int rval = 0;
+
+	if (bsg_job->request_len < sizeof(struct fc_bsg_request) +
+	    2 * sizeof(uint32_t) + sizeof(struct qla_block_rw))
+		return -EINVAL;
+
+	bwcmd =
+	   (struct qla_block_rw *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2];
+
+	ql_log(ql_log_info, vha, 0x7064,
+	       "%s: region 0x%x options 0x%x rw_length 0x%x offset 0x%x chunk_length 0x%x\n",
+		__func__, bwcmd->region, bwcmd->options, bwcmd->rw_length,
+		bwcmd->region_offset, bwcmd->chunk_length);
+
+	if (bwcmd->region > U16_MAX) {
+		ql_log(ql_log_warn, vha, 0x7067,
+		       "%s: invalid region 0x%x\n", __func__, bwcmd->region);
+		return -EINVAL;
+	}
+
+	mutex_lock(&ha->optrom_mutex);
+	rval = qla2x00_optrom_setup(bsg_job, vha, bwcmd->region_offset, 1);
+	if (rval) {
+		mutex_unlock(&ha->optrom_mutex);
+		return rval;
+	}
+
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+			bsg_job->request_payload.sg_cnt, ha->optrom_buffer,
+			ha->optrom_region_size);
+
+	ql_dump_buffer(ql_dbg_user + ql_dbg_verbose, vha, 0x73a6,
+		       ha->optrom_buffer, ha->optrom_region_size);
+
+	check_and_set_mbc_bits(bwcmd->options, opts, QLA_IS_TIM, BIT_15);
+	check_and_set_mbc_bits(bwcmd->options, opts, QLA_IS_SECURE, BIT_7);
+	check_and_set_mbc_bits(bwcmd->options, opts, QLA_UPDATE_MBR, BIT_8);
+
+	if (!ha->isp_ops->write_optrom_region) {
+		vfree(ha->optrom_buffer);
+		ha->optrom_buffer = NULL;
+		ha->optrom_state = QLA_SWAITING;
+		mutex_unlock(&ha->optrom_mutex);
+		return -EINVAL;
+	}
+
+	rval = ha->isp_ops->write_optrom_region(vha, bwcmd->region, opts,
+				ha->optrom_buffer, ha->optrom_region_start,
+				ha->optrom_region_size);
+	if (rval) {
+		ql_log(ql_log_warn, vha, 0x7065,
+			"%s failed to write flash %x\n", __func__, rval);
+		bsg_reply->result = -EINVAL;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+							EXT_STATUS_MAILBOX;
+	} else {
+		bsg_reply->result = DID_OK;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+			EXT_STATUS_OK;
+	}
+	vfree(ha->optrom_buffer);
+	ha->optrom_buffer = NULL;
+	ha->optrom_state = QLA_SWAITING;
+	mutex_unlock(&ha->optrom_mutex);
+	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+	bsg_job_done(bsg_job, bsg_reply->result,
+			bsg_reply->reply_payload_rcv_len);
+	return 0;
+}
+
 static int
 qla2x00_update_fru_versions(struct bsg_job *bsg_job)
 {
@@ -3007,6 +3221,12 @@ qla2x00_process_vendor_specific(struct scsi_qla_host *vha, struct bsg_job *bsg_j
 	case QL_VND_MBX_PASSTHRU:
 		return qla2x00_mailbox_passthru(bsg_job);
 
+	case QL_VND_READ_FLASH_BLOCK:
+		return qla29xx_bsg_flash_block_read(bsg_job);
+
+	case QL_VND_WRITE_FLASH_BLOCK:
+		return qla29xx_bsg_flash_block_write(bsg_job);
+
 	default:
 		return -ENOSYS;
 	}
diff --git a/drivers/scsi/qla2xxx/qla_bsg.h b/drivers/scsi/qla2xxx/qla_bsg.h
index a920c8e482bc..ca0d83986b57 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.h
+++ b/drivers/scsi/qla2xxx/qla_bsg.h
@@ -40,6 +40,8 @@
 #define QL_VND_MBX_PASSTHRU		0x2B
 #define QL_VND_DPORT_DIAGNOSTICS_V2	0x2C
 #define QL_VND_IMG_SET_VALID	0x30
+#define QL_VND_READ_FLASH_BLOCK		0x33
+#define QL_VND_WRITE_FLASH_BLOCK	0x34
 
 /* BSG Vendor specific subcode returns */
 #define EXT_STATUS_OK			0
@@ -83,6 +85,20 @@
 #define ELS_OPCODE_BYTE			0x10
 
 /* BSG Vendor specific definations */
+
+#define QLA_IS_TIM	0x1
+#define QLA_IS_SECURE	0x2
+#define QLA_UPDATE_MBR	0x4
+
+struct qla_block_rw {
+	uint32_t region;
+	uint32_t rw_length;
+	uint32_t options;
+	uint32_t region_offset;
+	uint32_t chunk_length;
+	uint8_t  reserved[44];
+} __packed;
+
 #define A84_ISSUE_WRITE_TYPE_CMD        0
 #define A84_ISSUE_READ_TYPE_CMD         1
 #define A84_CLEANUP_CMD                 2
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 1ec7ee578e0c..719b6a1f9123 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -3534,6 +3534,13 @@ struct isp_operations {
 	int (*write_optrom)(struct scsi_qla_host *, void *, uint32_t,
 		uint32_t);
 
+	void *(*read_optrom_region)(struct scsi_qla_host *vha,
+		uint16_t reg_code, uint16_t opts, void *buf,
+		uint32_t offset, uint32_t length);
+	int (*write_optrom_region)(struct scsi_qla_host *vha,
+		uint16_t reg_code, uint16_t opts, void *buf,
+		uint32_t offset, uint32_t length);
+
 	int (*get_flash_version) (struct scsi_qla_host *, void *);
 	int (*start_scsi) (srb_t *);
 	int (*start_scsi_mq) (srb_t *);
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 6560e58ad87c..948242f0088e 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -2640,6 +2640,24 @@ static struct isp_operations qla27xx_isp_ops = {
 	.initialize_adapter	= qla2x00_initialize_adapter,
 };
 
+static void *
+qla29xx_read_optrom_stub(struct scsi_qla_host *vha, void *buf,
+			 uint32_t offset, uint32_t length)
+{
+	ql_dbg(ql_dbg_init, vha, 0x0191,
+	    "read_optrom not supported on 29xx, use read_optrom_region.\n");
+	return NULL;
+}
+
+static int
+qla29xx_write_optrom_stub(struct scsi_qla_host *vha, void *buf,
+			  uint32_t offset, uint32_t length)
+{
+	ql_dbg(ql_dbg_init, vha, 0x0192,
+	    "write_optrom not supported on 29xx, use write_optrom_region.\n");
+	return QLA_FUNCTION_FAILED;
+}
+
 static struct isp_operations qla29xx_isp_ops = {
 	.pci_config		= qla25xx_pci_config,
 	.reset_chip		= qla24xx_reset_chip,
@@ -2670,8 +2688,10 @@ static struct isp_operations qla29xx_isp_ops = {
 	.beacon_on		= qla24xx_beacon_on,
 	.beacon_off		= qla24xx_beacon_off,
 	.beacon_blink		= qla83xx_beacon_blink,
-	.read_optrom		= qla25xx_read_optrom_data,
-	.write_optrom		= qla24xx_write_optrom_data,
+	.read_optrom		= qla29xx_read_optrom_stub,
+	.write_optrom		= qla29xx_write_optrom_stub,
+	.read_optrom_region	= qla29xx_read_optrom_data,
+	.write_optrom_region	= qla29xx_write_optrom_data,
 	.get_flash_version	= qla24xx_get_flash_version,
 	.start_scsi_mq		= qla2xxx_dif_start_scsi_mq,
 	.abort_isp		= qla2x00_abort_isp,
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 08/88] scsi: qla2xxx: Add BSG MPI firmware load/dump for 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (6 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 07/88] scsi: qla2xxx: Add flash block read/write BSG support for 29xx Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:13   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 09/88] scsi: qla2xxx: Add 128-byte IOCB definitions " Nilesh Javali
                   ` (79 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

From: Manish Rangankar <mrangankar@marvell.com>

Add BSG vendor commands for loading and dumping MPI firmware on
29xx adapters.  This extends the existing BSG infrastructure with
the necessary mailbox wrappers and flash helpers for MPI
operations.

Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_bsg.c | 189 +++++++++++++++++++++++++++++++++
 drivers/scsi/qla2xxx/qla_bsg.h |  18 ++++
 drivers/scsi/qla2xxx/qla_def.h |   5 +
 drivers/scsi/qla2xxx/qla_gbl.h |   5 +
 drivers/scsi/qla2xxx/qla_mbx.c |  45 ++++++++
 drivers/scsi/qla2xxx/qla_sup.c | 117 ++++++++++++++++++++
 6 files changed, 379 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index ff7421f3535d..5c6aedb3179a 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -1768,6 +1768,189 @@ static int qla29xx_bsg_flash_block_write(struct bsg_job *bsg_job)
 	return 0;
 }
 
+static int
+qla29xx_mpi_optrom_setup(struct bsg_job *bsg_job, scsi_qla_host_t *vha,
+	uint32_t start, uint8_t is_update)
+{
+	struct qla_hw_data *ha = vha->hw;
+
+	if (unlikely(pci_channel_offline(ha->pdev)))
+		return -EINVAL;
+
+	if (ha->optrom_state != QLA_SWAITING) {
+		ql_log(ql_log_info, vha, 0x7068,
+		       "optrom_state %d.\n", ha->optrom_state);
+		return -EBUSY;
+	}
+
+	ha->optrom_region_start = start;
+	if (is_update) {
+		ha->optrom_region_size = bsg_job->request_payload.payload_len;
+		ha->optrom_state = QLA_SWRITING;
+	} else {
+		ha->optrom_region_size = bsg_job->reply_payload.payload_len;
+		ha->optrom_state = QLA_SREADING;
+	}
+
+	ha->optrom_buffer = vzalloc(ha->optrom_region_size);
+	if (!ha->optrom_buffer) {
+		ql_log(ql_log_warn, vha, 0x7069,
+		       "%s: Unable to allocate memory for optrom retrieval (%x)\n",
+		       __func__, ha->optrom_region_size);
+		ha->optrom_state = QLA_SWAITING;
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int qla2900_bsg_dump_mpi(struct bsg_job *bsg_job)
+{
+	struct fc_bsg_request *bsg_req = bsg_job->request;
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
+	scsi_qla_host_t *vha = shost_priv(host);
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_load_dump_mpi *dmcmd;
+	uint16_t opts = 0;
+	int rval = 0;
+
+	if (bsg_job->request_len < sizeof(struct fc_bsg_request) +
+	    2 * sizeof(uint32_t) + sizeof(struct qla_load_dump_mpi))
+		return -EINVAL;
+
+	dmcmd =
+	(struct qla_load_dump_mpi *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2];
+
+	ql_log(ql_log_info, vha, 0xffff,
+	       "%s: mpi_address 0x%x mpi options 0x%x length 0x%x\n",
+		__func__, dmcmd->mpi_address, dmcmd->options, dmcmd->length);
+
+	mutex_lock(&ha->optrom_mutex);
+	rval = qla29xx_mpi_optrom_setup(bsg_job, vha, dmcmd->mpi_address, 1);
+	if (rval) {
+		mutex_unlock(&ha->optrom_mutex);
+		return rval;
+	}
+
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+			  bsg_job->request_payload.sg_cnt, ha->optrom_buffer,
+			  ha->optrom_region_size);
+
+	ql_dump_buffer(ql_dbg_init, vha, 0x00d7, ha->optrom_buffer,
+			ha->optrom_region_size);
+
+	check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_SECURE_ENABLE,
+			       BIT_3);
+	check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_OTP_PROV, BIT_4);
+	check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_DEV_CSR, BIT_5);
+	check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_AUTH_CMD_BIN,
+			       BIT_6);
+	check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_MLDSA_ALGO,
+			       BIT_9);
+
+	rval = qla29xx_mpi_optrom_data(vha, opts, ha->optrom_buffer,
+				       ha->optrom_region_start,
+				       ha->optrom_region_size,
+				       QLA29XX_MPI_OP_DUMP);
+	if (rval) {
+		ql_log(ql_log_warn, vha, 0xffff,
+			"%s failed mpi dump %x\n", __func__, rval);
+		bsg_reply->result = -EINVAL;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+							EXT_STATUS_MAILBOX;
+	} else {
+		bsg_reply->result = DID_OK;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+			EXT_STATUS_OK;
+	}
+
+	vfree(ha->optrom_buffer);
+	ha->optrom_buffer = NULL;
+	ha->optrom_state = QLA_SWAITING;
+	mutex_unlock(&ha->optrom_mutex);
+	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+	bsg_job_done(bsg_job, bsg_reply->result,
+			bsg_reply->reply_payload_rcv_len);
+	return 0;
+}
+
+static int qla2900_bsg_load_mpi(struct bsg_job *bsg_job)
+{
+	struct fc_bsg_request *bsg_req = bsg_job->request;
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
+	scsi_qla_host_t *vha = shost_priv(host);
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_load_dump_mpi *lmcmd;
+	uint16_t opts = 0;
+	int rval = 0;
+
+	if (bsg_job->request_len < sizeof(struct fc_bsg_request) +
+	    2 * sizeof(uint32_t) + sizeof(struct qla_load_dump_mpi))
+		return -EINVAL;
+
+	lmcmd =
+	(struct qla_load_dump_mpi *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2];
+
+	ql_log(ql_log_info, vha, 0xffff,
+	       "%s: mpi_address 0x%x mpi options 0x%x length 0x%x\n",
+		__func__, lmcmd->mpi_address, lmcmd->options, lmcmd->length);
+
+	mutex_lock(&ha->optrom_mutex);
+	rval = qla29xx_mpi_optrom_setup(bsg_job, vha, lmcmd->mpi_address, 0);
+	if (rval) {
+		mutex_unlock(&ha->optrom_mutex);
+		return rval;
+	}
+
+	check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_SECURE_ENABLE,
+			       BIT_3);
+	check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_DEV_CSR, BIT_5);
+	check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_SHADOW_REGS,
+			       BIT_7);
+	check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_CA_CSR, BIT_8);
+	check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_MLDSA_ALGO,
+			       BIT_9);
+	check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_MLDSA_SIGNATURE,
+			       BIT_10);
+
+	rval = qla29xx_mpi_optrom_data(vha, opts, ha->optrom_buffer,
+				       ha->optrom_region_start,
+				       ha->optrom_region_size,
+				       QLA29XX_MPI_OP_LOAD);
+	if (rval) {
+		ql_log(ql_log_warn, vha, 0xffff,
+			"%s failed mpi load %x\n", __func__, rval);
+		bsg_reply->result = -EINVAL;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+							EXT_STATUS_MAILBOX;
+	} else {
+		bsg_reply->result = DID_OK;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+			EXT_STATUS_OK;
+	}
+
+	ql_dump_buffer(ql_dbg_init, vha, 0x00d7, ha->optrom_buffer,
+			ha->optrom_region_size);
+
+	sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+			    bsg_job->reply_payload.sg_cnt,
+			    ha->optrom_buffer,
+			    ha->optrom_region_size);
+
+	bsg_reply->reply_payload_rcv_len = ha->optrom_region_size;
+	vfree(ha->optrom_buffer);
+	ha->optrom_buffer = NULL;
+	ha->optrom_state = QLA_SWAITING;
+	mutex_unlock(&ha->optrom_mutex);
+	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+	bsg_job_done(bsg_job, bsg_reply->result,
+		     bsg_reply->reply_payload_rcv_len);
+
+	return rval;
+}
+
 static int
 qla2x00_update_fru_versions(struct bsg_job *bsg_job)
 {
@@ -3227,6 +3410,12 @@ qla2x00_process_vendor_specific(struct scsi_qla_host *vha, struct bsg_job *bsg_j
 	case QL_VND_WRITE_FLASH_BLOCK:
 		return qla29xx_bsg_flash_block_write(bsg_job);
 
+	case QL_VND_LOAD_MPI:
+		return qla2900_bsg_load_mpi(bsg_job);
+
+	case QL_VND_DUMP_MPI:
+		return qla2900_bsg_dump_mpi(bsg_job);
+
 	default:
 		return -ENOSYS;
 	}
diff --git a/drivers/scsi/qla2xxx/qla_bsg.h b/drivers/scsi/qla2xxx/qla_bsg.h
index ca0d83986b57..8a784b8226cc 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.h
+++ b/drivers/scsi/qla2xxx/qla_bsg.h
@@ -42,6 +42,8 @@
 #define QL_VND_IMG_SET_VALID	0x30
 #define QL_VND_READ_FLASH_BLOCK		0x33
 #define QL_VND_WRITE_FLASH_BLOCK	0x34
+#define QL_VND_LOAD_MPI			0x35
+#define QL_VND_DUMP_MPI			0x36
 
 /* BSG Vendor specific subcode returns */
 #define EXT_STATUS_OK			0
@@ -99,6 +101,22 @@ struct qla_block_rw {
 	uint8_t  reserved[44];
 } __packed;
 
+struct qla_load_dump_mpi {
+	uint32_t mpi_address;
+	uint32_t length;
+	uint32_t options;
+#define	QLA_LDM_SECURE_ENABLE	0x1
+#define	QLA_LDM_OTP_PROV	0x2
+#define	QLA_LDM_DEV_CSR		0x4
+#define	QLA_LDM_AUTH_CMD_BIN	0x8
+#define	QLA_LDM_SHADOW_REGS	0x10
+#define	QLA_LDM_CA_CSR		0x20
+#define	QLA_LDM_MLDSA_ALGO	0x40
+#define	QLA_LDM_MLDSA_SIGNATURE	0x80
+
+	uint8_t reserved[52];
+} __packed;
+
 #define A84_ISSUE_WRITE_TYPE_CMD        0
 #define A84_ISSUE_READ_TYPE_CMD         1
 #define A84_CLEANUP_CMD                 2
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 719b6a1f9123..7423687578dc 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -1548,6 +1548,11 @@ typedef struct {
 #define PD_STATE_PORT_LOGOUT			10
 #define PD_STATE_WAIT_PORT_LOGOUT_ACK		11
 
+enum qla29xx_mpi_optrom_op {
+	QLA29XX_MPI_OP_DUMP,
+	QLA29XX_MPI_OP_LOAD,
+};
+
 
 #define QLA_ZIO_MODE_6		(BIT_2 | BIT_1)
 #define QLA_ZIO_DISABLED	0
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index ca5a80b9c3d2..84aaff130400 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -697,6 +697,11 @@ extern int qla29xx_write_optrom_data(struct scsi_qla_host *vha,
 				     uint16_t reg_code, uint16_t opts,
 				     void *buf, uint32_t offset,
 				     uint32_t length);
+extern int qla29xx_mpi_optrom_data(struct scsi_qla_host *vha, uint16_t opts,
+				   void *buf, uint32_t offset, uint32_t length,
+				   enum qla29xx_mpi_optrom_op op);
+extern int qla29xx_load_dump_mpi(scsi_qla_host_t *vha, uint16_t opt,
+				 uint32_t mpi_addr, uint32_t dlen, dma_addr_t req_dma);
 /*
  * Global Function Prototypes in qla_dbg.c source file.
  */
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index d49dacf6d576..9c2633ca5036 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -7415,3 +7415,48 @@ int qla29xx_flash_block_write(scsi_qla_host_t *vha, dma_addr_t req_dma,
 
 	return rval;
 }
+
+int
+qla29xx_load_dump_mpi(scsi_qla_host_t *vha, uint16_t opt, uint32_t mpi_addr,
+		      uint32_t dlen, dma_addr_t req_dma)
+{
+	mbx_cmd_t mc;
+	mbx_cmd_t *mcp = &mc;
+	int rval = 0;
+
+	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0xffff,
+	       "Entered %s mpi_addr 0x%x len 0x%x opt 0x%x.\n",
+		__func__, mpi_addr, dlen, opt);
+
+	memset(mcp->mb, 0, sizeof(mcp->mb));
+
+	mcp->mb[0] = MBC_LOAD_DUMP_MPI_RAM;
+	mcp->mb[9] = opt;
+	mcp->mb[1] = LSW(mpi_addr);
+	mcp->mb[8] = MSW(mpi_addr);
+
+	mcp->mb[2] = MSW(req_dma);
+	mcp->mb[3] = LSW(req_dma);
+	mcp->mb[6] = MSW(MSD(req_dma));
+	mcp->mb[7] = LSW(MSD(req_dma));
+
+	mcp->mb[4] = MSW(dlen);
+	mcp->mb[5] = LSW(dlen);
+
+	mcp->out_mb = MBX_9|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+	mcp->in_mb = MBX_1|MBX_0;
+	mcp->tov = MBX_TOV_SECONDS;
+	mcp->flags = 0;
+
+	rval = qla2x00_mailbox_command(vha, mcp);
+	if (rval != QLA_SUCCESS)
+		ql_dbg(ql_dbg_mbx, vha, 0x110a,
+		       "Failed=%x mb=(0x%x,0x%x).\n",
+			rval, mcp->mb[0], mcp->mb[1]);
+	else
+		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110b,
+		       "Done %s mb=(0x%x,0x%x,0x%x).\n", __func__,
+		       mcp->mb[0], mcp->mb[1],  mcp->mb[2]);
+
+	return rval;
+}
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
index 381d09145378..1f5596a4c405 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -543,6 +543,123 @@ qla29xx_read_optrom_data(struct scsi_qla_host *vha, uint16_t reg_code,
 	return NULL;
 }
 
+static void set_chunk_mpi_bits(uint16_t *options, int count, int total)
+{
+	/* - Single chunk complete segment/image
+	 * - 1st chunk of a segment/image
+	 * - Last chunk of a segment/image
+	 */
+	if (total == 1)
+		*options |= BIT_2 | BIT_1;
+	else if (count == 0)
+		*options |= BIT_1;
+	else if (count == total - 1)
+		*options |= BIT_2;
+}
+
+/**
+ * qla29xx_mpi_optrom_data - Dump/load MPI optrom data for 29xx.
+ * @vha: Pointer to SCSI QLogic host structure.
+ * @opts: Options for the operation.
+ * @buf: Buffer to read from/write to.
+ * @offset: Offset into the device memory.
+ * @length: Length of data, in bytes.
+ * @op: Operation, either QLA29XX_MPI_OP_DUMP or QLA29XX_MPI_OP_LOAD.
+ *
+ * Returns:
+ * QLA_SUCCESS on success or an error code on failure.
+ */
+int
+qla29xx_mpi_optrom_data(struct scsi_qla_host *vha, uint16_t opts, void *buf,
+		       uint32_t offset, uint32_t length, enum qla29xx_mpi_optrom_op op)
+{
+	struct qla_hw_data *ha = vha->hw;
+	dma_addr_t optrom_dma;
+	void *optrom;
+	uint32_t mpi_addr, mpi_size, burst = 0;
+	uint32_t total_chunks = 0;
+	uint32_t *dcode, *fwcode;
+	uint16_t chunk_count = 0;
+	int rval = -EINVAL;
+
+	optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
+				    &optrom_dma, GFP_KERNEL);
+	if (!optrom) {
+		ql_log(ql_log_warn, vha, 0x0090,
+			"Unable to allocate memory for optrom burst read (%x KB).\n",
+			OPTROM_BURST_SIZE / 1024);
+		rval = -ENOMEM;
+		goto exit_mpi_op;
+	}
+
+	mpi_addr = offset;
+	dcode = (uint32_t *)optrom;
+	memset(dcode, 0, OPTROM_BURST_SIZE);
+
+	fwcode = (uint32_t *)buf;
+	mpi_size = length >> 2;
+	burst = OPTROM_BURST_SIZE >> 2;
+	total_chunks = (mpi_size + burst - 1) / burst;
+
+	while (mpi_size > 0) {
+		uint16_t options = 0;
+
+		if (burst > mpi_size)
+			burst = mpi_size;
+
+		set_chunk_mpi_bits(&options, chunk_count, total_chunks);
+
+		if (op == QLA29XX_MPI_OP_DUMP) {
+			options |= (BIT_0);
+			options |= opts;
+
+			ql_log(ql_log_info, vha, 0x008b,
+				"-> %s (DUMP): %#x <-(%#x words) (0x%x options) chunk (0x%x, 0x%x)\n",
+				__func__, mpi_addr, burst, options, chunk_count,
+				total_chunks);
+
+			memcpy(dcode, fwcode, burst * 4);
+
+			rval = qla29xx_load_dump_mpi(vha, options, mpi_addr, burst,
+						     optrom_dma);
+			if (rval) {
+				ql_log(ql_log_fatal, vha, 0x0098,
+					"Failed dump mpi firmware.\n");
+				goto free_buf;
+			}
+		} else if (op == QLA29XX_MPI_OP_LOAD) {
+			options |= opts;
+
+			ql_log(ql_log_info, vha, 0x008b,
+				"-> %s (LOAD): %#x <-(%#x words) (0x%x options) chunk (0x%x, 0x%x)\n",
+				__func__, mpi_addr, burst, options, chunk_count,
+				total_chunks);
+
+			rval = qla29xx_load_dump_mpi(vha, options, mpi_addr, burst,
+						     optrom_dma);
+			if (rval) {
+				ql_log(ql_log_fatal, vha, 0x0098,
+					"Failed load mpi firmware.\n");
+				goto free_buf;
+			}
+			memcpy(fwcode, dcode, burst * 4);
+		}
+
+		chunk_count++;
+		fwcode += burst;
+		mpi_addr += burst;
+		mpi_size -= burst;
+	}
+
+	rval = QLA_SUCCESS;
+free_buf:
+	dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom,
+			  optrom_dma);
+
+exit_mpi_op:
+	return rval;
+}
+
 /*
  * NVRAM support routines
  */
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 09/88] scsi: qla2xxx: Add 128-byte IOCB definitions for 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (7 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 08/88] scsi: qla2xxx: Add BSG MPI firmware load/dump " Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:14   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 10/88] scsi: qla2xxx: Add extended status continuation and marker IOCBs Nilesh Javali
                   ` (78 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

From: Anil Gurumurthy <agurumurthy@marvell.com>

The 29xx series uses 128-byte IOCBs instead of the 64-byte IOCBs
used by earlier adapters.  Add a new header (qla_fw29.h) with the
extended IOCB structure definitions that match the 29xx firmware
interface.

Signed-off-by: Anil Gurumurthy <agurumurthy@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_fw29.h | 686 ++++++++++++++++++++++++++++++++
 1 file changed, 686 insertions(+)
 create mode 100644 drivers/scsi/qla2xxx/qla_fw29.h

diff --git a/drivers/scsi/qla2xxx/qla_fw29.h b/drivers/scsi/qla2xxx/qla_fw29.h
new file mode 100644
index 000000000000..efe1c60bee81
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_fw29.h
@@ -0,0 +1,686 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c)  2026- Marvell.
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#ifndef __QLA_FW29_H
+#define __QLA_FW29_H
+
+#include "qla_fw.h"
+
+/* Control Flags 2 common for cmd6 and 7 */
+#define CF2_VMID_ENABLE			BIT_0
+#define CF2_CSCTL_PRIORITY_TAG		BIT_1
+#define CF2_NO_TRNF_READY_ENABLE	BIT_2
+#define CF2_RX_ID_ENABLE		BIT_3
+
+/*
+ * vp_index layout for 29xx extended command IOCBs
+ * (cmd_type_6_ext, cmd_type_7_ext, cmd_type_crc_2_ext, ...):
+ *   bits [8:0]   - VP index (9 bits)
+ *   bits [15:9]  - reserved, must be zero
+ * Access on a host-endian value via le16_to_cpu(vp_index) & CMD_EXT_VP_INDEX_MASK.
+ */
+#define CMD_EXT_VP_INDEX_MASK		0x01ff
+/*
+ * ISP queue - command entry structure definition.
+ */
+#define NUM_CMD67_DSDS	4
+struct cmd_type_6_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	nport_handle;		/* N_PORT handle. */
+	__le16	timeout;		/* Command timeout. */
+
+	__le16	dseg_count;		/* Data segment count. */
+
+	__le16	fcp_rsp_dsd_len;	/* FCP_RSP DSD length. */
+
+	struct scsi_lun lun;		/* FCP LUN (BE). */
+
+	__le16	control_flags;		/* Control flags. */
+
+	__le16	fcp_cmnd_dseg_len;	/* Data segment length. */
+					/* Data segment address. */
+	__le64	 fcp_cmnd_dseg_address __packed;
+					/* Data segment address. */
+	__le64	 fcp_rsp_dseg_address __packed;
+
+	__le32	byte_count;		/* Total byte count. */
+	__le16	control_flags_2;		/* Control flags 2. */
+
+	__le16	vp_index;		/* VP Index 9bits*/
+	__le32	fburstlen_rxid;		/* First Burst length/RX ID */
+	__le16 io_tag;			/* I/O Tag */
+	uint8_t vl_n_fctl;		/* VL (7-4) | RSVD (3-2) | F_CTL [17] (1) | RSVD (0) */
+	uint8_t prtag_csctl;		/* Priority Tag or CS_CTL */
+	__le32	src_vm_id;		/* Source VM ID */
+	uint8_t reserved_2[16];		/* Reserved */
+	struct dsd64 dsd[NUM_CMD67_DSDS];		/* Data Segment Descriptors */
+};
+
+struct cmd_type_7_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+	uint32_t handle;		/* System handle. */
+	__le16	nport_handle;		/* N_PORT handle. */
+	__le16	timeout;		/* Command timeout. */
+
+	__le16	dseg_count;		/* Data segment count. */
+	uint16_t reserved_1;
+
+	struct scsi_lun lun;		/* FCP LUN (BE). */
+
+	__le16	task_mgmt_flags;	/* Task management flags. */
+
+	uint8_t task;
+	uint8_t crn;
+	uint8_t fcp_cdb[MAX_CMDSZ];	/* SCSI command words. */
+	__le32	byte_count;		/* Total byte count. */
+	__le16	ctrl_flags_2;		/* Control flags 2 */
+	__le16	vp_index;		/* VP Index 9bits*/
+	__le32	rx_id;			/* Receive Exchange ID */
+	__le16	io_tag;			/* I/O Tag */
+	uint8_t vl_n_fctl;		/* VL (7-4) | RSVD (3-2) | F_CTL [17] (1) | RSVD (0) */
+	uint8_t reserved_3[21];		/* Reserved */
+	struct dsd64 dsd[NUM_CMD67_DSDS];	/* Data Segment Descriptors */
+};
+
+struct cmd_type_crc_2_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	nport_handle;		/* N_PORT handle. */
+	__le16	timeout;		/* Command timeout. */
+
+	__le16	dseg_count;		/* Data segment count. */
+	__le16	fcp_rsp_dseg_len;	/* FCP_RSP DSD length. */
+
+	struct scsi_lun lun;		/* FCP LUN (BE). */
+
+	__le16	control_flags_1;		/* Control flags. */
+	__le16	fcp_cmnd_dseg_len;	/* Data segment length. */
+
+	__le64	 fcp_cmnd_dseg_address __packed;
+					/* Data segment address. */
+	__le64	 fcp_rsp_dseg_address __packed;
+
+	__le32	byte_count;		/* Total byte count. */
+
+	__le16	control_flags_2;		/* Control flags - 2 */
+	__le16	vp_index;		/* VP Index (bits [8:0]); bits [15:9] reserved.
+					 * See CMD_EXT_VP_INDEX_MASK.
+					 */
+
+	uint32_t reserved_1;
+
+	__le16	 iocb_tag; /* Unused */
+	__le16 vl_prio; /* Bit 1 - F_CTL, Bits 4-7 VL, rest are rsvd */
+
+	uint32_t reserved_2; /* 3C-3F offset */
+
+	__le32 ref_tag;
+	uint8_t ref_tag_mask[4];	/* Validation/Replacement Mask*/
+
+	__le16 app_tag;
+	uint8_t app_tag_mask[2];	/* Validation/Replacement Mask*/
+
+	__le16 blk_size;		/* Data size in bytes */
+	__le16 prot_opts;		/* Requested Data Protection Mode */
+
+	__le32 tot_byte_count;		/* Total byte count/ total data
+					 * transfer count
+					 */
+	union {
+		struct {
+			uint32_t	reserved_1; /* offset 54 */
+			uint16_t	reserved_2;
+			__le16		guard_seed; /* offset 5A */
+			struct dsd64	data_dsd[1];
+			uint32_t	reserved_5[2];
+			uint32_t	reserved_6;
+		} nobundling;
+		struct {
+			__le32	dif_byte_count;	/* Total DIF byte
+						 * count
+						 */
+			__le16	dseg_count;	/* Data segment count */
+			__le16 guard_seed;      /* Initial Guard Seed */
+			struct dsd64	data_dsd[1];
+			struct dsd64	dif_dsd;
+		} bundling;
+	} u;
+	uint8_t reserved_3[12];			/* MUST be set to 0. */
+};
+
+/*
+ * ISP queue - status entry structure definition.
+ */
+struct sts_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	comp_status;		/* Completion status. */
+	__le16	ox_id;			/* OX_ID used by the firmware. */
+
+	__le32	residual_len;		/* FW calc residual transfer length. */
+
+	union {
+		__le16 reserved_1;
+		__le16 nvme_rsp_pyld_len;
+	} u1;
+
+	__le16	state_flags;		/* State flags. */
+
+	__le16 read_sa_index;
+	__le16 wr_sa_index;
+	uint8_t	reserved_2[8];
+	uint8_t act_dif[8];
+	uint8_t exp_dif[8];
+	union {
+		struct {
+			__le32	rsp_data_len_dma;	/* FCP response data length  */
+			uint8_t reserved_3[76];
+		};
+		struct {
+			uint8_t nvme_ersp_data[32];
+			uint8_t reserved_4[48];
+		};
+		struct {
+			__le32	bid_rd_rsp_residual_count;	/* BID read rsp residual cnt */
+			__le16	retry_delay_timer;	/* Retry delay timer. */
+			__le16	scsi_status;		/* SCSI status. */
+			__le32	rsp_residual_count;	/* FCP RSP residual count. */
+			__le32	sense_len;		/* FCP SENSE length. */
+			__le32	rsp_data_len_ndma;	/* FCP response data length  */
+			uint8_t	data[60];	/* FCP rsp/sense information */
+		};
+	} u2;
+
+	/*
+	 * If DIF Error is set in comp_status, these additional fields are
+	 * defined:
+	 *
+	 * !!! NOTE: Firmware sends expected/actual DIF data in big endian
+	 * format; but all of the "data" field gets swab32-d in the beginning
+	 * of qla2900_status_entry().
+	 *
+	 * &data[10] : uint8_t report_runt_bg[2];	- computed guard
+	 * &data[12] : uint8_t actual_dif[8];		- DIF Data received
+	 * &data[20] : uint8_t expected_dif[8];		- DIF Data computed
+	 */
+};
+
+/*
+ * ISP queue - marker entry structure definition.
+ */
+struct mrk_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t handle_count;		/* Handle count. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	nport_handle;		/* N_PORT handle. */
+
+	uint8_t modifier;		/* Modifier (7-0). */
+	uint8_t reserved_1;
+
+	__le16	vp_index;	/* VP Index. 9bits*/
+	uint16_t reserved_3;
+
+	uint8_t lun[8];			/* FCP LUN (BE). */
+	uint8_t reserved_4[104];
+};
+
+/*
+ * ISP queue - CT Pass-Through entry structure definition.
+ */
+#define NUM_CT_DSDS	5
+struct ct_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System Defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	comp_status;		/* Completion status. */
+
+	__le16	nport_handle;		/* N_PORT handle. */
+
+	__le16	cmd_dsd_count;
+
+	__le16	vp_index;		/* vp index 9 bits*/
+
+	__le16	timeout;		/* Command timeout. */
+	uint16_t reserved_2;
+
+	__le16	rsp_dsd_count;
+
+	uint8_t reserved_3[10];
+	uint8_t reserved_4[28];		/* Reserved. */
+
+	__le32	rsp_byte_count;
+	__le32	cmd_byte_count;
+	struct dsd64 dsd[NUM_CT_DSDS];	/* Data Segment Descriptors */
+};
+
+/*
+ * ISP queue - PUREX IOCB entry structure definition
+ */
+struct purex_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	__le16	reserved1;
+	__le16	vp_idx;			/* VP index 9 bits*/
+
+	__le16	status_flags;
+	__le16	nport_handle;
+
+	__le16	frame_size;
+	__le16	trunc_frame_size;
+
+	__le32	rx_xchg_addr;
+
+	uint8_t d_id[3];
+	uint8_t r_ctl;
+
+	uint8_t s_id[3];
+	uint8_t cs_ctl;
+
+	uint8_t f_ctl[3];
+	uint8_t type;
+
+	__le16	seq_cnt;
+	uint8_t df_ctl;
+	uint8_t seq_id;
+
+	__le16	rx_id;
+	__le16	ox_id;
+	__le32	param;
+
+	uint8_t els_frame_payload[84];
+};
+
+/*
+ * ISP queue - ELS Pass-Through entry structure definition.
+ * ELS_EXT_EST_SOFI*: 4-bit sof_type for extended IOCBs (qla_fw.h EST_SOFI*
+ * is for els_entry_24xx byte layout).
+ */
+#define ELS_EXT_EST_SOFI3	(1 << 1)
+#define ELS_EXT_EST_SOFI2	(3 << 3)
+
+struct els_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System Defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	comp_status;		/* response only */
+	__le16	nport_handle;
+
+	__le16	tx_dsd_count;
+
+	__le16	vp_index : 9;		/* VP Index 9bits */
+	__le16	reserved_1_sof : 3;
+	__le16	sof_type : 4;
+
+	__le32	rx_xchg_address;	/* Receive exchange address. */
+	__le16	rx_dsd_count;
+
+	uint8_t opcode;
+	uint8_t reserved_2;
+
+	uint8_t d_id[3];
+	uint8_t s_id[3];
+
+	__le16	control_flags;		/* Control flags. */
+
+	union {
+		struct {
+			__le32	 rx_byte_count;
+			__le32	 tx_byte_count;
+
+			__le64	 tx_address __packed;	/* DSD 0 address. */
+			__le32	 tx_len;		/* DSD 0 length. */
+
+			__le64	 rx_address __packed;	/* DSD 1 address. */
+			__le32	 rx_len;		/* DSD 1 length. */
+		};
+		struct {
+			__le32	total_byte_count;
+			__le32	error_subcode_1;
+			__le32	error_subcode_2;
+			__le32	error_subcode_3;
+			uint8_t reserved_3[16];
+		};
+	};
+	uint8_t reserved_4[64];
+};
+
+struct els_sts_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System Defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	__le32	handle;		/* System handle. */
+
+	__le16	comp_status;
+
+	__le16	nport_handle;		/* N_PORT handle. */
+
+	__le16	reserved_1;
+
+	__le16	vp_index : 9;		/* VP Index 9bits */
+	__le16	reserved_1_sof : 3;
+	__le16	sof_type : 4;
+
+	__le32	rx_xchg_address;	/* Receive exchange address. */
+	__le16	reserved_2;
+
+	uint8_t opcode;
+	uint8_t reserved_3;
+
+	uint8_t d_id[3];
+	uint8_t s_id[3];
+
+	__le16	control_flags;		/* Control flags. */
+	__le32	total_byte_count;
+	__le32	error_subcode_1;
+	__le32	error_subcode_2;
+	__le32	error_subcode_3;
+
+	uint8_t	reserved_4[80];
+};
+
+struct logio_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	comp_status;		/* Completion status. */
+
+	__le16	nport_handle;		/* N_PORT handle. */
+
+	__le16	control_flags;		/* Control flags. */
+
+	__le16	vp_index;		/* VP Index 9bits*/
+
+	uint8_t port_id[3];		/* PortID of destination port. */
+
+	uint8_t rsp_size;		/* Response size in 32bit words. */
+
+	__le32	io_parameter[11];	/* General I/O parameters. */
+	uint8_t reserved_2[64];		/* Reserved*/
+};
+
+struct tsk_mgmt_entry_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t handle_count;		/* Handle count. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	nport_handle;		/* N_PORT handle. */
+
+	__le16	reserved_1;
+
+	__le16	delay;			/* Activity delay in seconds. */
+
+	__le16	timeout;		/* Command timeout. */
+
+	struct scsi_lun lun;		/* FCP LUN (BE). */
+
+	__le32	control_flags;		/* Control Flags. */
+
+	__le16	vp_index;	/* VP Index 9bits */
+
+	uint8_t reserved_3[98];
+};
+
+struct abort_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t handle_count;		/* Handle count. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	union {
+		__le16 nport_handle;            /* N_PORT handle. */
+		__le16 comp_status;             /* Completion status. */
+	};
+
+	__le16	options;		/* Options. */
+
+	uint32_t handle_to_abort;	/* System handle to abort. */
+
+	__le16	req_que_no;
+
+	__le16	vp_index;		/* VP Index 9bits*/
+	u8	reserved_2[4];
+	union {
+		struct {
+			__le16 abts_rty_cnt;
+			__le16 rsp_timeout;
+		} drv;
+		struct {
+			u8	ba_rjt_vendorUnique;
+			u8	ba_rjt_reasonCodeExpl;
+			u8	ba_rjt_reasonCode;
+			u8	reserved_3;
+		} fw;
+	};
+	u8	reserved_4[100];
+};
+
+struct abts_entry_24xx_ext {
+	uint8_t entry_type;
+	uint8_t entry_count;
+	uint8_t handle_count;
+	uint8_t entry_status;
+
+	__le32	handle;		/* type 0x55 only */
+
+	__le16	comp_status;		/* type 0x55 only */
+	__le16	nport_handle;		/* type 0x54 only */
+
+	__le16	control_flags;		/* type 0x55 only */
+	__le16	vp_idx : 9;		/* VP index 9 bits */
+	__le16	reserved_1_sof : 3;
+	__le16	sof_type : 4;		/* sof_type is upper nibble */
+
+	__le32	rx_xch_addr;
+
+	uint8_t d_id[3];
+	uint8_t r_ctl;
+
+	uint8_t s_id[3];
+	uint8_t cs_ctl;
+
+	uint8_t f_ctl[3];
+	uint8_t type;
+
+	__le16	seq_cnt;
+	uint8_t df_ctl;
+	uint8_t seq_id;
+
+	__le16	rx_id;
+	__le16	ox_id;
+
+	__le32	param;
+
+	union {
+		struct {
+			__le32	subcode3;
+			__le32	rsvd;
+			__le32	subcode1;
+			__le32	subcode2;
+		} error;
+		struct {
+			__le16	rsrvd1;
+			uint8_t last_seq_id;
+			uint8_t seq_id_valid;
+			__le16	aborted_rx_id;
+			__le16	aborted_ox_id;
+			__le16	high_seq_cnt;
+			__le16	low_seq_cnt;
+		} ba_acc;
+		struct {
+			uint8_t vendor_unique;
+			uint8_t explanation;
+			uint8_t reason;
+		} ba_rjt;
+	} payload;
+
+	__le32	rx_xch_addr_to_abort;
+	uint8_t reserved_2[64];
+} __packed;
+/*
+ * Virtual Port Control IOCB
+ */
+struct vp_ctrl_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	vp_idx_failed;
+
+	__le16	comp_status;		/* Completion status. */
+
+	__le16	command;
+
+	__le16	vp_count;
+
+	uint8_t vp_idx_map[16];
+	__le16	flags;
+	__le16	id;
+	uint16_t reserved_4;
+	__le16	hopct;
+	uint8_t reserved_5[88];
+};
+
+/*
+ * Modify Virtual Port Configuration IOCB
+ */
+struct vp_config_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t handle_count;
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	flags;
+
+	__le16	comp_status;		/* Completion status. */
+
+	uint8_t command;
+
+	uint8_t vp_count;
+
+	uint8_t vp_index1;
+	uint8_t vp_index2;
+
+	uint8_t options_idx1;
+	uint8_t hard_address_idx1;
+	uint16_t reserved_vp1;
+	uint8_t port_name_idx1[WWN_SIZE];
+	uint8_t node_name_idx1[WWN_SIZE];
+
+	uint8_t options_idx2;
+	uint8_t hard_address_idx2;
+	uint16_t reserved_vp2;
+	uint8_t port_name_idx2[WWN_SIZE];
+	uint8_t node_name_idx2[WWN_SIZE];
+	__le16	id;
+	uint16_t reserved_4;
+	__le16	hopct;
+	uint8_t reserved_5[66];
+};
+
+struct vp_rpt_id_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+	__le32 resv1;
+	uint8_t vp_acquired;
+	uint8_t vp_setup;
+	__le16	vp_idx : 9;		/* VP Index 9bits */
+	__le16	vp_status : 7;		/* VP Status 7bits */
+
+	uint8_t port_id[3];
+	uint8_t format;
+	union {
+		struct vp_rpt_id_ext_f1 {
+			/* format 1 fabric */
+			uint8_t vpstat1_subcode; /* vp_status=1 subcode */
+			uint8_t flags;
+
+			uint16_t fip_flags;
+			uint8_t rsv2[12];
+
+			uint8_t ls_rjt_vendor;
+			uint8_t ls_rjt_explanation;
+			uint8_t ls_rjt_reason;
+			uint8_t rsv3;
+			__le16	rsv8;
+			__le16	flogi_acc_payload_size;	/* bits [8:0] meaningful */
+			uint8_t port_name[8];
+			uint8_t node_name[8];
+			uint16_t bbcr;
+			uint8_t reserved_5[6];
+		} f1;
+		struct vp_rpt_id_ext_f2 { /* format 2: N2N direct connect */
+			uint8_t vpstat1_subcode;
+			uint8_t flags;
+			uint16_t fip_flags;
+			uint8_t rsv2[12];
+
+			uint8_t ls_rjt_vendor;
+			uint8_t ls_rjt_explanation;
+			uint8_t ls_rjt_reason;
+			uint8_t rsv3[5];
+
+			uint8_t port_name[8];
+			uint8_t node_name[8];
+			uint16_t bbcr;
+			uint8_t reserved_5[2];
+			uint8_t remote_nport_id[4];
+		} f2;
+	} u;
+};
+#endif
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 10/88] scsi: qla2xxx: Add extended status continuation and marker IOCBs
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (8 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 09/88] scsi: qla2xxx: Add 128-byte IOCB definitions " Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:10   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 11/88] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx Nilesh Javali
                   ` (77 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

From: Anil Gurumurthy <agurumurthy@marvell.com>

Add the 128-byte sts_cont_entry_ext_t and mrk_entry_ext_t
structures required by 29xx firmware.  Include the qla_fw29.h
header from qla_def.h so the new types are available throughout
the driver.

Signed-off-by: Anil Gurumurthy <agurumurthy@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_def.h | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 7423687578dc..4de0de5cccc8 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -337,6 +337,7 @@ static inline void wrt_reg_dword(volatile __le32 __iomem *addr, u32 data)
 
 #define MAX_CMDSZ	16		/* SCSI maximum CDB size. */
 #include "qla_fw.h"
+#include "qla_fw29.h"
 
 struct name_list_extended {
 	struct get_name_list_extended *l;
@@ -2360,6 +2361,15 @@ typedef struct {
 	uint8_t reserved_2[48];
 } mrk_entry_t;
 
+/* 29xx definitions */
+struct sts_cont_entry_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+	uint8_t data[124];		/* data */
+};
+
 /*
  * ISP queue - Management Server entry structure definition.
  */
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 11/88] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (9 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 10/88] scsi: qla2xxx: Add extended status continuation and marker IOCBs Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07 10:27   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 12/88] scsi: qla2xxx: Skip image-set-valid attribute " Nilesh Javali
                   ` (76 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

From: Anil Gurumurthy <agurumurthy@marvell.com>

Wire the 128-byte extended IOCB structures into the IO submission,
completion, and queue-management paths.  On 29xx adapters the driver
now builds cmd_type_6_ext / cmd_type_7_ext command IOCBs and processes
the corresponding extended status entries, while falling back to the
existing 64-byte IOCBs for earlier adapters.

Ring entry-size selection uses the qla_req_entry_size() /
qla_rsp_entry_size() helpers and ring slot advancement uses
qla_req_ring_advance() rather than open-coding IS_QLA29XX() branches
at every call site.

Signed-off-by: Anil Gurumurthy <agurumurthy@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_dbg.c    |  31 +-
 drivers/scsi/qla2xxx/qla_def.h    |  26 +-
 drivers/scsi/qla2xxx/qla_edif.c   |  18 +-
 drivers/scsi/qla2xxx/qla_fw29.h   |  77 +++
 drivers/scsi/qla2xxx/qla_gbl.h    |   4 +-
 drivers/scsi/qla2xxx/qla_init.c   |  53 +-
 drivers/scsi/qla2xxx/qla_inline.h | 174 +++++-
 drivers/scsi/qla2xxx/qla_iocb.c   | 990 +++++++++++++++++++++++++-----
 drivers/scsi/qla2xxx/qla_isr.c    |  55 +-
 drivers/scsi/qla2xxx/qla_mid.c    |  65 +-
 drivers/scsi/qla2xxx/qla_nvme.c   | 156 +++--
 drivers/scsi/qla2xxx/qla_os.c     |  39 +-
 drivers/scsi/qla2xxx/qla_target.c |  34 +-
 13 files changed, 1397 insertions(+), 325 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index a7e3ec9bba47..acb58daacf35 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -89,16 +89,17 @@ qla2xxx_copy_queues(struct qla_hw_data *ha, void *ptr)
 {
 	struct req_que *req = ha->req_q_map[0];
 	struct rsp_que *rsp = ha->rsp_q_map[0];
+	size_t req_entry_size = qla_req_entry_size(ha);
+	size_t rsp_entry_size = qla_rsp_entry_size(ha);
+
 	/* Request queue. */
-	memcpy(ptr, req->ring, req->length *
-	    sizeof(request_t));
+	memcpy(ptr, req->ring, req->length * req_entry_size);
 
 	/* Response queue. */
-	ptr += req->length * sizeof(request_t);
-	memcpy(ptr, rsp->ring, rsp->length  *
-	    sizeof(response_t));
+	ptr += req->length * req_entry_size;
+	memcpy(ptr, rsp->ring, rsp->length * rsp_entry_size);
 
-	return ptr + (rsp->length * sizeof(response_t));
+	return ptr + (rsp->length * rsp_entry_size);
 }
 
 int
@@ -606,6 +607,8 @@ qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, __be32 **last_chain)
 	struct req_que *req;
 	struct rsp_que *rsp;
 	int que;
+	size_t req_entry_size = qla_req_entry_size(ha);
+	size_t rsp_entry_size = qla_rsp_entry_size(ha);
 
 	if (!ha->mqenable)
 		return ptr;
@@ -623,19 +626,19 @@ qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, __be32 **last_chain)
 		q->chain_size = htonl(
 		    sizeof(struct qla2xxx_mqueue_chain) +
 		    sizeof(struct qla2xxx_mqueue_header) +
-		    (req->length * sizeof(request_t)));
+		    (req->length * req_entry_size));
 		ptr += sizeof(struct qla2xxx_mqueue_chain);
 
 		/* Add header. */
 		qh = ptr;
 		qh->queue = htonl(TYPE_REQUEST_QUEUE);
 		qh->number = htonl(que);
-		qh->size = htonl(req->length * sizeof(request_t));
+		qh->size = htonl(req->length * req_entry_size);
 		ptr += sizeof(struct qla2xxx_mqueue_header);
 
 		/* Add data. */
-		memcpy(ptr, req->ring, req->length * sizeof(request_t));
-		ptr += req->length * sizeof(request_t);
+		memcpy(ptr, req->ring, req->length * req_entry_size);
+		ptr += req->length * req_entry_size;
 	}
 
 	/* Response queues */
@@ -651,19 +654,19 @@ qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, __be32 **last_chain)
 		q->chain_size = htonl(
 		    sizeof(struct qla2xxx_mqueue_chain) +
 		    sizeof(struct qla2xxx_mqueue_header) +
-		    (rsp->length * sizeof(response_t)));
+		    (rsp->length * rsp_entry_size));
 		ptr += sizeof(struct qla2xxx_mqueue_chain);
 
 		/* Add header. */
 		qh = ptr;
 		qh->queue = htonl(TYPE_RESPONSE_QUEUE);
 		qh->number = htonl(que);
-		qh->size = htonl(rsp->length * sizeof(response_t));
+		qh->size = htonl(rsp->length * rsp_entry_size);
 		ptr += sizeof(struct qla2xxx_mqueue_header);
 
 		/* Add data. */
-		memcpy(ptr, rsp->ring, rsp->length * sizeof(response_t));
-		ptr += rsp->length * sizeof(response_t);
+		memcpy(ptr, rsp->ring, rsp->length * rsp_entry_size);
+		ptr += rsp->length * rsp_entry_size;
 	}
 
 	return ptr;
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 4de0de5cccc8..fcd185807e2b 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -577,7 +577,7 @@ struct srb_iocb {
 			__le16 comp_status;
 
 			uint8_t modifier;
-			uint8_t vp_index;
+			uint16_t vp_index;
 			uint16_t loop_id;
 		} tmf;
 		struct {
@@ -2528,6 +2528,14 @@ struct imm_ntfy_from_isp {
 #define REQUEST_ENTRY_SIZE	(sizeof(request_t))
 
 
+/*
+ * 29xx (qla29xx) uses 128-byte ring entries for both request and response
+ * queues.  These macros give the size of an extended IOCB slot and are
+ * used when allocating from / zeroing the 29xx request ring via ring_ext_ptr.
+ */
+#define RESPONSE_ENTRY_SIZE_EXT (sizeof(struct response_ext))
+#define REQUEST_ENTRY_SIZE_EXT  (sizeof(struct request_ext))
+
 
 /*
  * Switch info gathering structure.
@@ -3789,6 +3797,14 @@ struct rsp_que {
 	dma_addr_t  dma;
 	response_t *ring;
 	response_t *ring_ptr;
+	/*
+	 * 29xx extended IOCB ring (128-byte entries) aliases of ring/ring_ptr.
+	 * Allocated when IS_QLA29XX(ha); set up at queue-init time to point at
+	 * the same DMA memory as 'ring' but typed for the 29xx stride.  24xx
+	 * code paths walk via ring_ptr; 29xx paths walk via ring_ext_ptr.
+	 */
+	struct response_ext *ring_ext;
+	struct response_ext *ring_ext_ptr;
 	__le32	__iomem *rsp_q_in;	/* FWI2-capable only. */
 	__le32	__iomem *rsp_q_out;
 	uint16_t  ring_index;
@@ -3816,6 +3832,14 @@ struct req_que {
 	dma_addr_t  dma;
 	request_t *ring;
 	request_t *ring_ptr;
+	/*
+	 * 29xx extended IOCB ring (128-byte entries) aliases of ring/ring_ptr.
+	 * Allocated when IS_QLA29XX(ha); set up at queue-init time to point at
+	 * the same DMA memory as 'ring' but typed for the 29xx stride.  24xx
+	 * code paths must not run on 29xx HW, and vice-versa.
+	 */
+	struct request_ext *ring_ext;
+	struct request_ext *ring_ext_ptr;
 	__le32	__iomem *req_q_in;	/* FWI2-capable only. */
 	__le32	__iomem *req_q_out;
 	uint16_t  ring_index;
diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c
index eccedb38a515..4416197a35b0 100644
--- a/drivers/scsi/qla2xxx/qla_edif.c
+++ b/drivers/scsi/qla2xxx/qla_edif.c
@@ -2989,6 +2989,21 @@ qla28xx_start_scsi_edif(srb_t *sp)
 	struct req_que *req = sp->qpair->req;
 	spinlock_t *lock = sp->qpair->qp_lock_ptr;
 
+	/*
+	 * EDIF on 29xx is not supported by this driver yet.  The EDIF fast
+	 * path builds 64-byte cmd_type_6 IOCBs and advances req->ring_ptr
+	 * with a 64-byte stride, which would corrupt the 128-byte extended
+	 * request ring that 29xx hardware uses.  A proper 29xx EDIF port
+	 * requires a cmd_type_6_ext-shaped submission path; until that is
+	 * implemented, refuse the command rather than risk ring corruption.
+	 */
+	if (IS_QLA29XX(ha)) {
+		ql_log(ql_log_warn, vha, 0x13ae,
+		    "EDIF is not supported on 29xx hardware; failing cmd sp=%p.\n",
+		    sp);
+		return QLA_FUNCTION_FAILED;
+	}
+
 	/* Setup device pointers. */
 	cmd = GET_CMD_SP(sp);
 
@@ -3129,7 +3144,8 @@ qla28xx_start_scsi_edif(srb_t *sp)
 			 * Five DSDs are available in the Continuation
 			 * Type 1 IOCB.
 			 */
-			cont_pkt = qla2x00_prep_cont_type1_iocb(vha, req);
+			cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+			    ha, req);
 			cur_dsd = cont_pkt->dsd;
 			avail_dsds = 5;
 		}
diff --git a/drivers/scsi/qla2xxx/qla_fw29.h b/drivers/scsi/qla2xxx/qla_fw29.h
index efe1c60bee81..088a220472a5 100644
--- a/drivers/scsi/qla2xxx/qla_fw29.h
+++ b/drivers/scsi/qla2xxx/qla_fw29.h
@@ -95,6 +95,17 @@ struct cmd_type_7_ext {
 	struct dsd64 dsd[NUM_CMD67_DSDS];	/* Data Segment Descriptors */
 };
 
+/*
+ * Inline data-DSD capacity of the 29xx cmd_type_crc_2_ext IOCB.  Unlike
+ * cmd_type_6_ext / cmd_type_7_ext (which carry NUM_CMD67_DSDS inline DSDs),
+ * CRC_2 places the bulk of its DSDs in the separate CRC-context DMA; only
+ * a single data_dsd is carried inline in both the u.nobundling and
+ * u.bundling variants.  Use this constant wherever the IOCB-reservation
+ * calculator needs the CRC_2 ext inline capacity so it stays in sync with
+ * the firmware-facing layout below.
+ */
+#define NUM_CRC2_EXT_INLINE_DSDS	1
+
 struct cmd_type_crc_2_ext {
 	uint8_t entry_type;		/* Entry type. */
 	uint8_t entry_count;		/* Entry count. */
@@ -683,4 +694,70 @@ struct vp_rpt_id_entry_24xx_ext {
 		} f2;
 	} u;
 };
+
+/*
+ * ISP queue - 64-Bit addressing, continuation entry structure definition
+ * for the 29xx extended (128-byte) IOCB ring.  Mirrors cont_a64_entry_t
+ * but carries 10 DSDs per entry instead of 5.
+ */
+#define NUM_CONT1_DSDS	10
+struct cont_a64_entry_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+	uint32_t reserved;
+	struct dsd64 dsd[NUM_CONT1_DSDS];
+};
+
+/*
+ * 29xx extended Command Type FC-NVMe IOCB (128 bytes).
+ *
+ * The header layout up through 'byte_count' (offset 48) is identical to the
+ * 64-byte struct cmd_nvme used by 24xx-class adapters, so common code can
+ * populate those fields via either type.  Fields beyond 'byte_count' diverge:
+ * 29xx adds control_flags_2/vp_index/first_burst_rx_id/io_tag/..., drops
+ * port_id[3]+vp_index(byte), and carries NUM_NVME_DSDS inline DSDs.
+ */
+#define NUM_NVME_DSDS	4
+struct cmd_nvme_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+	__le16	nport_handle;		/* N_PORT handle. */
+	__le16	timeout;		/* Command timeout. */
+
+	__le16	dseg_count;		/* Data segment count. */
+	__le16	nvme_rsp_dsd_len;	/* NVMe RSP DSD length */
+
+	uint64_t rsvd;
+
+	__le16	control_flags;		/* Control Flags (see struct cmd_nvme) */
+	__le16	nvme_cmnd_dseg_len;			/* Data segment length. */
+
+	__le64	 nvme_cmnd_dseg_address __packed;	/* Data segment address. */
+
+	__le64	 nvme_rsp_dseg_address __packed;	/* Data segment address. */
+
+	__le32	byte_count;		/* Total byte count. */
+
+	__le16	control_flags_2;
+	/*
+	 * vp_index layout matches the other 29xx extended IOCBs: only bits
+	 * [8:0] are meaningful (see CMD_EXT_VP_INDEX_MASK).
+	 */
+	__le16	vp_index;
+	__le32	first_burst_rx_id;
+	__le16	io_tag;
+	uint8_t vl_n_fctl;	/* VL(7:4) | RSVD(3:2) | F_CTL[17](1) | RSVD(0) */
+	uint8_t prtag_csctl;	/* Priority Tag or CS_CTL */
+	__le32	src_vm_id;	/* Source VM ID */
+	uint8_t reserved_2[16];
+
+	struct dsd64 nvme_dsd[NUM_NVME_DSDS];
+};
+
 #endif
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 84aaff130400..04f4cfadc510 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -297,7 +297,9 @@ void qla_adjust_buf(struct scsi_qla_host *);
 void qla_els_pt_iocb(struct scsi_qla_host *vha,
 	struct els_entry_24xx *pkt, struct qla_els_pt_arg *a);
 cont_a64_entry_t *qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha,
-		struct req_que *que);
+		struct qla_hw_data *ha, struct req_que *que);
+struct cont_a64_entry_ext *qla2900_prep_cont_type1_iocb(scsi_qla_host_t *vha,
+		struct req_que *req);
 extern uint16_t qla2x00_calc_iocbs_32(uint16_t);
 extern uint16_t qla2x00_calc_iocbs_64(uint16_t);
 extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t);
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 79b8026cb33d..bd0684d97fef 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -3814,6 +3814,8 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
 	struct req_que *req = ha->req_q_map[0];
 	struct rsp_que *rsp = ha->rsp_q_map[0];
 	struct qla2xxx_fw_dump *fw_dump;
+	size_t req_entry_size = qla_req_entry_size(ha);
+	size_t rsp_entry_size = qla_rsp_entry_size(ha);
 
 	if (ha->fw_dump) {
 		ql_dbg(ql_dbg_init, vha, 0x00bd,
@@ -3852,9 +3854,9 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
 			 * Resizing must be done at end-of-dump processing.
 			 */
 			mq_size += (ha->max_req_queues - 1) *
-			    (req->length * sizeof(request_t));
+			    (req->length * req_entry_size);
 			mq_size += (ha->max_rsp_queues - 1) *
-			    (rsp->length * sizeof(response_t));
+			    (rsp->length * rsp_entry_size);
 		}
 		if (ha->tgt.atio_ring)
 			mq_size += ha->tgt.atio_q_length * sizeof(request_t);
@@ -3890,8 +3892,8 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
 		/* Add space for spare MPI fw dump. */
 		dump_size += ha->fwdt[1].dump_size;
 	} else {
-		req_q_size = req->length * sizeof(request_t);
-		rsp_q_size = rsp->length * sizeof(response_t);
+		req_q_size = req->length * req_entry_size;
+		rsp_q_size = rsp->length * rsp_entry_size;
 		dump_size = offsetof(struct qla2xxx_fw_dump, isp);
 		dump_size += fixed_size + mem_size + req_q_size + rsp_q_size
 			+ eft_size;
@@ -4479,15 +4481,33 @@ void
 qla2x00_init_response_q_entries(struct rsp_que *rsp)
 {
 	uint16_t cnt;
-	response_t *pkt;
 
 	rsp->ring_ptr = rsp->ring;
 	rsp->ring_index    = 0;
 	rsp->status_srb = NULL;
-	pkt = rsp->ring_ptr;
-	for (cnt = 0; cnt < rsp->length; cnt++) {
-		pkt->signature = RESPONSE_PROCESSED;
-		pkt++;
+
+	if (rsp->hw && IS_QLA29XX(rsp->hw)) {
+		/*
+		 * 29xx uses a 128-byte response-ring stride.  The signature
+		 * field offset matches response_t, but the entry pitch is
+		 * sizeof(struct response_ext); walk via ring_ext_ptr / struct response_ext.
+		 */
+		struct response_ext *pkt;
+
+		rsp->ring_ext_ptr = rsp->ring_ext;
+		pkt = rsp->ring_ext_ptr;
+		for (cnt = 0; cnt < rsp->length; cnt++) {
+			pkt->signature = RESPONSE_PROCESSED;
+			pkt++;
+		}
+	} else {
+		response_t *pkt;
+
+		pkt = rsp->ring_ptr;
+		for (cnt = 0; cnt < rsp->length; cnt++) {
+			pkt->signature = RESPONSE_PROCESSED;
+			pkt++;
+		}
 	}
 }
 
@@ -4808,7 +4828,12 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
 		req = ha->req_q_map[que];
 		if (!req || !test_bit(que, ha->req_qid_map))
 			continue;
-		req->out_ptr = (uint16_t *)(req->ring + req->length);
+		if (IS_QLA29XX(ha))
+			req->out_ptr =
+			    (uint16_t *)(req->ring_ext + req->length);
+		else
+			req->out_ptr =
+			    (uint16_t *)(req->ring + req->length);
 		*req->out_ptr = 0;
 		for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++)
 			req->outstanding_cmds[cnt] = NULL;
@@ -4819,13 +4844,19 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
 		req->ring_ptr  = req->ring;
 		req->ring_index    = 0;
 		req->cnt      = req->length;
+		if (IS_QLA29XX(ha))
+			req->ring_ext_ptr = req->ring_ext;
 	}
 
 	for (que = 0; que < ha->max_rsp_queues; que++) {
 		rsp = ha->rsp_q_map[que];
 		if (!rsp || !test_bit(que, ha->rsp_qid_map))
 			continue;
-		rsp->in_ptr = (uint16_t *)(rsp->ring + rsp->length);
+		if (IS_QLA29XX(ha))
+			rsp->in_ptr =
+			    (uint16_t *)(rsp->ring_ext + rsp->length);
+		else
+			rsp->in_ptr = (uint16_t *)(rsp->ring + rsp->length);
 		*rsp->in_ptr = 0;
 		/* Initialize response queue entries */
 		if (IS_QLAFX00(ha))
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index 53eaff1e0f65..cdbc3c5abf75 100644
--- a/drivers/scsi/qla2xxx/qla_inline.h
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -54,6 +54,55 @@ qla2x00_debounce_register(volatile __le16 __iomem *addr)
 	return (first);
 }
 
+/**
+ * qla29xx_calc_iocbs() - Determine number of Command-Type and Continuation
+ * IOCBs to allocate for the 29xx extended (128-byte) IOCB ring.
+ * @vha: HA context
+ * @dsds: number of data segment descriptors needed
+ * @iocb_dsds: number of DSDs embedded in the first (command) IOCB.  The
+ *	remaining DSDs ride on Continuation Type 1 Ext IOCBs which hold
+ *	NUM_CONT1_DSDS (10) each.
+ *
+ * Returns the total number of IOCB entries needed to carry @dsds.
+ */
+static inline uint16_t
+qla29xx_calc_iocbs(scsi_qla_host_t *vha, uint16_t dsds, uint8_t iocb_dsds)
+{
+	uint16_t iocbs = 1;
+
+	if (dsds > iocb_dsds) {
+		iocbs += (dsds - iocb_dsds) / NUM_CONT1_DSDS;
+		if ((dsds - iocb_dsds) % NUM_CONT1_DSDS)
+			iocbs++;
+	}
+	return iocbs;
+}
+
+/**
+ * qla_req_entry_size() - request-ring entry stride.
+ * @ha: HBA pointer
+ *
+ * Returns sizeof(struct request_ext) (128) on 29xx, sizeof(request_t) (64)
+ * everywhere else.
+ */
+static inline size_t
+qla_req_entry_size(struct qla_hw_data *ha)
+{
+	return IS_QLA29XX(ha) ? sizeof(struct request_ext) : sizeof(request_t);
+}
+
+/**
+ * qla_rsp_entry_size() - response-ring entry stride.
+ * @ha: HBA pointer
+ *
+ * Counterpart of qla_req_entry_size() for the response ring.
+ */
+static inline size_t
+qla_rsp_entry_size(struct qla_hw_data *ha)
+{
+	return IS_QLA29XX(ha) ? sizeof(struct response_ext) : sizeof(response_t);
+}
+
 static inline void
 qla2x00_poll(struct rsp_que *rsp)
 {
@@ -358,17 +407,132 @@ static inline void
 qla_83xx_start_iocbs(struct qla_qpair *qpair)
 {
 	struct req_que *req = qpair->req;
+	struct qla_hw_data *ha = qpair->vha->hw;
 
+	/*
+	 * 29xx uses the 128-byte-strided extended request ring; advance the
+	 * matching ring_ext_ptr so the next IOCB allocator sees the correct
+	 * slot.  All other 83xx-family generations (83xx/27xx/28xx) keep the
+	 * 64-byte ring_ptr.
+	 */
 	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else
-		req->ring_ptr++;
+	if (IS_QLA29XX(ha)) {
+		if (req->ring_index == req->length) {
+			req->ring_index = 0;
+			req->ring_ext_ptr = req->ring_ext;
+		} else {
+			req->ring_ext_ptr++;
+		}
+	} else {
+		if (req->ring_index == req->length) {
+			req->ring_index = 0;
+			req->ring_ptr = req->ring;
+		} else {
+			req->ring_ptr++;
+		}
+	}
 
 	wrt_reg_dword(req->req_q_in, req->ring_index);
 }
 
+/**
+ * qla_rsp_ring_advance() - Advance the response queue consumer pointer
+ * to the next IOCB slot, handling both 24xx (64-byte) and 29xx (128-byte)
+ * ring strides.
+ *
+ * On 29xx, ring_ext_ptr is the authoritative slot pointer (correct 128-byte
+ * pitch) and ring_ptr is kept in sync as a response_t view of the same slot
+ * so existing 24xx-shaped reads (rsp->ring_ptr->signature,
+ * (struct sts_entry_24xx *)rsp->ring_ptr, etc.) keep working unchanged; the
+ * first 64 bytes of struct response_ext are layout-compatible with response_t.
+ */
+static inline void
+qla_rsp_ring_advance(struct rsp_que *rsp)
+{
+	rsp->ring_index++;
+	if (rsp->ring_index == rsp->length) {
+		rsp->ring_index = 0;
+		rsp->ring_ptr = rsp->ring;
+		if (rsp->hw && IS_QLA29XX(rsp->hw))
+			rsp->ring_ext_ptr = rsp->ring_ext;
+	} else if (rsp->hw && IS_QLA29XX(rsp->hw)) {
+		rsp->ring_ext_ptr++;
+		rsp->ring_ptr = (response_t *)rsp->ring_ext_ptr;
+	} else {
+		rsp->ring_ptr++;
+	}
+}
+
+/**
+ * qla_req_ring_slot() - return the current request-ring producer slot.
+ * @ha: HBA pointer
+ * @req: request queue
+ *
+ * On 29xx the firmware-visible ring uses 128-byte-strided entries
+ * referenced by ring_ext_ptr; on earlier adapters the 64-byte ring
+ * referenced by ring_ptr is used.  The returned pointer is
+ * layout-compatible with request_t for common header writes; callers
+ * needing 29xx-specific fields should cast to struct request_ext.
+ */
+static inline void *
+qla_req_ring_slot(struct qla_hw_data *ha, struct req_que *req)
+{
+	return IS_QLA29XX(ha) ? (void *)req->ring_ext_ptr
+			      : (void *)req->ring_ptr;
+}
+
+/**
+ * qla_req_ring_advance() - advance request-ring producer pointer.
+ * @ha: HBA pointer
+ * @req: request queue
+ *
+ * Mirrors qla_rsp_ring_advance().  Does NOT publish the new producer
+ * index to firmware; callers that need to do so should follow with a
+ * wrt_reg_dword or qla_83xx_start_iocbs().
+ */
+static inline void
+qla_req_ring_advance(struct qla_hw_data *ha, struct req_que *req)
+{
+	req->ring_index++;
+	if (IS_QLA29XX(ha)) {
+		if (req->ring_index == req->length) {
+			req->ring_index = 0;
+			req->ring_ext_ptr = req->ring_ext;
+		} else {
+			req->ring_ext_ptr++;
+		}
+	} else {
+		if (req->ring_index == req->length) {
+			req->ring_index = 0;
+			req->ring_ptr = req->ring;
+		} else {
+			req->ring_ptr++;
+		}
+	}
+}
+
+/**
+ * qla_rsp_ring_rewind_to() - Restore the response queue consumer pointer
+ * to a previously-observed slot (used when we need to defer processing an
+ * IOCB whose continuation entries have not yet arrived).
+ * @rsp: response queue
+ * @pkt: 64-byte view of the slot to rewind to (captured from a prior read
+ *       of rsp->ring_ptr)
+ * @idx: matching ring_index value (also captured before the advance)
+ *
+ * On 29xx, pkt was originally obtained as (response_t *)rsp->ring_ext_ptr,
+ * so casting back to struct response_ext * recovers the 128-byte-stride slot
+ * pointer.
+ */
+static inline void
+qla_rsp_ring_rewind_to(struct rsp_que *rsp, response_t *pkt, uint16_t idx)
+{
+	rsp->ring_ptr = pkt;
+	rsp->ring_index = idx;
+	if (rsp->hw && IS_QLA29XX(rsp->hw))
+		rsp->ring_ext_ptr = (struct response_ext *)pkt;
+}
+
 static inline int
 qla2xxx_get_fc4_priority(struct scsi_qla_host *vha)
 {
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 9038f6723444..13882a399fbc 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -12,6 +12,19 @@
 #include <scsi/scsi_tcq.h>
 
 static int qla_start_scsi_type6(srb_t *sp);
+
+/*
+ * 29xx extended IOCB builders, implemented later in this file.  Forward
+ * declared here because the unified 24xx/29xx fast paths dispatch to them
+ * via IS_QLA29XX() branches.
+ */
+static void qla29xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7_ext *cmd_pkt,
+	uint16_t tot_dsds, struct req_que *req);
+static void qla29xx_build_scsi_type_6_iocbs(srb_t *sp,
+	struct cmd_type_6_ext *cmd_pkt, uint16_t tot_dsds);
+static int qla29xx_build_scsi_crc_2_iocbs(srb_t *sp,
+	struct cmd_type_crc_2_ext *cmd_pkt, uint16_t tot_dsds,
+	uint16_t tot_prot_dsds, uint16_t fw_prot_opts);
 /**
  * qla2x00_get_cmd_direction() - Determine control_flag data direction.
  * @sp: SCSI command
@@ -120,20 +133,15 @@ qla2x00_prep_cont_type0_iocb(struct scsi_qla_host *vha)
  * Returns a pointer to the continuation type 1 IOCB packet.
  */
 cont_a64_entry_t *
-qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct req_que *req)
+qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct qla_hw_data *ha,
+			      struct req_que *req)
 {
 	cont_a64_entry_t *cont_pkt;
 
 	/* Adjust ring index. */
-	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else {
-		req->ring_ptr++;
-	}
+	qla_req_ring_advance(ha, req);
 
-	cont_pkt = (cont_a64_entry_t *)req->ring_ptr;
+	cont_pkt = (cont_a64_entry_t *)qla_req_ring_slot(ha, req);
 
 	/* Load packet defaults. */
 	put_unaligned_le32(IS_QLAFX00(vha->hw) ? CONTINUE_A64_TYPE_FX00 :
@@ -284,7 +292,8 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
 			 * Five DSDs are available in the Continuation
 			 * Type 1 IOCB.
 			 */
-			cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req);
+			cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+			    vha->hw, vha->req);
 			cur_dsd = cont_pkt->dsd;
 			avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
 		}
@@ -464,16 +473,16 @@ qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req)
 	if (IS_P3P_TYPE(ha)) {
 		qla82xx_start_iocbs(vha);
 	} else {
-		/* Adjust ring index. */
-		req->ring_index++;
-		if (req->ring_index == req->length) {
-			req->ring_index = 0;
-			req->ring_ptr = req->ring;
-		} else
-			req->ring_ptr++;
+		/*
+		 * Adjust ring index.  29xx uses a 128-byte-strided ring, so
+		 * advance the extended pointer; all other generations advance
+		 * the 64-byte ring_ptr.
+		 */
+		qla_req_ring_advance(ha, req);
 
 		/* Set chip new ring index. */
-		if (ha->mqenable || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+		if (ha->mqenable || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+		    IS_QLA29XX(ha)) {
 			wrt_reg_dword(req->req_q_in, req->ring_index);
 		} else if (IS_QLA83XX(ha)) {
 			wrt_reg_dword(req->req_q_in, req->ring_index);
@@ -509,36 +518,46 @@ static int
 __qla2x00_marker(struct scsi_qla_host *vha, struct qla_qpair *qpair,
     uint16_t loop_id, uint64_t lun, uint8_t type)
 {
-	mrk_entry_t *mrk;
-	struct mrk_entry_24xx *mrk24 = NULL;
-	struct req_que *req = qpair->req;
 	struct qla_hw_data *ha = vha->hw;
+	struct mrk_entry_24xx *mrk24;
+	struct req_que *req = qpair->req;
 	scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
 
-	mrk = (mrk_entry_t *)__qla2x00_alloc_iocbs(qpair, NULL);
-	if (mrk == NULL) {
+	/*
+	 * FWI2-capable adapters use the 24xx marker layout; 29xx uses the
+	 * extended marker IOCB (128 bytes) whose first 64 bytes overlay
+	 * mrk_entry_24xx through 'lun', so the common header is written
+	 * through a struct mrk_entry_24xx * view and only vp_index (u8 vs
+	 * __le16) needs a stride-aware branch.  Pre-FWI2 adapters
+	 * (ISP21xx/22xx/23xx) use the legacy mrk_entry_t layout with a
+	 * 2-byte target ID and a 16-bit LUN.
+	 */
+	mrk24 = __qla2x00_alloc_iocbs(qpair, NULL);
+	if (!mrk24) {
 		ql_log(ql_log_warn, base_vha, 0x3026,
 		    "Failed to allocate Marker IOCB.\n");
-
-		return (QLA_FUNCTION_FAILED);
+		return QLA_FUNCTION_FAILED;
 	}
 
-	mrk24 = (struct mrk_entry_24xx *)mrk;
-
-	mrk->entry_type = MARKER_TYPE;
-	mrk->modifier = type;
+	mrk24->entry_type = MARKER_TYPE;
+	mrk24->modifier = type;
 	if (type != MK_SYNC_ALL) {
 		if (IS_FWI2_CAPABLE(ha)) {
 			mrk24->nport_handle = cpu_to_le16(loop_id);
 			int_to_scsilun(lun, (struct scsi_lun *)&mrk24->lun);
 			host_to_fcp_swap(mrk24->lun, sizeof(mrk24->lun));
-			mrk24->vp_index = vha->vp_idx;
+			if (IS_QLA29XX(ha))
+				((struct mrk_entry_24xx_ext *)mrk24)->vp_index =
+				    cpu_to_le16(vha->vp_idx);
+			else
+				mrk24->vp_index = vha->vp_idx;
 		} else {
+			mrk_entry_t *mrk = (mrk_entry_t *)mrk24;
+
 			SET_TARGET_ID(ha, mrk->target, loop_id);
 			mrk->lun = cpu_to_le16((uint16_t)lun);
 		}
 	}
-
 	if (IS_FWI2_CAPABLE(ha))
 		mrk24->handle = QLA_SKIP_HANDLE;
 
@@ -747,7 +766,8 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
 			 * Five DSDs are available in the Continuation
 			 * Type 1 IOCB.
 			 */
-			cont_pkt = qla2x00_prep_cont_type1_iocb(vha, req);
+			cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+			    vha->hw, req);
 			cur_dsd = cont_pkt->dsd;
 			avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
 		}
@@ -1620,13 +1640,13 @@ qla24xx_start_scsi(srb_t *sp)
 	cmd->host_scribble = (unsigned char *)(unsigned long)handle;
 	req->cnt -= req_cnt;
 
-	cmd_pkt = (struct cmd_type_7 *)req->ring_ptr;
+	cmd_pkt = (struct cmd_type_7 *)qla_req_ring_slot(ha, req);
 	cmd_pkt->handle = make_handle(req->id, handle);
 
 	/* Zero out remaining portion of packet. */
 	/*    tagged queuing modifier -- default is TSK_SIMPLE (0). */
 	clr_ptr = (uint32_t *)cmd_pkt + 2;
-	memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+	memset(clr_ptr, 0, qla_req_entry_size(ha) - 8);
 	cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
 
 	/* Set NPORT-ID and LUN number*/
@@ -1654,12 +1674,7 @@ qla24xx_start_scsi(srb_t *sp)
 	cmd_pkt->entry_count = (uint8_t)req_cnt;
 	wmb();
 	/* Adjust ring index. */
-	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else
-		req->ring_ptr++;
+	qla_req_ring_advance(ha, req);
 
 	sp->qpair->cmd_cnt++;
 	sp->flags |= SRB_DMA_VALID;
@@ -1829,11 +1844,11 @@ qla24xx_dif_start_scsi(srb_t *sp)
 	req->cnt -= req_cnt;
 
 	/* Fill-in common area */
-	cmd_pkt = (struct cmd_type_crc_2 *)req->ring_ptr;
+	cmd_pkt = (struct cmd_type_crc_2 *)qla_req_ring_slot(ha, req);
 	cmd_pkt->handle = make_handle(req->id, handle);
 
 	clr_ptr = (uint32_t *)cmd_pkt + 2;
-	memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+	memset(clr_ptr, 0, qla_req_entry_size(ha) - 8);
 
 	/* Set NPORT-ID and LUN number*/
 	cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
@@ -1849,8 +1864,8 @@ qla24xx_dif_start_scsi(srb_t *sp)
 
 	/* Build IOCB segments and adjust for data protection segments */
 	if (qla24xx_build_scsi_crc_2_iocbs(sp, (struct cmd_type_crc_2 *)
-	    req->ring_ptr, tot_dsds, tot_prot_dsds, fw_prot_opts) !=
-		QLA_SUCCESS)
+	    qla_req_ring_slot(ha, req), tot_dsds, tot_prot_dsds,
+	    fw_prot_opts) != QLA_SUCCESS)
 		goto queuing_error;
 
 	cmd_pkt->entry_count = (uint8_t)req_cnt;
@@ -1860,12 +1875,7 @@ qla24xx_dif_start_scsi(srb_t *sp)
 	wmb();
 
 	/* Adjust ring index. */
-	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else
-		req->ring_ptr++;
+	qla_req_ring_advance(ha, req);
 
 	sp->qpair->cmd_cnt++;
 	/* Set chip new ring index. */
@@ -1954,7 +1964,17 @@ qla2xxx_start_scsi_mq(srb_t *sp)
 		nseg = 0;
 
 	tot_dsds = nseg;
-	req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+	/*
+	 * 29xx posts into the 128-byte extended IOCB ring (cmd_type_7_ext,
+	 * NUM_CMD67_DSDS inline DSDs) via ring_ext_ptr; all other FWI2-capable
+	 * generations use the 64-byte cmd_type_7 / ring_ptr path.  The common
+	 * skeleton (marker/handle/sg-map/resource-check/ring advance) is
+	 * shared; only the cmd_pkt field-writes and ring stride differ.
+	 */
+	if (IS_QLA29XX(ha))
+		req_cnt = qla29xx_calc_iocbs(vha, tot_dsds, NUM_CMD67_DSDS);
+	else
+		req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
 
 	sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH;
 	sp->iores.exch_cnt = 1;
@@ -1987,46 +2007,84 @@ qla2xxx_start_scsi_mq(srb_t *sp)
 	cmd->host_scribble = (unsigned char *)(unsigned long)handle;
 	req->cnt -= req_cnt;
 
-	cmd_pkt = (struct cmd_type_7 *)req->ring_ptr;
-	cmd_pkt->handle = make_handle(req->id, handle);
+	if (IS_QLA29XX(ha)) {
+		/*
+		 * cmd_type_7_ext diverges from cmd_type_7 past offset 48
+		 * (port_id[3]+vp_index vs ctrl_flags_2+__le16 vp_index),
+		 * so the packet-field writes can't be shared via a common
+		 * pointer type.
+		 */
+		struct cmd_type_7_ext *cmd_pkt_ext =
+			(struct cmd_type_7_ext *)req->ring_ext_ptr;
 
-	/* Zero out remaining portion of packet. */
-	/*    tagged queuing modifier -- default is TSK_SIMPLE (0). */
-	clr_ptr = (uint32_t *)cmd_pkt + 2;
-	memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
-	cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
+		cmd_pkt_ext->handle = make_handle(req->id, handle);
 
-	/* Set NPORT-ID and LUN number*/
-	cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-	cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
-	cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
-	cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
-	cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
+		/* Zero out remaining portion of packet. */
+		/*    tagged queuing modifier -- default is TSK_SIMPLE (0). */
+		clr_ptr = (uint32_t *)cmd_pkt_ext + 2;
+		memset(clr_ptr, 0, REQUEST_ENTRY_SIZE_EXT - 8);
+		cmd_pkt_ext->dseg_count = cpu_to_le16(tot_dsds);
 
-	int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
-	host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
+		/* Set NPORT-ID and LUN number. */
+		cmd_pkt_ext->nport_handle =
+			cpu_to_le16(sp->fcport->loop_id);
+		cmd_pkt_ext->vp_index =
+			cpu_to_le16(sp->fcport->vha->vp_idx);
 
-	cmd_pkt->task = TSK_SIMPLE;
+		int_to_scsilun(cmd->device->lun, &cmd_pkt_ext->lun);
+		host_to_fcp_swap((uint8_t *)&cmd_pkt_ext->lun,
+				sizeof(cmd_pkt_ext->lun));
 
-	/* Load SCSI command packet. */
-	memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len);
-	host_to_fcp_swap(cmd_pkt->fcp_cdb, sizeof(cmd_pkt->fcp_cdb));
+		cmd_pkt_ext->task = TSK_SIMPLE;
 
-	cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd));
+		memcpy(cmd_pkt_ext->fcp_cdb, cmd->cmnd, cmd->cmd_len);
+		host_to_fcp_swap(cmd_pkt_ext->fcp_cdb,
+				sizeof(cmd_pkt_ext->fcp_cdb));
 
-	/* Build IOCB segments */
-	qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds, req);
+		cmd_pkt_ext->byte_count =
+			cpu_to_le32((uint32_t)scsi_bufflen(cmd));
+
+		qla29xx_build_scsi_iocbs(sp, cmd_pkt_ext, tot_dsds, req);
+
+		cmd_pkt_ext->entry_count = (uint8_t)req_cnt;
+	} else {
+		cmd_pkt = (struct cmd_type_7 *)req->ring_ptr;
+		cmd_pkt->handle = make_handle(req->id, handle);
+
+		/* Zero out remaining portion of packet. */
+		/*    tagged queuing modifier -- default is TSK_SIMPLE (0). */
+		clr_ptr = (uint32_t *)cmd_pkt + 2;
+		memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+		cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
+
+		/* Set NPORT-ID and LUN number. */
+		cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+		cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
+		cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
+		cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+		cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
+
+		int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
+		host_to_fcp_swap((uint8_t *)&cmd_pkt->lun,
+				sizeof(cmd_pkt->lun));
+
+		cmd_pkt->task = TSK_SIMPLE;
+
+		memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len);
+		host_to_fcp_swap(cmd_pkt->fcp_cdb,
+				sizeof(cmd_pkt->fcp_cdb));
+
+		cmd_pkt->byte_count =
+			cpu_to_le32((uint32_t)scsi_bufflen(cmd));
+
+		qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds, req);
+
+		cmd_pkt->entry_count = (uint8_t)req_cnt;
+	}
 
-	/* Set total data segment count. */
-	cmd_pkt->entry_count = (uint8_t)req_cnt;
 	wmb();
 	/* Adjust ring index. */
-	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else
-		req->ring_ptr++;
+	qla_req_ring_advance(ha, req);
 
 	sp->qpair->cmd_cnt++;
 	sp->flags |= SRB_DMA_VALID;
@@ -2034,8 +2092,12 @@ qla2xxx_start_scsi_mq(srb_t *sp)
 	/* Set chip new ring index. */
 	wrt_reg_dword(req->req_q_in, req->ring_index);
 
-	/* Manage unprocessed RIO/ZIO commands in response queue. */
-	if (vha->flags.process_response_queue &&
+	/*
+	 * Manage unprocessed RIO/ZIO commands in response queue. 29xx skips
+	 * this optimization until rsp_que is made 128-byte-stride aware;
+	 * completions still arrive via the regular ISR path.
+	 */
+	if (!IS_QLA29XX(ha) && vha->flags.process_response_queue &&
 	    rsp->ring_ptr->signature != RESPONSE_PROCESSED)
 		qla24xx_process_response_queue(vha, rsp);
 
@@ -2180,7 +2242,20 @@ qla2xxx_dif_start_scsi_mq(srb_t *sp)
 
 	sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH;
 	sp->iores.exch_cnt = 1;
-	sp->iores.iocb_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+	/*
+	 * Unlike cmd_type_6_ext / cmd_type_7_ext (which carry NUM_CMD67_DSDS
+	 * inline DSDs), cmd_type_crc_2_ext carries only a single inline
+	 * data_dsd; the remaining DSDs live in the separate CRC-context DMA.
+	 * Size the firmware IOCB-pool reservation against the CRC_2 ext inline
+	 * capacity (NUM_CRC2_EXT_INLINE_DSDS) so it mirrors the 24xx path
+	 * (qla24xx_calc_iocbs assumes 1 inline DSD for cmd_type_crc_2) and
+	 * doesn't under-count continuations expected by firmware.
+	 */
+	if (IS_QLA29XX(ha))
+		sp->iores.iocb_cnt = qla29xx_calc_iocbs(vha, tot_dsds,
+						       NUM_CRC2_EXT_INLINE_DSDS);
+	else
+		sp->iores.iocb_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
 	if (qla_get_fw_resources(sp->qpair, &sp->iores))
 		goto queuing_error;
 
@@ -2211,18 +2286,35 @@ qla2xxx_dif_start_scsi_mq(srb_t *sp)
 	cmd->host_scribble = (unsigned char *)(unsigned long)handle;
 	req->cnt -= req_cnt;
 
-	/* Fill-in common area */
-	cmd_pkt = (struct cmd_type_crc_2 *)req->ring_ptr;
+	/*
+	 * cmd_type_crc_2 and cmd_type_crc_2_ext share an identical header
+	 * layout through byte_count (offset 44); the trailing port_id /
+	 * vp_index / CRC-context fields diverge.  The common header writes
+	 * (handle, memset, nport_handle, lun, dseg_count, entry_count,
+	 * timeout) are performed via the 24xx-typed pointer in both paths;
+	 * the divergent tail and builder use hardware-specific handling.
+	 */
+	if (IS_QLA29XX(ha))
+		cmd_pkt = (struct cmd_type_crc_2 *)req->ring_ext_ptr;
+	else
+		cmd_pkt = (struct cmd_type_crc_2 *)req->ring_ptr;
 	cmd_pkt->handle = make_handle(req->id, handle);
 
 	clr_ptr = (uint32_t *)cmd_pkt + 2;
-	memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+	memset(clr_ptr, 0, qla_req_entry_size(ha) - 8);
 
 	/* Set NPORT-ID and LUN number*/
 	cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-	cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
-	cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
-	cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+	if (!IS_QLA29XX(ha)) {
+		cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
+		cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
+		cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+	}
+	/*
+	 * vp_index: 24xx uses a uint8_t field at offset 51 (in port_id
+	 * group); 29xx replaces that region with control_flags_2/vp_index as
+	 * __le16s starting at offset 48.
+	 */
 
 	int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
 	host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
@@ -2231,29 +2323,41 @@ qla2xxx_dif_start_scsi_mq(srb_t *sp)
 	cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
 
 	/* Build IOCB segments and adjust for data protection segments */
-	if (qla24xx_build_scsi_crc_2_iocbs(sp, (struct cmd_type_crc_2 *)
-	    req->ring_ptr, tot_dsds, tot_prot_dsds, fw_prot_opts) !=
-		QLA_SUCCESS)
-		goto queuing_error;
+	if (IS_QLA29XX(ha)) {
+		struct cmd_type_crc_2_ext *cmd_pkt_ext =
+			(struct cmd_type_crc_2_ext *)cmd_pkt;
+
+		cmd_pkt_ext->vp_index = cpu_to_le16(sp->vha->vp_idx);
+
+		if (qla29xx_build_scsi_crc_2_iocbs(sp, cmd_pkt_ext, tot_dsds,
+						   tot_prot_dsds,
+						   fw_prot_opts) !=
+		    QLA_SUCCESS)
+			goto queuing_error;
+	} else {
+		if (qla24xx_build_scsi_crc_2_iocbs(sp, cmd_pkt, tot_dsds,
+						   tot_prot_dsds,
+						   fw_prot_opts) !=
+		    QLA_SUCCESS)
+			goto queuing_error;
+	}
 
 	cmd_pkt->entry_count = (uint8_t)req_cnt;
 	cmd_pkt->timeout = cpu_to_le16(0);
 	wmb();
 
-	/* Adjust ring index. */
-	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else
-		req->ring_ptr++;
+	/* Adjust ring index.  29xx uses the 128-byte extended ring pointer. */
+	qla_req_ring_advance(ha, req);
 
 	sp->qpair->cmd_cnt++;
 	/* Set chip new ring index. */
 	wrt_reg_dword(req->req_q_in, req->ring_index);
 
-	/* Manage unprocessed RIO/ZIO commands in response queue. */
-	if (vha->flags.process_response_queue &&
+	/*
+	 * Manage unprocessed RIO/ZIO commands in response queue. 29xx skips
+	 * this optimization until rsp_que is made 128-byte-stride aware.
+	 */
+	if (!IS_QLA29XX(ha) && vha->flags.process_response_queue &&
 	    rsp->ring_ptr->signature != RESPONSE_PROCESSED)
 		qla24xx_process_response_queue(vha, rsp);
 
@@ -2303,7 +2407,7 @@ __qla2x00_alloc_iocbs(struct qla_qpair *qpair, srb_t *sp)
 		if (qpair->use_shadow_reg)
 			cnt = *req->out_ptr;
 		else if (ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha) ||
-		    IS_QLA28XX(ha))
+		    IS_QLA28XX(ha) || IS_QLA29XX(ha))
 			cnt = rd_reg_dword(&reg->isp25mq.req_q_out);
 		else if (IS_P3P_TYPE(ha))
 			cnt = rd_reg_dword(reg->isp82.req_q_out);
@@ -2343,10 +2447,20 @@ __qla2x00_alloc_iocbs(struct qla_qpair *qpair, srb_t *sp)
 		sp->handle = handle;
 	}
 
-	/* Prep packet */
+	/*
+	 * Prep packet.  29xx posts into a 128-byte-strided ring via
+	 * ring_ext_ptr; the first 8 bytes of struct request_ext overlay the
+	 * common request_t header, so entry_count/handle writes are
+	 * layout-compatible once we return the pkt as request_t *.
+	 */
 	req->cnt -= req_cnt;
-	pkt = req->ring_ptr;
-	memset(pkt, 0, REQUEST_ENTRY_SIZE);
+	if (IS_QLA29XX(ha)) {
+		pkt = (request_t *)req->ring_ext_ptr;
+		memset(pkt, 0, REQUEST_ENTRY_SIZE_EXT);
+	} else {
+		pkt = req->ring_ptr;
+		memset(pkt, 0, REQUEST_ENTRY_SIZE);
+	}
 	if (IS_QLAFX00(ha)) {
 		wrt_reg_byte((u8 __force __iomem *)&pkt->entry_count, req_cnt);
 		wrt_reg_dword((__le32 __force __iomem *)&pkt->handle, handle);
@@ -3271,7 +3385,7 @@ qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb)
 			* Type 1 IOCB.
 			       */
 			cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
-			    vha->hw->req_q_map[0]);
+			    ha, ha->req_q_map[0]);
 			cur_dsd = cont_pkt->dsd;
 			avail_dsds = 5;
 			entry_count++;
@@ -3297,7 +3411,6 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb)
 	struct qla_hw_data *ha = vha->hw;
 	struct bsg_job *bsg_job = sp->u.bsg_job;
 	int entry_count = 1;
-	cont_a64_entry_t *cont_pkt = NULL;
 
 	ct_iocb->entry_type = CT_IOCB_TYPE;
         ct_iocb->entry_status = 0;
@@ -3322,16 +3435,29 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb)
 	index = 0;
 
 	for_each_sg(bsg_job->request_payload.sg_list, sg, cmd_dsds, index) {
-		/* Allocate additional continuation packets? */
+		/*
+		 * Allocate additional continuation packets.  24xx uses the
+		 * 64-byte cont_a64_entry_t (5 DSDs); 29xx uses the 128-byte
+		 * cont_a64_entry_ext_t (NUM_CONT1_DSDS) and advances through
+		 * the ring_ext_ptr stride so the CT head IOCB isn't
+		 * overlapped.
+		 */
 		if (avail_dsds == 0) {
-			/*
-			 * Five DSDs are available in the Cont.
-			 * Type 1 IOCB.
-			 */
-			cont_pkt = qla2x00_prep_cont_type1_iocb(
-			    vha, ha->req_q_map[0]);
-			cur_dsd = cont_pkt->dsd;
-			avail_dsds = 5;
+			if (IS_QLA29XX(ha)) {
+				struct cont_a64_entry_ext *cont_pkt;
+
+				cont_pkt = qla2900_prep_cont_type1_iocb(vha,
+				    ha->req_q_map[0]);
+				cur_dsd = cont_pkt->dsd;
+				avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
+			} else {
+				cont_a64_entry_t *cont_pkt;
+
+				cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+				    ha, ha->req_q_map[0]);
+				cur_dsd = cont_pkt->dsd;
+				avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
+			}
 			entry_count++;
 		}
 
@@ -3342,16 +3468,22 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb)
 	index = 0;
 
 	for_each_sg(bsg_job->reply_payload.sg_list, sg, rsp_dsds, index) {
-		/* Allocate additional continuation packets? */
 		if (avail_dsds == 0) {
-			/*
-			* Five DSDs are available in the Cont.
-			* Type 1 IOCB.
-			       */
-			cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
-			    ha->req_q_map[0]);
-			cur_dsd = cont_pkt->dsd;
-			avail_dsds = 5;
+			if (IS_QLA29XX(ha)) {
+				struct cont_a64_entry_ext *cont_pkt;
+
+				cont_pkt = qla2900_prep_cont_type1_iocb(vha,
+				    ha->req_q_map[0]);
+				cur_dsd = cont_pkt->dsd;
+				avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
+			} else {
+				cont_a64_entry_t *cont_pkt;
+
+				cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+				    ha, ha->req_q_map[0]);
+				cur_dsd = cont_pkt->dsd;
+				avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
+			}
 			entry_count++;
 		}
 
@@ -3905,16 +4037,31 @@ static int qla_get_iocbs_resource(struct srb *sp)
 }
 
 static void
-qla_marker_iocb(srb_t *sp, struct mrk_entry_24xx *mrk)
+qla_marker_iocb(srb_t *sp, void *pkt)
 {
+	struct qla_hw_data *ha = sp->vha->hw;
+	struct mrk_entry_24xx *mrk = pkt;
+
+	/*
+	 * mrk_entry_24xx_ext overlays mrk_entry_24xx through 'lun', so
+	 * the common-header writes go through the 24xx-typed pointer;
+	 * only vp_index (u8 in 24xx, __le16 in the ext layout) needs a
+	 * stride-aware branch.
+	 */
 	mrk->entry_type = MARKER_TYPE;
 	mrk->modifier = sp->u.iocb_cmd.u.tmf.modifier;
 	mrk->handle = make_handle(sp->qpair->req->id, sp->handle);
 	if (sp->u.iocb_cmd.u.tmf.modifier != MK_SYNC_ALL) {
-		mrk->nport_handle = cpu_to_le16(sp->u.iocb_cmd.u.tmf.loop_id);
-		int_to_scsilun(sp->u.iocb_cmd.u.tmf.lun, (struct scsi_lun *)&mrk->lun);
+		mrk->nport_handle =
+		    cpu_to_le16(sp->u.iocb_cmd.u.tmf.loop_id);
+		int_to_scsilun(sp->u.iocb_cmd.u.tmf.lun,
+		    (struct scsi_lun *)&mrk->lun);
 		host_to_fcp_swap(mrk->lun, sizeof(mrk->lun));
-		mrk->vp_index = sp->u.iocb_cmd.u.tmf.vp_index;
+		if (IS_QLA29XX(ha))
+			((struct mrk_entry_24xx_ext *)pkt)->vp_index =
+			    cpu_to_le16(sp->u.iocb_cmd.u.tmf.vp_index);
+		else
+			mrk->vp_index = sp->u.iocb_cmd.u.tmf.vp_index;
 	}
 }
 
@@ -4100,7 +4247,8 @@ qla25xx_build_bidir_iocb(srb_t *sp, struct scsi_qla_host *vha,
 			/* Continuation type 1 IOCB can accomodate
 			 * 5 DSDS
 			 */
-			cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req);
+			cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+			    vha->hw, vha->req);
 			cur_dsd = cont_pkt->dsd;
 			avail_dsds = 5;
 			entry_count++;
@@ -4122,7 +4270,8 @@ qla25xx_build_bidir_iocb(srb_t *sp, struct scsi_qla_host *vha,
 			/* Continuation type 1 IOCB can accomodate
 			 * 5 DSDS
 			 */
-			cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req);
+			cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+			    vha->hw, vha->req);
 			cur_dsd = cont_pkt->dsd;
 			avail_dsds = 5;
 			entry_count++;
@@ -4154,6 +4303,21 @@ qla2x00_start_bidir(srb_t *sp, struct scsi_qla_host *vha, uint32_t tot_dsds)
 	rsp = ha->rsp_q_map[0];
 	req = vha->req;
 
+	/*
+	 * 29xx uses a 128-byte extended IOCB ring, but no extended
+	 * COMMAND_BIDIRECTIONAL IOCB layout has been defined and no
+	 * firmware-spec'd cmd_bidir_ext exists.  Posting the 64-byte
+	 * struct cmd_bidir against req->ring_ptr would overlap the next
+	 * ring slot on 29xx HW (see BUILD_BUG_ON(sizeof(struct cmd_bidir)
+	 * != 64) in qla_os.c), so refuse BSG BIDIR on 29xx rather than
+	 * corrupt the request ring.
+	 */
+	if (IS_QLA29XX(ha)) {
+		ql_log(ql_log_warn, vha, 0x70af,
+		    "BSG BIDIR not supported on 29xx adapters.\n");
+		return EXT_STATUS_INVALID_PARAM;
+	}
+
 	/* Send marker if required */
 	if (vha->marker_needed != 0) {
 		if (qla2x00_marker(vha, ha->base_qpair,
@@ -4291,8 +4455,15 @@ qla_start_scsi_type6(srb_t *sp)
 
 	tot_dsds = nseg;
 
-	/* eventhough driver only need 1 T6 IOCB, FW still convert DSD to Continueation IOCB */
-	req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+	/*
+	 * Even though the driver only needs 1 T6 IOCB, FW still converts the
+	 * DSD list into continuation IOCBs for reservation purposes.  29xx
+	 * follows the same rule against its extended-IOCB inline DSD count.
+	 */
+	if (IS_QLA29XX(ha))
+		req_cnt = qla29xx_calc_iocbs(vha, tot_dsds, NUM_CMD67_DSDS);
+	else
+		req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
 
 	sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH;
 	sp->iores.exch_cnt = 1;
@@ -4396,23 +4567,44 @@ qla_start_scsi_type6(srb_t *sp)
 	cmd->host_scribble = (unsigned char *)(unsigned long)handle;
 	req->cnt -= req_cnt;
 
-	cmd_pkt = (struct cmd_type_6 *)req->ring_ptr;
+	/*
+	 * cmd_type_6 and cmd_type_6_ext share an identical header layout
+	 * from offset 0 through byte_count (offset 48), so the common
+	 * header/nport/lun writes go through the 24xx-typed cmd_pkt even on
+	 * 29xx.  Only the divergent port_id/vp_index tail and the DSD layout
+	 * need hardware-specific handling.
+	 */
+	if (IS_QLA29XX(ha))
+		cmd_pkt = (struct cmd_type_6 *)req->ring_ext_ptr;
+	else
+		cmd_pkt = (struct cmd_type_6 *)req->ring_ptr;
 	cmd_pkt->handle = make_handle(req->id, handle);
 
 	/* tagged queuing modifier -- default is TSK_SIMPLE (0). */
 	clr_ptr = (uint32_t *)cmd_pkt + 2;
-	memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+	memset(clr_ptr, 0, qla_req_entry_size(ha) - 8);
 	cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
 
 	/* Set NPORT-ID and LUN number */
 	cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-	cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
-	cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
-	cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
-	cmd_pkt->vp_index = sp->vha->vp_idx;
 
-	/* Build IOCB segments */
-	qla24xx_build_scsi_type_6_iocbs(sp, cmd_pkt, tot_dsds);
+	if (IS_QLA29XX(ha)) {
+		struct cmd_type_6_ext *cmd_pkt_ext =
+			(struct cmd_type_6_ext *)cmd_pkt;
+
+		cmd_pkt_ext->vp_index = cpu_to_le16(sp->vha->vp_idx);
+
+		/* Build IOCB segments (dsd[] in 29xx extended layout). */
+		qla29xx_build_scsi_type_6_iocbs(sp, cmd_pkt_ext, tot_dsds);
+	} else {
+		cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
+		cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
+		cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+		cmd_pkt->vp_index = sp->vha->vp_idx;
+
+		/* Build IOCB segments (24xx fcp_dsd single-DSD layout). */
+		qla24xx_build_scsi_type_6_iocbs(sp, cmd_pkt, tot_dsds);
+	}
 
 	int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
 	host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
@@ -4447,14 +4639,8 @@ qla_start_scsi_type6(srb_t *sp)
 	cmd_pkt->entry_count = (uint8_t)req_cnt;
 
 	wmb();
-	/* Adjust ring index. */
-	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else {
-		req->ring_ptr++;
-	}
+	/* Adjust ring index. 29xx uses the 128-byte extended ring pointer.*/
+	qla_req_ring_advance(ha, req);
 
 	sp->qpair->cmd_cnt++;
 	sp->flags |= SRB_DMA_VALID;
@@ -4462,8 +4648,11 @@ qla_start_scsi_type6(srb_t *sp)
 	/* Set chip new ring index. */
 	wrt_reg_dword(req->req_q_in, req->ring_index);
 
-	/* Manage unprocessed RIO/ZIO commands in response queue. */
-	if (vha->flags.process_response_queue &&
+	/*
+	 * Manage unprocessed RIO/ZIO commands in response queue. 29xx skips
+	 * this optimization until rsp_que is made 128-byte-stride aware.
+	 */
+	if (!IS_QLA29XX(ha) &&  vha->flags.process_response_queue &&
 	    rsp->ring_ptr->signature != RESPONSE_PROCESSED)
 		qla24xx_process_response_queue(vha, rsp);
 
@@ -4489,3 +4678,498 @@ qla_start_scsi_type6(srb_t *sp)
 
 	return QLA_FUNCTION_FAILED;
 }
+
+/*
+ * ---------------------------------------------------------------------------
+ * 29xx extended (128-byte) IOCB fast-path helpers.
+ *
+ * These helpers drive the extended IOCB ring used by 29xx-class adapters and
+ * operate on the *_ext IOCB variants declared in qla_fw29.h
+ * (cmd_type_6_ext, cmd_type_7_ext, cmd_type_crc_2_ext, struct cont_a64_entry_ext).
+ *
+ * They are not yet wired into any dispatch path; the top-level entry points
+ * (e.g. qla29xx_start_scsi / qla29xx_dif_start_scsi_mq) and isp_ops hooks are
+ * added in subsequent merge phases.  Until then the top-of-tree builder
+ * functions are marked __maybe_unused so the build stays warning-clean.
+ *
+ * Ported from the out-of-tree qla29xx driver.  Signatures have been adapted
+ * to the Mach qla2xxx single-srb_t model:
+ *   struct qla_io_srb *  -->  srb_t *
+ *   sp->qcb.qpair        -->  sp->qpair
+ *   sp->qcb.iores        -->  sp->iores
+ *   sp->qcb.handle       -->  sp->handle
+ *   qla2900_hba_err_chk_enabled()  -->  qla2x00_hba_err_chk_enabled()
+ * ---------------------------------------------------------------------------
+ */
+
+static void qla29xx_copy_dif_iocb_data(struct cmd_type_crc_2_ext *cmd_pkt,
+	struct crc_context *crc_ctx_pkt, uint8_t dif_bundling);
+
+/**
+ * qla2900_prep_cont_type1_iocb() - Initialize a 29xx-ext Continuation
+ * Type 1 IOCB on the extended IOCB ring.
+ * @vha: HA context
+ * @req: request queue
+ *
+ * Returns a pointer to the continuation type 1 IOCB packet.
+ */
+struct cont_a64_entry_ext *
+qla2900_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct req_que *req)
+{
+	struct cont_a64_entry_ext *cont_pkt;
+
+	/*
+	 * 29xx uses the 128-byte extended IOCB ring.  Advance via ring_ext_ptr
+	 * so the pointer arithmetic matches the on-ring stride.
+	 */
+	req->ring_index++;
+	if (req->ring_index == req->length) {
+		req->ring_index = 0;
+		req->ring_ext_ptr = req->ring_ext;
+	} else {
+		req->ring_ext_ptr++;
+	}
+
+	cont_pkt = (struct cont_a64_entry_ext *)req->ring_ext_ptr;
+
+	/* Load packet defaults. */
+	put_unaligned_le32(CONTINUE_A64_TYPE, &cont_pkt->entry_type);
+
+	return cont_pkt;
+}
+
+/*
+ * Note: the 29xx extended path reuses qla24xx_configure_prot_mode() -- the
+ * SCSI-prot-op to PO_MODE_DIF_* translation is independent of the IOCB
+ * generation.
+ */
+
+/**
+ * qla29xx_build_scsi_type_6_iocbs() - Build IOCB command utilizing Command
+ * Type 6 IOCB types on the 29xx extended ring.
+ * @sp: SRB command to process
+ * @cmd_pkt: Command type 6 extended IOCB
+ * @tot_dsds: Total number of segments to transfer
+ */
+static void
+qla29xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6_ext *cmd_pkt,
+	uint16_t tot_dsds)
+{
+	struct dsd64 *cur_dsd = NULL, *next_dsd;
+	scsi_qla_host_t	*vha;
+	struct qla_hw_data *ha;
+	struct scsi_cmnd *cmd;
+	struct	scatterlist *cur_seg;
+	uint8_t avail_dsds;
+	uint8_t first_iocb = 1;
+	uint32_t dsd_list_len;
+	struct dsd_dma *dsd_ptr;
+	struct ct6_dsd *ctx;
+	struct qla_qpair *qpair = sp->qpair;
+
+	cmd = GET_CMD_SP(sp);
+
+	/* Update entry type to indicate Command Type 6 IOCB */
+	put_unaligned_le32(COMMAND_TYPE_6, &cmd_pkt->entry_type);
+
+	/* No data transfer */
+	if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) {
+		cmd_pkt->byte_count = cpu_to_le32(0);
+		goto function_end;
+	}
+
+	vha = sp->vha;
+	ha = vha->hw;
+
+	/* Set transfer direction */
+	if (cmd->sc_data_direction == DMA_TO_DEVICE) {
+		cmd_pkt->control_flags = cpu_to_le16(CF_WRITE_DATA);
+		qpair->counters.output_bytes += scsi_bufflen(cmd);
+		qpair->counters.output_requests++;
+	} else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
+		cmd_pkt->control_flags = cpu_to_le16(CF_READ_DATA);
+		qpair->counters.input_bytes += scsi_bufflen(cmd);
+		qpair->counters.input_requests++;
+	}
+
+	cur_seg = scsi_sglist(cmd);
+	ctx = &sp->u.scmd.ct6_ctx;
+
+	while (tot_dsds) {
+		avail_dsds = (tot_dsds > QLA_DSDS_PER_IOCB) ?
+		    QLA_DSDS_PER_IOCB : tot_dsds;
+		tot_dsds -= avail_dsds;
+		dsd_list_len = (avail_dsds + 1) * QLA_DSD_SIZE;
+
+		dsd_ptr = list_first_entry(&qpair->dsd_list, struct dsd_dma,
+					   list);
+		next_dsd = dsd_ptr->dsd_addr;
+		list_del(&dsd_ptr->list);
+		qpair->dsd_avail--;
+		list_add_tail(&dsd_ptr->list, &ctx->dsd_list);
+		ctx->dsd_use_cnt++;
+		qpair->dsd_inuse++;
+
+		if (first_iocb) {
+			first_iocb = 0;
+			put_unaligned_le64(dsd_ptr->dsd_list_dma,
+					   &cmd_pkt->dsd[0].address);
+			cmd_pkt->dsd[0].length = cpu_to_le32(dsd_list_len);
+		} else {
+			put_unaligned_le64(dsd_ptr->dsd_list_dma,
+					   &cur_dsd->address);
+			cur_dsd->length = cpu_to_le32(dsd_list_len);
+			cur_dsd++;
+		}
+		cur_dsd = next_dsd;
+		while (avail_dsds) {
+			append_dsd64(&cur_dsd, cur_seg);
+			cur_seg = sg_next(cur_seg);
+			avail_dsds--;
+		}
+	}
+
+	/* Null termination */
+	if (cur_dsd) {
+		cur_dsd->address = 0;
+		cur_dsd->length = 0;
+		cur_dsd++;
+	}
+	cmd_pkt->control_flags |= CF_DATA_SEG_DESCR_ENABLE;
+function_end:
+	return;
+}
+
+/*
+ * Note: the 29xx extended path reuses qla24xx_calc_dsd_lists() -- the
+ * DSD-list-per-IOCB arithmetic (based on QLA_DSDS_PER_IOCB) is generation-
+ * agnostic.
+ */
+
+/**
+ * qla29xx_build_scsi_iocbs() - Build IOCB command utilizing Command Type 7
+ * IOCB types on the 29xx extended ring.
+ * @sp: SRB command to process
+ * @cmd_pkt: Command type 7 extended IOCB
+ * @tot_dsds: Total number of segments to transfer
+ * @req: pointer to request queue
+ */
+static void __maybe_unused
+qla29xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7_ext *cmd_pkt,
+	uint16_t tot_dsds, struct req_que *req)
+{
+	uint8_t	avail_dsds;
+	struct dsd64 *cur_dsd;
+	scsi_qla_host_t	*vha;
+	struct scsi_cmnd *cmd;
+	struct scatterlist *sg;
+	int i;
+	struct qla_qpair *qpair = sp->qpair;
+
+	cmd = GET_CMD_SP(sp);
+
+	/* Update entry type to indicate Command Type 7 IOCB */
+	put_unaligned_le32(COMMAND_TYPE_7, &cmd_pkt->entry_type);
+
+	/* No data transfer */
+	if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) {
+		cmd_pkt->byte_count = cpu_to_le32(0);
+		return;
+	}
+
+	vha = sp->vha;
+
+	/* Set transfer direction */
+	if (cmd->sc_data_direction == DMA_TO_DEVICE) {
+		cmd_pkt->task_mgmt_flags = cpu_to_le16(TMF_WRITE_DATA);
+		qpair->counters.output_bytes += scsi_bufflen(cmd);
+		qpair->counters.output_requests++;
+	} else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
+		cmd_pkt->task_mgmt_flags = cpu_to_le16(TMF_READ_DATA);
+		qpair->counters.input_bytes += scsi_bufflen(cmd);
+		qpair->counters.input_requests++;
+	}
+
+	/* NUM_CMD67_DSDS DSDs available in the Command Type 7 ext IOCB */
+	avail_dsds = NUM_CMD67_DSDS;
+	cur_dsd = &cmd_pkt->dsd[0];
+
+	scsi_for_each_sg(cmd, sg, tot_dsds, i) {
+		struct cont_a64_entry_ext *cont_pkt;
+
+		/* Allocate additional continuation packets? */
+		if (avail_dsds == 0) {
+			/*
+			 * NUM_CONT1_DSDS DSDs are available in the Continuation
+			 * Type 1 extended IOCB.
+			 */
+			cont_pkt = qla2900_prep_cont_type1_iocb(vha, req);
+			cur_dsd = cont_pkt->dsd;
+			avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
+		}
+
+		append_dsd64(&cur_dsd, sg);
+		avail_dsds--;
+	}
+}
+
+/*
+ * Note: the 29xx extended path reuses qla24xx_set_t10dif_tags() (defined
+ * earlier in this file) -- the T10-DIF ref/app tag layout is identical
+ * between the 24xx and 29xx extended CRC_2 IOCBs (both share the same
+ * fw_dif_context layout inside struct crc_context).
+ */
+
+/*
+ * Note: the 29xx extended CRC_2 path reuses the 24xx SG walkers
+ * (qla24xx_get_one_block_sg, qla24xx_walk_and_build_sglist_no_difb,
+ * qla24xx_walk_and_build_sglist, qla24xx_walk_and_build_prot_sglist).
+ * The DSD-chain / CRC-context layout they produce is generation-agnostic;
+ * only the IOCB that points at the chain (Type-6/7/CRC_2 vs their _ext
+ * variants) differs between generations, and that is handled by the
+ * separate qla29xx_build_* builders.
+ *
+ * The walkers that take a struct qla_tc_param *tc are invoked with tc=NULL
+ * here because the 29xx fast-path does not use the tape/copy offload.
+ */
+
+/**
+ * qla29xx_build_scsi_crc_2_iocbs() - Build IOCB command utilizing Command
+ * Type CRC_2 IOCB types on the 29xx extended ring.
+ * @sp: SRB command to process
+ * @cmd_pkt: Command type CRC_2 extended IOCB
+ * @tot_dsds: Total number of segments to transfer
+ * @tot_prot_dsds: Total number of segments with protection information
+ * @fw_prot_opts: Protection options to be passed to firmware
+ * @bundling: Bundling flag
+ *
+ * Returns 0 if successful, 1 otherwise.
+ */
+static int
+qla29xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2_ext *cmd_pkt,
+	uint16_t tot_dsds, uint16_t tot_prot_dsds, uint16_t fw_prot_opts)
+{
+	struct dsd64		*cur_dsd;
+	__be32			*fcp_dl;
+	scsi_qla_host_t		*vha;
+	struct scsi_cmnd	*cmd;
+	uint32_t		total_bytes = 0;
+	uint32_t		data_bytes;
+	uint32_t		dif_bytes;
+	uint8_t			bundling = 1;
+	uint16_t		blk_size;
+	struct crc_context	*crc_ctx_pkt = NULL;
+	struct qla_hw_data	*ha;
+	uint8_t			additional_fcpcdb_len;
+	uint16_t		fcp_cmnd_len;
+	struct fcp_cmnd		*fcp_cmnd;
+	dma_addr_t		crc_ctx_dma;
+
+	cmd = GET_CMD_SP(sp);
+
+	/* Update entry type to indicate Command Type CRC_2 IOCB */
+	put_unaligned_le32(COMMAND_TYPE_CRC_2, &cmd_pkt->entry_type);
+
+	vha = sp->vha;
+	ha = vha->hw;
+
+	/* No data transfer */
+	data_bytes = scsi_bufflen(cmd);
+	if (!data_bytes || cmd->sc_data_direction == DMA_NONE) {
+		cmd_pkt->byte_count = cpu_to_le32(0);
+		return QLA_SUCCESS;
+	}
+
+	cmd_pkt->vp_index = cpu_to_le16(sp->vha->vp_idx);
+
+	/* Set transfer direction */
+	if (cmd->sc_data_direction == DMA_TO_DEVICE)
+		cmd_pkt->control_flags_1 = cpu_to_le16(CF_WRITE_DATA);
+	else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+		cmd_pkt->control_flags_1 = cpu_to_le16(CF_READ_DATA);
+
+	if ((scsi_get_prot_op(cmd) == SCSI_PROT_READ_INSERT) ||
+	    (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_STRIP) ||
+	    (scsi_get_prot_op(cmd) == SCSI_PROT_READ_STRIP) ||
+	    (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_INSERT))
+		bundling = 0;
+
+	/* Allocate CRC context from global pool */
+	crc_ctx_pkt = sp->u.scmd.crc_ctx =
+	    dma_pool_zalloc(ha->dl_dma_pool, GFP_ATOMIC, &crc_ctx_dma);
+
+	if (!crc_ctx_pkt)
+		goto crc_queuing_error;
+
+	crc_ctx_pkt->crc_ctx_dma = crc_ctx_dma;
+
+	sp->flags |= SRB_CRC_CTX_DMA_VALID;
+
+	/* Set handle */
+	crc_ctx_pkt->handle = cmd_pkt->handle;
+
+	INIT_LIST_HEAD(&crc_ctx_pkt->dsd_list);
+
+	qla24xx_set_t10dif_tags(sp, (struct fw_dif_context *)
+	    &crc_ctx_pkt->ref_tag, tot_prot_dsds);
+
+	/* Determine SCSI command length -- align to 4 byte boundary */
+	if (cmd->cmd_len > 16) {
+		additional_fcpcdb_len = cmd->cmd_len - 16;
+		if ((cmd->cmd_len % 4) != 0) {
+			/* SCSI cmd > 16 bytes must be multiple of 4 */
+			goto crc_queuing_error;
+		}
+		fcp_cmnd_len = 12 + cmd->cmd_len + 4;
+	} else {
+		additional_fcpcdb_len = 0;
+		fcp_cmnd_len = 12 + 16 + 4;
+	}
+
+	fcp_cmnd = &crc_ctx_pkt->fcp_cmnd;
+
+	fcp_cmnd->additional_cdb_len = additional_fcpcdb_len;
+	if (cmd->sc_data_direction == DMA_TO_DEVICE)
+		fcp_cmnd->additional_cdb_len |= 1;
+	else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+		fcp_cmnd->additional_cdb_len |= 2;
+
+	int_to_scsilun(cmd->device->lun, &fcp_cmnd->lun);
+	memcpy(fcp_cmnd->cdb, cmd->cmnd, cmd->cmd_len);
+	cmd_pkt->fcp_cmnd_dseg_len = cpu_to_le16(fcp_cmnd_len);
+	put_unaligned_le64(crc_ctx_dma + CRC_CONTEXT_FCPCMND_OFF,
+			   &cmd_pkt->fcp_cmnd_dseg_address);
+	fcp_cmnd->task_management = 0;
+	fcp_cmnd->task_attribute = TSK_SIMPLE;
+
+	cmd_pkt->fcp_rsp_dseg_len = 0; /* Let response come in status iocb */
+
+	/* Compute dif len and adjust data len to include protection */
+	dif_bytes = 0;
+	blk_size = cmd->device->sector_size;
+	dif_bytes = (data_bytes / blk_size) * 8;
+
+	switch (scsi_get_prot_op(GET_CMD_SP(sp))) {
+	case SCSI_PROT_READ_INSERT:
+	case SCSI_PROT_WRITE_STRIP:
+		total_bytes = data_bytes;
+		data_bytes += dif_bytes;
+		break;
+
+	case SCSI_PROT_READ_STRIP:
+	case SCSI_PROT_WRITE_INSERT:
+	case SCSI_PROT_READ_PASS:
+	case SCSI_PROT_WRITE_PASS:
+		total_bytes = data_bytes + dif_bytes;
+		break;
+	default:
+		WARN_ON_ONCE(1);
+		return 1;
+	}
+
+	if (!qla2x00_hba_err_chk_enabled(sp))
+		fw_prot_opts |= 0x10; /* Disable Guard tag checking */
+	/* HBA error checking enabled */
+	else {
+		if ((scsi_get_prot_type(GET_CMD_SP(sp)) == SCSI_PROT_DIF_TYPE1)
+		    || (scsi_get_prot_type(GET_CMD_SP(sp)) ==
+			SCSI_PROT_DIF_TYPE2))
+			fw_prot_opts |= BIT_10;
+		else if (scsi_get_prot_type(GET_CMD_SP(sp)) ==
+		    SCSI_PROT_DIF_TYPE3)
+			fw_prot_opts |= BIT_11;
+	}
+
+	if (!bundling) {
+		cur_dsd = &crc_ctx_pkt->u.nobundling.data_dsd[0];
+	} else {
+		/*
+		 * Configure Bundling if we need to fetch interleaving
+		 * protection PCI accesses
+		 */
+		fw_prot_opts |= PO_ENABLE_DIF_BUNDLING;
+		crc_ctx_pkt->u.bundling.dif_byte_count = cpu_to_le32(dif_bytes);
+		crc_ctx_pkt->u.bundling.dseg_count = cpu_to_le16(tot_dsds -
+							tot_prot_dsds);
+		cur_dsd = &crc_ctx_pkt->u.bundling.data_dsd[0];
+	}
+
+	/* Finish the common fields of CRC pkt */
+	crc_ctx_pkt->blk_size = cpu_to_le16(blk_size);
+	crc_ctx_pkt->prot_opts = cpu_to_le16(fw_prot_opts);
+	crc_ctx_pkt->byte_count = cpu_to_le32(data_bytes);
+	crc_ctx_pkt->guard_seed = cpu_to_le16(0);
+	/* Fibre channel byte count */
+	cmd_pkt->byte_count = cpu_to_le32(total_bytes);
+	fcp_dl = (__be32 *)(crc_ctx_pkt->fcp_cmnd.cdb + 16 +
+	    additional_fcpcdb_len);
+	*fcp_dl = htonl(total_bytes);
+
+	/* Walks data segments */
+
+	cmd_pkt->control_flags_1 |= cpu_to_le16(CF_DATA_SEG_DESCR_ENABLE);
+
+	if (!bundling && tot_prot_dsds) {
+		if (qla24xx_walk_and_build_sglist_no_difb(ha, sp,
+			cur_dsd, tot_dsds, NULL))
+			goto crc_queuing_error;
+	} else if (qla24xx_walk_and_build_sglist(ha, sp, cur_dsd,
+			(tot_dsds - tot_prot_dsds), NULL))
+		goto crc_queuing_error;
+
+	if (bundling && tot_prot_dsds) {
+		/* Walks dif segments */
+		cmd_pkt->control_flags_1 |= cpu_to_le16(CF_DIF_SEG_DESCR_ENABLE);
+		cur_dsd = &crc_ctx_pkt->u.bundling.dif_dsd;
+		if (qla24xx_walk_and_build_prot_sglist(ha, sp, cur_dsd,
+				tot_prot_dsds, NULL))
+			goto crc_queuing_error;
+	}
+	qla29xx_copy_dif_iocb_data(cmd_pkt, crc_ctx_pkt, bundling);
+
+	return QLA_SUCCESS;
+
+crc_queuing_error:
+	/* Cleanup will be performed by the caller */
+	return QLA_FUNCTION_FAILED;
+}
+
+/**
+ * qla29xx_copy_dif_iocb_data() - Populate the DIF-related fields of an
+ * extended CRC_2 command IOCB from a freshly populated crc_context.
+ * @cmd_pkt: Extended CRC_2 command IOCB
+ * @crc_ctx_pkt: Source CRC context
+ * @dif_bundling: DIF bundling flag
+ */
+static void
+qla29xx_copy_dif_iocb_data(struct cmd_type_crc_2_ext *cmd_pkt,
+	struct crc_context *crc_ctx_pkt, uint8_t dif_bundling)
+{
+	cmd_pkt->ref_tag = crc_ctx_pkt->ref_tag;
+	cmd_pkt->app_tag = crc_ctx_pkt->app_tag;
+	memcpy(cmd_pkt->ref_tag_mask, crc_ctx_pkt->ref_tag_mask,
+	       sizeof(cmd_pkt->ref_tag_mask));
+	memcpy(cmd_pkt->app_tag_mask, crc_ctx_pkt->app_tag_mask,
+	       sizeof(cmd_pkt->app_tag_mask));
+	cmd_pkt->blk_size = crc_ctx_pkt->blk_size;
+	cmd_pkt->prot_opts = crc_ctx_pkt->prot_opts;
+	cmd_pkt->tot_byte_count = crc_ctx_pkt->byte_count;
+	if (dif_bundling) {
+		cmd_pkt->u.bundling.guard_seed = crc_ctx_pkt->guard_seed;
+		cmd_pkt->u.bundling.dif_byte_count =
+			crc_ctx_pkt->u.bundling.dif_byte_count;
+		cmd_pkt->u.bundling.dseg_count =
+			crc_ctx_pkt->u.bundling.dseg_count;
+		memcpy(cmd_pkt->u.bundling.data_dsd,
+		       crc_ctx_pkt->u.bundling.data_dsd,
+		       sizeof(cmd_pkt->u.bundling.data_dsd));
+		memcpy(&cmd_pkt->u.bundling.dif_dsd,
+		       &crc_ctx_pkt->u.bundling.dif_dsd,
+		       sizeof(cmd_pkt->u.bundling.dif_dsd));
+	} else {
+		cmd_pkt->u.nobundling.guard_seed = crc_ctx_pkt->guard_seed;
+		memcpy(cmd_pkt->u.nobundling.data_dsd,
+		       crc_ctx_pkt->u.nobundling.data_dsd,
+		       sizeof(cmd_pkt->u.nobundling.data_dsd));
+	}
+}
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index c47c38e099ff..e95fb0e59f38 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -209,13 +209,7 @@ void __qla_consume_iocb(struct scsi_qla_host *vha,
 		new_pkt = rsp_q->ring_ptr;
 		*pkt = new_pkt;
 
-		rsp_q->ring_index++;
-		if (rsp_q->ring_index == rsp_q->length) {
-			rsp_q->ring_index = 0;
-			rsp_q->ring_ptr = rsp_q->ring;
-		} else {
-			rsp_q->ring_ptr++;
-		}
+		qla_rsp_ring_advance(rsp_q);
 
 		new_pkt->signature = RESPONSE_PROCESSED;
 		/* flush signature */
@@ -287,13 +281,7 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
 				break;
 			}
 
-			rsp_q->ring_index++;
-			if (rsp_q->ring_index == rsp_q->length) {
-				rsp_q->ring_index = 0;
-				rsp_q->ring_ptr = rsp_q->ring;
-			} else {
-				rsp_q->ring_ptr++;
-			}
+			qla_rsp_ring_advance(rsp_q);
 			no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
 			    sizeof(new_pkt->data) : pending_bytes;
 			if ((buffer_copy_offset + no_bytes) <= total_bytes) {
@@ -1212,13 +1200,7 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
 				break;
 			}
 
-			rsp_q->ring_index++;
-			if (rsp_q->ring_index == rsp_q->length) {
-				rsp_q->ring_index = 0;
-				rsp_q->ring_ptr = rsp_q->ring;
-			} else {
-				rsp_q->ring_ptr++;
-			}
+			qla_rsp_ring_advance(rsp_q);
 			no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
 			    sizeof(new_pkt->data) : pending_bytes;
 			if ((buffer_copy_offset + no_bytes) <= total_bytes) {
@@ -2973,13 +2955,7 @@ qla2x00_process_response_queue(struct rsp_que *rsp)
 	while (rsp->ring_ptr->signature != RESPONSE_PROCESSED) {
 		pkt = (sts_entry_t *)rsp->ring_ptr;
 
-		rsp->ring_index++;
-		if (rsp->ring_index == rsp->length) {
-			rsp->ring_index = 0;
-			rsp->ring_ptr = rsp->ring;
-		} else {
-			rsp->ring_ptr++;
-		}
+		qla_rsp_ring_advance(rsp);
 
 		if (pkt->entry_status != 0) {
 			qla2x00_error_entry(vha, rsp, pkt);
@@ -3941,9 +3917,10 @@ static int qla_chk_cont_iocb_avail(struct scsi_qla_host *vha,
 }
 
 static void qla_marker_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
-	struct mrk_entry_24xx *pkt)
+	void *pkt)
 {
 	const char func[] = "MRK-IOCB";
+	struct mrk_entry_24xx *mrk = pkt;
 	srb_t *sp;
 	int res = QLA_SUCCESS;
 
@@ -3954,7 +3931,7 @@ static void qla_marker_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
 	if (!sp)
 		return;
 
-	if (pkt->entry_status) {
+	if (mrk->entry_status) {
 		ql_dbg(ql_dbg_taskm, vha, 0x8025, "marker failure.\n");
 		res = QLA_COMMAND_ERROR;
 	}
@@ -4003,13 +3980,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 		pkt = (struct sts_entry_24xx *)rsp->ring_ptr;
 		cur_ring_index = rsp->ring_index;
 
-		rsp->ring_index++;
-		if (rsp->ring_index == rsp->length) {
-			rsp->ring_index = 0;
-			rsp->ring_ptr = rsp->ring;
-		} else {
-			rsp->ring_ptr++;
-		}
+		qla_rsp_ring_advance(rsp);
 
 		if (pkt->entry_status != 0) {
 			if (qla2x00_error_entry(vha, rsp, (sts_entry_t *) pkt))
@@ -4079,7 +4050,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 					(struct nack_to_isp *)pkt);
 			break;
 		case MARKER_TYPE:
-			qla_marker_iocb_entry(vha, rsp->req, (struct mrk_entry_24xx *)pkt);
+			qla_marker_iocb_entry(vha, rsp->req, pkt);
 			break;
 		case ABORT_IOCB_TYPE:
 			qla24xx_abort_iocb_entry(vha, rsp->req,
@@ -4127,8 +4098,8 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 					 * interrupt with all IOCBs to arrive
 					 * and re-process.
 					 */
-					rsp->ring_ptr = (response_t *)pkt;
-					rsp->ring_index = cur_ring_index;
+					qla_rsp_ring_rewind_to(rsp,
+					    (response_t *)pkt, cur_ring_index);
 
 					ql_dbg(ql_dbg_init, vha, 0x5091,
 					    "Defer processing ELS opcode %#x...\n",
@@ -4150,8 +4121,8 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 		case PT_LS4_UNSOL:
 			p = (void *)pkt;
 			if (qla_chk_cont_iocb_avail(vha, rsp, (response_t *)pkt, rsp_in)) {
-				rsp->ring_ptr = (response_t *)pkt;
-				rsp->ring_index = cur_ring_index;
+				qla_rsp_ring_rewind_to(rsp, (response_t *)pkt,
+						       cur_ring_index);
 
 				ql_dbg(ql_dbg_init, vha, 0x2124,
 				       "Defer processing UNSOL LS req opcode %#x...\n",
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index e75b7ae22bc5..bb2521c052bf 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -574,13 +574,11 @@ qla25xx_free_req_que(struct scsi_qla_host *vha, struct req_que *req)
 {
 	struct qla_hw_data *ha = vha->hw;
 	uint16_t que_id = req->id;
-	uint16_t reqsz;
+	size_t req_entry_size = qla_req_entry_size(ha);
 
-	reqsz = IS_QLA29XX(ha) ? sizeof(struct request_ext) :
-				 sizeof(request_t);
-
-	dma_free_coherent(&ha->pdev->dev, (req->length + 1) *
-			  reqsz, req->ring, req->dma);
+	dma_free_coherent(&ha->pdev->dev,
+			  (req->length + 1) * req_entry_size,
+			  req->ring, req->dma);
 	req->ring = NULL;
 	req->dma = 0;
 	if (que_id) {
@@ -598,10 +596,7 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
 {
 	struct qla_hw_data *ha = vha->hw;
 	uint16_t que_id = rsp->id;
-	uint16_t rspsz;
-
-	rspsz = IS_QLA29XX(ha) ? sizeof(struct response_ext) :
-				 sizeof(response_t);
+	size_t rsp_entry_size = qla_rsp_entry_size(ha);
 
 	if (rsp->msix && rsp->msix->have_irq) {
 		free_irq(rsp->msix->vector, rsp->msix->handle);
@@ -610,8 +605,9 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
 		rsp->msix->handle = NULL;
 	}
 
-	dma_free_coherent(&ha->pdev->dev, (rsp->length + 1) *
-			  rspsz, rsp->ring, rsp->dma);
+	dma_free_coherent(&ha->pdev->dev,
+			  (rsp->length + 1) * rsp_entry_size,
+			  rsp->ring, rsp->dma);
 	rsp->ring = NULL;
 	rsp->dma = 0;
 	if (que_id) {
@@ -715,7 +711,7 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
 	uint16_t que_id = 0;
 	device_reg_t *reg;
 	uint32_t cnt;
-	uint16_t reqsz;
+	size_t req_entry_size = qla_req_entry_size(ha);
 
 	req = kzalloc_obj(struct req_que);
 	if (req == NULL) {
@@ -724,18 +720,19 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
 		goto failed;
 	}
 
-	reqsz = IS_QLA29XX(ha) ? sizeof(struct request_ext) :
-				 sizeof(request_t);
-
 	req->length = REQUEST_ENTRY_CNT_24XX;
 	req->ring = dma_alloc_coherent(&ha->pdev->dev,
-			(req->length + 1) * reqsz,
+			(req->length + 1) * req_entry_size,
 			&req->dma, GFP_KERNEL);
 	if (req->ring == NULL) {
 		ql_log(ql_log_fatal, base_vha, 0x00da,
 		    "Failed to allocate memory for request_ring.\n");
 		goto que_failed;
 	}
+	if (IS_QLA29XX(ha)) {
+		req->ring_ext = (struct request_ext *)req->ring;
+		req->ring_ext_ptr = req->ring_ext;
+	}
 
 	ret = qla2x00_alloc_outstanding_cmds(ha, req);
 	if (ret != QLA_SUCCESS)
@@ -781,7 +778,15 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
 		req->outstanding_cmds[cnt] = NULL;
 	req->current_outstanding_cmd = 1;
 
+	/*
+	 * Re-anchor both the 24xx (ring_ptr) and 29xx (ring_ext_ptr) views
+	 * at the start of the ring.  Keeping them reset together here
+	 * guarantees they stay in sync with ring_index=0 regardless of any
+	 * prior allocator/fast-path advances against this queue.
+	 */
 	req->ring_ptr = req->ring;
+	if (IS_QLA29XX(ha))
+		req->ring_ext_ptr = req->ring_ext;
 	req->ring_index = 0;
 	req->cnt = req->length;
 	req->id = que_id;
@@ -789,7 +794,15 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
 	req->req_q_in = &reg->isp25mq.req_q_in;
 	req->req_q_out = &reg->isp25mq.req_q_out;
 	req->max_q_depth = ha->req_q_map[0]->max_q_depth;
-	req->out_ptr = (uint16_t *)(req->ring + req->length);
+	/*
+	 * out_ptr sits in the scratch slot immediately after the ring.  Use
+	 * the 29xx-stride pointer when the ring is 128-byte-per-entry so the
+	 * pointer arithmetic resolves to the correct byte offset.
+	 */
+	if (IS_QLA29XX(ha))
+		req->out_ptr = (uint16_t *)(req->ring_ext + req->length);
+	else
+		req->out_ptr = (uint16_t *)(req->ring + req->length);
 	mutex_unlock(&ha->mq_lock);
 	ql_dbg(ql_dbg_multiq, base_vha, 0xc004,
 	    "ring_ptr=%p ring_index=%d, "
@@ -846,7 +859,7 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
 	struct scsi_qla_host *vha = pci_get_drvdata(ha->pdev);
 	uint16_t que_id = 0;
 	device_reg_t *reg;
-	uint16_t rspsz;
+	size_t rsp_entry_size = qla_rsp_entry_size(ha);
 
 	rsp = kzalloc_obj(struct rsp_que);
 	if (rsp == NULL) {
@@ -855,18 +868,19 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
 		goto failed;
 	}
 
-	rspsz = IS_QLA29XX(ha) ? sizeof(struct response_ext) :
-				 sizeof(response_t);
-
 	rsp->length = RESPONSE_ENTRY_CNT_MQ;
 	rsp->ring = dma_alloc_coherent(&ha->pdev->dev,
-			(rsp->length + 1) * rspsz,
+			(rsp->length + 1) * rsp_entry_size,
 			&rsp->dma, GFP_KERNEL);
 	if (rsp->ring == NULL) {
 		ql_log(ql_log_warn, base_vha, 0x00e1,
 		    "Failed to allocate memory for response ring.\n");
 		goto que_failed;
 	}
+	if (IS_QLA29XX(ha)) {
+		rsp->ring_ext = (struct response_ext *)rsp->ring;
+		rsp->ring_ext_ptr = rsp->ring_ext;
+	}
 
 	mutex_lock(&ha->mq_lock);
 	que_id = find_first_zero_bit(ha->rsp_qid_map, ha->max_rsp_queues);
@@ -905,7 +919,10 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
 	reg = ISP_QUE_REG(ha, que_id);
 	rsp->rsp_q_in = &reg->isp25mq.rsp_q_in;
 	rsp->rsp_q_out = &reg->isp25mq.rsp_q_out;
-	rsp->in_ptr = (uint16_t *)(rsp->ring + rsp->length);
+	if (IS_QLA29XX(ha))
+		rsp->in_ptr = (uint16_t *)(rsp->ring_ext + rsp->length);
+	else
+		rsp->in_ptr = (uint16_t *)(rsp->ring + rsp->length);
 	mutex_unlock(&ha->mq_lock);
 	ql_dbg(ql_dbg_multiq, base_vha, 0xc00b,
 	    "options=%x id=%d rsp_q_in=%p rsp_q_out=%p\n",
diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index 2531e71c39dc..b2eacf0277f4 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -554,7 +554,8 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
 	unsigned long   flags;
 	uint32_t        *clr_ptr;
 	uint32_t        handle;
-	struct cmd_nvme *cmd_pkt;
+	struct cmd_nvme *cmd_pkt = NULL;
+	struct cmd_nvme_ext *cmd_pkt_ext = NULL;
 	uint16_t        cnt, i;
 	uint16_t        req_cnt;
 	uint16_t        tot_dsds;
@@ -584,7 +585,10 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
 		rval = -EBUSY;
 		goto queuing_error;
 	}
-	req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+	if (IS_QLA29XX(ha))
+		req_cnt = qla29xx_calc_iocbs(vha, tot_dsds, NUM_NVME_DSDS);
+	else
+		req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
 
 	sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH;
 	sp->iores.exch_cnt = 1;
@@ -629,19 +633,62 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
 	sp->handle = handle;
 	req->cnt -= req_cnt;
 
-	cmd_pkt = (struct cmd_nvme *)req->ring_ptr;
+	/*
+	 * 29xx operates on the 128-byte extended IOCB ring; the header
+	 * layout of struct cmd_nvme is identical to the head of struct
+	 * cmd_nvme_ext through 'byte_count', so the common-field writes
+	 * below go through cmd_pkt regardless of stride.  Divergent tail
+	 * fields (port_id/vp_index, DSD array) are handled via
+	 * IS_QLA29XX(ha) branches.
+	 *
+	 * Enforce the layout contract at compile time so any future
+	 * change to either struct that breaks the common-header overlap
+	 * fails the build rather than silently corrupting IOCBs.
+	 */
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, entry_type) !=
+		     offsetof(struct cmd_nvme_ext, entry_type));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, handle) !=
+		     offsetof(struct cmd_nvme_ext, handle));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, nport_handle) !=
+		     offsetof(struct cmd_nvme_ext, nport_handle));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, timeout) !=
+		     offsetof(struct cmd_nvme_ext, timeout));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, dseg_count) !=
+		     offsetof(struct cmd_nvme_ext, dseg_count));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, nvme_rsp_dsd_len) !=
+		     offsetof(struct cmd_nvme_ext, nvme_rsp_dsd_len));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, rsvd) !=
+		     offsetof(struct cmd_nvme_ext, rsvd));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, control_flags) !=
+		     offsetof(struct cmd_nvme_ext, control_flags));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, nvme_cmnd_dseg_len) !=
+		     offsetof(struct cmd_nvme_ext, nvme_cmnd_dseg_len));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, nvme_cmnd_dseg_address) !=
+		     offsetof(struct cmd_nvme_ext, nvme_cmnd_dseg_address));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, nvme_rsp_dseg_address) !=
+		     offsetof(struct cmd_nvme_ext, nvme_rsp_dseg_address));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, byte_count) !=
+		     offsetof(struct cmd_nvme_ext, byte_count));
+	BUILD_BUG_ON(sizeof_field(struct cmd_nvme, byte_count) !=
+		     sizeof_field(struct cmd_nvme_ext, byte_count));
+
+	if (IS_QLA29XX(ha))
+		cmd_pkt = (struct cmd_nvme *)req->ring_ext_ptr;
+	else
+		cmd_pkt = (struct cmd_nvme *)req->ring_ptr;
+	cmd_pkt_ext = (struct cmd_nvme_ext *)cmd_pkt;
 	cmd_pkt->handle = make_handle(req->id, handle);
 
 	/* Zero out remaining portion of packet. */
 	clr_ptr = (uint32_t *)cmd_pkt + 2;
-	memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+	if (IS_QLA29XX(ha))
+		memset(clr_ptr, 0, REQUEST_ENTRY_SIZE_EXT - 8);
+	else
+		memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
 
 	cmd_pkt->entry_status = 0;
-
-	/* Update entry type to indicate Command NVME IOCB */
 	cmd_pkt->entry_type = COMMAND_NVME;
 
-	/* No data transfer how do we check buffer len == 0?? */
 	if (fd->io_dir == NVMEFC_FCP_READ) {
 		cmd_pkt->control_flags = cpu_to_le16(CF_READ_DATA);
 		qpair->counters.input_bytes += fd->payload_length;
@@ -666,18 +713,23 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
 	if (sp->fcport->edif.enable && fd->io_dir != 0)
 		cmd_pkt->control_flags |= cpu_to_le16(CF_EN_EDIF);
 
-	/* Set BIT_13 of control flags for Async event */
 	if (vha->flags.nvme2_enabled &&
-	    cmd->sqe.common.opcode == nvme_admin_async_event) {
+	    cmd->sqe.common.opcode == nvme_admin_async_event)
 		cmd_pkt->control_flags |= cpu_to_le16(CF_ADMIN_ASYNC_EVENT);
-	}
 
-	/* Set NPORT-ID */
 	cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-	cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
-	cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
-	cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
-	cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
+	if (IS_QLA29XX(ha)) {
+		/*
+		 * 29xx extended NVMe IOCB has no port_id[] field; vp_index is
+		 * a 9-bit __le16 (see CMD_EXT_VP_INDEX_MASK).
+		 */
+		cmd_pkt_ext->vp_index = cpu_to_le16(sp->fcport->vha->vp_idx);
+	} else {
+		cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
+		cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
+		cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+		cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
+	}
 
 	/* NVME RSP IU */
 	cmd_pkt->nvme_rsp_dsd_len = cpu_to_le16(fd->rsplen);
@@ -690,36 +742,51 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
 	cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
 	cmd_pkt->byte_count = cpu_to_le32(fd->payload_length);
 
-	/* One DSD is available in the Command Type NVME IOCB */
-	avail_dsds = 1;
-	cur_dsd = &cmd_pkt->nvme_dsd;
+	/*
+	 * 24xx carries a single inline DSD in the NVMe command IOCB; 29xx
+	 * carries NUM_NVME_DSDS inline DSDs in the extended IOCB.
+	 */
+	if (IS_QLA29XX(ha)) {
+		avail_dsds = NUM_NVME_DSDS;
+		cur_dsd = &cmd_pkt_ext->nvme_dsd[0];
+	} else {
+		avail_dsds = 1;
+		cur_dsd = &cmd_pkt->nvme_dsd;
+	}
 	sgl = fd->first_sgl;
 
 	/* Load data segments */
 	for_each_sg(sgl, sg, tot_dsds, i) {
-		cont_a64_entry_t *cont_pkt;
-
 		/* Allocate additional continuation packets? */
 		if (avail_dsds == 0) {
-			/*
-			 * Five DSDs are available in the Continuation
-			 * Type 1 IOCB.
-			 */
-
-			/* Adjust ring index */
-			req->ring_index++;
-			if (req->ring_index == req->length) {
-				req->ring_index = 0;
-				req->ring_ptr = req->ring;
+			if (IS_QLA29XX(ha)) {
+				struct cont_a64_entry_ext *cont_pkt;
+
+				cont_pkt = qla2900_prep_cont_type1_iocb(vha,
+									req);
+				cur_dsd = cont_pkt->dsd;
+				avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
 			} else {
-				req->ring_ptr++;
+				cont_a64_entry_t *cont_pkt;
+
+				/*
+				 * Five DSDs are available in the 24xx
+				 * Continuation Type 1 IOCB.
+				 */
+				req->ring_index++;
+				if (req->ring_index == req->length) {
+					req->ring_index = 0;
+					req->ring_ptr = req->ring;
+				} else {
+					req->ring_ptr++;
+				}
+				cont_pkt = (cont_a64_entry_t *)req->ring_ptr;
+				put_unaligned_le32(CONTINUE_A64_TYPE,
+						   &cont_pkt->entry_type);
+
+				cur_dsd = cont_pkt->dsd;
+				avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
 			}
-			cont_pkt = (cont_a64_entry_t *)req->ring_ptr;
-			put_unaligned_le32(CONTINUE_A64_TYPE,
-					   &cont_pkt->entry_type);
-
-			cur_dsd = cont_pkt->dsd;
-			avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
 		}
 
 		append_dsd64(&cur_dsd, sg);
@@ -731,13 +798,7 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
 	wmb();
 
 	/* Adjust ring index. */
-	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else {
-		req->ring_ptr++;
-	}
+	qla_req_ring_advance(ha, req);
 
 	/* ignore nvme async cmd due to long timeout */
 	if (!nvme->u.nvme.aen_op)
@@ -746,7 +807,12 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
 	/* Set chip new ring index. */
 	wrt_reg_dword(req->req_q_in, req->ring_index);
 
-	if (vha->flags.process_response_queue &&
+	/*
+	 * 29xx inline response drain would require the 128-byte response ring
+	 * view, which the 24xx qla24xx_process_response_queue() does not walk;
+	 * skip here and rely on the normal ISR path.
+	 */
+	if (!IS_QLA29XX(ha) && vha->flags.process_response_queue &&
 	    rsp->ring_ptr->signature != RESPONSE_PROCESSED)
 		qla24xx_process_response_queue(vha, rsp);
 
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 948242f0088e..146701445485 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -502,6 +502,8 @@ static int qla2x00_alloc_queues(struct qla_hw_data *ha, struct req_que *req,
 
 static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req)
 {
+	size_t req_entry_size = qla_req_entry_size(ha);
+
 	if (IS_QLAFX00(ha)) {
 		if (req && req->ring_fx00)
 			dma_free_coherent(&ha->pdev->dev,
@@ -509,8 +511,8 @@ static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req)
 			    req->ring_fx00, req->dma_fx00);
 	} else if (req && req->ring)
 		dma_free_coherent(&ha->pdev->dev,
-		(req->length + 1) * sizeof(request_t),
-		req->ring, req->dma);
+		    (req->length + 1) * req_entry_size,
+		    req->ring, req->dma);
 
 	if (req)
 		kfree(req->outstanding_cmds);
@@ -520,6 +522,8 @@ static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req)
 
 static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp)
 {
+	size_t rsp_entry_size = qla_rsp_entry_size(ha);
+
 	if (IS_QLAFX00(ha)) {
 		if (rsp && rsp->ring_fx00)
 			dma_free_coherent(&ha->pdev->dev,
@@ -527,8 +531,8 @@ static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp)
 			    rsp->ring_fx00, rsp->dma_fx00);
 	} else if (rsp && rsp->ring) {
 		dma_free_coherent(&ha->pdev->dev,
-		(rsp->length + 1) * sizeof(response_t),
-		rsp->ring, rsp->dma);
+		    (rsp->length + 1) * rsp_entry_size,
+		    rsp->ring, rsp->dma);
 	}
 	kfree(rsp);
 }
@@ -2693,6 +2697,7 @@ static struct isp_operations qla29xx_isp_ops = {
 	.read_optrom_region	= qla29xx_read_optrom_data,
 	.write_optrom_region	= qla29xx_write_optrom_data,
 	.get_flash_version	= qla24xx_get_flash_version,
+	.start_scsi		= qla24xx_dif_start_scsi,
 	.start_scsi_mq		= qla2xxx_dif_start_scsi_mq,
 	.abort_isp		= qla2x00_abort_isp,
 	.iospace_config		= qla83xx_iospace_config,
@@ -4249,6 +4254,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 {
 	char	name[16];
 	int rc;
+	size_t req_entry_size = qla_req_entry_size(ha);
+	size_t rsp_entry_size = qla_rsp_entry_size(ha);
 
 	if (QLA_TGT_MODE_ENABLED() || EDIF_CAP(ha)) {
 		ha->vp_map = kzalloc_objs(struct qla_vp_map,
@@ -4444,13 +4451,17 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 	}
 	(*req)->length = req_len;
 	(*req)->ring = dma_alloc_coherent(&ha->pdev->dev,
-		((*req)->length + 1) * sizeof(request_t),
+		((*req)->length + 1) * req_entry_size,
 		&(*req)->dma, GFP_KERNEL);
 	if (!(*req)->ring) {
 		ql_log_pci(ql_log_fatal, ha->pdev, 0x0029,
 		    "Failed to allocate memory for req_ring.\n");
 		goto fail_req_ring;
 	}
+	if (IS_QLA29XX(ha)) {
+		(*req)->ring_ext = (struct request_ext *)(*req)->ring;
+		(*req)->ring_ext_ptr = (*req)->ring_ext;
+	}
 	/* Allocate memory for response ring */
 	*rsp = kzalloc_obj(struct rsp_que);
 	if (!*rsp) {
@@ -4461,13 +4472,17 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 	(*rsp)->hw = ha;
 	(*rsp)->length = rsp_len;
 	(*rsp)->ring = dma_alloc_coherent(&ha->pdev->dev,
-		((*rsp)->length + 1) * sizeof(response_t),
+		((*rsp)->length + 1) * rsp_entry_size,
 		&(*rsp)->dma, GFP_KERNEL);
 	if (!(*rsp)->ring) {
 		ql_log_pci(ql_log_fatal, ha->pdev, 0x002b,
 		    "Failed to allocate memory for rsp_ring.\n");
 		goto fail_rsp_ring;
 	}
+	if (IS_QLA29XX(ha)) {
+		(*rsp)->ring_ext = (struct response_ext *)(*rsp)->ring;
+		(*rsp)->ring_ext_ptr = (*rsp)->ring_ext;
+	}
 	(*req)->rsp = *rsp;
 	(*rsp)->req = *req;
 	ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002c,
@@ -4617,16 +4632,18 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 fail_ex_init_cb:
 	kfree(ha->npiv_info);
 fail_npiv_info:
-	dma_free_coherent(&ha->pdev->dev, ((*rsp)->length + 1) *
-		sizeof(response_t), (*rsp)->ring, (*rsp)->dma);
+	dma_free_coherent(&ha->pdev->dev,
+		((*rsp)->length + 1) * rsp_entry_size,
+		(*rsp)->ring, (*rsp)->dma);
 	(*rsp)->ring = NULL;
 	(*rsp)->dma = 0;
 fail_rsp_ring:
 	kfree(*rsp);
 	*rsp = NULL;
 fail_rsp:
-	dma_free_coherent(&ha->pdev->dev, ((*req)->length + 1) *
-		sizeof(request_t), (*req)->ring, (*req)->dma);
+	dma_free_coherent(&ha->pdev->dev,
+		((*req)->length + 1) * req_entry_size,
+		(*req)->ring, (*req)->dma);
 	(*req)->ring = NULL;
 	(*req)->dma = 0;
 fail_req_ring:
@@ -8324,6 +8341,7 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(struct access_chip_rsp_84xx) != 64);
 	BUILD_BUG_ON(sizeof(struct cmd_bidir) != 64);
 	BUILD_BUG_ON(sizeof(struct cmd_nvme) != 64);
+	BUILD_BUG_ON(sizeof(struct cmd_nvme_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct cmd_type_6) != 64);
 	BUILD_BUG_ON(sizeof(struct cmd_type_7) != 64);
 	BUILD_BUG_ON(sizeof(struct cmd_type_7_fx00) != 64);
@@ -8351,6 +8369,7 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(struct mbx_entry) != 64);
 	BUILD_BUG_ON(sizeof(struct mid_init_cb_24xx) != 5252);
 	BUILD_BUG_ON(sizeof(struct mrk_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct mrk_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct nvram_24xx) != 512);
 	BUILD_BUG_ON(sizeof(struct nvram_81xx) != 512);
 	BUILD_BUG_ON(sizeof(struct pt_ls4_request) != 64);
diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c
index e47da45e93a0..53a505df8da0 100644
--- a/drivers/scsi/qla2xxx/qla_target.c
+++ b/drivers/scsi/qla2xxx/qla_target.c
@@ -1738,8 +1738,8 @@ static int qlt_build_abts_resp_iocb(struct qla_tgt_mgmt_cmd *mcmd)
 		return -EAGAIN;
 	}
 
-	resp = (struct abts_resp_to_24xx *)qpair->req->ring_ptr;
-	memset(resp, 0, sizeof(*resp));
+	resp = (struct abts_resp_to_24xx *)qla_req_ring_slot(ha, qpair->req);
+	memset(resp, 0, qla_req_entry_size(ha));
 
 	h = qlt_make_handle(qpair);
 	if (unlikely(h == QLA_TGT_NULL_HANDLE)) {
@@ -2490,17 +2490,13 @@ static int qlt_check_reserve_free_req(struct qla_qpair *qpair,
 /*
  * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
  */
-static inline void *qlt_get_req_pkt(struct req_que *req)
+static inline void *qlt_get_req_pkt(struct qla_hw_data *ha,
+				     struct req_que *req)
 {
 	/* Adjust ring index. */
-	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else {
-		req->ring_ptr++;
-	}
-	return (cont_entry_t *)req->ring_ptr;
+	qla_req_ring_advance(ha, req);
+
+	return qla_req_ring_slot(ha, req);
 }
 
 /* ha->hardware_lock supposed to be held on entry */
@@ -2549,9 +2545,9 @@ static int qlt_24xx_build_ctio_pkt(struct qla_qpair *qpair,
 	uint16_t temp;
 	struct qla_tgt_cmd      *cmd = prm->cmd;
 
-	pkt = (struct ctio7_to_24xx *)qpair->req->ring_ptr;
+	pkt = (struct ctio7_to_24xx *)qla_req_ring_slot(qpair->hw, qpair->req);
 	prm->pkt = pkt;
-	memset(pkt, 0, sizeof(*pkt));
+	memset(pkt, 0, qla_req_entry_size(qpair->hw));
 
 	pkt->entry_type = CTIO_TYPE7;
 	pkt->entry_count = (uint8_t)prm->req_cnt;
@@ -2600,11 +2596,12 @@ static void qlt_load_cont_data_segments(struct qla_tgt_prm *prm)
 {
 	int cnt;
 	struct dsd64 *cur_dsd;
+	struct qla_hw_data *ha = prm->cmd->qpair->hw;
 
 	/* Build continuation packets */
 	while (prm->seg_cnt > 0) {
 		cont_a64_entry_t *cont_pkt64 =
-			(cont_a64_entry_t *)qlt_get_req_pkt(
+			(cont_a64_entry_t *)qlt_get_req_pkt(ha,
 			   prm->cmd->qpair->req);
 
 		/*
@@ -2614,7 +2611,7 @@ static void qlt_load_cont_data_segments(struct qla_tgt_prm *prm)
 		 * that.
 		 */
 
-		memset(cont_pkt64, 0, sizeof(*cont_pkt64));
+		memset(cont_pkt64, 0, qla_req_entry_size(ha));
 
 		cont_pkt64->entry_count = 1;
 		cont_pkt64->sys_define = 0;
@@ -3012,9 +3009,9 @@ qlt_build_ctio_crc2_pkt(struct qla_qpair *qpair, struct qla_tgt_prm *prm)
 
 	ha = vha->hw;
 
-	pkt = (struct ctio_crc2_to_fw *)qpair->req->ring_ptr;
+	pkt = (struct ctio_crc2_to_fw *)qla_req_ring_slot(ha, qpair->req);
 	prm->pkt = pkt;
-	memset(pkt, 0, sizeof(*pkt));
+	memset(pkt, 0, qla_req_entry_size(ha));
 
 	ql_dbg_qp(ql_dbg_tgt, cmd->qpair, 0xe071,
 		"qla_target(%d):%s: se_cmd[%p] CRC2 prot_op[0x%x] cmd prot sg:cnt[%p:%x] lba[%llu]\n",
@@ -3306,7 +3303,7 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type,
 			 */
 			struct ctio7_to_24xx *ctio =
 				(struct ctio7_to_24xx *)qlt_get_req_pkt(
-				    qpair->req);
+				    vha->hw, qpair->req);
 
 			ql_dbg_qp(ql_dbg_tgt, qpair, 0x305e,
 			    "Building additional status packet 0x%p.\n",
@@ -3316,6 +3313,7 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type,
 			 * T10Dif: ctio_crc2_to_fw overlay ontop of
 			 * ctio7_to_24xx
 			 */
+			memset(ctio, 0, qla_req_entry_size(vha->hw));
 			memcpy(ctio, pkt, sizeof(*ctio));
 			/* reset back to CTIO7 */
 			ctio->entry_count = 1;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 12/88] scsi: qla2xxx: Skip image-set-valid attribute for 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (10 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 11/88] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:12   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 13/88] scsi: qla2xxx: Skip unsupported sysfs attributes " Nilesh Javali
                   ` (75 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

The 29xx adapter does not support the QLA_IMG_SET_VALID_SUPPORT
driver attribute.  Gate the attribute behind an IS_QLA29XX()
check so that userspace applications querying driver capabilities
via BSG receive accurate information.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_bsg.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 5c6aedb3179a..8a969174a261 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -2952,11 +2952,14 @@ qla2x00_get_flash_image_status(struct bsg_job *bsg_job)
 static int
 qla2x00_get_drv_attr(struct bsg_job *bsg_job)
 {
+	scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
 	struct qla_drv_attr drv_attr;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct qla_hw_data *ha = vha->hw;
 
 	memset(&drv_attr, 0, sizeof(struct qla_drv_attr));
-	drv_attr.ext_attributes |= QLA_IMG_SET_VALID_SUPPORT;
+	if (!IS_QLA29XX(ha))
+		drv_attr.ext_attributes |= QLA_IMG_SET_VALID_SUPPORT;
 
 
 	sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 13/88] scsi: qla2xxx: Skip unsupported sysfs attributes for 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (11 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 12/88] scsi: qla2xxx: Skip image-set-valid attribute " Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 14/88] scsi: qla2xxx: Enable get_fw_version mailbox " Nilesh Javali
                   ` (74 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Not all sysfs attributes are applicable to the 29xx adapter.
Return -EPERM for attributes that are meaningless on 29xx (gold
firmware version, 84xx firmware version, flash block size, VLAN
ID, VN-port MAC address, and CNA firmware dump toggle) so that
userspace tools do not see stale or undefined values.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_attr.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 800751ab562a..e8755ab86b6a 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1471,6 +1471,9 @@ qla2x00_optrom_gold_fw_version_show(struct device *dev,
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 
+	if (IS_QLA29XX(ha))
+		return -EPERM;
+
 	if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
 	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
 		return scnprintf(buf, PAGE_SIZE, "\n");
@@ -1499,6 +1502,9 @@ qla24xx_84xx_fw_version_show(struct device *dev,
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 
+	if (IS_QLA29XX(ha))
+		return -EPERM;
+
 	if (!IS_QLA84XX(ha))
 		return scnprintf(buf, PAGE_SIZE, "\n");
 
@@ -1565,6 +1571,9 @@ qla2x00_flash_block_size_show(struct device *dev,
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 
+	if (IS_QLA29XX(ha))
+		return -EPERM;
+
 	return scnprintf(buf, PAGE_SIZE, "0x%x\n", ha->fdt_block_size);
 }
 
@@ -1573,6 +1582,10 @@ qla2x00_vlan_id_show(struct device *dev, struct device_attribute *attr,
     char *buf)
 {
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+	struct qla_hw_data *ha = vha->hw;
+
+	if (IS_QLA29XX(ha))
+		return -EPERM;
 
 	if (!IS_CNA_CAPABLE(vha->hw))
 		return scnprintf(buf, PAGE_SIZE, "\n");
@@ -1585,6 +1598,10 @@ qla2x00_vn_port_mac_address_show(struct device *dev,
     struct device_attribute *attr, char *buf)
 {
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+	struct qla_hw_data *ha = vha->hw;
+
+	if (IS_QLA29XX(ha))
+		return -EPERM;
 
 	if (!IS_CNA_CAPABLE(vha->hw))
 		return scnprintf(buf, PAGE_SIZE, "\n");
@@ -1716,6 +1733,10 @@ qla2x00_allow_cna_fw_dump_show(struct device *dev,
 	struct device_attribute *attr, char *buf)
 {
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+	struct qla_hw_data *ha = vha->hw;
+
+	if (IS_QLA29XX(ha))
+		return -EPERM;
 
 	if (!IS_P3P_TYPE(vha->hw))
 		return scnprintf(buf, PAGE_SIZE, "\n");
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 14/88] scsi: qla2xxx: Enable get_fw_version mailbox for 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (12 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 13/88] scsi: qla2xxx: Skip unsupported sysfs attributes " Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:12   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 15/88] scsi: qla2xxx: Extend execute_fw mailbox to include 29xx Nilesh Javali
                   ` (73 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

The serdes_version and several firmware capability fields were not
populated for 29xx because the get_fw_version mailbox path
excluded it from the 27xx/28xx checks.  Add IS_QLA29XX() to
the relevant conditionals so that firmware version, EDIF, and
serdes information are correctly retrieved on 29xx adapters.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_attr.c | 4 ++--
 drivers/scsi/qla2xxx/qla_mbx.c  | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index e8755ab86b6a..c44f5282abb3 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1526,7 +1526,7 @@ qla2x00_serdes_version_show(struct device *dev, struct device_attribute *attr,
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return scnprintf(buf, PAGE_SIZE, "\n");
 
 	return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d\n",
@@ -2386,7 +2386,7 @@ qla2x00_fw_attr_show(struct device *dev,
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return scnprintf(buf, PAGE_SIZE, "\n");
 
 	return scnprintf(buf, PAGE_SIZE, "%llx\n",
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 9c2633ca5036..0feb98b83293 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -1136,7 +1136,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
 		mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8;
 	if (IS_FWI2_CAPABLE(ha))
 		mcp->in_mb |= MBX_17|MBX_16|MBX_15;
-	if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 		mcp->in_mb |=
 		    MBX_25|MBX_24|MBX_23|MBX_22|MBX_21|MBX_20|MBX_19|MBX_18|
 		    MBX_14|MBX_13|MBX_11|MBX_10|MBX_9|MBX_8|MBX_7;
@@ -1212,7 +1212,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
 			vha->flags.nvme2_enabled = 1;
 		}
 
-		if (IS_QLA28XX(ha) && ha->flags.edif_hw && ql2xsecenable &&
+		if ((IS_QLA28XX(ha) || IS_QLA29XX(ha)) && ha->flags.edif_hw && ql2xsecenable &&
 		    (ha->fw_attributes_ext[0] & FW_ATTR_EXT0_EDIF)) {
 			ha->flags.edif_enabled = 1;
 			ql_log(ql_log_info, vha, 0xffff,
@@ -1220,7 +1220,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
 		}
 	}
 
-	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+	if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		ha->serdes_version[0] = mcp->mb[7] & 0xff;
 		ha->serdes_version[1] = mcp->mb[8] >> 8;
 		ha->serdes_version[2] = mcp->mb[8] & 0xff;
@@ -1234,7 +1234,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
 		ha->fw_shared_ram_end = (mcp->mb[21] << 16) | mcp->mb[20];
 		ha->fw_ddr_ram_start = (mcp->mb[23] << 16) | mcp->mb[22];
 		ha->fw_ddr_ram_end = (mcp->mb[25] << 16) | mcp->mb[24];
-		if (IS_QLA28XX(ha)) {
+		if (IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 			if (mcp->mb[16] & BIT_10)
 				ha->flags.secure_fw = 1;
 
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 15/88] scsi: qla2xxx: Extend execute_fw mailbox to include 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (13 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 14/88] scsi: qla2xxx: Enable get_fw_version mailbox " Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:23   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 16/88] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx Nilesh Javali
                   ` (72 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Add IS_QLA29XX() to the BPM capability macros and to the
execute-firmware mailbox command so that NVMe enable, minimum
speed negotiation, 128 Gbps speed reporting, EDIF hardware
detection, and FW-semaphore retry logic all apply to 29xx
adapters.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_attr.c | 12 ++++++++++--
 drivers/scsi/qla2xxx/qla_def.h  |  5 +++--
 drivers/scsi/qla2xxx/qla_gs.c   | 16 +++++++++++++---
 drivers/scsi/qla2xxx/qla_isr.c  |  2 +-
 drivers/scsi/qla2xxx/qla_mbx.c  | 23 +++++++++++++----------
 5 files changed, 40 insertions(+), 18 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index c44f5282abb3..7b7722de2844 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1784,10 +1784,11 @@ qla2x00_min_supported_speed_show(struct device *dev,
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return scnprintf(buf, PAGE_SIZE, "\n");
 
 	return scnprintf(buf, PAGE_SIZE, "%s\n",
+	    ha->min_supported_speed == 7 ? "128Gps" :
 	    ha->min_supported_speed == 6 ? "64Gps" :
 	    ha->min_supported_speed == 5 ? "32Gps" :
 	    ha->min_supported_speed == 4 ? "16Gps" :
@@ -1803,10 +1804,11 @@ qla2x00_max_supported_speed_show(struct device *dev,
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return scnprintf(buf, PAGE_SIZE, "\n");
 
 	return scnprintf(buf, PAGE_SIZE, "%s\n",
+	    ha->max_supported_speed  == 3 ? "128Gps" :
 	    ha->max_supported_speed  == 2 ? "64Gps" :
 	    ha->max_supported_speed  == 1 ? "32Gps" :
 	    ha->max_supported_speed  == 0 ? "16Gps" : "unknown");
@@ -1887,6 +1889,7 @@ static const struct {
 	{ PORT_SPEED_16GB, "16" },
 	{ PORT_SPEED_32GB, "32" },
 	{ PORT_SPEED_64GB, "64" },
+	{ PORT_SPEED_128GB, "128" },
 	{ PORT_SPEED_10GB, "10" },
 };
 
@@ -2668,6 +2671,9 @@ qla2x00_get_host_speed(struct Scsi_Host *shost)
 	case PORT_SPEED_64GB:
 		speed = FC_PORTSPEED_64GBIT;
 		break;
+	case PORT_SPEED_128GB:
+		speed = FC_PORTSPEED_128GBIT;
+		break;
 	default:
 		speed = FC_PORTSPEED_UNKNOWN;
 		break;
@@ -3429,6 +3435,8 @@ qla2x00_get_host_supported_speeds(scsi_qla_host_t *vha, uint speeds)
 {
 	uint supported_speeds = FC_PORTSPEED_UNKNOWN;
 
+	if (speeds & FDMI_PORT_SPEED_128GB)
+		supported_speeds |= FC_PORTSPEED_128GBIT;
 	if (speeds & FDMI_PORT_SPEED_64GB)
 		supported_speeds |= FC_PORTSPEED_64GBIT;
 	if (speeds & FDMI_PORT_SPEED_32GB)
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index fcd185807e2b..0bbe2bae7101 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -4333,6 +4333,7 @@ struct qla_hw_data {
 #define PORT_SPEED_16GB 0x05
 #define PORT_SPEED_32GB 0x06
 #define PORT_SPEED_64GB 0x07
+#define PORT_SPEED_128GB 0x08
 #define PORT_SPEED_10GB	0x13
 	uint16_t	link_data_rate;         /* F/W operating speed */
 	uint16_t	set_data_rate;		/* Set by user */
@@ -5581,9 +5582,9 @@ struct sff_8247_a0 {
 /* BPM -- Buffer Plus Management support. */
 #define IS_BPM_CAPABLE(ha) \
 	(IS_QLA25XX(ha) || IS_QLA81XX(ha) || IS_QLA83XX(ha) || \
-	 IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	 IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 #define IS_BPM_RANGE_CAPABLE(ha) \
-	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 #define IS_BPM_ENABLED(vha) \
 	(ql2xautodetectsfp && !vha->vp_idx && IS_BPM_CAPABLE(vha->hw))
 
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index 880cd73feaca..514f04aa1423 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -1508,12 +1508,18 @@ qla25xx_fdmi_port_speed_capability(struct qla_hw_data *ha)
 
 	if (IS_CNA_CAPABLE(ha))
 		return FDMI_PORT_SPEED_10GB;
-	if (IS_QLA28XX(ha) || IS_QLA27XX(ha)) {
-		if (ha->max_supported_speed == 2) {
+	if (IS_QLA28XX(ha) || IS_QLA27XX(ha) || IS_QLA29XX(ha)) {
+		if (ha->max_supported_speed == 3) {
+			if (ha->min_supported_speed <= 7)
+				speeds |= FDMI_PORT_SPEED_128GB;
+		}
+		if (ha->max_supported_speed == 3 ||
+		    ha->max_supported_speed == 2) {
 			if (ha->min_supported_speed <= 6)
 				speeds |= FDMI_PORT_SPEED_64GB;
 		}
-		if (ha->max_supported_speed == 2 ||
+		if (ha->max_supported_speed == 3 ||
+		    ha->max_supported_speed == 2 ||
 		    ha->max_supported_speed == 1) {
 			if (ha->min_supported_speed <= 5)
 				speeds |= FDMI_PORT_SPEED_32GB;
@@ -1577,6 +1583,8 @@ qla25xx_fdmi_port_speed_currently(struct qla_hw_data *ha)
 		return FDMI_PORT_SPEED_32GB;
 	case PORT_SPEED_64GB:
 		return FDMI_PORT_SPEED_64GB;
+	case PORT_SPEED_128GB:
+		return FDMI_PORT_SPEED_128GB;
 	default:
 		return FDMI_PORT_SPEED_UNKNOWN;
 	}
@@ -2620,6 +2628,8 @@ qla2x00_port_speed_capability(uint16_t speed)
 		return PORT_SPEED_32GB;
 	case BIT_7:
 		return PORT_SPEED_64GB;
+	case BIT_6:
+		return PORT_SPEED_128GB;
 	default:
 		return PORT_SPEED_UNKNOWN;
 	}
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index e95fb0e59f38..26722afa937c 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -652,7 +652,7 @@ const char *
 qla2x00_get_link_speed_str(struct qla_hw_data *ha, uint16_t speed)
 {
 	static const char *const link_speeds[] = {
-		"1", "2", "?", "4", "8", "16", "32", "64", "10"
+		"1", "2", "?", "4", "8", "16", "32", "64", "128", "10"
 	};
 #define	QLA_LAST_SPEED (ARRAY_SIZE(link_speeds) - 1)
 
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 0feb98b83293..52d70b61654c 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -699,6 +699,7 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
 {
 	int rval;
 	struct qla_hw_data *ha = vha->hw;
+	struct nvram_81xx *nv = ha->nvram;
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 	u8 semaphore = 0;
@@ -727,14 +728,13 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
 				    ha->lr_distance << LR_DIST_FW_POS;
 		}
 
-		if (ql2xnvmeenable && (IS_QLA27XX(ha) || IS_QLA28XX(ha)))
+		if (ql2xnvmeenable && (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)))
 			mcp->mb[4] |= NVME_ENABLE_FLAG;
 
-		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
-			struct nvram_81xx *nv = ha->nvram;
+		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 			/* set minimum speed if specified in nvram */
 			if (nv->min_supported_speed >= 2 &&
-			    nv->min_supported_speed <= 5) {
+			    nv->min_supported_speed <= 7) {
 				mcp->mb[4] |= BIT_4;
 				mcp->mb[11] |= nv->min_supported_speed & 0xF;
 				mcp->out_mb |= MBX_11;
@@ -772,7 +772,7 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		if (IS_QLA28XX(ha) && rval == QLA_COMMAND_ERROR &&
+		if ((IS_QLA28XX(ha) || IS_QLA29XX(ha)) && rval == QLA_COMMAND_ERROR &&
 		    mcp->mb[1] == 0x27 && retry) {
 			semaphore = 1;
 			retry--;
@@ -800,17 +800,20 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
 	ql_dbg(ql_dbg_mbx, vha, 0x119a,
 	    "fw_ability_mask=%x.\n", ha->fw_ability_mask);
 	ql_dbg(ql_dbg_mbx, vha, 0x1027, "exchanges=%x.\n", mcp->mb[1]);
-	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
-		ha->max_supported_speed = mcp->mb[2] & (BIT_0|BIT_1);
+
+	if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
+		ha->max_supported_speed = mcp->mb[2] & (BIT_0|BIT_1|BIT_2|BIT_3);
 		ql_dbg(ql_dbg_mbx, vha, 0x119b, "max_supported_speed=%s.\n",
 		    ha->max_supported_speed == 0 ? "16Gps" :
 		    ha->max_supported_speed == 1 ? "32Gps" :
-		    ha->max_supported_speed == 2 ? "64Gps" : "unknown");
+		    ha->max_supported_speed == 2 ? "64Gps" :
+		    ha->max_supported_speed == 3 ? "128Gps" : "unknown");
 		if (vha->min_supported_speed) {
 			ha->min_supported_speed = mcp->mb[5] &
-			    (BIT_0 | BIT_1 | BIT_2);
+			    (BIT_0 | BIT_1 | BIT_2 | BIT_3);
 			ql_dbg(ql_dbg_mbx, vha, 0x119c,
 			    "min_supported_speed=%s.\n",
+			    ha->min_supported_speed == 7 ? "128Gps" :
 			    ha->min_supported_speed == 6 ? "64Gps" :
 			    ha->min_supported_speed == 5 ? "32Gps" :
 			    ha->min_supported_speed == 4 ? "16Gps" :
@@ -819,7 +822,7 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
 		}
 	}
 
-	if (IS_QLA28XX(ha) && (mcp->mb[5] & EDIF_HW_SUPPORT)) {
+	if ((IS_QLA28XX(ha) || IS_QLA29XX(ha)) && (mcp->mb[5] & EDIF_HW_SUPPORT)) {
 		ha->flags.edif_hw = 1;
 		ql_log(ql_log_info, vha, 0xffff,
 		    "%s: edif HW\n", __func__);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 16/88] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (14 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 15/88] scsi: qla2xxx: Extend execute_fw mailbox to include 29xx Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:15   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 17/88] scsi: qla2xxx: Enable init_firmware " Nilesh Javali
                   ` (71 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Add IS_QLA29XX() alongside the existing 27xx/28xx checks in
qla2x00_get_adapter_id() so that the additional mailbox
registers (buffer-to-buffer credit, SCM/EDC status) are read
on 29xx adapters.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_mbx.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 52d70b61654c..3fc08120fdf1 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -1772,7 +1772,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
 		mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10;
 	if (IS_FWI2_CAPABLE(vha->hw))
 		mcp->in_mb |= MBX_19|MBX_18|MBX_17|MBX_16;
-	if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw))
+	if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw) || IS_QLA29XX(vha->hw))
 		mcp->in_mb |= MBX_15|MBX_21|MBX_22|MBX_23;
 
 	mcp->tov = MBX_TOV_SECONDS;
@@ -1827,7 +1827,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
 			}
 		}
 
-		if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw)) {
+		if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw) || IS_QLA29XX(vha->hw)) {
 			vha->bbcr = mcp->mb[15];
 			if (mcp->mb[7] & SCM_EDC_ACC_RECEIVED) {
 				ql_log(ql_log_info, vha, 0x11a4,
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 17/88] scsi: qla2xxx: Enable init_firmware mailbox for 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (15 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 16/88] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 18/88] scsi: qla2xxx: Enable get_firmware_state " Nilesh Javali
                   ` (70 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

The init_firmware mailbox command needs 29xx adapter support for reading
back SFP information via mb3 and for validating SFP status on successful
firmware initialization. Add IS_QLA29XX() checks alongside the existing
27xx/28xx checks.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_mbx.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 3fc08120fdf1..9c78aa66e12b 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -1968,7 +1968,7 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
 
 	/* 1 and 2 should normally be captured. */
 	mcp->in_mb = MBX_2|MBX_1|MBX_0;
-	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 		/* mb3 is additional info about the installed SFP. */
 		mcp->in_mb  |= MBX_3;
 	mcp->buf_size = size;
@@ -1992,7 +1992,7 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
 			    0x0104d, ha->ex_init_cb, sizeof(*ha->ex_init_cb));
 		}
 	} else {
-		if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+		if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 			if (mcp->mb[2] == 6 || mcp->mb[3] == 2)
 				ql_dbg(ql_dbg_mbx, vha, 0x119d,
 				    "Invalid SFP/Validation Failed\n");
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 18/88] scsi: qla2xxx: Enable get_firmware_state for 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (16 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 17/88] scsi: qla2xxx: Enable init_firmware " Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:15   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 19/88] scsi: qla2xxx: Enable serdes, resource count and FCE trace " Nilesh Javali
                   ` (69 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Enable get_firmware_state mailbox command for 29xx adapters by adding
IS_QLA29XX() checks alongside existing IS_QLA27XX/IS_QLA28XX checks.
This ensures MBX_12 (MPI state) is properly set up and reported for
29xx adapters.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_attr.c | 2 +-
 drivers/scsi/qla2xxx/qla_mbx.c  | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 7b7722de2844..5a934f40323e 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -2436,7 +2436,7 @@ qla2x00_mpi_fw_state_show(struct device *dev, struct device_attribute *attr,
 	u16 mpi_state;
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!(IS_QLA27XX(ha) || IS_QLA28XX(ha)))
+	if (!(IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)))
 		return scnprintf(buf, PAGE_SIZE,
 				"MPI state reporting is not supported for this HBA.\n");
 
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 9c78aa66e12b..a91ac59dd9c0 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -2283,7 +2283,7 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
 	else
 		mcp->in_mb = MBX_1|MBX_0;
 
-	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+	if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		mcp->mb[12] = 0;
 		mcp->out_mb |= MBX_12;
 		mcp->in_mb |= MBX_12;
@@ -2301,7 +2301,7 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
 		states[3] = mcp->mb[4];
 		states[4] = mcp->mb[5];
 		states[5] = mcp->mb[6];  /* DPORT status */
-		if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
+		if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 			states[11] = mcp->mb[12]; /* MPI state. */
 	}
 
@@ -2309,7 +2309,7 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
 		/*EMPTY*/
 		ql_dbg(ql_dbg_mbx, vha, 0x1055, "Failed=%x.\n", rval);
 	} else {
-		if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+		if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 			if (mcp->mb[2] == 6 || mcp->mb[3] == 2)
 				ql_dbg(ql_dbg_mbx, vha, 0x119e,
 				    "Invalid SFP/Validation Failed\n");
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 19/88] scsi: qla2xxx: Enable serdes, resource count and FCE trace for 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (17 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 18/88] scsi: qla2xxx: Enable get_firmware_state " Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:36   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 20/88] scsi: qla2xxx: Enable set_els_cmds and echo_test " Nilesh Javali
                   ` (68 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

The 29xx adapters share the diagnostic and management interfaces
already supported on ISP27xx/28xx, but several family capability
gates still omitted IS_QLA29XX(), leaving these paths unreachable
on 29xx.

Add IS_QLA29XX() to the relevant checks so the following work on
29xx adapters:

  - read/write SerDes word mailbox commands for PHY register access.
  - get_resource_cnts requests MBX_12 to report the extended
    firmware resource counts.
  - FCE trace: the enable-FCE mailbox command, the "fce" and
    "fw_resource_count" debugfs nodes in qla2x00_dfs_setup(), the
    debugfs enable write in qla2x00_dfs_fce_write(), and the FCE DMA
    buffer allocation in qla2x00_alloc_fce_trace().

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_dfs.c  |  4 ++--
 drivers/scsi/qla2xxx/qla_init.c |  2 +-
 drivers/scsi/qla2xxx/qla_mbx.c  | 11 +++++++----
 3 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c
index 43970caca7b3..177d47e92e49 100644
--- a/drivers/scsi/qla2xxx/qla_dfs.c
+++ b/drivers/scsi/qla2xxx/qla_dfs.c
@@ -497,7 +497,7 @@ qla2x00_dfs_fce_write(struct file *file, const char __user *buffer,
 	unsigned long enable;
 
 	if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
-	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha)) {
+	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) {
 		ql_dbg(ql_dbg_user, vha, 0xd034,
 		       "this adapter does not support FCE.");
 		return -EINVAL;
@@ -698,7 +698,7 @@ qla2x00_dfs_setup(scsi_qla_host_t *vha)
 	struct qla_hw_data *ha = vha->hw;
 
 	if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
-	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		goto out;
 
 	if (qla2x00_dfs_root)
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index bd0684d97fef..ae9bf6871079 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -3733,7 +3733,7 @@ int qla2x00_alloc_fce_trace(scsi_qla_host_t *vha)
 		return -EINVAL;
 
 	if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
-	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return -EINVAL;
 
 	if (ha->fce) {
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index a91ac59dd9c0..d195723fc06b 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -3093,7 +3093,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha)
 	mcp->out_mb = MBX_0;
 	mcp->in_mb = MBX_11|MBX_10|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
 	if (IS_QLA81XX(ha) || IS_QLA83XX(ha) ||
-	    IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	    IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+	    IS_QLA29XX(ha))
 		mcp->in_mb |= MBX_12;
 	mcp->tov = MBX_TOV_SECONDS;
 	mcp->flags = 0;
@@ -3565,7 +3566,8 @@ qla2x00_write_serdes_word(scsi_qla_host_t *vha, uint16_t addr, uint16_t data)
 	mbx_cmd_t *mcp = &mc;
 
 	if (!IS_QLA25XX(vha->hw) && !IS_QLA2031(vha->hw) &&
-	    !IS_QLA27XX(vha->hw) && !IS_QLA28XX(vha->hw))
+	    !IS_QLA27XX(vha->hw) && !IS_QLA28XX(vha->hw) &&
+	    !IS_QLA29XX(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1182,
@@ -3604,7 +3606,8 @@ qla2x00_read_serdes_word(scsi_qla_host_t *vha, uint16_t addr, uint16_t *data)
 	mbx_cmd_t *mcp = &mc;
 
 	if (!IS_QLA25XX(vha->hw) && !IS_QLA2031(vha->hw) &&
-	    !IS_QLA27XX(vha->hw) && !IS_QLA28XX(vha->hw))
+	    !IS_QLA27XX(vha->hw) && !IS_QLA28XX(vha->hw) &&
+	    !IS_QLA29XX(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1185,
@@ -3874,7 +3877,7 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *vha, dma_addr_t fce_dma,
 
 	if (!IS_QLA25XX(vha->hw) && !IS_QLA81XX(vha->hw) &&
 	    !IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw) &&
-	    !IS_QLA28XX(vha->hw))
+	    !IS_QLA28XX(vha->hw) && !IS_QLA29XX(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
 	if (unlikely(pci_channel_offline(vha->hw->pdev)))
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 20/88] scsi: qla2xxx: Enable set_els_cmds and echo_test for 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (18 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 19/88] scsi: qla2xxx: Enable serdes, resource count and FCE trace " Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 21/88] scsi: qla2xxx: Add support for QLA29XX in data rate functions Nilesh Javali
                   ` (67 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Add IS_QLA29XX() checks to qla25xx_set_els_cmds_supported() and
qla2x00_echo_test() so that ELS command support and echo test
diagnostics are available on 29xx series adapters.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_mbx.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index d195723fc06b..a8dd01cb9a9a 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -5087,7 +5087,8 @@ qla25xx_set_els_cmds_supported(scsi_qla_host_t *vha)
 	struct qla_hw_data *ha = vha->hw;
 
 	if (!IS_QLA25XX(ha) && !IS_QLA2031(ha) &&
-	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha) &&
+	    !IS_QLA29XX(ha))
 		return QLA_SUCCESS;
 
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1197,
@@ -5492,10 +5493,10 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq,
 
 	mcp->in_mb = MBX_0;
 	if (IS_CNA_CAPABLE(ha) || IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) ||
-	    IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	    IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 		mcp->in_mb |= MBX_1;
 	if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) ||
-	    IS_QLA28XX(ha))
+	    IS_QLA28XX(ha) || IS_QLA29XX(ha))
 		mcp->in_mb |= MBX_3;
 
 	mcp->tov = MBX_TOV_SECONDS;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 21/88] scsi: qla2xxx: Add support for QLA29XX in data rate functions
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (19 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 20/88] scsi: qla2xxx: Enable set_els_cmds and echo_test " Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 22/88] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx Nilesh Javali
                   ` (66 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Enhance the qla2x00_set_data_rate and qla2x00_get_data_rate
functions to include checks for the QLA29XX series adapters.
This modification ensures that the mailbox commands are correctly
configured for the 29xx series, improving functionality and
compatibility.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_attr.c | 2 +-
 drivers/scsi/qla2xxx/qla_mbx.c  | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 5a934f40323e..c32930f9e9f9 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1824,7 +1824,7 @@ qla2x00_port_speed_store(struct device *dev, struct device_attribute *attr,
 	int mode = QLA_SET_DATA_RATE_LR;
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha)) {
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) {
 		ql_log(ql_log_warn, vha, 0x70d8,
 		    "Speed setting not supported \n");
 		return -EINVAL;
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index a8dd01cb9a9a..c073dc35ef8e 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -5683,7 +5683,7 @@ qla2x00_set_data_rate(scsi_qla_host_t *vha, uint16_t mode)
 
 	mcp->out_mb = MBX_2|MBX_1|MBX_0;
 	mcp->in_mb = MBX_2|MBX_1|MBX_0;
-	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 		mcp->in_mb |= MBX_4|MBX_3;
 	mcp->tov = MBX_TOV_SECONDS;
 	mcp->flags = 0;
@@ -5721,7 +5721,7 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
 	mcp->mb[1] = QLA_GET_DATA_RATE;
 	mcp->out_mb = MBX_1|MBX_0;
 	mcp->in_mb = MBX_2|MBX_1|MBX_0;
-	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 		mcp->in_mb |= MBX_4|MBX_3;
 	mcp->tov = MBX_TOV_SECONDS;
 	mcp->flags = 0;
@@ -5733,7 +5733,7 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
 		if (mcp->mb[1] != 0x7)
 			ha->link_data_rate = mcp->mb[1];
 
-		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 			if (mcp->mb[4] & BIT_0)
 				ql_log(ql_log_info, vha, 0x11a2,
 				    "FEC=enabled (data rate).\n");
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 22/88] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (20 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 21/88] scsi: qla2xxx: Add support for QLA29XX in data rate functions Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:27   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 23/88] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs Nilesh Javali
                   ` (65 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Enable qla2x00_shutdown for 29xx adapter by adding IS_QLA29XX check
to the shutdown path that performs firmware abort cleanup.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_os.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 146701445485..5450c40259bf 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -3840,7 +3840,7 @@ qla2x00_shutdown(struct pci_dev *pdev)
 		qla2x00_disable_eft_trace(vha);
 
 	if (IS_QLA25XX(ha) ||  IS_QLA2031(ha) || IS_QLA27XX(ha) ||
-	    IS_QLA28XX(ha)) {
+	    IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		if (ha->flags.fw_started)
 			qla2x00_abort_isp_cleanup(vha);
 	} else {
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 23/88] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (21 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 22/88] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:26   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 24/88] scsi: qla2xxx: Add support for QLA29XX in memory allocation Nilesh Javali
                   ` (64 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

__qla2x00_alloc_iocbs() open-codes ring pointer selection and entry
size based on IS_QLA29XX(ha): 29xx reaches the slot via ring_ext_ptr
and zeroes REQUEST_ENTRY_SIZE_EXT bytes, while other adapters use
ring_ptr with REQUEST_ENTRY_SIZE bytes.

Replace the two branches with the qla_req_ring_slot() and
qla_req_entry_size() helpers, and initialise pkt at declaration.
The IS_QLAFX00 register-mapped writes remain guarded because
IS_QLAFX00 and IS_QLA29XX cannot be true simultaneously.

No functional change: the bytes written to the firmware-visible IOCB
are identical.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_iocb.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 13882a399fbc..c0cec1f74dfc 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -2390,10 +2390,9 @@ __qla2x00_alloc_iocbs(struct qla_qpair *qpair, srb_t *sp)
 	struct req_que *req = qpair->req;
 	device_reg_t *reg = ISP_QUE_REG(ha, req->id);
 	uint32_t handle;
-	request_t *pkt;
 	uint16_t cnt, req_cnt;
+	request_t *pkt = NULL;
 
-	pkt = NULL;
 	req_cnt = 1;
 	handle = 0;
 
@@ -2454,13 +2453,8 @@ __qla2x00_alloc_iocbs(struct qla_qpair *qpair, srb_t *sp)
 	 * layout-compatible once we return the pkt as request_t *.
 	 */
 	req->cnt -= req_cnt;
-	if (IS_QLA29XX(ha)) {
-		pkt = (request_t *)req->ring_ext_ptr;
-		memset(pkt, 0, REQUEST_ENTRY_SIZE_EXT);
-	} else {
-		pkt = req->ring_ptr;
-		memset(pkt, 0, REQUEST_ENTRY_SIZE);
-	}
+	pkt = qla_req_ring_slot(ha, req);
+	memset(pkt, 0, qla_req_entry_size(ha));
 	if (IS_QLAFX00(ha)) {
 		wrt_reg_byte((u8 __force __iomem *)&pkt->entry_count, req_cnt);
 		wrt_reg_dword((__le32 __force __iomem *)&pkt->handle, handle);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 24/88] scsi: qla2xxx: Add support for QLA29XX in memory allocation
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (22 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 23/88] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:22   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 25/88] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters Nilesh Javali
                   ` (63 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Enhance the qla2x00_mem_alloc function to include checks for
QLA29XX adapters.  This modification updates the conditions for
memory allocation and cleanup, ensuring proper handling of the
new adapter series alongside existing QLA27XX and QLA28XX checks.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_os.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 5450c40259bf..3be59179a024 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -4285,7 +4285,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 	if (!ha->srb_mempool)
 		goto fail_free_gid_list;
 
-	if (IS_P3P_TYPE(ha) || IS_QLA27XX(ha) || (ql2xsecenable && IS_QLA28XX(ha))) {
+	if (IS_P3P_TYPE(ha) || IS_QLA27XX(ha) ||
+	    (ql2xsecenable && (IS_QLA28XX(ha) || IS_QLA29XX(ha)))) {
 		/* Allocate cache for CT6 Ctx. */
 		if (!ctx_cachep) {
 			ctx_cachep = kmem_cache_create("qla2xxx_ctx",
@@ -4319,7 +4320,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 	    "init_cb=%p gid_list=%p, srb_mempool=%p s_dma_pool=%p.\n",
 	    ha->init_cb, ha->gid_list, ha->srb_mempool, ha->s_dma_pool);
 
-	if (IS_P3P_TYPE(ha) || ql2xenabledif || (IS_QLA28XX(ha) && ql2xsecenable)) {
+	if (IS_P3P_TYPE(ha) || ql2xenabledif ||
+	    ((IS_QLA28XX(ha) || IS_QLA29XX(ha)) && ql2xsecenable)) {
 		ha->dl_dma_pool = dma_pool_create(name, &ha->pdev->dev,
 			DSD_LIST_DMA_POOL_SIZE, 8, 0);
 		if (!ha->dl_dma_pool) {
@@ -4681,12 +4683,12 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 	}
 
 fail_dif_bundl_dma_pool:
-	if (IS_QLA82XX(ha) || ql2xenabledif) {
+	if (IS_QLA82XX(ha) || IS_QLA29XX(ha) || ql2xenabledif) {
 		dma_pool_destroy(ha->fcp_cmnd_dma_pool);
 		ha->fcp_cmnd_dma_pool = NULL;
 	}
 fail_dl_dma_pool:
-	if (IS_QLA82XX(ha) || ql2xenabledif) {
+	if (IS_QLA82XX(ha) || IS_QLA29XX(ha) || ql2xenabledif) {
 		dma_pool_destroy(ha->dl_dma_pool);
 		ha->dl_dma_pool = NULL;
 	}
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 25/88] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (23 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 24/88] scsi: qla2xxx: Add support for QLA29XX in memory allocation Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:31   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 26/88] scsi: qla2xxx: Update handling of status entries for 29xx series Nilesh Javali
                   ` (62 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

29xx adapters use 128-byte response queue entries
(sts_cont_entry_ext_t) instead of 64-byte (sts_cont_entry_t).  Update
all status continuation IOCB processing paths to branch on
IS_QLA29XX() and use the correct entry type and data payload size.

The affected functions are __qla_copy_purex_to_buffer(),
qla27xx_copy_multiple_pkt(), qla2x00_status_cont_entry(), and their
call sites in qla2x00_process_response_entry() and
qla24xx_process_response_queue().

Change qla2x00_status_cont_entry() to accept void * so callers no
longer need an explicit cast and the function can internally select the
right structure based on the adapter type.

Add BUILD_BUG_ON for sts_cont_entry_ext_t size (128 bytes).

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_inline.h |  16 ++++
 drivers/scsi/qla2xxx/qla_isr.c    | 117 ++++++++++++++++++------------
 drivers/scsi/qla2xxx/qla_os.c     |   1 +
 3 files changed, 86 insertions(+), 48 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index cdbc3c5abf75..8c581e37e6c3 100644
--- a/drivers/scsi/qla2xxx/qla_inline.h
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -103,6 +103,22 @@ qla_rsp_entry_size(struct qla_hw_data *ha)
 	return IS_QLA29XX(ha) ? sizeof(struct response_ext) : sizeof(response_t);
 }
 
+/**
+ * qla_sts_cont_data_size() - status-continuation IOCB data payload size.
+ * @ha: HBA pointer
+ *
+ * sts_cont_entry_t and struct sts_cont_entry_ext share the same header and
+ * data offset; only the trailing data[] size differs (60 vs 124 bytes).
+ * Returns that size so callers need not branch on the adapter type.
+ */
+static inline u32
+qla_sts_cont_data_size(struct qla_hw_data *ha)
+{
+	return IS_QLA29XX(ha) ?
+		sizeof_field(struct sts_cont_entry_ext, data) :
+		sizeof_field(sts_cont_entry_t, data);
+}
+
 static inline void
 qla2x00_poll(struct rsp_que *rsp)
 {
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 26722afa937c..f668012de3d4 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -19,7 +19,7 @@
 
 static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t);
 static void qla2x00_status_entry(scsi_qla_host_t *, struct rsp_que *, void *);
-static void qla2x00_status_cont_entry(struct rsp_que *, sts_cont_entry_t *);
+static void qla2x00_status_cont_entry(struct rsp_que *, void *, u32);
 static int qla2x00_error_entry(scsi_qla_host_t *, struct rsp_que *,
 	sts_entry_t *);
 static void qla27xx_process_purex_fpin(struct scsi_qla_host *vha,
@@ -231,8 +231,13 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
 	void **pkt, struct rsp_que **rsp, u8 *buf, u32 buf_len)
 {
 	struct purex_entry_24xx *purex = *pkt;
+	struct qla_hw_data *ha = vha->hw;
 	struct rsp_que *rsp_q = *rsp;
-	sts_cont_entry_t *new_pkt;
+	size_t payload_size = IS_QLA29XX(ha) ?
+		sizeof_field(struct purex_entry_24xx_ext, els_frame_payload) :
+		sizeof_field(struct purex_entry_24xx, els_frame_payload);
+	u8 *data;
+	u32 data_sz;
 	uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0;
 	uint16_t buffer_copy_offset = 0;
 	uint16_t entry_count_remaining;
@@ -271,10 +276,12 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
 
 	do {
 		while ((total_bytes > 0) && (entry_count_remaining > 0)) {
-			new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr;
-			*pkt = new_pkt;
+			*pkt = rsp_q->ring_ptr;
+			data = ((sts_cont_entry_t *)*pkt)->data;
+			data_sz = qla_sts_cont_data_size(ha);
 
-			if (new_pkt->entry_type != STATUS_CONT_TYPE) {
+			if (((sts_cont_entry_t *)*pkt)->entry_type !=
+			    STATUS_CONT_TYPE) {
 				ql_log(ql_log_warn, vha, 0x507a,
 				    "Unexpected IOCB type, partial data 0x%x\n",
 				    buffer_copy_offset);
@@ -282,10 +289,10 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
 			}
 
 			qla_rsp_ring_advance(rsp_q);
-			no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
-			    sizeof(new_pkt->data) : pending_bytes;
+			no_bytes = (pending_bytes > data_sz) ?
+			    data_sz : pending_bytes;
 			if ((buffer_copy_offset + no_bytes) <= total_bytes) {
-				memcpy((buf + buffer_copy_offset), new_pkt->data,
+				memcpy((buf + buffer_copy_offset), data,
 				    no_bytes);
 				buffer_copy_offset += no_bytes;
 				pending_bytes -= no_bytes;
@@ -294,11 +301,11 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
 				ql_log(ql_log_warn, vha, 0x5044,
 				    "Attempt to copy more that we got, optimizing..%x\n",
 				    buffer_copy_offset);
-				memcpy((buf + buffer_copy_offset), new_pkt->data,
+				memcpy((buf + buffer_copy_offset), data,
 				    total_bytes - buffer_copy_offset);
 			}
 
-			((response_t *)new_pkt)->signature = RESPONSE_PROCESSED;
+			((response_t *)*pkt)->signature = RESPONSE_PROCESSED;
 			/* flush signature */
 			wmb();
 		}
@@ -844,13 +851,15 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
 {
 	struct purex_entry_24xx *purex = NULL;
 	struct pt_ls4_rx_unsol *purls = NULL;
+	struct qla_hw_data *ha = vha->hw;
 	struct rsp_que *rsp_q = *rsp;
-	sts_cont_entry_t *new_pkt;
 	uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0;
 	uint16_t buffer_copy_offset = 0, payload_size = 0;
 	uint16_t entry_count, entry_count_remaining;
 	struct purex_item *item;
 	void *iocb_pkt = NULL;
+	u8 *data;
+	u32 data_sz;
 
 	if (is_purls) {
 		purls = *pkt;
@@ -906,28 +915,24 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
 				continue;
 			}
 
-			new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr;
-			*pkt = new_pkt;
+			*pkt = rsp_q->ring_ptr;
+			data = ((sts_cont_entry_t *)*pkt)->data;
+			data_sz = qla_sts_cont_data_size(ha);
 
-			if (new_pkt->entry_type != STATUS_CONT_TYPE) {
+			if (((sts_cont_entry_t *)*pkt)->entry_type !=
+			    STATUS_CONT_TYPE) {
 				ql_log(ql_log_warn, vha, 0x507a,
 				       "Unexpected IOCB type, partial data 0x%x\n",
 				       buffer_copy_offset);
 				break;
 			}
 
-			rsp_q->ring_index++;
-			if (rsp_q->ring_index == rsp_q->length) {
-				rsp_q->ring_index = 0;
-				rsp_q->ring_ptr = rsp_q->ring;
-			} else {
-				rsp_q->ring_ptr++;
-			}
-			no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
-				sizeof(new_pkt->data) : pending_bytes;
+			qla_rsp_ring_advance(rsp_q);
+			no_bytes = (pending_bytes > data_sz) ?
+				   data_sz : pending_bytes;
 			if ((buffer_copy_offset + no_bytes) <= total_bytes) {
-				memcpy(((uint8_t *)iocb_pkt + buffer_copy_offset),
-				       new_pkt->data, no_bytes);
+				memcpy(((uint8_t *)iocb_pkt +
+					buffer_copy_offset), data, no_bytes);
 				buffer_copy_offset += no_bytes;
 				pending_bytes -= no_bytes;
 				--entry_count_remaining;
@@ -935,12 +940,12 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
 				ql_log(ql_log_warn, vha, 0x5044,
 				       "Attempt to copy more that we got, optimizing..%x\n",
 				       buffer_copy_offset);
-				memcpy(((uint8_t *)iocb_pkt + buffer_copy_offset),
-				       new_pkt->data,
-				       total_bytes - buffer_copy_offset);
+				memcpy(((uint8_t *)iocb_pkt +
+					buffer_copy_offset), data,
+					total_bytes - buffer_copy_offset);
 			}
 
-			((response_t *)new_pkt)->signature = RESPONSE_PROCESSED;
+			((response_t *)*pkt)->signature = RESPONSE_PROCESSED;
 			wmb();
 		}
 
@@ -1144,8 +1149,13 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
 		      struct rsp_que **rsp)
 {
 	struct purex_entry_24xx *purex = *pkt;
+	struct qla_hw_data *ha = vha->hw;
 	struct rsp_que *rsp_q = *rsp;
-	sts_cont_entry_t *new_pkt;
+	size_t payload_size = IS_QLA29XX(ha) ?
+		sizeof_field(struct purex_entry_24xx_ext, els_frame_payload) :
+		sizeof_field(struct purex_entry_24xx, els_frame_payload);
+	u8 *data;
+	u32 data_sz;
 	uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0;
 	uint16_t buffer_copy_offset = 0;
 	uint16_t entry_count, entry_count_remaining;
@@ -1160,8 +1170,8 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
 
 	pending_bytes = total_bytes;
 	entry_count = entry_count_remaining = purex->entry_count;
-	no_bytes = (pending_bytes > sizeof(purex->els_frame_payload))  ?
-		   sizeof(purex->els_frame_payload) : pending_bytes;
+	no_bytes = (pending_bytes > payload_size) ?
+		   payload_size : pending_bytes;
 	ql_log(ql_log_info, vha, 0x509a,
 	       "FPIN ELS, frame_size 0x%x, entry count %d\n",
 	       total_bytes, entry_count);
@@ -1172,7 +1182,13 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
 
 	fpin_pkt = &item->iocb;
 
-	memcpy(fpin_pkt, &purex->els_frame_payload[0], no_bytes);
+	if (IS_QLA29XX(ha)) {
+		struct purex_entry_24xx_ext *purex_ext = *pkt;
+
+		memcpy(fpin_pkt, &purex_ext->els_frame_payload[0], no_bytes);
+	} else {
+		memcpy(fpin_pkt, &purex->els_frame_payload[0], no_bytes);
+	}
 	buffer_copy_offset += no_bytes;
 	pending_bytes -= no_bytes;
 	--entry_count_remaining;
@@ -1190,10 +1206,12 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
 				continue;
 			}
 
-			new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr;
-			*pkt = new_pkt;
+			*pkt = rsp_q->ring_ptr;
+			data = ((sts_cont_entry_t *)*pkt)->data;
+			data_sz = qla_sts_cont_data_size(ha);
 
-			if (new_pkt->entry_type != STATUS_CONT_TYPE) {
+			if (((sts_cont_entry_t *)*pkt)->entry_type !=
+			    STATUS_CONT_TYPE) {
 				ql_log(ql_log_warn, vha, 0x507a,
 				       "Unexpected IOCB type, partial data 0x%x\n",
 				       buffer_copy_offset);
@@ -1201,11 +1219,11 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
 			}
 
 			qla_rsp_ring_advance(rsp_q);
-			no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
-			    sizeof(new_pkt->data) : pending_bytes;
+			no_bytes = (pending_bytes > data_sz) ?
+			    data_sz : pending_bytes;
 			if ((buffer_copy_offset + no_bytes) <= total_bytes) {
 				memcpy(((uint8_t *)fpin_pkt +
-				    buffer_copy_offset), new_pkt->data,
+				    buffer_copy_offset), data,
 				    no_bytes);
 				buffer_copy_offset += no_bytes;
 				pending_bytes -= no_bytes;
@@ -1215,11 +1233,11 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
 				       "Attempt to copy more that we got, optimizing..%x\n",
 				       buffer_copy_offset);
 				memcpy(((uint8_t *)fpin_pkt +
-				    buffer_copy_offset), new_pkt->data,
+				    buffer_copy_offset), data,
 				    total_bytes - buffer_copy_offset);
 			}
 
-			((response_t *)new_pkt)->signature = RESPONSE_PROCESSED;
+			((response_t *)*pkt)->signature = RESPONSE_PROCESSED;
 			wmb();
 		}
 
@@ -2918,7 +2936,8 @@ static void qla2x00_process_response_entry(struct scsi_qla_host *vha,
 						sts22_entry->handle[cnt]);
 		break;
 	case STATUS_CONT_TYPE:
-		qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt);
+		qla2x00_status_cont_entry(rsp, pkt,
+		    qla_sts_cont_data_size(rsp->hw));
 		break;
 	case MBX_IOCB_TYPE:
 		qla2x00_mbx_iocb_entry(vha, rsp->req, (struct mbx_entry *)pkt);
@@ -3682,7 +3701,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
  * Extended sense data.
  */
 static void
-qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
+qla2x00_status_cont_entry(struct rsp_que *rsp, void *pkt, u32 data_sz)
 {
 	uint8_t	sense_sz = 0;
 	struct qla_hw_data *ha = rsp->hw;
@@ -3691,6 +3710,7 @@ qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
 	struct scsi_cmnd *cp;
 	uint32_t sense_len;
 	uint8_t *sense_ptr;
+	u8 *data = ((sts_cont_entry_t *)pkt)->data;
 
 	if (!sp || !GET_CMD_SENSE_LEN(sp))
 		return;
@@ -3707,15 +3727,15 @@ qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
 		return;
 	}
 
-	if (sense_len > sizeof(pkt->data))
-		sense_sz = sizeof(pkt->data);
+	if (sense_len > data_sz)
+		sense_sz = data_sz;
 	else
 		sense_sz = sense_len;
 
 	/* Move sense data. */
 	if (IS_FWI2_CAPABLE(ha))
-		host_to_fcp_swap(pkt->data, sizeof(pkt->data));
-	memcpy(sense_ptr, pkt->data, sense_sz);
+		host_to_fcp_swap(data, data_sz);
+	memcpy(sense_ptr, data, sense_sz);
 	ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x302c,
 		sense_ptr, sense_sz);
 
@@ -3997,7 +4017,8 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 			qla2x00_status_entry(vha, rsp, pkt);
 			break;
 		case STATUS_CONT_TYPE:
-			qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt);
+			qla2x00_status_cont_entry(rsp, pkt,
+			    qla_sts_cont_data_size(rsp->hw));
 			break;
 		case VP_RPT_ID_IOCB_TYPE:
 			qla24xx_report_id_acquisition(vha,
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 3be59179a024..fdb53811e0c7 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8411,6 +8411,7 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(sts21_entry_t) != 64);
 	BUILD_BUG_ON(sizeof(sts22_entry_t) != 64);
 	BUILD_BUG_ON(sizeof(sts_cont_entry_t) != 64);
+	BUILD_BUG_ON(sizeof(struct sts_cont_entry_ext) != 128);
 	BUILD_BUG_ON(sizeof(sts_entry_t) != 64);
 	BUILD_BUG_ON(sizeof(sw_info_t) != 32);
 	BUILD_BUG_ON(sizeof(target_id_t) != 2);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 26/88] scsi: qla2xxx: Update handling of status entries for 29xx series
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (24 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 25/88] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 27/88] scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling " Nilesh Javali
                   ` (61 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Modify the handling of status entries in the qla2xxx driver to
accommodate the extended structure for the 29xx series. Changes include
updating function signatures to accept a generic pointer for status
packets, and adjusting the logic to differentiate between the standard
and extended status entries. This ensures proper processing of
completion statuses and error handling for the new hardware
capabilities.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_edif.c   |  15 ++--
 drivers/scsi/qla2xxx/qla_gbl.h    |   2 +-
 drivers/scsi/qla2xxx/qla_inline.h |  45 ++++++++++++
 drivers/scsi/qla2xxx/qla_isr.c    | 111 +++++++++++++++++++-----------
 drivers/scsi/qla2xxx/qla_mbx.c    |  28 ++++----
 drivers/scsi/qla2xxx/qla_os.c     |   1 +
 6 files changed, 143 insertions(+), 59 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c
index 4416197a35b0..b05f8e0b705e 100644
--- a/drivers/scsi/qla2xxx/qla_edif.c
+++ b/drivers/scsi/qla2xxx/qla_edif.c
@@ -3501,12 +3501,15 @@ static void __chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
 }
 
 void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
-		srb_t *sp, struct sts_entry_24xx *sts24)
+		srb_t *sp, void *pkt)
 {
+	struct sts_entry_24xx *sts24 = pkt;
+	struct sts_entry_24xx_ext *stsext = pkt;
+	struct qla_hw_data *ha = vha->hw;
 	fc_port_t *fcport = sp->fcport;
-	/* sa_index used by this iocb */
 	struct scsi_cmnd *cmd = GET_CMD_SP(sp);
 	uint32_t handle;
+	uint16_t sa_index;
 
 	handle = (uint32_t)LSW(sts24->handle);
 
@@ -3514,8 +3517,12 @@ void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
 	if (cmd->sc_data_direction != DMA_FROM_DEVICE)
 		return;
 
-	return __chk_edif_rx_sa_delete_pending(vha, fcport, handle,
-	   le16_to_cpu(sts24->edif_sa_index));
+	if (IS_QLA29XX(ha))
+		sa_index = le16_to_cpu(stsext->read_sa_index);
+	else
+		sa_index = le16_to_cpu(sts24->edif_sa_index);
+
+	return __chk_edif_rx_sa_delete_pending(vha, fcport, handle, sa_index);
 }
 
 void qlt_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, fc_port_t *fcport,
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 04f4cfadc510..2b66c59e9a6d 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -136,7 +136,7 @@ void qla_edif_sadb_release(struct qla_hw_data *ha);
 int qla_edif_sadb_build_free_pool(struct qla_hw_data *ha);
 void qla_edif_sadb_release_free_pool(struct qla_hw_data *ha);
 void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
-		srb_t *sp, struct sts_entry_24xx *sts24);
+		srb_t *sp, void *pkt);
 void qlt_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, fc_port_t *fcport,
 		struct ctio7_from_24xx *ctio);
 void qla2x00_release_all_sadb(struct scsi_qla_host *vha, struct fc_port *fcport);
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index 8c581e37e6c3..cc613bc98e92 100644
--- a/drivers/scsi/qla2xxx/qla_inline.h
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -818,3 +818,48 @@ static inline bool val_is_in_range(u32 val, u32 start, u32 end)
 	else
 		return false;
 }
+
+/*
+ * Common fields extracted from FWI2 status IOCBs.  Populated once so
+ * callers avoid duplicated IS_QLA29XX() branches for every field access.
+ */
+struct qla_sts_fwi2 {
+	u8  *data;
+	u32 data_sz;
+	u16 scsi_status;
+	u16 sts_qual;
+	u32 sense_len;
+	u32 rsp_data_len;
+	u32 rsp_residual_count;
+};
+
+static inline void
+qla_sts_fwi2_extract(struct qla_hw_data *ha, void *pkt,
+		      struct qla_sts_fwi2 *sf)
+{
+	if (IS_QLA29XX(ha)) {
+		struct sts_entry_24xx_ext *s = pkt;
+
+		sf->scsi_status = le16_to_cpu(s->u2.scsi_status);
+		sf->sts_qual = le16_to_cpu(s->u2.retry_delay_timer);
+		sf->sense_len = le32_to_cpu(s->u2.sense_len);
+		sf->rsp_data_len = le32_to_cpu(s->u2.rsp_data_len_ndma);
+		sf->rsp_residual_count = le32_to_cpu(s->u2.rsp_residual_count);
+		sf->data = s->u2.data;
+		sf->data_sz = sizeof(s->u2.data);
+		host_to_fcp_swap(s->u2.data, sizeof(s->u2.data));
+		host_to_fcp_swap(s->act_dif, sizeof(s->act_dif));
+		host_to_fcp_swap(s->exp_dif, sizeof(s->exp_dif));
+	} else {
+		struct sts_entry_24xx *s = pkt;
+
+		sf->scsi_status = le16_to_cpu(s->scsi_status);
+		sf->sts_qual = le16_to_cpu(s->status_qualifier);
+		sf->sense_len = le32_to_cpu(s->sense_len);
+		sf->rsp_data_len = le32_to_cpu(s->rsp_data_len);
+		sf->rsp_residual_count = le32_to_cpu(s->rsp_residual_count);
+		sf->data = s->data;
+		sf->data_sz = sizeof(s->data);
+		host_to_fcp_swap(s->data, sizeof(s->data));
+	}
+}
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index f668012de3d4..c38771e4202c 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -2307,8 +2307,9 @@ qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
 
 static void
 qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
-    struct sts_entry_24xx *pkt, int iocb_type)
+		     void *pkt, int iocb_type)
 {
+	struct sts_entry_24xx *sts24 = pkt;
 	struct els_sts_entry_24xx *ese = (struct els_sts_entry_24xx *)pkt;
 	const char func[] = "ELS_CT_IOCB";
 	const char *type;
@@ -2331,9 +2332,9 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
 
 	type = NULL;
 
-	comp_status = fw_status[0] = le16_to_cpu(pkt->comp_status);
-	fw_status[1] = le32_to_cpu(((struct els_sts_entry_24xx *)pkt)->error_subcode_1);
-	fw_status[2] = le32_to_cpu(((struct els_sts_entry_24xx *)pkt)->error_subcode_2);
+	comp_status = fw_status[0] = le16_to_cpu(sts24->comp_status);
+	fw_status[1] = le32_to_cpu(ese->error_subcode_1);
+	fw_status[2] = le32_to_cpu(ese->error_subcode_2);
 
 	switch (sp->type) {
 	case SRB_ELS_CMD_RPT:
@@ -2669,7 +2670,9 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk)
 	srb_t *sp;
 	struct srb_iocb *iocb;
 	struct sts_entry_24xx *sts = (struct sts_entry_24xx *)tsk;
+	struct qla_hw_data *ha = vha->hw;
 	u16 comp_status;
+	struct qla_sts_fwi2 sf;
 
 	sp = qla2x00_get_sp_from_handle(vha, func, req, tsk);
 	if (!sp)
@@ -2691,18 +2694,19 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk)
 		    "Async-%s error - hdl=%x completion status(%x).\n",
 		    type, sp->handle, comp_status);
 		iocb->u.tmf.data = QLA_FUNCTION_FAILED;
-	} else if ((le16_to_cpu(sts->scsi_status) &
-	    SS_RESPONSE_INFO_LEN_VALID)) {
-		host_to_fcp_swap(sts->data, sizeof(sts->data));
-		if (le32_to_cpu(sts->rsp_data_len) < 4) {
-			ql_log(ql_log_warn, fcport->vha, 0x503b,
-			    "Async-%s error - hdl=%x not enough response(%d).\n",
-			    type, sp->handle, sts->rsp_data_len);
-		} else if (sts->data[3]) {
-			ql_log(ql_log_warn, fcport->vha, 0x503c,
-			    "Async-%s error - hdl=%x response(%x).\n",
-			    type, sp->handle, sts->data[3]);
-			iocb->u.tmf.data = QLA_FUNCTION_FAILED;
+	} else {
+		qla_sts_fwi2_extract(ha, tsk, &sf);
+		if (sf.scsi_status & SS_RESPONSE_INFO_LEN_VALID) {
+			if (sf.rsp_data_len < 4) {
+				ql_log(ql_log_warn, fcport->vha, 0x503b,
+				    "Async-%s error - hdl=%x not enough response(%d).\n",
+				    type, sp->handle, sf.rsp_data_len);
+			} else if (sf.data[3]) {
+				ql_log(ql_log_warn, fcport->vha, 0x503c,
+				    "Async-%s error - hdl=%x response(%x).\n",
+				    type, sp->handle, sf.data[3]);
+				iocb->u.tmf.data = QLA_FUNCTION_FAILED;
+			}
 		}
 	}
 
@@ -2731,7 +2735,8 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk)
 
 	if (iocb->u.tmf.data != QLA_SUCCESS)
 		ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, sp->vha, 0x5055,
-		    sts, sizeof(*sts));
+		    tsk, IS_QLA29XX(ha) ?
+		    sizeof(struct sts_entry_24xx_ext) : sizeof(*sts));
 
 	sp->done(sp, 0);
 }
@@ -2742,6 +2747,8 @@ static void qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
 	fc_port_t *fcport;
 	struct srb_iocb *iocb;
 	struct sts_entry_24xx *sts = (struct sts_entry_24xx *)tsk;
+	struct sts_entry_24xx_ext *stsext = (struct sts_entry_24xx_ext *)tsk;
+	struct qla_hw_data *ha = vha->hw;
 	uint16_t        state_flags;
 	struct nvmefc_fcp_req *fd;
 	uint16_t        ret = QLA_SUCCESS;
@@ -2793,7 +2800,10 @@ static void qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
 		uint32_t *inbuf, *outbuf;
 		uint16_t iter;
 
-		inbuf = (uint32_t *)&sts->nvme_ersp_data;
+		if (IS_QLA29XX(ha))
+			inbuf = (uint32_t *)stsext->u2.nvme_ersp_data;
+		else
+			inbuf = (uint32_t *)&sts->nvme_ersp_data;
 		outbuf = (uint32_t *)fd->rspaddr;
 		iocb->u.nvme.rsp_pyld_len = sts->nvme_rsp_pyld_len;
 		if (unlikely(le16_to_cpu(iocb->u.nvme.rsp_pyld_len) >
@@ -3038,16 +3048,26 @@ qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t par_sense_len,
  * to indicate to the kernel that the HBA detected error.
  */
 static inline int
-qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24)
+qla2x00_handle_dif_error(srb_t *sp, void *pkt)
 {
 	struct scsi_qla_host *vha = sp->vha;
+	struct qla_hw_data *ha = vha->hw;
 	struct scsi_cmnd *cmd = GET_CMD_SP(sp);
-	uint8_t		*ap = &sts24->data[12];
-	uint8_t		*ep = &sts24->data[20];
+	struct sts_entry_24xx *sts24 = pkt;
+	struct sts_entry_24xx_ext *stsext = pkt;
+	uint8_t		*ap, *ep;
 	uint32_t	e_ref_tag, a_ref_tag;
 	uint16_t	e_app_tag, a_app_tag;
 	uint16_t	e_guard, a_guard;
 
+	if (IS_QLA29XX(ha)) {
+		ap = stsext->act_dif;
+		ep = stsext->exp_dif;
+	} else {
+		ap = &sts24->data[12];
+		ep = &sts24->data[20];
+	}
+
 	/*
 	 * swab32 of the "data" field in the beginning of qla2x00_status_entry()
 	 * would make guard field appear at offset 2
@@ -3060,7 +3080,7 @@ qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24)
 	e_ref_tag = get_unaligned_le32(ep + 4);
 
 	ql_dbg(ql_dbg_io, vha, 0x3023,
-	    "iocb(s) %p Returned STATUS.\n", sts24);
+	    "iocb(s) %px Returned STATUS.\n", pkt);
 
 	ql_dbg(ql_dbg_io, vha, 0x3024,
 	    "DIF ERROR in cmd 0x%x lba 0x%llx act ref"
@@ -3192,7 +3212,9 @@ qla25xx_process_bidir_status_iocb(scsi_qla_host_t *vha, void *pkt,
 
 	if (IS_FWI2_CAPABLE(ha)) {
 		comp_status = le16_to_cpu(sts24->comp_status);
-		scsi_status = le16_to_cpu(sts24->scsi_status) & SS_MASK;
+		scsi_status = IS_QLA29XX(ha) ?
+		    le16_to_cpu(((struct sts_entry_24xx_ext *)pkt)->u2.scsi_status) & SS_MASK :
+		    le16_to_cpu(sts24->scsi_status) & SS_MASK;
 	} else {
 		comp_status = le16_to_cpu(sts->comp_status);
 		scsi_status = le16_to_cpu(sts->scsi_status) & SS_MASK;
@@ -3328,10 +3350,13 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
 	int res = 0;
 	uint16_t state_flags = 0;
 	uint16_t sts_qual = 0;
+	struct qla_sts_fwi2 sf;
 
 	if (IS_FWI2_CAPABLE(ha)) {
 		comp_status = le16_to_cpu(sts24->comp_status);
-		scsi_status = le16_to_cpu(sts24->scsi_status) & SS_MASK;
+		scsi_status = IS_QLA29XX(ha) ?
+		    le16_to_cpu(((struct sts_entry_24xx_ext *)pkt)->u2.scsi_status) & SS_MASK :
+		    le16_to_cpu(sts24->scsi_status) & SS_MASK;
 		state_flags = le16_to_cpu(sts24->state_flags);
 	} else {
 		comp_status = le16_to_cpu(sts->comp_status);
@@ -3402,7 +3427,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
 	}
 
 	/* Fast path completion. */
-	qla_chk_edif_rx_sa_delete_pending(vha, sp, sts24);
+	qla_chk_edif_rx_sa_delete_pending(vha, sp, pkt);
 	sp->qpair->cmd_completion_cnt++;
 
 	if (comp_status == CS_COMPLETE && scsi_status == 0) {
@@ -3429,20 +3454,20 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
 	sense_len = par_sense_len = rsp_info_len = resid_len =
 	    fw_resid_len = 0;
 	if (IS_FWI2_CAPABLE(ha)) {
+		qla_sts_fwi2_extract(ha, pkt, &sf);
 		if (scsi_status & SS_SENSE_LEN_VALID)
-			sense_len = le32_to_cpu(sts24->sense_len);
+			sense_len = sf.sense_len;
 		if (scsi_status & SS_RESPONSE_INFO_LEN_VALID)
-			rsp_info_len = le32_to_cpu(sts24->rsp_data_len);
+			rsp_info_len = sf.rsp_data_len;
 		if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER))
-			resid_len = le32_to_cpu(sts24->rsp_residual_count);
+			resid_len = sf.rsp_residual_count;
 		if (comp_status == CS_DATA_UNDERRUN)
 			fw_resid_len = le32_to_cpu(sts24->residual_len);
-		rsp_info = sts24->data;
-		sense_data = sts24->data;
-		host_to_fcp_swap(sts24->data, sizeof(sts24->data));
+		rsp_info = sf.data;
+		sense_data = sf.data;
+		par_sense_len = sf.data_sz;
+		sts_qual = sf.sts_qual;
 		ox_id = le16_to_cpu(sts24->ox_id);
-		par_sense_len = sizeof(sts24->data);
-		sts_qual = le16_to_cpu(sts24->status_qualifier);
 	} else {
 		if (scsi_status & SS_SENSE_LEN_VALID)
 			sense_len = le16_to_cpu(sts->req_sense_length);
@@ -3642,7 +3667,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
 		break;
 
 	case CS_DIF_ERROR:
-		logit = qla2x00_handle_dif_error(sp, sts24);
+		logit = qla2x00_handle_dif_error(sp, pkt);
 		res = cp->result;
 		break;
 
@@ -3667,7 +3692,8 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
 		    ox_id, cp->cmnd, scsi_bufflen(cp), rsp_info_len,
 		    resid_len, fw_resid_len, sp, cp);
 		ql_dump_buffer(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe0ee,
-		    pkt, sizeof(*sts24));
+		    pkt, IS_QLA29XX(ha) ?
+		    sizeof(struct sts_entry_24xx_ext) : sizeof(*sts24));
 		res = DID_ERROR << 16;
 		vha->hw_err_cnt++;
 		break;
@@ -3967,7 +3993,7 @@ static void qla_marker_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
 void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 	struct rsp_que *rsp)
 {
-	struct sts_entry_24xx *pkt;
+	void *pkt;
 	struct qla_hw_data *ha = vha->hw;
 	struct purex_entry_24xx *purex_entry;
 	struct purex_item *pure_item;
@@ -3997,13 +4023,13 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 
 	while (rsp->ring_index != rsp_in &&
 		       rsp->ring_ptr->signature != RESPONSE_PROCESSED) {
-		pkt = (struct sts_entry_24xx *)rsp->ring_ptr;
+		pkt = (void *)rsp->ring_ptr;
 		cur_ring_index = rsp->ring_index;
 
 		qla_rsp_ring_advance(rsp);
 
-		if (pkt->entry_status != 0) {
-			if (qla2x00_error_entry(vha, rsp, (sts_entry_t *) pkt))
+		if (((response_t *)pkt)->entry_status != 0) {
+			if (qla2x00_error_entry(vha, rsp, (sts_entry_t *)pkt))
 				goto process_err;
 
 			((response_t *)pkt)->signature = RESPONSE_PROCESSED;
@@ -4012,7 +4038,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 		}
 process_err:
 
-		switch (pkt->entry_type) {
+		switch (((response_t *)pkt)->entry_type) {
 		case STATUS_TYPE:
 			qla2x00_status_entry(vha, rsp, pkt);
 			break;
@@ -4063,7 +4089,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 			    rsp->req);
 			break;
 		case NOTIFY_ACK_TYPE:
-			if (pkt->handle == QLA_TGT_SKIP_HANDLE)
+			if (((response_t *)pkt)->handle == QLA_TGT_SKIP_HANDLE)
 				qlt_response_pkt_all_vps(vha, rsp,
 				    (response_t *)pkt);
 			else
@@ -4156,7 +4182,8 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 			/* Type Not Supported. */
 			ql_dbg(ql_dbg_async, vha, 0x5042,
 			       "Received unknown response pkt type 0x%x entry status=%x.\n",
-			       pkt->entry_type, pkt->entry_status);
+			       ((response_t *)pkt)->entry_type,
+			       ((response_t *)pkt)->entry_status);
 			break;
 		}
 		((response_t *)pkt)->signature = RESPONSE_PROCESSED;
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index c073dc35ef8e..f9b326c02055 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -3407,6 +3407,7 @@ struct tsk_mgmt_cmd {
 	union {
 		struct tsk_mgmt_entry tsk;
 		struct sts_entry_24xx sts;
+		struct sts_entry_24xx_ext sts_ext;
 	} p;
 };
 
@@ -3417,6 +3418,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
 	int		rval, rval2;
 	struct tsk_mgmt_cmd *tsk;
 	struct sts_entry_24xx *sts;
+	struct qla_sts_fwi2 sf;
 	dma_addr_t	tsk_dma;
 	scsi_qla_host_t *vha;
 	struct qla_hw_data *ha;
@@ -3474,18 +3476,20 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
 		    "Failed to complete IOCB -- completion status (%x).\n",
 		    le16_to_cpu(sts->comp_status));
 		rval = QLA_FUNCTION_FAILED;
-	} else if (le16_to_cpu(sts->scsi_status) &
-	    SS_RESPONSE_INFO_LEN_VALID) {
-		if (le32_to_cpu(sts->rsp_data_len) < 4) {
-			ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1097,
-			    "Ignoring inconsistent data length -- not enough "
-			    "response info (%d).\n",
-			    le32_to_cpu(sts->rsp_data_len));
-		} else if (sts->data[3]) {
-			ql_dbg(ql_dbg_mbx, vha, 0x1098,
-			    "Failed to complete IOCB -- response (%x).\n",
-			    sts->data[3]);
-			rval = QLA_FUNCTION_FAILED;
+	} else {
+		qla_sts_fwi2_extract(ha, sts, &sf);
+		if (sf.scsi_status & SS_RESPONSE_INFO_LEN_VALID) {
+			if (sf.rsp_data_len < 4) {
+				ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha,
+				    0x1097,
+				    "Ignoring inconsistent data length -- not enough response info (%d).\n",
+				    sf.rsp_data_len);
+			} else if (sf.data[3]) {
+				ql_dbg(ql_dbg_mbx, vha, 0x1098,
+				    "Failed to complete IOCB -- response (%x).\n",
+				    sf.data[3]);
+				rval = QLA_FUNCTION_FAILED;
+			}
 		}
 	}
 
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index fdb53811e0c7..4f7e5f9372ef 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8400,6 +8400,7 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(struct rdp_rsp_payload) != 336);
 	BUILD_BUG_ON(sizeof(struct sns_cmd_pkt) != 2064);
 	BUILD_BUG_ON(sizeof(struct sts_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct sts_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct tsk_mgmt_entry) != 64);
 	BUILD_BUG_ON(sizeof(struct tsk_mgmt_entry_fx00) != 64);
 	BUILD_BUG_ON(sizeof(struct verify_chip_entry_84xx) != 64);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 27/88] scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling for 29xx series
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (25 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 26/88] scsi: qla2xxx: Update handling of status entries for 29xx series Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:40   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 28/88] scsi: qla2xxx: Enhance purex_entry " Nilesh Javali
                   ` (60 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Refine the handling of I/O control blocks (IOCBs) for the 29xx series
by introducing support for the extended structure ct_entry_24xx_ext.
Update function signatures to accept a generic pointer for IOCB packets,
differentiating between standard and extended structures, and ensuring
proper initialization and processing of command and response data.
Additionally, the size check for the extended structure is added to
maintain integrity.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_gs.c   | 156 ++++++++++++++++++++++++--------
 drivers/scsi/qla2xxx/qla_iocb.c |  78 ++++++++++------
 drivers/scsi/qla2xxx/qla_os.c   |   1 +
 3 files changed, 168 insertions(+), 67 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index 514f04aa1423..7a4d2fdc095f 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -68,30 +68,62 @@ void *
 qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, struct ct_arg *arg)
 {
 	struct qla_hw_data *ha = vha->hw;
-	struct ct_entry_24xx *ct_pkt;
 
-	ct_pkt = (struct ct_entry_24xx *)arg->iocb;
-	memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
+	if (IS_QLA29XX(ha)) {
+		struct ct_entry_24xx_ext *ct_pkt;
 
-	ct_pkt->entry_type = CT_IOCB_TYPE;
-	ct_pkt->entry_count = 1;
-	ct_pkt->nport_handle = cpu_to_le16(arg->nport_handle);
-	ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
-	ct_pkt->cmd_dsd_count = cpu_to_le16(1);
-	ct_pkt->rsp_dsd_count = cpu_to_le16(1);
-	ct_pkt->rsp_byte_count = cpu_to_le32(arg->rsp_size);
-	ct_pkt->cmd_byte_count = cpu_to_le32(arg->req_size);
+		ct_pkt = (struct ct_entry_24xx_ext *)arg->iocb;
+		memset(ct_pkt, 0, sizeof(struct ct_entry_24xx_ext));
 
-	put_unaligned_le64(arg->req_dma, &ct_pkt->dsd[0].address);
-	ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
+		ct_pkt->entry_type = CT_IOCB_TYPE;
+		ct_pkt->entry_count = 1;
+		ct_pkt->nport_handle = cpu_to_le16(arg->nport_handle);
+		ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
+		ct_pkt->cmd_dsd_count = cpu_to_le16(1);
+		ct_pkt->rsp_dsd_count = cpu_to_le16(1);
+		ct_pkt->rsp_byte_count = cpu_to_le32(arg->rsp_size);
+		ct_pkt->cmd_byte_count = cpu_to_le32(arg->req_size);
 
-	put_unaligned_le64(arg->rsp_dma, &ct_pkt->dsd[1].address);
-	ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
-	ct_pkt->vp_index = vha->vp_idx;
+		put_unaligned_le64(arg->req_dma,
+				   &ct_pkt->dsd[0].address);
+		ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
 
-	vha->qla_stats.control_requests++;
+		put_unaligned_le64(arg->rsp_dma,
+				   &ct_pkt->dsd[1].address);
+		ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
+		ct_pkt->vp_index = cpu_to_le16(vha->vp_idx);
+
+		vha->qla_stats.control_requests++;
+
+		return ct_pkt;
+	} else {
+		struct ct_entry_24xx *ct_pkt;
+
+		ct_pkt = (struct ct_entry_24xx *)arg->iocb;
+		memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
+
+		ct_pkt->entry_type = CT_IOCB_TYPE;
+		ct_pkt->entry_count = 1;
+		ct_pkt->nport_handle = cpu_to_le16(arg->nport_handle);
+		ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
+		ct_pkt->cmd_dsd_count = cpu_to_le16(1);
+		ct_pkt->rsp_dsd_count = cpu_to_le16(1);
+		ct_pkt->rsp_byte_count = cpu_to_le32(arg->rsp_size);
+		ct_pkt->cmd_byte_count = cpu_to_le32(arg->req_size);
+
+		put_unaligned_le64(arg->req_dma,
+				   &ct_pkt->dsd[0].address);
+		ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
 
-	return (ct_pkt);
+		put_unaligned_le64(arg->rsp_dma,
+				   &ct_pkt->dsd[1].address);
+		ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
+		ct_pkt->vp_index = vha->vp_idx;
+
+		vha->qla_stats.control_requests++;
+
+		return ct_pkt;
+	}
 }
 
 /**
@@ -132,7 +164,10 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt,
 		    routine, ms_pkt->entry_status, vha->d_id.b.domain,
 		    vha->d_id.b.area, vha->d_id.b.al_pa);
 	} else {
-		if (IS_FWI2_CAPABLE(ha))
+		if (IS_QLA29XX(ha))
+			comp_status = le16_to_cpu(
+			    ((struct ct_entry_24xx_ext *)ms_pkt)->comp_status);
+		else if (IS_FWI2_CAPABLE(ha))
 			comp_status = le16_to_cpu(
 			    ((struct ct_entry_24xx *)ms_pkt)->comp_status);
 		else
@@ -1437,42 +1472,85 @@ void *
 qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size,
     uint32_t rsp_size)
 {
-	struct ct_entry_24xx *ct_pkt;
 	struct qla_hw_data *ha = vha->hw;
 
-	ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
-	memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
+	if (IS_QLA29XX(ha)) {
+		struct ct_entry_24xx_ext *ct_pkt;
+
+		ct_pkt = (struct ct_entry_24xx_ext *)ha->ms_iocb;
+		memset(ct_pkt, 0, sizeof(struct ct_entry_24xx_ext));
+
+		ct_pkt->entry_type = CT_IOCB_TYPE;
+		ct_pkt->entry_count = 1;
+		ct_pkt->nport_handle =
+		    cpu_to_le16(vha->mgmt_svr_loop_id);
+		ct_pkt->timeout =
+		    cpu_to_le16(ha->r_a_tov / 10 * 2);
+		ct_pkt->cmd_dsd_count = cpu_to_le16(1);
+		ct_pkt->rsp_dsd_count = cpu_to_le16(1);
+		ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size);
+		ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
+
+		put_unaligned_le64(ha->ct_sns_dma,
+				   &ct_pkt->dsd[0].address);
+		ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
+
+		put_unaligned_le64(ha->ct_sns_dma,
+				   &ct_pkt->dsd[1].address);
+		ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
+		ct_pkt->vp_index = cpu_to_le16(vha->vp_idx);
 
-	ct_pkt->entry_type = CT_IOCB_TYPE;
-	ct_pkt->entry_count = 1;
-	ct_pkt->nport_handle = cpu_to_le16(vha->mgmt_svr_loop_id);
-	ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
-	ct_pkt->cmd_dsd_count = cpu_to_le16(1);
-	ct_pkt->rsp_dsd_count = cpu_to_le16(1);
-	ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size);
-	ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
+		return ct_pkt;
+	} else {
+		struct ct_entry_24xx *ct_pkt;
+
+		ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
+		memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
+
+		ct_pkt->entry_type = CT_IOCB_TYPE;
+		ct_pkt->entry_count = 1;
+		ct_pkt->nport_handle =
+		    cpu_to_le16(vha->mgmt_svr_loop_id);
+		ct_pkt->timeout =
+		    cpu_to_le16(ha->r_a_tov / 10 * 2);
+		ct_pkt->cmd_dsd_count = cpu_to_le16(1);
+		ct_pkt->rsp_dsd_count = cpu_to_le16(1);
+		ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size);
+		ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
 
-	put_unaligned_le64(ha->ct_sns_dma, &ct_pkt->dsd[0].address);
-	ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
+		put_unaligned_le64(ha->ct_sns_dma,
+				   &ct_pkt->dsd[0].address);
+		ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
 
-	put_unaligned_le64(ha->ct_sns_dma, &ct_pkt->dsd[1].address);
-	ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
-	ct_pkt->vp_index = vha->vp_idx;
+		put_unaligned_le64(ha->ct_sns_dma,
+				   &ct_pkt->dsd[1].address);
+		ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
+		ct_pkt->vp_index = vha->vp_idx;
 
-	return ct_pkt;
+		return ct_pkt;
+	}
 }
 
 static void
 qla2x00_update_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size)
 {
 	struct qla_hw_data *ha = vha->hw;
-	ms_iocb_entry_t *ms_pkt = ha->ms_iocb;
-	struct ct_entry_24xx *ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
 
-	if (IS_FWI2_CAPABLE(ha)) {
+	if (IS_QLA29XX(ha)) {
+		struct ct_entry_24xx_ext *ct_pkt =
+		    (struct ct_entry_24xx_ext *)ha->ms_iocb;
+
+		ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
+		ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
+	} else if (IS_FWI2_CAPABLE(ha)) {
+		struct ct_entry_24xx *ct_pkt =
+		    (struct ct_entry_24xx *)ha->ms_iocb;
+
 		ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
 		ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
 	} else {
+		ms_iocb_entry_t *ms_pkt = ha->ms_iocb;
+
 		ms_pkt->req_bytecount = cpu_to_le32(req_size);
 		ms_pkt->req_dsd.length = ms_pkt->req_bytecount;
 	}
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index c0cec1f74dfc..79403d460977 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -3394,7 +3394,7 @@ qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb)
 }
 
 static void
-qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb)
+qla24xx_ct_iocb(srb_t *sp, void *pkt)
 {
 	uint16_t        avail_dsds;
 	struct dsd64	*cur_dsd;
@@ -3406,36 +3406,50 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb)
 	struct bsg_job *bsg_job = sp->u.bsg_job;
 	int entry_count = 1;
 
-	ct_iocb->entry_type = CT_IOCB_TYPE;
-        ct_iocb->entry_status = 0;
-        ct_iocb->sys_define = 0;
-        ct_iocb->handle = sp->handle;
-
-	ct_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-	ct_iocb->vp_index = sp->vha->vp_idx;
-	ct_iocb->comp_status = cpu_to_le16(0);
-
 	cmd_dsds = bsg_job->request_payload.sg_cnt;
 	rsp_dsds = bsg_job->reply_payload.sg_cnt;
 
-	ct_iocb->cmd_dsd_count = cpu_to_le16(cmd_dsds);
-        ct_iocb->timeout = 0;
-	ct_iocb->rsp_dsd_count = cpu_to_le16(rsp_dsds);
-        ct_iocb->cmd_byte_count =
-            cpu_to_le32(bsg_job->request_payload.payload_len);
+	if (IS_QLA29XX(ha)) {
+		struct ct_entry_24xx_ext *ct_iocb = pkt;
+
+		ct_iocb->entry_type = CT_IOCB_TYPE;
+		ct_iocb->entry_status = 0;
+		ct_iocb->sys_define = 0;
+		ct_iocb->handle = sp->handle;
+		ct_iocb->nport_handle =
+		    cpu_to_le16(sp->fcport->loop_id);
+		ct_iocb->vp_index = cpu_to_le16(sp->vha->vp_idx);
+		ct_iocb->comp_status = cpu_to_le16(0);
+		ct_iocb->cmd_dsd_count = cpu_to_le16(cmd_dsds);
+		ct_iocb->timeout = 0;
+		ct_iocb->rsp_dsd_count = cpu_to_le16(rsp_dsds);
+		ct_iocb->cmd_byte_count =
+		    cpu_to_le32(bsg_job->request_payload.payload_len);
+		avail_dsds = NUM_CT_DSDS;
+		cur_dsd = ct_iocb->dsd;
+	} else {
+		struct ct_entry_24xx *ct_iocb = pkt;
+
+		ct_iocb->entry_type = CT_IOCB_TYPE;
+		ct_iocb->entry_status = 0;
+		ct_iocb->sys_define = 0;
+		ct_iocb->handle = sp->handle;
+		ct_iocb->nport_handle =
+		    cpu_to_le16(sp->fcport->loop_id);
+		ct_iocb->vp_index = sp->vha->vp_idx;
+		ct_iocb->comp_status = cpu_to_le16(0);
+		ct_iocb->cmd_dsd_count = cpu_to_le16(cmd_dsds);
+		ct_iocb->timeout = 0;
+		ct_iocb->rsp_dsd_count = cpu_to_le16(rsp_dsds);
+		ct_iocb->cmd_byte_count =
+		    cpu_to_le32(bsg_job->request_payload.payload_len);
+		avail_dsds = 2;
+		cur_dsd = ct_iocb->dsd;
+	}
 
-	avail_dsds = 2;
-	cur_dsd = ct_iocb->dsd;
 	index = 0;
 
 	for_each_sg(bsg_job->request_payload.sg_list, sg, cmd_dsds, index) {
-		/*
-		 * Allocate additional continuation packets.  24xx uses the
-		 * 64-byte cont_a64_entry_t (5 DSDs); 29xx uses the 128-byte
-		 * cont_a64_entry_ext_t (NUM_CONT1_DSDS) and advances through
-		 * the ring_ext_ptr stride so the CT head IOCB isn't
-		 * overlapped.
-		 */
 		if (avail_dsds == 0) {
 			if (IS_QLA29XX(ha)) {
 				struct cont_a64_entry_ext *cont_pkt;
@@ -3484,7 +3498,11 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb)
 		append_dsd64(&cur_dsd, sg);
 		avail_dsds--;
 	}
-        ct_iocb->entry_count = entry_count;
+
+	if (IS_QLA29XX(ha))
+		((struct ct_entry_24xx_ext *)pkt)->entry_count = entry_count;
+	else
+		((struct ct_entry_24xx *)pkt)->entry_count = entry_count;
 }
 
 /*
@@ -3857,11 +3875,15 @@ qla2x00_mb_iocb(srb_t *sp, struct mbx_24xx_entry *mbx)
 }
 
 static void
-qla2x00_ctpthru_cmd_iocb(srb_t *sp, struct ct_entry_24xx *ct_pkt)
+qla2x00_ctpthru_cmd_iocb(srb_t *sp, void *pkt)
 {
-	sp->u.iocb_cmd.u.ctarg.iocb = ct_pkt;
+	sp->u.iocb_cmd.u.ctarg.iocb = pkt;
 	qla24xx_prep_ms_iocb(sp->vha, &sp->u.iocb_cmd.u.ctarg);
-	ct_pkt->handle = sp->handle;
+
+	if (IS_QLA29XX(sp->vha->hw))
+		((struct ct_entry_24xx_ext *)pkt)->handle = sp->handle;
+	else
+		((struct ct_entry_24xx *)pkt)->handle = sp->handle;
 }
 
 static void qla2x00_send_notify_ack_iocb(srb_t *sp,
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 4f7e5f9372ef..0b7c4ee73ebf 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8349,6 +8349,7 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(struct cmd_type_7_fx00) != 64);
 	BUILD_BUG_ON(sizeof(struct cmd_type_crc_2) != 64);
 	BUILD_BUG_ON(sizeof(struct ct_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct ct_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct ct_fdmi1_hba_attributes) != 2604);
 	BUILD_BUG_ON(sizeof(struct ct_fdmi2_hba_attributes) != 4424);
 	BUILD_BUG_ON(sizeof(struct ct_fdmi2_port_attributes) != 4164);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 28/88] scsi: qla2xxx: Enhance purex_entry handling for 29xx series
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (26 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 27/88] scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling " Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:35   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 29/88] scsi: qla2xxx: Update handling of ELS IOCBs " Nilesh Javali
                   ` (59 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Update function signatures and internal logic across qla_edif.c,
qla_isr.c, and qla_os.c to accept a generic pointer for packet data and
differentiate between standard purex_entry_24xx and the extended
purex_entry_24xx_ext structures based on IS_QLA29XX().

This ensures proper initialization and processing of command and response
data for both 64-byte and 128-byte PUREX IOCBs across all ELS paths
including auth_els, RDP, copy_std_pkt, copy_multiple_pkt, consume_iocb,
and copy_purex_to_buffer.

Where the two layouts overlap at byte-identical offsets (entry_count,
frame_size, nport_handle, rx_xchg_addr, ox_id, status_flags,
trunc_frame_size, s_id, d_id, els_frame_payload base, and
response_t::signature), use a single struct purex_entry_24xx * view to
avoid duplicating read paths.  Branch only where field encoding differs:
vp_idx (u8 at offset 6 in 24xx vs __le16 at offsets 6-7 in 29xx) and
els_frame_payload[] array length (20 vs 84 bytes, handled via a
sizeof_field()-based payload_size local).

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_def.h  |  3 +-
 drivers/scsi/qla2xxx/qla_edif.c | 65 ++++++++++++++++-------
 drivers/scsi/qla2xxx/qla_isr.c  | 94 ++++++++++++++++++++++++++-------
 drivers/scsi/qla2xxx/qla_os.c   | 24 +++++++--
 4 files changed, 142 insertions(+), 44 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 0bbe2bae7101..3e2f1d8ba904 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -5000,6 +5000,7 @@ struct active_regions {
 #define QLA_SET_DATA_RATE_LR	2 /* Set speed and initiate LR */
 
 #define QLA_DEFAULT_PAYLOAD_SIZE	64
+#define QLA_MAX_IOCB_SIZE		128
 /*
  * This item might be allocated with a size > sizeof(struct purex_item).
  * The "size" variable gives the size of the payload (which
@@ -5014,7 +5015,7 @@ struct purex_item {
 	atomic_t in_use;
 	uint16_t size;
 	struct {
-		uint8_t iocb[64];
+		u8 iocb[QLA_MAX_IOCB_SIZE];
 	} iocb;
 };
 
diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c
index b05f8e0b705e..ade1d8178573 100644
--- a/drivers/scsi/qla2xxx/qla_edif.c
+++ b/drivers/scsi/qla2xxx/qla_edif.c
@@ -2534,7 +2534,7 @@ qla24xx_sa_replace_iocb(srb_t *sp, struct sa_update_28xx *sa_update_iocb)
 
 void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp)
 {
-	struct purex_entry_24xx *p = *pkt;
+	struct qla_hw_data *ha = vha->hw;
 	struct enode		*ptr;
 	int		sid;
 	u16 totlen;
@@ -2544,26 +2544,55 @@ void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp)
 	struct fc_port *fcport;
 	struct qla_els_pt_arg a;
 	be_id_t beid;
+	__le16 nport_handle;
+	__le32 rx_xchg_addr;
+	__le16 ox_id;
+	__le16 frame_size, status_flags, trunc_frame_size;
+	uint8_t s_id[3], d_id[3];
+	uint8_t vp_idx;
+	struct purex_entry_24xx *p = *pkt;
 
 	memset(&a, 0, sizeof(a));
 
+	/*
+	 * purex_entry_24xx_ext (29xx) overlays purex_entry_24xx for every
+	 * field touched here -- nport_handle, rx_xchg_addr, ox_id, frame_size,
+	 * status_flags, trunc_frame_size, s_id[3], d_id[3] -- with only
+	 * vp_idx differing in width (u8 at offset 6 vs __le16 at offsets 6-7,
+	 * with reserved2 at offset 7 in the 24xx layout). So all reads but
+	 * vp_idx go through a single struct purex_entry_24xx * view.
+	 */
+	nport_handle = p->nport_handle;
+	rx_xchg_addr = p->rx_xchg_addr;
+	ox_id = p->ox_id;
+	frame_size = p->frame_size;
+	status_flags = p->status_flags;
+	trunc_frame_size = p->trunc_frame_size;
+	memcpy(s_id, p->s_id, sizeof(s_id));
+	memcpy(d_id, p->d_id, sizeof(d_id));
+	if (IS_QLA29XX(ha))
+		vp_idx = le16_to_cpu(((struct purex_entry_24xx_ext *)
+				      *pkt)->vp_idx);
+	else
+		vp_idx = p->vp_idx;
+
 	a.els_opcode = ELS_AUTH_ELS;
-	a.nport_handle = p->nport_handle;
-	a.rx_xchg_address = p->rx_xchg_addr;
-	a.did.b.domain = p->s_id[2];
-	a.did.b.area   = p->s_id[1];
-	a.did.b.al_pa  = p->s_id[0];
+	a.nport_handle = nport_handle;
+	a.rx_xchg_address = rx_xchg_addr;
+	a.did.b.domain = s_id[2];
+	a.did.b.area   = s_id[1];
+	a.did.b.al_pa  = s_id[0];
 	a.tx_byte_count = a.tx_len = sizeof(struct fc_els_ls_rjt);
-	a.tx_addr = vha->hw->elsrej.cdma;
+	a.tx_addr = ha->elsrej.cdma;
 	a.vp_idx = vha->vp_idx;
 	a.control_flags = EPD_ELS_RJT;
-	a.ox_id = le16_to_cpu(p->ox_id);
+	a.ox_id = le16_to_cpu(ox_id);
 
-	sid = p->s_id[0] | (p->s_id[1] << 8) | (p->s_id[2] << 16);
+	sid = s_id[0] | (s_id[1] << 8) | (s_id[2] << 16);
 
-	totlen = (le16_to_cpu(p->frame_size) & 0x0fff) - PURX_ELS_HEADER_SIZE;
-	if (le16_to_cpu(p->status_flags) & 0x8000) {
-		totlen = le16_to_cpu(p->trunc_frame_size);
+	totlen = (le16_to_cpu(frame_size) & 0x0fff) - PURX_ELS_HEADER_SIZE;
+	if (le16_to_cpu(status_flags) & 0x8000) {
+		totlen = le16_to_cpu(trunc_frame_size);
 		qla_els_reject_iocb(vha, (*rsp)->qpair, &a);
 		__qla_consume_iocb(vha, pkt, rsp);
 		return;
@@ -2600,12 +2629,12 @@ void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp)
 	purex = &ptr->u.purexinfo;
 	purex->pur_info.pur_sid = a.did;
 	purex->pur_info.pur_bytes_rcvd = totlen;
-	purex->pur_info.pur_rx_xchg_address = le32_to_cpu(p->rx_xchg_addr);
-	purex->pur_info.pur_nphdl = le16_to_cpu(p->nport_handle);
-	purex->pur_info.pur_did.b.domain =  p->d_id[2];
-	purex->pur_info.pur_did.b.area =  p->d_id[1];
-	purex->pur_info.pur_did.b.al_pa =  p->d_id[0];
-	purex->pur_info.vp_idx = p->vp_idx;
+	purex->pur_info.pur_rx_xchg_address = le32_to_cpu(rx_xchg_addr);
+	purex->pur_info.pur_nphdl = le16_to_cpu(nport_handle);
+	purex->pur_info.pur_did.b.domain =  d_id[2];
+	purex->pur_info.pur_did.b.area =  d_id[1];
+	purex->pur_info.pur_did.b.al_pa =  d_id[0];
+	purex->pur_info.vp_idx = vp_idx;
 
 	a.sid = purex->pur_info.pur_did;
 
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index c38771e4202c..c4e2e62f924f 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -202,6 +202,11 @@ void __qla_consume_iocb(struct scsi_qla_host *vha,
 	struct rsp_que *rsp_q = *rsp;
 	response_t *new_pkt;
 	uint16_t entry_count_remaining;
+	/*
+	 * entry_count is u8 at offset 1 in both purex_entry_24xx and
+	 * purex_entry_24xx_ext, so the 24xx view is layout-compatible with
+	 * either stride.
+	 */
 	struct purex_entry_24xx *purex = *pkt;
 
 	entry_count_remaining = purex->entry_count;
@@ -230,6 +235,14 @@ void __qla_consume_iocb(struct scsi_qla_host *vha,
 int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
 	void **pkt, struct rsp_que **rsp, u8 *buf, u32 buf_len)
 {
+	/*
+	 * purex_entry_24xx_ext overlays purex_entry_24xx for entry_count
+	 * (offset 1), frame_size (offset 12) and els_frame_payload (offset
+	 * 44, base address only -- the array size grows from 20 to 84
+	 * bytes).  Header fields are read through the 24xx view; the
+	 * initial payload memcpy uses a purex_entry_24xx_ext pointer on
+	 * 29xx so that FORTIFY_SOURCE sees the correct 84-byte source.
+	 */
 	struct purex_entry_24xx *purex = *pkt;
 	struct qla_hw_data *ha = vha->hw;
 	struct rsp_que *rsp_q = *rsp;
@@ -244,8 +257,8 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
 	u16 tpad;
 
 	entry_count_remaining = purex->entry_count;
-	total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF)
-		- PURX_ELS_HEADER_SIZE;
+	total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF) -
+		PURX_ELS_HEADER_SIZE;
 
 	/*
 	 * end of payload may not end in 4bytes boundary.  Need to
@@ -262,14 +275,24 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
 	}
 
 	pending_bytes = total_bytes = tpad;
-	no_bytes = (pending_bytes > sizeof(purex->els_frame_payload))  ?
-	    sizeof(purex->els_frame_payload) : pending_bytes;
+	no_bytes = (pending_bytes > payload_size) ?
+		payload_size : pending_bytes;
+	if (IS_QLA29XX(ha)) {
+		struct purex_entry_24xx_ext *purex_ext = *pkt;
 
-	memcpy(buf, &purex->els_frame_payload[0], no_bytes);
+		memcpy(buf, &purex_ext->els_frame_payload[0], no_bytes);
+	} else {
+		memcpy(buf, &purex->els_frame_payload[0], no_bytes);
+	}
 	buffer_copy_offset += no_bytes;
 	pending_bytes -= no_bytes;
 	--entry_count_remaining;
 
+	/*
+	 * response_t::signature and struct response_ext::signature are both u32
+	 * at offset 60 (handle:4 + data[52]:60), so the 24xx view writes
+	 * the right slot regardless of stride.
+	 */
 	((response_t *)purex)->signature = RESPONSE_PROCESSED;
 	/* flush signature */
 	wmb();
@@ -849,6 +872,7 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
 			  struct rsp_que **rsp, bool is_purls,
 			  bool byte_order)
 {
+	struct purex_entry_24xx_ext *purex_ext = NULL;
 	struct purex_entry_24xx *purex = NULL;
 	struct pt_ls4_rx_unsol *purls = NULL;
 	struct qla_hw_data *ha = vha->hw;
@@ -867,6 +891,13 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
 			      PURX_ELS_HEADER_SIZE;
 		entry_count = entry_count_remaining = purls->entry_count;
 		payload_size = sizeof(purls->payload);
+	} else if (IS_QLA29XX(ha)) {
+		purex_ext = *pkt;
+		total_bytes = (le16_to_cpu(purex_ext->frame_size) & 0x0FFF) -
+			      PURX_ELS_HEADER_SIZE;
+		entry_count = entry_count_remaining =
+		    purex_ext->entry_count;
+		payload_size = sizeof(purex_ext->els_frame_payload);
 	} else {
 		purex = *pkt;
 		total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF) -
@@ -875,8 +906,8 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
 		payload_size = sizeof(purex->els_frame_payload);
 	}
 
-	if (total_bytes > sizeof(item->iocb.iocb))
-		total_bytes = sizeof(item->iocb.iocb);
+	if (total_bytes > QLA_MAX_IOCB_SIZE)
+		total_bytes = QLA_MAX_IOCB_SIZE;
 
 	pending_bytes = total_bytes;
 	no_bytes = (pending_bytes > payload_size) ? payload_size :
@@ -893,6 +924,8 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
 
 	if (is_purls)
 		memcpy(iocb_pkt, &purls->payload[0], no_bytes);
+	else if (IS_QLA29XX(ha))
+		memcpy(iocb_pkt, &purex_ext->els_frame_payload[0], no_bytes);
 	else
 		memcpy(iocb_pkt, &purex->els_frame_payload[0], no_bytes);
 	buffer_copy_offset += no_bytes;
@@ -901,6 +934,8 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
 
 	if (is_purls)
 		((response_t *)purls)->signature = RESPONSE_PROCESSED;
+	else if (IS_QLA29XX(ha))
+		((struct response_ext *)purex_ext)->signature = RESPONSE_PROCESSED;
 	else
 		((response_t *)purex)->signature = RESPONSE_PROCESSED;
 	wmb();
@@ -1075,9 +1110,9 @@ qla24xx_alloc_purex_item(scsi_qla_host_t *vha, uint16_t size)
 	struct purex_item *item = NULL;
 	uint8_t item_hdr_size = sizeof(*item);
 
-	if (size > QLA_DEFAULT_PAYLOAD_SIZE) {
+	if (size > QLA_MAX_IOCB_SIZE) {
 		item = kzalloc(item_hdr_size +
-		    (size - QLA_DEFAULT_PAYLOAD_SIZE), GFP_ATOMIC);
+		    (size - QLA_MAX_IOCB_SIZE), GFP_ATOMIC);
 	} else {
 		if (atomic_inc_return(&vha->default_item.in_use) == 1) {
 			item = &vha->default_item;
@@ -1126,14 +1161,20 @@ qla24xx_queue_purex_item(scsi_qla_host_t *vha, struct purex_item *pkt,
 static struct purex_item
 *qla24xx_copy_std_pkt(struct scsi_qla_host *vha, void *pkt)
 {
+	struct qla_hw_data *ha = vha->hw;
 	struct purex_item *item;
+	u16 copy_sz;
 
-	item = qla24xx_alloc_purex_item(vha,
-					QLA_DEFAULT_PAYLOAD_SIZE);
+	if (IS_QLA29XX(ha))
+		copy_sz = sizeof(struct purex_entry_24xx_ext);
+	else
+		copy_sz = QLA_DEFAULT_PAYLOAD_SIZE;
+
+	item = qla24xx_alloc_purex_item(vha, copy_sz);
 	if (!item)
 		return item;
 
-	memcpy(&item->iocb, pkt, sizeof(item->iocb));
+	memcpy(&item->iocb, pkt, copy_sz);
 	return item;
 }
 
@@ -1165,8 +1206,8 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
 	total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF)
 	    - PURX_ELS_HEADER_SIZE;
 
-	if (total_bytes > sizeof(item->iocb.iocb))
-		total_bytes = sizeof(item->iocb.iocb);
+	if (total_bytes > QLA_MAX_IOCB_SIZE)
+		total_bytes = QLA_MAX_IOCB_SIZE;
 
 	pending_bytes = total_bytes;
 	entry_count = entry_count_remaining = purex->entry_count;
@@ -3995,6 +4036,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 {
 	void *pkt;
 	struct qla_hw_data *ha = vha->hw;
+	struct purex_entry_24xx_ext *purex_entry_ext;
 	struct purex_entry_24xx *purex_entry;
 	struct purex_item *pure_item;
 	struct pt_ls4_rx_unsol *p;
@@ -4112,8 +4154,16 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 			    (struct vp_ctrl_entry_24xx *)pkt);
 			break;
 		case PUREX_IOCB_TYPE:
-			purex_entry = (void *)pkt;
-			switch (purex_entry->els_frame_payload[3]) {
+			if (IS_QLA29XX(ha)) {
+				purex_entry_ext = (void *)pkt;
+				purex_entry = NULL;
+			} else {
+				purex_entry = (void *)pkt;
+				purex_entry_ext = NULL;
+			}
+			switch (IS_QLA29XX(ha) ?
+			    purex_entry_ext->els_frame_payload[3] :
+			    purex_entry->els_frame_payload[3]) {
 			case ELS_RDP:
 				pure_item = qla24xx_copy_std_pkt(vha, pkt);
 				if (!pure_item)
@@ -4148,16 +4198,20 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 					qla_rsp_ring_rewind_to(rsp,
 					    (response_t *)pkt, cur_ring_index);
 
-					ql_dbg(ql_dbg_init, vha, 0x5091,
-					    "Defer processing ELS opcode %#x...\n",
-					    purex_entry->els_frame_payload[3]);
+				ql_dbg(ql_dbg_init, vha, 0x5091,
+				    "Defer processing ELS opcode %#x...\n",
+				    IS_QLA29XX(ha) ?
+				    purex_entry_ext->els_frame_payload[3] :
+				    purex_entry->els_frame_payload[3]);
 					return;
 				}
 				qla24xx_auth_els(vha, (void **)&pkt, &rsp);
 				break;
 			default:
 				ql_log(ql_log_warn, vha, 0x509c,
-				       "Discarding ELS Request opcode 0x%x\n",
+				       "Discarding ELS Request opcode 0x%x...\n",
+				       IS_QLA29XX(ha) ?
+				       purex_entry_ext->els_frame_payload[3] :
 				       purex_entry->els_frame_payload[3]);
 			}
 			break;
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 0b7c4ee73ebf..c327d053f8ee 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -6138,13 +6138,15 @@ qla83xx_idc_lock(scsi_qla_host_t *base_vha, uint16_t requester_id)
 }
 
 static bool
-qla25xx_rdp_rsp_reduce_size(struct scsi_qla_host *vha,
-	struct purex_entry_24xx *purex)
+qla25xx_rdp_rsp_reduce_size(struct scsi_qla_host *vha, void *pkt)
 {
+	struct purex_entry_24xx *purex = pkt;
 	char fwstr[16];
-	u32 sid = purex->s_id[2] << 16 | purex->s_id[1] << 8 | purex->s_id[0];
+	u32 sid;
 	struct port_database_24xx *pdb;
 
+	sid = purex->s_id[2] << 16 | purex->s_id[1] << 8 | purex->s_id[0];
+
 	/* Domain Controller is always logged-out. */
 	/* if RDP request is not from Domain Controller: */
 	if (sid != 0xfffc01)
@@ -6209,15 +6211,26 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
 	uint8_t *sfp = NULL;
 	uint16_t sfp_flags = 0;
 	uint rsp_payload_length = sizeof(*rsp_payload);
+	uint8_t vp_idx;
+	size_t purex_sz;
 	int rval;
 
 	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0180,
 	    "%s: Enter\n", __func__);
 
+	if (IS_QLA29XX(ha)) {
+		vp_idx = le16_to_cpu(
+		    ((struct purex_entry_24xx_ext *)purex)->vp_idx);
+		purex_sz = sizeof(struct purex_entry_24xx_ext);
+	} else {
+		vp_idx = purex->vp_idx;
+		purex_sz = sizeof(*purex);
+	}
+
 	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0181,
 	    "-------- ELS REQ -------\n");
 	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0182,
-	    purex, sizeof(*purex));
+	    purex, purex_sz);
 
 	if (qla25xx_rdp_rsp_reduce_size(vha, purex)) {
 		rsp_payload_length =
@@ -6257,7 +6270,7 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
 	rsp_els->handle = 0;
 	rsp_els->nport_handle = purex->nport_handle;
 	rsp_els->tx_dsd_count = cpu_to_le16(1);
-	rsp_els->vp_index = purex->vp_idx;
+	rsp_els->vp_index = vp_idx;
 	rsp_els->sof_type = EST_SOFI3;
 	rsp_els->rx_xchg_address = purex->rx_xchg_addr;
 	rsp_els->rx_dsd_count = 0;
@@ -8378,6 +8391,7 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(struct pt_ls4_request) != 64);
 	BUILD_BUG_ON(sizeof(struct pt_ls4_rx_unsol) != 64);
 	BUILD_BUG_ON(sizeof(struct purex_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct purex_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct qla2100_fw_dump) != 123634);
 	BUILD_BUG_ON(sizeof(struct qla2300_fw_dump) != 136100);
 	BUILD_BUG_ON(sizeof(struct qla24xx_fw_dump) != 37976);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 29/88] scsi: qla2xxx: Update handling of ELS IOCBs for 29xx series
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (27 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 28/88] scsi: qla2xxx: Enhance purex_entry " Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:35   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 30/88] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx Nilesh Javali
                   ` (58 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Update ELS IOCB handling to support the extended 128-byte
els_entry_24xx_ext structure used by 29xx series adapters.

Change the signatures of qla24xx_els_logo_iocb(), qla_els_pt_iocb(),
and qla24xx_els_iocb() to accept a generic void pointer, enabling
differentiation between standard and extended ELS structures at
runtime.

Introduce a static inline helper qla_els_set_vp_sof() in qla_inline.h
that centralises the 24xx-vs-29xx vp_index/sof_type encoding: the 24xx
layout uses separate u8 vp_index + u8 sof_type (EST_SOFI3), while
29xx uses a __le16 with bitfields (vp_index:9 / sof_type:4 /
ELS_EXT_EST_SOFI3).  All ELS issue paths now call this helper instead
of open-coding the branch, including the RDP response path in qla_os.c.

In qla2x00_start_sp(), collapse the IS_QLA29XX() branch for the
handle assignment in SRB_ELS_CMD_HST_NOLOGIN: els_entry_24xx::handle
and els_entry_24xx_ext::handle are both u32 at offset 4, so a single
24xx-view write is layout-compatible with both strides.

DMA allocations in qla24xx_process_abts() and
qla24xx_process_purex_rdp() are updated to use the correct size for
the adapter type.  A BUILD_BUG_ON is added to verify the extended
structure size.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_gbl.h    |  2 +-
 drivers/scsi/qla2xxx/qla_inline.h | 28 ++++++++++++++++++++++++++++
 drivers/scsi/qla2xxx/qla_iocb.c   | 28 ++++++++++++++++++----------
 drivers/scsi/qla2xxx/qla_isr.c    | 26 ++++++++++++++++++--------
 drivers/scsi/qla2xxx/qla_os.c     | 31 ++++++++++++++++++++-----------
 5 files changed, 85 insertions(+), 30 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 2b66c59e9a6d..4ac3cb08fcc9 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -295,7 +295,7 @@ void qla_adjust_buf(struct scsi_qla_host *);
  * Global Function Prototypes in qla_iocb.c source file.
  */
 void qla_els_pt_iocb(struct scsi_qla_host *vha,
-	struct els_entry_24xx *pkt, struct qla_els_pt_arg *a);
+	void *pkt, struct qla_els_pt_arg *a);
 cont_a64_entry_t *qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha,
 		struct qla_hw_data *ha, struct req_que *que);
 struct cont_a64_entry_ext *qla2900_prep_cont_type1_iocb(scsi_qla_host_t *vha,
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index cc613bc98e92..695a5b7fc794 100644
--- a/drivers/scsi/qla2xxx/qla_inline.h
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -863,3 +863,31 @@ qla_sts_fwi2_extract(struct qla_hw_data *ha, void *pkt,
 		host_to_fcp_swap(s->data, sizeof(s->data));
 	}
 }
+
+/*
+ * qla_els_set_vp_sof() - write the vp_index / sof_type pair into an ELS
+ *    pass-through IOCB (els_entry_24xx{,_ext}).
+ *
+ * Both layouts have the same 16-bit slot at offset 14, but it is encoded
+ * differently:
+ *   - 24xx: separate u8 vp_index + u8 sof_type with EST_SOFI3 (1 << 4)
+ *   - 29xx: __le16 with bitfields { vp_index:9, reserved_1_sof:3,
+ *           sof_type:4 } and ELS_EXT_EST_SOFI3
+ * so this is the single point in the driver that knows about that
+ * encoding split.
+ */
+static inline void
+qla_els_set_vp_sof(struct scsi_qla_host *vha, void *pkt, u16 vp_idx)
+{
+	if (IS_QLA29XX(vha->hw)) {
+		struct els_entry_24xx_ext *ext = pkt;
+
+		ext->vp_index = vp_idx;
+		ext->sof_type = ELS_EXT_EST_SOFI3;
+	} else {
+		struct els_entry_24xx *e = pkt;
+
+		e->vp_index = vp_idx;
+		e->sof_type = EST_SOFI3;
+	}
+}
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 79403d460977..416fa78f6061 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -2901,8 +2901,9 @@ qla24xx_els_dcmd_iocb(scsi_qla_host_t *vha, int els_opcode,
 }
 
 static void
-qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
+qla24xx_els_logo_iocb(srb_t *sp, void *pkt)
 {
+	struct els_entry_24xx *els_iocb = pkt;
 	scsi_qla_host_t *vha = sp->vha;
 	struct srb_iocb *elsio = &sp->u.iocb_cmd;
 
@@ -2913,11 +2914,11 @@ qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
 	els_iocb->handle = sp->handle;
 	els_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
 	els_iocb->tx_dsd_count = cpu_to_le16(1);
-	els_iocb->vp_index = vha->vp_idx;
-	els_iocb->sof_type = EST_SOFI3;
 	els_iocb->rx_dsd_count = 0;
 	els_iocb->opcode = elsio->u.els_logo.els_cmd;
 
+	qla_els_set_vp_sof(vha, pkt, vha->vp_idx);
+
 	els_iocb->d_id[0] = sp->fcport->d_id.b.al_pa;
 	els_iocb->d_id[1] = sp->fcport->d_id.b.area;
 	els_iocb->d_id[2] = sp->fcport->d_id.b.domain;
@@ -3248,9 +3249,10 @@ qla24xx_els_dcmd2_iocb(scsi_qla_host_t *vha, int els_opcode,
 
 /* it is assume qpair lock is held */
 void qla_els_pt_iocb(struct scsi_qla_host *vha,
-	struct els_entry_24xx *els_iocb,
-	struct qla_els_pt_arg *a)
+	void *pkt, struct qla_els_pt_arg *a)
 {
+	struct els_entry_24xx *els_iocb = pkt;
+
 	els_iocb->entry_type = ELS_IOCB_TYPE;
 	els_iocb->entry_count = 1;
 	els_iocb->sys_define = 0;
@@ -3259,11 +3261,11 @@ void qla_els_pt_iocb(struct scsi_qla_host *vha,
 	els_iocb->nport_handle = a->nport_handle;
 	els_iocb->rx_xchg_address = a->rx_xchg_address;
 	els_iocb->tx_dsd_count = cpu_to_le16(1);
-	els_iocb->vp_index = a->vp_idx;
-	els_iocb->sof_type = EST_SOFI3;
 	els_iocb->rx_dsd_count = cpu_to_le16(0);
 	els_iocb->opcode = a->els_opcode;
 
+	qla_els_set_vp_sof(vha, pkt, a->vp_idx);
+
 	els_iocb->d_id[0] = a->did.b.al_pa;
 	els_iocb->d_id[1] = a->did.b.area;
 	els_iocb->d_id[2] = a->did.b.domain;
@@ -3284,8 +3286,9 @@ void qla_els_pt_iocb(struct scsi_qla_host *vha,
 }
 
 static void
-qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
+qla24xx_els_iocb(srb_t *sp, void *pkt)
 {
+	struct els_entry_24xx *els_iocb = pkt;
 	struct bsg_job *bsg_job = sp->u.bsg_job;
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 
@@ -3296,8 +3299,8 @@ qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
         els_iocb->handle = sp->handle;
 	els_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
 	els_iocb->tx_dsd_count = cpu_to_le16(bsg_job->request_payload.sg_cnt);
-	els_iocb->vp_index = sp->vha->vp_idx;
-        els_iocb->sof_type = EST_SOFI3;
+
+	qla_els_set_vp_sof(sp->vha, pkt, sp->vha->vp_idx);
 	els_iocb->rx_dsd_count = cpu_to_le16(bsg_job->reply_payload.sg_cnt);
 
 	els_iocb->opcode =
@@ -4129,6 +4132,11 @@ qla2x00_start_sp(srb_t *sp)
 		break;
 	case SRB_ELS_CMD_HST_NOLOGIN:
 		qla_els_pt_iocb(sp->vha, pkt,  &sp->u.bsg_cmd.u.els_arg);
+		/*
+		 * els_entry_24xx::handle and els_entry_24xx_ext::handle are
+		 * both u32 at offset 4, so a 24xx-view write is layout-
+		 * compatible with both strides.
+		 */
 		((struct els_entry_24xx *)pkt)->handle = sp->handle;
 		break;
 	case SRB_CT_CMD:
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index c4e2e62f924f..e2653620e80b 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -83,6 +83,8 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
 	dma_addr_t dma;
 	uint32_t fctl;
 	int rval;
+	void *rsp_pkt;
+	size_t rsp_sz;
 
 	ql_dbg(ql_dbg_init, vha, 0x0286, "%s: entered.\n", __func__);
 
@@ -95,15 +97,22 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
 	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0287,
 	    (uint8_t *)abts, sizeof(*abts));
 
-	rsp_els = dma_alloc_coherent(&ha->pdev->dev, sizeof(*rsp_els), &dma,
+	if (IS_QLA29XX(ha))
+		rsp_sz = sizeof(struct els_entry_24xx_ext);
+	else
+		rsp_sz = sizeof(struct els_entry_24xx);
+
+	rsp_pkt = dma_alloc_coherent(&ha->pdev->dev, rsp_sz, &dma,
 	    GFP_KERNEL);
-	if (!rsp_els) {
+	if (!rsp_pkt) {
 		ql_log(ql_log_warn, vha, 0x0287,
 		    "Failed allocate dma buffer ABTS/ELS RSP.\n");
 		return;
 	}
+	rsp_els = rsp_pkt;
 
 	/* terminate exchange */
+	memset(rsp_pkt, 0, rsp_sz);
 	rsp_els->entry_type = ELS_IOCB_TYPE;
 	rsp_els->entry_count = 1;
 	rsp_els->nport_handle = cpu_to_le16(~0);
@@ -115,8 +124,8 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
 	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0283,
 	    "-------- ELS RSP -------\n");
 	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0283,
-	    (uint8_t *)rsp_els, sizeof(*rsp_els));
-	rval = qla2x00_issue_iocb(vha, rsp_els, dma, 0);
+	    (uint8_t *)rsp_pkt, rsp_sz);
+	rval = qla2x00_issue_iocb(vha, rsp_pkt, dma, 0);
 	if (rval) {
 		ql_log(ql_log_warn, vha, 0x0288,
 		    "%s: iocb failed to execute -> %x\n", __func__, rval);
@@ -131,7 +140,7 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
 	}
 
 	/* send ABTS response */
-	abts_rsp = (void *)rsp_els;
+	abts_rsp = rsp_pkt;
 	memset(abts_rsp, 0, sizeof(*abts_rsp));
 	abts_rsp->entry_type = ABTS_RSP_TYPE;
 	abts_rsp->entry_count = 1;
@@ -182,7 +191,7 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
 		    "%s: done.\n", __func__);
 	}
 
-	dma_free_coherent(&ha->pdev->dev, sizeof(*rsp_els), rsp_els, dma);
+	dma_free_coherent(&ha->pdev->dev, rsp_sz, rsp_pkt, dma);
 }
 
 /**
@@ -2385,7 +2394,8 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
 	case SRB_ELS_CMD_HST_NOLOGIN:
 		type = "els";
 		{
-			struct els_entry_24xx *els = (void *)pkt;
+			__le16 ctl_flags =
+				((struct els_entry_24xx *)pkt)->control_flags;
 			struct qla_bsg_auth_els_request *p =
 				(struct qla_bsg_auth_els_request *)bsg_job->request;
 
@@ -2395,7 +2405,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
 			     e->d_id[2], e->d_id[1], e->d_id[0],
 			     comp_status, p->e.extra_rx_xchg_address, bsg_job);
 
-			if (!(le16_to_cpu(els->control_flags) & ECF_PAYLOAD_DESCR_MASK)) {
+			if (!(le16_to_cpu(ctl_flags) & ECF_PAYLOAD_DESCR_MASK)) {
 				if (sp->remap.remapped) {
 					n = sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
 						bsg_job->reply_payload.sg_cnt,
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index c327d053f8ee..74bf203f8d9f 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -6211,8 +6211,10 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
 	uint8_t *sfp = NULL;
 	uint16_t sfp_flags = 0;
 	uint rsp_payload_length = sizeof(*rsp_payload);
-	uint8_t vp_idx;
+	u16 vp_idx;
 	size_t purex_sz;
+	size_t rsp_els_sz;
+	void *rsp_els_pkt = NULL;
 	int rval;
 
 	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0180,
@@ -6240,13 +6242,19 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
 		    rsp_payload_length);
 	}
 
-	rsp_els = dma_alloc_coherent(&ha->pdev->dev, sizeof(*rsp_els),
+	if (IS_QLA29XX(ha))
+		rsp_els_sz = sizeof(struct els_entry_24xx_ext);
+	else
+		rsp_els_sz = sizeof(struct els_entry_24xx);
+
+	rsp_els_pkt = dma_alloc_coherent(&ha->pdev->dev, rsp_els_sz,
 	    &rsp_els_dma, GFP_KERNEL);
-	if (!rsp_els) {
+	if (!rsp_els_pkt) {
 		ql_log(ql_log_warn, vha, 0x0183,
-		    "Failed allocate dma buffer ELS RSP.\n");
+		    "Failed to allocate dma buffer ELS RSP.\n");
 		goto dealloc;
 	}
+	rsp_els = rsp_els_pkt;
 
 	rsp_payload = dma_alloc_coherent(&ha->pdev->dev, sizeof(*rsp_payload),
 	    &rsp_payload_dma, GFP_KERNEL);
@@ -6270,12 +6278,12 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
 	rsp_els->handle = 0;
 	rsp_els->nport_handle = purex->nport_handle;
 	rsp_els->tx_dsd_count = cpu_to_le16(1);
-	rsp_els->vp_index = vp_idx;
-	rsp_els->sof_type = EST_SOFI3;
 	rsp_els->rx_xchg_address = purex->rx_xchg_addr;
 	rsp_els->rx_dsd_count = 0;
 	rsp_els->opcode = purex->els_frame_payload[0];
 
+	qla_els_set_vp_sof(vha, rsp_els_pkt, vp_idx);
+
 	rsp_els->d_id[0] = purex->s_id[0];
 	rsp_els->d_id[1] = purex->s_id[1];
 	rsp_els->d_id[2] = purex->s_id[2];
@@ -6575,13 +6583,13 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
 	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0184,
 	    "-------- ELS RSP -------\n");
 	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0185,
-	    rsp_els, sizeof(*rsp_els));
+	    rsp_els_pkt, rsp_els_sz);
 	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0186,
 	    "-------- ELS RSP PAYLOAD -------\n");
 	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0187,
 	    rsp_payload, rsp_payload_length);
 
-	rval = qla2x00_issue_iocb(vha, rsp_els, rsp_els_dma, 0);
+	rval = qla2x00_issue_iocb(vha, rsp_els_pkt, rsp_els_dma, 0);
 
 	if (rval) {
 		ql_log(ql_log_warn, vha, 0x0188,
@@ -6605,9 +6613,9 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
 	if (rsp_payload)
 		dma_free_coherent(&ha->pdev->dev, sizeof(*rsp_payload),
 		    rsp_payload, rsp_payload_dma);
-	if (rsp_els)
-		dma_free_coherent(&ha->pdev->dev, sizeof(*rsp_els),
-		    rsp_els, rsp_els_dma);
+	if (rsp_els_pkt)
+		dma_free_coherent(&ha->pdev->dev, rsp_els_sz,
+		    rsp_els_pkt, rsp_els_dma);
 }
 
 void
@@ -8376,6 +8384,7 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(struct device_reg_82xx) != 1288);
 	BUILD_BUG_ON(sizeof(struct device_reg_fx00) != 216);
 	BUILD_BUG_ON(sizeof(struct els_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct els_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct els_sts_entry_24xx) != 64);
 	BUILD_BUG_ON(sizeof(struct fxdisc_entry_fx00) != 64);
 	BUILD_BUG_ON(sizeof(struct imm_ntfy_from_isp) != 64);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 30/88] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (28 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 29/88] scsi: qla2xxx: Update handling of ELS IOCBs " Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:31   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 31/88] scsi: qla2xxx: Add 29xx extended logio IOCB support Nilesh Javali
                   ` (57 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Add a BUILD_BUG_ON in qla2x00_module_init() to validate that struct
els_sts_entry_24xx_ext is 128 bytes, matching the 29xx firmware IOCB
size.

The extended layout (29xx) overlays the base els_sts_entry_24xx for
every field read in qla24xx_els_ct_entry(): comp_status,
total_byte_count, error_subcode_1/2, d_id[], s_id[], and
control_flags all sit at byte-identical offsets in both structs.  Only
vp_index/sof_type at offset 14-15 differs (bit-packed differently in
the ext variant), but that field is write-only on the issue path and
never read in this completion handler.

Add a docblock at the top of qla24xx_els_ct_entry() documenting this
layout property.  Improve a few log messages for clarity.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_isr.c | 31 +++++++++++++++++++++++--------
 drivers/scsi/qla2xxx/qla_os.c  |  1 +
 2 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index e2653620e80b..c286465ae013 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -2359,6 +2359,22 @@ static void
 qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
 		     void *pkt, int iocb_type)
 {
+	/*
+	 * els_sts_entry_24xx_ext (29xx) overlays els_sts_entry_24xx for every
+	 * field touched in this completion handler: comp_status (offset 8),
+	 * total_byte_count (32), error_subcode_1 (36), error_subcode_2 (40),
+	 * d_id[]/s_id[] (24..29), control_flags (30) all sit at byte-identical
+	 * offsets in both layouts (only vp_index/sof_type at offset 14-15 are
+	 * bit-packed differently, and that field is write-only on the issue
+	 * path -- we never read it here). All reads in this function are
+	 * therefore stride-agnostic and go through a single struct
+	 * els_sts_entry_24xx * view; the trailing reserved_4[] of the extended
+	 * layout is irrelevant on completion.
+	 *
+	 * Likewise els_entry_24xx_ext overlays els_entry_24xx through
+	 * control_flags (offset 30), so the SRB_ELS_CMD_HST_NOLOGIN ctl_flags
+	 * read below also goes through the 24xx view.
+	 */
 	struct sts_entry_24xx *sts24 = pkt;
 	struct els_sts_entry_24xx *ese = (struct els_sts_entry_24xx *)pkt;
 	const char func[] = "ELS_CT_IOCB";
@@ -2400,7 +2416,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
 				(struct qla_bsg_auth_els_request *)bsg_job->request;
 
 			ql_dbg(ql_dbg_user, vha, 0x700f,
-			     "%s %s. portid=%02x%02x%02x status %x xchg %x bsg ptr %p\n",
+			     "%s %s complete portid=%02x%02x%02x status %x xchg %x bsg ptr %p\n",
 			     __func__, sc_to_str(p->e.sub_cmd),
 			     e->d_id[2], e->d_id[1], e->d_id[0],
 			     comp_status, p->e.extra_rx_xchg_address, bsg_job);
@@ -2461,14 +2477,15 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
 		} else {
 			if (comp_status == CS_DATA_UNDERRUN) {
 				res =  DID_OK << 16;
-				els->u.els_plogi.len = cpu_to_le16(le32_to_cpu(
-					ese->total_byte_count));
+				els->u.els_plogi.len = cpu_to_le16(
+					le32_to_cpu(ese->total_byte_count));
 
 				if (sp->remap.remapped &&
 				    ((u8 *)sp->remap.rsp.buf)[0] == ELS_LS_ACC) {
 					ql_dbg(ql_dbg_user, vha, 0x503f,
 					    "%s IOCB Done LS_ACC %02x%02x%02x -> %02x%02x%02x",
-					    __func__, e->s_id[0], e->s_id[2], e->s_id[1],
+					    __func__,
+					    e->s_id[0], e->s_id[2], e->s_id[1],
 					    e->d_id[2], e->d_id[1], e->d_id[0]);
 					logit = 0;
 				}
@@ -2496,8 +2513,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
 					ql_dbg(ql_dbg_user, vha, 0x503f,
 					    "subcode 1=0x%x subcode 2=0x%x bytes=0x%x %02x%02x%02x -> %02x%02x%02x\n",
 					    fw_status[1], fw_status[2],
-					    le32_to_cpu(((struct els_sts_entry_24xx *)
-						pkt)->total_byte_count),
+					    le32_to_cpu(ese->total_byte_count),
 					    e->s_id[0], e->s_id[2], e->s_id[1],
 					    e->d_id[2], e->d_id[1], e->d_id[0]);
 				}
@@ -2514,8 +2530,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
 				ql_log(ql_log_info, vha, 0x503f,
 				    "subcode 1=0x%x subcode 2=0x%x bytes=0x%x %02x%02x%02x -> %02x%02x%02x\n",
 				    fw_status[1], fw_status[2],
-				    le32_to_cpu(((struct els_sts_entry_24xx *)
-				    pkt)->total_byte_count),
+				    le32_to_cpu(ese->total_byte_count),
 				    e->s_id[0], e->s_id[2], e->s_id[1],
 				    e->d_id[2], e->d_id[1], e->d_id[0]);
 			}
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 74bf203f8d9f..81f3731ee384 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8386,6 +8386,7 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(struct els_entry_24xx) != 64);
 	BUILD_BUG_ON(sizeof(struct els_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct els_sts_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct els_sts_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct fxdisc_entry_fx00) != 64);
 	BUILD_BUG_ON(sizeof(struct imm_ntfy_from_isp) != 64);
 	BUILD_BUG_ON(sizeof(struct init_cb_24xx) != 128);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 31/88] scsi: qla2xxx: Add 29xx extended logio IOCB support
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (29 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 30/88] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 32/88] scsi: qla2xxx: Enhance task management IOCB handling for 29xx series Nilesh Javali
                   ` (56 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

The 29xx series uses a wider IOCB stride (128 bytes vs 64 bytes).
The logio_entry_24xx_ext layout extends logio_entry_24xx with a
wider vp_index field (__le16 vs u8) while keeping all other
read-side fields (comp_status, io_parameter[0..10], entry_status)
at identical offsets and widths.

Update the logio IOCB builder functions (qla24xx_login_iocb,
qla24xx_logout_iocb, qla24xx_prli_iocb, qla24xx_prlo_iocb,
qla24xx_adisc_iocb) to accept a void pointer and dispatch the
vp_index write through IS_QLA29XX(), using an inline cast to the
extended layout at the single write site.

In the completion handler qla24xx_logio_entry(), accept a void
pointer and read through a single logio_entry_24xx view since all
accessed fields sit at the same offsets in both layouts.  Use the
qla_req_entry_size() helper for the dump buffer size.

In qla24xx_login_fabric() and qla24xx_fabric_logout(), allocate
through a void pointer from the DMA pool and dispatch vp_index
via the same inline-cast pattern.

Add a BUILD_BUG_ON for logio_entry_24xx_ext to enforce the 128-byte
size invariant at compile time.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_inline.h | 19 ++++++++++++++
 drivers/scsi/qla2xxx/qla_iocb.c   | 28 +++++++++++++--------
 drivers/scsi/qla2xxx/qla_isr.c    | 41 ++++++++++++++++++-------------
 drivers/scsi/qla2xxx/qla_mbx.c    | 27 +++++++++++---------
 drivers/scsi/qla2xxx/qla_os.c     |  1 +
 5 files changed, 78 insertions(+), 38 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index 695a5b7fc794..58a4a35e70c6 100644
--- a/drivers/scsi/qla2xxx/qla_inline.h
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -119,6 +119,25 @@ qla_sts_cont_data_size(struct qla_hw_data *ha)
 		sizeof_field(sts_cont_entry_t, data);
 }
 
+/**
+ * qla_logio_set_vp_index() - write vp_index into a login/logout IOCB.
+ * @ha: HBA pointer
+ * @pkt: logio IOCB (logio_entry_24xx or logio_entry_24xx_ext)
+ * @vp_idx: virtual port index
+ *
+ * vp_index widens from u8 (logio_entry_24xx) to __le16
+ * (logio_entry_24xx_ext) on 29xx; write the field at the right width.
+ */
+static inline void
+qla_logio_set_vp_index(struct qla_hw_data *ha, void *pkt, u16 vp_idx)
+{
+	if (IS_QLA29XX(ha))
+		((struct logio_entry_24xx_ext *)pkt)->vp_index =
+			cpu_to_le16(vp_idx);
+	else
+		((struct logio_entry_24xx *)pkt)->vp_index = vp_idx;
+}
+
 static inline void
 qla2x00_poll(struct rsp_que *rsp)
 {
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 416fa78f6061..01fd45b47e17 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -2488,8 +2488,9 @@ qla2x00_alloc_iocbs(struct scsi_qla_host *vha, srb_t *sp)
 }
 
 static void
-qla24xx_prli_iocb(srb_t *sp, struct logio_entry_24xx *logio)
+qla24xx_prli_iocb(srb_t *sp, void *pkt)
 {
+	struct logio_entry_24xx *logio = pkt;
 	struct srb_iocb *lio = &sp->u.iocb_cmd;
 
 	logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
@@ -2516,12 +2517,13 @@ qla24xx_prli_iocb(srb_t *sp, struct logio_entry_24xx *logio)
 	logio->port_id[0] = sp->fcport->d_id.b.al_pa;
 	logio->port_id[1] = sp->fcport->d_id.b.area;
 	logio->port_id[2] = sp->fcport->d_id.b.domain;
-	logio->vp_index = sp->vha->vp_idx;
+	qla_logio_set_vp_index(sp->vha->hw, pkt, sp->vha->vp_idx);
 }
 
 static void
-qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio)
+qla24xx_login_iocb(srb_t *sp, void *pkt)
 {
+	struct logio_entry_24xx *logio = pkt;
 	struct srb_iocb *lio = &sp->u.iocb_cmd;
 
 	logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
@@ -2546,7 +2548,7 @@ qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio)
 	logio->port_id[0] = sp->fcport->d_id.b.al_pa;
 	logio->port_id[1] = sp->fcport->d_id.b.area;
 	logio->port_id[2] = sp->fcport->d_id.b.domain;
-	logio->vp_index = sp->vha->vp_idx;
+	qla_logio_set_vp_index(sp->vha->hw, pkt, sp->vha->vp_idx);
 }
 
 static void
@@ -2574,9 +2576,11 @@ qla2x00_login_iocb(srb_t *sp, struct mbx_entry *mbx)
 }
 
 static void
-qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio)
+qla24xx_logout_iocb(srb_t *sp, void *pkt)
 {
+	struct logio_entry_24xx *logio = pkt;
 	u16 control_flags = LCF_COMMAND_LOGO;
+
 	logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
 
 	if (sp->fcport->explicit_logout) {
@@ -2593,7 +2597,7 @@ qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio)
 	logio->port_id[0] = sp->fcport->d_id.b.al_pa;
 	logio->port_id[1] = sp->fcport->d_id.b.area;
 	logio->port_id[2] = sp->fcport->d_id.b.domain;
-	logio->vp_index = sp->vha->vp_idx;
+	qla_logio_set_vp_index(sp->vha->hw, pkt, sp->vha->vp_idx);
 }
 
 static void
@@ -2615,12 +2619,14 @@ qla2x00_logout_iocb(srb_t *sp, struct mbx_entry *mbx)
 }
 
 static void
-qla24xx_adisc_iocb(srb_t *sp, struct logio_entry_24xx *logio)
+qla24xx_adisc_iocb(srb_t *sp, void *pkt)
 {
+	struct logio_entry_24xx *logio = pkt;
+
 	logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
 	logio->control_flags = cpu_to_le16(LCF_COMMAND_ADISC);
 	logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-	logio->vp_index = sp->vha->vp_idx;
+	qla_logio_set_vp_index(sp->vha->hw, pkt, sp->vha->vp_idx);
 }
 
 static void
@@ -3983,8 +3989,10 @@ qla25xx_ctrlvp_iocb(srb_t *sp, struct vp_ctrl_entry_24xx *vce)
 }
 
 static void
-qla24xx_prlo_iocb(srb_t *sp, struct logio_entry_24xx *logio)
+qla24xx_prlo_iocb(srb_t *sp, void *pkt)
 {
+	struct logio_entry_24xx *logio = pkt;
+
 	logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
 	logio->control_flags =
 	    cpu_to_le16(LCF_COMMAND_PRLO|LCF_IMPL_PRLO);
@@ -3993,7 +4001,7 @@ qla24xx_prlo_iocb(srb_t *sp, struct logio_entry_24xx *logio)
 	logio->port_id[0] = sp->fcport->d_id.b.al_pa;
 	logio->port_id[1] = sp->fcport->d_id.b.area;
 	logio->port_id[2] = sp->fcport->d_id.b.domain;
-	logio->vp_index = sp->fcport->vha->vp_idx;
+	qla_logio_set_vp_index(sp->vha->hw, pkt, sp->fcport->vha->vp_idx);
 }
 
 static int qla_get_iocbs_resource(struct srb *sp)
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index c286465ae013..afd685d48c6e 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -2583,8 +2583,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
 }
 
 static void
-qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
-    struct logio_entry_24xx *logio)
+qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, void *pkt)
 {
 	const char func[] = "LOGIO-IOCB";
 	const char *type;
@@ -2594,8 +2593,20 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
 	uint16_t *data;
 	uint32_t iop[2];
 	int logit = 1;
+	struct qla_hw_data *ha = vha->hw;
+	/*
+	 * logio_entry_24xx_ext overlays logio_entry_24xx through
+	 * io_parameter[10]: comp_status, io_parameter[0..10] and
+	 * entry_status are at identical offsets and types in both layouts
+	 * (only vp_index width differs, and that field is write-only on
+	 * the issue path).  So all reads in this completion handler are
+	 * stride-agnostic and we read through a single struct
+	 * logio_entry_24xx * view; the trailing reserved_2[64] of the
+	 * extended layout is irrelevant here.
+	 */
+	struct logio_entry_24xx *logio = pkt;
 
-	sp = qla2x00_get_sp_from_handle(vha, func, req, logio);
+	sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
 	if (!sp)
 		return;
 
@@ -2615,7 +2626,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
 		    fcport->d_id.b.area, fcport->d_id.b.al_pa,
 		    logio->entry_status);
 		ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x504d,
-		    logio, sizeof(*logio));
+		    pkt, qla_rsp_entry_size(ha));
 
 		goto logio_done;
 	}
@@ -2626,7 +2637,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
 		    type, sp->handle, fcport->d_id.b24, fcport->port_name,
 		    le32_to_cpu(logio->io_parameter[0]));
 
-		vha->hw->exch_starvation = 0;
+		ha->exch_starvation = 0;
 		data[0] = MBS_COMMAND_COMPLETE;
 
 		if (sp->type == SRB_PRLI_CMD) {
@@ -2665,6 +2676,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
 
 	iop[0] = le32_to_cpu(logio->io_parameter[0]);
 	iop[1] = le32_to_cpu(logio->io_parameter[1]);
+
 	lio->u.logio.iop[0] = iop[0];
 	lio->u.logio.iop[1] = iop[1];
 	switch (iop[0]) {
@@ -2689,14 +2701,14 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
 		data[0] = MBS_COMMAND_ERROR;
 		break;
 	case LSC_SCODE_NOXCB:
-		vha->hw->exch_starvation++;
-		if (vha->hw->exch_starvation > 5) {
+		ha->exch_starvation++;
+		if (ha->exch_starvation > 5) {
 			ql_log(ql_log_warn, vha, 0xd046,
 			    "Exchange starvation. Resetting RISC\n");
 
-			vha->hw->exch_starvation = 0;
+			ha->exch_starvation = 0;
 
-			if (IS_P3P_TYPE(vha->hw))
+			if (IS_P3P_TYPE(ha))
 				set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
 			else
 				set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
@@ -2712,16 +2724,12 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
 		ql_log(ql_log_warn, sp->vha, 0x5037, "Async-%s failed: "
 		       "handle=%x pid=%06x wwpn=%8phC comp_status=%x iop0=%x iop1=%x\n",
 		       type, sp->handle, fcport->d_id.b24, fcport->port_name,
-		       le16_to_cpu(logio->comp_status),
-		       le32_to_cpu(logio->io_parameter[0]),
-		       le32_to_cpu(logio->io_parameter[1]));
+		       le16_to_cpu(logio->comp_status), iop[0], iop[1]);
 	else
 		ql_dbg(ql_dbg_disc, sp->vha, 0x5037, "Async-%s failed: "
 		       "handle=%x pid=%06x wwpn=%8phC comp_status=%x iop0=%x iop1=%x\n",
 		       type, sp->handle, fcport->d_id.b24, fcport->port_name,
-		       le16_to_cpu(logio->comp_status),
-		       le32_to_cpu(logio->io_parameter[0]),
-		       le32_to_cpu(logio->io_parameter[1]));
+		       le16_to_cpu(logio->comp_status), iop[0], iop[1]);
 
 logio_done:
 	sp->done(sp, 0);
@@ -4118,8 +4126,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 			    (struct vp_rpt_id_entry_24xx *)pkt);
 			break;
 		case LOGINOUT_PORT_IOCB_TYPE:
-			qla24xx_logio_entry(vha, rsp->req,
-			    (struct logio_entry_24xx *)pkt);
+			qla24xx_logio_entry(vha, rsp->req, pkt);
 			break;
 		case CT_IOCB_TYPE:
 			qla24xx_els_ct_entry(vha, rsp->req, pkt, CT_IOCB_TYPE);
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index f9b326c02055..7dff227899a5 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -2573,7 +2573,7 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
     uint8_t area, uint8_t al_pa, uint16_t *mb, uint8_t opt)
 {
 	int		rval;
-
+	void		*lg_buf;
 	struct logio_entry_24xx *lg;
 	dma_addr_t	lg_dma;
 	uint32_t	iop[2];
@@ -2588,12 +2588,13 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
 	else
 		req = ha->req_q_map[0];
 
-	lg = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
-	if (lg == NULL) {
+	lg_buf = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
+	if (!lg_buf) {
 		ql_log(ql_log_warn, vha, 0x1062,
 		    "Failed to allocate login IOCB.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
+	lg = lg_buf;
 
 	lg->entry_type = LOGINOUT_PORT_IOCB_TYPE;
 	lg->entry_count = 1;
@@ -2607,8 +2608,9 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
 	lg->port_id[0] = al_pa;
 	lg->port_id[1] = area;
 	lg->port_id[2] = domain;
-	lg->vp_index = vha->vp_idx;
-	rval = qla2x00_issue_iocb_timeout(vha, lg, lg_dma, 0,
+	qla_logio_set_vp_index(ha, lg, vha->vp_idx);
+
+	rval = qla2x00_issue_iocb_timeout(vha, lg_buf, lg_dma, 0,
 	    (ha->r_a_tov / 10 * 2) + 2);
 	if (rval != QLA_SUCCESS) {
 		ql_dbg(ql_dbg_mbx, vha, 0x1063,
@@ -2678,7 +2680,7 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
 						 */
 	}
 
-	dma_pool_free(ha->s_dma_pool, lg, lg_dma);
+	dma_pool_free(ha->s_dma_pool, lg_buf, lg_dma);
 
 	return rval;
 }
@@ -2849,6 +2851,7 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
     uint8_t area, uint8_t al_pa)
 {
 	int		rval;
+	void		*lg_buf;
 	struct logio_entry_24xx *lg;
 	dma_addr_t	lg_dma;
 	struct qla_hw_data *ha = vha->hw;
@@ -2857,12 +2860,13 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x106d,
 	    "Entered %s.\n", __func__);
 
-	lg = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
-	if (lg == NULL) {
+	lg_buf = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
+	if (!lg_buf) {
 		ql_log(ql_log_warn, vha, 0x106e,
 		    "Failed to allocate logout IOCB.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
+	lg = lg_buf;
 
 	req = vha->req;
 	lg->entry_type = LOGINOUT_PORT_IOCB_TYPE;
@@ -2875,8 +2879,9 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
 	lg->port_id[0] = al_pa;
 	lg->port_id[1] = area;
 	lg->port_id[2] = domain;
-	lg->vp_index = vha->vp_idx;
-	rval = qla2x00_issue_iocb_timeout(vha, lg, lg_dma, 0,
+	qla_logio_set_vp_index(ha, lg, vha->vp_idx);
+
+	rval = qla2x00_issue_iocb_timeout(vha, lg_buf, lg_dma, 0,
 	    (ha->r_a_tov / 10 * 2) + 2);
 	if (rval != QLA_SUCCESS) {
 		ql_dbg(ql_dbg_mbx, vha, 0x106f,
@@ -2898,7 +2903,7 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
 		    "Done %s.\n", __func__);
 	}
 
-	dma_pool_free(ha->s_dma_pool, lg, lg_dma);
+	dma_pool_free(ha->s_dma_pool, lg_buf, lg_dma);
 
 	return rval;
 }
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 81f3731ee384..b0e89dfff2e8 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8392,6 +8392,7 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(struct init_cb_24xx) != 128);
 	BUILD_BUG_ON(sizeof(struct init_cb_81xx) != 128);
 	BUILD_BUG_ON(sizeof(struct logio_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct logio_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct mbx_entry) != 64);
 	BUILD_BUG_ON(sizeof(struct mid_init_cb_24xx) != 5252);
 	BUILD_BUG_ON(sizeof(struct mrk_entry_24xx) != 64);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 32/88] scsi: qla2xxx: Enhance task management IOCB handling for 29xx series
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (30 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 31/88] scsi: qla2xxx: Add 29xx extended logio IOCB support Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 33/88] scsi: qla2xxx: Add abort command " Nilesh Javali
                   ` (55 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Update qla24xx_tm_iocb() and __qla24xx_issue_tmf() to support the
extended task management structure (tsk_mgmt_entry_ext) for 29xx
adapters.

tsk_mgmt_entry_ext overlays tsk_mgmt_entry through control_flags
(offsets 0-27 are byte-identical): entry_type, entry_count, handle,
nport_handle, timeout, lun and control_flags sit at the same offsets
and widths.  The layouts diverge only after that point:

  - the 24xx layout has port_id[3] + u8 vp_index;
  - the ext layout has __le16 vp_index and no port_id.

Factor the common IOCB header writes through a single tsk_mgmt_entry *
view and branch on IS_QLA29XX() only for the diverging port_id /
vp_index assignments.  Change qla24xx_tm_iocb() to accept void *pkt
to allow casting to either structure type.

Add tsk_ext member to the tsk_mgmt_cmd union and a BUILD_BUG_ON size
check for the 128-byte extended structure.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_iocb.c | 31 ++++++++++++++++++++++---------
 drivers/scsi/qla2xxx/qla_mbx.c  | 18 ++++++++++++++----
 drivers/scsi/qla2xxx/qla_os.c   |  1 +
 3 files changed, 37 insertions(+), 13 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 01fd45b47e17..98d735c216da 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -2651,7 +2651,7 @@ qla2x00_adisc_iocb(srb_t *sp, struct mbx_entry *mbx)
 }
 
 static void
-qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk)
+qla24xx_tm_iocb(srb_t *sp, void *pkt)
 {
 	uint32_t flags;
 	uint64_t lun;
@@ -2660,26 +2660,39 @@ qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk)
 	struct qla_hw_data *ha = vha->hw;
 	struct srb_iocb *iocb = &sp->u.iocb_cmd;
 	struct req_que *req = sp->qpair->req;
+	struct tsk_mgmt_entry *tsk;
 
 	flags = iocb->u.tmf.flags;
 	lun = iocb->u.tmf.lun;
 
+	/*
+	 * tsk_mgmt_entry_ext overlays tsk_mgmt_entry through control_flags
+	 * (offsets 0-27 are byte-identical), so the common header writes
+	 * go through one struct tsk_mgmt_entry * view.  The ext layout
+	 * has no port_id and uses a wider __le16 vp_index at a different
+	 * offset, so port_id / vp_index assignments diverge per stride.
+	 */
+	tsk = pkt;
 	tsk->entry_type = TSK_MGMT_IOCB_TYPE;
 	tsk->entry_count = 1;
 	tsk->handle = make_handle(req->id, tsk->handle);
 	tsk->nport_handle = cpu_to_le16(fcport->loop_id);
 	tsk->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
 	tsk->control_flags = cpu_to_le32(flags);
-	tsk->port_id[0] = fcport->d_id.b.al_pa;
-	tsk->port_id[1] = fcport->d_id.b.area;
-	tsk->port_id[2] = fcport->d_id.b.domain;
-	tsk->vp_index = fcport->vha->vp_idx;
+	if (IS_QLA29XX(ha)) {
+		((struct tsk_mgmt_entry_ext *)pkt)->vp_index =
+		    cpu_to_le16(fcport->vha->vp_idx);
+	} else {
+		tsk->port_id[0] = fcport->d_id.b.al_pa;
+		tsk->port_id[1] = fcport->d_id.b.area;
+		tsk->port_id[2] = fcport->d_id.b.domain;
+		tsk->vp_index = fcport->vha->vp_idx;
+	}
 
-	if (flags & (TCF_LUN_RESET | TCF_ABORT_TASK_SET|
-	    TCF_CLEAR_TASK_SET|TCF_CLEAR_ACA)) {
+	if (flags & (TCF_LUN_RESET | TCF_ABORT_TASK_SET |
+	    TCF_CLEAR_TASK_SET | TCF_CLEAR_ACA)) {
 		int_to_scsilun(lun, &tsk->lun);
-		host_to_fcp_swap((uint8_t *)&tsk->lun,
-			sizeof(tsk->lun));
+		host_to_fcp_swap((uint8_t *)&tsk->lun, sizeof(tsk->lun));
 	}
 }
 
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 7dff227899a5..9bef87862077 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -3411,6 +3411,7 @@ qla24xx_abort_command(srb_t *sp)
 struct tsk_mgmt_cmd {
 	union {
 		struct tsk_mgmt_entry tsk;
+		struct tsk_mgmt_entry_ext tsk_ext;
 		struct sts_entry_24xx sts;
 		struct sts_entry_24xx_ext sts_ext;
 	} p;
@@ -3450,16 +3451,25 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 
+	/*
+	 * tsk_mgmt_entry_ext overlays tsk_mgmt_entry through control_flags;
+	 * the common-header writes go through tsk->p.tsk and only port_id
+	 * (24xx-only) and vp_index width / offset diverge.
+	 */
 	tsk->p.tsk.entry_type = TSK_MGMT_IOCB_TYPE;
 	tsk->p.tsk.entry_count = 1;
 	tsk->p.tsk.handle = make_handle(req->id, tsk->p.tsk.handle);
 	tsk->p.tsk.nport_handle = cpu_to_le16(fcport->loop_id);
 	tsk->p.tsk.timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
 	tsk->p.tsk.control_flags = cpu_to_le32(type);
-	tsk->p.tsk.port_id[0] = fcport->d_id.b.al_pa;
-	tsk->p.tsk.port_id[1] = fcport->d_id.b.area;
-	tsk->p.tsk.port_id[2] = fcport->d_id.b.domain;
-	tsk->p.tsk.vp_index = fcport->vha->vp_idx;
+	if (IS_QLA29XX(ha)) {
+		tsk->p.tsk_ext.vp_index = cpu_to_le16(fcport->vha->vp_idx);
+	} else {
+		tsk->p.tsk.port_id[0] = fcport->d_id.b.al_pa;
+		tsk->p.tsk.port_id[1] = fcport->d_id.b.area;
+		tsk->p.tsk.port_id[2] = fcport->d_id.b.domain;
+		tsk->p.tsk.vp_index = fcport->vha->vp_idx;
+	}
 	if (type == TCF_LUN_RESET) {
 		int_to_scsilun(l, &tsk->p.tsk.lun);
 		host_to_fcp_swap((uint8_t *)&tsk->p.tsk.lun,
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index b0e89dfff2e8..6c951839507b 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8428,6 +8428,7 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(struct sts_entry_24xx) != 64);
 	BUILD_BUG_ON(sizeof(struct sts_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct tsk_mgmt_entry) != 64);
+	BUILD_BUG_ON(sizeof(struct tsk_mgmt_entry_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct tsk_mgmt_entry_fx00) != 64);
 	BUILD_BUG_ON(sizeof(struct verify_chip_entry_84xx) != 64);
 	BUILD_BUG_ON(sizeof(struct verify_chip_rsp_84xx) != 52);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 33/88] scsi: qla2xxx: Add abort command handling for 29xx series
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (31 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 32/88] scsi: qla2xxx: Enhance task management IOCB handling for 29xx series Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 34/88] scsi: qla2xxx: Enhance ABTS processing " Nilesh Javali
                   ` (54 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Extend the abort-IOCB code path to support the 29xx extended
abort_entry_24xx_ext structure alongside the existing
abort_entry_24xx.

The two layouts overlay byte-for-byte through req_que_no
(offsets 0-17): entry_status (offset 3), the
nport_handle/comp_status union (offset 8), and options (offset 10)
sit at identical positions in both.  After that they diverge: the
24xx variant carries reserved_1[30], port_id[3], and a u8 vp_index
at offsets 48-51, while the ext variant places a __le16 vp_index at
offset 18 and drops port_id.  The drv / fw unions live at offset 56
in the 24xx layout but offset 24 in ext.

Leverage this overlap by using a single struct abort_entry_24xx *
view for the common header writes (entry_type, count, handle,
nport_handle, handle_to_abort, req_que_no) and completion-status
reads (entry_status, comp_status), branching on IS_QLA29XX() only
where the layouts genuinely diverge:

  - port_id (24xx-only) and vp_index width on the issue path
    (qla24xx_abort_iocb in qla_iocb.c, qla24xx_abort_command in
    qla_mbx.c);
  - drv / fw union access in qla_nvme_abort_set_option /
    qla_nvme_abort_process_comp_status (qla_nvme.c);
  - completion comp_status read in qla24xx_abort_iocb_entry
    (qla_isr.c) is stride-agnostic -- no IS_QLA29XX dispatch
    needed.

Function signatures in qla_nvme_abort_set_option(),
qla_nvme_abort_process_comp_status(), qla24xx_abort_iocb(), and
qla24xx_abort_iocb_entry() are widened to accept void * so both
struct variants can be passed through.  memset() uses
qla_req_entry_size(ha) to match the ring-slot size.  Response
status checking now reads comp_status instead of nport_handle.  A
BUILD_BUG_ON verifies abort_entry_24xx_ext is 128 bytes.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_gbl.h  |  6 ++--
 drivers/scsi/qla2xxx/qla_iocb.c | 52 ++++++++++++++++++++-----------
 drivers/scsi/qla2xxx/qla_isr.c  | 14 ++++++---
 drivers/scsi/qla2xxx/qla_mbx.c  | 45 ++++++++++++++++++---------
 drivers/scsi/qla2xxx/qla_nvme.c | 54 +++++++++++++++++++++++++--------
 drivers/scsi/qla2xxx/qla_os.c   |  1 +
 6 files changed, 120 insertions(+), 52 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 4ac3cb08fcc9..bba324febf09 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -989,10 +989,8 @@ extern void qla24xx_process_purex_list(struct purex_list *);
 extern void qla2x00_dfs_create_rport(scsi_qla_host_t *vha, struct fc_port *fp);
 extern void qla2x00_dfs_remove_rport(scsi_qla_host_t *vha, struct fc_port *fp);
 extern void qla_wait_nvme_release_cmd_kref(srb_t *sp);
-extern void qla_nvme_abort_set_option
-		(struct abort_entry_24xx *abt, srb_t *sp);
-extern void qla_nvme_abort_process_comp_status
-		(struct abort_entry_24xx *abt, srb_t *sp);
+extern void qla_nvme_abort_set_option(void *pkt, srb_t *sp);
+extern void qla_nvme_abort_process_comp_status(void *pkt, srb_t *sp);
 struct scsi_qla_host *qla_find_host_by_vp_idx(struct scsi_qla_host *vha,
 	uint16_t vp_idx);
 
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 98d735c216da..7d0392074760 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -3852,32 +3852,50 @@ qla82xx_start_scsi(srb_t *sp)
 }
 
 static void
-qla24xx_abort_iocb(srb_t *sp, struct abort_entry_24xx *abt_iocb)
+qla24xx_abort_iocb(srb_t *sp, void *pkt)
 {
 	struct srb_iocb *aio = &sp->u.iocb_cmd;
 	scsi_qla_host_t *vha = sp->vha;
+	struct qla_hw_data *ha = vha->hw;
 	struct req_que *req = sp->qpair->req;
 	srb_t *orig_sp = sp->cmd_sp;
+	struct abort_entry_24xx *abt = pkt;
 
-	memset(abt_iocb, 0, sizeof(struct abort_entry_24xx));
-	abt_iocb->entry_type = ABORT_IOCB_TYPE;
-	abt_iocb->entry_count = 1;
-	abt_iocb->handle = make_handle(req->id, sp->handle);
-	if (sp->fcport) {
-		abt_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-		abt_iocb->port_id[0] = sp->fcport->d_id.b.al_pa;
-		abt_iocb->port_id[1] = sp->fcport->d_id.b.area;
-		abt_iocb->port_id[2] = sp->fcport->d_id.b.domain;
-	}
-	abt_iocb->handle_to_abort =
-		make_handle(le16_to_cpu(aio->u.abt.req_que_no),
-			    aio->u.abt.cmd_hndl);
-	abt_iocb->vp_index = vha->vp_idx;
-	abt_iocb->req_que_no = aio->u.abt.req_que_no;
+	/*
+	 * abort_entry_24xx_ext overlays abort_entry_24xx through
+	 * req_que_no (offsets 0-17).  After that the layouts diverge:
+	 * the 24xx variant has 30 bytes of reserved_1 followed by
+	 * port_id[3] and a u8 vp_index at offsets 48-51, while the
+	 * ext variant places a __le16 vp_index at offset 18 and has
+	 * no port_id field.  Common-header writes go through one
+	 * struct abort_entry_24xx * view; only port_id / vp_index
+	 * branch on stride.
+	 */
+	memset(pkt, 0, qla_req_entry_size(ha));
+	abt->entry_type = ABORT_IOCB_TYPE;
+	abt->entry_count = 1;
+	abt->handle = make_handle(req->id, sp->handle);
+	if (sp->fcport)
+		abt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+	abt->handle_to_abort =
+	    make_handle(le16_to_cpu(aio->u.abt.req_que_no),
+			aio->u.abt.cmd_hndl);
+	abt->req_que_no = aio->u.abt.req_que_no;
+	if (IS_QLA29XX(ha)) {
+		((struct abort_entry_24xx_ext *)pkt)->vp_index =
+		    cpu_to_le16(vha->vp_idx);
+	} else {
+		if (sp->fcport) {
+			abt->port_id[0] = sp->fcport->d_id.b.al_pa;
+			abt->port_id[1] = sp->fcport->d_id.b.area;
+			abt->port_id[2] = sp->fcport->d_id.b.domain;
+		}
+		abt->vp_index = vha->vp_idx;
+	}
 
 	/* need to pass original sp */
 	if (orig_sp)
-		qla_nvme_abort_set_option(abt_iocb, orig_sp);
+		qla_nvme_abort_set_option(pkt, orig_sp);
 
 	/* Send the command to the firmware */
 	wmb();
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index afd685d48c6e..dd4416a3bf3a 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -3960,7 +3960,7 @@ qla24xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
 
 static void
 qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
-	struct abort_entry_24xx *pkt)
+	void *pkt)
 {
 	const char func[] = "ABT_IOCB";
 	srb_t *sp;
@@ -3972,7 +3972,14 @@ qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
 		return;
 
 	abt = &sp->u.iocb_cmd;
-	abt->u.abt.comp_status = pkt->comp_status;
+	/*
+	 * abort_entry_24xx_ext overlays abort_entry_24xx through the
+	 * nport_handle/comp_status union at offset 8, so reading
+	 * comp_status is stride-agnostic and goes through the 24xx view.
+	 */
+	abt->u.abt.comp_status =
+	    ((struct abort_entry_24xx *)pkt)->comp_status;
+
 	orig_sp = sp->cmd_sp;
 	/* Need to pass original sp */
 	if (orig_sp)
@@ -4174,8 +4181,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 			qla_marker_iocb_entry(vha, rsp->req, pkt);
 			break;
 		case ABORT_IOCB_TYPE:
-			qla24xx_abort_iocb_entry(vha, rsp->req,
-			    (struct abort_entry_24xx *)pkt);
+			qla24xx_abort_iocb_entry(vha, rsp->req, pkt);
 			break;
 		case MBX_IOCB_TYPE:
 			qla24xx_mbx_iocb_entry(vha, rsp->req,
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 9bef87862077..0d7aa6fce007 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -3324,7 +3324,7 @@ qla24xx_abort_command(srb_t *sp)
 {
 	int		rval;
 	unsigned long   flags = 0;
-
+	void		*abt_buf;
 	struct abort_entry_24xx *abt;
 	dma_addr_t	abt_dma;
 	uint32_t	handle;
@@ -3356,28 +3356,42 @@ qla24xx_abort_command(srb_t *sp)
 		return QLA_ERR_NOT_FOUND;
 	}
 
-	abt = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &abt_dma);
-	if (abt == NULL) {
+	abt_buf = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &abt_dma);
+	if (abt_buf == NULL) {
 		ql_log(ql_log_warn, vha, 0x108d,
 		    "Failed to allocate abort IOCB.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
+	abt = abt_buf;
 
+	/*
+	 * abort_entry_24xx_ext overlays abort_entry_24xx through
+	 * req_que_no (offsets 0-17), and entry_status (offset 3) and
+	 * comp_status (offset 8) sit at identical offsets in both, so
+	 * the common header writes and the completion-status reads are
+	 * stride-agnostic and go through the 24xx view.  Only port_id
+	 * (24xx-only) and vp_index width / offset diverge per stride.
+	 */
 	abt->entry_type = ABORT_IOCB_TYPE;
 	abt->entry_count = 1;
 	abt->handle = make_handle(req->id, abt->handle);
 	abt->nport_handle = cpu_to_le16(fcport->loop_id);
 	abt->handle_to_abort = make_handle(req->id, handle);
-	abt->port_id[0] = fcport->d_id.b.al_pa;
-	abt->port_id[1] = fcport->d_id.b.area;
-	abt->port_id[2] = fcport->d_id.b.domain;
-	abt->vp_index = fcport->vha->vp_idx;
-
 	abt->req_que_no = cpu_to_le16(req->id);
+	if (IS_QLA29XX(ha)) {
+		((struct abort_entry_24xx_ext *)abt)->vp_index =
+		    cpu_to_le16(fcport->vha->vp_idx);
+	} else {
+		abt->port_id[0] = fcport->d_id.b.al_pa;
+		abt->port_id[1] = fcport->d_id.b.area;
+		abt->port_id[2] = fcport->d_id.b.domain;
+		abt->vp_index = fcport->vha->vp_idx;
+	}
+
 	/* Need to pass original sp */
-	qla_nvme_abort_set_option(abt, sp);
+	qla_nvme_abort_set_option(abt_buf, sp);
 
-	rval = qla2x00_issue_iocb(vha, abt, abt_dma, 0);
+	rval = qla2x00_issue_iocb(vha, abt_buf, abt_dma, 0);
 	if (rval != QLA_SUCCESS) {
 		ql_dbg(ql_dbg_mbx, vha, 0x108e,
 		    "Failed to issue IOCB (%x).\n", rval);
@@ -3386,11 +3400,11 @@ qla24xx_abort_command(srb_t *sp)
 		    "Failed to complete IOCB -- error status (%x).\n",
 		    abt->entry_status);
 		rval = QLA_FUNCTION_FAILED;
-	} else if (abt->nport_handle != cpu_to_le16(0)) {
+	} else if (abt->comp_status != cpu_to_le16(0)) {
 		ql_dbg(ql_dbg_mbx, vha, 0x1090,
 		    "Failed to complete IOCB -- completion status (%x).\n",
-		    le16_to_cpu(abt->nport_handle));
-		if (abt->nport_handle == cpu_to_le16(CS_IOCB_ERROR))
+		    le16_to_cpu(abt->comp_status));
+		if (abt->comp_status == cpu_to_le16(CS_IOCB_ERROR))
 			rval = QLA_FUNCTION_PARAMETER_ERROR;
 		else
 			rval = QLA_FUNCTION_FAILED;
@@ -3398,12 +3412,13 @@ qla24xx_abort_command(srb_t *sp)
 		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1091,
 		    "Done %s.\n", __func__);
 	}
+
 	if (rval == QLA_SUCCESS)
-		qla_nvme_abort_process_comp_status(abt, sp);
+		qla_nvme_abort_process_comp_status(abt_buf, sp);
 
 	qla_wait_nvme_release_cmd_kref(sp);
 
-	dma_pool_free(ha->s_dma_pool, abt, abt_dma);
+	dma_pool_free(ha->s_dma_pool, abt_buf, abt_dma);
 
 	return rval;
 }
diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index b2eacf0277f4..8fa980db6ce4 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -1067,36 +1067,69 @@ int qla_nvme_register_hba(struct scsi_qla_host *vha)
 	return ret;
 }
 
-void qla_nvme_abort_set_option(struct abort_entry_24xx *abt, srb_t *orig_sp)
+void qla_nvme_abort_set_option(void *pkt, srb_t *orig_sp)
 {
 	struct qla_hw_data *ha;
+	struct abort_entry_24xx *abt = pkt;
 
 	if (!(ql2xabts_wait_nvme && QLA_ABTS_WAIT_ENABLED(orig_sp)))
 		return;
 
 	ha = orig_sp->fcport->vha->hw;
 
+	/*
+	 * abort_entry_24xx_ext overlays abort_entry_24xx through 'options'
+	 * (offset 10), so options writes are stride-agnostic.  The drv
+	 * union sits at offset 56 in the 24xx layout but offset 24 in the
+	 * ext layout, so the drv writes need a typed pointer.
+	 */
 	WARN_ON_ONCE(abt->options & cpu_to_le16(BIT_0));
-	/* Use Driver Specified Retry Count */
 	abt->options |= cpu_to_le16(AOF_ABTS_RTY_CNT);
-	abt->drv.abts_rty_cnt = cpu_to_le16(2);
-	/* Use specified response timeout */
 	abt->options |= cpu_to_le16(AOF_RSP_TIMEOUT);
-	/* set it to 2 * r_a_tov in secs */
-	abt->drv.rsp_timeout = cpu_to_le16(2 * (ha->r_a_tov / 10));
+	if (IS_QLA29XX(ha)) {
+		struct abort_entry_24xx_ext *abt_ext = pkt;
+
+		abt_ext->drv.abts_rty_cnt = cpu_to_le16(2);
+		abt_ext->drv.rsp_timeout =
+		    cpu_to_le16(2 * (ha->r_a_tov / 10));
+	} else {
+		abt->drv.abts_rty_cnt = cpu_to_le16(2);
+		abt->drv.rsp_timeout = cpu_to_le16(2 * (ha->r_a_tov / 10));
+	}
 }
 
-void qla_nvme_abort_process_comp_status(struct abort_entry_24xx *abt, srb_t *orig_sp)
+void qla_nvme_abort_process_comp_status(void *pkt, srb_t *orig_sp)
 {
 	u16	comp_status;
 	struct scsi_qla_host *vha;
+	u8	rjt_vendor_unique, rjt_reason_expl, rjt_reason_code;
+	struct abort_entry_24xx *abt = pkt;
 
 	if (!(ql2xabts_wait_nvme && QLA_ABTS_WAIT_ENABLED(orig_sp)))
 		return;
 
 	vha = orig_sp->fcport->vha;
 
+	/*
+	 * comp_status sits at offset 8 in both layouts (the
+	 * nport_handle/comp_status union), so the read is
+	 * stride-agnostic.  The fw union, like drv, lives at offset
+	 * 56 in the 24xx layout and offset 24 in the ext layout, so
+	 * those byte reads still need a typed pointer.
+	 */
 	comp_status = le16_to_cpu(abt->comp_status);
+	if (IS_QLA29XX(vha->hw)) {
+		struct abort_entry_24xx_ext *abt_ext = pkt;
+
+		rjt_vendor_unique = abt_ext->fw.ba_rjt_vendorUnique;
+		rjt_reason_expl = abt_ext->fw.ba_rjt_reasonCodeExpl;
+		rjt_reason_code = abt_ext->fw.ba_rjt_reasonCode;
+	} else {
+		rjt_vendor_unique = abt->fw.ba_rjt_vendorUnique;
+		rjt_reason_expl = abt->fw.ba_rjt_reasonCodeExpl;
+		rjt_reason_code = abt->fw.ba_rjt_reasonCode;
+	}
+
 	switch (comp_status) {
 	case CS_RESET:		/* reset event aborted */
 	case CS_ABORTED:	/* IOCB was cleaned */
@@ -1116,11 +1149,8 @@ void qla_nvme_abort_process_comp_status(struct abort_entry_24xx *abt, srb_t *ori
 	/* BA_RJT was received for the ABTS */
 	case CS_REJECT_RECEIVED:
 		ql_dbg(ql_dbg_async, vha, 0xf09e,
-		       "BA_RJT was received for the ABTS rjt_vendorUnique = %u",
-			abt->fw.ba_rjt_vendorUnique);
-		ql_dbg(ql_dbg_async + ql_dbg_mbx, vha, 0xf09e,
-		       "ba_rjt_reasonCodeExpl = %u, ba_rjt_reasonCode = %u\n",
-		       abt->fw.ba_rjt_reasonCodeExpl, abt->fw.ba_rjt_reasonCode);
+		    "BA_RJT was received for the ABTS rjt_vendorUnique=%u, ba_rjt_reasonCodeExpl=%u, ba_rjt_reasonCode=%u\n",
+		    rjt_vendor_unique, rjt_reason_expl, rjt_reason_code);
 		break;
 
 	case CS_COMPLETE:
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 6c951839507b..d9141d62bbb5 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8358,6 +8358,7 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(ms_iocb_entry_t) != 64);
 	BUILD_BUG_ON(sizeof(request_t) != 64);
 	BUILD_BUG_ON(sizeof(struct abort_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct abort_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct abort_iocb_entry_fx00) != 64);
 	BUILD_BUG_ON(sizeof(struct abts_entry_24xx) != 64);
 	BUILD_BUG_ON(sizeof(struct access_chip_84xx) != 64);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 34/88] scsi: qla2xxx: Enhance ABTS processing for 29xx series
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (32 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 33/88] scsi: qla2xxx: Add abort command " Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:46   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 35/88] scsi: qla2xxx: Update VP control IOCB handling " Nilesh Javali
                   ` (53 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Use extended ABTS entry structures (abts_entry_24xx_ext) for 29xx
series adapters to properly handle the larger 128-byte IOCB format.

Introduce type-generic macros (QLA_LOG_ABTS_RCV, QLA_BUILD_ABTS_BA_ACC,
QLA_LOG_ISSUE_ABTS_RSP) that leverage the shared field names between
abts_entry_24xx and abts_entry_24xx_ext to avoid code duplication.
Branch on IS_QLA29XX() for receive logging, exchange termination, and
BA_ACC response construction, with each path passing the correctly
typed pointer to the shared macros. The sof_type handling difference
(direct for 29xx bitfield vs & 0xf0 mask for legacy) is parameterized
through the sof_val macro argument.

Add BUILD_BUG_ON size check for struct abts_entry_24xx_ext.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_isr.c | 167 ++++++++++++++++++++-------------
 drivers/scsi/qla2xxx/qla_os.c  |   1 +
 2 files changed, 104 insertions(+), 64 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index dd4416a3bf3a..774ccf6b2adc 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -72,15 +72,89 @@ static inline void display_Laser_info(scsi_qla_host_t *vha,
 		       mb3);
 }
 
+/*
+ * Type-generic helpers for qla24xx_process_abts().
+ * Both abts_entry_24xx and abts_entry_24xx_ext share field names for all
+ * accessed fields, so these macros expand correctly for either type.
+ */
+#define QLA_LOG_ABTS_RCV(vha, abts_ptr) do {				\
+	ql_log(ql_log_warn, (vha), 0x0287,				\
+	    "Processing ABTS xchg=%#x oxid=%#x rxid=%#x "		\
+	    "seqid=%#x seqcnt=%#x\n",					\
+	    (abts_ptr)->rx_xch_addr_to_abort, (abts_ptr)->ox_id,	\
+	    (abts_ptr)->rx_id, (abts_ptr)->seq_id,			\
+	    (abts_ptr)->seq_cnt);					\
+	ql_dbg(ql_dbg_init + ql_dbg_verbose, (vha), 0x0287,		\
+	    "-------- ABTS RCV -------\n");				\
+	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, (vha), 0x0287,	\
+	    (uint8_t *)(abts_ptr), sizeof(*(abts_ptr)));		\
+} while (0)
+
+#define QLA_BUILD_ABTS_BA_ACC(rsp, src, sof_val, fctl) do {		\
+	memset((rsp), 0, sizeof(*(rsp)));				\
+	(rsp)->entry_type = ABTS_RSP_TYPE;				\
+	(rsp)->entry_count = 1;						\
+	(rsp)->nport_handle = (src)->nport_handle;			\
+	(rsp)->vp_idx = (src)->vp_idx;					\
+	(rsp)->sof_type = (sof_val);					\
+	(rsp)->rx_xch_addr = (src)->rx_xch_addr;			\
+	(rsp)->d_id[0] = (src)->s_id[0];				\
+	(rsp)->d_id[1] = (src)->s_id[1];				\
+	(rsp)->d_id[2] = (src)->s_id[2];				\
+	(rsp)->r_ctl = FC_ROUTING_BLD | FC_R_CTL_BLD_BA_ACC;		\
+	(rsp)->s_id[0] = (src)->d_id[0];				\
+	(rsp)->s_id[1] = (src)->d_id[1];				\
+	(rsp)->s_id[2] = (src)->d_id[2];				\
+	(rsp)->cs_ctl = (src)->cs_ctl;					\
+	(fctl) = ~((src)->f_ctl[2] | 0x7F) << 16 |			\
+	    FC_F_CTL_LAST_SEQ | FC_F_CTL_END_SEQ | FC_F_CTL_SEQ_INIT;	\
+	(rsp)->f_ctl[0] = (fctl) >> 0 & 0xff;				\
+	(rsp)->f_ctl[1] = (fctl) >> 8 & 0xff;				\
+	(rsp)->f_ctl[2] = (fctl) >> 16 & 0xff;				\
+	(rsp)->type = FC_TYPE_BLD;					\
+	(rsp)->rx_id = (src)->rx_id;					\
+	(rsp)->ox_id = (src)->ox_id;					\
+	(rsp)->payload.ba_acc.aborted_rx_id = (src)->rx_id;		\
+	(rsp)->payload.ba_acc.aborted_ox_id = (src)->ox_id;		\
+	(rsp)->payload.ba_acc.high_seq_cnt = cpu_to_le16(~0);		\
+	(rsp)->rx_xch_addr_to_abort = (src)->rx_xch_addr_to_abort;	\
+} while (0)
+
+#define QLA_LOG_ISSUE_ABTS_RSP(vha, rsp, dma, rval) do {		\
+	ql_dbg(ql_dbg_init, (vha), 0x028b,				\
+	    "Sending BA ACC response to ABTS %#x...\n",			\
+	    (rsp)->rx_xch_addr_to_abort);				\
+	ql_dbg(ql_dbg_init + ql_dbg_verbose, (vha), 0x028b,		\
+	    "-------- ELS RSP -------\n");				\
+	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, (vha), 0x028b,	\
+	    (uint8_t *)(rsp), sizeof(*(rsp)));				\
+	(rval) = qla2x00_issue_iocb((vha), (rsp), (dma), 0);		\
+	if (rval) {							\
+		ql_log(ql_log_warn, (vha), 0x028c,			\
+		    "%s: iocb failed to execute -> %x\n",		\
+		    __func__, (rval));					\
+	} else if ((rsp)->comp_status) {				\
+		ql_log(ql_log_warn, (vha), 0x028d,			\
+		    "%s: iocb failed to complete -> "			\
+		    "completion=%#x subcode=(%#x,%#x)\n",		\
+		    __func__, (rsp)->comp_status,			\
+		    (rsp)->payload.error.subcode1,			\
+		    (rsp)->payload.error.subcode2);			\
+	} else {							\
+		ql_dbg(ql_dbg_init, (vha), 0x028ea,			\
+		    "%s: done.\n", __func__);				\
+	}								\
+} while (0)
+
 static void
 qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
 {
-	struct abts_entry_24xx *abts =
-	    (struct abts_entry_24xx *)&pkt->iocb;
 	struct qla_hw_data *ha = vha->hw;
+	struct abts_entry_24xx *abts = NULL;
+	struct abts_entry_24xx_ext *abts_ext = NULL;
 	struct els_entry_24xx *rsp_els;
-	struct abts_entry_24xx *abts_rsp;
 	dma_addr_t dma;
+	__le32 rx_xch_addr_to_abort;
 	uint32_t fctl;
 	int rval;
 	void *rsp_pkt;
@@ -88,19 +162,17 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
 
 	ql_dbg(ql_dbg_init, vha, 0x0286, "%s: entered.\n", __func__);
 
-	ql_log(ql_log_warn, vha, 0x0287,
-	    "Processing ABTS xchg=%#x oxid=%#x rxid=%#x seqid=%#x seqcnt=%#x\n",
-	    abts->rx_xch_addr_to_abort, abts->ox_id, abts->rx_id,
-	    abts->seq_id, abts->seq_cnt);
-	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0287,
-	    "-------- ABTS RCV -------\n");
-	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0287,
-	    (uint8_t *)abts, sizeof(*abts));
-
-	if (IS_QLA29XX(ha))
+	if (IS_QLA29XX(ha)) {
+		abts_ext = (struct abts_entry_24xx_ext *)&pkt->iocb;
+		QLA_LOG_ABTS_RCV(vha, abts_ext);
 		rsp_sz = sizeof(struct els_entry_24xx_ext);
-	else
+		rx_xch_addr_to_abort = abts_ext->rx_xch_addr_to_abort;
+	} else {
+		abts = (struct abts_entry_24xx *)&pkt->iocb;
+		QLA_LOG_ABTS_RCV(vha, abts);
 		rsp_sz = sizeof(struct els_entry_24xx);
+		rx_xch_addr_to_abort = abts->rx_xch_addr_to_abort;
+	}
 
 	rsp_pkt = dma_alloc_coherent(&ha->pdev->dev, rsp_sz, &dma,
 	    GFP_KERNEL);
@@ -116,11 +188,11 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
 	rsp_els->entry_type = ELS_IOCB_TYPE;
 	rsp_els->entry_count = 1;
 	rsp_els->nport_handle = cpu_to_le16(~0);
-	rsp_els->rx_xchg_address = abts->rx_xch_addr_to_abort;
+	rsp_els->rx_xchg_address = rx_xch_addr_to_abort;
 	rsp_els->control_flags = cpu_to_le16(EPD_RX_XCHG);
 	ql_dbg(ql_dbg_init, vha, 0x0283,
 	    "Sending ELS Response to terminate exchange %#x...\n",
-	    abts->rx_xch_addr_to_abort);
+	    rx_xch_addr_to_abort);
 	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0283,
 	    "-------- ELS RSP -------\n");
 	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0283,
@@ -140,60 +212,27 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
 	}
 
 	/* send ABTS response */
-	abts_rsp = rsp_pkt;
-	memset(abts_rsp, 0, sizeof(*abts_rsp));
-	abts_rsp->entry_type = ABTS_RSP_TYPE;
-	abts_rsp->entry_count = 1;
-	abts_rsp->nport_handle = abts->nport_handle;
-	abts_rsp->vp_idx = abts->vp_idx;
-	abts_rsp->sof_type = abts->sof_type & 0xf0;
-	abts_rsp->rx_xch_addr = abts->rx_xch_addr;
-	abts_rsp->d_id[0] = abts->s_id[0];
-	abts_rsp->d_id[1] = abts->s_id[1];
-	abts_rsp->d_id[2] = abts->s_id[2];
-	abts_rsp->r_ctl = FC_ROUTING_BLD | FC_R_CTL_BLD_BA_ACC;
-	abts_rsp->s_id[0] = abts->d_id[0];
-	abts_rsp->s_id[1] = abts->d_id[1];
-	abts_rsp->s_id[2] = abts->d_id[2];
-	abts_rsp->cs_ctl = abts->cs_ctl;
-	/* include flipping bit23 in fctl */
-	fctl = ~(abts->f_ctl[2] | 0x7F) << 16 |
-	    FC_F_CTL_LAST_SEQ | FC_F_CTL_END_SEQ | FC_F_CTL_SEQ_INIT;
-	abts_rsp->f_ctl[0] = fctl >> 0 & 0xff;
-	abts_rsp->f_ctl[1] = fctl >> 8 & 0xff;
-	abts_rsp->f_ctl[2] = fctl >> 16 & 0xff;
-	abts_rsp->type = FC_TYPE_BLD;
-	abts_rsp->rx_id = abts->rx_id;
-	abts_rsp->ox_id = abts->ox_id;
-	abts_rsp->payload.ba_acc.aborted_rx_id = abts->rx_id;
-	abts_rsp->payload.ba_acc.aborted_ox_id = abts->ox_id;
-	abts_rsp->payload.ba_acc.high_seq_cnt = cpu_to_le16(~0);
-	abts_rsp->rx_xch_addr_to_abort = abts->rx_xch_addr_to_abort;
-	ql_dbg(ql_dbg_init, vha, 0x028b,
-	    "Sending BA ACC response to ABTS %#x...\n",
-	    abts->rx_xch_addr_to_abort);
-	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x028b,
-	    "-------- ELS RSP -------\n");
-	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x028b,
-	    (uint8_t *)abts_rsp, sizeof(*abts_rsp));
-	rval = qla2x00_issue_iocb(vha, abts_rsp, dma, 0);
-	if (rval) {
-		ql_log(ql_log_warn, vha, 0x028c,
-		    "%s: iocb failed to execute -> %x\n", __func__, rval);
-	} else if (abts_rsp->comp_status) {
-		ql_log(ql_log_warn, vha, 0x028d,
-		    "%s: iocb failed to complete -> completion=%#x subcode=(%#x,%#x)\n",
-		    __func__, abts_rsp->comp_status,
-		    abts_rsp->payload.error.subcode1,
-		    abts_rsp->payload.error.subcode2);
+	if (IS_QLA29XX(ha)) {
+		struct abts_entry_24xx_ext *rsp_ext = rsp_pkt;
+
+		QLA_BUILD_ABTS_BA_ACC(rsp_ext, abts_ext,
+		    abts_ext->sof_type, fctl);
+		QLA_LOG_ISSUE_ABTS_RSP(vha, rsp_ext, dma, rval);
 	} else {
-		ql_dbg(ql_dbg_init, vha, 0x028ea,
-		    "%s: done.\n", __func__);
+		struct abts_entry_24xx *abts_rsp = rsp_pkt;
+
+		QLA_BUILD_ABTS_BA_ACC(abts_rsp, abts,
+		    abts->sof_type & 0xf0, fctl);
+		QLA_LOG_ISSUE_ABTS_RSP(vha, abts_rsp, dma, rval);
 	}
 
 	dma_free_coherent(&ha->pdev->dev, rsp_sz, rsp_pkt, dma);
 }
 
+#undef QLA_LOG_ABTS_RCV
+#undef QLA_BUILD_ABTS_BA_ACC
+#undef QLA_LOG_ISSUE_ABTS_RSP
+
 /**
  * __qla_consume_iocb - this routine is used to tell fw driver has processed
  *   or consumed the head IOCB along with the continuation IOCB's from the
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index d9141d62bbb5..5f11c46821c1 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8361,6 +8361,7 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(struct abort_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct abort_iocb_entry_fx00) != 64);
 	BUILD_BUG_ON(sizeof(struct abts_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct abts_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct access_chip_84xx) != 64);
 	BUILD_BUG_ON(sizeof(struct access_chip_rsp_84xx) != 64);
 	BUILD_BUG_ON(sizeof(struct cmd_bidir) != 64);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 35/88] scsi: qla2xxx: Update VP control IOCB handling for 29xx series
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (33 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 34/88] scsi: qla2xxx: Enhance ABTS processing " Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 36/88] scsi: qla2xxx: Add build-time size check for VP config IOCB layout Nilesh Javali
                   ` (52 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Update VP control IOCB command and response handling to support the
29xx series adapters, which use the 128-byte vp_ctrl_entry_24xx_ext
layout.

Change the qla25xx_ctrlvp_iocb() and qla_ctrlvp_completed() function
signatures from typed struct pointers to void *, since callers already
pass a generic ring-slot pointer.  Both the standard 64-byte
vp_ctrl_entry_24xx and the 128-byte vp_ctrl_entry_24xx_ext are
layout-identical for every field touched in these helpers (entry_type,
handle, entry_count, command, vp_count, vp_idx_map, entry_status,
comp_status, vp_idx_failed), so a single struct vp_ctrl_entry_24xx *
view handles both adapter families without an IS_QLA29XX() branch.

Add a BUILD_BUG_ON size check for the extended structure.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_iocb.c | 23 ++++++++++++++++-------
 drivers/scsi/qla2xxx/qla_isr.c  | 13 +++++++++----
 drivers/scsi/qla2xxx/qla_os.c   |  1 +
 3 files changed, 26 insertions(+), 11 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 7d0392074760..05d480622db5 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -4000,22 +4000,31 @@ qla_nvme_ls(srb_t *sp, struct pt_ls4_request *cmd_pkt)
 }
 
 static void
-qla25xx_ctrlvp_iocb(srb_t *sp, struct vp_ctrl_entry_24xx *vce)
+qla25xx_ctrlvp_iocb(srb_t *sp, void *pkt)
 {
+	/*
+	 * vp_ctrl_entry_24xx_ext is layout-identical to vp_ctrl_entry_24xx
+	 * for all fields touched here (entry_type, handle, entry_count,
+	 * command, vp_count, vp_idx_map) -- they all sit at the same
+	 * offsets and types in both structs, and the ext layout merely
+	 * tacks on flags/id/hopct/reserved at offset 32+.  So no
+	 * IS_QLA29XX(ha) dispatch is needed on the issue path.
+	 */
+	struct vp_ctrl_entry_24xx *vce = pkt;
 	int map, pos;
 
-	vce->entry_type = VP_CTRL_IOCB_TYPE;
-	vce->handle = sp->handle;
-	vce->entry_count = 1;
-	vce->command = cpu_to_le16(sp->u.iocb_cmd.u.ctrlvp.cmd);
-	vce->vp_count = cpu_to_le16(1);
-
 	/*
 	 * index map in firmware starts with 1; decrement index
 	 * this is ok as we never use index 0
 	 */
 	map = (sp->u.iocb_cmd.u.ctrlvp.vp_index - 1) / 8;
 	pos = (sp->u.iocb_cmd.u.ctrlvp.vp_index - 1) & 7;
+
+	vce->entry_type = VP_CTRL_IOCB_TYPE;
+	vce->handle = sp->handle;
+	vce->entry_count = 1;
+	vce->command = cpu_to_le16(sp->u.iocb_cmd.u.ctrlvp.cmd);
+	vce->vp_count = cpu_to_le16(1);
 	vce->vp_idx_map[map] |= 1 << pos;
 }
 
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 774ccf6b2adc..7d2b6d135dc8 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -3000,13 +3000,19 @@ static void qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
 }
 
 static void qla_ctrlvp_completed(scsi_qla_host_t *vha, struct req_que *req,
-    struct vp_ctrl_entry_24xx *vce)
+				 void *pkt)
 {
 	const char func[] = "CTRLVP-IOCB";
+	/*
+	 * vp_ctrl_entry_24xx_ext overlays vp_ctrl_entry_24xx for all
+	 * fields read here (entry_status, comp_status, vp_idx_failed),
+	 * so the read goes through one struct vp_ctrl_entry_24xx * view.
+	 */
+	struct vp_ctrl_entry_24xx *vce = pkt;
 	srb_t *sp;
 	int rval = QLA_SUCCESS;
 
-	sp = qla2x00_get_sp_from_handle(vha, func, req, vce);
+	sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
 	if (!sp)
 		return;
 
@@ -4227,8 +4233,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 			    (struct mbx_24xx_entry *)pkt);
 			break;
 		case VP_CTRL_IOCB_TYPE:
-			qla_ctrlvp_completed(vha, rsp->req,
-			    (struct vp_ctrl_entry_24xx *)pkt);
+			qla_ctrlvp_completed(vha, rsp->req, pkt);
 			break;
 		case PUREX_IOCB_TYPE:
 			if (IS_QLA29XX(ha)) {
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 5f11c46821c1..ad070c0bf7db 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8437,6 +8437,7 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(struct vf_evfp_entry_24xx) != 56);
 	BUILD_BUG_ON(sizeof(struct vp_config_entry_24xx) != 64);
 	BUILD_BUG_ON(sizeof(struct vp_ctrl_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct vp_ctrl_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct vp_rpt_id_entry_24xx) != 64);
 	BUILD_BUG_ON(sizeof(sts21_entry_t) != 64);
 	BUILD_BUG_ON(sizeof(sts22_entry_t) != 64);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 36/88] scsi: qla2xxx: Add build-time size check for VP config IOCB layout
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (34 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 35/88] scsi: qla2xxx: Update VP control IOCB handling " Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 37/88] scsi: qla2xxx: Add size check for extended VP report ID entry Nilesh Javali
                   ` (51 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Add a BUILD_BUG_ON for struct vp_config_entry_24xx_ext to verify its
128-byte size at compile time alongside the existing 64-byte check for
struct vp_config_entry_24xx.

Document in qla24xx_modify_vp_config() that the ext variant overlays
the base 24xx layout for the first 64 bytes (all fields this helper
reads and writes), so the IOCB can be built through a single struct
vp_config_entry_24xx pointer regardless of the adapter's IOCB stride.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_mbx.c | 8 +++++++-
 drivers/scsi/qla2xxx/qla_os.c  | 1 +
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 0d7aa6fce007..3ebda35dd584 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -4364,6 +4364,13 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 
+	/*
+	 * vp_config_entry_24xx_ext overlays vp_config_entry_24xx for the
+	 * full 64-byte 24xx layout (the ext variant merely appends fields
+	 * at offset 64+ which this helper never touches), so the IOCB is
+	 * built and inspected through a single struct vp_config_entry_24xx
+	 * pointer regardless of adapter stride.
+	 */
 	vpmod->entry_type = VP_CONFIG_IOCB_TYPE;
 	vpmod->entry_count = 1;
 	vpmod->command = VCT_COMMAND_MOD_ENABLE_VPS;
@@ -4392,7 +4399,6 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
 		    le16_to_cpu(vpmod->comp_status));
 		rval = QLA_FUNCTION_FAILED;
 	} else {
-		/* EMPTY */
 		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c0,
 		    "Done %s.\n", __func__);
 		fc_vport_set_state(vha->fc_vport, FC_VPORT_INITIALIZING);
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index ad070c0bf7db..8e5d49abead4 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8436,6 +8436,7 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(struct verify_chip_rsp_84xx) != 52);
 	BUILD_BUG_ON(sizeof(struct vf_evfp_entry_24xx) != 56);
 	BUILD_BUG_ON(sizeof(struct vp_config_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct vp_config_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct vp_ctrl_entry_24xx) != 64);
 	BUILD_BUG_ON(sizeof(struct vp_ctrl_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct vp_rpt_id_entry_24xx) != 64);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 37/88] scsi: qla2xxx: Add size check for extended VP report ID entry
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (35 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 36/88] scsi: qla2xxx: Add build-time size check for VP config IOCB layout Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:46   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 38/88] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series Nilesh Javali
                   ` (50 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Add reserved_end[64] padding to bring the struct to 128 bytes, matching
the hardware IOCB stride.  Change qla24xx_report_id_acquisition() to
accept a void pointer and extract vp_idx and vp_status from the extended
structure on 29xx series adapters, maintaining data integrity for the
larger IOCB format.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_fw29.h |  1 +
 drivers/scsi/qla2xxx/qla_gbl.h  |  3 +--
 drivers/scsi/qla2xxx/qla_isr.c  |  3 +--
 drivers/scsi/qla2xxx/qla_mbx.c  | 32 +++++++++++++++++++++-----------
 drivers/scsi/qla2xxx/qla_os.c   |  1 +
 5 files changed, 25 insertions(+), 15 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_fw29.h b/drivers/scsi/qla2xxx/qla_fw29.h
index 088a220472a5..a4aa5bacb171 100644
--- a/drivers/scsi/qla2xxx/qla_fw29.h
+++ b/drivers/scsi/qla2xxx/qla_fw29.h
@@ -693,6 +693,7 @@ struct vp_rpt_id_entry_24xx_ext {
 			uint8_t remote_nport_id[4];
 		} f2;
 	} u;
+	uint8_t reserved_end[64];
 };
 
 /*
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index bba324febf09..cb48b9e15a44 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -266,8 +266,7 @@ extern int qla24xx_modify_vp_config(scsi_qla_host_t *);
 extern int qla2x00_send_change_request(scsi_qla_host_t *, uint16_t, uint16_t);
 extern void qla2x00_vp_stop_timer(scsi_qla_host_t *);
 extern int qla24xx_configure_vhba (scsi_qla_host_t *);
-extern void qla24xx_report_id_acquisition(scsi_qla_host_t *,
-    struct vp_rpt_id_entry_24xx *);
+extern void qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt);
 extern void qla2x00_do_dpc_all_vps(scsi_qla_host_t *);
 extern int qla24xx_vport_create_req_sanity_check(struct fc_vport *);
 extern scsi_qla_host_t *qla24xx_create_vhost(struct fc_vport *);
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 7d2b6d135dc8..c6e2323518f7 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -4174,8 +4174,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 			    qla_sts_cont_data_size(rsp->hw));
 			break;
 		case VP_RPT_ID_IOCB_TYPE:
-			qla24xx_report_id_acquisition(vha,
-			    (struct vp_rpt_id_entry_24xx *)pkt);
+			qla24xx_report_id_acquisition(vha, pkt);
 			break;
 		case LOGINOUT_PORT_IOCB_TYPE:
 			qla24xx_logio_entry(vha, rsp->req, pkt);
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 3ebda35dd584..cec308811d9e 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -4086,15 +4086,18 @@ qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
 }
 
 void
-qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
-	struct vp_rpt_id_entry_24xx *rptid_entry)
+qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
 {
 	struct qla_hw_data *ha = vha->hw;
+	struct vp_rpt_id_entry_24xx *rptid_entry = pkt;
+	struct vp_rpt_id_entry_24xx_ext *rptid_entry_ext = pkt;
 	scsi_qla_host_t *vp = NULL;
 	unsigned long   flags;
 	int found;
 	port_id_t id;
 	struct fc_port *fcport;
+	u16 vp_idx;
+	u8 vp_status;
 
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b6,
 	    "Entered %s.\n", __func__);
@@ -4102,6 +4105,14 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
 	if (rptid_entry->entry_status != 0)
 		return;
 
+	if (IS_QLA29XX(ha)) {
+		vp_idx = rptid_entry_ext->vp_idx;
+		vp_status = rptid_entry_ext->vp_status;
+	} else {
+		vp_idx = rptid_entry->vp_idx;
+		vp_status = rptid_entry->vp_status;
+	}
+
 	id.b.domain = rptid_entry->port_id[2];
 	id.b.area   = rptid_entry->port_id[1];
 	id.b.al_pa  = rptid_entry->port_id[0];
@@ -4124,9 +4135,8 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
 	} else if (rptid_entry->format == 1) {
 		/* fabric */
 		ql_dbg(ql_dbg_async, vha, 0x10b9,
-		    "Format 1: VP[%d] enabled - status %d - with "
-		    "port id %02x%02x%02x.\n", rptid_entry->vp_idx,
-			rptid_entry->vp_status,
+		    "Format 1: VP[%d] enabled - status %d - with port id %02x%02x%02x.\n",
+		    vp_idx, vp_status,
 		    rptid_entry->port_id[2], rptid_entry->port_id[1],
 		    rptid_entry->port_id[0]);
 		ql_dbg(ql_dbg_async, vha, 0x5075,
@@ -4223,8 +4233,8 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
 		/* buffer to buffer credit flag */
 		vha->flags.bbcr_enable = (rptid_entry->u.f1.bbcr & 0xf) != 0;
 
-		if (rptid_entry->vp_idx == 0) {
-			if (rptid_entry->vp_status == VP_STAT_COMPL) {
+		if (vp_idx == 0) {
+			if (vp_status == VP_STAT_COMPL) {
 				/* FA-WWN is only for physical port */
 				if (qla_ini_mode_enabled(vha) &&
 				    ha->flags.fawwpn_enabled &&
@@ -4241,18 +4251,18 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
 			set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
 			set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
 		} else {
-			if (rptid_entry->vp_status != VP_STAT_COMPL &&
-				rptid_entry->vp_status != VP_STAT_ID_CHG) {
+			if (vp_status != VP_STAT_COMPL &&
+				vp_status != VP_STAT_ID_CHG) {
 				ql_dbg(ql_dbg_mbx, vha, 0x10ba,
 				    "Could not acquire ID for VP[%d].\n",
-				    rptid_entry->vp_idx);
+				    vp_idx);
 				return;
 			}
 
 			found = 0;
 			spin_lock_irqsave(&ha->vport_slock, flags);
 			list_for_each_entry(vp, &ha->vp_list, list) {
-				if (rptid_entry->vp_idx == vp->vp_idx) {
+				if (vp_idx == vp->vp_idx) {
 					found = 1;
 					break;
 				}
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 8e5d49abead4..f539190dd504 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8440,6 +8440,7 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(struct vp_ctrl_entry_24xx) != 64);
 	BUILD_BUG_ON(sizeof(struct vp_ctrl_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct vp_rpt_id_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct vp_rpt_id_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(sts21_entry_t) != 64);
 	BUILD_BUG_ON(sizeof(sts22_entry_t) != 64);
 	BUILD_BUG_ON(sizeof(sts_cont_entry_t) != 64);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 38/88] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (36 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 37/88] scsi: qla2xxx: Add size check for extended VP report ID entry Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:45   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 39/88] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support Nilesh Javali
                   ` (49 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

From: Manish Rangankar <mrangankar@marvell.com>

Extend the LS4 pass-through IOCB handling to support the 128-byte
pt_ls4_request_ext layout used by 29xx series adapters.  The extension
grows inline DSD capacity from 2 to 5 entries.  Function signatures are
widened to void * so both layouts can be passed without casts.

pt_ls4_request_ext overlays pt_ls4_request through exchange_address
(offsets 0-27 are byte-identical), so common-header writes go through a
single struct pt_ls4_request * view; only the divergent fields
(vp_index width, tx_/rx_byte_count offset, dsd[] base) are branched.

Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_fw29.h | 37 +++++++++++++++++
 drivers/scsi/qla2xxx/qla_iocb.c | 73 ++++++++++++++++++++++-----------
 drivers/scsi/qla2xxx/qla_isr.c  |  9 ++--
 drivers/scsi/qla2xxx/qla_nvme.c | 64 +++++++++++++++++++----------
 drivers/scsi/qla2xxx/qla_nvme.h |  4 +-
 drivers/scsi/qla2xxx/qla_os.c   |  1 +
 6 files changed, 137 insertions(+), 51 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_fw29.h b/drivers/scsi/qla2xxx/qla_fw29.h
index a4aa5bacb171..600a40d8bd5f 100644
--- a/drivers/scsi/qla2xxx/qla_fw29.h
+++ b/drivers/scsi/qla2xxx/qla_fw29.h
@@ -295,6 +295,43 @@ struct ct_entry_24xx_ext {
 	struct dsd64 dsd[NUM_CT_DSDS];	/* Data Segment Descriptors */
 };
 
+/*
+ * 29xx extended Link Service pass-through request IOCB (128 bytes).
+ *
+ * Same wire purpose as the 64-byte struct pt_ls4_request used on 24xx-class
+ * adapters, but laid out for the 128-byte 29xx request ring:
+ *   - vp_index widened to __le16 (bits [8:0] meaningful, see
+ *     CMD_EXT_VP_INDEX_MASK).
+ *   - reserved area expanded to 32 bytes between exchange_address and
+ *     rx_byte_count.
+ *   - inline DSD capacity grown from 2 to 5.
+ * Header through 'tx_dseg_count' (offset 14) and the control_flags /
+ * exchange_address fields keep the same offsets as struct pt_ls4_request,
+ * so common code can populate them via either type once IS_QLA29XX(ha) is
+ * branched for the layout-divergent fields.
+ */
+#define NUM_PT_LS4_EXT_DSDS	5
+struct pt_ls4_request_ext {
+	uint8_t entry_type;
+	uint8_t entry_count;
+	uint8_t sys_define;
+	uint8_t entry_status;
+	uint32_t handle;
+	__le16	status;
+	__le16	nport_handle;
+	__le16	tx_dseg_count;
+	__le16	vp_index;	/* VP Index 9 bits; see CMD_EXT_VP_INDEX_MASK */
+	__le16	timeout;
+	__le16	control_flags;	/* CF_LS4_* (see struct pt_ls4_request) */
+	__le16	rx_dseg_count;
+	__le16	rsvd2;
+	__le32	exchange_address;
+	uint8_t rsvd3[32];
+	__le32	rx_byte_count;
+	__le32	tx_byte_count;
+	struct dsd64 dsd[NUM_PT_LS4_EXT_DSDS];
+};
+
 /*
  * ISP queue - PUREX IOCB entry structure definition
  */
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 05d480622db5..59648a9229ac 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -3965,38 +3965,65 @@ static void qla2x00_send_notify_ack_iocb(srb_t *sp,
 }
 
 /*
- * Build NVME LS request
+ * Build NVME LS request.
+ *
+ * pt_ls4_request_ext overlays pt_ls4_request through exchange_address
+ * (offsets 0-27 are byte-identical), so the common-header writes go
+ * through one struct pt_ls4_request * view.  The ext layout has a wider
+ * __le16 vp_index and places rx_/tx_byte_count and dsd[] at different
+ * offsets, so those assignments diverge per stride.
  */
 static void
-qla_nvme_ls(srb_t *sp, struct pt_ls4_request *cmd_pkt)
+qla_nvme_ls(srb_t *sp, void *cmd_pkt)
 {
-	struct srb_iocb *nvme;
+	struct srb_iocb *nvme = &sp->u.iocb_cmd;
+	struct qla_hw_data *ha = sp->vha->hw;
+	struct pt_ls4_request *pkt = cmd_pkt;
 
-	nvme = &sp->u.iocb_cmd;
-	cmd_pkt->entry_type = PT_LS4_REQUEST;
-	cmd_pkt->entry_count = 1;
-	cmd_pkt->timeout = cpu_to_le16(nvme->u.nvme.timeout_sec);
-	cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
+	pkt->entry_type = PT_LS4_REQUEST;
+	pkt->entry_count = 1;
+	pkt->timeout = cpu_to_le16(nvme->u.nvme.timeout_sec);
+	pkt->tx_dseg_count = cpu_to_le16(1);
 
 	if (sp->unsol_rsp) {
-		cmd_pkt->control_flags =
-				cpu_to_le16(CF_LS4_RESPONDER << CF_LS4_SHIFT);
-		cmd_pkt->nport_handle = nvme->u.nvme.nport_handle;
-		cmd_pkt->exchange_address = nvme->u.nvme.exchange_address;
+		pkt->control_flags =
+			cpu_to_le16(CF_LS4_RESPONDER << CF_LS4_SHIFT);
+		pkt->nport_handle = nvme->u.nvme.nport_handle;
+		pkt->exchange_address = nvme->u.nvme.exchange_address;
 	} else {
-		cmd_pkt->control_flags =
-				cpu_to_le16(CF_LS4_ORIGINATOR << CF_LS4_SHIFT);
-		cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-		cmd_pkt->rx_dseg_count = cpu_to_le16(1);
-		cmd_pkt->rx_byte_count = nvme->u.nvme.rsp_len;
-		cmd_pkt->dsd[1].length  = nvme->u.nvme.rsp_len;
-		put_unaligned_le64(nvme->u.nvme.rsp_dma, &cmd_pkt->dsd[1].address);
+		pkt->control_flags =
+			cpu_to_le16(CF_LS4_ORIGINATOR << CF_LS4_SHIFT);
+		pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+		pkt->rx_dseg_count = cpu_to_le16(1);
 	}
 
-	cmd_pkt->tx_dseg_count = cpu_to_le16(1);
-	cmd_pkt->tx_byte_count = nvme->u.nvme.cmd_len;
-	cmd_pkt->dsd[0].length = nvme->u.nvme.cmd_len;
-	put_unaligned_le64(nvme->u.nvme.cmd_dma, &cmd_pkt->dsd[0].address);
+	if (IS_QLA29XX(ha)) {
+		struct pt_ls4_request_ext *ext = cmd_pkt;
+
+		ext->vp_index = cpu_to_le16(sp->fcport->vha->vp_idx);
+		ext->tx_byte_count = nvme->u.nvme.cmd_len;
+		ext->dsd[0].length = nvme->u.nvme.cmd_len;
+		put_unaligned_le64(nvme->u.nvme.cmd_dma,
+				   &ext->dsd[0].address);
+		if (!sp->unsol_rsp) {
+			ext->rx_byte_count = nvme->u.nvme.rsp_len;
+			ext->dsd[1].length = nvme->u.nvme.rsp_len;
+			put_unaligned_le64(nvme->u.nvme.rsp_dma,
+					   &ext->dsd[1].address);
+		}
+	} else {
+		pkt->vp_index = sp->fcport->vha->vp_idx;
+		pkt->tx_byte_count = nvme->u.nvme.cmd_len;
+		pkt->dsd[0].length = nvme->u.nvme.cmd_len;
+		put_unaligned_le64(nvme->u.nvme.cmd_dma,
+				   &pkt->dsd[0].address);
+		if (!sp->unsol_rsp) {
+			pkt->rx_byte_count = nvme->u.nvme.rsp_len;
+			pkt->dsd[1].length = nvme->u.nvme.rsp_len;
+			put_unaligned_le64(nvme->u.nvme.rsp_dma,
+					   &pkt->dsd[1].address);
+		}
+	}
 }
 
 static void
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index c6e2323518f7..3a9237376050 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -4034,7 +4034,7 @@ qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
 }
 
 void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha,
-    struct pt_ls4_request *pkt, struct req_que *req)
+			   void *pkt, struct req_que *req)
 {
 	srb_t *sp;
 	const char func[] = "LS4_IOCB";
@@ -4044,7 +4044,9 @@ void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha,
 	if (!sp)
 		return;
 
-	comp_status = le16_to_cpu(pkt->status);
+	/* status lives at the same offset (8) in both IOCB strides */
+	comp_status = le16_to_cpu(((struct pt_ls4_request *)pkt)->status);
+
 	sp->done(sp, comp_status);
 }
 
@@ -4210,8 +4212,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 			qlt_response_pkt_all_vps(vha, rsp, (response_t *)pkt);
 			break;
 		case PT_LS4_REQUEST:
-			qla24xx_nvme_ls4_iocb(vha, (struct pt_ls4_request *)pkt,
-			    rsp->req);
+			qla24xx_nvme_ls4_iocb(vha, pkt, rsp->req);
 			break;
 		case NOTIFY_ACK_TYPE:
 			if (((response_t *)pkt)->handle == QLA_TGT_SKIP_HANDLE)
diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index 8fa980db6ce4..0038b6274d44 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -1197,37 +1197,57 @@ static void qla_nvme_fc_format_rjt(void *buf, u8 ls_cmd, u8 reason,
 	rjt->rjt.vendor = vendor;
 }
 
+/*
+ * pt_ls4_request_ext overlays pt_ls4_request through exchange_address
+ * (offsets 0-27 are byte-identical), so the common-header writes go
+ * through one struct pt_ls4_request * view.  The ext layout has a wider
+ * __le16 vp_index and places tx_/rx_byte_count and dsd[] at different
+ * offsets, so those assignments diverge per stride.
+ */
 static void qla_nvme_lsrjt_pt_iocb(struct scsi_qla_host *vha,
-				   struct pt_ls4_request *lsrjt_iocb,
+				   void *lsrjt_iocb,
 				   struct qla_nvme_lsrjt_pt_arg *a)
 {
-	lsrjt_iocb->entry_type = PT_LS4_REQUEST;
-	lsrjt_iocb->entry_count = 1;
-	lsrjt_iocb->sys_define = 0;
-	lsrjt_iocb->entry_status = 0;
-	lsrjt_iocb->handle = QLA_SKIP_HANDLE;
-	lsrjt_iocb->nport_handle = a->nport_handle;
-	lsrjt_iocb->exchange_address = a->xchg_address;
-	lsrjt_iocb->vp_index = a->vp_idx;
-
-	lsrjt_iocb->control_flags = cpu_to_le16(a->control_flags);
-
-	put_unaligned_le64(a->tx_addr, &lsrjt_iocb->dsd[0].address);
-	lsrjt_iocb->dsd[0].length = cpu_to_le32(a->tx_byte_count);
-	lsrjt_iocb->tx_dseg_count = cpu_to_le16(1);
-	lsrjt_iocb->tx_byte_count = cpu_to_le32(a->tx_byte_count);
-
-	put_unaligned_le64(a->rx_addr, &lsrjt_iocb->dsd[1].address);
-	lsrjt_iocb->dsd[1].length = 0;
-	lsrjt_iocb->rx_dseg_count = 0;
-	lsrjt_iocb->rx_byte_count = 0;
+	struct qla_hw_data *ha = vha->hw;
+	struct pt_ls4_request *pkt = lsrjt_iocb;
+
+	pkt->entry_type = PT_LS4_REQUEST;
+	pkt->entry_count = 1;
+	pkt->sys_define = 0;
+	pkt->entry_status = 0;
+	pkt->handle = QLA_SKIP_HANDLE;
+	pkt->nport_handle = a->nport_handle;
+	pkt->exchange_address = a->xchg_address;
+	pkt->control_flags = cpu_to_le16(a->control_flags);
+	pkt->tx_dseg_count = cpu_to_le16(1);
+	pkt->rx_dseg_count = 0;
+
+	if (IS_QLA29XX(ha)) {
+		struct pt_ls4_request_ext *ext = lsrjt_iocb;
+
+		ext->vp_index = cpu_to_le16(a->vp_idx);
+		ext->tx_byte_count = cpu_to_le32(a->tx_byte_count);
+		ext->rx_byte_count = 0;
+		put_unaligned_le64(a->tx_addr, &ext->dsd[0].address);
+		ext->dsd[0].length = cpu_to_le32(a->tx_byte_count);
+		put_unaligned_le64(a->rx_addr, &ext->dsd[1].address);
+		ext->dsd[1].length = 0;
+	} else {
+		pkt->vp_index = a->vp_idx;
+		pkt->tx_byte_count = cpu_to_le32(a->tx_byte_count);
+		pkt->rx_byte_count = 0;
+		put_unaligned_le64(a->tx_addr, &pkt->dsd[0].address);
+		pkt->dsd[0].length = cpu_to_le32(a->tx_byte_count);
+		put_unaligned_le64(a->rx_addr, &pkt->dsd[1].address);
+		pkt->dsd[1].length = 0;
+	}
 }
 
 static int
 qla_nvme_ls_reject_iocb(struct scsi_qla_host *vha, struct qla_qpair *qp,
 			struct qla_nvme_lsrjt_pt_arg *a, bool is_xchg_terminate)
 {
-	struct pt_ls4_request *lsrjt_iocb;
+	void *lsrjt_iocb;
 
 	lsrjt_iocb = __qla2x00_alloc_iocbs(qp, NULL);
 	if (!lsrjt_iocb) {
diff --git a/drivers/scsi/qla2xxx/qla_nvme.h b/drivers/scsi/qla2xxx/qla_nvme.h
index a253ac55171b..e6a41d53cbe1 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.h
+++ b/drivers/scsi/qla2xxx/qla_nvme.h
@@ -144,7 +144,7 @@ struct pt_ls4_rx_unsol {
 int qla_nvme_register_hba(struct scsi_qla_host *);
 int  qla_nvme_register_remote(struct scsi_qla_host *, struct fc_port *);
 void qla_nvme_delete(struct scsi_qla_host *);
-void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *, struct pt_ls4_request *,
-    struct req_que *);
+void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha, void *pkt,
+			   struct req_que *req);
 void qla24xx_async_gffid_sp_done(struct srb *sp, int);
 #endif
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index f539190dd504..186c6c7a3944 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8402,6 +8402,7 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(struct nvram_24xx) != 512);
 	BUILD_BUG_ON(sizeof(struct nvram_81xx) != 512);
 	BUILD_BUG_ON(sizeof(struct pt_ls4_request) != 64);
+	BUILD_BUG_ON(sizeof(struct pt_ls4_request_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct pt_ls4_rx_unsol) != 64);
 	BUILD_BUG_ON(sizeof(struct purex_entry_24xx) != 64);
 	BUILD_BUG_ON(sizeof(struct purex_entry_24xx_ext) != 128);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 39/88] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (37 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 38/88] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:48   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 40/88] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking Nilesh Javali
                   ` (48 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

From: Manish Rangankar <mrangankar@marvell.com>

Extend qla2xxx BSG command handling to recognize QLA29xx adapters and
align feature availability with hardware capabilities.

Allow QLA29xx in paths previously restricted to QLA27xx/28xx:
  - Flash update capability queries (get/set)
  - BBCR data retrieval
  - D-port diagnostics
  - MPI and PEP version sysfs attributes

Restrict unsupported operations on QLA29xx:
  - Reject flash image status query (no active image tracking)
  - Block qla28xx_validate_flash_image()

Guard the qla27xx_get_active_image() call with an explicit IS_QLA27XX
|| IS_QLA28XX check so it is not reached from adapters that lack the
legacy active-image layout.

Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_attr.c |  4 ++--
 drivers/scsi/qla2xxx/qla_bsg.c  | 19 ++++++++++++-------
 drivers/scsi/qla2xxx/qla_def.h  |  2 +-
 drivers/scsi/qla2xxx/qla_mbx.c  |  2 +-
 4 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index c32930f9e9f9..19aa66b8ca52 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1542,7 +1542,7 @@ qla2x00_mpi_version_show(struct device *dev, struct device_attribute *attr,
 	struct qla_hw_data *ha = vha->hw;
 
 	if (!IS_QLA81XX(ha) && !IS_QLA8031(ha) && !IS_QLA8044(ha) &&
-	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return scnprintf(buf, PAGE_SIZE, "\n");
 
 	return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d (%x)\n",
@@ -1770,7 +1770,7 @@ qla2x00_pep_version_show(struct device *dev, struct device_attribute *attr,
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return scnprintf(buf, PAGE_SIZE, "\n");
 
 	return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d\n",
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 8a969174a261..fade3638d31c 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -1948,7 +1948,7 @@ static int qla2900_bsg_load_mpi(struct bsg_job *bsg_job)
 	bsg_job_done(bsg_job, bsg_reply->result,
 		     bsg_reply->reply_payload_rcv_len);
 
-	return rval;
+	return 0;
 }
 
 static int
@@ -2592,7 +2592,7 @@ qla27xx_get_flash_upd_cap(struct bsg_job *bsg_job)
 	struct qla_hw_data *ha = vha->hw;
 	struct qla_flash_update_caps cap;
 
-	if (!(IS_QLA27XX(ha)) && !IS_QLA28XX(ha))
+	if (!(IS_QLA27XX(ha)) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return -EPERM;
 
 	memset(&cap, 0, sizeof(cap));
@@ -2625,7 +2625,7 @@ qla27xx_set_flash_upd_cap(struct bsg_job *bsg_job)
 	uint64_t online_fw_attr = 0;
 	struct qla_flash_update_caps cap;
 
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return -EPERM;
 
 	memset(&cap, 0, sizeof(cap));
@@ -2673,7 +2673,7 @@ qla27xx_get_bbcr_data(struct bsg_job *bsg_job)
 	uint8_t domain, area, al_pa, state;
 	int rval;
 
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return -EPERM;
 
 	memset(&bbcr, 0, sizeof(bbcr));
@@ -2789,7 +2789,7 @@ qla2x00_do_dport_diagnostics(struct bsg_job *bsg_job)
 	struct qla_dport_diag *dd;
 
 	if (!IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw) &&
-	    !IS_QLA28XX(vha->hw))
+	    !IS_QLA28XX(vha->hw) && !IS_QLA29XX(vha->hw))
 		return -EPERM;
 
 	dd = kmalloc_obj(*dd);
@@ -2915,8 +2915,13 @@ qla2x00_get_flash_image_status(struct bsg_job *bsg_job)
 	struct qla_active_regions regions = { };
 	struct active_regions active_regions = { };
 
-	qla27xx_get_active_image(vha, &active_regions);
-	regions.global_image = active_regions.global;
+	if (IS_QLA29XX(ha))
+		return -EPERM;
+
+	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+		qla27xx_get_active_image(vha, &active_regions);
+		regions.global_image = active_regions.global;
+	}
 
 	if (IS_QLA27XX(ha))
 		regions.nvme_params = QLA27XX_PRIMARY_IMAGE;
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 3e2f1d8ba904..4fd2a28af7e4 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -4454,7 +4454,7 @@ struct qla_hw_data {
 #define IS_QLA27XX(ha)  (IS_QLA2071(ha) || IS_QLA2271(ha) || IS_QLA2261(ha))
 #define IS_QLA28XX(ha)	(IS_QLA2081(ha) || IS_QLA2281(ha))
 #define IS_QLA29XX(ha)	(IS_QLA2099(ha) || IS_QLA2299(ha) || \
-			 IS_QLA2091(ha) || IS_QLA2291(ha))
+				IS_QLA2091(ha) || IS_QLA2291(ha))
 
 #define IS_QLA24XX_TYPE(ha)     (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \
 				IS_QLA84XX(ha))
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index cec308811d9e..ce845afe3e16 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -6566,7 +6566,7 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha,
 	dma_addr_t dd_dma;
 
 	if (!IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw) &&
-	    !IS_QLA28XX(vha->hw))
+	    !IS_QLA28XX(vha->hw) && !IS_QLA29XX(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x119f,
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 40/88] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (38 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 39/88] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:50   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 41/88] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors Nilesh Javali
                   ` (47 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla25xx_free_req_que() and qla25xx_free_rsp_que() have two pre-existing
bugs exposed on the error path of qla25xx_create_{req,rsp}_que():

1. When dma_alloc_coherent() fails during queue creation, the error path
   calls the free function with req->ring / rsp->ring still NULL (from
   kzalloc).  The unconditional dma_free_coherent() with a NULL cpu_addr
   is undefined behavior and can panic.

2. The free functions clear req_qid_map / rsp_qid_map under vport_lock,
   but the create functions protect the same bitmaps with mq_lock.  This
   provides no mutual exclusion.  Additionally, the create error path
   clears the bit and releases mq_lock before calling the free function,
   creating a window where another thread can allocate the same que_id
   and have its ha->req_q_map entry clobbered by the subsequent lockless
   NULL assignment in the free function.

Fix by:
 - Guarding dma_free_coherent() with a NULL check on the ring pointer.
 - Using mq_lock (the lock held by all creators) in the free functions
   to atomically NULL the map entry and clear the bitmap bit.
 - Removing the now-redundant clear_bit blocks from the create error
   paths since the free functions handle it atomically.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_mid.c | 28 ++++++++++++----------------
 1 file changed, 12 insertions(+), 16 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index bb2521c052bf..7072af5b4217 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -576,16 +576,17 @@ qla25xx_free_req_que(struct scsi_qla_host *vha, struct req_que *req)
 	uint16_t que_id = req->id;
 	size_t req_entry_size = qla_req_entry_size(ha);
 
-	dma_free_coherent(&ha->pdev->dev,
-			  (req->length + 1) * req_entry_size,
-			  req->ring, req->dma);
+	if (req->ring)
+		dma_free_coherent(&ha->pdev->dev,
+				  (req->length + 1) * req_entry_size,
+				  req->ring, req->dma);
 	req->ring = NULL;
 	req->dma = 0;
 	if (que_id) {
+		mutex_lock(&ha->mq_lock);
 		ha->req_q_map[que_id] = NULL;
-		mutex_lock(&ha->vport_lock);
 		clear_bit(que_id, ha->req_qid_map);
-		mutex_unlock(&ha->vport_lock);
+		mutex_unlock(&ha->mq_lock);
 	}
 	kfree(req->outstanding_cmds);
 	kfree(req);
@@ -605,16 +606,17 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
 		rsp->msix->handle = NULL;
 	}
 
-	dma_free_coherent(&ha->pdev->dev,
-			  (rsp->length + 1) * rsp_entry_size,
-			  rsp->ring, rsp->dma);
+	if (rsp->ring)
+		dma_free_coherent(&ha->pdev->dev,
+				  (rsp->length + 1) * rsp_entry_size,
+				  rsp->ring, rsp->dma);
 	rsp->ring = NULL;
 	rsp->dma = 0;
 	if (que_id) {
+		mutex_lock(&ha->mq_lock);
 		ha->rsp_q_map[que_id] = NULL;
-		mutex_lock(&ha->vport_lock);
 		clear_bit(que_id, ha->rsp_qid_map);
-		mutex_unlock(&ha->vport_lock);
+		mutex_unlock(&ha->mq_lock);
 	}
 	kfree(rsp);
 }
@@ -820,9 +822,6 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
 		if (ret != QLA_SUCCESS) {
 			ql_log(ql_log_fatal, base_vha, 0x00df,
 			    "%s failed.\n", __func__);
-			mutex_lock(&ha->mq_lock);
-			clear_bit(que_id, ha->req_qid_map);
-			mutex_unlock(&ha->mq_lock);
 			goto que_failed;
 		}
 		vha->flags.qpairs_req_created = 1;
@@ -942,9 +941,6 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
 		if (ret != QLA_SUCCESS) {
 			ql_log(ql_log_fatal, base_vha, 0x00e7,
 			    "%s failed.\n", __func__);
-			mutex_lock(&ha->mq_lock);
-			clear_bit(que_id, ha->rsp_qid_map);
-			mutex_unlock(&ha->mq_lock);
 			goto que_failed;
 		}
 		vha->flags.qpairs_rsp_created = 1;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 41/88] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (39 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 40/88] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:49   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 42/88] scsi: qla2xxx: Fix endianness annotations in vp_rpt_id_entry structures Nilesh Javali
                   ` (46 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

C bitfield packing order is implementation-defined: GCC packs LSB-first
on little-endian targets and MSB-first on big-endian targets.  The
__le16 bitfield declarations for vp_index/sof_type in the 29xx extended
IOCB structures produce incorrect bit positions on big-endian hosts,
and Sparse cannot enforce endianness checks on bitfield members.

Replace the three sets of __le16 bitfields (in els_entry_24xx_ext,
els_sts_entry_24xx_ext, and abts_entry_24xx_ext) with a single __le16
scalar field and provide inline accessor functions that use proper
le16_to_cpu()/cpu_to_le16() with shift-and-mask operations.

Fixes: 1b923fdfaeb5 ("scsi: qla2xxx: Add 128-byte IOCB definitions for 29xx")
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_fw29.h   | 39 ++++++++++++++++++++++++-------
 drivers/scsi/qla2xxx/qla_inline.h |  8 +++----
 drivers/scsi/qla2xxx/qla_isr.c    | 15 ++++++------
 3 files changed, 42 insertions(+), 20 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_fw29.h b/drivers/scsi/qla2xxx/qla_fw29.h
index 600a40d8bd5f..63bf350ddffc 100644
--- a/drivers/scsi/qla2xxx/qla_fw29.h
+++ b/drivers/scsi/qla2xxx/qla_fw29.h
@@ -24,6 +24,33 @@
  * Access on a host-endian value via le16_to_cpu(vp_index) & CMD_EXT_VP_INDEX_MASK.
  */
 #define CMD_EXT_VP_INDEX_MASK		0x01ff
+
+/*
+ * Combined vp_index/sof_type field layout (used by ELS and ABTS ext IOCBs):
+ *   bits [8:0]   - VP index (9 bits)
+ *   bits [11:9]  - reserved
+ *   bits [15:12] - SOF type (4 bits)
+ */
+#define EXT_VP_SOF_VP_INDEX_MASK	0x01ff
+#define EXT_VP_SOF_SOF_TYPE_SHIFT	12
+#define EXT_VP_SOF_SOF_TYPE_MASK	0xf000
+
+static inline u16 qla_ext_get_vp_index(__le16 vp_sof)
+{
+	return le16_to_cpu(vp_sof) & EXT_VP_SOF_VP_INDEX_MASK;
+}
+
+static inline u16 qla_ext_get_sof_type(__le16 vp_sof)
+{
+	return (le16_to_cpu(vp_sof) >> EXT_VP_SOF_SOF_TYPE_SHIFT) & 0xf;
+}
+
+static inline __le16 qla_ext_build_vp_sof(u16 vp_idx, u16 sof_type)
+{
+	return cpu_to_le16((vp_idx & EXT_VP_SOF_VP_INDEX_MASK) |
+			   ((sof_type & 0xf) << EXT_VP_SOF_SOF_TYPE_SHIFT));
+}
+
 /*
  * ISP queue - command entry structure definition.
  */
@@ -393,9 +420,7 @@ struct els_entry_24xx_ext {
 
 	__le16	tx_dsd_count;
 
-	__le16	vp_index : 9;		/* VP Index 9bits */
-	__le16	reserved_1_sof : 3;
-	__le16	sof_type : 4;
+	__le16	vp_index_sof;		/* bits [8:0]=VP index, [15:12]=SOF type */
 
 	__le32	rx_xchg_address;	/* Receive exchange address. */
 	__le16	rx_dsd_count;
@@ -444,9 +469,7 @@ struct els_sts_entry_24xx_ext {
 
 	__le16	reserved_1;
 
-	__le16	vp_index : 9;		/* VP Index 9bits */
-	__le16	reserved_1_sof : 3;
-	__le16	sof_type : 4;
+	__le16	vp_index_sof;		/* bits [8:0]=VP index, [15:12]=SOF type */
 
 	__le32	rx_xchg_address;	/* Receive exchange address. */
 	__le16	reserved_2;
@@ -563,9 +586,7 @@ struct abts_entry_24xx_ext {
 	__le16	nport_handle;		/* type 0x54 only */
 
 	__le16	control_flags;		/* type 0x55 only */
-	__le16	vp_idx : 9;		/* VP index 9 bits */
-	__le16	reserved_1_sof : 3;
-	__le16	sof_type : 4;		/* sof_type is upper nibble */
+	__le16	vp_idx_sof;		/* bits [8:0]=VP index, [15:12]=SOF type */
 
 	__le32	rx_xch_addr;
 
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index 58a4a35e70c6..6b8fb645c036 100644
--- a/drivers/scsi/qla2xxx/qla_inline.h
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -890,8 +890,8 @@ qla_sts_fwi2_extract(struct qla_hw_data *ha, void *pkt,
  * Both layouts have the same 16-bit slot at offset 14, but it is encoded
  * differently:
  *   - 24xx: separate u8 vp_index + u8 sof_type with EST_SOFI3 (1 << 4)
- *   - 29xx: __le16 with bitfields { vp_index:9, reserved_1_sof:3,
- *           sof_type:4 } and ELS_EXT_EST_SOFI3
+ *   - 29xx: __le16 vp_index_sof with bits [8:0]=VP index, [15:12]=SOF type
+ *           and ELS_EXT_EST_SOFI3
  * so this is the single point in the driver that knows about that
  * encoding split.
  */
@@ -901,8 +901,8 @@ qla_els_set_vp_sof(struct scsi_qla_host *vha, void *pkt, u16 vp_idx)
 	if (IS_QLA29XX(vha->hw)) {
 		struct els_entry_24xx_ext *ext = pkt;
 
-		ext->vp_index = vp_idx;
-		ext->sof_type = ELS_EXT_EST_SOFI3;
+		ext->vp_index_sof =
+		    qla_ext_build_vp_sof(vp_idx, ELS_EXT_EST_SOFI3);
 	} else {
 		struct els_entry_24xx *e = pkt;
 
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 3a9237376050..1c549ee313c4 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -90,13 +90,11 @@ static inline void display_Laser_info(scsi_qla_host_t *vha,
 	    (uint8_t *)(abts_ptr), sizeof(*(abts_ptr)));		\
 } while (0)
 
-#define QLA_BUILD_ABTS_BA_ACC(rsp, src, sof_val, fctl) do {		\
+#define QLA_BUILD_ABTS_BA_ACC(rsp, src, fctl) do {			\
 	memset((rsp), 0, sizeof(*(rsp)));				\
 	(rsp)->entry_type = ABTS_RSP_TYPE;				\
 	(rsp)->entry_count = 1;						\
 	(rsp)->nport_handle = (src)->nport_handle;			\
-	(rsp)->vp_idx = (src)->vp_idx;					\
-	(rsp)->sof_type = (sof_val);					\
 	(rsp)->rx_xch_addr = (src)->rx_xch_addr;			\
 	(rsp)->d_id[0] = (src)->s_id[0];				\
 	(rsp)->d_id[1] = (src)->s_id[1];				\
@@ -215,14 +213,17 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
 	if (IS_QLA29XX(ha)) {
 		struct abts_entry_24xx_ext *rsp_ext = rsp_pkt;
 
-		QLA_BUILD_ABTS_BA_ACC(rsp_ext, abts_ext,
-		    abts_ext->sof_type, fctl);
+		QLA_BUILD_ABTS_BA_ACC(rsp_ext, abts_ext, fctl);
+		rsp_ext->vp_idx_sof = qla_ext_build_vp_sof(
+		    qla_ext_get_vp_index(abts_ext->vp_idx_sof),
+		    qla_ext_get_sof_type(abts_ext->vp_idx_sof));
 		QLA_LOG_ISSUE_ABTS_RSP(vha, rsp_ext, dma, rval);
 	} else {
 		struct abts_entry_24xx *abts_rsp = rsp_pkt;
 
-		QLA_BUILD_ABTS_BA_ACC(abts_rsp, abts,
-		    abts->sof_type & 0xf0, fctl);
+		QLA_BUILD_ABTS_BA_ACC(abts_rsp, abts, fctl);
+		abts_rsp->vp_idx = abts->vp_idx;
+		abts_rsp->sof_type = abts->sof_type & 0xf0;
 		QLA_LOG_ISSUE_ABTS_RSP(vha, abts_rsp, dma, rval);
 	}
 
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 42/88] scsi: qla2xxx: Fix endianness annotations in vp_rpt_id_entry structures
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (40 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 41/88] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 43/88] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats Nilesh Javali
                   ` (45 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

The vp_rpt_id_entry_24xx and vp_rpt_id_entry_24xx_ext DMA structures
use plain uint16_t for fip_flags and bbcr fields that the firmware
writes in little-endian format.  On big-endian hosts, reading bbcr
without le16_to_cpu() produces an incorrect value, breaking the
buffer-to-buffer credit enable detection.

Additionally, the 29xx ext struct uses __le16 bitfields for
vp_idx:9/vp_status:7 which suffer from architecture-dependent
bit packing order (same class of bug fixed in the ELS/ABTS
extended IOCBs).

Fix by:
  - Changing uint16_t fip_flags/bbcr to __le16 in both qla_fw.h
    and qla_fw29.h (enables Sparse endianness checking)
  - Replacing the __le16 bitfields with a scalar __le16 vp_idx_status
    and defined shift/mask constants
  - Adding le16_to_cpu() at the bbcr and vp_idx_status access sites
    in qla_mbx.c

Fixes: 1b923fdfaeb5 ("scsi: qla2xxx: Add 128-byte IOCB definitions for 29xx")
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_fw.h   |  8 ++++----
 drivers/scsi/qla2xxx/qla_fw29.h | 20 ++++++++++++++------
 drivers/scsi/qla2xxx/qla_mbx.c  |  9 ++++++---
 3 files changed, 24 insertions(+), 13 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index 4d6f8b1a36d1..b29abcc7f74f 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -1538,7 +1538,7 @@ struct vp_rpt_id_entry_24xx {
 #define TOPO_N2N   0x4
 #define TOPO_F     0x6
 
-			uint16_t fip_flags;
+			__le16 fip_flags;
 			uint8_t rsv2[12];
 
 			uint8_t ls_rjt_vendor;
@@ -1548,13 +1548,13 @@ struct vp_rpt_id_entry_24xx {
 
 			uint8_t port_name[8];
 			uint8_t node_name[8];
-			uint16_t bbcr;
+			__le16 bbcr;
 			uint8_t reserved_5[6];
 		} f1;
 		struct _f2 { /* format 2: N2N direct connect */
 			uint8_t vpstat1_subcode;
 			uint8_t flags;
-			uint16_t fip_flags;
+			__le16 fip_flags;
 			uint8_t rsv2[12];
 
 			uint8_t ls_rjt_vendor;
@@ -1564,7 +1564,7 @@ struct vp_rpt_id_entry_24xx {
 
 			uint8_t port_name[8];
 			uint8_t node_name[8];
-			uint16_t bbcr;
+			__le16 bbcr;
 			uint8_t reserved_5[2];
 			uint8_t remote_nport_id[4];
 		} f2;
diff --git a/drivers/scsi/qla2xxx/qla_fw29.h b/drivers/scsi/qla2xxx/qla_fw29.h
index 63bf350ddffc..6382a054310a 100644
--- a/drivers/scsi/qla2xxx/qla_fw29.h
+++ b/drivers/scsi/qla2xxx/qla_fw29.h
@@ -51,6 +51,15 @@ static inline __le16 qla_ext_build_vp_sof(u16 vp_idx, u16 sof_type)
 			   ((sof_type & 0xf) << EXT_VP_SOF_SOF_TYPE_SHIFT));
 }
 
+/*
+ * Combined vp_idx/vp_status field layout (vp_rpt_id_entry_24xx_ext):
+ *   bits [8:0]   - VP index (9 bits)
+ *   bits [15:9]  - VP status (7 bits)
+ */
+#define EXT_VP_STATUS_VP_INDEX_MASK	0x01ff
+#define EXT_VP_STATUS_VP_STATUS_SHIFT	9
+#define EXT_VP_STATUS_VP_STATUS_MASK	0xfe00
+
 /*
  * ISP queue - command entry structure definition.
  */
@@ -708,8 +717,7 @@ struct vp_rpt_id_entry_24xx_ext {
 	__le32 resv1;
 	uint8_t vp_acquired;
 	uint8_t vp_setup;
-	__le16	vp_idx : 9;		/* VP Index 9bits */
-	__le16	vp_status : 7;		/* VP Status 7bits */
+	__le16	vp_idx_status;		/* bits [8:0]=VP index, [15:9]=VP status */
 
 	uint8_t port_id[3];
 	uint8_t format;
@@ -719,7 +727,7 @@ struct vp_rpt_id_entry_24xx_ext {
 			uint8_t vpstat1_subcode; /* vp_status=1 subcode */
 			uint8_t flags;
 
-			uint16_t fip_flags;
+			__le16 fip_flags;
 			uint8_t rsv2[12];
 
 			uint8_t ls_rjt_vendor;
@@ -730,13 +738,13 @@ struct vp_rpt_id_entry_24xx_ext {
 			__le16	flogi_acc_payload_size;	/* bits [8:0] meaningful */
 			uint8_t port_name[8];
 			uint8_t node_name[8];
-			uint16_t bbcr;
+			__le16 bbcr;
 			uint8_t reserved_5[6];
 		} f1;
 		struct vp_rpt_id_ext_f2 { /* format 2: N2N direct connect */
 			uint8_t vpstat1_subcode;
 			uint8_t flags;
-			uint16_t fip_flags;
+			__le16 fip_flags;
 			uint8_t rsv2[12];
 
 			uint8_t ls_rjt_vendor;
@@ -746,7 +754,7 @@ struct vp_rpt_id_entry_24xx_ext {
 
 			uint8_t port_name[8];
 			uint8_t node_name[8];
-			uint16_t bbcr;
+			__le16 bbcr;
 			uint8_t reserved_5[2];
 			uint8_t remote_nport_id[4];
 		} f2;
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index ce845afe3e16..d0894cc90470 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -4106,8 +4106,10 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
 		return;
 
 	if (IS_QLA29XX(ha)) {
-		vp_idx = rptid_entry_ext->vp_idx;
-		vp_status = rptid_entry_ext->vp_status;
+		vp_idx = le16_to_cpu(rptid_entry_ext->vp_idx_status) &
+			 EXT_VP_STATUS_VP_INDEX_MASK;
+		vp_status = (le16_to_cpu(rptid_entry_ext->vp_idx_status) >>
+			     EXT_VP_STATUS_VP_STATUS_SHIFT) & 0x7f;
 	} else {
 		vp_idx = rptid_entry->vp_idx;
 		vp_status = rptid_entry->vp_status;
@@ -4231,7 +4233,8 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
 		ha->flags.gpsc_supported = 1;
 		ha->current_topology = ISP_CFG_F;
 		/* buffer to buffer credit flag */
-		vha->flags.bbcr_enable = (rptid_entry->u.f1.bbcr & 0xf) != 0;
+		vha->flags.bbcr_enable =
+		    (le16_to_cpu(rptid_entry->u.f1.bbcr) & 0xf) != 0;
 
 		if (vp_idx == 0) {
 			if (vp_status == VP_STAT_COMPL) {
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 43/88] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (41 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 42/88] scsi: qla2xxx: Fix endianness annotations in vp_rpt_id_entry structures Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:49   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 44/88] scsi: qla2xxx: Add 64G/128G port speed setting support Nilesh Javali
                   ` (44 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

29xx provides the 64-bit FPM transmit/receive word counters in the link
statistics block, like 83xx/27xx/28xx.  qla2x00_get_fc_host_stats()
only consumed those counters for the older families and fell back to the
software approximation (input/output bytes >> 2) on 29xx, reporting less
accurate rx_words/tx_words.

Add IS_QLA29XX() to the high-speed branch so 29xx reports the hardware
word counters.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_attr.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 19aa66b8ca52..6cf74f8c9628 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -2990,7 +2990,8 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost)
 		p->error_frames =
 		    le32_to_cpu(stats->dropped_frames) +
 		    le32_to_cpu(stats->discarded_frames);
-		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+		    IS_QLA29XX(ha)) {
 			p->rx_words = le64_to_cpu(stats->fpm_recv_word_cnt);
 			p->tx_words = le64_to_cpu(stats->fpm_xmit_word_cnt);
 		} else {
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 44/88] scsi: qla2xxx: Add 64G/128G port speed setting support
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (42 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 43/88] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:52   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 45/88] scsi: qla2xxx: Fix 64G link speed reporting in get_data_rate Nilesh Javali
                   ` (43 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

The port speed setting paths topped out at 32G: qla2x00_port_speed_store()
only mapped sysfs inputs up to 32 (and their no-loss-of-sync forms up to
320), and qla2x00_set_data_rate() only accepted PORT_SPEED_AUTO/4/8/16/32
in its switch.  A user request for 64G or 128G therefore hit the default
arm and was silently downgraded to auto-negotiation.

Map the 64 and 128 sysfs inputs (and their /10 no-loss-of-sync forms 640
and 1280) to PORT_SPEED_64GB and PORT_SPEED_128GB, and accept those
values in qla2x00_set_data_rate().  The firmware validates the requested
rate against the adapter's actual capability.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_attr.c | 8 +++++++-
 drivers/scsi/qla2xxx/qla_mbx.c  | 2 ++
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 6cf74f8c9628..3b24e8a5e29b 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1835,7 +1835,7 @@ qla2x00_port_speed_store(struct device *dev, struct device_attribute *attr,
 		return rval;
 	speed = type;
 	if (type == 40 || type == 80 || type == 160 ||
-	    type == 320) {
+	    type == 320 || type == 640 || type == 1280) {
 		ql_dbg(ql_dbg_user, vha, 0x70d9,
 		    "Setting will be affected after a loss of sync\n");
 		type = type/10;
@@ -1860,6 +1860,12 @@ qla2x00_port_speed_store(struct device *dev, struct device_attribute *attr,
 	case 32:
 		ha->set_data_rate = PORT_SPEED_32GB;
 		break;
+	case 64:
+		ha->set_data_rate = PORT_SPEED_64GB;
+		break;
+	case 128:
+		ha->set_data_rate = PORT_SPEED_128GB;
+		break;
 	default:
 		ql_log(ql_log_warn, vha, 0x1199,
 		    "Unrecognized speed setting:%lx. Setting Autoneg\n",
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index d0894cc90470..ba822c196894 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -5720,6 +5720,8 @@ qla2x00_set_data_rate(scsi_qla_host_t *vha, uint16_t mode)
 	case PORT_SPEED_8GB:
 	case PORT_SPEED_16GB:
 	case PORT_SPEED_32GB:
+	case PORT_SPEED_64GB:
+	case PORT_SPEED_128GB:
 		val = ha->set_data_rate;
 		break;
 	default:
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 45/88] scsi: qla2xxx: Fix 64G link speed reporting in get_data_rate
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (43 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 44/88] scsi: qla2xxx: Add 64G/128G port speed setting support Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  5:53 ` [PATCH v3 46/88] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check Nilesh Javali
                   ` (42 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla2x00_get_data_rate() skips updating ha->link_data_rate when the
firmware returns mcp->mb[1] == 0x7.  That value was a legacy sentinel
from before 64G hardware existed, but PORT_SPEED_64GB is now defined as
0x07 and ha->link_data_rate is decoded with the PORT_SPEED_* encoding.
On a 64G-capable adapter a genuine 64G link is therefore dropped, and
the port speed is misreported (port_speed sysfs, fc_host speed, FDMI).

Only 28xx and 29xx support 64G, so accept 0x07 on those adapters while
keeping the legacy filter for older ones.  Also drop the duplicate
copy of the check at the end of the success branch; it repeated the
first assignment with no intervening change.

Fixes: ecc89f25e225 ("scsi: qla2xxx: Add Device ID for ISP28XX")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_mbx.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index ba822c196894..b32ca8ed274d 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -5785,10 +5785,11 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
 		ql_dbg(ql_dbg_mbx, vha, 0x1107,
 		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
-		if (mcp->mb[1] != 0x7)
+		if (mcp->mb[1] != 0x7 || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 			ha->link_data_rate = mcp->mb[1];
 
-		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
+		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+		    IS_QLA29XX(ha)) {
 			if (mcp->mb[4] & BIT_0)
 				ql_log(ql_log_info, vha, 0x11a2,
 				    "FEC=enabled (data rate).\n");
@@ -5796,8 +5797,6 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
 
 		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1108,
 		    "Done %s.\n", __func__);
-		if (mcp->mb[1] != 0x7)
-			ha->link_data_rate = mcp->mb[1];
 	}
 
 	return rval;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 46/88] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (44 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 45/88] scsi: qla2xxx: Fix 64G link speed reporting in get_data_rate Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  7:09   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 47/88] scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters Nilesh Javali
                   ` (41 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla_chk_edif_rx_sa_delete_pending() obtains the SCSI command via
GET_CMD_SP(sp) and immediately dereferences cmd->sc_data_direction.
That command pointer can be NULL: the firmware may post a status
completion for a command that has already been returned or aborted.
The caller qla2x00_status_entry() acknowledges this on the very same
status path, re-fetching GET_CMD_SP(sp) and bailing out with the
"Command already returned" message when it is NULL -- but that check
runs only after qla_chk_edif_rx_sa_delete_pending() has already
dereferenced the pointer, so a NULL cmd crashes the kernel in
interrupt context.

Return early when cmd is NULL, before touching cmd->sc_data_direction.

Fixes: dd30706e73b7 ("scsi: qla2xxx: edif: Add key update")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_edif.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c
index ade1d8178573..bfa520f936a2 100644
--- a/drivers/scsi/qla2xxx/qla_edif.c
+++ b/drivers/scsi/qla2xxx/qla_edif.c
@@ -3540,6 +3540,9 @@ void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
 	uint32_t handle;
 	uint16_t sa_index;
 
+	if (!cmd)
+		return;
+
 	handle = (uint32_t)LSW(sts24->handle);
 
 	/* find out if this status iosb is for a scsi read */
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 47/88] scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (45 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 46/88] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:53   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 48/88] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size Nilesh Javali
                   ` (40 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

In the CS_PORT_LOGGED_OUT case of qla2x00_chk_ms_status(), the
FWI2-capable branch compared ms_pkt->loop_id.extended against NPH_SNS
to decide whether the Name Server had logged out. On FWI2 and later
adapters the response is a ct_entry_24xx / ct_entry_24xx_ext, where
loop_id.extended (via the legacy ms_iocb_entry_t view) aliases offset 8,
which is comp_status, not nport_handle (offset 10). As this code runs
under CS_PORT_LOGGED_OUT, the field read back 0x29 (CS_PORT_LOGGED_OUT)
and the comparison against NPH_SNS (0x7fc) was always false.

As a result the driver never recognized a Name Server logout on FWI2/
29xx adapters: it returned the generic QLA_FUNCTION_FAILED instead of
QLA_NOT_LOGGED_IN and skipped setting LOOP_RESYNC_NEEDED /
LOCAL_LOOP_UPDATE, so the fabric rediscovery triggered by an SNS logout
did not happen.

Read nport_handle from the ct_entry_24xx layout (offset 10) instead.
nport_handle is at the same offset in ct_entry_24xx and
ct_entry_24xx_ext, so a single cast covers 24xx-class and 29xx. The
non-FWI2 branch keeps using loop_id.extended, which is correct for the
ms_iocb_entry_t response on those adapters.

Fixes: b98ae0d748db ("scsi: qla2xxx: Fix name server relogin")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_gs.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index 7a4d2fdc095f..20b1aef455c4 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -192,8 +192,8 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt,
 			break;
 		case CS_PORT_LOGGED_OUT:
 			if (IS_FWI2_CAPABLE(ha)) {
-				if (le16_to_cpu(ms_pkt->loop_id.extended) ==
-				    NPH_SNS)
+				if (le16_to_cpu(((struct ct_entry_24xx *)
+				    ms_pkt)->nport_handle) == NPH_SNS)
 					lid_is_sns = true;
 			} else {
 				if (le16_to_cpu(ms_pkt->loop_id.extended) ==
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 48/88] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (46 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 47/88] scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:55   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 49/88] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config() Nilesh Javali
                   ` (39 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

The VP control IOCB selects its target virtual port by setting one bit
in vp_idx_map, a fixed 16-byte (128-bit) array in both
vp_ctrl_entry_24xx and vp_ctrl_entry_24xx_ext. qla25xx_ctrlvp_iocb()
computes map = (vp_index - 1) / 8 and writes vce->vp_idx_map[map]
without checking that map stays within the array.

max_npiv_vports is taken from firmware and only sanitized to a
MIN_MULTI_ID_FABRIC-aligned boundary, so it can legitimately be 191 or
255, and qla24xx_control_vp() only rejects vp_index >= max_npiv_vports.
A vp_index above 128 therefore yields map >= 16 and an out-of-bounds
write of up to 16 bytes past vp_idx_map, corrupting the trailing IOCB
fields (or the adjacent request-ring slot on the 64-byte layout).

Reject a vp_index that cannot be represented in the IOCB bitmap in
qla24xx_control_vp(), and add a defensive ARRAY_SIZE() guard in
qla25xx_ctrlvp_iocb() before the write. Adapters that report the usual
63 or 127 NPIV vports are unaffected.

Fixes: 2853192e154b ("scsi: qla2xxx: Use IOCB path to submit Control VP MBX command")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_iocb.c | 6 ++++++
 drivers/scsi/qla2xxx/qla_mid.c  | 8 ++++++++
 2 files changed, 14 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 59648a9229ac..22f2d81e2009 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -4052,6 +4052,12 @@ qla25xx_ctrlvp_iocb(srb_t *sp, void *pkt)
 	vce->entry_count = 1;
 	vce->command = cpu_to_le16(sp->u.iocb_cmd.u.ctrlvp.cmd);
 	vce->vp_count = cpu_to_le16(1);
+	if (map >= ARRAY_SIZE(vce->vp_idx_map)) {
+		ql_log(ql_log_warn, sp->vha, 0x307c,
+		       "ctrlvp: vp_index %u exceeds vp_idx_map capacity\n",
+		       sp->u.iocb_cmd.u.ctrlvp.vp_index);
+		return;
+	}
 	vce->vp_idx_map[map] |= 1 << pos;
 }
 
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index 7072af5b4217..b7d9c1a53f3c 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -987,6 +987,14 @@ int qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
 	if (vp_index == 0 || vp_index >= ha->max_npiv_vports)
 		return QLA_PARAMETER_ERROR;
 
+	/*
+	 * The VP_CTRL IOCB selects the target VP through a fixed 128-bit
+	 * (16-byte) vp_idx_map bitmap, so vp_index must fit within it even
+	 * if firmware advertises more NPIV vports.
+	 */
+	if (vp_index > sizeof_field(struct vp_ctrl_entry_24xx, vp_idx_map) * 8)
+		return QLA_PARAMETER_ERROR;
+
 	/* ref: INIT */
 	sp = qla2x00_get_sp(base_vha, NULL, GFP_KERNEL);
 	if (!sp)
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 49/88] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config()
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (47 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 48/88] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  6:54   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 50/88] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition() Nilesh Javali
                   ` (38 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

The Modify VP Config completion handler labelled its first error branch
"error status" but tested vpmod->comp_status instead of
vpmod->entry_status. Because CS_COMPLETE is 0, the following
"comp_status != CS_COMPLETE" branch duplicated that test and was dead
code, and entry_status was never examined at all.

When firmware rejects the IOCB early it sets entry_status while leaving
comp_status zero. As the IOCB is allocated with dma_pool_zalloc(), both
comp_status branches evaluate false and the handler falls through to the
success path, calling fc_vport_set_state(FC_VPORT_INITIALIZING) for a
configuration the firmware never accepted. This can leave the virtual
port enabled on top of an invalid config and surface later as login
timeouts or follow-on firmware errors.

Test entry_status in the first branch, matching qla_ctrlvp_completed()
and the login/logout/abort/reset IOCB handlers; the comp_status branch
then becomes the live completion-status check.

Fixes: 2c3dfe3f6ad8 ("[SCSI] qla2xxx: add support for NPIV")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_mbx.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index b32ca8ed274d..59023492c5a9 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -4401,10 +4401,10 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
 	if (rval != QLA_SUCCESS) {
 		ql_dbg(ql_dbg_mbx, vha, 0x10bd,
 		    "Failed to issue VP config IOCB (%x).\n", rval);
-	} else if (vpmod->comp_status != 0) {
+	} else if (vpmod->entry_status != 0) {
 		ql_dbg(ql_dbg_mbx, vha, 0x10be,
 		    "Failed to complete IOCB -- error status (%x).\n",
-		    vpmod->comp_status);
+		    vpmod->entry_status);
 		rval = QLA_FUNCTION_FAILED;
 	} else if (vpmod->comp_status != cpu_to_le16(CS_COMPLETE)) {
 		ql_dbg(ql_dbg_mbx, vha, 0x10bf,
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 50/88] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition()
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (48 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 49/88] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config() Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  7:03   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 51/88] scsi: qla2xxx: Initialize NVMe abort_work once at submission Nilesh Javali
                   ` (37 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

In the format 1 path, the virtual port is located on ha->vp_list while
holding vport_slock, but the lock is dropped before vp is used:
qla_update_host_map() is called and VP_IDX_ACQUIRED/REGISTER_FC4_NEEDED/
REGISTER_FDMI_NEEDED are set on vp. No reference is taken across that
window, so a concurrent qla24xx_deallocate_vp_id() can tear the vport
down and free it, leading to a use-after-free.

Take a vport reference (vref_count) under vport_slock when the matching
vp is found, and drop it after the last use of vp. qla24xx_deallocate_vp_id()
waits for vref_count to reach zero before unlinking and freeing the vport,
so the pointer stays valid. This matches the reference idiom already used
by the other ha->vp_list traversals.

Fixes: 2c3dfe3f6ad8 ("[SCSI] qla2xxx: add support for NPIV")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_mbx.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 59023492c5a9..ba4a4764de1f 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -4267,6 +4267,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
 			list_for_each_entry(vp, &ha->vp_list, list) {
 				if (vp_idx == vp->vp_idx) {
 					found = 1;
+					atomic_inc(&vp->vref_count);
 					break;
 				}
 			}
@@ -4284,6 +4285,8 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
 			set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
 			set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
 			set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
+
+			atomic_dec(&vp->vref_count);
 		}
 		set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
 		qla2xxx_wake_dpc(vha);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 51/88] scsi: qla2xxx: Initialize NVMe abort_work once at submission
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (49 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 50/88] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition() Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  7:00   ` sashiko-bot
  2026-07-07  5:53 ` [PATCH v3 52/88] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject Nilesh Javali
                   ` (36 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla_nvme_fcp_abort() and qla_nvme_ls_abort() ran INIT_WORK() on
priv->abort_work immediately before schedule_work(). INIT_WORK()
reinitializes the work_struct, resetting its list head and clearing the
pending bit. If an abort is issued more than once for the same command
(for example, concurrent transport teardown and a timeout-driven abort),
the second INIT_WORK() reinitializes a work item that is already queued,
which can corrupt the workqueue list and lead to crashes or a looping
worker.

Initialize priv->abort_work once at command submission, next to the
existing per-command spin_lock_init(&priv->cmd_lock), and leave only
schedule_work() in the abort paths. schedule_work() already does nothing
when the work item is still pending, so a repeated abort no longer
disturbs an in-flight work item. The command is not returned to the
transport until the final kref_put()/release callback runs after
abort_work has completed, so the work item is idle before priv is
reused and the single submission-time INIT_WORK() is safe.

Fixes: e473b3074104 ("scsi: qla2xxx: Add FC-NVMe abort processing")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_nvme.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index 0038b6274d44..3b2f255a5d7d 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -463,7 +463,6 @@ static void qla_nvme_ls_abort(struct nvme_fc_local_port *lport,
 	}
 	spin_unlock_irqrestore(&priv->cmd_lock, flags);
 
-	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
 	schedule_work(&priv->abort_work);
 }
 
@@ -501,6 +500,7 @@ static int qla_nvme_ls_req(struct nvme_fc_local_port *lport,
 	priv->sp = sp;
 	kref_init(&sp->cmd_kref);
 	spin_lock_init(&priv->cmd_lock);
+	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
 	nvme = &sp->u.iocb_cmd;
 	priv->fd = fd;
 	nvme->u.nvme.desc = fd;
@@ -545,7 +545,6 @@ static void qla_nvme_fcp_abort(struct nvme_fc_local_port *lport,
 	}
 	spin_unlock_irqrestore(&priv->cmd_lock, flags);
 
-	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
 	schedule_work(&priv->abort_work);
 }
 
@@ -877,6 +876,7 @@ static int qla_nvme_post_cmd(struct nvme_fc_local_port *lport,
 
 	kref_init(&sp->cmd_kref);
 	spin_lock_init(&priv->cmd_lock);
+	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
 	sp->priv = priv;
 	priv->sp = sp;
 	sp->type = SRB_NVME_CMD;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 52/88] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (50 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 51/88] scsi: qla2xxx: Initialize NVMe abort_work once at submission Nilesh Javali
@ 2026-07-07  5:53 ` Nilesh Javali
  2026-07-07  7:07   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 53/88] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak Nilesh Javali
                   ` (35 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:53 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla_nvme_ls_reject_iocb() allocates from and advances the request ring
through __qla2x00_alloc_iocbs() (which assumes the hardware_lock is held)
and qla2x00_start_iocbs() (which advances the ring and rings the
request-in doorbell), but takes no lock itself. Two of its callers invoke
it without the producer lock held:

  - qla_nvme_xmt_ls_rsp(), the NVMe-FC .xmt_ls_rsp transport callback, on
    its error path, and
  - qla2xxx_process_purls_pkt(), run from the purex work/DPC context.

Both use ha->base_qpair, whose qp_lock_ptr is hardware_lock, so they can
run concurrently with normal I/O submission on the base ring and corrupt
the ring producer state, leading to duplicated or dropped commands. The
third caller, qla2xxx_process_purls_iocb(), runs inside
qla24xx_process_response_queue() with the qpair lock already held and is
safe; that is also why the lock cannot be taken inside the helper itself
(it would recursively re-acquire hardware_lock on the response path).

Take qp_lock_ptr around the two unlocked callers and document the helper
as caller-locked. Both run in process context, so spin_lock_irqsave() is
used and nothing in the locked region sleeps.

Fixes: 875386b98857 ("scsi: qla2xxx: Add Unsolicited LS Request and Response Support for NVMe")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_nvme.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index 3b2f255a5d7d..8dc6df6c2e1c 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -374,6 +374,7 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport,
 	srb_t *sp;
 	int rval = QLA_FUNCTION_FAILED;
 	uint8_t cnt = 0;
+	unsigned long flags;
 
 	if (!fcport || fcport->deleted)
 		goto out;
@@ -440,7 +441,9 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport,
 	a.vp_idx = vha->vp_idx;
 	a.nport_handle = uctx->nport_handle;
 	a.xchg_address = uctx->exchange_address;
+	spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
 	qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
+	spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
 	kfree(uctx);
 	return rval;
 }
@@ -1243,6 +1246,10 @@ static void qla_nvme_lsrjt_pt_iocb(struct scsi_qla_host *vha,
 	}
 }
 
+/*
+ * Allocates from and advances the request ring, so the caller must hold
+ * qp->qp_lock_ptr (the response-queue caller already holds it).
+ */
 static int
 qla_nvme_ls_reject_iocb(struct scsi_qla_host *vha, struct qla_qpair *qp,
 			struct qla_nvme_lsrjt_pt_arg *a, bool is_xchg_terminate)
@@ -1299,6 +1306,7 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item)
 {
 	struct qla_nvme_unsol_ctx *uctx = item->purls_context;
 	struct qla_nvme_lsrjt_pt_arg a;
+	unsigned long flags;
 	int ret = 1;
 
 #if (IS_ENABLED(CONFIG_NVME_FC))
@@ -1311,7 +1319,9 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item)
 		a.vp_idx = vha->vp_idx;
 		a.nport_handle = uctx->nport_handle;
 		a.xchg_address = uctx->exchange_address;
+		spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr, flags);
 		qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true);
+		spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr, flags);
 		list_del(&uctx->elem);
 		kfree(uctx);
 	}
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 53/88] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (51 preceding siblings ...)
  2026-07-07  5:53 ` [PATCH v3 52/88] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:06   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 54/88] scsi: qla2xxx: Fix BSG job leak on validate flash image error path Nilesh Javali
                   ` (34 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla2x00_do_dport_diagnostics() allocates the qla_dport_diag response
buffer with kmalloc_obj() (non-zeroing) and, on success, copies the
full sizeof(*dd) back to user space via sg_copy_from_buffer(). The
inbound sg_copy_to_buffer() only fills as many bytes as the user
request payload provides, and qla26xx_dport_diagnostics() zeroes only
dd->buf. The options and unused[] fields are therefore copied out
uninitialized, leaking kernel heap contents to user space.

Allocate with kzalloc_obj(), matching qla2x00_do_dport_diagnostics_v2().

Fixes: ec89146215d1 ("qla2xxx: Add bsg interface to support D_Port Diagnostics.")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_bsg.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index fade3638d31c..e7739cead967 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -2792,7 +2792,7 @@ qla2x00_do_dport_diagnostics(struct bsg_job *bsg_job)
 	    !IS_QLA28XX(vha->hw) && !IS_QLA29XX(vha->hw))
 		return -EPERM;
 
-	dd = kmalloc_obj(*dd);
+	dd = kzalloc_obj(*dd);
 	if (!dd) {
 		ql_log(ql_log_warn, vha, 0x70db,
 		    "Failed to allocate memory for dport.\n");
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 54/88] scsi: qla2xxx: Fix BSG job leak on validate flash image error path
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (52 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 53/88] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:03   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 55/88] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions() Nilesh Javali
                   ` (33 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla28xx_validate_flash_image() returns QLA_SUCCESS (0) unconditionally,
telling the FC BSG transport (fc_bsg_host_dispatch()) that the driver
owns and will complete the request. But bsg_job_done() is guarded by
"if (!rval)", so on the error path (rval == -EINVAL) neither the driver
nor the transport completes the job. The request dangles until it times
out, leaking block layer resources.

Commit c2c68225b145 ("scsi: qla2xxx: Fix bsg_done() causing double
free") added the "if (!rval)" guard to a batch of BSG handlers. That is
correct for handlers that also return the error code (the transport then
completes the job once via fail_host_msg), but this function returns
QLA_SUCCESS unconditionally, so the guard turned a correct single
completion into a leak.

Always call bsg_job_done(): bsg_reply->result is DID_OK and the error is
reported in vendor_rsp[0], and since the function returns 0 the transport
will not complete the job a second time.

Fixes: c2c68225b145 ("scsi: qla2xxx: Fix bsg_done() causing double free")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_bsg.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index e7739cead967..0c9174d6c887 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -3782,9 +3782,8 @@ static int qla28xx_validate_flash_image(struct bsg_job *bsg_job)
 	bsg_reply->result = DID_OK << 16;
 	bsg_reply->reply_payload_rcv_len = 0;
 	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
-	if (!rval)
-		bsg_job_done(bsg_job, bsg_reply->result,
-			     bsg_reply->reply_payload_rcv_len);
+	bsg_job_done(bsg_job, bsg_reply->result,
+		     bsg_reply->reply_payload_rcv_len);
 
 	return QLA_SUCCESS;
 }
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 55/88] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions()
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (53 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 54/88] scsi: qla2xxx: Fix BSG job leak on validate flash image error path Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:05   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 56/88] scsi: qla2xxx: Clamp MSI-X derived queue counts to avoid truncation Nilesh Javali
                   ` (32 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla2x00_update_fru_versions() copies the user-supplied BSG request into
a fixed 256-byte stack buffer (bsg[DMA_POOL_SIZE]) and then iterates
list->count times over the qla_image_version array embedded in that
buffer, advancing the image pointer each iteration. count is taken
directly from user input with no upper bound, while only
(DMA_POOL_SIZE - sizeof(list->count)) / sizeof(struct qla_image_version)
= 6 entries actually fit. A larger count walks the image pointer off the
end of the stack buffer, reading adjacent kernel stack memory and
sending it to the device via qla2x00_write_sfp().

Reject requests whose declared count does not fit in the buffer.

Fixes: 697a4bc69159 ("[SCSI] qla2xxx: Provide method for updating I2C attached VPD.")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
 drivers/scsi/qla2xxx/qla_bsg.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 0c9174d6c887..55142df30bda 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -1977,6 +1977,13 @@ qla2x00_update_fru_versions(struct bsg_job *bsg_job)
 
 	image = list->version;
 	count = list->count;
+
+	if (struct_size(list, version, count) > sizeof(bsg)) {
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+		    EXT_STATUS_INVALID_PARAM;
+		goto dealloc;
+	}
+
 	while (count--) {
 		memcpy(sfp, &image->field_info, sizeof(image->field_info));
 		rval = qla2x00_write_sfp(vha, sfp_dma, sfp,
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 56/88] scsi: qla2xxx: Clamp MSI-X derived queue counts to avoid truncation
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (54 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 55/88] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions() Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:06   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 57/88] scsi: qla2xxx: Improve firmware dump data capture Nilesh Javali
                   ` (31 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

ha->msix_count is u16, but ha->max_req_queues, ha->max_rsp_queues and
ha->max_qpairs are u8. Deriving the queue count as
"ha->max_req_queues = ha->msix_count - 1" therefore truncates: a board
(or a misconfigured/malicious hot-plugged device) advertising 257 MSI-X
vectors yields msix_count - 1 == 256, which truncates to 0. An MSI-X
count of 1 zeroes it as well, and in target mode the subsequent
"ha->max_req_queues--" then underflows 0 to 255.

When the count is 0, qla2x00_alloc_queues() calls
kzalloc_objs(struct req_que *, 0), which returns ZERO_SIZE_PTR. That is
not NULL, so the allocation check passes and the following
"ha->req_q_map[0] = req" dereferences ZERO_SIZE_PTR, corrupting memory
or crashing the kernel.

Add qla_calc_queue_count() to clamp the derived value into
[1, QLA_MAX_QUEUES - 1] so it always fits in u8 and is never zero, and
use it at all three derivation sites (qla25xx_iospace_config(),
qla83xx_iospace_config() and qla24xx_enable_msix()). Also guard the
target-mode decrement so it cannot reintroduce a zero (which would in
turn underflow max_qpairs).

Fixes: d74595278f4a ("scsi: qla2xxx: Add multiple queue pair functionality.")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_inline.h | 13 +++++++++++++
 drivers/scsi/qla2xxx/qla_isr.c    |  4 ++--
 drivers/scsi/qla2xxx/qla_os.c     |  6 +++---
 3 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index 6b8fb645c036..5b49e4798d78 100644
--- a/drivers/scsi/qla2xxx/qla_inline.h
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -138,6 +138,19 @@ qla_logio_set_vp_index(struct qla_hw_data *ha, void *pkt, u16 vp_idx)
 		((struct logio_entry_24xx *)pkt)->vp_index = vp_idx;
 }
 
+static inline u8
+qla_calc_queue_count(u16 msix_count)
+{
+	/*
+	 * Request/response queues are bounded by the MSI-X vector count less
+	 * the mailbox vector.  These counters are u8, so a board advertising
+	 * e.g. 257 vectors would truncate msix_count - 1 (256) to 0 and hand
+	 * kzalloc_objs() a zero count (ZERO_SIZE_PTR), faulting on the first
+	 * ha->req_q_map[0] store.  Clamp into [1, QLA_MAX_QUEUES - 1].
+	 */
+	return clamp_t(u16, msix_count - 1, 1, QLA_MAX_QUEUES - 1);
+}
+
 static inline void
 qla2x00_poll(struct rsp_que *rsp)
 {
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 1c549ee313c4..bca052fb3c8b 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -4704,10 +4704,10 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp)
 		ha->msix_count = ret;
 		/* Recalculate queue values */
 		if (ha->mqiobase && (ql2xmqsupport || ql2xnvmeenable)) {
-			ha->max_req_queues = ha->msix_count - 1;
+			ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
 
 			/* ATIOQ needs 1 vector. That's 1 less QPair */
-			if (QLA_TGT_MODE_ENABLED())
+			if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1)
 				ha->max_req_queues--;
 
 			ha->max_rsp_queues = ha->max_req_queues;
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 186c6c7a3944..5209dda45459 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -2132,7 +2132,7 @@ qla2x00_iospace_config(struct qla_hw_data *ha)
 		ha->msix_count = msix + 1;
 		/* Max queues are bounded by available msix vectors */
 		/* MB interrupt uses 1 vector */
-		ha->max_req_queues = ha->msix_count - 1;
+		ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
 		ha->max_rsp_queues = ha->max_req_queues;
 		/* Queue pairs is the max value minus the base queue pair */
 		ha->max_qpairs = ha->max_rsp_queues - 1;
@@ -2224,10 +2224,10 @@ qla83xx_iospace_config(struct qla_hw_data *ha)
 		 */
 		if (ql2xmqsupport || ql2xnvmeenable) {
 			/* MB interrupt uses 1 vector */
-			ha->max_req_queues = ha->msix_count - 1;
+			ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
 
 			/* ATIOQ needs 1 vector. That's 1 less QPair */
-			if (QLA_TGT_MODE_ENABLED())
+			if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1)
 				ha->max_req_queues--;
 
 			ha->max_rsp_queues = ha->max_req_queues;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 57/88] scsi: qla2xxx: Improve firmware dump data capture
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (55 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 56/88] scsi: qla2xxx: Clamp MSI-X derived queue counts to avoid truncation Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:18   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 58/88] scsi: qla2xxx: Serialize flash version read in reset handler Nilesh Javali
                   ` (30 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

From: Quinn Tran <qutran@marvell.com>

Capture as much firmware dump data as possible. Save the mailbox
registers at start-of-day, before firmware execution, so they are
available in the dump, and allocate a guestimate dump buffer early
during driver load to capture failures that happen before the final
dump buffer is sized.

Make template entry processing more robust: skip over any entry that
fails to capture and continue with the next one, and skip entries that
time out instead of aborting the whole dump. Notify udev once sysfs
nodes are available in case a dump was captured before they existed.

Signed-off-by: Quinn Tran <qutran@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_dbg.c  |   4 +-
 drivers/scsi/qla2xxx/qla_def.h  |   3 +
 drivers/scsi/qla2xxx/qla_init.c | 120 +++++++++++++++++---------------
 drivers/scsi/qla2xxx/qla_os.c   |   8 +++
 drivers/scsi/qla2xxx/qla_tmpl.c |  48 ++++++++++---
 5 files changed, 116 insertions(+), 67 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index acb58daacf35..4f756468ea64 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -172,7 +172,7 @@ qla27xx_dump_mpi_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram,
 
 		if (!test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
 			/* no interrupt, timed out*/
-			return rval;
+			return QLA_FUNCTION_TIMEOUT;
 		}
 		if (rval) {
 			/* error completion status */
@@ -255,7 +255,7 @@ qla24xx_dump_ram(struct qla_hw_data *ha, uint32_t addr, __be32 *ram,
 
 		if (!test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
 			/* no interrupt, timed out*/
-			return rval;
+			return QLA_FUNCTION_TIMEOUT;
 		}
 		if (rval) {
 			/* error completion status */
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 4fd2a28af7e4..bb4305f6a364 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -4176,6 +4176,7 @@ struct qla_hw_data {
 #define SRB_MIN_REQ     128
 	mempool_t       *srb_mempool;
 	u8 port_name[WWN_SIZE];
+	u16 mbregs[32];
 
 	volatile struct {
 		uint32_t	mbox_int		:1;
@@ -4246,6 +4247,8 @@ struct qla_hw_data {
 		uint32_t	eeh_flush:2;
 #define EEH_FLUSH_RDY  1
 #define EEH_FLUSH_DONE 2
+		uint32_t	t262_fail:1;
+		uint32_t	t272_fail:1;
 		uint32_t	secure_mcu:1;
 		uint32_t	valid_flt:1;
 	} flags;
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index ae9bf6871079..9d6b229bd352 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -3275,47 +3275,37 @@ qla81xx_reset_mpi(scsi_qla_host_t *vha)
 	return qla81xx_write_mpi_register(vha, mb);
 }
 
-static int
-qla_chk_risc_recovery(scsi_qla_host_t *vha)
+/* save MB regs at start of day for fw dump */
+static void
+qla_save_mbregs(scsi_qla_host_t *vha)
 {
 	struct qla_hw_data *ha = vha->hw;
 	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
 	__le16 __iomem *mbptr = &reg->mailbox0;
 	int i;
-	u16 mb[32];
-	int rc = QLA_SUCCESS;
-
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
-		return rc;
+	u16 *mb = ha->mbregs;
 
-	/* this check is only valid after RISC reset */
-	mb[0] = rd_reg_word(mbptr);
-	mbptr++;
-	if (mb[0] == 0xf) {
-		rc = QLA_FUNCTION_FAILED;
+	if ((!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) ||
+	    vha->flags.init_done)
+		return;
 
-		for (i = 1; i < 32; i++) {
-			mb[i] = rd_reg_word(mbptr);
-			mbptr++;
-		}
-
-		ql_log(ql_log_warn, vha, 0x1015,
-		       "RISC reset failed. mb[0-7] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
-		       mb[0], mb[1], mb[2], mb[3], mb[4], mb[5], mb[6], mb[7]);
-		ql_log(ql_log_warn, vha, 0x1015,
-		       "RISC reset failed. mb[8-15] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
-		       mb[8], mb[9], mb[10], mb[11], mb[12], mb[13], mb[14],
-		       mb[15]);
-		ql_log(ql_log_warn, vha, 0x1015,
-		       "RISC reset failed. mb[16-23] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
-		       mb[16], mb[17], mb[18], mb[19], mb[20], mb[21], mb[22],
-		       mb[23]);
-		ql_log(ql_log_warn, vha, 0x1015,
-		       "RISC reset failed. mb[24-31] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
-		       mb[24], mb[25], mb[26], mb[27], mb[28], mb[29], mb[30],
-		       mb[31]);
+	for (i = 0; i < 32; i++) {
+		mb[i] = rd_reg_word(mbptr);
+		mbptr++;
 	}
-	return rc;
+
+	ql_log(ql_log_info, vha, 0x1015,
+	    "mb[0-7] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
+	    mb[0], mb[1], mb[2], mb[3], mb[4], mb[5], mb[6], mb[7]);
+	ql_log(ql_log_info, vha, 0x1015,
+	    "mb[8-15] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
+	    mb[8], mb[9], mb[10], mb[11], mb[12], mb[13], mb[14], mb[15]);
+	ql_log(ql_log_info, vha, 0x1015,
+	    "mb[16-23] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
+	    mb[16], mb[17], mb[18], mb[19], mb[20], mb[21], mb[22], mb[23]);
+	ql_log(ql_log_info, vha, 0x1015,
+	    "mb[24-31] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
+	    mb[24], mb[25], mb[26], mb[27], mb[28], mb[29], mb[30], mb[31]);
 }
 
 /**
@@ -3334,7 +3324,6 @@ qla24xx_reset_risc(scsi_qla_host_t *vha)
 	uint16_t wd;
 	static int abts_cnt; /* ISP abort retry counts */
 	int rval = QLA_SUCCESS;
-	int print = 1;
 
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 
@@ -3431,9 +3420,6 @@ qla24xx_reset_risc(scsi_qla_host_t *vha)
 		barrier();
 		if (cnt) {
 			mdelay(1);
-			if (print && qla_chk_risc_recovery(vha))
-				print = 0;
-
 			wd = rd_reg_word(&reg->mailbox0);
 		} else {
 			rval = QLA_FUNCTION_TIMEOUT;
@@ -3453,6 +3439,8 @@ qla24xx_reset_risc(scsi_qla_host_t *vha)
 
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
+	qla_save_mbregs(vha);
+
 	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x015f,
 	    "Driver in %s mode\n",
 	    IS_NOPOLLING_TYPE(ha) ? "Interrupt" : "Polling");
@@ -3813,18 +3801,11 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
 	struct qla_hw_data *ha = vha->hw;
 	struct req_que *req = ha->req_q_map[0];
 	struct rsp_que *rsp = ha->rsp_q_map[0];
-	struct qla2xxx_fw_dump *fw_dump;
+	struct qla2xxx_fw_dump *fw_dump, *prev_fw_dump;
+	void *prev_mpi_fw_dump;
 	size_t req_entry_size = qla_req_entry_size(ha);
 	size_t rsp_entry_size = qla_rsp_entry_size(ha);
 
-	if (ha->fw_dump) {
-		ql_dbg(ql_dbg_init, vha, 0x00bd,
-		    "Firmware dump already allocated.\n");
-		return;
-	}
-
-	ha->fw_dumped = 0;
-	ha->fw_dump_cap_flags = 0;
 	dump_size = fixed_size = mem_size = eft_size = fce_size = mq_size = 0;
 	req_q_size = rsp_q_size = 0;
 
@@ -3907,13 +3888,11 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
 				ha->exlogin_size;
 	}
 
-	if (!ha->fw_dump_len || dump_size > ha->fw_dump_alloc_len) {
-
-		ql_dbg(ql_dbg_init, vha, 0x00c5,
-		    "%s dump_size %d fw_dump_len %d fw_dump_alloc_len %d\n",
-		    __func__, dump_size, ha->fw_dump_len,
-		    ha->fw_dump_alloc_len);
+	ql_dbg(ql_dbg_init, vha, 0x00c5,
+	    "%s dump_size %d fw_dump_len %d fw_dump_alloc_len %d\n",
+	    __func__, dump_size, ha->fw_dump_len, ha->fw_dump_alloc_len);
 
+	if (!ha->fw_dump_len || dump_size > ha->fw_dump_alloc_len) {
 		fw_dump = vmalloc(dump_size);
 		if (!fw_dump) {
 			ql_log(ql_log_warn, vha, 0x00c4,
@@ -3921,9 +3900,26 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
 			    dump_size / 1024);
 		} else {
 			mutex_lock(&ha->optrom_mutex);
-			if (ha->fw_dumped) {
-				memcpy(fw_dump, ha->fw_dump, ha->fw_dump_len);
-				vfree(ha->fw_dump);
+
+			if (ha->fw_dumped || ha->mpi_fw_dumped) {
+				prev_fw_dump = ha->fw_dump;
+
+				if (ha->fw_dumped)
+					memcpy(fw_dump, prev_fw_dump,
+					    ha->fw_dump_len);
+
+				if (IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+				    IS_QLA29XX(ha)) {
+					prev_mpi_fw_dump = ha->mpi_fw_dump;
+					ha->mpi_fw_dump = (char *)fw_dump +
+						ha->fwdt[0].dump_size;
+
+					if (ha->mpi_fw_dumped)
+						memcpy(ha->mpi_fw_dump,
+						    prev_mpi_fw_dump,
+						    ha->mpi_fw_dump_len);
+				}
+				vfree(prev_fw_dump);
 				ha->fw_dump = fw_dump;
 				ha->fw_dump_alloc_len =  dump_size;
 				ql_dbg(ql_dbg_init, vha, 0x00c5,
@@ -3942,7 +3938,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
 				if (IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
 				    IS_QLA29XX(ha)) {
 					ha->mpi_fw_dump = (char *)fw_dump +
-						ha->fwdt[1].dump_size;
+						ha->fwdt[0].dump_size;
 					mutex_unlock(&ha->optrom_mutex);
 					return;
 				}
@@ -4339,6 +4335,16 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
 
 		rval = qla2x00_verify_checksum(vha, srisc_address);
 		if (rval == QLA_SUCCESS) {
+			/*
+			 * Alloc a guestimate dump buffer to capture any failure
+			 * during early phase of driver load.
+			 */
+			if (ql2xallocfwdump &&
+			    (IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+			     IS_QLA29XX(ha)) &&
+			    !vha->flags.init_done)
+				qla2x00_alloc_fw_dump(vha);
+
 			/* Start firmware execution. */
 			ql_dbg(ql_dbg_init, vha, 0x00ca,
 			    "Starting firmware.\n");
@@ -4928,6 +4934,8 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
 		ql_dbg(ql_dbg_init, vha, 0x00d3,
 		    "Init Firmware -- success.\n");
 		vha->u_ql2xexchoffld = vha->u_ql2xiniexchg = 0;
+		vha->hw->flags.t262_fail = 0;
+		vha->hw->flags.t272_fail = 0;
 	}
 
 	return (rval);
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 5209dda45459..62c9bd0fe06d 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -3721,6 +3721,14 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (test_bit(UNLOADING, &base_vha->dpc_flags))
 		return -ENODEV;
 
+	/*
+	 * FW dump can happens before sysfs nodes are created.  If sysfs nodes
+	 * are unavailable then udev script will not be able to read the fw dump.
+	 * Notify udev to read again, now that sysfs nodes are available.
+	 */
+	if (ha->fw_dumped || ha->mpi_fw_dumped)
+		qla2x00_post_uevent_work(base_vha, QLA_UEVENT_CODE_FW_DUMP);
+
 	return 0;
 
 probe_failed:
diff --git a/drivers/scsi/qla2xxx/qla_tmpl.c b/drivers/scsi/qla2xxx/qla_tmpl.c
index b0a74b036cf4..fd3984127497 100644
--- a/drivers/scsi/qla2xxx/qla_tmpl.c
+++ b/drivers/scsi/qla2xxx/qla_tmpl.c
@@ -306,6 +306,12 @@ qla27xx_fwdt_entry_t262(struct scsi_qla_host *vha,
 		goto done;
 	}
 
+	if (vha->hw->flags.t262_fail) {
+		ql_dbg(ql_dbg_misc, vha, 0xd045,
+		    "%s: failed previously\n", __func__);
+		qla27xx_skip_entry(ent, buf);
+		goto done;
+	}
 	dwords = end - start + 1;
 	if (buf) {
 		buf += *len;
@@ -314,7 +320,12 @@ qla27xx_fwdt_entry_t262(struct scsi_qla_host *vha,
 			ql_dbg(ql_dbg_async, vha, 0xffff,
 			    "%s: dump ram MB failed. Area %xh start %lxh end %lxh\n",
 			    __func__, area, start, end);
-			return INVALID_ENTRY;
+
+			if (rc == QLA_FUNCTION_TIMEOUT)
+				vha->hw->flags.t262_fail = 1;
+
+			qla27xx_skip_entry(ent, buf);
+			goto done;
 		}
 	}
 	*len += dwords * sizeof(uint32_t);
@@ -536,13 +547,12 @@ qla27xx_fwdt_entry_t269(struct scsi_qla_host *vha,
 {
 	ql_dbg(ql_dbg_misc, vha, 0xd20d,
 	    "%s: scratch [%lx]\n", __func__, *len);
-	qla27xx_insert32(0xaaaaaaaa, buf, len);
-	qla27xx_insert32(0xbbbbbbbb, buf, len);
-	qla27xx_insert32(0xcccccccc, buf, len);
-	qla27xx_insert32(0xdddddddd, buf, len);
-	qla27xx_insert32(*len + sizeof(uint32_t), buf, len);
+
+	/* The data format is based on entry type t260. */
+	qla27xx_insert32(offsetof(struct device_reg_24xx, mailbox0), buf, len);
+	qla27xx_insertbuf(vha->hw->mbregs, sizeof(vha->hw->mbregs), buf, len);
 	if (buf)
-		ent->t269.scratch_size = 5 * sizeof(uint32_t);
+		ent->t269.scratch_size = sizeof(uint32_t) + sizeof(vha->hw->mbregs);
 
 	return qla27xx_next_entry(ent);
 }
@@ -589,17 +599,37 @@ qla27xx_fwdt_entry_t272(struct scsi_qla_host *vha,
 {
 	ulong dwords = le32_to_cpu(ent->t272.count);
 	ulong start = le32_to_cpu(ent->t272.addr);
+	int rc;
 
 	ql_dbg(ql_dbg_misc, vha, 0xd210,
 	    "%s: rdremram [%lx]\n", __func__, *len);
+
+	if (vha->hw->flags.t272_fail) {
+		ql_dbg(ql_dbg_misc, vha, 0xd04f,
+		    "%s: failed previously\n", __func__);
+		qla27xx_skip_entry(ent, buf);
+		goto done;
+	}
+
 	if (buf) {
 		ql_dbg(ql_dbg_misc, vha, 0xd02c,
 		    "%s: @%lx -> (%lx dwords)\n", __func__, start, dwords);
 		buf += *len;
-		qla27xx_dump_mpi_ram(vha->hw, start, buf, dwords, &buf);
+		rc = qla27xx_dump_mpi_ram(vha->hw, start, buf, dwords, &buf);
+		if (rc != QLA_SUCCESS) {
+			ql_log(ql_log_warn, vha, 0xd01b,
+			    "%s: dump mpi MB failed. Start %lxh dwords %lxh\n",
+			    __func__, start, dwords);
+
+			if (rc == QLA_FUNCTION_TIMEOUT)
+				vha->hw->flags.t272_fail = 1;
+
+			qla27xx_skip_entry(ent, buf);
+			goto done;
+		}
 	}
 	*len += dwords * sizeof(uint32_t);
-
+done:
 	return qla27xx_next_entry(ent);
 }
 
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 58/88] scsi: qla2xxx: Serialize flash version read in reset handler
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (56 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 57/88] scsi: qla2xxx: Improve firmware dump data capture Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  5:54 ` [PATCH v3 59/88] scsi: qla2xxx: Fix use-after-free of qpair work on queue teardown Nilesh Javali
                   ` (29 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

The "update cache versions without reset" sysfs reset operation (0x20261)
calls get_flash_version(), which reads hardware flash registers, without
holding ha->optrom_mutex. The VPD update path serializes the same call
under optrom_mutex, so this reset path can interleave its flash register
accesses with a concurrent VPD or optrom flash operation and corrupt the
reads.

Hold ha->optrom_mutex across the get_flash_version() call to match the
VPD update path.

Fixes: 8c2cf7d4e387 ("[SCSI] qla2xxx: Add a new interface to update versions.")
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_attr.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 3b24e8a5e29b..6a87d3bb0b0e 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -815,7 +815,9 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj,
 			    "Unable to allocate memory for VPD information update.\n");
 			return -ENOMEM;
 		}
+		mutex_lock(&ha->optrom_mutex);
 		ha->isp_ops->get_flash_version(vha, tmp_data);
+		mutex_unlock(&ha->optrom_mutex);
 		vfree(tmp_data);
 		break;
 	}
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 59/88] scsi: qla2xxx: Fix use-after-free of qpair work on queue teardown
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (57 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 58/88] scsi: qla2xxx: Serialize flash version read in reset handler Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:08   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 60/88] scsi: qla2xxx: Clarify MPI optrom address/length units Nilesh Javali
                   ` (28 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

The response queue MSI-X handler qla2xxx_msix_rsp_q() schedules
qla_do_work() via queue_work(ha->wq, &qpair->q_work). qla_do_work()
dereferences the qpair (vha, rsp) and takes qpair->qp_lock.

During teardown, qla2xxx_delete_qpair() deletes the response queue, which
calls free_irq() in qla25xx_free_rsp_que(), and then frees the queue and
the qpair. free_irq() waits for running hardirq handlers but does not
cancel work already placed on ha->wq. A still-pending q_work then runs
qla_do_work() against the freed qpair and response queue, causing a
use-after-free. This is especially likely during full adapter teardown,
where destroy_workqueue(ha->wq) forces pending work to run after the queue
pairs have been freed.

Flush the work item with cancel_work_sync() in qla25xx_free_rsp_que()
after free_irq() has released the interrupt (so no new work can be
queued) and before the response queue and qpair memory are freed (so the
flushed handler still sees valid memory). Guard on rsp->qpair and ha->wq
to match the INIT_WORK() condition and avoid operating on an
uninitialized work_struct.

Fixes: 68ca949cdb04 ("[SCSI] qla2xxx: Add CPU affinity support.")
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_mid.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index b7d9c1a53f3c..33bfc61d8165 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -606,6 +606,10 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
 		rsp->msix->handle = NULL;
 	}
 
+	/* Flush any queued response work before freeing the queue/qpair. */
+	if (rsp->qpair && ha->wq)
+		cancel_work_sync(&rsp->qpair->q_work);
+
 	if (rsp->ring)
 		dma_free_coherent(&ha->pdev->dev,
 				  (rsp->length + 1) * rsp_entry_size,
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 60/88] scsi: qla2xxx: Clarify MPI optrom address/length units
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (58 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 59/88] scsi: qla2xxx: Fix use-after-free of qpair work on queue teardown Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  5:54 ` [PATCH v3 61/88] scsi: qla2xxx: Fix cs84xx use-after-free on host teardown Nilesh Javali
                   ` (27 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

The kdoc for qla29xx_mpi_optrom_data() described @offset as an "Offset into
the device memory", which reads like a byte address and invites confusion
with the per-chunk word-granular address advance in the transfer loop.

MBC_LOAD_DUMP_MPI_RAM is word-addressed: @offset is an MPI RAM address in
32-bit words, and @length is a byte count that is converted internally to a
word count. Document this to reflect the existing behavior. No functional
change.

Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_sup.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
index 1f5596a4c405..d70ddba84305 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -562,8 +562,9 @@ static void set_chunk_mpi_bits(uint16_t *options, int count, int total)
  * @vha: Pointer to SCSI QLogic host structure.
  * @opts: Options for the operation.
  * @buf: Buffer to read from/write to.
- * @offset: Offset into the device memory.
- * @length: Length of data, in bytes.
+ * @offset: MPI RAM address, in 32-bit words (MBC_LOAD_DUMP_MPI_RAM is
+ *	    word-addressed; not a byte offset).
+ * @length: Length of data, in bytes (converted internally to a word count).
  * @op: Operation, either QLA29XX_MPI_OP_DUMP or QLA29XX_MPI_OP_LOAD.
  *
  * Returns:
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 61/88] scsi: qla2xxx: Fix cs84xx use-after-free on host teardown
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (59 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 60/88] scsi: qla2xxx: Clarify MPI optrom address/length units Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:11   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 62/88] scsi: qla2xxx: Don't query firmware state while chip is down Nilesh Javali
                   ` (26 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla84xx_put_chip() drops the last reference to ha->cs84xx and frees it via
__qla84xx_chip_release() without clearing ha->cs84xx. During teardown it ran
before scsi_remove_host(), which is what removes the 84xx_fw_version host
sysfs attribute. A concurrent read of that attribute in the window between
the two calls executes qla24xx_84xx_fw_version_show(), which dereferences
the freed ha->cs84xx, resulting in a use-after-free.

Move qla84xx_put_chip() to after scsi_remove_host() in both
qla2x00_remove_one() and qla2x00_disable_board_on_pci_error(). Once
scsi_remove_host() returns, the sysfs attribute is gone and kernfs has
drained any in-flight show(), so no reader can touch cs84xx; the put still
runs before the host and ha are freed.

Fixes: fe1b806f4f71 ("[SCSI] qla2xxx: Refactor shutdown code so some functionality can be reused.")
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_os.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 62c9bd0fe06d..4f485e4acf4a 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -4048,8 +4048,6 @@ qla2x00_remove_one(struct pci_dev *pdev)
 
 	qla2x00_dfs_remove(base_vha);
 
-	qla84xx_put_chip(base_vha);
-
 	/* Disable timer */
 	if (base_vha->timer_active)
 		qla2x00_stop_timer(base_vha);
@@ -4074,6 +4072,8 @@ qla2x00_remove_one(struct pci_dev *pdev)
 
 	scsi_remove_host(base_vha->host);
 
+	qla84xx_put_chip(base_vha);
+
 	qla2x00_free_device(base_vha);
 
 	qla2x00_clear_drv_active(ha);
@@ -6995,8 +6995,6 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work)
 
 	qla2x00_dfs_remove(base_vha);
 
-	qla84xx_put_chip(base_vha);
-
 	if (base_vha->timer_active)
 		qla2x00_stop_timer(base_vha);
 
@@ -7014,6 +7012,8 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work)
 
 	scsi_remove_host(base_vha->host);
 
+	qla84xx_put_chip(base_vha);
+
 	base_vha->flags.init_done = 0;
 	qla25xx_delete_queues(base_vha);
 	qla2x00_free_fcports(base_vha);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 62/88] scsi: qla2xxx: Don't query firmware state while chip is down
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (60 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 61/88] scsi: qla2xxx: Fix cs84xx use-after-free on host teardown Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:03   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 63/88] scsi: qla2xxx: Zero mailbox struct in qla2x00_get_firmware_state() Nilesh Javali
                   ` (25 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla2x00_fw_state_show() initializes rval to QLA_FUNCTION_FAILED and jumps
to the out: label when the chip is down or EEH is busy. The out: block
then re-issued qla2x00_get_firmware_state() because rval != QLA_SUCCESS,
defeating the chip-down/EEH-busy guards and issuing a mailbox command
(outside optrom_mutex) during ISP reset or PCI error recovery, which can
hang the adapter. It also turned a normal in-lock mailbox failure into a
second unsynchronized mailbox attempt.

Make the out: fallback only mark the firmware state as unknown. The
mailbox is now issued at most once, inside optrom_mutex, and only when
the chip is up and not EEH-busy.

Fixes: b6faaaf796d7 ("scsi: qla2xxx: Serialize mailbox request")
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_attr.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 6a87d3bb0b0e..a4ca22024ede 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1678,10 +1678,8 @@ qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr,
 	rval = qla2x00_get_firmware_state(vha, state);
 	mutex_unlock(&vha->hw->optrom_mutex);
 out:
-	if (rval != QLA_SUCCESS) {
+	if (rval != QLA_SUCCESS)
 		memset(state, -1, sizeof(state));
-		rval = qla2x00_get_firmware_state(vha, state);
-	}
 
 	return scnprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
 	    state[0], state[1], state[2], state[3], state[4], state[5]);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 63/88] scsi: qla2xxx: Zero mailbox struct in qla2x00_get_firmware_state()
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (61 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 62/88] scsi: qla2xxx: Don't query firmware state while chip is down Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:18   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 64/88] scsi: qla2xxx: Fix FCE trace enable parsing in debugfs Nilesh Javali
                   ` (24 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

The mbx_cmd_t is allocated on the stack but left uninitialized.
qla2x00_mailbox_command() has several early-return paths (PCI permanent
failure, device failed, EEH busy, ISP abort pending, mailbox access
timeout, purge mbox) that return without writing the input mailbox
registers back into mcp->mb[]. qla2x00_get_firmware_state() then
unconditionally copies mcp->mb[1..6] (and mb[12]) into the caller's
states[] array regardless of the return value.

On such a failure the copied values are uninitialized kernel stack
memory, which is then exposed to userspace via the fw_state and
mpi_fw_state sysfs handlers. Zero the mailbox struct so a failed query
yields deterministic zeroed state instead of leaking stack contents.

Fixes: 4d4df1932b6b ("[SCSI] qla2xxx: Add ISP84XX support.")
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_mbx.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index ba4a4764de1f..ab5648eb5f20 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -2276,6 +2276,8 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
 	if (!ha->flags.fw_started)
 		return QLA_FUNCTION_FAILED;
 
+	memset(&mc, 0, sizeof(mc));
+
 	mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
 	mcp->out_mb = MBX_0;
 	if (IS_FWI2_CAPABLE(vha->hw))
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 64/88] scsi: qla2xxx: Fix FCE trace enable parsing in debugfs
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (62 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 63/88] scsi: qla2xxx: Zero mailbox struct in qla2x00_get_firmware_state() Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:11   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 65/88] scsi: qla2xxx: Fix FCE trace use-after-free during firmware dump Nilesh Javali
                   ` (23 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla2x00_dfs_fce_write() called kstrtoul() with a NULL result pointer,
so a successful parse would dereference NULL and oops. Worse, the int
return value (0 on success, negative errno on failure) was assigned to
the unsigned long enable flag, inverting the intended logic: a valid
number was treated as "disable" while a parse failure enabled FCE.

Parse the value into enable and propagate parse errors to userspace.

Fixes: 841df27d619e ("scsi: qla2xxx: Move FCE Trace buffer allocation to user control")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_dfs.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c
index 177d47e92e49..5d08bdbcf70a 100644
--- a/drivers/scsi/qla2xxx/qla_dfs.c
+++ b/drivers/scsi/qla2xxx/qla_dfs.c
@@ -510,7 +510,9 @@ qla2x00_dfs_fce_write(struct file *file, const char __user *buffer,
 		return PTR_ERR(buf);
 	}
 
-	enable = kstrtoul(buf, 0, 0);
+	rc = kstrtoul(buf, 0, &enable);
+	if (rc)
+		goto out_free;
 	rc = count;
 
 	mutex_lock(&ha->fce_mutex);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 65/88] scsi: qla2xxx: Fix FCE trace use-after-free during firmware dump
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (63 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 64/88] scsi: qla2xxx: Fix FCE trace enable parsing in debugfs Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:25   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 66/88] scsi: qla2xxx: Use memset_io() to clear QLAFX00 request ring slot Nilesh Javali
                   ` (22 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla2x00_free_fce_trace() freed and cleared ha->fce while holding only
fce_mutex. The firmware-dump consumers qla27xx_fwdt_entry_t264() and
qla25xx_copy_fce() read ha->fce (NULL check followed by a copy of the
buffer) under hardware_lock and never take fce_mutex. A debugfs FCE
disable could therefore free the DMA buffer between a dump's NULL check
and its copy, resulting in a use-after-free.

Unpublish ha->fce under hardware_lock, then release the lock and free
the DMA buffer (dma_free_coherent() may sleep). A concurrent dump either
completes its check and copy with the buffer still valid, or observes
ha->fce == NULL and skips it.

Fixes: 841df27d619e ("scsi: qla2xxx: Move FCE Trace buffer allocation to user control")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_init.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 9d6b229bd352..76b7ed501b04 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -3752,11 +3752,27 @@ int qla2x00_alloc_fce_trace(scsi_qla_host_t *vha)
 
 void qla2x00_free_fce_trace(struct qla_hw_data *ha)
 {
-	if (!ha->fce)
+	void *fce;
+	dma_addr_t fce_dma;
+	unsigned long flags;
+
+	/*
+	 * Unpublish ha->fce under hardware_lock so a firmware dump in
+	 * progress (which reads ha->fce under the same lock) cannot race
+	 * with the buffer being freed.
+	 */
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	if (!ha->fce) {
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
 		return;
-	dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce, ha->fce_dma);
+	}
+	fce = ha->fce;
+	fce_dma = ha->fce_dma;
 	ha->fce = NULL;
 	ha->fce_dma = 0;
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	dma_free_coherent(&ha->pdev->dev, FCE_SIZE, fce, fce_dma);
 }
 
 static void
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 66/88] scsi: qla2xxx: Use memset_io() to clear QLAFX00 request ring slot
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (64 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 65/88] scsi: qla2xxx: Fix FCE trace use-after-free during firmware dump Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:26   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 67/88] scsi: qla2xxx: Null out freed pointers in qla2x00_mem_alloc() error path Nilesh Javali
                   ` (21 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

For QLAFX00 the request ring is ioremapped device I/O memory
(ha->iobase + req_que_off), not DMA-coherent RAM, which is why the rest
of the FX00 path accesses it through memcpy_toio() and the wrt_reg_*
helpers. __qla2x00_alloc_iocbs() however zeroed the producer slot with a
plain memset(). On architectures such as ARM64 a regular memset() may
emit unaligned or block-zeroing instructions (e.g. DC ZVA) that are
invalid on Device memory, leading to a synchronous external abort.

Use memset_io() to clear the slot for QLAFX00, matching the I/O
accessors used elsewhere on this ring. Other adapters keep the plain
memset() on their DMA-coherent rings. The zero-fill is retained for FX00
because its IOCB builders (e.g. qlafx00_fxdisc_iocb()) copy only part of
the entry and rely on the unused tail being pre-zeroed.

Fixes: 8ae6d9c7eb10 ("[SCSI] qla2xxx: Enhancements to support ISPFx00.")
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_iocb.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 22f2d81e2009..c81df5a96014 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -2454,11 +2454,13 @@ __qla2x00_alloc_iocbs(struct qla_qpair *qpair, srb_t *sp)
 	 */
 	req->cnt -= req_cnt;
 	pkt = qla_req_ring_slot(ha, req);
-	memset(pkt, 0, qla_req_entry_size(ha));
 	if (IS_QLAFX00(ha)) {
+		memset_io((void __iomem __force *)pkt, 0,
+			  qla_req_entry_size(ha));
 		wrt_reg_byte((u8 __force __iomem *)&pkt->entry_count, req_cnt);
 		wrt_reg_dword((__le32 __force __iomem *)&pkt->handle, handle);
 	} else {
+		memset(pkt, 0, qla_req_entry_size(ha));
 		pkt->entry_count = req_cnt;
 		pkt->handle = handle;
 	}
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 67/88] scsi: qla2xxx: Null out freed pointers in qla2x00_mem_alloc() error path
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (65 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 66/88] scsi: qla2xxx: Use memset_io() to clear QLAFX00 request ring slot Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:25   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 68/88] scsi: qla2xxx: Fix response queue over-consumption in __qla_consume_iocb() Nilesh Javali
                   ` (20 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

When qla2x00_mem_alloc() fails, qla2x00_probe_one() jumps to
probe_hw_failed and calls qla2x00_mem_free(). Several error labels in
qla2x00_mem_alloc() freed adapter members (elsrej.c, purex_dma_pool,
flt, sfp_data, loop_id_map, async_pd, sf_init_cb, ex_init_cb, npiv_info)
but left the pointers dangling. qla2x00_mem_free() then freed them a
second time. Worse, for the dma_pool members it issued
dma_pool_free(ha->s_dma_pool, ...) after s_dma_pool had already been
destroyed and set to NULL at fail_s_dma_pool, dereferencing a NULL pool.

Clear each freed pointer (and its DMA handle) in the error labels so the
subsequent qla2x00_mem_free() skips them.

Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_os.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 4f485e4acf4a..918b00aed8b8 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -4619,28 +4619,43 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 fail_lsrjt:
 	dma_free_coherent(&ha->pdev->dev, ha->elsrej.size,
 			  ha->elsrej.c, ha->elsrej.cdma);
+	ha->elsrej.c = NULL;
+	ha->elsrej.cdma = 0;
 fail_elsrej:
 	dma_pool_destroy(ha->purex_dma_pool);
+	ha->purex_dma_pool = NULL;
 fail_flt_data:
 	vfree(ha->flt_data);
 	ha->flt_data = NULL;
 fail_flt:
 	dma_free_coherent(&ha->pdev->dev, sizeof(struct qla_flt_header) + FLT_REGIONS_SIZE,
 	    ha->flt, ha->flt_dma);
+	ha->flt = NULL;
+	ha->flt_dma = 0;
 
 fail_flt_buffer:
 	dma_free_coherent(&ha->pdev->dev, SFP_DEV_SIZE,
 	    ha->sfp_data, ha->sfp_data_dma);
+	ha->sfp_data = NULL;
+	ha->sfp_data_dma = 0;
 fail_sfp_data:
 	kfree(ha->loop_id_map);
+	ha->loop_id_map = NULL;
 fail_loop_id_map:
 	dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma);
+	ha->async_pd = NULL;
+	ha->async_pd_dma = 0;
 fail_async_pd:
 	dma_pool_free(ha->s_dma_pool, ha->sf_init_cb, ha->sf_init_cb_dma);
+	ha->sf_init_cb = NULL;
+	ha->sf_init_cb_dma = 0;
 fail_sf_init_cb:
 	dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma);
+	ha->ex_init_cb = NULL;
+	ha->ex_init_cb_dma = 0;
 fail_ex_init_cb:
 	kfree(ha->npiv_info);
+	ha->npiv_info = NULL;
 fail_npiv_info:
 	dma_free_coherent(&ha->pdev->dev,
 		((*rsp)->length + 1) * rsp_entry_size,
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 68/88] scsi: qla2xxx: Fix response queue over-consumption in __qla_consume_iocb()
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (66 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 67/88] scsi: qla2xxx: Null out freed pointers in qla2x00_mem_alloc() error path Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  5:54 ` [PATCH v3 69/88] scsi: qla2xxx: Fix soft lockup polling continuation IOCB signature Nilesh Javali
                   ` (19 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla24xx_process_response_queue() advances ring_ptr past the head IOCB
before dispatching, so by the time __qla_consume_iocb() runs, ring_ptr
already points at the first continuation IOCB. The function however
looped purex->entry_count times starting at ring_ptr. As entry_count
includes the head, this consumed one entry too many: it stamped
RESPONSE_PROCESSED on the next, unrelated IOCB and advanced the ring
past it, silently dropping a legitimate firmware response. The head
IOCB's signature was also never marked.

Mark the head processed and account for it, then consume only the
entry_count - 1 continuation IOCBs, matching __qla_copy_purex_to_buffer().

Fixes: fac2807946c1 ("scsi: qla2xxx: edif: Add extraction of auth_els from the wire")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_isr.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index bca052fb3c8b..c36a2c69c219 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -259,6 +259,17 @@ void __qla_consume_iocb(struct scsi_qla_host *vha,
 	struct purex_entry_24xx *purex = *pkt;
 
 	entry_count_remaining = purex->entry_count;
+
+	/*
+	 * The caller already advanced ring_ptr past the head IOCB, so mark
+	 * the head processed and account for it here, then consume only the
+	 * continuation IOCBs that follow.
+	 */
+	((response_t *)purex)->signature = RESPONSE_PROCESSED;
+	/* flush signature */
+	wmb();
+	--entry_count_remaining;
+
 	while (entry_count_remaining > 0) {
 		new_pkt = rsp_q->ring_ptr;
 		*pkt = new_pkt;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 69/88] scsi: qla2xxx: Fix soft lockup polling continuation IOCB signature
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (67 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 68/88] scsi: qla2xxx: Fix response queue over-consumption in __qla_consume_iocb() Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:19   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 70/88] scsi: qla2xxx: Bound rsp_info_len to avoid OOB sense-data read Nilesh Javali
                   ` (18 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla27xx_copy_multiple_pkt() and qla27xx_copy_fpin_pkt() poll
rsp_q->ring_ptr->signature for RESPONSE_PROCESSED (0xDEADDEAD) to decide
whether the next continuation IOCB has arrived, spinning on cpu_relax()
without advancing the ring or decrementing the entry count while it has
not. response_t::signature lives at byte offset 60, but a continuation
IOCB (sts_cont_entry_t / struct sts_cont_entry_ext) carries raw FC frame
payload at that offset (data[56..59]). A received frame whose payload
bytes happen to equal 0xDEADDEAD is therefore misread as "not yet
arrived", and the loop spins forever in interrupt/DPC context, causing a
CPU soft lockup.

The poll is also unnecessary: callers of qla27xx_copy_multiple_pkt()
(PT_LS4_UNSOL and the NVMe purls path) already gate on
qla_chk_cont_iocb_avail(), which guarantees all entry_count IOCBs are
present before copying begins. The sibling helper
__qla_copy_purex_to_buffer() already drops the signature poll and relies
on the entry_type == STATUS_CONT_TYPE guard instead.

Remove the signature busy-wait from both helpers, keeping the entry_type
guard, and gate the FPIN path with qla_chk_cont_iocb_avail() so it defers
and re-processes on the next interrupt once all continuation IOCBs have
arrived, mirroring the ELS_AUTH_ELS and PT_LS4_UNSOL arms. With this the
signature field is never read on a continuation IOCB, eliminating the
payload-aliasing lockup.

Fixes: 9f2475fe7406 ("scsi: qla2xxx: SAN congestion management implementation")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_isr.c | 33 ++++++++++++++++-----------------
 1 file changed, 16 insertions(+), 17 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index c36a2c69c219..497a0fef742f 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -1002,14 +1002,6 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
 
 	do {
 		while ((total_bytes > 0) && (entry_count_remaining > 0)) {
-			if (rsp_q->ring_ptr->signature == RESPONSE_PROCESSED) {
-				ql_dbg(ql_dbg_async, vha, 0x5084,
-				       "Ran out of IOCBs, partial data 0x%x\n",
-				       buffer_copy_offset);
-				cpu_relax();
-				continue;
-			}
-
 			*pkt = rsp_q->ring_ptr;
 			data = ((sts_cont_entry_t *)*pkt)->data;
 			data_sz = qla_sts_cont_data_size(ha);
@@ -1299,14 +1291,6 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
 
 	do {
 		while ((total_bytes > 0) && (entry_count_remaining > 0)) {
-			if (rsp_q->ring_ptr->signature == RESPONSE_PROCESSED) {
-				ql_dbg(ql_dbg_async, vha, 0x5084,
-				       "Ran out of IOCBs, partial data 0x%x\n",
-				       buffer_copy_offset);
-				cpu_relax();
-				continue;
-			}
-
 			*pkt = rsp_q->ring_ptr;
 			data = ((sts_cont_entry_t *)*pkt)->data;
 			data_sz = qla_sts_cont_data_size(ha);
@@ -4271,9 +4255,24 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 					       "SCM not active for this port\n");
 					break;
 				}
+				if (qla_chk_cont_iocb_avail(vha, rsp,
+				    (response_t *)pkt, rsp_in)) {
+					/*
+					 * ring_ptr and ring_index were
+					 * pre-incremented above. Reset them
+					 * back to current. Wait for next
+					 * interrupt with all IOCBs to arrive
+					 * and re-process.
+					 */
+					qla_rsp_ring_rewind_to(rsp,
+					    (response_t *)pkt, cur_ring_index);
+
+					ql_dbg(ql_dbg_init, vha, 0x5095,
+					    "Defer processing FPIN...\n");
+					return;
+				}
 				pure_item = qla27xx_copy_fpin_pkt(vha,
 							  (void **)&pkt, &rsp);
-				__update_rsp_in(is_shadow_hba, rsp, rsp_in);
 				if (!pure_item)
 					break;
 				qla24xx_queue_purex_item(vha, pure_item,
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 70/88] scsi: qla2xxx: Bound rsp_info_len to avoid OOB sense-data read
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (68 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 69/88] scsi: qla2xxx: Fix soft lockup polling continuation IOCB signature Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:24   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 71/88] scsi: qla2xxx: Avoid req_q_map double-read in qla2x00_error_entry() Nilesh Javali
                   ` (17 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

In qla2x00_status_entry(), the FWI2 status path advances sense_data and
shrinks par_sense_len by rsp_info_len:

	if (IS_FWI2_CAPABLE(ha)) {
		sense_data += rsp_info_len;
		par_sense_len -= rsp_info_len;
	}

rsp_info_len is a 32-bit value taken directly from the target's FCP
response (sf.rsp_data_len), while par_sense_len is the IOCB data area
size (28 bytes for 24xx, 60 bytes for 29xx). A hostile or buggy target
reporting an rsp_info_len larger than par_sense_len makes the unsigned
subtraction underflow to a huge value and advances sense_data out of
bounds.

The underflowed par_sense_len then defeats the cap in
qla2x00_handle_sense():

	if (sense_len > par_sense_len)
		sense_len = par_sense_len;
	memcpy(cp->sense_buffer, sense_data, sense_len);

so the memcpy reads up to SCSI_SENSE_BUFFERSIZE bytes from the
out-of-bounds sense_data pointer, leaking adjacent response-ring/heap
memory into the command's sense buffer.

Clamp rsp_info_len to par_sense_len before the subtraction so
par_sense_len can never underflow and sense_data stays within the IOCB
data area. The fix sits before the comp_status switch, covering both
qla2x00_handle_sense() call sites.

Fixes: 5544213be7b4 ("[SCSI] qla2xxx: Correct extended sense-data handling.")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_isr.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 497a0fef742f..91a8344fea6c 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -3598,6 +3598,18 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
 	if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) {
 		/* Sense data lies beyond any FCP RESPONSE data. */
 		if (IS_FWI2_CAPABLE(ha)) {
+			/*
+			 * A hostile or buggy target may report an
+			 * rsp_info_len larger than the IOCB data area.
+			 * Clamp it so the par_sense_len subtraction cannot
+			 * underflow and walk sense_data out of bounds.
+			 */
+			if (rsp_info_len > par_sense_len) {
+				ql_log(ql_log_warn, fcport->vha, 0x3107,
+				       "Truncating bogus rsp_info_len 0x%x to 0x%x.\n",
+				       rsp_info_len, par_sense_len);
+				rsp_info_len = par_sense_len;
+			}
 			sense_data += rsp_info_len;
 			par_sense_len -= rsp_info_len;
 		}
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 71/88] scsi: qla2xxx: Avoid req_q_map double-read in qla2x00_error_entry()
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (69 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 70/88] scsi: qla2xxx: Bound rsp_info_len to avoid OOB sense-data read Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:21   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 72/88] scsi: qla2xxx: Quiesce response IRQ before freeing request queue Nilesh Javali
                   ` (16 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla2x00_error_entry() reads ha->req_q_map[que] twice: once for the NULL
check and again when assigning it to req. The map slot is cleared by
qla25xx_free_req_que() (ha->req_q_map[que_id] = NULL under mq_lock)
during queue teardown, while the response-queue interrupt that drives
qla2x00_error_entry() is still registered (the IRQ is released later in
qla25xx_free_rsp_que()). If the slot is set to NULL between the two
reads, req becomes NULL and is dereferenced.

Read the slot once into req and NULL-check the local before use. mq_lock
is a mutex and cannot be taken from interrupt context, so the single
read plus local check is the appropriate fix for the reported NULL
dereference.

Fixes: a6fe35c052c4 ("[SCSI] qla2xxx: Avoid invalid request queue dereference for bad response packets.")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_isr.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 91a8344fea6c..829671937f92 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -3927,10 +3927,12 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
 	    "iocb type %xh with error status %xh, handle %xh, rspq id %d\n",
 	    pkt->entry_type, pkt->entry_status, pkt->handle, rsp->id);
 
-	if (que >= ha->max_req_queues || !ha->req_q_map[que])
+	if (que >= ha->max_req_queues)
 		goto fatal;
 
 	req = ha->req_q_map[que];
+	if (!req)
+		goto fatal;
 
 	if (pkt->entry_status & RF_BUSY)
 		res = DID_BUS_BUSY << 16;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 72/88] scsi: qla2xxx: Quiesce response IRQ before freeing request queue
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (70 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 71/88] scsi: qla2xxx: Avoid req_q_map double-read in qla2x00_error_entry() Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:22   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 73/88] scsi: qla2xxx: Reject non-SCSI SRB on status IOCB fast path Nilesh Javali
                   ` (15 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla2xxx_delete_qpair() deletes the request queue before the response
queue. qla25xx_delete_req_que() frees the request queue memory
(kfree(req) in qla25xx_free_req_que()), but the response-queue MSI-X is
only released later, in qla25xx_free_rsp_que(). In that window the
response interrupt can still fire, qla2xxx_msix_rsp_q() queues
qpair->q_work, and qla_do_work() -> qla24xx_process_response_queue()
dereferences the now-freed rsp->req (LOGINOUT/CT/ELS entries and the
status path), a use-after-free.

The cancel_work_sync() added for the qpair teardown lives in the
response free path, which runs after the request queue is already freed,
so it does not protect rsp->req.

Release the response-queue interrupt and flush qpair->q_work before
deleting the request queue, so no late completion can reach the freed
request queue. Clearing have_irq makes the subsequent
qla25xx_free_rsp_que() skip its free_irq(), and the firmware
queue-delete order (request then response) is preserved; the
request-delete mailbox completes on the default vector and is unaffected
by dropping the qpair response interrupt early.

Fixes: d74595278f4a ("scsi: qla2xxx: Add multiple queue pair functionality.")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_init.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 76b7ed501b04..d678e27213a9 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -10686,11 +10686,28 @@ int qla2xxx_delete_qpair(struct scsi_qla_host *vha, struct qla_qpair *qpair)
 {
 	int ret = QLA_FUNCTION_FAILED;
 	struct qla_hw_data *ha = qpair->hw;
+	struct rsp_que *rsp = qpair->rsp;
 
 	qpair->delete_in_progress = 1;
 
 	qla_free_buf_pool(qpair);
 
+	/*
+	 * The response-queue interrupt schedules qla_do_work(), which
+	 * dereferences qpair->rsp->req.  Release the interrupt and flush
+	 * any pending work before the request queue is freed below so a
+	 * late completion cannot touch the freed request queue.  The
+	 * firmware queue-delete order (request then response) is kept.
+	 */
+	if (rsp && rsp->msix && rsp->msix->have_irq) {
+		free_irq(rsp->msix->vector, rsp->msix->handle);
+		rsp->msix->have_irq = 0;
+		rsp->msix->in_use = 0;
+		rsp->msix->handle = NULL;
+	}
+	if (rsp && ha->wq)
+		cancel_work_sync(&qpair->q_work);
+
 	ret = qla25xx_delete_req_que(vha, qpair->req);
 	if (ret != QLA_SUCCESS)
 		goto fail;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 73/88] scsi: qla2xxx: Reject non-SCSI SRB on status IOCB fast path
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (71 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 72/88] scsi: qla2xxx: Quiesce response IRQ before freeing request queue Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  5:54 ` [PATCH v3 74/88] scsi: qla2xxx: Clamp max_npiv_vports to VP_CTRL bitmap capacity Nilesh Javali
                   ` (14 subsequent siblings)
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla2x00_status_entry() filters out non-TYPE_SRB entries and the
SRB_NVME_CMD, SRB_BIDI_CMD and SRB_TM_CMD types, then falls through to a
SCSI fast path that assumes the command is an SRB_SCSI_CMD. The first
thing on that path, qla_chk_edif_rx_sa_delete_pending(), and the
subsequent handling both evaluate GET_CMD_SP(sp), i.e. sp->u.scmd.cmd.

The srb u union overlays the SCSI command pointer with other command
layouts (bsg_job, iocb_cmd). If firmware delivers an unexpected
STATUS_TYPE IOCB for a non-SCSI handle, sp->u.scmd.cmd can read as a
non-NULL garbage pointer, bypassing the NULL checks in
qla_chk_edif_rx_sa_delete_pending() and at the cp == NULL test, and
leading to a wild pointer dereference.

Reject any SRB whose type is not SRB_SCSI_CMD before entering the fast
path. The outstanding_cmds slot is left untouched so a genuinely
non-SCSI command still completes through its proper handler.

Fixes: dd30706e73b7 ("scsi: qla2xxx: edif: Add key update")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_dbg.c | 2 +-
 drivers/scsi/qla2xxx/qla_isr.c | 8 ++++++++
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index 4f756468ea64..196cfa8f8623 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -16,7 +16,7 @@
  * |                              |                    | 0x2127-0x2128  |
  * | Queue Command and IO tracing |       0x3074       | 0x300b         |
  * |                              |                    | 0x3027-0x3028  |
- * |                              |                    | 0x303d-0x3041  |
+ * |                              |                    | 0x303e-0x3041  |
  * |                              |                    | 0x302e,0x3033  |
  * |                              |                    | 0x3036,0x3038  |
  * |                              |                    | 0x303a		|
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 829671937f92..96b4721b4810 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -3541,6 +3541,14 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
 		return;
 	}
 
+	/* Everything below is the SCSI fast path; reject other SRB types. */
+	if (sp->type != SRB_SCSI_CMD) {
+		ql_dbg(ql_dbg_io, vha, 0x303d,
+		    "Unexpected SRB type %x for status IOCB, sp %p.\n",
+		    sp->type, sp);
+		return;
+	}
+
 	/* Fast path completion. */
 	qla_chk_edif_rx_sa_delete_pending(vha, sp, pkt);
 	sp->qpair->cmd_completion_cnt++;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 74/88] scsi: qla2xxx: Clamp max_npiv_vports to VP_CTRL bitmap capacity
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (72 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 73/88] scsi: qla2xxx: Reject non-SCSI SRB on status IOCB fast path Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:34   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 75/88] scsi: qla2xxx: Avoid double completion in async IOCB timeout Nilesh Javali
                   ` (13 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

ha->max_npiv_vports is taken from firmware (mcp->mb[11]) and only
constrained so that (max_npiv_vports + 1) is a multiple of
MIN_MULTI_ID_FABRIC, which permits values of 63, 127, 191 and 255.
NPIV vports are then allocated up to that count.

VP enable uses the VP_CONFIG IOCB, which addresses a vport through a
plain vp_index byte, so a vp_index beyond 128 is enabled without issue.
VP disable, however, uses the VP_CTRL IOCB, which selects target vports
through the fixed 128-bit vp_idx_map bitmap. qla24xx_control_vp()
rejects a vp_index past that bitmap and the IOCB builder cannot set a bit
beyond 127, yet qla24xx_vport_delete() frees the local state regardless.
A vport with vp_index > 128 can therefore be created and enabled but
never disabled, leaving it permanently active in firmware: a resource
leak.

Cap ha->max_npiv_vports at init to the vp_idx_map capacity so such
vports are never created. This collapses 191/255 to 127 (still
modulo-valid) and leaves the real-world 63/127 cases unaffected.

Fixes: 4d0ea24769c8 ("[SCSI] qla2xxx: Retrieve max-NPIV support capabilities from FW.")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_fw.h   |  4 ++++
 drivers/scsi/qla2xxx/qla_init.c | 13 +++++++++++++
 drivers/scsi/qla2xxx/qla_mid.c  |  2 +-
 3 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index b29abcc7f74f..98bc4a57b59b 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -1442,6 +1442,10 @@ struct vp_ctrl_entry_24xx {
 	uint8_t reserved_5[24];
 };
 
+/* vp_idx_map is a 128-bit (16-byte) bitmap selecting target VPs. */
+#define VP_CTRL_IDX_MAP_BITS \
+	(sizeof_field(struct vp_ctrl_entry_24xx, vp_idx_map) * 8)
+
 /*
  * Modify Virtual Port Configuration IOCB
  */
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index d678e27213a9..1f20ab386003 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -4412,6 +4412,19 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
 					    MIN_MULTI_ID_FABRIC))
 						ha->max_npiv_vports =
 						    MIN_MULTI_ID_FABRIC - 1;
+
+					/*
+					 * The VP_CTRL IOCB selects target VPs
+					 * through the fixed vp_idx_map bitmap,
+					 * so a vp_index beyond it can be enabled
+					 * via VP_CONFIG but never disabled via
+					 * VP_CTRL, leaking the VP.  Cap the count
+					 * to the bitmap capacity.
+					 */
+					if (ha->max_npiv_vports >=
+					    VP_CTRL_IDX_MAP_BITS)
+						ha->max_npiv_vports =
+						    VP_CTRL_IDX_MAP_BITS - 1;
 				}
 				qlt_config_nvram_with_fw_version(vha);
 				qla2x00_get_resource_cnts(vha);
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index 33bfc61d8165..4ad23d206add 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -996,7 +996,7 @@ int qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
 	 * (16-byte) vp_idx_map bitmap, so vp_index must fit within it even
 	 * if firmware advertises more NPIV vports.
 	 */
-	if (vp_index > sizeof_field(struct vp_ctrl_entry_24xx, vp_idx_map) * 8)
+	if (vp_index > VP_CTRL_IDX_MAP_BITS)
 		return QLA_PARAMETER_ERROR;
 
 	/* ref: INIT */
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 75/88] scsi: qla2xxx: Avoid double completion in async IOCB timeout
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (73 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 74/88] scsi: qla2xxx: Clamp max_npiv_vports to VP_CTRL bitmap capacity Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:37   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 76/88] scsi: qla2xxx: Skip vport under deletion in report ID acquisition Nilesh Javali
                   ` (12 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla2x00_async_iocb_timeout() tries to abort a timed-out async IOCB. When
qla24xx_async_abort_cmd() fails, both the SRB_LOGIN_CMD path and the
SRB_CTRL_VP/default path scan outstanding_cmds[] for the SRB and then
call sp->done(sp, QLA_FUNCTION_TIMEOUT) unconditionally, without checking
whether the SRB was actually found and removed.

If the response ISR completes the same handle first, it removes the SRB
under qp_lock_ptr and runs sp->done() -> complete(sp->comp). The
submitter qla24xx_control_vp() wakes from wait_for_completion(), clears
sp->comp, drops its reference and returns, reclaiming the on-stack
completion. The timer reference keeps the SRB alive across the timeout
handler, but not the submitter's stack. The timeout then issues a second
sp->done() -> qla_ctrlvp_sp_done(), which evaluates "if (sp->comp)
complete(sp->comp)"; with the pointer loaded before the submitter's NULL
store, complete() writes into the freed stack frame, a use-after-free.

Track whether this path removed the SRB from outstanding_cmds and only
call sp->done() when it did, so the command is completed exactly once by
whichever path owns it. This mirrors the sp_found guard already used in
qla24xx_abort_iocb_timeout().

Fixes: f6145e86d21f ("scsi: qla2xxx: Fix race between switch cmd completion and timeout")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_init.c | 24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 1f20ab386003..d935fe5e5316 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -228,7 +228,7 @@ qla2x00_async_iocb_timeout(void *data)
 	srb_t *sp = data;
 	fc_port_t *fcport = sp->fcport;
 	struct srb_iocb *lio = &sp->u.iocb_cmd;
-	int rc, h;
+	int rc, h, found;
 	unsigned long flags;
 
 	if (fcport) {
@@ -251,6 +251,7 @@ qla2x00_async_iocb_timeout(void *data)
 			lio->u.logio.data[1] =
 				lio->u.logio.flags & SRB_LOGIN_RETRIED ?
 				QLA_LOGIO_LOGIN_RETRIED : 0;
+			found = 0;
 			spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags);
 			for (h = 1; h < sp->qpair->req->num_outstanding_cmds;
 			    h++) {
@@ -258,11 +259,19 @@ qla2x00_async_iocb_timeout(void *data)
 				    sp) {
 					sp->qpair->req->outstanding_cmds[h] =
 					    NULL;
+					found = 1;
 					break;
 				}
 			}
 			spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags);
-			sp->done(sp, QLA_FUNCTION_TIMEOUT);
+			/*
+			 * Only complete the command if this path removed it
+			 * from outstanding_cmds.  Otherwise the ISR already
+			 * completed it and a second sp->done() would race the
+			 * submitter's freeing of the on-stack completion.
+			 */
+			if (found)
+				sp->done(sp, QLA_FUNCTION_TIMEOUT);
 		}
 		break;
 	case SRB_LOGOUT_CMD:
@@ -275,6 +284,7 @@ qla2x00_async_iocb_timeout(void *data)
 	default:
 		rc = qla24xx_async_abort_cmd(sp, false);
 		if (rc) {
+			found = 0;
 			spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags);
 			for (h = 1; h < sp->qpair->req->num_outstanding_cmds;
 			    h++) {
@@ -282,11 +292,19 @@ qla2x00_async_iocb_timeout(void *data)
 				    sp) {
 					sp->qpair->req->outstanding_cmds[h] =
 					    NULL;
+					found = 1;
 					break;
 				}
 			}
 			spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags);
-			sp->done(sp, QLA_FUNCTION_TIMEOUT);
+			/*
+			 * Only complete the command if this path removed it
+			 * from outstanding_cmds.  Otherwise the ISR already
+			 * completed it and a second sp->done() would race the
+			 * submitter's freeing of the on-stack completion.
+			 */
+			if (found)
+				sp->done(sp, QLA_FUNCTION_TIMEOUT);
 		}
 		break;
 	}
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 76/88] scsi: qla2xxx: Skip vport under deletion in report ID acquisition
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (74 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 75/88] scsi: qla2xxx: Avoid double completion in async IOCB timeout Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:27   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 77/88] scsi: qla2xxx: Drop vport reference under lock " Nilesh Javali
                   ` (11 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla24xx_report_id_acquisition() format-1 handling walks ha->vp_list under
vport_slock, takes a vref_count on the matching vport and calls
qla_update_host_map() to register its port id.

A vport teardown via qla24xx_vport_delete() sets VPORT_DELETE, then
qla24xx_disable_vp() removes the vport from the host_map btree and zeroes
vha->d_id (RESET_AL_PA). The vport is only unlinked from vp_list later,
in qla24xx_deallocate_vp_id(), which clears vp_map[idx] (RESET_VP_IDX)
but does not touch host_map. In the window in between, report ID
acquisition can still find the vport on vp_list and call
qla_update_host_map(); with d_id already zeroed it takes the
btree_insert32() path and re-inserts the dying vport into host_map.
Nothing cleans that entry afterwards, so once scsi_host_put() frees the
vha a later host_map lookup dereferences freed memory.

Skip a vport that has VPORT_DELETE set before taking the reference, so it
is neither re-registered nor scheduled for DPC re-registration. This
mirrors the existing guard in qla2x00_alert_all_vps().

Fixes: 41dc529a4602 ("qla2xxx: Improve RSCN handling in driver")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_mbx.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index ab5648eb5f20..affcd87893cd 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -4268,6 +4268,8 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
 			spin_lock_irqsave(&ha->vport_slock, flags);
 			list_for_each_entry(vp, &ha->vp_list, list) {
 				if (vp_idx == vp->vp_idx) {
+					if (test_bit(VPORT_DELETE, &vp->dpc_flags))
+						break;
 					found = 1;
 					atomic_inc(&vp->vref_count);
 					break;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 77/88] scsi: qla2xxx: Drop vport reference under lock in report ID acquisition
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (75 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 76/88] scsi: qla2xxx: Skip vport under deletion in report ID acquisition Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:33   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 78/88] scsi: qla2xxx: Hold vport_slock for host map update " Nilesh Javali
                   ` (10 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla24xx_report_id_acquisition() format-1 handling takes the vport
reference under vport_slock but drops it outside the lock, after setting
vp->vp_flags and vp->dpc_flags:

	set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
	set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
	set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);

	atomic_dec(&vp->vref_count);

Neither set_bit() nor atomic_dec() imply a memory barrier, so on a weakly
ordered architecture the decrement can become visible before the flag
stores. qla24xx_deallocate_vp_id() polls vref_count under vport_slock and
unlinks the vport once it reads zero, after which qla24xx_vport_delete()
frees it via scsi_host_put(). The poller could therefore observe
vref_count == 0 early and tear the vport down while the pending vp_flags/
dpc_flags stores land on freed memory.

Drop the reference under vport_slock, as is done for the matching
increment and by every other vref_count user. The unlock release pairs
with the deallocate poller's lock acquire so the flag stores are ordered
before vref_count == 0 can be observed.

Fixes: 87c20ed7521c ("scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition()")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_mbx.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index affcd87893cd..e88c3a989a51 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -4290,7 +4290,9 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
 			set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
 			set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
 
+			spin_lock_irqsave(&ha->vport_slock, flags);
 			atomic_dec(&vp->vref_count);
+			spin_unlock_irqrestore(&ha->vport_slock, flags);
 		}
 		set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
 		qla2xxx_wake_dpc(vha);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 78/88] scsi: qla2xxx: Hold vport_slock for host map update in report ID acquisition
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (76 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 77/88] scsi: qla2xxx: Drop vport reference under lock " Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:28   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 79/88] scsi: qla2xxx: Fix NVMe abort reference leak on repeated abort Nilesh Javali
                   ` (9 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla24xx_report_id_acquisition() format-1 handling drops vport_slock after
taking the vport reference and then calls qla_update_host_map() without
the lock. That reaches qla_update_vp_map(), which mutates the ha->host_map
btree via btree_insert32()/btree_update32()/btree_remove32() and is
documented to require vport_slock to be held by the caller. Running it
unlocked can race concurrent host_map updates and corrupt the btree.

The format-2 path in the same function already wraps its host_map update
(SET_AL_PA) in vport_slock; the format-1 path is the lone outlier.

Hold vport_slock across the format-1 qla_update_host_map() call to honor
the documented locking contract. The vref_count taken in the loop keeps
the vport valid, so this only adds the missing host_map serialization.

Fixes: 430eef03a763 ("scsi: qla2xxx: Relocate/rename vp map")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_mbx.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index e88c3a989a51..39544deab576 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -4280,7 +4280,9 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
 			if (!found)
 				return;
 
+			spin_lock_irqsave(&ha->vport_slock, flags);
 			qla_update_host_map(vp, id);
+			spin_unlock_irqrestore(&ha->vport_slock, flags);
 
 			/*
 			 * Cannot configure here as we are still sitting on the
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 79/88] scsi: qla2xxx: Fix NVMe abort reference leak on repeated abort
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (77 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 78/88] scsi: qla2xxx: Hold vport_slock for host map update " Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:27   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 80/88] scsi: qla2xxx: Skip NVMe LS reject IOCB when FW not started Nilesh Javali
                   ` (8 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla_nvme_ls_abort() and qla_nvme_fcp_abort() take a command reference with
kref_get_unless_zero() and then call schedule_work() on priv->abort_work,
ignoring its return value. qla_nvme_abort_work() runs once and drops
exactly one reference via kref_put(&sp->cmd_kref, sp->put_fn).

Since the per-abort INIT_WORK() was moved to submission time,
schedule_work() now returns false when the work is already pending, for
example on a concurrent transport teardown and timeout-driven abort of
the same command. In that case the reference taken for the second abort
is never released because the work still executes only once, leaking a
reference. The command is then never returned to the NVMe-FC transport,
which can hang the port.

Drop the reference when schedule_work() returns false, so each
kref_get_unless_zero() is balanced regardless of whether the work was
newly queued. The held reference keeps priv->sp valid for the put.

Fixes: 70cbb6fdd31b ("scsi: qla2xxx: Initialize NVMe abort_work once at submission")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_nvme.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index 8dc6df6c2e1c..fc8a344ec7d8 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -466,7 +466,8 @@ static void qla_nvme_ls_abort(struct nvme_fc_local_port *lport,
 	}
 	spin_unlock_irqrestore(&priv->cmd_lock, flags);
 
-	schedule_work(&priv->abort_work);
+	if (!schedule_work(&priv->abort_work))
+		kref_put(&priv->sp->cmd_kref, priv->sp->put_fn);
 }
 
 static int qla_nvme_ls_req(struct nvme_fc_local_port *lport,
@@ -548,7 +549,8 @@ static void qla_nvme_fcp_abort(struct nvme_fc_local_port *lport,
 	}
 	spin_unlock_irqrestore(&priv->cmd_lock, flags);
 
-	schedule_work(&priv->abort_work);
+	if (!schedule_work(&priv->abort_work))
+		kref_put(&priv->sp->cmd_kref, priv->sp->put_fn);
 }
 
 static inline int qla2x00_start_nvme_mq(srb_t *sp)
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 80/88] scsi: qla2xxx: Skip NVMe LS reject IOCB when FW not started
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (78 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 79/88] scsi: qla2xxx: Fix NVMe abort reference leak on repeated abort Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:38   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 81/88] scsi: qla2xxx: Unlink NVMe unsol ctx before freeing on LS reject error Nilesh Javali
                   ` (7 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla_nvme_xmt_ls_rsp() bails out to the out: label when firmware is not
started (!ha->flags.fw_started), but the out: path unconditionally calls
qla_nvme_ls_reject_iocb(), which ends in qla2x00_start_iocbs() and an
unconditional doorbell write to the request queue in-pointer register.
This rings the firmware doorbell and queues an IOCB that stopped or
resetting firmware cannot consume, and touches MMIO during the reset/EEH
window where fw_started is also clear.

Only emit the LS reject IOCB (and ring the doorbell) when fw_started is
set; otherwise just clean up and return. The post-allocation failure
cases (SRB alloc / qla2x00_start_sp() failure) run with firmware started
and still send the reject. Apply the same guard to the reject emission
in qla2xxx_process_purls_pkt().

Fixes: 875386b98857 ("scsi: qla2xxx: Add Unsolicited LS Request and Response Support for NVMe")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_nvme.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index fc8a344ec7d8..28a04e0ff660 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -441,9 +441,11 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport,
 	a.vp_idx = vha->vp_idx;
 	a.nport_handle = uctx->nport_handle;
 	a.xchg_address = uctx->exchange_address;
-	spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
-	qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
-	spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
+	if (ha->flags.fw_started) {
+		spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
+		qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
+		spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
+	}
 	kfree(uctx);
 	return rval;
 }
@@ -1321,9 +1323,14 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item)
 		a.vp_idx = vha->vp_idx;
 		a.nport_handle = uctx->nport_handle;
 		a.xchg_address = uctx->exchange_address;
-		spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr, flags);
-		qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true);
-		spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr, flags);
+		if (vha->hw->flags.fw_started) {
+			spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr,
+					  flags);
+			qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a,
+						true);
+			spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr,
+					       flags);
+		}
 		list_del(&uctx->elem);
 		kfree(uctx);
 	}
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 81/88] scsi: qla2xxx: Unlink NVMe unsol ctx before freeing on LS reject error
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (79 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 80/88] scsi: qla2xxx: Skip NVMe LS reject IOCB when FW not started Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:34   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 82/88] scsi: qla2xxx: Serialize NVMe unsol ctx list with a per-fcport lock Nilesh Javali
                   ` (6 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla_nvme_xmt_ls_rsp() obtains uctx, which was linked into
fcport->unsol_ctx_head by qla2xxx_process_purls_iocb() and is still linked
when the NVMe transport calls back to transmit the LS response. On the
error (out:) path the function frees uctx with kfree() but never removes
it from the list. This leaves a freed node in fcport->unsol_ctx_head: the
next list_add_tail() for that fcport writes through the freed node, and a
subsequent list_del() can corrupt the list or panic.

Unlink uctx with list_del() before kfree() on the error path, matching the
other free sites in qla_nvme_release_lsrsp_cmd_kref() and
qla2xxx_process_purls_pkt(). qla2x00_rel_sp() in the failure path only
returns the SRB to its pool and does not invoke sp->put_fn, so the out:
path is the sole free and uctx is always still linked there.

Fixes: 875386b98857 ("scsi: qla2xxx: Add Unsolicited LS Request and Response Support for NVMe")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_nvme.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index 28a04e0ff660..36b742f73abf 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -446,6 +446,7 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport,
 		qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
 		spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
 	}
+	list_del(&uctx->elem);
 	kfree(uctx);
 	return rval;
 }
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 82/88] scsi: qla2xxx: Serialize NVMe unsol ctx list with a per-fcport lock
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (80 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 81/88] scsi: qla2xxx: Unlink NVMe unsol ctx before freeing on LS reject error Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:38   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 83/88] scsi: qla2xxx: Use coherent DMA buffer for D_Port diagnostics Nilesh Javali
                   ` (5 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

The fcport->unsol_ctx_head list is modified from several contexts without
a common lock. Entries are added in qla2xxx_process_purls_iocb() from the
response queue ISR (under the qpair qp_lock), while they are removed from
qla2xxx_process_purls_pkt() (DPC/purex worker), qla_nvme_xmt_ls_rsp()
(NVMe-FC transport callback) and qla_nvme_release_lsrsp_cmd_kref() (SRB
completion). The qpair qp_lock cannot serialize this per-fcport list since
multiqueue adapters add entries through different qpairs, so a concurrent
add and delete (or two concurrent deletes) can corrupt the list pointers.

Introduce a dedicated per-fcport spinlock, unsol_ctx_lock, initialized in
qla2x00_alloc_fcport(), and take it around every list_add_tail()/list_del()
on unsol_ctx_head. The add nests under the existing qp_lock; no delete path
takes qp_lock, so the lock order is consistent and deadlock free.

Fixes: 875386b98857 ("scsi: qla2xxx: Add Unsolicited LS Request and Response Support for NVMe")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_def.h  | 2 ++
 drivers/scsi/qla2xxx/qla_init.c | 1 +
 drivers/scsi/qla2xxx/qla_nvme.c | 9 +++++++++
 3 files changed, 12 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index bb4305f6a364..afbd056148bd 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -2645,6 +2645,8 @@ typedef struct fc_port {
 	struct list_head list;
 	struct scsi_qla_host *vha;
 	struct list_head unsol_ctx_head;
+	/* Serializes unsol_ctx_head against ISR, DPC and NVMe transport. */
+	spinlock_t unsol_ctx_lock;
 
 	unsigned int conf_compl_supported:1;
 	unsigned int deleted:2;
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index d935fe5e5316..751388c5e3fe 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -5703,6 +5703,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
 	INIT_LIST_HEAD(&fcport->gnl_entry);
 	INIT_LIST_HEAD(&fcport->list);
 	INIT_LIST_HEAD(&fcport->unsol_ctx_head);
+	spin_lock_init(&fcport->unsol_ctx_lock);
 
 	INIT_LIST_HEAD(&fcport->sess_cmd_list);
 	spin_lock_init(&fcport->sess_cmd_lock);
diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index 36b742f73abf..beccece1e7d9 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -257,7 +257,9 @@ static void qla_nvme_release_lsrsp_cmd_kref(struct kref *kref)
 
 	fd_rsp = uctx->fd_rsp;
 
+	spin_lock_irqsave(&uctx->fcport->unsol_ctx_lock, flags);
 	list_del(&uctx->elem);
+	spin_unlock_irqrestore(&uctx->fcport->unsol_ctx_lock, flags);
 
 	fd_rsp->done(fd_rsp);
 	kfree(uctx);
@@ -446,7 +448,9 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport,
 		qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
 		spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
 	}
+	spin_lock_irqsave(&uctx->fcport->unsol_ctx_lock, flags);
 	list_del(&uctx->elem);
+	spin_unlock_irqrestore(&uctx->fcport->unsol_ctx_lock, flags);
 	kfree(uctx);
 	return rval;
 }
@@ -1332,7 +1336,9 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item)
 			spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr,
 					       flags);
 		}
+		spin_lock_irqsave(&uctx->fcport->unsol_ctx_lock, flags);
 		list_del(&uctx->elem);
+		spin_unlock_irqrestore(&uctx->fcport->unsol_ctx_lock, flags);
 		kfree(uctx);
 	}
 }
@@ -1374,6 +1380,7 @@ void qla2xxx_process_purls_iocb(void **pkt, struct rsp_que **rsp)
 	struct purex_item *item;
 	port_id_t d_id = {0};
 	port_id_t id = {0};
+	unsigned long flags;
 	u8 *opcode;
 	bool xmt_reject = false;
 
@@ -1439,7 +1446,9 @@ void qla2xxx_process_purls_iocb(void **pkt, struct rsp_que **rsp)
 	uctx->ox_id = p->ox_id;
 	qla_rport->uctx = uctx;
 	INIT_LIST_HEAD(&uctx->elem);
+	spin_lock_irqsave(&fcport->unsol_ctx_lock, flags);
 	list_add_tail(&uctx->elem, &fcport->unsol_ctx_head);
+	spin_unlock_irqrestore(&fcport->unsol_ctx_lock, flags);
 	item->purls_context = (void *)uctx;
 
 	ql_dbg(ql_dbg_unsol, vha, 0x2121,
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 83/88] scsi: qla2xxx: Use coherent DMA buffer for D_Port diagnostics
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (81 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 82/88] scsi: qla2xxx: Serialize NVMe unsol ctx list with a per-fcport lock Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:34   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 84/88] scsi: qla2xxx: Zero-init bsg stack buffers to avoid info leak Nilesh Javali
                   ` (4 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

qla26xx_dport_diagnostics() streaming-maps the caller's result buffer with
dma_map_single(). The bsg path passes &dd->buf from the __packed struct
qla_dport_diag, where buf lands at a 2-byte offset and shares cachelines
with the surrounding options/unused fields. Mapping such a misaligned
sub-buffer violates the DMA API requirement that streaming buffers be
cacheline aligned and not share a cacheline with other data, and can
corrupt data on non-DMA-coherent architectures.

Allocate a dedicated DMA-coherent buffer inside qla26xx_dport_diagnostics()
for the mailbox command and copy the result back into the caller's buffer.
This removes the streaming map of the misaligned sub-buffer entirely; the
caller's buffer is now only a plain CPU buffer, so its packing no longer
matters.

Fixes: ec89146215d1 ("qla2xxx: Add bsg interface to support D_Port Diagnostics.")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_mbx.c | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 39544deab576..59ec5605930b 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -6579,6 +6579,7 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha,
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 	dma_addr_t dd_dma;
+	void *dd;
 
 	if (!IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw) &&
 	    !IS_QLA28XX(vha->hw) && !IS_QLA29XX(vha->hw))
@@ -6587,15 +6588,12 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha,
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x119f,
 	    "Entered %s.\n", __func__);
 
-	dd_dma = dma_map_single(&vha->hw->pdev->dev,
-	    dd_buf, size, DMA_FROM_DEVICE);
-	if (dma_mapping_error(&vha->hw->pdev->dev, dd_dma)) {
-		ql_log(ql_log_warn, vha, 0x1194, "Failed to map dma buffer.\n");
+	dd = dma_alloc_coherent(&vha->hw->pdev->dev, size, &dd_dma, GFP_KERNEL);
+	if (!dd) {
+		ql_log(ql_log_warn, vha, 0x1194, "Failed to allocate dma buffer.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 
-	memset(dd_buf, 0, size);
-
 	mcp->mb[0] = MBC_DPORT_DIAGNOSTICS;
 	mcp->mb[1] = options;
 	mcp->mb[2] = MSW(LSD(dd_dma));
@@ -6617,8 +6615,9 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha,
 		    "Done %s.\n", __func__);
 	}
 
-	dma_unmap_single(&vha->hw->pdev->dev, dd_dma,
-	    size, DMA_FROM_DEVICE);
+	memcpy(dd_buf, dd, size);
+
+	dma_free_coherent(&vha->hw->pdev->dev, size, dd, dd_dma);
 
 	return rval;
 }
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 84/88] scsi: qla2xxx: Zero-init bsg stack buffers to avoid info leak
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (82 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 83/88] scsi: qla2xxx: Use coherent DMA buffer for D_Port diagnostics Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:37   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 85/88] scsi: qla2xxx: Validate BSG request_len before reading vendor_cmd[] Nilesh Javali
                   ` (3 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Several bsg handlers stage their request/reply in an uninitialized 256-byte
on-stack buffer (uint8_t bsg[DMA_POOL_SIZE]) and fill it via
sg_copy_to_buffer(), which only copies as many bytes as the user-supplied
request payload. When the request is shorter than the structure, the
remainder of the buffer is left holding stale stack data.

qla2x00_read_fru_status() and qla2x00_read_i2c() then copy the full
structure back to the reply payload with sg_copy_from_buffer(), leaking the
uninitialized stack bytes to user space. The write/update paths do not copy
the buffer back, but can feed uninitialized fields to the device.

Zero the stack buffer at declaration in all five handlers, mirroring the
heap kzalloc() approach, so short requests can no longer expose stale
memory.

Fixes: 697a4bc69159 ("[SCSI] qla2xxx: Provide method for updating I2C attached VPD.")
Fixes: 9ebb5d9c69f1 ("[SCSI] qla2xxx: Add I2C BSG interface.")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_bsg.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 55142df30bda..cb7227298b24 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -1959,7 +1959,7 @@ qla2x00_update_fru_versions(struct bsg_job *bsg_job)
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_image_version_list *list = (void *)bsg;
 	struct qla_image_version *image;
 	uint32_t count;
@@ -2019,7 +2019,7 @@ qla2x00_read_fru_status(struct bsg_job *bsg_job)
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_status_reg *sr = (void *)bsg;
 	dma_addr_t sfp_dma;
 	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
@@ -2070,7 +2070,7 @@ qla2x00_write_fru_status(struct bsg_job *bsg_job)
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_status_reg *sr = (void *)bsg;
 	dma_addr_t sfp_dma;
 	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
@@ -2117,7 +2117,7 @@ qla2x00_write_i2c(struct bsg_job *bsg_job)
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_i2c_access *i2c = (void *)bsg;
 	dma_addr_t sfp_dma;
 	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
@@ -2163,7 +2163,7 @@ qla2x00_read_i2c(struct bsg_job *bsg_job)
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_i2c_access *i2c = (void *)bsg;
 	dma_addr_t sfp_dma;
 	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 85/88] scsi: qla2xxx: Validate BSG request_len before reading vendor_cmd[]
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (83 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 84/88] scsi: qla2xxx: Zero-init bsg stack buffers to avoid info leak Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:46   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 86/88] scsi: qla2xxx: Zero SFP DMA buffer in FRU/I2C bsg handlers Nilesh Javali
                   ` (2 subsequent siblings)
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

The FC BSG transport allocates job->request via memdup_user() using the
exact user-supplied request_len. For FC_BSG_HST_VENDOR,
fc_bsg_host_dispatch() only guarantees request_len covers msgcode and
vendor_id; it does not account for the vendor_cmd[] flexible array.

qla2xxx then reads the command selector vendor_cmd[0] and, in several
sub-handlers, vendor_cmd[1]/[2] or structures overlaid on the vendor
command area without verifying request_len. A caller holding
CAP_SYS_RAWIO can submit a short request whose vendor_id matches the
host, triggering out-of-bounds heap reads (KASAN-detectable, and able to
mis-select a command or panic).

Add a central guard in qla2x00_process_vendor_specific() so the selector
is always in bounds, restrict the early vendor_cmd[0] read in
qla24xx_bsg_request() to sufficiently long vendor messages, and add
request_len checks to the sub-handlers that read further:
qla24xx_proc_fcp_prio_cfg_cmd(), qla2x00_process_loopback(),
qla84xx_reset(), qla84xx_updatefw(), qla2x00_read_optrom(),
qla2x00_update_optrom(), qlafx00_mgmt_cmd() and
qla28xx_validate_flash_image().

Fixes: 01e0e15c8b3b ("scsi: don't use fc_bsg_job::request and fc_bsg_job::reply directly")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_bsg.c | 57 +++++++++++++++++++++++++++++++---
 1 file changed, 53 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index cb7227298b24..e671c3de8c05 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -160,6 +160,12 @@ qla24xx_proc_fcp_prio_cfg_cmd(struct bsg_job *bsg_job)
 		goto exit_fcp_prio_cfg;
 	}
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t)) {
+		ret = -EINVAL;
+		goto exit_fcp_prio_cfg;
+	}
+
 	/* Get the sub command */
 	oper = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
 
@@ -758,6 +764,10 @@ qla2x00_process_loopback(struct bsg_job *bsg_job)
 		return -EIO;
 	}
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + 3 * sizeof(uint32_t))
+		return -EINVAL;
+
 	memset(&elreq, 0, sizeof(elreq));
 
 	elreq.req_sg_cnt = dma_map_sg(&ha->pdev->dev,
@@ -990,6 +1000,10 @@ qla84xx_reset(struct bsg_job *bsg_job)
 		return -EINVAL;
 	}
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
+		return -EINVAL;
+
 	flag = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
 
 	rval = qla84xx_reset_chip(vha, flag == A84_ISSUE_RESET_DIAG_FW);
@@ -1034,6 +1048,10 @@ qla84xx_updatefw(struct bsg_job *bsg_job)
 		return -EINVAL;
 	}
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
+		return -EINVAL;
+
 	sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
 		bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
 	if (!sg_cnt) {
@@ -1511,9 +1529,15 @@ qla2x00_read_optrom(struct bsg_job *bsg_job)
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
-	uint32_t start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
+	uint32_t start;
 	int rval = 0;
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
+		return -EINVAL;
+
+	start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
+
 	if (ha->flags.nic_core_reset_hdlr_active)
 		return -EBUSY;
 
@@ -1556,9 +1580,15 @@ qla2x00_update_optrom(struct bsg_job *bsg_job)
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
-	uint32_t start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
+	uint32_t start;
 	int rval = 0;
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
+		return -EINVAL;
+
+	start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
+
 	mutex_lock(&ha->optrom_mutex);
 	rval = qla2x00_optrom_setup(bsg_job, vha, start, 1);
 	if (rval) {
@@ -2397,6 +2427,11 @@ qlafx00_mgmt_cmd(struct bsg_job *bsg_job)
 	struct fc_port *fcport;
 	char  *type = "FC_BSG_HST_FX_MGMT";
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + sizeof(uint32_t) +
+	    sizeof(struct qla_mt_iocb_rqst_fx00))
+		return -EINVAL;
+
 	/* Copy the IOCB specific information */
 	piocb_rqst = (struct qla_mt_iocb_rqst_fx00 *)
 	    &bsg_request->rqst_data.h_vendor.vendor_cmd[1];
@@ -3318,6 +3353,13 @@ qla2x00_process_vendor_specific(struct scsi_qla_host *vha, struct bsg_job *bsg_j
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + sizeof(uint32_t)) {
+		ql_log(ql_log_warn, vha, 0x7000,
+		       "BSG request too small for vendor cmd.\n");
+		return -EINVAL;
+	}
+
 	ql_dbg(ql_dbg_edif, vha, 0x911b, "%s FC_BSG_HST_VENDOR cmd[0]=0x%x\n",
 	    __func__, bsg_request->rqst_data.h_vendor.vendor_cmd[0]);
 
@@ -3461,8 +3503,11 @@ qla24xx_bsg_request(struct bsg_job *bsg_job)
 	}
 
 	/* Disable port will bring down the chip, allow enable command */
-	if (bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_MANAGE_HOST_PORT ||
-	    bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS)
+	if (bsg_request->msgcode == FC_BSG_HST_VENDOR &&
+	    bsg_job->request_len >=
+		sizeof(struct fc_bsg_request) + sizeof(uint32_t) &&
+	    (bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_MANAGE_HOST_PORT ||
+	     bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS))
 		goto skip_chip_chk;
 
 	if (vha->hw->flags.port_isolated) {
@@ -3771,6 +3816,10 @@ static int qla28xx_validate_flash_image(struct bsg_job *bsg_job)
 	if (!IS_QLA28XX(ha) || vha->vp_idx != 0)
 		return -EPERM;
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
+		return -EINVAL;
+
 	mutex_lock(&ha->optrom_mutex);
 	rval = qla28xx_do_validate_flash_image(bsg_job, &state);
 	if (rval)
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 86/88] scsi: qla2xxx: Zero SFP DMA buffer in FRU/I2C bsg handlers
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (84 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 85/88] scsi: qla2xxx: Validate BSG request_len before reading vendor_cmd[] Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:38   ` sashiko-bot
  2026-07-07  5:54 ` [PATCH v3 87/88] scsi: qla2xxx: Bound i2c->length in I2C " Nilesh Javali
  2026-07-07  5:54 ` [PATCH v3 88/88] scsi: qla2xxx: Update version to 12.00.00.2607b1 Nilesh Javali
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

The FRU and I2C bsg handlers stage their transfer in a DMA_POOL_SIZE
(256-byte) bounce buffer obtained from dma_pool_alloc(), which does not
zero the allocation. They initialize only a few leading bytes before
handing the buffer to qla2x00_write_sfp().

qla2x00_write_sfp() can override the transfer length with a user-supplied
value:

	if (len == 1)
		opt |= BIT_0;
	if (opt & BIT_0)
		len = *sfp;

*sfp is the first byte of the (user-controlled) payload, so len can grow
up to 255. The device then DMA-reads len bytes from the 256-byte pool
buffer. Since only a small prefix was written
(e.g. MAX_FRU_SIZE == 36 bytes for a FRU version, one byte for a FRU
status register), the hardware reads past the initialized region and
writes up to ~219 bytes of stale DMA-pool heap memory to the device
flash.

Allocate the buffer with dma_pool_zalloc() in all five FRU/I2C handlers
so any bytes beyond the initialized data are zero rather than stale heap
contents.

Fixes: 697a4bc69159 ("[SCSI] qla2xxx: Provide method for updating I2C attached VPD.")
Fixes: 9ebb5d9c69f1 ("[SCSI] qla2xxx: Add I2C BSG interface.")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_bsg.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index e671c3de8c05..51ab638b4fc0 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -1994,7 +1994,7 @@ qla2x00_update_fru_versions(struct bsg_job *bsg_job)
 	struct qla_image_version *image;
 	uint32_t count;
 	dma_addr_t sfp_dma;
-	void *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	void *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -2052,7 +2052,7 @@ qla2x00_read_fru_status(struct bsg_job *bsg_job)
 	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_status_reg *sr = (void *)bsg;
 	dma_addr_t sfp_dma;
-	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -2103,7 +2103,7 @@ qla2x00_write_fru_status(struct bsg_job *bsg_job)
 	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_status_reg *sr = (void *)bsg;
 	dma_addr_t sfp_dma;
-	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -2150,7 +2150,7 @@ qla2x00_write_i2c(struct bsg_job *bsg_job)
 	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_i2c_access *i2c = (void *)bsg;
 	dma_addr_t sfp_dma;
-	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -2196,7 +2196,7 @@ qla2x00_read_i2c(struct bsg_job *bsg_job)
 	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_i2c_access *i2c = (void *)bsg;
 	dma_addr_t sfp_dma;
-	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 87/88] scsi: qla2xxx: Bound i2c->length in I2C bsg handlers
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (85 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 86/88] scsi: qla2xxx: Zero SFP DMA buffer in FRU/I2C bsg handlers Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  5:54 ` [PATCH v3 88/88] scsi: qla2xxx: Update version to 12.00.00.2607b1 Nilesh Javali
  87 siblings, 0 replies; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

struct qla_i2c_access carries a 16-bit length field alongside a fixed
64-byte buffer:

	struct qla_i2c_access {
		uint16_t device, offset, option, length;
		uint8_t  buffer[0x40];
	} __packed;

qla2x00_write_i2c() and qla2x00_read_i2c() use the user-supplied
i2c->length without any bounds check. i2c is overlaid on a 256-byte
on-stack buffer and sfp is a 256-byte DMA-pool buffer, so a length up to
65535 overruns both:

  - write: memcpy(sfp, i2c->buffer, i2c->length) over-reads the stack and
    over-writes the sfp heap buffer, and qla2x00_write_sfp() then DMAs
    i2c->length bytes out of the 256-byte buffer.
  - read: qla2x00_read_sfp() DMAs i2c->length bytes into the 256-byte sfp,
    then memcpy(i2c->buffer, sfp, i2c->length) overflows the 64-byte
    buffer inside the on-stack array.

A caller holding CAP_SYS_RAWIO can use this to corrupt the heap and the
kernel stack. Reject requests whose length exceeds the buffer before any
copy or DMA transfer in both handlers.

Fixes: 9ebb5d9c69f1 ("[SCSI] qla2xxx: Add I2C BSG interface.")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_bsg.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 51ab638b4fc0..6e7bd2bb634b 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -2161,6 +2161,12 @@ qla2x00_write_i2c(struct bsg_job *bsg_job)
 	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
 	    bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c));
 
+	if (i2c->length > sizeof(i2c->buffer)) {
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+		    EXT_STATUS_INVALID_PARAM;
+		goto dealloc;
+	}
+
 	memcpy(sfp, i2c->buffer, i2c->length);
 	rval = qla2x00_write_sfp(vha, sfp_dma, sfp,
 	    i2c->device, i2c->offset, i2c->length, i2c->option);
@@ -2207,6 +2213,12 @@ qla2x00_read_i2c(struct bsg_job *bsg_job)
 	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
 	    bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c));
 
+	if (i2c->length > sizeof(i2c->buffer)) {
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+		    EXT_STATUS_INVALID_PARAM;
+		goto dealloc;
+	}
+
 	rval = qla2x00_read_sfp(vha, sfp_dma, sfp,
 		i2c->device, i2c->offset, i2c->length, i2c->option);
 
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* [PATCH v3 88/88] scsi: qla2xxx: Update version to 12.00.00.2607b1
  2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
                   ` (86 preceding siblings ...)
  2026-07-07  5:54 ` [PATCH v3 87/88] scsi: qla2xxx: Bound i2c->length in I2C " Nilesh Javali
@ 2026-07-07  5:54 ` Nilesh Javali
  2026-07-07  7:35   ` sashiko-bot
  87 siblings, 1 reply; 159+ messages in thread
From: Nilesh Javali @ 2026-07-07  5:54 UTC (permalink / raw)
  To: martin.petersen
  Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
	hare

Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_version.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
index 9564beafdab7..1c0b01d70350 100644
--- a/drivers/scsi/qla2xxx/qla_version.h
+++ b/drivers/scsi/qla2xxx/qla_version.h
@@ -6,9 +6,9 @@
 /*
  * Driver version
  */
-#define QLA2XXX_VERSION      "10.02.10.100-k"
+#define QLA2XXX_VERSION      "12.00.00.2607b1"
 
-#define QLA_DRIVER_MAJOR_VER	10
-#define QLA_DRIVER_MINOR_VER	02
-#define QLA_DRIVER_PATCH_VER	10
-#define QLA_DRIVER_BETA_VER	100
+#define QLA_DRIVER_MAJOR_VER	12
+#define QLA_DRIVER_MINOR_VER	00
+#define QLA_DRIVER_PATCH_VER	00
+#define QLA_DRIVER_BETA_VER	2607
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 10/88] scsi: qla2xxx: Add extended status continuation and marker IOCBs
  2026-07-07  5:53 ` [PATCH v3 10/88] scsi: qla2xxx: Add extended status continuation and marker IOCBs Nilesh Javali
@ 2026-07-07  6:10   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:10 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Meaningful code and commit message mismatch regarding added structures.
--

--- Patch [10]: [PATCH v3 10/88] scsi: qla2xxx: Add extended status continuation and marker IOCBs ---
commit f6e2db946a6c0b0669be6dd9042f40e733197841
Author: Anil Gurumurthy <agurumurthy@marvell.com>

scsi: qla2xxx: Add extended status continuation and marker IOCBs

This commit adds the 128-byte extended status continuation structure for
29xx firmware. It also includes the qla_fw29.h header into qla_def.h.

> scsi: qla2xxx: Add extended status continuation and marker IOCBs
> 
> Add the 128-byte sts_cont_entry_ext_t and mrk_entry_ext_t
> structures required by 29xx firmware.

> diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
> --- a/drivers/scsi/qla2xxx/qla_def.h
> +++ b/drivers/scsi/qla2xxx/qla_def.h

[ ... ]

> @@ -2360,6 +2361,15 @@ typedef struct {
>  	uint8_t reserved_2[48];
>  } mrk_entry_t;
>  
> +/* 29xx definitions */
> +struct sts_cont_entry_ext {
> +	uint8_t entry_type;		/* Entry type. */
> +	uint8_t entry_count;		/* Entry count. */
> +	uint8_t sys_define;		/* System defined. */
> +	uint8_t entry_status;		/* Entry Status. */
> +	uint8_t data[124];		/* data */
> +};

[Severity: Medium]
This isn't a bug in the code, but is the description in the commit message
accurate for this specific patch?

The commit message explicitly claims to add two structures: the extended
status continuation (sts_cont_entry_ext_t) and the marker IOCB
(mrk_entry_ext_t).

However, looking at drivers/scsi/qla2xxx/qla_def.h, only struct
sts_cont_entry_ext is added here, and it omits the _t typedef referenced
in the description. There is no mrk_entry_ext_t structure included in this
change.

Could the commit message be updated to reflect the actual contents, or is
the mrk_entry_ext_t structure unintentionally missing from this patch?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=10

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 14/88] scsi: qla2xxx: Enable get_fw_version mailbox for 29xx
  2026-07-07  5:53 ` [PATCH v3 14/88] scsi: qla2xxx: Enable get_fw_version mailbox " Nilesh Javali
@ 2026-07-07  6:12   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:12 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] mpi_version and pep_version sysfs attributes return empty strings for 29xx adapters.
- [Medium] EDIF enablement logic for 29xx adapters is unreachable dead code.
--

--- Patch [14]: [PATCH v3 14/88] scsi: qla2xxx: Enable get_fw_version mailbox for 29xx ---
commit 2382364dd411bb453d1411f7e7ec1a1bea0e802e
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Enable get_fw_version mailbox for 29xx

The serdes_version and several firmware capability fields were not
populated for 29xx because the get_fw_version mailbox path excluded
it from the 27xx/28xx checks. This commit adds IS_QLA29XX() to the
relevant conditionals so that firmware version, EDIF, and serdes
information are correctly retrieved on 29xx adapters.

> diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
> index e8755ab86b6ac..c44f5282abb39 100644
> --- a/drivers/scsi/qla2xxx/qla_attr.c
> +++ b/drivers/scsi/qla2xxx/qla_attr.c
> @@ -1526,7 +1526,7 @@ qla2x00_serdes_version_show(struct device *dev, struct device_attribute *attr,
>  	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
>  	struct qla_hw_data *ha = vha->hw;
>  
> -	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
> +	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
>  		return scnprintf(buf, PAGE_SIZE, "\n");

[Severity: Medium]
Since IS_QLA29XX() is being added here to expose the serdes version, should it
also be added to qla2x00_mpi_version_show() and qla2x00_pep_version_show() in
the same file?

Without it, userspace tools querying the mpi_version and pep_version sysfs
attributes for 29xx adapters will receive empty strings instead of the actual
firmware version data:

drivers/scsi/qla2xxx/qla_attr.c:qla2x00_mpi_version_show() {
    ...
    if (!IS_QLA81XX(ha) && !IS_QLA8031(ha) && !IS_QLA8044(ha) &&
        !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
        return scnprintf(buf, PAGE_SIZE, "\n");
    ...
}

[ ... ]

> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index 9c2633ca5036c..0feb98b83293a 100644
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c
[ ... ]
> @@ -1212,7 +1212,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
>  			vha->flags.nvme2_enabled = 1;
>  		}
>  
> -		if (IS_QLA28XX(ha) && ha->flags.edif_hw && ql2xsecenable &&
> +		if ((IS_QLA28XX(ha) || IS_QLA29XX(ha)) && ha->flags.edif_hw && ql2xsecenable &&
>  		    (ha->fw_attributes_ext[0] & FW_ATTR_EXT0_EDIF)) {
>  			ha->flags.edif_enabled = 1;
>  			ql_log(ql_log_info, vha, 0xffff,

[Severity: Medium]
Will this EDIF enablement logic ever execute for 29xx adapters?

Looking at qla2x00_execute_fw() in the same file, ha->flags.edif_hw is only
initialized for 28xx adapters:

drivers/scsi/qla2xxx/qla_mbx.c:qla2x00_execute_fw() {
    ...
    if (IS_QLA28XX(ha) && (mcp->mb[5] & EDIF_HW_SUPPORT)) {
        ha->flags.edif_hw = 1;
    ...
}

Because ha->flags.edif_hw is never set for 29xx adapters, this patched
condition will always evaluate to false, leaving EDIF permanently disabled
on 29xx hardware.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=14

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 12/88] scsi: qla2xxx: Skip image-set-valid attribute for 29xx
  2026-07-07  5:53 ` [PATCH v3 12/88] scsi: qla2xxx: Skip image-set-valid attribute " Nilesh Javali
@ 2026-07-07  6:12   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:12 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Unprotected read-modify-write on vha->dport_status can lead to state corruption.
--

--- Patch [12]: [PATCH v3 12/88] scsi: qla2xxx: Skip image-set-valid attribute for 29xx ---
Note: Due to automated recitation restrictions, the diff is not quoted inline 
below. Instead, a free-form summary of the findings in the context of the 
modified file is provided.

commit 2275f80f5bc3cc74a549d7aa22a8aac6742fae34
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Skip image-set-valid attribute for 29xx

The 29xx adapter does not support the QLA_IMG_SET_VALID_SUPPORT driver
attribute. This commit gates the attribute behind an IS_QLA29XX() check to
provide accurate capabilities to userspace.

[Severity: High]
This is a pre-existing issue and was not introduced by this patch, but while
reviewing the related code in drivers/scsi/qla2xxx/qla_bsg.c, I noticed a
potential race condition in qla2x00_do_dport_diagnostics_v2().

Does the bitwise modification of vha->dport_status (specifically when setting
DPORT_DIAG_IN_PROGRESS) need to be protected by a lock, such as the
hardware_lock?

Since this read-modify-write happens in process context, it seems like it
could race with the asynchronous event interrupt handler. If a user issues a
BSG diagnostic command concurrently with the hardware completing a previous
command, the interrupt handler might clear a bit while this process context
overwrites the status using a stale read. 

Could this cause flag updates to be lost, potentially leaving the diagnostics
state machine permanently stuck in an in-progress state?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=12

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 08/88] scsi: qla2xxx: Add BSG MPI firmware load/dump for 29xx
  2026-07-07  5:53 ` [PATCH v3 08/88] scsi: qla2xxx: Add BSG MPI firmware load/dump " Nilesh Javali
@ 2026-07-07  6:13   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:13 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Double completion of the BSG job in `qla2900_bsg_load_mpi()` when an error occurs.
- [High] The logic for the `LOAD_MPI` and `DUMP_MPI` commands is fundamentally reversed, causing 'load' to read from the adapter and 'dump' to write to it.
- [Medium] Missing adapter type validation in `qla2900_bsg_dump_mpi()` and `qla2900_bsg_load_mpi()`.
- [Medium] Integer overflow in `chunk_count` causes firmware protocol corruption for large payloads.
--

--- Patch [8]: [PATCH v3 08/88] scsi: qla2xxx: Add BSG MPI firmware load/dump for 29xx ---
commit 7a0034bb29c7bf9118f18cc98555126fd42186bf
Author: Manish Rangankar <mrangankar@marvell.com>

scsi: qla2xxx: Add BSG MPI firmware load/dump for 29xx

Add BSG vendor commands for loading and dumping MPI firmware on
29xx adapters.  This extends the existing BSG infrastructure with
the necessary mailbox wrappers and flash helpers for MPI
operations.

> diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
> --- a/drivers/scsi/qla2xxx/qla_bsg.c
> +++ b/drivers/scsi/qla2xxx/qla_bsg.c

[ ... ]

> @@ -1804,6 +1804,18 @@ static int qla2900_bsg_dump_mpi(struct bsg_job *bsg_job)
>  {
>  	struct fc_bsg_request *bsg_req = bsg_job->request;
>  	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
>  	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
>  	scsi_qla_host_t *vha = shost_priv(host);
>  	struct qla_hw_data *ha = vha->hw;

[Severity: Medium]
Could this command be sent to older adapters that do not support it?
There doesn't appear to be a capability check (like IS_QLA29XX) before
processing these 29xx-specific mailbox commands, which might cause
unpredictable behavior on older hardware.

>  	struct qla_load_dump_mpi *dmcmd;
>  	uint16_t opts = 0;
>  	int rval = 0;
>  
>  	if (bsg_job->request_len < sizeof(struct fc_bsg_request) +
>  	    2 * sizeof(uint32_t) + sizeof(struct qla_load_dump_mpi))
>  		return -EINVAL;
>  
>  	dmcmd =
>  	(struct qla_load_dump_mpi *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2];
>  
>  	ql_log(ql_log_info, vha, 0xffff,
>  	       "%s: mpi_address 0x%x mpi options 0x%x length 0x%x\n",
>  		__func__, dmcmd->mpi_address, dmcmd->options, dmcmd->length);
>  
>  	mutex_lock(&ha->optrom_mutex);
>  	rval = qla29xx_mpi_optrom_setup(bsg_job, vha, dmcmd->mpi_address, 1);

[Severity: High]
Does this function implement the dump operation in reverse? Setting
is_update=1 reads from the host request payload and writes the contents
to the device, which is typically the behavior of a load operation.

[ ... ]

> @@ -1877,6 +1877,18 @@ static int qla2900_bsg_load_mpi(struct bsg_job *bsg_job)
>  {
>  	struct fc_bsg_request *bsg_req = bsg_job->request;
>  	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
>  	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
>  	scsi_qla_host_t *vha = shost_priv(host);
>  	struct qla_hw_data *ha = vha->hw;

[Severity: Medium]
Is there a missing adapter capability check here as well? Without verifying
the hardware type, this could be dispatched to older adapters that lack
support for this feature.

>  	struct qla_load_dump_mpi *lmcmd;
>  	uint16_t opts = 0;
>  	int rval = 0;
>  
>  	if (bsg_job->request_len < sizeof(struct fc_bsg_request) +
>  	    2 * sizeof(uint32_t) + sizeof(struct qla_load_dump_mpi))
>  		return -EINVAL;
>  
>  	lmcmd =
>  	(struct qla_load_dump_mpi *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2];
>  
>  	ql_log(ql_log_info, vha, 0xffff,
>  	       "%s: mpi_address 0x%x mpi options 0x%x length 0x%x\n",
>  		__func__, lmcmd->mpi_address, lmcmd->options, lmcmd->length);
>  
>  	mutex_lock(&ha->optrom_mutex);
>  	rval = qla29xx_mpi_optrom_setup(bsg_job, vha, lmcmd->mpi_address, 0);

[Severity: High]
Similarly, does this implement the load operation in reverse? The is_update=0
flag causes it to read from the device and write to the host reply payload,
which is typically the behavior of a dump operation.

[ ... ]

>  	bsg_reply->reply_payload_rcv_len = ha->optrom_region_size;
>  	vfree(ha->optrom_buffer);
>  	ha->optrom_buffer = NULL;
>  	ha->optrom_state = QLA_SWAITING;
>  	mutex_unlock(&ha->optrom_mutex);
>  	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
>  	bsg_job_done(bsg_job, bsg_reply->result,
>  		     bsg_reply->reply_payload_rcv_len);
>  
>  	return rval;
>  }

[Severity: High]
Will returning a non-zero error code here cause a double completion?
Since bsg_job_done() is already called to manually complete the job,
returning an error code in rval could cause the block layer to attempt
completion a second time, potentially causing block request list corruption.
The qla2900_bsg_dump_mpi() function returns 0 unconditionally after
calling bsg_job_done().

> diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
> --- a/drivers/scsi/qla2xxx/qla_sup.c
> +++ b/drivers/scsi/qla2xxx/qla_sup.c

[ ... ]

> @@ -573,6 +573,12 @@ int
>  qla29xx_mpi_optrom_data(struct scsi_qla_host *vha, uint16_t opts, void *buf,
>  		       uint32_t offset, uint32_t length, enum qla29xx_mpi_optrom_op op)
>  {
>  	struct qla_hw_data *ha = vha->hw;
>  	dma_addr_t optrom_dma;
>  	void *optrom;
>  	uint32_t mpi_addr, mpi_size, burst = 0;
>  	uint32_t total_chunks = 0;
>  	uint32_t *dcode, *fwcode;
>  	uint16_t chunk_count = 0;

[Severity: Medium]
Can chunk_count overflow if the payload is large enough?
Since total_chunks is a 32-bit integer, a large payload could cause the
16-bit chunk_count to wrap to zero. When passed to set_chunk_mpi_bits()
later in this loop, this wrapped value could incorrectly set the 'first chunk'
flags for segments in the middle of the payload.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=8

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 06/88] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path
  2026-07-07  5:53 ` [PATCH v3 06/88] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path Nilesh Javali
@ 2026-07-07  6:13   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:13 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Unprotected hardware flash accesses via MMIO without holding `optrom_mutex`.
--

--- Patch [6]: [PATCH v3 06/88] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path ---
commit a715b0e512d08c739093c4259bc98b7e60076b5e
Author: Manish Rangankar <mrangankar@marvell.com>

scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path

This commit removes a duplicate call to read_optrom() in the sysfs VPD read
path that was executing without the optrom_mutex held. This prevents
exposure of flash access to concurrent optrom operations.

> diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
> index 6a05ce195aa05..800751ab562a1 100644
> --- a/drivers/scsi/qla2xxx/qla_attr.c
> +++ b/drivers/scsi/qla2xxx/qla_attr.c
> @@ -580,7 +580,6 @@ qla2x00_sysfs_read_vpd(struct file *filp, struct kobject *kobj,
>  	ha->isp_ops->read_optrom(vha, ha->vpd, faddr, ha->vpd_size);
>  	mutex_unlock(&ha->optrom_mutex);
>  
> -	ha->isp_ops->read_optrom(vha, ha->vpd, faddr, ha->vpd_size);

[Severity: High]
This is a pre-existing issue, but since this patch fixes an unprotected flash
access by dropping this duplicate call, are there other unprotected hardware
flash accesses occurring in this same path?

Looking at qla2x00_sysfs_read_vpd() just before the optrom_mutex is acquired,
qla28xx_get_aux_images() is called:

drivers/scsi/qla2xxx/qla_attr.c:qla2x00_sysfs_read_vpd() {
    ...
    if (IS_QLA28XX(ha)) {
        qla28xx_get_aux_images(vha, &active_regions);
        ...
    }

    mutex_lock(&ha->optrom_mutex);
    ...
}

Does calling qla28xx_get_aux_images() outside the optrom_mutex critical
section expose flash access to concurrent optrom operations?

There appear to be similar unprotected hardware flash accesses via MMIO
elsewhere that could cause concurrent reads/writes to corrupt software
state regarding flash regions:

- drivers/scsi/qla2xxx/qla_attr.c:qla2x00_sysfs_write_reset() calling
  ha->isp_ops->get_flash_version()
- drivers/scsi/qla2xxx/qla_bsg.c:qla2x00_get_flash_image_status() calling
  qla27xx_get_active_image() and qla28xx_get_aux_images()

>  skip:
>  	return memory_read_from_buffer(buf, count, &off, ha->vpd, ha->vpd_size);
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=6

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 09/88] scsi: qla2xxx: Add 128-byte IOCB definitions for 29xx
  2026-07-07  5:53 ` [PATCH v3 09/88] scsi: qla2xxx: Add 128-byte IOCB definitions " Nilesh Javali
@ 2026-07-07  6:14   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:14 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Hardware IOCB structures incorrectly use C bitfields within `__le16` restricted types, which will cause ABI corruption on big-endian architectures.
- [High] The `vp_rpt_id_entry_24xx_ext` structure is missing trailing padding, making it exactly 64 bytes instead of the required 128 bytes for 29xx extended IOCBs.
--

--- Patch [9]: [PATCH v3 09/88] scsi: qla2xxx: Add 128-byte IOCB definitions for 29xx ---
commit 17c73130a2f51368d3463b2a88fa780313b4e3a3
Author: Anil Gurumurthy <agurumurthy@marvell.com>

scsi: qla2xxx: Add 128-byte queue definitions for 29xx

This commit adds a new header file containing 128-byte extended queue
structure definitions for the 29xx series adapters.

diff --git a/drivers/scsi/qla2xxx/qla_fw29.h b/drivers/scsi/qla2xxx/qla_fw29.h
new file mode 100644
index 0000000000000..efe1c60bee81d
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_fw29.h
[ ... ]
> +struct els_entry_24xx_ext {
> +	uint8_t entry_type;		/* Entry type. */
> +	uint8_t entry_count;		/* Entry count. */
> +	uint8_t sys_define;		/* System Defined. */
> +	uint8_t entry_status;		/* Entry Status. */
> +
> +	uint32_t handle;		/* System handle. */
> +
> +	__le16	comp_status;		/* response only */
> +	__le16	nport_handle;
> +
> +	__le16	tx_dsd_count;
> +
> +	__le16	vp_index : 9;		/* VP Index 9bits */
> +	__le16	reserved_1_sof : 3;
> +	__le16	sof_type : 4;

[Severity: High]
Does defining bitfields inside a restricted __le16 type cause layout
issues on big-endian architectures? 

The fields vp_index, reserved_1_sof, and sof_type will have their bits laid
out in reverse order on big-endian platforms compared to little-endian ones,
which may result in corruption of the firmware interface.

[ ... ]
> +struct els_sts_entry_24xx_ext {
> +	uint8_t entry_type;		/* Entry type. */
> +	uint8_t entry_count;		/* Entry count. */
> +	uint8_t sys_define;		/* System Defined. */
> +	uint8_t entry_status;		/* Entry Status. */
> +
> +	__le32	handle;		/* System handle. */
> +
> +	__le16	comp_status;
> +
> +	__le16	nport_handle;		/* N_PORT handle. */
> +
> +	__le16	reserved_1;
> +
> +	__le16	vp_index : 9;		/* VP Index 9bits */
> +	__le16	reserved_1_sof : 3;
> +	__le16	sof_type : 4;

[Severity: High]
Could this also experience the same big-endian layout regressions as
els_entry_24xx_ext?

[ ... ]
> +struct abts_entry_24xx_ext {
> +	uint8_t entry_type;
> +	uint8_t entry_count;
> +	uint8_t handle_count;
> +	uint8_t entry_status;
> +
> +	__le32	handle;		/* type 0x55 only */
> +
> +	__le16	comp_status;		/* type 0x55 only */
> +	__le16	nport_handle;		/* type 0x54 only */
> +
> +	__le16	control_flags;		/* type 0x55 only */
> +	__le16	vp_idx : 9;		/* VP index 9 bits */
> +	__le16	reserved_1_sof : 3;
> +	__le16	sof_type : 4;		/* sof_type is upper nibble */

[Severity: High]
Is it possible the use of bitfields within __le16 here will lead to the same
endianness regressions?

[ ... ]
> +struct vp_rpt_id_entry_24xx_ext {
> +	uint8_t entry_type;		/* Entry type. */
> +	uint8_t entry_count;		/* Entry count. */
> +	uint8_t sys_define;		/* System defined. */
> +	uint8_t entry_status;		/* Entry Status. */
> +	__le32 resv1;
> +	uint8_t vp_acquired;
> +	uint8_t vp_setup;
> +	__le16	vp_idx : 9;		/* VP Index 9bits */
> +	__le16	vp_status : 7;		/* VP Status 7bits */

[Severity: High]
Does this struct also share the big-endian layout issue due to bitfields
in __le16?

> +
> +	uint8_t port_id[3];
> +	uint8_t format;
> +	union {
> +		struct vp_rpt_id_ext_f1 {
[ ... ]
> +		} f1;
> +		struct vp_rpt_id_ext_f2 { /* format 2: N2N direct connect */
[ ... ]
> +		} f2;
> +	} u;
> +};

[Severity: High]
Is this structure missing the trailing padding needed to reach 128 bytes?

While the other extended structures intended for the 128-byte queue include
padding, this struct definition appears to sum to exactly 64 bytes
(16 bytes header plus 48 bytes union). 

Could this lead to incorrect pointer arithmetic or leave half of the
128-byte slot uninitialized?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=9

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 18/88] scsi: qla2xxx: Enable get_firmware_state for 29xx
  2026-07-07  5:53 ` [PATCH v3 18/88] scsi: qla2xxx: Enable get_firmware_state " Nilesh Javali
@ 2026-07-07  6:15   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:15 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Retry block in qla2x00_fw_state_show() defeats chip_is_down protections, issuing mailbox commands to unstable hardware without a lock.
--

--- Patch [18]: [PATCH v3 18/88] scsi: qla2xxx: Enable get_firmware_state for 29xx ---
commit ebf2e3d4435e44a4b6998187bcd7eea09d19c1be
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Enable get_firmware_state for 29xx

Enable get_firmware_state mailbox command for 29xx adapters by adding
IS_QLA29XX() checks alongside existing IS_QLA27XX/IS_QLA28XX checks.
This ensures MBX_12 (MPI state) is properly set up and reported for
29xx adapters.

> diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
> index 7b7722de28444..5a934f40323e6 100644
> --- a/drivers/scsi/qla2xxx/qla_attr.c
> +++ b/drivers/scsi/qla2xxx/qla_attr.c
> @@ -2436,7 +2436,7 @@ qla2x00_mpi_fw_state_show(struct device *dev, struct device_attribute *attr,
>  	u16 mpi_state;
>  	struct qla_hw_data *ha = vha->hw;
>  
> -	if (!(IS_QLA27XX(ha) || IS_QLA28XX(ha)))
> +	if (!(IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)))
>  		return scnprintf(buf, PAGE_SIZE,
>  				"MPI state reporting is not supported for this HBA.\n");
>  

[Severity: High]
This isn't a bug introduced by this patch, but while reviewing the use of
qla2x00_get_firmware_state() in this file, I noticed a potential problem in
qla2x00_fw_state_show() when an ISP reset is active.

In qla2x00_fw_state_show(), rval is initialized to QLA_FUNCTION_FAILED.
If the chip is down or eeh is busy, the code jumps to out:

drivers/scsi/qla2xxx/qla_attr.c:qla2x00_fw_state_show()
	int rval = QLA_FUNCTION_FAILED;
	...
	mutex_lock(&vha->hw->optrom_mutex);
	if (qla2x00_chip_is_down(vha)) {
		mutex_unlock(&vha->hw->optrom_mutex);
		ql_log(ql_log_warn, vha, 0x707c,
		    "ISP reset active.\n");
		goto out;
	} else if (vha->hw->flags.eeh_busy) {
		mutex_unlock(&vha->hw->optrom_mutex);
		goto out;
	}

	rval = qla2x00_get_firmware_state(vha, state);
	mutex_unlock(&vha->hw->optrom_mutex);
out:
	if (rval != QLA_SUCCESS) {
		memset(state, -1, sizeof(state));
		rval = qla2x00_get_firmware_state(vha, state);
	}

Because rval is still QLA_FUNCTION_FAILED when the goto is taken, the check
at out: evaluates to true.

Does this unintentionally force a mailbox command to be issued to the
resetting device anyway, without holding the optrom_mutex lock?

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=18

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 16/88] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx
  2026-07-07  5:53 ` [PATCH v3 16/88] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx Nilesh Javali
@ 2026-07-07  6:15   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:15 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Incomplete feature enablement: BBCR data for QLA29XX adapters is fetched but inaccessible to userspace.
--

--- Patch [16]: [PATCH v3 16/88] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx ---
commit 239e664ad50c43bb93243854a8f202ecb4bc46cc
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx

This commit adds the IS_QLA29XX() check to qla2x00_get_adapter_id() so that
the additional mailbox registers for buffer-to-buffer credit (BBCR) and
SCM/EDC status are read on 29xx adapters.

> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c
[ ... ]
> @@ -1827,7 +1827,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
>  			}
>  		}
>  
> -		if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw)) {
> +		if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw) || IS_QLA29XX(vha->hw)) {
>  			vha->bbcr = mcp->mb[15];

[Severity: Medium]
Does this change miss an update in the BSG interface for userspace?

While vha->bbcr is successfully read from the adapter for 29xx hardware
here, it appears userspace still cannot access it. Looking at
qla27xx_get_bbcr_data() in drivers/scsi/qla2xxx/qla_bsg.c, it unconditionally
rejects 29xx adapters:

drivers/scsi/qla2xxx/qla_bsg.c:qla27xx_get_bbcr_data() {
        ...
        if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
                return -EPERM;
        ...
}

Will this result in QL_VND_GET_BBCR_DATA BSG commands failing with -EPERM
for 29xx adapters, preventing userspace from retrieving the BBCR data read
by this patch?

>  			if (mcp->mb[7] & SCM_EDC_ACC_RECEIVED) {
>  				ql_log(ql_log_info, vha, 0x11a4,
[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=16

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 05/88] scsi: qla2xxx: Add FC operational firmware load for 29xx
  2026-07-07  5:53 ` [PATCH v3 05/88] scsi: qla2xxx: Add FC operational firmware load for 29xx Nilesh Javali
@ 2026-07-07  6:17   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:17 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Endianness bug in `qla29xx_process_rd_image` breaks firmware loading on little-endian hosts.
- [High] `qla29xx_isp_ops` assigns incompatible legacy optrom callbacks, leading to hardware address corruption.
- [Medium] Unvalidated template size leads to Slab Out-of-Bounds read.
- [Medium] Hardcoded dump size in `ql_dump_buffer` can cause a Slab Out-of-Bounds read.
--

--- Patch [5]: [PATCH v3 05/88] scsi: qla2xxx: Add FC operational firmware load for 29xx ---
commit 75f7a974c78a546ee01a4760d5cbc3099056329d
Author: Manish Rangankar <mrangankar@marvell.com>

scsi: qla2xxx: Add FC operational firmware load for 29xx

This commit adds support to load 29xx FC operational firmware from the
filesystem and to set up the corresponding firmware dump template. It
follows the same request_firmware and segment-load pattern used by earlier
adapters in the driver.

> diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
> --- a/drivers/scsi/qla2xxx/qla_init.c
> +++ b/drivers/scsi/qla2xxx/qla_init.c
[ ... ]
> @@ -9128,6 +9131,537 @@ qla81xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
[ ... ]
> +				if (is_first_segment)
> +					opt |= BIT_3;
> +				if (is_last_segment)
> +					opt |= BIT_4;
> +
> +				memcpy((char *)dcode, (char *)fwcode, size);
> +
> +			ql_dbg(ql_dbg_init, vha, 0x0098,

[Severity: High]
Does this break the DMA endianness translation on little-endian hosts?
The TIM section correctly uses swab32() to convert the big-endian firmware
blob into CPU-endian 32-bit words, but here the ARR sections use a raw
memcpy() in qla29xx_process_rd_image(). On little-endian architectures,
this will preserve the big-endian byte order in memory, potentially causing
the adapter DMA to receive byte-swapped instructions and fail to load the
firmware.

[ ... ]
> +	buf = qla29xx_read_optrom_data(vha, FLT_REG_FW_DUMP_TMPLT, 0,
> +				       fw_dump_tmplt, 0,
> +				       ha->fw_dump_tmplt_len);
> +	if (!buf) {
> +		ql_log(ql_log_info, vha, 0x0013,
> +		       "Unable to read fw dump temp info.\n");
> +		goto free_fw_dump;
> +	}
> +
> +	ql_dump_buffer(ql_dbg_init, vha, 0x006b,
> +		       (char *)fw_dump_tmplt, 1024);
> +

[Severity: Medium]
Could this hardcoded 1024-byte size cause a slab out-of-bounds read?
The fw_dump_tmplt buffer is allocated dynamically based on
ha->fw_dump_tmplt_len in qla29xx_load_fw_template(). If the actual template
length returned by the hardware is less than 1024 bytes, this will read
past the end of the kzalloc allocation when logging.

> +	for (j = 0; j < templates; j++, fwdt++) {
> +		vfree(fwdt->template);
> +		fwdt->template = NULL;
> +		fwdt->length = 0;
> +
> +		template_size = le32_to_cpu(((__le32 *)fw_dump_tmplt)[2]);
> +
> +		ql_dbg(ql_dbg_init, vha, 0x0161,
> +		       "-> fwdt%u template array at %p (0x%x bytes)\n",
> +		       j, fw_dump_tmplt, template_size);
> +
> +		if (!template_size || !~template_size ||
> +		    template_size > ha->fw_dump_tmplt_len) {
> +			ql_dbg(ql_dbg_init, vha, 0x0162,
> +			       "-> fwdt%u failed to read array\n", j);
> +			goto failed;
> +		}
> +
> +		fwdt->template = vmalloc(template_size);
> +		if (!fwdt->template) {
> +			ql_log(ql_log_warn, vha, 0x0164,
> +			       "-> fwdt%u failed allocate template.\n", j);
> +			goto failed;
> +		}
> +
> +		dcode = fwdt->template;
> +		memcpy((char *)dcode, (char *)fw_dump_tmplt, template_size);
> +
> +		if (!qla27xx_fwdt_template_valid(dcode)) {

[Severity: Medium]
Is there a missing minimum size check for template_size here?
If the template_size read from the flash template buffer is extremely
small (for example, 4 bytes), the vmalloc allocation succeeds, but
qla27xx_fwdt_template_valid() will read past the end of the allocated
buffer while verifying the template header, which requires up to 32 bytes.

[ ... ]
> diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
> --- a/drivers/scsi/qla2xxx/qla_os.c
> +++ b/drivers/scsi/qla2xxx/qla_os.c
[ ... ]
> @@ -2640,6 +2640,45 @@ static struct isp_operations qla27xx_isp_ops = {
[ ... ]
> +	.beacon_blink		= qla83xx_beacon_blink,
> +	.read_optrom		= qla25xx_read_optrom_data,
> +	.write_optrom		= qla24xx_write_optrom_data,
> +	.get_flash_version	= qla24xx_get_flash_version,

[Severity: High]
Are these legacy optrom callbacks compatible with the 29xx initialization?
In qla2x00_probe_one(), the ha->flash_data_off for 29xx adapters is
initialized to ~0 (0xFFFFFFFF). If qla25xx_read_optrom_data calculates the
hardware flash address by evaluating ha->flash_data_off + offset, it will
wrap around to offset - 1. Could this lead to corrupted hardware address
reads if an unprivileged user reads the sysfs optrom node?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=5

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 01/88] scsi: qla2xxx: Add 29xx series PCI device ID support
  2026-07-07  5:53 ` [PATCH v3 01/88] scsi: qla2xxx: Add 29xx series PCI device ID support Nilesh Javali
@ 2026-07-07  6:21   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:21 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Incomplete hardware initialization and capability detection for QLA29XX adapters.
- [Medium] Missing speed-capability logic modifications promised in the commit message.
--

--- Patch [1]: [PATCH v3 01/88] scsi: qla2xxx: Add 29xx series PCI device ID support ---
commit bb93cf91545563a12abad49c8f50d47644a2f103
Author: Manish Rangankar <mrangankar@marvell.com>
Date:   Tue Jul 7 05:53:08 2026 +0000

    scsi: qla2xxx: Add 29xx series PCI device ID support
    
    The QLA29xx is a new generation FC HBA that shares much of its
    architecture with the 27xx/28xx family.  Register the new PCI
    device IDs, wire up IS_QLA29XX() capability checks in the probe
    and ISP-flags paths,

>     and extend speed-capability logic so the driver correctly recognises and initialises 29xx adapters.

[Severity: Medium]
This isn't a bug, but the commit message states it extends speed-capability
logic. Are there changes missing from this patch? I don't see any
modifications related to speed capability or link rates in the diff.

>     
>     Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
>     Signed-off-by: Nilesh Javali <njavali@marvell.com>
>     Reviewed-by: Hannes Reinecke <hare@kernel.org>

> diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
> index 5593ad7fad274..6337a056b1497 100644
> --- a/drivers/scsi/qla2xxx/qla_def.h
> +++ b/drivers/scsi/qla2xxx/qla_def.h

[ ... ]

> @@ -4372,9 +4386,10 @@ struct qla_hw_data {
>  				IS_QLA25XX(ha) || IS_QLA81XX(ha) || \
>  				IS_QLA82XX(ha) || IS_QLA83XX(ha) || \
>  				IS_QLA8044(ha) || IS_QLA27XX(ha) || \
> -				IS_QLA28XX(ha))
> +				IS_QLA28XX(ha) || IS_QLA29XX(ha))
>  #define IS_MSIX_NACK_CAPABLE(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha) || \
> -				IS_QLA27XX(ha) || IS_QLA28XX(ha))
> +				IS_QLA27XX(ha) || IS_QLA28XX(ha) || \
> +				IS_QLA29XX(ha))
>  #define IS_NOPOLLING_TYPE(ha)	(IS_QLA81XX(ha) && (ha)->flags.msix_enabled)
>  #define IS_FAC_REQUIRED(ha)	(IS_QLA81XX(ha) || IS_QLA83XX(ha) || \
>  				IS_QLA27XX(ha) || IS_QLA28XX(ha))

[Severity: High]
Should IS_FAC_REQUIRED also be updated to include IS_QLA29XX(ha)? It looks
like it was missed here, which could cause missing hardware capability
detection.

[ ... ]

> @@ -4408,12 +4423,12 @@ struct qla_hw_data {
>  #define IS_PI_SPLIT_DET_CAPABLE(ha)	(IS_PI_SPLIT_DET_CAPABLE_HBA(ha) && \
>      (((ha)->fw_attributes_h << 16 | (ha)->fw_attributes) & BIT_22))
>  #define IS_ATIO_MSIX_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha) || \

[Severity: High]
Should IS_ATIO_MSIX_CAPABLE and IS_SHADOW_REG_CAPABLE be updated to include
IS_QLA29XX(ha) as well? The macro definitions appear to omit the new adapters,
which might break ATIO interrupt setup and shadow register support.

> diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
> index 72b1c28e4dae0..c91d2b8bd08e7 100644
> --- a/drivers/scsi/qla2xxx/qla_os.c
> +++ b/drivers/scsi/qla2xxx/qla_os.c

[ ... ]

> @@ -3195,6 +3221,22 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
>  		ha->flash_data_off = FARX_ACCESS_FLASH_DATA_28XX;
>  		ha->nvram_conf_off = ~0;
>  		ha->nvram_data_off = ~0;
> +	} else if (IS_QLA29XX(ha)) {
> +		ha->portnum = PCI_FUNC(ha->pdev->devfn);
> +		ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
> +		ha->mbx_count = MAILBOX_REGISTER_COUNT;
> +		req_length = REQUEST_ENTRY_CNT_83XX;
> +		rsp_length = RESPONSE_ENTRY_CNT_83XX;

[Severity: High]
Does this initialization block miss the ha->tgt.atio_q_length setup?
Looking at qla2x00_probe_one(), the older 27xx and 28xx families initialize
this field:

	} else if (IS_QLA28XX(ha)) {
		...
		req_length = REQUEST_ENTRY_CNT_83XX;
		rsp_length = RESPONSE_ENTRY_CNT_83XX;
		ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX;

Would omitting it for IS_QLA29XX(ha) break Target mode (ATIO)? Also, should
qlt_probe_one_stage1() in qla_target.c and hardware mailbox functions in
qla_mbx.c (such as qla25xx_init_req_que()) be updated to include the
new IS_QLA29XX(ha) check?

> +		ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
> +		ha->init_cb_size = sizeof(struct mid_init_cb_81xx);
> +		ha->gid_list_info_size = 8;
> +		ha->optrom_size = OPTROM_SIZE_28XX;
> +		ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
> +		ha->isp_ops = &qla27xx_isp_ops;
> +		ha->flash_conf_off = ~0;
> +		ha->flash_data_off = ~0;
> +		ha->nvram_conf_off = ~0;
> +		ha->nvram_data_off = ~0;
>  	}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=1

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 07/88] scsi: qla2xxx: Add flash block read/write BSG support for 29xx
  2026-07-07  5:53 ` [PATCH v3 07/88] scsi: qla2xxx: Add flash block read/write BSG support for 29xx Nilesh Javali
@ 2026-07-07  6:21   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:21 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Region-relative flash offset is incorrectly validated against the total flash size.
--

--- Patch [7]: [PATCH v3 07/88] scsi: qla2xxx: Add flash block read/write BSG support for 29xx ---
commit 31fdf59e3896b2b2cd9a7fe0c5b72367e33df679
Author: Manish Rangankar <mrangankar@marvell.com>

scsi: qla2xxx: Add flash block read/write BSG support for 29xx

Introduce QL_VND_READ_FLASH_BLOCK and QL_VND_WRITE_FLASH_BLOCK
BSG vendor commands so that userspace tools can perform flash
block-level operations on 29xx adapters via the isp_ops
interface.

> diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
> index 5e910b5ca670d..ff7421f3535dc 100644
> --- a/drivers/scsi/qla2xxx/qla_bsg.c
> +++ b/drivers/scsi/qla2xxx/qla_bsg.c

[ ... ]

> @@ -1431,42 +1422,79 @@ qla2x00_optrom_setup(struct bsg_job *bsg_job, scsi_qla_host_t *vha,
>  	}
>  
>  	ha->optrom_region_start = start;
> +
> +	if (IS_QLA29XX(ha)) {
> +		if (start > ha->optrom_size) {
> +			ql_log(ql_log_warn, vha, 0x7055,
> +			    "start %d > optrom_size %d.\n", start,
> +			    ha->optrom_size);
>  			return -EINVAL;
>  		}
>  
> +		if (is_update) {
> +			ha->optrom_region_size = start +
> +			    bsg_job->request_payload.payload_len >
> +			    ha->optrom_size ?
> +			    ha->optrom_size - start :
> +			    bsg_job->request_payload.payload_len;

[Severity: High]
Does this allow out-of-bounds cross-region flash operations?

For QLA29XX block operations, start is populated with a region-relative
offset, but it is being validated and clamped against the entire adapter
flash size (ha->optrom_size).

If a user specifies a valid region but provides a region-relative offset
and length that exceeds the specific region's actual boundaries, wouldn't
this code bypass region isolation and allow reading or writing outside the
intended region?

[ ... ]

> +static int qla29xx_bsg_flash_block_write(struct bsg_job *bsg_job)
> +{

[ ... ]

> +	bwcmd =
> +	   (struct qla_block_rw *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2];

[ ... ]

> +	mutex_lock(&ha->optrom_mutex);
> +	rval = qla2x00_optrom_setup(bsg_job, vha, bwcmd->region_offset, 1);

[Severity: High]
This is where the user-provided region-relative offset is passed to
qla2x00_optrom_setup().

After the incorrect global size clamping is applied, the offset is dispatched
to the firmware via write_optrom_region() without any further region boundary
checks.

Could this allow users to overwrite critical areas of flash and potentially
damage the hardware adapter?

> +	if (rval) {
> +		mutex_unlock(&ha->optrom_mutex);
> +		return rval;
> +	}
> +
> +	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
> +			bsg_job->request_payload.sg_cnt, ha->optrom_buffer,
> +			ha->optrom_region_size);

[ ... ]

> +	rval = ha->isp_ops->write_optrom_region(vha, bwcmd->region, opts,
> +				ha->optrom_buffer, ha->optrom_region_start,
> +				ha->optrom_region_size);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=7

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 24/88] scsi: qla2xxx: Add support for QLA29XX in memory allocation
  2026-07-07  5:53 ` [PATCH v3 24/88] scsi: qla2xxx: Add support for QLA29XX in memory allocation Nilesh Javali
@ 2026-07-07  6:22   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:22 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Null Pointer Dereference causing kernel panic on error teardown path for QLA2100/QLA2200 adapters
--

--- Patch [24]: [PATCH v3 24/88] scsi: qla2xxx: Add support for QLA29XX in memory allocation ---
commit db4cb43a507169e793ecdb17d74dbe9af9a96803
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Add support for QLA29XX in memory allocation

Enhance the qla2x00_mem_alloc function to include checks for QLA29XX
adapters. This modification updates the conditions for memory allocation
and cleanup, ensuring proper handling of the new adapter series alongside
existing QLA27XX and QLA28XX checks.

> diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
> index 5450c40259bf9..3be59179a0245 100644
> --- a/drivers/scsi/qla2xxx/qla_os.c
> +++ b/drivers/scsi/qla2xxx/qla_os.c

[ ... ]

> @@ -4681,12 +4683,12 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
>  	}
>  
>  fail_dif_bundl_dma_pool:

[Severity: High]
This is a pre-existing issue, but does the error path in qla2x00_mem_alloc()
just above this change have a potential null pointer dereference?

If the driver probes a QLA2100 or QLA2200 adapter, it skips allocating
ha->ms_iocb and ha->ct_sns, leaving them as NULL, and instead allocates
ha->sns_cmd. If a subsequent memory allocation fails, the teardown path
jumps to fail_req and unconditionally falls through to fail_free_ms_iocb:

fail_req:
        dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt),
                ha->ct_sns, ha->ct_sns_dma);
        ha->ct_sns = NULL;
        ha->ct_sns_dma = 0;
fail_free_ms_iocb:
        dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);

Since dma_pool_free() does not safely handle a NULL vaddr, could this
attempt to write to address 0 and cause a kernel panic?

> -	if (IS_QLA82XX(ha) || ql2xenabledif) {
> +	if (IS_QLA82XX(ha) || IS_QLA29XX(ha) || ql2xenabledif) {
>  		dma_pool_destroy(ha->fcp_cmnd_dma_pool);
>  		ha->fcp_cmnd_dma_pool = NULL;
>  	}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=24

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 15/88] scsi: qla2xxx: Extend execute_fw mailbox to include 29xx
  2026-07-07  5:53 ` [PATCH v3 15/88] scsi: qla2xxx: Extend execute_fw mailbox to include 29xx Nilesh Javali
@ 2026-07-07  6:23   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:23 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] Structure mismatch when preparing CT/FDMI MS IOCBs for QLA29XX adapters results in firmware executing wild DMAs using uninitialized garbage addresses.
--

--- Patch [15]: [PATCH v3 15/88] scsi: qla2xxx: Extend execute_fw mailbox to include 29xx ---
commit 29bbcde0cc31e2b4641ba7302984a3c7980dc0ef
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Extend execute_fw mailbox to include 29xx

Add IS_QLA29XX() to the BPM capability macros and to the
execute-firmware mailbox command so that NVMe enable, minimum
speed negotiation, 128 Gbps speed reporting, EDIF hardware
detection, and FW-semaphore retry logic all apply to 29xx
adapters.

> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c

[ ... ]

> @@ -727,14 +728,13 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
>  				    ha->lr_distance << LR_DIST_FW_POS;
>  		}
>  
> -		if (ql2xnvmeenable && (IS_QLA27XX(ha) || IS_QLA28XX(ha)))
> +		if (ql2xnvmeenable && (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)))
>  			mcp->mb[4] |= NVME_ENABLE_FLAG;
>  
> -		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
> -			struct nvram_81xx *nv = ha->nvram;
> +		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {

[Severity: Critical]
Since this patch enables initialization and capabilities for 29xx adapters,
does it also need to update CT/FDMI MS IOCB preparation to use the 128-byte
extended format required by QLA29XX hardware?

When the driver sends FDMI registrations or CT pass-through commands on
QLA29xx hardware, it unconditionally prepares the buffer using the legacy
64-byte struct ct_entry_24xx format. For example, in
qla24xx_prep_ms_fdmi_iocb():

drivers/scsi/qla2xxx/qla_gs.c:qla24xx_prep_ms_fdmi_iocb() {
    ...
    ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
    memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
    ...
}

And similarly in qla24xx_prep_ms_iocb():

drivers/scsi/qla2xxx/qla_gs.c:qla24xx_prep_ms_iocb() {
    ...
    ct_pkt = (struct ct_entry_24xx *)arg->iocb;
    memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
    ...
}

QLA29XX firmware expects the 128-byte struct ct_entry_24xx_ext format,
where lengths are at offsets 64/68 and DSDs begin at offset 72. Because
ha->ms_iocb is allocated via dma_pool_alloc without __GFP_ZERO, offsets
beyond 64 contain uninitialized memory. 

Can this cause the 29xx firmware to read garbage lengths and DSD DMA addresses
and perform wild DMAs, leading to arbitrary memory corruption or HBA firmware
panics during adapter initialization?

>  			/* set minimum speed if specified in nvram */
>  			if (nv->min_supported_speed >= 2 &&
> -			    nv->min_supported_speed <= 5) {
> +			    nv->min_supported_speed <= 7) {

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=15

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 23/88] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs
  2026-07-07  5:53 ` [PATCH v3 23/88] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs Nilesh Javali
@ 2026-07-07  6:26   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:26 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Standard memset() is called on memory-mapped I/O (MMIO) addresses for QLAFX00 adapters instead of memset_io().
--

--- Patch [23]: [PATCH v3 23/88] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs ---
commit d9b770194269becaf782d0cbaf7f08d1fbe9ade7
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs

This commit refactors __qla2x00_alloc_iocbs() to use the qla_req_ring_slot()
and qla_req_entry_size() helpers. This removes open-coded ring pointer
selection and simplifies the IOCB memory zeroing logic.

> diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
> index 13882a399fbc5..c0cec1f74dfc5 100644
> --- a/drivers/scsi/qla2xxx/qla_iocb.c
> +++ b/drivers/scsi/qla2xxx/qla_iocb.c
[ ... ]
> @@ -2454,13 +2453,8 @@ __qla2x00_alloc_iocbs(struct qla_qpair *qpair, srb_t *sp)
>  	 * layout-compatible once we return the pkt as request_t *.
>  	 */
>  	req->cnt -= req_cnt;
> -	if (IS_QLA29XX(ha)) {
> -		pkt = (request_t *)req->ring_ext_ptr;
> -		memset(pkt, 0, REQUEST_ENTRY_SIZE_EXT);
> -	} else {
> -		pkt = req->ring_ptr;
> -		memset(pkt, 0, REQUEST_ENTRY_SIZE);
> -	}
> +	pkt = qla_req_ring_slot(ha, req);
> +	memset(pkt, 0, qla_req_entry_size(ha));

[Severity: High]
This is a pre-existing issue, but does this standard memset() call cause
faults on strict architectures when using a QLAFX00 adapter?

In qlafx00_config_queues(), req->ring is initialized as an __iomem pointer
for QLAFX00 adapters:

qlafx00_config_queues() {
    ...
    req->ring = (void __force *)ha->iobase + ha->req_que_off;
    ...
}

Since qla_req_ring_slot() returns this __iomem pointer for QLAFX00, calling
memset() instead of memset_io() could trigger unaligned access faults or
unsupported memory transactions (such as Synchronous External Aborts on
ARM64) when allocating an IOCB.

>  	if (IS_QLAFX00(ha)) {
>  		wrt_reg_byte((u8 __force __iomem *)&pkt->entry_count, req_cnt);
>  		wrt_reg_dword((__le32 __force __iomem *)&pkt->handle, handle);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=23

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 22/88] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx
  2026-07-07  5:53 ` [PATCH v3 22/88] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx Nilesh Javali
@ 2026-07-07  6:27   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:27 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] The driver calls `msleep()` from a timer callback, which executes in atomic context, leading to a kernel panic.
- [High] Lockless traversal of the `vp_fcports` lists in `qla2x00_abort_isp_cleanup()` can race with concurrent list modifications.
--

--- Patch [22]: [PATCH v3 22/88] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx ---
commit 4be7d237f6e5d330b03e0072e4dabaec029ba791
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Enable qla2x00_shutdown for 29xx

This patch enables the qla2x00_shutdown behavior for QLA29XX adapters. It
adds an IS_QLA29XX check to the shutdown path to perform firmware abort
cleanup.

> diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
> index 146701445485f..5450c40259bf9 100644
> --- a/drivers/scsi/qla2xxx/qla_os.c
> +++ b/drivers/scsi/qla2xxx/qla_os.c
> @@ -3840,7 +3840,7 @@ qla2x00_shutdown(struct pci_dev *pdev)
>  		qla2x00_disable_eft_trace(vha);
>  
>  	if (IS_QLA25XX(ha) ||  IS_QLA2031(ha) || IS_QLA27XX(ha) ||
> -	    IS_QLA28XX(ha)) {
> +	    IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
>  		if (ha->flags.fw_started)
>  			qla2x00_abort_isp_cleanup(vha);

[Severity: High]
This is a pre-existing issue, but does qla2x00_abort_isp_cleanup() safely
handle atomic context?

If a hardware error occurs, qla2x00_timer() can execute in softirq context
and eventually trigger a sleep:

qla2x00_timer()
  qla_wind_down_chip()
    qla2x00_abort_isp_cleanup()
      msleep(20);

Could this lead to a scheduling while atomic panic?

[Severity: High]
This isn't a bug introduced by this patch, but does qla2x00_abort_isp_cleanup()
safely traverse the vp_fcports lists?

The function iterates over vha->vp_fcports locklessly. It also explicitly drops
vport_slock right before iterating over vp->vp_fcports.

If discovery threads or the dpc_thread concurrently modify these lists, such as
calling list_del() in qla2x00_free_fcport(), could this race and dereference
freed list nodes?

>  	} else {
>  		/* Stop currently executing firmware. */
>  		qla2x00_try_to_stop_firmware(vha);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=22

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 04/88] scsi: qla2xxx: Add 29xx support in queue initialisation path
  2026-07-07  5:53 ` [PATCH v3 04/88] scsi: qla2xxx: Add 29xx support in queue initialisation path Nilesh Javali
@ 2026-07-07  6:31   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:31 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

New issues:
- [Critical] The base queue (queue 0) allocation in `qla2x00_mem_alloc()` is not updated to allocate 128-byte entries for QLA29XX, leading to hardware DMA overruns.
- [Critical] The patch enlarges queue DMA buffers to 128-byte strides for 29xx but retains 64-byte pointer arithmetic, corrupting shadow pointers and fast-path iteration.

Pre-existing issues:
- [High] Inconsistent locking protecting the multiqueue mappings (`req_q_map`/`req_qid_map` and `rsp_q_map`/`rsp_qid_map`) leads to data races.
- [High] Pending response queue work (`qpair->q_work`) is not canceled before freeing the queue and its lock, leading to a Use-After-Free.
--

--- Patch [4]: [PATCH v3 04/88] scsi: qla2xxx: Add 29xx support in queue initialisation path ---
commit 4452b9447592dcc921780bd1cb74d843e0bd2ad1
Author: Manish Rangankar <mrangankar@marvell.com>
Subject: scsi: qla2xxx: Add 29xx support in queue initialisation path

This patch extends queue initialization to support the larger 128-byte
entries used by the 29xx adapters. It adds extended request and response
structures and updates the queue allocation sizes accordingly.

>     The enlarged entry stride doubles the DMA ring memory allocated for
>     both request and response queues on 29xx, and all ring pointer
>     arithmetic must account for the wider entries (handled by later
>     patches in this series).

[Severity: Critical]
Since the current patch enlarges the queue DMA buffers but leaves pointer
arithmetic for later patches, will shadow pointer calculations use the legacy
64-byte stride in the meantime?

For example, in qla25xx_create_req_que() and qla25xx_create_rsp_que():

    req->out_ptr = (uint16_t *)(req->ring + req->length);
    rsp->in_ptr = (uint16_t *)(rsp->ring + rsp->length);

Because req->ring is a 64-byte typed pointer, this will point to the middle
of the 128-byte stride array, which may miscalculate the hardware shadow
index. Additionally, fast-path iterations like rsp->ring_ptr++ in
qla24xx_process_response_queue() will implicitly advance by 64 bytes instead
of 128 bytes, which could cause the driver to interpret the zeroed padding
as the next response.

[Severity: Critical]
Also, it looks like the base queue allocation in qla2x00_mem_alloc() is not
updated to use the new 128-byte size:

    (*req)->ring = dma_alloc_coherent(&ha->pdev->dev,
        ((*req)->length + 1) * sizeof(request_t),
        &(*req)->dma, GFP_KERNEL);

Could this cause hardware DMA overruns during device probe if the base queue
is allocated at half the required size for a 29xx adapter? Is it safe to
separate these changes from the pointer arithmetic and base queue allocation
updates in the series?

> diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
> index c563133f751e8..e75b7ae22bc52 100644
> --- a/drivers/scsi/qla2xxx/qla_mid.c
> +++ b/drivers/scsi/qla2xxx/qla_mid.c
> @@ -574,9 +574,13 @@ qla25xx_free_req_que(struct scsi_qla_host *vha, struct req_que *req)
>  {
>  	struct qla_hw_data *ha = vha->hw;
>  	uint16_t que_id = req->id;
> +	uint16_t reqsz;
> +
> +	reqsz = IS_QLA29XX(ha) ? sizeof(struct request_ext) :
> +				 sizeof(request_t);
>  
>  	dma_free_coherent(&ha->pdev->dev, (req->length + 1) *
> -		sizeof(request_t), req->ring, req->dma);
> +			  reqsz, req->ring, req->dma);
>  	req->ring = NULL;
>  	req->dma = 0;
>  	if (que_id) {

[Severity: High]
This is a pre-existing issue, but is there a data race when cleaning up
these queues?

In qla25xx_create_req_que(), the queue map allocations are protected by
ha->mq_lock. However, in qla25xx_free_req_que() and qla25xx_free_rsp_que(),
it looks like ha->req_q_map is modified without locks, and ha->req_qid_map
is cleared using ha->vport_lock:

    ha->req_q_map[que_id] = NULL;
    mutex_lock(&ha->vport_lock);
    clear_bit(que_id, ha->req_qid_map);
    mutex_unlock(&ha->vport_lock);

Could this mismatch allow concurrent queue creation and teardown to race,
potentially leading to array corruption or dual-allocation of the same
queue ID?

[ ... ]

> @@ -594,6 +598,10 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
>  {
>  	struct qla_hw_data *ha = vha->hw;
>  	uint16_t que_id = rsp->id;
> +	uint16_t rspsz;
> +
> +	rspsz = IS_QLA29XX(ha) ? sizeof(struct response_ext) :
> +				 sizeof(response_t);
>  
>  	if (rsp->msix && rsp->msix->have_irq) {
>  		free_irq(rsp->msix->vector, rsp->msix->handle);
> @@ -601,8 +609,9 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
>  		rsp->msix->in_use = 0;
>  		rsp->msix->handle = NULL;
>  	}
> +

[Severity: High]
This isn't a bug introduced by this patch, but does this teardown path
need to cancel pending response queue work before freeing the DMA memory?

It looks like cancel_work_sync(&qpair->q_work) is not called before
dma_free_coherent() or before kfree(qpair) later on. If an interrupt fires
and queues the work just before free_irq(), could qla_do_work() execute
concurrently and try to access the freed qpair->rsp memory, resulting in a
use-after-free?

>  	dma_free_coherent(&ha->pdev->dev, (rsp->length + 1) *
> -		sizeof(response_t), rsp->ring, rsp->dma);
> +			  rspsz, rsp->ring, rsp->dma);
>  	rsp->ring = NULL;
>  	rsp->dma = 0;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=4

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 25/88] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters
  2026-07-07  5:53 ` [PATCH v3 25/88] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters Nilesh Javali
@ 2026-07-07  6:31   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:31 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [Critical] FORTIFY_SOURCE panic during 128-byte continuation packet processing on QLA29XX adapters due to statically tracked source pointer bounds.

Pre-existing issues:
- [Critical] A crafted FPIN, ELS, or SENSE payload containing the `RESPONSE_PROCESSED` signature causes a hard lockup or permanently stalls the response queue.
--

--- Patch [25]: [PATCH v3 25/88] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters ---
commit ba18e4a152f622363daff6c96a006be46727237c
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters

This commit updates the QLogic qla2xxx driver to handle 128-byte response
queue entries for 29xx adapters. It introduces a new size calculation
routine and updates the status continuation IOCB processing paths to branch
based on the adapter type, ensuring correct data payload sizes are used.

> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> --- a/drivers/scsi/qla2xxx/qla_isr.c
> +++ b/drivers/scsi/qla2xxx/qla_isr.c
> @@ -271,10 +276,12 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
>  
>  	do {
>  		while ((total_bytes > 0) && (entry_count_remaining > 0)) {
> -			new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr;
> -			*pkt = new_pkt;
> +			*pkt = rsp_q->ring_ptr;
> +			data = ((sts_cont_entry_t *)*pkt)->data;
> +			data_sz = qla_sts_cont_data_size(ha);

[Severity: Critical]
Will this trigger a FORTIFY_SOURCE panic on 29xx adapters?

When extracting the data pointer in __qla_copy_purex_to_buffer(), the
packet is explicitly cast to sts_cont_entry_t, which defines the data
array as fixed at 60 bytes. Later in the loop, memcpy() copies up to
data_sz (124 bytes for 29xx) from this pointer.

Since the compiler statically tracks the bounds of the 60-byte array,
won't this cause a panic when processing larger continuation packets?
This same pattern appears to be introduced in qla27xx_copy_multiple_pkt(),
qla27xx_copy_fpin_pkt(), and qla2x00_status_cont_entry().

>  
> -			if (new_pkt->entry_type != STATUS_CONT_TYPE) {
> +			if (((sts_cont_entry_t *)*pkt)->entry_type !=
> +			    STATUS_CONT_TYPE) {
>  				ql_log(ql_log_warn, vha, 0x507a,
>  				    "Unexpected IOCB type, partial data 0x%x\n",
>  				    buffer_copy_offset);
[ ... ]
> -			((response_t *)new_pkt)->signature = RESPONSE_PROCESSED;
> +			((response_t *)*pkt)->signature = RESPONSE_PROCESSED;

[Severity: Critical]
This is a pre-existing issue, but could a crafted payload cause a hard
lockup or queue stall?

The driver uses the in-band sentinel value RESPONSE_PROCESSED
(0xDEADDEAD) at offset 60 to detect when the hardware has finished DMAing
the slot. However, this offset aliases with the packet payload itself
(for example, bytes 56-59 of sts_cont_entry_t::data).

If a remote target or fabric switch sends an FPIN, ELS frame, or SCSI
SENSE response containing 0xDEADDEAD at this exact offset, the hardware
will write this into the ring slot. When the driver polls the signature
in functions like qla27xx_copy_multiple_pkt() or
qla24xx_process_response_queue():

    if (rsp_q->ring_ptr->signature == RESPONSE_PROCESSED) {
        ...
        cpu_relax();
        continue;
    }

Won't it mistakenly assume the slot is unwritten, leading to an infinite
spin loop or a permanently stalled queue?

>  			/* flush signature */
>  			wmb();
>  		}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=25

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 30/88] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx
  2026-07-07  5:53 ` [PATCH v3 30/88] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx Nilesh Javali
@ 2026-07-07  6:31   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:31 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Hardware DMA structures use C bit-fields which break cross-architecture endianness safety.
--

--- Patch [30]: [PATCH v3 30/88] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx ---
commit be1adf44a1c5565c9a85fa67802469ba04fa81af
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Add size check for ELS status entry layout on 29xx

Add a BUILD_BUG_ON in qla2x00_module_init() to validate that struct
els_sts_entry_24xx_ext is 128 bytes, matching the 29xx firmware IOCB
size. It also documents the overlay layout in qla24xx_els_ct_entry().

> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> index e2653620e80be..c286465ae013d 100644
> --- a/drivers/scsi/qla2xxx/qla_isr.c
> +++ b/drivers/scsi/qla2xxx/qla_isr.c
> @@ -2359,6 +2359,22 @@ static void
>  qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
>  		     void *pkt, int iocb_type)
>  {
> +	/*
> +	 * els_sts_entry_24xx_ext (29xx) overlays els_sts_entry_24xx for every
> +	 * field touched in this completion handler: comp_status (offset 8),
> +	 * total_byte_count (32), error_subcode_1 (36), error_subcode_2 (40),
> +	 * d_id[]/s_id[] (24..29), control_flags (30) all sit at byte-identical
> +	 * offsets in both layouts (only vp_index/sof_type at offset 14-15 are
> +	 * bit-packed differently, and that field is write-only on the issue

[Severity: High]
Does this bit-packing break cross-architecture endianness safety for the
hardware DMA structures?

The documentation mentions that vp_index/sof_type are bit-packed, and
looking at the struct definition for els_entry_24xx_ext:

drivers/scsi/qla2xxx/qla_fw29.h:
	__le16	vp_index : 9;		/* VP Index 9bits */
	__le16	reserved_1_sof : 3;
	__le16	sof_type : 4;

And how it is written to the structure:

drivers/scsi/qla2xxx/qla_inline.h:qla_els_set_vp_sof() {
	struct els_entry_24xx_ext *ext = pkt;

	ext->vp_index = vp_idx;
	ext->sof_type = ELS_EXT_EST_SOFI3;
}

Since C standard bit-field layout depends on the compiler's ABI and
endianness, won't this result in garbled values being sent to the 29xx
hardware on big-endian systems?

Could this be updated to use an integer type like __le16 with explicit
bitwise shifts and masks instead of C bit-fields?

> +	 * path -- we never read it here). All reads in this function are
> +	 * therefore stride-agnostic and go through a single struct

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=30

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 28/88] scsi: qla2xxx: Enhance purex_entry handling for 29xx series
  2026-07-07  5:53 ` [PATCH v3 28/88] scsi: qla2xxx: Enhance purex_entry " Nilesh Javali
@ 2026-07-07  6:35   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:35 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [High] Copying continuation IOCBs will trigger a runtime panic under CONFIG_FORTIFY_SOURCE=y on 29xx hardware because the memcpy source bound is evaluated against a 64-byte struct while copying up to 124 bytes.

Pre-existing issues:
- [High] Non-atomic memset of the locklessly shared `default_item` creates a race condition that corrupts payload and control structures for concurrent PUREX allocations.
--

--- Patch [28]: [PATCH v3 28/88] scsi: qla2xxx: Enhance purex_entry handling for 29xx series ---
commit 085db48bd7b4d1648d53d6f0f6d5ff0614740cce
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Enhance purex_entry handling for 29xx series

Update function signatures and internal logic across qla_edif.c, qla_isr.c,
and qla_os.c to accept a generic pointer for packet data. This differentiates
between standard purex_entry_24xx and the extended purex_entry_24xx_ext
structures based on IS_QLA29XX(). It ensures proper initialization and
processing of command and response data for both 64-byte and 128-byte
PUREX IOCBs.

> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> --- a/drivers/scsi/qla2xxx/qla_isr.c
> +++ b/drivers/scsi/qla2xxx/qla_isr.c

[ ... ]

> @@ -262,14 +275,24 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
>  	}
>  
>  	pending_bytes = total_bytes = tpad;
> -	no_bytes = (pending_bytes > sizeof(purex->els_frame_payload))  ?
> -	    sizeof(purex->els_frame_payload) : pending_bytes;
> +	no_bytes = (pending_bytes > payload_size) ?
> +		payload_size : pending_bytes;
> +	if (IS_QLA29XX(ha)) {
> +		struct purex_entry_24xx_ext *purex_ext = *pkt;
>  
> -	memcpy(buf, &purex->els_frame_payload[0], no_bytes);
> +		memcpy(buf, &purex_ext->els_frame_payload[0], no_bytes);
> +	} else {
> +		memcpy(buf, &purex->els_frame_payload[0], no_bytes);
> +	}
>  	buffer_copy_offset += no_bytes;
>  	pending_bytes -= no_bytes;
>  	--entry_count_remaining;
>  
> +	/*
> +	 * response_t::signature and struct response_ext::signature are both u32
> +	 * at offset 60 (handle:4 + data[52]:60), so the 24xx view writes
> +	 * the right slot regardless of stride.
> +	 */
>  	((response_t *)purex)->signature = RESPONSE_PROCESSED;
>  	/* flush signature */
>  	wmb();

[Severity: High]
Will this cause a runtime panic under CONFIG_FORTIFY_SOURCE=y on 29xx hardware
when processing continuation IOCBs?

Later in __qla_copy_purex_to_buffer() (and similarly in
qla27xx_copy_multiple_pkt() and qla27xx_copy_fpin_pkt()), the continuation
IOCB data is extracted using the 64-byte struct view:

do {
    while ((total_bytes > 0) && (entry_count_remaining > 0)) {
        *pkt = rsp_q->ring_ptr;
        data = ((sts_cont_entry_t *)*pkt)->data;
        data_sz = qla_sts_cont_data_size(ha);
        ...
        memcpy((buf + buffer_copy_offset), data, no_bytes);

The compiler tracks the object size of sts_cont_entry_t::data as exactly
60 bytes. On 29xx hardware, data_sz can be up to 124 bytes. Is it possible
that FORTIFY_SOURCE will intercept this as a deterministic out-of-bounds
read (124 > 60) and panic the kernel?

[ ... ]

> @@ -1075,9 +1110,9 @@ qla24xx_alloc_purex_item(scsi_qla_host_t *vha, uint16_t size)
>  	struct purex_item *item = NULL;
>  	uint8_t item_hdr_size = sizeof(*item);
>  
> -	if (size > QLA_DEFAULT_PAYLOAD_SIZE) {
> +	if (size > QLA_MAX_IOCB_SIZE) {
>  		item = kzalloc(item_hdr_size +
> -		    (size - QLA_DEFAULT_PAYLOAD_SIZE), GFP_ATOMIC);
> +		    (size - QLA_MAX_IOCB_SIZE), GFP_ATOMIC);
>  	} else {
>  		if (atomic_inc_return(&vha->default_item.in_use) == 1) {
>  			item = &vha->default_item;

[Severity: High]
This is a pre-existing issue, but can the non-atomic memset of default_item
create a race condition here?

If heavy I/O exhausts standard PUREX item allocations,
qla24xx_alloc_purex_item() falls back to the locklessly shared default_item:

    if (atomic_inc_return(&vha->default_item.in_use) == 1) {
        item = &vha->default_item;

When this item is later freed in qla24xx_free_purex_item(), a non-atomic
memset is used to clear it:

drivers/scsi/qla2xxx/qla_os.c:qla24xx_free_purex_item() {
    if (item == &item->vha->default_item)
        memset(&item->vha->default_item, 0, sizeof(struct purex_item));
}

If memset clears the in_use atomic flag before finishing zeroing the rest
of the struct, could a concurrent hardware interrupt successfully claim the
item and begin writing its payload, only to have the ongoing memset
overwrite and corrupt the newly written data?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=28

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 29/88] scsi: qla2xxx: Update handling of ELS IOCBs for 29xx series
  2026-07-07  5:53 ` [PATCH v3 29/88] scsi: qla2xxx: Update handling of ELS IOCBs " Nilesh Javali
@ 2026-07-07  6:35   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:35 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Hardware-facing structures use C bitfields for little-endian 16-bit integers, breaking on big-endian architectures.
- [High] The extended ABTS response IOCB leaves the upper 64 bytes of the reused DMA buffer uninitialized, leaking stale hardware completion data.
- [High] Virtual Port index (vp_idx) >= 256 is truncated and corrupted during ABTS response processing on 29xx adapters due to an invalid struct cast.
--

--- Patch [29]: [PATCH v3 29/88] scsi: qla2xxx: Update handling of ELS IOCBs for 29xx series ---
commit 75f1549af39bd863f96218c48272faaf14654107
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Update handling of ELS IOCBs for 29xx series

Update ELS IOCB handling to support the extended 128-byte els_entry_24xx_ext
structure used by 29xx series adapters. Change the signatures of ELS issue
paths to accept a generic void pointer and introduce a helper
qla_els_set_vp_sof() for encoding vp_index and sof_type.

> diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
> index cc613bc98e926..695a5b7fc7946 100644
> --- a/drivers/scsi/qla2xxx/qla_inline.h
> +++ b/drivers/scsi/qla2xxx/qla_inline.h
> @@ -863,3 +863,31 @@ qla_sts_fwi2_extract(struct qla_hw_data *ha, void *pkt,
[ ... ]
> +static inline void
> +qla_els_set_vp_sof(struct scsi_qla_host *vha, void *pkt, u16 vp_idx)
> +{
> +	if (IS_QLA29XX(vha->hw)) {
> +		struct els_entry_24xx_ext *ext = pkt;
> +
> +		ext->vp_index = vp_idx;
> +		ext->sof_type = ELS_EXT_EST_SOFI3;

[Severity: High]
Are C bitfields being used for a hardware-facing structure here?

If els_entry_24xx_ext uses __le16 with bitfields for vp_index and sof_type,
the memory layout will be compiler- and endian-dependent. On big-endian
architectures, the bitfields will be packed in reverse order compared to
what the little-endian hardware expects, which could corrupt the virtual
port and SOF values.

> +	} else {
> +		struct els_entry_24xx *e = pkt;
> +
> +		e->vp_index = vp_idx;
> +		e->sof_type = EST_SOFI3;
> +	}
> +}
> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> index c4e2e62f924ff..e2653620e80be 100644
> --- a/drivers/scsi/qla2xxx/qla_isr.c
> +++ b/drivers/scsi/qla2xxx/qla_isr.c
> @@ -83,6 +83,8 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
[ ... ]
>  	/* send ABTS response */
> -	abts_rsp = (void *)rsp_els;
> +	abts_rsp = rsp_pkt;
>  	memset(abts_rsp, 0, sizeof(*abts_rsp));

[Severity: High]
Could this memset clear only 64 bytes instead of the full 128 bytes allocated
for 29xx adapters?

Since abts_rsp is a pointer to the 64-byte legacy struct abts_entry_24xx,
sizeof(*abts_rsp) is 64. If the buffer was populated during the ELS_IOCB_TYPE
command earlier in this function, could this leave stale hardware completion
data in the upper 64 bytes when the buffer is reused for the ABTS_RSP_TYPE
command?

[Severity: High]
Further down in this function, does this legacy cast corrupt the 9th bit of
the virtual port index for 29xx adapters?

    abts_rsp->vp_idx = abts->vp_idx;
    abts_rsp->sof_type = abts->sof_type & 0xf0;

Since the 29xx hardware packs these into a single 16-bit word, the 9th bit
of vp_idx resides in the lower nibble of the byte corresponding to the legacy
8-bit sof_type. Masking with 0xf0 would erase the 9th bit of vp_idx,
potentially breaking I/O aborts on virtual ports with an ID >= 256.

>  	abts_rsp->entry_type = ABTS_RSP_TYPE;
>  	abts_rsp->entry_count = 1;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=29

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 19/88] scsi: qla2xxx: Enable serdes, resource count and FCE trace for 29xx
  2026-07-07  5:53 ` [PATCH v3 19/88] scsi: qla2xxx: Enable serdes, resource count and FCE trace " Nilesh Javali
@ 2026-07-07  6:36   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:36 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

New issues:
- [High] Multi-queue qpairs on 29xx miss the inline IOCB submission optimization, leading to request ring corruption.

Pre-existing issues:
- [High] Enabling FCE trace via debugfs when `fw_dump` is already allocated causes a vmalloc out-of-bounds write during firmware dump.
- [High] Race condition between disabling FCE trace via debugfs and capturing a firmware dump leads to a Use-After-Free of `ha->fce`.
- [High] TOCTOU race condition in `qla2x00_dfs_fce_release()` causing a NULL pointer dereference.
- [High] Data race on bitfields in `ha->flags` between process context and hardware interrupt context.
--

--- Patch [19]: [PATCH v3 19/88] scsi: qla2xxx: Enable serdes, resource count and FCE trace for 29xx ---
commit edd8bd44658eaa99ef8b8fe98bcd34952eb875a0
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Enable serdes, resource count and FCE trace for 29xx

The 29xx adapters share the diagnostic and management interfaces
already supported on ISP27xx/28xx, but several family capability
gates still omitted IS_QLA29XX(), leaving these paths unreachable
on 29xx.

> diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c
> index 43970caca7b39..177d47e92e494 100644
> --- a/drivers/scsi/qla2xxx/qla_dfs.c
> +++ b/drivers/scsi/qla2xxx/qla_dfs.c
> @@ -497,7 +497,7 @@ qla2x00_dfs_fce_write(struct file *file, const char __user *buffer,
>  	unsigned long enable;
>  
>  	if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
> -	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha)) {
> +	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) {
>  		ql_dbg(ql_dbg_user, vha, 0xd034,
>  		       "this adapter does not support FCE.");
>  		return -EINVAL;

[Severity: High]
This isn't a bug introduced by this patch, but enabling FCE trace via debugfs
when fw_dump is already allocated seems to cause a vmalloc out-of-bounds write
during a firmware dump.

If ql2xallocfwdump is true, qla2x00_alloc_fw_dump() is called at probe while
ha->fce is still NULL, omitting FCE_SIZE.  If we then enable FCE via debugfs:

drivers/scsi/qla2xxx/qla_dfs.c:qla2x00_dfs_fce_write() {
    ...
    if (!ha->flags.fce_dump_buf_alloced)
        qla2x00_alloc_fw_dump(vha);
    ...
}

qla2x00_alloc_fw_dump() returns early if ha->fw_dump is non-NULL, skipping
the resize.  When the dump is later captured:

drivers/scsi/qla2xxx/qla_dbg.c:qla25xx_copy_fce() {
    ...
    memcpy(iter_reg, ha->fce, ntohl(fcec->size));
    ...
}

Does this blindly copy FCE_SIZE into the undersized dump buffer, triggering an
out-of-bounds write?

[Severity: High]
This is a pre-existing issue, but there appears to be a race condition between
disabling FCE trace and capturing a firmware dump that leads to a
use-after-free.

Disabling FCE frees the DMA memory and NULLs ha->fce holding only
ha->fce_mutex:

drivers/scsi/qla2xxx/qla_dfs.c:qla2x00_dfs_fce_write() {
    ...
    qla2x00_free_fce_trace(ha);
    ...
}

However, a concurrent firmware dump executes under ha->hardware_lock but not
ha->fce_mutex.  If the debugfs thread frees the trace buffer between the
firmware dump's NULL check and the subsequent memcpy():

drivers/scsi/qla2xxx/qla_dbg.c:qla25xx_copy_fce() {
    ...
    memcpy(iter_reg, ha->fce, ntohl(fcec->size));
    ...
}

Could this result in a use-after-free read and data leak?

[Severity: High]
This is a pre-existing issue, but does qla2x00_dfs_fce_release() have a TOCTOU
race causing a NULL pointer dereference?

The guard condition is checked outside the critical section:

drivers/scsi/qla2xxx/qla_dfs.c:qla2x00_dfs_fce_release() {
    ...
    if (ha->flags.fce_enabled || !ha->fce)
        goto out;

    mutex_lock(&ha->fce_mutex);
    ...
}

If a concurrent thread writes 0 to the FCE node, it acquires the mutex,
frees ha->fce, and sets it to NULL.  When the first thread resumes, it
acquires the mutex and executes:

drivers/scsi/qla2xxx/qla_dfs.c:qla2x00_dfs_fce_release() {
    ...
    memset(ha->fce, 0, fce_calc_size(ha->fce_bufs));
    ...
}

Will this panic the kernel by dereferencing the newly NULL'd pointer?

[Severity: High]
This isn't a bug introduced by this patch, but does modifying user_enabled_fce
cause a data race with hardware interrupt context?

In qla2x00_dfs_fce_write():

drivers/scsi/qla2xxx/qla_dfs.c:qla2x00_dfs_fce_write() {
    ...
    ha->flags.user_enabled_fce = 1;
    ...
}

This executes in process context holding only fce_mutex.  However, it shares
the same 32-bit word as mbox_int:

drivers/scsi/qla2xxx/qla_def.h:
    volatile struct {
        uint32_t    mbox_int            :1;
        ...
        uint32_t    user_enabled_fce    :1;
        ...

The compiler updates these via non-atomic read-modify-write instructions.
If the ISR sets ha->flags.mbox_int = 1 under hardware_lock while the process
context is in the middle of its RMW cycle, won't the process context overwrite
the ISR's update, clearing mbox_int and causing dropped mailbox interrupts?

[ ... ]

> diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
> index bd0684d97fef1..ae9bf68710793 100644
> --- a/drivers/scsi/qla2xxx/qla_init.c
> +++ b/drivers/scsi/qla2xxx/qla_init.c
> @@ -3733,7 +3733,7 @@ int qla2x00_alloc_fce_trace(scsi_qla_host_t *vha)
>  		return -EINVAL;
>  
>  	if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
> -	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
> +	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
>  		return -EINVAL;
>  
>  	if (ha->fce) {

[Severity: High]
When dynamically creating multi-queue qpairs for a 29xx adapter, target paths
can fall back to calling qla2x00_start_iocbs() if the inline IOCB submission
optimization is missed:

drivers/scsi/qla2xxx/qla_init.c:qla2xxx_create_qpair() {
    ...
    if (IS_QLA27XX(ha) || IS_QLA83XX(ha) || IS_QLA28XX(ha))
        qpair->reqq_start_iocbs = qla_83xx_start_iocbs;
    ...
}

Because 29xx adapters use 128-byte IOCBs and track the ring slot using
req->ring_ext_ptr, qla2x00_start_iocbs() fails to advance ring_ext_ptr.

Does the lack of IS_QLA29XX(ha) here cause the next request to blindly overwrite
the same ring slot, corrupting the hardware request queue?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=19

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 27/88] scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling for 29xx series
  2026-07-07  5:53 ` [PATCH v3 27/88] scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling " Nilesh Javali
@ 2026-07-07  6:40   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:40 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Integer truncation of `entry_count` leads to a ring buffer desynchronization vulnerability with large BSG scatterlists.
--

--- Patch [27]: [PATCH v3 27/88] scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling for 29xx series ---
commit 189e3a470ed46e95c6229a85f830b4242d2ff725
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling for 29xx series

Refine the handling of I/O control blocks (IOCBs) for the 29xx series
by introducing support for the extended structure ct_entry_24xx_ext.

> diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
> --- a/drivers/scsi/qla2xxx/qla_iocb.c
> +++ b/drivers/scsi/qla2xxx/qla_iocb.c

[ ... ]

> @@ -3484,7 +3498,11 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb)
>  		append_dsd64(&cur_dsd, sg);
>  		avail_dsds--;
>  	}
> -        ct_iocb->entry_count = entry_count;
> +
> +	if (IS_QLA29XX(ha))
> +		((struct ct_entry_24xx_ext *)pkt)->entry_count = entry_count;
> +	else
> +		((struct ct_entry_24xx *)pkt)->entry_count = entry_count;

[Severity: High]
This is a pre-existing issue, but does assigning the accumulated entry_count
to this 8-bit entry_count field risk integer truncation with large BSG
scatterlists?

If a local user submits a BSG job (like an FC_BSG_HST_CT request) with a
large number of scatterlist entries (for example, 1024 for request and
1024 for reply), qla24xx_calc_ct_iocbs could calculate a required number
of IOCBs greater than 255 (such as 411).

Because the hardware packet header's entry_count is an 8-bit field, a
value like 411 would be truncated to 155.

Would the firmware then only consume 155 packets, leaving the remaining
continuation packets on the ring buffer to be interpreted as new, arbitrary
command headers? This ring buffer desynchronization could cause the firmware
to generate completion interrupts with random handle values. Since the
driver uses this handle to index into req->outstanding_cmds, could this lead
to out-of-bounds accesses, NULL pointer dereferences, or use-after-free
crashes in the completion interrupt handler?

>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=27

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 38/88] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series
  2026-07-07  5:53 ` [PATCH v3 38/88] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series Nilesh Javali
@ 2026-07-07  6:45   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:45 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Virtual Port Index (vp_idx) > 255 is silently truncated when building extended NVMe Link Service Reject IOCBs for 29xx adapters.
--

--- Patch [38]: [PATCH v3 38/88] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series ---
commit ae8cdc66a77f292b668d7a567f0615478271726c
Author: Manish Rangankar <mrangankar@marvell.com>

scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series
    
Extend the LS4 pass-through IOCB handling to support the 128-byte
pt_ls4_request_ext layout used by 29xx series adapters.  The extension
grows inline DSD capacity from 2 to 5 entries.  Function signatures are
widened to void * so both layouts can be passed without casts.

> diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
> index 8fa980db6ce4e..0038b6274d444 100644
> --- a/drivers/scsi/qla2xxx/qla_nvme.c
> +++ b/drivers/scsi/qla2xxx/qla_nvme.c
[ ... ]
>  static void qla_nvme_lsrjt_pt_iocb(struct scsi_qla_host *vha,
> -				   struct pt_ls4_request *lsrjt_iocb,
> +				   void *lsrjt_iocb,
>  				   struct qla_nvme_lsrjt_pt_arg *a)
>  {
[ ... ]
> +	if (IS_QLA29XX(ha)) {
> +		struct pt_ls4_request_ext *ext = lsrjt_iocb;
> +
> +		ext->vp_index = cpu_to_le16(a->vp_idx);

[Severity: Medium]
Will this silently truncate Virtual Port Index (vp_idx) values > 255?

Looking at the definition of struct qla_nvme_lsrjt_pt_arg in
drivers/scsi/qla2xxx/qla_def.h, vp_idx is currently defined as a u8:

struct qla_nvme_lsrjt_pt_arg {
	struct fc_port *fcport;
	u8 opcode;
	u8 vp_idx;
...

Since the new 29xx extended layout expands the hardware vp_index to 16 bits,
is the value already truncated to 8 bits when passed through the vp_idx field
of struct qla_nvme_lsrjt_pt_arg before being assigned here?

For instance, in qla_nvme_ls_req() the 16-bit vha->vp_idx is written to this
u8 field:

	a.vp_idx = vha->vp_idx;
	a.nport_handle = uctx->nport_handle;
	...
	qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);

> +		ext->tx_byte_count = cpu_to_le32(a->tx_byte_count);
> +		ext->rx_byte_count = 0;
> +		put_unaligned_le64(a->tx_addr, &ext->dsd[0].address);
> +		ext->dsd[0].length = cpu_to_le32(a->tx_byte_count);
> +		put_unaligned_le64(a->rx_addr, &ext->dsd[1].address);
> +		ext->dsd[1].length = 0;
> +	} else {

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=38

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 37/88] scsi: qla2xxx: Add size check for extended VP report ID entry
  2026-07-07  5:53 ` [PATCH v3 37/88] scsi: qla2xxx: Add size check for extended VP report ID entry Nilesh Javali
@ 2026-07-07  6:46   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:46 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [High] Accessing hardware structures via C bitfields defined on `__le16` causes silent data corruption on big-endian architectures.

Pre-existing issues:
- [High] Use-after-free of `vp` pointer due to unlocked access without reference pinning.
--

--- Patch [37]: [PATCH v3 37/88] scsi: qla2xxx: Add size check for extended VP report ID entry ---
commit 4fb51fa003e8fc668f1e298648baf9a8b3e6b47d
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Add size check for extended VP report ID entry

This commit adds reserved_end padding to the extended VP report ID entry
structure to match the hardware IOCB stride. It also updates
qla24xx_report_id_acquisition() to handle extracting vp_idx and vp_status
from the larger IOCB format on 29xx series adapters.

> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index 3ebda35dd584f..cec308811d9eb 100644
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c

[ ... ]

> @@ -4102,6 +4105,14 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
>  	if (rptid_entry->entry_status != 0)
>  		return;
>  
> +	if (IS_QLA29XX(ha)) {
> +		vp_idx = rptid_entry_ext->vp_idx;
> +		vp_status = rptid_entry_ext->vp_status;

[Severity: High]
Does accessing hardware structures via C bitfields defined on __le16
cause silent data corruption on big-endian architectures?

If the hardware writes this DMA data in little-endian format, reading it
natively without le16_to_cpu() could result in byte-swapped data being
loaded into registers on big-endian systems. The compiler would then extract
bits from this mangled word, which might lead to corrupted values for vport
indexing and state transitions.

Should we use le16_to_cpu() here before extracting the bitfields?

> +	} else {
> +		vp_idx = rptid_entry->vp_idx;
> +		vp_status = rptid_entry->vp_status;
> +	}
> +
>  	id.b.domain = rptid_entry->port_id[2];

[ ... ]

> @@ -4241,18 +4251,18 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
>  			set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
>  			set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
>  		} else {
> -			if (rptid_entry->vp_status != VP_STAT_COMPL &&
> -				rptid_entry->vp_status != VP_STAT_ID_CHG) {
> +			if (vp_status != VP_STAT_COMPL &&
> +				vp_status != VP_STAT_ID_CHG) {
>  				ql_dbg(ql_dbg_mbx, vha, 0x10ba,
>  				    "Could not acquire ID for VP[%d].\n",
> -				    rptid_entry->vp_idx);
> +				    vp_idx);
>  				return;
>  			}
>  
>  			found = 0;
>  			spin_lock_irqsave(&ha->vport_slock, flags);
>  			list_for_each_entry(vp, &ha->vp_list, list) {
> -				if (rptid_entry->vp_idx == vp->vp_idx) {
> +				if (vp_idx == vp->vp_idx) {
>  					found = 1;
>  					break;
>  				}
>  			}
>  			spin_unlock_irqrestore(&ha->vport_slock, flags);

[Severity: High]
This is a pre-existing issue, but is there a potential use-after-free
of the vp pointer after we release the lock?

The code iterates over ha->vp_list while holding ha->vport_slock to find a
matching vp. Once found, it breaks the loop, releases the spinlock, and
subsequently accesses the vp pointer (e.g., calling qla_update_host_map()
and setting flags further down in qla24xx_report_id_acquisition()).

Since no reference count is taken while the lock is held, could a
concurrent vport deletion remove the vp from the list and free it while
this function is still using it?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=37

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 34/88] scsi: qla2xxx: Enhance ABTS processing for 29xx series
  2026-07-07  5:53 ` [PATCH v3 34/88] scsi: qla2xxx: Enhance ABTS processing " Nilesh Javali
@ 2026-07-07  6:46   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:46 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [High] Hardware DMA structure `struct abts_entry_24xx_ext` uses C bitfields for `vp_idx` and `sof_type`, causing silent data corruption on big-endian architectures.

Pre-existing issues:
- [High] DMA buffer use-after-free and memory corruption when exchange termination IOCB times out.
--

--- Patch [34]: [PATCH v3 34/88] scsi: qla2xxx: Enhance ABTS processing for 29xx series ---
commit f8517ffc817e63944bb6e7bda5e281dcbfefa061
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Enhance ABTS processing for 29xx series

Use extended ABTS entry structures for 29xx series adapters to properly handle
the larger 128-byte IOCB format. Introduce type-generic macros that leverage
the shared field names between abts_entry_24xx and abts_entry_24xx_ext to
avoid code duplication. Add BUILD_BUG_ON size check for struct
abts_entry_24xx_ext.

> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> index dd4416a3bf3ae..774ccf6b2adc8 100644
> --- a/drivers/scsi/qla2xxx/qla_isr.c
> +++ b/drivers/scsi/qla2xxx/qla_isr.c
> @@ -72,15 +72,89 @@ static inline void display_Laser_info(scsi_qla_host_t *vha,

[ ... ]

> +#define QLA_BUILD_ABTS_BA_ACC(rsp, src, sof_val, fctl) do {		\
> +	memset((rsp), 0, sizeof(*(rsp)));				\
> +	(rsp)->entry_type = ABTS_RSP_TYPE;				\
> +	(rsp)->entry_count = 1;						\
> +	(rsp)->nport_handle = (src)->nport_handle;			\
> +	(rsp)->vp_idx = (src)->vp_idx;					\
> +	(rsp)->sof_type = (sof_val);					\

[Severity: High]
Does this assignment in QLA_BUILD_ABTS_BA_ACC cause a regression via silent
data corruption on big-endian architectures?

Because vp_idx and sof_type are defined as C bitfields within
struct abts_entry_24xx_ext (e.g. __le16 vp_idx : 9; __le16 sof_type : 4;),
their memory layout is implementation-defined and endian-dependent.

On big-endian CPUs, the compiler packs the bitfield starting from the
most-significant bit, placing vp_idx at the top of the __le16, contradicting
the hardware's fixed expectation. 

Could this cause the hardware to receive incorrect data, leading to dropped
exchanges or failed commands?

[ ... ]

> @@ -140,60 +212,27 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
>  	}
>  
>  	/* send ABTS response */
> -	abts_rsp = rsp_pkt;
> -	memset(abts_rsp, 0, sizeof(*abts_rsp));
> -	abts_rsp->entry_type = ABTS_RSP_TYPE;
> -	abts_rsp->entry_count = 1;
> -	abts_rsp->nport_handle = abts->nport_handle;
> -	abts_rsp->vp_idx = abts->vp_idx;
> -	abts_rsp->sof_type = abts->sof_type & 0xf0;
> -	abts_rsp->rx_xch_addr = abts->rx_xch_addr;
> -	abts_rsp->d_id[0] = abts->s_id[0];
> -	abts_rsp->d_id[1] = abts->s_id[1];
> -	abts_rsp->d_id[2] = abts->s_id[2];
> -	abts_rsp->r_ctl = FC_ROUTING_BLD | FC_R_CTL_BLD_BA_ACC;
> -	abts_rsp->s_id[0] = abts->d_id[0];
> -	abts_rsp->s_id[1] = abts->d_id[1];
> -	abts_rsp->s_id[2] = abts->d_id[2];
> -	abts_rsp->cs_ctl = abts->cs_ctl;
> -	/* include flipping bit23 in fctl */
> -	fctl = ~(abts->f_ctl[2] | 0x7F) << 16 |
> -	    FC_F_CTL_LAST_SEQ | FC_F_CTL_END_SEQ | FC_F_CTL_SEQ_INIT;
> -	abts_rsp->f_ctl[0] = fctl >> 0 & 0xff;
> -	abts_rsp->f_ctl[1] = fctl >> 8 & 0xff;
> -	abts_rsp->f_ctl[2] = fctl >> 16 & 0xff;
> -	abts_rsp->type = FC_TYPE_BLD;
> -	abts_rsp->rx_id = abts->rx_id;
> -	abts_rsp->ox_id = abts->ox_id;
> -	abts_rsp->payload.ba_acc.aborted_rx_id = abts->rx_id;
> -	abts_rsp->payload.ba_acc.aborted_ox_id = abts->ox_id;
> -	abts_rsp->payload.ba_acc.high_seq_cnt = cpu_to_le16(~0);
> -	abts_rsp->rx_xch_addr_to_abort = abts->rx_xch_addr_to_abort;
> -	ql_dbg(ql_dbg_init, vha, 0x028b,
> -	    "Sending BA ACC response to ABTS %#x...\n",
> -	    abts->rx_xch_addr_to_abort);
> -	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x028b,
> -	    "-------- ELS RSP -------\n");
> -	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x028b,
> -	    (uint8_t *)abts_rsp, sizeof(*abts_rsp));
> -	rval = qla2x00_issue_iocb(vha, abts_rsp, dma, 0);
> -	if (rval) {
> -		ql_log(ql_log_warn, vha, 0x028c,
> -		    "%s: iocb failed to execute -> %x\n", __func__, rval);
> -	} else if (abts_rsp->comp_status) {
> -		ql_log(ql_log_warn, vha, 0x028d,
> -		    "%s: iocb failed to complete -> completion=%#x subcode=(%#x,%#x)\n",
> -		    __func__, abts_rsp->comp_status,
> -		    abts_rsp->payload.error.subcode1,
> -		    abts_rsp->payload.error.subcode2);
> +	if (IS_QLA29XX(ha)) {
> +		struct abts_entry_24xx_ext *rsp_ext = rsp_pkt;
> +
> +		QLA_BUILD_ABTS_BA_ACC(rsp_ext, abts_ext,
> +		    abts_ext->sof_type, fctl);
> +		QLA_LOG_ISSUE_ABTS_RSP(vha, rsp_ext, dma, rval);
>  	} else {
> -		ql_dbg(ql_dbg_init, vha, 0x028ea,
> -		    "%s: done.\n", __func__);
> +		struct abts_entry_24xx *abts_rsp = rsp_pkt;
> +
> +		QLA_BUILD_ABTS_BA_ACC(abts_rsp, abts,
> +		    abts->sof_type & 0xf0, fctl);
> +		QLA_LOG_ISSUE_ABTS_RSP(vha, abts_rsp, dma, rval);
>  	}
>  
>  	dma_free_coherent(&ha->pdev->dev, rsp_sz, rsp_pkt, dma);
>  }

[Severity: High]
This is a pre-existing issue, but does qla24xx_process_abts() in
drivers/scsi/qla2xxx/qla_isr.c have a DMA buffer use-after-free or memory
corruption if the initial exchange termination IOCB times out?

If the initial qla2x00_issue_iocb() operation to terminate the exchange times
out, it returns an error, but the hardware may still be actively reading or
preparing to read from the DMA address. 

The driver then continues execution here, overwrites this same rsp_pkt buffer
via memset() in QLA_BUILD_ABTS_BA_ACC, attempts to issue it a second time for
the ABTS response, and finally calls dma_free_coherent(). 

If the firmware continues processing, could it read/write to freed memory or
interpret the ABTS response data as the original termination command?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=34

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 39/88] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support
  2026-07-07  5:53 ` [PATCH v3 39/88] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support Nilesh Javali
@ 2026-07-07  6:48   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:48 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

New issues:
- [Medium] Incomplete Feature Enablement: D-port diagnostics v2 API is not enabled for QLA29xx adapters because the IS_DPORT_CAPABLE macro was not updated, causing the command to unconditionally reject QLA29xx.

Pre-existing issues:
- [Critical] Information Leak: The function qla2x00_do_dport_diagnostics() leaks up to 62 bytes of uninitialized kernel heap memory to user space.
- [High] Resource Leak / Hanging Process: BSG job hangs indefinitely on validation error because `bsg_job_done()` is skipped but the function returns `QLA_SUCCESS`.
--

--- Patch [39]: [PATCH v3 39/88] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support ---
commit 9bba08258f3cd9b122f24b8370fef88669196b77
Author: Manish Rangankar <mrangankar@marvell.com>

scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support

Extend qla2xxx BSG command handling to recognize QLA29xx adapters and
align feature availability with hardware capabilities.

> diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
> index 8a969174a261e..fade3638d31cb 100644
> --- a/drivers/scsi/qla2xxx/qla_bsg.c
> +++ b/drivers/scsi/qla2xxx/qla_bsg.c

[ ... ]

> @@ -2789,7 +2789,7 @@ qla2x00_do_dport_diagnostics(struct bsg_job *bsg_job)
>  	struct qla_dport_diag *dd;
>  
>  	if (!IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw) &&
> -	    !IS_QLA28XX(vha->hw))
> +	    !IS_QLA28XX(vha->hw) && !IS_QLA29XX(vha->hw))
>  		return -EPERM;

[Severity: Medium]
Does enabling QLA29xx here leave out the v2 diagnostics interface?

Looking at qla2x00_do_dport_diagnostics_v2() in the same file, it relies on
the IS_DPORT_CAPABLE macro:

    if (!IS_DPORT_CAPABLE(vha->hw))
            return -EPERM;

Since IS_DPORT_CAPABLE in qla_def.h only checks for 83XX, 27XX, and 28XX,
won't qla2x00_do_dport_diagnostics_v2() incorrectly reject QLA29xx adapters
and fail with -EPERM?

[Severity: Critical]
This is a pre-existing issue, but does enabling this function for QLA29xx
expose an information leak?

In qla2x00_do_dport_diagnostics(), the dd structure is allocated without
zeroing the memory:

    dd = kmalloc_obj(*dd);

Later, sg_copy_to_buffer() only overwrites the portion of dd provided by
the user's request payload, which can leave bytes uninitialized (such as
the unused fields). Finally, sg_copy_from_buffer() copies the entire
sizeof(*dd) back to the reply payload:

    sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
        bsg_job->reply_payload.sg_cnt, dd, sizeof(*dd));

Could this leak uninitialized kernel heap memory back to user space?

[ ... ]

> @@ -2915,8 +2915,13 @@ qla2x00_get_flash_image_status(struct bsg_job *bsg_job)
>  	struct qla_active_regions regions = { };
>  	struct active_regions active_regions = { };
>  
> -	qla27xx_get_active_image(vha, &active_regions);
> -	regions.global_image = active_regions.global;
> +	if (IS_QLA29XX(ha))
> +		return -EPERM;
> +
> +	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
> +		qla27xx_get_active_image(vha, &active_regions);
> +		regions.global_image = active_regions.global;
> +	}

[Severity: High]
This is a pre-existing issue, but while reviewing the flash image handling
in this file, I noticed a potential hang in qla28xx_validate_flash_image().

When validation fails and rval is non-zero, the error path explicitly skips
calling bsg_job_done() but still returns QLA_SUCCESS:

    if (!rval)
            bsg_job_done(bsg_job, bsg_reply->result,
                         bsg_reply->reply_payload_rcv_len);

    return QLA_SUCCESS;

Since the BSG core expects bsg_job_done() to be called if the driver
returns a success code, won't this cause the user space process to hang
indefinitely waiting for the job to complete?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=39

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 41/88] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors
  2026-07-07  5:53 ` [PATCH v3 41/88] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors Nilesh Javali
@ 2026-07-07  6:49   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:49 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [High] The patch fixes endianness issues for `__le16` bitfields in 29xx extended IOCBs, but misses the identical bitfield layout bug in `struct vp_rpt_id_entry_24xx_ext`.

Pre-existing issues:
- [High] Target mode structures rely on `uint8_t` bitfields that are broken on big-endian architectures.
--

--- Patch [41]: [PATCH v3 41/88] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors ---
commit 2a52cf1de747d5bdd6a7b2e026697c3d01b78f41
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors

This commit fixes endianness issues for __le16 bitfields in the 29xx extended
IOCB structures by replacing them with a single __le16 scalar field and
accessor functions. It applies this fix to els_entry_24xx_ext,
els_sts_entry_24xx_ext, and abts_entry_24xx_ext to prevent incorrect bit
positions on big-endian hosts.

> diff --git a/drivers/scsi/qla2xxx/qla_fw29.h b/drivers/scsi/qla2xxx/qla_fw29.h
> index 600a40d8bd5ff..63bf350ddffc0 100644
> --- a/drivers/scsi/qla2xxx/qla_fw29.h
> +++ b/drivers/scsi/qla2xxx/qla_fw29.h
[ ... ]
> @@ -563,9 +586,7 @@ struct abts_entry_24xx_ext {
>  	__le16	nport_handle;		/* type 0x54 only */
>  
>  	__le16	control_flags;		/* type 0x55 only */
> -	__le16	vp_idx : 9;		/* VP index 9 bits */
> -	__le16	reserved_1_sof : 3;
> -	__le16	sof_type : 4;		/* sof_type is upper nibble */
> +	__le16	vp_idx_sof;		/* bits [8:0]=VP index, [15:12]=SOF type */
>  
>  	__le32	rx_xch_addr;
>  

[Severity: High]
Are there other structures in this file that require a similar update?

It appears struct vp_rpt_id_entry_24xx_ext uses a similar __le16 bitfield 
layout that could result in hardware payload parsing corruption on 
big-endian systems:

struct vp_rpt_id_entry_24xx_ext {
	...
	__le16 vp_idx : 9;
	__le16 vp_status : 7;
	...
}

If left unpatched, could qla24xx_report_id_acquisition() read incorrect 
VP IDs and statuses during firmware responses on big-endian platforms?

[Severity: High]
This is a pre-existing issue, but does the target mode code suffer from a 
similar problem? 

Looking at struct abts_recv_from_24xx and struct atio7_fcp_cmnd in 
drivers/scsi/qla2xxx/qla_target.h, they use uint8_t bitfields directly mapped 
to hardware structures without endianness protections:

struct abts_recv_from_24xx {
	...
	uint8_t reserved_3:4;
	uint8_t sof_type:4;
	...
}

struct atio7_fcp_cmnd {
	...
	uint8_t task_attr:3;
	uint8_t reserved:5;
	...
}

Could these declarations cause hardware payload corruption on big-endian 
hosts for target-mode FC communication, given they lack the usual endianness 
guards?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=41

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 43/88] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats
  2026-07-07  5:53 ` [PATCH v3 43/88] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats Nilesh Javali
@ 2026-07-07  6:49   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:49 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [Medium] Double byte-swap on 32-bit stats and interleaved byte-swap on 64-bit stats on Big Endian architectures causes incorrect statistics.

Pre-existing issues:
- [High] Lockless iteration over queue_pair_map can lead to a Use-After-Free if a queue pair is deleted concurrently.
--

--- Patch [43]: [PATCH v3 43/88] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats ---
commit 49bea1c486fa84692809036041ccbb835c83caa2
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats

This patch updates the statistics retrieval for QLA29XX adapters. It enables
reporting hardware 64-bit FPM transmit and receive word counters instead of
falling back to a software approximation.

> diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
> index 19aa66b8ca521..6cf74f8c9628d 100644
> --- a/drivers/scsi/qla2xxx/qla_attr.c
> +++ b/drivers/scsi/qla2xxx/qla_attr.c
> @@ -2990,7 +2990,8 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost)

[Severity: High]
This is a pre-existing issue, but does this function have a use-after-free
race when accessing queue pairs?

Earlier in qla2x00_get_fc_host_stats(), the code iterates over
queue_pair_map without holding ha->mq_lock or using RCU:

    for (i = 0; i < vha->hw->max_qpairs; i++) {
        qpair = vha->hw->queue_pair_map[i];
        if (!qpair)
            continue;
        ir += qpair->counters.input_requests;

If a user reads sysfs concurrently while a queue pair is being deleted via
qla2xxx_delete_qpair() (which holds mq_lock, removes the map entry, and calls
kfree), could the sysfs reader be preempted after loading a non-NULL qpair
pointer and then dereference freed memory when it resumes?

>  		p->error_frames =
>  		    le32_to_cpu(stats->dropped_frames) +
>  		    le32_to_cpu(stats->discarded_frames);
> -		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
> +		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
> +		    IS_QLA29XX(ha)) {
>  			p->rx_words = le64_to_cpu(stats->fpm_recv_word_cnt);
>  			p->tx_words = le64_to_cpu(stats->fpm_xmit_word_cnt);

[Severity: Medium]
Will this result in corrupted 64-bit statistics on big endian architectures?

When qla24xx_get_isp_stats() populates the buffer via DMA, it performs an
in-place byte-swap of the entire buffer as an array of 32-bit words:

    for ( ; dwords--; iter++)
        le32_to_cpus(iter);

If we apply le64_to_cpu() here on the 64-bit fpm_recv_word_cnt and
fpm_xmit_word_cnt fields after they were already in-place swapped as 32-bit
words, won't it result in the two 32-bit halves being swapped, but their
internal bytes being reversed relative to little endian?

This issue seems to exist for older adapters, but this patch now exposes
QLA29XX to the same problem by adding it to the condition.

>  		} else {
>  			p->rx_words = ib >> 2;
>  			p->tx_words = ob >> 2;
>  		}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=43

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 40/88] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking
  2026-07-07  5:53 ` [PATCH v3 40/88] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking Nilesh Javali
@ 2026-07-07  6:50   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:50 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [High] Queue ring buffer is freed before being removed from the global map, creating a race with asynchronous firmware dumps.
- [High] Pending queue pair worker (`q_work`) is not cancelled during a `qla2xxx_create_qpair()` failure, leading to a Use-After-Free of the `qpair` struct.
- [High] The queue pair `work_struct` (`q_work`) can be queued by the interrupt handler before it is initialized, causing a NULL pointer dereference or list corruption.
--

--- Patch [40]: [PATCH v3 40/88] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking ---
commit 88d0670e7fb3084bb51e8aaca31241034a20bd22
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking

This commit fixes queue teardown bugs in qla25xx_free_req_que() and
qla25xx_free_rsp_que(). It guards dma_free_coherent() with a NULL check
and safely locks map assignments under mq_lock. It also removes redundant
bitmap clears from the creation error paths.

> diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
> index bb2521c052bf8..7072af5b42178 100644
> --- a/drivers/scsi/qla2xxx/qla_mid.c
> +++ b/drivers/scsi/qla2xxx/qla_mid.c
> @@ -576,16 +576,17 @@ qla25xx_free_req_que(struct scsi_qla_host *vha, struct req_que *req)
>  	uint16_t que_id = req->id;
>  	size_t req_entry_size = qla_req_entry_size(ha);
>  
> -	dma_free_coherent(&ha->pdev->dev,
> -			  (req->length + 1) * req_entry_size,
> -			  req->ring, req->dma);
> +	if (req->ring)
> +		dma_free_coherent(&ha->pdev->dev,
> +				  (req->length + 1) * req_entry_size,
> +				  req->ring, req->dma);
>  	req->ring = NULL;

[Severity: High]
This is a pre-existing issue, but does clearing req->ring before removing the
queue from ha->req_q_map create a race condition?

If an asynchronous firmware error interrupt fires right after req->ring is
set to NULL:

ISR
ha->isp_ops->fw_dump()
    qla25xx_copy_mqueues()
        req = ha->req_q_map[que];
        if (!req)
            break;
        ...
        memcpy(ptr, req->ring, req->length * sizeof(request_t));
        
Could the firmware dump locklessly iterate ha->req_q_map, find the queue, and
dereference the newly NULL pointer or freed memory?

>  	req->dma = 0;
>  	if (que_id) {
> +		mutex_lock(&ha->mq_lock);
>  		ha->req_q_map[que_id] = NULL;
> -		mutex_lock(&ha->vport_lock);
>  		clear_bit(que_id, ha->req_qid_map);
> -		mutex_unlock(&ha->vport_lock);
> +		mutex_unlock(&ha->mq_lock);
>  	}

[ ... ]

> @@ -605,16 +606,17 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
>  		rsp->msix->handle = NULL;
>  	}
>  
> -	dma_free_coherent(&ha->pdev->dev,
> -			  (rsp->length + 1) * rsp_entry_size,
> -			  rsp->ring, rsp->dma);
> +	if (rsp->ring)
> +		dma_free_coherent(&ha->pdev->dev,
> +				  (rsp->length + 1) * rsp_entry_size,
> +				  rsp->ring, rsp->dma);
>  	rsp->ring = NULL;
>  	rsp->dma = 0;
>  	if (que_id) {
> +		mutex_lock(&ha->mq_lock);
>  		ha->rsp_q_map[que_id] = NULL;
> -		mutex_lock(&ha->vport_lock);
>  		clear_bit(que_id, ha->rsp_qid_map);
> -		mutex_unlock(&ha->vport_lock);
> +		mutex_unlock(&ha->mq_lock);
>  	}
>  	kfree(rsp);

[Severity: High]
This is a pre-existing issue, but could this teardown path lead to a
use-after-free of the qpair structure?

During a qla2xxx_create_qpair() failure where the response queue creation
succeeds but the request queue creation fails, the error path calls
qla25xx_free_rsp_que(). 

At this point, qpair->rsp->qpair is not yet assigned (it is assigned after
both queues are successfully created). Because of this, the check for
rsp->qpair evaluates to NULL, and cancel_work_sync(&rsp->qpair->q_work)
is bypassed.

If an early interrupt fired on the response queue's MSI-X vector before
teardown, the un-cancelled work item would remain active on ha->wq after
the qpair struct is freed.

[ ... ]

> @@ -942,9 +941,6 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
>  		if (ret != QLA_SUCCESS) {
>  			ql_log(ql_log_fatal, base_vha, 0x00e7,
>  			    "%s failed.\n", __func__);
> -			mutex_lock(&ha->mq_lock);
> -			clear_bit(que_id, ha->rsp_qid_map);
> -			mutex_unlock(&ha->mq_lock);
>  			goto que_failed;
>  		}
>  		vha->flags.qpairs_rsp_created = 1;

[Severity: High]
This is a pre-existing issue, but does this function safely initialize the
workqueue?

Earlier in qla25xx_create_rsp_que(), qla25xx_request_irq() is called, which
unmasks the MSI-X vector and activates the ISR qla2xxx_msix_rsp_q(). Then
qla25xx_init_rsp_que() activates the queue in firmware.

However, INIT_WORK(&qpair->q_work, qla_do_work) is not called until the
very end of the function.

If an interrupt fires immediately after IRQ registration and queue
activation, the ISR calls queue_work() on the work_struct which is completely
zeroed (from the kzalloc of qpair). Could this enqueueing of a
zero-initialized work_struct corrupt workqueue lists or cause a crash when
executing a NULL function pointer?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=40

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 44/88] scsi: qla2xxx: Add 64G/128G port speed setting support
  2026-07-07  5:53 ` [PATCH v3 44/88] scsi: qla2xxx: Add 64G/128G port speed setting support Nilesh Javali
@ 2026-07-07  6:52   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:52 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The 64GB port speed is silently ignored when read from the firmware due to a legacy 0x7 exclusion check.
- [High] Setting port speed to 128GB overflows the 3-bit data rate field, configuring 1GB speed and mistakenly enabling 75 ohm termination.
--

--- Patch [44]: [PATCH v3 44/88] scsi: qla2xxx: Add 64G/128G port speed setting support ---
commit 64dcf15cf40895ac2298a3903e0d9735e5bc9ed2
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Add 64G/128G port speed setting support

The patch adds support for 64G and 128G port speeds by mapping sysfs inputs to
PORT_SPEED_64GB and PORT_SPEED_128GB, and accepting these values in
qla2x00_set_data_rate().

> diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
> index 6cf74f8c9628d..3b24e8a5e29ba 100644
> --- a/drivers/scsi/qla2xxx/qla_attr.c
> +++ b/drivers/scsi/qla2xxx/qla_attr.c

[ ... ]

> @@ -1860,6 +1860,12 @@ qla2x00_port_speed_store(struct device *dev, struct device_attribute *attr,
>  	case 32:
>  		ha->set_data_rate = PORT_SPEED_32GB;
>  		break;
> +	case 64:
> +		ha->set_data_rate = PORT_SPEED_64GB;
> +		break;
> +	case 128:
> +		ha->set_data_rate = PORT_SPEED_128GB;

[Severity: High]
When setting ha->set_data_rate = PORT_SPEED_128GB (which is 0x08), does
this overflow the 3-bit data rate field during adapter initialization?

Looking at qla24xx_config_rings() in drivers/scsi/qla2xxx/qla_init.c:

icb->firmware_options_3 = cpu_to_le32(ha->set_data_rate << 13);

Since the data rate is a 3-bit field (bits 13-15), shifting 0x08 (binary 1000)
by 13 overflows into bit 16.

Bits 13-15 would become 0 (1GB speed), and bit 16 (Enable 75 ohm Termination
Select) would become 1.

Could this cause the hardware to initialize at 1GB instead of 128GB and
erroneously enable hardware termination?

> +		break;
>  	default:

[ ... ]

> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index d0894cc904700..ba822c196894b 100644
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c

[ ... ]

> @@ -5720,6 +5720,8 @@ qla2x00_set_data_rate(scsi_qla_host_t *vha, uint16_t mode)
>  	case PORT_SPEED_8GB:
>  	case PORT_SPEED_16GB:
>  	case PORT_SPEED_32GB:
> +	case PORT_SPEED_64GB:

[Severity: High]
Since PORT_SPEED_64GB is defined as 0x07, will this new speed be silently
ignored by legacy 0x7 exclusion checks when reading from the firmware?

For example, in qla2x00_get_data_rate() the code explicitly drops 0x7:

drivers/scsi/qla2xxx/qla_mbx.c:qla2x00_get_data_rate() {
    ...
    if (mcp->mb[1] != 0x7)
        ha->link_data_rate = mcp->mb[1];
    ...
}

Does this leave the speed as unknown or stale in ha->link_data_rate when the
firmware is operating at 64GB, breaking sysfs port speed reporting?

Similarly, the debug log lower down in this function is also suppressed for
the newly added 64GB speed:

drivers/scsi/qla2xxx/qla_mbx.c:qla2x00_set_data_rate() {
    ...
    if (mcp->mb[1] != 0x7)
        ql_dbg(ql_dbg_mbx, vha, 0x1179,
               "Speed set:0x%x\n", mcp->mb[1]);
    ...
}

> +	case PORT_SPEED_128GB:
>  		val = ha->set_data_rate;
>  		break;
>  	default:

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=44

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 47/88] scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters
  2026-07-07  5:53 ` [PATCH v3 47/88] scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters Nilesh Javali
@ 2026-07-07  6:53   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:53 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Incorrect check for Name Server logout on legacy adapters without extended IDs (ISP2100/ISP2200).
--

--- Patch [47]: [PATCH v3 47/88] scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters ---
commit afcd1252a5a5629b694324167fa2faf1b7a9ef47
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters

This commit fixes Name Server logout detection on FWI2 and later adapters by
reading the correct nport_handle field instead of incorrectly aliasing the
comp_status field.

> diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
> --- a/drivers/scsi/qla2xxx/qla_gs.c
> +++ b/drivers/scsi/qla2xxx/qla_gs.c

[ ... ]

> @@ -192,8 +192,8 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt,
>  			break;
>  		case CS_PORT_LOGGED_OUT:
>  			if (IS_FWI2_CAPABLE(ha)) {
> -				if (le16_to_cpu(ms_pkt->loop_id.extended) ==
> -				    NPH_SNS)
> +				if (le16_to_cpu(((struct ct_entry_24xx *)
> +				    ms_pkt)->nport_handle) == NPH_SNS)
>  					lid_is_sns = true;
>  			} else {
>  				if (le16_to_cpu(ms_pkt->loop_id.extended) ==
>  				    SIMPLE_NAME_SERVER)

[Severity: High]
This isn't a bug introduced by this patch, but does the legacy adapter
path have a similar issue for older adapters without extended IDs?

For older adapters (like ISP2100/2200) where extended IDs are not supported,
the 8-bit loop ID is stored in the id.standard field (byte 1) of the
target_id_t union rather than as a 16-bit little-endian value in extended.

If the 8-bit ID is read using le16_to_cpu(ms_pkt->loop_id.extended) in
qla2x00_chk_ms_status(), could it evaluate to 0x8000 instead of 0x80
(SIMPLE_NAME_SERVER)?

This would cause the check to unconditionally fail, meaning legacy adapters
might also fail to recognize a Name Server logout in the exact same way
that FWI2 adapters were failing.

>  					lid_is_sns = true;
>  			}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=47

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 49/88] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config()
  2026-07-07  5:53 ` [PATCH v3 49/88] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config() Nilesh Javali
@ 2026-07-07  6:54   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:54 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] Missing early reject check in qla84xx_verify_chip() leads to false success.
- [High] Ignored IOCB execution status in qla84xx_updatefw() and qla84xx_mgmt_cmd().
--

--- Patch [49]: [PATCH v3 49/88] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config() ---
commit a6a888a28160758908720e759f4660c0e9650700
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config()

The Modify VP Config completion handler labelled its first error branch
"error status" but tested vpmod->comp_status instead of
vpmod->entry_status. Because CS_COMPLETE is 0, the following
"comp_status != CS_COMPLETE" branch duplicated that test and was dead
code, and entry_status was never examined at all.

Cc: stable@vger.kernel.org

> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index b32ca8ed274d6..59023492c5a98 100644
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c
> @@ -4401,10 +4401,10 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
>  	if (rval != QLA_SUCCESS) {
>  		ql_dbg(ql_dbg_mbx, vha, 0x10bd,
>  		    "Failed to issue VP config IOCB (%x).\n", rval);
> -	} else if (vpmod->comp_status != 0) {
> +	} else if (vpmod->entry_status != 0) {
>  		ql_dbg(ql_dbg_mbx, vha, 0x10be,
>  		    "Failed to complete IOCB -- error status (%x).\n",
> -		    vpmod->comp_status);
> +		    vpmod->entry_status);
>  		rval = QLA_FUNCTION_FAILED;
>  	} else if (vpmod->comp_status != cpu_to_le16(CS_COMPLETE)) {
>  		ql_dbg(ql_dbg_mbx, vha, 0x10bf,

[Severity: High]
This isn't a bug introduced by this patch, but while you are fixing this
missing entry_status check here, does qla84xx_verify_chip() suffer from the
exact same flaw?

During driver initialization or error recovery, if the firmware rejects the
verify IOCB early, it sets entry_status but leaves comp_status as 0 
(CS_COMPLETE) from the initial memset. The driver only evaluates
comp_status:

drivers/scsi/qla2xxx/qla_mbx.c:qla84xx_verify_chip() {
    ...
    status[0] = le16_to_cpu(mn->p.rsp.comp_status);
    status[1] = status[0] == CS_VCS_CHIP_FAILURE ?
        le16_to_cpu(mn->p.rsp.failure_code) : 0;
    ...
}

Could this result in a malfunctioning 84xx adapter, or one with an invalid
firmware image, being falsely marked as initialized and ready?

[Severity: High]
This is also a pre-existing issue, but do qla84xx_updatefw() and
qla84xx_mgmt_cmd() similarly ignore the IOCB execution status?

In both of these functions, the driver calls qla2x00_issue_iocb_timeout()
which verifies that the mailbox command was accepted. However, the driver
then unconditionally sets the result to DID_OK without checking the
entry_status or comp_status of the returned IOCB:

drivers/scsi/qla2xxx/qla_bsg.c:qla84xx_updatefw() {
    ...
    bsg_reply->result = DID_OK;
    ...
}

drivers/scsi/qla2xxx/qla_bsg.c:qla84xx_mgmt_cmd() {
    ...
    bsg_reply->result = DID_OK;
    ...
}

Would this cause BSG operations like firmware updates to silently fail
but report success to userspace, or return zeroed memory for read commands
instead of an error?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=49

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 48/88] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size
  2026-07-07  5:53 ` [PATCH v3 48/88] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size Nilesh Javali
@ 2026-07-07  6:55   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  6:55 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] The patch prevents memory corruption by rejecting VP indices > 128 during disable operations, but fails to restrict their creation, leading to undeletable virtual ports that permanently leak hardware resources.
--

--- Patch [48]: [PATCH v3 48/88] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size ---
commit b7da58fc93a7a4174b4900187f0c2564ae2b437d
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size

This patch fixes an out-of-bounds write in qla25xx_ctrlvp_iocb() by bounding
the virtual port index against the size of the vp_idx_map bitmap. It adds a
check in qla24xx_control_vp() to reject operations on vp_index values that
cannot fit in the 128-bit bitmap, preventing memory corruption.

> diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
> index 59648a9229ace..22f2d81e20099 100644
> --- a/drivers/scsi/qla2xxx/qla_iocb.c
> +++ b/drivers/scsi/qla2xxx/qla_iocb.c

[ ... ]

> diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
> index 7072af5b42178..b7d9c1a53f3ce 100644
> --- a/drivers/scsi/qla2xxx/qla_mid.c
> +++ b/drivers/scsi/qla2xxx/qla_mid.c
> @@ -987,6 +987,14 @@ int qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
>  	if (vp_index == 0 || vp_index >= ha->max_npiv_vports)
>  		return QLA_PARAMETER_ERROR;
>  
> +	/*
> +	 * The VP_CTRL IOCB selects the target VP through a fixed 128-bit
> +	 * (16-byte) vp_idx_map bitmap, so vp_index must fit within it even
> +	 * if firmware advertises more NPIV vports.
> +	 */
> +	if (vp_index > sizeof_field(struct vp_ctrl_entry_24xx, vp_idx_map) * 8)
> +		return QLA_PARAMETER_ERROR;
> +

[Severity: High]
This isn't a bug introduced by this patch, but does this new check leave
undeletable virtual ports that permanently leak hardware resources?

If a user creates more than 128 NPIV vports, the driver will allocate a
vp_index > 128 and successfully create it in firmware. When the user later
deletes the vport, this new check in qla24xx_control_vp() will reject the
operation because the VP index cannot fit in the 128-bit vp_idx_map.

When qla24xx_control_vp() fails, qla24xx_disable_vp() fails and returns -1.
However, qla24xx_vport_delete() ignores this failure:

drivers/scsi/qla2xxx/qla_attr.c:qla24xx_vport_delete() {
    ...
    qla24xx_disable_vp(vha);
    qla2x00_wait_for_sess_deletion(vha);
    ...
}

This proceeds to tear down and free all software structures, leaving the
firmware with a dangling, active VP and permanently leaking the hardware NPIV
slot until a full adapter reset.

Should ha->max_npiv_vports be capped at 128 when it is initialized from the
firmware in qla2x00_get_resource_cnts() to prevent creating these
unmanageable vports in the first place?

drivers/scsi/qla2xxx/qla_mbx.c:qla2x00_get_resource_cnts() {
    ...
    ha->max_npiv_vports = mcp->mb[11];
    ...
}

>  	/* ref: INIT */
>  	sp = qla2x00_get_sp(base_vha, NULL, GFP_KERNEL);
>  	if (!sp)

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=48

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 51/88] scsi: qla2xxx: Initialize NVMe abort_work once at submission
  2026-07-07  5:53 ` [PATCH v3 51/88] scsi: qla2xxx: Initialize NVMe abort_work once at submission Nilesh Javali
@ 2026-07-07  7:00   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:00 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

New issues:
- [High] Moving INIT_WORK to command submission introduces workqueue list corruption on the error path if an abort was concurrently scheduled.

Pre-existing issues:
- [Critical] The error path in qla_nvme_post_cmd and qla_nvme_ls_req unconditionally frees `sp` and nullifies `priv->sp` without lock protection, bypassing `cmd_kref`.
- [High] Data race and missing memory barrier when initializing priv->abort_work, priv->sp, and priv->cmd_lock.
--

--- Patch [51]: [PATCH v3 51/88] scsi: qla2xxx: Initialize NVMe abort_work once at submission ---
commit b03391e97f40b451e572a2e6302f81908aa69bdd
Author: Nilesh Javali <njavali@marvell.com>
Subject: scsi: qla2xxx: Initialize NVMe abort_work once at submission

This patch moves the initialization of priv->abort_work from the abort
handlers to the command submission paths. This is intended to prevent
corrupting the workqueue list by reinitializing an already pending
work item if an abort is issued more than once for the same command.

> diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
> index 0038b6274d444..3b2f255a5d7d4 100644
> --- a/drivers/scsi/qla2xxx/qla_nvme.c
> +++ b/drivers/scsi/qla2xxx/qla_nvme.c

[ ... ]

> @@ -501,6 +500,7 @@ static int qla_nvme_ls_req(struct nvme_fc_local_port *lport,
>  	priv->sp = sp;
>  	kref_init(&sp->cmd_kref);
>  	spin_lock_init(&priv->cmd_lock);
> +	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
>  	nvme = &sp->u.iocb_cmd;

[Severity: High]
This is a pre-existing issue, but is the initialization of priv->abort_work,
priv->sp, and priv->cmd_lock safe from concurrent aborts?

Because the NVMe FC transport can start the block layer timer before
calling command submission, a timeout can trigger a concurrent
qla_nvme_ls_abort() while this code is running.

Without memory barriers, a concurrent abort thread might observe
priv->sp != NULL before the spinlock or work struct are fully initialized
in memory. Could this lead to locking an uninitialized spinlock or
scheduling a partially initialized work struct?

[Severity: High]
If a concurrent abort schedules abort_work while this submission is
executing, and the submission subsequently fails, does this lead to a
use-after-free?

If the submission fails, the transport layer synchronously receives an
error and frees the request buffer. If abort_work was already queued
by a concurrent abort, won't this free the work struct while it is
still pending on the system workqueue?

[ ... ]

> @@ -877,6 +876,7 @@ static int qla_nvme_post_cmd(struct nvme_fc_local_port *lport,
>  
>  	kref_init(&sp->cmd_kref);
>  	spin_lock_init(&priv->cmd_lock);
> +	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
>  	sp->priv = priv;
>  	priv->sp = sp;

[Severity: High]
If a concurrent abort schedules abort_work while this FCP submission is
executing, and the submission subsequently fails, could this cause
workqueue corruption?

If the transport layer receives an error, the block layer retries the
request using the exact same priv structure. This causes INIT_WORK()
to execute again. Will reinitializing this queued work item reset its
list pointers and corrupt the workqueue linked list?

[Severity: Critical]
This is a pre-existing issue, but does the error path in this function
(and similarly in qla_nvme_ls_req) safely handle a concurrent abort?

If qla2x00_start_nvme_mq() fails, the error path nullifies pointers and
frees the structure:

drivers/scsi/qla2xxx/qla_nvme.c:qla_nvme_post_cmd() {
    ...
    rval = qla2x00_start_nvme_mq(sp);
    if (rval != QLA_SUCCESS) {
        ql_dbg(ql_dbg_io + ql_dbg_verbose, vha, 0x212d,
            "qla2x00_start_nvme_mq failed = %d\n", rval);
        sp->priv = NULL;
        priv->sp = NULL;
        qla2xxx_rel_qpair_sp(sp->qpair, sp);
    }
    ...
}

If a concurrent abort already executed, cmd_kref is elevated and
abort_work is scheduled. This error path locklessly sets priv->sp = NULL
and unconditionally frees sp back to the mempool, bypassing the reference
held by the pending abort_work.

When qla_nvme_abort_work() later executes, won't it either read the
nullified priv->sp or operate on the freed sp structure, causing a
use-after-free?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=51

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 50/88] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition()
  2026-07-07  5:53 ` [PATCH v3 50/88] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition() Nilesh Javali
@ 2026-07-07  7:03   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:03 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [High] The lockless atomic_dec(&vp->vref_count) lacks a memory barrier, allowing CPU reordering that leads to a Use-After-Free.

Pre-existing issues:
- [High] Failing to check the VPORT_DELETE flag before updating the vport allows a dying vport to be resurrected into the global B-tree, creating a dangling pointer.
--

--- Patch [50]: [PATCH v3 50/88] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition() ---
commit cc05285288870e8f9cfc205d3a17b3cc31c7a415
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition()

This commit acquires a vport reference (vref_count) under vport_slock
during format 1 ID acquisition to prevent concurrent teardown. The
reference is dropped after the last use of the vp pointer.

> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index 59023492c5a98..ba4a4764de1fb 100644
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c
> @@ -4267,6 +4267,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
>  			list_for_each_entry(vp, &ha->vp_list, list) {
>  				if (vp_idx == vp->vp_idx) {
>  					found = 1;
> +					atomic_inc(&vp->vref_count);
>  					break;
>  				}
>  			}

[Severity: High]
This is a pre-existing issue, but should we check for the VPORT_DELETE flag
here before taking the reference?

If qla24xx_vport_delete() is running concurrently, it sets VPORT_DELETE and
clears vha->d_id.b24. If we don't check test_bit(VPORT_DELETE, &vp->dpc_flags),
we could take a reference on a dying vport.

When qla_update_host_map(vp, id) is subsequently called just below this block,
it might re-insert this dying vp back into the ha->host_map B-tree.

After the reference is dropped and the vport freed, could this leave a dangling
pointer in the B-tree?

Also, is it safe that qla_update_host_map(vp, id) is called without holding
ha->vport_slock?

[ ... ]

> @@ -4284,6 +4285,8 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
>  			set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
>  			set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
>  			set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
> +
> +			atomic_dec(&vp->vref_count);

[Severity: High]
Could the CPU reorder the set_bit() stores to happen after this atomic_dec()?

On weakly-ordered architectures like ARM64 or PowerPC, atomic_dec() does not
provide a memory barrier.

If qla24xx_deallocate_vp_id() is polling for atomic_read(&vp->vref_count) == 0
concurrently, it might observe the 0 and free the vp struct before the
set_bit() operations are globally visible.

Would it be safer to use atomic_dec_and_test() or add a memory barrier here
to ensure the bit flag updates complete before the reference is released?

>  		}
>  		set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
>  		qla2xxx_wake_dpc(vha);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=50

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 54/88] scsi: qla2xxx: Fix BSG job leak on validate flash image error path
  2026-07-07  5:54 ` [PATCH v3 54/88] scsi: qla2xxx: Fix BSG job leak on validate flash image error path Nilesh Javali
@ 2026-07-07  7:03   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:03 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] Payload truncation and loss of vendor response on error paths in BSG handlers.
- [High] Out-of-bounds read of `vendor_cmd` array and other flexible fields due to insufficient `request_len` validation in the BSG transport and qla2xxx driver.
--

--- Patch [54]: [PATCH v3 54/88] scsi: qla2xxx: Fix BSG job leak on validate flash image error path ---
commit e89e7bde46a1b68a965d717fd50351f1638ad942
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Fix BSG job leak on validate flash image error path

This commit removes the conditional check around bsg_job_done() in
qla28xx_validate_flash_image() to ensure the BSG job is always completed.
Since the function unconditionally returns QLA_SUCCESS, the transport layer
will not complete the job, so the driver must do it to prevent resource
leaks on error paths.

> diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
> index e7739cead967a..0c9174d6c887a 100644
> --- a/drivers/scsi/qla2xxx/qla_bsg.c
> +++ b/drivers/scsi/qla2xxx/qla_bsg.c
> @@ -3782,9 +3782,8 @@ static int qla28xx_validate_flash_image(struct bsg_job *bsg_job)
>  	bsg_reply->result = DID_OK << 16;
>  	bsg_reply->reply_payload_rcv_len = 0;
>  	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
> -	if (!rval)
> -		bsg_job_done(bsg_job, bsg_reply->result,
> -			     bsg_reply->reply_payload_rcv_len);
> +	bsg_job_done(bsg_job, bsg_reply->result,
> +		     bsg_reply->reply_payload_rcv_len);
>  
>  	return QLA_SUCCESS;
>  }

[Severity: High]
This is a pre-existing issue, but other BSG handlers seem to share the
same flawed error-handling pattern that was just fixed here. For example, 
in drivers/scsi/qla2xxx/qla_bsg.c:qla2x00_mailbox_passthru():

        if (!ret)
                bsg_job_done(bsg_job, bsg_reply->result, 
                             bsg_reply->reply_payload_rcv_len);
        kfree(req_data);
        return ret;

And similarly in qla2x00_manage_host_stats():

        if (!ret)
                bsg_job_done(bsg_job, bsg_reply->result,
                             bsg_reply->reply_payload_rcv_len);
        return ret;

When ret != 0, bsg_job_done() is skipped and the error code is returned
to the transport. Does this cause the transport's error path to force
reply_payload_rcv_len = 0, discarding the mbx_out registers and vendor 
status?

If so, user space applications relying on the BSG vendor response 
payload to diagnose command failures might receive truncated payloads.

[Severity: High]
This is also a pre-existing issue, but could there be an out-of-bounds
read of the vendor_cmd array due to insufficient request_len validation?

In drivers/scsi/scsi_transport_fc.c:fc_bsg_host_dispatch(), the switch 
statement updates cmdlen but appears to skip bounds-checking 
job->request_len against it:

        cmdlen += sizeof(struct fc_bsg_host_vendor);
        if ((shost->hostt->vendor_id == 0L) ||
            (bsg_request->rqst_data.h_vendor.vendor_id !=
                shost->hostt->vendor_id)) {

Then, down in drivers/scsi/qla2xxx/qla_bsg.c:qla24xx_bsg_request(), the 
driver blindly accesses vendor_cmd[0] before checking msgcode or 
request_len:

        if (bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_MANAGE_HOST_PORT ||
            bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS)

And in qla2x00_process_loopback(), it accesses vendor_cmd[1] and [2]:

        elreq.options = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
        elreq.iteration_count =
            bsg_request->rqst_data.h_vendor.vendor_cmd[2];

If a privileged user sends a BSG command with a request_len smaller than 
the expected size, could this lead to an out-of-bounds read past the 
allocated buffer?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=54

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 62/88] scsi: qla2xxx: Don't query firmware state while chip is down
  2026-07-07  5:54 ` [PATCH v3 62/88] scsi: qla2xxx: Don't query firmware state while chip is down Nilesh Javali
@ 2026-07-07  7:03   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:03 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] The `qla2x00_fw_state_show` sysfs attribute handler leaks 10 bytes of uninitialized kernel stack memory to unprivileged userspace on older (non-FWI2 capable) adapters.
- [High] TOCTOU race condition in `qlafx00_fw_state_show()` due to lockless checks, allowing unsynchronized mailbox commands during ISP reset.
--

--- Patch [62]: [PATCH v3 62/88] scsi: qla2xxx: Don't query firmware state while chip is down ---
commit 7d6c905f305cb054a288e70e2b54f81eb14573f5
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Don't query firmware state while chip is down

This commit addresses an issue where qla2x00_fw_state_show() issues an
unsynchronized mailbox command during an ISP reset or PCI error recovery.
It updates the error path to avoid re-issuing qla2x00_get_firmware_state()
outside of the optrom_mutex. This ensures mailbox commands are only sent
when the chip is up and not EEH-busy.

> diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
> index 6a87d3bb0b0eb..a4ca22024edeb 100644
> --- a/drivers/scsi/qla2xxx/qla_attr.c
> +++ b/drivers/scsi/qla2xxx/qla_attr.c
> @@ -1678,10 +1678,8 @@ qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr,
>  	rval = qla2x00_get_firmware_state(vha, state);
>  	mutex_unlock(&vha->hw->optrom_mutex);
>  out:
> -	if (rval != QLA_SUCCESS) {
> +	if (rval != QLA_SUCCESS)
>  		memset(state, -1, sizeof(state));
> -		rval = qla2x00_get_firmware_state(vha, state);
> -	}
>  
>  	return scnprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
>  	    state[0], state[1], state[2], state[3], state[4], state[5]);

[Severity: High]
This is a pre-existing issue, but does this function leak uninitialized kernel
stack memory to userspace on older adapters?

In qla2x00_fw_state_show(), the state array is declared on the stack without
initialization. If qla2x00_get_firmware_state() succeeds on a non-FWI2
adapter, only the first element is populated:

qla2x00_get_firmware_state() {
        ...
        states[0] = mcp->mb[1];
        if (IS_FWI2_CAPABLE(vha->hw)) {
                states[1] = mcp->mb[2];
                ...
        }
        ...
}

The remaining elements (state[1] through state[5]) remain uninitialized, but
they are unconditionally formatted by scnprintf() and exposed to userspace
when reading the sysfs attribute. Should the state array be zero-initialized
when it is declared?

[Severity: High]
This isn't a bug introduced by this patch, but while fixing the missing
serialization here, is the qlafx00-specific path still vulnerable to the
same adapter hang?

When qla2x00_fw_state_show() calls qlafx00_fw_state_show() earlier in the
function for fx00 adapters, the reset and EEH busy states are checked
without holding the optrom_mutex:

qlafx00_fw_state_show() {
        ...
        if (qla2x00_reset_active(vha))
                ql_log(ql_log_warn, vha, 0x70ce, "ISP reset active.\n");
        else if (!vha->hw->flags.eeh_busy) {
                rval = qlafx00_get_firmware_state(vha, state);
        }
        ...
}

If an ISP reset or PCI error recovery begins immediately after this lockless
check, does qlafx00_get_firmware_state() issue an unsynchronized mailbox
command to the resetting chip? Could this be prevented by applying the same
mutex protection to the qlafx00 branch?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=62

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 55/88] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions()
  2026-07-07  5:54 ` [PATCH v3 55/88] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions() Nilesh Javali
@ 2026-07-07  7:05   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:05 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [High] Missing bounds check on user-supplied `i2c->length` in `qla2x00_read_i2c` and `qla2x00_write_i2c` leads to severe kernel stack and DMA pool heap buffer overflows.
- [High] Kernel stack memory is leaked to userspace and hardware due to uninitialized `bsg` stack buffers and missing payload length validation.
- [Medium] Preexisting leak of uninitialized kernel memory to user space via `bsg_reply->reply_payload_rcv_len`.
--

--- Patch [55]: [PATCH v3 55/88] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions() ---
commit 649b4e1d5ee505bfadef89861d13191eb9aaf100
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions()

qla2x00_update_fru_versions() copies the user-supplied BSG request into
a fixed 256-byte stack buffer (bsg[DMA_POOL_SIZE]) and then iterates
list->count times over the qla_image_version array embedded in that
buffer, advancing the image pointer each iteration. count is taken
directly from user input with no upper bound, while only
(DMA_POOL_SIZE - sizeof(list->count)) / sizeof(struct qla_image_version)
= 6 entries actually fit. A larger count walks the image pointer off the
end of the stack buffer, reading adjacent kernel stack memory and
sending it to the device via qla2x00_write_sfp().

Reject requests whose declared count does not fit in the buffer.

> diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
> --- a/drivers/scsi/qla2xxx/qla_bsg.c
> +++ b/drivers/scsi/qla2xxx/qla_bsg.c
> @@ -1977,6 +1977,13 @@ qla2x00_update_fru_versions(struct bsg_job *bsg_job)
>  
>  	image = list->version;
>  	count = list->count;
> +
> +	if (struct_size(list, version, count) > sizeof(bsg)) {
> +		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
> +		    EXT_STATUS_INVALID_PARAM;
> +		goto dealloc;
> +	}
> +
>  	while (count--) {
>  		memcpy(sfp, &image->field_info, sizeof(image->field_info));
>  		rval = qla2x00_write_sfp(vha, sfp_dma, sfp,

[ ... ]

> @@ -2005,6 +2012,8 @@ qla2x00_update_fru_versions(struct bsg_job *bsg_job)
>  
>  done:
>  	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
>  	bsg_reply->result = DID_OK << 16;
>  	bsg_job_done(bsg_job, bsg_reply->result,
>  		       bsg_reply->reply_payload_rcv_len);

[Severity: Medium]
This is a pre-existing issue, but is bsg_reply->reply_payload_rcv_len properly
initialized here? 

Both error paths (including the newly added length check) and the success path
jump to the done label, but bsg_reply->reply_payload_rcv_len is passed to
bsg_job_done() without ever being assigned a value.

Does this leak 4 bytes of uninitialized kernel memory to user space via the BSG
reply?

[ ... ]

> @@ -2012,6 +2021,21 @@ qla2x00_read_fru_status(struct bsg_job *bsg_job)
>  {
>  	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
>  	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
>  	scsi_qla_host_t *vha = shost_priv(host);
>  	struct qla_hw_data *ha = vha->hw;
>  	int rval = 0;
>  	uint8_t bsg[DMA_POOL_SIZE];
>  	struct qla_status_reg *sr = (void *)bsg;

[Severity: High]
This isn't a bug introduced by this patch, but does this uninitialized bsg array
cause a memory leak?

If a user issues a BSG command (e.g., QL_VND_READ_FRU_STATUS) with a truncated
payload length, sg_copy_to_buffer partially populates the uninitialized 256-byte
bsg stack array. 

The uninitialized data is then used in device commands and copied back to the
user reply via sg_copy_from_buffer. Can this leak up to 256 bytes of
uninitialized kernel stack memory to userspace and hardware?

[ ... ]

> @@ -2115,18 +2124,31 @@ qla2x00_write_i2c(struct bsg_job *bsg_job)
>  {
>  	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
>  	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
>  	scsi_qla_host_t *vha = shost_priv(host);
>  	struct qla_hw_data *ha = vha->hw;
>  	int rval = 0;
>  	uint8_t bsg[DMA_POOL_SIZE];
>  	struct qla_i2c_access *i2c = (void *)bsg;
>  	dma_addr_t sfp_dma;
>  	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
>  
>  	if (!sfp) {
>  		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
>  		    EXT_STATUS_NO_MEMORY;
>  		goto done;
>  	}
>  
>  	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
>  	    bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c));
>  
>  	memcpy(sfp, i2c->buffer, i2c->length);

[Severity: High]
This is a pre-existing issue, but does this lack bounds checking on i2c->length?

A user can invoke the BSG interface with QL_VND_WRITE_I2C and provide an
i2c->length larger than the 256-byte DMA pool allocation (sfp).

Will this cause an unbounded read from the stack and an unbounded write into the
256-byte DMA pool buffer?

[ ... ]

> @@ -2157,20 +2179,35 @@ qla2x00_read_i2c(struct bsg_job *bsg_job)
>  {
>  	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
>  	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
>  	scsi_qla_host_t *vha = shost_priv(host);
>  	struct qla_hw_data *ha = vha->hw;
>  	int rval = 0;
>  	uint8_t bsg[DMA_POOL_SIZE];
>  	struct qla_i2c_access *i2c = (void *)bsg;

[Severity: High]
This is a pre-existing issue, but does this uninitialized bsg array
cause a memory leak?

Similar to qla2x00_read_fru_status, if a user issues a BSG command (e.g.,
QL_VND_READ_I2C) with a truncated payload length, sg_copy_to_buffer partially
populates the uninitialized 256-byte bsg stack array. 

Can this leak up to 256 bytes of uninitialized kernel stack memory to userspace
and hardware?

>  	dma_addr_t sfp_dma;
>  	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
>  
>  	if (!sfp) {
>  		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
>  		    EXT_STATUS_NO_MEMORY;
>  		goto done;
>  	}
>  
>  	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
>  	    bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c));
>  
>  	rval = qla2x00_read_sfp(vha, sfp_dma, sfp,
>  		i2c->device, i2c->offset, i2c->length, i2c->option);
>  
>  	if (rval) {
>  		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
>  		    EXT_STATUS_MAILBOX;
>  		goto dealloc;
>  	}
>  
>  	memcpy(i2c->buffer, sfp, i2c->length);

[Severity: High]
This is a pre-existing issue, but is there a missing bounds check on
i2c->length here as well?

If a user invokes the BSG interface with QL_VND_READ_I2C and provides an
i2c->length larger than the 64-byte stack buffer (i2c->buffer) inside the
256-byte bsg array, can this cause an unbounded copy into the fixed 64-byte
stack buffer?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=55

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 53/88] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak
  2026-07-07  5:54 ` [PATCH v3 53/88] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak Nilesh Javali
@ 2026-07-07  7:06   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:06 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Uninitialized kernel stack memory leak and severe stack buffer overflow in multiple BSG handlers.
- [High] Calling `memset()` on a buffer after `dma_map_single()` and using unaligned structs causes DMA cacheline sharing and data corruption.
- [High] Out-of-bounds heap read due to missing `bsg_job->request_len` validation.
--

--- Patch [53]: [PATCH v3 53/88] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak ---
commit 4b26f2e53fe6f8ddd499850e1bc9b9280efa8bd9
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak

This commit fixes an information leak in qla2x00_do_dport_diagnostics().
It changes the allocation of the qla_dport_diag response buffer from
kmalloc_obj() to kzalloc_obj() to ensure the buffer is fully zeroed
before it is copied to user space.

> diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
> index fade3638d31cb..e7739cead967a 100644
> --- a/drivers/scsi/qla2xxx/qla_bsg.c
> +++ b/drivers/scsi/qla2xxx/qla_bsg.c
> @@ -2792,7 +2792,7 @@ qla2x00_do_dport_diagnostics(struct bsg_job *bsg_job)
>  	    !IS_QLA28XX(vha->hw) && !IS_QLA29XX(vha->hw))
>  		return -EPERM;
>  
> -	dd = kmalloc_obj(*dd);
> +	dd = kzalloc_obj(*dd);
>  	if (!dd) {
>  		ql_log(ql_log_warn, vha, 0x70db,
>  		    "Failed to allocate memory for dport.\n");

[Severity: High]
This is a pre-existing issue, but does calling memset() on a buffer after
dma_map_single() corrupt the DMA data?

In drivers/scsi/qla2xxx/qla_mbx.c:qla26xx_dport_diagnostics(), the code does:

	dd_dma = dma_map_single(&vha->hw->pdev->dev,
	    dd_buf, size, DMA_FROM_DEVICE);
...
	memset(dd_buf, 0, size);

The dma_map_single() call invalidates the CPU cache for the buffer on
non-coherent architectures. The immediate memset() dirties the CPU cache lines,
which could overwrite the diagnostic data written by the device via DMA if
they are evicted to main memory.

Additionally, the mapped buffer dd_buf starts at an offset that may not be
aligned to ARCH_DMA_MINALIGN. Mapping unaligned buffers can corrupt adjacent
struct fields sharing the same cache line. The same DMA cacheline sharing and
memset corruption pattern occurs in qla26xx_dport_diagnostics_v2().


[Severity: Critical]
This is a pre-existing issue, but is there a risk of an uninitialized kernel
stack memory leak and stack buffer overflow in multiple BSG handlers?

In drivers/scsi/qla2xxx/qla_bsg.c:qla2x00_read_i2c():

	uint8_t bsg[DMA_POOL_SIZE];
	struct qla_i2c_access *i2c = (void *)bsg;
...
	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
	    bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c));

	rval = qla2x00_read_sfp(vha, sfp_dma, sfp,
		i2c->device, i2c->offset, i2c->length, i2c->option);
...
	memcpy(i2c->buffer, sfp, i2c->length);
	sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
	    bsg_job->reply_payload.sg_cnt, i2c, sizeof(*i2c));

If the user provides a short payload, i2c->length remains uninitialized. It is
then used as the size for memcpy() without bounds checking, which could cause a
stack buffer overflow (writing past the bsg array) and a heap out-of-bounds
read from the sfp DMA pool buffer.

Passing this unbounded length to qla2x00_read_sfp() can also cause a DMA
buffer overflow into the kernel heap. Finally, the bsg buffer is copied back
to user space, potentially leaking uninitialized kernel stack memory. A similar
leak pattern exists in qla2x00_read_fru_status().


[Severity: High]
This is a pre-existing issue, but does this code read out-of-bounds heap memory
if the user provides a short request?

In drivers/scsi/qla2xxx/qla_bsg.c:qla84xx_mgmt_cmd(), the handler expects a
vendor-specific structure appended to the generic request:

	ql84_mgmt = (void *)bsg_request + sizeof(struct fc_bsg_request);
	switch (ql84_mgmt->mgmt.cmd) {

While the block layer validates that bsg_job->request_len covers the generic
struct fc_bsg_request, there does not appear to be validation that it covers
the appended qla_bsg_a84_mgmt structure.

If a user issues a BSG command with a request_len of exactly
sizeof(struct fc_bsg_request), this function will read out of bounds from the
allocated heap buffer, which can cause a kernel panic if the read crosses a
page boundary. A similar out-of-bounds heap read exists in qla24xx_iidma() due
to a lack of request_len validation for the appended qla_port_param structure.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=53

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 56/88] scsi: qla2xxx: Clamp MSI-X derived queue counts to avoid truncation
  2026-07-07  5:54 ` [PATCH v3 56/88] scsi: qla2xxx: Clamp MSI-X derived queue counts to avoid truncation Nilesh Javali
@ 2026-07-07  7:06   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:06 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but the patch failed to fully resolve the ZERO_SIZE_PTR vulnerability it aimed to fix. A malicious or misconfigured PCI device returning a capability offset that causes ha->msix_count to become 0 still leads to a ZERO_SIZE_PTR allocation and subsequent out-of-bounds write.
--

--- Patch [56]: [PATCH v3 56/88] scsi: qla2xxx: Clamp MSI-X derived queue counts to avoid truncation ---
commit 96603c11bc04fcf9f524adb1b5140a4ef7b1d7f9
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Clamp MSI-X derived queue counts to avoid truncation

This commit adds qla_calc_queue_count() to clamp the derived MSI-X queue
count into a valid range. This prevents an allocation of zero elements
that leads to memory corruption when too many vectors are advertised.

> diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
> index 186c6c7a39448..5209dda454596 100644
> --- a/drivers/scsi/qla2xxx/qla_os.c
> +++ b/drivers/scsi/qla2xxx/qla_os.c
> @@ -2132,7 +2132,7 @@ qla2x00_iospace_config(struct qla_hw_data *ha)
>  		ha->msix_count = msix + 1;
>  		/* Max queues are bounded by available msix vectors */
>  		/* MB interrupt uses 1 vector */
> -		ha->max_req_queues = ha->msix_count - 1;
> +		ha->max_req_queues = qla_calc_queue_count(ha->msix_count);

[Severity: High]
This isn't a bug introduced by this patch, but it looks like the ZERO_SIZE_PTR
vulnerability this patch aims to fix might still be reachable through a
different path.

If a malicious or misconfigured PCI device returns 0xFFFF for
QLA_PCI_MSIX_CONTROL, could ha->msix_count wrap to 0?

drivers/scsi/qla2xxx/qla_os.c:qla2x00_iospace_config() {
    ...
    pci_read_config_word(ha->pdev, QLA_PCI_MSIX_CONTROL, &msix);
    ha->msix_count = msix + 1;
    ...
}

Later in qla24xx_enable_msix(), if pci_alloc_irq_vectors() successfully
allocates vectors (e.g., returning 2), the condition (ret < ha->msix_count)
evaluates as (2 < 0) due to type promotion, which is false:

drivers/scsi/qla2xxx/qla_isr.c:qla24xx_enable_msix() {
    ...
    } else if (ret < ha->msix_count) {
        ...
        ha->msix_count = ret;
    }
    ...
    ha->msix_entries = kzalloc_objs(struct qla_msix_entry, ha->msix_count);
    ...
}

This leaves ha->msix_count at 0, resulting in kzalloc_objs() returning
ZERO_SIZE_PTR.

When the base vectors are unconditionally initialized shortly after, does
this cause an out-of-bounds write by dereferencing ZERO_SIZE_PTR (0x10)?

drivers/scsi/qla2xxx/qla_isr.c:qla24xx_enable_msix() {
    ...
    /* Enable MSI-X vectors for the base queue */
    for (i = 0; i < QLA_BASE_VECTORS; i++) {
        qentry = &ha->msix_entries[i];
        qentry->handle = rsp;
    ...
}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=56

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 52/88] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject
  2026-07-07  5:53 ` [PATCH v3 52/88] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject Nilesh Javali
@ 2026-07-07  7:07   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:07 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Concurrent, unprotected modifications to `fcport->unsol_ctx_head` lead to list corruption.
- [Critical] Missing `list_del()` before freeing `uctx` on the error path in `qla_nvme_xmt_ls_rsp()` causes use-after-free and list corruption.
- [High] Concurrent calls to `qla_nvme_ls_reject_iocb()` corrupt the shared HBA-wide DMA buffer `vha->hw->lsrjt.c`.
- [High] Hardware request ring and MMIO doorbell are accessed unconditionally on error paths even when the firmware is not started.
--

--- Patch [52]: [PATCH v3 52/88] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject ---
commit f0adf8d49d698bd5a0280fe2d4d514336c997f62
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject

qla_nvme_ls_reject_iocb() allocates from and advances the request ring
through __qla2x00_alloc_iocbs() (which assumes the hardware_lock is held)
and qla2x00_start_iocbs() (which advances the ring and rings the
request-in doorbell), but takes no lock itself. Two of its callers invoke
it without the producer lock held.

> diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
> index 3b2f255a5d7d4..8dc6df6c2e1c5 100644
> --- a/drivers/scsi/qla2xxx/qla_nvme.c
> +++ b/drivers/scsi/qla2xxx/qla_nvme.c

[ ... ]

> @@ -440,7 +441,9 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport,
>  	a.vp_idx = vha->vp_idx;
>  	a.nport_handle = uctx->nport_handle;
>  	a.xchg_address = uctx->exchange_address;
> +	spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
>  	qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
> +	spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);

[Severity: High]
This is a pre-existing issue, but does this error path access the hardware
request ring and MMIO doorbell unconditionally even when the firmware is not
started?

Earlier in the function, there is a check:

    if (!ha->flags.fw_started)
        goto out;
        
If we jump to the out label because the firmware is offline or in reset,
we still call qla_nvme_ls_reject_iocb() here, which allocates IOCBs and
explicitly rings the MMIO doorbell. Can accessing ring structures and
ringing doorbells when the firmware is stopped result in hardware state
machine violations or a PCI Master Abort?

>  	kfree(uctx);

[Severity: Critical]
This isn't a bug introduced by this patch, but is a list_del() call missing
before freeing uctx on this error path?

If qla_nvme_xmt_ls_rsp() encounters an error and jumps to the out label,
it frees uctx via kfree(uctx). However, uctx was previously added to
fcport->unsol_ctx_head in qla2xxx_process_purls_iocb(). Won't skipping
list_del() leave a freed structure linked in the active list, leading to a
use-after-free the next time the list is traversed?

>  	return rval;
>  }

[ ... ]

> @@ -1243,6 +1246,10 @@ static void qla_nvme_lsrjt_pt_iocb(struct scsi_qla_host *vha,
>  	}
>  }
>  
> +/*
> + * Allocates from and advances the request ring, so the caller must hold
> + * qp->qp_lock_ptr (the response-queue caller already holds it).
> + */
>  static int
>  qla_nvme_ls_reject_iocb(struct scsi_qla_host *vha, struct qla_qpair *qp,
>  			struct qla_nvme_lsrjt_pt_arg *a, bool is_xchg_terminate)

[Severity: High]
This is a pre-existing issue, but are there missing concurrency protections
around the formatting of the HBA-wide DMA buffer vha->hw->lsrjt.c?

When qla_nvme_ls_reject_iocb() is called with is_xchg_terminate = false
from qla2xxx_process_purls_iocb(), it formats the reject payload directly
into this single shared buffer and submits an asynchronous command pointing
to its DMA address:

    qla_nvme_fc_format_rjt((void *)vha->hw->lsrjt.c, a->opcode,
                           a->reason, a->explanation, 0);
    a->tx_addr = vha->hw->lsrjt.cdma;

Since it can be called concurrently on different response queues (which
hold different qp_lock_ptr locks), could the payload be overwritten before
hardware DMA completes, causing the hardware to transmit mixed or corrupted
NVMe LS reject payloads?

[ ... ]

> @@ -1311,7 +1319,9 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item)
>  		a.vp_idx = vha->vp_idx;
>  		a.nport_handle = uctx->nport_handle;
>  		a.xchg_address = uctx->exchange_address;
> +		spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr, flags);
>  		qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true);
> +		spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr, flags);

[Severity: High]
This isn't a bug introduced by this patch, but does this function also issue
a reject unconditionally on the error path without checking if the firmware
is started?

If the firmware is stopped, could calling qla_nvme_ls_reject_iocb() here
result in similar hardware state machine violations?

>  		list_del(&uctx->elem);

[Severity: Critical]
This is a pre-existing issue, but does list_del() need a dedicated lock to
protect against concurrent list modifications?

The fcport->unsol_ctx_head list is modified in qla2xxx_process_purls_iocb()
using list_add_tail() while holding only the queue pair's hardware lock.
Meanwhile, list_del() is called here in a workqueue context entirely
unprotected.

Could multiple PURLS packets processed concurrently trigger concurrent
list_add_tail() and list_del() calls, leading to list corruption and kernel
panics?

>  		kfree(uctx);
>  	}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=52

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 59/88] scsi: qla2xxx: Fix use-after-free of qpair work on queue teardown
  2026-07-07  5:54 ` [PATCH v3 59/88] scsi: qla2xxx: Fix use-after-free of qpair work on queue teardown Nilesh Javali
@ 2026-07-07  7:08   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:08 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] Incomplete fix for Use-After-Free of `qpair->q_work` on queue creation error path.
- [High] Race condition causing kernel panic if an interrupt fires before `INIT_WORK()`.
--

--- Patch [59]: [PATCH v3 59/88] scsi: qla2xxx: Fix use-after-free of qpair work on queue teardown ---
commit 7b4564aa91c2297e4133057969d048526e18052f
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Fix use-after-free of qpair work on queue teardown

The response queue MSI-X handler qla2xxx_msix_rsp_q() schedules
qla_do_work() via queue_work(ha->wq, &qpair->q_work). qla_do_work()
dereferences the qpair (vha, rsp) and takes qpair->qp_lock.

During teardown, qla2xxx_delete_qpair() deletes the response queue, which
calls free_irq() in qla25xx_free_rsp_que(), and then frees the queue and
the qpair. free_irq() waits for running hardirq handlers but does not
cancel work already placed on ha->wq. A still-pending q_work then runs
qla_do_work() against the freed qpair and response queue, causing a
use-after-free.

> diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
> index b7d9c1a53f3ce..33bfc61d81652 100644
> --- a/drivers/scsi/qla2xxx/qla_mid.c
> +++ b/drivers/scsi/qla2xxx/qla_mid.c

[ ... ]

> @@ -606,6 +606,10 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
>  		rsp->msix->handle = NULL;
>  	}
>  
> +	/* Flush any queued response work before freeing the queue/qpair. */
> +	if (rsp->qpair && ha->wq)
> +		cancel_work_sync(&rsp->qpair->q_work);
> +

[Severity: High]
This isn't a bug introduced by this patch, but does this newly added guard
completely mitigate the use-after-free on the queue creation error path?

Looking at qla2xxx_create_qpair() in drivers/scsi/qla2xxx/qla_init.c,
rsp->qpair is only assigned after the request queue is successfully created:

drivers/scsi/qla2xxx/qla_init.c:qla2xxx_create_qpair() {
    ...
	ret = qla25xx_create_req_que(ha, options, vp_id, 0,
				     req_req_dma(qpair), req_req_size(qpair));
	if (!ret) {
		qpair->req = ha->req_q_map[vp_id];
        ...
		qpair->rsp->req = qpair->req;
		qpair->rsp->qpair = qpair;
    ...
}

If qla25xx_create_req_que() fails, the code jumps to fail_req and deletes
the response queue:

drivers/scsi/qla2xxx/qla_init.c:qla2xxx_create_qpair() {
    ...
fail_req:
	qla25xx_delete_rsp_que(vha, qpair->rsp);
    ...
}

When qla25xx_delete_rsp_que() calls qla25xx_free_rsp_que(), rsp->qpair will
be NULL. This bypasses the cancel_work_sync(), allowing any pending work
(scheduled by a spurious or early interrupt) to execute after qpair is freed
via kfree(qpair).

Is there a way to ensure the work is flushed even on this error path?


[Severity: High]
This is also a pre-existing issue, but is there a risk of enqueuing an
uninitialized work_struct if an interrupt fires too early during setup?

Looking at qla25xx_create_rsp_que() in drivers/scsi/qla2xxx/qla_mid.c,
the interrupt vector is requested before the work_struct is initialized:

drivers/scsi/qla2xxx/qla_mid.c:qla25xx_create_rsp_que() {
    ...
	ret = qla25xx_request_irq(ha, qpair, qpair->msix);
	if (ret)
		goto que_failed;
    ...
	if (qpair->hw->wq)
		INIT_WORK(&qpair->q_work, qla_do_work);
    ...
}

If a spurious interrupt fires immediately after qla25xx_request_irq(), it
triggers qla2xxx_msix_rsp_q() which calls queue_work() on qpair->q_work
before INIT_WORK() has been executed.

Additionally, if qla25xx_init_rsp_que() fails, the code jumps to que_failed
and INIT_WORK() is bypassed entirely, leaving the work completely
uninitialized when the ISR runs.

Could INIT_WORK() be moved before requesting the interrupt to prevent this?

>  	if (rsp->ring)
>  		dma_free_coherent(&ha->pdev->dev,
>  				  (rsp->length + 1) * rsp_entry_size,

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=59

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 46/88] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check
  2026-07-07  5:53 ` [PATCH v3 46/88] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check Nilesh Javali
@ 2026-07-07  7:09   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:09 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [Critical] The patch adds a NULL check for `cmd` to prevent a crash, assuming an aborted command leaves `cmd` as NULL. However, `cmd` can only be NULL due to an ABA race condition where `req->outstanding_cmds[handle]` has been reallocated to a new SRB. The patch fails to prevent type confusion if the handle is reused for non-SCSI commands (like `SRB_SA_UPDATE` or `SRB_LOGIN_CMD`) where `GET_CMD_SP(sp)` aliases with non-NULL union members.
--

--- Patch [46]: [PATCH v3 46/88] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check ---
commit edde2908b0e57558092f680fc5317a31b4bd99de
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check

This commit addresses a NULL pointer dereference in
qla_chk_edif_rx_sa_delete_pending() by adding an early return when the
command pointer is NULL. The pointer can be NULL if the firmware posts a
status completion for an already returned or aborted command.

> diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c
> index ade1d81785736..bfa520f936a24 100644
> --- a/drivers/scsi/qla2xxx/qla_edif.c
> +++ b/drivers/scsi/qla2xxx/qla_edif.c
> @@ -3540,6 +3540,9 @@ void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
>  	uint32_t handle;
>  	uint16_t sa_index;
>  
> +	if (!cmd)
> +		return;
> +

[Severity: Critical]
This is a pre-existing issue, but does this NULL check completely prevent
type confusion if the handle is reused for non-SCSI commands?

Due to an ABA race condition where req->outstanding_cmds[handle] can be
reallocated to a new SRB without a generation counter check, a late
firmware completion for a freed handle could map to a reused SRB:

drivers/scsi/qla2xxx/qla_isr.c:qla2x00_status_entry() {
    ...
	/* Validate handle. */
	if (handle < req->num_outstanding_cmds) {
		sp = req->outstanding_cmds[handle];
		if (!sp) {
    ...
}

If the handle is reused for a non-SCSI command like SRB_SA_UPDATE or
SRB_LOGIN_CMD, GET_CMD_SP(sp) might alias with non-NULL union members
such as the bsg_job pointer.

In that case, cmd would evaluate to a non-NULL value, bypassing this check
and potentially leading to an out-of-bounds read or invalid pointer
dereference when accessing cmd->sc_data_direction.

Could the underlying handle reuse race in qla2x00_status_entry() be
addressed to prevent late completions from mapping to the wrong command
type?

>  	handle = (uint32_t)LSW(sts24->handle);
>  
>  	/* find out if this status iosb is for a scsi read */

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=46

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 64/88] scsi: qla2xxx: Fix FCE trace enable parsing in debugfs
  2026-07-07  5:54 ` [PATCH v3 64/88] scsi: qla2xxx: Fix FCE trace enable parsing in debugfs Nilesh Javali
@ 2026-07-07  7:11   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:11 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] Lack of lock intersection causes a Use-After-Free when the firmware dump buffer (ha->fw_dump) is reallocated or freed concurrently with a firmware dump execution.
- [High] Lack of lock intersection causes a Use-After-Free on the FCE DMA trace buffer (ha->fce) when FCE is disabled concurrently with a firmware dump.
--

--- Patch [64]: [PATCH v3 64/88] scsi: qla2xxx: Fix FCE trace enable parsing in debugfs ---
commit 02e83541ddb08a379d31174a562473a3c77f5887
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Fix FCE trace enable parsing in debugfs

qla2x00_dfs_fce_write() called kstrtoul() with a NULL result pointer,
so a successful parse would dereference NULL and oops. Worse, the int
return value (0 on success, negative errno on failure) was assigned to
the unsigned long enable flag, inverting the intended logic: a valid
number was treated as "disable" while a parse failure enabled FCE.

Parse the value into enable and propagate parse errors to userspace.

Cc: stable@vger.kernel.org

> diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c
> index 177d47e92e494..5d08bdbcf70ae 100644
> --- a/drivers/scsi/qla2xxx/qla_dfs.c
> +++ b/drivers/scsi/qla2xxx/qla_dfs.c
> @@ -510,7 +510,9 @@ qla2x00_dfs_fce_write(struct file *file, const char __user *buffer,
>  		return PTR_ERR(buf);
>  	}
>  
> -	enable = kstrtoul(buf, 0, 0);
> +	rc = kstrtoul(buf, 0, &enable);
> +	if (rc)
> +		goto out_free;
>  	rc = count;
>  
>  	mutex_lock(&ha->fce_mutex);

[Severity: High]
This isn't a bug introduced by this patch, but could this cause a use-after-free
when the firmware dump buffer is reallocated?

When this function calls qla2x00_alloc_fw_dump(), it can free and reassign
ha->fw_dump under optrom_mutex:

drivers/scsi/qla2xxx/qla_init.c:qla2x00_alloc_fw_dump() {
    ...
                vfree(ha->fw_dump);
                ha->fw_dump = fw_dump;
    ...
}

Concurrently, if a hardware error triggers a firmware dump, qla25xx_fw_dump()
reads from ha->fw_dump holding only hardware_lock:

drivers/scsi/qla2xxx/qla_dbg.c:qla25xx_fw_dump() {
    ...
    if (!ha->fw_dump) {
        ql_log(ql_log_warn, vha, 0xd008,
    ...
}

Since hardware_lock is not held during the vfree() in qla2x00_alloc_fw_dump(),
could the concurrent dump write to freed memory?


[Severity: High]
This is a pre-existing issue, but does freeing the FCE trace buffer here
expose a use-after-free race with firmware dumps?

When FCE is disabled, qla2x00_free_fce_trace() is called, which frees ha->fce
without holding hardware_lock:

drivers/scsi/qla2xxx/qla_init.c:qla2x00_free_fce_trace() {
    ...
    dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce, ha->fce_dma);
    ha->fce = NULL;
    ...
}

Concurrently, if a firmware dump runs under hardware_lock, it copies from
ha->fce:

drivers/scsi/qla2xxx/qla_dbg.c:qla25xx_copy_fce() {
    ...
    if (!ha->fce)
        return ptr;
    ...
    memcpy(iter_reg, ha->fce, FCE_SIZE);
    ...
}

If qla2x00_free_fce_trace() frees the buffer just before the memcpy(), could
the dump read freed DMA memory?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=64

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 61/88] scsi: qla2xxx: Fix cs84xx use-after-free on host teardown
  2026-07-07  5:54 ` [PATCH v3 61/88] scsi: qla2xxx: Fix cs84xx use-after-free on host teardown Nilesh Javali
@ 2026-07-07  7:11   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:11 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [High] Use-After-Free of `ha->cs84xx` in ISR during device teardown due to dangling pointer.
- [High] Massive resource leaks and Use-After-Free of `ha` due to bypassed teardown logic when a PCI error occurs.
- [Medium] Reference count and memory leak of `ha->cs84xx` when `qla2x00_probe_one()` fails.
--

--- Patch [61]: [PATCH v3 61/88] scsi: qla2xxx: Fix cs84xx use-after-free on host teardown ---
commit 38a63280418c73c296cf766449f8dec71bf6e908
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Fix cs84xx use-after-free on host teardown
    
This commit moves qla84xx_put_chip() to after scsi_remove_host() in the
teardown paths to prevent a sysfs-triggered use-after-free on ha->cs84xx.
By doing this, it ensures that the host sysfs attributes are removed and
in-flight readers are drained before the cs84xx structure is freed.

> diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
> index 62c9bd0fe06df..4f485e4acf4a2 100644
> --- a/drivers/scsi/qla2xxx/qla_os.c
> +++ b/drivers/scsi/qla2xxx/qla_os.c
> @@ -4074,6 +4072,8 @@ qla2x00_remove_one(struct pci_dev *pdev)
>  
>  	scsi_remove_host(base_vha->host);
>  
> +	qla84xx_put_chip(base_vha);
> +

[Severity: High]
This is a pre-existing issue, but does freeing the cs84xx structure here
still leave a window for a use-after-free during an interrupt?

Looking at the teardown sequence, qla84xx_put_chip() drops the reference
and frees ha->cs84xx without setting the pointer to NULL.

Shortly after this, qla2x00_free_device() disables hardware interrupts
by calling ha->isp_ops->disable_intrs(ha).

Before those interrupts are disabled, could a concurrent MBA_ISP84XX_ALERT
event trigger the qla2x00_async_event() interrupt handler?

drivers/scsi/qla2xxx/qla_isr.c:qla2x00_async_event() {
    ...
    case MBA_ISP84XX_ALERT:
        ...
        spin_lock_irqsave(&ha->cs84xx->access_lock, flags);
    ...
}

Would this result in dereferencing the freed ha->cs84xx memory?

>  	qla2x00_free_device(base_vha);
>  
>  	qla2x00_clear_drv_active(ha);

[ ... ]

> @@ -7014,6 +7012,8 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work)
>  
>  	scsi_remove_host(base_vha->host);
>  
> +	qla84xx_put_chip(base_vha);
> +

[Severity: High]
This isn't a bug introduced by this patch, but does the PCI error handler
bypass teardown logic and leak resources?

In qla2x00_disable_board_on_pci_error(), the teardown skips destroying
the workqueue ha->wq and freeing DMA buffers like ha->exlogin_buf.

It then calls pci_disable_device(), which sets pdev->enable_cnt to 0.

When qla2x00_remove_one() later executes, it hits the early return path
because the count was cleared:

drivers/scsi/qla2xxx/qla_os.c:qla2x00_remove_one() {
    ...
    if (!atomic_read(&pdev->enable_cnt)) {
        dma_free_coherent(&ha->pdev->dev, base_vha->gnl.size,
            base_vha->gnl.l, base_vha->gnl.ldma);
        base_vha->gnl.l = NULL;
        scsi_host_put(base_vha->host);
        kfree(ha);
        pci_set_drvdata(pdev, NULL);
        return;
    }
    ...
}

This early return frees the ha structure while skipping the rest of the
teardown.

Since ha->wq is never destroyed, does this leak memory?

Also, if any background work items are still scheduled on ha->wq, will they
execute post-free and dereference the deallocated ha structure?

[Severity: Medium]
This is a pre-existing issue, but does the probe error path leak the
ha->cs84xx reference?

During initialization, qla2x00_initialize_adapter() acquires the chip:

drivers/scsi/qla2xxx/qla_init.c:qla2x00_initialize_adapter() {
    ...
    ha->cs84xx = qla84xx_get_chip(vha);
    ...
    if (qla_ini_mode_enabled(vha) || qla_dual_mode_enabled(vha))
        rval = qla2x00_init_rings(vha);

    if (rval != QLA_SUCCESS)
        return rval;
    ...
}

If qla2x00_init_rings() fails, the function returns without dropping
the acquired reference.

Additionally, if qla2x00_initialize_adapter() succeeds but a later step
in qla2x00_probe_one() fails, the driver jumps to the probe_failed label.

The probe_failed path does not call qla84xx_put_chip() to release the
reference either.

Will this result in the allocated chip remaining stuck in qla_cs84xx_list
permanently?

>  	base_vha->flags.init_done = 0;
>  	qla25xx_delete_queues(base_vha);
>  	qla2x00_free_fcports(base_vha);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=61

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 63/88] scsi: qla2xxx: Zero mailbox struct in qla2x00_get_firmware_state()
  2026-07-07  5:54 ` [PATCH v3 63/88] scsi: qla2xxx: Zero mailbox struct in qla2x00_get_firmware_state() Nilesh Javali
@ 2026-07-07  7:18   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:18 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Uninitialized kernel stack memory disclosure vulnerability remains unfixed in the sysfs handler `qla2x00_fw_state_show()` on the success path.
--

--- Patch [63]: [PATCH v3 63/88] scsi: qla2xxx: Zero mailbox struct in qla2x00_get_firmware_state() ---
commit 9d1cc31f70b4b1282c00f58b1e61ed39b581f84d
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Zero mailbox struct in qla2x00_get_firmware_state()

This commit zero-initializes the mailbox command structure in
qla2x00_get_firmware_state() to prevent leaking uninitialized kernel stack
memory on failure paths. It ensures that when early returns occur, userspace
receives deterministic zeroed state rather than stack contents.

> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index ba4a4764de1fb..ab5648eb5f206 100644
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c
> @@ -2276,6 +2276,8 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
>  	if (!ha->flags.fw_started)
>  		return QLA_FUNCTION_FAILED;
>  
> +	memset(&mc, 0, sizeof(mc));
> +
>  	mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
>  	mcp->out_mb = MBX_0;
>  	if (IS_FWI2_CAPABLE(vha->hw))

[Severity: High]
This is a pre-existing issue, but does this still leak uninitialized stack
memory on the success path for adapters that are not FWI2 capable?

Looking at the caller qla2x00_fw_state_show() in
drivers/scsi/qla2xxx/qla_attr.c, an uninitialized array is declared on the
stack and passed to qla2x00_get_firmware_state():

drivers/scsi/qla2xxx/qla_attr.c:qla2x00_fw_state_show() {
	uint16_t state[16];
        ...
	rval = qla2x00_get_firmware_state(vha, state);
        ...
}

When qla2x00_get_firmware_state() completes successfully, it only populates
states[1] through states[5] if the adapter is FWI2 capable:

drivers/scsi/qla2xxx/qla_mbx.c:qla2x00_get_firmware_state() {
	states[0] = mcp->mb[1];
	if (IS_FWI2_CAPABLE(vha->hw)) {
		states[1] = mcp->mb[2];
		states[2] = mcp->mb[3];  /* SFP info */
		states[3] = mcp->mb[4];
		states[4] = mcp->mb[5];
		states[5] = mcp->mb[6];  /* DPORT status */
        ...
}

However, qla2x00_fw_state_show() unconditionally formats all six elements into
the sysfs output buffer, which could expose uninitialized stack memory to
userspace for non-FWI2 adapters:

drivers/scsi/qla2xxx/qla_attr.c:qla2x00_fw_state_show() {
        ...
	return scnprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
	    state[0], state[1], state[2], state[3], state[4], state[5]);
}

Should the state array be zero-initialized in qla2x00_fw_state_show(), or
should qla2x00_get_firmware_state() guarantee all returned values are
initialized regardless of the adapter capability?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=63

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 57/88] scsi: qla2xxx: Improve firmware dump data capture
  2026-07-07  5:54 ` [PATCH v3 57/88] scsi: qla2xxx: Improve firmware dump data capture Nilesh Javali
@ 2026-07-07  7:18   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:18 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Dynamic reallocation of `ha->fw_dump` races with firmware dump capture in the interrupt handler, leading to a Use-After-Free.
- [High] Unprotected read-modify-write of the `flags` bitfield causes a data race that can drop concurrent flag updates (e.g., `lip_ae`).
- [Medium] The mailbox registers (`mbregs`) are dumped in native byte order, which breaks parsing on Big-Endian architectures.
--

--- Patch [57]: [PATCH v3 57/88] scsi: qla2xxx: Improve firmware dump data capture ---
commit 17ed3e31ad505ff9b7203eaec313bd7169cf990b
Author: Quinn Tran <qutran@marvell.com>

scsi: qla2xxx: Improve firmware dump data capture

Capture as much firmware dump data as possible. Save the mailbox
registers at start-of-day, before firmware execution, so they are
available in the dump, and allocate a guestimate dump buffer early
during driver load to capture failures that happen before the final
dump buffer is sized.

> diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
> index ae9bf68710793..9d6b229bd3525 100644
> --- a/drivers/scsi/qla2xxx/qla_init.c
> +++ b/drivers/scsi/qla2xxx/qla_init.c
[ ... ]
> @@ -3907,13 +3888,11 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
[ ... ]
>  		fw_dump = vmalloc(dump_size);
>  		if (!fw_dump) {
[ ... ]
>  		} else {
>  			mutex_lock(&ha->optrom_mutex);
> -			if (ha->fw_dumped) {
> -				memcpy(fw_dump, ha->fw_dump, ha->fw_dump_len);
> -				vfree(ha->fw_dump);
> +
> +			if (ha->fw_dumped || ha->mpi_fw_dumped) {
> +				prev_fw_dump = ha->fw_dump;
> +
> +				if (ha->fw_dumped)
> +					memcpy(fw_dump, prev_fw_dump,
> +					    ha->fw_dump_len);
> +
> +				if (IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
> +				    IS_QLA29XX(ha)) {
> +					prev_mpi_fw_dump = ha->mpi_fw_dump;
> +					ha->mpi_fw_dump = (char *)fw_dump +
> +						ha->fwdt[0].dump_size;
> +
> +					if (ha->mpi_fw_dumped)
> +						memcpy(ha->mpi_fw_dump,
> +						    prev_mpi_fw_dump,
> +						    ha->mpi_fw_dump_len);
> +				}
> +				vfree(prev_fw_dump);
>  				ha->fw_dump = fw_dump;

[Severity: High]
Could this lead to a Use-After-Free if the interrupt handler accesses the
firmware dump concurrently?

Since qla2x00_alloc_fw_dump() frees and reassigns ha->fw_dump while holding
optrom_mutex, but the interrupt handler (which calls qla27xx_fwdump())
writes to ha->fw_dump holding only hardware_lock, a concurrent firmware
crash might cause the ISR to write into the freed buffer.

[ ... ]
> @@ -4928,6 +4934,8 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
>  		ql_dbg(ql_dbg_init, vha, 0x00d3,
>  		    "Init Firmware -- success.\n");
>  		vha->u_ql2xexchoffld = vha->u_ql2xiniexchg = 0;
> +		vha->hw->flags.t262_fail = 0;
> +		vha->hw->flags.t272_fail = 0;
>  	}
>  
>  	return (rval);

[Severity: High]
Does modifying these bitfields without a lock cause a data race that could
drop concurrent flag updates?

These flags reside in the same 32-bit memory word as lip_ae and n2n_ae in
struct qla_hw_data. Because bitfield assignment compiles to a
read-modify-write operation, updating them here without holding
hardware_lock could overwrite and lose an asynchronous event update (like
lip_ae being set by the ISR).

> diff --git a/drivers/scsi/qla2xxx/qla_tmpl.c b/drivers/scsi/qla2xxx/qla_tmpl.c
> index b0a74b036cf4b..fd39841274974 100644
> --- a/drivers/scsi/qla2xxx/qla_tmpl.c
> +++ b/drivers/scsi/qla2xxx/qla_tmpl.c
[ ... ]
> @@ -536,13 +547,12 @@ qla27xx_fwdt_entry_t269(struct scsi_qla_host *vha,
>  {
>  	ql_dbg(ql_dbg_misc, vha, 0xd20d,
>  	    "%s: scratch [%lx]\n", __func__, *len);
> -	qla27xx_insert32(0xaaaaaaaa, buf, len);
> -	qla27xx_insert32(0xbbbbbbbb, buf, len);
> -	qla27xx_insert32(0xcccccccc, buf, len);
> -	qla27xx_insert32(0xdddddddd, buf, len);
> -	qla27xx_insert32(*len + sizeof(uint32_t), buf, len);
> +
> +	/* The data format is based on entry type t260. */
> +	qla27xx_insert32(offsetof(struct device_reg_24xx, mailbox0), buf, len);
> +	qla27xx_insertbuf(vha->hw->mbregs, sizeof(vha->hw->mbregs), buf, len);

[Severity: Medium]
Are the mailbox registers being dumped in native byte order instead of
Little-Endian?

Since mbregs was populated using rd_reg_word() (which converts to host byte
order), qla27xx_insertbuf() will copy these bytes directly into the dump,
which may corrupt the diagnostic data on Big-Endian architectures.

>  	if (buf)
> -		ent->t269.scratch_size = 5 * sizeof(uint32_t);
> +		ent->t269.scratch_size = sizeof(uint32_t) + sizeof(vha->hw->mbregs);
>  
>  	return qla27xx_next_entry(ent);
>  }

[Severity: Medium]
Could this assignment break parsing tools expecting a standard Little-Endian
format?

Directly assigning a native uint32_t constant to ent->t269.scratch_size (which
is an __le32 field) will store the size in native byte order. It likely needs
cpu_to_le32().

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=57

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 69/88] scsi: qla2xxx: Fix soft lockup polling continuation IOCB signature
  2026-07-07  5:54 ` [PATCH v3 69/88] scsi: qla2xxx: Fix soft lockup polling continuation IOCB signature Nilesh Javali
@ 2026-07-07  7:19   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:19 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Incomplete fix for payload aliasing lockup: The head IOCB also carries raw payload at offset 60, which aliases the `signature` field checked in the outer response queue loop.
- [High] Bypassing the `rsp_q_out` register update when deferring IOCB processing causes response queue entries to leak, potentially leading to a firmware deadlock.
- [High] Encountering an invalid continuation IOCB in `qla27xx_copy_fpin_pkt()` or `qla27xx_copy_multiple_pkt()` causes the driver to permanently deadlock the response queue instead of recovering or dropping the frame cleanly.
--

--- Patch [69]: [PATCH v3 69/88] scsi: qla2xxx: Fix soft lockup polling continuation IOCB signature ---
commit dac7bfdc42a973841c8497f7fb4cafd346a7adea
Author: Nilesh Javali <njavali@marvell.com>
Subject: scsi: qla2xxx: Fix soft lockup polling continuation IOCB signature

This commit removes the polling of `rsp_q->ring_ptr->signature` for 
`RESPONSE_PROCESSED` in `qla27xx_copy_multiple_pkt()` and 
`qla27xx_copy_fpin_pkt()` to prevent a CPU soft lockup when raw FC frame
payload aliases the signature field. It replaces the wait with a check 
for available continuation IOCBs before processing.

> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> index c36a2c69c219c..497a0fef742fa 100644
> --- a/drivers/scsi/qla2xxx/qla_isr.c
> +++ b/drivers/scsi/qla2xxx/qla_isr.c

[ ... ]

> @@ -1299,14 +1291,6 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
>  
>  	do {
>  		while ((total_bytes > 0) && (entry_count_remaining > 0)) {
> -			if (rsp_q->ring_ptr->signature == RESPONSE_PROCESSED) {
> -				ql_dbg(ql_dbg_async, vha, 0x5084,
> -				       "Ran out of IOCBs, partial data 0x%x\n",
> -				       buffer_copy_offset);
> -				cpu_relax();
> -				continue;
> -			}
> -
>  			*pkt = rsp_q->ring_ptr;
>  			data = ((sts_cont_entry_t *)*pkt)->data;
>  			data_sz = qla_sts_cont_data_size(ha);

[Severity: High]
This isn't a bug introduced by this patch, but does encountering an invalid
continuation IOCB cause the response queue to permanently deadlock?

If the firmware provides an unexpected IOCB type during this loop:

qla27xx_copy_fpin_pkt() {
    ...
    if (((sts_cont_entry_t *)*pkt)->entry_type != STATUS_CONT_TYPE) {
        ql_log(ql_log_warn, vha, 0x507a, "Unexpected IOCB type...\n", ...);
        break;
    }
    ...
}

The loop breaks early and returns NULL without advancing the ring pointer via
qla_rsp_ring_advance(). The caller, qla24xx_process_response_queue(), then
unconditionally executes:

    ((response_t *)pkt)->signature = RESPONSE_PROCESSED;

This overwrites the stranded IOCB. On the next interrupt, the outer loop sees
rsp->ring_ptr->signature == RESPONSE_PROCESSED on this stranded IOCB and
immediately terminates, never advancing the hardware consumer index.

[ ... ]

> @@ -4271,9 +4255,24 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
>  					       "SCM not active for this port\n");
>  					break;
>  				}
> +				if (qla_chk_cont_iocb_avail(vha, rsp,
> +				    (response_t *)pkt, rsp_in)) {
> +					/*
> +					 * ring_ptr and ring_index were
> +					 * pre-incremented above. Reset them
> +					 * back to current. Wait for next
> +					 * interrupt with all IOCBs to arrive
> +					 * and re-process.
> +					 */
> +					qla_rsp_ring_rewind_to(rsp,
> +					    (response_t *)pkt, cur_ring_index);
> +
> +					ql_dbg(ql_dbg_init, vha, 0x5095,
> +					    "Defer processing FPIN...\n");
> +					return;

[Severity: High]
This is a pre-existing issue, but does this early return skip the hardware
doorbell update?

When deferring IOCB processing, returning directly skips the mandatory
doorbell update at the end of qla24xx_process_response_queue():

    wrt_reg_dword(rsp->rsp_q_out, rsp->ring_index);

If other IOCBs were successfully processed in previous iterations of the
while loop during the same interrupt, the firmware will not be notified
that those slots have been freed. Could this cause the response queue to
fill up and stall?

[Severity: Critical]
This is also a pre-existing issue, but does the same payload aliasing lockup
fixed by this patch still exist for the head IOCB?

The outer loop in qla24xx_process_response_queue() polls the signature field:

    while (rsp->ring_index != rsp_in &&
           rsp->ring_ptr->signature != RESPONSE_PROCESSED) {

The head IOCB structures (like purex_entry_24xx) carry raw payloads spanning
offset 60, just like the continuation IOCBs. If an incoming ELS or
pass-through frame has 0xDEADDEAD at offset 16-19 of its payload, the driver
sees signature == RESPONSE_PROCESSED and prematurely terminates the loop.

Since the consumer pointer is not advanced past this IOCB, could every
subsequent interrupt immediately break the loop and permanently deadlock the
response queue?

> +				}
>  				pure_item = qla27xx_copy_fpin_pkt(vha,
>  							  (void **)&pkt, &rsp);
> -				__update_rsp_in(is_shadow_hba, rsp, rsp_in);
>  				if (!pure_item)
>  					break;
>  				qla24xx_queue_purex_item(vha, pure_item,

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=69

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 71/88] scsi: qla2xxx: Avoid req_q_map double-read in qla2x00_error_entry()
  2026-07-07  5:54 ` [PATCH v3 71/88] scsi: qla2xxx: Avoid req_q_map double-read in qla2x00_error_entry() Nilesh Javali
@ 2026-07-07  7:21   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:21 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Missing bounds and NULL pointer checks on hardware-supplied queue index in QLAFX00 handlers lead to OOB reads and wild pointer dereferences.
- [High] The patch transforms a NULL pointer dereference into a Use-After-Free and fails to prevent compiler reloads due to a missing READ_ONCE().
- [High] Out-of-bounds array access on `req_q_map` before validation in `qla2x00_status_entry`.
- [High] Lack of bounds checking for target QID index on `req_q_map` array access in `qlt_ctio_to_cmd`.
--

--- Patch [71]: [PATCH v3 71/88] scsi: qla2xxx: Avoid req_q_map double-read in qla2x00_error_entry() ---
commit c14818342918ef21459d32bbecb1126af60c505c
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Avoid req_q_map double-read in qla2x00_error_entry()

This patch attempts to fix a potential double-read of the request queue map
in the error entry ISR. It caches the map slot into a local variable to
perform the NULL check and subsequent use. The issue was observed during
queue teardown when the map slot is cleared concurrently.

> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> index 91a8344fea6c7..829671937f92e 100644
> --- a/drivers/scsi/qla2xxx/qla_isr.c
> +++ b/drivers/scsi/qla2xxx/qla_isr.c
> @@ -3927,10 +3927,12 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
[ ... ]
>  
> -	if (que >= ha->max_req_queues || !ha->req_q_map[que])
> +	if (que >= ha->max_req_queues)
>  		goto fatal;
>  
>  	req = ha->req_q_map[que];
> +	if (!req)
> +		goto fatal;

[Severity: High]
This isn't a bug introduced by this patch, but caching the pointer without
READ_ONCE() might allow the compiler to reload the pointer from memory,
potentially re-introducing the NULL dereference.

Additionally, does this code allow a use-after-free?

As mentioned in the commit message, qla25xx_free_req_que() runs concurrently
and clears the slot before freeing the queue:

> drivers/scsi/qla2xxx/qla_mid.c:qla25xx_free_req_que() {
> ...
>         mutex_lock(&ha->mq_lock);
>         ha->req_q_map[que_id] = NULL;
>         clear_bit(que_id, ha->req_qid_map);
>         mutex_unlock(&ha->mq_lock);
>     }
>     kfree(req->outstanding_cmds);
>     kfree(req);
> }

Since this ISR does not synchronize with the teardown, could the ISR fetch
the pointer just before it is cleared, pass the NULL check, and then access
req after it has been freed by the other CPU?


[Severity: Critical]
This is a pre-existing issue, but does qlafx00_status_entry() lack bounds
and NULL pointer checks on the hardware-supplied queue index?

> drivers/scsi/qla2xxx/qla_mr.c:qlafx00_status_entry() {
>     ...
>     que = MSW(hindex);
>     req = ha->req_q_map[que];
> 
>     /* Validate handle. */
>     if (handle < req->num_outstanding_cmds)
>     ...
> }

Could a malformed hardware response trigger an out-of-bounds read and
subsequent wild pointer dereference if que exceeds max_req_queues or req
is NULL?

This same pattern appears to be present in qlafx00_multistatus_entry()
and qlafx00_error_entry().


[Severity: High]
This is a pre-existing issue, but does qla2x00_status_entry() access the
array before validating the index bounds?

> drivers/scsi/qla2xxx/qla_isr.c:qla2x00_status_entry() {
>     ...
>     que = MSW(sts->handle);
>     req = ha->req_q_map[que];
> 
>     /* Check for invalid queue pointer */
>     if (req == NULL ||
>         que >= find_first_zero_bit(ha->req_qid_map, ha->max_req_queues)) {
>     ...
> }

Can a corrupted hardware input cause an out-of-bounds memory read during
the assignment to req, before the bounds check evaluates?


[Severity: High]
This is a pre-existing issue, but does qlt_ctio_to_cmd() access the
req_q_map array without bounds checking?

> drivers/scsi/qla2xxx/qla_target.c:qlt_ctio_to_cmd() {
>     ...
>     int qid = GET_QID(handle);
>     ...
>     } else if (vha->hw->req_q_map[qid]) {
>         ql_dbg(ql_dbg_tgt_mgt, vha, 0x1000a,
>             "qla_target(%d): CTIO completion with different QID %d handle %x\n",
>             vha->vp_idx, rsp->id, handle);
>         req = vha->hw->req_q_map[qid];
>     ...
> }

Could an untrusted hardware handle with a qid exceeding max_req_queues
cause an out-of-bounds read?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=71

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 72/88] scsi: qla2xxx: Quiesce response IRQ before freeing request queue
  2026-07-07  5:54 ` [PATCH v3 72/88] scsi: qla2xxx: Quiesce response IRQ before freeing request queue Nilesh Javali
@ 2026-07-07  7:22   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:22 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

New issues:
- [High] Lockless early clearing of `rsp->msix->in_use` creates a race condition that leads to an IRQ double-free and cross-qpair interrupt hijacking.

Pre-existing issues:
- [High] Use-after-free of `rsp->req` in the `qla2xxx_create_qpair` error path.
- [High] Use-after-free on `qp->buf_pool.buf_map` during queue teardown due to early `qla_free_buf_pool`.
--

--- Patch [72]: [PATCH v3 72/88] scsi: qla2xxx: Quiesce response IRQ before freeing request queue ---
commit ae81f8a97822c69143d672e39645c29cff10e16e
Author: Nilesh Javali <njavali@marvell.com>
Subject: scsi: qla2xxx: Quiesce response IRQ before freeing request queue

This patch quiesces the response IRQ and flushes pending work before freeing
the request queue in qla2xxx_delete_qpair(). This prevents a regression where
late completions could dereference a freed request queue.

Fixes: d74595278f4a ("scsi: qla2xxx: Add multiple queue pair functionality.")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-dev@google.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>

> diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
> index 76b7ed501b04e..d678e27213a94 100644
> --- a/drivers/scsi/qla2xxx/qla_init.c
> +++ b/drivers/scsi/qla2xxx/qla_init.c
> @@ -10686,11 +10686,28 @@ int qla2xxx_delete_qpair(struct scsi_qla_host *vha, struct qla_qpair *qpair)
>  {
>  	int ret = QLA_FUNCTION_FAILED;
>  	struct qla_hw_data *ha = qpair->hw;
> +	struct rsp_que *rsp = qpair->rsp;
>  
>  	qpair->delete_in_progress = 1;
>  
>  	qla_free_buf_pool(qpair);

[Severity: High]
This is a pre-existing issue, but by calling qla_free_buf_pool() here, the
buf_map and other arrays are freed before the interrupt is disabled and
pending work is flushed. If a late hardware completion arrives during this
window, could qla_do_work() process it and invoke qla_put_buf()?

If that happens, does this code execute clear_bit(dsc->tag,
qp->buf_pool.buf_map) on the freed memory?

> +	/*
> +	 * The response-queue interrupt schedules qla_do_work(), which
[ ... ]
> +	 * firmware queue-delete order (request then response) is kept.
> +	 */
> +	if (rsp && rsp->msix && rsp->msix->have_irq) {
> +		free_irq(rsp->msix->vector, rsp->msix->handle);
> +		rsp->msix->have_irq = 0;
> +		rsp->msix->in_use = 0;

[Severity: High]
Does clearing rsp->msix->in_use here without holding ha->mq_lock prematurely
expose the msix_entry to reallocation?

If a concurrent qla2xxx_create_qpair() allocates this entry for a new queue
pair and requests the IRQ, it will set msix->have_irq = 1.

When this teardown path subsequently reaches qla25xx_free_rsp_que(), it
evaluates the shared struct and could incorrectly execute free_irq() on the
new queue pair's active IRQ handler. Should we wait to clear in_use until the
original teardown path is fully complete?

> +		rsp->msix->handle = NULL;
> +	}
> +	if (rsp && ha->wq)
> +		cancel_work_sync(&qpair->q_work);
> +
>  	ret = qla25xx_delete_req_que(vha, qpair->req);
>  	if (ret != QLA_SUCCESS)
>  		goto fail;

[Severity: High]
This isn't a bug introduced by this patch, but while reviewing the
fix for the teardown path, I noticed a similar issue in the error path of
qla2xxx_create_qpair():

drivers/scsi/qla2xxx/qla_init.c:qla2xxx_create_qpair() {
[ ... ]
fail_mempool:
	qla25xx_delete_req_que(vha, qpair->req);
fail_req:
	qla25xx_delete_rsp_que(vha, qpair->rsp);
[ ... ]
}

If a failure occurs after the queues are created, the request queue is freed
while the response queue's interrupt is still active. Can an interrupt fire
here and schedule qla_do_work(), leading to a dereference of the freed
rsp->req in qla24xx_process_response_queue()?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=72

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 70/88] scsi: qla2xxx: Bound rsp_info_len to avoid OOB sense-data read
  2026-07-07  5:54 ` [PATCH v3 70/88] scsi: qla2xxx: Bound rsp_info_len to avoid OOB sense-data read Nilesh Javali
@ 2026-07-07  7:24   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:24 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] The target-provided residual length (resid_len) is not validated against the command buffer size, allowing a malicious or buggy target to bypass the mid-layer underflow check and cause a massive integer underflow in the SCSI mid-layer's good_bytes calculation.
- [High] A malicious or buggy target can permanently wedge the response queue by causing track_sense_len to be non-zero without providing a subsequent Status Continuation IOCB. The patch exacerbates this by clamping rsp_info_len to par_sense_len, which zeroes par_sense_len and guarantees the driver waits for a continuation IOCB that the hardware will never send.
--

--- Patch [70]: [PATCH v3 70/88] scsi: qla2xxx: Bound rsp_info_len to avoid OOB sense-data read ---
commit b3865601a37114806cad8f5a0140e56664a684aa
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Bound rsp_info_len to avoid OOB sense-data read

This commit addresses an out-of-bounds sense-data read in
qla2x00_status_entry() when the target reports a larger rsp_info_len than
par_sense_len. It clamps rsp_info_len to par_sense_len before subtraction
to prevent underflow and memory disclosure.

> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> index 497a0fef742fa..91a8344fea6c7 100644
> --- a/drivers/scsi/qla2xxx/qla_isr.c
> +++ b/drivers/scsi/qla2xxx/qla_isr.c
> @@ -3598,6 +3598,18 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
>  	if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) {
>  		/* Sense data lies beyond any FCP RESPONSE data. */
>  		if (IS_FWI2_CAPABLE(ha)) {
> +			/*
> +			 * A hostile or buggy target may report an
> +			 * rsp_info_len larger than the IOCB data area.
> +			 * Clamp it so the par_sense_len subtraction cannot
> +			 * underflow and walk sense_data out of bounds.
> +			 */
> +			if (rsp_info_len > par_sense_len) {
> +				ql_log(ql_log_warn, fcport->vha, 0x3107,
> +				       "Truncating bogus rsp_info_len 0x%x to 0x%x.\n",
> +				       rsp_info_len, par_sense_len);
> +				rsp_info_len = par_sense_len;
> +			}
>  			sense_data += rsp_info_len;
>  			par_sense_len -= rsp_info_len;

[Severity: High]
This is a pre-existing issue, but could this change permanently stall the
response queue?

If a hostile or buggy target provides an rsp_info_len larger than
par_sense_len, the clamping here sets rsp_info_len to par_sense_len, which
results in par_sense_len becoming 0.

If the target also reported sense_len > 0, qla2x00_handle_sense() will clamp
sense_len to 0, but track_sense_len remains > 0.

This causes qla2x00_handle_sense() to expect a Continuation IOCB:

    if (track_sense_len != 0) {
        rsp->status_srb = sp;
        cp->result = res;
    }

Since no continuation IOCB is actually coming, rsp->status_srb is never
cleared. Could this skip the done() callback for all subsequent commands and
permanently stall the queue?

[ ... ]

>  		if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER)) {
>  			resid = resid_len;
>  			scsi_set_resid(cp, resid);
> 
>  			if (!lscsi_status &&
>  			    ((unsigned)(scsi_bufflen(cp) - resid) <
>  			     cp->underflow)) {

[Severity: High]
This isn't a bug introduced by this patch, but does this calculation underflow
if the target provides a resid_len that is greater than scsi_bufflen(cp)?

If resid is larger than the buffer size, the unsigned subtraction wraps around
to a huge value, which would bypass the underflow check and skip assigning
DID_ERROR to the command result.

Could this lead to the block layer incorrectly assuming the data transfer was
fully successful?

This same pattern appears to exist in qlafx00_status_entry() as well.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=70

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 65/88] scsi: qla2xxx: Fix FCE trace use-after-free during firmware dump
  2026-07-07  5:54 ` [PATCH v3 65/88] scsi: qla2xxx: Fix FCE trace use-after-free during firmware dump Nilesh Javali
@ 2026-07-07  7:25   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:25 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Race condition between FCE enable and firmware dump causes Use-After-Free and Buffer Overflow.
- [High] Missing lock / Use-after-free in `qla_enable_fce_trace` and `qla82xx_restart_isp`.
- [High] TOCTOU race leading to NULL pointer dereference in `qla2x00_dfs_fce_release`.
- [Medium] Asymmetrical locking in `qla2x00_alloc_fce_trace` leading to data race and uninitialized memory access during firmware dumps.
--

--- Patch [65]: [PATCH v3 65/88] scsi: qla2xxx: Fix FCE trace use-after-free during firmware dump ---
commit d9c15811116bd4288fadefd8eb8bb3d8713c9a4a
Author: Nilesh Javali <njavali@marvell.com>
Subject: scsi: qla2xxx: Fix FCE trace use-after-free during firmware dump

The commit fixes a use-after-free during a firmware dump by unpublishing
ha->fce under hardware_lock before freeing the dma buffer. A concurrent
dump will now either safely complete its copy or observe a null pointer.

> diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
> index 9d6b229bd3525..76b7ed501b04e 100644
> --- a/drivers/scsi/qla2xxx/qla_init.c
> +++ b/drivers/scsi/qla2xxx/qla_init.c
> @@ -3752,11 +3752,27 @@ int qla2x00_alloc_fce_trace(scsi_qla_host_t *vha)
>  
>  void qla2x00_free_fce_trace(struct qla_hw_data *ha)
>  {

[Severity: Medium]
This isn't a bug introduced by this patch, but since we are fixing fce
synchronization during firmware dumps, does the allocation side in
qla2x00_alloc_fce_trace() also need hardware_lock protection?

qla2x00_alloc_fce_trace() assigns ha->fce and ha->fce_dma without holding
ha->hardware_lock:

	ha->fce_dma = tc_dma;
	ha->fce = tc;
	ha->fce_bufs = FCE_NUM_BUFFERS;

A concurrent firmware dump triggered via qla27xx_fwdump() under
hardware_lock could observe a non-null ha->fce but read uninitialized
or stale values for the dma address and other fields.

> -	if (!ha->fce)
> +	void *fce;
> +	dma_addr_t fce_dma;
> +	unsigned long flags;
> +
> +	/*
> +	 * Unpublish ha->fce under hardware_lock so a firmware dump in
> +	 * progress (which reads ha->fce under the same lock) cannot race
> +	 * with the buffer being freed.
> +	 */
> +	spin_lock_irqsave(&ha->hardware_lock, flags);
> +	if (!ha->fce) {
> +		spin_unlock_irqrestore(&ha->hardware_lock, flags);
>  		return;
> -	dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce, ha->fce_dma);
> +	}

[Severity: High]
This is a pre-existing issue, but can qla_enable_fce_trace() and
qla82xx_restart_isp() race with this hardware_lock protected clearing
of ha->fce?

In qla_enable_fce_trace():

	if (ha->fce) {
		ha->flags.fce_enabled = 1;
		memset(ha->fce, 0, fce_calc_size(ha->fce_bufs));

Neither function holds ha->hardware_lock or ha->fce_mutex.

If a debugfs fce trace disable runs concurrently, this function clears
ha->fce and frees the dma buffer. The abort path could read a non-null
ha->fce, get preempted, and then execute memset() on the freed memory.

[Severity: High]
This is another pre-existing issue, but is there a time-of-check to
time-of-use race in qla2x00_dfs_fce_release() that could cause a null
pointer dereference?

drivers/scsi/qla2xxx/qla_dfs.c:qla2x00_dfs_fce_release():

	if (ha->flags.fce_enabled || !ha->fce)
		goto out;

	mutex_lock(&ha->fce_mutex);

	/* Re-enable FCE tracing. */
	ha->flags.fce_enabled = 1;
	memset(ha->fce, 0, fce_calc_size(ha->fce_bufs));

If !ha->fce is evaluated without holding ha->fce_mutex, a concurrent
write to the fce debugfs node could disable tracing, acquire fce_mutex,
and call qla2x00_free_fce_trace() which sets ha->fce to null.

Once the write releases the mutex, qla2x00_dfs_fce_release() acquires
it and calls memset(ha->fce), causing a crash.

[Severity: Critical]
This is also a pre-existing issue, but is there a race between fce
enable and firmware dumps that can cause a buffer overflow and
use-after-free?

In qla2x00_dfs_fce_write():

	if (!ha->fce) {
		rc = qla2x00_alloc_fce_trace(vha);
		...
		if (!ha->flags.fce_dump_buf_alloced)
			qla2x00_alloc_fw_dump(vha);
	}

This populates ha->fce before resizing the fw_dump buffer.
qla2x00_alloc_fw_dump() calls vfree(ha->fw_dump) holding optrom_mutex
but not hardware_lock.

If a firmware dump begins under hardware_lock just after ha->fce is
populated but before the new buffer is allocated, it will see
ha->fce is not null and insert bytes into the old fw_dump buffer:

drivers/scsi/qla2xxx/qla_tmpl.c:qla27xx_fwdt_entry_t264():

	if (vha->hw->fce) {
		...
		qla27xx_insertbuf(vha->hw->fce, FCE_SIZE, buf, len);
	}

This overflows the buffer since it wasn't sized for fce data.

Additionally, while the dump is writing to buf (which is ha->fw_dump),
alloc_fw_dump() could concurrently call vfree() on that exact buffer.

> +	fce = ha->fce;
> +	fce_dma = ha->fce_dma;
>  	ha->fce = NULL;
>  	ha->fce_dma = 0;
> +	spin_unlock_irqrestore(&ha->hardware_lock, flags);
> +
> +	dma_free_coherent(&ha->pdev->dev, FCE_SIZE, fce, fce_dma);
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=65

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 67/88] scsi: qla2xxx: Null out freed pointers in qla2x00_mem_alloc() error path
  2026-07-07  5:54 ` [PATCH v3 67/88] scsi: qla2xxx: Null out freed pointers in qla2x00_mem_alloc() error path Nilesh Javali
@ 2026-07-07  7:25   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:25 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Double free of ha->sns_cmd in error path of qla2x00_mem_alloc.
--

--- Patch [67]: [PATCH v3 67/88] scsi: qla2xxx: Null out freed pointers in qla2x00_mem_alloc() error path ---
commit 67cec0babb19e6c4e62f136f94d2843f83907bad
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Null out freed pointers in qla2x00_mem_alloc() error path

This commit updates the error handling path in qla2x00_mem_alloc() to null
out several pointers after freeing them. This prevents a subsequent call
to qla2x00_mem_free() from double-freeing these resources when the hardware
probe fails.

> diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
> --- a/drivers/scsi/qla2xxx/qla_os.c
> +++ b/drivers/scsi/qla2xxx/qla_os.c
> @@ -4619,28 +4619,43 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
[ ... ]
>  fail_ex_init_cb:
>  	kfree(ha->npiv_info);
> +	ha->npiv_info = NULL;
>  fail_npiv_info:
>  	dma_free_coherent(&ha->pdev->dev,
>  		((*rsp)->length + 1) * rsp_entry_size,

[Severity: High]
This is a pre-existing issue, but since this patch is cleaning up dangling
pointers in the qla2x00_mem_alloc() error path, should ha->sns_cmd also
be cleared?

Further down in the error cleanup for qla2x00_mem_alloc(), ha->sns_cmd is 
freed without setting the pointer to NULL:

fail_free_ms_iocb:
        dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
        ha->ms_iocb = NULL;
        ha->ms_iocb_dma = 0;

        if (ha->sns_cmd)
                dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt),
                    ha->sns_cmd, ha->sns_cmd_dma);
fail_dma_pool:

If an earlier allocation fails on legacy hardware, the cleanup code frees 
ha->sns_cmd and leaves it dangling. When the error propagates up, the probe 
error handler probe_hw_failed calls qla2x00_mem_free(). Since ha->sns_cmd 
is still non-NULL, qla2x00_mem_free() will free it a second time.

Could this lead to memory corruption during probe failure on older systems?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=67

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 66/88] scsi: qla2xxx: Use memset_io() to clear QLAFX00 request ring slot
  2026-07-07  5:54 ` [PATCH v3 66/88] scsi: qla2xxx: Use memset_io() to clear QLAFX00 request ring slot Nilesh Javali
@ 2026-07-07  7:26   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:26 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [Medium] The new wrt_reg_byte() and wrt_reg_dword() calls in __qla2x00_alloc_iocbs() introduce an endianness accessor violation and are dead code, as they are completely overwritten by QLAFX00 IOCB builder functions.

Pre-existing issues:
- [High] The patch replaces memset() with memset_io() for QLAFX00 request rings to prevent ARM64 synchronous external aborts, but fails to replace identical memcpy() calls in QLAFX00 IOCB builder functions that write to the same I/O memory ring.
--

--- Patch [66]: [PATCH v3 66/88] scsi: qla2xxx: Use memset_io() to clear QLAFX00 request ring slot ---
commit 9fcba115efaf36871216e19534a66706185340b3
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Use memset_io() to clear QLAFX00 request ring slot

For QLAFX00 the request ring is ioremapped device I/O memory
(ha->iobase + req_que_off), not DMA-coherent RAM, which is why the rest
of the FX00 path accesses it through memcpy_toio() and the wrt_reg_*
helpers. __qla2x00_alloc_iocbs() however zeroed the producer slot with a
plain memset(). On architectures such as ARM64 a regular memset() may
emit unaligned or block-zeroing instructions (e.g. DC ZVA) that are
invalid on Device memory, leading to a synchronous external abort.

Use memset_io() to clear the slot for QLAFX00, matching the I/O
accessors used elsewhere on this ring. Other adapters keep the plain
memset() on their DMA-coherent rings. The zero-fill is retained for FX00
because its IOCB builders (e.g. qlafx00_fxdisc_iocb()) copy only part of
the entry and rely on the unused tail being pre-zeroed.

> The zero-fill is retained for FX00 because its IOCB builders (e.g.
> qlafx00_fxdisc_iocb()) copy only part of the entry and rely on the
> unused tail being pre-zeroed.

[Severity: Medium]
Is this description accurate?

Looking at qlafx00_fxdisc_iocb(), it appears to copy the entire 64-byte
struct using memcpy_toio(), rather than only part of the entry:

drivers/scsi/qla2xxx/qla_mr.c:qlafx00_fxdisc_iocb() {
    ...
    memcpy_toio((void __iomem *)pfxiocb, &fx_iocb, sizeof(fx_iocb));
    ...
}

Will this overwrite the handle and entry_count fields written by the new
wrt_reg_* calls in __qla2x00_alloc_iocbs()?

> diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
> index 22f2d81e20099..c81df5a96014a 100644
> --- a/drivers/scsi/qla2xxx/qla_iocb.c
> +++ b/drivers/scsi/qla2xxx/qla_iocb.c
> @@ -2454,11 +2454,13 @@ __qla2x00_alloc_iocbs(struct qla_qpair *qpair, srb_t *sp)
>  	 */
>  	req->cnt -= req_cnt;
>  	pkt = qla_req_ring_slot(ha, req);
> -	memset(pkt, 0, qla_req_entry_size(ha));
>  	if (IS_QLAFX00(ha)) {
> +		memset_io((void __iomem __force *)pkt, 0,
> +			  qla_req_entry_size(ha));
> +		wrt_reg_byte((u8 __force __iomem *)&pkt->entry_count, req_cnt);
> +		wrt_reg_dword((__le32 __force __iomem *)&pkt->handle, handle);

[Severity: Medium]
Do these wrt_reg_* calls introduce an endianness violation by mixing 
byteswapping register accessors with stream copies? 

wrt_reg_dword() performs CPU-to-little-endian byte swapping, but 
subsequent builders like qlafx00_fxdisc_iocb() write to this same I/O
memory ring using memcpy_toio(), which preserves host endianness.

[Severity: High]
This isn't a bug introduced by this patch, but does the same ARM64
synchronous external abort issue exist in other QLAFX00 builders?

While this patch addresses memset() in __qla2x00_alloc_iocbs(), functions
like qlafx00_tm_iocb() and qlafx00_abort_iocb() in 
drivers/scsi/qla2xxx/qla_mr.c still appear to use plain memcpy() to copy
structs directly to the I/O memory ring slot instead of memcpy_toio():

drivers/scsi/qla2xxx/qla_mr.c:qlafx00_tm_iocb() {
    ...
    memcpy(ptm_iocb, &tm_iocb,
           sizeof(struct tsk_mgmt_entry_fx00));
    ...
}

drivers/scsi/qla2xxx/qla_mr.c:qlafx00_abort_iocb() {
    ...
    memcpy(pabt_iocb, &abt_iocb,
           sizeof(struct abort_iocb_entry_fx00));
    ...
}

Can these plain memcpy() calls also emit unaligned or block-copying
instructions that crash the system on Device memory?

>  	} else {
> +		memset(pkt, 0, qla_req_entry_size(ha));
>  		pkt->entry_count = req_cnt;
>  		pkt->handle = handle;
>  	}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=66

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 76/88] scsi: qla2xxx: Skip vport under deletion in report ID acquisition
  2026-07-07  5:54 ` [PATCH v3 76/88] scsi: qla2xxx: Skip vport under deletion in report ID acquisition Nilesh Javali
@ 2026-07-07  7:27   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:27 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

Pre-existing issues:
- [Critical] The patch attempts to fix a Use-After-Free (UAF) by checking `VPORT_DELETE` before incrementing `vref_count`, but introduces a Time-of-Check to Time-of-Use (TOCTOU) race condition that leaves the bug open.
- [Critical] Inconsistent locking across multiple threads protecting the `host_map` btree causes severe structural corruption.
- [High] Dropping a list lock inside `list_for_each_entry_safe` exposes the cached next element to concurrent destruction, causing a Use-After-Free.
- [High] The Deferred Processing Context (DPC) thread processes a dying virtual port, resulting in firmware state corruption and Use-After-Free.
--

--- Patch [76]: [PATCH v3 76/88] scsi: qla2xxx: Skip vport under deletion in report ID acquisition ---
commit 6dadc96ded15841eb3868816e697201cbe752d9d
Author: Nilesh Javali <njavali@marvell.com>
Subject: scsi: qla2xxx: Skip vport under deletion in report ID acquisition

This commit addresses an issue in qla24xx_report_id_acquisition() by
skipping vports that have VPORT_DELETE set before taking a reference.
This prevents dying vports from being re-registered in the host_map.

Reported-by: Sashiko <sashiko-dev@google.com>
Cc: stable@vger.kernel.org

> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c
> @@ -4268,6 +4268,8 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
>  			spin_lock_irqsave(&ha->vport_slock, flags);
>  			list_for_each_entry(vp, &ha->vp_list, list) {

[Severity: High]
This isn't a bug introduced by this patch, but is it safe to drop the
vport_slock while iterating the vport list in other areas of the driver?

In functions like qla2x00_quiesce_io() and qla2x00_do_dpc_all_vps(), the
code uses list_for_each_entry_safe() but temporarily drops the lock:

qla2xxx/qla_mid.c:qla2x00_do_dpc_all_vps() {
    list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
        if (vp->vp_idx) {
            atomic_inc(&vp->vref_count);
            spin_unlock_irqrestore(&ha->vport_slock, flags);

            qla2x00_do_dpc_vp(vp);

            spin_lock_irqsave(&ha->vport_slock, flags);
            atomic_dec(&vp->vref_count);
        }
    }
}

Since list_for_each_entry_safe() caches the next element (tvp), could a
concurrent qla24xx_vport_delete() completely remove and free tvp while the
lock is dropped, leading to a use-after-free when the loop advances?

>  				if (vp_idx == vp->vp_idx) {
> +					if (test_bit(VPORT_DELETE, &vp->dpc_flags))
> +						break;

[Severity: Critical]
This isn't a bug introduced by this patch, but does checking VPORT_DELETE
here fully resolve the race condition?

In qla24xx_report_id_acquisition(), the vport_slock is dropped shortly after
this check and the vref_count increment:

qla2xxx/qla_mbx.c:qla24xx_report_id_acquisition() {
    ...
    spin_unlock_irqrestore(&ha->vport_slock, flags);

    if (!found)
        return;

    spin_lock_irqsave(&ha->vport_slock, flags);
    qla_update_host_map(vp, id);
    spin_unlock_irqrestore(&ha->vport_slock, flags);
    ...
}

During the unlocked window, could a concurrent qla24xx_vport_delete()
disable the vport and remove it from host_map?

If so, when the lock is reacquired, qla_update_host_map() would re-insert
the now-dying vport into the btree.

[Severity: High]
This isn't a bug introduced by this patch, but should a similar VPORT_DELETE
check be added to the DPC thread iteration?

In qla2xxx/qla_mid.c:qla2x00_do_dpc_all_vps(), the DPC thread iterates
vp_list and invokes qla2x00_do_dpc_vp(vp) without checking if VPORT_DELETE
is set.

If qla24xx_vport_delete() marks a vport with VPORT_DELETE and disables it
in firmware, could the concurrent DPC thread issue mailbox commands that
inadvertently resurrect the dying vport in firmware?

>  					found = 1;
>  					atomic_inc(&vp->vref_count);
>  					break;

[Severity: Critical]
This isn't a bug introduced by this patch, but does the host_map btree have
consistent locking across the driver?

When qla24xx_report_id_acquisition() modifies the btree for physical ports,
it appears to do so without holding vport_slock.

Meanwhile, qla24xx_disable_vp() modifies the btree using hardware_lock
instead of vport_slock:

qla2xxx/qla_mid.c:qla24xx_disable_vp() {
    spin_lock_irqsave(&vha->hw->hardware_lock, flags);
    qla_update_vp_map(vha, RESET_AL_PA);
    spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
}

Could simultaneous operations from these different threads with
non-overlapping locks cause structural corruption of the btree?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=76

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 79/88] scsi: qla2xxx: Fix NVMe abort reference leak on repeated abort
  2026-07-07  5:54 ` [PATCH v3 79/88] scsi: qla2xxx: Fix NVMe abort reference leak on repeated abort Nilesh Javali
@ 2026-07-07  7:27   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:27 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Reference leak of NVMe command kref when abort fails early and async TMF is disabled.
--

--- Patch [79]: [PATCH v3 79/88] scsi: qla2xxx: Fix NVMe abort reference leak on repeated abort ---
commit 5145be10a2c483912089116ba7647d2593449e4f
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Fix NVMe abort reference leak on repeated abort

This commit drops the reference taken for a command abort when schedule_work()
returns false due to the work already being pending. It ensures the command
kref is balanced regardless of whether the work was newly queued or not.

> diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
> index 8dc6df6c2e1c5..fc8a344ec7d81 100644
> --- a/drivers/scsi/qla2xxx/qla_nvme.c
> +++ b/drivers/scsi/qla2xxx/qla_nvme.c
[ ... ]
> @@ -548,7 +549,8 @@ static void qla_nvme_fcp_abort(struct nvme_fc_local_port *lport,
>  	}
>  	spin_unlock_irqrestore(&priv->cmd_lock, flags);
>  
> -	schedule_work(&priv->abort_work);
> +	if (!schedule_work(&priv->abort_work))
> +		kref_put(&priv->sp->cmd_kref, priv->sp->put_fn);
>  }

[Severity: High]
This is a pre-existing issue, but can this lead to a reference leak of the
NVMe command kref when asynchronous TMF is disabled and the abort fails early?

When schedule_work() runs qla_nvme_abort_work(), if ql2xasynctmfenable is 0,
the abort falls back to the synchronous qla24xx_abort_command().

If qla24xx_abort_command() returns an early error (for example,
QLA_ERR_NOT_FOUND because the command completed concurrently):

drivers/scsi/qla2xxx/qla_mbx.c:qla24xx_abort_command() {
	...
	if (handle == req->num_outstanding_cmds) {
		/* Command not found. */
		return QLA_ERR_NOT_FOUND;
	}
	...
}

It skips calling qla_wait_nvme_release_cmd_kref() at the end of the function.

Then qla_nvme_abort_work() checks abts_done_called (which remains 1)
and returns early:

drivers/scsi/qla2xxx/qla_nvme.c:qla_nvme_abort_work() {
	...
	if (abts_done_called && io_wait_for_abort_done)
		return;

out:
	/* kref_get was done before work was schedule. */
	kref_put(&sp->cmd_kref, sp->put_fn);
}

Does this skip the out label where the reference is supposed to be dropped,
preventing the command from being returned to the NVMe-FC transport?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=79

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 78/88] scsi: qla2xxx: Hold vport_slock for host map update in report ID acquisition
  2026-07-07  5:54 ` [PATCH v3 78/88] scsi: qla2xxx: Hold vport_slock for host map update " Nilesh Javali
@ 2026-07-07  7:28   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:28 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] Incomplete fix leaves physical port `host_map` updates unprotected against concurrent btree corruption.
- [High] Global lock mismatch for `ha->host_map` and `vp_map` structures, allowing concurrent btree corruption.
--

--- Patch [78]: [PATCH v3 78/88] scsi: qla2xxx: Hold vport_slock for host map update in report ID acquisition ---
commit e3ac99c05ebbead772a6f7fee7f4f320422f988b
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Hold vport_slock for host map update in report ID acquisition
    
This commit adds a missing vport_slock around qla_update_host_map() in
the format-1 handling path of qla24xx_report_id_acquisition() to prevent
concurrent host_map updates from corrupting the btree.

> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index e88c3a989a51b..39544deab576a 100644
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c
> @@ -4280,7 +4280,9 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
>  			if (!found)
>  				return;
>  
> +			spin_lock_irqsave(&ha->vport_slock, flags);
>  			qla_update_host_map(vp, id);
> +			spin_unlock_irqrestore(&ha->vport_slock, flags);

[Severity: High]
This isn't a bug introduced by this patch, but does the physical port also
need to be protected by vport_slock?

Earlier in qla24xx_report_id_acquisition(), when handling format == 0 or
when vp_idx == 0 for format == 1, qla_update_host_map(vha, id) is called
without locks.

If qla24xx_report_id_acquisition() is called from
qlt_24xx_process_atio_queue(vha, 0) where no locks are held, could these
unprotected physical port updates race and corrupt the btree?

[Severity: High]
This is a pre-existing issue, but does using vport_slock here conflict with
other contexts that update the host_map?

Looking at other updaters, it seems they hold hardware_lock instead of
vport_slock:

drivers/scsi/qla2xxx/qla_init.c:qla2x00_configure_hba() {
    ...
    spin_lock_irqsave(&ha->hardware_lock, flags);
    ...
    qla_update_host_map(vha, id);
    spin_unlock_irqrestore(&ha->hardware_lock, flags);
    ...
}

drivers/scsi/qla2xxx/qla_mid.c:qla24xx_disable_vp() {
    ...
    spin_lock_irqsave(&vha->hw->hardware_lock, flags);
    qla_update_vp_map(vha, RESET_AL_PA);
    spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
    ...
}

Because these contexts use disjoint locks to serialize modifications to the
exact same btree (ha->host_map), could this lead to concurrent btree
corruption?

>  
>  			/*
>  			 * Cannot configure here as we are still sitting on the

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=78

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 77/88] scsi: qla2xxx: Drop vport reference under lock in report ID acquisition
  2026-07-07  5:54 ` [PATCH v3 77/88] scsi: qla2xxx: Drop vport reference under lock " Nilesh Javali
@ 2026-07-07  7:33   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:33 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [Critical] The `QLA_VHA_MARK_NOT_BUSY()` and `qla_vha_mark_busy()` macros drop the vport reference count without synchronization, risking a use-after-free.
- [High] Unprotected btree modifications in `qla_update_host_map` due to missing `vport_slock`.
- [High] Missing `sess_lock` when traversing `vp_fcports` and calling `qla2x00_find_fcport_by_wwpn` in format 2 handling.
--

--- Patch [77]: [PATCH v3 77/88] scsi: qla2xxx: Drop vport reference under lock in report ID acquisition ---
commit 5bf158aa54ed892e70c7c619b33f75270e50d57a
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Drop vport reference under lock in report ID acquisition

qla24xx_report_id_acquisition() format-1 handling takes the vport
reference under vport_slock but drops it outside the lock, after setting
flags. This patch drops the reference under vport_slock to ensure the
flag stores are ordered before the decrement becomes visible.

> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index affcd87893cda..e88c3a989a51b 100644
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c
> @@ -4290,7 +4290,9 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
>  			set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
>  			set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
>  
> +			spin_lock_irqsave(&ha->vport_slock, flags);
>  			atomic_dec(&vp->vref_count);

[Severity: Critical]
This is a pre-existing issue, but while we're fixing the vref_count decrement
here, I noticed a similar pattern in drivers/scsi/qla2xxx/qla_def.h.

In QLA_VHA_MARK_NOT_BUSY() and qla_vha_mark_busy(), the vport reference count
is dropped without synchronization:

drivers/scsi/qla2xxx/qla_def.h:QLA_VHA_MARK_NOT_BUSY() {
    ...
    atomic_dec(&__vha->vref_count);
    wake_up(&__vha->vref_waitq);
    ...
}

If the count reaches 0, the concurrent polling thread in
qla24xx_deallocate_vp_id() might observe it, delete the vport, and free it.
Will the subsequent wake_up(&__vha->vref_waitq) then result in a
use-after-free by dereferencing the freed __vha?


> +			spin_unlock_irqrestore(&ha->vport_slock, flags);
>  		}
>  		set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
>  		qla2xxx_wake_dpc(vha);

[Severity: High]
This isn't a bug introduced by this patch, but I noticed another unprotected
operation earlier in this same function.

When handling the physical port (vp_idx == 0) in
qla24xx_report_id_acquisition(), qla_update_host_map() is called without
any lock:

drivers/scsi/qla2xxx/qla_mbx.c:qla24xx_report_id_acquisition() {
    ...
    if (vp_idx == 0) {
        if (vp_status == VP_STAT_COMPL) {
            ...
            qla_update_host_map(vha, id);
        }
    ...
}

Since other paths modify the shared ha->host_map b-tree under ha->vport_slock
or ha->hardware_lock, will modifying it here without vport_slock lead to
b-tree corruption?


[Severity: High]
This is a pre-existing issue, but looking further down in the format 2
handling of qla24xx_report_id_acquisition(), the vp_fcports list is traversed
without holding a lock:

drivers/scsi/qla2xxx/qla_mbx.c:qla24xx_report_id_acquisition() {
    ...
    } else if (rptid_entry->format == 2) {
        ...
        list_for_each_entry(fcport, &vha->vp_fcports, list) {
            fcport->scan_state = QLA_FCPORT_SCAN;
            fcport->n2n_flag = 0;
        }

        fcport = qla2x00_find_fcport_by_wwpn(vha,
            rptid_entry->u.f2.port_name, 1);
    ...
}

The sess_lock is typically acquired when traversing vp_fcports. If the list
is concurrently modified by session addition or deletion, can this
unprotected traversal race and cause list corruption or a use-after-free?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=77

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 81/88] scsi: qla2xxx: Unlink NVMe unsol ctx before freeing on LS reject error
  2026-07-07  5:54 ` [PATCH v3 81/88] scsi: qla2xxx: Unlink NVMe unsol ctx before freeing on LS reject error Nilesh Javali
@ 2026-07-07  7:34   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:34 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] Lockless list manipulation of `fcport->unsol_ctx_head` leads to data races and list corruption.
- [High] `fcport` is freed without canceling or synchronizing background purex tasks, leading to Use-After-Free when those tasks access `uctx->fcport`.
--

--- Patch [81]: [PATCH v3 81/88] scsi: qla2xxx: Unlink NVMe unsol ctx before freeing on LS reject error ---
commit f4f1b05ef4c1210be894b2dc013ef3b077996e9e
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Unlink NVMe unsol ctx before freeing on LS reject error

This patch addresses a potential list corruption issue in the NVMe
unsolicited context handling. It unlinks the context from the list
before freeing it on the error path. This ensures that subsequent operations
on the fcport list do not interact with freed memory.

> diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
> index 28a04e0ff660b..36b742f73abf3 100644
> --- a/drivers/scsi/qla2xxx/qla_nvme.c
> +++ b/drivers/scsi/qla2xxx/qla_nvme.c
[ ... ]
> @@ -446,6 +446,7 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport,
>  		qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
>  		spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
>  	}
> +	list_del(&uctx->elem);

[Severity: High]
This isn't a bug introduced by this patch, but is it safe to manipulate
the list here without any locking?

Elements are added to the list without synchronization in the hardware
response interrupt context:

drivers/scsi/qla2xxx/qla_nvme.c:qla2xxx_process_purls_iocb() {
    ...
    INIT_LIST_HEAD(&uctx->elem);
    list_add_tail(&uctx->elem, &fcport->unsol_ctx_head);
    ...
}

Items are also removed from a workqueue context without locking:

drivers/scsi/qla2xxx/qla_nvme.c:qla_nvme_release_lsrsp_cmd_kref() {
    ...
    list_del(&uctx->elem);
    ...
}

Since operations can execute concurrently across multiple CPUs asynchronously,
will this unprotected list_del() modification lead to further list
corruption and kernel oops from non-atomic pointer updates?

[Severity: High]
This is a pre-existing issue, but does this code correctly handle the
lifetime of the fcport pointer?

When an fcport logs out, it can be freed directly:

drivers/scsi/qla2xxx/qla_init.c:qla2x00_free_fcport() {
    ...
    qla_edif_list_del(fcport);
    kfree(fcport);
    ...
}

If a background task is simultaneously processing queued items, it might
access the freed structure:

drivers/scsi/qla2xxx/qla_nvme.c:qla2xxx_process_purls_pkt() {
    ...
    ret = nvme_fc_rcv_ls_req(uctx->fcport->nvme_remote_port, &uctx->lsrsp,
                 &item->iocb, item->size);
    ...
}

Since qla2x00_free_fcport() doesn't appear to cancel pending work or clear
vha->purex_list, could this sequence cause a use-after-free when the target
drops connection after sending an unsolicited NVMe LS packet?

>  	kfree(uctx);
>  	return rval;
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=81

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 74/88] scsi: qla2xxx: Clamp max_npiv_vports to VP_CTRL bitmap capacity
  2026-07-07  5:54 ` [PATCH v3 74/88] scsi: qla2xxx: Clamp max_npiv_vports to VP_CTRL bitmap capacity Nilesh Javali
@ 2026-07-07  7:34   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:34 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

New issues:
- [High] The introduced cap on `ha->max_npiv_vports` is immediately overwritten by firmware values, leaving the intended resource leak completely unpatched.

Pre-existing issues:
- [High] A pre-existing off-by-one error in `qla24xx_control_vp()` guarantees that the vport at the highest valid index will always leak.
- [High] Data race on bitfields in the `ha->flags` structure leading to lost mailbox interrupts and adapter hangs.
--

--- Patch [74]: [PATCH v3 74/88] scsi: qla2xxx: Clamp max_npiv_vports to VP_CTRL bitmap capacity ---
commit 200dabdd0444e358cb943332543c7d1ad6735f45
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Clamp max_npiv_vports to VP_CTRL bitmap capacity

This commit attempts to prevent a virtual port resource leak by capping the
maximum number of NPIV vports to the capacity of the VP_CTRL IOCB index
bitmap. It updates the maximum count during chip setup and modifies the
bounds check during VP control operations.

[ ... ]

> diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
> index d678e27213a94..1f20ab386003d 100644
> --- a/drivers/scsi/qla2xxx/qla_init.c
> +++ b/drivers/scsi/qla2xxx/qla_init.c
> @@ -4412,6 +4412,19 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
>  					    MIN_MULTI_ID_FABRIC))
>  						ha->max_npiv_vports =
>  						    MIN_MULTI_ID_FABRIC - 1;

[Severity: High]
This isn't a bug introduced by this patch, but can the initialization of
ha->flags in this block cause a data race that drops mailbox interrupts?

The ha->flags structure is a collection of bitfields sharing a 32-bit word.
During qla2x00_setup_chip(), lockless writes occur right before this logic,
such as setting npiv_supported:

qla2x00_setup_chip()
    ha->flags.npiv_supported = 1;

If the hardware issues a concurrent mailbox completion interrupt, the ISR
will acquire the hardware lock and set the mbox_int bit:

qla24xx_intr_handler() -> qla2x00_mbx_completion()
    ha->flags.mbox_int = 1;

Could the lockless read-modify-write phase of setting npiv_supported
overwrite and clear the newly set mbox_int bit, leading to a mailbox command
timeout?

> +
> +					/*
> +					 * The VP_CTRL IOCB selects target VPs
> +					 * through the fixed vp_idx_map bitmap,
> +					 * so a vp_index beyond it can be enabled
> +					 * via VP_CONFIG but never disabled via
> +					 * VP_CTRL, leaking the VP.  Cap the count
> +					 * to the bitmap capacity.
> +					 */
> +					if (ha->max_npiv_vports >=
> +					    VP_CTRL_IDX_MAP_BITS)
> +						ha->max_npiv_vports =
> +						    VP_CTRL_IDX_MAP_BITS - 1;
>  				}
>  				qlt_config_nvram_with_fw_version(vha);
>  				qla2x00_get_resource_cnts(vha);

[Severity: High]
Does the subsequent call to qla2x00_get_resource_cnts() immediately overwrite
this newly calculated cap?

Looking at qla2x00_get_resource_cnts(), it unconditionally assigns the raw
firmware value back to max_npiv_vports:

qla2x00_get_resource_cnts() {
    ...
    if (ha->flags.npiv_supported)
        ha->max_npiv_vports = mcp->mb[11];
    ...
}

Will this cause the intended cap to be lost before it can prevent the
resource leak?

[ ... ]

> diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
> index 33bfc61d81652..4ad23d206add2 100644
> --- a/drivers/scsi/qla2xxx/qla_mid.c
> +++ b/drivers/scsi/qla2xxx/qla_mid.c
> @@ -996,7 +996,7 @@ int qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
>  	 * (16-byte) vp_idx_map bitmap, so vp_index must fit within it even
>  	 * if firmware advertises more NPIV vports.
>  	 */
> -	if (vp_index > sizeof_field(struct vp_ctrl_entry_24xx, vp_idx_map) * 8)
> +	if (vp_index > VP_CTRL_IDX_MAP_BITS)
>  		return QLA_PARAMETER_ERROR;

[Severity: High]
This is a pre-existing issue, but does the strict inequality check on
max_npiv_vports earlier in this function create an off-by-one error?

In qla24xx_allocate_vp_id(), the allocator searches up to max_npiv_vports + 1:

qla24xx_allocate_vp_id() {
    ...
    vp_id = find_first_zero_bit(ha->vp_idx_map, ha->max_npiv_vports + 1);
    if (vp_id > ha->max_npiv_vports) {
    ...
}

This permits allocating an ID exactly equal to ha->max_npiv_vports.

However, earlier in qla24xx_control_vp(), the check rejects values greater
than or equal to max_npiv_vports:

    if (vp_index == 0 || vp_index >= ha->max_npiv_vports)
        return QLA_PARAMETER_ERROR;

Will this cause the virtual port at the highest index to be successfully
created, but silently rejected during deletion, leaving it permanently
active in the firmware?

>  
>  	/* ref: INIT */

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=74

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 83/88] scsi: qla2xxx: Use coherent DMA buffer for D_Port diagnostics
  2026-07-07  5:54 ` [PATCH v3 83/88] scsi: qla2xxx: Use coherent DMA buffer for D_Port diagnostics Nilesh Javali
@ 2026-07-07  7:34   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:34 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] The patch misses identical DMA API violations in `qla26xx_dport_diagnostics_v2`
- [High] DMA Use-After-Free on mailbox command timeout
--

--- Patch [83]: [PATCH v3 83/88] scsi: qla2xxx: Use coherent DMA buffer for D_Port diagnostics ---
commit 49a3a18e5cf5c905cd3b276b6e0bdaad40f07c7f
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Use coherent DMA buffer for D_Port diagnostics

qla26xx_dport_diagnostics() streaming-maps the caller's result buffer with
dma_map_single(). The bsg path passes &dd->buf from the __packed struct
qla_dport_diag, where buf lands at a 2-byte offset and shares cachelines
with the surrounding options/unused fields. Mapping such a misaligned
sub-buffer violates the DMA API requirement that streaming buffers be
cacheline aligned and not share a cacheline with other data, and can
corrupt data on non-DMA-coherent architectures.

Allocate a dedicated DMA-coherent buffer inside qla26xx_dport_diagnostics()
for the mailbox command and copy the result back into the caller's buffer.
This removes the streaming map of the misaligned sub-buffer entirely; the
caller's buffer is now only a plain CPU buffer, so its packing no longer
matters.

> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index 39544deab576a..59ec5605930b7 100644
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c

[ ... ]

> @@ -6617,8 +6615,9 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha,
>  		    "Done %s.\n", __func__);
>  	}
>  
> -	dma_unmap_single(&vha->hw->pdev->dev, dd_dma,
> -	    size, DMA_FROM_DEVICE);
> +	memcpy(dd_buf, dd, size);
> +
> +	dma_free_coherent(&vha->hw->pdev->dev, size, dd, dd_dma);

[Severity: High]
This is a pre-existing issue, but does this unconditional free create a DMA 
use-after-free when the mailbox command times out?

If qla2x00_mailbox_command() returns QLA_FUNCTION_TIMEOUT, an ISP abort is
scheduled asynchronously and the function returns immediately. Freeing the
coherent DMA buffer here releases the physical memory while the hardware
may still be actively running and writing to it, which could corrupt memory.

>  
>  	return rval;
>  }

[Severity: High]
This isn't a bug introduced by this patch, but does the adjacent function
qla26xx_dport_diagnostics_v2() suffer from the exact same DMA API violations
that this patch fixes?

Looking at qla26xx_dport_diagnostics_v2() in the same file:

drivers/scsi/qla2xxx/qla_mbx.c:qla26xx_dport_diagnostics_v2() {
    ...
	dd_dma = dma_map_single(&vha->hw->pdev->dev,
				dd->buf, size, DMA_FROM_DEVICE);
    ...
	memset(dd->buf, 0, size);
    ...
}

It continues to stream-map the misaligned sub-buffer and improperly modifies
the buffer from the CPU side via memset() while it is mapped, which could
similarly corrupt memory on non-DMA-coherent architectures.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=83

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 88/88] scsi: qla2xxx: Update version to 12.00.00.2607b1
  2026-07-07  5:54 ` [PATCH v3 88/88] scsi: qla2xxx: Update version to 12.00.00.2607b1 Nilesh Javali
@ 2026-07-07  7:35   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:35 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The odd-length version string causes an infinite loop condition evaluation, resulting in an out-of-bounds memory read into firmware mailbox registers.
- [Medium] The new QLA_DRIVER_BETA_VER (2607) exceeds 8 bits, causing data corruption and truncation in minidump headers.
--

--- Patch [88]: [PATCH v3 88/88] scsi: qla2xxx: Update version to 12.00.00.2607b1 ---
commit 7835c1af22d4a49f4cede46a4b0344d098aa7b95
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Update version to 12.00.00.2607b1

This commit updates the driver version, major, minor, patch, and beta
version macros to reflect the new 12.00.00.2607b1 release.

> diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
> index 9564beafdab71..1c0b01d703509 100644
> --- a/drivers/scsi/qla2xxx/qla_version.h
> +++ b/drivers/scsi/qla2xxx/qla_version.h
> @@ -6,9 +6,9 @@
>  /*
>   * Driver version
>   */
> -#define QLA2XXX_VERSION      "10.02.10.100-k"
> +#define QLA2XXX_VERSION      "12.00.00.2607b1"

[Severity: High]
Does changing the version string length to an odd number (15 characters)
cause an out-of-bounds read?

In drivers/scsi/qla2xxx/qla_mbx.c:qla82xx_set_driver_version(), the loop
decrements the length by 2 on each iteration:

        for (i = 4; i < 16 && len; i++, str++, len -= 2) {
                mcp->mb[i] = le16_to_cpup(str);
                mcp->out_mb |= 1<<i;
        }

Since len starts as an odd number, len -= 2 will skip 0 and evaluate to
true for subsequent checks, causing the loop to continue reading past the
end of the string into adjacent memory until i reaches 16.

>  
> -#define QLA_DRIVER_MAJOR_VER	10
> -#define QLA_DRIVER_MINOR_VER	02
> -#define QLA_DRIVER_PATCH_VER	10
> -#define QLA_DRIVER_BETA_VER	100
> +#define QLA_DRIVER_MAJOR_VER	12
> +#define QLA_DRIVER_MINOR_VER	00
> +#define QLA_DRIVER_PATCH_VER	00
> +#define QLA_DRIVER_BETA_VER	2607

[Severity: Medium]
Will this new beta version exceed 8-bit packing constraints and corrupt the
version data?

The value 2607 requires 12 bits, but it is used in contexts expecting an
8-bit value. For example, in drivers/scsi/qla2xxx/qla_nx.c:qla82xx_md_collect(),
it spills into the patch version byte due to an unmasked bitwise OR:

        tmplt_hdr->driver_info[1] = (QLA_DRIVER_MAJOR_VER << 24) |
            (QLA_DRIVER_MINOR_VER << 16) | (QLA_DRIVER_PATCH_VER << 8) |
            QLA_DRIVER_BETA_VER;

Additionally, in drivers/scsi/qla2xxx/qla_tmpl.c:qla27xx_driver_info(), it
is parsed using the %hhu format string which silently truncates 2607 to 47:

        WARN_ON_ONCE(sscanf(qla2x00_version_str,
                            "%hhu.%hhu.%hhu.%hhu",
                            v + 0, v + 1, v + 2, v + 3) != 4);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=88

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 84/88] scsi: qla2xxx: Zero-init bsg stack buffers to avoid info leak
  2026-07-07  5:54 ` [PATCH v3 84/88] scsi: qla2xxx: Zero-init bsg stack buffers to avoid info leak Nilesh Javali
@ 2026-07-07  7:37   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:37 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] The `qla24xx_iidma` BSG handler fails to validate `bsg_job->request_len`, leading to heap out-of-bounds read and write vulnerabilities when processing a short request.
- [High] Missing bounds check on user-supplied `i2c->length` in `qla2x00_read_i2c` allows kernel stack buffer overflow and DMA memory corruption.
--

--- Patch [84]: [PATCH v3 84/88] scsi: qla2xxx: Zero-init bsg stack buffers to avoid info leak ---
commit e991f43fbeb0a4f289a4242fcd01f28211667aba
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Zero-init bsg stack buffers to avoid info leak

This patch zero-initializes the stack buffers in several qla2xxx bsg handlers
to prevent leaking stale kernel stack memory to user space on short requests.
It explicitly sets the bsg arrays to zero at declaration across the handlers.

> diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
> index 55142df30bda7..cb7227298b24b 100644
> --- a/drivers/scsi/qla2xxx/qla_bsg.c
> +++ b/drivers/scsi/qla2xxx/qla_bsg.c
[ ... ]
> @@ -2163,7 +2163,7 @@ qla2x00_read_i2c(struct bsg_job *bsg_job)
>  	scsi_qla_host_t *vha = shost_priv(host);
>  	struct qla_hw_data *ha = vha->hw;
>  	int rval = 0;
> -	uint8_t bsg[DMA_POOL_SIZE];
> +	uint8_t bsg[DMA_POOL_SIZE] = {};
>  	struct qla_i2c_access *i2c = (void *)bsg;
>  	dma_addr_t sfp_dma;
>  	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);

[Severity: High]
This is a pre-existing issue, but does this function lack a bounds check on
the user-supplied i2c->length?

In qla2x00_write_i2c(), there is a check to ensure i2c->length is not larger
than sizeof(i2c->buffer). However, in qla2x00_read_i2c(), the unvalidated
i2c->length is passed directly to qla2x00_read_sfp():

    rval = qla2x00_read_sfp(vha, sfp_dma, sfp,
        i2c->device, i2c->offset, i2c->length, i2c->option);

Could this instruct the firmware to perform a DMA transfer larger than the
allocated sfp buffer?

Following that, the unbounded length is used in memcpy():

    memcpy(i2c->buffer, sfp, i2c->length);

Since i2c points to the local bsg stack array, could a length greater than
248 bytes cause a kernel stack buffer overflow?


While reviewing the BSG handlers in this file, I also noticed another
pre-existing issue in qla24xx_iidma().

[Severity: High]
This is a pre-existing issue, but does qla24xx_iidma() fail to validate
bsg_job->request_len?

In qla24xx_iidma(), port_param is derived by appending the size of the
request header to the user-supplied bsg_request:

    port_param = (void *)bsg_request + sizeof(struct fc_bsg_request);

If a user provides a short request where bsg_job->request_len is not
sufficiently large, port_param points past the end of the buffer.

The function then passes &port_param->speed to qla2x00_get_idma_speed():

    rval = qla2x00_get_idma_speed(vha, fcport->loop_id,
            &port_param->speed, mb);

Could this result in an out-of-bounds heap read and write when the speed
is read from or written to the out-of-bounds location?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=84

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 75/88] scsi: qla2xxx: Avoid double completion in async IOCB timeout
  2026-07-07  5:54 ` [PATCH v3 75/88] scsi: qla2xxx: Avoid double completion in async IOCB timeout Nilesh Javali
@ 2026-07-07  7:37   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:37 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

Pre-existing issues:
- [High] Incomplete Fix / Use-After-Free in timeout handlers
- [High] Firmware resource leak on timeout path
- [High] Spurious Abort / Race Condition due to handle reuse
- [High] Use-After-Free in abort completion handler
- [High] Indefinite Hang and Resource Leak in abort timeout handler
--

--- Patch [75]: [PATCH v3 75/88] scsi: qla2xxx: Avoid double completion in async IOCB timeout ---
commit f23f1a72942887da2ba25584e4324ef06430c7bc
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Avoid double completion in async IOCB timeout

This commit addresses a double completion and potential use-after-free
in qla2x00_async_iocb_timeout() when an async IOCB times out. It tracks
whether the SRB was successfully removed from outstanding_cmds[] before
invoking sp->done(), preventing a race with the response ISR.

> diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
> index 1f20ab386003d..d935fe5e5316a 100644
> --- a/drivers/scsi/qla2xxx/qla_init.c
> +++ b/drivers/scsi/qla2xxx/qla_init.c
[ ... ]
> @@ -251,6 +251,7 @@ qla2x00_async_iocb_timeout(void *data)
>  			lio->u.logio.data[1] =
>  				lio->u.logio.flags & SRB_LOGIN_RETRIED ?
>  				QLA_LOGIO_LOGIN_RETRIED : 0;
> +			found = 0;
>  			spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags);
>  			for (h = 1; h < sp->qpair->req->num_outstanding_cmds;
>  			    h++) {
> @@ -258,11 +259,19 @@ qla2x00_async_iocb_timeout(void *data)
>  				    sp) {
>  					sp->qpair->req->outstanding_cmds[h] =
>  					    NULL;

[Severity: High]
This isn't a bug introduced by this patch, but does removing the SRB here leak
firmware resources?

By setting the outstanding_cmds[] slot to NULL without calling
qla_put_fw_resources(sp->qpair, &sp->iores), the resources are never released.

The ISR would normally execute this cleanup, but since the SRB is removed from
the array, the ISR will never process it.

Does this permanently leak ha->fwres.iocb_used and ha->fwres.exch_used?

> +					found = 1;
>  					break;
>  				}
>  			}
>  			spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags);
> -			sp->done(sp, QLA_FUNCTION_TIMEOUT);
> +			/*
> +			 * Only complete the command if this path removed it
> +			 * from outstanding_cmds.  Otherwise the ISR already
> +			 * completed it and a second sp->done() would race the
> +			 * submitter's freeing of the on-stack completion.
> +			 */
> +			if (found)
> +				sp->done(sp, QLA_FUNCTION_TIMEOUT);

[Severity: High]
This is a pre-existing issue, but is this same tracking logic needed in other
timeout handlers?

Functions like qla2x00_els_dcmd2_iocb_timeout(), qla2x00_fxdisc_iocb_timeout(),
and qla2x00_els_dcmd_iocb_timeout() currently invoke sp->done() or complete()
unconditionally, even if the command was not found in outstanding_cmds.

Could those functions suffer from the exact same double-completion and stack
use-after-free this patch fixes?

[ ... ]
> @@ -275,6 +284,7 @@ qla2x00_async_iocb_timeout(void *data)
>  	default:
>  		rc = qla24xx_async_abort_cmd(sp, false);

[Severity: High]
This is a pre-existing issue, but does this unconditional abort submission race
with handle reuse?

If the timer execution races with the ISR, the ISR may complete the original
command and return its handle to the pool before the abort is submitted.

Then qla2xxx_get_next_handle() could assign that same handle to a newly
submitted, unrelated command. Will the firmware then receive the abort and
incorrectly terminate the new command?

[Severity: High]
This is also a pre-existing issue, but can qla24xx_async_abort_cmd() introduce
a use-after-free in its completion handler?

When building the abort command, it stores a weak pointer to the original
command:
    abt_sp->cmd_sp = sp;
    
This is done without incrementing the original command's reference count.

If the ISR completes and frees the original command before the abort completes,
would qla24xx_abort_sp_done() dereference the freed memory via
qla_wait_nvme_release_cmd_kref(orig_sp)?

[Severity: High]
While looking at the abort timeout path, there is another pre-existing issue.
Does qla24xx_abort_iocb_timeout() have a loop termination bug that leaves
commands stranded?

When iterating over outstanding_cmds to locate both the abort command (sp)
and the original command (sp->cmd_sp), the loop breaks immediately if it
encounters the abort command first.

Because handles can wrap around, the abort command can receive a lower index.
In that scenario, sp->cmd_sp is never reached in the search.

Does this leave the original command in outstanding_cmds forever, causing the
submitter to hang indefinitely?

>  		if (rc) {
[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=75

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 80/88] scsi: qla2xxx: Skip NVMe LS reject IOCB when FW not started
  2026-07-07  5:54 ` [PATCH v3 80/88] scsi: qla2xxx: Skip NVMe LS reject IOCB when FW not started Nilesh Javali
@ 2026-07-07  7:38   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:38 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Use-After-Free and list corruption on NVMe LS response error path.
- [Critical] Concurrent lockless list manipulation of `fcport->unsol_ctx_head` across interrupt and process contexts.
- [High] Lockless read-modify-write bitfield race on `ha->flags` corrupts state and causes lost interrupts.
--

--- Patch [80]: [PATCH v3 80/88] scsi: qla2xxx: Skip NVMe LS reject IOCB when FW not started ---
commit 4b34d405682300e7feb89bc5e0c5433498c1683d
Author: Nilesh Javali <njavali@marvell.com>
Date:   Tue Jul 7 05:54:27 2026 +0000

scsi: qla2xxx: Skip NVMe LS reject IOCB when FW not started

This patch modifies the error paths in qla_nvme_xmt_ls_rsp() and
qla2xxx_process_purls_pkt() to check if the firmware is started before
sending an NVMe LS reject IOCB. This avoids ringing the doorbell and queuing
IOCBs when the firmware is stopped or resetting.

> diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
> index fc8a344ec7d81..28a04e0ff660b 100644
> --- a/drivers/scsi/qla2xxx/qla_nvme.c
> +++ b/drivers/scsi/qla2xxx/qla_nvme.c
> @@ -441,9 +441,11 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport,
>  	a.vp_idx = vha->vp_idx;
>  	a.nport_handle = uctx->nport_handle;
>  	a.xchg_address = uctx->exchange_address;
> -	spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
> -	qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
> -	spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
> +	if (ha->flags.fw_started) {

[Severity: High]
This is a pre-existing issue, but does using a bitfield for ha->flags cause
read-modify-write races?

Looking at struct qla_hw_data, fw_started and mbox_int share the same 32-bit
word:

drivers/scsi/qla2xxx/qla_def.h:
	volatile struct {
		uint32_t	mbox_int		:1;
...
		uint32_t	fw_started:1;
...
	} flags;

Since qla2x00_abort_isp_cleanup() locklessly clears fw_started in process
context, and qla2x00_mbx_completion() sets mbox_int in interrupt context,
couldn't a concurrent update cause one of the flags to be lost and stall the
firmware initialization?

> +		spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
> +		qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
> +		spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
> +	}
>  	kfree(uctx);

[Severity: Critical]
This is a pre-existing issue, but does this error path leave a dangling
pointer in the fcport->unsol_ctx_head list?

When kfree(uctx) is called here, it doesn't appear to be removed from the
list via list_del(&uctx->elem). Later unsolicited requests could then attempt
to use list_add_tail() on the corrupted list pointers of the freed block,
potentially leading to slab corruption.

>  	return rval;
>  }

[ ... ]

> @@ -1321,9 +1323,14 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item)
>  		a.vp_idx = vha->vp_idx;
>  		a.nport_handle = uctx->nport_handle;
>  		a.xchg_address = uctx->exchange_address;
> -		spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr, flags);
> -		qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true);
> -		spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr, flags);
> +		if (vha->hw->flags.fw_started) {
> +			spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr,
> +					  flags);
> +			qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a,
> +						true);
> +			spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr,
> +					       flags);
> +		}
>  		list_del(&uctx->elem);

[Severity: Critical]
This is a pre-existing issue, but does this list manipulation require a 
spinlock to prevent concurrent corruption?

The list_del(&uctx->elem) here executes in a workqueue context. At the same
time, qla2xxx_process_purls_iocb() adds to this list from the hardware
response queue ISR context:

drivers/scsi/qla2xxx/qla_nvme.c:qla2xxx_process_purls_iocb()
    INIT_LIST_HEAD(&uctx->elem);
    list_add_tail(&uctx->elem, &fcport->unsol_ctx_head);

Could overlapping unsolicited requests race between the interrupt and workqueue
contexts and corrupt the prev/next pointers?

>  		kfree(uctx);
>  	}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=80

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 86/88] scsi: qla2xxx: Zero SFP DMA buffer in FRU/I2C bsg handlers
  2026-07-07  5:54 ` [PATCH v3 86/88] scsi: qla2xxx: Zero SFP DMA buffer in FRU/I2C bsg handlers Nilesh Javali
@ 2026-07-07  7:38   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:38 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Heap buffer overflow in qla_edif_process_els() due to unbounded user payload lengths.
--

--- Patch [86]: [PATCH v3 86/88] scsi: qla2xxx: Zero SFP DMA buffer in FRU/I2C bsg handlers ---
commit 5b2a349a69efbe478558db07cf4b96e8fb11e62b
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Zero SFP DMA buffer in FRU/I2C bsg handlers

The FRU and I2C bsg handlers stage their transfer in a DMA_POOL_SIZE
bounce buffer obtained from dma_pool_alloc(), which does not zero the
allocation. This commit changes these handlers to use dma_pool_zalloc()
to prevent writing stale heap memory to the device flash.

> diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
> index e671c3de8c058..51ab638b4fc09 100644
> --- a/drivers/scsi/qla2xxx/qla_bsg.c
> +++ b/drivers/scsi/qla2xxx/qla_bsg.c
> @@ -1994,7 +1994,7 @@ qla2x00_update_fru_versions(struct bsg_job *bsg_job)
>  	struct qla_image_version *image;
>  	uint32_t count;
>  	dma_addr_t sfp_dma;
> -	void *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
> +	void *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
>  
>  	if (!sfp) {
>  		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =

[ ... ]

[Severity: High]
This isn't a bug introduced by this patch, but while reviewing the DMA pool
usage in BSG handlers, I noticed a potential buffer overflow in
qla_edif_process_els() located in drivers/scsi/qla2xxx/qla_edif.c.

Does this code allow an unbounded copy into the fixed-size purex_dma_pool?

drivers/scsi/qla2xxx/qla_edif.c:qla_edif_process_els() {
        ...
        sp->remap.req.len = bsg_job->request_payload.payload_len;
        sp->remap.req.buf = dma_pool_alloc(ha->purex_dma_pool, GFP_KERNEL,
                                           &sp->remap.req.dma);
        ...
        sg_copy_to_buffer(bsg_job->request_payload.sg_list,
            bsg_job->request_payload.sg_cnt, sp->remap.req.buf,
            sp->remap.req.len);
        ...
        sp->remap.rsp.len = bsg_job->reply_payload.payload_len;
        sp->remap.rsp.buf = dma_pool_alloc(ha->purex_dma_pool, GFP_KERNEL,
                                           &sp->remap.rsp.dma);
        ...
}

Since purex_dma_pool has a fixed object size (2112 bytes), can a user submit
a BSG request with a request_payload.payload_len or
reply_payload.payload_len greater than 2112 bytes?

If so, could this cause an out-of-bounds write on the DMA pool memory during
the sg_copy_to_buffer() call or during the firmware DMA transfer?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=86

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 82/88] scsi: qla2xxx: Serialize NVMe unsol ctx list with a per-fcport lock
  2026-07-07  5:54 ` [PATCH v3 82/88] scsi: qla2xxx: Serialize NVMe unsol ctx list with a per-fcport lock Nilesh Javali
@ 2026-07-07  7:38   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:38 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The `unsol_ctx_head` list is functionally write-only and never traversed, making the list and the newly added `unsol_ctx_lock` dead state that should be removed instead of synchronized.
--

--- Patch [82]: [PATCH v3 82/88] scsi: qla2xxx: Serialize NVMe unsol ctx list with a per-fcport lock ---
commit f8ccd94b18e0f83a1d0b27da47b7d2c7baf3e9c0
Author: Nilesh Javali <njavali@marvell.com>

scsi: qla2xxx: Serialize NVMe unsol ctx list with a per-fcport lock

This commit introduces a per-fcport spinlock to serialize accesses to
the unsol_ctx_head list. It adds the lock to protect list_add_tail and
list_del operations from concurrent accesses in different contexts.

> diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
> index bb4305f6a364c..afbd056148bd5 100644
> --- a/drivers/scsi/qla2xxx/qla_def.h
> +++ b/drivers/scsi/qla2xxx/qla_def.h
[ ... ]
> @@ -2645,6 +2645,8 @@ typedef struct fc_port {
>  	struct list_head list;
>  	struct scsi_qla_host *vha;
>  	struct list_head unsol_ctx_head;
> +	/* Serializes unsol_ctx_head against ISR, DPC and NVMe transport. */
> +	spinlock_t unsol_ctx_lock;

[Severity: Medium]
Is the unsol_ctx_head list ever actually read or traversed anywhere in the
codebase? Looking through the driver, it appears this list might be 
functionally write-only. If the list is dead state, would it be better
to completely remove unsol_ctx_head and uctx->elem rather than adding a
spinlock to protect them?

[ ... ]
> diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
> index 36b742f73abf3..beccece1e7d92 100644
> --- a/drivers/scsi/qla2xxx/qla_nvme.c
> +++ b/drivers/scsi/qla2xxx/qla_nvme.c
[ ... ]
> @@ -1439,7 +1446,9 @@ void qla2xxx_process_purls_iocb(void **pkt, struct rsp_que **rsp)
>  	uctx->ox_id = p->ox_id;
>  	qla_rport->uctx = uctx;
>  	INIT_LIST_HEAD(&uctx->elem);
> +	spin_lock_irqsave(&fcport->unsol_ctx_lock, flags);
>  	list_add_tail(&uctx->elem, &fcport->unsol_ctx_head);
> +	spin_unlock_irqrestore(&fcport->unsol_ctx_lock, flags);
>  	item->purls_context = (void *)uctx;

[Severity: Medium]
Does taking this new spinlock in the hot path to add an element to an 
unused list introduce unnecessary synchronization overhead? If the list
is never traversed, could we avoid this performance penalty by removing
the list operations entirely instead of serializing it?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=82

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 85/88] scsi: qla2xxx: Validate BSG request_len before reading vendor_cmd[]
  2026-07-07  5:54 ` [PATCH v3 85/88] scsi: qla2xxx: Validate BSG request_len before reading vendor_cmd[] Nilesh Javali
@ 2026-07-07  7:46   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  7:46 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:

New issues:
- [Critical] The newly added bounds checks incorrectly use `sizeof(struct fc_bsg_request)` to calculate the required payload size, artificially rejecting valid, properly sized userspace requests and breaking the UAPI.

Pre-existing issues:
- [High] The patch fails to add `request_len` validation to `qla84xx_mgmt_cmd` and `qla24xx_iidma`, leaving them vulnerable to out-of-bounds heap reads.
- [High] The transport layer `fc_bsg_host_dispatch()` does not actually guarantee that `request_len` covers `vendor_id`, leading to a heap out-of-bounds read before reaching the driver.
- [High] Leaked `vha` and `qpair` busy reference counters in the `qlafx00_mgmt_cmd` error path.
- [High] Concurrent BSG requests to QLFC_FCP_PRIO_SET_CONFIG and QLFC_FCP_PRIO_GET_CONFIG can race on the unprotected `ha->fcp_prio_cfg` pointer, leading to memory leaks, data corruption, Use-After-Free, or NULL pointer dereferences.
- [High] Concurrent loopback BSG requests can cause a TOCTOU race on the loopback state and wait incorrectly on a shared completion variable.
--

--- Patch [85]: [PATCH v3 85/88] scsi: qla2xxx: Validate BSG request_len before reading vendor_cmd[] ---
commit a39aea7848cc6caa6a7176a258e927c5587f88e5
Author: Nilesh Javali <njavali@marvell.com>
Subject: scsi: qla2xxx: Validate BSG request_len before reading vendor_cmd[]

This commit attempts to fix out-of-bounds reads in the transport handlers
by adding a central length guard in qla2x00_process_vendor_specific and
additional request length checks in various sub-handlers.

> diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
> index cb7227298b24b..e671c3de8c058 100644
> --- a/drivers/scsi/qla2xxx/qla_bsg.c
> +++ b/drivers/scsi/qla2xxx/qla_bsg.c
> @@ -160,6 +160,12 @@ qla24xx_proc_fcp_prio_cfg_cmd(struct bsg_job *bsg_job)
>  		goto exit_fcp_prio_cfg;
>  	}
>  
> +	if (bsg_job->request_len <
> +	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t)) {
> +		ret = -EINVAL;
> +		goto exit_fcp_prio_cfg;
> +	}
> +

[Severity: Critical]
Does this inflate the requirement to 28 bytes when only 20 bytes are actually
needed to safely read vendor_cmd[1]?

[Severity: High]
This is a pre-existing issue, but looking at the rest of this function,
can concurrent requests race on the unprotected ha->fcp_prio_cfg pointer?

If two concurrent requests execute the set config and get config operations,
could one thread free the pointer and set it to null while the other is
copying from it to userspace, leading to a null pointer dereference or
use-after-free?

[ ... ]

> @@ -758,6 +764,10 @@ qla2x00_process_loopback(struct bsg_job *bsg_job)
>  		return -EIO;
>  	}
>  
> +	if (bsg_job->request_len <
> +	    sizeof(struct fc_bsg_request) + 3 * sizeof(uint32_t))
> +		return -EINVAL;
> +
>  	memset(&elreq, 0, sizeof(elreq));

[Severity: High]
This isn't a bug introduced by this patch, but further down in this function,
concurrent loopback requests perform a lockless read of the loopback state.

Can multiple threads bypass the check, issue hardware commands, and wait on
the same shared ha->dcbx_comp completion variable? Since complete() only wakes
one thread, wouldn't the other time out and trigger an unjustified abort of
the hardware?

[ ... ]

> @@ -2397,6 +2427,11 @@ qlafx00_mgmt_cmd(struct bsg_job *bsg_job)
>  	struct fc_port *fcport;
>  	char  *type = "FC_BSG_HST_FX_MGMT";
>  
> +	if (bsg_job->request_len <
> +	    sizeof(struct fc_bsg_request) + sizeof(uint32_t) +
> +	    sizeof(struct qla_mt_iocb_rqst_fx00))
> +		return -EINVAL;
> +
>  	/* Copy the IOCB specific information */

[Severity: High]
This isn't a bug introduced by this patch, but looking at the error path for
qla2x00_start_sp() in this function, the code manually calls mempool_free()
instead of qla2x00_rel_sp().

Does this skip decrementing the busy reference counters for the hardware state
and queue pair, permanently leaking them and preventing the adapter from being
safely unloaded?

[ ... ]

> @@ -3318,6 +3353,13 @@ qla2x00_process_vendor_specific(struct scsi_qla_host *vha, struct bsg_job *bsg_j
>  {
>  	struct fc_bsg_request *bsg_request = bsg_job->request;
>  
> +	if (bsg_job->request_len <
> +	    sizeof(struct fc_bsg_request) + sizeof(uint32_t)) {
> +		ql_log(ql_log_warn, vha, 0x7000,
> +		       "BSG request too small for vendor cmd.\n");
> +		return -EINVAL;
> +	}
> +

[Severity: Critical]
Does this central check mathematically overcalculate the required payload size
and artificially reject legacy tools?

The size of struct fc_bsg_request is 20 bytes due to the union, but the
vendor_cmd flexible array starts at offset 12. Wouldn't a 16-byte payload
safely cover vendor_cmd[0]? It appears enforcing a minimum of 24 bytes here
will break backwards compatibility for legitimate userspace tools sending
valid 16-byte requests.

[Severity: High]
This is a pre-existing issue, but while we are adding bounds checks, it looks
like qla84xx_mgmt_cmd() and qla24xx_iidma() might also need validation.

They cast the payload starting at offset 20 to structs like qla_bsg_a84_mgmt
or qla_port_param and access fields up to offset 39 without verifying if the
request length is sufficiently large. Could this lead to out-of-bounds heap
reads?

[Severity: High]
This is also a pre-existing issue, but looking further up the stack in the
generic transport layer, does fc_bsg_host_dispatch() in
drivers/scsi/scsi_transport_fc.c actually guarantee that the request length
covers the vendor_id field?

It increments cmdlen but accesses vendor_id immediately without verifying if
the user supplied at least 8 bytes, which seems to allow an out-of-bounds read
before the request even reaches the driver.

[ ... ]

> @@ -3461,8 +3503,11 @@ qla24xx_bsg_request(struct bsg_job *bsg_job)
>  	}
>  
>  	/* Disable port will bring down the chip, allow enable command */
> -	if (bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_MANAGE_HOST_PORT ||
> -	    bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS)
> +	if (bsg_request->msgcode == FC_BSG_HST_VENDOR &&
> +	    bsg_job->request_len >=
> +		sizeof(struct fc_bsg_request) + sizeof(uint32_t) &&
> +	    (bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_MANAGE_HOST_PORT ||
> +	     bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS))
>  		goto skip_chip_chk;

[Severity: Critical]
Similar to the check in qla2x00_process_vendor_specific(), does this fail to
bypass the chip checks for valid, tightly-packed 16-byte requests because it
inflates the required length to 24 bytes?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=85

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 02/88] scsi: qla2xxx: Add flash read/write interface for 29xx
  2026-07-07  5:53 ` [PATCH v3 02/88] scsi: qla2xxx: Add flash read/write interface for 29xx Nilesh Javali
@ 2026-07-07  8:24   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07  8:24 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Missing `isp_ops` hooks for QLA29xx flash operations, causing integer overflow and broken flash access.
--

--- Patch [2]: [PATCH v3 02/88] scsi: qla2xxx: Add flash read/write interface for 29xx ---
commit 2be5c3c775caf216f27f9159429f66a07c2ee89a
Author: Manish Rangankar <mrangankar@marvell.com>

scsi: qla2xxx: Add flash read/write interface for 29xx

This commit introduces new mailbox wrappers and helpers for flash read
and write operations on QLA29xx series adapters. It also aims to hook
these into the existing flash infrastructure via isp_ops.

> diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
> index b6c36a8a2d60b..6b9938d1a7501 100644
> --- a/drivers/scsi/qla2xxx/qla_sup.c
> +++ b/drivers/scsi/qla2xxx/qla_sup.c
> @@ -10,6 +10,539 @@
[ ... ]
> +int
> +qla29xx_write_optrom_data(struct scsi_qla_host *vha, uint16_t reg_code,
> +			 uint16_t opts, void *buf, uint32_t offset,
> +			 uint32_t length)
> +{
[ ... ]
> +void *
> +qla29xx_read_optrom_data(struct scsi_qla_host *vha, uint16_t reg_code,
> +			 uint16_t opts, void *buf, uint32_t offset,
> +			 uint32_t length)
> +{
[ ... ]
> diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
> index c91d2b8bd08e7..ebb97329e6eac 100644
> --- a/drivers/scsi/qla2xxx/qla_os.c
> +++ b/drivers/scsi/qla2xxx/qla_os.c
> @@ -3237,6 +3237,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
>  		ha->flash_data_off = ~0;
>  		ha->nvram_conf_off = ~0;
>  		ha->nvram_data_off = ~0;
> +		ha->flt_segment_length = QLA_SEGMENT_LENGTH;
>  	}

[Severity: High]
Are the newly introduced qla29xx_read_optrom_data() and
qla29xx_write_optrom_data() functions ever assigned to the qla29xx_isp_ops
structure?

If these new handlers are missing from ha->isp_ops->read_optrom and
write_optrom, won't sysfs or BSG flash operations route to the legacy
handlers instead?

Since ha->flash_data_off is explicitly initialized to ~0 (0xFFFFFFFF) for
the 29xx adapters here in qla2x00_probe_one(), using the legacy handlers
would cause an integer overflow when calculating the physical address.

This could route invalid memory addresses to the adapter and lead to
failed operations, timeouts, or firmware crashes.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=2

^ permalink raw reply	[flat|nested] 159+ messages in thread

* Re: [PATCH v3 11/88] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx
  2026-07-07  5:53 ` [PATCH v3 11/88] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx Nilesh Javali
@ 2026-07-07 10:27   ` sashiko-bot
  0 siblings, 0 replies; 159+ messages in thread
From: sashiko-bot @ 2026-07-07 10:27 UTC (permalink / raw)
  To: Nilesh Javali; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:

New issues:
- [High] Legacy non-MQ `.start_scsi` execution path builds 64-byte IOCB structures inside 128-byte ring slots on 29xx adapters, corrupting fields.
- [High] Task Management, Abort, and ELS LOGO commands are incorrectly formatted using legacy 64-byte builders on 29xx hardware.
- [High] Status continuation (STATUS_CONT_TYPE) payloads and PUREX/FPIN continuations are truncated to ~60 bytes on 29xx adapters.
- [High] `qla27xx_copy_multiple_pkt()` incorrectly advances the response queue ring pointer by 64 bytes instead of 128 bytes on 29xx hardware.
- [High] `qla2x00_status_entry()` parses the 128-byte 29xx extended status IOCB as a legacy 64-byte IOCB, reading garbage values for SCSI status and sense data.

Pre-existing issues:
- [High] Double free and cross-allocator mismatch of `crc_ctx` in `qla2x00_sp_free_dma()`.
- [High] Unprotected access to `ha->req_q_map` and its associated ring structures in `qla25xx_copy_mqueues` causes a Use-After-Free during concurrent queue teardown.
--

--- Patch [11]: [PATCH v3 11/88] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx ---
commit b89f951ad304cc6622ae64928f42f9fb462d5532
Author: Anil Gurumurthy <agurumurthy@marvell.com>

scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx

This patch updates the QLogic qla2xxx driver IO path to support 128-byte
extended IOCBs for 29xx-class adapters. It introduces structure definitions
and extended fast-path helpers to manage the new 128-byte ring stride, while
falling back to 64-byte structures for older hardware.

> diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
> index a7e3ec9bba47f..acb58daacf358 100644
> --- a/drivers/scsi/qla2xxx/qla_dbg.c
> +++ b/drivers/scsi/qla2xxx/qla_dbg.c
[ ... ]
> @@ -623,19 +626,19 @@ qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, __be32 **last_chain)
>  		q->chain_size = htonl(
>  		    sizeof(struct qla2xxx_mqueue_chain) +
>  		    sizeof(struct qla2xxx_mqueue_header) +
> -		    (req->length * sizeof(request_t)));
> +		    (req->length * req_entry_size));
>  		ptr += sizeof(struct qla2xxx_mqueue_chain);
>  
>  		/* Add header. */
>  		qh = ptr;
>  		qh->queue = htonl(TYPE_REQUEST_QUEUE);
>  		qh->number = htonl(que);
> -		qh->size = htonl(req->length * sizeof(request_t));
> +		qh->size = htonl(req->length * req_entry_size);
>  		ptr += sizeof(struct qla2xxx_mqueue_header);
>  
>  		/* Add data. */
> -		memcpy(ptr, req->ring, req->length * sizeof(request_t));
> -		ptr += req->length * sizeof(request_t);
> +		memcpy(ptr, req->ring, req->length * req_entry_size);
> +		ptr += req->length * req_entry_size;

[Severity: High]
This is a pre-existing issue, but is it possible for this unprotected memcpy 
to trigger a Use-After-Free during concurrent queue teardown?

Looking at qla25xx_copy_mqueues(), it iterates over ha->req_q_map and 
copies req->ring without holding ha->mq_lock. Concurrently, 
qla25xx_free_req_que() calls dma_free_coherent() on req->ring prior to 
acquiring ha->mq_lock to clear the map entry. If a firmware dump races with 
queue teardown, it might access already-freed DMA memory.

[ ... ]
> diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
> index 9038f67234448..13882a399fbc5 100644
> --- a/drivers/scsi/qla2xxx/qla_iocb.c
> +++ b/drivers/scsi/qla2xxx/qla_iocb.c
[ ... ]
> @@ -1620,13 +1640,13 @@ qla24xx_start_scsi(srb_t *sp)
>  	cmd->host_scribble = (unsigned char *)(unsigned long)handle;
>  	req->cnt -= req_cnt;
>  
> -	cmd_pkt = (struct cmd_type_7 *)req->ring_ptr;
> +	cmd_pkt = (struct cmd_type_7 *)qla_req_ring_slot(ha, req);
>  	cmd_pkt->handle = make_handle(req->id, handle);
>  
>  	/* Zero out remaining portion of packet. */
>  	/*    tagged queuing modifier -- default is TSK_SIMPLE (0). */
>  	clr_ptr = (uint32_t *)cmd_pkt + 2;
> -	memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
> +	memset(clr_ptr, 0, qla_req_entry_size(ha) - 8);
>  	cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
>  
>  	/* Set NPORT-ID and LUN number*/

[Severity: High]
Might this format a 64-byte cmd_type_7 layout into a 128-byte slot on 29xx 
hardware?

Because this legacy non-MQ path doesn't branch to the extended builder, it 
writes the 8-bit vp_index to offset 51 instead of the 16-bit vp_index at 
offset 48, and places DSDs at offset 56 instead of 80. This could cause 
the firmware to read empty data segments resulting in DMA faults.

[ ... ]
> @@ -2343,10 +2447,20 @@ __qla2x00_alloc_iocbs(struct qla_qpair *qpair, srb_t *sp)
>  		sp->handle = handle;
>  	}
>  
> -	/* Prep packet */
> +	/*
> +	 * Prep packet.  29xx posts into a 128-byte-strided ring via
> +	 * ring_ext_ptr; the first 8 bytes of struct request_ext overlay the
> +	 * common request_t header, so entry_count/handle writes are
> +	 * layout-compatible once we return the pkt as request_t *.
> +	 */
>  	req->cnt -= req_cnt;
> -	pkt = req->ring_ptr;
> -	memset(pkt, 0, REQUEST_ENTRY_SIZE);
> +	if (IS_QLA29XX(ha)) {
> +		pkt = (request_t *)req->ring_ext_ptr;
> +		memset(pkt, 0, REQUEST_ENTRY_SIZE_EXT);
> +	} else {

[Severity: High]
Does returning a 128-byte slot here for all commands lead to memory 
corruption in the legacy dispatch paths?

Functions like qla2x00_start_sp() dispatch Task Management, Abort, and ELS 
LOGO commands to legacy 24xx builders (e.g., qla24xx_els_logo_iocb) which 
cast this 128-byte slot to the 64-byte structs. This causes critical fields 
like vp_index to be written as 8-bit values at incorrect offsets, which 
could misroute the commands to the physical port instead of the intended 
NPIV virtual port.

[ ... ]
> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> index c47c38e099ff8..e95fb0e59f387 100644
> --- a/drivers/scsi/qla2xxx/qla_isr.c
> +++ b/drivers/scsi/qla2xxx/qla_isr.c
[ ... ]
> @@ -287,13 +281,7 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
>  				break;
>  			}
>  
> -			rsp_q->ring_index++;
> -			if (rsp_q->ring_index == rsp_q->length) {
> -				rsp_q->ring_index = 0;
> -				rsp_q->ring_ptr = rsp_q->ring;
> -			} else {
> -				rsp_q->ring_ptr++;
> -			}
> +			qla_rsp_ring_advance(rsp_q);
>  			no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
>  			    sizeof(new_pkt->data) : pending_bytes;

[Severity: High]
Could this truncate the extended PUREX payload continuations?

The 29xx hardware places up to ~120 bytes of payload per 128-byte extended 
IOCB slot. However, bounding the copy length against sizeof(new_pkt->data) 
uses the legacy 64-byte limit. Because qla_rsp_ring_advance() steps by 128 
bytes, this skips over and permanently drops the remaining ~70 bytes of 
payload in each extended continuation IOCB. This same truncation issue 
appears in qla27xx_copy_fpin_pkt().

[ ... ]
> @@ -4003,13 +3980,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
>  		pkt = (struct sts_entry_24xx *)rsp->ring_ptr;
>  		cur_ring_index = rsp->ring_index;
>  
> -		rsp->ring_index++;
> -		if (rsp->ring_index == rsp->length) {
> -			rsp->ring_index = 0;
> -			rsp->ring_ptr = rsp->ring;
> -		} else {
> -			rsp->ring_ptr++;
> -		}
> +		qla_rsp_ring_advance(rsp);
>  
>  		if (pkt->entry_status != 0) {
>  			if (qla2x00_error_entry(vha, rsp, (sts_entry_t *) pkt))

[Severity: High]
Are we parsing the 128-byte 29xx extended status IOCB as a legacy 64-byte 
IOCB here?

For standard SCSI completions on 29xx hardware, the firmware generates 
sts_entry_24xx_ext where fields like scsi_status are at offset 48. Because 
qla2x00_status_entry() was not updated with an IS_QLA29XX() branch, it 
casts to the 64-byte sts_entry_24xx and reads scsi_status from offset 22. 
On 29xx, this offset holds the wr_sa_index, causing random SA index values 
to be interpreted as SCSI errors.

Additionally, did we miss updating qla27xx_copy_multiple_pkt() to use the 
new stride-aware qla_rsp_ring_advance() macro? It appears to still use 
rsp_q->ring_ptr++ which only increments by 64 bytes on 29xx hardware, 
desynchronizing the consumer pointer.

[ ... ]
> diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
> index 948242f0088e7..146701445485f 100644
> --- a/drivers/scsi/qla2xxx/qla_os.c
> +++ b/drivers/scsi/qla2xxx/qla_os.c
[ ... ]
> @@ -2693,6 +2697,7 @@ static struct isp_operations qla29xx_isp_ops = {
>  	.read_optrom_region	= qla29xx_read_optrom_data,
>  	.write_optrom_region	= qla29xx_write_optrom_data,
>  	.get_flash_version	= qla24xx_get_flash_version,
> +	.start_scsi		= qla24xx_dif_start_scsi,
>  	.start_scsi_mq		= qla2xxx_dif_start_scsi_mq,
>  	.abort_isp		= qla2x00_abort_isp,
>  	.iospace_config		= qla83xx_iospace_config,

[Severity: High]
Will assigning the legacy qla24xx_dif_start_scsi handler here corrupt 
the extended 29xx hardware rings?

If multiqueue is disabled or the block layer invokes this non-MQ hook, it 
formats 64-byte layouts (cmd_type_7, cmd_type_crc_2) into the 128-byte 
slots fetched via qla_req_ring_slot(). This writes the 8-bit vp_index to 
offset 51, corrupting the 16-bit vp_index and ctrl_flags_2 expected by 
29xx hardware.

[Severity: High]
This is a pre-existing issue, but can qla2x00_sp_free_dma() trigger a double 
free of the crc_ctx buffer?

When cleaning up a T10-DIF protected SCSI command, it frees the context:

qla2x00_sp_free_dma() {
    ...
    if (sp->u.scmd.crc_ctx) {
        dma_pool_free(ha->dl_dma_pool, sp->u.scmd.crc_ctx,
                      sp->u.scmd.crc_ctx->crc_ctx_dma);
    }
    ...
}

The pointer sp->u.scmd.crc_ctx is not set to NULL after being freed to the 
DMA pool. A subsequent check for the same pointer evaluates to true and 
attempts to free it again via mempool_free(), corrupting the ctx_mempool 
slab pool.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707055435.2680300-1-njavali@marvell.com?part=11

^ permalink raw reply	[flat|nested] 159+ messages in thread

end of thread, other threads:[~2026-07-07 10:27 UTC | newest]

Thread overview: 159+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-07  5:53 [PATCH v3 00/88] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
2026-07-07  5:53 ` [PATCH v3 01/88] scsi: qla2xxx: Add 29xx series PCI device ID support Nilesh Javali
2026-07-07  6:21   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 02/88] scsi: qla2xxx: Add flash read/write interface for 29xx Nilesh Javali
2026-07-07  8:24   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 03/88] scsi: qla2xxx: Add NVRAM config support for 29xx adapters Nilesh Javali
2026-07-07  5:53 ` [PATCH v3 04/88] scsi: qla2xxx: Add 29xx support in queue initialisation path Nilesh Javali
2026-07-07  6:31   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 05/88] scsi: qla2xxx: Add FC operational firmware load for 29xx Nilesh Javali
2026-07-07  6:17   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 06/88] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path Nilesh Javali
2026-07-07  6:13   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 07/88] scsi: qla2xxx: Add flash block read/write BSG support for 29xx Nilesh Javali
2026-07-07  6:21   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 08/88] scsi: qla2xxx: Add BSG MPI firmware load/dump " Nilesh Javali
2026-07-07  6:13   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 09/88] scsi: qla2xxx: Add 128-byte IOCB definitions " Nilesh Javali
2026-07-07  6:14   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 10/88] scsi: qla2xxx: Add extended status continuation and marker IOCBs Nilesh Javali
2026-07-07  6:10   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 11/88] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx Nilesh Javali
2026-07-07 10:27   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 12/88] scsi: qla2xxx: Skip image-set-valid attribute " Nilesh Javali
2026-07-07  6:12   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 13/88] scsi: qla2xxx: Skip unsupported sysfs attributes " Nilesh Javali
2026-07-07  5:53 ` [PATCH v3 14/88] scsi: qla2xxx: Enable get_fw_version mailbox " Nilesh Javali
2026-07-07  6:12   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 15/88] scsi: qla2xxx: Extend execute_fw mailbox to include 29xx Nilesh Javali
2026-07-07  6:23   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 16/88] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx Nilesh Javali
2026-07-07  6:15   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 17/88] scsi: qla2xxx: Enable init_firmware " Nilesh Javali
2026-07-07  5:53 ` [PATCH v3 18/88] scsi: qla2xxx: Enable get_firmware_state " Nilesh Javali
2026-07-07  6:15   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 19/88] scsi: qla2xxx: Enable serdes, resource count and FCE trace " Nilesh Javali
2026-07-07  6:36   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 20/88] scsi: qla2xxx: Enable set_els_cmds and echo_test " Nilesh Javali
2026-07-07  5:53 ` [PATCH v3 21/88] scsi: qla2xxx: Add support for QLA29XX in data rate functions Nilesh Javali
2026-07-07  5:53 ` [PATCH v3 22/88] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx Nilesh Javali
2026-07-07  6:27   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 23/88] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs Nilesh Javali
2026-07-07  6:26   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 24/88] scsi: qla2xxx: Add support for QLA29XX in memory allocation Nilesh Javali
2026-07-07  6:22   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 25/88] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters Nilesh Javali
2026-07-07  6:31   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 26/88] scsi: qla2xxx: Update handling of status entries for 29xx series Nilesh Javali
2026-07-07  5:53 ` [PATCH v3 27/88] scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling " Nilesh Javali
2026-07-07  6:40   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 28/88] scsi: qla2xxx: Enhance purex_entry " Nilesh Javali
2026-07-07  6:35   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 29/88] scsi: qla2xxx: Update handling of ELS IOCBs " Nilesh Javali
2026-07-07  6:35   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 30/88] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx Nilesh Javali
2026-07-07  6:31   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 31/88] scsi: qla2xxx: Add 29xx extended logio IOCB support Nilesh Javali
2026-07-07  5:53 ` [PATCH v3 32/88] scsi: qla2xxx: Enhance task management IOCB handling for 29xx series Nilesh Javali
2026-07-07  5:53 ` [PATCH v3 33/88] scsi: qla2xxx: Add abort command " Nilesh Javali
2026-07-07  5:53 ` [PATCH v3 34/88] scsi: qla2xxx: Enhance ABTS processing " Nilesh Javali
2026-07-07  6:46   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 35/88] scsi: qla2xxx: Update VP control IOCB handling " Nilesh Javali
2026-07-07  5:53 ` [PATCH v3 36/88] scsi: qla2xxx: Add build-time size check for VP config IOCB layout Nilesh Javali
2026-07-07  5:53 ` [PATCH v3 37/88] scsi: qla2xxx: Add size check for extended VP report ID entry Nilesh Javali
2026-07-07  6:46   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 38/88] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series Nilesh Javali
2026-07-07  6:45   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 39/88] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support Nilesh Javali
2026-07-07  6:48   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 40/88] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking Nilesh Javali
2026-07-07  6:50   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 41/88] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors Nilesh Javali
2026-07-07  6:49   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 42/88] scsi: qla2xxx: Fix endianness annotations in vp_rpt_id_entry structures Nilesh Javali
2026-07-07  5:53 ` [PATCH v3 43/88] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats Nilesh Javali
2026-07-07  6:49   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 44/88] scsi: qla2xxx: Add 64G/128G port speed setting support Nilesh Javali
2026-07-07  6:52   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 45/88] scsi: qla2xxx: Fix 64G link speed reporting in get_data_rate Nilesh Javali
2026-07-07  5:53 ` [PATCH v3 46/88] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check Nilesh Javali
2026-07-07  7:09   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 47/88] scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters Nilesh Javali
2026-07-07  6:53   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 48/88] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size Nilesh Javali
2026-07-07  6:55   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 49/88] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config() Nilesh Javali
2026-07-07  6:54   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 50/88] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition() Nilesh Javali
2026-07-07  7:03   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 51/88] scsi: qla2xxx: Initialize NVMe abort_work once at submission Nilesh Javali
2026-07-07  7:00   ` sashiko-bot
2026-07-07  5:53 ` [PATCH v3 52/88] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject Nilesh Javali
2026-07-07  7:07   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 53/88] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak Nilesh Javali
2026-07-07  7:06   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 54/88] scsi: qla2xxx: Fix BSG job leak on validate flash image error path Nilesh Javali
2026-07-07  7:03   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 55/88] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions() Nilesh Javali
2026-07-07  7:05   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 56/88] scsi: qla2xxx: Clamp MSI-X derived queue counts to avoid truncation Nilesh Javali
2026-07-07  7:06   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 57/88] scsi: qla2xxx: Improve firmware dump data capture Nilesh Javali
2026-07-07  7:18   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 58/88] scsi: qla2xxx: Serialize flash version read in reset handler Nilesh Javali
2026-07-07  5:54 ` [PATCH v3 59/88] scsi: qla2xxx: Fix use-after-free of qpair work on queue teardown Nilesh Javali
2026-07-07  7:08   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 60/88] scsi: qla2xxx: Clarify MPI optrom address/length units Nilesh Javali
2026-07-07  5:54 ` [PATCH v3 61/88] scsi: qla2xxx: Fix cs84xx use-after-free on host teardown Nilesh Javali
2026-07-07  7:11   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 62/88] scsi: qla2xxx: Don't query firmware state while chip is down Nilesh Javali
2026-07-07  7:03   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 63/88] scsi: qla2xxx: Zero mailbox struct in qla2x00_get_firmware_state() Nilesh Javali
2026-07-07  7:18   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 64/88] scsi: qla2xxx: Fix FCE trace enable parsing in debugfs Nilesh Javali
2026-07-07  7:11   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 65/88] scsi: qla2xxx: Fix FCE trace use-after-free during firmware dump Nilesh Javali
2026-07-07  7:25   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 66/88] scsi: qla2xxx: Use memset_io() to clear QLAFX00 request ring slot Nilesh Javali
2026-07-07  7:26   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 67/88] scsi: qla2xxx: Null out freed pointers in qla2x00_mem_alloc() error path Nilesh Javali
2026-07-07  7:25   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 68/88] scsi: qla2xxx: Fix response queue over-consumption in __qla_consume_iocb() Nilesh Javali
2026-07-07  5:54 ` [PATCH v3 69/88] scsi: qla2xxx: Fix soft lockup polling continuation IOCB signature Nilesh Javali
2026-07-07  7:19   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 70/88] scsi: qla2xxx: Bound rsp_info_len to avoid OOB sense-data read Nilesh Javali
2026-07-07  7:24   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 71/88] scsi: qla2xxx: Avoid req_q_map double-read in qla2x00_error_entry() Nilesh Javali
2026-07-07  7:21   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 72/88] scsi: qla2xxx: Quiesce response IRQ before freeing request queue Nilesh Javali
2026-07-07  7:22   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 73/88] scsi: qla2xxx: Reject non-SCSI SRB on status IOCB fast path Nilesh Javali
2026-07-07  5:54 ` [PATCH v3 74/88] scsi: qla2xxx: Clamp max_npiv_vports to VP_CTRL bitmap capacity Nilesh Javali
2026-07-07  7:34   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 75/88] scsi: qla2xxx: Avoid double completion in async IOCB timeout Nilesh Javali
2026-07-07  7:37   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 76/88] scsi: qla2xxx: Skip vport under deletion in report ID acquisition Nilesh Javali
2026-07-07  7:27   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 77/88] scsi: qla2xxx: Drop vport reference under lock " Nilesh Javali
2026-07-07  7:33   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 78/88] scsi: qla2xxx: Hold vport_slock for host map update " Nilesh Javali
2026-07-07  7:28   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 79/88] scsi: qla2xxx: Fix NVMe abort reference leak on repeated abort Nilesh Javali
2026-07-07  7:27   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 80/88] scsi: qla2xxx: Skip NVMe LS reject IOCB when FW not started Nilesh Javali
2026-07-07  7:38   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 81/88] scsi: qla2xxx: Unlink NVMe unsol ctx before freeing on LS reject error Nilesh Javali
2026-07-07  7:34   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 82/88] scsi: qla2xxx: Serialize NVMe unsol ctx list with a per-fcport lock Nilesh Javali
2026-07-07  7:38   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 83/88] scsi: qla2xxx: Use coherent DMA buffer for D_Port diagnostics Nilesh Javali
2026-07-07  7:34   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 84/88] scsi: qla2xxx: Zero-init bsg stack buffers to avoid info leak Nilesh Javali
2026-07-07  7:37   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 85/88] scsi: qla2xxx: Validate BSG request_len before reading vendor_cmd[] Nilesh Javali
2026-07-07  7:46   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 86/88] scsi: qla2xxx: Zero SFP DMA buffer in FRU/I2C bsg handlers Nilesh Javali
2026-07-07  7:38   ` sashiko-bot
2026-07-07  5:54 ` [PATCH v3 87/88] scsi: qla2xxx: Bound i2c->length in I2C " Nilesh Javali
2026-07-07  5:54 ` [PATCH v3 88/88] scsi: qla2xxx: Update version to 12.00.00.2607b1 Nilesh Javali
2026-07-07  7:35   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox