]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
qla2xxx: Enable buffer boundary check when DIF bundling is on.
authorRajan Shanmugavelu <rajan.shanmugavelu@oracle.com>
Mon, 4 Jun 2018 22:22:43 +0000 (15:22 -0700)
committerBrian Maly <brian.maly@oracle.com>
Mon, 11 Jun 2018 18:44:51 +0000 (14:44 -0400)
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 <giridhar.malavali@cavium.com>
Co-authored-by: Joe Carnuccio <joe.carnuccio@cavium.com>
Reviewed-by: Giri Malavali <giridhar.malavali@cavium.com>
Signed-off-by: Rajan Shanmugavelu <rajan.shanmugavelu@oracle.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Brian Maly <brian.maly@oracle.com>
drivers/scsi/qla2xxx/qla_attr.c
drivers/scsi/qla2xxx/qla_def.h
drivers/scsi/qla2xxx/qla_gbl.h
drivers/scsi/qla2xxx/qla_iocb.c
drivers/scsi/qla2xxx/qla_isr.c
drivers/scsi/qla2xxx/qla_os.c
drivers/scsi/qla2xxx/qla_version.h

index 2381f73cf543e0d48c086c832e23db33b9eef696..6a37a8b36ebb5cc6c9b10effc67476be57bd31e9 100644 (file)
@@ -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,
 };
 
index 868fa24ce68d8bffa9eb990f7ed0d1d718caef11..33d400bdc98f46ee6c619d941b506a615deb6d76 100644 (file)
@@ -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 {
index 6b7b1c344193990b1a8eb88af379fb8db836afc4..ed3baa994c169b53ce3a54896d31074065b5736e 100644 (file)
@@ -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);
index 7dc213ceee581ae40bc2dc5a56780fabb4f8ab24..e7485f5f92df7a72125699e39bf4d7bfe0318bda 100644 (file)
@@ -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;
 
index ae1d767bb751e4860b0debb21cb722b5e38102ba..d60a767b3bbda31b4c6d3706ea79fd672ac91841 100644 (file)
@@ -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;
index d5931ace3e40b0533dd636a9d709e39b08224b8c..46b36cab65d963b003e91ab94ebf72aefddc9568 100644 (file)
@@ -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)
index e41bf66510a640c9df8a10effff064b692fb3b9f..7fb213b37a0aaaca49a8d93e3a4d6bbd1191d65c 100644 (file)
@@ -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