* [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support
@ 2026-07-14 9:52 Nilesh Javali
2026-07-14 9:52 ` [PATCH v4 01/56] scsi: qla2xxx: Add 29xx series PCI device ID support Nilesh Javali
` (55 more replies)
0 siblings, 56 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:52 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 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 | 1424 ++++++++++++++++++++++------
drivers/scsi/qla2xxx/qla_isr.c | 683 ++++++++-----
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 | 783 ++++++++++++++-
drivers/scsi/qla2xxx/qla_target.c | 34 +-
drivers/scsi/qla2xxx/qla_version.h | 10 +-
24 files changed, 6163 insertions(+), 1052 deletions(-)
create mode 100644 drivers/scsi/qla2xxx/qla_fw29.h
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
--
2.47.3
^ permalink raw reply [flat|nested] 87+ messages in thread
* [PATCH v4 01/56] scsi: qla2xxx: Add 29xx series PCI device ID support
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
@ 2026-07-14 9:52 ` Nilesh Javali
2026-07-14 10:19 ` sashiko-bot
2026-07-14 9:52 ` [PATCH v4 02/56] scsi: qla2xxx: Add flash read/write interface for 29xx Nilesh Javali
` (54 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:52 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] 87+ messages in thread
* [PATCH v4 02/56] scsi: qla2xxx: Add flash read/write interface for 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
2026-07-14 9:52 ` [PATCH v4 01/56] scsi: qla2xxx: Add 29xx series PCI device ID support Nilesh Javali
@ 2026-07-14 9:52 ` Nilesh Javali
2026-07-14 12:26 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 03/56] scsi: qla2xxx: Add NVRAM config support for 29xx adapters Nilesh Javali
` (53 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:52 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
From: Manish Rangankar <mrangankar@marvell.com>
The 29xx series uses a different flash access mechanism than
earlier adapters. Add the mailbox wrappers and qla_sup helpers
needed for flash read and write operations, including the
necessary hooks in isp_ops so that the existing flash
infrastructure can drive the new hardware.
Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_def.h | 20 +
drivers/scsi/qla2xxx/qla_fw.h | 54 +++
drivers/scsi/qla2xxx/qla_gbl.h | 15 +-
drivers/scsi/qla2xxx/qla_mbx.c | 144 +++++++
drivers/scsi/qla2xxx/qla_os.c | 18 +-
drivers/scsi/qla2xxx/qla_sup.c | 662 ++++++++++++++++++++++++++++++++-
6 files changed, 900 insertions(+), 13 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 6337a056b149..b5ad6ed3d5d1 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -1278,6 +1278,7 @@ static inline bool qla2xxx_is_valid_mbs(unsigned int mbs)
#define MBC_LOAD_RISC_RAM 9 /* Load RAM command. */
#define MBC_DUMP_RISC_RAM 0xa /* Dump RAM command. */
#define MBC_SECURE_FLASH_UPDATE 0xa /* Secure Flash Update(28xx) */
+#define MBC_RD_WR_FLASH 0xa /* Read/write Dword/block Flash(29xx) */
#define MBC_LOAD_RISC_RAM_EXTENDED 0xb /* Load RAM extended. */
#define MBC_DUMP_RISC_RAM_EXTENDED 0xc /* Dump RAM extended. */
#define MBC_WRITE_RAM_WORD_EXTENDED 0xd /* Write RAM word extended */
@@ -4170,6 +4171,7 @@ struct qla_hw_data {
#define EEH_FLUSH_RDY 1
#define EEH_FLUSH_DONE 2
uint32_t secure_mcu:1;
+ uint32_t valid_flt:1;
} flags;
uint16_t max_exchg;
@@ -4498,6 +4500,8 @@ struct qla_hw_data {
struct qla_flt_header *flt;
dma_addr_t flt_dma;
+ struct qla_flash_layout *flt_data;
+ uint32_t fw_dump_tmplt_len;
#define XGMAC_DATA_SIZE 4096
void *xgmac_data;
@@ -4722,6 +4726,9 @@ struct qla_hw_data {
uint32_t fdt_protect_sec_cmd;
uint32_t fdt_wrt_sts_reg_cmd;
+#define QLA_SEGMENT_LENGTH 0x25000
+ uint32_t flt_segment_length;
+
struct {
uint32_t flt_region_flt;
uint32_t flt_region_fdt;
@@ -5348,6 +5355,14 @@ static inline bool qla_vha_mark_busy(scsi_qla_host_t *vha)
/*
* Flash support definitions
*/
+#define check_and_set_mbc_bits(bopt, dopt, bit_to_check, bit_to_set) { \
+ if (bopt & bit_to_check) \
+ dopt |= bit_to_set; \
+}
+
+#define SET_FW_BIT(__opts, bit) ((__opts) |= (bit))
+#define CLEAR_FW_BIT(__opts, bit) ((__opts) &= ~(bit))
+
#define OPTROM_SIZE_2300 0x20000
#define OPTROM_SIZE_2322 0x100000
#define OPTROM_SIZE_24XX 0x100000
@@ -5612,4 +5627,9 @@ struct ql_vnd_tgt_stats_resp {
(!_fcport || IS_SESSION_DELETED(_fcport) || atomic_read(&_fcport->state) != FCS_ONLINE || \
!_fcport->vha->hw->flags.fw_started)
+#define is_flash_read(_opt) \
+ (!(_opt & BIT_9) && !(_opt & BIT_6))
+
+#define is_flash_write(_opt) \
+ (!(_opt & BIT_9) && (_opt & BIT_6))
#endif
diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index f307beed9d29..d27d09964a24 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -1695,6 +1695,10 @@ struct qla_flt_location {
#define FLT_REG_NVME_PARAMS_PRI_28XX 0x14E
#define FLT_REG_NVME_PARAMS_SEC_28XX 0x179
+/* 29xx */
+#define FLT_REG_MINI_FLT 0x201
+#define FLT_REG_FW_DUMP_TMPLT 0x1A0
+
struct qla_flt_region {
__le16 code;
uint8_t attribute;
@@ -1716,6 +1720,56 @@ struct qla_flt_header {
#define FLT_MAX_REGIONS 0xFF
#define FLT_REGIONS_SIZE (FLT_REGION_SIZE * FLT_MAX_REGIONS)
+/* 29xx */
+#define FLT_HDR_VERSION 0x2
+
+struct qla_flt_region_header {
+ __le32 signature;
+ __le32 version;
+ __le32 length;
+ __le32 checksum;
+ __le16 region_count;
+ __le16 region_size;
+ __le32 segment_size;
+ __le32 res3;
+ __le32 res4;
+ __le32 res5;
+ __le32 res6;
+ __le32 res7;
+ __le32 res8;
+ __le32 res9;
+ __le32 res10;
+ __le32 res11;
+ __le32 res12;
+};
+
+struct qla_flt_region_data {
+ __le16 region_code;
+ __le16 reserved;
+ __le32 attribute;
+ __le32 image_length;
+ __le32 mbi_offset;
+ __le32 version;
+ __le32 card_type;
+ __le32 chip_revision;
+ __le32 res4;
+ __le32 res5;
+ __le32 res6;
+ __le32 res7;
+ __le32 res8;
+ __le32 res9;
+ __le32 res10;
+ __le32 res11;
+ __le32 res12;
+};
+
+struct qla_flash_layout {
+ struct qla_flt_region_header flt_header;
+ struct qla_flt_region_data region[];
+};
+
+#define FLT_DATA_MAX_REGIONS 0xFF
+
/* Flash NPIV Configuration Table ********************************************/
struct qla_npiv_header {
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 9e328c235e39..a032dd047a8f 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -580,6 +580,12 @@ extern int qla2xxx_read_remote_register(scsi_qla_host_t *, uint32_t,
extern int qla2xxx_write_remote_register(scsi_qla_host_t *, uint32_t,
uint32_t);
void qla_no_op_mb(struct scsi_qla_host *vha);
+extern int qla29xx_flash_block_read(scsi_qla_host_t *vha, dma_addr_t req_dma,
+ uint32_t flash_addr, uint32_t flash_size,
+ uint16_t reg_code, uint16_t opt);
+extern int qla29xx_flash_block_write(scsi_qla_host_t *vha, dma_addr_t req_dma,
+ uint32_t flash_addr, uint32_t flash_size,
+ uint16_t reg_code, uint16_t opt);
/*
* Global Function Prototypes in qla_isr.c source file.
@@ -682,7 +688,14 @@ struct purex_item *qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha,
void **pkt, struct rsp_que **rsp, bool is_purls, bool byte_order);
int qla_mailbox_passthru(scsi_qla_host_t *vha, uint16_t *mbx_in,
uint16_t *mbx_out);
-
+void *qla29xx_read_optrom_data(struct scsi_qla_host *vha,
+ uint16_t reg_code, uint16_t opts,
+ void *buf, uint32_t offset,
+ uint32_t length);
+extern int qla29xx_write_optrom_data(struct scsi_qla_host *vha,
+ uint16_t reg_code, uint16_t opts,
+ void *buf, uint32_t offset,
+ uint32_t length);
/*
* Global Function Prototypes in qla_dbg.c source file.
*/
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 44e310f1a370..2d052f870b2b 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -7254,3 +7254,147 @@ int qla_mpipt_validate_fw(scsi_qla_host_t *vha, u16 img_idx, uint16_t *state)
return rval;
}
+
+int qla29xx_flash_block_read(scsi_qla_host_t *vha, dma_addr_t req_dma,
+ uint32_t flash_addr, uint32_t flash_size,
+ uint16_t reg_code, uint16_t opt)
+{
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ int rval = 0;
+
+ ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1067,
+ "Entered %s options 0x%x.\n", __func__, opt);
+
+ memset(mcp->mb, 0, sizeof(mcp->mb));
+
+ if (!is_flash_read(opt)) {
+ ql_log(ql_log_info, vha, 0x1068,
+ "%s: Invalid flash option 0x%x.\n", __func__, opt);
+ return -EINVAL;
+ }
+
+ mcp->mb[0] = MBC_RD_WR_FLASH;
+
+ /* mailbox option field :
+ * TIM img or Img : BIT_15 (1/0)
+ * Last seg img : BIT_11 (1)
+ * First seg img : BIT_10 (1)
+ * dword or block : BIT_9 (1/0)
+ * flash MBR update : BIT_8 (1/0)
+ * Secure or non-secure : BIT_7 (1/0)
+ * Write or Read : BIT_6 (1/0)
+ * Last block img : BIT_5 (1)
+ * First block img : BIT_4 (1)
+ * Request type : (BIT3 - BIT_0)
+ * - Normal
+ * - Normal, Force Sema
+ * - Initialize
+ * - Initialize, Force sema
+ * - Abort secured update
+ */
+ mcp->mb[1] = opt;
+ mcp->mb[2] = reg_code;
+
+ mcp->mb[3] = MSW(flash_size);
+ mcp->mb[4] = LSW(flash_size);
+
+ mcp->mb[5] = MSW(req_dma);
+ mcp->mb[6] = LSW(req_dma);
+ mcp->mb[7] = MSW(MSD(req_dma));
+ mcp->mb[8] = LSW(MSD(req_dma));
+
+ mcp->mb[9] = MSW(flash_addr);
+ mcp->mb[10] = LSW(flash_addr);
+
+ mcp->out_mb =
+ MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x103f,
+ "Failed=%x mb=(0x%x,0x%x,0x%x,0x%x).\n",
+ rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3]);
+ } else {
+ ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1043,
+ "Done %s mb=(0x%x,0x%x,0x%x).\n", __func__,
+ mcp->mb[0], mcp->mb[1], mcp->mb[2]);
+ }
+
+ return rval;
+}
+
+int qla29xx_flash_block_write(scsi_qla_host_t *vha, dma_addr_t req_dma,
+ uint32_t flash_addr, uint32_t flash_size,
+ uint16_t reg_code, uint16_t opt)
+{
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ int rval = 0;
+
+ ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1069,
+ "Entered %s options 0x%x.\n", __func__, opt);
+
+ memset(mcp->mb, 0, sizeof(mcp->mb));
+
+ if (!is_flash_write(opt)) {
+ ql_log(ql_log_info, vha, 0x106a,
+ "%s: Invalid flash option 0x%x.\n", __func__, opt);
+ return -EINVAL;
+ }
+
+ mcp->mb[0] = MBC_RD_WR_FLASH;
+
+ /* mailbox option field :
+ * TIM img or Img : BIT_15 (1/0)
+ * Last seg img : BIT_11 (1)
+ * First seg img : BIT_10 (1)
+ * dword or block : BIT_9 (1/0)
+ * flash MBR update : BIT_8 (1/0)
+ * Secure or non-secure : BIT_7 (1/0)
+ * Write or Read : BIT_6 (1/0)
+ * Last block img : BIT_5 (1)
+ * First block img : BIT_4 (1)
+ * Request type : (BIT3 - BIT_0)
+ * - Normal
+ * - Normal, Force Sema
+ * - Initialize
+ * - Initialize, Force sema
+ * - Abort secured update
+ */
+ mcp->mb[1] = opt;
+ mcp->mb[2] = reg_code;
+
+ mcp->mb[3] = MSW(flash_size);
+ mcp->mb[4] = LSW(flash_size);
+
+ mcp->mb[5] = MSW(req_dma);
+ mcp->mb[6] = LSW(req_dma);
+ mcp->mb[7] = MSW(MSD(req_dma));
+ mcp->mb[8] = LSW(MSD(req_dma));
+
+ mcp->mb[9] = MSW(flash_addr);
+ mcp->mb[10] = LSW(flash_addr);
+
+ mcp->out_mb =
+ MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x110a,
+ "Failed=%x mb=(0x%x,0x%x,0x%x,0x%x).\n",
+ rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3]);
+ } else {
+ ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110b,
+ "Done %s mb=(0x%x,0x%x,0x%x).\n", __func__,
+ mcp->mb[0], mcp->mb[1], mcp->mb[2]);
+ }
+
+ return rval;
+}
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index c91d2b8bd08e..ebb97329e6ea 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -3237,6 +3237,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
ha->flash_data_off = ~0;
ha->nvram_conf_off = ~0;
ha->nvram_data_off = ~0;
+ ha->flt_segment_length = QLA_SEGMENT_LENGTH;
}
ql_dbg_pci(ql_dbg_init, pdev, 0x001e,
@@ -4488,6 +4489,14 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
goto fail_flt_buffer;
}
+ ha->flt_data = vzalloc(sizeof(struct qla_flash_layout) +
+ (sizeof(struct qla_flt_region_data) * FLT_MAX_REGIONS));
+ if (!ha->flt_data) {
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x001a,
+ "Unable to allocate memory for mini FLT data.\n");
+ goto fail_flt;
+ }
+
/* allocate the purex dma pool */
ha->purex_dma_pool = dma_pool_create(name, &ha->pdev->dev,
ELS_MAX_PAYLOAD, 8, 0);
@@ -4495,7 +4504,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
if (!ha->purex_dma_pool) {
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011b,
"Unable to allocate purex_dma_pool.\n");
- goto fail_flt;
+ goto fail_flt_data;
}
ha->elsrej.size = sizeof(struct fc_els_ls_rjt) + 16;
@@ -4528,6 +4537,9 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
ha->elsrej.c, ha->elsrej.cdma);
fail_elsrej:
dma_pool_destroy(ha->purex_dma_pool);
+fail_flt_data:
+ vfree(ha->flt_data);
+ ha->flt_data = NULL;
fail_flt:
dma_free_coherent(&ha->pdev->dev, sizeof(struct qla_flt_header) + FLT_REGIONS_SIZE,
ha->flt, ha->flt_dma);
@@ -4963,6 +4975,10 @@ qla2x00_mem_free(struct qla_hw_data *ha)
ha->flt = NULL;
ha->flt_dma = 0;
+ if (ha->flt_data)
+ vfree(ha->flt_data);
+ ha->flt_data = NULL;
+
if (ha->ms_iocb)
dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
ha->ms_iocb = NULL;
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
index b6c36a8a2d60..6b9938d1a750 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -10,6 +10,539 @@
#include <linux/vmalloc.h>
#include <linux/uaccess.h>
+/**
+ * qla29xx_get_flt_layout - Retrieve the flash layout table (FLT) for QLA29xx.
+ * @vha: Pointer to SCSI QLogic host structure.
+ *
+ * This function reads and validates the FLT structure from the flash memory.
+ * It extracts region information and updates the hardware data structure.
+ */
+static void
+qla29xx_get_flt_layout(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct qla_flash_layout *flt_layout = ha->flt_data;
+ struct qla_flt_region_header *flt_header = &flt_layout->flt_header;
+ struct qla_flt_region_data *region = &flt_layout->region[0];
+ uint32_t flt_options = 0;
+ uint32_t flt_size;
+ uint32_t cnt, chksum;
+ uint16_t reg_cnt, i;
+ __le32 *wptr;
+ void *buf = NULL;
+
+ flt_size = sizeof(struct qla_flt_region_header);
+ wptr = (__force __le32 *)ha->flt_data;
+
+ buf = qla29xx_read_optrom_data(vha, FLT_REG_MINI_FLT, flt_options,
+ ha->flt_data, 0, flt_size);
+ if (!buf) {
+ ql_log(ql_log_warn, vha, 0x007b,
+ "Failed to read FLT information.\n");
+ goto exit_flt;
+ }
+
+ ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x0111,
+ "Contents of Flash Layout (0x%x):\n", flt_size);
+ ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0112,
+ ha->flt_data, flt_size);
+
+ /* check flt version checksum and other info */
+ if ((le32_to_cpu(*wptr) == 0xffffffff) ||
+ flt_header->version != cpu_to_le32(FLT_HDR_VERSION)) {
+ ql_log(ql_log_warn, vha, 0x007c,
+ "Unsupported FLT detected: version=0x%x length=0x%x signature=0x%x region_count=0x%x checksum=0x%x.\n",
+ le32_to_cpu(flt_header->version),
+ le32_to_cpu(flt_header->length),
+ le32_to_cpu(flt_header->signature),
+ le16_to_cpu(flt_header->region_count),
+ le32_to_cpu(flt_header->checksum));
+ goto exit_flt;
+ }
+
+ reg_cnt = le16_to_cpu(flt_header->region_count);
+ if (reg_cnt > FLT_MAX_REGIONS)
+ goto exit_flt;
+
+ if (le16_to_cpu(flt_header->region_size) !=
+ sizeof(struct qla_flt_region_data)) {
+ ql_log(ql_log_warn, vha, 0x0082,
+ "Unsupported FLT region size 0x%x (expected 0x%zx).\n",
+ le16_to_cpu(flt_header->region_size),
+ sizeof(struct qla_flt_region_data));
+ goto exit_flt;
+ }
+
+ flt_size = sizeof(struct qla_flash_layout) +
+ (reg_cnt * le16_to_cpu(flt_header->region_size));
+ if (flt_size > sizeof(struct qla_flash_layout) +
+ sizeof(struct qla_flt_region_data) * FLT_DATA_MAX_REGIONS) {
+ ql_log(ql_log_warn, vha, 0x007d,
+ "FLT size 0x%x exceeds buffer, region_size=0x%x.\n",
+ flt_size, le16_to_cpu(flt_header->region_size));
+ goto exit_flt;
+ }
+
+ buf = qla29xx_read_optrom_data(vha, FLT_REG_MINI_FLT, flt_options,
+ ha->flt_data, 0, flt_size);
+ if (!buf) {
+ ql_log(ql_log_warn, vha, 0x007b,
+ "Failed to read FLT information.\n");
+ goto exit_flt;
+ }
+
+ cnt = le32_to_cpu(flt_header->length) / sizeof(*wptr);
+ if (cnt > flt_size / sizeof(*wptr)) {
+ ql_log(ql_log_warn, vha, 0x007e,
+ "FLT length 0x%x exceeds read size 0x%x, rejecting.\n",
+ le32_to_cpu(flt_header->length), flt_size);
+ goto exit_flt;
+ }
+ for (chksum = 0; cnt--; wptr++)
+ chksum += le32_to_cpu(*wptr);
+ if (chksum) {
+ ql_log(ql_log_fatal, vha, 0x007f,
+ "Inconsistent FLT detected: version=0x%x length=0x%x signature=0x%x checksum=0x%x.\n",
+ le32_to_cpu(flt_header->version),
+ le32_to_cpu(flt_header->length),
+ le32_to_cpu(flt_header->signature),
+ le32_to_cpu(flt_header->checksum));
+ goto exit_flt;
+ }
+
+ ql_dbg(ql_dbg_init, vha, 0x007f,
+ "FLT detected: version=0x%08x length=0x%08x signature=0x%08x region_count=0x%04x region_len=0x%04x segment_len=0x%08x checksum=0x%08x.\n",
+ le32_to_cpu(flt_header->version),
+ le32_to_cpu(flt_header->length),
+ le32_to_cpu(flt_header->signature),
+ le16_to_cpu(flt_header->region_count),
+ le16_to_cpu(flt_header->region_size),
+ le32_to_cpu(flt_header->segment_size),
+ le32_to_cpu(flt_header->checksum));
+
+ for (i = 0; reg_cnt; i++, reg_cnt--) {
+ region = &flt_layout->region[i];
+ ql_dbg(ql_dbg_init, vha, 0x0080,
+ "FLT[%03x]: len=0x%08x version=0x%08x attr=0x%02x.\n",
+ le16_to_cpu(region->region_code),
+ le32_to_cpu(region->image_length),
+ le32_to_cpu(region->version),
+ le32_to_cpu(region->attribute));
+ if (le16_to_cpu(region->region_code) == FLT_REG_FW_DUMP_TMPLT) {
+ ql_dbg(ql_dbg_init, vha, 0x0080,
+ "%s: %d: Found FW dump template", __func__, __LINE__);
+ ha->fw_dump_tmplt_len = le32_to_cpu(region->image_length);
+ }
+ }
+
+ if (le32_to_cpu(flt_header->segment_size) >= sizeof(uint32_t)) {
+ ha->flt_segment_length = le32_to_cpu(flt_header->segment_size);
+ } else {
+ ql_log(ql_log_warn, vha, 0x0081,
+ "FLT segment size 0x%x too small, using default 0x%x.\n",
+ le32_to_cpu(flt_header->segment_size), QLA_SEGMENT_LENGTH);
+ ha->flt_segment_length = QLA_SEGMENT_LENGTH;
+ }
+ ha->flags.valid_flt = true;
+ return;
+
+exit_flt:
+ ha->flt_segment_length = QLA_SEGMENT_LENGTH;
+}
+
+/**
+ * qla29xx_get_fdt_info - Retrieve flash descriptor table (FDT) information.
+ * @vha: Pointer to SCSI QLogic host structure.
+ *
+ * This function reads and validates the FDT structure from the flash memory.
+ * It extracts manufacturer and device-specific information and updates
+ * the hardware data structure.
+ */
+static void
+qla29xx_get_fdt_info(scsi_qla_host_t *vha)
+{
+#define FLASH_BLK_SIZE_4K 0x1000
+#define FLASH_BLK_SIZE_32K 0x8000
+#define FLASH_BLK_SIZE_64K 0x10000
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+ uint16_t cnt, chksum;
+ __le16 *wptr = (__force __le16 *)req->ring;
+ struct qla_fdt_layout *fdt = (struct qla_fdt_layout *)req->ring;
+ uint32_t fdt_options = 0;
+ void *buf = NULL;
+
+ buf = qla29xx_read_optrom_data(vha, FLT_REG_FDT, fdt_options,
+ fdt, 0, sizeof(*fdt));
+ if (!buf) {
+ ql_log(ql_log_warn, vha, 0x0047,
+ "Failed to read FLT information.\n");
+ return;
+ }
+
+ if (le16_to_cpu(*wptr) == 0xffff)
+ return;
+ if (memcmp(fdt->sig, "QLID", 4))
+ return;
+
+ for (cnt = 0, chksum = 0; cnt < sizeof(*fdt) >> 1; cnt++, wptr++)
+ chksum += le16_to_cpu(*wptr);
+ if (chksum) {
+ ql_dbg(ql_dbg_init, vha, 0x004c,
+ "Inconsistent FDT detected: checksum=0x%x id=%c version0x%x.\n",
+ chksum, fdt->sig[0], le16_to_cpu(fdt->version));
+ ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0113,
+ fdt, sizeof(*fdt));
+ return;
+ }
+}
+
+/**
+ * qla29xx_get_flash_region - Retrieve flash region information for QLA29xx adapters.
+ * @vha: Pointer to SCSI QLogic host structure.
+ * @code: Region code to identify the flash region.
+ * @region: Pointer to store the retrieved flash region information.
+ *
+ * This function retrieves the flash region information for QLA29xx adapters
+ * based on the specified region code.
+ *
+ * Returns QLA_SUCCESS on success or QLA_FUNCTION_FAILED on failure.
+ */
+static int
+qla29xx_get_flash_region(struct scsi_qla_host *vha, uint32_t code,
+ struct qla_flt_region_data *region)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct qla_flash_layout *flt = ha->flt_data;
+ struct qla_flt_region_header *flt_hdr = &flt->flt_header;
+ struct qla_flt_region_data *flt_reg = &flt->region[0];
+ uint16_t cnt;
+ int rval = QLA_FUNCTION_FAILED;
+
+ if (!ha->flt_data || !ha->flags.valid_flt)
+ return QLA_FUNCTION_FAILED;
+
+ cnt = le16_to_cpu(flt_hdr->region_count);
+ if (cnt > FLT_MAX_REGIONS)
+ return QLA_FUNCTION_FAILED;
+ /*
+ * flt_reg++ strides by sizeof(struct qla_flt_region_data). This is
+ * safe because valid_flt is set only after qla29xx_get_flt_layout()
+ * has verified the firmware region_size equals that struct size.
+ */
+ for (; cnt; cnt--, flt_reg++) {
+ if (le16_to_cpu(flt_reg->region_code) == code) {
+ memcpy((uint8_t *)region, flt_reg,
+ sizeof(struct qla_flt_region_data));
+ rval = QLA_SUCCESS;
+ break;
+ }
+ }
+
+ return rval;
+}
+
+/**
+ * set_segment_bits - Set segment-related bits in the options field.
+ * @options: Pointer to the options field.
+ * @segment_index: Index of the current segment.
+ * @total: Total number of segments.
+ *
+ * This function sets the appropriate bits in the options field to indicate
+ * whether the current segment is the first, last, or a single segment.
+ */
+static void set_segment_bits(uint16_t *options, int segment_index, int total)
+{
+ /* - Single segment complete image.
+ * - 1st Segment of an image.
+ * - Last segment of an image.
+ */
+ if (total == 1)
+ *options |= (1 << 10) | (1 << 11);
+ else if (segment_index == 0)
+ *options |= (1 << 10);
+ else if (segment_index == total - 1)
+ *options |= (1 << 11);
+}
+
+/**
+ * set_chunk_bits - Set chunk-related bits in the options field.
+ * @options: Pointer to the options field.
+ * @count: Index of the current chunk.
+ * @total: Total number of chunks.
+ *
+ * This function sets the appropriate bits in the options field to indicate
+ * whether the current chunk is the first, last, or a single chunk.
+ */
+static void set_chunk_bits(uint16_t *options, int count, int total)
+{
+ /* - Single chunk complete segment
+ * - 1st chunk of a segment
+ * - Last chunk of a segment
+ */
+ if (total == 1)
+ *options |= (1 << 4) | (1 << 5);
+ else if (count == 0)
+ *options |= (1 << 4);
+ else if (count == total - 1)
+ *options |= (1 << 5);
+}
+
+/**
+ * qla29xx_write_optrom_data - Write data to the optrom for QLA29xx adapters.
+ * @vha: Pointer to SCSI QLogic host structure.
+ * @reg_code: Region code to write to.
+ * @opts: Options for the write operation.
+ * @buf: Buffer containing the data to write.
+ * @offset: Offset within the region to start writing.
+ * @length: Length of data to write.
+ *
+ * This function writes data to the specified optrom region for QLA29xx adapters.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int
+qla29xx_write_optrom_data(struct scsi_qla_host *vha, uint16_t reg_code,
+ uint16_t opts, void *buf, uint32_t offset,
+ uint32_t length)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct qla_flt_region_data region;
+ dma_addr_t optrom_dma;
+ uint32_t faddr, left, burst;
+ uint32_t img_len, seg_dlen;
+ uint32_t region_len, region_dlen;
+ void *optrom;
+ uint8_t *pbuf;
+ int rval = -EINVAL;
+ uint16_t total_segments, segment_index = 0;
+ uint16_t chunk_index = 0, chunk_count = 0;
+
+ memset(®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++;
+ chunk_count++;
+ if (chunk_index >= total_chunks) {
+ chunk_index = 0;
+ segment_index++;
+ region_dlen -= segment_size;
+ }
+ }
+
+free_buf:
+ dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom,
+ optrom_dma);
+ return rval;
+
+exit_write:
+ return rval;
+}
+
+/**
+ * qla29xx_read_optrom_data - Read data from the optrom for QLA29xx adapters.
+ * @vha: Pointer to SCSI QLogic host structure.
+ * @reg_code: Region code to read from.
+ * @opts: Options for the read operation.
+ * @buf: Buffer to store the read data.
+ * @offset: Offset within the region to start reading.
+ * @length: Length of data to read.
+ *
+ * This function reads data from the specified optrom region for QLA29xx adapters.
+ *
+ * Returns a pointer to the buffer on success or NULL on failure.
+ */
+void *
+qla29xx_read_optrom_data(struct scsi_qla_host *vha, uint16_t reg_code,
+ uint16_t opts, void *buf, uint32_t offset,
+ uint32_t length)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct qla_flt_region_data region;
+ dma_addr_t optrom_dma;
+ uint32_t faddr, left, burst;
+ uint32_t img_len, seg_dlen;
+ uint32_t region_len, region_dlen;
+ void *optrom;
+ uint8_t *pbuf;
+ uint16_t total_segments, segment_index = 0;
+ uint16_t chunk_index = 0, chunk_count = 0;
+ int rval;
+
+ memset(®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++;
+ chunk_count++;
+ if (chunk_index >= total_chunks) {
+ chunk_index = 0;
+ segment_index++;
+ region_dlen -= segment_size;
+ }
+ }
+
+ dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom,
+ optrom_dma);
+ return buf;
+
+free_buf:
+ dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom,
+ optrom_dma);
+ return NULL;
+}
+
/*
* NVRAM support routines
*/
@@ -1117,10 +1650,18 @@ qla2xxx_get_flash_info(scsi_qla_host_t *vha)
uint32_t flt_addr;
struct qla_hw_data *ha = vha->hw;
- if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) &&
- !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha) &&
- !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
- return QLA_SUCCESS;
+ if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && !IS_CNA_CAPABLE(ha) &&
+ !IS_QLA2031(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha) &&
+ !IS_QLA29XX(ha))
+ goto done;
+
+ if (IS_QLA29XX(ha)) {
+ mutex_lock(&ha->optrom_mutex);
+ qla29xx_get_flt_layout(vha);
+ qla29xx_get_fdt_info(vha);
+ mutex_unlock(&ha->optrom_mutex);
+ goto done;
+ }
if (IS_QLA28XX(ha) && !qla28xx_validate_mcu_signature(vha))
ha->flags.secure_mcu = 1;
@@ -1132,7 +1673,7 @@ qla2xxx_get_flash_info(scsi_qla_host_t *vha)
qla2xxx_get_flt_info(vha, flt_addr);
qla2xxx_get_fdt_info(vha);
qla2xxx_get_idc_param(vha);
-
+done:
return QLA_SUCCESS;
}
@@ -3448,6 +3989,86 @@ qla82xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
return ret;
}
+/*
+ * Read the boot BIOS/FCODE/EFI version for 29xx adapters.
+ *
+ * 29xx reads flash through an FLT region code plus a region-relative
+ * offset. The firmware only honours such a read at the region base
+ * (offset 0) and rejects a non-zero offset with MBS_COMMAND_ERROR. The
+ * PCI expansion ROM header and the PCI data structure it points to both
+ * live within the first dwords of FLT_REG_BOOT_CODE, so read one bounded
+ * block at offset 0 and parse both structures from memory instead of
+ * issuing a second read at the PCI-data-structure offset.
+ */
+#define QLA29XX_BOOT_PROBE_LEN 0x100
+
+static void
+qla29xx_get_boot_version(scsi_qla_host_t *vha, void *mbuf)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint8_t *bcode = mbuf;
+ uint32_t pcids;
+ uint8_t code_type;
+
+ if (!qla29xx_read_optrom_data(vha, FLT_REG_BOOT_CODE, 0, mbuf, 0,
+ QLA29XX_BOOT_PROBE_LEN)) {
+ ql_log(ql_log_info, vha, 0x018e,
+ "Unable to read PCI Data Structure.\n");
+ return;
+ }
+
+ /* Verify PCI expansion ROM header. */
+ if (memcmp(bcode, "\x55\xaa", 2)) {
+ ql_log(ql_log_info, vha, 0x0059,
+ "No matching ROM signature.\n");
+ return;
+ }
+
+ /* Locate the PCI data structure within the buffer. */
+ pcids = (bcode[0x19] << 8) | bcode[0x18];
+ if (pcids + 0x16 > QLA29XX_BOOT_PROBE_LEN)
+ return;
+
+ bcode += pcids;
+
+ /* Validate signature of PCI data structure. */
+ if (memcmp(bcode, "PCIR", 4)) {
+ ql_log(ql_log_info, vha, 0x005a,
+ "PCI data struct not found pcir_adr=%x.\n", pcids);
+ return;
+ }
+
+ code_type = bcode[0x14];
+ switch (code_type) {
+ case ROM_CODE_TYPE_BIOS:
+ /* Intel x86, PC-AT compatible. */
+ ha->bios_revision[0] = bcode[0x12];
+ ha->bios_revision[1] = bcode[0x13];
+ ql_dbg(ql_dbg_init, vha, 0x005b, "Read BIOS %d.%d.\n",
+ ha->bios_revision[1], ha->bios_revision[0]);
+ break;
+ case ROM_CODE_TYPE_FCODE:
+ /* Open Firmware standard for PCI (FCode). */
+ ha->fcode_revision[0] = bcode[0x12];
+ ha->fcode_revision[1] = bcode[0x13];
+ ql_dbg(ql_dbg_init, vha, 0x005c, "Read FCODE %d.%d.\n",
+ ha->fcode_revision[1], ha->fcode_revision[0]);
+ break;
+ case ROM_CODE_TYPE_EFI:
+ /* Extensible Firmware Interface (EFI). */
+ ha->efi_revision[0] = bcode[0x12];
+ ha->efi_revision[1] = bcode[0x13];
+ ql_dbg(ql_dbg_init, vha, 0x005d, "Read EFI %d.%d.\n",
+ ha->efi_revision[1], ha->efi_revision[0]);
+ break;
+ default:
+ ql_log(ql_log_warn, vha, 0x005e,
+ "Unrecognized code type %x at pcids %x.\n",
+ code_type, pcids);
+ break;
+ }
+}
+
int
qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
{
@@ -3472,12 +4093,31 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
+ /* ISP29xx: get FW version from FLT region metadata */
+ if (IS_QLA29XX(ha)) {
+ struct qla_flt_region_data region;
+
+ ret = qla29xx_get_flash_region(vha, FLT_REG_FW, ®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 +4126,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
if (ret) {
ql_log(ql_log_info, vha, 0x017d,
"Unable to read PCI EXP Rom Header(%x).\n", ret);
- return QLA_FUNCTION_FAILED;
+ break;
}
bcode = mbuf + (pcihdr % 4);
@@ -3494,7 +4134,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
/* No signature */
ql_log(ql_log_fatal, vha, 0x0059,
"No matching ROM signature.\n");
- return QLA_FUNCTION_FAILED;
+ break;
}
/* Locate PCI data structure. */
@@ -3504,7 +4144,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
if (ret) {
ql_log(ql_log_info, vha, 0x018e,
"Unable to read PCI Data Structure (%x).\n", ret);
- return QLA_FUNCTION_FAILED;
+ break;
}
bcode = mbuf + (pcihdr % 4);
@@ -3515,7 +4155,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
ql_log(ql_log_fatal, vha, 0x005a,
"PCI data struct not found pcir_adr=%x.\n", pcids);
ql_dump_buffer(ql_dbg_init, vha, 0x0059, dcode, 32);
- return QLA_FUNCTION_FAILED;
+ break;
}
/* Read version */
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 03/56] scsi: qla2xxx: Add NVRAM config support for 29xx adapters
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
2026-07-14 9:52 ` [PATCH v4 01/56] scsi: qla2xxx: Add 29xx series PCI device ID support Nilesh Javali
2026-07-14 9:52 ` [PATCH v4 02/56] scsi: qla2xxx: Add flash read/write interface for 29xx Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 10:11 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 04/56] scsi: qla2xxx: Add 29xx support in queue initialisation path Nilesh Javali
` (52 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
From: Manish Rangankar <mrangankar@marvell.com>
Extend the NVRAM read and configuration-apply paths to handle the
29xx series. The 29xx NVRAM layout is similar to the 81xx family,
so reuse the existing nvram_81xx parsing while adding
29xx-specific fields and init-sequence integration.
Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_def.h | 1 +
drivers/scsi/qla2xxx/qla_fw.h | 42 ++++++++++-
drivers/scsi/qla2xxx/qla_init.c | 123 +++++++++++++++++++++++++-------
drivers/scsi/qla2xxx/qla_os.c | 2 +
4 files changed, 141 insertions(+), 27 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index b5ad6ed3d5d1..89ddf332cdbd 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -4469,6 +4469,7 @@ struct qla_hw_data {
uint16_t vpd_size;
uint16_t vpd_base;
void *vpd;
+ struct qla_flash_memo_block *fiv;
uint16_t loop_reset_delay;
uint8_t retry_count;
diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index d27d09964a24..42f6125b3cfb 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -1676,6 +1676,8 @@ struct qla_flt_location {
#define FLT_REG_VPD_SEC_27XX_2 0xD8
#define FLT_REG_VPD_SEC_27XX_3 0xDA
#define FLT_REG_NVME_PARAMS_27XX 0x21
+#define FLT_REG_FMB_PRI 0xDF
+#define FLT_REG_FMB_SEC 0x124
/* 28xx */
#define FLT_REG_AUX_IMG_PRI_28XX 0x125
@@ -2110,7 +2112,7 @@ struct nvram_81xx {
* BIT 7 = SCM Disabled if BIT is set (1)
* BIT 8-15 = Unused
*/
- uint16_t enhanced_features;
+ __le16 enhanced_features;
uint16_t reserved_24[4];
@@ -2338,4 +2340,42 @@ struct qla_fcp_prio_cfg {
#define NVRAM_DUAL_FCP_NVME_FLAG_OFFSET 0x196
+struct qla_fmb_version {
+ uint8_t major;
+ uint8_t minor;
+ uint8_t sub;
+ uint8_t build;
+};
+
+struct qla_fmb_upd_time {
+ __le16 year;
+ uint8_t month;
+ uint8_t day;
+
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
+ uint8_t reserved;
+};
+
+struct qla_flash_memo_block {
+ __le32 signature; /* "FMBS" */
+#define QLFC_FMB_SIG cpu_to_le32(0x53424D46)
+ __le32 length;
+ __le32 version;
+#define QLFC_FMB_VERSION 3
+ __le32 checksum;
+ struct qla_fmb_version ffv_ver;
+ struct qla_fmb_version mbi_ver;
+ struct {
+ __le16 year;
+ uint8_t month;
+ uint8_t day;
+ uint8_t reserve[4];
+ } bld_time;
+ uint8_t tool_id[4];
+ struct qla_fmb_upd_time upd_time;
+ struct qla_fmb_version tool_version;
+};
+
#endif
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index e23e7ac48ae2..f5d230fc3642 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -4194,11 +4194,11 @@ qla24xx_detect_sfp(scsi_qla_host_t *vha)
used_nvram = 0;
ha->flags.lr_detected = 0;
if (IS_BPM_RANGE_CAPABLE(ha) &&
- (nv->enhanced_features & NEF_LR_DIST_ENABLE)) {
+ (le16_to_cpu(nv->enhanced_features) & NEF_LR_DIST_ENABLE)) {
used_nvram = 1;
ha->flags.lr_detected = 1;
ha->lr_distance =
- (nv->enhanced_features >> LR_DIST_NV_POS)
+ (le16_to_cpu(nv->enhanced_features) >> LR_DIST_NV_POS)
& LR_DIST_NV_MASK;
}
@@ -9306,34 +9306,97 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
if (IS_P3P_TYPE(ha) || IS_QLA8031(ha))
ha->vpd_size = FA_VPD_SIZE_82XX;
- if (IS_QLA28XX(ha) || IS_QLA27XX(ha))
- qla28xx_get_aux_images(vha, &active_regions);
-
/* Get VPD data into cache */
ha->vpd = ha->nvram + VPD_OFFSET;
- faddr = ha->flt_region_vpd;
- if (IS_QLA28XX(ha)) {
- if (active_regions.aux.vpd_nvram == QLA27XX_SECONDARY_IMAGE)
- faddr = ha->flt_region_vpd_sec;
+ if (IS_QLA29XX(ha)) {
+ uint16_t fw_options = 0, r_code;
+ uint32_t vpd_r[] = {FLT_REG_VPD_0, FLT_REG_VPD_1,
+ FLT_REG_VPD_2, FLT_REG_VPD_3};
+ uint32_t nvram_r[] = {FLT_REG_NVRAM_0, FLT_REG_NVRAM_1,
+ FLT_REG_NVRAM_2, FLT_REG_NVRAM_3};
+ void *buf;
+
+ BUILD_BUG_ON((VPD_OFFSET + FA_NVRAM_VPD_SIZE +
+ sizeof(struct qla_flash_memo_block)) >
+ MAX_NVRAM_SIZE);
+
+ ha->fiv = (struct qla_flash_memo_block *)
+ ((char *)ha->vpd + ha->vpd_size);
+
+ buf = qla29xx_read_optrom_data(vha, FLT_REG_FMB_PRI,
+ fw_options, ha->fiv, 0,
+ sizeof(struct qla_flash_memo_block));
+ if (!buf) {
+ ql_log(ql_log_info, vha, 0x01be,
+ "Unable to read Flash Image Version.\n");
+ } else if (ha->fiv->signature != QLFC_FMB_SIG) {
+ ql_log(ql_log_warn, vha, 0x01bf,
+ "Invalid FMB signature %#x, expected %#x.\n",
+ le32_to_cpu(ha->fiv->signature),
+ le32_to_cpu(QLFC_FMB_SIG));
+ ha->fiv = NULL;
+ } else {
+ ql_log(ql_log_info, vha, 0x0024,
+ "Flash Image Version %u.%02u.%02u\n",
+ ha->fiv->mbi_ver.major,
+ ha->fiv->mbi_ver.minor,
+ ha->fiv->mbi_ver.sub);
+ }
+
+ if (ha->port_no >= ARRAY_SIZE(vpd_r)) {
+ ql_log(ql_log_warn, vha, 0x002e,
+ "Invalid port number %u, skipping VPD/NVRAM read.\n",
+ ha->port_no);
+ goto out_29xx;
+ }
+
+ r_code = vpd_r[ha->port_no];
+ buf = qla29xx_read_optrom_data(vha, r_code, fw_options,
+ ha->vpd, 0, ha->vpd_size);
+ if (!buf)
+ ql_log(ql_log_info, vha, 0x002d,
+ "Unable to read VPD info.\n");
+
+ r_code = nvram_r[ha->port_no];
+ buf = qla29xx_read_optrom_data(vha, r_code, fw_options,
+ ha->nvram, 0, ha->nvram_size);
+ if (!buf)
+ ql_log(ql_log_info, vha, 0x0013,
+ "Unable to read nvram config info.\n");
+out_29xx:
+ } else {
+ if (IS_QLA28XX(ha) || IS_QLA27XX(ha))
+ qla28xx_get_aux_images(vha, &active_regions);
+
+ faddr = ha->flt_region_vpd;
+ if (IS_QLA28XX(ha)) {
+ if (active_regions.aux.vpd_nvram ==
+ QLA27XX_SECONDARY_IMAGE)
+ faddr = ha->flt_region_vpd_sec;
+ ql_dbg(ql_dbg_init, vha, 0x0110,
+ "Loading %s nvram image.\n",
+ active_regions.aux.vpd_nvram ==
+ QLA27XX_PRIMARY_IMAGE ?
+ "primary" : "secondary");
+ }
+ ha->isp_ops->read_optrom(vha, ha->vpd, faddr << 2,
+ ha->vpd_size);
+
+ /* Get NVRAM data into cache and calculate checksum. */
+ faddr = ha->flt_region_nvram;
+ if (IS_QLA28XX(ha)) {
+ if (active_regions.aux.vpd_nvram ==
+ QLA27XX_SECONDARY_IMAGE)
+ faddr = ha->flt_region_nvram_sec;
+ }
ql_dbg(ql_dbg_init, vha, 0x0110,
"Loading %s nvram image.\n",
active_regions.aux.vpd_nvram == QLA27XX_PRIMARY_IMAGE ?
"primary" : "secondary");
+ ha->isp_ops->read_optrom(vha, ha->nvram, faddr << 2,
+ ha->nvram_size);
}
- ha->isp_ops->read_optrom(vha, ha->vpd, faddr << 2, ha->vpd_size);
-
- /* Get NVRAM data into cache and calculate checksum. */
- faddr = ha->flt_region_nvram;
- if (IS_QLA28XX(ha)) {
- if (active_regions.aux.vpd_nvram == QLA27XX_SECONDARY_IMAGE)
- faddr = ha->flt_region_nvram_sec;
- }
- ql_dbg(ql_dbg_init, vha, 0x0110,
- "Loading %s nvram image.\n",
- active_regions.aux.vpd_nvram == QLA27XX_PRIMARY_IMAGE ?
- "primary" : "secondary");
- ha->isp_ops->read_optrom(vha, ha->nvram, faddr << 2, ha->nvram_size);
dptr = (__force __le32 *)nv;
for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++, dptr++)
@@ -9386,7 +9449,10 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
nv->login_timeout = cpu_to_le16(0);
nv->firmware_options_1 =
cpu_to_le32(BIT_14|BIT_13|BIT_2|BIT_1);
- nv->firmware_options_2 = cpu_to_le32(2 << 4);
+ if (IS_QLA29XX(ha))
+ nv->firmware_options_2 = cpu_to_le32(BIT_4);
+ else
+ nv->firmware_options_2 = cpu_to_le32(BIT_5);
nv->firmware_options_2 |= cpu_to_le32(BIT_12);
nv->firmware_options_3 = cpu_to_le32(2 << 13);
nv->host_p = cpu_to_le32(BIT_11|BIT_10);
@@ -9468,9 +9534,13 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
icb->node_name[0] &= 0xF0;
}
- if (IS_QLA28XX(ha) || IS_QLA27XX(ha)) {
- if ((nv->enhanced_features & BIT_7) == 0)
+ /* SCM Enabled in NVRAM */
+ if (IS_QLA29XX(ha) || IS_QLA28XX(ha) || IS_QLA27XX(ha)) {
+ if ((le16_to_cpu(nv->enhanced_features) & BIT_7) == 0) {
+ ql_log(ql_log_info, vha, 0x0062,
+ "USCM enabled in NVRAM\n");
ha->flags.scm_supported_a = 1;
+ }
}
/* Set host adapter parameters. */
@@ -9546,7 +9616,8 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
/* if not running MSI-X we need handshaking on interrupts */
if (!vha->hw->flags.msix_enabled &&
- (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)))
+ (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+ IS_QLA29XX(ha)))
icb->firmware_options_2 |= cpu_to_le32(BIT_22);
/* Enable ZIO. */
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index ebb97329e6ea..c3eb941f71ad 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -4620,6 +4620,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
fail_free_nvram:
kfree(ha->nvram);
ha->nvram = NULL;
+ ha->fiv = NULL;
fail_free_ctx_mempool:
mempool_destroy(ha->ctx_mempool);
ha->ctx_mempool = NULL;
@@ -5085,6 +5086,7 @@ qla2x00_mem_free(struct qla_hw_data *ha)
ha->optrom_buffer = NULL;
kfree(ha->nvram);
ha->nvram = NULL;
+ ha->fiv = NULL;
kfree(ha->npiv_info);
ha->npiv_info = NULL;
kfree(ha->swl);
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 04/56] scsi: qla2xxx: Add 29xx support in queue initialisation path
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (2 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 03/56] scsi: qla2xxx: Add NVRAM config support for 29xx adapters Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 10:28 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 05/56] scsi: qla2xxx: Add FC operational firmware load for 29xx Nilesh Javali
` (51 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
From: Manish Rangankar <mrangankar@marvell.com>
Extend the queue initialisation and multi-queue management mailbox
commands to include IS_QLA29XX() checks, following the same mailbox
interface as 27xx/28xx.
Unlike earlier adapters that use 64-byte request/response ring entries
(request_t / response_t), 29xx uses 128-byte entries. Add struct
request_ext and struct response_ext, which extend the legacy 64-byte
layout with a 64-byte reserved area. The first 64 bytes are
layout-compatible with the legacy structures, so common header
accesses remain valid.
The enlarged entry stride doubles the DMA ring memory allocated for
both request and response queues on 29xx, and all ring pointer
arithmetic must account for the wider entries (handled by later
patches in this series).
Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_def.h | 30 ++++++++++++++++++++++++++++++
drivers/scsi/qla2xxx/qla_mbx.c | 20 +++++++++++++-------
drivers/scsi/qla2xxx/qla_mid.c | 25 +++++++++++++++++++++----
3 files changed, 64 insertions(+), 11 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 89ddf332cdbd..1ec7ee578e0c 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -1989,6 +1989,18 @@ typedef struct {
#define RESPONSE_PROCESSED 0xDEADDEAD /* Signature */
} response_t;
+struct response_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t handle; /* System defined handle */
+ uint8_t data[52];
+ uint32_t signature;
+ uint8_t reserved[64];
+#define RESPONSE_PROCESSED 0xDEADDEAD /* Signature */
+};
+
/*
* ISP queue - ATIO queue entry definition.
*/
@@ -2067,6 +2079,24 @@ typedef struct {
struct dsd64 dsd[2];
} cmd_a64_entry_t, request_t;
+struct request_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t handle; /* System handle. */
+ target_id_t target; /* SCSI ID */
+ __le16 lun; /* SCSI LUN */
+ __le16 control_flags; /* Control flags. */
+ uint16_t reserved_1;
+ __le16 timeout; /* Command timeout. */
+ __le16 dseg_count; /* Data segment count. */
+ uint8_t scsi_cdb[MAX_CMDSZ]; /* SCSI command words. */
+ uint32_t byte_count; /* Total byte count. */
+ struct dsd64 dsd[2];
+ uint8_t reserved[64];
+};
+
/*
* ISP queue - continuation entry structure definition.
*/
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 2d052f870b2b..2b232b225c90 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -4584,7 +4584,8 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
mcp->mb[12] = req->qos;
mcp->mb[11] = req->vp_idx;
mcp->mb[13] = req->rid;
- if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+ if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+ IS_QLA29XX(ha))
mcp->mb[15] = 0;
mcp->mb[4] = req->id;
@@ -4599,9 +4600,10 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
mcp->tov = MBX_TOV_SECONDS * 2;
if (IS_QLA81XX(ha) || IS_QLA83XX(ha) || IS_QLA27XX(ha) ||
- IS_QLA28XX(ha))
+ IS_QLA28XX(ha) || IS_QLA29XX(ha))
mcp->in_mb |= MBX_1;
- if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+ if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+ IS_QLA29XX(ha)) {
mcp->out_mb |= MBX_15;
/* debug q create issue in SR-IOV */
mcp->in_mb |= MBX_9 | MBX_8 | MBX_7;
@@ -4610,7 +4612,8 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
spin_lock_irqsave(&ha->hardware_lock, flags);
if (!(req->options & BIT_0)) {
wrt_reg_dword(req->req_q_in, 0);
- if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+ if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha) &&
+ !IS_QLA29XX(ha))
wrt_reg_dword(req->req_q_out, 0);
}
spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -4654,7 +4657,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
mcp->mb[5] = rsp->length;
mcp->mb[14] = rsp->msix->entry;
mcp->mb[13] = rsp->rid;
- if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+ if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+ IS_QLA29XX(ha))
mcp->mb[15] = 0;
mcp->mb[4] = rsp->id;
@@ -4671,7 +4675,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
if (IS_QLA81XX(ha)) {
mcp->out_mb |= MBX_12|MBX_11|MBX_10;
mcp->in_mb |= MBX_1;
- } else if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+ } else if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+ IS_QLA29XX(ha)) {
mcp->out_mb |= MBX_15|MBX_12|MBX_11|MBX_10;
mcp->in_mb |= MBX_1;
/* debug q create issue in SR-IOV */
@@ -4681,7 +4686,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
spin_lock_irqsave(&ha->hardware_lock, flags);
if (!(rsp->options & BIT_0)) {
wrt_reg_dword(rsp->rsp_q_out, 0);
- if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+ if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha) &&
+ !IS_QLA29XX(ha))
wrt_reg_dword(rsp->rsp_q_in, 0);
}
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index c563133f751e..e75b7ae22bc5 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -574,9 +574,13 @@ qla25xx_free_req_que(struct scsi_qla_host *vha, struct req_que *req)
{
struct qla_hw_data *ha = vha->hw;
uint16_t que_id = req->id;
+ uint16_t reqsz;
+
+ reqsz = IS_QLA29XX(ha) ? sizeof(struct request_ext) :
+ sizeof(request_t);
dma_free_coherent(&ha->pdev->dev, (req->length + 1) *
- sizeof(request_t), req->ring, req->dma);
+ reqsz, req->ring, req->dma);
req->ring = NULL;
req->dma = 0;
if (que_id) {
@@ -594,6 +598,10 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
{
struct qla_hw_data *ha = vha->hw;
uint16_t que_id = rsp->id;
+ uint16_t rspsz;
+
+ rspsz = IS_QLA29XX(ha) ? sizeof(struct response_ext) :
+ sizeof(response_t);
if (rsp->msix && rsp->msix->have_irq) {
free_irq(rsp->msix->vector, rsp->msix->handle);
@@ -601,8 +609,9 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
rsp->msix->in_use = 0;
rsp->msix->handle = NULL;
}
+
dma_free_coherent(&ha->pdev->dev, (rsp->length + 1) *
- sizeof(response_t), rsp->ring, rsp->dma);
+ rspsz, rsp->ring, rsp->dma);
rsp->ring = NULL;
rsp->dma = 0;
if (que_id) {
@@ -706,6 +715,7 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
uint16_t que_id = 0;
device_reg_t *reg;
uint32_t cnt;
+ uint16_t reqsz;
req = kzalloc_obj(struct req_que);
if (req == NULL) {
@@ -714,9 +724,12 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
goto failed;
}
+ reqsz = IS_QLA29XX(ha) ? sizeof(struct request_ext) :
+ sizeof(request_t);
+
req->length = REQUEST_ENTRY_CNT_24XX;
req->ring = dma_alloc_coherent(&ha->pdev->dev,
- (req->length + 1) * sizeof(request_t),
+ (req->length + 1) * reqsz,
&req->dma, GFP_KERNEL);
if (req->ring == NULL) {
ql_log(ql_log_fatal, base_vha, 0x00da,
@@ -833,6 +846,7 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
struct scsi_qla_host *vha = pci_get_drvdata(ha->pdev);
uint16_t que_id = 0;
device_reg_t *reg;
+ uint16_t rspsz;
rsp = kzalloc_obj(struct rsp_que);
if (rsp == NULL) {
@@ -841,9 +855,12 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
goto failed;
}
+ rspsz = IS_QLA29XX(ha) ? sizeof(struct response_ext) :
+ sizeof(response_t);
+
rsp->length = RESPONSE_ENTRY_CNT_MQ;
rsp->ring = dma_alloc_coherent(&ha->pdev->dev,
- (rsp->length + 1) * sizeof(response_t),
+ (rsp->length + 1) * rspsz,
&rsp->dma, GFP_KERNEL);
if (rsp->ring == NULL) {
ql_log(ql_log_warn, base_vha, 0x00e1,
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 05/56] scsi: qla2xxx: Add FC operational firmware load for 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (3 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 04/56] scsi: qla2xxx: Add 29xx support in queue initialisation path Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 10:43 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 06/56] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path Nilesh Javali
` (50 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
From: Manish Rangankar <mrangankar@marvell.com>
Add support to load the 29xx FC operational firmware from the
filesystem and to set up the corresponding firmware dump
template. This follows the same request_firmware / segment-load
pattern used by earlier adapters.
Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_fw.h | 36 +++
drivers/scsi/qla2xxx/qla_gbl.h | 3 +-
drivers/scsi/qla2xxx/qla_init.c | 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 f5d230fc3642..f6a8bcbb2a03 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 6b9938d1a750..381d09145378 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -1915,7 +1915,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, __le32 *dwptr, uint32_t faddr,
ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
"Write burst (%#lx dwords)...\n", dburst);
ret = qla2x00_load_ram(vha, optrom_dma,
- flash_data_addr(ha, faddr), dburst);
+ flash_data_addr(ha, faddr), dburst, 0);
if (!ret) {
liter += dburst - 1;
faddr += dburst - 1;
@@ -3515,7 +3515,7 @@ qla28xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
"Write burst (%#lx dwords)...\n", dburst);
rval = qla2x00_load_ram(vha, optrom_dma,
- flash_data_addr(ha, faddr), dburst);
+ flash_data_addr(ha, faddr), dburst, 0);
if (rval != QLA_SUCCESS) {
ql_log(ql_log_warn, vha, 0x7097,
"Failed burst write at %x (%p/%#llx)...\n",
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 06/56] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (4 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 05/56] scsi: qla2xxx: Add FC operational firmware load for 29xx Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 10:42 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 07/56] scsi: qla2xxx: Add flash block read/write BSG support for 29xx Nilesh Javali
` (49 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
From: Manish Rangankar <mrangankar@marvell.com>
qla2x00_sysfs_read_vpd() called ha->isp_ops->read_optrom() a second
time after releasing optrom_mutex. The repeated read is redundant and,
unlike the first, runs without optrom_mutex held, exposing flash access
to concurrent optrom operations. Drop the duplicate call.
Fixes: 5fa8774c7f38 ("scsi: qla2xxx: Add 28xx flash primary/secondary status/image mechanism")
Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_attr.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 6a05ce195aa0..800751ab562a 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -580,7 +580,6 @@ qla2x00_sysfs_read_vpd(struct file *filp, struct kobject *kobj,
ha->isp_ops->read_optrom(vha, ha->vpd, faddr, ha->vpd_size);
mutex_unlock(&ha->optrom_mutex);
- ha->isp_ops->read_optrom(vha, ha->vpd, faddr, ha->vpd_size);
skip:
return memory_read_from_buffer(buf, count, &off, ha->vpd, ha->vpd_size);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 07/56] scsi: qla2xxx: Add flash block read/write BSG support for 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (5 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 06/56] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 08/56] scsi: qla2xxx: Add BSG MPI firmware load/dump " Nilesh Javali
` (48 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
From: Manish Rangankar <mrangankar@marvell.com>
Introduce QL_VND_READ_FLASH_BLOCK and QL_VND_WRITE_FLASH_BLOCK
BSG vendor commands so that userspace tools can perform flash
block-level operations on 29xx adapters via the isp_ops
interface.
Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_bsg.c | 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 381d09145378..51ea481c68eb 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] 87+ messages in thread
* [PATCH v4 08/56] scsi: qla2xxx: Add BSG MPI firmware load/dump for 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (6 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 07/56] scsi: qla2xxx: Add flash block read/write BSG support for 29xx Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 10:59 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 09/56] scsi: qla2xxx: Add 128-byte IOCB definitions " Nilesh Javali
` (47 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
From: Manish Rangankar <mrangankar@marvell.com>
Add BSG vendor commands for loading and dumping MPI firmware on
29xx adapters. This extends the existing BSG infrastructure with
the necessary mailbox wrappers and flash helpers for MPI
operations.
Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_bsg.c | 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 51ea481c68eb..255aa3c731a2 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -543,6 +543,123 @@ qla29xx_read_optrom_data(struct scsi_qla_host *vha, uint16_t reg_code,
return NULL;
}
+static void set_chunk_mpi_bits(uint16_t *options, int count, int total)
+{
+ /* - Single chunk complete segment/image
+ * - 1st chunk of a segment/image
+ * - Last chunk of a segment/image
+ */
+ if (total == 1)
+ *options |= BIT_2 | BIT_1;
+ else if (count == 0)
+ *options |= BIT_1;
+ else if (count == total - 1)
+ *options |= BIT_2;
+}
+
+/**
+ * qla29xx_mpi_optrom_data - Dump/load MPI optrom data for 29xx.
+ * @vha: Pointer to SCSI QLogic host structure.
+ * @opts: Options for the operation.
+ * @buf: Buffer to read from/write to.
+ * @offset: Offset into the device memory.
+ * @length: Length of data, in bytes.
+ * @op: Operation, either QLA29XX_MPI_OP_DUMP or QLA29XX_MPI_OP_LOAD.
+ *
+ * Returns:
+ * QLA_SUCCESS on success or an error code on failure.
+ */
+int
+qla29xx_mpi_optrom_data(struct scsi_qla_host *vha, uint16_t opts, void *buf,
+ uint32_t offset, uint32_t length, enum qla29xx_mpi_optrom_op op)
+{
+ struct qla_hw_data *ha = vha->hw;
+ dma_addr_t optrom_dma;
+ void *optrom;
+ uint32_t mpi_addr, mpi_size, burst = 0;
+ uint32_t total_chunks = 0;
+ uint32_t *dcode, *fwcode;
+ 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] 87+ messages in thread
* [PATCH v4 09/56] scsi: qla2xxx: Add 128-byte IOCB definitions for 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (7 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 08/56] scsi: qla2xxx: Add BSG MPI firmware load/dump " Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 11:09 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 10/56] scsi: qla2xxx: Add extended status continuation and marker IOCBs Nilesh Javali
` (46 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
From: Anil Gurumurthy <agurumurthy@marvell.com>
The 29xx series uses 128-byte IOCBs instead of the 64-byte IOCBs
used by earlier adapters. Add a new header (qla_fw29.h) with the
extended IOCB structure definitions that match the 29xx firmware
interface.
Signed-off-by: Anil Gurumurthy <agurumurthy@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_fw29.h | 686 ++++++++++++++++++++++++++++++++
1 file changed, 686 insertions(+)
create mode 100644 drivers/scsi/qla2xxx/qla_fw29.h
diff --git a/drivers/scsi/qla2xxx/qla_fw29.h b/drivers/scsi/qla2xxx/qla_fw29.h
new file mode 100644
index 000000000000..efe1c60bee81
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_fw29.h
@@ -0,0 +1,686 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2026- Marvell.
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#ifndef __QLA_FW29_H
+#define __QLA_FW29_H
+
+#include "qla_fw.h"
+
+/* Control Flags 2 common for cmd6 and 7 */
+#define CF2_VMID_ENABLE BIT_0
+#define CF2_CSCTL_PRIORITY_TAG BIT_1
+#define CF2_NO_TRNF_READY_ENABLE BIT_2
+#define CF2_RX_ID_ENABLE BIT_3
+
+/*
+ * vp_index layout for 29xx extended command IOCBs
+ * (cmd_type_6_ext, cmd_type_7_ext, cmd_type_crc_2_ext, ...):
+ * bits [8:0] - VP index (9 bits)
+ * bits [15:9] - reserved, must be zero
+ * Access on a host-endian value via le16_to_cpu(vp_index) & CMD_EXT_VP_INDEX_MASK.
+ */
+#define CMD_EXT_VP_INDEX_MASK 0x01ff
+/*
+ * ISP queue - command entry structure definition.
+ */
+#define NUM_CMD67_DSDS 4
+struct cmd_type_6_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ __le16 nport_handle; /* N_PORT handle. */
+ __le16 timeout; /* Command timeout. */
+
+ __le16 dseg_count; /* Data segment count. */
+
+ __le16 fcp_rsp_dsd_len; /* FCP_RSP DSD length. */
+
+ struct scsi_lun lun; /* FCP LUN (BE). */
+
+ __le16 control_flags; /* Control flags. */
+
+ __le16 fcp_cmnd_dseg_len; /* Data segment length. */
+ /* Data segment address. */
+ __le64 fcp_cmnd_dseg_address __packed;
+ /* Data segment address. */
+ __le64 fcp_rsp_dseg_address __packed;
+
+ __le32 byte_count; /* Total byte count. */
+ __le16 control_flags_2; /* Control flags 2. */
+
+ __le16 vp_index; /* VP Index 9bits*/
+ __le32 fburstlen_rxid; /* First Burst length/RX ID */
+ __le16 io_tag; /* I/O Tag */
+ uint8_t vl_n_fctl; /* VL (7-4) | RSVD (3-2) | F_CTL [17] (1) | RSVD (0) */
+ uint8_t prtag_csctl; /* Priority Tag or CS_CTL */
+ __le32 src_vm_id; /* Source VM ID */
+ uint8_t reserved_2[16]; /* Reserved */
+ struct dsd64 dsd[NUM_CMD67_DSDS]; /* Data Segment Descriptors */
+};
+
+struct cmd_type_7_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t handle; /* System handle. */
+ __le16 nport_handle; /* N_PORT handle. */
+ __le16 timeout; /* Command timeout. */
+
+ __le16 dseg_count; /* Data segment count. */
+ uint16_t reserved_1;
+
+ struct scsi_lun lun; /* FCP LUN (BE). */
+
+ __le16 task_mgmt_flags; /* Task management flags. */
+
+ uint8_t task;
+ uint8_t crn;
+ uint8_t fcp_cdb[MAX_CMDSZ]; /* SCSI command words. */
+ __le32 byte_count; /* Total byte count. */
+ __le16 ctrl_flags_2; /* Control flags 2 */
+ __le16 vp_index; /* VP Index 9bits*/
+ __le32 rx_id; /* Receive Exchange ID */
+ __le16 io_tag; /* I/O Tag */
+ uint8_t vl_n_fctl; /* VL (7-4) | RSVD (3-2) | F_CTL [17] (1) | RSVD (0) */
+ uint8_t reserved_3[21]; /* Reserved */
+ struct dsd64 dsd[NUM_CMD67_DSDS]; /* Data Segment Descriptors */
+};
+
+struct cmd_type_crc_2_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ __le16 nport_handle; /* N_PORT handle. */
+ __le16 timeout; /* Command timeout. */
+
+ __le16 dseg_count; /* Data segment count. */
+ __le16 fcp_rsp_dseg_len; /* FCP_RSP DSD length. */
+
+ struct scsi_lun lun; /* FCP LUN (BE). */
+
+ __le16 control_flags_1; /* Control flags. */
+ __le16 fcp_cmnd_dseg_len; /* Data segment length. */
+
+ __le64 fcp_cmnd_dseg_address __packed;
+ /* Data segment address. */
+ __le64 fcp_rsp_dseg_address __packed;
+
+ __le32 byte_count; /* Total byte count. */
+
+ __le16 control_flags_2; /* Control flags - 2 */
+ __le16 vp_index; /* VP Index (bits [8:0]); bits [15:9] reserved.
+ * See CMD_EXT_VP_INDEX_MASK.
+ */
+
+ uint32_t reserved_1;
+
+ __le16 iocb_tag; /* Unused */
+ __le16 vl_prio; /* Bit 1 - F_CTL, Bits 4-7 VL, rest are rsvd */
+
+ uint32_t reserved_2; /* 3C-3F offset */
+
+ __le32 ref_tag;
+ uint8_t ref_tag_mask[4]; /* Validation/Replacement Mask*/
+
+ __le16 app_tag;
+ uint8_t app_tag_mask[2]; /* Validation/Replacement Mask*/
+
+ __le16 blk_size; /* Data size in bytes */
+ __le16 prot_opts; /* Requested Data Protection Mode */
+
+ __le32 tot_byte_count; /* Total byte count/ total data
+ * transfer count
+ */
+ union {
+ struct {
+ uint32_t reserved_1; /* offset 54 */
+ uint16_t reserved_2;
+ __le16 guard_seed; /* offset 5A */
+ struct dsd64 data_dsd[1];
+ uint32_t reserved_5[2];
+ uint32_t reserved_6;
+ } nobundling;
+ struct {
+ __le32 dif_byte_count; /* Total DIF byte
+ * count
+ */
+ __le16 dseg_count; /* Data segment count */
+ __le16 guard_seed; /* Initial Guard Seed */
+ struct dsd64 data_dsd[1];
+ struct dsd64 dif_dsd;
+ } bundling;
+ } u;
+ uint8_t reserved_3[12]; /* MUST be set to 0. */
+};
+
+/*
+ * ISP queue - status entry structure definition.
+ */
+struct sts_entry_24xx_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ __le16 comp_status; /* Completion status. */
+ __le16 ox_id; /* OX_ID used by the firmware. */
+
+ __le32 residual_len; /* FW calc residual transfer length. */
+
+ union {
+ __le16 reserved_1;
+ __le16 nvme_rsp_pyld_len;
+ } u1;
+
+ __le16 state_flags; /* State flags. */
+
+ __le16 read_sa_index;
+ __le16 wr_sa_index;
+ uint8_t reserved_2[8];
+ uint8_t act_dif[8];
+ uint8_t exp_dif[8];
+ union {
+ struct {
+ __le32 rsp_data_len_dma; /* FCP response data length */
+ uint8_t reserved_3[76];
+ };
+ struct {
+ uint8_t nvme_ersp_data[32];
+ uint8_t reserved_4[48];
+ };
+ struct {
+ __le32 bid_rd_rsp_residual_count; /* BID read rsp residual cnt */
+ __le16 retry_delay_timer; /* Retry delay timer. */
+ __le16 scsi_status; /* SCSI status. */
+ __le32 rsp_residual_count; /* FCP RSP residual count. */
+ __le32 sense_len; /* FCP SENSE length. */
+ __le32 rsp_data_len_ndma; /* FCP response data length */
+ uint8_t data[60]; /* FCP rsp/sense information */
+ };
+ } u2;
+
+ /*
+ * If DIF Error is set in comp_status, these additional fields are
+ * defined:
+ *
+ * !!! NOTE: Firmware sends expected/actual DIF data in big endian
+ * format; but all of the "data" field gets swab32-d in the beginning
+ * of qla2900_status_entry().
+ *
+ * &data[10] : uint8_t report_runt_bg[2]; - computed guard
+ * &data[12] : uint8_t actual_dif[8]; - DIF Data received
+ * &data[20] : uint8_t expected_dif[8]; - DIF Data computed
+ */
+};
+
+/*
+ * ISP queue - marker entry structure definition.
+ */
+struct mrk_entry_24xx_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count; /* Handle count. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ __le16 nport_handle; /* N_PORT handle. */
+
+ uint8_t modifier; /* Modifier (7-0). */
+ uint8_t reserved_1;
+
+ __le16 vp_index; /* VP Index. 9bits*/
+ uint16_t reserved_3;
+
+ uint8_t lun[8]; /* FCP LUN (BE). */
+ uint8_t reserved_4[104];
+};
+
+/*
+ * ISP queue - CT Pass-Through entry structure definition.
+ */
+#define NUM_CT_DSDS 5
+struct ct_entry_24xx_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System Defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ __le16 comp_status; /* Completion status. */
+
+ __le16 nport_handle; /* N_PORT handle. */
+
+ __le16 cmd_dsd_count;
+
+ __le16 vp_index; /* vp index 9 bits*/
+
+ __le16 timeout; /* Command timeout. */
+ uint16_t reserved_2;
+
+ __le16 rsp_dsd_count;
+
+ uint8_t reserved_3[10];
+ uint8_t reserved_4[28]; /* Reserved. */
+
+ __le32 rsp_byte_count;
+ __le32 cmd_byte_count;
+ struct dsd64 dsd[NUM_CT_DSDS]; /* Data Segment Descriptors */
+};
+
+/*
+ * ISP queue - PUREX IOCB entry structure definition
+ */
+struct purex_entry_24xx_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ __le16 reserved1;
+ __le16 vp_idx; /* VP index 9 bits*/
+
+ __le16 status_flags;
+ __le16 nport_handle;
+
+ __le16 frame_size;
+ __le16 trunc_frame_size;
+
+ __le32 rx_xchg_addr;
+
+ uint8_t d_id[3];
+ uint8_t r_ctl;
+
+ uint8_t s_id[3];
+ uint8_t cs_ctl;
+
+ uint8_t f_ctl[3];
+ uint8_t type;
+
+ __le16 seq_cnt;
+ uint8_t df_ctl;
+ uint8_t seq_id;
+
+ __le16 rx_id;
+ __le16 ox_id;
+ __le32 param;
+
+ uint8_t els_frame_payload[84];
+};
+
+/*
+ * ISP queue - ELS Pass-Through entry structure definition.
+ * ELS_EXT_EST_SOFI*: 4-bit sof_type for extended IOCBs (qla_fw.h EST_SOFI*
+ * is for els_entry_24xx byte layout).
+ */
+#define ELS_EXT_EST_SOFI3 (1 << 1)
+#define ELS_EXT_EST_SOFI2 (3 << 3)
+
+struct els_entry_24xx_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System Defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ __le16 comp_status; /* response only */
+ __le16 nport_handle;
+
+ __le16 tx_dsd_count;
+
+ __le16 vp_index : 9; /* VP Index 9bits */
+ __le16 reserved_1_sof : 3;
+ __le16 sof_type : 4;
+
+ __le32 rx_xchg_address; /* Receive exchange address. */
+ __le16 rx_dsd_count;
+
+ uint8_t opcode;
+ uint8_t reserved_2;
+
+ uint8_t d_id[3];
+ uint8_t s_id[3];
+
+ __le16 control_flags; /* Control flags. */
+
+ union {
+ struct {
+ __le32 rx_byte_count;
+ __le32 tx_byte_count;
+
+ __le64 tx_address __packed; /* DSD 0 address. */
+ __le32 tx_len; /* DSD 0 length. */
+
+ __le64 rx_address __packed; /* DSD 1 address. */
+ __le32 rx_len; /* DSD 1 length. */
+ };
+ struct {
+ __le32 total_byte_count;
+ __le32 error_subcode_1;
+ __le32 error_subcode_2;
+ __le32 error_subcode_3;
+ uint8_t reserved_3[16];
+ };
+ };
+ uint8_t reserved_4[64];
+};
+
+struct els_sts_entry_24xx_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System Defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ __le32 handle; /* System handle. */
+
+ __le16 comp_status;
+
+ __le16 nport_handle; /* N_PORT handle. */
+
+ __le16 reserved_1;
+
+ __le16 vp_index : 9; /* VP Index 9bits */
+ __le16 reserved_1_sof : 3;
+ __le16 sof_type : 4;
+
+ __le32 rx_xchg_address; /* Receive exchange address. */
+ __le16 reserved_2;
+
+ uint8_t opcode;
+ uint8_t reserved_3;
+
+ uint8_t d_id[3];
+ uint8_t s_id[3];
+
+ __le16 control_flags; /* Control flags. */
+ __le32 total_byte_count;
+ __le32 error_subcode_1;
+ __le32 error_subcode_2;
+ __le32 error_subcode_3;
+
+ uint8_t reserved_4[80];
+};
+
+struct logio_entry_24xx_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ __le16 comp_status; /* Completion status. */
+
+ __le16 nport_handle; /* N_PORT handle. */
+
+ __le16 control_flags; /* Control flags. */
+
+ __le16 vp_index; /* VP Index 9bits*/
+
+ uint8_t port_id[3]; /* PortID of destination port. */
+
+ uint8_t rsp_size; /* Response size in 32bit words. */
+
+ __le32 io_parameter[11]; /* General I/O parameters. */
+ uint8_t reserved_2[64]; /* Reserved*/
+};
+
+struct tsk_mgmt_entry_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count; /* Handle count. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ __le16 nport_handle; /* N_PORT handle. */
+
+ __le16 reserved_1;
+
+ __le16 delay; /* Activity delay in seconds. */
+
+ __le16 timeout; /* Command timeout. */
+
+ struct scsi_lun lun; /* FCP LUN (BE). */
+
+ __le32 control_flags; /* Control Flags. */
+
+ __le16 vp_index; /* VP Index 9bits */
+
+ uint8_t reserved_3[98];
+};
+
+struct abort_entry_24xx_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count; /* Handle count. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ union {
+ __le16 nport_handle; /* N_PORT handle. */
+ __le16 comp_status; /* Completion status. */
+ };
+
+ __le16 options; /* Options. */
+
+ uint32_t handle_to_abort; /* System handle to abort. */
+
+ __le16 req_que_no;
+
+ __le16 vp_index; /* VP Index 9bits*/
+ u8 reserved_2[4];
+ union {
+ struct {
+ __le16 abts_rty_cnt;
+ __le16 rsp_timeout;
+ } drv;
+ struct {
+ u8 ba_rjt_vendorUnique;
+ u8 ba_rjt_reasonCodeExpl;
+ u8 ba_rjt_reasonCode;
+ u8 reserved_3;
+ } fw;
+ };
+ u8 reserved_4[100];
+};
+
+struct abts_entry_24xx_ext {
+ uint8_t entry_type;
+ uint8_t entry_count;
+ uint8_t handle_count;
+ uint8_t entry_status;
+
+ __le32 handle; /* type 0x55 only */
+
+ __le16 comp_status; /* type 0x55 only */
+ __le16 nport_handle; /* type 0x54 only */
+
+ __le16 control_flags; /* type 0x55 only */
+ __le16 vp_idx : 9; /* VP index 9 bits */
+ __le16 reserved_1_sof : 3;
+ __le16 sof_type : 4; /* sof_type is upper nibble */
+
+ __le32 rx_xch_addr;
+
+ uint8_t d_id[3];
+ uint8_t r_ctl;
+
+ uint8_t s_id[3];
+ uint8_t cs_ctl;
+
+ uint8_t f_ctl[3];
+ uint8_t type;
+
+ __le16 seq_cnt;
+ uint8_t df_ctl;
+ uint8_t seq_id;
+
+ __le16 rx_id;
+ __le16 ox_id;
+
+ __le32 param;
+
+ union {
+ struct {
+ __le32 subcode3;
+ __le32 rsvd;
+ __le32 subcode1;
+ __le32 subcode2;
+ } error;
+ struct {
+ __le16 rsrvd1;
+ uint8_t last_seq_id;
+ uint8_t seq_id_valid;
+ __le16 aborted_rx_id;
+ __le16 aborted_ox_id;
+ __le16 high_seq_cnt;
+ __le16 low_seq_cnt;
+ } ba_acc;
+ struct {
+ uint8_t vendor_unique;
+ uint8_t explanation;
+ uint8_t reason;
+ } ba_rjt;
+ } payload;
+
+ __le32 rx_xch_addr_to_abort;
+ uint8_t reserved_2[64];
+} __packed;
+/*
+ * Virtual Port Control IOCB
+ */
+struct vp_ctrl_entry_24xx_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ __le16 vp_idx_failed;
+
+ __le16 comp_status; /* Completion status. */
+
+ __le16 command;
+
+ __le16 vp_count;
+
+ uint8_t vp_idx_map[16];
+ __le16 flags;
+ __le16 id;
+ uint16_t reserved_4;
+ __le16 hopct;
+ uint8_t reserved_5[88];
+};
+
+/*
+ * Modify Virtual Port Configuration IOCB
+ */
+struct vp_config_entry_24xx_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count;
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ __le16 flags;
+
+ __le16 comp_status; /* Completion status. */
+
+ uint8_t command;
+
+ uint8_t vp_count;
+
+ uint8_t vp_index1;
+ uint8_t vp_index2;
+
+ uint8_t options_idx1;
+ uint8_t hard_address_idx1;
+ uint16_t reserved_vp1;
+ uint8_t port_name_idx1[WWN_SIZE];
+ uint8_t node_name_idx1[WWN_SIZE];
+
+ uint8_t options_idx2;
+ uint8_t hard_address_idx2;
+ uint16_t reserved_vp2;
+ uint8_t port_name_idx2[WWN_SIZE];
+ uint8_t node_name_idx2[WWN_SIZE];
+ __le16 id;
+ uint16_t reserved_4;
+ __le16 hopct;
+ uint8_t reserved_5[66];
+};
+
+struct vp_rpt_id_entry_24xx_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ __le32 resv1;
+ uint8_t vp_acquired;
+ uint8_t vp_setup;
+ __le16 vp_idx : 9; /* VP Index 9bits */
+ __le16 vp_status : 7; /* VP Status 7bits */
+
+ uint8_t port_id[3];
+ uint8_t format;
+ union {
+ struct vp_rpt_id_ext_f1 {
+ /* format 1 fabric */
+ uint8_t vpstat1_subcode; /* vp_status=1 subcode */
+ uint8_t flags;
+
+ uint16_t fip_flags;
+ uint8_t rsv2[12];
+
+ uint8_t ls_rjt_vendor;
+ uint8_t ls_rjt_explanation;
+ uint8_t ls_rjt_reason;
+ uint8_t rsv3;
+ __le16 rsv8;
+ __le16 flogi_acc_payload_size; /* bits [8:0] meaningful */
+ uint8_t port_name[8];
+ uint8_t node_name[8];
+ uint16_t bbcr;
+ uint8_t reserved_5[6];
+ } f1;
+ struct vp_rpt_id_ext_f2 { /* format 2: N2N direct connect */
+ uint8_t vpstat1_subcode;
+ uint8_t flags;
+ uint16_t fip_flags;
+ uint8_t rsv2[12];
+
+ uint8_t ls_rjt_vendor;
+ uint8_t ls_rjt_explanation;
+ uint8_t ls_rjt_reason;
+ uint8_t rsv3[5];
+
+ uint8_t port_name[8];
+ uint8_t node_name[8];
+ uint16_t bbcr;
+ uint8_t reserved_5[2];
+ uint8_t remote_nport_id[4];
+ } f2;
+ } u;
+};
+#endif
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 10/56] scsi: qla2xxx: Add extended status continuation and marker IOCBs
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (8 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 09/56] scsi: qla2xxx: Add 128-byte IOCB definitions " Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 11:06 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 11/56] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx Nilesh Javali
` (45 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
From: Anil Gurumurthy <agurumurthy@marvell.com>
Add the 128-byte sts_cont_entry_ext_t and mrk_entry_ext_t
structures required by 29xx firmware. Include the qla_fw29.h
header from qla_def.h so the new types are available throughout
the driver.
Signed-off-by: Anil Gurumurthy <agurumurthy@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_def.h | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 7423687578dc..4de0de5cccc8 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -337,6 +337,7 @@ static inline void wrt_reg_dword(volatile __le32 __iomem *addr, u32 data)
#define MAX_CMDSZ 16 /* SCSI maximum CDB size. */
#include "qla_fw.h"
+#include "qla_fw29.h"
struct name_list_extended {
struct get_name_list_extended *l;
@@ -2360,6 +2361,15 @@ typedef struct {
uint8_t reserved_2[48];
} mrk_entry_t;
+/* 29xx definitions */
+struct sts_cont_entry_ext {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint8_t data[124]; /* data */
+};
+
/*
* ISP queue - Management Server entry structure definition.
*/
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 11/56] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (9 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 10/56] scsi: qla2xxx: Add extended status continuation and marker IOCBs Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 11:54 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 12/56] scsi: qla2xxx: Skip image-set-valid attribute " Nilesh Javali
` (44 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
From: Anil Gurumurthy <agurumurthy@marvell.com>
Wire the 128-byte extended IOCB structures into the IO submission,
completion, and queue-management paths. On 29xx adapters the driver
now builds cmd_type_6_ext / cmd_type_7_ext command IOCBs and processes
the corresponding extended status entries, while falling back to the
existing 64-byte IOCBs for earlier adapters.
Ring entry-size selection uses the qla_req_entry_size() /
qla_rsp_entry_size() helpers and ring slot advancement uses
qla_req_ring_advance() rather than open-coding IS_QLA29XX() branches
at every call site.
Signed-off-by: Anil Gurumurthy <agurumurthy@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_dbg.c | 31 +-
drivers/scsi/qla2xxx/qla_def.h | 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 | 1121 ++++++++++++++++++++++++-----
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, 1502 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 f6a8bcbb2a03..bea7b5403650 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..7136ad4ec9a8 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -12,6 +12,19 @@
#include <scsi/scsi_tcq.h>
static int qla_start_scsi_type6(srb_t *sp);
+
+/*
+ * 29xx extended IOCB builders, implemented later in this file. Forward
+ * declared here because the unified 24xx/29xx fast paths dispatch to them
+ * via IS_QLA29XX() branches.
+ */
+static void qla29xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7_ext *cmd_pkt,
+ uint16_t tot_dsds, struct req_que *req);
+static void qla29xx_build_scsi_type_6_iocbs(srb_t *sp,
+ struct cmd_type_6_ext *cmd_pkt, uint16_t tot_dsds);
+static int qla29xx_build_scsi_crc_2_iocbs(srb_t *sp,
+ struct cmd_type_crc_2_ext *cmd_pkt, uint16_t tot_dsds,
+ uint16_t tot_prot_dsds, uint16_t fw_prot_opts);
/**
* qla2x00_get_cmd_direction() - Determine control_flag data direction.
* @sp: SCSI command
@@ -120,20 +133,15 @@ qla2x00_prep_cont_type0_iocb(struct scsi_qla_host *vha)
* Returns a pointer to the continuation type 1 IOCB packet.
*/
cont_a64_entry_t *
-qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct req_que *req)
+qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct qla_hw_data *ha,
+ struct req_que *req)
{
cont_a64_entry_t *cont_pkt;
/* Adjust ring index. */
- req->ring_index++;
- if (req->ring_index == req->length) {
- req->ring_index = 0;
- req->ring_ptr = req->ring;
- } else {
- req->ring_ptr++;
- }
+ qla_req_ring_advance(ha, req);
- cont_pkt = (cont_a64_entry_t *)req->ring_ptr;
+ cont_pkt = (cont_a64_entry_t *)qla_req_ring_slot(ha, req);
/* Load packet defaults. */
put_unaligned_le32(IS_QLAFX00(vha->hw) ? CONTINUE_A64_TYPE_FX00 :
@@ -284,7 +292,8 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
* Five DSDs are available in the Continuation
* Type 1 IOCB.
*/
- cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req);
+ cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+ vha->hw, vha->req);
cur_dsd = cont_pkt->dsd;
avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
}
@@ -464,16 +473,16 @@ qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req)
if (IS_P3P_TYPE(ha)) {
qla82xx_start_iocbs(vha);
} else {
- /* Adjust ring index. */
- req->ring_index++;
- if (req->ring_index == req->length) {
- req->ring_index = 0;
- req->ring_ptr = req->ring;
- } else
- req->ring_ptr++;
+ /*
+ * Adjust ring index. 29xx uses a 128-byte-strided ring, so
+ * advance the extended pointer; all other generations advance
+ * the 64-byte ring_ptr.
+ */
+ qla_req_ring_advance(ha, req);
/* Set chip new ring index. */
- if (ha->mqenable || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+ if (ha->mqenable || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+ IS_QLA29XX(ha)) {
wrt_reg_dword(req->req_q_in, req->ring_index);
} else if (IS_QLA83XX(ha)) {
wrt_reg_dword(req->req_q_in, req->ring_index);
@@ -509,36 +518,46 @@ static int
__qla2x00_marker(struct scsi_qla_host *vha, struct qla_qpair *qpair,
uint16_t loop_id, uint64_t lun, uint8_t type)
{
- mrk_entry_t *mrk;
- struct mrk_entry_24xx *mrk24 = NULL;
- struct req_que *req = qpair->req;
struct qla_hw_data *ha = vha->hw;
+ struct mrk_entry_24xx *mrk24;
+ struct req_que *req = qpair->req;
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
- mrk = (mrk_entry_t *)__qla2x00_alloc_iocbs(qpair, NULL);
- if (mrk == NULL) {
+ /*
+ * FWI2-capable adapters use the 24xx marker layout; 29xx uses the
+ * extended marker IOCB (128 bytes) whose first 64 bytes overlay
+ * mrk_entry_24xx through 'lun', so the common header is written
+ * through a struct mrk_entry_24xx * view and only vp_index (u8 vs
+ * __le16) needs a stride-aware branch. Pre-FWI2 adapters
+ * (ISP21xx/22xx/23xx) use the legacy mrk_entry_t layout with a
+ * 2-byte target ID and a 16-bit LUN.
+ */
+ mrk24 = __qla2x00_alloc_iocbs(qpair, NULL);
+ if (!mrk24) {
ql_log(ql_log_warn, base_vha, 0x3026,
"Failed to allocate Marker IOCB.\n");
-
- return (QLA_FUNCTION_FAILED);
+ return QLA_FUNCTION_FAILED;
}
- mrk24 = (struct mrk_entry_24xx *)mrk;
-
- mrk->entry_type = MARKER_TYPE;
- mrk->modifier = type;
+ mrk24->entry_type = MARKER_TYPE;
+ mrk24->modifier = type;
if (type != MK_SYNC_ALL) {
if (IS_FWI2_CAPABLE(ha)) {
mrk24->nport_handle = cpu_to_le16(loop_id);
int_to_scsilun(lun, (struct scsi_lun *)&mrk24->lun);
host_to_fcp_swap(mrk24->lun, sizeof(mrk24->lun));
- mrk24->vp_index = vha->vp_idx;
+ if (IS_QLA29XX(ha))
+ ((struct mrk_entry_24xx_ext *)mrk24)->vp_index =
+ cpu_to_le16(vha->vp_idx);
+ else
+ mrk24->vp_index = vha->vp_idx;
} else {
+ mrk_entry_t *mrk = (mrk_entry_t *)mrk24;
+
SET_TARGET_ID(ha, mrk->target, loop_id);
mrk->lun = cpu_to_le16((uint16_t)lun);
}
}
-
if (IS_FWI2_CAPABLE(ha))
mrk24->handle = QLA_SKIP_HANDLE;
@@ -747,7 +766,8 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
* Five DSDs are available in the Continuation
* Type 1 IOCB.
*/
- cont_pkt = qla2x00_prep_cont_type1_iocb(vha, req);
+ cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+ vha->hw, req);
cur_dsd = cont_pkt->dsd;
avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
}
@@ -1620,46 +1640,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 +1856,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 +1900,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 +1926,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 +1950,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 +2039,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 +2082,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 +2167,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 +2317,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 +2361,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 +2398,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 +2482,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 +2522,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 +3458,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 +3484,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 +3508,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 +3541,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 +4110,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 +4320,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 +4343,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 +4376,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 +4528,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 +4640,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 +4712,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 +4721,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 +4751,498 @@ qla_start_scsi_type6(srb_t *sp)
return QLA_FUNCTION_FAILED;
}
+
+/*
+ * ---------------------------------------------------------------------------
+ * 29xx extended (128-byte) IOCB fast-path helpers.
+ *
+ * These helpers drive the extended IOCB ring used by 29xx-class adapters and
+ * operate on the *_ext IOCB variants declared in qla_fw29.h
+ * (cmd_type_6_ext, cmd_type_7_ext, cmd_type_crc_2_ext, struct cont_a64_entry_ext).
+ *
+ * They are not yet wired into any dispatch path; the top-level entry points
+ * (e.g. qla29xx_start_scsi / qla29xx_dif_start_scsi_mq) and isp_ops hooks are
+ * added in subsequent merge phases. Until then the top-of-tree builder
+ * functions are marked __maybe_unused so the build stays warning-clean.
+ *
+ * Ported from the out-of-tree qla29xx driver. Signatures have been adapted
+ * to the Mach qla2xxx single-srb_t model:
+ * struct qla_io_srb * --> srb_t *
+ * sp->qcb.qpair --> sp->qpair
+ * sp->qcb.iores --> sp->iores
+ * sp->qcb.handle --> sp->handle
+ * qla2900_hba_err_chk_enabled() --> qla2x00_hba_err_chk_enabled()
+ * ---------------------------------------------------------------------------
+ */
+
+static void qla29xx_copy_dif_iocb_data(struct cmd_type_crc_2_ext *cmd_pkt,
+ struct crc_context *crc_ctx_pkt, uint8_t dif_bundling);
+
+/**
+ * qla2900_prep_cont_type1_iocb() - Initialize a 29xx-ext Continuation
+ * Type 1 IOCB on the extended IOCB ring.
+ * @vha: HA context
+ * @req: request queue
+ *
+ * Returns a pointer to the continuation type 1 IOCB packet.
+ */
+struct cont_a64_entry_ext *
+qla2900_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct req_que *req)
+{
+ struct cont_a64_entry_ext *cont_pkt;
+
+ /*
+ * 29xx uses the 128-byte extended IOCB ring. Advance via ring_ext_ptr
+ * so the pointer arithmetic matches the on-ring stride.
+ */
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ext_ptr = req->ring_ext;
+ } else {
+ req->ring_ext_ptr++;
+ }
+
+ cont_pkt = (struct cont_a64_entry_ext *)req->ring_ext_ptr;
+
+ /* Load packet defaults. */
+ put_unaligned_le32(CONTINUE_A64_TYPE, &cont_pkt->entry_type);
+
+ return cont_pkt;
+}
+
+/*
+ * Note: the 29xx extended path reuses qla24xx_configure_prot_mode() -- the
+ * SCSI-prot-op to PO_MODE_DIF_* translation is independent of the IOCB
+ * generation.
+ */
+
+/**
+ * qla29xx_build_scsi_type_6_iocbs() - Build IOCB command utilizing Command
+ * Type 6 IOCB types on the 29xx extended ring.
+ * @sp: SRB command to process
+ * @cmd_pkt: Command type 6 extended IOCB
+ * @tot_dsds: Total number of segments to transfer
+ */
+static void
+qla29xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6_ext *cmd_pkt,
+ uint16_t tot_dsds)
+{
+ struct dsd64 *cur_dsd = NULL, *next_dsd;
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+ struct scsi_cmnd *cmd;
+ struct scatterlist *cur_seg;
+ uint8_t avail_dsds;
+ uint8_t first_iocb = 1;
+ uint32_t dsd_list_len;
+ struct dsd_dma *dsd_ptr;
+ struct ct6_dsd *ctx;
+ struct qla_qpair *qpair = sp->qpair;
+
+ cmd = GET_CMD_SP(sp);
+
+ /* Update entry type to indicate Command Type 6 IOCB */
+ put_unaligned_le32(COMMAND_TYPE_6, &cmd_pkt->entry_type);
+
+ /* No data transfer */
+ if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) {
+ cmd_pkt->byte_count = cpu_to_le32(0);
+ goto function_end;
+ }
+
+ vha = sp->vha;
+ ha = vha->hw;
+
+ /* Set transfer direction */
+ if (cmd->sc_data_direction == DMA_TO_DEVICE) {
+ cmd_pkt->control_flags = cpu_to_le16(CF_WRITE_DATA);
+ qpair->counters.output_bytes += scsi_bufflen(cmd);
+ qpair->counters.output_requests++;
+ } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ cmd_pkt->control_flags = cpu_to_le16(CF_READ_DATA);
+ qpair->counters.input_bytes += scsi_bufflen(cmd);
+ qpair->counters.input_requests++;
+ }
+
+ cur_seg = scsi_sglist(cmd);
+ ctx = &sp->u.scmd.ct6_ctx;
+
+ while (tot_dsds) {
+ avail_dsds = (tot_dsds > QLA_DSDS_PER_IOCB) ?
+ QLA_DSDS_PER_IOCB : tot_dsds;
+ tot_dsds -= avail_dsds;
+ dsd_list_len = (avail_dsds + 1) * QLA_DSD_SIZE;
+
+ dsd_ptr = list_first_entry(&qpair->dsd_list, struct dsd_dma,
+ list);
+ next_dsd = dsd_ptr->dsd_addr;
+ list_del(&dsd_ptr->list);
+ qpair->dsd_avail--;
+ list_add_tail(&dsd_ptr->list, &ctx->dsd_list);
+ ctx->dsd_use_cnt++;
+ qpair->dsd_inuse++;
+
+ if (first_iocb) {
+ first_iocb = 0;
+ put_unaligned_le64(dsd_ptr->dsd_list_dma,
+ &cmd_pkt->dsd[0].address);
+ cmd_pkt->dsd[0].length = cpu_to_le32(dsd_list_len);
+ } else {
+ put_unaligned_le64(dsd_ptr->dsd_list_dma,
+ &cur_dsd->address);
+ cur_dsd->length = cpu_to_le32(dsd_list_len);
+ cur_dsd++;
+ }
+ cur_dsd = next_dsd;
+ while (avail_dsds) {
+ append_dsd64(&cur_dsd, cur_seg);
+ cur_seg = sg_next(cur_seg);
+ avail_dsds--;
+ }
+ }
+
+ /* Null termination */
+ if (cur_dsd) {
+ cur_dsd->address = 0;
+ cur_dsd->length = 0;
+ cur_dsd++;
+ }
+ cmd_pkt->control_flags |= CF_DATA_SEG_DESCR_ENABLE;
+function_end:
+ return;
+}
+
+/*
+ * Note: the 29xx extended path reuses qla24xx_calc_dsd_lists() -- the
+ * DSD-list-per-IOCB arithmetic (based on QLA_DSDS_PER_IOCB) is generation-
+ * agnostic.
+ */
+
+/**
+ * qla29xx_build_scsi_iocbs() - Build IOCB command utilizing Command Type 7
+ * IOCB types on the 29xx extended ring.
+ * @sp: SRB command to process
+ * @cmd_pkt: Command type 7 extended IOCB
+ * @tot_dsds: Total number of segments to transfer
+ * @req: pointer to request queue
+ */
+static void __maybe_unused
+qla29xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7_ext *cmd_pkt,
+ uint16_t tot_dsds, struct req_que *req)
+{
+ uint8_t avail_dsds;
+ struct dsd64 *cur_dsd;
+ scsi_qla_host_t *vha;
+ struct scsi_cmnd *cmd;
+ struct scatterlist *sg;
+ int i;
+ struct qla_qpair *qpair = sp->qpair;
+
+ cmd = GET_CMD_SP(sp);
+
+ /* Update entry type to indicate Command Type 7 IOCB */
+ put_unaligned_le32(COMMAND_TYPE_7, &cmd_pkt->entry_type);
+
+ /* No data transfer */
+ if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) {
+ cmd_pkt->byte_count = cpu_to_le32(0);
+ return;
+ }
+
+ vha = sp->vha;
+
+ /* Set transfer direction */
+ if (cmd->sc_data_direction == DMA_TO_DEVICE) {
+ cmd_pkt->task_mgmt_flags = cpu_to_le16(TMF_WRITE_DATA);
+ qpair->counters.output_bytes += scsi_bufflen(cmd);
+ qpair->counters.output_requests++;
+ } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ cmd_pkt->task_mgmt_flags = cpu_to_le16(TMF_READ_DATA);
+ qpair->counters.input_bytes += scsi_bufflen(cmd);
+ qpair->counters.input_requests++;
+ }
+
+ /* NUM_CMD67_DSDS DSDs available in the Command Type 7 ext IOCB */
+ avail_dsds = NUM_CMD67_DSDS;
+ cur_dsd = &cmd_pkt->dsd[0];
+
+ scsi_for_each_sg(cmd, sg, tot_dsds, i) {
+ struct cont_a64_entry_ext *cont_pkt;
+
+ /* Allocate additional continuation packets? */
+ if (avail_dsds == 0) {
+ /*
+ * NUM_CONT1_DSDS DSDs are available in the Continuation
+ * Type 1 extended IOCB.
+ */
+ cont_pkt = qla2900_prep_cont_type1_iocb(vha, req);
+ cur_dsd = cont_pkt->dsd;
+ avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
+ }
+
+ append_dsd64(&cur_dsd, sg);
+ avail_dsds--;
+ }
+}
+
+/*
+ * Note: the 29xx extended path reuses qla24xx_set_t10dif_tags() (defined
+ * earlier in this file) -- the T10-DIF ref/app tag layout is identical
+ * between the 24xx and 29xx extended CRC_2 IOCBs (both share the same
+ * fw_dif_context layout inside struct crc_context).
+ */
+
+/*
+ * Note: the 29xx extended CRC_2 path reuses the 24xx SG walkers
+ * (qla24xx_get_one_block_sg, qla24xx_walk_and_build_sglist_no_difb,
+ * qla24xx_walk_and_build_sglist, qla24xx_walk_and_build_prot_sglist).
+ * The DSD-chain / CRC-context layout they produce is generation-agnostic;
+ * only the IOCB that points at the chain (Type-6/7/CRC_2 vs their _ext
+ * variants) differs between generations, and that is handled by the
+ * separate qla29xx_build_* builders.
+ *
+ * The walkers that take a struct qla_tc_param *tc are invoked with tc=NULL
+ * here because the 29xx fast-path does not use the tape/copy offload.
+ */
+
+/**
+ * qla29xx_build_scsi_crc_2_iocbs() - Build IOCB command utilizing Command
+ * Type CRC_2 IOCB types on the 29xx extended ring.
+ * @sp: SRB command to process
+ * @cmd_pkt: Command type CRC_2 extended IOCB
+ * @tot_dsds: Total number of segments to transfer
+ * @tot_prot_dsds: Total number of segments with protection information
+ * @fw_prot_opts: Protection options to be passed to firmware
+ * @bundling: Bundling flag
+ *
+ * Returns 0 if successful, 1 otherwise.
+ */
+static int
+qla29xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2_ext *cmd_pkt,
+ uint16_t tot_dsds, uint16_t tot_prot_dsds, uint16_t fw_prot_opts)
+{
+ struct dsd64 *cur_dsd;
+ __be32 *fcp_dl;
+ scsi_qla_host_t *vha;
+ struct scsi_cmnd *cmd;
+ uint32_t total_bytes = 0;
+ uint32_t data_bytes;
+ uint32_t dif_bytes;
+ uint8_t bundling = 1;
+ uint16_t blk_size;
+ struct crc_context *crc_ctx_pkt = NULL;
+ struct qla_hw_data *ha;
+ uint8_t additional_fcpcdb_len;
+ uint16_t fcp_cmnd_len;
+ struct fcp_cmnd *fcp_cmnd;
+ dma_addr_t crc_ctx_dma;
+
+ cmd = GET_CMD_SP(sp);
+
+ /* Update entry type to indicate Command Type CRC_2 IOCB */
+ put_unaligned_le32(COMMAND_TYPE_CRC_2, &cmd_pkt->entry_type);
+
+ vha = sp->vha;
+ ha = vha->hw;
+
+ /* No data transfer */
+ data_bytes = scsi_bufflen(cmd);
+ if (!data_bytes || cmd->sc_data_direction == DMA_NONE) {
+ cmd_pkt->byte_count = cpu_to_le32(0);
+ return QLA_SUCCESS;
+ }
+
+ cmd_pkt->vp_index = cpu_to_le16(sp->vha->vp_idx);
+
+ /* Set transfer direction */
+ if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ cmd_pkt->control_flags_1 = cpu_to_le16(CF_WRITE_DATA);
+ else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+ cmd_pkt->control_flags_1 = cpu_to_le16(CF_READ_DATA);
+
+ if ((scsi_get_prot_op(cmd) == SCSI_PROT_READ_INSERT) ||
+ (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_STRIP) ||
+ (scsi_get_prot_op(cmd) == SCSI_PROT_READ_STRIP) ||
+ (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_INSERT))
+ bundling = 0;
+
+ /* Allocate CRC context from global pool */
+ crc_ctx_pkt = sp->u.scmd.crc_ctx =
+ dma_pool_zalloc(ha->dl_dma_pool, GFP_ATOMIC, &crc_ctx_dma);
+
+ if (!crc_ctx_pkt)
+ goto crc_queuing_error;
+
+ crc_ctx_pkt->crc_ctx_dma = crc_ctx_dma;
+
+ sp->flags |= SRB_CRC_CTX_DMA_VALID;
+
+ /* Set handle */
+ crc_ctx_pkt->handle = cmd_pkt->handle;
+
+ INIT_LIST_HEAD(&crc_ctx_pkt->dsd_list);
+
+ qla24xx_set_t10dif_tags(sp, (struct fw_dif_context *)
+ &crc_ctx_pkt->ref_tag, tot_prot_dsds);
+
+ /* Determine SCSI command length -- align to 4 byte boundary */
+ if (cmd->cmd_len > 16) {
+ additional_fcpcdb_len = cmd->cmd_len - 16;
+ if ((cmd->cmd_len % 4) != 0) {
+ /* SCSI cmd > 16 bytes must be multiple of 4 */
+ goto crc_queuing_error;
+ }
+ fcp_cmnd_len = 12 + cmd->cmd_len + 4;
+ } else {
+ additional_fcpcdb_len = 0;
+ fcp_cmnd_len = 12 + 16 + 4;
+ }
+
+ fcp_cmnd = &crc_ctx_pkt->fcp_cmnd;
+
+ fcp_cmnd->additional_cdb_len = additional_fcpcdb_len;
+ if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ fcp_cmnd->additional_cdb_len |= 1;
+ else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+ fcp_cmnd->additional_cdb_len |= 2;
+
+ int_to_scsilun(cmd->device->lun, &fcp_cmnd->lun);
+ memcpy(fcp_cmnd->cdb, cmd->cmnd, cmd->cmd_len);
+ cmd_pkt->fcp_cmnd_dseg_len = cpu_to_le16(fcp_cmnd_len);
+ put_unaligned_le64(crc_ctx_dma + CRC_CONTEXT_FCPCMND_OFF,
+ &cmd_pkt->fcp_cmnd_dseg_address);
+ fcp_cmnd->task_management = 0;
+ fcp_cmnd->task_attribute = TSK_SIMPLE;
+
+ cmd_pkt->fcp_rsp_dseg_len = 0; /* Let response come in status iocb */
+
+ /* Compute dif len and adjust data len to include protection */
+ dif_bytes = 0;
+ blk_size = cmd->device->sector_size;
+ dif_bytes = (data_bytes / blk_size) * 8;
+
+ switch (scsi_get_prot_op(GET_CMD_SP(sp))) {
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ total_bytes = data_bytes;
+ data_bytes += dif_bytes;
+ break;
+
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ total_bytes = data_bytes + dif_bytes;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return 1;
+ }
+
+ if (!qla2x00_hba_err_chk_enabled(sp))
+ fw_prot_opts |= 0x10; /* Disable Guard tag checking */
+ /* HBA error checking enabled */
+ else {
+ if ((scsi_get_prot_type(GET_CMD_SP(sp)) == SCSI_PROT_DIF_TYPE1)
+ || (scsi_get_prot_type(GET_CMD_SP(sp)) ==
+ SCSI_PROT_DIF_TYPE2))
+ fw_prot_opts |= BIT_10;
+ else if (scsi_get_prot_type(GET_CMD_SP(sp)) ==
+ SCSI_PROT_DIF_TYPE3)
+ fw_prot_opts |= BIT_11;
+ }
+
+ if (!bundling) {
+ cur_dsd = &crc_ctx_pkt->u.nobundling.data_dsd[0];
+ } else {
+ /*
+ * Configure Bundling if we need to fetch interleaving
+ * protection PCI accesses
+ */
+ fw_prot_opts |= PO_ENABLE_DIF_BUNDLING;
+ crc_ctx_pkt->u.bundling.dif_byte_count = cpu_to_le32(dif_bytes);
+ crc_ctx_pkt->u.bundling.dseg_count = cpu_to_le16(tot_dsds -
+ tot_prot_dsds);
+ cur_dsd = &crc_ctx_pkt->u.bundling.data_dsd[0];
+ }
+
+ /* Finish the common fields of CRC pkt */
+ crc_ctx_pkt->blk_size = cpu_to_le16(blk_size);
+ crc_ctx_pkt->prot_opts = cpu_to_le16(fw_prot_opts);
+ crc_ctx_pkt->byte_count = cpu_to_le32(data_bytes);
+ crc_ctx_pkt->guard_seed = cpu_to_le16(0);
+ /* Fibre channel byte count */
+ cmd_pkt->byte_count = cpu_to_le32(total_bytes);
+ fcp_dl = (__be32 *)(crc_ctx_pkt->fcp_cmnd.cdb + 16 +
+ additional_fcpcdb_len);
+ *fcp_dl = htonl(total_bytes);
+
+ /* Walks data segments */
+
+ cmd_pkt->control_flags_1 |= cpu_to_le16(CF_DATA_SEG_DESCR_ENABLE);
+
+ if (!bundling && tot_prot_dsds) {
+ if (qla24xx_walk_and_build_sglist_no_difb(ha, sp,
+ cur_dsd, tot_dsds, NULL))
+ goto crc_queuing_error;
+ } else if (qla24xx_walk_and_build_sglist(ha, sp, cur_dsd,
+ (tot_dsds - tot_prot_dsds), NULL))
+ goto crc_queuing_error;
+
+ if (bundling && tot_prot_dsds) {
+ /* Walks dif segments */
+ cmd_pkt->control_flags_1 |= cpu_to_le16(CF_DIF_SEG_DESCR_ENABLE);
+ cur_dsd = &crc_ctx_pkt->u.bundling.dif_dsd;
+ if (qla24xx_walk_and_build_prot_sglist(ha, sp, cur_dsd,
+ tot_prot_dsds, NULL))
+ goto crc_queuing_error;
+ }
+ qla29xx_copy_dif_iocb_data(cmd_pkt, crc_ctx_pkt, bundling);
+
+ return QLA_SUCCESS;
+
+crc_queuing_error:
+ /* Cleanup will be performed by the caller */
+ return QLA_FUNCTION_FAILED;
+}
+
+/**
+ * qla29xx_copy_dif_iocb_data() - Populate the DIF-related fields of an
+ * extended CRC_2 command IOCB from a freshly populated crc_context.
+ * @cmd_pkt: Extended CRC_2 command IOCB
+ * @crc_ctx_pkt: Source CRC context
+ * @dif_bundling: DIF bundling flag
+ */
+static void
+qla29xx_copy_dif_iocb_data(struct cmd_type_crc_2_ext *cmd_pkt,
+ struct crc_context *crc_ctx_pkt, uint8_t dif_bundling)
+{
+ cmd_pkt->ref_tag = crc_ctx_pkt->ref_tag;
+ cmd_pkt->app_tag = crc_ctx_pkt->app_tag;
+ memcpy(cmd_pkt->ref_tag_mask, crc_ctx_pkt->ref_tag_mask,
+ sizeof(cmd_pkt->ref_tag_mask));
+ memcpy(cmd_pkt->app_tag_mask, crc_ctx_pkt->app_tag_mask,
+ sizeof(cmd_pkt->app_tag_mask));
+ cmd_pkt->blk_size = crc_ctx_pkt->blk_size;
+ cmd_pkt->prot_opts = crc_ctx_pkt->prot_opts;
+ cmd_pkt->tot_byte_count = crc_ctx_pkt->byte_count;
+ if (dif_bundling) {
+ cmd_pkt->u.bundling.guard_seed = crc_ctx_pkt->guard_seed;
+ cmd_pkt->u.bundling.dif_byte_count =
+ crc_ctx_pkt->u.bundling.dif_byte_count;
+ cmd_pkt->u.bundling.dseg_count =
+ crc_ctx_pkt->u.bundling.dseg_count;
+ memcpy(cmd_pkt->u.bundling.data_dsd,
+ crc_ctx_pkt->u.bundling.data_dsd,
+ sizeof(cmd_pkt->u.bundling.data_dsd));
+ memcpy(&cmd_pkt->u.bundling.dif_dsd,
+ &crc_ctx_pkt->u.bundling.dif_dsd,
+ sizeof(cmd_pkt->u.bundling.dif_dsd));
+ } else {
+ cmd_pkt->u.nobundling.guard_seed = crc_ctx_pkt->guard_seed;
+ memcpy(cmd_pkt->u.nobundling.data_dsd,
+ crc_ctx_pkt->u.nobundling.data_dsd,
+ sizeof(cmd_pkt->u.nobundling.data_dsd));
+ }
+}
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index c47c38e099ff..e95fb0e59f38 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -209,13 +209,7 @@ void __qla_consume_iocb(struct scsi_qla_host *vha,
new_pkt = rsp_q->ring_ptr;
*pkt = new_pkt;
- rsp_q->ring_index++;
- if (rsp_q->ring_index == rsp_q->length) {
- rsp_q->ring_index = 0;
- rsp_q->ring_ptr = rsp_q->ring;
- } else {
- rsp_q->ring_ptr++;
- }
+ qla_rsp_ring_advance(rsp_q);
new_pkt->signature = RESPONSE_PROCESSED;
/* flush signature */
@@ -287,13 +281,7 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
break;
}
- rsp_q->ring_index++;
- if (rsp_q->ring_index == rsp_q->length) {
- rsp_q->ring_index = 0;
- rsp_q->ring_ptr = rsp_q->ring;
- } else {
- rsp_q->ring_ptr++;
- }
+ qla_rsp_ring_advance(rsp_q);
no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
sizeof(new_pkt->data) : pending_bytes;
if ((buffer_copy_offset + no_bytes) <= total_bytes) {
@@ -1212,13 +1200,7 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
break;
}
- rsp_q->ring_index++;
- if (rsp_q->ring_index == rsp_q->length) {
- rsp_q->ring_index = 0;
- rsp_q->ring_ptr = rsp_q->ring;
- } else {
- rsp_q->ring_ptr++;
- }
+ qla_rsp_ring_advance(rsp_q);
no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
sizeof(new_pkt->data) : pending_bytes;
if ((buffer_copy_offset + no_bytes) <= total_bytes) {
@@ -2973,13 +2955,7 @@ qla2x00_process_response_queue(struct rsp_que *rsp)
while (rsp->ring_ptr->signature != RESPONSE_PROCESSED) {
pkt = (sts_entry_t *)rsp->ring_ptr;
- rsp->ring_index++;
- if (rsp->ring_index == rsp->length) {
- rsp->ring_index = 0;
- rsp->ring_ptr = rsp->ring;
- } else {
- rsp->ring_ptr++;
- }
+ qla_rsp_ring_advance(rsp);
if (pkt->entry_status != 0) {
qla2x00_error_entry(vha, rsp, pkt);
@@ -3941,9 +3917,10 @@ static int qla_chk_cont_iocb_avail(struct scsi_qla_host *vha,
}
static void qla_marker_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
- struct mrk_entry_24xx *pkt)
+ void *pkt)
{
const char func[] = "MRK-IOCB";
+ struct mrk_entry_24xx *mrk = pkt;
srb_t *sp;
int res = QLA_SUCCESS;
@@ -3954,7 +3931,7 @@ static void qla_marker_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
if (!sp)
return;
- if (pkt->entry_status) {
+ if (mrk->entry_status) {
ql_dbg(ql_dbg_taskm, vha, 0x8025, "marker failure.\n");
res = QLA_COMMAND_ERROR;
}
@@ -4003,13 +3980,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
pkt = (struct sts_entry_24xx *)rsp->ring_ptr;
cur_ring_index = rsp->ring_index;
- rsp->ring_index++;
- if (rsp->ring_index == rsp->length) {
- rsp->ring_index = 0;
- rsp->ring_ptr = rsp->ring;
- } else {
- rsp->ring_ptr++;
- }
+ qla_rsp_ring_advance(rsp);
if (pkt->entry_status != 0) {
if (qla2x00_error_entry(vha, rsp, (sts_entry_t *) pkt))
@@ -4079,7 +4050,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
(struct nack_to_isp *)pkt);
break;
case MARKER_TYPE:
- qla_marker_iocb_entry(vha, rsp->req, (struct mrk_entry_24xx *)pkt);
+ qla_marker_iocb_entry(vha, rsp->req, pkt);
break;
case ABORT_IOCB_TYPE:
qla24xx_abort_iocb_entry(vha, rsp->req,
@@ -4127,8 +4098,8 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
* interrupt with all IOCBs to arrive
* and re-process.
*/
- rsp->ring_ptr = (response_t *)pkt;
- rsp->ring_index = cur_ring_index;
+ qla_rsp_ring_rewind_to(rsp,
+ (response_t *)pkt, cur_ring_index);
ql_dbg(ql_dbg_init, vha, 0x5091,
"Defer processing ELS opcode %#x...\n",
@@ -4150,8 +4121,8 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
case PT_LS4_UNSOL:
p = (void *)pkt;
if (qla_chk_cont_iocb_avail(vha, rsp, (response_t *)pkt, rsp_in)) {
- rsp->ring_ptr = (response_t *)pkt;
- rsp->ring_index = cur_ring_index;
+ qla_rsp_ring_rewind_to(rsp, (response_t *)pkt,
+ cur_ring_index);
ql_dbg(ql_dbg_init, vha, 0x2124,
"Defer processing UNSOL LS req opcode %#x...\n",
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index e75b7ae22bc5..bb2521c052bf 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -574,13 +574,11 @@ qla25xx_free_req_que(struct scsi_qla_host *vha, struct req_que *req)
{
struct qla_hw_data *ha = vha->hw;
uint16_t que_id = req->id;
- uint16_t reqsz;
+ size_t req_entry_size = qla_req_entry_size(ha);
- reqsz = IS_QLA29XX(ha) ? sizeof(struct request_ext) :
- sizeof(request_t);
-
- dma_free_coherent(&ha->pdev->dev, (req->length + 1) *
- reqsz, req->ring, req->dma);
+ dma_free_coherent(&ha->pdev->dev,
+ (req->length + 1) * req_entry_size,
+ req->ring, req->dma);
req->ring = NULL;
req->dma = 0;
if (que_id) {
@@ -598,10 +596,7 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
{
struct qla_hw_data *ha = vha->hw;
uint16_t que_id = rsp->id;
- uint16_t rspsz;
-
- rspsz = IS_QLA29XX(ha) ? sizeof(struct response_ext) :
- sizeof(response_t);
+ size_t rsp_entry_size = qla_rsp_entry_size(ha);
if (rsp->msix && rsp->msix->have_irq) {
free_irq(rsp->msix->vector, rsp->msix->handle);
@@ -610,8 +605,9 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
rsp->msix->handle = NULL;
}
- dma_free_coherent(&ha->pdev->dev, (rsp->length + 1) *
- rspsz, rsp->ring, rsp->dma);
+ dma_free_coherent(&ha->pdev->dev,
+ (rsp->length + 1) * rsp_entry_size,
+ rsp->ring, rsp->dma);
rsp->ring = NULL;
rsp->dma = 0;
if (que_id) {
@@ -715,7 +711,7 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
uint16_t que_id = 0;
device_reg_t *reg;
uint32_t cnt;
- uint16_t reqsz;
+ size_t req_entry_size = qla_req_entry_size(ha);
req = kzalloc_obj(struct req_que);
if (req == NULL) {
@@ -724,18 +720,19 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
goto failed;
}
- reqsz = IS_QLA29XX(ha) ? sizeof(struct request_ext) :
- sizeof(request_t);
-
req->length = REQUEST_ENTRY_CNT_24XX;
req->ring = dma_alloc_coherent(&ha->pdev->dev,
- (req->length + 1) * reqsz,
+ (req->length + 1) * req_entry_size,
&req->dma, GFP_KERNEL);
if (req->ring == NULL) {
ql_log(ql_log_fatal, base_vha, 0x00da,
"Failed to allocate memory for request_ring.\n");
goto que_failed;
}
+ if (IS_QLA29XX(ha)) {
+ req->ring_ext = (struct request_ext *)req->ring;
+ req->ring_ext_ptr = req->ring_ext;
+ }
ret = qla2x00_alloc_outstanding_cmds(ha, req);
if (ret != QLA_SUCCESS)
@@ -781,7 +778,15 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
req->outstanding_cmds[cnt] = NULL;
req->current_outstanding_cmd = 1;
+ /*
+ * Re-anchor both the 24xx (ring_ptr) and 29xx (ring_ext_ptr) views
+ * at the start of the ring. Keeping them reset together here
+ * guarantees they stay in sync with ring_index=0 regardless of any
+ * prior allocator/fast-path advances against this queue.
+ */
req->ring_ptr = req->ring;
+ if (IS_QLA29XX(ha))
+ req->ring_ext_ptr = req->ring_ext;
req->ring_index = 0;
req->cnt = req->length;
req->id = que_id;
@@ -789,7 +794,15 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
req->req_q_in = ®->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] 87+ messages in thread
* [PATCH v4 12/56] scsi: qla2xxx: Skip image-set-valid attribute for 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (10 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 11/56] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 13/56] scsi: qla2xxx: Skip unsupported sysfs attributes " Nilesh Javali
` (43 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
The 29xx adapter does not support the QLA_IMG_SET_VALID_SUPPORT
driver attribute. Gate the attribute behind an IS_QLA29XX()
check so that userspace applications querying driver capabilities
via BSG receive accurate information.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_bsg.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 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] 87+ messages in thread
* [PATCH v4 13/56] scsi: qla2xxx: Skip unsupported sysfs attributes for 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (11 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 12/56] scsi: qla2xxx: Skip image-set-valid attribute " Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 14/56] scsi: qla2xxx: Enable get_fw_version mailbox " Nilesh Javali
` (42 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Not all sysfs attributes are applicable to the 29xx adapter.
Return -EPERM for attributes that are meaningless on 29xx (gold
firmware version, 84xx firmware version, flash block size, VLAN
ID, VN-port MAC address, and CNA firmware dump toggle) so that
userspace tools do not see stale or undefined values.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_attr.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 800751ab562a..e8755ab86b6a 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1471,6 +1471,9 @@ qla2x00_optrom_gold_fw_version_show(struct device *dev,
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
struct qla_hw_data *ha = vha->hw;
+ if (IS_QLA29XX(ha))
+ return -EPERM;
+
if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
return scnprintf(buf, PAGE_SIZE, "\n");
@@ -1499,6 +1502,9 @@ qla24xx_84xx_fw_version_show(struct device *dev,
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
struct qla_hw_data *ha = vha->hw;
+ if (IS_QLA29XX(ha))
+ return -EPERM;
+
if (!IS_QLA84XX(ha))
return scnprintf(buf, PAGE_SIZE, "\n");
@@ -1565,6 +1571,9 @@ qla2x00_flash_block_size_show(struct device *dev,
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
struct qla_hw_data *ha = vha->hw;
+ if (IS_QLA29XX(ha))
+ return -EPERM;
+
return scnprintf(buf, PAGE_SIZE, "0x%x\n", ha->fdt_block_size);
}
@@ -1573,6 +1582,10 @@ qla2x00_vlan_id_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+
+ if (IS_QLA29XX(ha))
+ return -EPERM;
if (!IS_CNA_CAPABLE(vha->hw))
return scnprintf(buf, PAGE_SIZE, "\n");
@@ -1585,6 +1598,10 @@ qla2x00_vn_port_mac_address_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+
+ if (IS_QLA29XX(ha))
+ return -EPERM;
if (!IS_CNA_CAPABLE(vha->hw))
return scnprintf(buf, PAGE_SIZE, "\n");
@@ -1716,6 +1733,10 @@ qla2x00_allow_cna_fw_dump_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+
+ if (IS_QLA29XX(ha))
+ return -EPERM;
if (!IS_P3P_TYPE(vha->hw))
return scnprintf(buf, PAGE_SIZE, "\n");
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 14/56] scsi: qla2xxx: Enable get_fw_version mailbox for 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (12 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 13/56] scsi: qla2xxx: Skip unsupported sysfs attributes " Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 12:10 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 15/56] scsi: qla2xxx: Extend execute_fw mailbox to include 29xx Nilesh Javali
` (41 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
The serdes_version and several firmware capability fields were not
populated for 29xx because the get_fw_version mailbox path
excluded it from the 27xx/28xx checks. Add IS_QLA29XX() to
the relevant conditionals so that firmware version, EDIF, and
serdes information are correctly retrieved on 29xx adapters.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_attr.c | 4 ++--
drivers/scsi/qla2xxx/qla_mbx.c | 8 ++++----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index e8755ab86b6a..c44f5282abb3 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1526,7 +1526,7 @@ qla2x00_serdes_version_show(struct device *dev, struct device_attribute *attr,
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
struct qla_hw_data *ha = vha->hw;
- if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+ if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
return scnprintf(buf, PAGE_SIZE, "\n");
return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d\n",
@@ -2386,7 +2386,7 @@ qla2x00_fw_attr_show(struct device *dev,
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
struct qla_hw_data *ha = vha->hw;
- if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+ if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
return scnprintf(buf, PAGE_SIZE, "\n");
return scnprintf(buf, PAGE_SIZE, "%llx\n",
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 9c2633ca5036..0feb98b83293 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -1136,7 +1136,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8;
if (IS_FWI2_CAPABLE(ha))
mcp->in_mb |= MBX_17|MBX_16|MBX_15;
- if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
+ if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
mcp->in_mb |=
MBX_25|MBX_24|MBX_23|MBX_22|MBX_21|MBX_20|MBX_19|MBX_18|
MBX_14|MBX_13|MBX_11|MBX_10|MBX_9|MBX_8|MBX_7;
@@ -1212,7 +1212,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
vha->flags.nvme2_enabled = 1;
}
- if (IS_QLA28XX(ha) && ha->flags.edif_hw && ql2xsecenable &&
+ if ((IS_QLA28XX(ha) || IS_QLA29XX(ha)) && ha->flags.edif_hw && ql2xsecenable &&
(ha->fw_attributes_ext[0] & FW_ATTR_EXT0_EDIF)) {
ha->flags.edif_enabled = 1;
ql_log(ql_log_info, vha, 0xffff,
@@ -1220,7 +1220,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
}
}
- if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+ if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
ha->serdes_version[0] = mcp->mb[7] & 0xff;
ha->serdes_version[1] = mcp->mb[8] >> 8;
ha->serdes_version[2] = mcp->mb[8] & 0xff;
@@ -1234,7 +1234,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
ha->fw_shared_ram_end = (mcp->mb[21] << 16) | mcp->mb[20];
ha->fw_ddr_ram_start = (mcp->mb[23] << 16) | mcp->mb[22];
ha->fw_ddr_ram_end = (mcp->mb[25] << 16) | mcp->mb[24];
- if (IS_QLA28XX(ha)) {
+ if (IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
if (mcp->mb[16] & BIT_10)
ha->flags.secure_fw = 1;
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 15/56] scsi: qla2xxx: Extend execute_fw mailbox to include 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (13 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 14/56] scsi: qla2xxx: Enable get_fw_version mailbox " Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 16/56] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx Nilesh Javali
` (40 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Add IS_QLA29XX() to the BPM capability macros and to the
execute-firmware mailbox command so that NVMe enable, minimum
speed negotiation, 128 Gbps speed reporting, EDIF hardware
detection, and FW-semaphore retry logic all apply to 29xx
adapters.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_attr.c | 12 ++++++++++--
drivers/scsi/qla2xxx/qla_def.h | 5 +++--
drivers/scsi/qla2xxx/qla_gs.c | 16 +++++++++++++---
drivers/scsi/qla2xxx/qla_isr.c | 2 +-
drivers/scsi/qla2xxx/qla_mbx.c | 23 +++++++++++++----------
5 files changed, 40 insertions(+), 18 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index c44f5282abb3..7b7722de2844 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1784,10 +1784,11 @@ qla2x00_min_supported_speed_show(struct device *dev,
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
struct qla_hw_data *ha = vha->hw;
- if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+ if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
return scnprintf(buf, PAGE_SIZE, "\n");
return scnprintf(buf, PAGE_SIZE, "%s\n",
+ ha->min_supported_speed == 7 ? "128Gps" :
ha->min_supported_speed == 6 ? "64Gps" :
ha->min_supported_speed == 5 ? "32Gps" :
ha->min_supported_speed == 4 ? "16Gps" :
@@ -1803,10 +1804,11 @@ qla2x00_max_supported_speed_show(struct device *dev,
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
struct qla_hw_data *ha = vha->hw;
- if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+ if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
return scnprintf(buf, PAGE_SIZE, "\n");
return scnprintf(buf, PAGE_SIZE, "%s\n",
+ ha->max_supported_speed == 3 ? "128Gps" :
ha->max_supported_speed == 2 ? "64Gps" :
ha->max_supported_speed == 1 ? "32Gps" :
ha->max_supported_speed == 0 ? "16Gps" : "unknown");
@@ -1887,6 +1889,7 @@ static const struct {
{ PORT_SPEED_16GB, "16" },
{ PORT_SPEED_32GB, "32" },
{ PORT_SPEED_64GB, "64" },
+ { PORT_SPEED_128GB, "128" },
{ PORT_SPEED_10GB, "10" },
};
@@ -2668,6 +2671,9 @@ qla2x00_get_host_speed(struct Scsi_Host *shost)
case PORT_SPEED_64GB:
speed = FC_PORTSPEED_64GBIT;
break;
+ case PORT_SPEED_128GB:
+ speed = FC_PORTSPEED_128GBIT;
+ break;
default:
speed = FC_PORTSPEED_UNKNOWN;
break;
@@ -3429,6 +3435,8 @@ qla2x00_get_host_supported_speeds(scsi_qla_host_t *vha, uint speeds)
{
uint supported_speeds = FC_PORTSPEED_UNKNOWN;
+ if (speeds & FDMI_PORT_SPEED_128GB)
+ supported_speeds |= FC_PORTSPEED_128GBIT;
if (speeds & FDMI_PORT_SPEED_64GB)
supported_speeds |= FC_PORTSPEED_64GBIT;
if (speeds & FDMI_PORT_SPEED_32GB)
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 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] 87+ messages in thread
* [PATCH v4 16/56] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (14 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 15/56] scsi: qla2xxx: Extend execute_fw mailbox to include 29xx Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 12:19 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 17/56] scsi: qla2xxx: Enable init_firmware " Nilesh Javali
` (39 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Add IS_QLA29XX() alongside the existing 27xx/28xx checks in
qla2x00_get_adapter_id() so that the additional mailbox
registers (buffer-to-buffer credit, SCM/EDC status) are read
on 29xx adapters.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_mbx.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 52d70b61654c..3fc08120fdf1 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -1772,7 +1772,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10;
if (IS_FWI2_CAPABLE(vha->hw))
mcp->in_mb |= MBX_19|MBX_18|MBX_17|MBX_16;
- if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw))
+ if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw) || IS_QLA29XX(vha->hw))
mcp->in_mb |= MBX_15|MBX_21|MBX_22|MBX_23;
mcp->tov = MBX_TOV_SECONDS;
@@ -1827,7 +1827,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
}
}
- if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw)) {
+ if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw) || IS_QLA29XX(vha->hw)) {
vha->bbcr = mcp->mb[15];
if (mcp->mb[7] & SCM_EDC_ACC_RECEIVED) {
ql_log(ql_log_info, vha, 0x11a4,
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 17/56] scsi: qla2xxx: Enable init_firmware mailbox for 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (15 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 16/56] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 12:48 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 18/56] scsi: qla2xxx: Enable get_firmware_state " Nilesh Javali
` (38 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
The init_firmware mailbox command needs 29xx adapter support for reading
back SFP information via mb3 and for validating SFP status on successful
firmware initialization. Add IS_QLA29XX() checks alongside the existing
27xx/28xx checks.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_mbx.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 3fc08120fdf1..9c78aa66e12b 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -1968,7 +1968,7 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
/* 1 and 2 should normally be captured. */
mcp->in_mb = MBX_2|MBX_1|MBX_0;
- if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+ if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
/* mb3 is additional info about the installed SFP. */
mcp->in_mb |= MBX_3;
mcp->buf_size = size;
@@ -1992,7 +1992,7 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
0x0104d, ha->ex_init_cb, sizeof(*ha->ex_init_cb));
}
} else {
- if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+ if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
if (mcp->mb[2] == 6 || mcp->mb[3] == 2)
ql_dbg(ql_dbg_mbx, vha, 0x119d,
"Invalid SFP/Validation Failed\n");
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 18/56] scsi: qla2xxx: Enable get_firmware_state for 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (16 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 17/56] scsi: qla2xxx: Enable init_firmware " Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 13:13 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 19/56] scsi: qla2xxx: Enable serdes, resource count and FCE trace " Nilesh Javali
` (37 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Enable get_firmware_state mailbox command for 29xx adapters by adding
IS_QLA29XX() checks alongside existing IS_QLA27XX/IS_QLA28XX checks.
This ensures MBX_12 (MPI state) is properly set up and reported for
29xx adapters.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_attr.c | 2 +-
drivers/scsi/qla2xxx/qla_mbx.c | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 7b7722de2844..5a934f40323e 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -2436,7 +2436,7 @@ qla2x00_mpi_fw_state_show(struct device *dev, struct device_attribute *attr,
u16 mpi_state;
struct qla_hw_data *ha = vha->hw;
- if (!(IS_QLA27XX(ha) || IS_QLA28XX(ha)))
+ if (!(IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)))
return scnprintf(buf, PAGE_SIZE,
"MPI state reporting is not supported for this HBA.\n");
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 9c78aa66e12b..a91ac59dd9c0 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -2283,7 +2283,7 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
else
mcp->in_mb = MBX_1|MBX_0;
- if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+ if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
mcp->mb[12] = 0;
mcp->out_mb |= MBX_12;
mcp->in_mb |= MBX_12;
@@ -2301,7 +2301,7 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
states[3] = mcp->mb[4];
states[4] = mcp->mb[5];
states[5] = mcp->mb[6]; /* DPORT status */
- if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
+ if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
states[11] = mcp->mb[12]; /* MPI state. */
}
@@ -2309,7 +2309,7 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
/*EMPTY*/
ql_dbg(ql_dbg_mbx, vha, 0x1055, "Failed=%x.\n", rval);
} else {
- if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+ if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
if (mcp->mb[2] == 6 || mcp->mb[3] == 2)
ql_dbg(ql_dbg_mbx, vha, 0x119e,
"Invalid SFP/Validation Failed\n");
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 19/56] scsi: qla2xxx: Enable serdes, resource count and FCE trace for 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (17 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 18/56] scsi: qla2xxx: Enable get_firmware_state " Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 13:10 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 20/56] scsi: qla2xxx: Enable set_els_cmds and echo_test " Nilesh Javali
` (36 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
The 29xx adapters share the diagnostic and management interfaces
already supported on ISP27xx/28xx, but several family capability
gates still omitted IS_QLA29XX(), leaving these paths unreachable
on 29xx.
Add IS_QLA29XX() to the relevant checks so the following work on
29xx adapters:
- read/write SerDes word mailbox commands for PHY register access.
- get_resource_cnts requests MBX_12 to report the extended
firmware resource counts.
- FCE trace: the enable-FCE mailbox command, the "fce" and
"fw_resource_count" debugfs nodes in qla2x00_dfs_setup(), the
debugfs enable write in qla2x00_dfs_fce_write(), and the FCE DMA
buffer allocation in qla2x00_alloc_fce_trace().
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_dfs.c | 4 ++--
drivers/scsi/qla2xxx/qla_init.c | 2 +-
drivers/scsi/qla2xxx/qla_mbx.c | 11 +++++++----
3 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c
index 43970caca7b3..177d47e92e49 100644
--- a/drivers/scsi/qla2xxx/qla_dfs.c
+++ b/drivers/scsi/qla2xxx/qla_dfs.c
@@ -497,7 +497,7 @@ qla2x00_dfs_fce_write(struct file *file, const char __user *buffer,
unsigned long enable;
if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
- !IS_QLA27XX(ha) && !IS_QLA28XX(ha)) {
+ !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) {
ql_dbg(ql_dbg_user, vha, 0xd034,
"this adapter does not support FCE.");
return -EINVAL;
@@ -698,7 +698,7 @@ qla2x00_dfs_setup(scsi_qla_host_t *vha)
struct qla_hw_data *ha = vha->hw;
if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
- !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+ !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
goto out;
if (qla2x00_dfs_root)
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index bea7b5403650..e31997241c34 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] 87+ messages in thread
* [PATCH v4 20/56] scsi: qla2xxx: Enable set_els_cmds and echo_test for 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (18 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 19/56] scsi: qla2xxx: Enable serdes, resource count and FCE trace " Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 21/56] scsi: qla2xxx: Add support for QLA29XX in data rate functions Nilesh Javali
` (35 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Add IS_QLA29XX() checks to qla25xx_set_els_cmds_supported() and
qla2x00_echo_test() so that ELS command support and echo test
diagnostics are available on 29xx series adapters.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_mbx.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index d195723fc06b..a8dd01cb9a9a 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -5087,7 +5087,8 @@ qla25xx_set_els_cmds_supported(scsi_qla_host_t *vha)
struct qla_hw_data *ha = vha->hw;
if (!IS_QLA25XX(ha) && !IS_QLA2031(ha) &&
- !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+ !IS_QLA27XX(ha) && !IS_QLA28XX(ha) &&
+ !IS_QLA29XX(ha))
return QLA_SUCCESS;
ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1197,
@@ -5492,10 +5493,10 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq,
mcp->in_mb = MBX_0;
if (IS_CNA_CAPABLE(ha) || IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) ||
- IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+ IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
mcp->in_mb |= MBX_1;
if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) ||
- IS_QLA28XX(ha))
+ IS_QLA28XX(ha) || IS_QLA29XX(ha))
mcp->in_mb |= MBX_3;
mcp->tov = MBX_TOV_SECONDS;
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 21/56] scsi: qla2xxx: Add support for QLA29XX in data rate functions
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (19 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 20/56] scsi: qla2xxx: Enable set_els_cmds and echo_test " Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 22/56] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx Nilesh Javali
` (34 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Enhance the qla2x00_set_data_rate and qla2x00_get_data_rate
functions to include checks for the QLA29XX series adapters.
This modification ensures that the mailbox commands are correctly
configured for the 29xx series, improving functionality and
compatibility.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_attr.c | 2 +-
drivers/scsi/qla2xxx/qla_mbx.c | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 5a934f40323e..c32930f9e9f9 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1824,7 +1824,7 @@ qla2x00_port_speed_store(struct device *dev, struct device_attribute *attr,
int mode = QLA_SET_DATA_RATE_LR;
struct qla_hw_data *ha = vha->hw;
- if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha)) {
+ if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) {
ql_log(ql_log_warn, vha, 0x70d8,
"Speed setting not supported \n");
return -EINVAL;
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index a8dd01cb9a9a..c073dc35ef8e 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -5683,7 +5683,7 @@ qla2x00_set_data_rate(scsi_qla_host_t *vha, uint16_t mode)
mcp->out_mb = MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_2|MBX_1|MBX_0;
- if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+ if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
mcp->in_mb |= MBX_4|MBX_3;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
@@ -5721,7 +5721,7 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
mcp->mb[1] = QLA_GET_DATA_RATE;
mcp->out_mb = MBX_1|MBX_0;
mcp->in_mb = MBX_2|MBX_1|MBX_0;
- if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+ if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
mcp->in_mb |= MBX_4|MBX_3;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
@@ -5733,7 +5733,7 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
if (mcp->mb[1] != 0x7)
ha->link_data_rate = mcp->mb[1];
- if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+ if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
if (mcp->mb[4] & BIT_0)
ql_log(ql_log_info, vha, 0x11a2,
"FEC=enabled (data rate).\n");
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 22/56] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (20 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 21/56] scsi: qla2xxx: Add support for QLA29XX in data rate functions Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 13:30 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 23/56] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs Nilesh Javali
` (33 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Enable qla2x00_shutdown for 29xx adapter by adding IS_QLA29XX check
to the shutdown path that performs firmware abort cleanup.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_os.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 146701445485..5450c40259bf 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -3840,7 +3840,7 @@ qla2x00_shutdown(struct pci_dev *pdev)
qla2x00_disable_eft_trace(vha);
if (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) ||
- IS_QLA28XX(ha)) {
+ IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
if (ha->flags.fw_started)
qla2x00_abort_isp_cleanup(vha);
} else {
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 23/56] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (21 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 22/56] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 24/56] scsi: qla2xxx: Add support for QLA29XX in memory allocation Nilesh Javali
` (32 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
__qla2x00_alloc_iocbs() open-codes ring pointer selection and entry
size based on IS_QLA29XX(ha): 29xx reaches the slot via ring_ext_ptr
and zeroes REQUEST_ENTRY_SIZE_EXT bytes, while other adapters use
ring_ptr with REQUEST_ENTRY_SIZE bytes.
Replace the two branches with the qla_req_ring_slot() and
qla_req_entry_size() helpers, and initialise pkt at declaration.
The IS_QLAFX00 register-mapped writes remain guarded because
IS_QLAFX00 and IS_QLA29XX cannot be true simultaneously.
No functional change: the bytes written to the firmware-visible IOCB
are identical.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_iocb.c | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 7136ad4ec9a8..71fb6f3584ea 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -2465,10 +2465,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;
@@ -2529,13 +2528,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] 87+ messages in thread
* [PATCH v4 24/56] scsi: qla2xxx: Add support for QLA29XX in memory allocation
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (22 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 23/56] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 25/56] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters Nilesh Javali
` (31 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Enhance the qla2x00_mem_alloc function to include checks for
QLA29XX adapters. This modification updates the conditions for
memory allocation and cleanup, ensuring proper handling of the
new adapter series alongside existing QLA27XX and QLA28XX checks.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_os.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 5450c40259bf..3be59179a024 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -4285,7 +4285,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
if (!ha->srb_mempool)
goto fail_free_gid_list;
- if (IS_P3P_TYPE(ha) || IS_QLA27XX(ha) || (ql2xsecenable && IS_QLA28XX(ha))) {
+ if (IS_P3P_TYPE(ha) || IS_QLA27XX(ha) ||
+ (ql2xsecenable && (IS_QLA28XX(ha) || IS_QLA29XX(ha)))) {
/* Allocate cache for CT6 Ctx. */
if (!ctx_cachep) {
ctx_cachep = kmem_cache_create("qla2xxx_ctx",
@@ -4319,7 +4320,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
"init_cb=%p gid_list=%p, srb_mempool=%p s_dma_pool=%p.\n",
ha->init_cb, ha->gid_list, ha->srb_mempool, ha->s_dma_pool);
- if (IS_P3P_TYPE(ha) || ql2xenabledif || (IS_QLA28XX(ha) && ql2xsecenable)) {
+ if (IS_P3P_TYPE(ha) || ql2xenabledif ||
+ ((IS_QLA28XX(ha) || IS_QLA29XX(ha)) && ql2xsecenable)) {
ha->dl_dma_pool = dma_pool_create(name, &ha->pdev->dev,
DSD_LIST_DMA_POOL_SIZE, 8, 0);
if (!ha->dl_dma_pool) {
@@ -4681,12 +4683,12 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
}
fail_dif_bundl_dma_pool:
- if (IS_QLA82XX(ha) || ql2xenabledif) {
+ if (IS_QLA82XX(ha) || IS_QLA29XX(ha) || ql2xenabledif) {
dma_pool_destroy(ha->fcp_cmnd_dma_pool);
ha->fcp_cmnd_dma_pool = NULL;
}
fail_dl_dma_pool:
- if (IS_QLA82XX(ha) || ql2xenabledif) {
+ if (IS_QLA82XX(ha) || IS_QLA29XX(ha) || ql2xenabledif) {
dma_pool_destroy(ha->dl_dma_pool);
ha->dl_dma_pool = NULL;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 25/56] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (23 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 24/56] scsi: qla2xxx: Add support for QLA29XX in memory allocation Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 13:45 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 26/56] scsi: qla2xxx: Update handling of status entries for 29xx series Nilesh Javali
` (30 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
29xx adapters use 128-byte response queue entries
(sts_cont_entry_ext_t) instead of 64-byte (sts_cont_entry_t). Update
all status continuation IOCB processing paths to branch on
IS_QLA29XX() and use the correct entry type and data payload size.
The affected functions are __qla_copy_purex_to_buffer(),
qla27xx_copy_multiple_pkt(), qla2x00_status_cont_entry(), and their
call sites in qla2x00_process_response_entry() and
qla24xx_process_response_queue().
Change qla2x00_status_cont_entry() to accept void * so callers no
longer need an explicit cast and the function can internally select the
right structure based on the adapter type.
Add BUILD_BUG_ON for sts_cont_entry_ext_t size (128 bytes).
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_inline.h | 16 ++++
drivers/scsi/qla2xxx/qla_isr.c | 117 ++++++++++++++++++------------
drivers/scsi/qla2xxx/qla_os.c | 1 +
3 files changed, 86 insertions(+), 48 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index 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..f668012de3d4 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -19,7 +19,7 @@
static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t);
static void qla2x00_status_entry(scsi_qla_host_t *, struct rsp_que *, void *);
-static void qla2x00_status_cont_entry(struct rsp_que *, sts_cont_entry_t *);
+static void qla2x00_status_cont_entry(struct rsp_que *, void *, u32);
static int qla2x00_error_entry(scsi_qla_host_t *, struct rsp_que *,
sts_entry_t *);
static void qla27xx_process_purex_fpin(struct scsi_qla_host *vha,
@@ -231,8 +231,13 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
void **pkt, struct rsp_que **rsp, u8 *buf, u32 buf_len)
{
struct purex_entry_24xx *purex = *pkt;
+ struct qla_hw_data *ha = vha->hw;
struct rsp_que *rsp_q = *rsp;
- sts_cont_entry_t *new_pkt;
+ size_t payload_size = IS_QLA29XX(ha) ?
+ sizeof_field(struct purex_entry_24xx_ext, els_frame_payload) :
+ sizeof_field(struct purex_entry_24xx, els_frame_payload);
+ u8 *data;
+ u32 data_sz;
uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0;
uint16_t buffer_copy_offset = 0;
uint16_t entry_count_remaining;
@@ -271,10 +276,12 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
do {
while ((total_bytes > 0) && (entry_count_remaining > 0)) {
- new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr;
- *pkt = new_pkt;
+ *pkt = rsp_q->ring_ptr;
+ data = ((sts_cont_entry_t *)*pkt)->data;
+ data_sz = qla_sts_cont_data_size(ha);
- if (new_pkt->entry_type != STATUS_CONT_TYPE) {
+ if (((sts_cont_entry_t *)*pkt)->entry_type !=
+ STATUS_CONT_TYPE) {
ql_log(ql_log_warn, vha, 0x507a,
"Unexpected IOCB type, partial data 0x%x\n",
buffer_copy_offset);
@@ -282,10 +289,10 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
}
qla_rsp_ring_advance(rsp_q);
- no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
- sizeof(new_pkt->data) : pending_bytes;
+ no_bytes = (pending_bytes > data_sz) ?
+ data_sz : pending_bytes;
if ((buffer_copy_offset + no_bytes) <= total_bytes) {
- memcpy((buf + buffer_copy_offset), new_pkt->data,
+ memcpy((buf + buffer_copy_offset), data,
no_bytes);
buffer_copy_offset += no_bytes;
pending_bytes -= no_bytes;
@@ -294,11 +301,11 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
ql_log(ql_log_warn, vha, 0x5044,
"Attempt to copy more that we got, optimizing..%x\n",
buffer_copy_offset);
- memcpy((buf + buffer_copy_offset), new_pkt->data,
+ memcpy((buf + buffer_copy_offset), data,
total_bytes - buffer_copy_offset);
}
- ((response_t *)new_pkt)->signature = RESPONSE_PROCESSED;
+ ((response_t *)*pkt)->signature = RESPONSE_PROCESSED;
/* flush signature */
wmb();
}
@@ -844,13 +851,15 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
{
struct purex_entry_24xx *purex = NULL;
struct pt_ls4_rx_unsol *purls = NULL;
+ struct qla_hw_data *ha = vha->hw;
struct rsp_que *rsp_q = *rsp;
- sts_cont_entry_t *new_pkt;
uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0;
uint16_t buffer_copy_offset = 0, payload_size = 0;
uint16_t entry_count, entry_count_remaining;
struct purex_item *item;
void *iocb_pkt = NULL;
+ u8 *data;
+ u32 data_sz;
if (is_purls) {
purls = *pkt;
@@ -906,28 +915,24 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
continue;
}
- new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr;
- *pkt = new_pkt;
+ *pkt = rsp_q->ring_ptr;
+ data = ((sts_cont_entry_t *)*pkt)->data;
+ data_sz = qla_sts_cont_data_size(ha);
- if (new_pkt->entry_type != STATUS_CONT_TYPE) {
+ if (((sts_cont_entry_t *)*pkt)->entry_type !=
+ STATUS_CONT_TYPE) {
ql_log(ql_log_warn, vha, 0x507a,
"Unexpected IOCB type, partial data 0x%x\n",
buffer_copy_offset);
break;
}
- rsp_q->ring_index++;
- if (rsp_q->ring_index == rsp_q->length) {
- rsp_q->ring_index = 0;
- rsp_q->ring_ptr = rsp_q->ring;
- } else {
- rsp_q->ring_ptr++;
- }
- no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
- sizeof(new_pkt->data) : pending_bytes;
+ qla_rsp_ring_advance(rsp_q);
+ no_bytes = (pending_bytes > data_sz) ?
+ data_sz : pending_bytes;
if ((buffer_copy_offset + no_bytes) <= total_bytes) {
- memcpy(((uint8_t *)iocb_pkt + buffer_copy_offset),
- new_pkt->data, no_bytes);
+ memcpy(((uint8_t *)iocb_pkt +
+ buffer_copy_offset), data, no_bytes);
buffer_copy_offset += no_bytes;
pending_bytes -= no_bytes;
--entry_count_remaining;
@@ -935,12 +940,12 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
ql_log(ql_log_warn, vha, 0x5044,
"Attempt to copy more that we got, optimizing..%x\n",
buffer_copy_offset);
- memcpy(((uint8_t *)iocb_pkt + buffer_copy_offset),
- new_pkt->data,
- total_bytes - buffer_copy_offset);
+ memcpy(((uint8_t *)iocb_pkt +
+ buffer_copy_offset), data,
+ total_bytes - buffer_copy_offset);
}
- ((response_t *)new_pkt)->signature = RESPONSE_PROCESSED;
+ ((response_t *)*pkt)->signature = RESPONSE_PROCESSED;
wmb();
}
@@ -1144,8 +1149,13 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
struct rsp_que **rsp)
{
struct purex_entry_24xx *purex = *pkt;
+ struct qla_hw_data *ha = vha->hw;
struct rsp_que *rsp_q = *rsp;
- sts_cont_entry_t *new_pkt;
+ size_t payload_size = IS_QLA29XX(ha) ?
+ sizeof_field(struct purex_entry_24xx_ext, els_frame_payload) :
+ sizeof_field(struct purex_entry_24xx, els_frame_payload);
+ u8 *data;
+ u32 data_sz;
uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0;
uint16_t buffer_copy_offset = 0;
uint16_t entry_count, entry_count_remaining;
@@ -1160,8 +1170,8 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
pending_bytes = total_bytes;
entry_count = entry_count_remaining = purex->entry_count;
- no_bytes = (pending_bytes > sizeof(purex->els_frame_payload)) ?
- sizeof(purex->els_frame_payload) : pending_bytes;
+ no_bytes = (pending_bytes > payload_size) ?
+ payload_size : pending_bytes;
ql_log(ql_log_info, vha, 0x509a,
"FPIN ELS, frame_size 0x%x, entry count %d\n",
total_bytes, entry_count);
@@ -1172,7 +1182,13 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
fpin_pkt = &item->iocb;
- memcpy(fpin_pkt, &purex->els_frame_payload[0], no_bytes);
+ if (IS_QLA29XX(ha)) {
+ struct purex_entry_24xx_ext *purex_ext = *pkt;
+
+ memcpy(fpin_pkt, &purex_ext->els_frame_payload[0], no_bytes);
+ } else {
+ memcpy(fpin_pkt, &purex->els_frame_payload[0], no_bytes);
+ }
buffer_copy_offset += no_bytes;
pending_bytes -= no_bytes;
--entry_count_remaining;
@@ -1190,10 +1206,12 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
continue;
}
- new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr;
- *pkt = new_pkt;
+ *pkt = rsp_q->ring_ptr;
+ data = ((sts_cont_entry_t *)*pkt)->data;
+ data_sz = qla_sts_cont_data_size(ha);
- if (new_pkt->entry_type != STATUS_CONT_TYPE) {
+ if (((sts_cont_entry_t *)*pkt)->entry_type !=
+ STATUS_CONT_TYPE) {
ql_log(ql_log_warn, vha, 0x507a,
"Unexpected IOCB type, partial data 0x%x\n",
buffer_copy_offset);
@@ -1201,11 +1219,11 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
}
qla_rsp_ring_advance(rsp_q);
- no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
- sizeof(new_pkt->data) : pending_bytes;
+ no_bytes = (pending_bytes > data_sz) ?
+ data_sz : pending_bytes;
if ((buffer_copy_offset + no_bytes) <= total_bytes) {
memcpy(((uint8_t *)fpin_pkt +
- buffer_copy_offset), new_pkt->data,
+ buffer_copy_offset), data,
no_bytes);
buffer_copy_offset += no_bytes;
pending_bytes -= no_bytes;
@@ -1215,11 +1233,11 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
"Attempt to copy more that we got, optimizing..%x\n",
buffer_copy_offset);
memcpy(((uint8_t *)fpin_pkt +
- buffer_copy_offset), new_pkt->data,
+ buffer_copy_offset), data,
total_bytes - buffer_copy_offset);
}
- ((response_t *)new_pkt)->signature = RESPONSE_PROCESSED;
+ ((response_t *)*pkt)->signature = RESPONSE_PROCESSED;
wmb();
}
@@ -2918,7 +2936,8 @@ static void qla2x00_process_response_entry(struct scsi_qla_host *vha,
sts22_entry->handle[cnt]);
break;
case STATUS_CONT_TYPE:
- qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt);
+ qla2x00_status_cont_entry(rsp, pkt,
+ qla_sts_cont_data_size(rsp->hw));
break;
case MBX_IOCB_TYPE:
qla2x00_mbx_iocb_entry(vha, rsp->req, (struct mbx_entry *)pkt);
@@ -3682,7 +3701,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
* Extended sense data.
*/
static void
-qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
+qla2x00_status_cont_entry(struct rsp_que *rsp, void *pkt, u32 data_sz)
{
uint8_t sense_sz = 0;
struct qla_hw_data *ha = rsp->hw;
@@ -3691,6 +3710,7 @@ qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
struct scsi_cmnd *cp;
uint32_t sense_len;
uint8_t *sense_ptr;
+ u8 *data = ((sts_cont_entry_t *)pkt)->data;
if (!sp || !GET_CMD_SENSE_LEN(sp))
return;
@@ -3707,15 +3727,15 @@ qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
return;
}
- if (sense_len > sizeof(pkt->data))
- sense_sz = sizeof(pkt->data);
+ if (sense_len > data_sz)
+ sense_sz = data_sz;
else
sense_sz = sense_len;
/* Move sense data. */
if (IS_FWI2_CAPABLE(ha))
- host_to_fcp_swap(pkt->data, sizeof(pkt->data));
- memcpy(sense_ptr, pkt->data, sense_sz);
+ host_to_fcp_swap(data, data_sz);
+ memcpy(sense_ptr, data, sense_sz);
ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x302c,
sense_ptr, sense_sz);
@@ -3997,7 +4017,8 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
qla2x00_status_entry(vha, rsp, pkt);
break;
case STATUS_CONT_TYPE:
- qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt);
+ qla2x00_status_cont_entry(rsp, pkt,
+ qla_sts_cont_data_size(rsp->hw));
break;
case VP_RPT_ID_IOCB_TYPE:
qla24xx_report_id_acquisition(vha,
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 3be59179a024..fdb53811e0c7 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8411,6 +8411,7 @@ qla2x00_module_init(void)
BUILD_BUG_ON(sizeof(sts21_entry_t) != 64);
BUILD_BUG_ON(sizeof(sts22_entry_t) != 64);
BUILD_BUG_ON(sizeof(sts_cont_entry_t) != 64);
+ BUILD_BUG_ON(sizeof(struct sts_cont_entry_ext) != 128);
BUILD_BUG_ON(sizeof(sts_entry_t) != 64);
BUILD_BUG_ON(sizeof(sw_info_t) != 32);
BUILD_BUG_ON(sizeof(target_id_t) != 2);
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 26/56] scsi: qla2xxx: Update handling of status entries for 29xx series
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (24 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 25/56] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 14:21 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 27/56] scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling " Nilesh Javali
` (29 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Modify the handling of status entries in the qla2xxx driver to
accommodate the extended structure for the 29xx series. Changes include
updating function signatures to accept a generic pointer for status
packets, and adjusting the logic to differentiate between the standard
and extended status entries. This ensures proper processing of
completion statuses and error handling for the new hardware
capabilities.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_edif.c | 15 ++--
drivers/scsi/qla2xxx/qla_gbl.h | 2 +-
drivers/scsi/qla2xxx/qla_inline.h | 45 ++++++++++++
drivers/scsi/qla2xxx/qla_isr.c | 111 +++++++++++++++++++-----------
drivers/scsi/qla2xxx/qla_mbx.c | 28 ++++----
drivers/scsi/qla2xxx/qla_os.c | 1 +
6 files changed, 143 insertions(+), 59 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c
index 4416197a35b0..b05f8e0b705e 100644
--- a/drivers/scsi/qla2xxx/qla_edif.c
+++ b/drivers/scsi/qla2xxx/qla_edif.c
@@ -3501,12 +3501,15 @@ static void __chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
}
void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
- srb_t *sp, struct sts_entry_24xx *sts24)
+ srb_t *sp, void *pkt)
{
+ struct sts_entry_24xx *sts24 = pkt;
+ struct sts_entry_24xx_ext *stsext = pkt;
+ struct qla_hw_data *ha = vha->hw;
fc_port_t *fcport = sp->fcport;
- /* sa_index used by this iocb */
struct scsi_cmnd *cmd = GET_CMD_SP(sp);
uint32_t handle;
+ uint16_t sa_index;
handle = (uint32_t)LSW(sts24->handle);
@@ -3514,8 +3517,12 @@ void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
if (cmd->sc_data_direction != DMA_FROM_DEVICE)
return;
- return __chk_edif_rx_sa_delete_pending(vha, fcport, handle,
- le16_to_cpu(sts24->edif_sa_index));
+ if (IS_QLA29XX(ha))
+ sa_index = le16_to_cpu(stsext->read_sa_index);
+ else
+ sa_index = le16_to_cpu(sts24->edif_sa_index);
+
+ return __chk_edif_rx_sa_delete_pending(vha, fcport, handle, sa_index);
}
void qlt_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, fc_port_t *fcport,
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 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 f668012de3d4..c38771e4202c 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -2307,8 +2307,9 @@ qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
static void
qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
- struct sts_entry_24xx *pkt, int iocb_type)
+ void *pkt, int iocb_type)
{
+ struct sts_entry_24xx *sts24 = pkt;
struct els_sts_entry_24xx *ese = (struct els_sts_entry_24xx *)pkt;
const char func[] = "ELS_CT_IOCB";
const char *type;
@@ -2331,9 +2332,9 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
type = NULL;
- comp_status = fw_status[0] = le16_to_cpu(pkt->comp_status);
- fw_status[1] = le32_to_cpu(((struct els_sts_entry_24xx *)pkt)->error_subcode_1);
- fw_status[2] = le32_to_cpu(((struct els_sts_entry_24xx *)pkt)->error_subcode_2);
+ comp_status = fw_status[0] = le16_to_cpu(sts24->comp_status);
+ fw_status[1] = le32_to_cpu(ese->error_subcode_1);
+ fw_status[2] = le32_to_cpu(ese->error_subcode_2);
switch (sp->type) {
case SRB_ELS_CMD_RPT:
@@ -2669,7 +2670,9 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk)
srb_t *sp;
struct srb_iocb *iocb;
struct sts_entry_24xx *sts = (struct sts_entry_24xx *)tsk;
+ struct qla_hw_data *ha = vha->hw;
u16 comp_status;
+ struct qla_sts_fwi2 sf;
sp = qla2x00_get_sp_from_handle(vha, func, req, tsk);
if (!sp)
@@ -2691,18 +2694,19 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk)
"Async-%s error - hdl=%x completion status(%x).\n",
type, sp->handle, comp_status);
iocb->u.tmf.data = QLA_FUNCTION_FAILED;
- } else if ((le16_to_cpu(sts->scsi_status) &
- SS_RESPONSE_INFO_LEN_VALID)) {
- host_to_fcp_swap(sts->data, sizeof(sts->data));
- if (le32_to_cpu(sts->rsp_data_len) < 4) {
- ql_log(ql_log_warn, fcport->vha, 0x503b,
- "Async-%s error - hdl=%x not enough response(%d).\n",
- type, sp->handle, sts->rsp_data_len);
- } else if (sts->data[3]) {
- ql_log(ql_log_warn, fcport->vha, 0x503c,
- "Async-%s error - hdl=%x response(%x).\n",
- type, sp->handle, sts->data[3]);
- iocb->u.tmf.data = QLA_FUNCTION_FAILED;
+ } else {
+ qla_sts_fwi2_extract(ha, tsk, &sf);
+ if (sf.scsi_status & SS_RESPONSE_INFO_LEN_VALID) {
+ if (sf.rsp_data_len < 4) {
+ ql_log(ql_log_warn, fcport->vha, 0x503b,
+ "Async-%s error - hdl=%x not enough response(%d).\n",
+ type, sp->handle, sf.rsp_data_len);
+ } else if (sf.data[3]) {
+ ql_log(ql_log_warn, fcport->vha, 0x503c,
+ "Async-%s error - hdl=%x response(%x).\n",
+ type, sp->handle, sf.data[3]);
+ iocb->u.tmf.data = QLA_FUNCTION_FAILED;
+ }
}
}
@@ -2731,7 +2735,8 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk)
if (iocb->u.tmf.data != QLA_SUCCESS)
ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, sp->vha, 0x5055,
- sts, sizeof(*sts));
+ tsk, IS_QLA29XX(ha) ?
+ sizeof(struct sts_entry_24xx_ext) : sizeof(*sts));
sp->done(sp, 0);
}
@@ -2742,6 +2747,8 @@ static void qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
fc_port_t *fcport;
struct srb_iocb *iocb;
struct sts_entry_24xx *sts = (struct sts_entry_24xx *)tsk;
+ struct sts_entry_24xx_ext *stsext = (struct sts_entry_24xx_ext *)tsk;
+ struct qla_hw_data *ha = vha->hw;
uint16_t state_flags;
struct nvmefc_fcp_req *fd;
uint16_t ret = QLA_SUCCESS;
@@ -2793,7 +2800,10 @@ static void qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
uint32_t *inbuf, *outbuf;
uint16_t iter;
- inbuf = (uint32_t *)&sts->nvme_ersp_data;
+ if (IS_QLA29XX(ha))
+ inbuf = (uint32_t *)stsext->u2.nvme_ersp_data;
+ else
+ inbuf = (uint32_t *)&sts->nvme_ersp_data;
outbuf = (uint32_t *)fd->rspaddr;
iocb->u.nvme.rsp_pyld_len = sts->nvme_rsp_pyld_len;
if (unlikely(le16_to_cpu(iocb->u.nvme.rsp_pyld_len) >
@@ -3038,16 +3048,26 @@ qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t par_sense_len,
* to indicate to the kernel that the HBA detected error.
*/
static inline int
-qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24)
+qla2x00_handle_dif_error(srb_t *sp, void *pkt)
{
struct scsi_qla_host *vha = sp->vha;
+ struct qla_hw_data *ha = vha->hw;
struct scsi_cmnd *cmd = GET_CMD_SP(sp);
- uint8_t *ap = &sts24->data[12];
- uint8_t *ep = &sts24->data[20];
+ struct sts_entry_24xx *sts24 = pkt;
+ struct sts_entry_24xx_ext *stsext = pkt;
+ uint8_t *ap, *ep;
uint32_t e_ref_tag, a_ref_tag;
uint16_t e_app_tag, a_app_tag;
uint16_t e_guard, a_guard;
+ if (IS_QLA29XX(ha)) {
+ ap = stsext->act_dif;
+ ep = stsext->exp_dif;
+ } else {
+ ap = &sts24->data[12];
+ ep = &sts24->data[20];
+ }
+
/*
* swab32 of the "data" field in the beginning of qla2x00_status_entry()
* would make guard field appear at offset 2
@@ -3060,7 +3080,7 @@ qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24)
e_ref_tag = get_unaligned_le32(ep + 4);
ql_dbg(ql_dbg_io, vha, 0x3023,
- "iocb(s) %p Returned STATUS.\n", sts24);
+ "iocb(s) %px Returned STATUS.\n", pkt);
ql_dbg(ql_dbg_io, vha, 0x3024,
"DIF ERROR in cmd 0x%x lba 0x%llx act ref"
@@ -3192,7 +3212,9 @@ qla25xx_process_bidir_status_iocb(scsi_qla_host_t *vha, void *pkt,
if (IS_FWI2_CAPABLE(ha)) {
comp_status = le16_to_cpu(sts24->comp_status);
- scsi_status = le16_to_cpu(sts24->scsi_status) & SS_MASK;
+ scsi_status = IS_QLA29XX(ha) ?
+ le16_to_cpu(((struct sts_entry_24xx_ext *)pkt)->u2.scsi_status) & SS_MASK :
+ le16_to_cpu(sts24->scsi_status) & SS_MASK;
} else {
comp_status = le16_to_cpu(sts->comp_status);
scsi_status = le16_to_cpu(sts->scsi_status) & SS_MASK;
@@ -3328,10 +3350,13 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
int res = 0;
uint16_t state_flags = 0;
uint16_t sts_qual = 0;
+ struct qla_sts_fwi2 sf;
if (IS_FWI2_CAPABLE(ha)) {
comp_status = le16_to_cpu(sts24->comp_status);
- scsi_status = le16_to_cpu(sts24->scsi_status) & SS_MASK;
+ scsi_status = IS_QLA29XX(ha) ?
+ le16_to_cpu(((struct sts_entry_24xx_ext *)pkt)->u2.scsi_status) & SS_MASK :
+ le16_to_cpu(sts24->scsi_status) & SS_MASK;
state_flags = le16_to_cpu(sts24->state_flags);
} else {
comp_status = le16_to_cpu(sts->comp_status);
@@ -3402,7 +3427,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
}
/* Fast path completion. */
- qla_chk_edif_rx_sa_delete_pending(vha, sp, sts24);
+ qla_chk_edif_rx_sa_delete_pending(vha, sp, pkt);
sp->qpair->cmd_completion_cnt++;
if (comp_status == CS_COMPLETE && scsi_status == 0) {
@@ -3429,20 +3454,20 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
sense_len = par_sense_len = rsp_info_len = resid_len =
fw_resid_len = 0;
if (IS_FWI2_CAPABLE(ha)) {
+ qla_sts_fwi2_extract(ha, pkt, &sf);
if (scsi_status & SS_SENSE_LEN_VALID)
- sense_len = le32_to_cpu(sts24->sense_len);
+ sense_len = sf.sense_len;
if (scsi_status & SS_RESPONSE_INFO_LEN_VALID)
- rsp_info_len = le32_to_cpu(sts24->rsp_data_len);
+ rsp_info_len = sf.rsp_data_len;
if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER))
- resid_len = le32_to_cpu(sts24->rsp_residual_count);
+ resid_len = sf.rsp_residual_count;
if (comp_status == CS_DATA_UNDERRUN)
fw_resid_len = le32_to_cpu(sts24->residual_len);
- rsp_info = sts24->data;
- sense_data = sts24->data;
- host_to_fcp_swap(sts24->data, sizeof(sts24->data));
+ rsp_info = sf.data;
+ sense_data = sf.data;
+ par_sense_len = sf.data_sz;
+ sts_qual = sf.sts_qual;
ox_id = le16_to_cpu(sts24->ox_id);
- par_sense_len = sizeof(sts24->data);
- sts_qual = le16_to_cpu(sts24->status_qualifier);
} else {
if (scsi_status & SS_SENSE_LEN_VALID)
sense_len = le16_to_cpu(sts->req_sense_length);
@@ -3642,7 +3667,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
break;
case CS_DIF_ERROR:
- logit = qla2x00_handle_dif_error(sp, sts24);
+ logit = qla2x00_handle_dif_error(sp, pkt);
res = cp->result;
break;
@@ -3667,7 +3692,8 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
ox_id, cp->cmnd, scsi_bufflen(cp), rsp_info_len,
resid_len, fw_resid_len, sp, cp);
ql_dump_buffer(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe0ee,
- pkt, sizeof(*sts24));
+ pkt, IS_QLA29XX(ha) ?
+ sizeof(struct sts_entry_24xx_ext) : sizeof(*sts24));
res = DID_ERROR << 16;
vha->hw_err_cnt++;
break;
@@ -3967,7 +3993,7 @@ static void qla_marker_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
void qla24xx_process_response_queue(struct scsi_qla_host *vha,
struct rsp_que *rsp)
{
- struct sts_entry_24xx *pkt;
+ void *pkt;
struct qla_hw_data *ha = vha->hw;
struct purex_entry_24xx *purex_entry;
struct purex_item *pure_item;
@@ -3997,13 +4023,13 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
while (rsp->ring_index != rsp_in &&
rsp->ring_ptr->signature != RESPONSE_PROCESSED) {
- pkt = (struct sts_entry_24xx *)rsp->ring_ptr;
+ pkt = (void *)rsp->ring_ptr;
cur_ring_index = rsp->ring_index;
qla_rsp_ring_advance(rsp);
- if (pkt->entry_status != 0) {
- if (qla2x00_error_entry(vha, rsp, (sts_entry_t *) pkt))
+ if (((response_t *)pkt)->entry_status != 0) {
+ if (qla2x00_error_entry(vha, rsp, (sts_entry_t *)pkt))
goto process_err;
((response_t *)pkt)->signature = RESPONSE_PROCESSED;
@@ -4012,7 +4038,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
}
process_err:
- switch (pkt->entry_type) {
+ switch (((response_t *)pkt)->entry_type) {
case STATUS_TYPE:
qla2x00_status_entry(vha, rsp, pkt);
break;
@@ -4063,7 +4089,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
rsp->req);
break;
case NOTIFY_ACK_TYPE:
- if (pkt->handle == QLA_TGT_SKIP_HANDLE)
+ if (((response_t *)pkt)->handle == QLA_TGT_SKIP_HANDLE)
qlt_response_pkt_all_vps(vha, rsp,
(response_t *)pkt);
else
@@ -4156,7 +4182,8 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
/* Type Not Supported. */
ql_dbg(ql_dbg_async, vha, 0x5042,
"Received unknown response pkt type 0x%x entry status=%x.\n",
- pkt->entry_type, pkt->entry_status);
+ ((response_t *)pkt)->entry_type,
+ ((response_t *)pkt)->entry_status);
break;
}
((response_t *)pkt)->signature = RESPONSE_PROCESSED;
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index c073dc35ef8e..f9b326c02055 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -3407,6 +3407,7 @@ struct tsk_mgmt_cmd {
union {
struct tsk_mgmt_entry tsk;
struct sts_entry_24xx sts;
+ struct sts_entry_24xx_ext sts_ext;
} p;
};
@@ -3417,6 +3418,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
int rval, rval2;
struct tsk_mgmt_cmd *tsk;
struct sts_entry_24xx *sts;
+ struct qla_sts_fwi2 sf;
dma_addr_t tsk_dma;
scsi_qla_host_t *vha;
struct qla_hw_data *ha;
@@ -3474,18 +3476,20 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
"Failed to complete IOCB -- completion status (%x).\n",
le16_to_cpu(sts->comp_status));
rval = QLA_FUNCTION_FAILED;
- } else if (le16_to_cpu(sts->scsi_status) &
- SS_RESPONSE_INFO_LEN_VALID) {
- if (le32_to_cpu(sts->rsp_data_len) < 4) {
- ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1097,
- "Ignoring inconsistent data length -- not enough "
- "response info (%d).\n",
- le32_to_cpu(sts->rsp_data_len));
- } else if (sts->data[3]) {
- ql_dbg(ql_dbg_mbx, vha, 0x1098,
- "Failed to complete IOCB -- response (%x).\n",
- sts->data[3]);
- rval = QLA_FUNCTION_FAILED;
+ } else {
+ qla_sts_fwi2_extract(ha, sts, &sf);
+ if (sf.scsi_status & SS_RESPONSE_INFO_LEN_VALID) {
+ if (sf.rsp_data_len < 4) {
+ ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha,
+ 0x1097,
+ "Ignoring inconsistent data length -- not enough response info (%d).\n",
+ sf.rsp_data_len);
+ } else if (sf.data[3]) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1098,
+ "Failed to complete IOCB -- response (%x).\n",
+ sf.data[3]);
+ rval = QLA_FUNCTION_FAILED;
+ }
}
}
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index fdb53811e0c7..4f7e5f9372ef 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8400,6 +8400,7 @@ qla2x00_module_init(void)
BUILD_BUG_ON(sizeof(struct rdp_rsp_payload) != 336);
BUILD_BUG_ON(sizeof(struct sns_cmd_pkt) != 2064);
BUILD_BUG_ON(sizeof(struct sts_entry_24xx) != 64);
+ BUILD_BUG_ON(sizeof(struct sts_entry_24xx_ext) != 128);
BUILD_BUG_ON(sizeof(struct tsk_mgmt_entry) != 64);
BUILD_BUG_ON(sizeof(struct tsk_mgmt_entry_fx00) != 64);
BUILD_BUG_ON(sizeof(struct verify_chip_entry_84xx) != 64);
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 27/56] scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling for 29xx series
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (25 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 26/56] scsi: qla2xxx: Update handling of status entries for 29xx series Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 28/56] scsi: qla2xxx: Enhance purex_entry " Nilesh Javali
` (28 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Refine the handling of I/O control blocks (IOCBs) for the 29xx series
by introducing support for the extended structure ct_entry_24xx_ext.
Update function signatures to accept a generic pointer for IOCB packets,
differentiating between standard and extended structures, and ensuring
proper initialization and processing of command and response data.
Additionally, the size check for the extended structure is added to
maintain integrity.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_gs.c | 156 ++++++++++++++++++++++++--------
drivers/scsi/qla2xxx/qla_iocb.c | 78 ++++++++++------
drivers/scsi/qla2xxx/qla_os.c | 1 +
3 files changed, 168 insertions(+), 67 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index 514f04aa1423..7a4d2fdc095f 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -68,30 +68,62 @@ void *
qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, struct ct_arg *arg)
{
struct qla_hw_data *ha = vha->hw;
- struct ct_entry_24xx *ct_pkt;
- ct_pkt = (struct ct_entry_24xx *)arg->iocb;
- memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
+ if (IS_QLA29XX(ha)) {
+ struct ct_entry_24xx_ext *ct_pkt;
- ct_pkt->entry_type = CT_IOCB_TYPE;
- ct_pkt->entry_count = 1;
- ct_pkt->nport_handle = cpu_to_le16(arg->nport_handle);
- ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
- ct_pkt->cmd_dsd_count = cpu_to_le16(1);
- ct_pkt->rsp_dsd_count = cpu_to_le16(1);
- ct_pkt->rsp_byte_count = cpu_to_le32(arg->rsp_size);
- ct_pkt->cmd_byte_count = cpu_to_le32(arg->req_size);
+ ct_pkt = (struct ct_entry_24xx_ext *)arg->iocb;
+ memset(ct_pkt, 0, sizeof(struct ct_entry_24xx_ext));
- put_unaligned_le64(arg->req_dma, &ct_pkt->dsd[0].address);
- ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
+ ct_pkt->entry_type = CT_IOCB_TYPE;
+ ct_pkt->entry_count = 1;
+ ct_pkt->nport_handle = cpu_to_le16(arg->nport_handle);
+ ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
+ ct_pkt->cmd_dsd_count = cpu_to_le16(1);
+ ct_pkt->rsp_dsd_count = cpu_to_le16(1);
+ ct_pkt->rsp_byte_count = cpu_to_le32(arg->rsp_size);
+ ct_pkt->cmd_byte_count = cpu_to_le32(arg->req_size);
- put_unaligned_le64(arg->rsp_dma, &ct_pkt->dsd[1].address);
- ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
- ct_pkt->vp_index = vha->vp_idx;
+ put_unaligned_le64(arg->req_dma,
+ &ct_pkt->dsd[0].address);
+ ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
- vha->qla_stats.control_requests++;
+ put_unaligned_le64(arg->rsp_dma,
+ &ct_pkt->dsd[1].address);
+ ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
+ ct_pkt->vp_index = cpu_to_le16(vha->vp_idx);
+
+ vha->qla_stats.control_requests++;
+
+ return ct_pkt;
+ } else {
+ struct ct_entry_24xx *ct_pkt;
+
+ ct_pkt = (struct ct_entry_24xx *)arg->iocb;
+ memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
+
+ ct_pkt->entry_type = CT_IOCB_TYPE;
+ ct_pkt->entry_count = 1;
+ ct_pkt->nport_handle = cpu_to_le16(arg->nport_handle);
+ ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
+ ct_pkt->cmd_dsd_count = cpu_to_le16(1);
+ ct_pkt->rsp_dsd_count = cpu_to_le16(1);
+ ct_pkt->rsp_byte_count = cpu_to_le32(arg->rsp_size);
+ ct_pkt->cmd_byte_count = cpu_to_le32(arg->req_size);
+
+ put_unaligned_le64(arg->req_dma,
+ &ct_pkt->dsd[0].address);
+ ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
- return (ct_pkt);
+ put_unaligned_le64(arg->rsp_dma,
+ &ct_pkt->dsd[1].address);
+ ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
+ ct_pkt->vp_index = vha->vp_idx;
+
+ vha->qla_stats.control_requests++;
+
+ return ct_pkt;
+ }
}
/**
@@ -132,7 +164,10 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt,
routine, ms_pkt->entry_status, vha->d_id.b.domain,
vha->d_id.b.area, vha->d_id.b.al_pa);
} else {
- if (IS_FWI2_CAPABLE(ha))
+ if (IS_QLA29XX(ha))
+ comp_status = le16_to_cpu(
+ ((struct ct_entry_24xx_ext *)ms_pkt)->comp_status);
+ else if (IS_FWI2_CAPABLE(ha))
comp_status = le16_to_cpu(
((struct ct_entry_24xx *)ms_pkt)->comp_status);
else
@@ -1437,42 +1472,85 @@ void *
qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size,
uint32_t rsp_size)
{
- struct ct_entry_24xx *ct_pkt;
struct qla_hw_data *ha = vha->hw;
- ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
- memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
+ if (IS_QLA29XX(ha)) {
+ struct ct_entry_24xx_ext *ct_pkt;
+
+ ct_pkt = (struct ct_entry_24xx_ext *)ha->ms_iocb;
+ memset(ct_pkt, 0, sizeof(struct ct_entry_24xx_ext));
+
+ ct_pkt->entry_type = CT_IOCB_TYPE;
+ ct_pkt->entry_count = 1;
+ ct_pkt->nport_handle =
+ cpu_to_le16(vha->mgmt_svr_loop_id);
+ ct_pkt->timeout =
+ cpu_to_le16(ha->r_a_tov / 10 * 2);
+ ct_pkt->cmd_dsd_count = cpu_to_le16(1);
+ ct_pkt->rsp_dsd_count = cpu_to_le16(1);
+ ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size);
+ ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
+
+ put_unaligned_le64(ha->ct_sns_dma,
+ &ct_pkt->dsd[0].address);
+ ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
+
+ put_unaligned_le64(ha->ct_sns_dma,
+ &ct_pkt->dsd[1].address);
+ ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
+ ct_pkt->vp_index = cpu_to_le16(vha->vp_idx);
- ct_pkt->entry_type = CT_IOCB_TYPE;
- ct_pkt->entry_count = 1;
- ct_pkt->nport_handle = cpu_to_le16(vha->mgmt_svr_loop_id);
- ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
- ct_pkt->cmd_dsd_count = cpu_to_le16(1);
- ct_pkt->rsp_dsd_count = cpu_to_le16(1);
- ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size);
- ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
+ return ct_pkt;
+ } else {
+ struct ct_entry_24xx *ct_pkt;
+
+ ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
+ memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
+
+ ct_pkt->entry_type = CT_IOCB_TYPE;
+ ct_pkt->entry_count = 1;
+ ct_pkt->nport_handle =
+ cpu_to_le16(vha->mgmt_svr_loop_id);
+ ct_pkt->timeout =
+ cpu_to_le16(ha->r_a_tov / 10 * 2);
+ ct_pkt->cmd_dsd_count = cpu_to_le16(1);
+ ct_pkt->rsp_dsd_count = cpu_to_le16(1);
+ ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size);
+ ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
- put_unaligned_le64(ha->ct_sns_dma, &ct_pkt->dsd[0].address);
- ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
+ put_unaligned_le64(ha->ct_sns_dma,
+ &ct_pkt->dsd[0].address);
+ ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
- put_unaligned_le64(ha->ct_sns_dma, &ct_pkt->dsd[1].address);
- ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
- ct_pkt->vp_index = vha->vp_idx;
+ put_unaligned_le64(ha->ct_sns_dma,
+ &ct_pkt->dsd[1].address);
+ ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
+ ct_pkt->vp_index = vha->vp_idx;
- return ct_pkt;
+ return ct_pkt;
+ }
}
static void
qla2x00_update_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size)
{
struct qla_hw_data *ha = vha->hw;
- ms_iocb_entry_t *ms_pkt = ha->ms_iocb;
- struct ct_entry_24xx *ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
- if (IS_FWI2_CAPABLE(ha)) {
+ if (IS_QLA29XX(ha)) {
+ struct ct_entry_24xx_ext *ct_pkt =
+ (struct ct_entry_24xx_ext *)ha->ms_iocb;
+
+ ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
+ ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
+ } else if (IS_FWI2_CAPABLE(ha)) {
+ struct ct_entry_24xx *ct_pkt =
+ (struct ct_entry_24xx *)ha->ms_iocb;
+
ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
} else {
+ ms_iocb_entry_t *ms_pkt = ha->ms_iocb;
+
ms_pkt->req_bytecount = cpu_to_le32(req_size);
ms_pkt->req_dsd.length = ms_pkt->req_bytecount;
}
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 71fb6f3584ea..69ed3e8e0259 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -3467,7 +3467,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;
@@ -3479,36 +3479,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;
@@ -3557,7 +3571,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;
}
/*
@@ -3930,11 +3948,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] 87+ messages in thread
* [PATCH v4 28/56] scsi: qla2xxx: Enhance purex_entry handling for 29xx series
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (26 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 27/56] scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling " Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 14:45 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 29/56] scsi: qla2xxx: Update handling of ELS IOCBs " Nilesh Javali
` (27 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Update function signatures and internal logic across qla_edif.c,
qla_isr.c, and qla_os.c to accept a generic pointer for packet data and
differentiate between standard purex_entry_24xx and the extended
purex_entry_24xx_ext structures based on IS_QLA29XX().
This ensures proper initialization and processing of command and response
data for both 64-byte and 128-byte PUREX IOCBs across all ELS paths
including auth_els, RDP, copy_std_pkt, copy_multiple_pkt, consume_iocb,
and copy_purex_to_buffer.
Where the two layouts overlap at byte-identical offsets (entry_count,
frame_size, nport_handle, rx_xchg_addr, ox_id, status_flags,
trunc_frame_size, s_id, d_id, els_frame_payload base, and
response_t::signature), use a single struct purex_entry_24xx * view to
avoid duplicating read paths. Branch only where field encoding differs:
vp_idx (u8 at offset 6 in 24xx vs __le16 at offsets 6-7 in 29xx) and
els_frame_payload[] array length (20 vs 84 bytes, handled via a
sizeof_field()-based payload_size local).
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_def.h | 3 +-
drivers/scsi/qla2xxx/qla_edif.c | 65 ++++++++++++++++-------
drivers/scsi/qla2xxx/qla_isr.c | 94 ++++++++++++++++++++++++++-------
drivers/scsi/qla2xxx/qla_os.c | 24 +++++++--
4 files changed, 142 insertions(+), 44 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 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 c38771e4202c..c4e2e62f924f 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -202,6 +202,11 @@ void __qla_consume_iocb(struct scsi_qla_host *vha,
struct rsp_que *rsp_q = *rsp;
response_t *new_pkt;
uint16_t entry_count_remaining;
+ /*
+ * entry_count is u8 at offset 1 in both purex_entry_24xx and
+ * purex_entry_24xx_ext, so the 24xx view is layout-compatible with
+ * either stride.
+ */
struct purex_entry_24xx *purex = *pkt;
entry_count_remaining = purex->entry_count;
@@ -230,6 +235,14 @@ void __qla_consume_iocb(struct scsi_qla_host *vha,
int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
void **pkt, struct rsp_que **rsp, u8 *buf, u32 buf_len)
{
+ /*
+ * purex_entry_24xx_ext overlays purex_entry_24xx for entry_count
+ * (offset 1), frame_size (offset 12) and els_frame_payload (offset
+ * 44, base address only -- the array size grows from 20 to 84
+ * bytes). Header fields are read through the 24xx view; the
+ * initial payload memcpy uses a purex_entry_24xx_ext pointer on
+ * 29xx so that FORTIFY_SOURCE sees the correct 84-byte source.
+ */
struct purex_entry_24xx *purex = *pkt;
struct qla_hw_data *ha = vha->hw;
struct rsp_que *rsp_q = *rsp;
@@ -244,8 +257,8 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
u16 tpad;
entry_count_remaining = purex->entry_count;
- total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF)
- - PURX_ELS_HEADER_SIZE;
+ total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF) -
+ PURX_ELS_HEADER_SIZE;
/*
* end of payload may not end in 4bytes boundary. Need to
@@ -262,14 +275,24 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
}
pending_bytes = total_bytes = tpad;
- no_bytes = (pending_bytes > sizeof(purex->els_frame_payload)) ?
- sizeof(purex->els_frame_payload) : pending_bytes;
+ no_bytes = (pending_bytes > payload_size) ?
+ payload_size : pending_bytes;
+ if (IS_QLA29XX(ha)) {
+ struct purex_entry_24xx_ext *purex_ext = *pkt;
- memcpy(buf, &purex->els_frame_payload[0], no_bytes);
+ memcpy(buf, &purex_ext->els_frame_payload[0], no_bytes);
+ } else {
+ memcpy(buf, &purex->els_frame_payload[0], no_bytes);
+ }
buffer_copy_offset += no_bytes;
pending_bytes -= no_bytes;
--entry_count_remaining;
+ /*
+ * response_t::signature and struct response_ext::signature are both u32
+ * at offset 60 (handle:4 + data[52]:60), so the 24xx view writes
+ * the right slot regardless of stride.
+ */
((response_t *)purex)->signature = RESPONSE_PROCESSED;
/* flush signature */
wmb();
@@ -849,6 +872,7 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
struct rsp_que **rsp, bool is_purls,
bool byte_order)
{
+ struct purex_entry_24xx_ext *purex_ext = NULL;
struct purex_entry_24xx *purex = NULL;
struct pt_ls4_rx_unsol *purls = NULL;
struct qla_hw_data *ha = vha->hw;
@@ -867,6 +891,13 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
PURX_ELS_HEADER_SIZE;
entry_count = entry_count_remaining = purls->entry_count;
payload_size = sizeof(purls->payload);
+ } else if (IS_QLA29XX(ha)) {
+ purex_ext = *pkt;
+ total_bytes = (le16_to_cpu(purex_ext->frame_size) & 0x0FFF) -
+ PURX_ELS_HEADER_SIZE;
+ entry_count = entry_count_remaining =
+ purex_ext->entry_count;
+ payload_size = sizeof(purex_ext->els_frame_payload);
} else {
purex = *pkt;
total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF) -
@@ -875,8 +906,8 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
payload_size = sizeof(purex->els_frame_payload);
}
- if (total_bytes > sizeof(item->iocb.iocb))
- total_bytes = sizeof(item->iocb.iocb);
+ if (total_bytes > QLA_MAX_IOCB_SIZE)
+ total_bytes = QLA_MAX_IOCB_SIZE;
pending_bytes = total_bytes;
no_bytes = (pending_bytes > payload_size) ? payload_size :
@@ -893,6 +924,8 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
if (is_purls)
memcpy(iocb_pkt, &purls->payload[0], no_bytes);
+ else if (IS_QLA29XX(ha))
+ memcpy(iocb_pkt, &purex_ext->els_frame_payload[0], no_bytes);
else
memcpy(iocb_pkt, &purex->els_frame_payload[0], no_bytes);
buffer_copy_offset += no_bytes;
@@ -901,6 +934,8 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
if (is_purls)
((response_t *)purls)->signature = RESPONSE_PROCESSED;
+ else if (IS_QLA29XX(ha))
+ ((struct response_ext *)purex_ext)->signature = RESPONSE_PROCESSED;
else
((response_t *)purex)->signature = RESPONSE_PROCESSED;
wmb();
@@ -1075,9 +1110,9 @@ qla24xx_alloc_purex_item(scsi_qla_host_t *vha, uint16_t size)
struct purex_item *item = NULL;
uint8_t item_hdr_size = sizeof(*item);
- if (size > QLA_DEFAULT_PAYLOAD_SIZE) {
+ if (size > QLA_MAX_IOCB_SIZE) {
item = kzalloc(item_hdr_size +
- (size - QLA_DEFAULT_PAYLOAD_SIZE), GFP_ATOMIC);
+ (size - QLA_MAX_IOCB_SIZE), GFP_ATOMIC);
} else {
if (atomic_inc_return(&vha->default_item.in_use) == 1) {
item = &vha->default_item;
@@ -1126,14 +1161,20 @@ qla24xx_queue_purex_item(scsi_qla_host_t *vha, struct purex_item *pkt,
static struct purex_item
*qla24xx_copy_std_pkt(struct scsi_qla_host *vha, void *pkt)
{
+ struct qla_hw_data *ha = vha->hw;
struct purex_item *item;
+ u16 copy_sz;
- item = qla24xx_alloc_purex_item(vha,
- QLA_DEFAULT_PAYLOAD_SIZE);
+ if (IS_QLA29XX(ha))
+ copy_sz = sizeof(struct purex_entry_24xx_ext);
+ else
+ copy_sz = QLA_DEFAULT_PAYLOAD_SIZE;
+
+ item = qla24xx_alloc_purex_item(vha, copy_sz);
if (!item)
return item;
- memcpy(&item->iocb, pkt, sizeof(item->iocb));
+ memcpy(&item->iocb, pkt, copy_sz);
return item;
}
@@ -1165,8 +1206,8 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF)
- PURX_ELS_HEADER_SIZE;
- if (total_bytes > sizeof(item->iocb.iocb))
- total_bytes = sizeof(item->iocb.iocb);
+ if (total_bytes > QLA_MAX_IOCB_SIZE)
+ total_bytes = QLA_MAX_IOCB_SIZE;
pending_bytes = total_bytes;
entry_count = entry_count_remaining = purex->entry_count;
@@ -3995,6 +4036,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
{
void *pkt;
struct qla_hw_data *ha = vha->hw;
+ struct purex_entry_24xx_ext *purex_entry_ext;
struct purex_entry_24xx *purex_entry;
struct purex_item *pure_item;
struct pt_ls4_rx_unsol *p;
@@ -4112,8 +4154,16 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
(struct vp_ctrl_entry_24xx *)pkt);
break;
case PUREX_IOCB_TYPE:
- purex_entry = (void *)pkt;
- switch (purex_entry->els_frame_payload[3]) {
+ if (IS_QLA29XX(ha)) {
+ purex_entry_ext = (void *)pkt;
+ purex_entry = NULL;
+ } else {
+ purex_entry = (void *)pkt;
+ purex_entry_ext = NULL;
+ }
+ switch (IS_QLA29XX(ha) ?
+ purex_entry_ext->els_frame_payload[3] :
+ purex_entry->els_frame_payload[3]) {
case ELS_RDP:
pure_item = qla24xx_copy_std_pkt(vha, pkt);
if (!pure_item)
@@ -4148,16 +4198,20 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
qla_rsp_ring_rewind_to(rsp,
(response_t *)pkt, cur_ring_index);
- ql_dbg(ql_dbg_init, vha, 0x5091,
- "Defer processing ELS opcode %#x...\n",
- purex_entry->els_frame_payload[3]);
+ ql_dbg(ql_dbg_init, vha, 0x5091,
+ "Defer processing ELS opcode %#x...\n",
+ IS_QLA29XX(ha) ?
+ purex_entry_ext->els_frame_payload[3] :
+ purex_entry->els_frame_payload[3]);
return;
}
qla24xx_auth_els(vha, (void **)&pkt, &rsp);
break;
default:
ql_log(ql_log_warn, vha, 0x509c,
- "Discarding ELS Request opcode 0x%x\n",
+ "Discarding ELS Request opcode 0x%x...\n",
+ IS_QLA29XX(ha) ?
+ purex_entry_ext->els_frame_payload[3] :
purex_entry->els_frame_payload[3]);
}
break;
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 0b7c4ee73ebf..c327d053f8ee 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -6138,13 +6138,15 @@ qla83xx_idc_lock(scsi_qla_host_t *base_vha, uint16_t requester_id)
}
static bool
-qla25xx_rdp_rsp_reduce_size(struct scsi_qla_host *vha,
- struct purex_entry_24xx *purex)
+qla25xx_rdp_rsp_reduce_size(struct scsi_qla_host *vha, void *pkt)
{
+ struct purex_entry_24xx *purex = pkt;
char fwstr[16];
- u32 sid = purex->s_id[2] << 16 | purex->s_id[1] << 8 | purex->s_id[0];
+ u32 sid;
struct port_database_24xx *pdb;
+ sid = purex->s_id[2] << 16 | purex->s_id[1] << 8 | purex->s_id[0];
+
/* Domain Controller is always logged-out. */
/* if RDP request is not from Domain Controller: */
if (sid != 0xfffc01)
@@ -6209,15 +6211,26 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
uint8_t *sfp = NULL;
uint16_t sfp_flags = 0;
uint rsp_payload_length = sizeof(*rsp_payload);
+ uint8_t vp_idx;
+ size_t purex_sz;
int rval;
ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0180,
"%s: Enter\n", __func__);
+ if (IS_QLA29XX(ha)) {
+ vp_idx = le16_to_cpu(
+ ((struct purex_entry_24xx_ext *)purex)->vp_idx);
+ purex_sz = sizeof(struct purex_entry_24xx_ext);
+ } else {
+ vp_idx = purex->vp_idx;
+ purex_sz = sizeof(*purex);
+ }
+
ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0181,
"-------- ELS REQ -------\n");
ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0182,
- purex, sizeof(*purex));
+ purex, purex_sz);
if (qla25xx_rdp_rsp_reduce_size(vha, purex)) {
rsp_payload_length =
@@ -6257,7 +6270,7 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
rsp_els->handle = 0;
rsp_els->nport_handle = purex->nport_handle;
rsp_els->tx_dsd_count = cpu_to_le16(1);
- rsp_els->vp_index = purex->vp_idx;
+ rsp_els->vp_index = vp_idx;
rsp_els->sof_type = EST_SOFI3;
rsp_els->rx_xchg_address = purex->rx_xchg_addr;
rsp_els->rx_dsd_count = 0;
@@ -8378,6 +8391,7 @@ qla2x00_module_init(void)
BUILD_BUG_ON(sizeof(struct pt_ls4_request) != 64);
BUILD_BUG_ON(sizeof(struct pt_ls4_rx_unsol) != 64);
BUILD_BUG_ON(sizeof(struct purex_entry_24xx) != 64);
+ BUILD_BUG_ON(sizeof(struct purex_entry_24xx_ext) != 128);
BUILD_BUG_ON(sizeof(struct qla2100_fw_dump) != 123634);
BUILD_BUG_ON(sizeof(struct qla2300_fw_dump) != 136100);
BUILD_BUG_ON(sizeof(struct qla24xx_fw_dump) != 37976);
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 29/56] scsi: qla2xxx: Update handling of ELS IOCBs for 29xx series
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (27 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 28/56] scsi: qla2xxx: Enhance purex_entry " Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 15:00 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 30/56] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx Nilesh Javali
` (26 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Update ELS IOCB handling to support the extended 128-byte
els_entry_24xx_ext structure used by 29xx series adapters.
Change the signatures of qla24xx_els_logo_iocb(), qla_els_pt_iocb(),
and qla24xx_els_iocb() to accept a generic void pointer, enabling
differentiation between standard and extended ELS structures at
runtime.
Introduce a static inline helper qla_els_set_vp_sof() in qla_inline.h
that centralises the 24xx-vs-29xx vp_index/sof_type encoding: the 24xx
layout uses separate u8 vp_index + u8 sof_type (EST_SOFI3), while
29xx uses a __le16 with bitfields (vp_index:9 / sof_type:4 /
ELS_EXT_EST_SOFI3). All ELS issue paths now call this helper instead
of open-coding the branch, including the RDP response path in qla_os.c.
In qla2x00_start_sp(), collapse the IS_QLA29XX() branch for the
handle assignment in SRB_ELS_CMD_HST_NOLOGIN: els_entry_24xx::handle
and els_entry_24xx_ext::handle are both u32 at offset 4, so a single
24xx-view write is layout-compatible with both strides.
DMA allocations in qla24xx_process_abts() and
qla24xx_process_purex_rdp() are updated to use the correct size for
the adapter type. A BUILD_BUG_ON is added to verify the extended
structure size.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_gbl.h | 2 +-
drivers/scsi/qla2xxx/qla_inline.h | 28 ++++++++++++++++++++++++++++
drivers/scsi/qla2xxx/qla_iocb.c | 28 ++++++++++++++++++----------
drivers/scsi/qla2xxx/qla_isr.c | 26 ++++++++++++++++++--------
drivers/scsi/qla2xxx/qla_os.c | 31 ++++++++++++++++++++-----------
5 files changed, 85 insertions(+), 30 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 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 69ed3e8e0259..d4d7f75ca158 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -2974,8 +2974,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;
@@ -2986,11 +2987,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;
@@ -3321,9 +3322,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;
@@ -3332,11 +3334,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;
@@ -3357,8 +3359,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;
@@ -3369,8 +3372,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 =
@@ -4202,6 +4205,11 @@ qla2x00_start_sp(srb_t *sp)
break;
case SRB_ELS_CMD_HST_NOLOGIN:
qla_els_pt_iocb(sp->vha, pkt, &sp->u.bsg_cmd.u.els_arg);
+ /*
+ * els_entry_24xx::handle and els_entry_24xx_ext::handle are
+ * both u32 at offset 4, so a 24xx-view write is layout-
+ * compatible with both strides.
+ */
((struct els_entry_24xx *)pkt)->handle = sp->handle;
break;
case SRB_CT_CMD:
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index c4e2e62f924f..e2653620e80b 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -83,6 +83,8 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
dma_addr_t dma;
uint32_t fctl;
int rval;
+ void *rsp_pkt;
+ size_t rsp_sz;
ql_dbg(ql_dbg_init, vha, 0x0286, "%s: entered.\n", __func__);
@@ -95,15 +97,22 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0287,
(uint8_t *)abts, sizeof(*abts));
- rsp_els = dma_alloc_coherent(&ha->pdev->dev, sizeof(*rsp_els), &dma,
+ if (IS_QLA29XX(ha))
+ rsp_sz = sizeof(struct els_entry_24xx_ext);
+ else
+ rsp_sz = sizeof(struct els_entry_24xx);
+
+ rsp_pkt = dma_alloc_coherent(&ha->pdev->dev, rsp_sz, &dma,
GFP_KERNEL);
- if (!rsp_els) {
+ if (!rsp_pkt) {
ql_log(ql_log_warn, vha, 0x0287,
"Failed allocate dma buffer ABTS/ELS RSP.\n");
return;
}
+ rsp_els = rsp_pkt;
/* terminate exchange */
+ memset(rsp_pkt, 0, rsp_sz);
rsp_els->entry_type = ELS_IOCB_TYPE;
rsp_els->entry_count = 1;
rsp_els->nport_handle = cpu_to_le16(~0);
@@ -115,8 +124,8 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0283,
"-------- ELS RSP -------\n");
ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0283,
- (uint8_t *)rsp_els, sizeof(*rsp_els));
- rval = qla2x00_issue_iocb(vha, rsp_els, dma, 0);
+ (uint8_t *)rsp_pkt, rsp_sz);
+ rval = qla2x00_issue_iocb(vha, rsp_pkt, dma, 0);
if (rval) {
ql_log(ql_log_warn, vha, 0x0288,
"%s: iocb failed to execute -> %x\n", __func__, rval);
@@ -131,7 +140,7 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
}
/* send ABTS response */
- abts_rsp = (void *)rsp_els;
+ abts_rsp = rsp_pkt;
memset(abts_rsp, 0, sizeof(*abts_rsp));
abts_rsp->entry_type = ABTS_RSP_TYPE;
abts_rsp->entry_count = 1;
@@ -182,7 +191,7 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
"%s: done.\n", __func__);
}
- dma_free_coherent(&ha->pdev->dev, sizeof(*rsp_els), rsp_els, dma);
+ dma_free_coherent(&ha->pdev->dev, rsp_sz, rsp_pkt, dma);
}
/**
@@ -2385,7 +2394,8 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
case SRB_ELS_CMD_HST_NOLOGIN:
type = "els";
{
- struct els_entry_24xx *els = (void *)pkt;
+ __le16 ctl_flags =
+ ((struct els_entry_24xx *)pkt)->control_flags;
struct qla_bsg_auth_els_request *p =
(struct qla_bsg_auth_els_request *)bsg_job->request;
@@ -2395,7 +2405,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
e->d_id[2], e->d_id[1], e->d_id[0],
comp_status, p->e.extra_rx_xchg_address, bsg_job);
- if (!(le16_to_cpu(els->control_flags) & ECF_PAYLOAD_DESCR_MASK)) {
+ if (!(le16_to_cpu(ctl_flags) & ECF_PAYLOAD_DESCR_MASK)) {
if (sp->remap.remapped) {
n = sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
bsg_job->reply_payload.sg_cnt,
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index c327d053f8ee..74bf203f8d9f 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -6211,8 +6211,10 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
uint8_t *sfp = NULL;
uint16_t sfp_flags = 0;
uint rsp_payload_length = sizeof(*rsp_payload);
- uint8_t vp_idx;
+ u16 vp_idx;
size_t purex_sz;
+ size_t rsp_els_sz;
+ void *rsp_els_pkt = NULL;
int rval;
ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0180,
@@ -6240,13 +6242,19 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
rsp_payload_length);
}
- rsp_els = dma_alloc_coherent(&ha->pdev->dev, sizeof(*rsp_els),
+ if (IS_QLA29XX(ha))
+ rsp_els_sz = sizeof(struct els_entry_24xx_ext);
+ else
+ rsp_els_sz = sizeof(struct els_entry_24xx);
+
+ rsp_els_pkt = dma_alloc_coherent(&ha->pdev->dev, rsp_els_sz,
&rsp_els_dma, GFP_KERNEL);
- if (!rsp_els) {
+ if (!rsp_els_pkt) {
ql_log(ql_log_warn, vha, 0x0183,
- "Failed allocate dma buffer ELS RSP.\n");
+ "Failed to allocate dma buffer ELS RSP.\n");
goto dealloc;
}
+ rsp_els = rsp_els_pkt;
rsp_payload = dma_alloc_coherent(&ha->pdev->dev, sizeof(*rsp_payload),
&rsp_payload_dma, GFP_KERNEL);
@@ -6270,12 +6278,12 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
rsp_els->handle = 0;
rsp_els->nport_handle = purex->nport_handle;
rsp_els->tx_dsd_count = cpu_to_le16(1);
- rsp_els->vp_index = vp_idx;
- rsp_els->sof_type = EST_SOFI3;
rsp_els->rx_xchg_address = purex->rx_xchg_addr;
rsp_els->rx_dsd_count = 0;
rsp_els->opcode = purex->els_frame_payload[0];
+ qla_els_set_vp_sof(vha, rsp_els_pkt, vp_idx);
+
rsp_els->d_id[0] = purex->s_id[0];
rsp_els->d_id[1] = purex->s_id[1];
rsp_els->d_id[2] = purex->s_id[2];
@@ -6575,13 +6583,13 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0184,
"-------- ELS RSP -------\n");
ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0185,
- rsp_els, sizeof(*rsp_els));
+ rsp_els_pkt, rsp_els_sz);
ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0186,
"-------- ELS RSP PAYLOAD -------\n");
ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0187,
rsp_payload, rsp_payload_length);
- rval = qla2x00_issue_iocb(vha, rsp_els, rsp_els_dma, 0);
+ rval = qla2x00_issue_iocb(vha, rsp_els_pkt, rsp_els_dma, 0);
if (rval) {
ql_log(ql_log_warn, vha, 0x0188,
@@ -6605,9 +6613,9 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
if (rsp_payload)
dma_free_coherent(&ha->pdev->dev, sizeof(*rsp_payload),
rsp_payload, rsp_payload_dma);
- if (rsp_els)
- dma_free_coherent(&ha->pdev->dev, sizeof(*rsp_els),
- rsp_els, rsp_els_dma);
+ if (rsp_els_pkt)
+ dma_free_coherent(&ha->pdev->dev, rsp_els_sz,
+ rsp_els_pkt, rsp_els_dma);
}
void
@@ -8376,6 +8384,7 @@ qla2x00_module_init(void)
BUILD_BUG_ON(sizeof(struct device_reg_82xx) != 1288);
BUILD_BUG_ON(sizeof(struct device_reg_fx00) != 216);
BUILD_BUG_ON(sizeof(struct els_entry_24xx) != 64);
+ BUILD_BUG_ON(sizeof(struct els_entry_24xx_ext) != 128);
BUILD_BUG_ON(sizeof(struct els_sts_entry_24xx) != 64);
BUILD_BUG_ON(sizeof(struct fxdisc_entry_fx00) != 64);
BUILD_BUG_ON(sizeof(struct imm_ntfy_from_isp) != 64);
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 30/56] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (28 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 29/56] scsi: qla2xxx: Update handling of ELS IOCBs " Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 14:31 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 31/56] scsi: qla2xxx: Add 29xx extended logio IOCB support Nilesh Javali
` (25 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Add a BUILD_BUG_ON in qla2x00_module_init() to validate that struct
els_sts_entry_24xx_ext is 128 bytes, matching the 29xx firmware IOCB
size.
The extended layout (29xx) overlays the base els_sts_entry_24xx for
every field read in qla24xx_els_ct_entry(): comp_status,
total_byte_count, error_subcode_1/2, d_id[], s_id[], and
control_flags all sit at byte-identical offsets in both structs. Only
vp_index/sof_type at offset 14-15 differs (bit-packed differently in
the ext variant), but that field is write-only on the issue path and
never read in this completion handler.
Add a docblock at the top of qla24xx_els_ct_entry() documenting this
layout property. Improve a few log messages for clarity.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_isr.c | 31 +++++++++++++++++++++++--------
drivers/scsi/qla2xxx/qla_os.c | 1 +
2 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index e2653620e80b..c286465ae013 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -2359,6 +2359,22 @@ static void
qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
void *pkt, int iocb_type)
{
+ /*
+ * els_sts_entry_24xx_ext (29xx) overlays els_sts_entry_24xx for every
+ * field touched in this completion handler: comp_status (offset 8),
+ * total_byte_count (32), error_subcode_1 (36), error_subcode_2 (40),
+ * d_id[]/s_id[] (24..29), control_flags (30) all sit at byte-identical
+ * offsets in both layouts (only vp_index/sof_type at offset 14-15 are
+ * bit-packed differently, and that field is write-only on the issue
+ * path -- we never read it here). All reads in this function are
+ * therefore stride-agnostic and go through a single struct
+ * els_sts_entry_24xx * view; the trailing reserved_4[] of the extended
+ * layout is irrelevant on completion.
+ *
+ * Likewise els_entry_24xx_ext overlays els_entry_24xx through
+ * control_flags (offset 30), so the SRB_ELS_CMD_HST_NOLOGIN ctl_flags
+ * read below also goes through the 24xx view.
+ */
struct sts_entry_24xx *sts24 = pkt;
struct els_sts_entry_24xx *ese = (struct els_sts_entry_24xx *)pkt;
const char func[] = "ELS_CT_IOCB";
@@ -2400,7 +2416,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
(struct qla_bsg_auth_els_request *)bsg_job->request;
ql_dbg(ql_dbg_user, vha, 0x700f,
- "%s %s. portid=%02x%02x%02x status %x xchg %x bsg ptr %p\n",
+ "%s %s complete portid=%02x%02x%02x status %x xchg %x bsg ptr %p\n",
__func__, sc_to_str(p->e.sub_cmd),
e->d_id[2], e->d_id[1], e->d_id[0],
comp_status, p->e.extra_rx_xchg_address, bsg_job);
@@ -2461,14 +2477,15 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
} else {
if (comp_status == CS_DATA_UNDERRUN) {
res = DID_OK << 16;
- els->u.els_plogi.len = cpu_to_le16(le32_to_cpu(
- ese->total_byte_count));
+ els->u.els_plogi.len = cpu_to_le16(
+ le32_to_cpu(ese->total_byte_count));
if (sp->remap.remapped &&
((u8 *)sp->remap.rsp.buf)[0] == ELS_LS_ACC) {
ql_dbg(ql_dbg_user, vha, 0x503f,
"%s IOCB Done LS_ACC %02x%02x%02x -> %02x%02x%02x",
- __func__, e->s_id[0], e->s_id[2], e->s_id[1],
+ __func__,
+ e->s_id[0], e->s_id[2], e->s_id[1],
e->d_id[2], e->d_id[1], e->d_id[0]);
logit = 0;
}
@@ -2496,8 +2513,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
ql_dbg(ql_dbg_user, vha, 0x503f,
"subcode 1=0x%x subcode 2=0x%x bytes=0x%x %02x%02x%02x -> %02x%02x%02x\n",
fw_status[1], fw_status[2],
- le32_to_cpu(((struct els_sts_entry_24xx *)
- pkt)->total_byte_count),
+ le32_to_cpu(ese->total_byte_count),
e->s_id[0], e->s_id[2], e->s_id[1],
e->d_id[2], e->d_id[1], e->d_id[0]);
}
@@ -2514,8 +2530,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
ql_log(ql_log_info, vha, 0x503f,
"subcode 1=0x%x subcode 2=0x%x bytes=0x%x %02x%02x%02x -> %02x%02x%02x\n",
fw_status[1], fw_status[2],
- le32_to_cpu(((struct els_sts_entry_24xx *)
- pkt)->total_byte_count),
+ le32_to_cpu(ese->total_byte_count),
e->s_id[0], e->s_id[2], e->s_id[1],
e->d_id[2], e->d_id[1], e->d_id[0]);
}
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 74bf203f8d9f..81f3731ee384 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8386,6 +8386,7 @@ qla2x00_module_init(void)
BUILD_BUG_ON(sizeof(struct els_entry_24xx) != 64);
BUILD_BUG_ON(sizeof(struct els_entry_24xx_ext) != 128);
BUILD_BUG_ON(sizeof(struct els_sts_entry_24xx) != 64);
+ BUILD_BUG_ON(sizeof(struct els_sts_entry_24xx_ext) != 128);
BUILD_BUG_ON(sizeof(struct fxdisc_entry_fx00) != 64);
BUILD_BUG_ON(sizeof(struct imm_ntfy_from_isp) != 64);
BUILD_BUG_ON(sizeof(struct init_cb_24xx) != 128);
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 31/56] scsi: qla2xxx: Add 29xx extended logio IOCB support
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (29 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 30/56] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 14:49 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 32/56] scsi: qla2xxx: Enhance task management IOCB handling for 29xx series Nilesh Javali
` (24 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
The 29xx series uses a wider IOCB stride (128 bytes vs 64 bytes).
The logio_entry_24xx_ext layout extends logio_entry_24xx with a
wider vp_index field (__le16 vs u8) while keeping all other
read-side fields (comp_status, io_parameter[0..10], entry_status)
at identical offsets and widths.
Update the logio IOCB builder functions (qla24xx_login_iocb,
qla24xx_logout_iocb, qla24xx_prli_iocb, qla24xx_prlo_iocb,
qla24xx_adisc_iocb) to accept a void pointer and dispatch the
vp_index write through IS_QLA29XX(), using an inline cast to the
extended layout at the single write site.
In the completion handler qla24xx_logio_entry(), accept a void
pointer and read through a single logio_entry_24xx view since all
accessed fields sit at the same offsets in both layouts. Use the
qla_req_entry_size() helper for the dump buffer size.
In qla24xx_login_fabric() and qla24xx_fabric_logout(), allocate
through a void pointer from the DMA pool and dispatch vp_index
via the same inline-cast pattern.
Add a BUILD_BUG_ON for logio_entry_24xx_ext to enforce the 128-byte
size invariant at compile time.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_inline.h | 19 ++++++++++++++
drivers/scsi/qla2xxx/qla_iocb.c | 28 +++++++++++++--------
drivers/scsi/qla2xxx/qla_isr.c | 41 ++++++++++++++++++-------------
drivers/scsi/qla2xxx/qla_mbx.c | 27 +++++++++++---------
drivers/scsi/qla2xxx/qla_os.c | 1 +
5 files changed, 78 insertions(+), 38 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index 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 d4d7f75ca158..c7669541ce07 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -2563,8 +2563,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;
@@ -2591,12 +2592,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;
@@ -2621,7 +2623,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
@@ -2649,9 +2651,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) {
@@ -2668,7 +2672,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
@@ -2690,12 +2694,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
@@ -4056,8 +4062,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);
@@ -4066,7 +4074,7 @@ qla24xx_prlo_iocb(srb_t *sp, struct logio_entry_24xx *logio)
logio->port_id[0] = sp->fcport->d_id.b.al_pa;
logio->port_id[1] = sp->fcport->d_id.b.area;
logio->port_id[2] = sp->fcport->d_id.b.domain;
- logio->vp_index = sp->fcport->vha->vp_idx;
+ qla_logio_set_vp_index(sp->vha->hw, pkt, sp->fcport->vha->vp_idx);
}
static int qla_get_iocbs_resource(struct srb *sp)
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index c286465ae013..afd685d48c6e 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -2583,8 +2583,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
}
static void
-qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
- struct logio_entry_24xx *logio)
+qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, void *pkt)
{
const char func[] = "LOGIO-IOCB";
const char *type;
@@ -2594,8 +2593,20 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
uint16_t *data;
uint32_t iop[2];
int logit = 1;
+ struct qla_hw_data *ha = vha->hw;
+ /*
+ * logio_entry_24xx_ext overlays logio_entry_24xx through
+ * io_parameter[10]: comp_status, io_parameter[0..10] and
+ * entry_status are at identical offsets and types in both layouts
+ * (only vp_index width differs, and that field is write-only on
+ * the issue path). So all reads in this completion handler are
+ * stride-agnostic and we read through a single struct
+ * logio_entry_24xx * view; the trailing reserved_2[64] of the
+ * extended layout is irrelevant here.
+ */
+ struct logio_entry_24xx *logio = pkt;
- sp = qla2x00_get_sp_from_handle(vha, func, req, logio);
+ sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
if (!sp)
return;
@@ -2615,7 +2626,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
fcport->d_id.b.area, fcport->d_id.b.al_pa,
logio->entry_status);
ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x504d,
- logio, sizeof(*logio));
+ pkt, qla_rsp_entry_size(ha));
goto logio_done;
}
@@ -2626,7 +2637,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
type, sp->handle, fcport->d_id.b24, fcport->port_name,
le32_to_cpu(logio->io_parameter[0]));
- vha->hw->exch_starvation = 0;
+ ha->exch_starvation = 0;
data[0] = MBS_COMMAND_COMPLETE;
if (sp->type == SRB_PRLI_CMD) {
@@ -2665,6 +2676,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
iop[0] = le32_to_cpu(logio->io_parameter[0]);
iop[1] = le32_to_cpu(logio->io_parameter[1]);
+
lio->u.logio.iop[0] = iop[0];
lio->u.logio.iop[1] = iop[1];
switch (iop[0]) {
@@ -2689,14 +2701,14 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
data[0] = MBS_COMMAND_ERROR;
break;
case LSC_SCODE_NOXCB:
- vha->hw->exch_starvation++;
- if (vha->hw->exch_starvation > 5) {
+ ha->exch_starvation++;
+ if (ha->exch_starvation > 5) {
ql_log(ql_log_warn, vha, 0xd046,
"Exchange starvation. Resetting RISC\n");
- vha->hw->exch_starvation = 0;
+ ha->exch_starvation = 0;
- if (IS_P3P_TYPE(vha->hw))
+ if (IS_P3P_TYPE(ha))
set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
else
set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
@@ -2712,16 +2724,12 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
ql_log(ql_log_warn, sp->vha, 0x5037, "Async-%s failed: "
"handle=%x pid=%06x wwpn=%8phC comp_status=%x iop0=%x iop1=%x\n",
type, sp->handle, fcport->d_id.b24, fcport->port_name,
- le16_to_cpu(logio->comp_status),
- le32_to_cpu(logio->io_parameter[0]),
- le32_to_cpu(logio->io_parameter[1]));
+ le16_to_cpu(logio->comp_status), iop[0], iop[1]);
else
ql_dbg(ql_dbg_disc, sp->vha, 0x5037, "Async-%s failed: "
"handle=%x pid=%06x wwpn=%8phC comp_status=%x iop0=%x iop1=%x\n",
type, sp->handle, fcport->d_id.b24, fcport->port_name,
- le16_to_cpu(logio->comp_status),
- le32_to_cpu(logio->io_parameter[0]),
- le32_to_cpu(logio->io_parameter[1]));
+ le16_to_cpu(logio->comp_status), iop[0], iop[1]);
logio_done:
sp->done(sp, 0);
@@ -4118,8 +4126,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
(struct vp_rpt_id_entry_24xx *)pkt);
break;
case LOGINOUT_PORT_IOCB_TYPE:
- qla24xx_logio_entry(vha, rsp->req,
- (struct logio_entry_24xx *)pkt);
+ qla24xx_logio_entry(vha, rsp->req, pkt);
break;
case CT_IOCB_TYPE:
qla24xx_els_ct_entry(vha, rsp->req, pkt, CT_IOCB_TYPE);
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index f9b326c02055..7dff227899a5 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -2573,7 +2573,7 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
uint8_t area, uint8_t al_pa, uint16_t *mb, uint8_t opt)
{
int rval;
-
+ void *lg_buf;
struct logio_entry_24xx *lg;
dma_addr_t lg_dma;
uint32_t iop[2];
@@ -2588,12 +2588,13 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
else
req = ha->req_q_map[0];
- lg = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
- if (lg == NULL) {
+ lg_buf = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
+ if (!lg_buf) {
ql_log(ql_log_warn, vha, 0x1062,
"Failed to allocate login IOCB.\n");
return QLA_MEMORY_ALLOC_FAILED;
}
+ lg = lg_buf;
lg->entry_type = LOGINOUT_PORT_IOCB_TYPE;
lg->entry_count = 1;
@@ -2607,8 +2608,9 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
lg->port_id[0] = al_pa;
lg->port_id[1] = area;
lg->port_id[2] = domain;
- lg->vp_index = vha->vp_idx;
- rval = qla2x00_issue_iocb_timeout(vha, lg, lg_dma, 0,
+ qla_logio_set_vp_index(ha, lg, vha->vp_idx);
+
+ rval = qla2x00_issue_iocb_timeout(vha, lg_buf, lg_dma, 0,
(ha->r_a_tov / 10 * 2) + 2);
if (rval != QLA_SUCCESS) {
ql_dbg(ql_dbg_mbx, vha, 0x1063,
@@ -2678,7 +2680,7 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
*/
}
- dma_pool_free(ha->s_dma_pool, lg, lg_dma);
+ dma_pool_free(ha->s_dma_pool, lg_buf, lg_dma);
return rval;
}
@@ -2849,6 +2851,7 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
uint8_t area, uint8_t al_pa)
{
int rval;
+ void *lg_buf;
struct logio_entry_24xx *lg;
dma_addr_t lg_dma;
struct qla_hw_data *ha = vha->hw;
@@ -2857,12 +2860,13 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x106d,
"Entered %s.\n", __func__);
- lg = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
- if (lg == NULL) {
+ lg_buf = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
+ if (!lg_buf) {
ql_log(ql_log_warn, vha, 0x106e,
"Failed to allocate logout IOCB.\n");
return QLA_MEMORY_ALLOC_FAILED;
}
+ lg = lg_buf;
req = vha->req;
lg->entry_type = LOGINOUT_PORT_IOCB_TYPE;
@@ -2875,8 +2879,9 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
lg->port_id[0] = al_pa;
lg->port_id[1] = area;
lg->port_id[2] = domain;
- lg->vp_index = vha->vp_idx;
- rval = qla2x00_issue_iocb_timeout(vha, lg, lg_dma, 0,
+ qla_logio_set_vp_index(ha, lg, vha->vp_idx);
+
+ rval = qla2x00_issue_iocb_timeout(vha, lg_buf, lg_dma, 0,
(ha->r_a_tov / 10 * 2) + 2);
if (rval != QLA_SUCCESS) {
ql_dbg(ql_dbg_mbx, vha, 0x106f,
@@ -2898,7 +2903,7 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
"Done %s.\n", __func__);
}
- dma_pool_free(ha->s_dma_pool, lg, lg_dma);
+ dma_pool_free(ha->s_dma_pool, lg_buf, lg_dma);
return rval;
}
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 81f3731ee384..b0e89dfff2e8 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8392,6 +8392,7 @@ qla2x00_module_init(void)
BUILD_BUG_ON(sizeof(struct init_cb_24xx) != 128);
BUILD_BUG_ON(sizeof(struct init_cb_81xx) != 128);
BUILD_BUG_ON(sizeof(struct logio_entry_24xx) != 64);
+ BUILD_BUG_ON(sizeof(struct logio_entry_24xx_ext) != 128);
BUILD_BUG_ON(sizeof(struct mbx_entry) != 64);
BUILD_BUG_ON(sizeof(struct mid_init_cb_24xx) != 5252);
BUILD_BUG_ON(sizeof(struct mrk_entry_24xx) != 64);
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 32/56] scsi: qla2xxx: Enhance task management IOCB handling for 29xx series
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (30 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 31/56] scsi: qla2xxx: Add 29xx extended logio IOCB support Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 33/56] scsi: qla2xxx: Add abort command " Nilesh Javali
` (23 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Update qla24xx_tm_iocb() and __qla24xx_issue_tmf() to support the
extended task management structure (tsk_mgmt_entry_ext) for 29xx
adapters.
tsk_mgmt_entry_ext overlays tsk_mgmt_entry through control_flags
(offsets 0-27 are byte-identical): entry_type, entry_count, handle,
nport_handle, timeout, lun and control_flags sit at the same offsets
and widths. The layouts diverge only after that point:
- the 24xx layout has port_id[3] + u8 vp_index;
- the ext layout has __le16 vp_index and no port_id.
Factor the common IOCB header writes through a single tsk_mgmt_entry *
view and branch on IS_QLA29XX() only for the diverging port_id /
vp_index assignments. Change qla24xx_tm_iocb() to accept void *pkt
to allow casting to either structure type.
Add tsk_ext member to the tsk_mgmt_cmd union and a BUILD_BUG_ON size
check for the 128-byte extended structure.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_iocb.c | 31 ++++++++++++++++++++++---------
drivers/scsi/qla2xxx/qla_mbx.c | 18 ++++++++++++++----
drivers/scsi/qla2xxx/qla_os.c | 1 +
3 files changed, 37 insertions(+), 13 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index c7669541ce07..f481e84c93f9 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -2726,7 +2726,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;
@@ -2735,26 +2735,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] 87+ messages in thread
* [PATCH v4 33/56] scsi: qla2xxx: Add abort command handling for 29xx series
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (31 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 32/56] scsi: qla2xxx: Enhance task management IOCB handling for 29xx series Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 15:18 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 34/56] scsi: qla2xxx: Enhance ABTS processing " Nilesh Javali
` (22 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Extend the abort-IOCB code path to support the 29xx extended
abort_entry_24xx_ext structure alongside the existing
abort_entry_24xx.
The two layouts overlay byte-for-byte through req_que_no
(offsets 0-17): entry_status (offset 3), the
nport_handle/comp_status union (offset 8), and options (offset 10)
sit at identical positions in both. After that they diverge: the
24xx variant carries reserved_1[30], port_id[3], and a u8 vp_index
at offsets 48-51, while the ext variant places a __le16 vp_index at
offset 18 and drops port_id. The drv / fw unions live at offset 56
in the 24xx layout but offset 24 in ext.
Leverage this overlap by using a single struct abort_entry_24xx *
view for the common header writes (entry_type, count, handle,
nport_handle, handle_to_abort, req_que_no) and completion-status
reads (entry_status, comp_status), branching on IS_QLA29XX() only
where the layouts genuinely diverge:
- port_id (24xx-only) and vp_index width on the issue path
(qla24xx_abort_iocb in qla_iocb.c, qla24xx_abort_command in
qla_mbx.c);
- drv / fw union access in qla_nvme_abort_set_option /
qla_nvme_abort_process_comp_status (qla_nvme.c);
- completion comp_status read in qla24xx_abort_iocb_entry
(qla_isr.c) is stride-agnostic -- no IS_QLA29XX dispatch
needed.
Function signatures in qla_nvme_abort_set_option(),
qla_nvme_abort_process_comp_status(), qla24xx_abort_iocb(), and
qla24xx_abort_iocb_entry() are widened to accept void * so both
struct variants can be passed through. memset() uses
qla_req_entry_size(ha) to match the ring-slot size. Response
status checking now reads comp_status instead of nport_handle. A
BUILD_BUG_ON verifies abort_entry_24xx_ext is 128 bytes.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_gbl.h | 6 ++--
drivers/scsi/qla2xxx/qla_iocb.c | 52 ++++++++++++++++++++-----------
drivers/scsi/qla2xxx/qla_isr.c | 14 ++++++---
drivers/scsi/qla2xxx/qla_mbx.c | 45 ++++++++++++++++++---------
drivers/scsi/qla2xxx/qla_nvme.c | 54 +++++++++++++++++++++++++--------
drivers/scsi/qla2xxx/qla_os.c | 1 +
6 files changed, 120 insertions(+), 52 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 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 f481e84c93f9..54c8d2feb4ae 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -3925,32 +3925,50 @@ qla82xx_start_scsi(srb_t *sp)
}
static void
-qla24xx_abort_iocb(srb_t *sp, struct abort_entry_24xx *abt_iocb)
+qla24xx_abort_iocb(srb_t *sp, void *pkt)
{
struct srb_iocb *aio = &sp->u.iocb_cmd;
scsi_qla_host_t *vha = sp->vha;
+ struct qla_hw_data *ha = vha->hw;
struct req_que *req = sp->qpair->req;
srb_t *orig_sp = sp->cmd_sp;
+ struct abort_entry_24xx *abt = pkt;
- memset(abt_iocb, 0, sizeof(struct abort_entry_24xx));
- abt_iocb->entry_type = ABORT_IOCB_TYPE;
- abt_iocb->entry_count = 1;
- abt_iocb->handle = make_handle(req->id, sp->handle);
- if (sp->fcport) {
- abt_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
- abt_iocb->port_id[0] = sp->fcport->d_id.b.al_pa;
- abt_iocb->port_id[1] = sp->fcport->d_id.b.area;
- abt_iocb->port_id[2] = sp->fcport->d_id.b.domain;
- }
- abt_iocb->handle_to_abort =
- make_handle(le16_to_cpu(aio->u.abt.req_que_no),
- aio->u.abt.cmd_hndl);
- abt_iocb->vp_index = vha->vp_idx;
- abt_iocb->req_que_no = aio->u.abt.req_que_no;
+ /*
+ * abort_entry_24xx_ext overlays abort_entry_24xx through
+ * req_que_no (offsets 0-17). After that the layouts diverge:
+ * the 24xx variant has 30 bytes of reserved_1 followed by
+ * port_id[3] and a u8 vp_index at offsets 48-51, while the
+ * ext variant places a __le16 vp_index at offset 18 and has
+ * no port_id field. Common-header writes go through one
+ * struct abort_entry_24xx * view; only port_id / vp_index
+ * branch on stride.
+ */
+ memset(pkt, 0, qla_req_entry_size(ha));
+ abt->entry_type = ABORT_IOCB_TYPE;
+ abt->entry_count = 1;
+ abt->handle = make_handle(req->id, sp->handle);
+ if (sp->fcport)
+ abt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ abt->handle_to_abort =
+ make_handle(le16_to_cpu(aio->u.abt.req_que_no),
+ aio->u.abt.cmd_hndl);
+ abt->req_que_no = aio->u.abt.req_que_no;
+ if (IS_QLA29XX(ha)) {
+ ((struct abort_entry_24xx_ext *)pkt)->vp_index =
+ cpu_to_le16(vha->vp_idx);
+ } else {
+ if (sp->fcport) {
+ abt->port_id[0] = sp->fcport->d_id.b.al_pa;
+ abt->port_id[1] = sp->fcport->d_id.b.area;
+ abt->port_id[2] = sp->fcport->d_id.b.domain;
+ }
+ abt->vp_index = vha->vp_idx;
+ }
/* need to pass original sp */
if (orig_sp)
- qla_nvme_abort_set_option(abt_iocb, orig_sp);
+ qla_nvme_abort_set_option(pkt, orig_sp);
/* Send the command to the firmware */
wmb();
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index afd685d48c6e..dd4416a3bf3a 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -3960,7 +3960,7 @@ qla24xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
static void
qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
- struct abort_entry_24xx *pkt)
+ void *pkt)
{
const char func[] = "ABT_IOCB";
srb_t *sp;
@@ -3972,7 +3972,14 @@ qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
return;
abt = &sp->u.iocb_cmd;
- abt->u.abt.comp_status = pkt->comp_status;
+ /*
+ * abort_entry_24xx_ext overlays abort_entry_24xx through the
+ * nport_handle/comp_status union at offset 8, so reading
+ * comp_status is stride-agnostic and goes through the 24xx view.
+ */
+ abt->u.abt.comp_status =
+ ((struct abort_entry_24xx *)pkt)->comp_status;
+
orig_sp = sp->cmd_sp;
/* Need to pass original sp */
if (orig_sp)
@@ -4174,8 +4181,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
qla_marker_iocb_entry(vha, rsp->req, pkt);
break;
case ABORT_IOCB_TYPE:
- qla24xx_abort_iocb_entry(vha, rsp->req,
- (struct abort_entry_24xx *)pkt);
+ qla24xx_abort_iocb_entry(vha, rsp->req, pkt);
break;
case MBX_IOCB_TYPE:
qla24xx_mbx_iocb_entry(vha, rsp->req,
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 9bef87862077..0d7aa6fce007 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -3324,7 +3324,7 @@ qla24xx_abort_command(srb_t *sp)
{
int rval;
unsigned long flags = 0;
-
+ void *abt_buf;
struct abort_entry_24xx *abt;
dma_addr_t abt_dma;
uint32_t handle;
@@ -3356,28 +3356,42 @@ qla24xx_abort_command(srb_t *sp)
return QLA_ERR_NOT_FOUND;
}
- abt = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &abt_dma);
- if (abt == NULL) {
+ abt_buf = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &abt_dma);
+ if (abt_buf == NULL) {
ql_log(ql_log_warn, vha, 0x108d,
"Failed to allocate abort IOCB.\n");
return QLA_MEMORY_ALLOC_FAILED;
}
+ abt = abt_buf;
+ /*
+ * abort_entry_24xx_ext overlays abort_entry_24xx through
+ * req_que_no (offsets 0-17), and entry_status (offset 3) and
+ * comp_status (offset 8) sit at identical offsets in both, so
+ * the common header writes and the completion-status reads are
+ * stride-agnostic and go through the 24xx view. Only port_id
+ * (24xx-only) and vp_index width / offset diverge per stride.
+ */
abt->entry_type = ABORT_IOCB_TYPE;
abt->entry_count = 1;
abt->handle = make_handle(req->id, abt->handle);
abt->nport_handle = cpu_to_le16(fcport->loop_id);
abt->handle_to_abort = make_handle(req->id, handle);
- abt->port_id[0] = fcport->d_id.b.al_pa;
- abt->port_id[1] = fcport->d_id.b.area;
- abt->port_id[2] = fcport->d_id.b.domain;
- abt->vp_index = fcport->vha->vp_idx;
-
abt->req_que_no = cpu_to_le16(req->id);
+ if (IS_QLA29XX(ha)) {
+ ((struct abort_entry_24xx_ext *)abt)->vp_index =
+ cpu_to_le16(fcport->vha->vp_idx);
+ } else {
+ abt->port_id[0] = fcport->d_id.b.al_pa;
+ abt->port_id[1] = fcport->d_id.b.area;
+ abt->port_id[2] = fcport->d_id.b.domain;
+ abt->vp_index = fcport->vha->vp_idx;
+ }
+
/* Need to pass original sp */
- qla_nvme_abort_set_option(abt, sp);
+ qla_nvme_abort_set_option(abt_buf, sp);
- rval = qla2x00_issue_iocb(vha, abt, abt_dma, 0);
+ rval = qla2x00_issue_iocb(vha, abt_buf, abt_dma, 0);
if (rval != QLA_SUCCESS) {
ql_dbg(ql_dbg_mbx, vha, 0x108e,
"Failed to issue IOCB (%x).\n", rval);
@@ -3386,11 +3400,11 @@ qla24xx_abort_command(srb_t *sp)
"Failed to complete IOCB -- error status (%x).\n",
abt->entry_status);
rval = QLA_FUNCTION_FAILED;
- } else if (abt->nport_handle != cpu_to_le16(0)) {
+ } else if (abt->comp_status != cpu_to_le16(0)) {
ql_dbg(ql_dbg_mbx, vha, 0x1090,
"Failed to complete IOCB -- completion status (%x).\n",
- le16_to_cpu(abt->nport_handle));
- if (abt->nport_handle == cpu_to_le16(CS_IOCB_ERROR))
+ le16_to_cpu(abt->comp_status));
+ if (abt->comp_status == cpu_to_le16(CS_IOCB_ERROR))
rval = QLA_FUNCTION_PARAMETER_ERROR;
else
rval = QLA_FUNCTION_FAILED;
@@ -3398,12 +3412,13 @@ qla24xx_abort_command(srb_t *sp)
ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1091,
"Done %s.\n", __func__);
}
+
if (rval == QLA_SUCCESS)
- qla_nvme_abort_process_comp_status(abt, sp);
+ qla_nvme_abort_process_comp_status(abt_buf, sp);
qla_wait_nvme_release_cmd_kref(sp);
- dma_pool_free(ha->s_dma_pool, abt, abt_dma);
+ dma_pool_free(ha->s_dma_pool, abt_buf, abt_dma);
return rval;
}
diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index b2eacf0277f4..8fa980db6ce4 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -1067,36 +1067,69 @@ int qla_nvme_register_hba(struct scsi_qla_host *vha)
return ret;
}
-void qla_nvme_abort_set_option(struct abort_entry_24xx *abt, srb_t *orig_sp)
+void qla_nvme_abort_set_option(void *pkt, srb_t *orig_sp)
{
struct qla_hw_data *ha;
+ struct abort_entry_24xx *abt = pkt;
if (!(ql2xabts_wait_nvme && QLA_ABTS_WAIT_ENABLED(orig_sp)))
return;
ha = orig_sp->fcport->vha->hw;
+ /*
+ * abort_entry_24xx_ext overlays abort_entry_24xx through 'options'
+ * (offset 10), so options writes are stride-agnostic. The drv
+ * union sits at offset 56 in the 24xx layout but offset 24 in the
+ * ext layout, so the drv writes need a typed pointer.
+ */
WARN_ON_ONCE(abt->options & cpu_to_le16(BIT_0));
- /* Use Driver Specified Retry Count */
abt->options |= cpu_to_le16(AOF_ABTS_RTY_CNT);
- abt->drv.abts_rty_cnt = cpu_to_le16(2);
- /* Use specified response timeout */
abt->options |= cpu_to_le16(AOF_RSP_TIMEOUT);
- /* set it to 2 * r_a_tov in secs */
- abt->drv.rsp_timeout = cpu_to_le16(2 * (ha->r_a_tov / 10));
+ if (IS_QLA29XX(ha)) {
+ struct abort_entry_24xx_ext *abt_ext = pkt;
+
+ abt_ext->drv.abts_rty_cnt = cpu_to_le16(2);
+ abt_ext->drv.rsp_timeout =
+ cpu_to_le16(2 * (ha->r_a_tov / 10));
+ } else {
+ abt->drv.abts_rty_cnt = cpu_to_le16(2);
+ abt->drv.rsp_timeout = cpu_to_le16(2 * (ha->r_a_tov / 10));
+ }
}
-void qla_nvme_abort_process_comp_status(struct abort_entry_24xx *abt, srb_t *orig_sp)
+void qla_nvme_abort_process_comp_status(void *pkt, srb_t *orig_sp)
{
u16 comp_status;
struct scsi_qla_host *vha;
+ u8 rjt_vendor_unique, rjt_reason_expl, rjt_reason_code;
+ struct abort_entry_24xx *abt = pkt;
if (!(ql2xabts_wait_nvme && QLA_ABTS_WAIT_ENABLED(orig_sp)))
return;
vha = orig_sp->fcport->vha;
+ /*
+ * comp_status sits at offset 8 in both layouts (the
+ * nport_handle/comp_status union), so the read is
+ * stride-agnostic. The fw union, like drv, lives at offset
+ * 56 in the 24xx layout and offset 24 in the ext layout, so
+ * those byte reads still need a typed pointer.
+ */
comp_status = le16_to_cpu(abt->comp_status);
+ if (IS_QLA29XX(vha->hw)) {
+ struct abort_entry_24xx_ext *abt_ext = pkt;
+
+ rjt_vendor_unique = abt_ext->fw.ba_rjt_vendorUnique;
+ rjt_reason_expl = abt_ext->fw.ba_rjt_reasonCodeExpl;
+ rjt_reason_code = abt_ext->fw.ba_rjt_reasonCode;
+ } else {
+ rjt_vendor_unique = abt->fw.ba_rjt_vendorUnique;
+ rjt_reason_expl = abt->fw.ba_rjt_reasonCodeExpl;
+ rjt_reason_code = abt->fw.ba_rjt_reasonCode;
+ }
+
switch (comp_status) {
case CS_RESET: /* reset event aborted */
case CS_ABORTED: /* IOCB was cleaned */
@@ -1116,11 +1149,8 @@ void qla_nvme_abort_process_comp_status(struct abort_entry_24xx *abt, srb_t *ori
/* BA_RJT was received for the ABTS */
case CS_REJECT_RECEIVED:
ql_dbg(ql_dbg_async, vha, 0xf09e,
- "BA_RJT was received for the ABTS rjt_vendorUnique = %u",
- abt->fw.ba_rjt_vendorUnique);
- ql_dbg(ql_dbg_async + ql_dbg_mbx, vha, 0xf09e,
- "ba_rjt_reasonCodeExpl = %u, ba_rjt_reasonCode = %u\n",
- abt->fw.ba_rjt_reasonCodeExpl, abt->fw.ba_rjt_reasonCode);
+ "BA_RJT was received for the ABTS rjt_vendorUnique=%u, ba_rjt_reasonCodeExpl=%u, ba_rjt_reasonCode=%u\n",
+ rjt_vendor_unique, rjt_reason_expl, rjt_reason_code);
break;
case CS_COMPLETE:
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 6c951839507b..d9141d62bbb5 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8358,6 +8358,7 @@ qla2x00_module_init(void)
BUILD_BUG_ON(sizeof(ms_iocb_entry_t) != 64);
BUILD_BUG_ON(sizeof(request_t) != 64);
BUILD_BUG_ON(sizeof(struct abort_entry_24xx) != 64);
+ BUILD_BUG_ON(sizeof(struct abort_entry_24xx_ext) != 128);
BUILD_BUG_ON(sizeof(struct abort_iocb_entry_fx00) != 64);
BUILD_BUG_ON(sizeof(struct abts_entry_24xx) != 64);
BUILD_BUG_ON(sizeof(struct access_chip_84xx) != 64);
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 34/56] scsi: qla2xxx: Enhance ABTS processing for 29xx series
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (32 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 33/56] scsi: qla2xxx: Add abort command " Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 35/56] scsi: qla2xxx: Update VP control IOCB handling " Nilesh Javali
` (21 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Use extended ABTS entry structures (abts_entry_24xx_ext) for 29xx
series adapters to properly handle the larger 128-byte IOCB format.
Introduce type-generic macros (QLA_LOG_ABTS_RCV, QLA_BUILD_ABTS_BA_ACC,
QLA_LOG_ISSUE_ABTS_RSP) that leverage the shared field names between
abts_entry_24xx and abts_entry_24xx_ext to avoid code duplication.
Branch on IS_QLA29XX() for receive logging, exchange termination, and
BA_ACC response construction, with each path passing the correctly
typed pointer to the shared macros. The sof_type handling difference
(direct for 29xx bitfield vs & 0xf0 mask for legacy) is parameterized
through the sof_val macro argument.
Add BUILD_BUG_ON size check for struct abts_entry_24xx_ext.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_isr.c | 167 ++++++++++++++++++++-------------
drivers/scsi/qla2xxx/qla_os.c | 1 +
2 files changed, 104 insertions(+), 64 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index dd4416a3bf3a..774ccf6b2adc 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -72,15 +72,89 @@ static inline void display_Laser_info(scsi_qla_host_t *vha,
mb3);
}
+/*
+ * Type-generic helpers for qla24xx_process_abts().
+ * Both abts_entry_24xx and abts_entry_24xx_ext share field names for all
+ * accessed fields, so these macros expand correctly for either type.
+ */
+#define QLA_LOG_ABTS_RCV(vha, abts_ptr) do { \
+ ql_log(ql_log_warn, (vha), 0x0287, \
+ "Processing ABTS xchg=%#x oxid=%#x rxid=%#x " \
+ "seqid=%#x seqcnt=%#x\n", \
+ (abts_ptr)->rx_xch_addr_to_abort, (abts_ptr)->ox_id, \
+ (abts_ptr)->rx_id, (abts_ptr)->seq_id, \
+ (abts_ptr)->seq_cnt); \
+ ql_dbg(ql_dbg_init + ql_dbg_verbose, (vha), 0x0287, \
+ "-------- ABTS RCV -------\n"); \
+ ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, (vha), 0x0287, \
+ (uint8_t *)(abts_ptr), sizeof(*(abts_ptr))); \
+} while (0)
+
+#define QLA_BUILD_ABTS_BA_ACC(rsp, src, sof_val, fctl) do { \
+ memset((rsp), 0, sizeof(*(rsp))); \
+ (rsp)->entry_type = ABTS_RSP_TYPE; \
+ (rsp)->entry_count = 1; \
+ (rsp)->nport_handle = (src)->nport_handle; \
+ (rsp)->vp_idx = (src)->vp_idx; \
+ (rsp)->sof_type = (sof_val); \
+ (rsp)->rx_xch_addr = (src)->rx_xch_addr; \
+ (rsp)->d_id[0] = (src)->s_id[0]; \
+ (rsp)->d_id[1] = (src)->s_id[1]; \
+ (rsp)->d_id[2] = (src)->s_id[2]; \
+ (rsp)->r_ctl = FC_ROUTING_BLD | FC_R_CTL_BLD_BA_ACC; \
+ (rsp)->s_id[0] = (src)->d_id[0]; \
+ (rsp)->s_id[1] = (src)->d_id[1]; \
+ (rsp)->s_id[2] = (src)->d_id[2]; \
+ (rsp)->cs_ctl = (src)->cs_ctl; \
+ (fctl) = ~((src)->f_ctl[2] | 0x7F) << 16 | \
+ FC_F_CTL_LAST_SEQ | FC_F_CTL_END_SEQ | FC_F_CTL_SEQ_INIT; \
+ (rsp)->f_ctl[0] = (fctl) >> 0 & 0xff; \
+ (rsp)->f_ctl[1] = (fctl) >> 8 & 0xff; \
+ (rsp)->f_ctl[2] = (fctl) >> 16 & 0xff; \
+ (rsp)->type = FC_TYPE_BLD; \
+ (rsp)->rx_id = (src)->rx_id; \
+ (rsp)->ox_id = (src)->ox_id; \
+ (rsp)->payload.ba_acc.aborted_rx_id = (src)->rx_id; \
+ (rsp)->payload.ba_acc.aborted_ox_id = (src)->ox_id; \
+ (rsp)->payload.ba_acc.high_seq_cnt = cpu_to_le16(~0); \
+ (rsp)->rx_xch_addr_to_abort = (src)->rx_xch_addr_to_abort; \
+} while (0)
+
+#define QLA_LOG_ISSUE_ABTS_RSP(vha, rsp, dma, rval) do { \
+ ql_dbg(ql_dbg_init, (vha), 0x028b, \
+ "Sending BA ACC response to ABTS %#x...\n", \
+ (rsp)->rx_xch_addr_to_abort); \
+ ql_dbg(ql_dbg_init + ql_dbg_verbose, (vha), 0x028b, \
+ "-------- ELS RSP -------\n"); \
+ ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, (vha), 0x028b, \
+ (uint8_t *)(rsp), sizeof(*(rsp))); \
+ (rval) = qla2x00_issue_iocb((vha), (rsp), (dma), 0); \
+ if (rval) { \
+ ql_log(ql_log_warn, (vha), 0x028c, \
+ "%s: iocb failed to execute -> %x\n", \
+ __func__, (rval)); \
+ } else if ((rsp)->comp_status) { \
+ ql_log(ql_log_warn, (vha), 0x028d, \
+ "%s: iocb failed to complete -> " \
+ "completion=%#x subcode=(%#x,%#x)\n", \
+ __func__, (rsp)->comp_status, \
+ (rsp)->payload.error.subcode1, \
+ (rsp)->payload.error.subcode2); \
+ } else { \
+ ql_dbg(ql_dbg_init, (vha), 0x028ea, \
+ "%s: done.\n", __func__); \
+ } \
+} while (0)
+
static void
qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
{
- struct abts_entry_24xx *abts =
- (struct abts_entry_24xx *)&pkt->iocb;
struct qla_hw_data *ha = vha->hw;
+ struct abts_entry_24xx *abts = NULL;
+ struct abts_entry_24xx_ext *abts_ext = NULL;
struct els_entry_24xx *rsp_els;
- struct abts_entry_24xx *abts_rsp;
dma_addr_t dma;
+ __le32 rx_xch_addr_to_abort;
uint32_t fctl;
int rval;
void *rsp_pkt;
@@ -88,19 +162,17 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
ql_dbg(ql_dbg_init, vha, 0x0286, "%s: entered.\n", __func__);
- ql_log(ql_log_warn, vha, 0x0287,
- "Processing ABTS xchg=%#x oxid=%#x rxid=%#x seqid=%#x seqcnt=%#x\n",
- abts->rx_xch_addr_to_abort, abts->ox_id, abts->rx_id,
- abts->seq_id, abts->seq_cnt);
- ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0287,
- "-------- ABTS RCV -------\n");
- ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0287,
- (uint8_t *)abts, sizeof(*abts));
-
- if (IS_QLA29XX(ha))
+ if (IS_QLA29XX(ha)) {
+ abts_ext = (struct abts_entry_24xx_ext *)&pkt->iocb;
+ QLA_LOG_ABTS_RCV(vha, abts_ext);
rsp_sz = sizeof(struct els_entry_24xx_ext);
- else
+ rx_xch_addr_to_abort = abts_ext->rx_xch_addr_to_abort;
+ } else {
+ abts = (struct abts_entry_24xx *)&pkt->iocb;
+ QLA_LOG_ABTS_RCV(vha, abts);
rsp_sz = sizeof(struct els_entry_24xx);
+ rx_xch_addr_to_abort = abts->rx_xch_addr_to_abort;
+ }
rsp_pkt = dma_alloc_coherent(&ha->pdev->dev, rsp_sz, &dma,
GFP_KERNEL);
@@ -116,11 +188,11 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
rsp_els->entry_type = ELS_IOCB_TYPE;
rsp_els->entry_count = 1;
rsp_els->nport_handle = cpu_to_le16(~0);
- rsp_els->rx_xchg_address = abts->rx_xch_addr_to_abort;
+ rsp_els->rx_xchg_address = rx_xch_addr_to_abort;
rsp_els->control_flags = cpu_to_le16(EPD_RX_XCHG);
ql_dbg(ql_dbg_init, vha, 0x0283,
"Sending ELS Response to terminate exchange %#x...\n",
- abts->rx_xch_addr_to_abort);
+ rx_xch_addr_to_abort);
ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0283,
"-------- ELS RSP -------\n");
ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0283,
@@ -140,60 +212,27 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
}
/* send ABTS response */
- abts_rsp = rsp_pkt;
- memset(abts_rsp, 0, sizeof(*abts_rsp));
- abts_rsp->entry_type = ABTS_RSP_TYPE;
- abts_rsp->entry_count = 1;
- abts_rsp->nport_handle = abts->nport_handle;
- abts_rsp->vp_idx = abts->vp_idx;
- abts_rsp->sof_type = abts->sof_type & 0xf0;
- abts_rsp->rx_xch_addr = abts->rx_xch_addr;
- abts_rsp->d_id[0] = abts->s_id[0];
- abts_rsp->d_id[1] = abts->s_id[1];
- abts_rsp->d_id[2] = abts->s_id[2];
- abts_rsp->r_ctl = FC_ROUTING_BLD | FC_R_CTL_BLD_BA_ACC;
- abts_rsp->s_id[0] = abts->d_id[0];
- abts_rsp->s_id[1] = abts->d_id[1];
- abts_rsp->s_id[2] = abts->d_id[2];
- abts_rsp->cs_ctl = abts->cs_ctl;
- /* include flipping bit23 in fctl */
- fctl = ~(abts->f_ctl[2] | 0x7F) << 16 |
- FC_F_CTL_LAST_SEQ | FC_F_CTL_END_SEQ | FC_F_CTL_SEQ_INIT;
- abts_rsp->f_ctl[0] = fctl >> 0 & 0xff;
- abts_rsp->f_ctl[1] = fctl >> 8 & 0xff;
- abts_rsp->f_ctl[2] = fctl >> 16 & 0xff;
- abts_rsp->type = FC_TYPE_BLD;
- abts_rsp->rx_id = abts->rx_id;
- abts_rsp->ox_id = abts->ox_id;
- abts_rsp->payload.ba_acc.aborted_rx_id = abts->rx_id;
- abts_rsp->payload.ba_acc.aborted_ox_id = abts->ox_id;
- abts_rsp->payload.ba_acc.high_seq_cnt = cpu_to_le16(~0);
- abts_rsp->rx_xch_addr_to_abort = abts->rx_xch_addr_to_abort;
- ql_dbg(ql_dbg_init, vha, 0x028b,
- "Sending BA ACC response to ABTS %#x...\n",
- abts->rx_xch_addr_to_abort);
- ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x028b,
- "-------- ELS RSP -------\n");
- ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x028b,
- (uint8_t *)abts_rsp, sizeof(*abts_rsp));
- rval = qla2x00_issue_iocb(vha, abts_rsp, dma, 0);
- if (rval) {
- ql_log(ql_log_warn, vha, 0x028c,
- "%s: iocb failed to execute -> %x\n", __func__, rval);
- } else if (abts_rsp->comp_status) {
- ql_log(ql_log_warn, vha, 0x028d,
- "%s: iocb failed to complete -> completion=%#x subcode=(%#x,%#x)\n",
- __func__, abts_rsp->comp_status,
- abts_rsp->payload.error.subcode1,
- abts_rsp->payload.error.subcode2);
+ if (IS_QLA29XX(ha)) {
+ struct abts_entry_24xx_ext *rsp_ext = rsp_pkt;
+
+ QLA_BUILD_ABTS_BA_ACC(rsp_ext, abts_ext,
+ abts_ext->sof_type, fctl);
+ QLA_LOG_ISSUE_ABTS_RSP(vha, rsp_ext, dma, rval);
} else {
- ql_dbg(ql_dbg_init, vha, 0x028ea,
- "%s: done.\n", __func__);
+ struct abts_entry_24xx *abts_rsp = rsp_pkt;
+
+ QLA_BUILD_ABTS_BA_ACC(abts_rsp, abts,
+ abts->sof_type & 0xf0, fctl);
+ QLA_LOG_ISSUE_ABTS_RSP(vha, abts_rsp, dma, rval);
}
dma_free_coherent(&ha->pdev->dev, rsp_sz, rsp_pkt, dma);
}
+#undef QLA_LOG_ABTS_RCV
+#undef QLA_BUILD_ABTS_BA_ACC
+#undef QLA_LOG_ISSUE_ABTS_RSP
+
/**
* __qla_consume_iocb - this routine is used to tell fw driver has processed
* or consumed the head IOCB along with the continuation IOCB's from the
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index d9141d62bbb5..5f11c46821c1 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8361,6 +8361,7 @@ qla2x00_module_init(void)
BUILD_BUG_ON(sizeof(struct abort_entry_24xx_ext) != 128);
BUILD_BUG_ON(sizeof(struct abort_iocb_entry_fx00) != 64);
BUILD_BUG_ON(sizeof(struct abts_entry_24xx) != 64);
+ BUILD_BUG_ON(sizeof(struct abts_entry_24xx_ext) != 128);
BUILD_BUG_ON(sizeof(struct access_chip_84xx) != 64);
BUILD_BUG_ON(sizeof(struct access_chip_rsp_84xx) != 64);
BUILD_BUG_ON(sizeof(struct cmd_bidir) != 64);
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 35/56] scsi: qla2xxx: Update VP control IOCB handling for 29xx series
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (33 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 34/56] scsi: qla2xxx: Enhance ABTS processing " Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 36/56] scsi: qla2xxx: Add build-time size check for VP config IOCB layout Nilesh Javali
` (20 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Update VP control IOCB command and response handling to support the
29xx series adapters, which use the 128-byte vp_ctrl_entry_24xx_ext
layout.
Change the qla25xx_ctrlvp_iocb() and qla_ctrlvp_completed() function
signatures from typed struct pointers to void *, since callers already
pass a generic ring-slot pointer. Both the standard 64-byte
vp_ctrl_entry_24xx and the 128-byte vp_ctrl_entry_24xx_ext are
layout-identical for every field touched in these helpers (entry_type,
handle, entry_count, command, vp_count, vp_idx_map, entry_status,
comp_status, vp_idx_failed), so a single struct vp_ctrl_entry_24xx *
view handles both adapter families without an IS_QLA29XX() branch.
Add a BUILD_BUG_ON size check for the extended structure.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_iocb.c | 23 ++++++++++++++++-------
drivers/scsi/qla2xxx/qla_isr.c | 13 +++++++++----
drivers/scsi/qla2xxx/qla_os.c | 1 +
3 files changed, 26 insertions(+), 11 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 54c8d2feb4ae..25e3db0b8137 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -4073,22 +4073,31 @@ qla_nvme_ls(srb_t *sp, struct pt_ls4_request *cmd_pkt)
}
static void
-qla25xx_ctrlvp_iocb(srb_t *sp, struct vp_ctrl_entry_24xx *vce)
+qla25xx_ctrlvp_iocb(srb_t *sp, void *pkt)
{
+ /*
+ * vp_ctrl_entry_24xx_ext is layout-identical to vp_ctrl_entry_24xx
+ * for all fields touched here (entry_type, handle, entry_count,
+ * command, vp_count, vp_idx_map) -- they all sit at the same
+ * offsets and types in both structs, and the ext layout merely
+ * tacks on flags/id/hopct/reserved at offset 32+. So no
+ * IS_QLA29XX(ha) dispatch is needed on the issue path.
+ */
+ struct vp_ctrl_entry_24xx *vce = pkt;
int map, pos;
- vce->entry_type = VP_CTRL_IOCB_TYPE;
- vce->handle = sp->handle;
- vce->entry_count = 1;
- vce->command = cpu_to_le16(sp->u.iocb_cmd.u.ctrlvp.cmd);
- vce->vp_count = cpu_to_le16(1);
-
/*
* index map in firmware starts with 1; decrement index
* this is ok as we never use index 0
*/
map = (sp->u.iocb_cmd.u.ctrlvp.vp_index - 1) / 8;
pos = (sp->u.iocb_cmd.u.ctrlvp.vp_index - 1) & 7;
+
+ vce->entry_type = VP_CTRL_IOCB_TYPE;
+ vce->handle = sp->handle;
+ vce->entry_count = 1;
+ vce->command = cpu_to_le16(sp->u.iocb_cmd.u.ctrlvp.cmd);
+ vce->vp_count = cpu_to_le16(1);
vce->vp_idx_map[map] |= 1 << pos;
}
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 774ccf6b2adc..7d2b6d135dc8 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -3000,13 +3000,19 @@ static void qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
}
static void qla_ctrlvp_completed(scsi_qla_host_t *vha, struct req_que *req,
- struct vp_ctrl_entry_24xx *vce)
+ void *pkt)
{
const char func[] = "CTRLVP-IOCB";
+ /*
+ * vp_ctrl_entry_24xx_ext overlays vp_ctrl_entry_24xx for all
+ * fields read here (entry_status, comp_status, vp_idx_failed),
+ * so the read goes through one struct vp_ctrl_entry_24xx * view.
+ */
+ struct vp_ctrl_entry_24xx *vce = pkt;
srb_t *sp;
int rval = QLA_SUCCESS;
- sp = qla2x00_get_sp_from_handle(vha, func, req, vce);
+ sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
if (!sp)
return;
@@ -4227,8 +4233,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
(struct mbx_24xx_entry *)pkt);
break;
case VP_CTRL_IOCB_TYPE:
- qla_ctrlvp_completed(vha, rsp->req,
- (struct vp_ctrl_entry_24xx *)pkt);
+ qla_ctrlvp_completed(vha, rsp->req, pkt);
break;
case PUREX_IOCB_TYPE:
if (IS_QLA29XX(ha)) {
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 5f11c46821c1..ad070c0bf7db 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8437,6 +8437,7 @@ qla2x00_module_init(void)
BUILD_BUG_ON(sizeof(struct vf_evfp_entry_24xx) != 56);
BUILD_BUG_ON(sizeof(struct vp_config_entry_24xx) != 64);
BUILD_BUG_ON(sizeof(struct vp_ctrl_entry_24xx) != 64);
+ BUILD_BUG_ON(sizeof(struct vp_ctrl_entry_24xx_ext) != 128);
BUILD_BUG_ON(sizeof(struct vp_rpt_id_entry_24xx) != 64);
BUILD_BUG_ON(sizeof(sts21_entry_t) != 64);
BUILD_BUG_ON(sizeof(sts22_entry_t) != 64);
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 36/56] scsi: qla2xxx: Add build-time size check for VP config IOCB layout
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (34 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 35/56] scsi: qla2xxx: Update VP control IOCB handling " Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 37/56] scsi: qla2xxx: Add size check for extended VP report ID entry Nilesh Javali
` (19 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Add a BUILD_BUG_ON for struct vp_config_entry_24xx_ext to verify its
128-byte size at compile time alongside the existing 64-byte check for
struct vp_config_entry_24xx.
Document in qla24xx_modify_vp_config() that the ext variant overlays
the base 24xx layout for the first 64 bytes (all fields this helper
reads and writes), so the IOCB can be built through a single struct
vp_config_entry_24xx pointer regardless of the adapter's IOCB stride.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_mbx.c | 8 +++++++-
drivers/scsi/qla2xxx/qla_os.c | 1 +
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 0d7aa6fce007..3ebda35dd584 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -4364,6 +4364,13 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
return QLA_MEMORY_ALLOC_FAILED;
}
+ /*
+ * vp_config_entry_24xx_ext overlays vp_config_entry_24xx for the
+ * full 64-byte 24xx layout (the ext variant merely appends fields
+ * at offset 64+ which this helper never touches), so the IOCB is
+ * built and inspected through a single struct vp_config_entry_24xx
+ * pointer regardless of adapter stride.
+ */
vpmod->entry_type = VP_CONFIG_IOCB_TYPE;
vpmod->entry_count = 1;
vpmod->command = VCT_COMMAND_MOD_ENABLE_VPS;
@@ -4392,7 +4399,6 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
le16_to_cpu(vpmod->comp_status));
rval = QLA_FUNCTION_FAILED;
} else {
- /* EMPTY */
ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c0,
"Done %s.\n", __func__);
fc_vport_set_state(vha->fc_vport, FC_VPORT_INITIALIZING);
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index ad070c0bf7db..8e5d49abead4 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8436,6 +8436,7 @@ qla2x00_module_init(void)
BUILD_BUG_ON(sizeof(struct verify_chip_rsp_84xx) != 52);
BUILD_BUG_ON(sizeof(struct vf_evfp_entry_24xx) != 56);
BUILD_BUG_ON(sizeof(struct vp_config_entry_24xx) != 64);
+ BUILD_BUG_ON(sizeof(struct vp_config_entry_24xx_ext) != 128);
BUILD_BUG_ON(sizeof(struct vp_ctrl_entry_24xx) != 64);
BUILD_BUG_ON(sizeof(struct vp_ctrl_entry_24xx_ext) != 128);
BUILD_BUG_ON(sizeof(struct vp_rpt_id_entry_24xx) != 64);
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 37/56] scsi: qla2xxx: Add size check for extended VP report ID entry
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (35 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 36/56] scsi: qla2xxx: Add build-time size check for VP config IOCB layout Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 15:40 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 38/56] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series Nilesh Javali
` (18 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Add reserved_end[64] padding to bring the struct to 128 bytes, matching
the hardware IOCB stride. Change qla24xx_report_id_acquisition() to
accept a void pointer and extract vp_idx and vp_status from the extended
structure on 29xx series adapters, maintaining data integrity for the
larger IOCB format.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_fw29.h | 1 +
drivers/scsi/qla2xxx/qla_gbl.h | 3 +--
drivers/scsi/qla2xxx/qla_isr.c | 3 +--
drivers/scsi/qla2xxx/qla_mbx.c | 32 +++++++++++++++++++++-----------
drivers/scsi/qla2xxx/qla_os.c | 1 +
5 files changed, 25 insertions(+), 15 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_fw29.h b/drivers/scsi/qla2xxx/qla_fw29.h
index 088a220472a5..a4aa5bacb171 100644
--- a/drivers/scsi/qla2xxx/qla_fw29.h
+++ b/drivers/scsi/qla2xxx/qla_fw29.h
@@ -693,6 +693,7 @@ struct vp_rpt_id_entry_24xx_ext {
uint8_t remote_nport_id[4];
} f2;
} u;
+ uint8_t reserved_end[64];
};
/*
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 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 7d2b6d135dc8..c6e2323518f7 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -4174,8 +4174,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
qla_sts_cont_data_size(rsp->hw));
break;
case VP_RPT_ID_IOCB_TYPE:
- qla24xx_report_id_acquisition(vha,
- (struct vp_rpt_id_entry_24xx *)pkt);
+ qla24xx_report_id_acquisition(vha, pkt);
break;
case LOGINOUT_PORT_IOCB_TYPE:
qla24xx_logio_entry(vha, rsp->req, pkt);
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 3ebda35dd584..cec308811d9e 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -4086,15 +4086,18 @@ qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
}
void
-qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
- struct vp_rpt_id_entry_24xx *rptid_entry)
+qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
{
struct qla_hw_data *ha = vha->hw;
+ struct vp_rpt_id_entry_24xx *rptid_entry = pkt;
+ struct vp_rpt_id_entry_24xx_ext *rptid_entry_ext = pkt;
scsi_qla_host_t *vp = NULL;
unsigned long flags;
int found;
port_id_t id;
struct fc_port *fcport;
+ u16 vp_idx;
+ u8 vp_status;
ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b6,
"Entered %s.\n", __func__);
@@ -4102,6 +4105,14 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
if (rptid_entry->entry_status != 0)
return;
+ if (IS_QLA29XX(ha)) {
+ vp_idx = rptid_entry_ext->vp_idx;
+ vp_status = rptid_entry_ext->vp_status;
+ } else {
+ vp_idx = rptid_entry->vp_idx;
+ vp_status = rptid_entry->vp_status;
+ }
+
id.b.domain = rptid_entry->port_id[2];
id.b.area = rptid_entry->port_id[1];
id.b.al_pa = rptid_entry->port_id[0];
@@ -4124,9 +4135,8 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
} else if (rptid_entry->format == 1) {
/* fabric */
ql_dbg(ql_dbg_async, vha, 0x10b9,
- "Format 1: VP[%d] enabled - status %d - with "
- "port id %02x%02x%02x.\n", rptid_entry->vp_idx,
- rptid_entry->vp_status,
+ "Format 1: VP[%d] enabled - status %d - with port id %02x%02x%02x.\n",
+ vp_idx, vp_status,
rptid_entry->port_id[2], rptid_entry->port_id[1],
rptid_entry->port_id[0]);
ql_dbg(ql_dbg_async, vha, 0x5075,
@@ -4223,8 +4233,8 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
/* buffer to buffer credit flag */
vha->flags.bbcr_enable = (rptid_entry->u.f1.bbcr & 0xf) != 0;
- if (rptid_entry->vp_idx == 0) {
- if (rptid_entry->vp_status == VP_STAT_COMPL) {
+ if (vp_idx == 0) {
+ if (vp_status == VP_STAT_COMPL) {
/* FA-WWN is only for physical port */
if (qla_ini_mode_enabled(vha) &&
ha->flags.fawwpn_enabled &&
@@ -4241,18 +4251,18 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
} else {
- if (rptid_entry->vp_status != VP_STAT_COMPL &&
- rptid_entry->vp_status != VP_STAT_ID_CHG) {
+ if (vp_status != VP_STAT_COMPL &&
+ vp_status != VP_STAT_ID_CHG) {
ql_dbg(ql_dbg_mbx, vha, 0x10ba,
"Could not acquire ID for VP[%d].\n",
- rptid_entry->vp_idx);
+ vp_idx);
return;
}
found = 0;
spin_lock_irqsave(&ha->vport_slock, flags);
list_for_each_entry(vp, &ha->vp_list, list) {
- if (rptid_entry->vp_idx == vp->vp_idx) {
+ if (vp_idx == vp->vp_idx) {
found = 1;
break;
}
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 8e5d49abead4..f539190dd504 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8440,6 +8440,7 @@ qla2x00_module_init(void)
BUILD_BUG_ON(sizeof(struct vp_ctrl_entry_24xx) != 64);
BUILD_BUG_ON(sizeof(struct vp_ctrl_entry_24xx_ext) != 128);
BUILD_BUG_ON(sizeof(struct vp_rpt_id_entry_24xx) != 64);
+ BUILD_BUG_ON(sizeof(struct vp_rpt_id_entry_24xx_ext) != 128);
BUILD_BUG_ON(sizeof(sts21_entry_t) != 64);
BUILD_BUG_ON(sizeof(sts22_entry_t) != 64);
BUILD_BUG_ON(sizeof(sts_cont_entry_t) != 64);
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 38/56] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (36 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 37/56] scsi: qla2xxx: Add size check for extended VP report ID entry Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 15:40 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 39/56] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support Nilesh Javali
` (17 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
From: Manish Rangankar <mrangankar@marvell.com>
Extend the LS4 pass-through IOCB handling to support the 128-byte
pt_ls4_request_ext layout used by 29xx series adapters. The extension
grows inline DSD capacity from 2 to 5 entries. Function signatures are
widened to void * so both layouts can be passed without casts.
pt_ls4_request_ext overlays pt_ls4_request through exchange_address
(offsets 0-27 are byte-identical), so common-header writes go through a
single struct pt_ls4_request * view; only the divergent fields
(vp_index width, tx_/rx_byte_count offset, dsd[] base) are branched.
Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_fw29.h | 37 +++++++++++++++++
drivers/scsi/qla2xxx/qla_iocb.c | 73 ++++++++++++++++++++++-----------
drivers/scsi/qla2xxx/qla_isr.c | 9 ++--
drivers/scsi/qla2xxx/qla_nvme.c | 64 +++++++++++++++++++----------
drivers/scsi/qla2xxx/qla_nvme.h | 4 +-
drivers/scsi/qla2xxx/qla_os.c | 1 +
6 files changed, 137 insertions(+), 51 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_fw29.h b/drivers/scsi/qla2xxx/qla_fw29.h
index a4aa5bacb171..600a40d8bd5f 100644
--- a/drivers/scsi/qla2xxx/qla_fw29.h
+++ b/drivers/scsi/qla2xxx/qla_fw29.h
@@ -295,6 +295,43 @@ struct ct_entry_24xx_ext {
struct dsd64 dsd[NUM_CT_DSDS]; /* Data Segment Descriptors */
};
+/*
+ * 29xx extended Link Service pass-through request IOCB (128 bytes).
+ *
+ * Same wire purpose as the 64-byte struct pt_ls4_request used on 24xx-class
+ * adapters, but laid out for the 128-byte 29xx request ring:
+ * - vp_index widened to __le16 (bits [8:0] meaningful, see
+ * CMD_EXT_VP_INDEX_MASK).
+ * - reserved area expanded to 32 bytes between exchange_address and
+ * rx_byte_count.
+ * - inline DSD capacity grown from 2 to 5.
+ * Header through 'tx_dseg_count' (offset 14) and the control_flags /
+ * exchange_address fields keep the same offsets as struct pt_ls4_request,
+ * so common code can populate them via either type once IS_QLA29XX(ha) is
+ * branched for the layout-divergent fields.
+ */
+#define NUM_PT_LS4_EXT_DSDS 5
+struct pt_ls4_request_ext {
+ uint8_t entry_type;
+ uint8_t entry_count;
+ uint8_t sys_define;
+ uint8_t entry_status;
+ uint32_t handle;
+ __le16 status;
+ __le16 nport_handle;
+ __le16 tx_dseg_count;
+ __le16 vp_index; /* VP Index 9 bits; see CMD_EXT_VP_INDEX_MASK */
+ __le16 timeout;
+ __le16 control_flags; /* CF_LS4_* (see struct pt_ls4_request) */
+ __le16 rx_dseg_count;
+ __le16 rsvd2;
+ __le32 exchange_address;
+ uint8_t rsvd3[32];
+ __le32 rx_byte_count;
+ __le32 tx_byte_count;
+ struct dsd64 dsd[NUM_PT_LS4_EXT_DSDS];
+};
+
/*
* ISP queue - PUREX IOCB entry structure definition
*/
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 25e3db0b8137..6057d7da507e 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -4038,38 +4038,65 @@ static void qla2x00_send_notify_ack_iocb(srb_t *sp,
}
/*
- * Build NVME LS request
+ * Build NVME LS request.
+ *
+ * pt_ls4_request_ext overlays pt_ls4_request through exchange_address
+ * (offsets 0-27 are byte-identical), so the common-header writes go
+ * through one struct pt_ls4_request * view. The ext layout has a wider
+ * __le16 vp_index and places rx_/tx_byte_count and dsd[] at different
+ * offsets, so those assignments diverge per stride.
*/
static void
-qla_nvme_ls(srb_t *sp, struct pt_ls4_request *cmd_pkt)
+qla_nvme_ls(srb_t *sp, void *cmd_pkt)
{
- struct srb_iocb *nvme;
+ struct srb_iocb *nvme = &sp->u.iocb_cmd;
+ struct qla_hw_data *ha = sp->vha->hw;
+ struct pt_ls4_request *pkt = cmd_pkt;
- nvme = &sp->u.iocb_cmd;
- cmd_pkt->entry_type = PT_LS4_REQUEST;
- cmd_pkt->entry_count = 1;
- cmd_pkt->timeout = cpu_to_le16(nvme->u.nvme.timeout_sec);
- cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
+ pkt->entry_type = PT_LS4_REQUEST;
+ pkt->entry_count = 1;
+ pkt->timeout = cpu_to_le16(nvme->u.nvme.timeout_sec);
+ pkt->tx_dseg_count = cpu_to_le16(1);
if (sp->unsol_rsp) {
- cmd_pkt->control_flags =
- cpu_to_le16(CF_LS4_RESPONDER << CF_LS4_SHIFT);
- cmd_pkt->nport_handle = nvme->u.nvme.nport_handle;
- cmd_pkt->exchange_address = nvme->u.nvme.exchange_address;
+ pkt->control_flags =
+ cpu_to_le16(CF_LS4_RESPONDER << CF_LS4_SHIFT);
+ pkt->nport_handle = nvme->u.nvme.nport_handle;
+ pkt->exchange_address = nvme->u.nvme.exchange_address;
} else {
- cmd_pkt->control_flags =
- cpu_to_le16(CF_LS4_ORIGINATOR << CF_LS4_SHIFT);
- cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
- cmd_pkt->rx_dseg_count = cpu_to_le16(1);
- cmd_pkt->rx_byte_count = nvme->u.nvme.rsp_len;
- cmd_pkt->dsd[1].length = nvme->u.nvme.rsp_len;
- put_unaligned_le64(nvme->u.nvme.rsp_dma, &cmd_pkt->dsd[1].address);
+ pkt->control_flags =
+ cpu_to_le16(CF_LS4_ORIGINATOR << CF_LS4_SHIFT);
+ pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ pkt->rx_dseg_count = cpu_to_le16(1);
}
- cmd_pkt->tx_dseg_count = cpu_to_le16(1);
- cmd_pkt->tx_byte_count = nvme->u.nvme.cmd_len;
- cmd_pkt->dsd[0].length = nvme->u.nvme.cmd_len;
- put_unaligned_le64(nvme->u.nvme.cmd_dma, &cmd_pkt->dsd[0].address);
+ if (IS_QLA29XX(ha)) {
+ struct pt_ls4_request_ext *ext = cmd_pkt;
+
+ ext->vp_index = cpu_to_le16(sp->fcport->vha->vp_idx);
+ ext->tx_byte_count = nvme->u.nvme.cmd_len;
+ ext->dsd[0].length = nvme->u.nvme.cmd_len;
+ put_unaligned_le64(nvme->u.nvme.cmd_dma,
+ &ext->dsd[0].address);
+ if (!sp->unsol_rsp) {
+ ext->rx_byte_count = nvme->u.nvme.rsp_len;
+ ext->dsd[1].length = nvme->u.nvme.rsp_len;
+ put_unaligned_le64(nvme->u.nvme.rsp_dma,
+ &ext->dsd[1].address);
+ }
+ } else {
+ pkt->vp_index = sp->fcport->vha->vp_idx;
+ pkt->tx_byte_count = nvme->u.nvme.cmd_len;
+ pkt->dsd[0].length = nvme->u.nvme.cmd_len;
+ put_unaligned_le64(nvme->u.nvme.cmd_dma,
+ &pkt->dsd[0].address);
+ if (!sp->unsol_rsp) {
+ pkt->rx_byte_count = nvme->u.nvme.rsp_len;
+ pkt->dsd[1].length = nvme->u.nvme.rsp_len;
+ put_unaligned_le64(nvme->u.nvme.rsp_dma,
+ &pkt->dsd[1].address);
+ }
+ }
}
static void
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index c6e2323518f7..3a9237376050 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -4034,7 +4034,7 @@ qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
}
void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha,
- struct pt_ls4_request *pkt, struct req_que *req)
+ void *pkt, struct req_que *req)
{
srb_t *sp;
const char func[] = "LS4_IOCB";
@@ -4044,7 +4044,9 @@ void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha,
if (!sp)
return;
- comp_status = le16_to_cpu(pkt->status);
+ /* status lives at the same offset (8) in both IOCB strides */
+ comp_status = le16_to_cpu(((struct pt_ls4_request *)pkt)->status);
+
sp->done(sp, comp_status);
}
@@ -4210,8 +4212,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
qlt_response_pkt_all_vps(vha, rsp, (response_t *)pkt);
break;
case PT_LS4_REQUEST:
- qla24xx_nvme_ls4_iocb(vha, (struct pt_ls4_request *)pkt,
- rsp->req);
+ qla24xx_nvme_ls4_iocb(vha, pkt, rsp->req);
break;
case NOTIFY_ACK_TYPE:
if (((response_t *)pkt)->handle == QLA_TGT_SKIP_HANDLE)
diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index 8fa980db6ce4..0038b6274d44 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -1197,37 +1197,57 @@ static void qla_nvme_fc_format_rjt(void *buf, u8 ls_cmd, u8 reason,
rjt->rjt.vendor = vendor;
}
+/*
+ * pt_ls4_request_ext overlays pt_ls4_request through exchange_address
+ * (offsets 0-27 are byte-identical), so the common-header writes go
+ * through one struct pt_ls4_request * view. The ext layout has a wider
+ * __le16 vp_index and places tx_/rx_byte_count and dsd[] at different
+ * offsets, so those assignments diverge per stride.
+ */
static void qla_nvme_lsrjt_pt_iocb(struct scsi_qla_host *vha,
- struct pt_ls4_request *lsrjt_iocb,
+ void *lsrjt_iocb,
struct qla_nvme_lsrjt_pt_arg *a)
{
- lsrjt_iocb->entry_type = PT_LS4_REQUEST;
- lsrjt_iocb->entry_count = 1;
- lsrjt_iocb->sys_define = 0;
- lsrjt_iocb->entry_status = 0;
- lsrjt_iocb->handle = QLA_SKIP_HANDLE;
- lsrjt_iocb->nport_handle = a->nport_handle;
- lsrjt_iocb->exchange_address = a->xchg_address;
- lsrjt_iocb->vp_index = a->vp_idx;
-
- lsrjt_iocb->control_flags = cpu_to_le16(a->control_flags);
-
- put_unaligned_le64(a->tx_addr, &lsrjt_iocb->dsd[0].address);
- lsrjt_iocb->dsd[0].length = cpu_to_le32(a->tx_byte_count);
- lsrjt_iocb->tx_dseg_count = cpu_to_le16(1);
- lsrjt_iocb->tx_byte_count = cpu_to_le32(a->tx_byte_count);
-
- put_unaligned_le64(a->rx_addr, &lsrjt_iocb->dsd[1].address);
- lsrjt_iocb->dsd[1].length = 0;
- lsrjt_iocb->rx_dseg_count = 0;
- lsrjt_iocb->rx_byte_count = 0;
+ struct qla_hw_data *ha = vha->hw;
+ struct pt_ls4_request *pkt = lsrjt_iocb;
+
+ pkt->entry_type = PT_LS4_REQUEST;
+ pkt->entry_count = 1;
+ pkt->sys_define = 0;
+ pkt->entry_status = 0;
+ pkt->handle = QLA_SKIP_HANDLE;
+ pkt->nport_handle = a->nport_handle;
+ pkt->exchange_address = a->xchg_address;
+ pkt->control_flags = cpu_to_le16(a->control_flags);
+ pkt->tx_dseg_count = cpu_to_le16(1);
+ pkt->rx_dseg_count = 0;
+
+ if (IS_QLA29XX(ha)) {
+ struct pt_ls4_request_ext *ext = lsrjt_iocb;
+
+ ext->vp_index = cpu_to_le16(a->vp_idx);
+ ext->tx_byte_count = cpu_to_le32(a->tx_byte_count);
+ ext->rx_byte_count = 0;
+ put_unaligned_le64(a->tx_addr, &ext->dsd[0].address);
+ ext->dsd[0].length = cpu_to_le32(a->tx_byte_count);
+ put_unaligned_le64(a->rx_addr, &ext->dsd[1].address);
+ ext->dsd[1].length = 0;
+ } else {
+ pkt->vp_index = a->vp_idx;
+ pkt->tx_byte_count = cpu_to_le32(a->tx_byte_count);
+ pkt->rx_byte_count = 0;
+ put_unaligned_le64(a->tx_addr, &pkt->dsd[0].address);
+ pkt->dsd[0].length = cpu_to_le32(a->tx_byte_count);
+ put_unaligned_le64(a->rx_addr, &pkt->dsd[1].address);
+ pkt->dsd[1].length = 0;
+ }
}
static int
qla_nvme_ls_reject_iocb(struct scsi_qla_host *vha, struct qla_qpair *qp,
struct qla_nvme_lsrjt_pt_arg *a, bool is_xchg_terminate)
{
- struct pt_ls4_request *lsrjt_iocb;
+ void *lsrjt_iocb;
lsrjt_iocb = __qla2x00_alloc_iocbs(qp, NULL);
if (!lsrjt_iocb) {
diff --git a/drivers/scsi/qla2xxx/qla_nvme.h b/drivers/scsi/qla2xxx/qla_nvme.h
index a253ac55171b..e6a41d53cbe1 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.h
+++ b/drivers/scsi/qla2xxx/qla_nvme.h
@@ -144,7 +144,7 @@ struct pt_ls4_rx_unsol {
int qla_nvme_register_hba(struct scsi_qla_host *);
int qla_nvme_register_remote(struct scsi_qla_host *, struct fc_port *);
void qla_nvme_delete(struct scsi_qla_host *);
-void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *, struct pt_ls4_request *,
- struct req_que *);
+void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha, void *pkt,
+ struct req_que *req);
void qla24xx_async_gffid_sp_done(struct srb *sp, int);
#endif
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index f539190dd504..186c6c7a3944 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -8402,6 +8402,7 @@ qla2x00_module_init(void)
BUILD_BUG_ON(sizeof(struct nvram_24xx) != 512);
BUILD_BUG_ON(sizeof(struct nvram_81xx) != 512);
BUILD_BUG_ON(sizeof(struct pt_ls4_request) != 64);
+ BUILD_BUG_ON(sizeof(struct pt_ls4_request_ext) != 128);
BUILD_BUG_ON(sizeof(struct pt_ls4_rx_unsol) != 64);
BUILD_BUG_ON(sizeof(struct purex_entry_24xx) != 64);
BUILD_BUG_ON(sizeof(struct purex_entry_24xx_ext) != 128);
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 39/56] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (37 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 38/56] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 15:52 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 40/56] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking Nilesh Javali
` (16 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
From: Manish Rangankar <mrangankar@marvell.com>
Extend qla2xxx BSG command handling to recognize QLA29xx adapters and
align feature availability with hardware capabilities.
Allow QLA29xx in paths previously restricted to QLA27xx/28xx:
- Flash update capability queries (get/set)
- BBCR data retrieval
- D-port diagnostics
- MPI and PEP version sysfs attributes
Restrict unsupported operations on QLA29xx:
- Reject flash image status query (no active image tracking)
- Block qla28xx_validate_flash_image()
Guard the qla27xx_get_active_image() call with an explicit IS_QLA27XX
|| IS_QLA28XX check so it is not reached from adapters that lack the
legacy active-image layout.
Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_attr.c | 4 ++--
drivers/scsi/qla2xxx/qla_bsg.c | 19 ++++++++++++-------
drivers/scsi/qla2xxx/qla_def.h | 2 +-
drivers/scsi/qla2xxx/qla_mbx.c | 2 +-
4 files changed, 16 insertions(+), 11 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index c32930f9e9f9..19aa66b8ca52 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1542,7 +1542,7 @@ qla2x00_mpi_version_show(struct device *dev, struct device_attribute *attr,
struct qla_hw_data *ha = vha->hw;
if (!IS_QLA81XX(ha) && !IS_QLA8031(ha) && !IS_QLA8044(ha) &&
- !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+ !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
return scnprintf(buf, PAGE_SIZE, "\n");
return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d (%x)\n",
@@ -1770,7 +1770,7 @@ qla2x00_pep_version_show(struct device *dev, struct device_attribute *attr,
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
struct qla_hw_data *ha = vha->hw;
- if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+ if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
return scnprintf(buf, PAGE_SIZE, "\n");
return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d\n",
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 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] 87+ messages in thread
* [PATCH v4 40/56] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (38 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 39/56] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 16:02 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 41/56] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors Nilesh Javali
` (15 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
qla25xx_free_req_que() and qla25xx_free_rsp_que() have two pre-existing
bugs exposed on the error path of qla25xx_create_{req,rsp}_que():
1. When dma_alloc_coherent() fails during queue creation, the error path
calls the free function with req->ring / rsp->ring still NULL (from
kzalloc). The unconditional dma_free_coherent() with a NULL cpu_addr
is undefined behavior and can panic.
2. The free functions clear req_qid_map / rsp_qid_map under vport_lock,
but the create functions protect the same bitmaps with mq_lock. This
provides no mutual exclusion. Additionally, the create error path
clears the bit and releases mq_lock before calling the free function,
creating a window where another thread can allocate the same que_id
and have its ha->req_q_map entry clobbered by the subsequent lockless
NULL assignment in the free function.
Fix by:
- Guarding dma_free_coherent() with a NULL check on the ring pointer.
- Using mq_lock (the lock held by all creators) in the free functions
to atomically NULL the map entry and clear the bitmap bit.
- Removing the now-redundant clear_bit blocks from the create error
paths since the free functions handle it atomically.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_mid.c | 28 ++++++++++++----------------
1 file changed, 12 insertions(+), 16 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index bb2521c052bf..7072af5b4217 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -576,16 +576,17 @@ qla25xx_free_req_que(struct scsi_qla_host *vha, struct req_que *req)
uint16_t que_id = req->id;
size_t req_entry_size = qla_req_entry_size(ha);
- dma_free_coherent(&ha->pdev->dev,
- (req->length + 1) * req_entry_size,
- req->ring, req->dma);
+ if (req->ring)
+ dma_free_coherent(&ha->pdev->dev,
+ (req->length + 1) * req_entry_size,
+ req->ring, req->dma);
req->ring = NULL;
req->dma = 0;
if (que_id) {
+ mutex_lock(&ha->mq_lock);
ha->req_q_map[que_id] = NULL;
- mutex_lock(&ha->vport_lock);
clear_bit(que_id, ha->req_qid_map);
- mutex_unlock(&ha->vport_lock);
+ mutex_unlock(&ha->mq_lock);
}
kfree(req->outstanding_cmds);
kfree(req);
@@ -605,16 +606,17 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
rsp->msix->handle = NULL;
}
- dma_free_coherent(&ha->pdev->dev,
- (rsp->length + 1) * rsp_entry_size,
- rsp->ring, rsp->dma);
+ if (rsp->ring)
+ dma_free_coherent(&ha->pdev->dev,
+ (rsp->length + 1) * rsp_entry_size,
+ rsp->ring, rsp->dma);
rsp->ring = NULL;
rsp->dma = 0;
if (que_id) {
+ mutex_lock(&ha->mq_lock);
ha->rsp_q_map[que_id] = NULL;
- mutex_lock(&ha->vport_lock);
clear_bit(que_id, ha->rsp_qid_map);
- mutex_unlock(&ha->vport_lock);
+ mutex_unlock(&ha->mq_lock);
}
kfree(rsp);
}
@@ -820,9 +822,6 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
if (ret != QLA_SUCCESS) {
ql_log(ql_log_fatal, base_vha, 0x00df,
"%s failed.\n", __func__);
- mutex_lock(&ha->mq_lock);
- clear_bit(que_id, ha->req_qid_map);
- mutex_unlock(&ha->mq_lock);
goto que_failed;
}
vha->flags.qpairs_req_created = 1;
@@ -942,9 +941,6 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
if (ret != QLA_SUCCESS) {
ql_log(ql_log_fatal, base_vha, 0x00e7,
"%s failed.\n", __func__);
- mutex_lock(&ha->mq_lock);
- clear_bit(que_id, ha->rsp_qid_map);
- mutex_unlock(&ha->mq_lock);
goto que_failed;
}
vha->flags.qpairs_rsp_created = 1;
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 41/56] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (39 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 40/56] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 16:24 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 42/56] scsi: qla2xxx: Fix endianness annotations in vp_rpt_id_entry structures Nilesh Javali
` (14 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
C bitfield packing order is implementation-defined: GCC packs LSB-first
on little-endian targets and MSB-first on big-endian targets. The
__le16 bitfield declarations for vp_index/sof_type in the 29xx extended
IOCB structures produce incorrect bit positions on big-endian hosts,
and Sparse cannot enforce endianness checks on bitfield members.
Replace the three sets of __le16 bitfields (in els_entry_24xx_ext,
els_sts_entry_24xx_ext, and abts_entry_24xx_ext) with a single __le16
scalar field and provide inline accessor functions that use proper
le16_to_cpu()/cpu_to_le16() with shift-and-mask operations.
Fixes: 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 3a9237376050..1c549ee313c4 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -90,13 +90,11 @@ static inline void display_Laser_info(scsi_qla_host_t *vha,
(uint8_t *)(abts_ptr), sizeof(*(abts_ptr))); \
} while (0)
-#define QLA_BUILD_ABTS_BA_ACC(rsp, src, sof_val, fctl) do { \
+#define QLA_BUILD_ABTS_BA_ACC(rsp, src, fctl) do { \
memset((rsp), 0, sizeof(*(rsp))); \
(rsp)->entry_type = ABTS_RSP_TYPE; \
(rsp)->entry_count = 1; \
(rsp)->nport_handle = (src)->nport_handle; \
- (rsp)->vp_idx = (src)->vp_idx; \
- (rsp)->sof_type = (sof_val); \
(rsp)->rx_xch_addr = (src)->rx_xch_addr; \
(rsp)->d_id[0] = (src)->s_id[0]; \
(rsp)->d_id[1] = (src)->s_id[1]; \
@@ -215,14 +213,17 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
if (IS_QLA29XX(ha)) {
struct abts_entry_24xx_ext *rsp_ext = rsp_pkt;
- QLA_BUILD_ABTS_BA_ACC(rsp_ext, abts_ext,
- abts_ext->sof_type, fctl);
+ QLA_BUILD_ABTS_BA_ACC(rsp_ext, abts_ext, fctl);
+ rsp_ext->vp_idx_sof = qla_ext_build_vp_sof(
+ qla_ext_get_vp_index(abts_ext->vp_idx_sof),
+ qla_ext_get_sof_type(abts_ext->vp_idx_sof));
QLA_LOG_ISSUE_ABTS_RSP(vha, rsp_ext, dma, rval);
} else {
struct abts_entry_24xx *abts_rsp = rsp_pkt;
- QLA_BUILD_ABTS_BA_ACC(abts_rsp, abts,
- abts->sof_type & 0xf0, fctl);
+ QLA_BUILD_ABTS_BA_ACC(abts_rsp, abts, fctl);
+ abts_rsp->vp_idx = abts->vp_idx;
+ abts_rsp->sof_type = abts->sof_type & 0xf0;
QLA_LOG_ISSUE_ABTS_RSP(vha, abts_rsp, dma, rval);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 42/56] scsi: qla2xxx: Fix endianness annotations in vp_rpt_id_entry structures
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (40 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 41/56] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 43/56] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats Nilesh Javali
` (13 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
The vp_rpt_id_entry_24xx and vp_rpt_id_entry_24xx_ext DMA structures
use plain uint16_t for fip_flags and bbcr fields that the firmware
writes in little-endian format. On big-endian hosts, reading bbcr
without le16_to_cpu() produces an incorrect value, breaking the
buffer-to-buffer credit enable detection.
Additionally, the 29xx ext struct uses __le16 bitfields for
vp_idx:9/vp_status:7 which suffer from architecture-dependent
bit packing order (same class of bug fixed in the ELS/ABTS
extended IOCBs).
Fix by:
- Changing uint16_t fip_flags/bbcr to __le16 in both qla_fw.h
and qla_fw29.h (enables Sparse endianness checking)
- Replacing the __le16 bitfields with a scalar __le16 vp_idx_status
and defined shift/mask constants
- Adding le16_to_cpu() at the bbcr and vp_idx_status access sites
in qla_mbx.c
Fixes: 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] 87+ messages in thread
* [PATCH v4 43/56] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (41 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 42/56] scsi: qla2xxx: Fix endianness annotations in vp_rpt_id_entry structures Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 16:25 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 44/56] scsi: qla2xxx: Add 64G/128G port speed setting support Nilesh Javali
` (12 subsequent siblings)
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
29xx provides the 64-bit FPM transmit/receive word counters in the link
statistics block, like 83xx/27xx/28xx. qla2x00_get_fc_host_stats()
only consumed those counters for the older families and fell back to the
software approximation (input/output bytes >> 2) on 29xx, reporting less
accurate rx_words/tx_words.
Add IS_QLA29XX() to the high-speed branch so 29xx reports the hardware
word counters.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_attr.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 19aa66b8ca52..6cf74f8c9628 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -2990,7 +2990,8 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost)
p->error_frames =
le32_to_cpu(stats->dropped_frames) +
le32_to_cpu(stats->discarded_frames);
- if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+ if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+ IS_QLA29XX(ha)) {
p->rx_words = le64_to_cpu(stats->fpm_recv_word_cnt);
p->tx_words = le64_to_cpu(stats->fpm_xmit_word_cnt);
} else {
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 44/56] scsi: qla2xxx: Add 64G/128G port speed setting support
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (42 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 43/56] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 45/56] scsi: qla2xxx: Fix 64G link speed reporting in get_data_rate Nilesh Javali
` (11 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
The port speed setting paths topped out at 32G: qla2x00_port_speed_store()
only mapped sysfs inputs up to 32 (and their no-loss-of-sync forms up to
320), and qla2x00_set_data_rate() only accepted PORT_SPEED_AUTO/4/8/16/32
in its switch. A user request for 64G or 128G therefore hit the default
arm and was silently downgraded to auto-negotiation.
Map the 64 and 128 sysfs inputs (and their /10 no-loss-of-sync forms 640
and 1280) to PORT_SPEED_64GB and PORT_SPEED_128GB, and accept those
values in qla2x00_set_data_rate(). The firmware validates the requested
rate against the adapter's actual capability.
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_attr.c | 8 +++++++-
drivers/scsi/qla2xxx/qla_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 e31997241c34..5f04190c1487 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] 87+ messages in thread
* [PATCH v4 45/56] scsi: qla2xxx: Fix 64G link speed reporting in get_data_rate
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (43 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 44/56] scsi: qla2xxx: Add 64G/128G port speed setting support Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 46/56] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check Nilesh Javali
` (10 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
qla2x00_get_data_rate() skips updating ha->link_data_rate when the
firmware returns mcp->mb[1] == 0x7. That value was a legacy sentinel
from before 64G hardware existed, but PORT_SPEED_64GB is now defined as
0x07 and ha->link_data_rate is decoded with the PORT_SPEED_* encoding.
On a 64G-capable adapter a genuine 64G link is therefore dropped, and
the port speed is misreported (port_speed sysfs, fc_host speed, FDMI).
Only 28xx and 29xx support 64G, so accept 0x07 on those adapters while
keeping the legacy filter for older ones. Also drop the duplicate
copy of the check at the end of the success branch; it repeated the
first assignment with no intervening change.
Fixes: ecc89f25e225 ("scsi: qla2xxx: Add Device ID for ISP28XX")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_mbx.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index ba822c196894..b32ca8ed274d 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -5785,10 +5785,11 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
ql_dbg(ql_dbg_mbx, vha, 0x1107,
"Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
} else {
- if (mcp->mb[1] != 0x7)
+ if (mcp->mb[1] != 0x7 || IS_QLA28XX(ha) || IS_QLA29XX(ha))
ha->link_data_rate = mcp->mb[1];
- if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
+ if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+ IS_QLA29XX(ha)) {
if (mcp->mb[4] & BIT_0)
ql_log(ql_log_info, vha, 0x11a2,
"FEC=enabled (data rate).\n");
@@ -5796,8 +5797,6 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1108,
"Done %s.\n", __func__);
- if (mcp->mb[1] != 0x7)
- ha->link_data_rate = mcp->mb[1];
}
return rval;
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 46/56] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (44 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 45/56] scsi: qla2xxx: Fix 64G link speed reporting in get_data_rate Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 47/56] scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters Nilesh Javali
` (9 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
qla_chk_edif_rx_sa_delete_pending() obtains the SCSI command via
GET_CMD_SP(sp) and immediately dereferences cmd->sc_data_direction.
That command pointer can be NULL: the firmware may post a status
completion for a command that has already been returned or aborted.
The caller qla2x00_status_entry() acknowledges this on the very same
status path, re-fetching GET_CMD_SP(sp) and bailing out with the
"Command already returned" message when it is NULL -- but that check
runs only after qla_chk_edif_rx_sa_delete_pending() has already
dereferenced the pointer, so a NULL cmd crashes the kernel in
interrupt context.
Return early when cmd is NULL, before touching cmd->sc_data_direction.
Fixes: dd30706e73b7 ("scsi: qla2xxx: edif: Add key update")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_edif.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c
index ade1d8178573..bfa520f936a2 100644
--- a/drivers/scsi/qla2xxx/qla_edif.c
+++ b/drivers/scsi/qla2xxx/qla_edif.c
@@ -3540,6 +3540,9 @@ void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
uint32_t handle;
uint16_t sa_index;
+ if (!cmd)
+ return;
+
handle = (uint32_t)LSW(sts24->handle);
/* find out if this status iosb is for a scsi read */
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 47/56] scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (45 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 46/56] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 48/56] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size Nilesh Javali
` (8 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
In the CS_PORT_LOGGED_OUT case of qla2x00_chk_ms_status(), the
FWI2-capable branch compared ms_pkt->loop_id.extended against NPH_SNS
to decide whether the Name Server had logged out. On FWI2 and later
adapters the response is a ct_entry_24xx / ct_entry_24xx_ext, where
loop_id.extended (via the legacy ms_iocb_entry_t view) aliases offset 8,
which is comp_status, not nport_handle (offset 10). As this code runs
under CS_PORT_LOGGED_OUT, the field read back 0x29 (CS_PORT_LOGGED_OUT)
and the comparison against NPH_SNS (0x7fc) was always false.
As a result the driver never recognized a Name Server logout on FWI2/
29xx adapters: it returned the generic QLA_FUNCTION_FAILED instead of
QLA_NOT_LOGGED_IN and skipped setting LOOP_RESYNC_NEEDED /
LOCAL_LOOP_UPDATE, so the fabric rediscovery triggered by an SNS logout
did not happen.
Read nport_handle from the ct_entry_24xx layout (offset 10) instead.
nport_handle is at the same offset in ct_entry_24xx and
ct_entry_24xx_ext, so a single cast covers 24xx-class and 29xx. The
non-FWI2 branch keeps using loop_id.extended, which is correct for the
ms_iocb_entry_t response on those adapters.
Fixes: b98ae0d748db ("scsi: qla2xxx: Fix name server relogin")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_gs.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index 7a4d2fdc095f..20b1aef455c4 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -192,8 +192,8 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt,
break;
case CS_PORT_LOGGED_OUT:
if (IS_FWI2_CAPABLE(ha)) {
- if (le16_to_cpu(ms_pkt->loop_id.extended) ==
- NPH_SNS)
+ if (le16_to_cpu(((struct ct_entry_24xx *)
+ ms_pkt)->nport_handle) == NPH_SNS)
lid_is_sns = true;
} else {
if (le16_to_cpu(ms_pkt->loop_id.extended) ==
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 48/56] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (46 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 47/56] scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 49/56] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config() Nilesh Javali
` (7 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
The VP control IOCB selects its target virtual port by setting one bit
in vp_idx_map, a fixed 16-byte (128-bit) array in both
vp_ctrl_entry_24xx and vp_ctrl_entry_24xx_ext. qla25xx_ctrlvp_iocb()
computes map = (vp_index - 1) / 8 and writes vce->vp_idx_map[map]
without checking that map stays within the array.
max_npiv_vports is taken from firmware and only sanitized to a
MIN_MULTI_ID_FABRIC-aligned boundary, so it can legitimately be 191 or
255, and qla24xx_control_vp() only rejects vp_index >= max_npiv_vports.
A vp_index above 128 therefore yields map >= 16 and an out-of-bounds
write of up to 16 bytes past vp_idx_map, corrupting the trailing IOCB
fields (or the adjacent request-ring slot on the 64-byte layout).
Reject a vp_index that cannot be represented in the IOCB bitmap in
qla24xx_control_vp(), and add a defensive ARRAY_SIZE() guard in
qla25xx_ctrlvp_iocb() before the write. Adapters that report the usual
63 or 127 NPIV vports are unaffected.
Fixes: 2853192e154b ("scsi: qla2xxx: Use IOCB path to submit Control VP MBX command")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_iocb.c | 6 ++++++
drivers/scsi/qla2xxx/qla_mid.c | 8 ++++++++
2 files changed, 14 insertions(+)
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 6057d7da507e..fbd940742abb 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -4125,6 +4125,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] 87+ messages in thread
* [PATCH v4 49/56] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config()
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (47 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 48/56] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 50/56] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition() Nilesh Javali
` (6 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
The Modify VP Config completion handler labelled its first error branch
"error status" but tested vpmod->comp_status instead of
vpmod->entry_status. Because CS_COMPLETE is 0, the following
"comp_status != CS_COMPLETE" branch duplicated that test and was dead
code, and entry_status was never examined at all.
When firmware rejects the IOCB early it sets entry_status while leaving
comp_status zero. As the IOCB is allocated with dma_pool_zalloc(), both
comp_status branches evaluate false and the handler falls through to the
success path, calling fc_vport_set_state(FC_VPORT_INITIALIZING) for a
configuration the firmware never accepted. This can leave the virtual
port enabled on top of an invalid config and surface later as login
timeouts or follow-on firmware errors.
Test entry_status in the first branch, matching qla_ctrlvp_completed()
and the login/logout/abort/reset IOCB handlers; the comp_status branch
then becomes the live completion-status check.
Fixes: 2c3dfe3f6ad8 ("[SCSI] qla2xxx: add support for NPIV")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_mbx.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index b32ca8ed274d..59023492c5a9 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -4401,10 +4401,10 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
if (rval != QLA_SUCCESS) {
ql_dbg(ql_dbg_mbx, vha, 0x10bd,
"Failed to issue VP config IOCB (%x).\n", rval);
- } else if (vpmod->comp_status != 0) {
+ } else if (vpmod->entry_status != 0) {
ql_dbg(ql_dbg_mbx, vha, 0x10be,
"Failed to complete IOCB -- error status (%x).\n",
- vpmod->comp_status);
+ vpmod->entry_status);
rval = QLA_FUNCTION_FAILED;
} else if (vpmod->comp_status != cpu_to_le16(CS_COMPLETE)) {
ql_dbg(ql_dbg_mbx, vha, 0x10bf,
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 50/56] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition()
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (48 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 49/56] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config() Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 51/56] scsi: qla2xxx: Initialize NVMe abort_work once at submission Nilesh Javali
` (5 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
In the format 1 path, the virtual port is located on ha->vp_list while
holding vport_slock, but the lock is dropped before vp is used:
qla_update_host_map() is called and VP_IDX_ACQUIRED/REGISTER_FC4_NEEDED/
REGISTER_FDMI_NEEDED are set on vp. No reference is taken across that
window, so a concurrent qla24xx_deallocate_vp_id() can tear the vport
down and free it, leading to a use-after-free.
Take a vport reference (vref_count) under vport_slock when the matching
vp is found, and drop it after the last use of vp. qla24xx_deallocate_vp_id()
waits for vref_count to reach zero before unlinking and freeing the vport,
so the pointer stays valid. This matches the reference idiom already used
by the other ha->vp_list traversals.
Fixes: 2c3dfe3f6ad8 ("[SCSI] qla2xxx: add support for NPIV")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_mbx.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 59023492c5a9..ba4a4764de1f 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -4267,6 +4267,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
list_for_each_entry(vp, &ha->vp_list, list) {
if (vp_idx == vp->vp_idx) {
found = 1;
+ atomic_inc(&vp->vref_count);
break;
}
}
@@ -4284,6 +4285,8 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
+
+ atomic_dec(&vp->vref_count);
}
set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
qla2xxx_wake_dpc(vha);
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 51/56] scsi: qla2xxx: Initialize NVMe abort_work once at submission
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (49 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 50/56] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition() Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 52/56] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject Nilesh Javali
` (4 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
qla_nvme_fcp_abort() and qla_nvme_ls_abort() ran INIT_WORK() on
priv->abort_work immediately before schedule_work(). INIT_WORK()
reinitializes the work_struct, resetting its list head and clearing the
pending bit. If an abort is issued more than once for the same command
(for example, concurrent transport teardown and a timeout-driven abort),
the second INIT_WORK() reinitializes a work item that is already queued,
which can corrupt the workqueue list and lead to crashes or a looping
worker.
Initialize priv->abort_work once at command submission, next to the
existing per-command spin_lock_init(&priv->cmd_lock), and leave only
schedule_work() in the abort paths. schedule_work() already does nothing
when the work item is still pending, so a repeated abort no longer
disturbs an in-flight work item. The command is not returned to the
transport until the final kref_put()/release callback runs after
abort_work has completed, so the work item is idle before priv is
reused and the single submission-time INIT_WORK() is safe.
Fixes: e473b3074104 ("scsi: qla2xxx: Add FC-NVMe abort processing")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_nvme.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index 0038b6274d44..3b2f255a5d7d 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -463,7 +463,6 @@ static void qla_nvme_ls_abort(struct nvme_fc_local_port *lport,
}
spin_unlock_irqrestore(&priv->cmd_lock, flags);
- INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
schedule_work(&priv->abort_work);
}
@@ -501,6 +500,7 @@ static int qla_nvme_ls_req(struct nvme_fc_local_port *lport,
priv->sp = sp;
kref_init(&sp->cmd_kref);
spin_lock_init(&priv->cmd_lock);
+ INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
nvme = &sp->u.iocb_cmd;
priv->fd = fd;
nvme->u.nvme.desc = fd;
@@ -545,7 +545,6 @@ static void qla_nvme_fcp_abort(struct nvme_fc_local_port *lport,
}
spin_unlock_irqrestore(&priv->cmd_lock, flags);
- INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
schedule_work(&priv->abort_work);
}
@@ -877,6 +876,7 @@ static int qla_nvme_post_cmd(struct nvme_fc_local_port *lport,
kref_init(&sp->cmd_kref);
spin_lock_init(&priv->cmd_lock);
+ INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
sp->priv = priv;
priv->sp = sp;
sp->type = SRB_NVME_CMD;
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 52/56] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (50 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 51/56] scsi: qla2xxx: Initialize NVMe abort_work once at submission Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 53/56] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak Nilesh Javali
` (3 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
qla_nvme_ls_reject_iocb() allocates from and advances the request ring
through __qla2x00_alloc_iocbs() (which assumes the hardware_lock is held)
and qla2x00_start_iocbs() (which advances the ring and rings the
request-in doorbell), but takes no lock itself. Two of its callers invoke
it without the producer lock held:
- qla_nvme_xmt_ls_rsp(), the NVMe-FC .xmt_ls_rsp transport callback, on
its error path, and
- qla2xxx_process_purls_pkt(), run from the purex work/DPC context.
Both use ha->base_qpair, whose qp_lock_ptr is hardware_lock, so they can
run concurrently with normal I/O submission on the base ring and corrupt
the ring producer state, leading to duplicated or dropped commands. The
third caller, qla2xxx_process_purls_iocb(), runs inside
qla24xx_process_response_queue() with the qpair lock already held and is
safe; that is also why the lock cannot be taken inside the helper itself
(it would recursively re-acquire hardware_lock on the response path).
Take qp_lock_ptr around the two unlocked callers and document the helper
as caller-locked. Both run in process context, so spin_lock_irqsave() is
used and nothing in the locked region sleeps.
Fixes: 875386b98857 ("scsi: qla2xxx: Add Unsolicited LS Request and Response Support for NVMe")
Cc: stable@vger.kernel.org
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
---
drivers/scsi/qla2xxx/qla_nvme.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index 3b2f255a5d7d..8dc6df6c2e1c 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -374,6 +374,7 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport,
srb_t *sp;
int rval = QLA_FUNCTION_FAILED;
uint8_t cnt = 0;
+ unsigned long flags;
if (!fcport || fcport->deleted)
goto out;
@@ -440,7 +441,9 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport,
a.vp_idx = vha->vp_idx;
a.nport_handle = uctx->nport_handle;
a.xchg_address = uctx->exchange_address;
+ spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
+ spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
kfree(uctx);
return rval;
}
@@ -1243,6 +1246,10 @@ static void qla_nvme_lsrjt_pt_iocb(struct scsi_qla_host *vha,
}
}
+/*
+ * Allocates from and advances the request ring, so the caller must hold
+ * qp->qp_lock_ptr (the response-queue caller already holds it).
+ */
static int
qla_nvme_ls_reject_iocb(struct scsi_qla_host *vha, struct qla_qpair *qp,
struct qla_nvme_lsrjt_pt_arg *a, bool is_xchg_terminate)
@@ -1299,6 +1306,7 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item)
{
struct qla_nvme_unsol_ctx *uctx = item->purls_context;
struct qla_nvme_lsrjt_pt_arg a;
+ unsigned long flags;
int ret = 1;
#if (IS_ENABLED(CONFIG_NVME_FC))
@@ -1311,7 +1319,9 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item)
a.vp_idx = vha->vp_idx;
a.nport_handle = uctx->nport_handle;
a.xchg_address = uctx->exchange_address;
+ spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr, flags);
qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true);
+ spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr, flags);
list_del(&uctx->elem);
kfree(uctx);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* [PATCH v4 53/56] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (51 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 52/56] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 54/56] scsi: qla2xxx: Fix BSG job leak on validate flash image error path Nilesh Javali
` (2 subsequent siblings)
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 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] 87+ messages in thread
* [PATCH v4 54/56] scsi: qla2xxx: Fix BSG job leak on validate flash image error path
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (52 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 53/56] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 55/56] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions() Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 56/56] scsi: qla2xxx: Update version to 12.00.00.2607b1 Nilesh Javali
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 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] 87+ messages in thread
* [PATCH v4 55/56] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions()
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (53 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 54/56] scsi: qla2xxx: Fix BSG job leak on validate flash image error path Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 56/56] scsi: qla2xxx: Update version to 12.00.00.2607b1 Nilesh Javali
55 siblings, 0 replies; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 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] 87+ messages in thread
* [PATCH v4 56/56] scsi: qla2xxx: Update version to 12.00.00.2607b1
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
` (54 preceding siblings ...)
2026-07-14 9:53 ` [PATCH v4 55/56] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions() Nilesh Javali
@ 2026-07-14 9:53 ` Nilesh Javali
2026-07-14 10:41 ` Hannes Reinecke
55 siblings, 1 reply; 87+ messages in thread
From: Nilesh Javali @ 2026-07-14 9:53 UTC (permalink / raw)
To: martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi,
hare
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
drivers/scsi/qla2xxx/qla_version.h | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
index 9564beafdab7..1c0b01d70350 100644
--- a/drivers/scsi/qla2xxx/qla_version.h
+++ b/drivers/scsi/qla2xxx/qla_version.h
@@ -6,9 +6,9 @@
/*
* Driver version
*/
-#define QLA2XXX_VERSION "10.02.10.100-k"
+#define QLA2XXX_VERSION "12.00.00.2607b1"
-#define QLA_DRIVER_MAJOR_VER 10
-#define QLA_DRIVER_MINOR_VER 02
-#define QLA_DRIVER_PATCH_VER 10
-#define QLA_DRIVER_BETA_VER 100
+#define QLA_DRIVER_MAJOR_VER 12
+#define QLA_DRIVER_MINOR_VER 00
+#define QLA_DRIVER_PATCH_VER 00
+#define QLA_DRIVER_BETA_VER 2607
--
2.47.3
^ permalink raw reply related [flat|nested] 87+ messages in thread
* Re: [PATCH v4 03/56] scsi: qla2xxx: Add NVRAM config support for 29xx adapters
2026-07-14 9:53 ` [PATCH v4 03/56] scsi: qla2xxx: Add NVRAM config support for 29xx adapters Nilesh Javali
@ 2026-07-14 10:11 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 10:11 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] Missing assignment `ha->fiv = NULL` when `qla29xx_read_optrom_data` fails to read the Flash Image Version (FMB).
--
--- Patch [3]: [PATCH v4 03/56] scsi: qla2xxx: Add NVRAM config support for 29xx adapters ---
commit 9423305e2fdeec148901334c684199e074dc7d5b
Author: Manish Rangankar <mrangankar@marvell.com>
scsi: qla2xxx: Add NVRAM config support for 29xx adapters
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.
> diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
> index e23e7ac48ae26..f5d230fc3642d 100644
> --- a/drivers/scsi/qla2xxx/qla_init.c
> +++ b/drivers/scsi/qla2xxx/qla_init.c
[ ... ]
> @@ -9306,34 +9306,97 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
[ ... ]
> + 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");
[Severity: Medium]
Does this code leave ha->fiv pointing to an uninitialized buffer if the
qla29xx_read_optrom_data() read fails?
In the invalid signature check immediately below, ha->fiv is explicitly set
to NULL. If ha->fiv isn't also set to NULL here on a read failure, any future
code checking if ha->fiv is populated might falsely assume the FMB data is
available and attempt to operate on the uninitialized buffer.
> + } 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 {
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=3
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 01/56] scsi: qla2xxx: Add 29xx series PCI device ID support
2026-07-14 9:52 ` [PATCH v4 01/56] scsi: qla2xxx: Add 29xx series PCI device ID support Nilesh Javali
@ 2026-07-14 10:19 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 10:19 UTC (permalink / raw)
To: Nilesh Javali; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [Critical] Missing initialization of ha->tgt.atio_q_length for QLA29XX causes an infinite loop in the interrupt handler and out-of-bounds DMA corruption.
- [High] Missing IS_QLA29XX capability checks in target mode initialization logic causes fallback to legacy MMIO paths and skips ICB vector setup.
- [High] Mailbox queue creation commands for QLA29XX omit mandatory SR-IOV bits and illegally write to read-only RISC registers.
- [High] Omission of IS_QLA29XX from multiple core architectural capability macros cripples 29xx functionality.
- [Medium] The patch fails to extend speed-capability logic for 29xx adapters, directly contradicting the commit message.
Pre-existing issues:
- [High] Freeing DMA pool memory into a slab-backed mempool causes slab corruption.
--
--- Patch [1]: [PATCH v4 01/56] scsi: qla2xxx: Add 29xx series PCI device ID support ---
commit d176fdd8e23c51253c0c8552a2ec8303d2e13126
Author: Manish Rangankar <mrangankar@marvell.com>
scsi: qla2xxx: Add 29xx series PCI device ID support
This commit adds PCI device ID support for the new generation QLA29xx
series FC HBA. It registers the new PCI device IDs and wires up
IS_QLA29XX() capability checks in the probe and ISP-flags paths.
> 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 does this actually happen? In qla2x00_execute_fw(), the
speed-capability parsing logic checks IS_QLA27XX(ha) || IS_QLA28XX(ha)
but omits IS_QLA29XX. Will this cause the driver to fail to correctly
parse or report supported link speeds for 29xx hardware?
[Severity: High]
Are there missing IS_QLA29XX capability checks in the target mode
initialization logic? In qlt_probe_one_stage1(), if mqenable evaluates
to false on a 29xx adapter, the driver configures ATIO queue pointers
to the legacy iobase->isp24 instead of mqiobase. Since 29xx uses a
small legacy iobase, won't accessing ATIO queue registers lead to
out-of-bounds MMIO accesses? Furthermore, qlt_24xx_config_rings() omits
29xx from its checks, completely skipping the configuration of the ATIO
interrupt vector in the ICB.
[Severity: High]
Does this code omit mandatory SR-IOV bits and illegally write to
read-only RISC registers for 29xx? In qla25xx_init_req_que() and
qla25xx_init_rsp_que(), mailbox instructions are constructed using
checks for 83XX, 27XX, and 28XX. Missing the 29XX check skips setting
MBX_15 and MBX_9 | MBX_8 | MBX_7 for SR-IOV support, and critically,
it bypasses the check protecting against writing to the read-only
hardware pointers req_q_out and rsp_q_in, which could corrupt active
queue state on 29xx hardware.
[Severity: High]
This is a pre-existing issue, but can the error path in
qla24xx_start_scsi() and qla24xx_dif_start_scsi() corrupt the slab
allocator? The crc_ctx pointer is allocated from a DMA pool using
dma_pool_zalloc(ha->dl_dma_pool), but in the queuing_error paths, it is
incorrectly freed using mempool_free() back to the slab-backed
ha->ctx_mempool. Should this be freed with dma_pool_free() instead?
> ---
> 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 5593ad7fad274..6337a056b1497 100644
> --- a/drivers/scsi/qla2xxx/qla_def.h
> +++ b/drivers/scsi/qla2xxx/qla_def.h
[ ... ]
> @@ -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))
[Severity: High]
Are there other capability macros that need IS_QLA29XX added? For
example, IS_SHADOW_REG_CAPABLE and USE_ASYNC_SCAN in this file appear
to be missing the 29XX check. Will omitting this silently disable high
performance shadow registers and async discovery logic natively supported
by this architecture?
[ ... ]
> diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
> index 72b1c28e4dae0..c91d2b8bd08e7 100644
> --- a/drivers/scsi/qla2xxx/qla_os.c
> +++ b/drivers/scsi/qla2xxx/qla_os.c
[ ... ]
> @@ -3195,6 +3221,22 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
> ha->flash_data_off = FARX_ACCESS_FLASH_DATA_28XX;
> ha->nvram_conf_off = ~0;
> ha->nvram_data_off = ~0;
> + } else if (IS_QLA29XX(ha)) {
> + ha->portnum = PCI_FUNC(ha->pdev->devfn);
> + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
> + ha->mbx_count = MAILBOX_REGISTER_COUNT;
> + req_length = REQUEST_ENTRY_CNT_83XX;
> + rsp_length = RESPONSE_ENTRY_CNT_83XX;
> + 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;
> }
[Severity: Critical]
Does this initialization block miss setting ha->tgt.atio_q_length? Leaving
it zero-initialized results in qlt_mem_alloc() allocating only a 1-entry DMA
buffer for the ATIO ring.
When an ATIO interrupt triggers in qlt_24xx_process_atio_queue(), cnt
evaluates to 0, causing the code to never update the ATIO_PROCESSED
signature or advance the pointer, trapping the CPU in an infinite loop.
Furthermore, if an NPIV vport is created, qlt_vport_create() overwrites
the length with ATIO_ENTRY_CNT_24XX, leading to out-of-bounds DMA accesses.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=1
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 04/56] scsi: qla2xxx: Add 29xx support in queue initialisation path
2026-07-14 9:53 ` [PATCH v4 04/56] scsi: qla2xxx: Add 29xx support in queue initialisation path Nilesh Javali
@ 2026-07-14 10:28 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 10:28 UTC (permalink / raw)
To: Nilesh Javali; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [Critical] Base queue DMA rings (Queue 0) are undersized for 29xx hardware, which will lead to firmware reading and writing out-of-bounds memory.
- [Critical] Response queue lockless synchronization signatures are improperly initialized for 29xx, leaving half the ring buffer zero-initialized and vulnerable to phantom processing.
- [High] Mismatched queue pointer arithmetic for QLA29XX introduces hardware synchronization failures, queue stalls, and breaks bisectability.
Pre-existing issues:
- [High] The queue cleanup paths blindly pass a potentially NULL pointer to `dma_free_coherent()`, triggering a kernel crash if the initial queue allocation fails.
- [High] Inconsistent locking between `vport_lock` and `mq_lock` when managing dynamic queue IDs and queue maps.
- [High] Uninitialized kernel stack memory is written to hardware mailbox registers.
--
--- Patch [4]: [PATCH v4 04/56] scsi: qla2xxx: Add 29xx support in queue initialisation path ---
commit 13a40ca7120445e92ae33446755917919989aa14
Author: Manish Rangankar <mrangankar@marvell.com>
scsi: qla2xxx: Add 29xx support in queue initialisation path
This patch extends queue initialization and mailbox commands to support the
QLA29XX hardware. It introduces 128-byte request and response structures and
doubles the DMA ring memory allocated for multi-queues.
> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index 2d052f870b2bc..2b232b225c900 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;
[Severity: High]
This is a pre-existing issue, but does this leak uninitialized kernel stack
memory to the hardware mailbox?
The mbx_cmd_t structure is allocated on the stack but not zero-initialized:
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
Several mailbox fields (like mcp->mb[14]) are left unwritten, yet their
corresponding validity bits (MBX_14) are set in the mcp->out_mb bitmask:
mcp->out_mb = MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|...
When qla2x00_mailbox_command() iterates over the out_mb mask, does it read
these uninitialized stack values and issue them directly to the hardware via
wrt_reg_word()?
[ ... ]
> 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 is a pre-existing issue, but can this trigger a kernel panic if the
initial allocation fails?
If memory pressure causes dma_alloc_coherent() to return NULL during queue
creation, the error path executes this cleanup and passes the NULL req->ring
pointer directly into dma_free_coherent().
[Severity: High]
This is a pre-existing issue, but is there a data race in the queue ID
cleanup later in this function?
Dynamic queue ID allocation in qla25xx_create_req_que() protects the
ha->req_qid_map read-modify-write cycle using mutex_lock(&ha->mq_lock).
However, the cleanup path clears the map globally without holding mq_lock
and clears the bitmap using vport_lock:
ha->req_q_map[que_id] = NULL;
mutex_lock(&ha->vport_lock);
clear_bit(que_id, ha->req_qid_map);
mutex_unlock(&ha->vport_lock);
Could this corrupt the queue tracking structures when racing against queue
creation?
> 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);
[Severity: High]
This is a pre-existing issue, but passing a potentially NULL rsp->ring pointer
into dma_free_coherent() can also trigger a wild pointer dereference here if
queue creation fails.
> 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);
[Severity: Critical]
Does this leave the base DMA rings (Queue 0) undersized for 29xx hardware?
While this patch updates MQ allocations here to use 128-byte strides (reqsz),
the base queue allocations in qla_os.c:qla2x00_mem_alloc() appear to still be
hardcoded to sizeof(request_t) (64 bytes).
Since 29xx hardware expects 128-byte entries across all rings, won't the
firmware perform out-of-bounds DMA read/writes into a buffer sized for
64-byte entries, potentially corrupting adjacent physical memory?
[Severity: High]
Does deferring shadow pointer calculation updates to later patches break
bisectability and cause hardware synchronization failures?
The commit message notes pointer arithmetic is handled by later patches.
However, since req->ring is a request_t * (64 bytes), calculations later in
this initialization sequence like:
req->out_ptr = (uint16_t *)(req->ring + req->length);
will use 64-byte pointer arithmetic. For 29xx, which expects a 128-byte
stride, this pointer lands in the middle of the active ring buffer instead
of the trailing slot. Is it safe to allocate 128-byte strides here without
simultaneously updating the shadow pointer calculations?
> 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);
[Severity: Critical]
Does this cause phantom processing of zero-initialized memory due to
mismatched buffer strides?
This patch provisions 128-byte strides (rspsz) for QLA29XX, but
qla2x00_init_response_q_entries() populates the RESPONSE_PROCESSED signature
by stepping linearly using pkt++ on a 64-byte response_t * pointer:
for (cnt = 0; cnt < rsp->length; cnt++) {
pkt->signature = RESPONSE_PROCESSED;
pkt++;
}
Won't this only write the signatures into the first half of the 128-byte
structures (hitting the reserved space) and leave the second half zeroed?
Since 0 != RESPONSE_PROCESSED, will the driver's interrupt handler falsely
interpret these zeroes as valid incoming firmware responses?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=4
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 56/56] scsi: qla2xxx: Update version to 12.00.00.2607b1
2026-07-14 9:53 ` [PATCH v4 56/56] scsi: qla2xxx: Update version to 12.00.00.2607b1 Nilesh Javali
@ 2026-07-14 10:41 ` Hannes Reinecke
0 siblings, 0 replies; 87+ messages in thread
From: Hannes Reinecke @ 2026-07-14 10:41 UTC (permalink / raw)
To: Nilesh Javali, martin.petersen
Cc: linux-scsi, GR-FC-Storage-Upstream, agurumurthy, emilne, jmeneghi
On 7/14/26 11:53 AM, Nilesh Javali wrote:
> Signed-off-by: Nilesh Javali <njavali@marvell.com>
> ---
> drivers/scsi/qla2xxx/qla_version.h | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
> index 9564beafdab7..1c0b01d70350 100644
> --- a/drivers/scsi/qla2xxx/qla_version.h
> +++ b/drivers/scsi/qla2xxx/qla_version.h
> @@ -6,9 +6,9 @@
> /*
> * Driver version
> */
> -#define QLA2XXX_VERSION "10.02.10.100-k"
> +#define QLA2XXX_VERSION "12.00.00.2607b1"
>
> -#define QLA_DRIVER_MAJOR_VER 10
> -#define QLA_DRIVER_MINOR_VER 02
> -#define QLA_DRIVER_PATCH_VER 10
> -#define QLA_DRIVER_BETA_VER 100
> +#define QLA_DRIVER_MAJOR_VER 12
> +#define QLA_DRIVER_MINOR_VER 00
> +#define QLA_DRIVER_PATCH_VER 00
> +#define QLA_DRIVER_BETA_VER 2607
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.com +49 173 5876786
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: J.Jaser, A.McDonald, W.Knoblich
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 06/56] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path
2026-07-14 9:53 ` [PATCH v4 06/56] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path Nilesh Javali
@ 2026-07-14 10:42 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 10: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:
Pre-existing issues:
- [High] Unprotected hardware flash register accesses via `qla28xx_get_aux_images()` before acquiring `optrom_mutex`.
- [High] Concurrent accesses to fw_dump sysfs attributes can trigger a Use-After-Free or NULL pointer dereference because resources are freed without acquiring optrom_mutex.
--
--- Patch [6]: [PATCH v4 06/56] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path ---
commit 62e5b966d9421643c341b9735e054bbf8a187205
Author: Manish Rangankar <mrangankar@marvell.com>
scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path
This commit removes a redundant read_optrom() call in qla2x00_sysfs_read_vpd()
that executes without the optrom_mutex, preventing concurrent flash access
issues.
> diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
> index 6a05ce195aa05..800751ab562a1 100644
> --- a/drivers/scsi/qla2xxx/qla_attr.c
> +++ b/drivers/scsi/qla2xxx/qla_attr.c
[ ... ]
> @@ -580,7 +580,6 @@ qla2x00_sysfs_read_vpd(struct file *filp, struct kobject *kobj,
> ha->isp_ops->read_optrom(vha, ha->vpd, faddr, ha->vpd_size);
> mutex_unlock(&ha->optrom_mutex);
>
> - ha->isp_ops->read_optrom(vha, ha->vpd, faddr, ha->vpd_size);
[Severity: High]
This is a pre-existing issue, but does this code leave hardware flash register
accesses unprotected earlier in qla2x00_sysfs_read_vpd()?
Looking at qla2x00_sysfs_read_vpd(), qla28xx_get_aux_images() is called before
acquiring optrom_mutex:
if (IS_QLA28XX(ha)) {
qla28xx_get_aux_images(vha, &active_regions);
...
}
mutex_lock(&ha->optrom_mutex);
The qla28xx_get_aux_images() call chain reaches qla24xx_read_flash_data(),
which directly manipulates ISP hardware flash registers. Without optrom_mutex
protection, could this race with concurrent sysfs invocations or other flash
operations and corrupt the hardware state?
> skip:
> return memory_read_from_buffer(buf, count, &off, ha->vpd, ha->vpd_size);
> }
[Severity: High]
This is also a pre-existing issue in the same file, but can concurrent access
to fw_dump sysfs attributes trigger a use-after-free?
If thread 1 reads the minidump via qla2x00_sysfs_read_fw_dump(), it acquires
optrom_mutex and accesses ha->md_dump:
qla2x00_sysfs_read_fw_dump()
mutex_lock(&ha->optrom_mutex);
...
rval = memory_read_from_buffer(buf, count, &off, ha->md_dump, ...);
mutex_unlock(&ha->optrom_mutex);
If thread 2 concurrently writes to the sysfs node via
qla2x00_sysfs_write_fw_dump(), it can free ha->md_dump without acquiring
optrom_mutex:
qla2x00_sysfs_write_fw_dump()
...
switch (dump_type) {
case 0:
...
qla82xx_md_free(vha);
Couldn't thread 1 end up accessing freed memory in memory_read_from_buffer()
if qla82xx_md_free() is called concurrently by thread 2?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=6
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 05/56] scsi: qla2xxx: Add FC operational firmware load for 29xx
2026-07-14 9:53 ` [PATCH v4 05/56] scsi: qla2xxx: Add FC operational firmware load for 29xx Nilesh Javali
@ 2026-07-14 10:43 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 10:43 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] Missing `.start_scsi` function pointer in `qla29xx_isp_ops` leads to NULL pointer dereference.
- [Medium] Out-of-bounds read and missing minimum size check in `qla29xx_load_fw_template()`.
- [Low] Wasteful use of `vmalloc()` for a small, short-lived 80-byte structure (`struct fcop_header`).
--
--- Patch [5]: [PATCH v4 05/56] scsi: qla2xxx: Add FC operational firmware load for 29xx ---
commit 9c9c2ed926e65df8d7c92e4c4efc603458f304c5
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 implements the request_firmware and segment-load pattern
used by earlier adapters and sets up the firmware dump template.
> diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
> index f5d230fc3642d..f6a8bcbb2a037 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)
> +{
> + 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));
[Severity: Low]
Is it necessary to use vmalloc() for this 80-byte fcop_header structure?
Allocating such a small structure with vmalloc consumes a full page and TLB
entries. Would it be better to allocate this on the stack or use kzalloc()
instead?
> + if (!header)
> + return QLA_FUNCTION_FAILED;
[ ... ]
> +static int
> +qla29xx_load_fw_template(scsi_qla_host_t *vha)
> +{
[ ... ]
> + 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]);
[Severity: Medium]
Could this access out-of-bounds memory?
This reads the third 32-bit word of the fw_dump_tmplt buffer. If the
flash or firmware advertises a ha->fw_dump_tmplt_len that is smaller
than 12 bytes, this will read past the end of the allocation. Should
there be a minimum size check against fw_dump_tmplt_len before this
access?
> +
> + ql_dbg(ql_dbg_init, vha, 0x0161,
> + "-> fwdt%u template array at %p (0x%x bytes)\n",
> + j, fw_dump_tmplt, template_size);
[ ... ]
> 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,
[ ... ]
> + .get_flash_version = qla24xx_get_flash_version,
> + .start_scsi_mq = qla2xxx_dif_start_scsi_mq,
> + .abort_isp = qla2x00_abort_isp,
[Severity: High]
Does this isp_operations struct need a .start_scsi callback?
In qla2xxx_queuecommand(), if multiqueue is disabled via module parameters
(ql2xmqsupport=0) or falls back because MSI-X allocation fails, the code
bypasses the MQ path and invokes:
rval = ha->isp_ops->start_scsi(sp);
Since the .start_scsi pointer is omitted for 29xx, won't this result in a
NULL pointer dereference?
> + .iospace_config = qla83xx_iospace_config,
> + .initialize_adapter = qla2x00_initialize_adapter,
> +};
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=5
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 08/56] scsi: qla2xxx: Add BSG MPI firmware load/dump for 29xx
2026-07-14 9:53 ` [PATCH v4 08/56] scsi: qla2xxx: Add BSG MPI firmware load/dump " Nilesh Javali
@ 2026-07-14 10:59 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 10:59 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] Returning a non-zero error code from `qla2900_bsg_load_mpi()` after explicitly calling `bsg_job_done()` leads to a double completion of the block request.
- [High] The implementations for the LOAD and DUMP MPI commands are perfectly inverted, leading to unintended firmware overwrite or data discard.
--
--- Patch [8]: [PATCH v4 08/56] scsi: qla2xxx: Add BSG MPI firmware load/dump for 29xx ---
commit 472cec56cca22244e315ce21a1660801bbbc160d
Author: Manish Rangankar <mrangankar@marvell.com>
scsi: qla2xxx: Add BSG MPI firmware load/dump for 29xx
This patch 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
> @@ -1820,6 +1820,203 @@ static int qla29xx_bsg_flash_block_write(struct bsg_job *bsg_job)
[ ... ]
> +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);
[Severity: High]
Are the implementations for the DUMP and LOAD MPI commands inverted here?
In drivers/scsi/qla2xxx/qla_bsg.c:qla2900_bsg_dump_mpi(), this path configures
the operation as a write (is_update=1) and takes data from the user's
request_payload. This matches the behavior of a firmware load, which could
inadvertently write uninitialized or incorrect data to the device when a user
is attempting to dump the firmware.
[ ... ]
> +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);
[Severity: High]
Does this execute a read/dump flow during a load command?
In drivers/scsi/qla2xxx/qla_bsg.c:qla2900_bsg_load_mpi(), the function
configures the operation as a read (is_update=0) and later copies data to
the user's reply_payload. This behaves like a dump instead of a load, causing
the firmware image provided by the user to be discarded rather than written to
the device.
[ ... ]
> + 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]
Does this code risk a double completion of the block request?
If qla29xx_mpi_optrom_data() fails and sets an error code in rval, this path
calls bsg_job_done() and then returns the non-zero rval.
When a BSG vendor-specific request handler returns a non-zero value, the SCSI
FC transport layer (fc_bsg_host_dispatch) assumes the driver rejected the
request and falls back to completing the job itself. Since bsg_job_done() was
already called, this could complete the same block request twice, which could
lead to a use-after-free or list corruption.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=8
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 10/56] scsi: qla2xxx: Add extended status continuation and marker IOCBs
2026-07-14 9:53 ` [PATCH v4 10/56] scsi: qla2xxx: Add extended status continuation and marker IOCBs Nilesh Javali
@ 2026-07-14 11:06 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 11:06 UTC (permalink / raw)
To: Nilesh Javali; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The commit message explicitly claims to add a structure named `mrk_entry_ext_t`, but this structure is completely missing from the patch and the entire codebase.
--
--- Patch [10]: [PATCH v4 10/56] scsi: qla2xxx: Add extended status continuation and marker IOCBs ---
commit 7582f36ee7094ac8210ce81133caf62e1a0ea4c4
Author: Anil Gurumurthy <agurumurthy@marvell.com>
scsi: qla2xxx: Add extended status continuation and marker IOCBs
This commit adds the 128-byte extended status continuation structure for
29xx firmware and includes the qla_fw29.h header. The commit message also
mentions adding a marker IOCB structure.
> Add the 128-byte sts_cont_entry_ext_t and mrk_entry_ext_t
> structures required by 29xx firmware.
> diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
> --- a/drivers/scsi/qla2xxx/qla_def.h
> +++ b/drivers/scsi/qla2xxx/qla_def.h
[ ... ]
> @@ -2360,6 +2361,15 @@ typedef struct {
> uint8_t reserved_2[48];
> } mrk_entry_t;
>
> +/* 29xx definitions */
> +struct sts_cont_entry_ext {
> + uint8_t entry_type; /* Entry type. */
> + uint8_t entry_count; /* Entry count. */
> + uint8_t sys_define; /* System defined. */
> + uint8_t entry_status; /* Entry Status. */
> + uint8_t data[124]; /* data */
> +};
> +
[Severity: Medium]
This isn't a bug, but the commit message states that the mrk_entry_ext_t
structure is being added.
Looking at the changes in qla_def.h above, only the sts_cont_entry_ext
structure is included. Is the mrk_entry_ext_t structure missing from this
patch, or should the commit message be updated to reflect that it is not
included?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=10
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 09/56] scsi: qla2xxx: Add 128-byte IOCB definitions for 29xx
2026-07-14 9:53 ` [PATCH v4 09/56] scsi: qla2xxx: Add 128-byte IOCB definitions " Nilesh Javali
@ 2026-07-14 11:09 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 11:09 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] Usage of C bitfields with restricted `__le16` types in hardware-facing structures breaks memory layout on big-endian architectures and violates Sparse rules.
- [High] The struct `vp_rpt_id_entry_24xx_ext` is missing padding to reach the 128-byte IOCB size, evaluating to only 64 bytes.
--
--- Patch [9]: [PATCH v4 09/56] scsi: qla2xxx: Add 128-byte IOCB definitions for 29xx ---
commit 109fd3a5dadf86d8a0a91f525e8bcde2ce34fc5b
Author: Anil Gurumurthy <agurumurthy@marvell.com>
scsi: qla2xxx: Add 128-byte IOCB definitions for 29xx
The patch adds a new header qla_fw29.h with 128-byte IOCB structure
definitions to support the 29xx series firmware interface.
> --- /dev/null
> +++ b/drivers/scsi/qla2xxx/qla_fw29.h
[ ... ]
> +struct els_entry_24xx_ext {
> + uint8_t entry_type; /* Entry type. */
> + uint8_t entry_count; /* Entry count. */
> + uint8_t sys_define; /* System Defined. */
> + uint8_t entry_status; /* Entry Status. */
> +
> + uint32_t handle; /* System handle. */
> +
> + __le16 comp_status; /* response only */
> + __le16 nport_handle;
> +
> + __le16 tx_dsd_count;
> +
> + __le16 vp_index : 9; /* VP Index 9bits */
> + __le16 reserved_1_sof : 3;
> + __le16 sof_type : 4;
[Severity: High]
Does this C bitfield definition on a restricted __le16 type break memory
layout on big-endian architectures and violate Sparse rules?
Since bitfields are packed differently depending on host endianness, wouldn't
applying them directly to a little-endian type cause the hardware to receive
garbled values for ELS and ABTS operations on big-endian systems like PowerPC
or s390x?
[ ... ]
> +struct els_sts_entry_24xx_ext {
> + uint8_t entry_type; /* Entry type. */
> + uint8_t entry_count; /* Entry count. */
> + uint8_t sys_define; /* System Defined. */
> + uint8_t entry_status; /* Entry Status. */
> +
> + __le32 handle; /* System handle. */
> +
> + __le16 comp_status;
> +
> + __le16 nport_handle; /* N_PORT handle. */
> +
> + __le16 reserved_1;
> +
> + __le16 vp_index : 9; /* VP Index 9bits */
> + __le16 reserved_1_sof : 3;
> + __le16 sof_type : 4;
[Severity: High]
Could this bitfield on an __le16 type also result in endianness issues?
[ ... ]
> +struct abts_entry_24xx_ext {
> + uint8_t entry_type;
> + uint8_t entry_count;
> + uint8_t handle_count;
> + uint8_t entry_status;
> +
> + __le32 handle; /* type 0x55 only */
> +
> + __le16 comp_status; /* type 0x55 only */
> + __le16 nport_handle; /* type 0x54 only */
> +
> + __le16 control_flags; /* type 0x55 only */
> + __le16 vp_idx : 9; /* VP index 9 bits */
> + __le16 reserved_1_sof : 3;
> + __le16 sof_type : 4; /* sof_type is upper nibble */
[Severity: High]
Can this also cause similar memory layout issues on big-endian systems as seen
above?
[ ... ]
> +struct vp_rpt_id_entry_24xx_ext {
> + uint8_t entry_type; /* Entry type. */
> + uint8_t entry_count; /* Entry count. */
> + uint8_t sys_define; /* System defined. */
> + uint8_t entry_status; /* Entry Status. */
> + __le32 resv1;
> + uint8_t vp_acquired;
> + uint8_t vp_setup;
> + __le16 vp_idx : 9; /* VP Index 9bits */
> + __le16 vp_status : 7; /* VP Status 7bits */
[Severity: High]
Does this __le16 bitfield also suffer from endianness packing issues?
> +
> + 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
[Severity: High]
Is struct vp_rpt_id_entry_24xx_ext missing padding at the end to reach the
128-byte IOCB size?
The commit message mentions adding 128-byte IOCB definitions, and other structs
in this file appear padded to 128 bytes, but this struct evaluates to exactly
64 bytes (16 bytes header + 48 bytes union).
If the driver logic uses sizeof(struct vp_rpt_id_entry_24xx_ext) for queue
processing or memcopying entries, could this result in misaligned hardware
command rings or queue corruption?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=9
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 11/56] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx
2026-07-14 9:53 ` [PATCH v4 11/56] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx Nilesh Javali
@ 2026-07-14 11:54 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 11:54 UTC (permalink / raw)
To: Nilesh Javali; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Legacy 64-byte IOCB structures are incorrectly used on 29xx hardware for management, control, and abort paths, causing field corruption and out-of-bounds writes.
- [High] Response queue payload extraction and continuation processing loops fail to account for the 128-byte extended format on 29xx hardware, dropping data.
- [High] The driver miscalculates the required IOCB count (`req_cnt`) for standard SCSI commands on 29xx hardware, causing ring desynchronization.
- [High] Target mode I/O requests with >6 scatter-gather segments on 29xx hardware cause ring desynchronization and zero-length DMA truncation.
- [High] Open-coded legacy response ring advance in `qla27xx_copy_multiple_pkt()` incorrectly increments by 64 bytes on 29xx hardware.
--
--- Patch [11]: [PATCH v4 11/56] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx ---
commit 610d3e25167b47c9d3bb68554aac42ed0e7ff0a5
Author: Anil Gurumurthy <agurumurthy@marvell.com>
scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx
This commit wires the 128-byte extended IOCB structures into the IO
submission, completion, and queue-management paths for the 29xx adapters.
It introduces helpers for determining ring entry sizes and advancing ring
slots while falling back to the existing 64-byte IOCBs for earlier adapters.
> 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
[ ... ]
> @@ -1620,46 +1640,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)) {
[ ... ]
> + /* 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;
[Severity: High]
Could this miscalculate the required IOCB count for standard SCSI commands on
29xx hardware? Earlier in qla24xx_start_scsi(), req_cnt is calculated using
qla24xx_calc_iocbs(). That assumes 1 inline DSD and 5 per continuation, but
29xx extended IOCBs carry 4 inline DSDs and 10 per continuation. Writing the
oversized req_cnt to cmd_pkt_ext->entry_count might desynchronize the ring if
it doesn't match the slots built by qla29xx_build_scsi_iocbs().
[ ... ]
> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> --- a/drivers/scsi/qla2xxx/qla_isr.c
> +++ b/drivers/scsi/qla2xxx/qla_isr.c
[ ... ]
> @@ -287,13 +281,7 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
> break;
> }
>
> - rsp_q->ring_index++;
> - if (rsp_q->ring_index == rsp_q->length) {
> - rsp_q->ring_index = 0;
> - rsp_q->ring_ptr = rsp_q->ring;
> - } else {
> - rsp_q->ring_ptr++;
> - }
> + qla_rsp_ring_advance(rsp_q);
> no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
> sizeof(new_pkt->data) : pending_bytes;
[Severity: High]
Does this truncate the payload extraction on 29xx hardware? The code caps the
read to sizeof(new_pkt->data) (which is 60 bytes) instead of the 124-byte
capacity of the 29xx sts_cont_entry_ext structure. Because
qla_rsp_ring_advance() moves the pointer by 128 bytes, it seems this might
permanently skip the remaining 68 bytes of data in every extended continuation
slot. A similar cap exists in qla27xx_copy_fpin_pkt() and
qla2x00_status_cont_entry().
[Severity: High]
Is there a missing update to qla27xx_copy_multiple_pkt() in this patch?
Many open-coded ring advances were replaced with qla_rsp_ring_advance()
to handle the 128-byte stride on 29xx hardware, but it appears
qla27xx_copy_multiple_pkt() still manually increments rsp_q->ring_ptr++.
Advancing by 64 bytes instead of the 29xx 128-byte stride might cause the
firmware to read zeros and drop multi-IOCB unsolicited ELS/FPIN packets.
[Severity: High]
Does this also cause field corruption on 29xx hardware for abort paths? In
qla24xx_process_abts() in drivers/scsi/qla2xxx/qla_isr.c, the 128-byte slot is
cast to the 64-byte els_entry_24xx struct for the ABTS response, which might
corrupt fields like control_flags. Similarly, legacy 64-byte structures are
still used in qla24xx_abort_iocb(), qla24xx_tm_iocb(), and qla_els_pt_iocb(),
writing vp_index to offset 51 instead of the 18 or 30 required by the 29xx
extended abort/TM IOCBs.
[ ... ]
> diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c
> --- a/drivers/scsi/qla2xxx/qla_target.c
> +++ b/drivers/scsi/qla2xxx/qla_target.c
[ ... ]
> @@ -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));
[Severity: High]
Will target mode I/O requests with more than 6 scatter-gather segments cause
ring desynchronization here? The loop right below this initialization still
hardcodes QLA_TGT_DATASEGS_PER_CONT_24XX (5 DSDs) per continuation packet. On
29xx hardware, the firmware reads 10 DSDs per continuation packet from the
128-byte slot, which could lead to reading empty DSDs and terminating the
transfer early. qlt_pci_map_calc_cnt() also appears to allocate the number of
continuation packets based on 5 DSDs.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=11
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 14/56] scsi: qla2xxx: Enable get_fw_version mailbox for 29xx
2026-07-14 9:53 ` [PATCH v4 14/56] scsi: qla2xxx: Enable get_fw_version mailbox " Nilesh Javali
@ 2026-07-14 12:10 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 12:10 UTC (permalink / raw)
To: Nilesh Javali; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] Missing QLA29XX hardware state initialization in `qla2x00_execute_fw()` results in dead code and missing features.
- [High] Missing macro and memory allocation updates for QLA29XX EDIF support lead to unallocated memory pools.
- [Medium] The patch populates `mpi_version` and `pep_version` capability fields for 29XX adapters but fails to expose them to userspace due to missing sysfs updates.
Pre-existing issues:
- [Critical] Incorrect mempool_free on a DMA pool allocation in the error path.
--
--- Patch [14]: [PATCH v4 14/56] scsi: qla2xxx: Enable get_fw_version mailbox for 29xx ---
commit 7ec86c4145546172d16b2ee833700898350294f6
Author: Nilesh Javali <njavali@marvell.com>
Subject: scsi: qla2xxx: Enable get_fw_version mailbox for 29xx
This patch populates the serdes_version and several firmware capability fields
for 29xx adapters by including them in the get_fw_version mailbox path. It
adds IS_QLA29XX() checks to the relevant conditionals so that firmware version,
EDIF, and serdes information are retrieved correctly.
> diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
> --- a/drivers/scsi/qla2xxx/qla_attr.c
> +++ b/drivers/scsi/qla2xxx/qla_attr.c
> @@ -1526,7 +1526,7 @@ qla2x00_serdes_version_show(struct device *dev, struct device_attribute *attr,
> scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
> struct qla_hw_data *ha = vha->hw;
>
> - if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
> + if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
> return scnprintf(buf, PAGE_SIZE, "\n");
[Severity: Medium]
Since we are exposing the serdes_version for 29xx here, should we also
update qla2x00_mpi_version_show() and qla2x00_pep_version_show() in this
same file?
The patch populates ha->mpi_version and ha->pep_version in
qla2x00_get_fw_version() for 29xx, but the sysfs show functions still
restrict output to older models. This causes them to return an empty line,
hiding the newly available data.
[ ... ]
> 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)) {
> ha->flags.edif_enabled = 1;
[Severity: High]
Will ha->flags.edif_hw ever actually be set for 29xx adapters?
Looking at qla2x00_execute_fw() in qla_mbx.c, the initialization seems to be
missing the IS_QLA29XX check:
qla_mbx.c:qla2x00_execute_fw()
if (IS_QLA28XX(ha) && (mcp->mb[5] & EDIF_HW_SUPPORT)) {
ha->flags.edif_hw = 1;
If ha->flags.edif_hw remains 0, won't this new check in get_fw_version() act
as dead code for 29xx?
Additionally, qla2x00_execute_fw() appears to be missing 29xx checks for
NVME_ENABLE_FLAG and for initializing ha->max_supported_speed.
[Severity: High]
If EDIF is enabled on a 29xx adapter, will this lead to unallocated memory
pools?
The EDIF_CAP macro in qla_edif.h only checks IS_QLA28XX. Furthermore, in
qla_os.c:qla2x00_mem_alloc(), the memory pools are allocated like this:
qla_os.c:qla2x00_mem_alloc()
if (ql2xenabledif || (IS_QLA28XX(ha) && ql2xsecenable)) {
ha->ctx_mempool = mempool_create_slab_pool(...)
Without IS_QLA29XX here, ha->ctx_mempool and ha->dl_dma_pool won't be
allocated when ql2xsecenable=1 on a 29xx adapter. Could this cause null
pointer dereferences when the driver attempts to use EDIF paths?
[Severity: Critical]
This is a pre-existing issue, but I noticed a potential memory corruption
hazard in the error handling paths for type 6 SCSI commands.
In qla_iocb.c:qla_start_scsi_type6() and qla2xxx_dif_start_scsi_mq(),
sp->u.scmd.crc_ctx is allocated from a DMA pool:
sp->u.scmd.crc_ctx = dma_pool_zalloc(ha->dl_dma_pool, GFP_ATOMIC, ...);
However, if an error occurs later (e.g., mapping fails or resources run
out), the error path frees it using a different mempool:
mempool_free(sp->u.scmd.crc_ctx, ha->ctx_mempool);
Since ha->ctx_mempool is a slab-backed mempool (meant for struct ct6_dsd)
and not a DMA pool, doesn't freeing a DMA-backed address to this slab pool
corrupt memory?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=14
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 16/56] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx
2026-07-14 9:53 ` [PATCH v4 16/56] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx Nilesh Javali
@ 2026-07-14 12:19 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 12:19 UTC (permalink / raw)
To: Nilesh Javali; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Medium] The patch enables reading Buffer-to-Buffer Credit (BBCR) data for 29xx adapters but fails to update the corresponding BSG handler `qla27xx_get_bbcr_data()`, preventing userspace retrieval.
Pre-existing issues:
- [High] Kernel stack memory leak to userspace due to uninitialized variable usage when `qla2x00_mailbox_command` fails.
--
--- Patch [16]: [PATCH v4 16/56] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx ---
commit 357f8972de7904807dc36791b48b4a0731c044b6
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx
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.
> 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))
> mcp->in_mb |= MBX_15|MBX_21|MBX_22|MBX_23;
[Severity: Medium]
Will userspace actually be able to retrieve BBCR data for 29xx adapters?
Looking at qla27xx_get_bbcr_data() in drivers/scsi/qla2xxx/qla_bsg.c,
the BSG handler seems to explicitly reject anything other than 27xx and 28xx:
drivers/scsi/qla2xxx/qla_bsg.c:qla27xx_get_bbcr_data() {
...
if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
return -EPERM;
...
}
Could qla27xx_get_bbcr_data() also need an update to allow 29xx adapters so
userspace doesn't just receive an -EPERM error when requesting this data?
[ ... ]
> @@ -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) {
[Severity: High]
This is a pre-existing issue, but does qla2x00_get_adapter_id() leak
uninitialized kernel stack memory to userspace on error paths?
In qla2x00_get_adapter_id(), mc is allocated on the stack but not fully
initialized. If qla2x00_mailbox_command() fails, mcp->mb[1] remains
uninitialized, but it is unconditionally copied to the output pointer:
drivers/scsi/qla2xxx/qla_mbx.c:qla2x00_get_adapter_id() {
mbx_cmd_t mc;
...
rval = qla2x00_mailbox_command(vha, mcp);
...
*id = mcp->mb[1];
...
}
When called from qla27xx_get_bbcr_data(), this uninitialized loop_id
(which comes from *id) is copied to userspace via bbcr.mbx1 when an
error is handled:
drivers/scsi/qla2xxx/qla_bsg.c:qla27xx_get_bbcr_data() {
...
if (rval != QLA_SUCCESS) {
bbcr.status = QLA_BBCR_STATUS_UNKNOWN;
bbcr.state = QLA_BBCR_STATE_OFFLINE;
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 initialized to zero, or should the output assignments be skipped
if qla2x00_mailbox_command() fails?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=16
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 02/56] scsi: qla2xxx: Add flash read/write interface for 29xx
2026-07-14 9:52 ` [PATCH v4 02/56] scsi: qla2xxx: Add flash read/write interface for 29xx Nilesh Javali
@ 2026-07-14 12:26 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 12:26 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] The newly added qla29xx flash read/write functions are not hooked to the isp_ops struct, breaking sysfs/BSG flash access.
- [Medium] Integer overflow in total_chunks and total_segments variables, causing an infinite loop in flash reads/writes
- [Low] The check_and_set_mbc_bits macro lacks a do { ... } while(0) wrapper, violating coding guidelines and causing potential compilation errors in unbraced conditionals.
--
--- Patch [2]: [PATCH v4 02/56] scsi: qla2xxx: Add flash read/write interface for 29xx ---
commit cc824b368b84bc7c06a17348bb30ee20ef746c82
Author: Manish Rangankar <mrangankar@marvell.com>
scsi: qla2xxx: Add flash read/write interface for 29xx
This patch adds the mailbox wrappers and helpers needed for flash read and
write operations on the 29xx series adapters. It provides the infrastructure
to parse the flash layout table and execute block reads and writes.
> 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
[ ... ]
> @@ -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; \
> +}
[Severity: Low]
This isn't a bug, but this macro is defined as a block rather than with a
do-while(0) loop. Could this cause compilation errors if used inside an
unbraced if statement followed by an else?
> +
> +#define SET_FW_BIT(__opts, bit) ((__opts) |= (bit))
> +#define CLEAR_FW_BIT(__opts, bit) ((__opts) &= ~(bit))
[ ... ]
> 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]
The commit message mentions adding the necessary hooks in isp_ops so that the
existing flash infrastructure can drive the new hardware. However, it looks
like the patch fails to provide the wrapper functions to map the 6-argument
qla29xx_read_optrom_data to the 4-argument isp_ops->read_optrom.
Does this mean sysfs/BSG flash access will fall back to legacy routines and
break due to ha->flash_data_off being ~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
[ ... ]
> +qla29xx_write_optrom_data(struct scsi_qla_host *vha, uint16_t reg_code,
> + uint16_t opts, void *buf, uint32_t offset,
> + uint32_t length)
> +{
[ ... ]
> + uint16_t total_segments, segment_index = 0;
> + uint16_t chunk_index = 0, chunk_count = 0;
[ ... ]
> + 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;
[ ... ]
> + chunk_index++;
> + chunk_count++;
> + if (chunk_index >= total_chunks) {
[Severity: Medium]
Will this loop ever terminate if total_chunks is greater than 65535?
Because chunk_index is declared as a uint16_t while total_chunks is a
uint32_t, chunk_index will wrap around to 0.
Could total_segments also suffer from integer truncation since it is
declared as a uint16_t?
> + chunk_index = 0;
> + segment_index++;
> + region_dlen -= segment_size;
> + }
> + }
[ ... ]
> +qla29xx_read_optrom_data(struct scsi_qla_host *vha, uint16_t reg_code,
> + uint16_t opts, void *buf, uint32_t offset,
> + uint32_t length)
> +{
[ ... ]
> + uint16_t total_segments, segment_index = 0;
> + uint16_t chunk_index = 0, chunk_count = 0;
[ ... ]
> + 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;
[ ... ]
> + chunk_index++;
> + chunk_count++;
> + if (chunk_index >= total_chunks) {
[Severity: Medium]
Similarly, does this read path also suffer from the same uint16_t wrap
issue for chunk_index and total_segments?
> + chunk_index = 0;
> + segment_index++;
> + region_dlen -= segment_size;
> + }
> + }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=2
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 17/56] scsi: qla2xxx: Enable init_firmware mailbox for 29xx
2026-07-14 9:53 ` [PATCH v4 17/56] scsi: qla2xxx: Enable init_firmware " Nilesh Javali
@ 2026-07-14 12:48 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 12: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:
- [Low] SFP validation debug logging for QLA29XX is missing in qla2x00_get_firmware_state().
--
--- Patch [17]: [PATCH v4 17/56] scsi: qla2xxx: Enable init_firmware mailbox for 29xx ---
commit 46f14f9f25f252d1cca6f3d9503e629aa6ac9d97
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Enable init_firmware mailbox for 29xx
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.
> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index 3fc08120fdf11..9c78aa66e12b3 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");
[Severity: Low]
Does this code also need to update qla2x00_get_firmware_state()?
In qla2x00_get_firmware_state(), there is a similar block that logs a
warning for an invalid SFP, but it currently only checks for 27XX and 28XX:
drivers/scsi/qla2xxx/qla_mbx.c:qla2x00_get_firmware_state() {
...
} else {
if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
if (mcp->mb[2] == 6 || mcp->mb[3] == 2)
ql_dbg(ql_dbg_mbx, vha, 0x119e,
"Invalid SFP/Validation Failed\n");
}
...
}
Will retrieving the firmware state on a QLA29XX adapter with an invalid SFP
fail to produce the expected debug log?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=17
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 19/56] scsi: qla2xxx: Enable serdes, resource count and FCE trace for 29xx
2026-07-14 9:53 ` [PATCH v4 19/56] scsi: qla2xxx: Enable serdes, resource count and FCE trace " Nilesh Javali
@ 2026-07-14 13:10 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 13:10 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] Incorrect usage of `kstrtoul` in `qla2x00_dfs_fce_write` passes a NULL pointer for the result argument, causing an unconditional kernel oops upon successful parsing.
- [High] A TOCTOU race condition in `qla2x00_dfs_fce_release` can lead to a NULL pointer dereference when accessing `ha->fce`.
- [High] Concurrent non-atomic modification of the `ha->flags` bitfield leads to data corruption, potentially dropping critical interrupts or mailbox states.
- [High] A concurrent firmware dump can trigger a Use-After-Free on `ha->fce` while the FCE trace is being disabled via DebugFS.
--
--- Patch [19]: [PATCH v4 19/56] scsi: qla2xxx: Enable serdes, resource count and FCE trace for 29xx ---
commit c0d2af5f457ed884072ede4fabe84388c9abed9e
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Enable serdes, resource count and FCE trace for 29xx
The 29xx adapters share the diagnostic and management interfaces
already supported on ISP27xx/28xx, but several family capability
gates still omitted IS_QLA29XX(), leaving these paths unreachable
on 29xx.
> diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c
> index 43970caca7b39..177d47e92e494 100644
> --- a/drivers/scsi/qla2xxx/qla_dfs.c
> +++ b/drivers/scsi/qla2xxx/qla_dfs.c
> @@ -497,7 +497,7 @@ qla2x00_dfs_fce_write(struct file *file, const char __user *buffer,
> unsigned long enable;
>
> if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
> - !IS_QLA27XX(ha) && !IS_QLA28XX(ha)) {
> + !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) {
> ql_dbg(ql_dbg_user, vha, 0xd034,
> "this adapter does not support FCE.");
> return -EINVAL;
[Severity: High]
This isn't a bug introduced by this patch, but looking at the parsing logic
further down in qla2x00_dfs_fce_write():
qla2x00_dfs_fce_write() {
...
enable = kstrtoul(buf, 0, 0);
...
}
Does this cause an unconditional kernel oops? kstrtoul() writes the parsed
result to the pointer provided in the third argument, which is 0 (NULL)
here. Additionally, the return value (0 on success, negative on error) is
assigned to enable, which appears to invert the intended logic.
[Severity: High]
This is a pre-existing issue, but does modifying the bitfields in
qla2x00_dfs_fce_write() risk dropping mailbox interrupts?
qla2x00_dfs_fce_write() {
...
ha->flags.user_enabled_fce = 1;
...
}
Since user_enabled_fce shares the same 32-bit volatile struct word as
mbox_int and mbox_busy, and compilers use non-atomic read-modify-write
operations for bitfields, can a concurrent interrupt handler modifying those
mailbox flags overwrite this update or vice versa?
[Severity: High]
This is also a pre-existing issue, but is there a use-after-free risk when
disabling the FCE trace?
In qla2x00_dfs_fce_write(), the trace buffer is freed:
qla2x00_free_fce_trace(ha);
This is called under ha->fce_mutex. However, a concurrent firmware dump in
qla25xx_fw_dump() calls qla25xx_copy_fce() which copies data from ha->fce
while only holding ha->hardware_lock:
qla25xx_copy_fce() {
...
memcpy(iter_reg, ha->fce, ntohl(fcec->size));
...
}
If these paths do not share a lock, could a concurrent firmware dump read
from the freed FCE buffer?
[Severity: High]
This is another pre-existing issue, but could a race condition cause a NULL
pointer dereference in qla2x00_dfs_fce_release()?
The release function checks ha->fce outside of the mutex:
qla2x00_dfs_fce_release() {
if (ha->flags.fce_enabled || !ha->fce)
goto out;
mutex_lock(&ha->fce_mutex);
ha->flags.fce_enabled = 1;
memset(ha->fce, 0, fce_calc_size(ha->fce_bufs));
...
}
If a concurrent write to qla2x00_dfs_fce_write() acquires the mutex, frees
ha->fce, sets it to NULL, and releases the mutex, will the release thread
then acquire the mutex and execute memset() on the NULL pointer?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=19
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 18/56] scsi: qla2xxx: Enable get_firmware_state for 29xx
2026-07-14 9:53 ` [PATCH v4 18/56] scsi: qla2xxx: Enable get_firmware_state " Nilesh Javali
@ 2026-07-14 13:13 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 13:13 UTC (permalink / raw)
To: Nilesh Javali; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Kernel stack data is leaked to userspace via the `fw_state` sysfs attribute for non-FWI2 host bus adapters.
--
--- Patch [18]: [PATCH v4 18/56] scsi: qla2xxx: Enable get_firmware_state for 29xx ---
commit ef943e04ab0672ac4bb82500a8cba28415fa19ec
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Enable get_firmware_state for 29xx
Enable get_firmware_state mailbox command for 29xx adapters by adding
IS_QLA29XX() checks alongside existing IS_QLA27XX/IS_QLA28XX checks.
This ensures MBX_12 (MPI state) is properly set up and reported for
29xx adapters.
> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c
[ ... ]
> @@ -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 */
[Severity: High]
This is a pre-existing issue, but does this conditional assignment leave
the remaining array elements uninitialized for non-FWI2 adapters?
Looking at qla2x00_fw_state_show() in drivers/scsi/qla2xxx/qla_attr.c,
it appears to allocate an uninitialized array on the kernel stack and
unconditionally prints state[0] through state[5] to sysfs:
uint16_t state[16];
...
return scnprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
state[0], state[1], state[2], state[3], state[4], state[5]);
Since qla2x00_get_firmware_state() only populates states[0] when the adapter
is not FWI2 capable, could this leak uninitialized kernel stack memory to
userspace when reading the fw_state sysfs attribute?
> - if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
> + if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
> states[11] = mcp->mb[12]; /* MPI state. */
> }
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=18
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 22/56] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx
2026-07-14 9:53 ` [PATCH v4 22/56] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx Nilesh Javali
@ 2026-07-14 13:30 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 13:30 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] Mismatched allocator and deallocator for crc_ctx in the command queuing error paths.
- [High] Logic inversion and Use-After-Free in qla2x00_abort_srb when waiting for firmware abort completion.
--
--- Patch [22]: [PATCH v4 22/56] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx ---
commit f4293733252c862c9a670e30cbe9ec9b3965ea0f
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);
> } else {
[Severity: High]
This is a pre-existing issue, but is it safe to free a DMA pool allocation
using mempool_free()?
In the queuing_error paths of qla24xx_start_scsi() and
qla2xxx_dif_start_scsi_mq() when I/O submission fails, the dma_pool_zalloc'd
crc_ctx is freed using mempool_free():
drivers/scsi/qla2xxx/qla_iocb.c:qla24xx_start_scsi() {
...
mempool_free(sp->u.scmd.crc_ctx, ha->ctx_mempool);
...
}
drivers/scsi/qla2xxx/qla_iocb.c:qla2xxx_dif_start_scsi_mq() {
...
mempool_free(sp->u.scmd.crc_ctx, ha->ctx_mempool);
...
}
Could this lead to memory corruption or allocator crashes? Furthermore, if
ha->ctx_mempool is null (which happens when dif is enabled but security is
not), could this cause a null pointer dereference?
[Severity: High]
This isn't a bug introduced by this patch, but does this logic inversion
cause a use-after-free and potential stack corruption?
Looking at qla2x00_abort_srb() when waiting for firmware abort completion:
drivers/scsi/qla2xxx/qla_os.c:qla2x00_abort_srb() {
...
if (wait_for_completion_timeout(&comp, ratov_j)) {
ql_dbg(ql_dbg_taskm, vha, 0xffff,
"%s: Abort wait timer (4 * R_A_TOV[%d]) expired\n",
__func__, ha->r_a_tov/10);
ret_cmd = true;
}
...
}
Since wait_for_completion_timeout() returns > 0 on successful completion,
is a success treated as a timeout here due to a missing logical NOT (!)?
Because successful completion means the interrupt handler already freed the
srb (sp), will the function then dereference sp->type and call
sp->done(sp, res), resulting in a use-after-free?
Conversely, if a genuine timeout occurs and wait_for_completion_timeout()
returns 0, will it skip this block entirely? Would this leave sp->comp
pointing to the stack-allocated comp, causing stack corruption when the
firmware eventually responds?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=22
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 25/56] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters
2026-07-14 9:53 ` [PATCH v4 25/56] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters Nilesh Javali
@ 2026-07-14 13:45 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 13: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:
- [Critical] Reading up to 124 bytes from a pointer derived from a 60-byte array triggers a FORTIFY_SOURCE runtime kernel panic on QLA29XX adapters.
--
--- Patch [25]: [PATCH v4 25/56] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters ---
commit c469c92ca0a450f7a9aa8d8525c84c9f7c522b1c
Author: Nilesh Javali <njavali@marvell.com>
Subject: scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters
This commit adds support for 128-byte response queue entries on 29xx
adapters. It updates status continuation IOCB processing paths to use
the correct entry type and data payload size based on the adapter type.
> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> index 26722afa937c8..f668012de3d44 100644
> --- 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;
[Severity: Critical]
Will this trigger a FORTIFY_SOURCE panic on QLA29XX adapters?
The pointer data is unconditionally derived by casting to sts_cont_entry_t,
which has a 60-byte data array. The compiler tracks this 60-byte bound,
so when memcpy() copies up to 124 bytes later (from the 128-byte continuation
entry), FORTIFY_SOURCE will detect a read buffer overflow and panic the kernel.
> + 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) {
[ ... ]
> @@ -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;
[Severity: Critical]
Does this face the same FORTIFY_SOURCE bounds checking issue on the 60-byte
data array as __qla_copy_purex_to_buffer() above?
> + data_sz = qla_sts_cont_data_size(ha);
[ ... ]
> @@ -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;
[Severity: Critical]
Does this face the same FORTIFY_SOURCE bounds checking issue on the 60-byte
data array as above?
> + data_sz = qla_sts_cont_data_size(ha);
[ ... ]
> @@ -3682,7 +3701,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
> * Extended sense data.
> */
> static void
> -qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
> +qla2x00_status_cont_entry(struct rsp_que *rsp, void *pkt, u32 data_sz)
> {
> uint8_t sense_sz = 0;
> struct qla_hw_data *ha = rsp->hw;
> @@ -3691,6 +3710,7 @@ qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
> struct scsi_cmnd *cp;
> uint32_t sense_len;
> uint8_t *sense_ptr;
> + u8 *data = ((sts_cont_entry_t *)pkt)->data;
[Severity: Critical]
The commit message mentions that qla2x00_status_cont_entry() was changed to
accept void * so it can internally select the right structure based on adapter
type. However, doesn't this hardcoded cast to sts_cont_entry_t * defeat that
and restrict the pointer to a 60-byte array, causing the same FORTIFY_SOURCE
panic when sense_sz is larger than 60 bytes?
>
> if (!sp || !GET_CMD_SENSE_LEN(sp))
> return;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=25
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 26/56] scsi: qla2xxx: Update handling of status entries for 29xx series
2026-07-14 9:53 ` [PATCH v4 26/56] scsi: qla2xxx: Update handling of status entries for 29xx series Nilesh Javali
@ 2026-07-14 14:21 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 14:21 UTC (permalink / raw)
To: Nilesh Javali; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Integer underflow in `par_sense_len` calculation allows an out-of-bounds read of the response queue memory into the SCSI command's sense buffer.
--
--- Patch [26]: [PATCH v4 26/56] scsi: qla2xxx: Update handling of status entries for 29xx series ---
commit e2f9d1fbc9c40b5adf48f40bcf83f40782a2ac76
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Update handling of status entries for 29xx series
This commit updates the handling of status entries in the qla2xxx driver
to support the 29xx series. It updates function signatures to accept generic
pointers for status packets and differentiates between standard and
extended status entries.
> 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;
> 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;
[Severity: High]
This is a pre-existing issue, but could this par_sense_len calculation
lead to an integer underflow?
Further down in qla2x00_status_entry(), when checking for FCP transport
errors, the code adjusts these pointers:
if (IS_FWI2_CAPABLE(ha)) {
sense_data += rsp_info_len;
par_sense_len -= rsp_info_len;
}
If a maliciously large rsp_info_len is received in the FCP_RSP frame,
par_sense_len could underflow since there is no validation checking if
rsp_info_len exceeds par_sense_len.
Later in qla2x00_handle_sense(), this bounds check wouldn't protect against
the underflowed par_sense_len:
if (sense_len > par_sense_len)
sense_len = par_sense_len;
memcpy(cp->sense_buffer, sense_data, sense_len);
Can this result in an out-of-bounds read of the response queue memory into
the SCSI command's sense buffer?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=26
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 30/56] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx
2026-07-14 9:53 ` [PATCH v4 30/56] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx Nilesh Javali
@ 2026-07-14 14:31 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 14: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:
Pre-existing issues:
- [High] Preexisting endianness bug in 29xx extended IOCB bitfields, which will cause corrupted DMA descriptors on Big Endian architectures.
--
--- Patch [30]: [PATCH v4 30/56] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx ---
commit ef8ae6f09d5507520905d2b5e51c92a44459978f
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Add size check for ELS status entry layout on 29xx
This commit adds a BUILD_BUG_ON size check for the 29xx extended ELS status
entry layout. It also adds a documentation block explaining the byte-identical
offsets shared between the 24xx and 29xx layouts to justify casting in the
completion handler.
> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> index e2653620e80be..c286465ae013d 100644
> --- a/drivers/scsi/qla2xxx/qla_isr.c
> +++ b/drivers/scsi/qla2xxx/qla_isr.c
> @@ -2359,6 +2359,22 @@ static void
> qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
> void *pkt, int iocb_type)
> {
> + /*
> + * els_sts_entry_24xx_ext (29xx) overlays els_sts_entry_24xx for every
> + * field touched in this completion handler: comp_status (offset 8),
> + * total_byte_count (32), error_subcode_1 (36), error_subcode_2 (40),
> + * d_id[]/s_id[] (24..29), control_flags (30) all sit at byte-identical
> + * offsets in both layouts (only vp_index/sof_type at offset 14-15 are
> + * bit-packed differently, and that field is write-only on the issue
> + * path -- we never read it here). All reads in this function are
[Severity: High]
This is a pre-existing issue, but since this new comment mentions the
differently bit-packed vp_index and sof_type fields on the issue path, is it
possible these bitfields are fundamentally broken on Big Endian machines?
Looking at struct els_entry_24xx_ext in drivers/scsi/qla2xxx/qla_fw29.h:
__le16 vp_index : 9;
__le16 reserved_1_sof : 3;
__le16 sof_type : 4;
And its assignment in drivers/scsi/qla2xxx/qla_inline.h:qla_els_set_vp_sof():
ext->vp_index = vp_idx;
ext->sof_type = ELS_EXT_EST_SOFI3;
Since compilers pack bitfields based on host endianness (typically starting
from the MSB on Big Endian systems) and do not automatically byte-swap
bitfields on assignment to an __le16 type, will this result in garbled DMA
descriptors when the Little Endian firmware reads this 16-bit word on a
Big Endian host?
> + * 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;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=30
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 28/56] scsi: qla2xxx: Enhance purex_entry handling for 29xx series
2026-07-14 9:53 ` [PATCH v4 28/56] scsi: qla2xxx: Enhance purex_entry " Nilesh Javali
@ 2026-07-14 14:45 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 14:45 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] Incomplete fix for FORTIFY_SOURCE buffer limits in continuation IOCBs on QLA29xx adapters, leading to guaranteed kernel panics on multi-IOCB frames.
- [Medium] Silent truncation of the 16-bit Virtual Port (VP) index to 8 bits, breaking >255 VP support on 29xx adapters.
Pre-existing issues:
- [Critical] `__qla_consume_iocb` incorrectly loops `entry_count` times without accounting for the already-consumed head IOCB, skipping the head signature and discarding unrelated IOCBs.
--
--- Patch [28]: [PATCH v4 28/56] scsi: qla2xxx: Enhance purex_entry handling for 29xx series ---
commit 91252616a5e800941a18bc247ce6eecf4c680a85
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Enhance purex_entry handling for 29xx series
This updates function signatures and internal logic across multiple files
to accept a generic pointer for packet data. It differentiates between
standard purex_entry_24xx and extended purex_entry_24xx_ext structures
based on the IS_QLA29XX() macro to support 64-byte and 128-byte IOCBs.
> 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: Medium]
Can this silently truncate the virtual port index on 29xx adapters?
The 29xx extended structure defines a 16-bit vp_idx to support 9 bits of
virtual port index data. However, the value is being stored in the new
uint8_t vp_idx local variable in both qla24xx_auth_els() and
qla24xx_process_purex_rdp(). Does this break support for setups with more
than 255 NPIV vports by silently discarding the upper bits?
Additionally, when qla24xx_process_purex_rdp() populates the response
IOCB later on, writing the 8-bit vp_idx and sof_type into rsp_els
overwrites offsets 14 and 15 individually. Could this corrupt the 9th bit
of the __le16 vp_idx_sof union expected by the 29xx firmware?
[ ... ]
> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> index c38771e4202c0..c4e2e62f924ff 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,
> new_pkt = rsp_q->ring_ptr;
> *pkt = new_pkt;
[Severity: Critical]
This is a pre-existing issue, but does this loop correctly handle the head
IOCB?
In __qla_consume_iocb(), the while loop processes entry_count_remaining
items by advancing the ring pointer and marking new_pkt as
RESPONSE_PROCESSED. Because qla24xx_process_response_queue() has already
advanced the ring pointer over the head IOCB before calling this function,
this loop appears to leave the head IOCB's signature unmarked.
Consequently, it looks like it will consume and discard unrelated subsequent
IOCBs by looping entry_count times from the already-advanced position. Could
this lead to response ring corruption or a permanent queue stall when the
ring wraps around and hits the unmarked head IOCB?
[ ... ]
> @@ -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: Critical]
Will the continuation IOCB extraction loops trigger a fortify source panic
on 29xx adapters?
Further down in __qla_copy_purex_to_buffer() (and similarly in the loops
for qla27xx_copy_multiple_pkt(), qla27xx_copy_fpin_pkt(), and
qla2x00_status_cont_entry()), the code casts the packet to sts_cont_entry_t,
which defines data as a 60-byte array:
data = ((sts_cont_entry_t *)*pkt)->data;
data_sz = qla_sts_cont_data_size(ha);
...
memcpy((buf + buffer_copy_offset), data, no_bytes);
However, on 29xx adapters, data_sz can be up to 124 bytes. When memcpy()
attempts to copy up to 124 bytes from a struct member declared as a 60-byte
array, won't this trigger a __read_overflow2_field panic when processing
multi-IOCB PUREX, PURLS, or FPIN frames?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=28
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 31/56] scsi: qla2xxx: Add 29xx extended logio IOCB support
2026-07-14 9:53 ` [PATCH v4 31/56] scsi: qla2xxx: Add 29xx extended logio IOCB support Nilesh Javali
@ 2026-07-14 14:49 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 14: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:
Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but DMA memory corruption occurs due to freeing the IOCB buffer before the firmware is reset on a timeout.
--
--- Patch [31]: [PATCH v4 31/56] scsi: qla2xxx: Add 29xx extended logio IOCB support ---
commit fd255e4f21f55aa8baef081e73e914c2d628d372
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Add 29xx extended logio IOCB support
This commit adds extended logio IOCB support for the 29xx series, which uses
a wider IOCB stride (128 bytes vs 64 bytes). It updates various logio IOCB
builder and completion functions to accept a void pointer and cast dynamically
to accommodate both layouts.
> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index f9b326c020557..7dff227899a52 100644
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c
[ ... ]
> @@ -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);
[Severity: High]
This is a pre-existing issue, but could this lead to DMA memory corruption if
the command times out?
If qla2x00_issue_iocb_timeout() times out, it schedules an asynchronous DPC
reset and returns, causing lg_buf to be immediately freed back to the DMA
pool here in qla24xx_login_fabric().
If the firmware is merely slow and completes the command before the adapter
reset fully finishes, won't it write the completion status to the physical
address (lg_dma) of this freed buffer?
This could corrupt whatever new allocation is currently using that DMA memory.
>
> return rval;
> }
[ ... ]
> @@ -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);
[Severity: High]
This is the same pre-existing issue, but in qla24xx_fabric_logout().
Does freeing lg_buf here after a timeout expose the system to use-after-free
DMA corruption if the hardware writes to lg_dma before the asynchronous reset
completes?
>
> return rval;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=31
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 29/56] scsi: qla2xxx: Update handling of ELS IOCBs for 29xx series
2026-07-14 9:53 ` [PATCH v4 29/56] scsi: qla2xxx: Update handling of ELS IOCBs " Nilesh Javali
@ 2026-07-14 15:00 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 15:00 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] Endian-unsafe use of C bitfields for hardware-facing DMA descriptors in `qla_els_set_vp_sof()`.
--
--- Patch [29]: [PATCH v4 29/56] scsi: qla2xxx: Update handling of ELS IOCBs for 29xx series ---
commit c4548b60cd936f5a4a71288b8a287eb9009e46b8
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Update handling of ELS IOCBs for 29xx series
Update ELS IOCB handling to support the extended 128-byte
els_entry_24xx_ext structure used by 29xx series adapters.
> diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
> index 49b858e7cf2a1..2ade5e8522516 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;
[Severity: High]
Can the use of C bitfields here cause endianness issues on big-endian
architectures?
Since the hardware DMA descriptor struct els_entry_24xx_ext defines these
fields as:
__le16 vp_index : 9;
__le16 reserved_1_sof : 3;
__le16 sof_type : 4;
the compiler will pack the bitfields starting from the most significant bit
of the word on big-endian systems (such as s390x or PowerPC). However, the
little-endian 29xx hardware expects vp_index to occupy the lowest 9 bits.
Could this mismatch cause the adapter firmware to read corrupted Virtual Port
(VP) indices and SOF types when qla_els_set_vp_sof() is called on the ELS
issue paths, potentially routing ELS commands to the wrong VP and leading to
login failures, cross-VM traffic leakage, or firmware crashes?
> + } else {
> + struct els_entry_24xx *e = pkt;
> +
> + e->vp_index = vp_idx;
> + e->sof_type = EST_SOFI3;
> + }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=29
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 33/56] scsi: qla2xxx: Add abort command handling for 29xx series
2026-07-14 9:53 ` [PATCH v4 33/56] scsi: qla2xxx: Add abort command " Nilesh Javali
@ 2026-07-14 15:18 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 15:18 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] Deadlock risk in the abort path due to `GFP_KERNEL` allocation inside SCSI error handler / NVMe abort workqueue.
- [High] Time-Of-Check to Time-Of-Use (TOCTOU) race condition in the abort path allows the firmware to abort an unrelated, newly queued command.
--
--- Patch [33]: [PATCH v4 33/56] scsi: qla2xxx: Add abort command handling for 29xx series ---
commit 062493151f7c65c9841b286b24c4fdbc4ccf220d
Author: Nilesh Javali <njavali@marvell.com>
Subject: scsi: qla2xxx: Add abort command handling for 29xx series
This commit extends 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_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index 9bef87862077e..0d7aa6fce0072 100644
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c
> @@ -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);
[Severity: High]
This is a pre-existing issue, but does this allocation risk causing a
deadlock? When an IO times out, the error handling path (such as
qla2xxx_eh_abort or the NVMe qla_nvme_abort_work) calls
qla24xx_abort_command().
If the system is under memory pressure, using GFP_KERNEL allows the
allocator to sleep and enter memory reclaim. If the reclaim process
attempts to flush dirty pages to the block layer, and those IOs are
routed to this same HBA, it creates a circular dependency where the
block layer waits for the HBA to recover, but the HBA recovery thread is
blocked here waiting for memory reclaim.
Should this use GFP_ATOMIC or GFP_NOIO to avoid re-entering the storage
stack?
Also, this sleeping allocation seems to open a race window. Before this
allocation, qla24xx_abort_command() releases the hardware queue lock:
spin_lock_irqsave(qpair->qp_lock_ptr, flags);
for (handle = 1; handle < req->num_outstanding_cmds; handle++) {
if (req->outstanding_cmds[handle] == sp)
break;
}
spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
During the unlocked, sleeping window in dma_pool_zalloc(), can the
original command complete naturally, freeing up the handle? If a
concurrent thread issuing a new IO allocates this same freed handle for
an unrelated command, could the firmware end up aborting the wrong,
newly queued command once we resume?
> + 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);
[Severity: High]
This isn't a bug introduced by this patch, but if the handle was reassigned
during the GFP_KERNEL allocation above, wouldn't we embed a stale handle
into the abort payload here?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=33
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 37/56] scsi: qla2xxx: Add size check for extended VP report ID entry
2026-07-14 9:53 ` [PATCH v4 37/56] scsi: qla2xxx: Add size check for extended VP report ID entry Nilesh Javali
@ 2026-07-14 15:40 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 15:40 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 hardware-defined structure `vp_rpt_id_entry_24xx_ext` uses C bitfields (`vp_idx : 9` and `vp_status : 7`) to map to Little-Endian firmware IOCB data, causing endianness and ABI layout corruption on Big-Endian architectures.
Pre-existing issues:
- [Critical] The virtual port (`vp`) pointer is accessed and modified after releasing `ha->vport_slock` without acquiring a reference.
- [Critical] The `fcport` pointer is dereferenced and modified after the `sess_lock` is released, without acquiring a reference.
- [Critical] In the format 2 branch of `qla24xx_report_id_acquisition`, the driver iterates over the `vha->vp_fcports` list to modify `fcport->scan_state` without acquiring `vha->hw->tgt.sess_lock`.
- [High] Missing endianness conversion when reading the little-endian `bbcr` hardware field.
--
--- Patch [37]: [PATCH v4 37/56] scsi: qla2xxx: Add size check for extended VP report ID entry ---
commit 0a18f95655fa2d187c369d0dd1d03c6b0b2a0d73
Author: Nilesh Javali <njavali@marvell.com>
Subject: scsi: qla2xxx: Add size check for extended VP report ID entry
This commit adds padding to the extended VP report ID entry to match
the hardware IOCB stride. It also updates qla24xx_report_id_acquisition()
to accept a void pointer and extract the virtual port index and status
from the extended structure on 29xx series adapters.
> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index 3ebda35dd584f..cec308811d9eb 100644
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c
> @@ -4102,6 +4105,14 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
> if (rptid_entry->entry_status != 0)
> return;
>
> + if (IS_QLA29XX(ha)) {
> + vp_idx = rptid_entry_ext->vp_idx;
> + vp_status = rptid_entry_ext->vp_status;
[Severity: High]
Do C bitfields map correctly on Big-Endian architectures here?
The hardware-defined structure vp_rpt_id_entry_24xx_ext maps to
Little-Endian firmware IOCB data. If vp_idx and vp_status are defined
as bitfields, this can cause endianness and ABI layout corruption on
Big-Endian hosts, leading to mapping to the wrong virtual ports.
[ ... ]
> @@ -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 is an endianness conversion missing when
reading the bbcr field here?
The hardware bbcr field is little-endian (defined as __le16), but it is
read natively without byte-swapping. On a big-endian system, the bitwise
AND will mask the wrong byte, resulting in invalid credit behavior.
[ ... ]
> @@ -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;
> }
[Severity: Critical]
This is a pre-existing issue, but is the vp pointer safe to access here?
The vp pointer is extracted from ha->vp_list, but the vport_slock is
dropped before qla_update_host_map(vp, id) is called, and no reference is
taken on the port:
qla24xx_report_id_acquisition() {
spin_lock_irqsave(&ha->vport_slock, flags);
list_for_each_entry(vp, &ha->vp_list, list) {
if (vp_idx == vp->vp_idx) {
found = 1;
break;
}
}
spin_unlock_irqrestore(&ha->vport_slock, flags);
if (!found)
return;
qla_update_host_map(vp, id);
}
If another thread concurrently deletes the virtual port via
qla24xx_vport_delete(), the memory could be freed while it's still being
accessed, causing a use-after-free.
[Severity: Critical]
This is a pre-existing issue, but is the fcport pointer safe from a
use-after-free in the format 1 branch of qla24xx_report_id_acquisition()?
The fcport pointer is acquired under a spinlock, but the lock is released
before the pointer is safely used or reference counted:
qla24xx_report_id_acquisition() {
...
fcport = qla2x00_find_fcport_by_wwpn(vha, rptid_entry->u.f1.port_name, 1);
spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
if (fcport) {
fcport->plogi_nack_done_deadline = jiffies + HZ;
fcport->dm_login_expire = jiffies + QLA_N2N_WAIT_TIME * HZ;
fcport->scan_state = QLA_FCPORT_FOUND;
...
}
A concurrent session teardown can free the fcport before these updates
complete.
[Severity: Critical]
This is a pre-existing issue, but does iterating over vha->vp_fcports
in the format 2 branch require the sess_lock?
In the format 2 branch (RIDA format 2 / N2N direct connect) of
qla24xx_report_id_acquisition(), the code calls list_for_each_entry
without acquiring the sess_lock that protects vp_fcports:
qla24xx_report_id_acquisition() {
...
list_for_each_entry(fcport, &vha->vp_fcports, list) {
fcport->scan_state = QLA_FCPORT_SCAN;
fcport->n2n_flag = 0;
}
...
}
Other threads (e.g., session deletion) might concurrently add or remove
nodes from this list. Iterating a mutating linked list concurrently without
locks could lead to structural corruption or infinite loops.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=37
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 38/56] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series
2026-07-14 9:53 ` [PATCH v4 38/56] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series Nilesh Javali
@ 2026-07-14 15:40 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 15:40 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] Silent truncation of the 9-bit vp_index to 8 bits when handling NVMe LS rejects and aborts on 29xx adapters.
- [High] Incoming unsolicited NVMe LS requests silently truncate the 9-bit vp_index on 29xx adapters due to missing extended structure.
--
--- Patch [38]: [PATCH v4 38/56] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series ---
commit d14caf7c2396c7b59c2084f1b4f6a4306e45737c
Author: Manish Rangankar <mrangankar@marvell.com>
scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series
This commit extends 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 and handles the
divergent fields per adapter stride.
> 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,
> rjt->rjt.vendor = vendor;
> }
>
[ ... ]
> + if (IS_QLA29XX(ha)) {
> + struct pt_ls4_request_ext *ext = lsrjt_iocb;
> +
> + ext->vp_index = cpu_to_le16(a->vp_idx);
[Severity: High]
Can this silently truncate the 16-bit virtual port index?
Looking at struct qla_nvme_lsrjt_pt_arg, vp_idx is defined as an 8-bit
value:
struct qla_nvme_lsrjt_pt_arg {
...
u8 vp_idx;
...
When an unsolicited NVMe LS request fails or needs to be rejected, this
u8 field silently truncates the 16-bit vha->vp_idx upon assignment. We
then assign this 8-bit truncated value to the extended IOCB's 16-bit
ext->vp_index here.
Could this cause NVMe LS reject requests to be routed out of the wrong
virtual port if the vport index is >= 256, breaking functionality for
NPIV ports on 29xx adapters?
> + 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 {
[ ... ]
> diff --git a/drivers/scsi/qla2xxx/qla_nvme.h b/drivers/scsi/qla2xxx/qla_nvme.h
> index a253ac55171bd..e6a41d53cbe1c 100644
> --- a/drivers/scsi/qla2xxx/qla_nvme.h
> +++ b/drivers/scsi/qla2xxx/qla_nvme.h
> @@ -144,7 +144,7 @@ struct pt_ls4_rx_unsol {
[Severity: High]
Does incoming unsolicited NVMe LS request handling truncate the 9-bit
vp_index on 29xx adapters?
When a 29xx adapter delivers an unsolicited LS request, it posts an
extended RX IOCB. If the driver casts it to the unextended 64-byte
struct pt_ls4_rx_unsol, vp_index is read as a uint8_t, truncating the
9-bit index provided by the hardware to 8 bits.
Should there be an extended layout for struct pt_ls4_rx_unsol for 29xx
adapters to ensure unsolicited NVMe LS requests on VPs >= 256 are not
misrouted?
> 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
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=38
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 39/56] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support
2026-07-14 9:53 ` [PATCH v4 39/56] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support Nilesh Javali
@ 2026-07-14 15:52 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 15:52 UTC (permalink / raw)
To: Nilesh Javali; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Missing bounds check on user-supplied count leads to out-of-bounds stack read and information leak to hardware.
- [High] Uninitialized kernel heap memory is leaked to user space in qla2x00_do_dport_diagnostics.
- [High] Modifying a DMA buffer after mapping it for device use causes data corruption on non-coherent architectures.
--
--- Patch [39]: [PATCH v4 39/56] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support ---
commit 3425e7689599b4f90cb066bb5b9b5a8c81315e6d
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. Allow QLA29xx in
paths previously restricted to QLA27xx/28xx and restrict unsupported
operations. Guard the active image layout logic with explicit checks.
> 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: Critical]
This is a pre-existing issue, but could this lead to an out-of-bounds stack
read?
In the neighboring function qla2x00_update_fru_versions() in qla_bsg.c:
uint8_t bsg[DMA_POOL_SIZE];
struct qla_image_version_list *list = (void *)bsg;
...
image = list->version;
count = list->count;
while (count--) {
memcpy(sfp, &image->field_info, sizeof(image->field_info));
rval = qla2x00_write_sfp(vha, sfp_dma, sfp, ...);
...
image++;
}
It reads the user-supplied list->count and uses it as a loop condition.
If the count is maliciously large, could this loop iterate past the end of the
bsg stack array, reading out-of-bounds stack memory and sending it to the
device via qla2x00_write_sfp()?
[ ... ]
> @@ -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 does this allocation leak
uninitialized kernel heap memory?
If a user triggers this BSG request with a payload smaller than
sizeof(struct qla_dport_diag), kmalloc_obj(*dd) leaves the buffer
uninitialized, and sg_copy_to_buffer() only partially overwrites it.
Then qla26xx_dport_diagnostics() memsets only dd->buf. The remaining 62 bytes
in dd->unused remain uninitialized before being copied back to userspace via
sg_copy_from_buffer():
if (rval == QLA_SUCCESS) {
sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
bsg_job->reply_payload.sg_cnt, dd, sizeof(*dd));
}
Could we use kzalloc_obj() instead?
> 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 is a pre-existing issue, but does this modify a DMA buffer after mapping
it for device use?
Looking at qla26xx_dport_diagnostics():
dd_dma = dma_map_single(&vha->hw->pdev->dev,
dd_buf, size, DMA_FROM_DEVICE);
...
memset(dd_buf, 0, size);
Modifying the buffer after mapping it with DMA_FROM_DEVICE violates the DMA
API, which can cause data corruption on non-coherent architectures when the
CPU writes dirty cache lines over the device's DMA writes.
Should the memset() occur before the dma_map_single() call?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=39
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 40/56] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking
2026-07-14 9:53 ` [PATCH v4 40/56] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking Nilesh Javali
@ 2026-07-14 16:02 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 16:02 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] Missing cancellation of `qpair->q_work` during queue pair teardown leads to Use-After-Free of `qpair`.
- [High] DMA memory for queues is freed before safely clearing the queue pointers, leading to a race condition and Use-After-Free by concurrent readers like firmware dump.
- [High] Hardware-supplied queue indices are used to index `ha->req_q_map` prior to bounds validation, leading to Out-of-Bounds Read.
- [Medium] Host memory and DMA mappings are leaked during teardown if queues were allocated but not started in hardware.
--
--- Patch [40]: [PATCH v4 40/56] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking ---
commit 00cbb695fac148d9ec27ea62ef3a784eac6e57fb
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking
This patch fixes a potential panic when freeing uninitialized DMA memory on
error paths. It also adds locking to prevent concurrent queue ID allocation
races.
> 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);
[Severity: High]
This is a pre-existing issue, but does freeing the DMA memory here before
clearing the queue pointer create a race condition?
If a firmware dump occurs concurrently (e.g., triggered by a mailbox timeout),
it executes qla27xx_fwdt_entry_t263() under hardware_lock and reads
from ha->req_q_map[i]. It could then copy from req->ring via
qla27xx_insertbuf() after the memory has been freed here.
Would it be safer to nullify ha->req_q_map[que_id] before calling
dma_free_coherent()?
> 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;
> }
[Severity: High]
This isn't a bug introduced by this patch, but is it possible for a pending
work item to execute after the qpair is destroyed?
The interrupt handler qla2xxx_msix_rsp_q() queues qpair->q_work on the
workqueue. During teardown, we free the IRQ here, but the already-queued
work doesn't appear to be flushed or cancelled before qla2xxx_delete_qpair()
frees the qpair structure.
When the work eventually executes, qla_do_work() resolves the qpair:
qla_do_work() {
struct qla_qpair *qpair = container_of(work, struct qla_qpair, q_work);
...
}
Could this lead to a use-after-free if the work runs after the qpair is
freed?
> - 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);
[ ... ]
While reviewing the queue teardown paths, I also noticed a few other
pre-existing issues in the driver:
[Severity: Medium]
This is a pre-existing issue, but does qla25xx_delete_req_que() leak memory
if the queues were allocated but never started?
In qla25xx_delete_req_que():
qla25xx_delete_req_que() {
...
if (req && vha->flags.qpairs_req_created) {
req->options |= BIT_0;
ret = qla25xx_init_req_que(vha, req);
if (ret != QLA_SUCCESS)
return QLA_FUNCTION_FAILED;
qla25xx_free_req_que(vha, req);
}
}
If qpairs_req_created is false (such as during a driver initialization
failure or if startqp was false), the check fails and we never call
qla25xx_free_req_que(), which appears to leak the req structure and its DMA
mappings.
[Severity: High]
This is a pre-existing issue, but is it safe to use the hardware-supplied
queue index before validating it?
In qla2x00_status_entry(), the hardware returns sts->handle which is
used to look up the queue:
qla2x00_status_entry() {
...
que = MSW(sts->handle);
req = ha->req_q_map[que];
/* Check for invalid queue pointer */
if (req == NULL ||
que >= find_first_zero_bit(ha->req_qid_map, ha->max_req_queues)) {
...
}
The index que is used to access ha->req_q_map before checking if it is
within bounds. A similar pattern exists in qlafx00_status_entry() where
req is dereferenced before validation. Could a corrupted handle from
the firmware cause an out-of-bounds read here?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=40
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 41/56] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors
2026-07-14 9:53 ` [PATCH v4 41/56] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors Nilesh Javali
@ 2026-07-14 16:24 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 16:24 UTC (permalink / raw)
To: Nilesh Javali; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The patch replaces `__le16` bitfields with scalar fields and accessor functions in several 29xx extended IOCB structures to fix implementation-defined bitfield packing on big-endian hosts. However, it completely misses `struct vp_rpt_id_entry_24xx_ext`, which exhibits the exact same buggy pattern (`__le16 vp_idx : 9; __le16 vp_status : 7;`).
--
--- Patch [41]: [PATCH v4 41/56] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors ---
commit 0cf9b60149a7b5c26196bf1b6738600fa9600e4f
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors
Replace the three sets of __le16 bitfields in 29xx extended IOCB structures
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.
This fixes implementation-defined bitfield packing on big-endian hosts.
> diff --git a/drivers/scsi/qla2xxx/qla_fw29.h b/drivers/scsi/qla2xxx/qla_fw29.h
> index 600a40d8bd5ff..63bf350ddffc0 100644
> --- a/drivers/scsi/qla2xxx/qla_fw29.h
> +++ b/drivers/scsi/qla2xxx/qla_fw29.h
[ ... ]
> @@ -563,9 +586,7 @@ struct abts_entry_24xx_ext {
> __le16 nport_handle; /* type 0x54 only */
>
> __le16 control_flags; /* type 0x55 only */
> - __le16 vp_idx : 9; /* VP index 9 bits */
> - __le16 reserved_1_sof : 3;
> - __le16 sof_type : 4; /* sof_type is upper nibble */
> + __le16 vp_idx_sof; /* bits [8:0]=VP index, [15:12]=SOF type */
>
> __le32 rx_xch_addr;
[Severity: High]
Does this patch miss updating struct vp_rpt_id_entry_24xx_ext which seems to
exhibit the exact same buggy pattern?
drivers/scsi/qla2xxx/qla_fw29.h:
struct vp_rpt_id_entry_24xx_ext {
...
__le16 vp_idx : 9; /* VP Index 9bits */
__le16 vp_status : 7; /* VP Status 7bits */
...
};
This structure is used in qla24xx_report_id_acquisition() in qla_mbx.c where
the improperly packed bitfields are accessed from the hardware DMA response:
drivers/scsi/qla2xxx/qla_mbx.c:qla24xx_report_id_acquisition() {
...
vp_idx = rptid_entry_ext->vp_idx;
vp_status = rptid_entry_ext->vp_status;
...
}
This could result in corrupted virtual port data on big-endian hosts.
Is this intentional or should it be converted to a scalar with accessors as
well?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=41
^ permalink raw reply [flat|nested] 87+ messages in thread
* Re: [PATCH v4 43/56] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats
2026-07-14 9:53 ` [PATCH v4 43/56] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats Nilesh Javali
@ 2026-07-14 16:25 ` sashiko-bot
0 siblings, 0 replies; 87+ messages in thread
From: sashiko-bot @ 2026-07-14 16:25 UTC (permalink / raw)
To: Nilesh Javali; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] On big-endian platforms, substituting the software approximation for QLA29XX with hardware stats introduces statistics corruption due to a pre-existing double byte-swap bug in the hardware stats reading path.
--
--- Patch [43]: [PATCH v4 43/56] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats ---
commit 28f78cc5cc3d52625ba0892069cf854bf1149ff9
Author: Nilesh Javali <njavali@marvell.com>
scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats
This commit updates qla2x00_get_fc_host_stats() to use the 64-bit hardware
FPM transmit and receive word counters for QLA29XX adapters. This replaces
the previous software approximation with more accurate hardware counters.
> diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
> --- 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);
[Severity: Medium]
Will this return corrupted statistics on big-endian systems?
The stats buffer is populated earlier by qla24xx_get_isp_stats(), which
converts the entire structure to native byte order by treating it as an array
of 32-bit words:
drivers/scsi/qla2xxx/qla_mbx.c:qla24xx_get_isp_stats() {
...
/* Re-endianize - firmware data is le32. */
for ( ; dwords--; iter++)
le32_to_cpus(iter);
...
}
Because the buffer is already byte-swapped in 32-bit chunks, applying
le64_to_cpu() to the 64-bit fields here will incorrectly swap the two 32-bit
halves relative to each other on big-endian architectures, producing
mangled values.
While this double byte-swap bug was already present for older adapters, this
patch introduces it for QLA29XX by replacing a safe software approximation
with these mangled hardware counters.
> } else {
> p->rx_words = ib >> 2;
> p->tx_words = ob >> 2;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260714095353.289460-1-njavali@marvell.com?part=43
^ permalink raw reply [flat|nested] 87+ messages in thread
end of thread, other threads:[~2026-07-14 16:25 UTC | newest]
Thread overview: 87+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-14 9:52 [PATCH v4 00/56] scsi: qla2xxx: Add QLA29xx series adapter support Nilesh Javali
2026-07-14 9:52 ` [PATCH v4 01/56] scsi: qla2xxx: Add 29xx series PCI device ID support Nilesh Javali
2026-07-14 10:19 ` sashiko-bot
2026-07-14 9:52 ` [PATCH v4 02/56] scsi: qla2xxx: Add flash read/write interface for 29xx Nilesh Javali
2026-07-14 12:26 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 03/56] scsi: qla2xxx: Add NVRAM config support for 29xx adapters Nilesh Javali
2026-07-14 10:11 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 04/56] scsi: qla2xxx: Add 29xx support in queue initialisation path Nilesh Javali
2026-07-14 10:28 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 05/56] scsi: qla2xxx: Add FC operational firmware load for 29xx Nilesh Javali
2026-07-14 10:43 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 06/56] scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path Nilesh Javali
2026-07-14 10:42 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 07/56] scsi: qla2xxx: Add flash block read/write BSG support for 29xx Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 08/56] scsi: qla2xxx: Add BSG MPI firmware load/dump " Nilesh Javali
2026-07-14 10:59 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 09/56] scsi: qla2xxx: Add 128-byte IOCB definitions " Nilesh Javali
2026-07-14 11:09 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 10/56] scsi: qla2xxx: Add extended status continuation and marker IOCBs Nilesh Javali
2026-07-14 11:06 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 11/56] scsi: qla2xxx: Update IO path to use 128-byte IOCBs for 29xx Nilesh Javali
2026-07-14 11:54 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 12/56] scsi: qla2xxx: Skip image-set-valid attribute " Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 13/56] scsi: qla2xxx: Skip unsupported sysfs attributes " Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 14/56] scsi: qla2xxx: Enable get_fw_version mailbox " Nilesh Javali
2026-07-14 12:10 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 15/56] scsi: qla2xxx: Extend execute_fw mailbox to include 29xx Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 16/56] scsi: qla2xxx: Enable get_adapter_id mailbox for 29xx Nilesh Javali
2026-07-14 12:19 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 17/56] scsi: qla2xxx: Enable init_firmware " Nilesh Javali
2026-07-14 12:48 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 18/56] scsi: qla2xxx: Enable get_firmware_state " Nilesh Javali
2026-07-14 13:13 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 19/56] scsi: qla2xxx: Enable serdes, resource count and FCE trace " Nilesh Javali
2026-07-14 13:10 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 20/56] scsi: qla2xxx: Enable set_els_cmds and echo_test " Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 21/56] scsi: qla2xxx: Add support for QLA29XX in data rate functions Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 22/56] scsi: qla2xxx: Enable qla2x00_shutdown for 29xx Nilesh Javali
2026-07-14 13:30 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 23/56] scsi: qla2xxx: Use ring-slot helpers in __qla2x00_alloc_iocbs Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 24/56] scsi: qla2xxx: Add support for QLA29XX in memory allocation Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 25/56] scsi: qla2xxx: Handle sts_cont_entry_ext_t for 29xx adapters Nilesh Javali
2026-07-14 13:45 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 26/56] scsi: qla2xxx: Update handling of status entries for 29xx series Nilesh Javali
2026-07-14 14:21 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 27/56] scsi: qla2xxx: Enhance ct_entry_24xx_ext iocb handling " Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 28/56] scsi: qla2xxx: Enhance purex_entry " Nilesh Javali
2026-07-14 14:45 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 29/56] scsi: qla2xxx: Update handling of ELS IOCBs " Nilesh Javali
2026-07-14 15:00 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 30/56] scsi: qla2xxx: Add size check for ELS status entry layout on 29xx Nilesh Javali
2026-07-14 14:31 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 31/56] scsi: qla2xxx: Add 29xx extended logio IOCB support Nilesh Javali
2026-07-14 14:49 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 32/56] scsi: qla2xxx: Enhance task management IOCB handling for 29xx series Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 33/56] scsi: qla2xxx: Add abort command " Nilesh Javali
2026-07-14 15:18 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 34/56] scsi: qla2xxx: Enhance ABTS processing " Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 35/56] scsi: qla2xxx: Update VP control IOCB handling " Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 36/56] scsi: qla2xxx: Add build-time size check for VP config IOCB layout Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 37/56] scsi: qla2xxx: Add size check for extended VP report ID entry Nilesh Javali
2026-07-14 15:40 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 38/56] scsi: qla2xxx: Add LS4 pass-through IOCB handling for 29xx series Nilesh Javali
2026-07-14 15:40 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 39/56] scsi: qla2xxx: Adjust feature gating in BSG paths for 29xx support Nilesh Javali
2026-07-14 15:52 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 40/56] scsi: qla2xxx: Fix queue teardown NULL dma_free and bitmap locking Nilesh Javali
2026-07-14 16:02 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 41/56] scsi: qla2xxx: Replace __le16 bitfields with scalar and accessors Nilesh Javali
2026-07-14 16:24 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 42/56] scsi: qla2xxx: Fix endianness annotations in vp_rpt_id_entry structures Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 43/56] scsi: qla2xxx: Use 64-bit FPM word counters for 29xx host stats Nilesh Javali
2026-07-14 16:25 ` sashiko-bot
2026-07-14 9:53 ` [PATCH v4 44/56] scsi: qla2xxx: Add 64G/128G port speed setting support Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 45/56] scsi: qla2xxx: Fix 64G link speed reporting in get_data_rate Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 46/56] scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 47/56] scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 48/56] scsi: qla2xxx: Bound VP index against VP_CTRL IOCB bitmap size Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 49/56] scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config() Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 50/56] scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition() Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 51/56] scsi: qla2xxx: Initialize NVMe abort_work once at submission Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 52/56] scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 53/56] scsi: qla2xxx: Zero dport diagnostics buffer to avoid info leak Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 54/56] scsi: qla2xxx: Fix BSG job leak on validate flash image error path Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 55/56] scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions() Nilesh Javali
2026-07-14 9:53 ` [PATCH v4 56/56] scsi: qla2xxx: Update version to 12.00.00.2607b1 Nilesh Javali
2026-07-14 10:41 ` Hannes Reinecke
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox