Merge patch series "scsi: qla2xxx: Bug fixes and hardening"

Nilesh Javali <njavali@marvell.com> says:

This series collects bug fixes, hardening, and small cleanups for the
qla2xxx driver that are independent of the QLA29xx adapter enablement.
Most were uncovered by static analysis and fuzzing of the driver's
interrupt, mailbox, NVMe, and BSG paths; 30 of the 33 patches carry a
Fixes: tag and are marked for stable.

The series is organised as follows:

Queue pairs, MSI-X, and interrupt setup/teardown
  Clamp MSI-X derived queue counts to avoid truncation, fix a
  use-after-free of qpair work on queue teardown, and quiesce the
  response IRQ before freeing the request queue.

Firmware dump, FCE trace, and flash/version paths
  Improve firmware dump data capture, serialize the flash version read in
  the reset handler, clarify the MPI optrom address/length units, fix FCE
  trace enable parsing in debugfs, and fix a use-after-free of the FCE
  trace during a firmware dump.

Probe and mailbox paths
  Fix the cs84xx use-after-free on host teardown, don't query firmware
  state while the chip is down, zero the mailbox struct in
  qla2x00_get_firmware_state(), and null out freed pointers in the
  qla2x00_mem_alloc() error path.

Response/status IOCB path
  Use memset_io() to clear the QLAFX00 request ring slot, fix response
  queue over-consumption in __qla_consume_iocb(), fix a soft lockup in
  the polling continuation IOCB signature, bound rsp_info_len to avoid an
  out-of-bounds sense-data read, avoid a req_q_map double-read in
  qla2x00_error_entry(), and reject non-SCSI SRBs on the status IOCB fast
  path.

NPIV and report-ID acquisition
  Clamp max_npiv_vports to the VP_CTRL bitmap capacity, avoid a double
  completion on async IOCB timeout, and correct vport
  handling in report ID acquisition (skip a vport under deletion, drop
  the vport reference under lock, and hold vport_slock for the host map
  update).

NVMe LS and abort handling
  Fix an abort reference leak on repeated abort, skip the NVMe LS reject
  IOCB when firmware is not started, unlink the unsolicited context
  before freeing on the LS reject error path, and serialize the
  unsolicited context list with a per-fcport lock.

BSG passthrough hardening
  Use a coherent DMA buffer for D_Port diagnostics, zero-init bsg stack
  buffers and the SFP DMA buffer to avoid information leaks, validate the
  BSG request_len before reading vendor_cmd[], and bound i2c->length in
  the I2C bsg handlers.

The final patch bumps the driver version to 12.00.00.2607b2.

The series applies on top of the qla2xxx QLA29xx series (v6, 56 patches)
on Linux 7.2-rc1.

Link: https://patch.msgid.link/20260730155838.2119230-1-njavali@marvell.com
Signed-off-by: Martin K. Petersen (Oracle) <mkp@kernel.org>
This commit is contained in:
Martin K. Petersen (Oracle)
2026-08-07 10:33:34 -04:00
17 changed files with 415 additions and 139 deletions

View File

@@ -815,7 +815,9 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj,
"Unable to allocate memory for VPD information update.\n");
return -ENOMEM;
}
mutex_lock(&ha->optrom_mutex);
ha->isp_ops->get_flash_version(vha, tmp_data);
mutex_unlock(&ha->optrom_mutex);
vfree(tmp_data);
break;
}
@@ -1676,10 +1678,8 @@ qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr,
rval = qla2x00_get_firmware_state(vha, state);
mutex_unlock(&vha->hw->optrom_mutex);
out:
if (rval != QLA_SUCCESS) {
if (rval != QLA_SUCCESS)
memset(state, -1, sizeof(state));
rval = qla2x00_get_firmware_state(vha, state);
}
return scnprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
state[0], state[1], state[2], state[3], state[4], state[5]);

View File

@@ -160,6 +160,12 @@ qla24xx_proc_fcp_prio_cfg_cmd(struct bsg_job *bsg_job)
goto exit_fcp_prio_cfg;
}
if (bsg_job->request_len <
sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t)) {
ret = -EINVAL;
goto exit_fcp_prio_cfg;
}
/* Get the sub command */
oper = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
@@ -758,6 +764,10 @@ qla2x00_process_loopback(struct bsg_job *bsg_job)
return -EIO;
}
if (bsg_job->request_len <
sizeof(struct fc_bsg_request) + 3 * sizeof(uint32_t))
return -EINVAL;
memset(&elreq, 0, sizeof(elreq));
elreq.req_sg_cnt = dma_map_sg(&ha->pdev->dev,
@@ -990,6 +1000,10 @@ qla84xx_reset(struct bsg_job *bsg_job)
return -EINVAL;
}
if (bsg_job->request_len <
sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
return -EINVAL;
flag = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
rval = qla84xx_reset_chip(vha, flag == A84_ISSUE_RESET_DIAG_FW);
@@ -1034,6 +1048,10 @@ qla84xx_updatefw(struct bsg_job *bsg_job)
return -EINVAL;
}
if (bsg_job->request_len <
sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
return -EINVAL;
sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
if (!sg_cnt) {
@@ -1511,9 +1529,15 @@ qla2x00_read_optrom(struct bsg_job *bsg_job)
struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
scsi_qla_host_t *vha = shost_priv(host);
struct qla_hw_data *ha = vha->hw;
uint32_t start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
uint32_t start;
int rval = 0;
if (bsg_job->request_len <
sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
return -EINVAL;
start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
if (ha->flags.nic_core_reset_hdlr_active)
return -EBUSY;
@@ -1556,9 +1580,15 @@ qla2x00_update_optrom(struct bsg_job *bsg_job)
struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
scsi_qla_host_t *vha = shost_priv(host);
struct qla_hw_data *ha = vha->hw;
uint32_t start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
uint32_t start;
int rval = 0;
if (bsg_job->request_len <
sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
return -EINVAL;
start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
mutex_lock(&ha->optrom_mutex);
rval = qla2x00_optrom_setup(bsg_job, vha, start, 1);
if (rval) {
@@ -1973,12 +2003,12 @@ qla2x00_update_fru_versions(struct bsg_job *bsg_job)
scsi_qla_host_t *vha = shost_priv(host);
struct qla_hw_data *ha = vha->hw;
int rval = 0;
uint8_t bsg[DMA_POOL_SIZE];
uint8_t bsg[DMA_POOL_SIZE] = {};
struct qla_image_version_list *list = (void *)bsg;
struct qla_image_version *image;
uint32_t count;
dma_addr_t sfp_dma;
void *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
void *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
if (!sfp) {
bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -2033,10 +2063,10 @@ qla2x00_read_fru_status(struct bsg_job *bsg_job)
scsi_qla_host_t *vha = shost_priv(host);
struct qla_hw_data *ha = vha->hw;
int rval = 0;
uint8_t bsg[DMA_POOL_SIZE];
uint8_t bsg[DMA_POOL_SIZE] = {};
struct qla_status_reg *sr = (void *)bsg;
dma_addr_t sfp_dma;
uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
if (!sfp) {
bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -2084,10 +2114,10 @@ qla2x00_write_fru_status(struct bsg_job *bsg_job)
scsi_qla_host_t *vha = shost_priv(host);
struct qla_hw_data *ha = vha->hw;
int rval = 0;
uint8_t bsg[DMA_POOL_SIZE];
uint8_t bsg[DMA_POOL_SIZE] = {};
struct qla_status_reg *sr = (void *)bsg;
dma_addr_t sfp_dma;
uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
if (!sfp) {
bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -2131,10 +2161,10 @@ qla2x00_write_i2c(struct bsg_job *bsg_job)
scsi_qla_host_t *vha = shost_priv(host);
struct qla_hw_data *ha = vha->hw;
int rval = 0;
uint8_t bsg[DMA_POOL_SIZE];
uint8_t bsg[DMA_POOL_SIZE] = {};
struct qla_i2c_access *i2c = (void *)bsg;
dma_addr_t sfp_dma;
uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
if (!sfp) {
bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -2145,6 +2175,12 @@ qla2x00_write_i2c(struct bsg_job *bsg_job)
sg_copy_to_buffer(bsg_job->request_payload.sg_list,
bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c));
if (i2c->length > sizeof(i2c->buffer)) {
bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
EXT_STATUS_INVALID_PARAM;
goto dealloc;
}
memcpy(sfp, i2c->buffer, i2c->length);
rval = qla2x00_write_sfp(vha, sfp_dma, sfp,
i2c->device, i2c->offset, i2c->length, i2c->option);
@@ -2177,10 +2213,10 @@ qla2x00_read_i2c(struct bsg_job *bsg_job)
scsi_qla_host_t *vha = shost_priv(host);
struct qla_hw_data *ha = vha->hw;
int rval = 0;
uint8_t bsg[DMA_POOL_SIZE];
uint8_t bsg[DMA_POOL_SIZE] = {};
struct qla_i2c_access *i2c = (void *)bsg;
dma_addr_t sfp_dma;
uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
if (!sfp) {
bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -2191,6 +2227,12 @@ qla2x00_read_i2c(struct bsg_job *bsg_job)
sg_copy_to_buffer(bsg_job->request_payload.sg_list,
bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c));
if (i2c->length > sizeof(i2c->buffer)) {
bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
EXT_STATUS_INVALID_PARAM;
goto dealloc;
}
rval = qla2x00_read_sfp(vha, sfp_dma, sfp,
i2c->device, i2c->offset, i2c->length, i2c->option);
@@ -2411,6 +2453,11 @@ qlafx00_mgmt_cmd(struct bsg_job *bsg_job)
struct fc_port *fcport;
char *type = "FC_BSG_HST_FX_MGMT";
if (bsg_job->request_len <
sizeof(struct fc_bsg_request) + sizeof(uint32_t) +
sizeof(struct qla_mt_iocb_rqst_fx00))
return -EINVAL;
/* Copy the IOCB specific information */
piocb_rqst = (struct qla_mt_iocb_rqst_fx00 *)
&bsg_request->rqst_data.h_vendor.vendor_cmd[1];
@@ -3332,6 +3379,13 @@ qla2x00_process_vendor_specific(struct scsi_qla_host *vha, struct bsg_job *bsg_j
{
struct fc_bsg_request *bsg_request = bsg_job->request;
if (bsg_job->request_len <
sizeof(struct fc_bsg_request) + sizeof(uint32_t)) {
ql_log(ql_log_warn, vha, 0x7000,
"BSG request too small for vendor cmd.\n");
return -EINVAL;
}
ql_dbg(ql_dbg_edif, vha, 0x911b, "%s FC_BSG_HST_VENDOR cmd[0]=0x%x\n",
__func__, bsg_request->rqst_data.h_vendor.vendor_cmd[0]);
@@ -3475,8 +3529,11 @@ qla24xx_bsg_request(struct bsg_job *bsg_job)
}
/* Disable port will bring down the chip, allow enable command */
if (bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_MANAGE_HOST_PORT ||
bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS)
if (bsg_request->msgcode == FC_BSG_HST_VENDOR &&
bsg_job->request_len >=
sizeof(struct fc_bsg_request) + sizeof(uint32_t) &&
(bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_MANAGE_HOST_PORT ||
bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS))
goto skip_chip_chk;
if (vha->hw->flags.port_isolated) {
@@ -3785,6 +3842,10 @@ static int qla28xx_validate_flash_image(struct bsg_job *bsg_job)
if (!IS_QLA28XX(ha) || vha->vp_idx != 0)
return -EPERM;
if (bsg_job->request_len <
sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
return -EINVAL;
mutex_lock(&ha->optrom_mutex);
rval = qla28xx_do_validate_flash_image(bsg_job, &state);
if (rval)

View File

@@ -16,7 +16,7 @@
* | | | 0x2127-0x2128 |
* | Queue Command and IO tracing | 0x3074 | 0x300b |
* | | | 0x3027-0x3028 |
* | | | 0x303d-0x3041 |
* | | | 0x303e-0x3041 |
* | | | 0x302e,0x3033 |
* | | | 0x3036,0x3038 |
* | | | 0x303a |
@@ -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

@@ -2645,6 +2645,8 @@ typedef struct fc_port {
struct list_head list;
struct scsi_qla_host *vha;
struct list_head unsol_ctx_head;
/* Serializes unsol_ctx_head against ISR, DPC and NVMe transport. */
spinlock_t unsol_ctx_lock;
unsigned int conf_compl_supported:1;
unsigned int deleted:2;
@@ -4176,6 +4178,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 +4249,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

@@ -510,7 +510,9 @@ qla2x00_dfs_fce_write(struct file *file, const char __user *buffer,
return PTR_ERR(buf);
}
enable = kstrtoul(buf, 0, 0);
rc = kstrtoul(buf, 0, &enable);
if (rc)
goto out_free;
rc = count;
mutex_lock(&ha->fce_mutex);

View File

@@ -1442,6 +1442,10 @@ struct vp_ctrl_entry_24xx {
uint8_t reserved_5[24];
};
/* vp_idx_map is a 128-bit (16-byte) bitmap selecting target VPs. */
#define VP_CTRL_IDX_MAP_BITS \
(sizeof_field(struct vp_ctrl_entry_24xx, vp_idx_map) * 8)
/*
* Modify Virtual Port Configuration IOCB
*/

View File

@@ -228,7 +228,7 @@ qla2x00_async_iocb_timeout(void *data)
srb_t *sp = data;
fc_port_t *fcport = sp->fcport;
struct srb_iocb *lio = &sp->u.iocb_cmd;
int rc, h;
int rc, h, found;
unsigned long flags;
if (fcport) {
@@ -251,6 +251,7 @@ qla2x00_async_iocb_timeout(void *data)
lio->u.logio.data[1] =
lio->u.logio.flags & SRB_LOGIN_RETRIED ?
QLA_LOGIO_LOGIN_RETRIED : 0;
found = 0;
spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags);
for (h = 1; h < sp->qpair->req->num_outstanding_cmds;
h++) {
@@ -258,11 +259,19 @@ qla2x00_async_iocb_timeout(void *data)
sp) {
sp->qpair->req->outstanding_cmds[h] =
NULL;
found = 1;
break;
}
}
spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags);
sp->done(sp, QLA_FUNCTION_TIMEOUT);
/*
* Only complete the command if this path removed it
* from outstanding_cmds. Otherwise the ISR already
* completed it and a second sp->done() would race the
* submitter's freeing of the on-stack completion.
*/
if (found)
sp->done(sp, QLA_FUNCTION_TIMEOUT);
}
break;
case SRB_LOGOUT_CMD:
@@ -275,6 +284,7 @@ qla2x00_async_iocb_timeout(void *data)
default:
rc = qla24xx_async_abort_cmd(sp, false);
if (rc) {
found = 0;
spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags);
for (h = 1; h < sp->qpair->req->num_outstanding_cmds;
h++) {
@@ -282,11 +292,19 @@ qla2x00_async_iocb_timeout(void *data)
sp) {
sp->qpair->req->outstanding_cmds[h] =
NULL;
found = 1;
break;
}
}
spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags);
sp->done(sp, QLA_FUNCTION_TIMEOUT);
/*
* Only complete the command if this path removed it
* from outstanding_cmds. Otherwise the ISR already
* completed it and a second sp->done() would race the
* submitter's freeing of the on-stack completion.
*/
if (found)
sp->done(sp, QLA_FUNCTION_TIMEOUT);
}
break;
}
@@ -3275,47 +3293,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 +3342,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 +3438,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 +3457,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");
@@ -3764,11 +3770,27 @@ int qla2x00_alloc_fce_trace(scsi_qla_host_t *vha)
void qla2x00_free_fce_trace(struct qla_hw_data *ha)
{
if (!ha->fce)
void *fce;
dma_addr_t fce_dma;
unsigned long flags;
/*
* Unpublish ha->fce under hardware_lock so a firmware dump in
* progress (which reads ha->fce under the same lock) cannot race
* with the buffer being freed.
*/
spin_lock_irqsave(&ha->hardware_lock, flags);
if (!ha->fce) {
spin_unlock_irqrestore(&ha->hardware_lock, flags);
return;
dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce, ha->fce_dma);
}
fce = ha->fce;
fce_dma = ha->fce_dma;
ha->fce = NULL;
ha->fce_dma = 0;
spin_unlock_irqrestore(&ha->hardware_lock, flags);
dma_free_coherent(&ha->pdev->dev, FCE_SIZE, fce, fce_dma);
}
static void
@@ -3813,18 +3835,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 +3922,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 +3934,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 +3972,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 +4369,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");
@@ -4390,6 +4430,19 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
MIN_MULTI_ID_FABRIC))
ha->max_npiv_vports =
MIN_MULTI_ID_FABRIC - 1;
/*
* The VP_CTRL IOCB selects target VPs
* through the fixed vp_idx_map bitmap,
* so a vp_index beyond it can be enabled
* via VP_CONFIG but never disabled via
* VP_CTRL, leaking the VP. Cap the count
* to the bitmap capacity.
*/
if (ha->max_npiv_vports >=
VP_CTRL_IDX_MAP_BITS)
ha->max_npiv_vports =
VP_CTRL_IDX_MAP_BITS - 1;
}
qlt_config_nvram_with_fw_version(vha);
qla2x00_get_resource_cnts(vha);
@@ -4935,6 +4988,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);
@@ -5655,6 +5710,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
INIT_LIST_HEAD(&fcport->gnl_entry);
INIT_LIST_HEAD(&fcport->list);
INIT_LIST_HEAD(&fcport->unsol_ctx_head);
spin_lock_init(&fcport->unsol_ctx_lock);
INIT_LIST_HEAD(&fcport->sess_cmd_list);
spin_lock_init(&fcport->sess_cmd_lock);
@@ -10671,11 +10727,28 @@ int qla2xxx_delete_qpair(struct scsi_qla_host *vha, struct qla_qpair *qpair)
{
int ret = QLA_FUNCTION_FAILED;
struct qla_hw_data *ha = qpair->hw;
struct rsp_que *rsp = qpair->rsp;
qpair->delete_in_progress = 1;
qla_free_buf_pool(qpair);
/*
* The response-queue interrupt schedules qla_do_work(), which
* dereferences qpair->rsp->req. Release the interrupt and flush
* any pending work before the request queue is freed below so a
* late completion cannot touch the freed request queue. The
* firmware queue-delete order (request then response) is kept.
*/
if (rsp && rsp->msix && rsp->msix->have_irq) {
free_irq(rsp->msix->vector, rsp->msix->handle);
rsp->msix->have_irq = 0;
rsp->msix->in_use = 0;
rsp->msix->handle = NULL;
}
if (rsp && ha->wq)
cancel_work_sync(&qpair->q_work);
ret = qla25xx_delete_req_que(vha, qpair->req);
if (ret != QLA_SUCCESS)
goto fail;

View File

@@ -138,6 +138,19 @@ qla_logio_set_vp_index(struct qla_hw_data *ha, void *pkt, u16 vp_idx)
((struct logio_entry_24xx *)pkt)->vp_index = vp_idx;
}
static inline u8
qla_calc_queue_count(u16 msix_count)
{
/*
* Request/response queues are bounded by the MSI-X vector count less
* the mailbox vector. These counters are u8, so a board advertising
* e.g. 257 vectors would truncate msix_count - 1 (256) to 0 and hand
* kzalloc_objs() a zero count (ZERO_SIZE_PTR), faulting on the first
* ha->req_q_map[0] store. Clamp into [1, QLA_MAX_QUEUES - 1].
*/
return clamp_t(u16, msix_count - 1, 1, QLA_MAX_QUEUES - 1);
}
static inline void
qla2x00_poll(struct rsp_que *rsp)
{

View File

@@ -2530,11 +2530,13 @@ __qla2x00_alloc_iocbs(struct qla_qpair *qpair, srb_t *sp)
*/
req->cnt -= req_cnt;
pkt = qla_req_ring_slot(ha, req);
memset(pkt, 0, qla_req_entry_size(ha));
if (IS_QLAFX00(ha)) {
memset_io((void __iomem __force *)pkt, 0,
qla_req_entry_size(ha));
wrt_reg_byte((u8 __force __iomem *)&pkt->entry_count, req_cnt);
wrt_reg_dword((__le32 __force __iomem *)&pkt->handle, handle);
} else {
memset(pkt, 0, qla_req_entry_size(ha));
pkt->entry_count = req_cnt;
pkt->handle = handle;
}

View File

@@ -259,6 +259,17 @@ void __qla_consume_iocb(struct scsi_qla_host *vha,
struct purex_entry_24xx *purex = *pkt;
entry_count_remaining = purex->entry_count;
/*
* The caller already advanced ring_ptr past the head IOCB, so mark
* the head processed and account for it here, then consume only the
* continuation IOCBs that follow.
*/
((response_t *)purex)->signature = RESPONSE_PROCESSED;
/* flush signature */
wmb();
--entry_count_remaining;
while (entry_count_remaining > 0) {
new_pkt = rsp_q->ring_ptr;
*pkt = new_pkt;
@@ -991,14 +1002,6 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
do {
while ((total_bytes > 0) && (entry_count_remaining > 0)) {
if (rsp_q->ring_ptr->signature == RESPONSE_PROCESSED) {
ql_dbg(ql_dbg_async, vha, 0x5084,
"Ran out of IOCBs, partial data 0x%x\n",
buffer_copy_offset);
cpu_relax();
continue;
}
*pkt = rsp_q->ring_ptr;
data = ((sts_cont_entry_t *)*pkt)->data;
data_sz = qla_sts_cont_data_size(ha);
@@ -1288,14 +1291,6 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
do {
while ((total_bytes > 0) && (entry_count_remaining > 0)) {
if (rsp_q->ring_ptr->signature == RESPONSE_PROCESSED) {
ql_dbg(ql_dbg_async, vha, 0x5084,
"Ran out of IOCBs, partial data 0x%x\n",
buffer_copy_offset);
cpu_relax();
continue;
}
*pkt = rsp_q->ring_ptr;
data = ((sts_cont_entry_t *)*pkt)->data;
data_sz = qla_sts_cont_data_size(ha);
@@ -3546,6 +3541,14 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
return;
}
/* Everything below is the SCSI fast path; reject other SRB types. */
if (sp->type != SRB_SCSI_CMD) {
ql_dbg(ql_dbg_io, vha, 0x303d,
"Unexpected SRB type %x for status IOCB, sp %p.\n",
sp->type, sp);
return;
}
/* Fast path completion. */
qla_chk_edif_rx_sa_delete_pending(vha, sp, pkt);
sp->qpair->cmd_completion_cnt++;
@@ -3603,6 +3606,18 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) {
/* Sense data lies beyond any FCP RESPONSE data. */
if (IS_FWI2_CAPABLE(ha)) {
/*
* A hostile or buggy target may report an
* rsp_info_len larger than the IOCB data area.
* Clamp it so the par_sense_len subtraction cannot
* underflow and walk sense_data out of bounds.
*/
if (rsp_info_len > par_sense_len) {
ql_log(ql_log_warn, fcport->vha, 0x3107,
"Truncating bogus rsp_info_len 0x%x to 0x%x.\n",
rsp_info_len, par_sense_len);
rsp_info_len = par_sense_len;
}
sense_data += rsp_info_len;
par_sense_len -= rsp_info_len;
}
@@ -3921,10 +3936,12 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
"iocb type %xh with error status %xh, handle %xh, rspq id %d\n",
pkt->entry_type, pkt->entry_status, pkt->handle, rsp->id);
if (que >= ha->max_req_queues || !ha->req_q_map[que])
if (que >= ha->max_req_queues)
goto fatal;
req = ha->req_q_map[que];
if (!req)
goto fatal;
if (pkt->entry_status & RF_BUSY)
res = DID_BUS_BUSY << 16;
@@ -4261,9 +4278,24 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
"SCM not active for this port\n");
break;
}
if (qla_chk_cont_iocb_avail(vha, rsp,
(response_t *)pkt, rsp_in)) {
/*
* ring_ptr and ring_index were
* pre-incremented above. Reset them
* back to current. Wait for next
* interrupt with all IOCBs to arrive
* and re-process.
*/
qla_rsp_ring_rewind_to(rsp,
(response_t *)pkt, cur_ring_index);
ql_dbg(ql_dbg_init, vha, 0x5095,
"Defer processing FPIN...\n");
return;
}
pure_item = qla27xx_copy_fpin_pkt(vha,
(void **)&pkt, &rsp);
__update_rsp_in(is_shadow_hba, rsp, rsp_in);
if (!pure_item)
break;
qla24xx_queue_purex_item(vha, pure_item,
@@ -4705,10 +4737,10 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp)
ha->msix_count = ret;
/* Recalculate queue values */
if (ha->mqiobase && (ql2xmqsupport || ql2xnvmeenable)) {
ha->max_req_queues = ha->msix_count - 1;
ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
/* ATIOQ needs 1 vector. That's 1 less QPair */
if (QLA_TGT_MODE_ENABLED())
if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1)
ha->max_req_queues--;
ha->max_rsp_queues = ha->max_req_queues;

View File

@@ -2276,6 +2276,8 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
if (!ha->flags.fw_started)
return QLA_FUNCTION_FAILED;
memset(&mc, 0, sizeof(mc));
mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
mcp->out_mb = MBX_0;
if (IS_FWI2_CAPABLE(vha->hw))
@@ -4266,6 +4268,8 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
spin_lock_irqsave(&ha->vport_slock, flags);
list_for_each_entry(vp, &ha->vp_list, list) {
if (vp_idx == vp->vp_idx) {
if (test_bit(VPORT_DELETE, &vp->dpc_flags))
break;
found = 1;
atomic_inc(&vp->vref_count);
break;
@@ -4276,7 +4280,9 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
if (!found)
return;
spin_lock_irqsave(&ha->vport_slock, flags);
qla_update_host_map(vp, id);
spin_unlock_irqrestore(&ha->vport_slock, flags);
/*
* Cannot configure here as we are still sitting on the
@@ -4286,7 +4292,9 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
spin_lock_irqsave(&ha->vport_slock, flags);
atomic_dec(&vp->vref_count);
spin_unlock_irqrestore(&ha->vport_slock, flags);
}
set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
qla2xxx_wake_dpc(vha);
@@ -6571,6 +6579,7 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha,
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
dma_addr_t dd_dma;
void *dd;
if (!IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw) &&
!IS_QLA28XX(vha->hw) && !IS_QLA29XX(vha->hw))
@@ -6579,15 +6588,12 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha,
ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x119f,
"Entered %s.\n", __func__);
dd_dma = dma_map_single(&vha->hw->pdev->dev,
dd_buf, size, DMA_FROM_DEVICE);
if (dma_mapping_error(&vha->hw->pdev->dev, dd_dma)) {
ql_log(ql_log_warn, vha, 0x1194, "Failed to map dma buffer.\n");
dd = dma_alloc_coherent(&vha->hw->pdev->dev, size, &dd_dma, GFP_KERNEL);
if (!dd) {
ql_log(ql_log_warn, vha, 0x1194, "Failed to allocate dma buffer.\n");
return QLA_MEMORY_ALLOC_FAILED;
}
memset(dd_buf, 0, size);
mcp->mb[0] = MBC_DPORT_DIAGNOSTICS;
mcp->mb[1] = options;
mcp->mb[2] = MSW(LSD(dd_dma));
@@ -6609,8 +6615,9 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha,
"Done %s.\n", __func__);
}
dma_unmap_single(&vha->hw->pdev->dev, dd_dma,
size, DMA_FROM_DEVICE);
memcpy(dd_buf, dd, size);
dma_free_coherent(&vha->hw->pdev->dev, size, dd, dd_dma);
return rval;
}

View File

@@ -606,6 +606,10 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
rsp->msix->handle = NULL;
}
/* Flush any queued response work before freeing the queue/qpair. */
if (rsp->qpair && ha->wq)
cancel_work_sync(&rsp->qpair->q_work);
if (rsp->ring)
dma_free_coherent(&ha->pdev->dev,
(rsp->length + 1) * rsp_entry_size,
@@ -992,7 +996,7 @@ int qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
* (16-byte) vp_idx_map bitmap, so vp_index must fit within it even
* if firmware advertises more NPIV vports.
*/
if (vp_index > sizeof_field(struct vp_ctrl_entry_24xx, vp_idx_map) * 8)
if (vp_index > VP_CTRL_IDX_MAP_BITS)
return QLA_PARAMETER_ERROR;
/* ref: INIT */

View File

@@ -257,7 +257,9 @@ static void qla_nvme_release_lsrsp_cmd_kref(struct kref *kref)
fd_rsp = uctx->fd_rsp;
spin_lock_irqsave(&uctx->fcport->unsol_ctx_lock, flags);
list_del(&uctx->elem);
spin_unlock_irqrestore(&uctx->fcport->unsol_ctx_lock, flags);
fd_rsp->done(fd_rsp);
kfree(uctx);
@@ -441,9 +443,14 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport,
a.vp_idx = vha->vp_idx;
a.nport_handle = uctx->nport_handle;
a.xchg_address = uctx->exchange_address;
spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
if (ha->flags.fw_started) {
spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
}
spin_lock_irqsave(&uctx->fcport->unsol_ctx_lock, flags);
list_del(&uctx->elem);
spin_unlock_irqrestore(&uctx->fcport->unsol_ctx_lock, flags);
kfree(uctx);
return rval;
}
@@ -466,7 +473,8 @@ static void qla_nvme_ls_abort(struct nvme_fc_local_port *lport,
}
spin_unlock_irqrestore(&priv->cmd_lock, flags);
schedule_work(&priv->abort_work);
if (!schedule_work(&priv->abort_work))
kref_put(&priv->sp->cmd_kref, priv->sp->put_fn);
}
static int qla_nvme_ls_req(struct nvme_fc_local_port *lport,
@@ -548,7 +556,8 @@ static void qla_nvme_fcp_abort(struct nvme_fc_local_port *lport,
}
spin_unlock_irqrestore(&priv->cmd_lock, flags);
schedule_work(&priv->abort_work);
if (!schedule_work(&priv->abort_work))
kref_put(&priv->sp->cmd_kref, priv->sp->put_fn);
}
static inline int qla2x00_start_nvme_mq(srb_t *sp)
@@ -1319,10 +1328,17 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item)
a.vp_idx = vha->vp_idx;
a.nport_handle = uctx->nport_handle;
a.xchg_address = uctx->exchange_address;
spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr, flags);
qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true);
spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr, flags);
if (vha->hw->flags.fw_started) {
spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr,
flags);
qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a,
true);
spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr,
flags);
}
spin_lock_irqsave(&uctx->fcport->unsol_ctx_lock, flags);
list_del(&uctx->elem);
spin_unlock_irqrestore(&uctx->fcport->unsol_ctx_lock, flags);
kfree(uctx);
}
}
@@ -1364,6 +1380,7 @@ void qla2xxx_process_purls_iocb(void **pkt, struct rsp_que **rsp)
struct purex_item *item;
port_id_t d_id = {0};
port_id_t id = {0};
unsigned long flags;
u8 *opcode;
bool xmt_reject = false;
@@ -1429,7 +1446,9 @@ void qla2xxx_process_purls_iocb(void **pkt, struct rsp_que **rsp)
uctx->ox_id = p->ox_id;
qla_rport->uctx = uctx;
INIT_LIST_HEAD(&uctx->elem);
spin_lock_irqsave(&fcport->unsol_ctx_lock, flags);
list_add_tail(&uctx->elem, &fcport->unsol_ctx_head);
spin_unlock_irqrestore(&fcport->unsol_ctx_lock, flags);
item->purls_context = (void *)uctx;
ql_dbg(ql_dbg_unsol, vha, 0x2121,

View File

@@ -2132,7 +2132,7 @@ qla2x00_iospace_config(struct qla_hw_data *ha)
ha->msix_count = msix + 1;
/* Max queues are bounded by available msix vectors */
/* MB interrupt uses 1 vector */
ha->max_req_queues = ha->msix_count - 1;
ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
ha->max_rsp_queues = ha->max_req_queues;
/* Queue pairs is the max value minus the base queue pair */
ha->max_qpairs = ha->max_rsp_queues - 1;
@@ -2224,10 +2224,10 @@ qla83xx_iospace_config(struct qla_hw_data *ha)
*/
if (ql2xmqsupport || ql2xnvmeenable) {
/* MB interrupt uses 1 vector */
ha->max_req_queues = ha->msix_count - 1;
ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
/* ATIOQ needs 1 vector. That's 1 less QPair */
if (QLA_TGT_MODE_ENABLED())
if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1)
ha->max_req_queues--;
ha->max_rsp_queues = ha->max_req_queues;
@@ -3725,6 +3725,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:
@@ -4044,8 +4052,6 @@ qla2x00_remove_one(struct pci_dev *pdev)
qla2x00_dfs_remove(base_vha);
qla84xx_put_chip(base_vha);
/* Disable timer */
if (base_vha->timer_active)
qla2x00_stop_timer(base_vha);
@@ -4070,6 +4076,8 @@ qla2x00_remove_one(struct pci_dev *pdev)
scsi_remove_host(base_vha->host);
qla84xx_put_chip(base_vha);
qla2x00_free_device(base_vha);
qla2x00_clear_drv_active(ha);
@@ -4615,28 +4623,43 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
fail_lsrjt:
dma_free_coherent(&ha->pdev->dev, ha->elsrej.size,
ha->elsrej.c, ha->elsrej.cdma);
ha->elsrej.c = NULL;
ha->elsrej.cdma = 0;
fail_elsrej:
dma_pool_destroy(ha->purex_dma_pool);
ha->purex_dma_pool = NULL;
fail_flt_data:
vfree(ha->flt_data);
ha->flt_data = NULL;
fail_flt:
dma_free_coherent(&ha->pdev->dev, sizeof(struct qla_flt_header) + FLT_REGIONS_SIZE,
ha->flt, ha->flt_dma);
ha->flt = NULL;
ha->flt_dma = 0;
fail_flt_buffer:
dma_free_coherent(&ha->pdev->dev, SFP_DEV_SIZE,
ha->sfp_data, ha->sfp_data_dma);
ha->sfp_data = NULL;
ha->sfp_data_dma = 0;
fail_sfp_data:
kfree(ha->loop_id_map);
ha->loop_id_map = NULL;
fail_loop_id_map:
dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma);
ha->async_pd = NULL;
ha->async_pd_dma = 0;
fail_async_pd:
dma_pool_free(ha->s_dma_pool, ha->sf_init_cb, ha->sf_init_cb_dma);
ha->sf_init_cb = NULL;
ha->sf_init_cb_dma = 0;
fail_sf_init_cb:
dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma);
ha->ex_init_cb = NULL;
ha->ex_init_cb_dma = 0;
fail_ex_init_cb:
kfree(ha->npiv_info);
ha->npiv_info = NULL;
fail_npiv_info:
dma_free_coherent(&ha->pdev->dev,
((*rsp)->length + 1) * rsp_entry_size,
@@ -6991,8 +7014,6 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work)
qla2x00_dfs_remove(base_vha);
qla84xx_put_chip(base_vha);
if (base_vha->timer_active)
qla2x00_stop_timer(base_vha);
@@ -7010,6 +7031,8 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work)
scsi_remove_host(base_vha->host);
qla84xx_put_chip(base_vha);
base_vha->flags.init_done = 0;
qla25xx_delete_queues(base_vha);
qla2x00_free_fcports(base_vha);

View File

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

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);
}

View File

@@ -6,7 +6,7 @@
/*
* Driver version
*/
#define QLA2XXX_VERSION "12.00.00.2607b1"
#define QLA2XXX_VERSION "12.00.00.2607b2"
#define QLA_DRIVER_MAJOR_VER 12
#define QLA_DRIVER_MINOR_VER 00