* [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support
@ 2026-07-17 15:28 Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 01/56] scsi: qla2xxx: Add 29xx series PCI device ID support Nilesh Javali
` (55 more replies)
0 siblings, 56 replies; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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.
Patch 56: bump the driver version to 12.00.00.2607b1.
The series applies on top of Linux 7.2-rc1.
Changes in v5:
- No functional change from v4. Folded the static-analysis and
kernel test robot cleanups into the feature commits they touch, so
every commit stays self-contained and bisectable:
- removed set-but-unused variables (chunk_count in the flash
read/write interface, vha/ha in the 128-byte IO path);
- fixed kernel-doc parameter lists (added @ha and @data_sz, dropped
the excess @bundling description);
- corrected the control_flags endianness annotation to
cpu_to_le16(CF_DATA_SEG_DESCR_ENABLE);
- relocated a misplaced out_29xx label flagged by sparse; and
- fixed two checkpatch indentation warnings on ql_dbg() calls.
- Added Reported-by: kernel test robot <lkp@intel.com> and the
matching Closes: links to the four affected commits.
Changes in v4:
- Folded the follow-up robustness and bug-fix commits that were posted
as separate patches in v3 into the feature commits they correct,
resolving their Fixes: tags by squashing, so every commit in the
series is self-contained and bisectable with no "fix the previous
patch" commits. The posting is now 56 patches (down from 88).
- Dropped fixes to pre-existing driver code that are independent of the
29xx series (e.g. the firmware-dump data-capture change) from this
posting; they will be submitted separately.
- Rebased the series onto Linux 7.2-rc1.
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, 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.
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 (43):
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: Update version to 12.00.00.2607b1
drivers/scsi/qla2xxx/qla_attr.c | 57 +-
drivers/scsi/qla2xxx/qla_bsg.c | 589 +++++++++++-
drivers/scsi/qla2xxx/qla_bsg.h | 34 +
drivers/scsi/qla2xxx/qla_dbg.c | 31 +-
drivers/scsi/qla2xxx/qla_def.h | 143 ++-
drivers/scsi/qla2xxx/qla_dfs.c | 4 +-
drivers/scsi/qla2xxx/qla_edif.c | 101 +-
drivers/scsi/qla2xxx/qla_fw.h | 140 ++-
drivers/scsi/qla2xxx/qla_fw29.h | 830 ++++++++++++++++
drivers/scsi/qla2xxx/qla_gbl.h | 42 +-
drivers/scsi/qla2xxx/qla_gs.c | 176 +++-
drivers/scsi/qla2xxx/qla_init.c | 735 +++++++++++++-
drivers/scsi/qla2xxx/qla_inline.h | 282 +++++-
drivers/scsi/qla2xxx/qla_iocb.c | 1419 ++++++++++++++++++++++------
drivers/scsi/qla2xxx/qla_isr.c | 676 ++++++++-----
drivers/scsi/qla2xxx/qla_mbx.c | 476 ++++++++--
drivers/scsi/qla2xxx/qla_mid.c | 74 +-
drivers/scsi/qla2xxx/qla_nvme.c | 288 ++++--
drivers/scsi/qla2xxx/qla_nvme.h | 4 +-
drivers/scsi/qla2xxx/qla_nx.c | 2 +-
drivers/scsi/qla2xxx/qla_os.c | 273 +++++-
drivers/scsi/qla2xxx/qla_sup.c | 781 ++++++++++++++-
drivers/scsi/qla2xxx/qla_target.c | 34 +-
drivers/scsi/qla2xxx/qla_version.h | 10 +-
24 files changed, 6153 insertions(+), 1048 deletions(-)
create mode 100644 drivers/scsi/qla2xxx/qla_fw29.h
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
--
2.47.3
^ permalink raw reply [flat|nested] 100+ messages in thread
* [PATCH v5 01/56] scsi: qla2xxx: Add 29xx series PCI device ID support
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:36 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 02/56] scsi: qla2xxx: Add flash read/write interface for 29xx Nilesh Javali
` (54 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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(®->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] 100+ messages in thread
* [PATCH v5 02/56] scsi: qla2xxx: Add flash read/write interface for 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 01/56] scsi: qla2xxx: Add 29xx series PCI device ID support Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:05 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 03/56] scsi: qla2xxx: Add NVRAM config support for 29xx adapters Nilesh Javali
` (53 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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.
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202607141520.k9T31Dpp-lkp@intel.com/
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 | 660 ++++++++++++++++++++++++++++++++-
6 files changed, 898 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..ea5e4901cc70 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -10,6 +10,537 @@
#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;
+
+ memset(®ion, 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, ®ion);
+ 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++;
+ 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;
+ int rval;
+
+ memset(®ion, 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, ®ion);
+ 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++;
+ 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 +1648,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 +1671,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 +3987,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 +4091,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, ®ion);
+ 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 +4124,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 +4132,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 +4142,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 +4153,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] 100+ messages in thread
* [PATCH v5 03/56] scsi: qla2xxx: Add NVRAM config support for 29xx adapters
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 01/56] scsi: qla2xxx: Add 29xx series PCI device ID support Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 02/56] scsi: qla2xxx: Add flash read/write interface for 29xx Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 04/56] scsi: qla2xxx: Add 29xx support in queue initialisation path Nilesh Javali
` (52 subsequent siblings)
55 siblings, 0 replies; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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.
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202607141733.w1IF5m43-lkp@intel.com/
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..c6e928f11758 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,35 +9306,98 @@ 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");
+ } 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);
+out_29xx:
dptr = (__force __le32 *)nv;
for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++, dptr++)
chksum += le32_to_cpu(*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] 100+ messages in thread
* [PATCH v5 04/56] scsi: qla2xxx: Add 29xx support in queue initialisation path
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (2 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 03/56] scsi: qla2xxx: Add NVRAM config support for 29xx adapters Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:09 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 05/56] scsi: qla2xxx: Add FC operational firmware load for 29xx Nilesh Javali
` (51 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 05/56] scsi: qla2xxx: Add FC operational firmware load for 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (3 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 04/56] scsi: qla2xxx: Add 29xx support in queue initialisation path Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:53 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 06/56] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path Nilesh Javali
` (50 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 | 546 +++++++++++++++++++++++++++++++-
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, 643 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 c6e928f11758..5b0dc9b0a679 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,539 @@ 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;
+
+ for (i = 0; i < (size >> 2); i++)
+ dcode[i] =
+ swab32((__force u32)fwcode[i]);
+
+ 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, fw_dump_tmplt,
+ min_t(uint32_t, ha->fw_dump_tmplt_len, 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 ea5e4901cc70..ee7562bf8d38 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -1913,7 +1913,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;
@@ -3513,7 +3513,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] 100+ messages in thread
* [PATCH v5 06/56] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (4 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 05/56] scsi: qla2xxx: Add FC operational firmware load for 29xx Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:58 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 07/56] scsi: qla2xxx: Add flash block read/write BSG support for 29xx Nilesh Javali
` (49 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 07/56] scsi: qla2xxx: Add flash block read/write BSG support for 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (5 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 06/56] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:02 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 08/56] scsi: qla2xxx: Add BSG MPI firmware load/dump " Nilesh Javali
` (48 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 | 350 +++++++++++++++++++++++++++++----
drivers/scsi/qla2xxx/qla_bsg.h | 16 ++
drivers/scsi/qla2xxx/qla_def.h | 7 +
drivers/scsi/qla2xxx/qla_gbl.h | 2 +
drivers/scsi/qla2xxx/qla_os.c | 24 ++-
drivers/scsi/qla2xxx/qla_sup.c | 2 +-
6 files changed, 359 insertions(+), 42 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 5e910b5ca670..92a1af81c057 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,234 @@ qla2x00_update_optrom(struct bsg_job *bsg_job)
return rval;
}
+/**
+ * qla29xx_validate_flash_region - Bounds-check a region-relative flash access.
+ * @vha: Pointer to SCSI QLogic host structure.
+ * @region_code: Flash region the access is confined to.
+ * @offset: Region-relative byte offset of the access.
+ * @len: Length in bytes of the access.
+ *
+ * The block read/write BSG commands operate on a region-relative offset.
+ * Validate that the requested offset and length lie entirely within the
+ * region's actual size (from the FLT) so a request cannot cross into an
+ * adjacent flash region.
+ *
+ * Returns 0 when the access is within the region, -EINVAL otherwise.
+ */
+static int
+qla29xx_validate_flash_region(scsi_qla_host_t *vha, uint32_t region_code,
+ uint32_t offset, uint32_t len)
+{
+ struct qla_flt_region_data region;
+ uint32_t img_len;
+
+ if (qla29xx_get_flash_region(vha, region_code, ®ion) != QLA_SUCCESS)
+ return -EINVAL;
+
+ img_len = le32_to_cpu(region.image_length);
+ /* subtraction form avoids offset + len overflow */
+ if (offset > img_len || len > img_len - offset)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * 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;
+ }
+
+ if (qla29xx_validate_flash_region(vha, brcmd->region,
+ brcmd->region_offset,
+ bsg_job->reply_payload.payload_len)) {
+ ql_log(ql_log_warn, vha, 0x706a,
+ "%s: region 0x%x offset 0x%x len 0x%x out of region bounds\n",
+ __func__, brcmd->region, brcmd->region_offset,
+ bsg_job->reply_payload.payload_len);
+ 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 0;
+}
+
+/**
+ * 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;
+ }
+
+ if (qla29xx_validate_flash_region(vha, bwcmd->region,
+ bwcmd->region_offset,
+ bsg_job->request_payload.payload_len)) {
+ ql_log(ql_log_warn, vha, 0x706b,
+ "%s: region 0x%x offset 0x%x len 0x%x out of region bounds\n",
+ __func__, bwcmd->region, bwcmd->region_offset,
+ bsg_job->request_payload.payload_len);
+ 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 +3273,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_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index ca5a80b9c3d2..120a7b1bb217 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -697,6 +697,8 @@ 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);
+int qla29xx_get_flash_region(struct scsi_qla_host *vha, uint32_t code,
+ struct qla_flt_region_data *region);
/*
* Global Function Prototypes in qla_dbg.c source file.
*/
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,
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
index ee7562bf8d38..c30ec9cab7bb 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -208,7 +208,7 @@ qla29xx_get_fdt_info(scsi_qla_host_t *vha)
*
* Returns QLA_SUCCESS on success or QLA_FUNCTION_FAILED on failure.
*/
-static int
+int
qla29xx_get_flash_region(struct scsi_qla_host *vha, uint32_t code,
struct qla_flt_region_data *region)
{
--
2.47.3
^ permalink raw reply related [flat|nested] 100+ messages in thread
* [PATCH v5 08/56] scsi: qla2xxx: Add BSG MPI firmware load/dump for 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (6 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 07/56] scsi: qla2xxx: Add flash block read/write BSG support for 29xx Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:51 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 09/56] scsi: qla2xxx: Add 128-byte IOCB definitions " Nilesh Javali
` (47 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 | 203 +++++++++++++++++++++++++++++++++
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, 393 insertions(+)
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 92a1af81c057..5dd7e5e969ab 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -1820,6 +1820,203 @@ 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;
+
+ if (!IS_QLA29XX(ha)) {
+ ql_log(ql_log_warn, vha, 0x706c,
+ "%s: MPI dump not supported on this adapter.\n",
+ __func__);
+ 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;
+
+ if (!IS_QLA29XX(ha)) {
+ ql_log(ql_log_warn, vha, 0x706d,
+ "%s: MPI load not supported on this adapter.\n",
+ __func__);
+ 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)
{
@@ -3279,6 +3476,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 120a7b1bb217..106f95a4e7f0 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -699,6 +699,11 @@ extern int qla29xx_write_optrom_data(struct scsi_qla_host *vha,
uint32_t length);
int qla29xx_get_flash_region(struct scsi_qla_host *vha, uint32_t code,
struct qla_flt_region_data *region);
+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 c30ec9cab7bb..3b0a0cfe361d 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -541,6 +541,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;
+ uint32_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] 100+ messages in thread
* [PATCH v5 09/56] scsi: qla2xxx: Add 128-byte IOCB definitions for 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (7 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 08/56] scsi: qla2xxx: Add BSG MPI firmware load/dump " Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:49 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 10/56] scsi: qla2xxx: Add extended status continuation and marker IOCBs Nilesh Javali
` (46 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 10/56] scsi: qla2xxx: Add extended status continuation and marker IOCBs
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (8 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 09/56] scsi: qla2xxx: Add 128-byte IOCB definitions " Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:42 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 11/56] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx Nilesh Javali
` (45 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 11/56] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (9 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 10/56] scsi: qla2xxx: Add extended status continuation and marker IOCBs Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:13 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 12/56] scsi: qla2xxx: Skip image-set-valid attribute " Nilesh Javali
` (44 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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.
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202607141827.R9mpeMZx-lkp@intel.com/
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 | 29 +-
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 | 1116 ++++++++++++++++++++++++-----
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, 1497 insertions(+), 354 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..7220617c2568 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;
@@ -4488,7 +4512,8 @@ struct qla_hw_data {
#define IS_ATIO_MSIX_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
IS_QLA28XX(ha))
#define IS_TGT_MODE_CAPABLE(ha) (ha->tgt.atio_q_length)
-#define IS_SHADOW_REG_CAPABLE(ha) (IS_QLA27XX(ha) || IS_QLA28XX(ha))
+#define IS_SHADOW_REG_CAPABLE(ha) (IS_QLA27XX(ha) || IS_QLA28XX(ha) || \
+ IS_QLA29XX(ha))
#define IS_DPORT_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
IS_QLA28XX(ha))
#define IS_FAWWN_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
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 106f95a4e7f0..91900a601a97 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 5b0dc9b0a679..1b011aea432f 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 47fbd830ff16..d5178e0ace22 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 dbe3cd4e274c..aab7905c0d6c 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
@@ -115,25 +128,21 @@ qla2x00_prep_cont_type0_iocb(struct scsi_qla_host *vha)
/**
* qla2x00_prep_cont_type1_iocb() - Initialize a Continuation Type 1 IOCB.
* @vha: HA context
+ * @ha: pointer to adapter hardware data
* @req: request queue
*
* 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 +293,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 +474,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 +519,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 +767,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,46 +1641,84 @@ 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->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->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));
+ /* Load SCSI command packet. */
+ 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));
- /* Set total data segment count. */
- cmd_pkt->entry_count = (uint8_t)req_cnt;
+ /* Build IOCB segments */
+ qla29xx_build_scsi_iocbs(sp, cmd_pkt_ext, tot_dsds, req);
+
+ /* Set total data segment count. */
+ 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->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;
+
+ /* 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->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd));
+
+ /* Build IOCB segments */
+ qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds, req);
+
+ /* 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;
@@ -1798,7 +1857,20 @@ qla24xx_dif_start_scsi(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;
@@ -1829,17 +1901,24 @@ 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);
- 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));
@@ -1848,10 +1927,22 @@ qla24xx_dif_start_scsi(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;
/* Specify response queue number where completion should happen */
@@ -1860,12 +1951,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 +2040,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 +2083,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 +2168,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 +2318,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 +2362,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 +2399,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 +2483,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(®->isp25mq.req_q_out);
else if (IS_P3P_TYPE(ha))
cnt = rd_reg_dword(reg->isp82.req_q_out);
@@ -2343,10 +2523,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);
@@ -3269,7 +3459,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++;
@@ -3295,7 +3485,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;
@@ -3320,16 +3509,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++;
}
@@ -3340,16 +3542,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++;
}
@@ -3903,16 +4111,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;
}
}
@@ -4098,7 +4321,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++;
@@ -4120,7 +4344,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++;
@@ -4152,6 +4377,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,
@@ -4289,8 +4529,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;
@@ -4394,23 +4641,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));
@@ -4445,14 +4713,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;
@@ -4460,8 +4722,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);
@@ -4487,3 +4752,492 @@ 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;
+ 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;
+ }
+
+ /* 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 |= cpu_to_le16(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
+ *
+ * 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 = ®->isp25mq.req_q_in;
req->req_q_out = ®->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 = ®->isp25mq.rsp_q_in;
rsp->rsp_q_out = ®->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] 100+ messages in thread
* [PATCH v5 12/56] scsi: qla2xxx: Skip image-set-valid attribute for 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (10 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 11/56] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 13/56] scsi: qla2xxx: Skip unsupported sysfs attributes " Nilesh Javali
` (43 subsequent siblings)
55 siblings, 0 replies; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 5dd7e5e969ab..46eed5df7eef 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -3018,11 +3018,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] 100+ messages in thread
* [PATCH v5 13/56] scsi: qla2xxx: Skip unsupported sysfs attributes for 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (11 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 12/56] scsi: qla2xxx: Skip image-set-valid attribute " Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 14/56] scsi: qla2xxx: Enable get_fw_version mailbox " Nilesh Javali
` (42 subsequent siblings)
55 siblings, 0 replies; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 14/56] scsi: qla2xxx: Enable get_fw_version mailbox for 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (12 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 13/56] scsi: qla2xxx: Skip unsupported sysfs attributes " Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:55 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 15/56] scsi: qla2xxx: Extend execute_fw mailbox to include 29xx Nilesh Javali
` (41 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 15/56] scsi: qla2xxx: Extend execute_fw mailbox to include 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (13 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 14/56] scsi: qla2xxx: Enable get_fw_version mailbox " Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 16/56] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx Nilesh Javali
` (40 subsequent siblings)
55 siblings, 0 replies; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 7220617c2568..0fc858ab4e8c 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 */
@@ -5582,9 +5583,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] 100+ messages in thread
* [PATCH v5 16/56] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (14 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 15/56] scsi: qla2xxx: Extend execute_fw mailbox to include 29xx Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:00 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 17/56] scsi: qla2xxx: Enable init_firmware " Nilesh Javali
` (39 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 17/56] scsi: qla2xxx: Enable init_firmware mailbox for 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (15 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 16/56] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 18/56] scsi: qla2xxx: Enable get_firmware_state " Nilesh Javali
` (38 subsequent siblings)
55 siblings, 0 replies; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 18/56] scsi: qla2xxx: Enable get_firmware_state for 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (16 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 17/56] scsi: qla2xxx: Enable init_firmware " Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 19/56] scsi: qla2xxx: Enable serdes, resource count and FCE trace " Nilesh Javali
` (37 subsequent siblings)
55 siblings, 0 replies; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 19/56] scsi: qla2xxx: Enable serdes, resource count and FCE trace for 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (17 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 18/56] scsi: qla2xxx: Enable get_firmware_state " Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:30 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 20/56] scsi: qla2xxx: Enable set_els_cmds and echo_test " Nilesh Javali
` (36 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 1b011aea432f..545066f67e02 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] 100+ messages in thread
* [PATCH v5 20/56] scsi: qla2xxx: Enable set_els_cmds and echo_test for 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (18 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 19/56] scsi: qla2xxx: Enable serdes, resource count and FCE trace " Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:56 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 21/56] scsi: qla2xxx: Add support for QLA29XX in data rate functions Nilesh Javali
` (35 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 21/56] scsi: qla2xxx: Add support for QLA29XX in data rate functions
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (19 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 20/56] scsi: qla2xxx: Enable set_els_cmds and echo_test " Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 22/56] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx Nilesh Javali
` (34 subsequent siblings)
55 siblings, 0 replies; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 22/56] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (20 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 21/56] scsi: qla2xxx: Add support for QLA29XX in data rate functions Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:01 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 23/56] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs Nilesh Javali
` (33 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 23/56] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (21 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 22/56] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:03 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 24/56] scsi: qla2xxx: Add support for QLA29XX in memory allocation Nilesh Javali
` (32 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 aab7905c0d6c..1ca50fc200d6 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -2466,10 +2466,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;
@@ -2530,13 +2529,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] 100+ messages in thread
* [PATCH v5 24/56] scsi: qla2xxx: Add support for QLA29XX in memory allocation
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (22 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 23/56] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:20 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 25/56] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters Nilesh Javali
` (31 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 25/56] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (23 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 24/56] scsi: qla2xxx: Add support for QLA29XX in memory allocation Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:11 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 26/56] scsi: qla2xxx: Update handling of status entries for 29xx series Nilesh Javali
` (30 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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).
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202607142020.gqDaApes-lkp@intel.com/
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 | 118 ++++++++++++++++++------------
drivers/scsi/qla2xxx/qla_os.c | 1 +
3 files changed, 87 insertions(+), 48 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index d5178e0ace22..ff0fd0bff6d0 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..0d58dc470e87 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);
@@ -3678,11 +3697,12 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
* qla2x00_status_cont_entry() - Process a Status Continuations entry.
* @rsp: response queue
* @pkt: Entry pointer
+ * @data_sz: maximum sense-data payload size for this entry
*
* 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 +3711,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 +3728,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 +4018,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] 100+ messages in thread
* [PATCH v5 26/56] scsi: qla2xxx: Update handling of status entries for 29xx series
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (24 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 25/56] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:20 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 27/56] scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling " Nilesh Javali
` (29 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 91900a601a97..5d5ee3ee321f 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 ff0fd0bff6d0..49b858e7cf2a 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 0d58dc470e87..5aa45b8f4929 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;
@@ -3968,7 +3994,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;
@@ -3998,13 +4024,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;
@@ -4013,7 +4039,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;
@@ -4064,7 +4090,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
@@ -4157,7 +4183,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] 100+ messages in thread
* [PATCH v5 27/56] scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling for 29xx series
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (25 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 26/56] scsi: qla2xxx: Update handling of status entries for 29xx series Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 28/56] scsi: qla2xxx: Enhance purex_entry " Nilesh Javali
` (28 subsequent siblings)
55 siblings, 0 replies; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 1ca50fc200d6..7315bc4df00e 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -3468,7 +3468,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;
@@ -3480,36 +3480,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;
@@ -3558,7 +3572,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;
}
/*
@@ -3931,11 +3949,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] 100+ messages in thread
* [PATCH v5 28/56] scsi: qla2xxx: Enhance purex_entry handling for 29xx series
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (26 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 27/56] scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling " Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 18:29 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 29/56] scsi: qla2xxx: Update handling of ELS IOCBs " Nilesh Javali
` (27 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 | 88 ++++++++++++++++++++++++++-------
drivers/scsi/qla2xxx/qla_os.c | 24 +++++++--
4 files changed, 139 insertions(+), 41 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 0fc858ab4e8c..42826c1e4959 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -5001,6 +5001,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
@@ -5015,7 +5016,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 5aa45b8f4929..75ce09933afc 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;
@@ -3996,6 +4037,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;
@@ -4113,8 +4155,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)
@@ -4151,6 +4201,8 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
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;
}
@@ -4158,7 +4210,9 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
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] 100+ messages in thread
* [PATCH v5 29/56] scsi: qla2xxx: Update handling of ELS IOCBs for 29xx series
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (27 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 28/56] scsi: qla2xxx: Enhance purex_entry " Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:17 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 30/56] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx Nilesh Javali
` (26 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 5d5ee3ee321f..9db6efaa6c4d 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 49b858e7cf2a..2ade5e852251 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 7315bc4df00e..ab4f5f1ec73f 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -2975,8 +2975,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;
@@ -2987,11 +2988,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;
@@ -3322,9 +3323,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;
@@ -3333,11 +3335,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;
@@ -3358,8 +3360,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;
@@ -3370,8 +3373,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 =
@@ -4203,6 +4206,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 75ce09933afc..404dcb24b688 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] 100+ messages in thread
* [PATCH v5 30/56] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (28 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 29/56] scsi: qla2xxx: Update handling of ELS IOCBs " Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:14 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 31/56] scsi: qla2xxx: Add 29xx extended logio IOCB support Nilesh Javali
` (25 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 404dcb24b688..14b774f25b97 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] 100+ messages in thread
* [PATCH v5 31/56] scsi: qla2xxx: Add 29xx extended logio IOCB support
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (29 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 30/56] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 32/56] scsi: qla2xxx: Enhance task management IOCB handling for 29xx series Nilesh Javali
` (24 subsequent siblings)
55 siblings, 0 replies; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 2ade5e852251..d6140a92251f 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 ab4f5f1ec73f..4dcacee61e77 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -2564,8 +2564,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;
@@ -2592,12 +2593,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;
@@ -2622,7 +2624,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
@@ -2650,9 +2652,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) {
@@ -2669,7 +2673,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
@@ -2691,12 +2695,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
@@ -4057,8 +4063,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);
@@ -4067,7 +4075,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 14b774f25b97..855d6dbeedec 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);
@@ -4119,8 +4127,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] 100+ messages in thread
* [PATCH v5 32/56] scsi: qla2xxx: Enhance task management IOCB handling for 29xx series
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (30 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 31/56] scsi: qla2xxx: Add 29xx extended logio IOCB support Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 18:31 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 33/56] scsi: qla2xxx: Add abort command " Nilesh Javali
` (23 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 4dcacee61e77..c21d7f418f47 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -2727,7 +2727,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;
@@ -2736,26 +2736,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] 100+ messages in thread
* [PATCH v5 33/56] scsi: qla2xxx: Add abort command handling for 29xx series
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (31 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 32/56] scsi: qla2xxx: Enhance task management IOCB handling for 29xx series Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:42 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 34/56] scsi: qla2xxx: Enhance ABTS processing " Nilesh Javali
` (22 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 9db6efaa6c4d..fa302778d2ac 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -991,10 +991,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 c21d7f418f47..b631fee8c430 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -3926,32 +3926,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 855d6dbeedec..6fbcbdd208c3 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -3961,7 +3961,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;
@@ -3973,7 +3973,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)
@@ -4175,8 +4182,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] 100+ messages in thread
* [PATCH v5 34/56] scsi: qla2xxx: Enhance ABTS processing for 29xx series
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (32 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 33/56] scsi: qla2xxx: Add abort command " Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:49 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 35/56] scsi: qla2xxx: Update VP control IOCB handling " Nilesh Javali
` (21 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 6fbcbdd208c3..8367aa005d1b 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] 100+ messages in thread
* [PATCH v5 35/56] scsi: qla2xxx: Update VP control IOCB handling for 29xx series
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (33 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 34/56] scsi: qla2xxx: Enhance ABTS processing " Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:48 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 36/56] scsi: qla2xxx: Add build-time size check for VP config IOCB layout Nilesh Javali
` (20 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 b631fee8c430..7e3a415d2c43 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -4074,22 +4074,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 8367aa005d1b..128138b4d16a 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;
@@ -4228,8 +4234,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] 100+ messages in thread
* [PATCH v5 36/56] scsi: qla2xxx: Add build-time size check for VP config IOCB layout
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (34 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 35/56] scsi: qla2xxx: Update VP control IOCB handling " Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 37/56] scsi: qla2xxx: Add size check for extended VP report ID entry Nilesh Javali
` (19 subsequent siblings)
55 siblings, 0 replies; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 37/56] scsi: qla2xxx: Add size check for extended VP report ID entry
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (35 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 36/56] scsi: qla2xxx: Add build-time size check for VP config IOCB layout Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:29 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 38/56] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series Nilesh Javali
` (18 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 fa302778d2ac..79a1e16d56ab 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 128138b4d16a..085961e04ec2 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -4175,8 +4175,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] 100+ messages in thread
* [PATCH v5 38/56] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (36 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 37/56] scsi: qla2xxx: Add size check for extended VP report ID entry Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:36 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 39/56] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support Nilesh Javali
` (17 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 7e3a415d2c43..8bd7c94d7c7c 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -4039,38 +4039,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 085961e04ec2..30ddf9d76cae 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -4035,7 +4035,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";
@@ -4045,7 +4045,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);
}
@@ -4211,8 +4213,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] 100+ messages in thread
* [PATCH v5 39/56] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (37 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 38/56] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:38 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 40/56] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking Nilesh Javali
` (16 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 46eed5df7eef..9ee56ccd52b0 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -2014,7 +2014,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
@@ -2658,7 +2658,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));
@@ -2691,7 +2691,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));
@@ -2739,7 +2739,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));
@@ -2855,7 +2855,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);
@@ -2981,8 +2981,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 42826c1e4959..c10414453c2d 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] 100+ messages in thread
* [PATCH v5 40/56] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (38 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 39/56] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:49 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 41/56] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors Nilesh Javali
` (15 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 41/56] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (39 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 40/56] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:46 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 42/56] scsi: qla2xxx: Fix endianness annotations in vp_rpt_id_entry structures Nilesh Javali
` (14 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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: 4e0e5b8c80e8 ("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 d6140a92251f..9e33bcc87b39 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 30ddf9d76cae..9d2d11f10a76 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] 100+ messages in thread
* [PATCH v5 42/56] scsi: qla2xxx: Fix endianness annotations in vp_rpt_id_entry structures
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (40 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 41/56] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 43/56] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats Nilesh Javali
` (13 subsequent siblings)
55 siblings, 0 replies; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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: 4e0e5b8c80e8 ("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] 100+ messages in thread
* [PATCH v5 43/56] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (41 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 42/56] scsi: qla2xxx: Fix endianness annotations in vp_rpt_id_entry structures Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:28 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 44/56] scsi: qla2xxx: Add 64G/128G port speed setting support Nilesh Javali
` (12 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 44/56] scsi: qla2xxx: Add 64G/128G port speed setting support
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (42 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 43/56] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:32 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 45/56] scsi: qla2xxx: Fix 64G link speed reporting in get_data_rate Nilesh Javali
` (11 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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_init.c | 9 ++++++++-
drivers/scsi/qla2xxx/qla_mbx.c | 2 ++
3 files changed, 17 insertions(+), 2 deletions(-)
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_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 545066f67e02..5f7bc68aab3c 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -4793,7 +4793,14 @@ qla24xx_config_rings(struct scsi_qla_host *vha)
ql_dbg(ql_dbg_init, vha, 0x00fd,
"Speed set by user : %s Gbps \n",
qla2x00_get_link_speed_str(ha, ha->set_data_rate));
- icb->firmware_options_3 = cpu_to_le32(ha->set_data_rate << 13);
+ /*
+ * The ICB data-rate field is 3 bits (bits 13-15); rates above
+ * 64G do not fit and would overflow into bit 16 (75 ohm
+ * termination select). Such rates are forced via MBC_DATA_RATE.
+ */
+ if (ha->set_data_rate <= PORT_SPEED_64GB)
+ icb->firmware_options_3 =
+ cpu_to_le32(ha->set_data_rate << 13);
}
/* PCI posting */
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] 100+ messages in thread
* [PATCH v5 45/56] scsi: qla2xxx: Fix 64G link speed reporting in get_data_rate
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (43 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 44/56] scsi: qla2xxx: Add 64G/128G port speed setting support Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 46/56] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check Nilesh Javali
` (10 subsequent siblings)
55 siblings, 0 replies; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 46/56] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (44 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 45/56] scsi: qla2xxx: Fix 64G link speed reporting in get_data_rate Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 17:37 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 47/56] scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters Nilesh Javali
` (9 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 47/56] scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (45 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 46/56] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 48/56] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size Nilesh Javali
` (8 subsequent siblings)
55 siblings, 0 replies; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 48/56] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (46 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 47/56] scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:53 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 49/56] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config() Nilesh Javali
` (7 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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 8bd7c94d7c7c..c4595626c16b 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -4126,6 +4126,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] 100+ messages in thread
* [PATCH v5 49/56] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config()
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (47 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 48/56] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:59 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 50/56] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition() Nilesh Javali
` (6 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 50/56] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition()
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (48 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 49/56] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config() Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 17:08 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 51/56] scsi: qla2xxx: Initialize NVMe abort_work once at submission Nilesh Javali
` (5 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 51/56] scsi: qla2xxx: Initialize NVMe abort_work once at submission
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (49 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 50/56] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition() Nilesh Javali
@ 2026-07-17 15:28 ` Nilesh Javali
2026-07-17 16:45 ` sashiko-bot
2026-07-17 15:29 ` [PATCH v5 52/56] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject Nilesh Javali
` (4 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:28 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] 100+ messages in thread
* [PATCH v5 52/56] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (50 preceding siblings ...)
2026-07-17 15:28 ` [PATCH v5 51/56] scsi: qla2xxx: Initialize NVMe abort_work once at submission Nilesh Javali
@ 2026-07-17 15:29 ` Nilesh Javali
2026-07-17 16:55 ` sashiko-bot
2026-07-17 15:29 ` [PATCH v5 53/56] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak Nilesh Javali
` (3 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:29 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] 100+ messages in thread
* [PATCH v5 53/56] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (51 preceding siblings ...)
2026-07-17 15:29 ` [PATCH v5 52/56] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject Nilesh Javali
@ 2026-07-17 15:29 ` Nilesh Javali
2026-07-17 17:10 ` sashiko-bot
2026-07-17 15:29 ` [PATCH v5 54/56] scsi: qla2xxx: Fix BSG job leak on validate flash image error path Nilesh Javali
` (2 subsequent siblings)
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:29 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 9ee56ccd52b0..3bf3a7b96eb5 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -2858,7 +2858,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] 100+ messages in thread
* [PATCH v5 54/56] scsi: qla2xxx: Fix BSG job leak on validate flash image error path
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (52 preceding siblings ...)
2026-07-17 15:29 ` [PATCH v5 53/56] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak Nilesh Javali
@ 2026-07-17 15:29 ` Nilesh Javali
2026-07-17 16:57 ` sashiko-bot
2026-07-17 15:29 ` [PATCH v5 55/56] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions() Nilesh Javali
2026-07-17 15:29 ` [PATCH v5 56/56] scsi: qla2xxx: Update version to 12.00.00.2607b1 Nilesh Javali
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:29 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 3bf3a7b96eb5..2f73c9af418d 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -3848,9 +3848,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] 100+ messages in thread
* [PATCH v5 55/56] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions()
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (53 preceding siblings ...)
2026-07-17 15:29 ` [PATCH v5 54/56] scsi: qla2xxx: Fix BSG job leak on validate flash image error path Nilesh Javali
@ 2026-07-17 15:29 ` Nilesh Javali
2026-07-17 17:04 ` sashiko-bot
2026-07-17 15:29 ` [PATCH v5 56/56] scsi: qla2xxx: Update version to 12.00.00.2607b1 Nilesh Javali
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:29 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 2f73c9af418d..95cfdb020319 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -2043,6 +2043,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] 100+ messages in thread
* [PATCH v5 56/56] scsi: qla2xxx: Update version to 12.00.00.2607b1
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (54 preceding siblings ...)
2026-07-17 15:29 ` [PATCH v5 55/56] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions() Nilesh Javali
@ 2026-07-17 15:29 ` Nilesh Javali
2026-07-17 16:54 ` sashiko-bot
55 siblings, 1 reply; 100+ messages in thread
From: Nilesh Javali @ 2026-07-17 15:29 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>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
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] 100+ messages in thread
* Re: [PATCH v5 10/56] scsi: qla2xxx: Add extended status continuation and marker IOCBs
2026-07-17 15:28 ` [PATCH v5 10/56] scsi: qla2xxx: Add extended status continuation and marker IOCBs Nilesh Javali
@ 2026-07-17 15:42 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 15:42 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] Commit Message and Implementation Mismatch
- [Low] Architectural Inconsistency in Header Placement
--
--- Patch [10]: [PATCH v5 10/56] scsi: qla2xxx: Add extended status continuation and marker IOCBs ---
commit 1df9414084849f00c05087b262dc690e15f3cf79
Author: Anil Gurumurthy <agurumurthy@marvell.com>
scsi: qla2xxx: Add extended status continuation and marker IOCBs
This commit adds a 128-byte status continuation entry structure for 29xx
firmware and includes the new qla_fw29.h header. The commit message also notes
the addition of a marker IOCB.
> Add the 128-byte sts_cont_entry_ext_t and mrk_entry_ext_t
> structures required by 29xx firmware.
[Severity: Medium]
Is this commit message accurate for the provided code changes?
The diff only introduces struct sts_cont_entry_ext without the _t typedef
convention, and entirely omits the mrk_entry_ext_t marker entry structure.
It appears the marker structure was added in an earlier patch under a
different name.
> diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
> index 7423687578dc9..4de0de5cccc8d 100644
> --- 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 {
[Severity: Low]
Should this 128-byte IOCB definition be placed in qla_fw29.h instead of
qla_def.h?
The qla_fw29.h header was recently created specifically to hold new 128-byte
IOCB definitions for 29xx and isolate them from earlier generations. Placing
sts_cont_entry_ext in the generic header seems to bypass the dedicated
firmware header.
> + 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.
> */
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=10
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 09/56] scsi: qla2xxx: Add 128-byte IOCB definitions for 29xx
2026-07-17 15:28 ` [PATCH v5 09/56] scsi: qla2xxx: Add 128-byte IOCB definitions " Nilesh Javali
@ 2026-07-17 15:49 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 15:49 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-mapped structures use C bitfields on `__le16` bitwise types, leading to memory layout bugs on big-endian architectures and violating Sparse checks.
--
--- Patch [9]: [PATCH v5 09/56] scsi: qla2xxx: Add 128-byte IOCB definitions for 29xx ---
commit 16ec74f0c14d7afa47bfe50e3dd3cef172201ab7
Author: Anil Gurumurthy <agurumurthy@marvell.com>
scsi: qla2xxx: Add 128-byte IOCB definitions for 29xx
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.
> 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
> @@ -0,0 +1,686 @@
[ ... ]
> +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;
> +
> + __le32 rx_xchg_address; /* Receive exchange address. */
[Severity: High]
Does this bitfield usage on a restricted bitwise type (__le16) break memory
layout on big-endian hosts?
The C standard leaves bitfield memory layout up to the compiler. On big-endian
architectures, bitfields are typically allocated from MSB to LSB rather than
LSB to MSB.
Could the firmware read or write corrupted VP indices and SOF types on
big-endian hosts because the bytes won't map to the hardware's expected
layout?
Sparse also explicitly rejects bitfields on restricted bitwise types,
producing "degrading to integer" errors.
Could this use a plain __le16 field and apply bitmasks manually via
le16_to_cpu() and bitwise operations instead, similar to how it is handled
in struct cmd_type_6_ext?
[ ... ]
> +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;
> +
> + __le32 rx_xchg_address; /* Receive exchange address. */
[Severity: High]
Will this bitfield layout also cause endianness mapping issues with the
hardware on big-endian architectures?
[ ... ]
> +struct abts_entry_24xx_ext {
[ ... ]
> + __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;
[Severity: High]
Could this bitfield definition on __le16 similarly break hardware mapping
on big-endian hosts?
[ ... ]
> +struct vp_rpt_id_entry_24xx_ext {
[ ... ]
> + 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];
[Severity: High]
Is this C bitfield definition on __le16 also resulting in incorrect memory
layout when accessed on big-endian architectures?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=9
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 08/56] scsi: qla2xxx: Add BSG MPI firmware load/dump for 29xx
2026-07-17 15:28 ` [PATCH v5 08/56] scsi: qla2xxx: Add BSG MPI firmware load/dump " Nilesh Javali
@ 2026-07-17 15:51 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 15:51 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] Double completion of BSG job in qla2900_bsg_load_mpi() on error path.
- [High] The implementations of QL_VND_LOAD_MPI and QL_VND_DUMP_MPI are completely swapped, breaking API semantics.
--
--- Patch [8]: [PATCH v5 08/56] scsi: qla2xxx: Add BSG MPI firmware load/dump for 29xx ---
commit 90e9d607deb3d4b5831bfa77970f0b380447e7b0
Author: Manish Rangankar <mrangankar@marvell.com>
scsi: qla2xxx: Add BSG MPI firmware load/dump for 29xx
This commit adds BSG vendor commands for loading and dumping MPI firmware
on 29xx adapters. It 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
> index 92a1af81c057f..5dd7e5e969ab0 100644
> --- a/drivers/scsi/qla2xxx/qla_bsg.c
> +++ b/drivers/scsi/qla2xxx/qla_bsg.c
[ ... ]
> +static int qla2900_bsg_dump_mpi(struct bsg_job *bsg_job)
> +{
[ ... ]
> + mutex_lock(&ha->optrom_mutex);
> + rval = qla29xx_mpi_optrom_setup(bsg_job, vha, dmcmd->mpi_address, 1);
[Severity: High]
Does this function implement the wrong operation? Setting is_update=1 here
causes qla29xx_mpi_optrom_setup() to set ha->optrom_state to QLA_SWRITING.
> + 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);
And this copies data from the host's request_payload, suggesting this is
actually a load (host-to-device) operation rather than a dump.
[ ... ]
> +static int qla2900_bsg_load_mpi(struct bsg_job *bsg_job)
> +{
[ ... ]
> + mutex_lock(&ha->optrom_mutex);
> + rval = qla29xx_mpi_optrom_setup(bsg_job, vha, lmcmd->mpi_address, 0);
Conversely, this function is meant for loading but passes is_update=0, putting
the optrom into the QLA_SREADING state.
[ ... ]
> + 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;
> +}
[Severity: High]
Can this cause a double completion of the bsg_job?
If qla29xx_mpi_optrom_data() fails, rval is set to an error code
(e.g., -EINVAL). The function calls bsg_job_done() directly to complete
the request, but then returns the non-zero rval to the caller.
The caller (qla2x00_process_vendor_specific) passes this non-zero return value
up to the BSG layer. If a handler returns a non-zero error, the transport
layer's error path will typically call bsg_job_done() a second time on the
same job, which can lead to list corruption or a use-after-free.
Should this return 0 instead of rval, or avoid calling bsg_job_done() when
returning an error? (Note that qla2900_bsg_dump_mpi() safely returns 0).
> diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
> index c30ec9cab7bba..3b0a0cfe361dd 100644
> --- a/drivers/scsi/qla2xxx/qla_sup.c
> +++ b/drivers/scsi/qla2xxx/qla_sup.c
[ ... ]
> + if (op == QLA29XX_MPI_OP_DUMP) {
> + options |= (BIT_0);
> + options |= opts;
[ ... ]
> + memcpy(dcode, fwcode, burst * 4);
> +
> + rval = qla29xx_load_dump_mpi(vha, options, mpi_addr, burst,
> + optrom_dma);
[Severity: High]
Does QLA29XX_MPI_OP_DUMP actually perform a load? It copies the host buffer
(fwcode) to the DMA buffer (dcode) before issuing the mailbox command, which
sends data to the device.
> + } else if (op == QLA29XX_MPI_OP_LOAD) {
> + options |= opts;
[ ... ]
> + rval = qla29xx_load_dump_mpi(vha, options, mpi_addr, burst,
> + optrom_dma);
[ ... ]
> + memcpy(fwcode, dcode, burst * 4);
> + }
And conversely, OP_LOAD issues the mailbox command first and then copies
data from the DMA buffer to the host buffer, which performs a dump from the
device. Should the DUMP and LOAD implementations be swapped?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=8
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 05/56] scsi: qla2xxx: Add FC operational firmware load for 29xx
2026-07-17 15:28 ` [PATCH v5 05/56] scsi: qla2xxx: Add FC operational firmware load for 29xx Nilesh Javali
@ 2026-07-17 15:53 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 15:53 UTC (permalink / raw)
To: Nilesh Javali; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] The `qla29xx_isp_ops` structure is missing the `.start_scsi` function pointer, leading to a NULL pointer dereference.
- [High] `qla29xx_load_fw_template` misinterprets the template size, truncating the buffer, and incorrectly copies the flash wrapper header.
- [Medium] Missing bounds check on `ha->fw_dump_tmplt_len` before parsing template size in `qla29xx_load_fw_template`.
- [Medium] Flawed bounds check when reading `risc_attr` from Array 1 in `qla29xx_load_risc_blob()`.
--
--- Patch [5]: [PATCH v5 05/56] scsi: qla2xxx: Add FC operational firmware load for 29xx ---
commit 1d779103e29201d2b067488fba6b287ea9f225e4
Author: Manish Rangankar <mrangankar@marvell.com>
scsi: qla2xxx: Add FC operational firmware load for 29xx
This commit adds support for loading 29xx FC operational firmware from the
filesystem. It also sets up the corresponding firmware dump template following
the request_firmware and segment-load pattern used by earlier adapters.
> diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
> index c6e928f11758c..5b0dc9b0a679c 100644
> --- a/drivers/scsi/qla2xxx/qla_init.c
> +++ b/drivers/scsi/qla2xxx/qla_init.c
[ ... ]
> @@ -9128,6 +9131,539 @@ qla81xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
> +static int
> +qla29xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr)
> +{
[ ... ]
> + /* 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]);
[Severity: Medium]
Does this bounds check correctly protect the read from array1_addr?
At this point, offset has already been advanced to the start of Array 2.
This checks if there are 40 bytes available after Array 2, but doesn't
verify if Array 1 itself is at least 40 bytes long. If Array 1 is
undersized, array1_addr[9] will erroneously read into Array 2 data.
[ ... ]
> +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;
[ ... ]
> + 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]);
[Severity: Medium]
Could ha->fw_dump_tmplt_len be less than 12 bytes here? If the flash layout
defines a length smaller than 12, this array index will read out of bounds
of the kzalloc buffer.
> +
> + 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);
[Severity: High]
Is template_size stored in dwords in the flash region? If so,
vmalloc(template_size) allocates 1/4th of the required memory.
Also, does the memcpy() need to skip the 7-dword flash wrapper header? As
written, it copies the header into fwdt->template, which will cause
qla27xx_fwdt_template_valid() to fail.
Furthermore, the loop hardcodes templates = 1 and doesn't advance
fw_dump_tmplt, which ignores any secondary MPI firmware dump template.
> +
> + 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;
> + }
[ ... ]
> diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
> index c3eb941f71ad9..6560e58ad87c7 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,
> +};
[Severity: Critical]
Is the missing .start_scsi pointer here intentional?
If ha->mqenable is false or the designated qpair is NULL (e.g., due to a
queue mapping failure or fallback to single-queue mode),
qla2xxx_queuecommand() falls back to the legacy single-queue path by calling
ha->isp_ops->start_scsi(sp).
Since this pointer is omitted, it defaults to NULL and will result in a NULL
pointer dereference.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=5
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 14/56] scsi: qla2xxx: Enable get_fw_version mailbox for 29xx
2026-07-17 15:28 ` [PATCH v5 14/56] scsi: qla2xxx: Enable get_fw_version mailbox " Nilesh Javali
@ 2026-07-17 15:55 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 15:55 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] EDIF hardware support (`edif_hw`) is never initialized for 29xx adapters, rendering the newly added EDIF enablement logic in `qla2x00_get_fw_version()` dead code.
- [Medium] Firmware capabilities (MPI version, PEP version, and MPI firmware state) successfully retrieved for 29xx adapters cannot be read via sysfs.
--
--- Patch [14]: [PATCH v5 14/56] scsi: qla2xxx: Enable get_fw_version mailbox for 29xx ---
commit 0005c70371943e965a788152e6f343dce174f604
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Enable get_fw_version mailbox for 29xx
This commit adds IS_QLA29XX checks to various firmware version retrieval
paths in the get_fw_version mailbox and sysfs handlers. It enables 29xx
adapters to correctly retrieve and expose firmware version, EDIF, and
serdes information.
> 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
[ ... ]
> @@ -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)) {
[Severity: Medium]
Will this EDIF check actually work for 29xx adapters?
Looking at qla2x00_execute_fw(), edif_hw is only initialized for 28xx:
drivers/scsi/qla2xxx/qla_mbx.c:qla2x00_execute_fw() {
...
if (IS_QLA28XX(ha) && (mcp->mb[5] & EDIF_HW_SUPPORT)) {
ha->flags.edif_hw = 1;
...
}
Since edif_hw is never set for 29xx, does this make the newly added EDIF
enablement logic dead code for those adapters?
> 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;
[Severity: Medium]
Are we missing sysfs updates for the newly retrieved 29xx firmware data?
This change successfully retrieves the MPI version, PEP version, and
MPI firmware state for 29xx adapters. However, the sysfs show functions
still exclude 29xx.
For example, in qla2x00_mpi_version_show():
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");
...
}
Similar exclusions exist in qla2x00_pep_version_show() and
qla2x00_mpi_fw_state_show().
Will userspace receive empty newlines or 'not supported' messages instead
of the actual data when reading these attributes on 29xx?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=14
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 20/56] scsi: qla2xxx: Enable set_els_cmds and echo_test for 29xx
2026-07-17 15:28 ` [PATCH v5 20/56] scsi: qla2xxx: Enable set_els_cmds and echo_test " Nilesh Javali
@ 2026-07-17 15:56 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 15:56 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] qla2x00_process_loopback violates the DMA API by writing to memory mapped with DMA_FROM_DEVICE, leading to data corruption.
--
--- Patch [20]: [PATCH v5 20/56] scsi: qla2xxx: Enable set_els_cmds and echo_test for 29xx ---
commit 3169ddf029c9e47c02297da5b77b2f8371f5f469
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Enable set_els_cmds and echo_test for 29xx
This commit enables ELS command support and echo test diagnostics on 29xx
series adapters by adding IS_QLA29XX checks.
> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index d195723fc06bf..a8dd01cb9a9ae 100644
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c
[ ... ]
> @@ -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;
[Severity: High]
This is a pre-existing issue, but enabling echo tests for the 29xx series
exposes these adapters to a DMA API violation in the BSG handling path.
When processing a loopback or echo test BSG command,
qla2x00_process_loopback() maps the user's reply scatterlist with
DMA_FROM_DEVICE. It then asks the hardware to write to a separately
allocated coherent bounce buffer.
The CPU then copies the data from the bounce buffer to the user's
scatterlist:
drivers/scsi/qla2xxx/qla_bsg.c:qla2x00_process_loopback() {
...
sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
bsg_job->reply_payload.sg_cnt, rsp_data,
rsp_data_len);
...
done_unmap_sg:
dma_unmap_sg(&ha->pdev->dev,
bsg_job->reply_payload.sg_list,
bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
}
On non-cache-coherent architectures, calling dma_unmap_sg() with
DMA_FROM_DEVICE invalidates the CPU cache for the mapped memory.
Since the CPU wrote to this memory via sg_copy_from_buffer() before the
unmap, are we discarding the dirty cache lines before they can be flushed to
RAM?
Could this destroy the payload data copied to the user's reply buffer,
causing userspace to receive stale or empty data?
> 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;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=20
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 06/56] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path
2026-07-17 15:28 ` [PATCH v5 06/56] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path Nilesh Javali
@ 2026-07-17 15:58 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 15:58 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 calls to `qla28xx_get_aux_images` and `qla27xx_get_active_image` cause data races on hardware flash MMIO registers.
- [Medium] Lockless read of shared buffer `ha->vpd` via `memory_read_from_buffer` allows torn reads from concurrent modifications.
--
--- Patch [6]: [PATCH v5 06/56] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path ---
commit 58886d8609178ebe2a5d0c1f553bf284e06082ab
Author: Manish Rangankar <mrangankar@marvell.com>
scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path
This commit removes a redundant flash read from the qla2x00_sysfs_read_vpd
sysfs read path. The second read executed without holding the optrom_mutex,
exposing flash access to concurrent 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);
> skip:
> return memory_read_from_buffer(buf, count, &off, ha->vpd, ha->vpd_size);
[Severity: Medium]
This is a pre-existing issue, but does dropping the optrom_mutex right
before calling memory_read_from_buffer() expose the shared ha->vpd buffer
to torn reads?
If another privileged thread calls qla2x00_sysfs_write_vpd() concurrently,
it acquires optrom_mutex and overwrites the shared ha->vpd buffer.
Because memory_read_from_buffer() executes without the lock here, it might
copy a concurrently mutating buffer to userspace.
> }
[Severity: High]
This is another pre-existing issue, but can the earlier lockless call to
qla28xx_get_aux_images() cause hardware data races?
The qla28xx_get_aux_images() function is called before optrom_mutex is
acquired in qla2x00_sysfs_read_vpd():
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);
...
}
This eventually calls qla24xx_read_flash_dword(), which issues hardware
writes to reg->flash_addr and polling reads from reg->flash_data.
Concurrently executing another flash operation interleaves these hardware
register accesses and can irreparably corrupt the flash operation state
machine for both threads.
A similar lockless access happens in the BSG handler
qla2x00_get_flash_image_status() when it calls qla27xx_get_active_image().
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=6
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 16/56] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx
2026-07-17 15:28 ` [PATCH v5 16/56] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx Nilesh Javali
@ 2026-07-17 16:00 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16: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:
- [Medium] Missing QLA29XX support in BSG command handler prevents BBCR feature access.
Pre-existing issues:
- [High] Uninitialized kernel stack memory leak to userspace in `qla27xx_get_bbcr_data()`.
- [High] Race condition due to unprotected read-modify-write on shared `vha->hw->flags` bitfield.
--
--- Patch [16]: [PATCH v5 16/56] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx ---
commit 6a9facae245c0e3d5c4d496991f281dbefb90de2
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx
This patch adds the IS_QLA29XX() check to qla2x00_get_adapter_id() alongside
the existing 27xx/28xx logic. This allows 29xx adapters to read additional
mailbox registers for buffer-to-buffer credits and SCM/EDC status.
> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index 52d70b61654c7..3fc08120fdf11 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))
[Severity: High]
This is a pre-existing issue, but does this function leak uninitialized kernel
stack memory to userspace?
In qla2x00_get_adapter_id(), the mailbox command structure is allocated on
the stack without initialization:
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
If qla2x00_mailbox_command() fails early (e.g., due to timeout or reset),
the mc.mb array isn't populated from the hardware registers. However, the
function still unconditionally assigns the uninitialized stack data to the
id pointer:
/* Return data. */
*id = mcp->mb[1];
When called from the BSG handler, this uninitialized data is saved to
bbcr.mbx1 in the error path and then copied to userspace:
drivers/scsi/qla2xxx/qla_bsg.c:qla27xx_get_bbcr_data() {
...
rval = qla2x00_get_adapter_id(vha, &loop_id, &al_pa, &area,
...
if (rval != QLA_SUCCESS) {
bbcr.status = QLA_ST_WWN_MN_FAIL;
bbcr.mbx1 = loop_id;
goto done;
}
...
done:
sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
bsg_job->reply_payload.sg_cnt, &bbcr, sizeof(bbcr));
...
}
Should mc be zero-initialized to prevent leaking stack data?
> 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)) {
[Severity: Medium]
Will this BBCR data actually be accessible for 29xx adapters from userspace?
While this patch adds support for reading the BBCR and SCM/EDC data for 29xx
adapters, the BSG command handler used to retrieve this data appears to lack
the corresponding IS_QLA29XX() check:
drivers/scsi/qla2xxx/qla_bsg.c:qla27xx_get_bbcr_data() {
...
if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
return -EPERM;
...
}
Does qla27xx_get_bbcr_data() also need to be updated to include 29xx adapters
so the feature is fully reachable?
> vha->bbcr = mcp->mb[15];
> if (mcp->mb[7] & SCM_EDC_ACC_RECEIVED) {
> ql_log(ql_log_info, vha, 0x11a4,
[Severity: High]
This isn't a bug introduced by this patch, but does the following SCM logic
have a race condition?
Just below this log statement in the same block, there is an unprotected
read-modify-write on a shared bitfield:
if (mcp->mb[7] & SCM_RDF_ACC_RECEIVED) {
vha->hw->flags.scm_enabled = 1;
The flags member is a volatile struct containing bitfields like
scm_enabled:1, lip_ae:1, and n2n_ae:1. Because this modification occurs
in process context without holding a hardware spinlock, it performs a
non-atomic read-modify-write on the shared 32-bit word.
If an asynchronous hardware event fires during this operation, the ISR
might set a bit like lip_ae = 1. The process context would then overwrite
the change with stale data, silently dropping the ISR's modification.
Could this lead to dropped loop initialization events?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=16
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 22/56] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx
2026-07-17 15:28 ` [PATCH v5 22/56] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx Nilesh Javali
@ 2026-07-17 16:01 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:01 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] Dropping a spinlock inside list_for_each_entry_safe exposes the cached next pointer to use-after-free.
- [High] Lockless iteration over fcport list exposes it to concurrent modifications.
--
--- Patch [22]: [PATCH v5 22/56] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx ---
commit 6046de798bc7fa0a80928785cf9ec35ed974164d
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Enable qla2x00_shutdown for 29xx
Enable qla2x00_shutdown for 29xx adapter by adding IS_QLA29XX check
to the shutdown path that performs 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 isn't a bug introduced by this patch, but it exposes 29xx adapters to a
pre-existing use-after-free issue in qla2x00_abort_isp_cleanup().
Dropping the vport_slock inside list_for_each_entry_safe() exposes the cached
next pointer (tvp) to concurrent removal. If a concurrent vport removal (e.g.,
via sysfs /sys/class/fc_host/hostX/vport_delete) acquires vport_slock, removes
tvp from vp_list, and frees it, the loop will dereference a freed pointer when
resuming:
drivers/scsi/qla2xxx/qla_init.c:qla2x00_abort_isp_cleanup() {
...
spin_lock_irqsave(&ha->vport_slock, flags);
list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
atomic_inc(&vp->vref_count);
spin_unlock_irqrestore(&ha->vport_slock, flags);
qla2x00_mark_all_devices_lost(vp);
spin_lock_irqsave(&ha->vport_slock, flags);
atomic_dec(&vp->vref_count);
}
...
}
Can this lead to a use-after-free when the lock is reacquired and the loop
proceeds to tvp?
[Severity: High]
This is also a pre-existing issue, but this patch additionally exposes 29xx
adapters to a lockless list iteration bug in qla2x00_abort_isp_cleanup().
The vp_fcports list is iterated without holding any locks, which can race with
concurrent modifications like device discovery or target login/logout events
that modify vp_fcports under tgt.sess_lock.
For the primary host:
drivers/scsi/qla2xxx/qla_init.c:qla2x00_abort_isp_cleanup() {
...
/* Clear all async request states across all VPs. */
list_for_each_entry(fcport, &vha->vp_fcports, list) {
fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT);
fcport->scan_state = 0;
}
...
}
And for other vports where vport_slock is explicitly dropped before iterating:
drivers/scsi/qla2xxx/qla_init.c:qla2x00_abort_isp_cleanup() {
...
list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
atomic_inc(&vp->vref_count);
spin_unlock_irqrestore(&ha->vport_slock, flags);
list_for_each_entry(fcport, &vp->vp_fcports, list)
fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT);
...
}
Does this lockless iteration risk list corruption or use-after-free if the
list is concurrently modified?
> } else {
> /* Stop currently executing firmware. */
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=22
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 07/56] scsi: qla2xxx: Add flash block read/write BSG support for 29xx
2026-07-17 15:28 ` [PATCH v5 07/56] scsi: qla2xxx: Add flash block read/write BSG support for 29xx Nilesh Javali
@ 2026-07-17 16:02 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:02 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] Uninitialized usage of `bsg_reply->reply_payload_rcv_len` and partial initialization of `bsg_reply` leads to an information leak and potential kernel warning.
- [Medium] Incorrect BSG request length validation forces user space to over-allocate the payload buffer by 8 unused bytes and rejects exactly-sized valid requests.
- [Medium] Passing a 0-length payload to flash block read/write commands triggers a kernel WARN_ON_ONCE via `vzalloc(0)`.
--
--- Patch [7]: [PATCH v5 07/56] scsi: qla2xxx: Add flash block read/write BSG support for 29xx ---
commit a2bf87754d2c500ea1dbb98bcd9b4f70437eb23f
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
> --- 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,
[ ... ]
> + 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;
> + }
[ ... ]
> ha->optrom_buffer = vzalloc(ha->optrom_region_size);
> if (!ha->optrom_buffer) {
[Severity: Medium]
If a user space tool sends a flash block read or write command with a
payload length of 0, ha->optrom_region_size will become 0.
Won't calling vzalloc(0) here trigger a WARN_ON_ONCE(!size) in the kernel
memory allocator?
Should there be an explicit check to reject 0-length payloads early to
prevent this warning and potential system instability if panic_on_warn
is enabled?
[ ... ]
> @@ -1554,6 +1592,234 @@ qla2x00_update_optrom(struct bsg_job *bsg_job)
[ ... ]
> +static int qla29xx_bsg_flash_block_read(struct bsg_job *bsg_job)
> +{
[ ... ]
> + if (bsg_job->request_len < sizeof(struct fc_bsg_request) +
> + 2 * sizeof(uint32_t) + sizeof(struct qla_block_rw))
> + return -EINVAL;
[Severity: Medium]
Does this length calculation double-count the first two elements of the
vendor command array?
Since sizeof(struct fc_bsg_request) is 20 bytes and the vendor_cmd array
starts at offset 12, vendor_cmd[2] is at offset 20. Adding
2 * sizeof(uint32_t) requires an extra 8 bytes that are already accounted
for in the struct size.
Will this reject perfectly sized valid requests and force userspace to
over-allocate the payload buffer by 8 unused bytes?
[ ... ]
> +static int qla29xx_bsg_flash_block_write(struct bsg_job *bsg_job)
> +{
[ ... ]
> + if (bsg_job->request_len < sizeof(struct fc_bsg_request) +
> + 2 * sizeof(uint32_t) + sizeof(struct qla_block_rw))
> + return -EINVAL;
[Severity: Medium]
Is this subject to the same length calculation issue as the flash block
read path above, where it over-estimates the required request 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 0;
> +}
[Severity: High]
Because bsg_reply->reply_payload_rcv_len is never initialized in
qla29xx_bsg_flash_block_write(), could this pass a stale value from a
previous IO back to bsg_job_done()?
If the block layer reuses the kzalloc'd job->reply buffer from initialization,
setting bsg_job->reply_len to the full size of struct fc_bsg_reply might
copy this partially uninitialized buffer to userspace, leaking kernel memory
and potentially triggering a WARN_ON in the BSG layer if the stale value
is too large.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=7
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 23/56] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs
2026-07-17 15:28 ` [PATCH v5 23/56] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs Nilesh Javali
@ 2026-07-17 16:03 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:03 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 refactored code retains a dangerous flaw: the driver uses the standard library `memset()` function to zero an IOCB slot that, for QLAFX00 adapters, resides in PCI MMIO space. In `__qla2x00_alloc_iocbs()`, `qla_req_ring_slot()` retrieves the ring slot pointer and assigns it to `pkt`. For QLAFX00 adapters, the request ring is mapped directly into device memory (`req->ring = (void __force *)ha->iobase + ha->req_que_off;`), meaning `pkt` is an `__iomem` pointer. Calling `memset()` on MMIO memory can generate unaligned or byte-wise bus transactions. On strict-alignment architectures like ARM64 or SPARC, this triggers a synchronous external abort (kernel oops) and crashes the system. The subsequent `if (IS_QLAFX00(ha))` block correctly casts the fields back to `__iomem` to use `wrt_reg_byte()` and `wrt_reg_dword()`, demonstrating that the memory is indeed MMIO, but the unsafe `memset()` remains.
--
--- Patch [23]: [PATCH v5 23/56] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs ---
commit 30045e77ead5341377665972b3b6c891e41c1958
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs
This commit refactors __qla2x00_alloc_iocbs to use qla_req_ring_slot()
and qla_req_entry_size() helpers for ring pointer selection and entry
size. It consolidates the branching logic for QLA29XX and standard
adapters into a unified memset.
> diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
> index aab7905c0d6ca..1ca50fc200d65 100644
> --- a/drivers/scsi/qla2xxx/qla_iocb.c
> +++ b/drivers/scsi/qla2xxx/qla_iocb.c
[ ... ]
> @@ -2530,13 +2529,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 isn't a bug introduced by this patch, but could using the standard
memset() here cause unaligned or byte-wise bus transactions on QLAFX00
adapters?
For QLAFX00, the request ring is mapped directly into device memory, meaning
pkt points to __iomem. The subsequent if (IS_QLAFX00(ha)) block correctly
casts the fields back to __iomem to use wrt_reg_byte() and wrt_reg_dword(),
demonstrating that the memory is indeed MMIO.
Could calling memset() directly on this MMIO pointer trigger a synchronous
external abort or alignment fault on strict-alignment architectures like
ARM64 or SPARC, since it is not using an I/O specific function like
memset_io()?
> 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/20260717152904.3155730-1-njavali@marvell.com?part=23
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 02/56] scsi: qla2xxx: Add flash read/write interface for 29xx
2026-07-17 15:28 ` [PATCH v5 02/56] scsi: qla2xxx: Add flash read/write interface for 29xx Nilesh Javali
@ 2026-07-17 16:05 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:05 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] Missing `isp_ops` hooks for 29xx flash read/write cause sysfs MMIO out-of-bounds crashes.
- [High] Concurrent stateful flash read operations cause firmware state corruption due to missing `optrom_mutex`.
--
--- Patch [2]: [PATCH v5 02/56] scsi: qla2xxx: Add flash read/write interface for 29xx ---
commit 3849a5ddec04d8b5a4e4730fd38a29a1014a62fa
Author: Manish Rangankar <mrangankar@marvell.com>
scsi: qla2xxx: Add flash read/write interface for 29xx
The 29xx series uses a different flash access mechanism than earlier adapters.
This patch adds the mailbox wrappers and helpers needed for flash read and
write operations so that the existing flash infrastructure can drive the new
hardware.
Link: https://lore.kernel.org/oe-kbuild-all/202607141520.k9T31Dpp-lkp@intel.com/
> 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
[ ... ]
> @@ -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 isp_ops hooks for the 29xx flash read/write missing here?
The newly introduced qla29xx_read_optrom_data() and
qla29xx_write_optrom_data() functions appear to be left unassigned to the
29xx operations table. If the legacy handlers (like qla25xx_read_optrom_data)
are still used by default, would reading or writing the optrom sysfs node
attempt MMIO operations at iobase + ~0 and cause out-of-bounds PCIe memory
crashes, since flash_conf_off and flash_data_off are initialized to ~0?
[ ... ]
> 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
[ ... ]
> +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)) {
[Severity: High]
Is it safe to call qla29xx_read_optrom_data() here without holding
ha->optrom_mutex?
During an adapter reset, the background DPC thread can call
qla2x00_initialize_adapter() which invokes ha->isp_ops->get_flash_version(),
leading to this code without acquiring the mutex.
If a user is concurrently reading or writing the sysfs optrom file (which
does hold the mutex but wouldn't block this background path), could the
interleaved stateful mailbox commands (MBC_RD_WR_FLASH) violate the firmware's
flash state machine sequence and cause corruption?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=2
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 04/56] scsi: qla2xxx: Add 29xx support in queue initialisation path
2026-07-17 15:28 ` [PATCH v5 04/56] scsi: qla2xxx: Add 29xx support in queue initialisation path Nilesh Javali
@ 2026-07-17 16:09 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:09 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:
- [Critical] The patch intentionally introduces broken pointer arithmetic and structure size mismatches for QLA29XX hardware, severely breaking bisectability.
- [Critical] Base queues are allocated with 64-byte entries, but initialized as 128-byte entries on 29xx hardware, causing severe DMA out-of-bounds access.
Pre-existing issues:
- [High] Unconditional call to dma_free_coherent() with a NULL virtual address in the queue allocation error paths.
- [High] A queue ID is cleared twice during initialization failure and its mapping is nullified without locks, causing a race condition that silently destroys a concurrently allocated queue.
- [High] If the firmware is unresponsive during teardown, queue deletion aborts and permanently leaks DMA memory and host structs.
--
--- Patch [4]: [PATCH v5 04/56] scsi: qla2xxx: Add 29xx support in queue initialisation path ---
commit eb9f04cb3a041d8d7db2e1662875c85a2485be0c
Author: Manish Rangankar <mrangankar@marvell.com>
scsi: qla2xxx: Add 29xx support in queue initialisation path
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, 29xx uses 128-byte entries.
> 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);
[Severity: High]
This isn't a bug introduced by this patch, but if dma_alloc_coherent()
fails in qla25xx_create_req_que() or qla25xx_create_rsp_que(), the code
branches to the que_failed label and calls qla25xx_free_req_que() or
qla25xx_free_rsp_que().
Will this unconditionally call dma_free_coherent() with a NULL virtual
address for req->ring or rsp->ring? Could this trigger a kernel oops or
WARN during low memory conditions?
[ ... ]
> @@ -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);
[Severity: Critical]
The commit message notes that pointer arithmetic for the wider entries is
handled by later patches in the series.
Does this mean this commit leaves req->out_ptr and rsp->in_ptr using 64-byte
pointer arithmetic on a 128-byte stride ring during initialization?
Could this place the DMA pointers exactly halfway through the allocated
ring buffer instead of at the end, causing the hardware to read and write
out of bounds, and potentially breaking bisectability if this specific commit
is tested on QLA29XX adapters?
[Severity: Critical]
While this allocates 128-byte stride entries for multi-queues on 29xx
hardware, what happens to the primary base queues (queue 0)?
Looking at qla2x00_mem_alloc() in qla_os.c, the base queues still appear to
be allocated with a hardcoded sizeof(request_t) which is 64 bytes.
When the driver probes the 29xx hardware and initializes these base queues
with a 128-byte stride, could the hardware read and write past the end of the
allocated DMA buffers?
[Severity: High]
This is a pre-existing issue, but looking at the error path in
qla25xx_create_req_que():
If qla25xx_init_req_que() fails, the code clears que_id from ha->req_qid_map
while holding ha->mq_lock, and then calls qla25xx_free_req_que().
Inside qla25xx_free_req_que():
if (que_id) {
ha->req_q_map[que_id] = NULL;
mutex_lock(&ha->vport_lock);
clear_bit(que_id, ha->req_qid_map);
...
}
Could a concurrent thread re-allocate this same que_id before
qla25xx_free_req_que() sets ha->req_q_map[que_id] = NULL without holding
mq_lock? Would this silently destroy the concurrent allocation's queue pointer
and clear the bit again using the wrong lock (vport_lock)?
[Severity: High]
This is also a pre-existing issue, but looking at queue teardown:
In qla25xx_delete_req_que(), the driver attempts to notify the firmware by
calling qla25xx_init_req_que().
If the firmware is unresponsive (e.g. due to a crash or PCI error), does
this function return early and skip calling qla25xx_free_req_que()?
Furthermore, in qla25xx_delete_queues(), the loop processing all queues
aborts on the first failure. Could this permanently leak all subsequent
struct req_que allocations and their large DMA rings, leading to DMA pool
exhaustion after device resets?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=4
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 25/56] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters
2026-07-17 15:28 ` [PATCH v5 25/56] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters Nilesh Javali
@ 2026-07-17 16:11 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16: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:
New issues:
- [Critical] FORTIFY_SOURCE buffer overread panic in continuation IOCB processing
Pre-existing issues:
- [High] Remote Denial of Service (Infinite Loop) via crafted payload colliding with RESPONSE_PROCESSED
- [Medium] Missing read memory barrier after signature polling loop
--
--- Patch [25]: [PATCH v5 25/56] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters ---
commit 5af8ad3c722085f26948d73f06875dd765494eea
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters
This commit updates all status continuation IOCB processing paths to branch
on adapter type and handle 128-byte response queue entries for QLA29XX
hardware. It modifies several functions to use the correct data payload
size and structure cast to prevent truncation.
Closes: https://lore.kernel.org/oe-kbuild-all/202607142020.gqDaApes-lkp@intel.com/
> 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]
Can this cast trigger a FORTIFY_SOURCE panic?
Looking at __qla_copy_purex_to_buffer() in drivers/scsi/qla2xxx/qla_isr.c,
the pointer data is initialized by casting the packet to sts_cont_entry_t,
which defines the data field as a statically known 60-byte array.
On QLA29XX adapters, data_sz is set to 124 bytes. When the subsequent
memcpy attempts to copy up to 124 bytes from this 60-byte pointer,
FORTIFY_SOURCE will detect a field-spanning read using __builtin_object_size
and panic the kernel with __read_overflow2.
Does this need to cast to the extended struct for 29xx adapters before
taking the address of the data field?
>
> - if (new_pkt->entry_type != STATUS_CONT_TYPE) {
> + if (((sts_cont_entry_t *)*pkt)->entry_type !=
> + STATUS_CONT_TYPE) {
[ ... ]
> @@ -906,28 +915,24 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
> continue;
> }
[Severity: High]
This is a pre-existing issue, but could this loop spin infinitely?
Looking at the context above this diff block in qla27xx_copy_multiple_pkt(),
the driver waits for the firmware to populate the continuation IOCB by
spinning until signature != RESPONSE_PROCESSED.
Since the signature field is at offset 60, it overlaps with the payload
data written by the firmware. If a remote endpoint sends a crafted
payload where the bytes at offset 60 happen to match the
RESPONSE_PROCESSED constant, the driver will never see the signature
change and will loop infinitely with interrupts disabled.
Is there a safer way to poll for DMA completion without risking payload
collisions?
[Severity: Medium]
This is a pre-existing issue, but is a read memory barrier missing here?
After breaking out of the cpu_relax() polling loop, the code immediately
reads the payload data from the ring buffer. On weakly-ordered
architectures, the CPU can speculatively load the payload data before
the signature validation completes.
Does this need a dma_rmb() after the signature check to ensure the CPU
processes the newly DMA'd packet instead of stale ring memory?
>
> - 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]
Can this cast also trigger a FORTIFY_SOURCE panic during memcpy?
Similar to __qla_copy_purex_to_buffer(), casting to sts_cont_entry_t
bounds the data pointer to 60 bytes, causing a panic when copying
up to 124 bytes on QLA29XX hardware.
[ ... ]
> @@ -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);
[Severity: Critical]
Does this copy path also suffer from the FORTIFY_SOURCE panic?
The same 60-byte array limitation applies here when qla27xx_copy_fpin_pkt()
processes FPIN packets on QLA29XX adapters.
[ ... ]
> @@ -3691,6 +3711,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;
[Severity: Critical]
Can this cast trigger a FORTIFY_SOURCE panic when copying sense data?
Looking at qla2x00_status_cont_entry() in drivers/scsi/qla2xxx/qla_isr.c,
the data pointer is bounded to 60 bytes due to the sts_cont_entry_t cast.
When sense_sz exceeds 60 bytes on 29xx adapters, the subsequent memcpy
at the end of this function will panic the kernel.
Does this need to branch on adapter type to extract the data pointer safely?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=25
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 11/56] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx
2026-07-17 15:28 ` [PATCH v5 11/56] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx Nilesh Javali
@ 2026-07-17 16:13 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:13 UTC (permalink / raw)
To: Nilesh Javali; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [Critical] Non-IO path IOCB builders (Abort, Task Management, ELS, Login, Logout, PRLI) fail to use the extended 128-byte struct layouts on QLA29XX adapters, corrupting extended IOCBs and breaking NPIV isolation.
- [Critical] The ISR completion path fails to handle 29xx extended status IOCBs, resulting in garbage values being read for SCSI status, sense lengths, and NVMe ERSP data.
- [High] Manual 64-byte ring pointer advancement in `qla27xx_copy_multiple_pkt()` corrupts the response ring on QLA29XX adapters.
- [High] Continuation IOCB payloads are severely truncated on QLA29XX adapters due to hardcoded 60-byte copy limits.
- [High] Continuation IOCBs in target mode are under-packed with DSDs on 29xx hardware, leading to truncated data transfers.
--
--- Patch [11]: [PATCH v5 11/56] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx ---
commit 5e78bc810efc46ed3e8aed3841ea031f78694acf
Author: Anil Gurumurthy <agurumurthy@marvell.com>
scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx
This commit updates the driver to support 128-byte extended IOCB
structures for the IO submission, completion, and queue management paths
on 29xx adapters. It introduces helpers for entry size selection and
slot advancement, falling back to 64-byte IOCBs for earlier adapters.
Link: https://lore.kernel.org/oe-kbuild-all/202607141827.R9mpeMZx-lkp@intel.com/
> diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
> index dbe3cd4e274c8..aab7905c0d6ca 100644
> --- a/drivers/scsi/qla2xxx/qla_iocb.c
> +++ b/drivers/scsi/qla2xxx/qla_iocb.c
[ ... ]
[Severity: Critical]
Does qla2x00_start_sp() properly dispatch non-IO paths for QLA29XX adapters?
Currently, it seems to dispatch management commands (like Abort, Task
Management, ELS, Login, Logout, PRLI) to legacy 64-byte builders like
qla24xx_tm_iocb() and qla24xx_abort_iocb().
If these builders cast the 128-byte slot to the legacy struct and write to
the old offsets (e.g., vp_index written to offset 51 instead of 18 or 28),
could this result in management commands being sent to the wrong virtual
port (VP 0) and breaking NPIV isolation?
> 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
[ ... ]
> @@ -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;
[Severity: High]
Does this logic truncate extended payloads on 29xx adapters?
The advancement helper qla_rsp_ring_advance() steps by 128 bytes for extended
rings, but the copy size is still bounded by sizeof(new_pkt->data) which is 60
bytes. Can this ignore the remaining data the 29xx firmware places in the
extended slot?
Also, is there a similar issue in qla27xx_copy_multiple_pkt() where the manual
rsp_q->ring_index advancement was left behind? Using the old 64-byte stride
logic there could corrupt the response ring on QLA29XX adapters by misaligning
the pointer.
[ ... ]
> @@ -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: Critical]
Will reading completion entries through a hardcoded 64-byte structure like
sts_entry_24xx cause problems on 29xx hardware?
If functions like qla2x00_status_entry() or qla24xx_nvme_iocb_entry()
unconditionally cast the 128-byte response slot to the legacy 64-byte struct,
wouldn't they read unrelated fields for the SCSI status, sense lengths, and
NVMe ERSP data?
> diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c
> index e47da45e93a01..53a505df8da0a 100644
> --- a/drivers/scsi/qla2xxx/qla_target.c
> +++ b/drivers/scsi/qla2xxx/qla_target.c
[ ... ]
> @@ -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;
[Severity: High]
Does this loop pack enough DSDs for 29xx hardware?
Looking slightly further down in qlt_load_cont_data_segments():
for (cnt = 0;
cnt < QLA_TGT_DATASEGS_PER_CONT_24XX && prm->seg_cnt;
cnt++, prm->seg_cnt--) {
append_dsd64(&cur_dsd, prm->sg);
prm->sg = sg_next(prm->sg);
}
If the loop strictly limits packing to QLA_TGT_DATASEGS_PER_CONT_24XX (5) DSDs,
will the 29xx firmware (which expects 10 DSDs per 128-byte slot) encounter
zero-length DSDs mid-transfer and prematurely terminate target mode data
transfers?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=11
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 30/56] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx
2026-07-17 15:28 ` [PATCH v5 30/56] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx Nilesh Javali
@ 2026-07-17 16:14 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:14 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 descriptor bit-fields in `els_entry_24xx_ext`, `els_sts_entry_24xx_ext`, and other 29xx IOCBs are fundamentally broken on Big-Endian (BE) architectures.
--
--- Patch [30]: [PATCH v5 30/56] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx ---
commit e3db5f38021ae8b35506a27dde8324d79fd2361c
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Add size check for ELS status entry layout on 29xx
This patch adds a build-time assertion to validate the size of the extended
29xx ELS status entry struct. It also adds documentation detailing how the
extended struct overlays the base 24xx layout, explaining why completion
handler reads are stride-agnostic.
> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> index 404dcb24b6887..14b774f25b97e 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 hardware descriptor layouts on Big-Endian
architectures?
Looking at the definitions for els_entry_24xx_ext and els_sts_entry_24xx_ext
in drivers/scsi/qla2xxx/qla_fw29.h, they use C bit-fields inside an __le16
type:
__le16 vp_index : 9; /* VP Index 9bits */
__le16 reserved_1_sof : 3;
__le16 sof_type : 4;
When the driver initializes an ELS pass-through IOCB on the issue path:
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;
}
Because these are C bit-fields, the compiler will pack them LSB-to-MSB on
Little-Endian architectures (which matches firmware expectations), but
MSB-to-LSB on Big-Endian architectures.
Will this cause the driver to write garbled DMA descriptors to the firmware
when issuing ELS commands on Big-Endian platforms using a 29xx adapter? Should
these hardware DMA bit-fields be wrapped in endianness checks or
accessed via explicit bitwise shifting to ensure portability?
> + * 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.
> + */
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=30
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 29/56] scsi: qla2xxx: Update handling of ELS IOCBs for 29xx series
2026-07-17 15:28 ` [PATCH v5 29/56] scsi: qla2xxx: Update handling of ELS IOCBs " Nilesh Javali
@ 2026-07-17 16:17 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:17 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] Endianness portability bug in `qla_els_set_vp_sof()` due to native C bitfield assignments on a `__le16` hardware struct field.
- [High] Incomplete 29xx hardware support in `qla24xx_process_abts()` uses the legacy 24xx struct layout for the ABTS response, truncating the virtual port index and corrupting the descriptor.
- [Medium] Partial zeroing of the reused DMA buffer in `qla24xx_process_abts()` leaves upper 64 bytes uninitialized for 29xx adapters.
--
--- Patch [29]: [PATCH v5 29/56] scsi: qla2xxx: Update handling of ELS IOCBs for 29xx series ---
commit 0508a9076a015bdb2b8eca42fabd1c2776bb924b
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Update handling of ELS IOCBs for 29xx series
This commit updates ELS IOCB handling to support the extended 128-byte
els_entry_24xx_ext structure for 29xx series adapters. It modifies several
functions to take generic void pointers and introduces a helper to handle
24xx vs 29xx bitfield encoding.
> diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
> --- 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;
[Severity: High]
Does this code introduce an endianness bug on big-endian architectures?
The 29xx extended ELS struct defines these fields as C bitfields inside a
__le16 type. On big-endian architectures like s390x or PowerPC, the compiler
will place these bitfields in the most significant bits of the 16-bit word,
reversing the layout expected by the little-endian hardware.
Could this corrupt the descriptor DMA'd to the firmware, causing ELS
commands to be rejected or misrouted?
> + } 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
> --- a/drivers/scsi/qla2xxx/qla_isr.c
> +++ b/drivers/scsi/qla2xxx/qla_isr.c
[ ... ]
> @@ -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));
[Severity: Medium]
Will this memset only clear the first 64 bytes of the newly allocated
128-byte buffer on 29xx hardware?
Since abts_rsp is cast to the legacy 64-byte struct abts_entry_24xx,
sizeof(*abts_rsp) evaluates to 64. The remaining 64 bytes of the 128-byte
buffer may contain stale data from the prior firmware interaction, which
could then be sent back to the hardware.
[Severity: High]
Is the 29xx hardware fully supported by the ABTS response logic here?
While the buffer is allocated dynamically for 128 bytes, the struct parsing
and formatting logic casts the buffer to the legacy struct abts_entry_24xx.
For 29xx hardware, vp_idx and sof_type are packed into a 16-bit field, but
the legacy struct uses two 8-bit fields.
Writing to the 8-bit sof_type and masking it:
abts_rsp->vp_idx = abts->vp_idx;
abts_rsp->sof_type = abts->sof_type & 0xf0;
will overwrite the 9th bit of vp_index, silently truncating it to 8 bits.
Could this cause NPIV port traffic for virtual ports with an index > 255 to
be misrouted or dropped?
> abts_rsp->entry_type = ABTS_RSP_TYPE;
> abts_rsp->entry_count = 1;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=29
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 26/56] scsi: qla2xxx: Update handling of status entries for 29xx series
2026-07-17 15:28 ` [PATCH v5 26/56] scsi: qla2xxx: Update handling of status entries for 29xx series Nilesh Javali
@ 2026-07-17 16:20 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:20 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 underflow when subtracting `rsp_info_len` from `par_sense_len` allows an out-of-bounds read of kernel memory into the SCSI sense buffer.
--
--- Patch [26]: [PATCH v5 26/56] scsi: qla2xxx: Update handling of status entries for 29xx series ---
commit a044d1392eb6caf2137f3007abf552f366a595c5
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Update handling of status entries for 29xx series
This commit modifies the handling of status entries in the qla2xxx driver to
accommodate the extended structure for the 29xx series. It abstracts the
extraction of status fields via the qla_sts_fwi2_extract helper to handle
both standard and extended layouts.
> 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
> @@ -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;
[Severity: High]
This is a pre-existing issue, but does this code allow an integer underflow
on par_sense_len?
A few lines further down in qla2x00_status_entry(), rsp_info_len is used to
advance sense_data and decrement par_sense_len:
if (IS_FWI2_CAPABLE(ha)) {
sense_data += rsp_info_len;
par_sense_len -= rsp_info_len;
}
If a target sends a maliciously large rsp_info_len, par_sense_len will
underflow. This underflowed value is then passed into
qla2x00_handle_sense(), which uses it for bounds checking before calling
memcpy():
if (sense_len > par_sense_len)
sense_len = par_sense_len;
memcpy(cp->sense_buffer, sense_data, sense_len);
Could this result in an out-of-bounds read of kernel memory into the SCSI
sense buffer, since rsp_info_len isn't validated against the initial size of
par_sense_len before the subtraction?
> 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 {
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=26
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 24/56] scsi: qla2xxx: Add support for QLA29XX in memory allocation
2026-07-17 15:28 ` [PATCH v5 24/56] scsi: qla2xxx: Add support for QLA29XX in memory allocation Nilesh Javali
@ 2026-07-17 16:20 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:20 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] Unconditional calls to `dma_pool_free()` with conditionally allocated (and potentially NULL) pointers cause a kernel panic during error recovery.
--
--- Patch [24]: [PATCH v5 24/56] scsi: qla2xxx: Add support for QLA29XX in memory allocation ---
commit d2e304ac1a70602773864240ceed6148180e105c
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Add support for QLA29XX in memory allocation
This patch enhances the qla2x00_mem_alloc function to include checks for
QLA29XX adapters. This ensures 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:
> - 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;
> }
[Severity: High]
This is a pre-existing issue, but while reviewing the error cleanup paths here,
I noticed a potential issue slightly higher up in the same function.
Could the unconditional calls to dma_pool_free() higher up in the cleanup
sequence cause a kernel panic?
In drivers/scsi/qla2xxx/qla_os.c:qla2x00_mem_alloc(), several DMA pool
allocations are conditional based on adapter capabilities. For example,
ha->ex_init_cb and ha->sf_init_cb are allocated for CNA/newer adapters,
ha->async_pd for non-FWI2, and ha->ms_iocb for non-2100/2200.
If a subsequent memory allocation fails, the error recovery path jumps
to labels that unconditionally call dma_pool_free():
fail_loop_id_map:
dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma);
fail_async_pd:
dma_pool_free(ha->s_dma_pool, ha->sf_init_cb, ha->sf_init_cb_dma);
fail_sf_init_cb:
dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma);
And later:
fail_free_ms_iocb:
dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
If the pointers were never allocated, they remain NULL. Since
dma_pool_free() does not safely handle NULL pointers in its fast path,
calling it with a NULL pointer could trigger a kernel panic during error
recovery. Should these calls be guarded with NULL checks to prevent
crashing when handling an allocation failure?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=24
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 43/56] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats
2026-07-17 15:28 ` [PATCH v5 43/56] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats Nilesh Javali
@ 2026-07-17 16:28 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:28 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 patch introduces corrupted RX/TX word counter reporting for QLA29XX hardware on big-endian architectures due to a double byte-swap bug.
--
--- Patch [43]: [PATCH v5 43/56] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats ---
commit 6dc53192ab6558b115cab1a52a856c1edbdfd7e4
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats
This patch adds IS_QLA29XX() to the high-speed branch in
qla2x00_get_fc_host_stats() so that 29xx reports hardware word counters
instead of falling back to software approximations.
> 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)
> 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);
[Severity: Medium]
Will this result in corrupted RX/TX word counters for QLA29XX on big-endian
systems?
The link_statistics DMA buffer is globally byte-swapped in-place as 32-bit
integers by le32_to_cpus() in qla24xx_get_isp_stats():
drivers/scsi/qla2xxx/qla_mbx.c:qla24xx_get_isp_stats() {
...
/* Re-endianize - firmware data is le32. */
for ( ; dwords--; iter++)
le32_to_cpus(iter);
...
}
For 64-bit fields like fpm_recv_word_cnt, this independently swaps the two
32-bit halves.
Later, this code calls le64_to_cpu() on the result, which effectively jumbles
the 8 bytes into an invalid sequence on big-endian machines.
While this double-swap logic seems to be a pre-existing flaw for older
adapters (83xx/27xx/28xx), this patch specifically introduces the bug for
QLA29XX by disabling its previously working software-based fallback metric
and routing it into the broken hardware reading path.
> p->tx_words = le64_to_cpu(stats->fpm_xmit_word_cnt);
> } else {
> p->rx_words = ib >> 2;
> p->tx_words = ob >> 2;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=43
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 37/56] scsi: qla2xxx: Add size check for extended VP report ID entry
2026-07-17 15:28 ` [PATCH v5 37/56] scsi: qla2xxx: Add size check for extended VP report ID entry Nilesh Javali
@ 2026-07-17 16:29 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:29 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:
- [High] The patch series introduces C bitfields (`__le16 vp_idx : 9`, `__le16 vp_status : 7`) in a hardware descriptor structure and directly extracts them, which breaks on Big-Endian architectures.
Pre-existing issues:
- [High] The `bbcr` field is accessed and masked without being converted to CPU byte order, erroneously disabling buffer-to-buffer credits on big-endian architectures.
- [High] The virtual port (`vp`) pointer is accessed and modified after `ha->vport_slock` is dropped, without acquiring a reference count.
- [High] DMA pool memory is freed immediately upon mailbox command timeout without guaranteeing firmware quiescence, leading to DMA corruption.
- [High] `qla_update_host_map` is called locklessly, violating its requirement for `ha->vport_slock`.
- [High] Unprotected traversal of `vha->vp_fcports` and lockless modifications to `fcport` state.
--
--- Patch [37]: [PATCH v5 37/56] scsi: qla2xxx: Add size check for extended VP report ID entry ---
commit aa254942b9f9f6093a5fcd8b950678e07b111a9c
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Add size check for extended VP report ID entry
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.
> 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;
> + } else {
> + vp_idx = rptid_entry->vp_idx;
> + vp_status = rptid_entry->vp_status;
> + }
> +
[Severity: High]
Are we sure these bitfields are safe on big-endian architectures?
Memory layout of C bitfields is implementation-defined, and on big-endian
platforms, GCC typically allocates bitfields starting from the most
significant bit. Reading the 16-bit little-endian IOCB value from the
hardware will yield mixed bits for vp_idx and vp_status, which might cause
the driver to configure the wrong virtual port or fail entirely.
[ ... ]
> @@ -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;
>
[Severity: High]
This is a pre-existing issue, but does evaluating
(rptid_entry->u.f1.bbcr & 0xf) != 0 without applying le16_to_cpu()
incorrectly check the least significant bits of the host's integer
representation?
On big-endian machines, this checks bits 8-11 of the little-endian value
instead of bits 0-3, which might improperly disable the buffer-to-buffer
credit feature.
> - 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 &&
> (rptid_entry->u.f1.flags &
> BIT_6)) {
> memcpy(vha->port_name,
> rptid_entry->u.f1.port_name,
> WWN_SIZE);
> }
>
> qla_update_host_map(vha, id);
[Severity: High]
This is a pre-existing issue, but is it safe to call qla_update_host_map()
here without holding ha->vport_slock?
qla_update_host_map() modifies the B-tree ha->host_map, which doesn't appear
to be thread-safe. Other parts of the codebase hold the lock before updating
it. Could this result in a data race that corrupts the B-tree?
> }
>
> 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;
> }
> }
[Severity: High]
This is a pre-existing issue, but is the virtual port pointer protected
against concurrent deletion after the lock is dropped?
After dropping ha->vport_slock at the end of this loop, the code does not
increment vp->vref_count via atomic_inc(). If the virtual port is
concurrently removed and freed, could the subsequent use of vp (e.g. in
qla_update_host_map(vp, id)) lead to a use-after-free?
[Severity: High]
This is a pre-existing issue, but is it safe to traverse vha->vp_fcports
and modify fcport states in this file without holding
vha->hw->tgt.sess_lock?
In format == 2 processing later in qla24xx_report_id_acquisition(), the
code traverses the list locklessly:
list_for_each_entry(fcport, &vha->vp_fcports, list) {
fcport->scan_state = QLA_FCPORT_SCAN;
fcport->n2n_flag = 0;
}
In format == 1 processing, the lock is acquired for the lookup but dropped
before modifying fields like fcport->plogi_nack_done_deadline. Since no
reference count is taken on the pointer, could this risk a use-after-free
if a concurrent deletion occurs?
[Severity: High]
This is a pre-existing issue, but in timeout paths like
qla24xx_login_fabric(), is it safe to immediately free DMA pool memory
without guaranteeing firmware quiescence?
When a mailbox command times out, a DMA buffer from ha->s_dma_pool is freed
immediately via dma_pool_free():
dma_pool_free(ha->s_dma_pool, lg_buf, lg_dma);
Since scheduling an ISP abort does not synchronously halt the hardware, could
the active firmware complete the delayed IOCB and write its response to the
freed DMA physical address, causing silent DMA memory corruption?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=37
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 19/56] scsi: qla2xxx: Enable serdes, resource count and FCE trace for 29xx
2026-07-17 15:28 ` [PATCH v5 19/56] scsi: qla2xxx: Enable serdes, resource count and FCE trace " Nilesh Javali
@ 2026-07-17 16:30 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:30 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:
- [High] The `kstrtoul` function in `qla2x00_dfs_fce_write` is called with a NULL pointer for its output parameter, leading to a guaranteed kernel crash when a valid input is parsed.
- [High] Dynamically enabling FCE via debugfs causes a heap buffer overflow during firmware dump collection because the pre-allocated dump buffer is not resized.
- [High] Concurrent execution of `qla2x00_dfs_fce_write` and firmware dump generation causes a use-after-free on `ha->fce`.
- [High] QLA29XX is missing from the adapter shutdown firmware cleanup logic, leading to improper shutdown state.
--
--- Patch [19]: [PATCH v5 19/56] scsi: qla2xxx: Enable serdes, resource count and FCE trace for 29xx ---
commit 128c0c55efd466adf4231392e126249fba36c3dc
Author: Nilesh Javali <njavali@marvell.com>
Subject: 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 since 29xx adapters will now
execute this code, could we fix a NULL pointer dereference later in this
function?
In qla2x00_dfs_fce_write(), kstrtoul() is called with a NULL pointer for the
result:
enable = kstrtoul(buf, 0, 0);
A successful parse will unconditionally write the value to the address
provided, resulting in a kernel panic when a valid number string is
written to the debugfs node.
[Severity: High]
This is a pre-existing issue, but enabling FCE dynamically via debugfs might
corrupt memory when a firmware dump is collected.
When FCE is enabled, qla2x00_dfs_fce_write() attempts to adjust the firmware
dump buffer:
if (!ha->flags.fce_dump_buf_alloced)
qla2x00_alloc_fw_dump(vha);
However, qla2x00_alloc_fw_dump() returns early if the buffer is already
allocated, failing to resize it to accommodate the FCE data:
if (ha->fw_dump) {
ql_dbg(ql_dbg_init, vha, 0x00bd,
"Firmware dump already allocated.\n");
return;
}
If a firmware dump is later triggered, functions like qla27xx_insertbuf()
will append the FCE trace to the undersized buffer:
buf += *len;
memcpy(buf, mem, size);
Could this cause a heap buffer overflow?
[Severity: High]
This is a pre-existing issue, but does the FCE trace buffer free path
have a race condition with firmware dump generation?
When disabling FCE, qla2x00_dfs_fce_write() frees the buffer under fce_mutex:
qla2x00_free_fce_trace(ha);
Concurrently, if a firmware crash triggers a dump, qla27xx_fwdt_entry_t264()
reads and dereferences the pointer without holding fce_mutex:
if (vha->hw->fce) {
...
qla27xx_insertbuf(vha->hw->fce, FCE_SIZE, buf, len);
Can this lead to a use-after-free if the dump routine reads the pointer before
it is freed but executes the memcpy after the free?
[ ... ]
[Severity: High]
This isn't a bug introduced by this patch, but should the IS_QLA29XX check
also be added to qla2x00_shutdown() in qla_os.c?
During system shutdown, the capability gate bypasses
qla2x00_abort_isp_cleanup() for QLA29XX:
if (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) ||
IS_QLA28XX(ha)) {
if (ha->flags.fw_started)
qla2x00_abort_isp_cleanup(vha);
} else {
...
This leaves the firmware and DMA engines active, which might cause memory
corruption or hangs during kexec or a warm reboot.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=19
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 44/56] scsi: qla2xxx: Add 64G/128G port speed setting support
2026-07-17 15:28 ` [PATCH v5 44/56] scsi: qla2xxx: Add 64G/128G port speed setting support Nilesh Javali
@ 2026-07-17 16:32 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:32 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 128G port speed setting is not re-applied across adapter resets, causing it to silently revert to default speeds (e.g., Auto-negotiation).
- [Medium] A Time-Of-Check to Time-Of-Use race condition on `ha->set_data_rate` can bypass the boundary check, causing an out-of-bounds bit shift that corrupts bit 16.
Pre-existing issues:
- [High] Direct assignment to `icb->firmware_options_3` using `=` silently overwrites and clears previously configured NVRAM-derived flags.
--
--- Patch [44]: [PATCH v5 44/56] scsi: qla2xxx: Add 64G/128G port speed setting support ---
commit 8fb2f0a9558bd7064a13dd344fce6bb250eb5ac0
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Add 64G/128G port speed setting support
The port speed setting paths topped out at 32G. This patch maps the 64 and
128 sysfs inputs to PORT_SPEED_64GB and PORT_SPEED_128GB, and accepts those
values in qla2x00_set_data_rate(). Firmware validates the requested rate.
> diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
> index 545066f67e020..5f7bc68aab3c5 100644
> --- a/drivers/scsi/qla2xxx/qla_init.c
> +++ b/drivers/scsi/qla2xxx/qla_init.c
[ ... ]
> @@ -4793,7 +4793,14 @@ qla24xx_config_rings(struct scsi_qla_host *vha)
> ql_dbg(ql_dbg_init, vha, 0x00fd,
> "Speed set by user : %s Gbps \n",
> qla2x00_get_link_speed_str(ha, ha->set_data_rate));
> - icb->firmware_options_3 = cpu_to_le32(ha->set_data_rate << 13);
> + /*
> + * The ICB data-rate field is 3 bits (bits 13-15); rates above
> + * 64G do not fit and would overflow into bit 16 (75 ohm
> + * termination select). Such rates are forced via MBC_DATA_RATE.
> + */
[Severity: High]
The comment mentions rates above 64G are forced via MBC_DATA_RATE. Is this
compensatory MBC_DATA_RATE command actually sent during the adapter
initialization or reset path?
If a user configures 128G via sysfs and the adapter undergoes an ISP abort or
link reset, qla24xx_config_rings() skips setting the speed in the ICB here.
Without a subsequent MBC_DATA_RATE command in the initialization path, does
this cause the 128G setting to be silently lost and revert to default speeds
like Auto-negotiation?
> + if (ha->set_data_rate <= PORT_SPEED_64GB)
[Severity: Medium]
Is there a Time-Of-Check to Time-Of-Use race condition here between the
boundary check and the shift below?
Since ha->set_data_rate can be updated locklessly via the sysfs store
function, if a concurrent write changes the rate from PORT_SPEED_64GB to
PORT_SPEED_128GB exactly between this check and the assignment, will the
shift use the 128GB value?
This could bypass the safety check and cause an out-of-bounds shift
(128 << 13) which overflows into bit 16 and unintentionally toggles the
75 ohm termination select bit on the adapter.
> + icb->firmware_options_3 =
> + cpu_to_le32(ha->set_data_rate << 13);
[Severity: High]
This is a pre-existing issue, but does this direct assignment to
icb->firmware_options_3 silently overwrite previously configured
NVRAM-derived flags?
During adapter initialization, functions like qla24xx_nvram_config() set
multiple flags in icb->firmware_options_3, such as BIT_8 for N2N
driver-initiated login. Using an assignment (=) instead of a
read-modify-write (|= and &= ~MASK) appears to wipe out those previously
configured flags.
> }
>
> /* PCI posting */
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=44
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 01/56] scsi: qla2xxx: Add 29xx series PCI device ID support
2026-07-17 15:28 ` [PATCH v5 01/56] scsi: qla2xxx: Add 29xx series PCI device ID support Nilesh Javali
@ 2026-07-17 16:36 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16: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] The patch fails to integrate IS_QLA29XX() into numerous hardware capability macros and control paths, silently breaking hardware queue pointers, shadow registers, and other driver capabilities.
- [Medium] Initialization of ha->isp_type for QLA29XX incorrectly sets all four device variant bits simultaneously, breaking discrete model tracking.
- [Medium] The patch claims to extend speed-capability logic for 29xx adapters, but the logic is missing from the implementation.
Pre-existing issues:
- [High] Unconditional dereference of unmapped IO memory `ha->mqiobase` if ioremap fails for modern adapters (83XX, 27XX, 28XX, 29XX).
- [High] An integer underflow occurs when calculating the number of supported queue pairs if the PCI device exposes exactly 1 MSI-X vector, leading to out-of-bounds dereferences.
--
--- Patch [1]: [PATCH v5 01/56] scsi: qla2xxx: Add 29xx series PCI device ID support ---
commit b71b2c76f9739885a4fcd4a38d9ac5ed74df7767
Author: Manish Rangankar <mrangankar@marvell.com>
Subject: scsi: qla2xxx: Add 29xx series PCI device ID support
This commit adds support for the new QLA29xx generation of FC HBAs. It
registers the new PCI device IDs and updates various initialization paths
and capability checks to correctly recognize the 29xx adapters.
> 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]
The commit message mentions extending speed-capability logic for 29xx adapters,
but are these changes missing from the implementation? Looking at functions
like qla25xx_fdmi_port_speed_capability() in qla_gs.c, they do not appear to
have been updated to check for IS_QLA29XX, which seems to contradict the
commit message.
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
[ ... ]
@@ -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]
Does this patch miss integrating IS_QLA29XX() into some hardware capability
macros, such as ISP_QUE_REG() and IS_SHADOW_REG_CAPABLE()?
If IS_QLA29XX() is omitted from ISP_QUE_REG():
drivers/scsi/qla2xxx/qla_def.h:ISP_QUE_REG() {
#define ISP_QUE_REG(ha, id) \
((ha->mqenable || IS_QLA83XX(ha) || \
IS_QLA27XX(ha) || IS_QLA28XX(ha)) ? \
((void __iomem *)ha->mqiobase + (QLA_QUE_PAGE * id)) :\
((void __iomem *)ha->iobase))
}
The driver will incorrectly fall back to ha->iobase instead of ha->mqiobase
when mqenable is false, causing queue pointers to be initialized to the wrong
MMIO registers and leading to timeouts and crashes when qla24xx_config_rings()
is called.
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
[ ... ]
@@ -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;
[Severity: High]
This is a pre-existing issue, but can this error path lead to a NULL pointer
dereference? If ioremap for ha->mqiobase fails earlier in
qla83xx_iospace_config(), or if we jump to mqiobase_exit here, the function
returns 0 (success). Later in probe, qla2x00_probe_one() unconditionally
computes offsets against ha->mqiobase for these modern adapters:
drivers/scsi/qla2xxx/qla_os.c:qla2x00_probe_one() {
if (ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha) ||
IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
req->req_q_in = &ha->mqiobase->isp25mq.req_q_in;
...
}
This would cause a crash during I/O since ha->mqiobase is NULL.
[Severity: High]
This is a pre-existing issue, but does this calculation risk integer underflow
if the PCI device exposes exactly 1 MSI-X vector? If msix_cnt is 1, the code
later in qla83xx_iospace_config() does:
drivers/scsi/qla2xxx/qla_os.c:qla83xx_iospace_config() {
ha->max_req_queues = ha->msix_count - 1;
...
ha->max_qpairs = ha->max_req_queues - 1;
}
Since ha->max_qpairs is a uint8_t, it would underflow to 255. During probe,
qla2x00_probe_one() will assign this underflowed value to host->nr_hw_queues if
ql2xnvmeenable is active, which exposes 255 nonexistent hardware queues to the
NVMe/block layer and can lead to out-of-bounds accesses.
+ }
+ 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;
[Severity: Medium]
Will setting all four discrete device variant bits simultaneously break the
discrete model tracking? Since ha->isp_type contains all four bits, macros
like IS_QLA2091() will return true for any QLA29xx adapter. Should this code
only set the specific bit that corresponds to the detected PCI device ID?
+ 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;
}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=1
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 38/56] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series
2026-07-17 15:28 ` [PATCH v5 38/56] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series Nilesh Javali
@ 2026-07-17 16:36 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:36 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 `vp_idx` field in `struct qla_nvme_lsrjt_pt_arg` is defined as `u8`, which silently truncates the 16-bit Virtual Port index to 8 bits when sending NVMe LS Rejects, breaking 9-bit VP index support for 29xx adapters.
- [High] Unsolicited NVMe LS request handling (`PT_LS4_UNSOL`) lacks support for the 9-bit Virtual Port index on 29xx adapters.
Pre-existing issues:
- [High] `qla_nvme_ls_reject_iocb()` modifies the shared hardware request ring without holding the hardware queue lock (`qp_lock_ptr`).
--
--- Patch [38]: [PATCH v5 38/56] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series ---
commit 5707bda86601ab783dbcbdb4edacb7475a352b7d
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
> @@ -1197,37 +1197,57 @@ static void qla_nvme_fc_format_rjt(void *buf, u8 ls_cmd, u8 reason,
[ ... ]
> + if (IS_QLA29XX(ha)) {
> + struct pt_ls4_request_ext *ext = lsrjt_iocb;
> +
> + ext->vp_index = cpu_to_le16(a->vp_idx);
[Severity: High]
Does this assignment truncate the 16-bit virtual port index to 8 bits?
The struct qla_nvme_lsrjt_pt_arg defines vp_idx as a u8. When an unsolicited
NVMe LS request fails, qla2xxx_process_purls_pkt() assigns the 16-bit
vha->vp_idx to a.vp_idx, dropping the high bits:
qla2xxx_process_purls_pkt()
a.vp_idx = vha->vp_idx;
This truncates any virtual port index >= 256. When qla_nvme_lsrjt_pt_iocb()
then zero-extends this back to 16 bits for the extended IOCB here, the NVMe
LS Reject IOCB will be routed to the wrong virtual port.
> + 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 {
[ ... ]
> @@ -1245,13 +1265,13 @@ 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);
[Severity: High]
This is a pre-existing issue, but does qla_nvme_ls_reject_iocb() modify
the shared hardware request ring without holding the hardware queue lock?
When called from the DPC workqueue thread via qla2xxx_process_purls_pkt(),
the caller does not hold the queue lock (qp_lock_ptr):
vha->iocb_work (workqueue thread)
qla2xxx_process_purls_pkt()
qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true);
This function calls __qla2x00_alloc_iocbs() here, and later calls
qla2x00_start_iocbs(), both of which require the lock to safely update
the request ring state. Can this concurrent modification corrupt the ring
structure?
> if (!lsrjt_iocb) {
> ql_log(ql_log_warn, vha, 0x210e,
> "qla2x00_alloc_iocbs failed.\n");
> return QLA_FUNCTION_FAILED;
> }
[ ... ]
> diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
> index f539190dd5044..186c6c7a39448 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);
[Severity: High]
Does the handling for unsolicited NVMe LS requests also need a 29xx
extension to support 9-bit virtual port indices?
The struct pt_ls4_rx_unsol defines vp_index as an 8-bit uint8_t. When the
29xx firmware passes an unsolicited NVMe LS request to the driver,
qla2xxx_process_purls_iocb() extracts the 8-bit vp_index to look up the
virtual port:
qla2xxx_process_purls_iocb()
vha = qla2xxx_get_vha_from_vp_idx(ha, p->vp_index);
If the incoming request is for a virtual port >= 256, truncating the 9-bit
index provided by the 29xx firmware will cause the driver to route the
request to the wrong virtual port.
> 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);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=38
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 39/56] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support
2026-07-17 15:28 ` [PATCH v5 39/56] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support Nilesh Javali
@ 2026-07-17 16:38 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16: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:
- [High] The `qla2x00_do_dport_diagnostics` BSG handler leaks up to 128 bytes of uninitialized kernel heap memory to userspace.
- [High] The D-port diagnostics function passes an unaligned inner struct field to `dma_map_single()` and subsequently modifies it with `memset()` after it has been mapped for `DMA_FROM_DEVICE`, violating DMA API rules and risking memory corruption.
- [High] The `qla2x00_update_fru_versions` BSG handler writes uninitialized kernel stack memory to the adapter's persistent storage (NVRAM/flash) and can cause a kernel panic when provided a malformed request.
--
--- Patch [39]: [PATCH v5 39/56] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support ---
commit e379ff9d45844a3d1de7b4db9393d48209a2ed9c
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 46eed5df7eefb..9ee56ccd52b01 100644
> --- a/drivers/scsi/qla2xxx/qla_bsg.c
> +++ b/drivers/scsi/qla2xxx/qla_bsg.c
> @@ -2014,7 +2014,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;
> }
[Severity: High]
This isn't a bug introduced by this patch, but does qla2x00_update_fru_versions()
in drivers/scsi/qla2xxx/qla_bsg.c have an out-of-bounds read?
If a user provides a small payload but specifies a large count, the loop:
while (count--) {
memcpy(sfp, &image->field_info, sizeof(image->field_info));
will iterate over uninitialized trailing stack memory in the bsg array
since there is no bounds check on count against the payload size.
Could this leak kernel stack memory to hardware storage or cause a panic?
[ ... ]
> @@ -2855,7 +2855,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);
[Severity: High]
This isn't a bug introduced by this patch, but by allowing QLA29xx adapters
here, does qla2x00_do_dport_diagnostics() leak kernel heap memory?
The dd buffer is allocated without zero-initialization. If a user provides
a short request payload (e.g., 0 bytes), trailing fields like dd->options
and the 62-byte unused array remain uninitialized.
When sg_copy_from_buffer() copies the full 128 bytes back to the user's
reply payload, could this disclose uninitialized kernel heap memory?
[ ... ]
> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index cec308811d9eb..ce845afe3e16a 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,
[Severity: High]
This isn't a bug introduced by this patch, but does exposing this to QLA29xx
adapters risk memory corruption via DMA API violations in
qla26xx_dport_diagnostics() in drivers/scsi/qla2xxx/qla_mbx.c?
The struct qla_dport_diag is packed, placing its buf array at an unaligned
offset. When this is mapped:
dd_dma = dma_map_single(&vha->hw->pdev->dev, dd_buf, size, DMA_FROM_DEVICE);
Mapping an unaligned address violates DMA rules. Furthermore, immediately
after mapping, the memory is modified:
memset(dd_buf, 0, size);
Does writing to memory while mapped for DMA_FROM_DEVICE dirty the CPU cache,
which could overwrite the hardware DMA results upon eviction?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=39
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 33/56] scsi: qla2xxx: Add abort command handling for 29xx series
2026-07-17 15:28 ` [PATCH v5 33/56] scsi: qla2xxx: Add abort command " Nilesh Javali
@ 2026-07-17 16:42 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:42 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] Race condition in asynchronous aborts (e.g., ELS DCMD timeouts) leads to a Use-After-Free of the original command SRB (`orig_sp`) and potential memory corruption of newly allocated NVMe commands.
--
--- Patch [33]: [PATCH v5 33/56] scsi: qla2xxx: Add abort command handling for 29xx series ---
commit d0967e5f98d09c6d227bb75d1093a54e5b842954
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Add abort command handling for 29xx series
Extend the abort-IOCB code path to support the 29xx extended
abort_entry_24xx_ext structure alongside the existing
abort_entry_24xx.
> 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
[ ... ]
> @@ -3973,7 +3973,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)
[Severity: High]
This is a pre-existing issue, but could this race condition cause a
use-after-free on orig_sp?
If an SRB_ELS_DCMD or SRB_LOGIN_CMD times out, it triggers an async abort.
Since wait is false, the timeout handler returns immediately without waiting
for the abort to complete.
If the firmware completes the original command (e.g., with CS_ABORTED), the
driver processes the completion, calls the done() callback, and frees the SRB
back to srb_mempool.
When the firmware later completes the ABT_IOCB, this code reads the now-freed
orig_sp:
qla24xx_abort_iocb_entry() {
orig_sp = sp->cmd_sp;
if (orig_sp)
qla_nvme_abort_process_comp_status(pkt, orig_sp);
}
If that freed memory was reallocated for an active NVMe command,
qla_nvme_abort_process_comp_status() dereferences it, and
qla_wait_nvme_release_cmd_kref() drops the cmd_kref:
qla_wait_nvme_release_cmd_kref() {
if (!(ql2xabts_wait_nvme && QLA_ABTS_WAIT_ENABLED(orig_sp)))
return;
kref_put(&orig_sp->cmd_kref, orig_sp->put_fn);
}
Wouldn't this prematurely free an unrelated active NVMe command and cause
memory corruption?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=33
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 51/56] scsi: qla2xxx: Initialize NVMe abort_work once at submission
2026-07-17 15:28 ` [PATCH v5 51/56] scsi: qla2xxx: Initialize NVMe abort_work once at submission Nilesh Javali
@ 2026-07-17 16:45 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16: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:
- [High] Concurrent calls to qla_nvme_fcp_abort or qla_nvme_ls_abort leak cmd_kref references because the return value of schedule_work() is ignored, leading to a permanent transport hang.
--
--- Patch [51]: [PATCH v5 51/56] scsi: qla2xxx: Initialize NVMe abort_work once at submission ---
commit ae623a4cb67f2592c5287e656c5d7842f196c53f
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Initialize NVMe abort_work once at submission
This commit moves the initialization of priv->abort_work from the abort path to
the command submission path. This prevents workqueue corruption when concurrent
aborts are issued 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
> @@ -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);
[Severity: High]
Does this code leak the cmd_kref reference if schedule_work() returns false?
Since INIT_WORK() was removed, schedule_work() can now return false if the work
item is already pending (e.g., from concurrent aborts). However, a reference was
unconditionally acquired earlier in the function via kref_get_unless_zero().
If schedule_work() returns false, the work function won't execute an extra time
to drop this reference, which could permanently hang NVMe-FC transport teardown.
> }
>
[ ... ]
> @@ -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);
[Severity: High]
This appears to have the exact same reference leak pattern as qla_nvme_ls_abort()
when schedule_work() returns false. Should we drop the kref if schedule_work()
fails to queue the work?
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=51
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 41/56] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors
2026-07-17 15:28 ` [PATCH v5 41/56] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors Nilesh Javali
@ 2026-07-17 16:46 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16: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] Incomplete fix: `struct vp_rpt_id_entry_24xx_ext` still contains `__le16` bitfields, which causes endianness corruption on big-endian hosts.
Pre-existing issues:
- [High] Hardware-mapped target mode structures use `uint8_t` bitfields that are improperly packed on big-endian architectures.
--
--- Patch [41]: [PATCH v5 41/56] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors ---
commit 0d741959136e452484c2d9e17866323337b3d108
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors
This commit replaces __le16 bitfields in the 29xx extended IOCB structures
with a single __le16 scalar field and accessor functions. This change
addresses endianness corruption on big-endian hosts caused by C bitfield
packing order.
> 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]
Does this patch miss replacing the __le16 bitfields in the
vp_rpt_id_entry_24xx_ext structure?
Looking at drivers/scsi/qla2xxx/qla_fw29.h:
struct vp_rpt_id_entry_24xx_ext {
...
__le16 vp_idx : 9;
__le16 vp_status : 7;
...
};
When the firmware sends a 29xx extended VP Report ID IOCB,
qla24xx_report_id_acquisition() extracts the port ID from the bitfield:
drivers/scsi/qla2xxx/qla_mbx.c:qla24xx_report_id_acquisition() {
...
vp_idx = rptid_entry_ext->vp_idx;
vp_status = rptid_entry_ext->vp_status;
...
}
On big-endian architectures, C bitfield packing allocates from MSB to LSB.
Could the vp_idx field map to the wrong bits of the 16-bit word, causing
virtual port lookup to fail and breaking NPIV operations?
[Severity: High]
This is a pre-existing issue, but do the hardware-mapped target mode
structures use uint8_t bitfields that are improperly packed on big-endian
architectures?
In drivers/scsi/qla2xxx/qla_target.h, both abts_recv_from_24xx and
abts_resp_to_24xx define bitfields:
struct abts_recv_from_24xx {
...
uint8_t reserved_3:4;
uint8_t sof_type:4;
...
};
On big-endian hosts, sof_type maps to bits 0-3 instead of bits 4-7.
When qlt_24xx_send_abts_resp() assigns the response:
drivers/scsi/qla2xxx/qla_target.c:qlt_24xx_send_abts_resp() {
...
resp->sof_type = abts->sof_type;
...
}
Will the hardware receive a corrupted byte where sof_type is encoded in the
wrong nibble, breaking target mode ABTS task management responses?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=41
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 35/56] scsi: qla2xxx: Update VP control IOCB handling for 29xx series
2026-07-17 15:28 ` [PATCH v5 35/56] scsi: qla2xxx: Update VP control IOCB handling " Nilesh Javali
@ 2026-07-17 16:48 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:48 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 VP control IOCB builder `qla25xx_ctrlvp_iocb()` lacks a bounds check when calculating the index for `vce->vp_idx_map`, allowing an out-of-bounds array write if `vp_index` is large (> 128), which corrupts adjacent IOCB payload fields.
--
--- Patch [35]: [PATCH v5 35/56] scsi: qla2xxx: Update VP control IOCB handling for 29xx series ---
commit 38b9d8323d247867ce71b530d5556e360732b075
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Update VP control IOCB handling for 29xx series
This updates VP control IOCB command and response handling to support the
29xx series adapters. It modifies helper functions to accept generic void
pointers so they can handle both standard and extended VP control entry
layouts interchangeably.
> 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
[ ... ]
> @@ -4074,22 +4074,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,
[ ... ]
> /*
> * 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;
[Severity: High]
This is a pre-existing issue, but does this code overflow the vp_idx_map
array if vp_index exceeds 128?
Looking at the struct definition for vp_ctrl_entry_24xx in qla_fw.h,
vp_idx_map is a 16-byte array. If the firmware advertises support for more
than 128 NPIV vports, the calculated map index in qla25xx_ctrlvp_iocb() could
be up to 31.
Could this out-of-bounds write corrupt the subsequent fields in the
vp_ctrl_entry_24xx payload (like flags, id, and hopct) when an administrator
configures a large number of vports?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=35
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 40/56] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking
2026-07-17 15:28 ` [PATCH v5 40/56] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking Nilesh Javali
@ 2026-07-17 16:49 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:49 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:
- [High] Interrupt handler drops valid interrupts if any lower-numbered queue is deleted, due to misuse of find_first_zero_bit().
- [High] Sysfs firmware dump can concurrently read freed `req->ring` or `rsp->ring` memory.
- [High] Race condition during qpair deletion causes UAF of `req->outstanding_cmds` in the interrupt handler.
- [High] Orphaned request/response queues when deleting queue pairs created with `startqp = false`.
--
--- Patch [40]: [PATCH v5 40/56] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking ---
commit 9e994d85d8469000c7bf32a3a2ebd3db56b893a4
Author: Nilesh Javali <njavali@marvell.com>
Date: Fri Jul 17 15:28:48 2026 +0000
scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking
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():
[ ... ]
> 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 isn't a bug introduced by this patch, but does this create a use-after-free
or NULL pointer dereference during sysfs firmware dumps?
If a firmware dump is triggered concurrently with a queue teardown,
qla25xx_copy_mqueues() copies queue memory into the dump buffer under
hardware_lock. However, here we free the DMA memory and set req->ring to NULL
before the queue is removed from req_q_map, and without holding hardware_lock:
drivers/scsi/qla2xxx/qla_dbg.c:qla25xx_copy_mqueues() {
...
memcpy(ptr, req->ring, req->length * req_entry_size);
...
}
> 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);
[Severity: High]
This is a pre-existing issue, but does clearing bits here expose a flaw with
how interrupts are validated?
If multiple queue pairs are created and a lower-numbered queue is deleted,
this clear_bit creates a hole in the bitmask. When an interrupt arrives for
a higher-numbered queue, qla2x00_status_entry() validates it using:
drivers/scsi/qla2xxx/qla_isr.c:qla2x00_status_entry() {
...
if (req == NULL ||
que >= find_first_zero_bit(ha->req_qid_map, ha->max_req_queues)) {
...
}
The find_first_zero_bit call returns the index of the deleted queue, causing
the check to incorrectly drop valid interrupts for the higher-numbered queues.
> - mutex_unlock(&ha->vport_lock);
> + mutex_unlock(&ha->mq_lock);
> }
> kfree(req->outstanding_cmds);
[Severity: High]
This is a pre-existing issue, but could this cause a use-after-free of
req->outstanding_cmds in the interrupt handler?
During queue pair deletion in qla2xxx_delete_qpair(), the request queue is
deleted and freed here before the response queue is deleted. The response
queue's MSI-X interrupt remains active during this window.
If an interrupt fires (or q_work is executing), it reads req without mq_lock.
If it fetches req just before it is nullified but dereferences
req->outstanding_cmds after it is freed here, a use-after-free occurs.
Additionally, the qpair->q_work work item appears to never be canceled.
> kfree(req);
> }
[Severity: High]
This is a pre-existing issue, but do we leak these request and response
queues when they are created with startqp = false?
If a queue pair is created with startqp = false, vha->flags.qpairs_req_created
is not set. Later, qla25xx_delete_req_que() skips calling qla25xx_free_req_que()
completely because of this check:
drivers/scsi/qla2xxx/qla_mid.c:qla25xx_delete_req_que() {
...
if (req && vha->flags.qpairs_req_created) {
...
qla25xx_free_req_que(vha, req);
}
return ret;
}
The same bypass happens in qla25xx_delete_rsp_que(). This seems to leak the
DMA rings, struct req/rsp, and the map indexes forever.
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=40
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 34/56] scsi: qla2xxx: Enhance ABTS processing for 29xx series
2026-07-17 15:28 ` [PATCH v5 34/56] scsi: qla2xxx: Enhance ABTS processing " Nilesh Javali
@ 2026-07-17 16:49 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16: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] Usage of C bitfields in the hardware-defined DMA structure `struct abts_entry_24xx_ext` breaks data layout on Big-Endian architectures.
Pre-existing issues:
- [Critical] The DMA buffer `rsp_pkt` can be freed while the hardware firmware is still actively processing it if the mailbox command times out.
--
--- Patch [34]: [PATCH v5 34/56] scsi: qla2xxx: Enhance ABTS processing for 29xx series ---
commit 64217fa79af8b429a3a92eb33321f697d422a0cf
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Enhance ABTS processing for 29xx series
This patch enhances ABTS processing for the 29xx series adapters by using
extended ABTS entry structures to handle the larger 128-byte IOCB format.
It introduces type-generic macros for logging and response construction
to avoid code duplication, branching on IS_QLA29XX() for type handling.
> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> index 6fbcbdd208c38..8367aa005d1b5 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_LOG_ISSUE_ABTS_RSP(vha, rsp, dma, rval) do { \
[ ... ]
> + (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) { \
[ ... ]
> @@ -140,60 +212,27 @@ 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);
[Severity: High]
Does reading and writing the sof_type bitfield directly cause data corruption
on big-endian systems?
Since struct abts_entry_24xx_ext defines sof_type as a C bitfield over an
__le16 integer:
__le16 vp_idx : 9;
__le16 reserved_1_sof : 3;
__le16 sof_type : 4;
Compilers on big-endian architectures will reverse the packing order of the
bits. If the hardware expects fixed little-endian bit offsets for DMA
reads/writes, wouldn't passing abts_ext->sof_type directly result in a
mangled bit sequence?
> + 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: Critical]
This is a pre-existing issue, but can this lead to kernel memory corruption
if the mailbox command times out?
In qla24xx_process_abts(), qla2x00_issue_iocb() executes a synchronous IOCB
command (called via QLA_LOG_ISSUE_ABTS_RSP). If this command times out, it
returns an error but the hardware reset is only scheduled asynchronously,
meaning the hardware might still be actively processing.
The function then unconditionally calls dma_free_coherent() on rsp_pkt. If
the hardware is delayed and finishes processing the timed-out command later,
could it DMA-write its completion status to this now-freed memory?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=34
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 48/56] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size
2026-07-17 15:28 ` [PATCH v5 48/56] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size Nilesh Javali
@ 2026-07-17 16:53 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16: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] The patch prevents disabling virtual ports with an index > 128 but fails to prevent their creation. This creates an asymmetric lifecycle where high-index vports can be successfully created but will silently fail to be disabled, permanently leaking firmware resources.
--
--- Patch [48]: [PATCH v5 48/56] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size ---
commit d14a096bfdf302413bad8d30ac696540d3cc6f7a
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size
This commit adds bounds checking for vp_index when issuing VP_CTRL IOCBs
to prevent out-of-bounds writes to the vp_idx_map array. It explicitly
rejects commands for virtual ports with an index greater than 128.
> diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
> --- 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 is a pre-existing issue, but by blocking control commands for virtual
ports with an index over 128, does this create an asymmetric lifecycle that
leaks firmware resources?
If the firmware advertises ha->max_npiv_vports up to 255, creating a vport
with an index > 128 can still succeed since qla24xx_vport_create() uses
VP_CONFIG_IOCB, which has an 8-bit index capable of addressing up to 255.
However, when deleting the vport, qla24xx_vport_delete() calls
qla24xx_disable_vp(), which in turn calls qla24xx_control_vp() to send
the disable command. With this new check, disabling the vport will fail
and return QLA_PARAMETER_ERROR.
Looking at qla24xx_vport_delete(), it ignores the return value of
qla24xx_disable_vp():
drivers/scsi/qla2xxx/qla_attr.c:qla24xx_vport_delete() {
...
qla24xx_disable_vp(vha);
qla2x00_wait_for_sess_deletion(vha);
qla_nvme_delete(vha);
qla_enode_stop(vha);
qla_edb_stop(vha);
vha->flags.delete_progress = 1;
...
}
Since the failure is ignored, the OS-side structures are destroyed and the
vp_idx is freed for reuse, but the firmware port remains fully active and
logged into the fabric. Reusing the ID later could then corrupt the firmware
state machine.
Should ha->max_npiv_vports be capped at 128 during initialization to
prevent creating these unmanageable virtual ports in the first place?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=48
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 56/56] scsi: qla2xxx: Update version to 12.00.00.2607b1
2026-07-17 15:29 ` [PATCH v5 56/56] scsi: qla2xxx: Update version to 12.00.00.2607b1 Nilesh Javali
@ 2026-07-17 16:54 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:54 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 new odd-length driver version string triggers an out-of-bounds read vulnerability in qla82xx_set_driver_version().
--
--- Patch [56]: [PATCH v5 56/56] scsi: qla2xxx: Update version to 12.00.00.2607b1 ---
commit 06852b0036d05a19a62576cee2a7f97ae01c17ca
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Update version to 12.00.00.2607b1
This commit updates the QLA2XXX driver version from 10.02.10.100-k to
12.00.00.2607b1 and adjusts the corresponding major, minor, patch, and
beta version macros.
> 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]
This isn't a bug introduced by this patch, but could changing the version
string to an odd length trigger an out-of-bounds read regression in
qla82xx_set_driver_version()?
Looking at drivers/scsi/qla2xxx/qla_mbx.c:qla82xx_set_driver_version():
for (i = 4; i < 16 && len; i++, str++, len -= 2) {
mcp->mb[i] = le16_to_cpup(str);
mcp->out_mb |= 1<<i;
}
Because `len` is a signed integer and is decremented by 2, an odd-length string
will bypass the zero check and become negative. The `&& len` condition will
remain true, causing the loop to read past the end of the version string
literal.
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=56
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 52/56] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject
2026-07-17 15:29 ` [PATCH v5 52/56] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject Nilesh Javali
@ 2026-07-17 16:55 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:55 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 `list_del()` on the error path in `qla_nvme_xmt_ls_rsp()` leaves a freed object in `fcport->unsol_ctx_head`, causing use-after-free and list corruption.
- [High] Concurrent, unlocked list operations on `fcport->unsol_ctx_head` lead to race conditions and list pointer corruption.
- [High] Unconditional hardware doorbell writes in `qla_nvme_xmt_ls_rsp()` when firmware is stopped.
- [Medium] Concurrent handling of unsolicited NVMe Link Services on different response queues causes a data race on the globally shared reject DMA buffer (`vha->hw->lsrjt.c`).
--
--- Patch [52]: [PATCH v5 52/56] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject ---
commit 4dcde06839422633d8d12bf8b040c6155f279d01
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject
This patch adds queue pair lock protection around calls to
qla_nvme_ls_reject_iocb() in unlocked purex work and xmt_ls_rsp contexts.
This prevents concurrent accesses from corrupting the request ring
producer state.
> 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);
> kfree(uctx);
[Severity: Critical]
This isn't a bug introduced by this patch, but does the error path in
qla_nvme_xmt_ls_rsp() leave a freed object in the list?
If we reach the out label (for instance, when !ha->flags.fw_started is true),
kfree(uctx) is called, but list_del(&uctx->elem) is omitted. Could this lead
to a use-after-free if the fcport->unsol_ctx_head list is traversed later?
[Severity: High]
This is also a pre-existing issue, but does calling qla_nvme_ls_reject_iocb()
here unconditionally ring the hardware doorbell even when the firmware is
stopped?
Earlier in qla_nvme_xmt_ls_rsp(), if !ha->flags.fw_started is true, it jumps
to the out label, which calls qla_nvme_ls_reject_iocb(). That helper allocates
an iocb and calls qla2x00_start_iocbs(), which writes to the hardware doorbell.
Can this cause hardware faults on a stopped or resetting device?
> 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,
[Severity: Medium]
This is a pre-existing issue, but can concurrent calls to
qla_nvme_ls_reject_iocb() corrupt the shared reject buffer?
When called with is_xchg_terminate as false from different response queues,
the helper writes directly to the globally shared vha->hw->lsrjt.c buffer:
qla_nvme_ls_reject_iocb() {
...
if (!is_xchg_terminate) {
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 multiple response queues can process unsolicited link service packets
concurrently while holding different queue-specific locks, can this lead to
torn writes and corrupted payloads being sent to the hardware?
> struct qla_nvme_lsrjt_pt_arg *a, bool is_xchg_terminate)
[ ... ]
> @@ -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);
[Severity: High]
This is a pre-existing issue, but is the fcport->unsol_ctx_head list protected
against concurrent modifications?
In qla2xxx_process_purls_pkt(), list_del(&uctx->elem) is called from a workqueue
context without holding a lock:
qla2xxx_process_purls_pkt() {
...
list_del(&uctx->elem);
kfree(uctx);
...
}
Elements are added to this list concurrently using list_add_tail() from the
response queue context (like in qla2xxx_process_purls_iocb), and removed
from other contexts (like qla_nvme_release_lsrsp_cmd_kref). Could this result
in list pointer corruption?
> }
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=52
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 54/56] scsi: qla2xxx: Fix BSG job leak on validate flash image error path
2026-07-17 15:29 ` [PATCH v5 54/56] scsi: qla2xxx: Fix BSG job leak on validate flash image error path Nilesh Javali
@ 2026-07-17 16:57 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:57 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] Pervasive slab out-of-bounds reads when accessing BSG vendor_cmd payload due to missing request_len checks.
--
--- Patch [54]: [PATCH v5 54/56] scsi: qla2xxx: Fix BSG job leak on validate flash image error path ---
commit f84362ea09312f70b70c60c0ecfaeb501ca572d1
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Fix BSG job leak on validate flash image error path
qla28xx_validate_flash_image() returns QLA_SUCCESS unconditionally, telling
the FC BSG transport that the driver owns and will complete the request.
However, bsg_job_done() was guarded by an error check, causing requests to
dangle on error paths until timeout. This commit removes the error check
to ensure the job is always completed.
> diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
> index 3bf3a7b96eb50..2f73c9af418d7 100644
> --- a/drivers/scsi/qla2xxx/qla_bsg.c
> +++ b/drivers/scsi/qla2xxx/qla_bsg.c
> @@ -3848,9 +3848,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);
[Severity: High]
This isn't a bug introduced by this patch, but there is a pervasive slab
out-of-bounds read issue when accessing the BSG vendor_cmd payload due to
missing request_len checks.
Is it possible for a privileged user (with CAP_SYS_RAWIO) to issue a short
BSG command with a small request_len (e.g. 4 or 8 bytes), causing the driver
to read out-of-bounds slab memory?
For example, when this function calls qla28xx_do_validate_flash_image(),
the command reads vendor_cmd[1] unconditionally:
qla28xx_do_validate_flash_image() {
...
img_idx = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
...
}
Similarly, earlier in the BSG path, qla24xx_bsg_request() unconditionally
reads vendor_cmd[0] before checking if the request length covers it:
qla24xx_bsg_request() {
...
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)
...
}
Should the transport layer or the driver validate that the user's length is
large enough before accessing these vendor command array elements to avoid
a KASAN panic or executing arbitrary driver logic with uninitialized memory?
>
> return QLA_SUCCESS;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=54
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 49/56] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config()
2026-07-17 15:28 ` [PATCH v5 49/56] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config() Nilesh Javali
@ 2026-07-17 16:59 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 16:59 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 if the mailbox command times out, the IOCB DMA memory is freed immediately while the hardware may still write to it, leading to potential DMA-to-freed-memory corruption.
--
--- Patch [49]: [PATCH v5 49/56] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config() ---
commit 3dcfe3a8a4d45934e8e20a4f3d032dc1de73fa0f
Author: Nilesh Javali <njavali@marvell.com>
Subject: scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config()
The commit fixes the completion status check in qla24xx_modify_vp_config() by
correctly examining entry_status instead of testing comp_status twice. This
prevents the driver from incorrectly initializing the virtual port when the
firmware has rejected the IOCB.
> 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,
[ ... ]
> rval = QLA_FUNCTION_FAILED;
> } else {
> ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c0,
> "Done %s.\n", __func__);
> fc_vport_set_state(vha->fc_vport, FC_VPORT_INITIALIZING);
> }
> dma_pool_free(ha->s_dma_pool, vpmod, vpmod_dma);
[Severity: High]
This isn't a bug introduced by this patch, but if the mailbox command times
out, could this lead to DMA-to-freed-memory corruption?
If qla2x00_issue_iocb() returns QLA_FUNCTION_TIMEOUT (which schedules an
asynchronous ISP abort), qla24xx_modify_vp_config() immediately frees the
vpmod DMA buffer back to s_dma_pool.
Since the hardware has not yet been reset, could it still complete the
delayed command and perform a DMA write of the completion status to the
vpmod_dma address?
If the DMA pool has reused this memory for another command, could the delayed
write corrupt the new structure?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=49
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 55/56] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions()
2026-07-17 15:29 ` [PATCH v5 55/56] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions() Nilesh Javali
@ 2026-07-17 17:04 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 17:04 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:
- [Critical] Severe heap and stack buffer overflows in adjacent I2C BSG handlers due to unvalidated length parameters.
- [High] Systemic uninitialized kernel stack memory disclosure due to lack of request payload length validation.
--
--- Patch [55]: [PATCH v5 55/56] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions() ---
commit f53c11833e45a89be53a1b6f8eff4d77147f2782
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions()
This commit introduces a bounds check in qla2x00_update_fru_versions() to
ensure the user-supplied count does not exceed the size of the local stack
buffer. It rejects requests where the declared count would cause an
out-of-bounds read.
> diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
> index 2f73c9af418d7..95cfdb020319d 100644
> --- a/drivers/scsi/qla2xxx/qla_bsg.c
> +++ b/drivers/scsi/qla2xxx/qla_bsg.c
> @@ -2043,6 +2043,13 @@ qla2x00_update_fru_versions(struct bsg_job *bsg_job)
>
> image = list->version;
> count = list->count;
> +
> + if (struct_size(list, version, count) > sizeof(bsg)) {
[Severity: High]
This isn't a bug introduced by this patch, but does this bounds check
fully protect against reading uninitialized memory?
The check limits the loop count against the fixed sizeof(bsg) (256 bytes),
but it doesn't appear to check bsg_job->request_payload.payload_len.
If a user issues a BSG request with a short payload, sg_copy_to_buffer()
will only populate a portion of the 256-byte array, leaving the remainder
uninitialized:
qla2x00_update_fru_versions() {
...
sg_copy_to_buffer(bsg_job->request_payload.sg_list,
bsg_job->request_payload.sg_cnt, list, sizeof(bsg));
...
while (count--) {
memcpy(sfp, &image->field_info, sizeof(image->field_info));
rval = qla2x00_write_sfp(...);
}
Could a short payload cause the loop to read uninitialized kernel stack
memory and send it to the device?
Similarly, in adjacent functions like qla2x00_read_fru_status() and
qla2x00_read_i2c(), if the initial request payload is short, could
sg_copy_from_buffer() copy uninitialized stack memory back to user space?
[Severity: Critical]
This is a pre-existing issue, but do adjacent I2C BSG handlers lack length
validation?
In qla2x00_write_i2c(), i2c->length comes from user input and appears to be
unconstrained. Could this cause a massive out-of-bounds read from the
stack into the sfp DMA pool allocation?
qla2x00_write_i2c() {
...
memcpy(sfp, i2c->buffer, i2c->length);
...
}
Likewise, in qla2x00_read_i2c(), could an unvalidated i2c->length allow the
device to DMA up to 65535 bytes into the 256-byte sfp heap buffer, followed
by a stack buffer overflow?
qla2x00_read_i2c() {
...
rval = qla2x00_read_sfp(vha, sfp_dma, sfp,
i2c->device, i2c->offset, i2c->length, i2c->option);
...
memcpy(i2c->buffer, sfp, i2c->length);
...
}
> + 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,
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=55
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 50/56] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition()
2026-07-17 15:28 ` [PATCH v5 50/56] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition() Nilesh Javali
@ 2026-07-17 17:08 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 17:08 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:
- [High] Deferred Use-After-Free due to re-inserting a dying vport into the global host_map B-tree.
- [High] Data race and potential B-tree corruption due to missing locks in qla_update_host_map.
- [High] Use-After-Free during list traversal due to dropping locks inside list_for_each_entry_safe.
- [High] Forced Use-After-Free due to hardcoded timeout in vref_count polling.
--
--- Patch [50]: [PATCH v5 50/56] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition() ---
commit 888c874111e5c34314eddd622176ba0ffd527654
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition()
This patch adds vport reference counting inside
qla24xx_report_id_acquisition() by incrementing vref_count when the matching
vp is found under vport_slock. The reference is held during the virtual port
updates to prevent a concurrent qla24xx_deallocate_vp_id() from tearing down
and freeing the vport prematurely.
> 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
> @@ -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);
[Severity: High]
This isn't a bug introduced by this patch, but does dropping the lock before
calling qla_update_host_map() risk corrupting the host_map B-tree?
In qla24xx_report_id_acquisition(), vport_slock is unlocked right before
the unlocked modification to the global B-tree:
qla24xx_report_id_acquisition() {
...
spin_unlock_irqrestore(&ha->vport_slock, flags);
if (!found)
return;
qla_update_host_map(vp, id);
...
}
Inside qla_update_vp_map(), it calls btree_insert32():
qla_update_vp_map() {
...
rc = btree_insert32(&vha->hw->host_map, key, vha, GFP_ATOMIC);
...
}
Since the Linux B-tree requires external synchronization and this path
appears to lack a common lock, could concurrent modifications to ha->host_map
from other queues or teardown paths collide here?
[Severity: High]
This is also a pre-existing issue, but could this unconditional call to
qla_update_host_map() re-insert a dying vport into the global B-tree?
If qla24xx_vport_delete() initiates teardown concurrently, it calls
qla24xx_disable_vp() which removes the vport from ha->host_map and clears
vp->d_id.b24.
When qla24xx_report_id_acquisition() pins the dying vport via vref_count
(stalling the teardown), drops vport_slock, and calls qla_update_host_map()
without verifying the teardown state, the vport is re-inserted back into
the B-tree. Once vref_count reaches 0, the teardown thread frees the vport,
but it would remain accessible in the B-tree, potentially causing a
use-after-free for incoming traffic.
[Severity: High]
This is another pre-existing issue, but does the reliance on vref_count
polling in qla24xx_deallocate_vp_id() still leave a use-after-free window?
The commit message notes that qla24xx_deallocate_vp_id() waits for vref_count
to reach zero. However, qla24xx_deallocate_vp_id() loops for up to 10 seconds
and then forcefully unlinks the vport if it times out:
qla_mid.c:qla24xx_deallocate_vp_id() {
if (!bailout) {
ql_log(ql_log_info, vha, 0xfffa,
"vha->vref_count=%u timeout\n", vha->vref_count.counter);
spin_lock_irqsave(&ha->vport_slock, flags);
list_del(&vha->list);
qla_update_vp_map(vha, RESET_VP_IDX);
spin_unlock_irqrestore(&ha->vport_slock, flags);
}
}
The caller, qla24xx_vport_delete(), then proceeds to unconditionally free the
memory via scsi_host_put(vha->host). If a thread holding a reference was
stalled and resumes after the timeout, could it access freed memory?
[Severity: High]
Lastly, this isn't a bug introduced by this patch, but are the other
vp_list traversals safe when dropping the lock?
Several functions iterate over ha->vp_list using list_for_each_entry_safe()
while dropping the vport_slock inside the loop. For example:
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);
...
}
Since list_for_each_entry_safe() caches the tvp pointer before the loop body
executes, dropping the lock exposes the unreferenced tvp node to concurrent
deletion. Could qla24xx_deallocate_vp_id() unlink and free tvp while the
lock is dropped, leading to a use-after-free when advancing to the next
iteration?
> }
> set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
> qla2xxx_wake_dpc(vha);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=50
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 53/56] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak
2026-07-17 15:29 ` [PATCH v5 53/56] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak Nilesh Javali
@ 2026-07-17 17:10 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 17:10 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:
- [Critical] Missing bounds checks on user-provided lengths cause severe stack and heap buffer overflows, as well as out-of-bounds DMA transfers.
- [High] DMA API violation: modifying a buffer via memset() immediately after mapping it for DMA.
- [High] Uninitialized stack buffer leaks kernel stack memory to user space via BSG replies.
- [High] Unlocked read-modify-write of `vha->dport_status` across process and hardirq contexts leads to a data race.
- [Medium] A struct field at an unaligned offset is mapped for DMA, resulting in unsafe cache-line sharing with adjacent struct fields.
--
--- Patch [53]: [PATCH v5 53/56] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak ---
commit d9e6712c9051d0111bc1221f583961121d35d896
Author: Nilesh Javali <njavali@marvell.com>
Subject: scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak
This commit changes kmalloc_obj to kzalloc_obj in
qla2x00_do_dport_diagnostics. This ensures the qla_dport_diag response
buffer is fully zeroed, preventing uninitialized fields from being
leaked to user space.
> diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
> index 9ee56ccd52b01..3bf3a7b96eb50 100644
> --- a/drivers/scsi/qla2xxx/qla_bsg.c
> +++ b/drivers/scsi/qla2xxx/qla_bsg.c
> @@ -2858,7 +2858,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);
[Severity: Medium]
This isn't a bug introduced by this patch, but does mapping the buf field
of this struct for DMA cause unsafe cache-line sharing?
Looking at struct qla_dport_diag in qla_bsg.h, the __packed attribute
places the buf array at an unaligned 2-byte offset:
struct qla_dport_diag {
uint16_t options;
uint32_t buf[16];
uint8_t unused[62];
} __packed;
This same unaligned buffer is then mapped for DMA:
qla_mbx.c:qla26xx_dport_diagnostics() {
dd_dma = dma_map_single(&vha->hw->pdev->dev,
dd_buf, size, DMA_FROM_DEVICE);
}
Because the struct is packed, the mapped buf array shares a cache line
with the preceding options field, which violates DMA mapping hardware
cache alignment rules. Can this lead to data corruption on architectures
with non-coherent DMA caches?
> 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 is it safe to modify a buffer with
memset while it is actively mapped for DMA?
In the underlying diagnostic functions called after this allocation, the
CPU modifies the buffer via memset after mapping it:
qla_mbx.c:qla26xx_dport_diagnostics() {
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");
return QLA_MEMORY_ALLOC_FAILED;
}
memset(dd_buf, 0, size);
}
A similar pattern exists in qla26xx_dport_diagnostics_v2(). Since the
DMA API strictly prohibits the CPU from accessing or modifying a buffer
while it is mapped for device access, could the CPU cache lines dirtied
by memset be evicted during the DMA operation and overwrite the
diagnostic test data written by the device?
[Severity: Critical]
This is a pre-existing issue, but does this code lack bounds checks on
user-provided lengths, leading to buffer overflows?
In several BSG handler paths, user-controlled lengths dictate memory
operations without bounds checking:
qla_bsg.c:qla2x00_read_i2c() {
rval = qla2x00_read_sfp(vha, sfp_dma, sfp,
i2c->device, i2c->offset, i2c->length, i2c->option);
if (rval) {
...
}
memcpy(i2c->buffer, sfp, i2c->length);
}
Here, the user-controlled i2c->length is passed directly to the firmware
for DMA, potentially overflowing the 256-byte sfp allocation. It is then
used in a memcpy to overflow the 256-byte bsg array on the stack.
Similarly, in qla2x00_write_i2c():
qla_bsg.c:qla2x00_write_i2c() {
memcpy(sfp, i2c->buffer, i2c->length);
rval = qla2x00_write_sfp(vha, sfp_dma, sfp,
i2c->device, i2c->offset, i2c->length, i2c->option);
}
This causes an out-of-bounds memcpy from the stack into the 256-byte
sfp heap chunk.
Finally, in qla2x00_update_fru_versions():
qla_bsg.c:qla2x00_update_fru_versions() {
count = list->count;
while (count--) {
memcpy(sfp, &image->field_info, sizeof(image->field_info));
...
image++;
}
}
An unbounded count dictates unbounded traversal of the image pointer
beyond the stack buffer. Could these lead to out-of-bounds DMA transfers
and severe memory corruption?
[Severity: High]
This isn't a bug introduced by this patch, but does this code leak
uninitialized stack memory to user space?
There are uninitialized stack buffers populated by BSG requests that
are fully copied back to user space:
qla_bsg.c:qla2x00_read_fru_status() {
uint8_t bsg[DMA_POOL_SIZE];
struct qla_status_reg *sr = (void *)bsg;
...
sg_copy_to_buffer(bsg_job->request_payload.sg_list,
bsg_job->request_payload.sg_cnt, sr, sizeof(*sr));
...
sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
bsg_job->reply_payload.sg_cnt, sr, sizeof(*sr));
}
A similar pattern exists in qla2x00_read_i2c(). If a user issues a BSG
request with a small or zero payload size, the rest of the array remains
uninitialized. Will the entire structure size then be copied back via
sg_copy_from_buffer(), leaking kernel stack frames to user space?
[Severity: High]
This is a pre-existing issue, but does this code have an unlocked data
race on vha->dport_status?
In process context:
qla_bsg.c:qla2x00_do_dport_diagnostics_v2() {
if (options == QLA_START_DPORT_TEST_V2 ||
options == QLA_RESTART_DPORT_TEST_V2) {
dd->mbx1 = mcp->mb[0];
dd->mbx2 = mcp->mb[1];
vha->dport_status |= DPORT_DIAG_IN_PROGRESS;
}
}
In hardirq context:
qla_isr.c:qla2x00_async_event() {
...
case MBA_DPORT_DIAGNOSTICS:
vha->dport_status &= ~DPORT_DIAG_IN_PROGRESS;
}
Both contexts modify the status field asynchronously without locks.
If the async completion interrupt fires before or during the process
context's bitwise OR operation, could the clear operation performed
by the interrupt be overwritten, permanently sticking the flag in
DPORT_DIAG_IN_PROGRESS?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=53
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 46/56] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check
2026-07-17 15:28 ` [PATCH v5 46/56] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check Nilesh Javali
@ 2026-07-17 17:37 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 17: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] Type confusion via union aliasing leads to garbage pointer dereference and out-of-bounds memory access.
- [High] Lockless access and lifecycle violations of `sp->comp` in `qla2xxx_eh_abort` lead to Use-After-Free writes and stack memory corruption.
--
--- Patch [46]: [PATCH v5 46/56] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check ---
commit f3d9d21eec008d26eea4a3d4464f6407598d13d3
Author: Nilesh Javali <njavali@marvell.com>
Subject: scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check
This patch adds a check in qla_chk_edif_rx_sa_delete_pending() to return
early if the command pointer obtained via GET_CMD_SP(sp) is NULL. This
prevents a kernel crash in interrupt context when accessing
cmd->sc_data_direction for commands that have already been returned or
aborted.
Cc: stable@vger.kernel.org
> diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c
> --- 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: High]
This is a pre-existing issue, but does this fully protect against
dereferencing an invalid pointer?
If a non-SCSI command completion (such as SRB_BIDI_CMD or SRB_CT_CMD)
is processed by qla2x00_status_entry() in qla_isr.c and falls through to
qla_chk_edif_rx_sa_delete_pending(), GET_CMD_SP() will read from the
overlapping fields in the sp->u union.
If those overlapping bytes happen to be non-zero, this new check passes,
and we could end up dereferencing a garbage pointer when accessing:
if (cmd->sc_data_direction != DMA_FROM_DEVICE)
return;
Could this lead to out-of-bounds memory accesses or a kernel panic if
the firmware sends a STATUS_TYPE completion for a non-SCSI command?
> handle = (uint32_t)LSW(sts24->handle);
>
> /* find out if this status iosb is for a scsi read */
While reviewing the SRB completion paths related to this change, I noticed
another unrelated lifecycle issue in drivers/scsi/qla2xxx/qla_os.c.
[Severity: High]
This is a pre-existing issue, but is there a potential use-after-free
race condition between qla2xxx_eh_abort() and qla2x00_sp_compl()?
In qla2xxx_eh_abort(), a completion struct is allocated on the stack
and assigned to sp->comp. If the firmware concurrently completes the
command, qla2x00_sp_compl() might execute this sequence:
drivers/scsi/qla2xxx/qla_os.c:qla2x00_sp_compl() {
...
scsi_done(cmd);
if (comp)
complete(comp);
...
}
Because scsi_done() is called before complete(comp), ownership of the
scsi_cmnd transfers to the block layer, which could asynchronously free it.
If qla2xxx_eh_abort() wakes up and clears the pointer:
drivers/scsi/qla2xxx/qla_os.c:qla2xxx_eh_abort() {
...
sp->comp = NULL;
...
}
Could it write to a freed scsi_cmnd?
Furthermore, if qla2x00_sp_compl() reads sp->comp but is preempted before
calling complete(), and qla2xxx_eh_abort() times out and returns,
destroying its local stack frame, could qla2x00_sp_compl() later resume
and call complete() on a destroyed stack variable?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=46
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 28/56] scsi: qla2xxx: Enhance purex_entry handling for 29xx series
2026-07-17 15:28 ` [PATCH v5 28/56] scsi: qla2xxx: Enhance purex_entry " Nilesh Javali
@ 2026-07-17 18:29 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 18:29 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] The `vp_idx` is truncated to 8 bits, and the 24xx struct layout is incorrectly used to populate the 29xx response IOCB, corrupting vport indexing and SOF type.
- [High] `memcpy` from `sts_cont_entry_t->data` triggers a `FORTIFY_SOURCE` panic on 29xx hardware because it copies up to 124 bytes from an array with a statically defined size of 60 bytes.
- [High] Mismatched DMA free size for `rsp_els` on QLA29XX adapters.
Pre-existing issues:
- [High] `__qla_consume_iocb` skips marking the head slot as processed and prematurely marks unrelated future slots, leading to dropped hardware responses.
- [High] `qla24xx_alloc_purex_item` has a data race on `default_item.in_use` with `qla24xx_free_purex_item`.
--
--- Patch [28]: [PATCH v5 28/56] scsi: qla2xxx: Enhance purex_entry handling for 29xx series ---
commit 778e5ac5d8bcf791b8a97a7b9c1906d28083e550
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Enhance purex_entry handling for 29xx series
This commit updates function signatures and internal logic across qla_edif.c,
qla_isr.c, and qla_os.c to differentiate between standard and extended purex
structures based on IS_QLA29XX(). It ensures proper initialization and
processing of command and response data for 64-byte and 128-byte PUREX IOCBs
across ELS paths.
> diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c
> index b05f8e0b705ee..ade1d81785736 100644
> --- a/drivers/scsi/qla2xxx/qla_edif.c
> +++ b/drivers/scsi/qla2xxx/qla_edif.c
[ ... ]
> @@ -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;
[Severity: High]
Does this code truncate the 9-bit virtual port index?
Since vp_idx is defined as a uint8_t, assigning the 16-bit vp_idx from
the 29xx extended structure drops the MSB, which might corrupt the
vport index routing.
> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> index 5aa45b8f49290..75ce09933afc1 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;
[Severity: High]
This is a pre-existing issue, but does the loop in __qla_consume_iocb()
corrupt the ring tracking by skipping the head slot?
If the caller, such as qla24xx_process_response_queue(), advances the ring
pointer before calling this function, the loop will grab the next slot:
void __qla_consume_iocb(...)
{
...
while (entry_count_remaining > 0) {
new_pkt = rsp_q->ring_ptr;
*pkt = new_pkt;
qla_rsp_ring_advance(rsp_q);
new_pkt->signature = RESPONSE_PROCESSED;
...
}
}
This marks the newly advanced pointer as processed and leaks the original
head slot, which can lead to hardware hangs or dropped responses.
[ ... ]
> @@ -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;
[Severity: High]
Can the subsequent continuation loop in __qla_copy_purex_to_buffer()
overread the sts_cont_entry_t buffer on 29xx hardware?
Further down in this function (and similarly in qla27xx_copy_multiple_pkt
and qla2x00_status_cont_entry), the code copies data from the continuation
entry:
data = ((sts_cont_entry_t *)*pkt)->data;
data_sz = qla_sts_cont_data_size(ha);
...
memcpy((buf + buffer_copy_offset), data, no_bytes);
On 29xx hardware, qla_sts_cont_data_size(ha) returns 124. However, the data
array in sts_cont_entry_t is statically defined as 60 bytes. Copying up to
124 bytes from a 60-byte array triggers a FORTIFY_SOURCE panic.
[ ... ]
> @@ -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);
[Severity: High]
This is a pre-existing issue, but does allocating the fallback purex item
race with freeing it?
In qla24xx_alloc_purex_item(), the fallback item is claimed via:
if (atomic_inc_return(&vha->default_item.in_use) == 1) {
item = &vha->default_item;
However, qla24xx_free_purex_item() resets it using a non-atomic memset:
if (item == &item->vha->default_item)
memset(&item->vha->default_item, 0, sizeof(struct purex_item));
If another CPU calls atomic_inc_return() concurrently with the memset, the
atomic increment could be lost, allowing multiple CPUs to claim the
default item at the same time and corrupting memory.
> diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
> index 0b7c4ee73ebfd..c327d053f8eee 100644
> --- a/drivers/scsi/qla2xxx/qla_os.c
> +++ b/drivers/scsi/qla2xxx/qla_os.c
[ ... ]
> @@ -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 {
[Severity: High]
Does this code truncate the 9-bit virtual port index, similar to the logic
in qla24xx_auth_els()?
The vp_idx is defined as a uint8_t, which causes the 16-bit vp_idx from
the extended structure to lose its MSB.
> @@ -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;
[Severity: High]
Does freeing rsp_els result in a size mismatch on 29xx hardware, and does
this corrupt the bitfield on 29xx?
This function dynamically allocates rsp_els_pkt using rsp_els_sz, which
is 128 bytes on 29xx hardware, and then casts it to the 64-byte
struct els_entry_24xx:
if (IS_QLA29XX(ha))
rsp_els_sz = sizeof(struct els_entry_24xx_ext);
...
rsp_els_pkt = dma_alloc_coherent(..., rsp_els_sz, ...);
rsp_els = rsp_els_pkt;
In the cleanup paths, the memory is freed using sizeof(*rsp_els), which
evaluates statically to 64 bytes:
dma_free_coherent(&ha->pdev->dev, sizeof(*rsp_els), rsp_els, rsp_els_dma);
This passes a 64-byte size to free a 128-byte DMA allocation.
Additionally, writing the 8-bit EST_SOFI3 to the legacy sof_type offset on
the 29xx extended layout misaligns with the __le16 bitfield on
little-endian systems, incorrectly setting sof_type to 1 instead of 2.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=28
^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [PATCH v5 32/56] scsi: qla2xxx: Enhance task management IOCB handling for 29xx series
2026-07-17 15:28 ` [PATCH v5 32/56] scsi: qla2xxx: Enhance task management IOCB handling for 29xx series Nilesh Javali
@ 2026-07-17 18:31 ` sashiko-bot
0 siblings, 0 replies; 100+ messages in thread
From: sashiko-bot @ 2026-07-17 18: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] The async abort IOCB builder `qla24xx_abort_iocb()` uses the 64-byte `struct abort_entry_24xx` layout unconditionally without adapting for QLA29XX adapters, which require the 128-byte `struct abort_entry_24xx_ext` layout. In the 29xx extended layout, the `vp_index` field is at offset 18, whereas in the 24xx layout it is at offset 51. Since the request slot is zero-initialized and the function writes to offset 51, the 29xx firmware will read a `vp_index` of 0.
--
--- Patch [32]: [PATCH v5 32/56] scsi: qla2xxx: Enhance task management IOCB handling for 29xx series ---
commit 64d0e684253fddc727ea9ea4498791ce4527162b
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Enhance task management IOCB handling for 29xx series
This commit updates the QLA2XXX task management IOCB builders to use the
extended tsk_mgmt_entry_ext structure for QLA29XX adapters. By checking
IS_QLA29XX(), it correctly shifts the vp_index field assignment to match
the 128-byte stride layout expected by the 29xx series firmware.
> 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
[ ... ]
> @@ -2736,26 +2736,39 @@ qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk)
[ ... ]
> 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;
> + }
[Severity: High]
Does this logic also need to be applied to the asynchronous abort path in
qla24xx_abort_iocb() within the same file?
Looking at qla24xx_abort_iocb(), it appears to still use the legacy 64-byte
layout unconditionally:
static void
qla24xx_abort_iocb(srb_t *sp, struct abort_entry_24xx *abt_iocb)
{
...
abt_iocb->vp_index = vha->vp_idx;
abt_iocb->req_que_no = aio->u.abt.req_que_no;
...
}
Because the 29xx extended layout expects vp_index at offset 18 instead
of offset 51, and the request slot is zero-initialized, won't the 29xx
firmware read a vp_index of 0 here?
Could this result in asynchronous I/O aborts on NPIV virtual ports being
incorrectly routed through the physical port (VP 0) or rejected with
errors like CS_PORT_UNAVAILABLE?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260717152904.3155730-1-njavali@marvell.com?part=32
^ permalink raw reply [flat|nested] 100+ messages in thread
end of thread, other threads:[~2026-07-17 18:31 UTC | newest]
Thread overview: 100+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-17 15:28 [PATCH v5 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 01/56] scsi: qla2xxx: Add 29xx series PCI device ID support Nilesh Javali
2026-07-17 16:36 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 02/56] scsi: qla2xxx: Add flash read/write interface for 29xx Nilesh Javali
2026-07-17 16:05 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 03/56] scsi: qla2xxx: Add NVRAM config support for 29xx adapters Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 04/56] scsi: qla2xxx: Add 29xx support in queue initialisation path Nilesh Javali
2026-07-17 16:09 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 05/56] scsi: qla2xxx: Add FC operational firmware load for 29xx Nilesh Javali
2026-07-17 15:53 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 06/56] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path Nilesh Javali
2026-07-17 15:58 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 07/56] scsi: qla2xxx: Add flash block read/write BSG support for 29xx Nilesh Javali
2026-07-17 16:02 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 08/56] scsi: qla2xxx: Add BSG MPI firmware load/dump " Nilesh Javali
2026-07-17 15:51 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 09/56] scsi: qla2xxx: Add 128-byte IOCB definitions " Nilesh Javali
2026-07-17 15:49 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 10/56] scsi: qla2xxx: Add extended status continuation and marker IOCBs Nilesh Javali
2026-07-17 15:42 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 11/56] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx Nilesh Javali
2026-07-17 16:13 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 12/56] scsi: qla2xxx: Skip image-set-valid attribute " Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 13/56] scsi: qla2xxx: Skip unsupported sysfs attributes " Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 14/56] scsi: qla2xxx: Enable get_fw_version mailbox " Nilesh Javali
2026-07-17 15:55 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 15/56] scsi: qla2xxx: Extend execute_fw mailbox to include 29xx Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 16/56] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx Nilesh Javali
2026-07-17 16:00 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 17/56] scsi: qla2xxx: Enable init_firmware " Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 18/56] scsi: qla2xxx: Enable get_firmware_state " Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 19/56] scsi: qla2xxx: Enable serdes, resource count and FCE trace " Nilesh Javali
2026-07-17 16:30 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 20/56] scsi: qla2xxx: Enable set_els_cmds and echo_test " Nilesh Javali
2026-07-17 15:56 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 21/56] scsi: qla2xxx: Add support for QLA29XX in data rate functions Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 22/56] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx Nilesh Javali
2026-07-17 16:01 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 23/56] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs Nilesh Javali
2026-07-17 16:03 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 24/56] scsi: qla2xxx: Add support for QLA29XX in memory allocation Nilesh Javali
2026-07-17 16:20 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 25/56] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters Nilesh Javali
2026-07-17 16:11 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 26/56] scsi: qla2xxx: Update handling of status entries for 29xx series Nilesh Javali
2026-07-17 16:20 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 27/56] scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling " Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 28/56] scsi: qla2xxx: Enhance purex_entry " Nilesh Javali
2026-07-17 18:29 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 29/56] scsi: qla2xxx: Update handling of ELS IOCBs " Nilesh Javali
2026-07-17 16:17 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 30/56] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx Nilesh Javali
2026-07-17 16:14 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 31/56] scsi: qla2xxx: Add 29xx extended logio IOCB support Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 32/56] scsi: qla2xxx: Enhance task management IOCB handling for 29xx series Nilesh Javali
2026-07-17 18:31 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 33/56] scsi: qla2xxx: Add abort command " Nilesh Javali
2026-07-17 16:42 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 34/56] scsi: qla2xxx: Enhance ABTS processing " Nilesh Javali
2026-07-17 16:49 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 35/56] scsi: qla2xxx: Update VP control IOCB handling " Nilesh Javali
2026-07-17 16:48 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 36/56] scsi: qla2xxx: Add build-time size check for VP config IOCB layout Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 37/56] scsi: qla2xxx: Add size check for extended VP report ID entry Nilesh Javali
2026-07-17 16:29 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 38/56] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series Nilesh Javali
2026-07-17 16:36 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 39/56] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support Nilesh Javali
2026-07-17 16:38 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 40/56] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking Nilesh Javali
2026-07-17 16:49 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 41/56] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors Nilesh Javali
2026-07-17 16:46 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 42/56] scsi: qla2xxx: Fix endianness annotations in vp_rpt_id_entry structures Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 43/56] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats Nilesh Javali
2026-07-17 16:28 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 44/56] scsi: qla2xxx: Add 64G/128G port speed setting support Nilesh Javali
2026-07-17 16:32 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 45/56] scsi: qla2xxx: Fix 64G link speed reporting in get_data_rate Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 46/56] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check Nilesh Javali
2026-07-17 17:37 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 47/56] scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters Nilesh Javali
2026-07-17 15:28 ` [PATCH v5 48/56] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size Nilesh Javali
2026-07-17 16:53 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 49/56] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config() Nilesh Javali
2026-07-17 16:59 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 50/56] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition() Nilesh Javali
2026-07-17 17:08 ` sashiko-bot
2026-07-17 15:28 ` [PATCH v5 51/56] scsi: qla2xxx: Initialize NVMe abort_work once at submission Nilesh Javali
2026-07-17 16:45 ` sashiko-bot
2026-07-17 15:29 ` [PATCH v5 52/56] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject Nilesh Javali
2026-07-17 16:55 ` sashiko-bot
2026-07-17 15:29 ` [PATCH v5 53/56] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak Nilesh Javali
2026-07-17 17:10 ` sashiko-bot
2026-07-17 15:29 ` [PATCH v5 54/56] scsi: qla2xxx: Fix BSG job leak on validate flash image error path Nilesh Javali
2026-07-17 16:57 ` sashiko-bot
2026-07-17 15:29 ` [PATCH v5 55/56] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions() Nilesh Javali
2026-07-17 17:04 ` sashiko-bot
2026-07-17 15:29 ` [PATCH v5 56/56] scsi: qla2xxx: Update version to 12.00.00.2607b1 Nilesh Javali
2026-07-17 16:54 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.