* [PATCH v5 01/10] mpi3mr: Skip device shutdown during unload per controller configuration
2026-09-16 8:26 [PATCH v5 00/10] mpi3mr: Few Enhancements and minor fixes Ranjan Kumar
@ 2026-09-16 8:26 ` Ranjan Kumar
2026-09-16 8:26 ` [PATCH v5 02/10] mpi3mr: Update MPI Headers to revision 41 Ranjan Kumar
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ranjan Kumar @ 2026-09-16 8:26 UTC (permalink / raw)
To: linux-scsi, martin.petersen
Cc: sathya.prakash, chandrakanth.patil, vishakhavc, ipylypiv,
Ranjan Kumar, Sashiko
The controller may be configured through Driver Page 1 to suppress
device shutdown requests during driver unload. Cache this setting and
skip the device shutdown request during IOC shutdown when unloading
the driver.
Additionally, ensure the driver_pg1 fields are properly converted
from little-endian to CPU endianness using le32_to_cpu() and le16_to_cpu()
before evaluating the shutdown disable flag and allocating diag buffers.
This prevents failures and massive memory allocation errors on big-endian
architectures.
Also harden the diagnostic buffer allocation retry loops against
invalid firmware-provided decrement sizes. The trace buffer loop
already guarded against a zero or oversized decrement size (infinite
loop or unsigned underflow). The firmware buffer loop had the same
gap and now carries the same guard.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260626114109.43685-1-ranjan.kumar@broadcom.com?part=1
Closes: https://sashiko.dev/#/patchset/20260708183305.244485-1-ranjan.kumar@broadcom.com?part=1
Closes: https://sashiko.dev/#/patchset/20260724102505.115136-1-ranjan.kumar@broadcom.com?part=1
Signed-off-by: Chandrakanth Patil <chandrakanth.patil@broadcom.com>
Signed-off-by: Ranjan Kumar <ranjan.kumar@broadcom.com>
---
drivers/scsi/mpi3mr/mpi3mr.h | 3 +++
drivers/scsi/mpi3mr/mpi3mr_app.c | 44 ++++++++++++++++++++------------
drivers/scsi/mpi3mr/mpi3mr_fw.c | 35 ++++++++++++++++++-------
drivers/scsi/mpi3mr/mpi3mr_os.c | 2 ++
4 files changed, 58 insertions(+), 26 deletions(-)
diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h
index c25525fe0671..39096004c60a 100644
--- a/drivers/scsi/mpi3mr/mpi3mr.h
+++ b/drivers/scsi/mpi3mr/mpi3mr.h
@@ -1410,6 +1410,9 @@ struct mpi3mr_ioc {
struct dma_pool *trace_buf_pool;
struct segments *trace_buf;
u8 invalid_io_comp;
+ bool is_unload;
+ bool skip_dev_shutdown_on_unload;
+
};
diff --git a/drivers/scsi/mpi3mr/mpi3mr_app.c b/drivers/scsi/mpi3mr/mpi3mr_app.c
index 0cdcb8d236d3..5184d9d516a8 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_app.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_app.c
@@ -141,25 +141,27 @@ void mpi3mr_alloc_diag_bufs(struct mpi3mr_ioc *mrioc)
trace_min_size = fw_min_size = MPI3MR_DEFAULT_HDB_MIN_SZ;
} else {
- trace_size = driver_pg1.host_diag_trace_max_size * 1024;
- trace_dec_size = driver_pg1.host_diag_trace_decrement_size
+ trace_size = le16_to_cpu(driver_pg1.host_diag_trace_max_size) * 1024;
+ trace_dec_size = le16_to_cpu(driver_pg1.host_diag_trace_decrement_size)
* 1024;
- trace_min_size = driver_pg1.host_diag_trace_min_size * 1024;
- fw_size = driver_pg1.host_diag_fw_max_size * 1024;
- fw_dec_size = driver_pg1.host_diag_fw_decrement_size * 1024;
- fw_min_size = driver_pg1.host_diag_fw_min_size * 1024;
+ trace_min_size = le16_to_cpu(driver_pg1.host_diag_trace_min_size) * 1024;
+ fw_size = le16_to_cpu(driver_pg1.host_diag_fw_max_size) * 1024;
+ fw_dec_size = le16_to_cpu(driver_pg1.host_diag_fw_decrement_size) * 1024;
+ fw_min_size = le16_to_cpu(driver_pg1.host_diag_fw_min_size) * 1024;
dprint_init(mrioc,
"%s:trace diag buffer sizes read from driver\n"
"page1: maximum size = %dKB, decrement size = %dKB\n"
- ", minimum size = %dKB\n", __func__, driver_pg1.host_diag_trace_max_size,
- driver_pg1.host_diag_trace_decrement_size,
- driver_pg1.host_diag_trace_min_size);
+ ", minimum size = %dKB\n", __func__,
+ le16_to_cpu(driver_pg1.host_diag_trace_max_size),
+ le16_to_cpu(driver_pg1.host_diag_trace_decrement_size),
+ le16_to_cpu(driver_pg1.host_diag_trace_min_size));
dprint_init(mrioc,
"%s:firmware diag buffer sizes read from driver\n"
"page1: maximum size = %dKB, decrement size = %dKB\n"
- ", minimum size = %dKB\n", __func__, driver_pg1.host_diag_fw_max_size,
- driver_pg1.host_diag_fw_decrement_size,
- driver_pg1.host_diag_fw_min_size);
+ ", minimum size = %dKB\n", __func__,
+ le16_to_cpu(driver_pg1.host_diag_fw_max_size),
+ le16_to_cpu(driver_pg1.host_diag_fw_decrement_size),
+ le16_to_cpu(driver_pg1.host_diag_fw_min_size));
if ((trace_size == 0) && (fw_size == 0))
return;
}
@@ -179,6 +181,12 @@ void mpi3mr_alloc_diag_bufs(struct mpi3mr_ioc *mrioc)
mpi3mr_alloc_trace_buffer(mrioc, trace_size)) {
retry = true;
+
+ if (!trace_dec_size || trace_dec_size > trace_size) {
+ retry = false;
+ goto retry_fw;
+ }
+
trace_size -= trace_dec_size;
dprint_init(mrioc, "trace diag buffer allocation failed\n"
"retrying smaller size %dKB\n", trace_size / 1024);
@@ -211,11 +219,13 @@ void mpi3mr_alloc_diag_bufs(struct mpi3mr_ioc *mrioc)
diag_buffer->size = fw_size;
} else {
retry = true;
- fw_size -= fw_dec_size;
- dprint_init(mrioc, "%s:trace diag buffer allocation failed,\n"
- "retrying smaller size %dKB\n",
- __func__, fw_size / 1024);
- goto retry_fw;
+ if (fw_dec_size && fw_dec_size <= fw_size) {
+ fw_size -= fw_dec_size;
+ dprint_init(mrioc, "%s:trace diag buffer allocation failed,\n"
+ "retrying smaller size %dKB\n",
+ __func__, fw_size / 1024);
+ goto retry_fw;
+ }
}
}
}
diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c
index 5c2547bb67a5..d122e95c9081 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_fw.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c
@@ -4127,26 +4127,35 @@ static int mpi3mr_repost_diag_bufs(struct mpi3mr_ioc *mrioc)
}
/**
- * mpi3mr_read_tsu_interval - Update time stamp interval
+ * mpi3mr_read_driver_page1 - Read Driver Page 1 parameters
* @mrioc: Adapter instance reference
*
- * Update time stamp interval if its defined in driver page 1,
- * otherwise use default value.
+ * Reads and caches Driver Page 1 parameters such as
+ * timestamp update interval and driver behavior flags.
*
* Return: Nothing
*/
static void
-mpi3mr_read_tsu_interval(struct mpi3mr_ioc *mrioc)
+mpi3mr_read_driver_page1(struct mpi3mr_ioc *mrioc)
{
struct mpi3_driver_page1 driver_pg1;
u16 pg_sz = sizeof(driver_pg1);
int retval = 0;
mrioc->ts_update_interval = MPI3MR_TSUPDATE_INTERVAL;
+ mrioc->skip_dev_shutdown_on_unload = 0;
retval = mpi3mr_cfg_get_driver_pg1(mrioc, &driver_pg1, pg_sz);
- if (!retval && driver_pg1.time_stamp_update)
+
+ if (retval)
+ return;
+
+ if (driver_pg1.time_stamp_update)
mrioc->ts_update_interval = (driver_pg1.time_stamp_update * 60);
+
+ mrioc->skip_dev_shutdown_on_unload =
+ (le32_to_cpu(driver_pg1.flags) &
+ MPI3_DRIVER1_FLAGS_DEVICE_SHUTDOWN_ON_UNLOAD_DISABLE) ? 1 : 0;
}
/**
@@ -4452,7 +4461,7 @@ int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc)
goto out_failed_noretry;
}
- mpi3mr_read_tsu_interval(mrioc);
+ mpi3mr_read_driver_page1(mrioc);
mpi3mr_print_ioc_info(mrioc);
dprint_init(mrioc, "allocating host diag buffers\n");
@@ -4624,7 +4633,7 @@ int mpi3mr_reinit_ioc(struct mpi3mr_ioc *mrioc, u8 is_resume)
goto out_failed_noretry;
}
- mpi3mr_read_tsu_interval(mrioc);
+ mpi3mr_read_driver_page1(mrioc);
mpi3mr_print_ioc_info(mrioc);
if (is_resume) {
@@ -5109,8 +5118,16 @@ static void mpi3mr_issue_ioc_shutdown(struct mpi3mr_ioc *mrioc)
return;
}
- shutdown_action = MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_NORMAL |
- MPI3_SYSIF_IOC_CONFIG_DEVICE_SHUTDOWN_SEND_REQ;
+ shutdown_action = MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_NORMAL;
+
+ if (!(mrioc->is_unload && mrioc->skip_dev_shutdown_on_unload))
+ shutdown_action |=
+ MPI3_SYSIF_IOC_CONFIG_DEVICE_SHUTDOWN_SEND_REQ;
+ else
+ ioc_info(mrioc,
+ "The shutdown request is issued without the device shutdown bit set\n"
+ "as indicated by the controller configuration\n");
+
ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
ioc_config |= shutdown_action;
diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c
index a389f7f2c741..07a2a45b4957 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_os.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_os.c
@@ -5728,6 +5728,8 @@ static void mpi3mr_remove(struct pci_dev *pdev)
return;
mrioc = shost_priv(shost);
+ mrioc->is_unload = true;
+
while (mrioc->reset_in_progress || mrioc->is_driver_loading)
ssleep(1);
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 02/10] mpi3mr: Update MPI Headers to revision 41
2026-09-16 8:26 [PATCH v5 00/10] mpi3mr: Few Enhancements and minor fixes Ranjan Kumar
2026-09-16 8:26 ` [PATCH v5 01/10] mpi3mr: Skip device shutdown during unload per controller configuration Ranjan Kumar
@ 2026-09-16 8:26 ` Ranjan Kumar
2026-09-16 8:26 ` [PATCH v5 03/10] mpi3mr: Add early timestamp synchronization after driver load Ranjan Kumar
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ranjan Kumar @ 2026-09-16 8:26 UTC (permalink / raw)
To: linux-scsi, martin.petersen
Cc: sathya.prakash, chandrakanth.patil, vishakhavc, ipylypiv,
Ranjan Kumar
Update MPI Headers to revision 41
Signed-off-by: Ranjan Kumar <ranjan.kumar@broadcom.com>
---
drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h | 77 +++++++++++++++++++++--
drivers/scsi/mpi3mr/mpi/mpi30_image.h | 7 ++-
drivers/scsi/mpi3mr/mpi/mpi30_ioc.h | 15 +++--
drivers/scsi/mpi3mr/mpi/mpi30_transport.h | 2 +-
4 files changed, 87 insertions(+), 14 deletions(-)
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h b/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h
index 33dd303c97bb..7cf16a5c15b7 100644
--- a/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h
@@ -72,6 +72,12 @@
#define MPI3_SECURITY_PGAD_SLOT_GROUP_SHIFT (8)
#define MPI3_SECURITY_PGAD_SLOT_MASK (0x000000ff)
#define MPI3_INSTANCE_PGAD_INSTANCE_MASK (0x0000ffff)
+#define MPI3_INSTANCE_PGAD_INSTANCE_SHIFT (0)
+#define MPI3_INTERFACE_PGAD_INTERFACE_MASK (0x0000000f)
+#define MPI3_INTERFACE_PGAD_INTERFACE_SHIFT (0)
+#define MPI3_INTERFACE_PGAD_INTERFACE_MPI (0)
+#define MPI3_INTERFACE_PGAD_INTERFACE_NVME_VD (1)
+#define MPI3_INTERFACE_PGAD_INTERFACE_NVME_PD (2)
struct mpi3_config_request {
__le16 host_tag;
u8 ioc_use_only02;
@@ -492,10 +498,31 @@ struct mpi3_man10_istwi_ctrlr_entry {
};
#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_MASK (0x000c)
-#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_100K (0x0000)
-#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_400K (0x0004)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_100_KHZ (0x0000)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_400_KHZ (0x0004)
#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_TARGET_ENABLED (0x0002)
#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_INITIATOR_ENABLED (0x0001)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I2C_GLITCH_FLTR_MASK (0xc000)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I2C_GLITCH_FLTR_SHIFT (14)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I2C_GLITCH_FLTR_50_NS (0x0000)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I2C_GLITCH_FLTR_10_NS (0x4000)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I2C_GLITCH_FLTR_5_NS (0x8000)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I2C_GLITCH_FLTR_0_NS (0xc000)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_TYPE_MASK (0x3000)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_TYPE_SHIFT (12)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_TYPE_I2C (0x0000)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_TYPE_I3C (0x1000)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_TYPE_AUTO (0x2000)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I3C_MAX_DATA_RATE_MASK (0x0e00)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I3C_MAX_DATA_RATE_SHIFT (9)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I3C_MAX_DATA_RATE_12_5_MHZ (0x0000)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I3C_MAX_DATA_RATE_8_MHZ (0x0200)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I3C_MAX_DATA_RATE_6_MHZ (0x0400)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I3C_MAX_DATA_RATE_4_MHZ (0x0600)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_I3C_MAX_DATA_RATE_2_MHZ (0x0800)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_MASK (0x000c)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_SHIFT (0)
+
#ifndef MPI3_MAN10_ISTWI_CTRLR_MAX
#define MPI3_MAN10_ISTWI_CTRLR_MAX (1)
#endif
@@ -1027,6 +1054,16 @@ struct mpi3_io_unit_page5 {
#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SWITCH_ATTACHED (0x02)
#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_DIRECT_AND_EXPANDER (0x03)
#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_DIRECT_AND_SWITCH (0x03)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_HDD_SPINDOWN_MASK (0xc000)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_HDD_SPINDOWN_NONE (0x0000)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_HDD_SPINDOWN_ALL (0x4000)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_HDD_SPINDOWN_FILTERED (0x8000)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_HDD_SPINDOWN_RESERVED (0xc000)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SYNC_CACHE_MASK (0x3000)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SYNC_CACHE_ALL (0x0000)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SYNC_CACHE_FILTERED (0x1000)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SYNC_CACHE_NONE (0x2000)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SYNC_CACHE_RESERVED (0x3000)
#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SATA_HDD_MASK (0x0300)
#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SATA_HDD_SHIFT (8)
#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SAS_HDD_MASK (0x00c0)
@@ -1069,7 +1106,8 @@ struct mpi3_io_unit_page8 {
struct mpi3_config_page_header header;
u8 sb_mode;
u8 sb_state;
- __le16 reserved0a;
+ u8 flags;
+ u8 reserved0b;
u8 num_slots;
u8 slots_available;
u8 current_key_encryption_algo;
@@ -1088,6 +1126,8 @@ struct mpi3_io_unit_page8 {
#define MPI3_IOUNIT8_SBSTATE_SVN_UPDATE_PENDING (0x04)
#define MPI3_IOUNIT8_SBSTATE_KEY_UPDATE_PENDING (0x02)
#define MPI3_IOUNIT8_SBSTATE_SECURE_BOOT_ENABLED (0x01)
+#define MPI3_IOUNIT8_FLAGS_FWQR_CAPABLE (0x80)
+#define MPI3_IOUNIT8_FLAGS_FWQR_SECURED (0x40)
#define MPI3_IOUNIT8_SBMODE_CURRENT_KEY_IOUNIT17 (0x10)
#define MPI3_IOUNIT8_SBMODE_HARD_SECURE_RECERTIFIED (0x08)
struct mpi3_io_unit_page9 {
@@ -1174,10 +1214,16 @@ struct mpi3_io_unit_page12 {
#define MPI3_IOUNIT12_FLAGS_NUMPASSES_32 (0x00000200)
#define MPI3_IOUNIT12_FLAGS_NUMPASSES_64 (0x00000300)
#define MPI3_IOUNIT12_FLAGS_PASSPERIOD_MASK (0x00000003)
+#define MPI3_IOUNIT12_FLAGS_PASSPERIOD_SHIFT (0)
#define MPI3_IOUNIT12_FLAGS_PASSPERIOD_DISABLED (0x00000000)
#define MPI3_IOUNIT12_FLAGS_PASSPERIOD_500US (0x00000001)
#define MPI3_IOUNIT12_FLAGS_PASSPERIOD_1MS (0x00000002)
#define MPI3_IOUNIT12_FLAGS_PASSPERIOD_2MS (0x00000003)
+#define MPI3_IOUNIT12_FLAGS_INTERFACE_MASK (0x0000000c)
+#define MPI3_IOUNIT12_FLAGS_INTERFACE_SHIFT (2)
+#define MPI3_IOUNIT12_FLAGS_INTERFACE_MPI (0x00000000)
+#define MPI3_IOUNIT12_FLAGS_INTERFACE_NVME_VD (0x00000004)
+#define MPI3_IOUNIT12_FLAGS_INTERFACE_NVME_PD (0x00000008)
#ifndef MPI3_IOUNIT13_FUNC_MAX
#define MPI3_IOUNIT13_FUNC_MAX (1)
#endif
@@ -1238,6 +1284,7 @@ struct mpi3_io_unit_page15 {
#define MPI3_IOUNIT15_PAGEVERSION (0x00)
#define MPI3_IOUNIT15_FLAGS_EPRINIT_INITREQUIRED (0x04)
#define MPI3_IOUNIT15_FLAGS_EPRSUPPORT_MASK (0x03)
+#define MPI3_IOUNIT15_FLAGS_EPRSUPPORT_SHIFT (0)
#define MPI3_IOUNIT15_FLAGS_EPRSUPPORT_NOT_SUPPORTED (0x00)
#define MPI3_IOUNIT15_FLAGS_EPRSUPPORT_WITHOUT_POWER_BRAKE_GPIO (0x01)
#define MPI3_IOUNIT15_FLAGS_EPRSUPPORT_WITH_POWER_BRAKE_GPIO (0x02)
@@ -1255,6 +1302,9 @@ struct mpi3_io_unit_page17 {
__le32 current_key[];
};
#define MPI3_IOUNIT17_PAGEVERSION (0x00)
+#define MPI3_IOUNIT17_FLAGS_KEYROOT_MASK (0x01)
+#define MPI3_IOUNIT17_FLAGS_KEYROOT_HW (0x00)
+#define MPI3_IOUNIT17_FLAGS_KEYROOT_FW (0x01)
struct mpi3_io_unit_page18 {
struct mpi3_config_page_header header;
u8 flags;
@@ -1640,11 +1690,28 @@ struct mpi3_security_page3 {
};
#define MPI3_SECURITY3_PAGEVERSION (0x00)
-#define MPI3_SECURITY3_FLAGS_TYPE_MASK (0x0f)
+#define MPI3_SECURITY3_FLAGS_TYPE_MASK (0x1f)
#define MPI3_SECURITY3_FLAGS_TYPE_SHIFT (0)
#define MPI3_SECURITY3_FLAGS_TYPE_NOT_VALID (0)
#define MPI3_SECURITY3_FLAGS_TYPE_MLDSA_PRIVATE (1)
#define MPI3_SECURITY3_FLAGS_TYPE_MLDSA_PUBLIC (2)
+union mpi3_security_digest {
+ __le32 dword[16];
+ __le16 word[32];
+ u8 byte[64];
+};
+struct mpi3_security_page4 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08[2];
+ union mpi3_security_mac mac;
+ union mpi3_security_nonce nonce;
+ u8 num_digests;
+ u8 hash_algorithm;
+ __le16 reserved92;
+ __le32 reserved94[3];
+ union mpi3_security_digest digest[];
+};
+#define MPI3_SECURITY4_PAGEVERSION (0x00)
struct mpi3_security_page10 {
struct mpi3_config_page_header header;
__le32 reserved08[2];
@@ -2074,7 +2141,7 @@ struct mpi3_sas_phy3_phy_event_config {
#define MPI3_SASPHY3_EVENT_CODE_LCCONN_TIME (0xd5)
#define MPI3_SASPHY3_EVENT_CODE_SSP_TX_START_TRANSMIT (0xd6)
#define MPI3_SASPHY3_EVENT_CODE_SATA_TX_START (0xd7)
-#define MPI3_SASPHY3_EVENT_CODE_SMP_TX_START_TRANSMT (0xd8)
+#define MPI3_SASPHY3_EVENT_CODE_SMP_TX_START_TRANSMIT (0xd8)
#define MPI3_SASPHY3_EVENT_CODE_TX_SMP_BREAK_CONN (0xd9)
#define MPI3_SASPHY3_EVENT_CODE_SSP_RX_START_RECEIVE (0xda)
#define MPI3_SASPHY3_EVENT_CODE_SATA_RX_START_RECEIVE (0xdb)
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_image.h b/drivers/scsi/mpi3mr/mpi/mpi30_image.h
index 62ddf094d46c..5fa09fa79358 100644
--- a/drivers/scsi/mpi3mr/mpi/mpi30_image.h
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_image.h
@@ -18,7 +18,7 @@ struct mpi3_hash_exclusion_format {
__le32 size;
};
-#define MPI3_IMAGE_HASH_EXCUSION_NUM (4)
+#define MPI3_IMAGE_HASH_EXCLUSION_NUM (4)
struct mpi3_component_image_header {
__le32 signature0;
__le32 load_address;
@@ -42,7 +42,7 @@ struct mpi3_component_image_header {
union mpi3_version_union rmc_interface_version;
union mpi3_version_union etp_interface_version;
struct mpi3_comp_image_version component_image_version;
- struct mpi3_hash_exclusion_format hash_exclusion[MPI3_IMAGE_HASH_EXCUSION_NUM];
+ struct mpi3_hash_exclusion_format hash_exclusion[MPI3_IMAGE_HASH_EXCLUSION_NUM];
__le32 next_image_header_offset;
union mpi3_version_union security_version;
__le32 reserved84[31];
@@ -347,7 +347,8 @@ struct mpi3_encrypted_hash_entry {
struct mpi3_encrypted_hash_data {
u8 image_version;
u8 num_hash;
- __le16 reserved02;
+ u8 fw_num_hash;
+ u8 reserved03;
__le32 reserved04;
struct mpi3_encrypted_hash_entry encrypted_hash_entry[MPI3_ENCRYPTED_HASH_ENTRY_MAX];
};
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h b/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h
index 68efa0d51345..aa42fba7f930 100644
--- a/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h
@@ -428,10 +428,10 @@ struct mpi3_event_data_sas_discovery {
#define MPI3_EVENT_SAS_DISC_FLAGS_IN_PROGRESS (0x01)
#define MPI3_EVENT_SAS_DISC_RC_STARTED (0x01)
#define MPI3_EVENT_SAS_DISC_RC_COMPLETED (0x02)
-#define MPI3_SAS_DISC_STATUS_MAX_ENCLOSURES_EXCEED (0x80000000)
-#define MPI3_SAS_DISC_STATUS_MAX_EXPANDERS_EXCEED (0x40000000)
-#define MPI3_SAS_DISC_STATUS_MAX_DEVICES_EXCEED (0x20000000)
-#define MPI3_SAS_DISC_STATUS_MAX_TOPO_PHYS_EXCEED (0x10000000)
+#define MPI3_SAS_DISC_STATUS_MAX_ENCLOSURES_EXCEEDED (0x80000000)
+#define MPI3_SAS_DISC_STATUS_MAX_EXPANDERS_EXCEEDED (0x40000000)
+#define MPI3_SAS_DISC_STATUS_MAX_DEVICES_EXCEEDED (0x20000000)
+#define MPI3_SAS_DISC_STATUS_MAX_TOPO_PHYS_EXCEEDED (0x10000000)
#define MPI3_SAS_DISC_STATUS_INVALID_CEI (0x00010000)
#define MPI3_SAS_DISC_STATUS_FECEI_MISMATCH (0x00008000)
#define MPI3_SAS_DISC_STATUS_MULTIPLE_DEVICES_IN_SLOT (0x00004000)
@@ -965,7 +965,7 @@ struct mpi3_ci_download_reply {
u8 flags;
u8 cache_dirty;
u8 pending_count;
- u8 reserved13;
+ u8 additional_flags;
};
#define MPI3_CI_DOWNLOAD_FLAGS_DOWNLOAD_IN_PROGRESS (0x80)
@@ -979,6 +979,11 @@ struct mpi3_ci_download_reply {
#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_ONLINE_PENDING (0x04)
#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_OFFLINE_PENDING (0x06)
#define MPI3_CI_DOWNLOAD_FLAGS_COMPATIBLE (0x01)
+#define MPI3_CI_DOWNLOAD_ADDITIONALFLAGS_REDUNDANCYRESTORATION_MASK (0x03)
+#define MPI3_CI_DOWNLOAD_ADDITIONALFLAGS_REDUNDANCYRESTORATION_SHIFT (0)
+#define MPI3_CI_DOWNLOAD_ADDITIONALFLAGS_REDUNDANCYRESTORATION_NONE (0x00)
+#define MPI3_CI_DOWNLOAD_ADDITIONALFLAGS_REDUNDANCYRESTORATION_PRIMARY (0x01)
+#define MPI3_CI_DOWNLOAD_ADDITIONALFLAGS_REDUNDANCYRESTORATION_SECONDARY (0x02)
struct mpi3_ci_upload_request {
__le16 host_tag;
u8 ioc_use_only02;
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_transport.h b/drivers/scsi/mpi3mr/mpi/mpi30_transport.h
index 290a1f5c2924..794ecc778945 100644
--- a/drivers/scsi/mpi3mr/mpi/mpi30_transport.h
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_transport.h
@@ -18,7 +18,7 @@ union mpi3_version_union {
#define MPI3_VERSION_MAJOR (3)
#define MPI3_VERSION_MINOR (0)
-#define MPI3_VERSION_UNIT (39)
+#define MPI3_VERSION_UNIT (41)
#define MPI3_VERSION_DEV (0)
#define MPI3_DEVHANDLE_INVALID (0xffff)
struct mpi3_sysif_oper_queue_indexes {
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 03/10] mpi3mr: Add early timestamp synchronization after driver load
2026-09-16 8:26 [PATCH v5 00/10] mpi3mr: Few Enhancements and minor fixes Ranjan Kumar
2026-09-16 8:26 ` [PATCH v5 01/10] mpi3mr: Skip device shutdown during unload per controller configuration Ranjan Kumar
2026-09-16 8:26 ` [PATCH v5 02/10] mpi3mr: Update MPI Headers to revision 41 Ranjan Kumar
@ 2026-09-16 8:26 ` Ranjan Kumar
2026-09-16 8:26 ` [PATCH v5 04/10] mpi3mr: Fix NVMe page size caching for non-operational devices Ranjan Kumar
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ranjan Kumar @ 2026-09-16 8:26 UTC (permalink / raw)
To: linux-scsi, martin.petersen
Cc: sathya.prakash, chandrakanth.patil, vishakhavc, ipylypiv,
Ranjan Kumar
When the driver is loaded from initramfs, the controller timestamp may
be initialized before the system clock has been synchronized. As a
result, the controller can operate with a stale timestamp until the
first periodic synchronization occurs.
Currently, the first controller timestamp synchronization occurs only
after the configured ts_update_interval expires (15 minutes by default).
Add an early timestamp synchronization 60 seconds after driver load,
followed by the existing periodic synchronization interval.
Signed-off-by: Ranjan Kumar <ranjan.kumar@broadcom.com>
---
drivers/scsi/mpi3mr/mpi3mr.h | 3 +++
drivers/scsi/mpi3mr/mpi3mr_fw.c | 25 +++++++++++++++++++------
2 files changed, 22 insertions(+), 6 deletions(-)
diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h
index 39096004c60a..1f2f0951b560 100644
--- a/drivers/scsi/mpi3mr/mpi3mr.h
+++ b/drivers/scsi/mpi3mr/mpi3mr.h
@@ -125,6 +125,7 @@ extern atomic64_t event_counter;
#define MPI3MR_RESETTM_TIMEOUT 60
#define MPI3MR_RESET_HOST_IOWAIT_TIMEOUT 5
#define MPI3MR_TSUPDATE_INTERVAL 900
+#define MPI3MR_EARLY_TSUPDATE_SECONDS 60
#define MPI3MR_DEFAULT_SHUTDOWN_TIME 120
#define MPI3MR_RAID_ERRREC_RESET_TIMEOUT 180
#define MPI3MR_PREPARE_FOR_RESET_TIMEOUT 180
@@ -1118,6 +1119,7 @@ struct scmd_priv {
* @evtack_cmds_bitmap: Event Ack bitmap
* @delayed_evtack_cmds_list: Delayed event acknowledgment list
* @ts_update_counter: Timestamp update counter
+ * @early_ts_sync_done: Early (1 min) timestamp sync completed after load
* @ts_update_interval: Timestamp update interval
* @reset_in_progress: Reset in progress flag
* @unrecoverable: Controller unrecoverable flag
@@ -1318,6 +1320,7 @@ struct mpi3mr_ioc {
struct list_head delayed_evtack_cmds_list;
u16 ts_update_counter;
+ u8 early_ts_sync_done;
u16 ts_update_interval;
u8 reset_in_progress;
u8 unrecoverable;
diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c
index d122e95c9081..d8a68d7fdf19 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_fw.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c
@@ -2890,8 +2890,9 @@ static int mpi3mr_print_pkg_ver(struct mpi3mr_ioc *mrioc)
* @work: work struct
*
* Watch dog work periodically executed (1 second interval) to
- * monitor firmware fault and to issue periodic timer sync to
- * the firmware.
+ * monitor firmware fault and perform timestamp synchronization
+ * to firmware, with an early sync 1 minute after load followed
+ * by periodic updates at ts_update_interval seconds (default 15 minutes).
*
* Return: Nothing.
*/
@@ -2937,11 +2938,23 @@ static void mpi3mr_watchdog_work(struct work_struct *work)
}
if (!(mrioc->facts.ioc_capabilities &
- MPI3_IOCFACTS_CAPABILITY_NON_SUPERVISOR_IOC) &&
- (mrioc->ts_update_counter++ >= mrioc->ts_update_interval)) {
+ MPI3_IOCFACTS_CAPABILITY_NON_SUPERVISOR_IOC)) {
+ if (!mrioc->early_ts_sync_done) {
+ /*
+ * Send time sync 1 min after load
+ */
+ if (mrioc->ts_update_counter++ >=
+ MPI3MR_EARLY_TSUPDATE_SECONDS) {
+ mrioc->early_ts_sync_done = 1;
+ mrioc->ts_update_counter = 0;
+ mpi3mr_sync_timestamp(mrioc);
+ }
+ } else if (mrioc->ts_update_counter++ >=
+ mrioc->ts_update_interval) {
+ mrioc->ts_update_counter = 0;
+ mpi3mr_sync_timestamp(mrioc);
+ }
- mrioc->ts_update_counter = 0;
- mpi3mr_sync_timestamp(mrioc);
}
if ((mrioc->prepare_for_reset) &&
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 04/10] mpi3mr: Fix NVMe page size caching for non-operational devices
2026-09-16 8:26 [PATCH v5 00/10] mpi3mr: Few Enhancements and minor fixes Ranjan Kumar
` (2 preceding siblings ...)
2026-09-16 8:26 ` [PATCH v5 03/10] mpi3mr: Add early timestamp synchronization after driver load Ranjan Kumar
@ 2026-09-16 8:26 ` Ranjan Kumar
2026-09-16 8:27 ` [PATCH v5 05/10] mpi3mr: Fix performance regression caused by extended IRQ poll sleep Ranjan Kumar
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ranjan Kumar @ 2026-09-16 8:26 UTC (permalink / raw)
To: linux-scsi, martin.petersen
Cc: sathya.prakash, chandrakanth.patil, vishakhavc, ipylypiv,
Ranjan Kumar, Sashiko
For NVMe devices in an error state, the cached PCIe page size remains
unset. This causes management IOCTL validation to fail, preventing
requests from reaching firmware and returning incorrect errors to
userspace. Populate the page size attribute irrespective of
device access status so firmware can process IOCTLs and report
appropriate errors.
Additionally, harden the device initialization path against invalid
firmware data for non-operational devices:
1. Add bounds checking for page_size, falling back to 4096 bytes (shift
exponent 12) to prevent undefined shift behavior and kernel panics.
The minimum valid NVMe page size shift is 12 and maximum is 27.
2. Initialize reset_to and abort_to timeouts with default values to
prevent IOCTLs from failing instantly. To avoid race conditions
where concurrent readers might observe these default timeouts before
they are updated with firmware values, use local variables to
compute the final values before writing them to the device structure.
3. Read the firmware-provided page size once into a local variable
before validating and using it, since it lives in memory the
device can also write to. Using it directly in both the check and
the assignment allowed the two to observe different values.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260626114109.43685-1-ranjan.kumar@broadcom.com?part=4
Closes: https://sashiko.dev/#/patchset/20260708183305.244485-1-ranjan.kumar@broadcom.com?part=4
Closes: https://sashiko.dev/#/patchset/20260724102505.115136-1-ranjan.kumar@broadcom.com?part=4
Closes: https://sashiko.dev/#/patchset/20260805110634.346670-1-ranjan.kumar@broadcom.com?part=4
Signed-off-by: Chandrakanth Patil <chandrakanth.patil@broadcom.com>
Signed-off-by: Ranjan Kumar <ranjan.kumar@broadcom.com>
---
drivers/scsi/mpi3mr/mpi3mr.h | 1 +
drivers/scsi/mpi3mr/mpi3mr_os.c | 24 ++++++++++++++++--------
2 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h
index 1f2f0951b560..6128b30112e2 100644
--- a/drivers/scsi/mpi3mr/mpi3mr.h
+++ b/drivers/scsi/mpi3mr/mpi3mr.h
@@ -169,6 +169,7 @@ extern atomic64_t event_counter;
#define MPI3MR_DEFAULT_MDTS (128 * 1024)
#define MPI3MR_DEFAULT_PGSZEXP (12)
+#define MPI3MR_MAX_PGSZEXP (27)
/* Command retry count definitions */
#define MPI3MR_DEV_RMHS_RETRY_COUNT 3
diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c
index 07a2a45b4957..5506fc87f1ca 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_os.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_os.c
@@ -1350,24 +1350,32 @@ static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc,
struct mpi3_device0_pcie_format *pcieinf =
&dev_pg0->device_specific.pcie_format;
u16 dev_info = le16_to_cpu(pcieinf->device_info);
+ u8 pgsz = MPI3MR_DEFAULT_PGSZEXP;
+ u8 reset_to = MPI3MR_INTADMCMD_TIMEOUT;
+ u8 abort_to = MPI3MR_INTADMCMD_TIMEOUT;
+ u8 fw_pgsz = READ_ONCE(pcieinf->page_size);
tgtdev->dev_spec.pcie_inf.dev_info = dev_info;
tgtdev->dev_spec.pcie_inf.capb =
le32_to_cpu(pcieinf->capabilities);
tgtdev->dev_spec.pcie_inf.mdts = MPI3MR_DEFAULT_MDTS;
- /* 2^12 = 4096 */
- tgtdev->dev_spec.pcie_inf.pgsz = 12;
+ /* Validate firmware page size to prevent undefined shift behavior */
+ if (fw_pgsz >= MPI3MR_DEFAULT_PGSZEXP && fw_pgsz <= MPI3MR_MAX_PGSZEXP)
+ pgsz = fw_pgsz;
+
if (dev_pg0->access_status == MPI3_DEVICE0_ASTATUS_NO_ERRORS) {
tgtdev->dev_spec.pcie_inf.mdts =
le32_to_cpu(pcieinf->maximum_data_transfer_size);
- tgtdev->dev_spec.pcie_inf.pgsz = pcieinf->page_size;
- tgtdev->dev_spec.pcie_inf.reset_to =
- max_t(u8, pcieinf->controller_reset_to,
- MPI3MR_INTADMCMD_TIMEOUT);
- tgtdev->dev_spec.pcie_inf.abort_to =
- max_t(u8, pcieinf->nvme_abort_to,
+ reset_to = max_t(u8, pcieinf->controller_reset_to,
+ MPI3MR_INTADMCMD_TIMEOUT);
+ abort_to = max_t(u8, pcieinf->nvme_abort_to,
MPI3MR_INTADMCMD_TIMEOUT);
}
+
+ tgtdev->dev_spec.pcie_inf.pgsz = pgsz;
+ tgtdev->dev_spec.pcie_inf.reset_to = reset_to;
+ tgtdev->dev_spec.pcie_inf.abort_to = abort_to;
+
if (tgtdev->dev_spec.pcie_inf.mdts > (1024 * 1024))
tgtdev->dev_spec.pcie_inf.mdts = (1024 * 1024);
if (((dev_info & MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_MASK) !=
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 05/10] mpi3mr: Fix performance regression caused by extended IRQ poll sleep
2026-09-16 8:26 [PATCH v5 00/10] mpi3mr: Few Enhancements and minor fixes Ranjan Kumar
` (3 preceding siblings ...)
2026-09-16 8:26 ` [PATCH v5 04/10] mpi3mr: Fix NVMe page size caching for non-operational devices Ranjan Kumar
@ 2026-09-16 8:27 ` Ranjan Kumar
2026-09-16 8:27 ` [PATCH v5 06/10] mpi3mr: Fix memory leak on operational queue creation failure Ranjan Kumar
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ranjan Kumar @ 2026-09-16 8:27 UTC (permalink / raw)
To: linux-scsi, martin.petersen
Cc: sathya.prakash, chandrakanth.patil, vishakhavc, ipylypiv,
Ranjan Kumar, Sashiko
Commit 24d7071d9645 ("scsi: mpi3mr: A performance fix") increased the
threaded IRQ poll sleep range from 2-20 us to 20-21 us to work around a
timer slack issue.
On kernels unaffected by the timer slack issue, the longer sleep interval
reduces reply queue processing efficiency and causes an approximately 7%
throughput regression on NVMe direct-attached RAID10 configurations.
Restore the IRQ poll sleep base to 2 us (widening the usleep_range() upper
bound to 10x the base instead of a fixed +1 us) to recover the lost
throughput, and skip the sleep entirely once pend_ios reaches 0 so the
poll loop exits immediately at the tail of a completion burst.
Additionally, resolve the following issues in the reply queue processing
and polling logic:
1. Add missing dma_rmb() memory barriers in the admin and operational
reply queue processing loops. This ensures that the descriptor
payload is only read after the phase bit check is complete, preventing
weakly ordered architectures from speculatively processing stale data.
2. Add bounds checking for `request_queue_id` in
mpi3mr_process_op_reply_q(). An out-of-range id is now logged and the
descriptor is retired (consumer index advanced, phase toggled on
wraparound) rather than aborting the loop in place, which previously
left the same corrupted descriptor at the head of the ring forever
and stalled polling indefinitely. It is not counted toward pend_ios,
since no real completion was processed for it.
3. Recheck for a late-arriving descriptor via dma_rmb() while still
holding op_reply_q->in_use, instead of releasing it and reclaiming
it afterward, which could race and reprocess a descriptor with
stale indices or double-decrement in_use.
4. Replace a direct panic() call with a safe ioc_err() log and abort in
mpi3mr_process_op_reply_desc() when mpi3mr_get_reply_virt_addr()
returns NULL. This prevents a single malformed DMA reply address from
crashing the entire host OS. The reply_dma output parameter is also
cleared before returning, since it was already populated with the
unvalidated address before the NULL check. Leaving it set would make
the caller repost that unvalidated address back to the hardware.
Note: The unbounded busy-wait loop (usleep_range) in mpi3mr_isr_poll()
flagged by automated review is intentionally retained. This short sleep
polling mechanism is critical for batching completions and achieving the
target throughput on high-performance NVMe configurations.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260626114109.43685-1-ranjan.kumar@broadcom.com?part=5
Closes: https://sashiko.dev/#/patchset/20260708183305.244485-1-ranjan.kumar@broadcom.com?part=5
Closes: https://sashiko.dev/#/patchset/20260724102505.115136-1-ranjan.kumar@broadcom.com?part=5
Closes: https://sashiko.dev/#/patchset/20260805110634.346670-1-ranjan.kumar@broadcom.com?part=5
Signed-off-by: Chandrakanth Patil <chandrakanth.patil@broadcom.com>
Signed-off-by: Ranjan Kumar <ranjan.kumar@broadcom.com>
---
drivers/scsi/mpi3mr/mpi3mr.h | 2 +-
drivers/scsi/mpi3mr/mpi3mr_fw.c | 54 ++++++++++++++++++++++++++++++---
drivers/scsi/mpi3mr/mpi3mr_os.c | 8 +++--
3 files changed, 56 insertions(+), 8 deletions(-)
diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h
index 6128b30112e2..4d19a9460d38 100644
--- a/drivers/scsi/mpi3mr/mpi3mr.h
+++ b/drivers/scsi/mpi3mr/mpi3mr.h
@@ -179,7 +179,7 @@ extern atomic64_t event_counter;
#define MPI3MR_DEFAULT_SDEV_QD 32
/* Definitions for Threaded IRQ poll*/
-#define MPI3MR_IRQ_POLL_SLEEP 20
+#define MPI3MR_IRQ_POLL_SLEEP 2
#define MPI3MR_IRQ_POLL_TRIGGER_IOCOUNT 8
/* Definitions for the controller security status*/
diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c
index d8a68d7fdf19..51ddcc1008c2 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_fw.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c
@@ -489,6 +489,12 @@ int mpi3mr_process_admin_reply_q(struct mpi3mr_ioc *mrioc)
return 0;
}
+ /*
+ * Ensure that the descriptor payload is read only after
+ * the phase bit check is complete.
+ */
+ dma_rmb();
+
do {
if (mrioc->unrecoverable || mrioc->io_admin_reset_sync)
break;
@@ -509,6 +515,13 @@ int mpi3mr_process_admin_reply_q(struct mpi3mr_ioc *mrioc)
if ((le16_to_cpu(reply_desc->reply_flags) &
MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase)
break;
+
+ /*
+ * Ensure that the descriptor payload is read only after
+ * the phase bit check is complete.
+ */
+ dma_rmb();
+
if (threshold_comps == MPI3MR_THRESHOLD_REPLY_COUNT) {
writel(admin_reply_ci,
&mrioc->sysif_regs->admin_reply_queue_ci);
@@ -580,15 +593,33 @@ int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc,
reply_desc = mpi3mr_get_reply_desc(op_reply_q, reply_ci);
if ((le16_to_cpu(reply_desc->reply_flags) &
MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) {
+ /* Recheck under in_use before releasing, to avoid a reclaim race */
+ dma_rmb();
+ if ((le16_to_cpu(reply_desc->reply_flags) &
+ MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) == exp_phase)
+ goto process_desc;
atomic_dec(&op_reply_q->in_use);
return 0;
}
+process_desc:
+ /*
+ * Ensure that the descriptor payload is read only after
+ * the phase bit check is complete.
+ */
+ dma_rmb();
do {
if (mrioc->unrecoverable || mrioc->io_admin_reset_sync)
break;
req_q_idx = le16_to_cpu(reply_desc->request_queue_id) - 1;
+
+ if (unlikely(req_q_idx >= mrioc->num_op_req_q)) {
+ ioc_err(mrioc, "Invalid request queue id %d, skipping reply\n",
+ req_q_idx + 1);
+ goto next_reply;
+ }
+
op_req_q = &mrioc->req_qinfo[req_q_idx];
WRITE_ONCE(op_req_q->ci, le16_to_cpu(reply_desc->request_queue_ci));
@@ -597,8 +628,9 @@ int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc,
if (reply_dma)
mpi3mr_repost_reply_buf(mrioc, reply_dma);
- num_op_reply++;
threshold_comps++;
+next_reply:
+ num_op_reply++;
if (++reply_ci == op_reply_q->num_replies) {
reply_ci = 0;
@@ -608,8 +640,19 @@ int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc,
reply_desc = mpi3mr_get_reply_desc(op_reply_q, reply_ci);
if ((le16_to_cpu(reply_desc->reply_flags) &
- MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase)
+ MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) {
+ dma_rmb();
+ if ((le16_to_cpu(reply_desc->reply_flags) &
+ MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) == exp_phase)
+ goto reply_ready;
break;
+ }
+reply_ready:
+ /*
+ * Ensure that the descriptor payload is read only after
+ * the phase bit check is complete.
+ */
+ dma_rmb();
#ifndef CONFIG_PREEMPT_RT
/*
* Exit completion loop to avoid CPU lockup
@@ -759,11 +802,12 @@ static irqreturn_t mpi3mr_isr_poll(int irq, void *privdata)
num_op_reply +=
mpi3mr_process_op_reply_q(mrioc,
intr_info->op_reply_q);
+ if (!atomic_read(&intr_info->op_reply_q->pend_ios))
+ break;
- usleep_range(MPI3MR_IRQ_POLL_SLEEP, MPI3MR_IRQ_POLL_SLEEP + 1);
+ usleep_range(MPI3MR_IRQ_POLL_SLEEP, 10 * MPI3MR_IRQ_POLL_SLEEP);
- } while (atomic_read(&intr_info->op_reply_q->pend_ios) &&
- (num_op_reply < mrioc->max_host_ios));
+ } while (num_op_reply < mrioc->max_host_ios);
intr_info->op_reply_q->enable_irq_poll = false;
enable_irq(intr_info->os_irq);
diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c
index 5506fc87f1ca..7e59773c0276 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_os.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_os.c
@@ -3493,8 +3493,12 @@ void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc,
scsi_reply = mpi3mr_get_reply_virt_addr(mrioc,
*reply_dma);
if (!scsi_reply) {
- panic("%s: scsi_reply is NULL, this shouldn't happen\n",
- mrioc->name);
+ ioc_err(mrioc, "scsi_reply is NULL, invalid reply_frame_address\n");
+ /*
+ * Do not let the caller repost an address that
+ * failed virt-addr lookup back to the hardware.
+ */
+ *reply_dma = 0;
goto out;
}
host_tag = le16_to_cpu(scsi_reply->host_tag);
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 06/10] mpi3mr: Fix memory leak on operational queue creation failure
2026-09-16 8:26 [PATCH v5 00/10] mpi3mr: Few Enhancements and minor fixes Ranjan Kumar
` (4 preceding siblings ...)
2026-09-16 8:27 ` [PATCH v5 05/10] mpi3mr: Fix performance regression caused by extended IRQ poll sleep Ranjan Kumar
@ 2026-09-16 8:27 ` Ranjan Kumar
2026-09-16 8:27 ` [PATCH v5 07/10] mpi3mr: Fix firmware event reference leak during cleanup Ranjan Kumar
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ranjan Kumar @ 2026-09-16 8:27 UTC (permalink / raw)
To: linux-scsi, martin.petersen
Cc: sathya.prakash, chandrakanth.patil, vishakhavc, ipylypiv,
Ranjan Kumar, Sashiko
When operational queue creation fails after one or more queues have
been created, the error path frees the queue information arrays but
does not release the DMA memory segments associated with the created
queues, resulting in a memory leak. Fix this by ensuring that partially
allocated segments are freed immediately if a queue fails to create.
Additionally, resolve the following issues in the queue segment
free/alloc paths:
1. Clear mrioc->intr_info[].op_reply_q with WRITE_ONCE() and follow it
with synchronize_irq() before freeing segments, and have the ISR
paths read it once via READ_ONCE() into a local, to close a race
where the ISR could use the pointer while it is being freed.
2. Free q_segment_list before checking q_segments in both free
functions, since a kzalloc_objs() failure on q_segments left
q_segment_list leaked via the early return.
3. The threaded poll handler returned without re-enabling the
interrupt when the reply queue was already gone, leaving that
interrupt line permanently masked. It now re-enables it before
returning.
4. Two other callers read the same pointer without a NULL check,
which could now be reached with a NULL value. Add the check at
the single point they both call through, and read the pointer
consistently with the writer above.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260626114109.43685-1-ranjan.kumar@broadcom.com?part=6
Closes: https://sashiko.dev/#/patchset/20260708183305.244485-1-ranjan.kumar@broadcom.com?part=6
Closes: https://sashiko.dev/#/patchset/20260724102505.115136-1-ranjan.kumar@broadcom.com?part=6
Closes: https://sashiko.dev/#/patchset/20260805110634.346670-1-ranjan.kumar@broadcom.com?part=6
Signed-off-by: Chandrakanth Patil <chandrakanth.patil@broadcom.com>
Signed-off-by: Ranjan Kumar <ranjan.kumar@broadcom.com>
---
drivers/scsi/mpi3mr/mpi3mr_fw.c | 91 ++++++++++++++++++++++++---------
drivers/scsi/mpi3mr/mpi3mr_os.c | 2 +-
2 files changed, 68 insertions(+), 25 deletions(-)
diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c
index 51ddcc1008c2..d63870b087bc 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_fw.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c
@@ -582,6 +582,9 @@ int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc,
struct mpi3_default_reply_descriptor *reply_desc;
u16 req_q_idx = 0, reply_qidx, threshold_comps = 0;
+ if (!op_reply_q)
+ return 0;
+
reply_qidx = op_reply_q->qid - 1;
if (!atomic_add_unless(&op_reply_q->in_use, 1, 1))
@@ -714,6 +717,7 @@ static irqreturn_t mpi3mr_isr_primary(int irq, void *privdata)
{
struct mpi3mr_intr_info *intr_info = privdata;
struct mpi3mr_ioc *mrioc;
+ struct op_reply_qinfo *op_reply_q;
u16 midx;
u32 num_admin_replies = 0, num_op_reply = 0;
@@ -729,9 +733,9 @@ static irqreturn_t mpi3mr_isr_primary(int irq, void *privdata)
if (!midx)
num_admin_replies = mpi3mr_process_admin_reply_q(mrioc);
- if (intr_info->op_reply_q)
- num_op_reply = mpi3mr_process_op_reply_q(mrioc,
- intr_info->op_reply_q);
+ op_reply_q = READ_ONCE(intr_info->op_reply_q);
+ if (op_reply_q)
+ num_op_reply = mpi3mr_process_op_reply_q(mrioc, op_reply_q);
if (num_admin_replies || num_op_reply)
return IRQ_HANDLED;
@@ -744,6 +748,7 @@ static irqreturn_t mpi3mr_isr_primary(int irq, void *privdata)
static irqreturn_t mpi3mr_isr(int irq, void *privdata)
{
struct mpi3mr_intr_info *intr_info = privdata;
+ struct op_reply_qinfo *op_reply_q;
int ret;
if (!intr_info)
@@ -756,11 +761,12 @@ static irqreturn_t mpi3mr_isr(int irq, void *privdata)
* If more IOs are expected, schedule IRQ polling thread.
* Otherwise exit from ISR.
*/
- if ((threaded_isr_poll == false) || !intr_info->op_reply_q)
+ op_reply_q = READ_ONCE(intr_info->op_reply_q);
+ if ((threaded_isr_poll == false) || !op_reply_q)
return ret;
- if (!intr_info->op_reply_q->enable_irq_poll ||
- !atomic_read(&intr_info->op_reply_q->pend_ios))
+ if (!op_reply_q->enable_irq_poll ||
+ !atomic_read(&op_reply_q->pend_ios))
return ret;
disable_irq_nosync(intr_info->os_irq);
@@ -782,12 +788,19 @@ static irqreturn_t mpi3mr_isr_poll(int irq, void *privdata)
{
struct mpi3mr_intr_info *intr_info = privdata;
struct mpi3mr_ioc *mrioc;
+ struct op_reply_qinfo *op_reply_q;
u16 midx;
u32 num_op_reply = 0;
- if (!intr_info || !intr_info->op_reply_q)
+ if (!intr_info)
return IRQ_NONE;
+ op_reply_q = READ_ONCE(intr_info->op_reply_q);
+ if (!op_reply_q) {
+ enable_irq(intr_info->os_irq);
+ return IRQ_HANDLED;
+ }
+
mrioc = intr_info->mrioc;
midx = intr_info->msix_index;
@@ -796,20 +809,23 @@ static irqreturn_t mpi3mr_isr_poll(int irq, void *privdata)
if (!mrioc->intr_enabled || mrioc->unrecoverable)
break;
+ op_reply_q = READ_ONCE(intr_info->op_reply_q);
+ if (!op_reply_q)
+ break;
+
if (!midx)
mpi3mr_process_admin_reply_q(mrioc);
- if (intr_info->op_reply_q)
- num_op_reply +=
- mpi3mr_process_op_reply_q(mrioc,
- intr_info->op_reply_q);
- if (!atomic_read(&intr_info->op_reply_q->pend_ios))
+ num_op_reply +=
+ mpi3mr_process_op_reply_q(mrioc, op_reply_q);
+ if (!atomic_read(&op_reply_q->pend_ios))
break;
usleep_range(MPI3MR_IRQ_POLL_SLEEP, 10 * MPI3MR_IRQ_POLL_SLEEP);
} while (num_op_reply < mrioc->max_host_ios);
- intr_info->op_reply_q->enable_irq_poll = false;
+ if (op_reply_q)
+ op_reply_q->enable_irq_poll = false;
enable_irq(intr_info->os_irq);
return IRQ_HANDLED;
@@ -1993,10 +2009,6 @@ static void mpi3mr_free_op_req_q_segments(struct mpi3mr_ioc *mrioc, u16 q_idx)
int size;
struct segments *segments;
- segments = mrioc->req_qinfo[q_idx].q_segments;
- if (!segments)
- return;
-
if (mrioc->enable_segqueue) {
size = MPI3MR_OP_REQ_Q_SEG_SIZE;
if (mrioc->req_qinfo[q_idx].q_segment_list) {
@@ -2010,6 +2022,10 @@ static void mpi3mr_free_op_req_q_segments(struct mpi3mr_ioc *mrioc, u16 q_idx)
size = mrioc->req_qinfo[q_idx].segment_qd *
mrioc->facts.op_req_sz;
+ segments = mrioc->req_qinfo[q_idx].q_segments;
+ if (!segments)
+ return;
+
for (j = 0; j < mrioc->req_qinfo[q_idx].num_segments; j++) {
if (!segments[j].segment)
continue;
@@ -2036,10 +2052,17 @@ static void mpi3mr_free_op_reply_q_segments(struct mpi3mr_ioc *mrioc, u16 q_idx)
u16 j;
int size;
struct segments *segments;
+ u16 midx = REPLY_QUEUE_IDX_TO_MSIX_IDX(q_idx, mrioc->op_reply_q_offset);
- segments = mrioc->op_reply_qinfo[q_idx].q_segments;
- if (!segments)
- return;
+ /*
+ * Stop the ISR/poll thread from picking up this queue before its
+ * segments are freed below, and wait for any in-flight handler
+ * that already has the old pointer to finish using it.
+ */
+ if (midx < mrioc->intr_info_count) {
+ WRITE_ONCE(mrioc->intr_info[midx].op_reply_q, NULL);
+ synchronize_irq(pci_irq_vector(mrioc->pdev, midx));
+ }
if (mrioc->enable_segqueue) {
size = MPI3MR_OP_REP_Q_SEG_SIZE;
@@ -2054,6 +2077,10 @@ static void mpi3mr_free_op_reply_q_segments(struct mpi3mr_ioc *mrioc, u16 q_idx)
size = mrioc->op_reply_qinfo[q_idx].segment_qd *
mrioc->op_reply_desc_sz;
+ segments = mrioc->op_reply_qinfo[q_idx].q_segments;
+ if (!segments)
+ return;
+
for (j = 0; j < mrioc->op_reply_qinfo[q_idx].num_segments; j++) {
if (!segments[j].segment)
continue;
@@ -2520,7 +2547,7 @@ static int mpi3mr_create_op_req_q(struct mpi3mr_ioc *mrioc, u16 idx,
static int mpi3mr_create_op_queues(struct mpi3mr_ioc *mrioc)
{
int retval = 0;
- u16 num_queues = 0, i = 0, msix_count_op_q = 1;
+ u16 num_queues = 0, i = 0, j = 0, msix_count_op_q = 1;
u32 ioc_status;
enum mpi3mr_iocstate ioc_state;
@@ -2572,6 +2599,13 @@ static int mpi3mr_create_op_queues(struct mpi3mr_ioc *mrioc)
}
}
+ if (i < num_queues) {
+ for (j = i; j < num_queues; j++) {
+ mpi3mr_free_op_req_q_segments(mrioc, j);
+ mpi3mr_free_op_reply_q_segments(mrioc, j);
+ }
+ }
+
if (i == 0) {
/* Not even one queue is created successfully*/
retval = -1;
@@ -2593,11 +2627,19 @@ static int mpi3mr_create_op_queues(struct mpi3mr_ioc *mrioc)
return retval;
out_failed:
- kfree(mrioc->req_qinfo);
- mrioc->req_qinfo = NULL;
+ if (mrioc->req_qinfo) {
+ for (j = 0; j < i; j++) {
+ mpi3mr_free_op_req_q_segments(mrioc, j);
+ mpi3mr_free_op_reply_q_segments(mrioc, j);
+ }
+ kfree(mrioc->req_qinfo);
+ mrioc->req_qinfo = NULL;
+ }
+ mrioc->num_op_req_q = 0;
kfree(mrioc->op_reply_qinfo);
mrioc->op_reply_qinfo = NULL;
+ mrioc->num_op_reply_q = 0;
return retval;
}
@@ -2641,7 +2683,8 @@ int mpi3mr_op_request_post(struct mpi3mr_ioc *mrioc,
if (mpi3mr_check_req_qfull(op_req_q)) {
midx = REPLY_QUEUE_IDX_TO_MSIX_IDX(
reply_qidx, mrioc->op_reply_q_offset);
- mpi3mr_process_op_reply_q(mrioc, mrioc->intr_info[midx].op_reply_q);
+ mpi3mr_process_op_reply_q(mrioc,
+ READ_ONCE(mrioc->intr_info[midx].op_reply_q));
if (mpi3mr_check_req_qfull(op_req_q)) {
diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c
index 7e59773c0276..3412e1e0e8ce 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_os.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_os.c
@@ -3993,7 +3993,7 @@ inline void mpi3mr_poll_pend_io_completions(struct mpi3mr_ioc *mrioc)
for (i = mrioc->op_reply_q_offset; i < num_of_reply_queues; i++)
mpi3mr_process_op_reply_q(mrioc,
- mrioc->intr_info[i].op_reply_q);
+ READ_ONCE(mrioc->intr_info[i].op_reply_q));
}
/**
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 07/10] mpi3mr: Fix firmware event reference leak during cleanup
2026-09-16 8:26 [PATCH v5 00/10] mpi3mr: Few Enhancements and minor fixes Ranjan Kumar
` (5 preceding siblings ...)
2026-09-16 8:27 ` [PATCH v5 06/10] mpi3mr: Fix memory leak on operational queue creation failure Ranjan Kumar
@ 2026-09-16 8:27 ` Ranjan Kumar
2026-09-16 8:27 ` [PATCH v5 08/10] mpi3mr: Fix SAS port allocation and registration error handling Ranjan Kumar
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Ranjan Kumar @ 2026-09-16 8:27 UTC (permalink / raw)
To: linux-scsi, martin.petersen
Cc: sathya.prakash, chandrakanth.patil, vishakhavc, ipylypiv,
Ranjan Kumar, Sashiko
During firmware event cleanup, when an event is currently executing or
pending at the SCSI mid-layer, the driver sets a discard flag and exits
the cleanup routine early. This early exit skips the normal cancel path,
resulting in the firmware event reference count not being decremented,
leading to a reference leak.
Additionally, resolve the following concurrency issues in the firmware
event handling paths:
1. mpi3mr_cleanup_fwevt_list() read current_event locklessly. It is
now acquired under fwevt_lock.
2. mpi3mr_dequeue_fwevt() dropped the reference before returning it,
risking a use-after-free. The drop is now moved into
mpi3mr_cancel_work().
3. mpi3mr_fwevt_bh() dropped fwevt_lock mid-move, racing with unload.
The move is now inlined under one continuous lock hold.
4. pending_at_sml was read/written without a lock, risking an ABBA
deadlock. It is now protected by fwevt_lock throughout.
5. mpi3mr_suspend() could unmap PCI resources before the event
worker finished. It now flushes the workqueue first.
6. mpi3mr_report_tgtdev_to_host() and mpi3mr_remove_tgtdev_from_host()
could still set pending_at_sml and block in the SCSI mid-layer
after a stop or reset had already begun, deadlocking against the
thread waiting on that flag. Both now bail out beforehand once
stop_drv_processing or reset_in_progress is set.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260626114109.43685-1-ranjan.kumar@broadcom.com?part=7
Closes: https://sashiko.dev/#/patchset/20260708183305.244485-1-ranjan.kumar@broadcom.com?part=7
Closes: https://sashiko.dev/#/patchset/20260724102505.115136-1-ranjan.kumar@broadcom.com?part=7
Signed-off-by: Chandrakanth Patil <chandrakanth.patil@broadcom.com>
Signed-off-by: Ranjan Kumar <ranjan.kumar@broadcom.com>
---
drivers/scsi/mpi3mr/mpi3mr_os.c | 139 +++++++++++++++++--------
drivers/scsi/mpi3mr/mpi3mr_transport.c | 20 +++-
2 files changed, 109 insertions(+), 50 deletions(-)
diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c
index 3412e1e0e8ce..dd11b13f9d0b 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_os.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_os.c
@@ -283,32 +283,6 @@ void mpi3mr_hdb_trigger_data_event(struct mpi3mr_ioc *mrioc,
mpi3mr_fwevt_add_to_list(mrioc, fwevt);
}
-/**
- * mpi3mr_fwevt_del_from_list - Delete firmware event from list
- * @mrioc: Adapter instance reference
- * @fwevt: Firmware event reference
- *
- * Delete the given firmware event from the firmware event list.
- *
- * Return: Nothing.
- */
-static void mpi3mr_fwevt_del_from_list(struct mpi3mr_ioc *mrioc,
- struct mpi3mr_fwevt *fwevt)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&mrioc->fwevt_lock, flags);
- if (!list_empty(&fwevt->list)) {
- list_del_init(&fwevt->list);
- /*
- * Put fwevt reference count after
- * removing it from fwevt_list
- */
- mpi3mr_fwevt_put(fwevt);
- }
- spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
-}
-
/**
* mpi3mr_dequeue_fwevt - Dequeue firmware event from the list
* @mrioc: Adapter instance reference
@@ -328,11 +302,7 @@ static struct mpi3mr_fwevt *mpi3mr_dequeue_fwevt(
fwevt = list_first_entry(&mrioc->fwevt_list,
struct mpi3mr_fwevt, list);
list_del_init(&fwevt->list);
- /*
- * Put fwevt reference count after
- * removing it from fwevt_list
- */
- mpi3mr_fwevt_put(fwevt);
+
}
spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
@@ -366,6 +336,11 @@ static void mpi3mr_cancel_work(struct mpi3mr_fwevt *fwevt)
*/
mpi3mr_fwevt_put(fwevt);
}
+
+ /*
+ * Drop the reference count that was acquired by the caller.
+ */
+ mpi3mr_fwevt_put(fwevt);
}
/**
@@ -380,17 +355,44 @@ static void mpi3mr_cancel_work(struct mpi3mr_fwevt *fwevt)
void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc)
{
struct mpi3mr_fwevt *fwevt = NULL;
+ unsigned long flags;
+ /*
+ * Safely read current_event under lock to prevent TOCTOU race
+ * with the firmware event worker thread.
+ */
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
if ((list_empty(&mrioc->fwevt_list) && !mrioc->current_event) ||
- !mrioc->fwevt_worker_thread)
+ !mrioc->fwevt_worker_thread) {
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
return;
+ }
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
while ((fwevt = mpi3mr_dequeue_fwevt(mrioc)))
mpi3mr_cancel_work(fwevt);
- if (mrioc->current_event) {
- fwevt = mrioc->current_event;
+ /*
+ * Safely read current_event under lock to prevent TOCTOU race
+ * with the firmware event worker thread.
+ */
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ fwevt = mrioc->current_event;
+ if (fwevt) {
+ /*
+ * Take a reference to ensure the event is not freed by the
+ * worker thread while we are evaluating or cancelling it.
+ */
+ mpi3mr_fwevt_get(fwevt);
+ }
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+
+ if (fwevt) {
+ bool pending_at_sml;
+
/*
+ * Read pending_at_sml under lock to avoid a stale value.
+ *
* Don't call cancel_work_sync() API for the
* fwevt work if the controller reset is
* get called as part of processing the
@@ -398,8 +400,13 @@ void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc)
* waiting for device add/remove APIs to complete.
* Otherwise we will see deadlock.
*/
- if (current_work() == &fwevt->work || fwevt->pending_at_sml) {
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ pending_at_sml = fwevt->pending_at_sml;
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+
+ if (current_work() == &fwevt->work || pending_at_sml) {
fwevt->discard = 1;
+ mpi3mr_fwevt_put(fwevt);
return;
}
@@ -913,6 +920,8 @@ void mpi3mr_remove_tgtdev_from_host(struct mpi3mr_ioc *mrioc,
struct mpi3mr_tgt_dev *tgtdev)
{
struct mpi3mr_stgt_priv_data *tgt_priv;
+ unsigned long flags;
+ bool discard = false;
ioc_info(mrioc, "%s :Removing handle(0x%04x), wwid(0x%016llx)\n",
__func__, tgtdev->dev_handle, (unsigned long long)tgtdev->wwid);
@@ -925,17 +934,27 @@ void mpi3mr_remove_tgtdev_from_host(struct mpi3mr_ioc *mrioc,
if (!mrioc->sas_transport_enabled || (tgtdev->dev_type !=
MPI3_DEVICE_DEVFORM_SAS_SATA) || tgtdev->non_stl) {
if (tgtdev->starget) {
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ if (mrioc->stop_drv_processing ||
+ mrioc->reset_in_progress) {
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+ return;
+ }
if (mrioc->current_event)
mrioc->current_event->pending_at_sml = 1;
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
scsi_remove_target(&tgtdev->starget->dev);
tgtdev->host_exposed = 0;
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
if (mrioc->current_event) {
mrioc->current_event->pending_at_sml = 0;
- if (mrioc->current_event->discard) {
- mpi3mr_print_device_event_notice(mrioc,
- false);
- return;
- }
+ discard = mrioc->current_event->discard;
+ }
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+ if (discard) {
+ mpi3mr_print_device_event_notice(mrioc,
+ false);
+ return;
}
}
} else
@@ -963,6 +982,8 @@ static int mpi3mr_report_tgtdev_to_host(struct mpi3mr_ioc *mrioc,
{
int retval = 0;
struct mpi3mr_tgt_dev *tgtdev;
+ unsigned long flags;
+ bool discard = false;
if (mrioc->reset_in_progress || mrioc->pci_err_recovery)
return -1;
@@ -979,19 +1000,29 @@ static int mpi3mr_report_tgtdev_to_host(struct mpi3mr_ioc *mrioc,
if (!mrioc->sas_transport_enabled || (tgtdev->dev_type !=
MPI3_DEVICE_DEVFORM_SAS_SATA) || tgtdev->non_stl){
tgtdev->host_exposed = 1;
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ if (mrioc->stop_drv_processing || mrioc->reset_in_progress) {
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+ tgtdev->host_exposed = 0;
+ goto out;
+ }
if (mrioc->current_event)
mrioc->current_event->pending_at_sml = 1;
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
scsi_scan_target(&mrioc->shost->shost_gendev,
mrioc->scsi_device_channel, tgtdev->perst_id,
SCAN_WILD_CARD, SCSI_SCAN_INITIAL);
if (!tgtdev->starget)
tgtdev->host_exposed = 0;
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
if (mrioc->current_event) {
mrioc->current_event->pending_at_sml = 0;
- if (mrioc->current_event->discard) {
- mpi3mr_print_device_event_notice(mrioc, true);
- goto out;
- }
+ discard = mrioc->current_event->discard;
+ }
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+ if (discard) {
+ mpi3mr_print_device_event_notice(mrioc, true);
+ goto out;
}
dprint_event_bh(mrioc,
"exposed target device with handle(0x%04x), perst_id(%d)\n",
@@ -2134,9 +2165,19 @@ static void mpi3mr_fwevt_bh(struct mpi3mr_ioc *mrioc,
u16 perst_id, handle, dev_info;
struct mpi3_device0_sas_sata_format *sasinf = NULL;
unsigned int timeout;
+ unsigned long flags;
- mpi3mr_fwevt_del_from_list(mrioc, fwevt);
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ if (!list_empty(&fwevt->list)) {
+ list_del_init(&fwevt->list);
+ /*
+ * Put fwevt reference count after
+ * removing it from fwevt_list
+ */
+ mpi3mr_fwevt_put(fwevt);
+ }
mrioc->current_event = fwevt;
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
if (mrioc->stop_drv_processing || mrioc->pci_err_recovery) {
dprint_event_bh(mrioc,
@@ -2271,9 +2312,12 @@ static void mpi3mr_fwevt_bh(struct mpi3mr_ioc *mrioc,
mpi3mr_process_event_ack(mrioc, fwevt->event_id,
fwevt->evt_ctx);
out:
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ mrioc->current_event = NULL;
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+
/* Put fwevt reference count to neutralize kref_init increment */
mpi3mr_fwevt_put(fwevt);
- mrioc->current_event = NULL;
}
/**
@@ -5874,6 +5918,9 @@ mpi3mr_suspend(struct device *dev)
ssleep(1);
mrioc->stop_drv_processing = 1;
mpi3mr_cleanup_fwevt_list(mrioc);
+ /* Flush any pending discarded event before unmapping PCI resources below. */
+ if (mrioc->fwevt_worker_thread)
+ flush_workqueue(mrioc->fwevt_worker_thread);
scsi_block_requests(shost);
mpi3mr_stop_watchdog(mrioc);
mpi3mr_cleanup_ioc(mrioc);
diff --git a/drivers/scsi/mpi3mr/mpi3mr_transport.c b/drivers/scsi/mpi3mr/mpi3mr_transport.c
index 2afff1a002ed..be794fe8eb7d 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_transport.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_transport.c
@@ -1337,6 +1337,7 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc,
struct mpi3mr_sas_phy *mr_sas_phy, *next;
struct mpi3mr_sas_port *mr_sas_port;
unsigned long flags;
+ bool discard = false;
struct mpi3mr_sas_node *mr_sas_node;
struct sas_rphy *rphy;
struct mpi3mr_tgt_dev *tgtdev = NULL;
@@ -1464,8 +1465,10 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc,
}
rphy->identify = mr_sas_port->remote_identify;
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
if (mrioc->current_event)
mrioc->current_event->pending_at_sml = 1;
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
if ((sas_rphy_add(rphy))) {
ioc_err(mrioc, "failure at %s:%d/%s()!\n",
@@ -1487,11 +1490,14 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc,
list_add_tail(&mr_sas_port->port_list, &mr_sas_node->sas_port_list);
spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
if (mrioc->current_event) {
mrioc->current_event->pending_at_sml = 0;
- if (mrioc->current_event->discard)
- mpi3mr_print_device_event_notice(mrioc, true);
+ discard = mrioc->current_event->discard;
}
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+ if (discard)
+ mpi3mr_print_device_event_notice(mrioc, true);
/* fill in report manufacture */
if (mr_sas_port->remote_identify.device_type ==
@@ -1529,6 +1535,7 @@ static void mpi3mr_sas_port_remove(struct mpi3mr_ioc *mrioc, u64 sas_address,
{
int i;
unsigned long flags;
+ bool discard = false;
struct mpi3mr_sas_port *mr_sas_port, *next;
struct mpi3mr_sas_node *mr_sas_node;
u8 found = 0;
@@ -1585,8 +1592,10 @@ static void mpi3mr_sas_port_remove(struct mpi3mr_ioc *mrioc, u64 sas_address,
spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
if (mrioc->current_event)
mrioc->current_event->pending_at_sml = 1;
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
list_for_each_entry_safe(mr_sas_phy, next_phy,
&mr_sas_port->phy_list, port_siblings) {
@@ -1608,11 +1617,14 @@ static void mpi3mr_sas_port_remove(struct mpi3mr_ioc *mrioc, u64 sas_address,
ioc_info(mrioc, "%s: removed sas_address(0x%016llx)\n",
__func__, (unsigned long long)sas_address);
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
if (mrioc->current_event) {
mrioc->current_event->pending_at_sml = 0;
- if (mrioc->current_event->discard)
- mpi3mr_print_device_event_notice(mrioc, false);
+ discard = mrioc->current_event->discard;
}
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+ if (discard)
+ mpi3mr_print_device_event_notice(mrioc, false);
kfree(mr_sas_port);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 08/10] mpi3mr: Fix SAS port allocation and registration error handling
2026-09-16 8:26 [PATCH v5 00/10] mpi3mr: Few Enhancements and minor fixes Ranjan Kumar
` (6 preceding siblings ...)
2026-09-16 8:27 ` [PATCH v5 07/10] mpi3mr: Fix firmware event reference leak during cleanup Ranjan Kumar
@ 2026-09-16 8:27 ` Ranjan Kumar
2026-09-16 8:27 ` [PATCH v5 09/10] mpi3mr: Fix SAS PHY cleanup in host addition error paths Ranjan Kumar
2026-09-16 8:27 ` [PATCH v5 10/10] mpi3mr: Driver version update to 8.18.0.8.50 Ranjan Kumar
9 siblings, 0 replies; 11+ messages in thread
From: Ranjan Kumar @ 2026-09-16 8:27 UTC (permalink / raw)
To: linux-scsi, martin.petersen
Cc: sathya.prakash, chandrakanth.patil, vishakhavc, ipylypiv,
Ranjan Kumar, Sashiko
During SAS port creation, the driver does not verify successful port
allocation before attempting registration, which can lead to a NULL
pointer dereference. Additionally, if registration fails, the allocated
port is not freed, resulting in a memory leak.
Fix this by adding a NULL check after allocation and freeing the port
when registration fails.
Additional fixes in the error handling path include:
1. Fixing similar missing NULL checks for rphy allocations.
2. Cleaning up after a failed rphy registration tried to remove a
device that was never added, causing a crash. The rphy is now
freed directly instead.
3. A failed rphy registration left the target device with a dangling
pointer and a stuck pending flag. Both are now cleared.
4. Phys removed on error kept an internal flag set, permanently
blocking them from being added to a port again. Now cleared
alongside the list removal.
5. Could block in the SCSI mid-layer after a stop or reset had
already begun, the same ABBA deadlock class fixed elsewhere. The
port allocation path now stops before that call once that is
detected.
6. The same reset check on the port removal path caused a memory
leak and a kernel BUG() on rediscovery.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260626114109.43685-1-ranjan.kumar@broadcom.com?part=8
Closes: https://sashiko.dev/#/patchset/20260708183305.244485-1-ranjan.kumar@broadcom.com?part=8
Closes: https://sashiko.dev/#/patchset/20260724102505.115136-1-ranjan.kumar@broadcom.com?part=8
Closes: https://sashiko.dev/#/patchset/20260805110634.346670-1-ranjan.kumar@broadcom.com?part=8
Signed-off-by: Chandrakanth Patil <chandrakanth.patil@broadcom.com>
Signed-off-by: Ranjan Kumar <ranjan.kumar@broadcom.com>
---
drivers/scsi/mpi3mr/mpi3mr_transport.c | 46 +++++++++++++++++++++++++-
1 file changed, 45 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/mpi3mr/mpi3mr_transport.c b/drivers/scsi/mpi3mr/mpi3mr_transport.c
index be794fe8eb7d..0dacfae6fa9d 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_transport.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_transport.c
@@ -1436,9 +1436,15 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc,
}
port = sas_port_alloc_num(mr_sas_node->parent_dev);
+ if (!port) {
+ ioc_err(mrioc, "failure at %s:%d/%s() (sas_port_alloc)!\n",
+ __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
if ((sas_port_add(port))) {
ioc_err(mrioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__);
+ sas_port_free(port);
goto out_fail;
}
@@ -1458,14 +1464,32 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc,
mr_sas_port->port = port;
if (mr_sas_port->remote_identify.device_type == SAS_END_DEVICE) {
rphy = sas_end_device_alloc(port);
+ if (!rphy) {
+ ioc_err(mrioc, "failure at %s:%d/%s() (sas_end_device_alloc)!\n",
+ __FILE__, __LINE__, __func__);
+ sas_port_delete(port);
+ goto out_fail;
+ }
tgtdev->dev_spec.sas_sata_inf.rphy = rphy;
} else {
rphy = sas_expander_alloc(port,
mr_sas_port->remote_identify.device_type);
+ if (!rphy) {
+ ioc_err(mrioc, "failure at %s:%d/%s() (sas_expander_alloc)!\n",
+ __FILE__, __LINE__, __func__);
+ sas_port_delete(port);
+ goto out_fail;
+ }
}
rphy->identify = mr_sas_port->remote_identify;
spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ if (mrioc->stop_drv_processing || mrioc->reset_in_progress) {
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+ sas_rphy_free(rphy);
+ sas_port_delete(port);
+ goto out_fail;
+ }
if (mrioc->current_event)
mrioc->current_event->pending_at_sml = 1;
spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
@@ -1473,6 +1497,18 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc,
if ((sas_rphy_add(rphy))) {
ioc_err(mrioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__);
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ if (mrioc->current_event) {
+ mrioc->current_event->pending_at_sml = 0;
+ discard = mrioc->current_event->discard;
+ }
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+ if (discard)
+ mpi3mr_print_device_event_notice(mrioc, true);
+ sas_rphy_unlink(rphy);
+ sas_rphy_free(rphy);
+ sas_port_delete(port);
+ goto out_fail;
}
if (mr_sas_port->remote_identify.device_type == SAS_END_DEVICE) {
tgtdev->dev_spec.sas_sata_inf.pend_sas_rphy_add = 0;
@@ -1511,9 +1547,17 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc,
return mr_sas_port;
out_fail:
+ if (tgtdev) {
+ tgtdev->dev_spec.sas_sata_inf.pend_sas_rphy_add = 0;
+ tgtdev->dev_spec.sas_sata_inf.rphy = NULL;
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+
list_for_each_entry_safe(mr_sas_phy, next, &mr_sas_port->phy_list,
- port_siblings)
+ port_siblings) {
+ mr_sas_phy->phy_belongs_to_port = 0;
list_del(&mr_sas_phy->port_siblings);
+ }
kfree(mr_sas_port);
return NULL;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 09/10] mpi3mr: Fix SAS PHY cleanup in host addition error paths
2026-09-16 8:26 [PATCH v5 00/10] mpi3mr: Few Enhancements and minor fixes Ranjan Kumar
` (7 preceding siblings ...)
2026-09-16 8:27 ` [PATCH v5 08/10] mpi3mr: Fix SAS port allocation and registration error handling Ranjan Kumar
@ 2026-09-16 8:27 ` Ranjan Kumar
2026-09-16 8:27 ` [PATCH v5 10/10] mpi3mr: Driver version update to 8.18.0.8.50 Ranjan Kumar
9 siblings, 0 replies; 11+ messages in thread
From: Ranjan Kumar @ 2026-09-16 8:27 UTC (permalink / raw)
To: linux-scsi, martin.petersen
Cc: sathya.prakash, chandrakanth.patil, vishakhavc, ipylypiv,
Ranjan Kumar, Sashiko
When adding a SAS host, the driver allocates a PHY array and subsequently
creates individual SAS PHYs. If a later step fails, the error path exits
without cleaning up previously allocated resources, resulting in leaks of
both the PHY array and any registered SAS PHYs.
Additionally, the return value of mpi3mr_add_host_phy() was being ignored.
If it failed, mr_sas_phy->phy would be left as NULL, which could later
lead to a NULL pointer dereference in mpi3mr_sas_port_add() when the
attached device triggers a device addition event.
Add a dedicated cleanup path that deletes any successfully created SAS
PHYs and frees the PHY array before returning from initialization
failure paths. Also, check the return value of mpi3mr_add_host_phy()
and jump to the cleanup path on failure.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260626114109.43685-1-ranjan.kumar@broadcom.com?part=9
Signed-off-by: Chandrakanth Patil <chandrakanth.patil@broadcom.com>
Signed-off-by: Ranjan Kumar <ranjan.kumar@broadcom.com>
---
drivers/scsi/mpi3mr/mpi3mr_transport.c | 33 ++++++++++++++++++--------
1 file changed, 23 insertions(+), 10 deletions(-)
diff --git a/drivers/scsi/mpi3mr/mpi3mr_transport.c b/drivers/scsi/mpi3mr/mpi3mr_transport.c
index 0dacfae6fa9d..a3412cd4ca5b 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_transport.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_transport.c
@@ -1223,13 +1223,14 @@ void mpi3mr_sas_host_add(struct mpi3mr_ioc *mrioc)
}
num_phys = sas_io_unit_pg0->num_phys;
kfree(sas_io_unit_pg0);
+ sas_io_unit_pg0 = NULL;
mrioc->sas_hba.host_node = 1;
INIT_LIST_HEAD(&mrioc->sas_hba.sas_port_list);
mrioc->sas_hba.parent_dev = &mrioc->shost->shost_gendev;
mrioc->sas_hba.phy = kzalloc_objs(struct mpi3mr_sas_phy, num_phys);
if (!mrioc->sas_hba.phy)
- return;
+ goto out;
mrioc->sas_hba.num_phys = num_phys;
@@ -1237,12 +1238,12 @@ void mpi3mr_sas_host_add(struct mpi3mr_ioc *mrioc)
(num_phys * sizeof(struct mpi3_sas_io_unit0_phy_data));
sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL);
if (!sas_io_unit_pg0)
- return;
+ goto out_free_phy;
if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) {
ioc_err(mrioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__);
- goto out;
+ goto out_free_phy;
}
mrioc->sas_hba.handle = 0;
@@ -1256,12 +1257,12 @@ void mpi3mr_sas_host_add(struct mpi3mr_ioc *mrioc)
MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER, i)) {
ioc_err(mrioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__);
- goto out;
+ goto out_free_phy;
}
if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
ioc_err(mrioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__);
- goto out;
+ goto out_free_phy;
}
if (!mrioc->sas_hba.handle)
@@ -1271,26 +1272,27 @@ void mpi3mr_sas_host_add(struct mpi3mr_ioc *mrioc)
if (!(mpi3mr_get_hba_port_by_id(mrioc, port_id)))
if (!mpi3mr_alloc_hba_port(mrioc, port_id))
- goto out;
+ goto out_free_phy;
mrioc->sas_hba.phy[i].handle = mrioc->sas_hba.handle;
mrioc->sas_hba.phy[i].phy_id = i;
mrioc->sas_hba.phy[i].hba_port =
mpi3mr_get_hba_port_by_id(mrioc, port_id);
- mpi3mr_add_host_phy(mrioc, &mrioc->sas_hba.phy[i],
- phy_pg0, mrioc->sas_hba.parent_dev);
+ if (mpi3mr_add_host_phy(mrioc, &mrioc->sas_hba.phy[i],
+ phy_pg0, mrioc->sas_hba.parent_dev))
+ goto out_free_phy;
}
if ((mpi3mr_cfg_get_dev_pg0(mrioc, &ioc_status, &dev_pg0,
sizeof(dev_pg0), MPI3_DEVICE_PGAD_FORM_HANDLE,
mrioc->sas_hba.handle))) {
ioc_err(mrioc, "%s: device page0 read failed\n", __func__);
- goto out;
+ goto out_free_phy;
}
if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
ioc_err(mrioc, "device page read failed for handle(0x%04x), with ioc_status(0x%04x) failure at %s:%d/%s()!\n",
mrioc->sas_hba.handle, ioc_status, __FILE__, __LINE__,
__func__);
- goto out;
+ goto out_free_phy;
}
mrioc->sas_hba.enclosure_handle =
le16_to_cpu(dev_pg0.enclosure_handle);
@@ -1313,6 +1315,17 @@ void mpi3mr_sas_host_add(struct mpi3mr_ioc *mrioc)
le64_to_cpu(encl_pg0.enclosure_logical_id);
}
+ goto out;
+
+out_free_phy:
+ for (i = 0; i < mrioc->sas_hba.num_phys; i++) {
+ if (mrioc->sas_hba.phy[i].phy)
+ sas_phy_delete(mrioc->sas_hba.phy[i].phy);
+ }
+ kfree(mrioc->sas_hba.phy);
+ mrioc->sas_hba.phy = NULL;
+ mrioc->sas_hba.num_phys = 0;
+
out:
kfree(sas_io_unit_pg0);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 10/10] mpi3mr: Driver version update to 8.18.0.8.50
2026-09-16 8:26 [PATCH v5 00/10] mpi3mr: Few Enhancements and minor fixes Ranjan Kumar
` (8 preceding siblings ...)
2026-09-16 8:27 ` [PATCH v5 09/10] mpi3mr: Fix SAS PHY cleanup in host addition error paths Ranjan Kumar
@ 2026-09-16 8:27 ` Ranjan Kumar
9 siblings, 0 replies; 11+ messages in thread
From: Ranjan Kumar @ 2026-09-16 8:27 UTC (permalink / raw)
To: linux-scsi, martin.petersen
Cc: sathya.prakash, chandrakanth.patil, vishakhavc, ipylypiv,
Ranjan Kumar
Update driver version to 8.18.0.8.50
Signed-off-by: Ranjan Kumar <ranjan.kumar@broadcom.com>
---
drivers/scsi/mpi3mr/mpi3mr.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h
index 4d19a9460d38..d6e16707fd97 100644
--- a/drivers/scsi/mpi3mr/mpi3mr.h
+++ b/drivers/scsi/mpi3mr/mpi3mr.h
@@ -56,8 +56,8 @@ extern struct list_head mrioc_list;
extern int prot_mask;
extern atomic64_t event_counter;
-#define MPI3MR_DRIVER_VERSION "8.17.0.3.50"
-#define MPI3MR_DRIVER_RELDATE "09-January-2026"
+#define MPI3MR_DRIVER_VERSION "8.18.0.8.50"
+#define MPI3MR_DRIVER_RELDATE "26-June-2026"
#define MPI3MR_DRIVER_NAME "mpi3mr"
#define MPI3MR_DRIVER_LICENSE "GPL"
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread