From: Rajan Shanmugavelu Date: Mon, 4 Jun 2018 22:22:43 +0000 (-0700) Subject: qla2xxx: Enable buffer boundary check when DIF bundling is on. X-Git-Tag: v4.1.12-124.31.3~728 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=dc8b3c90ef02a29503cfd5a32c42562a3ad23fcb;p=users%2Fjedix%2Flinux-maple.git qla2xxx: Enable buffer boundary check when DIF bundling is on. The Qlogic firmware requires the upper 32-bit of dma buffer address not flipped, this happens when DIF bundling is enabled and the SGE buffer address plus length changes the upper 32-bit address, a local buffer is used for DIF information. Orabug: 28130589 Co-authored-by: Giri Malavali Co-authored-by: Joe Carnuccio Reviewed-by: Giri Malavali Signed-off-by: Rajan Shanmugavelu Reviewed-by: Martin K. Petersen Signed-off-by: Brian Maly --- diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 2381f73cf543e..6a37a8b36ebb5 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -1512,6 +1512,23 @@ qla2x00_pep_version_show(struct device *dev, struct device_attribute *attr, ha->pep_version[0], ha->pep_version[1], ha->pep_version[2]); } +static ssize_t +qla2x00_dif_bundle_statistics_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; + + return scnprintf(buf, PAGE_SIZE, + "cross=%llu read=%llu write=%llu kalloc=%llu dma_alloc=%llu unusable=%u\n", + ha->dif_bundle_crossed_pages, + ha->dif_bundle_reads, + ha->dif_bundle_writes, + ha->dif_bundle_kallocs, + ha->dif_bundle_dma_allocs, + ha->pool.unusable.count); +} + static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL); static DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL); static DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL); @@ -1557,6 +1574,8 @@ static DEVICE_ATTR(allow_cna_fw_dump, S_IRUGO | S_IWUSR, qla2x00_allow_cna_fw_dump_show, qla2x00_allow_cna_fw_dump_store); static DEVICE_ATTR(pep_version, S_IRUGO, qla2x00_pep_version_show, NULL); +static DEVICE_ATTR(dif_bundle_statistics, S_IRUGO, + qla2x00_dif_bundle_statistics_show, NULL); struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_driver_version, @@ -1591,6 +1610,7 @@ struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_fw_dump_size, &dev_attr_allow_cna_fw_dump, &dev_attr_pep_version, + &dev_attr_dif_bundle_statistics, NULL, }; diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 868fa24ce68d8..33d400bdc98f4 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -317,6 +317,7 @@ struct srb_cmd { #define SRB_CRC_CTX_DMA_VALID BIT_2 /* DIF: context DMA valid */ #define SRB_CRC_PROT_DMA_VALID BIT_4 /* DIF: prot DMA valid */ #define SRB_CRC_CTX_DSD_VALID BIT_5 /* DIF: dsd_list valid */ +#define SRB_DIF_BUNDL_DMA_VALID BIT_6 /* DIF Bundling - DMA list valid */ /* To identify if a srb is of T10-CRC type. @sp => srb_t pointer */ #define IS_PROT_IO(sp) (sp->flags & SRB_CRC_CTX_DSD_VALID) @@ -1850,10 +1851,18 @@ struct crc_context { } u; struct fcp_cmnd fcp_cmnd; - dma_addr_t crc_ctx_dma; + dma_addr_t crc_ctx_dma; /* List of DMA context transfers */ struct list_head dsd_list; + /* List of DIF Bundling context DMA address */ + struct list_head ldif_dsd_list; + u8 no_ldif_dsd; + + struct list_head ldif_dma_hndl_list; + u32 dif_bundl_len; + u8 no_dif_bundl; + /* This structure should not exceed 512 bytes */ }; @@ -3938,6 +3947,26 @@ struct qla_hw_data { struct qlt_hw_data tgt; int allow_cna_fw_dump; + + /* DMA pool for the DIF bundling buffers */ + struct dma_pool *dif_bundl_pool; +#define DIF_BUNDLING_DMA_POOL_SIZE 1024 + struct { + struct { + struct list_head head; + uint count; + } good; + struct { + struct list_head head; + uint count; + } unusable; + } pool; + + unsigned long long dif_bundle_crossed_pages; + unsigned long long dif_bundle_reads; + unsigned long long dif_bundle_writes; + unsigned long long dif_bundle_kallocs; + unsigned long long dif_bundle_dma_allocs; }; struct qla_tgt_counters { diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 6b7b1c3441939..ed3baa994c169 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -139,6 +139,7 @@ extern int ql2xmdcapmask; extern int ql2xmdenable; extern int ql2xfwholdabts; extern int ql2xmvasynctoatio; +extern int ql2xdifbundlinginternalbuffers; extern int qla2x00_loop_reset(scsi_qla_host_t *); extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int); diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 7dc213ceee581..e7485f5f92df7 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -1098,88 +1098,252 @@ qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd, int qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp, - uint32_t *dsd, uint16_t tot_dsds, struct qla_tgt_cmd *tc) + uint32_t *cur_dsd, uint16_t tot_dsds, struct qla_tgt_cmd *tc) { - void *next_dsd; - uint8_t avail_dsds = 0; - uint32_t dsd_list_len; - struct dsd_dma *dsd_ptr; + struct dsd_dma *dsd_ptr, *dif_dsd, *nxt_dsd; struct scatterlist *sg, *sgl; - int i; - struct scsi_cmnd *cmd; - uint32_t *cur_dsd = dsd; - uint16_t used_dsds = tot_dsds; + struct crc_context *difctx = NULL; struct scsi_qla_host *vha; + uint dsd_list_len; + uint avail_dsds = 0; + uint used_dsds = tot_dsds; + bool dif_local_dma_alloc = false; + bool direction_to_device = false; + int i; if (sp) { - cmd = GET_CMD_SP(sp); + struct scsi_cmnd *cmd = GET_CMD_SP(sp); sgl = scsi_prot_sglist(cmd); vha = sp->vha; + difctx = sp->u.scmd.ctx; + direction_to_device = cmd->sc_data_direction == DMA_TO_DEVICE; } else if (tc) { vha = tc->vha; sgl = tc->prot_sg; + difctx = tc->ctx; + direction_to_device = tc->dma_data_direction == DMA_TO_DEVICE; } else { BUG(); return 1; } - ql_dbg(ql_dbg_tgt, vha, 0xe021, - "%s: enter\n", __func__); - - for_each_sg(sgl, sg, tot_dsds, i) { - dma_addr_t sle_dma; - - /* Allocate additional continuation packets? */ - if (avail_dsds == 0) { - avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ? - QLA_DSDS_PER_IOCB : used_dsds; - dsd_list_len = (avail_dsds + 1) * 12; - used_dsds -= avail_dsds; - - /* allocate tracking DS */ - dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC); - if (!dsd_ptr) - return 1; - - /* allocate new list */ - dsd_ptr->dsd_addr = next_dsd = - dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC, - &dsd_ptr->dsd_list_dma); - - if (!next_dsd) { - /* - * Need to cleanup only this dsd_ptr, rest - * will be done by sp_free_dma() - */ - kfree(dsd_ptr); - return 1; + ql_dbg(ql_dbg_tgt+ql_dbg_verbose, vha, 0xe021, + "%s: enter (write=%u)\n", __func__, direction_to_device); + + /* if initiator doing write or target doing read */ + if (direction_to_device) { + for_each_sg(sgl, sg, tot_dsds, i) { + dma_addr_t sle_phys = sg_phys(sg); + + /* If SGE address + length flips bits in upper 32-bits */ + if (MSD(sle_phys + sg->length) ^ MSD(sle_phys)) { + ql_dbg(ql_dbg_tgt+ql_dbg_verbose, vha, 0xe022, + "%s: page boundary crossing (phys=%llx len=%x)\n", + __func__, sle_phys, sg->length); + + if (difctx) { + ha->dif_bundle_crossed_pages++; + dif_local_dma_alloc = true; + } else { + ql_dbg(ql_dbg_tgt+ql_dbg_verbose, vha, 0xe022, + "%s: difctx pointer is NULL\n", __func__); + } + break; } + } + ha->dif_bundle_writes++; + } else { + ha->dif_bundle_reads++; + } + + /* if test override is on, then do local dma alloc for writes */ + if (ql2xdifbundlinginternalbuffers) { + dif_local_dma_alloc = direction_to_device; + } + + if (dif_local_dma_alloc) { + u8 track_difbundl_buf = 0; + difctx->no_dif_bundl = 0; + difctx->dif_bundl_len = 0; + + INIT_LIST_HEAD(&difctx->ldif_dsd_list); /* Track DSD buffers */ + INIT_LIST_HEAD(&difctx->ldif_dma_hndl_list); /* Track local DMA buffers */ + + for_each_sg(sgl, sg, tot_dsds, i) { + u32 sglen = sg_dma_len(sg); + + ql_dbg(ql_dbg_tgt+ql_dbg_verbose, vha, 0xe023, + "%s: sg[%x] (phys=%llx sglen=%x)\n", + __func__, i, sg_phys(sg), sglen); + + while (sglen) { + u32 xfrlen = 0; + + /* allocate list item to store the DMA buffers */ + dsd_ptr = kzalloc(sizeof(*dsd_ptr), GFP_ATOMIC); + if (!dsd_ptr) { + ql_dbg(ql_dbg_tgt, vha, 0xe024, + "%s: failed alloc dsd_ptr\n", __func__); + return 1; + } + ha->dif_bundle_kallocs++; + + /* allocate dma buffer */ + dsd_ptr->dsd_addr = dma_pool_alloc(ha->dif_bundl_pool, + GFP_ATOMIC, &dsd_ptr->dsd_list_dma); + if (!dsd_ptr->dsd_addr) { + ql_dbg(ql_dbg_tgt, vha, 0xe024, + "%s: failed alloc ->dsd_addr\n", __func__); + /* need to cleanup only this dsd_ptr */ + /* rest will be done by sp_free_dma() */ + kfree(dsd_ptr); + ha->dif_bundle_kallocs--; + return 1; + } + ha->dif_bundle_dma_allocs++; + + /* xfrlen is min of dma pool size and sglen */ + xfrlen = (sglen > DIF_BUNDLING_DMA_POOL_SIZE) ? + DIF_BUNDLING_DMA_POOL_SIZE : sglen; + + /* replace with local allocated dma buffer */ + sg_pcopy_to_buffer(sgl, sg_nents(sgl), + dsd_ptr->dsd_addr, xfrlen, difctx->dif_bundl_len); + difctx->dif_bundl_len += xfrlen; + sglen -= xfrlen; + difctx->no_dif_bundl++; - if (sp) { list_add_tail(&dsd_ptr->list, - &((struct crc_context *) - sp->u.scmd.ctx)->dsd_list); - - sp->flags |= SRB_CRC_CTX_DSD_VALID; - } else { - list_add_tail(&dsd_ptr->list, - &(tc->ctx->dsd_list)); - tc->ctx_dsd_alloced = 1; + &difctx->ldif_dma_hndl_list); } - - /* add new list to cmd iocb or last list */ - *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); - *cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma)); - *cur_dsd++ = dsd_list_len; - cur_dsd = (uint32_t *)next_dsd; } - sle_dma = sg_dma_address(sg); - *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); - *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); - *cur_dsd++ = cpu_to_le32(sg_dma_len(sg)); + ql_dbg(ql_dbg_tgt+ql_dbg_verbose, vha, 0xe025, + "dif_bundl_len=%x, no_dif_bundl=%x\n", + difctx->dif_bundl_len, difctx->no_dif_bundl); + + track_difbundl_buf = used_dsds = difctx->no_dif_bundl; + sp->flags |= SRB_DIF_BUNDL_DMA_VALID; + + list_for_each_entry_safe(dif_dsd, nxt_dsd, + &difctx->ldif_dma_hndl_list, list) { + u32 sglen = (difctx->dif_bundl_len > DIF_BUNDLING_DMA_POOL_SIZE) ? + DIF_BUNDLING_DMA_POOL_SIZE : difctx->dif_bundl_len; + BUG_ON(track_difbundl_buf == 0); + + /* Allocate additional continuation packets? */ + if (avail_dsds == 0) { + ql_dbg(ql_dbg_tgt+ql_dbg_verbose, vha, 0xe024, + "%s: adding continuation iocb's\n", __func__); + avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ? + QLA_DSDS_PER_IOCB : used_dsds; + dsd_list_len = (avail_dsds + 1) * 12; + used_dsds -= avail_dsds; + + /* allocate tracking DS */ + dsd_ptr = kzalloc(sizeof(*dsd_ptr), GFP_ATOMIC); + if (!dsd_ptr) { + ql_dbg(ql_dbg_tgt, vha, 0xe026, + "%s: failed alloc dsd_ptr\n", __func__); + return 1; + } + ha->dif_bundle_kallocs++; + + difctx->no_ldif_dsd++; + /* allocate new list */ + dsd_ptr->dsd_addr = + dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC, + &dsd_ptr->dsd_list_dma); + if (!dsd_ptr->dsd_addr) { + ql_dbg(ql_dbg_tgt, vha, 0xe026, + "%s: failed alloc ->dsd_addr\n", __func__); + /* need to cleanup only this dsd_ptr */ + /* rest will be done by sp_free_dma() */ + kfree(dsd_ptr); + ha->dif_bundle_kallocs--; + return 1; + } + ha->dif_bundle_dma_allocs++; + + if (sp) { + list_add_tail(&dsd_ptr->list, + &difctx->ldif_dsd_list); + sp->flags |= SRB_CRC_CTX_DSD_VALID; + } else { + list_add_tail(&dsd_ptr->list, + &difctx->ldif_dsd_list); + tc->ctx_dsd_alloced = 1; + } + + /* add new list to cmd iocb or last list */ + *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = dsd_list_len; + cur_dsd = dsd_ptr->dsd_addr; + } + *cur_dsd++ = cpu_to_le32(LSD(dif_dsd->dsd_list_dma)); + *cur_dsd++ = cpu_to_le32(MSD(dif_dsd->dsd_list_dma)); + *cur_dsd++ = cpu_to_le32(sglen); + avail_dsds--; + difctx->dif_bundl_len -= sglen; + track_difbundl_buf--; + } - avail_dsds--; + ql_dbg(ql_dbg_tgt+ql_dbg_verbose, vha, 0xe026, + "%s: no_ldif_dsd:%x, no_dif_bundl:%x\n", __func__, + difctx->no_ldif_dsd, difctx->no_dif_bundl); + } else { + for_each_sg(sgl, sg, tot_dsds, i) { + dma_addr_t sle_dma; + + /* Allocate additional continuation packets? */ + if (avail_dsds == 0) { + avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ? + QLA_DSDS_PER_IOCB : used_dsds; + dsd_list_len = (avail_dsds + 1) * 12; + used_dsds -= avail_dsds; + + /* allocate tracking DS */ + dsd_ptr = kzalloc(sizeof(*dsd_ptr), GFP_ATOMIC); + if (!dsd_ptr) { + ql_dbg(ql_dbg_tgt+ql_dbg_verbose, vha, 0xe027, + "%s: failed alloc dsd_dma...\n", __func__); + return 1; + } + + /* allocate new list */ + dsd_ptr->dsd_addr = + dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC, + &dsd_ptr->dsd_list_dma); + if (!dsd_ptr->dsd_addr) { + /* need to cleanup only this dsd_ptr */ + /* rest will be done by sp_free_dma() */ + kfree(dsd_ptr); + return 1; + } + + if (sp) { + list_add_tail(&dsd_ptr->list, + &difctx->dsd_list); + sp->flags |= SRB_CRC_CTX_DSD_VALID; + } else { + list_add_tail(&dsd_ptr->list, + &difctx->dsd_list); + tc->ctx_dsd_alloced = 1; + } + + /* add new list to cmd iocb or last list */ + *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = dsd_list_len; + cur_dsd = dsd_ptr->dsd_addr; + } + sle_dma = sg_dma_address(sg); + *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(sg_dma_len(sg)); + avail_dsds--; + } } /* Null termination */ *cur_dsd++ = 0; @@ -1392,6 +1556,9 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, if (qla24xx_walk_and_build_prot_sglist(ha, sp, cur_dsd, tot_prot_dsds, NULL)) goto crc_queuing_error; + + ql_dbg(ql_dbg_tgt+ql_dbg_verbose, vha, 0xe0aa, + "%s: crc_ctx_pkt=%p:\n", __func__, crc_ctx_pkt); } return QLA_SUCCESS; diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index ae1d767bb751e..d60a767b3bbda 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -2522,6 +2522,21 @@ check_scsi_status: cp->device->vendor); break; + case CS_DMA: + ql_log(ql_log_info, fcport->vha, 0x3022, + "CS_DMA error: 0x%x-0x%x (0x%x) nexus=%ld:%d:%llu " + "portid=%02x%02x%02x oxid=0x%x cdb=%10phN len=0x%x " + "rsp_info=0x%x resid=0x%x fw_resid=0x%x sp=%p cp=%p.\n", + comp_status, scsi_status, res, vha->host_no, + cp->device->id, cp->device->lun, fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa, ox_id, + cp->cmnd, scsi_bufflen(cp), rsp_info_len, + resid_len, fw_resid_len, sp, cp); + ql_dump_buffer(ql_dbg_tgt+ql_dbg_verbose, vha, 0xe0ee, + pkt, sizeof(*sts24)); + res = DID_ERROR << 16; + break; + default: res = DID_ERROR << 16; break; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index d5931ace3e40b..46b36cab65d96 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -231,6 +231,13 @@ MODULE_PARM_DESC(ql2xmvasynctoatio, "0 (Default). Do not move IOCBs" "1 - Move IOCBs."); +int ql2xdifbundlinginternalbuffers; +module_param(ql2xdifbundlinginternalbuffers, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(ql2xdifbundlinginternalbuffers, + "Force using internal buffers for DIF information" + "0 (Default). Based on check." + "1 Force using internal buffers"); + /* * SCSI host template entry points */ @@ -631,6 +638,41 @@ qla2x00_sp_free_dma(void *ptr) ctx1 = NULL; } + if (sp->flags & SRB_DIF_BUNDL_DMA_VALID) { + struct crc_context *difctx = sp->u.scmd.ctx; + struct dsd_dma *dif_dsd, *nxt_dsd; + + list_for_each_entry_safe(dif_dsd, nxt_dsd, + &difctx->ldif_dma_hndl_list, list) { + list_del(&dif_dsd->list); + dma_pool_free(ha->dif_bundl_pool, dif_dsd->dsd_addr, + dif_dsd->dsd_list_dma); + kfree(dif_dsd); + difctx->no_dif_bundl--; + } + + list_for_each_entry_safe(dif_dsd, nxt_dsd, + &difctx->ldif_dsd_list, list) { + list_del(&dif_dsd->list); + dma_pool_free(ha->dl_dma_pool, dif_dsd->dsd_addr, + dif_dsd->dsd_list_dma); + kfree(dif_dsd); + difctx->no_ldif_dsd--; + } + + if (difctx->no_ldif_dsd) { + ql_dbg(ql_dbg_tgt+ql_dbg_verbose, sp->vha, 0xe022, + "%s: difctx->no_ldif_dsd=%x\n", + __func__, difctx->no_ldif_dsd); + } + + if (difctx->no_dif_bundl) { + ql_dbg(ql_dbg_tgt+ql_dbg_verbose, sp->vha, 0xe022, + "%s: difctx->no_dif_bundl=%x\n", + __func__, difctx->no_dif_bundl); + } + } + CMD_SP(cmd) = NULL; qla2x00_rel_sp(sp); } @@ -3722,9 +3764,72 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, "Failed to allocate memory for fcp_cmnd_dma_pool.\n"); goto fail_dl_dma_pool; } + + if (ql2xenabledif) { + u64 bufsize = DIF_BUNDLING_DMA_POOL_SIZE; + struct dsd_dma *dsd, *nxt; + uint i; + /* Creata a DMA pool of buffers for DIF bundling */ + ha->dif_bundl_pool = dma_pool_create(name, &ha->pdev->dev, + DIF_BUNDLING_DMA_POOL_SIZE, 8, 0); + if (!ha->dif_bundl_pool) { + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0024, + "%s: failed create dif_bundl_pool\n", __func__); + goto fail_dif_bundl_dma_pool; + } + + INIT_LIST_HEAD(&ha->pool.good.head); + INIT_LIST_HEAD(&ha->pool.unusable.head); + ha->pool.good.count = 0; + ha->pool.unusable.count = 0; + for (i = 0; i < 128; i++) { + dsd = kzalloc(sizeof(*dsd), GFP_ATOMIC); + if (!dsd) { + ql_dbg_pci(ql_dbg_init, ha->pdev, 0xe0ee, + "%s: failed alloc dsd\n", __func__); + return 1; + } + ha->dif_bundle_kallocs++; + + dsd->dsd_addr = dma_pool_alloc( + ha->dif_bundl_pool, GFP_ATOMIC, &dsd->dsd_list_dma); + if (!dsd->dsd_addr) { + ql_dbg_pci(ql_dbg_init, ha->pdev, 0xe0ee, + "%s: failed alloc ->dsd_addr\n", __func__); + kfree(dsd); + ha->dif_bundle_kallocs--; + continue; + } + ha->dif_bundle_dma_allocs++; + + /* if dma buffer crosses 4G boundary, put it on bad list */ + if (MSD(dsd->dsd_list_dma) ^ MSD(dsd->dsd_list_dma+bufsize)) { + list_add_tail(&dsd->list, &ha->pool.unusable.head); + ha->pool.unusable.count++; + } else { + list_add_tail(&dsd->list, &ha->pool.good.head); + ha->pool.good.count++; + } + } + + /* return the good ones back to the pool */ + list_for_each_entry_safe(dsd, nxt, &ha->pool.good.head, list) { + list_del(&dsd->list); + dma_pool_free(ha->dif_bundl_pool, dsd->dsd_addr, + dsd->dsd_list_dma); + ha->dif_bundle_dma_allocs--; + kfree(dsd); + ha->dif_bundle_kallocs--; + } + + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0024, + "%s: dif dma pool (good=%u unusable=%u)\n", + __func__, ha->pool.good.count, ha->pool.unusable.count); + } + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0025, - "dl_dma_pool=%p fcp_cmnd_dma_pool=%p.\n", - ha->dl_dma_pool, ha->fcp_cmnd_dma_pool); + "dl_dma_pool=%p fcp_cmnd_dma_pool=%p dif_bundl_pool=%p.\n", + ha->dl_dma_pool, ha->fcp_cmnd_dma_pool, ha->dif_bundl_pool); } /* Allocate memory for SNS commands */ @@ -3875,6 +3980,21 @@ fail_free_ms_iocb: dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt), ha->sns_cmd, ha->sns_cmd_dma); fail_dma_pool: + if (ql2xenabledif) { + struct dsd_dma *dsd, *nxt; + list_for_each_entry_safe(dsd, nxt, &ha->pool.unusable.head, list) { + list_del(&dsd->list); + dma_pool_free(ha->dif_bundl_pool, dsd->dsd_addr, dsd->dsd_list_dma); + ha->dif_bundle_dma_allocs--; + kfree(dsd); + ha->dif_bundle_kallocs--; + ha->pool.unusable.count--; + } + dma_pool_destroy(ha->dif_bundl_pool); + ha->dif_bundl_pool = NULL; + } + +fail_dif_bundl_dma_pool: if (IS_QLA82XX(ha) || ql2xenabledif) { dma_pool_destroy(ha->fcp_cmnd_dma_pool); ha->fcp_cmnd_dma_pool = NULL; @@ -4032,6 +4152,29 @@ qla2x00_mem_free(struct qla_hw_data *ha) if (ha->ctx_mempool) mempool_destroy(ha->ctx_mempool); + if (ql2xenabledif) { + struct dsd_dma *dsd, *nxt; + + list_for_each_entry_safe(dsd, nxt, &ha->pool.unusable.head, list) { + list_del(&dsd->list); + dma_pool_free(ha->dif_bundl_pool, dsd->dsd_addr, dsd->dsd_list_dma); + ha->dif_bundle_dma_allocs--; + kfree(dsd); + ha->dif_bundle_kallocs--; + ha->pool.unusable.count--; + } + list_for_each_entry_safe(dsd, nxt, &ha->pool.good.head, list) { + list_del(&dsd->list); + dma_pool_free(ha->dif_bundl_pool, dsd->dsd_addr, dsd->dsd_list_dma); + ha->dif_bundle_dma_allocs--; + kfree(dsd); + ha->dif_bundle_kallocs--; + } + } + + if (ha->dif_bundl_pool) + dma_pool_destroy(ha->dif_bundl_pool); + qlt_mem_free(ha); if (ha->init_cb) diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index e41bf66510a64..7fb213b37a0aa 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "9.00.00.00.40.0-k" +#define QLA2XXX_VERSION "9.00.00.00.40.0-k1" #define QLA_DRIVER_MAJOR_VER 9 #define QLA_DRIVER_MINOR_VER 0