scsi: qla2xxx: Improve firmware dump data capture

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

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

Signed-off-by: Quinn Tran <qutran@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Link: https://patch.msgid.link/20260730155838.2119230-3-njavali@marvell.com
Signed-off-by: Martin K. Petersen (Oracle) <mkp@kernel.org>
This commit is contained in:
Quinn Tran 2026-07-30 21:28:07 +05:30 committed by Martin K. Petersen (Oracle)
parent ebfd35c644
commit ce4f9a383d
5 changed files with 116 additions and 67 deletions

View File

@ -172,7 +172,7 @@ qla27xx_dump_mpi_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram,
if (!test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
/* no interrupt, timed out*/
return rval;
return QLA_FUNCTION_TIMEOUT;
}
if (rval) {
/* error completion status */
@ -255,7 +255,7 @@ qla24xx_dump_ram(struct qla_hw_data *ha, uint32_t addr, __be32 *ram,
if (!test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
/* no interrupt, timed out*/
return rval;
return QLA_FUNCTION_TIMEOUT;
}
if (rval) {
/* error completion status */

View File

@ -4176,6 +4176,7 @@ struct qla_hw_data {
#define SRB_MIN_REQ 128
mempool_t *srb_mempool;
u8 port_name[WWN_SIZE];
u16 mbregs[32];
volatile struct {
uint32_t mbox_int :1;
@ -4246,6 +4247,8 @@ struct qla_hw_data {
uint32_t eeh_flush:2;
#define EEH_FLUSH_RDY 1
#define EEH_FLUSH_DONE 2
uint32_t t262_fail:1;
uint32_t t272_fail:1;
uint32_t secure_mcu:1;
uint32_t valid_flt:1;
} flags;

View File

@ -3275,47 +3275,37 @@ qla81xx_reset_mpi(scsi_qla_host_t *vha)
return qla81xx_write_mpi_register(vha, mb);
}
static int
qla_chk_risc_recovery(scsi_qla_host_t *vha)
/* save MB regs at start of day for fw dump */
static void
qla_save_mbregs(scsi_qla_host_t *vha)
{
struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
__le16 __iomem *mbptr = &reg->mailbox0;
int i;
u16 mb[32];
int rc = QLA_SUCCESS;
u16 *mb = ha->mbregs;
if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
return rc;
if ((!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) ||
vha->flags.init_done)
return;
/* this check is only valid after RISC reset */
mb[0] = rd_reg_word(mbptr);
mbptr++;
if (mb[0] == 0xf) {
rc = QLA_FUNCTION_FAILED;
for (i = 1; i < 32; i++) {
mb[i] = rd_reg_word(mbptr);
mbptr++;
}
ql_log(ql_log_warn, vha, 0x1015,
"RISC reset failed. mb[0-7] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
mb[0], mb[1], mb[2], mb[3], mb[4], mb[5], mb[6], mb[7]);
ql_log(ql_log_warn, vha, 0x1015,
"RISC reset failed. mb[8-15] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
mb[8], mb[9], mb[10], mb[11], mb[12], mb[13], mb[14],
mb[15]);
ql_log(ql_log_warn, vha, 0x1015,
"RISC reset failed. mb[16-23] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
mb[16], mb[17], mb[18], mb[19], mb[20], mb[21], mb[22],
mb[23]);
ql_log(ql_log_warn, vha, 0x1015,
"RISC reset failed. mb[24-31] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
mb[24], mb[25], mb[26], mb[27], mb[28], mb[29], mb[30],
mb[31]);
for (i = 0; i < 32; i++) {
mb[i] = rd_reg_word(mbptr);
mbptr++;
}
return rc;
ql_log(ql_log_info, vha, 0x1015,
"mb[0-7] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
mb[0], mb[1], mb[2], mb[3], mb[4], mb[5], mb[6], mb[7]);
ql_log(ql_log_info, vha, 0x1015,
"mb[8-15] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
mb[8], mb[9], mb[10], mb[11], mb[12], mb[13], mb[14], mb[15]);
ql_log(ql_log_info, vha, 0x1015,
"mb[16-23] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
mb[16], mb[17], mb[18], mb[19], mb[20], mb[21], mb[22], mb[23]);
ql_log(ql_log_info, vha, 0x1015,
"mb[24-31] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
mb[24], mb[25], mb[26], mb[27], mb[28], mb[29], mb[30], mb[31]);
}
/**
@ -3334,7 +3324,6 @@ qla24xx_reset_risc(scsi_qla_host_t *vha)
uint16_t wd;
static int abts_cnt; /* ISP abort retry counts */
int rval = QLA_SUCCESS;
int print = 1;
spin_lock_irqsave(&ha->hardware_lock, flags);
@ -3431,9 +3420,6 @@ qla24xx_reset_risc(scsi_qla_host_t *vha)
barrier();
if (cnt) {
mdelay(1);
if (print && qla_chk_risc_recovery(vha))
print = 0;
wd = rd_reg_word(&reg->mailbox0);
} else {
rval = QLA_FUNCTION_TIMEOUT;
@ -3453,6 +3439,8 @@ qla24xx_reset_risc(scsi_qla_host_t *vha)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
qla_save_mbregs(vha);
ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x015f,
"Driver in %s mode\n",
IS_NOPOLLING_TYPE(ha) ? "Interrupt" : "Polling");
@ -3813,18 +3801,11 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
struct qla_hw_data *ha = vha->hw;
struct req_que *req = ha->req_q_map[0];
struct rsp_que *rsp = ha->rsp_q_map[0];
struct qla2xxx_fw_dump *fw_dump;
struct qla2xxx_fw_dump *fw_dump, *prev_fw_dump;
void *prev_mpi_fw_dump;
size_t req_entry_size = qla_req_entry_size(ha);
size_t rsp_entry_size = qla_rsp_entry_size(ha);
if (ha->fw_dump) {
ql_dbg(ql_dbg_init, vha, 0x00bd,
"Firmware dump already allocated.\n");
return;
}
ha->fw_dumped = 0;
ha->fw_dump_cap_flags = 0;
dump_size = fixed_size = mem_size = eft_size = fce_size = mq_size = 0;
req_q_size = rsp_q_size = 0;
@ -3907,13 +3888,11 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
ha->exlogin_size;
}
ql_dbg(ql_dbg_init, vha, 0x00c5,
"%s dump_size %d fw_dump_len %d fw_dump_alloc_len %d\n",
__func__, dump_size, ha->fw_dump_len, ha->fw_dump_alloc_len);
if (!ha->fw_dump_len || dump_size > ha->fw_dump_alloc_len) {
ql_dbg(ql_dbg_init, vha, 0x00c5,
"%s dump_size %d fw_dump_len %d fw_dump_alloc_len %d\n",
__func__, dump_size, ha->fw_dump_len,
ha->fw_dump_alloc_len);
fw_dump = vmalloc(dump_size);
if (!fw_dump) {
ql_log(ql_log_warn, vha, 0x00c4,
@ -3921,9 +3900,26 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
dump_size / 1024);
} else {
mutex_lock(&ha->optrom_mutex);
if (ha->fw_dumped) {
memcpy(fw_dump, ha->fw_dump, ha->fw_dump_len);
vfree(ha->fw_dump);
if (ha->fw_dumped || ha->mpi_fw_dumped) {
prev_fw_dump = ha->fw_dump;
if (ha->fw_dumped)
memcpy(fw_dump, prev_fw_dump,
ha->fw_dump_len);
if (IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
IS_QLA29XX(ha)) {
prev_mpi_fw_dump = ha->mpi_fw_dump;
ha->mpi_fw_dump = (char *)fw_dump +
ha->fwdt[0].dump_size;
if (ha->mpi_fw_dumped)
memcpy(ha->mpi_fw_dump,
prev_mpi_fw_dump,
ha->mpi_fw_dump_len);
}
vfree(prev_fw_dump);
ha->fw_dump = fw_dump;
ha->fw_dump_alloc_len = dump_size;
ql_dbg(ql_dbg_init, vha, 0x00c5,
@ -3942,7 +3938,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
if (IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
IS_QLA29XX(ha)) {
ha->mpi_fw_dump = (char *)fw_dump +
ha->fwdt[1].dump_size;
ha->fwdt[0].dump_size;
mutex_unlock(&ha->optrom_mutex);
return;
}
@ -4339,6 +4335,16 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
rval = qla2x00_verify_checksum(vha, srisc_address);
if (rval == QLA_SUCCESS) {
/*
* Alloc a guestimate dump buffer to capture any failure
* during early phase of driver load.
*/
if (ql2xallocfwdump &&
(IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
IS_QLA29XX(ha)) &&
!vha->flags.init_done)
qla2x00_alloc_fw_dump(vha);
/* Start firmware execution. */
ql_dbg(ql_dbg_init, vha, 0x00ca,
"Starting firmware.\n");
@ -4935,6 +4941,8 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
ql_dbg(ql_dbg_init, vha, 0x00d3,
"Init Firmware -- success.\n");
vha->u_ql2xexchoffld = vha->u_ql2xiniexchg = 0;
vha->hw->flags.t262_fail = 0;
vha->hw->flags.t272_fail = 0;
}
return (rval);

View File

@ -3721,6 +3721,14 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (test_bit(UNLOADING, &base_vha->dpc_flags))
return -ENODEV;
/*
* FW dump can happens before sysfs nodes are created. If sysfs nodes
* are unavailable then udev script will not be able to read the fw dump.
* Notify udev to read again, now that sysfs nodes are available.
*/
if (ha->fw_dumped || ha->mpi_fw_dumped)
qla2x00_post_uevent_work(base_vha, QLA_UEVENT_CODE_FW_DUMP);
return 0;
probe_failed:

View File

@ -306,6 +306,12 @@ qla27xx_fwdt_entry_t262(struct scsi_qla_host *vha,
goto done;
}
if (vha->hw->flags.t262_fail) {
ql_dbg(ql_dbg_misc, vha, 0xd045,
"%s: failed previously\n", __func__);
qla27xx_skip_entry(ent, buf);
goto done;
}
dwords = end - start + 1;
if (buf) {
buf += *len;
@ -314,7 +320,12 @@ qla27xx_fwdt_entry_t262(struct scsi_qla_host *vha,
ql_dbg(ql_dbg_async, vha, 0xffff,
"%s: dump ram MB failed. Area %xh start %lxh end %lxh\n",
__func__, area, start, end);
return INVALID_ENTRY;
if (rc == QLA_FUNCTION_TIMEOUT)
vha->hw->flags.t262_fail = 1;
qla27xx_skip_entry(ent, buf);
goto done;
}
}
*len += dwords * sizeof(uint32_t);
@ -536,13 +547,12 @@ qla27xx_fwdt_entry_t269(struct scsi_qla_host *vha,
{
ql_dbg(ql_dbg_misc, vha, 0xd20d,
"%s: scratch [%lx]\n", __func__, *len);
qla27xx_insert32(0xaaaaaaaa, buf, len);
qla27xx_insert32(0xbbbbbbbb, buf, len);
qla27xx_insert32(0xcccccccc, buf, len);
qla27xx_insert32(0xdddddddd, buf, len);
qla27xx_insert32(*len + sizeof(uint32_t), buf, len);
/* The data format is based on entry type t260. */
qla27xx_insert32(offsetof(struct device_reg_24xx, mailbox0), buf, len);
qla27xx_insertbuf(vha->hw->mbregs, sizeof(vha->hw->mbregs), buf, len);
if (buf)
ent->t269.scratch_size = 5 * sizeof(uint32_t);
ent->t269.scratch_size = sizeof(uint32_t) + sizeof(vha->hw->mbregs);
return qla27xx_next_entry(ent);
}
@ -589,17 +599,37 @@ qla27xx_fwdt_entry_t272(struct scsi_qla_host *vha,
{
ulong dwords = le32_to_cpu(ent->t272.count);
ulong start = le32_to_cpu(ent->t272.addr);
int rc;
ql_dbg(ql_dbg_misc, vha, 0xd210,
"%s: rdremram [%lx]\n", __func__, *len);
if (vha->hw->flags.t272_fail) {
ql_dbg(ql_dbg_misc, vha, 0xd04f,
"%s: failed previously\n", __func__);
qla27xx_skip_entry(ent, buf);
goto done;
}
if (buf) {
ql_dbg(ql_dbg_misc, vha, 0xd02c,
"%s: @%lx -> (%lx dwords)\n", __func__, start, dwords);
buf += *len;
qla27xx_dump_mpi_ram(vha->hw, start, buf, dwords, &buf);
rc = qla27xx_dump_mpi_ram(vha->hw, start, buf, dwords, &buf);
if (rc != QLA_SUCCESS) {
ql_log(ql_log_warn, vha, 0xd01b,
"%s: dump mpi MB failed. Start %lxh dwords %lxh\n",
__func__, start, dwords);
if (rc == QLA_FUNCTION_TIMEOUT)
vha->hw->flags.t272_fail = 1;
qla27xx_skip_entry(ent, buf);
goto done;
}
}
*len += dwords * sizeof(uint32_t);
done:
return qla27xx_next_entry(ent);
}