[SCSI] lpfc 8.3.29: T10 Diff fixes and enhancements
authorJames Smart <james.smart@emulex.com>
Wed, 18 Jan 2012 21:25:09 +0000 (16:25 -0500)
committerJames Bottomley <JBottomley@Parallels.com>
Sun, 19 Feb 2012 14:08:52 +0000 (08:08 -0600)
T10 Diff fixes and enhancements:

- Add SLI4 Lancer support for T10 DIF / BlockGuard (121980)
- Fix SLI4 BlockGuard behavior when protection data is generated by HBA (121980)
- Enhance debugfs for injecting T10 DIF errors (123966, 132966)
- Fix Incorrect usage of bghm for BlockGuard errors (127022)

Signed-off-by: Alex Iannicelli <alex.iannicelli@emulex.com>
Signed-off-by: James Smart <james.smart@emulex.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
drivers/scsi/lpfc/lpfc.h
drivers/scsi/lpfc/lpfc_debugfs.c
drivers/scsi/lpfc/lpfc_hw4.h
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/lpfc/lpfc_scsi.c
drivers/scsi/lpfc/lpfc_sli.c
drivers/scsi/lpfc/lpfc_sli.h

index 825f9307417a8dcdb4240763476ad41107c0d02b..5fc044ff656eb75f85bf61ec2666aa2465c35f02 100644 (file)
@@ -534,6 +534,7 @@ struct lpfc_hba {
        void (*lpfc_scsi_prep_cmnd)
                (struct lpfc_vport *, struct lpfc_scsi_buf *,
                 struct lpfc_nodelist *);
+
        /* IOCB interface function jump table entries */
        int (*__lpfc_sli_issue_iocb)
                (struct lpfc_hba *, uint32_t,
@@ -541,8 +542,6 @@ struct lpfc_hba {
        void (*__lpfc_sli_release_iocbq)(struct lpfc_hba *,
                         struct lpfc_iocbq *);
        int (*lpfc_hba_down_post)(struct lpfc_hba *phba);
-
-
        IOCB_t * (*lpfc_get_iocb_from_iocbq)
                (struct lpfc_iocbq *);
        void (*lpfc_scsi_cmd_iocb_cmpl)
@@ -551,10 +550,12 @@ struct lpfc_hba {
        /* MBOX interface function jump table entries */
        int (*lpfc_sli_issue_mbox)
                (struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
+
        /* Slow-path IOCB process function jump table entries */
        void (*lpfc_sli_handle_slow_ring_event)
                (struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
                 uint32_t mask);
+
        /* INIT device interface function jump table entries */
        int (*lpfc_sli_hbq_to_firmware)
                (struct lpfc_hba *, uint32_t, struct hbq_dmabuf *);
@@ -573,6 +574,10 @@ struct lpfc_hba {
        int (*lpfc_selective_reset)
                (struct lpfc_hba *);
 
+       int (*lpfc_bg_scsi_prep_dma_buf)
+               (struct lpfc_hba *, struct lpfc_scsi_buf *);
+       /* Add new entries here */
+
        /* SLI4 specific HBA data structure */
        struct lpfc_sli4_hba sli4_hba;
 
@@ -838,6 +843,7 @@ struct lpfc_hba {
        struct dentry *debug_writeGuard; /* inject write guard_tag errors */
        struct dentry *debug_writeApp;   /* inject write app_tag errors */
        struct dentry *debug_writeRef;   /* inject write ref_tag errors */
+       struct dentry *debug_readGuard;  /* inject read guard_tag errors */
        struct dentry *debug_readApp;    /* inject read app_tag errors */
        struct dentry *debug_readRef;    /* inject read ref_tag errors */
 
@@ -845,10 +851,11 @@ struct lpfc_hba {
        uint32_t lpfc_injerr_wgrd_cnt;
        uint32_t lpfc_injerr_wapp_cnt;
        uint32_t lpfc_injerr_wref_cnt;
+       uint32_t lpfc_injerr_rgrd_cnt;
        uint32_t lpfc_injerr_rapp_cnt;
        uint32_t lpfc_injerr_rref_cnt;
        sector_t lpfc_injerr_lba;
-#define LPFC_INJERR_LBA_OFF    (sector_t)0xffffffffffffffff
+#define LPFC_INJERR_LBA_OFF    (sector_t)(-1)
 
        struct dentry *debug_slow_ring_trc;
        struct lpfc_debugfs_trc *slow_ring_trc;
index 3587a3fe8fcb62bb627e0f3dfac15ef45bc1df7e..22e17be04d8af228b33efef2009f2a9cfc7a7471 100644 (file)
@@ -1019,6 +1019,8 @@ lpfc_debugfs_dif_err_read(struct file *file, char __user *buf,
                cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_wapp_cnt);
        else if (dent == phba->debug_writeRef)
                cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_wref_cnt);
+       else if (dent == phba->debug_readGuard)
+               cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_rgrd_cnt);
        else if (dent == phba->debug_readApp)
                cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_rapp_cnt);
        else if (dent == phba->debug_readRef)
@@ -1057,6 +1059,8 @@ lpfc_debugfs_dif_err_write(struct file *file, const char __user *buf,
                phba->lpfc_injerr_wapp_cnt = (uint32_t)tmp;
        else if (dent == phba->debug_writeRef)
                phba->lpfc_injerr_wref_cnt = (uint32_t)tmp;
+       else if (dent == phba->debug_readGuard)
+               phba->lpfc_injerr_rgrd_cnt = (uint32_t)tmp;
        else if (dent == phba->debug_readApp)
                phba->lpfc_injerr_rapp_cnt = (uint32_t)tmp;
        else if (dent == phba->debug_readRef)
@@ -3978,6 +3982,17 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
                        goto debug_failed;
                }
 
+               snprintf(name, sizeof(name), "readGuardInjErr");
+               phba->debug_readGuard =
+                       debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+                       phba->hba_debugfs_root,
+                       phba, &lpfc_debugfs_op_dif_err);
+               if (!phba->debug_readGuard) {
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+                               "0808 Cannot create debugfs readGuard\n");
+                       goto debug_failed;
+               }
+
                snprintf(name, sizeof(name), "readAppInjErr");
                phba->debug_readApp =
                        debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
@@ -4318,6 +4333,10 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
                        debugfs_remove(phba->debug_writeRef); /* writeRef */
                        phba->debug_writeRef = NULL;
                }
+               if (phba->debug_readGuard) {
+                       debugfs_remove(phba->debug_readGuard); /* readGuard */
+                       phba->debug_readGuard = NULL;
+               }
                if (phba->debug_readApp) {
                        debugfs_remove(phba->debug_readApp); /* readApp */
                        phba->debug_readApp = NULL;
index cc19bc1b5ace2d3c8deb4b52dadb77fe4c922aa7..9e2b9b227e1a2b732b39ee6cb424d421c6c03230 100644 (file)
@@ -321,6 +321,10 @@ struct lpfc_cqe {
 #define CQE_STATUS_CMD_REJECT          0xb
 #define CQE_STATUS_FCP_TGT_LENCHECK    0xc
 #define CQE_STATUS_NEED_BUFF_ENTRY     0xf
+#define CQE_STATUS_DI_ERROR            0x16
+
+/* Used when mapping CQE status to IOCB */
+#define LPFC_IOCB_STATUS_MASK          0xf
 
 /* Status returned by hardware (valid only if status = CQE_STATUS_SUCCESS). */
 #define CQE_HW_STATUS_NO_ERR           0x0
@@ -348,6 +352,21 @@ struct lpfc_wcqe_complete {
 #define lpfc_wcqe_c_hw_status_WORD     word0
        uint32_t total_data_placed;
        uint32_t parameter;
+#define lpfc_wcqe_c_bg_edir_SHIFT      5
+#define lpfc_wcqe_c_bg_edir_MASK       0x00000001
+#define lpfc_wcqe_c_bg_edir_WORD       parameter
+#define lpfc_wcqe_c_bg_tdpv_SHIFT      3
+#define lpfc_wcqe_c_bg_tdpv_MASK       0x00000001
+#define lpfc_wcqe_c_bg_tdpv_WORD       parameter
+#define lpfc_wcqe_c_bg_re_SHIFT                2
+#define lpfc_wcqe_c_bg_re_MASK         0x00000001
+#define lpfc_wcqe_c_bg_re_WORD         parameter
+#define lpfc_wcqe_c_bg_ae_SHIFT                1
+#define lpfc_wcqe_c_bg_ae_MASK         0x00000001
+#define lpfc_wcqe_c_bg_ae_WORD         parameter
+#define lpfc_wcqe_c_bg_ge_SHIFT                0
+#define lpfc_wcqe_c_bg_ge_MASK         0x00000001
+#define lpfc_wcqe_c_bg_ge_WORD         parameter
        uint32_t word3;
 #define lpfc_wcqe_c_valid_SHIFT                lpfc_cqe_valid_SHIFT
 #define lpfc_wcqe_c_valid_MASK         lpfc_cqe_valid_MASK
@@ -359,8 +378,8 @@ struct lpfc_wcqe_complete {
 #define lpfc_wcqe_c_pv_MASK            0x00000001
 #define lpfc_wcqe_c_pv_WORD            word3
 #define lpfc_wcqe_c_priority_SHIFT     24
-#define lpfc_wcqe_c_priority_MASK              0x00000007
-#define lpfc_wcqe_c_priority_WORD              word3
+#define lpfc_wcqe_c_priority_MASK      0x00000007
+#define lpfc_wcqe_c_priority_WORD      word3
 #define lpfc_wcqe_c_code_SHIFT         lpfc_cqe_code_SHIFT
 #define lpfc_wcqe_c_code_MASK          lpfc_cqe_code_MASK
 #define lpfc_wcqe_c_code_WORD          lpfc_cqe_code_WORD
index f3ad7349f5d130a84aeadf2fb575919256e2cd11..d9628770f11e3466a0fd5b835f9760299e1442d2 100644 (file)
@@ -4380,6 +4380,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
        uint8_t pn_page[LPFC_MAX_SUPPORTED_PAGES] = {0};
        struct lpfc_mqe *mqe;
        int longs, sli_family;
+       int sges_per_segment;
 
        /* Before proceed, wait for POST done and device ready */
        rc = lpfc_sli4_post_status_check(phba);
@@ -4443,6 +4444,11 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
        phba->fc_map[1] = LPFC_FCOE_FCF_MAP1;
        phba->fc_map[2] = LPFC_FCOE_FCF_MAP2;
 
+       /* With BlockGuard we can have multiple SGEs per Data Segemnt */
+       sges_per_segment = 1;
+       if (phba->cfg_enable_bg)
+               sges_per_segment = 2;
+
        /*
         * Since the sg_tablesize is module parameter, the sg_dma_buf_size
         * used to create the sg_dma_buf_pool must be dynamically calculated.
@@ -4451,7 +4457,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
         * sgl sizes of must be a power of 2.
         */
        buf_size = (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp) +
-                   ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct sli4_sge)));
+                   (((phba->cfg_sg_seg_cnt * sges_per_segment) + 2) *
+                   sizeof(struct sli4_sge)));
 
        sli_family = bf_get(lpfc_sli_intf_sli_family, &phba->sli4_hba.sli_intf);
        max_buf_size = LPFC_SLI4_MAX_BUF_SIZE;
@@ -4468,6 +4475,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
        default:
                break;
        }
+
        for (dma_buf_size = LPFC_SLI4_MIN_BUF_SIZE;
             dma_buf_size < max_buf_size && buf_size > dma_buf_size;
             dma_buf_size = dma_buf_size << 1)
index c60f5d0b38696c022326c76e937f57e1d72a6626..efc055b6bac4fe6fffb8432c4004ea2cbc9ac5aa 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2011 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2012 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -1280,31 +1280,45 @@ lpfc_cmd_blksize(struct scsi_cmnd *sc)
 }
 
 #ifdef CONFIG_SCSI_LPFC_DEBUG_FS
-/*
- * Given a scsi cmnd, determine the BlockGuard tags to be used with it
+
+#define BG_ERR_INIT    1
+#define BG_ERR_TGT     2
+#define BG_ERR_SWAP    3
+#define BG_ERR_CHECK   4
+
+/**
+ * lpfc_bg_err_inject - Determine if we should inject an error
+ * @phba: The Hba for which this call is being executed.
  * @sc: The SCSI command to examine
  * @reftag: (out) BlockGuard reference tag for transmitted data
  * @apptag: (out) BlockGuard application tag for transmitted data
  * @new_guard (in) Value to replace CRC with if needed
  *
- * Returns (1) if error injection was performed, (0) otherwise
- */
+ * Returns (1) if error injection is detected by Initiator
+ * Returns (2) if error injection is detected by Target
+ * Returns (3) if swapping CSUM->CRC is required for error injection
+ * Returns (4) disabling Guard/Ref/App checking is required for error injection
+ **/
 static int
 lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                uint32_t *reftag, uint16_t *apptag, uint32_t new_guard)
 {
        struct scatterlist *sgpe; /* s/g prot entry */
        struct scatterlist *sgde; /* s/g data entry */
-       struct scsi_dif_tuple *src;
+       struct scsi_dif_tuple *src = NULL;
        uint32_t op = scsi_get_prot_op(sc);
        uint32_t blksize;
        uint32_t numblks;
        sector_t lba;
        int rc = 0;
+       int blockoff = 0;
 
        if (op == SCSI_PROT_NORMAL)
                return 0;
 
+       sgpe = scsi_prot_sglist(sc);
+       sgde = scsi_sglist(sc);
+
        lba = scsi_get_lba(sc);
        if (phba->lpfc_injerr_lba != LPFC_INJERR_LBA_OFF) {
                blksize = lpfc_cmd_blksize(sc);
@@ -1314,142 +1328,296 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                if ((phba->lpfc_injerr_lba < lba) ||
                        (phba->lpfc_injerr_lba >= (lba + numblks)))
                        return 0;
+               if (sgpe) {
+                       blockoff = phba->lpfc_injerr_lba - lba;
+                       numblks = sg_dma_len(sgpe) /
+                               sizeof(struct scsi_dif_tuple);
+                       if (numblks < blockoff)
+                               blockoff = numblks;
+                       src = (struct scsi_dif_tuple *)sg_virt(sgpe);
+                       src += blockoff;
+               }
        }
 
-       sgpe = scsi_prot_sglist(sc);
-       sgde = scsi_sglist(sc);
-
        /* Should we change the Reference Tag */
        if (reftag) {
-               /*
-                * If we are SCSI_PROT_WRITE_STRIP, the protection data is
-                * being stripped from the wire, thus it doesn't matter.
-                */
-               if ((op == SCSI_PROT_WRITE_PASS) ||
-                       (op == SCSI_PROT_WRITE_INSERT)) {
-                       if (phba->lpfc_injerr_wref_cnt) {
+               if (phba->lpfc_injerr_wref_cnt) {
+                       switch (op) {
+                       case SCSI_PROT_WRITE_PASS:
+                               if (blockoff && src) {
+                                       /* Insert error in middle of the IO */
+
+                                       lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                                       "9076 BLKGRD: Injecting reftag error: "
+                                       "write lba x%lx + x%x oldrefTag x%x\n",
+                                       (unsigned long)lba, blockoff,
+                                       src->ref_tag);
 
+                                       /*
+                                        * NOTE, this will change ref tag in
+                                        * the memory location forever!
+                                        */
+                                       src->ref_tag = 0xDEADBEEF;
+                                       phba->lpfc_injerr_wref_cnt--;
+                                       phba->lpfc_injerr_lba =
+                                               LPFC_INJERR_LBA_OFF;
+                                       rc = BG_ERR_CHECK;
+                                       break;
+                               }
+                               /* Drop thru */
+                       case SCSI_PROT_WRITE_STRIP:
+                               /*
+                                * For WRITE_STRIP and WRITE_PASS,
+                                * force the error on data
+                                * being copied from SLI-Host to SLI-Port.
+                                */
+                               *reftag = 0xDEADBEEF;
+                               phba->lpfc_injerr_wref_cnt--;
+                               phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+                               rc = BG_ERR_INIT;
+
+                               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                                       "9077 BLKGRD: Injecting reftag error: "
+                                       "write lba x%lx\n", (unsigned long)lba);
+                               break;
+                       case SCSI_PROT_WRITE_INSERT:
+                               /*
+                                * For WRITE_INSERT, force the
+                                * error to be sent on the wire. It should be
+                                * detected by the Target.
+                                */
                                /* DEADBEEF will be the reftag on the wire */
                                *reftag = 0xDEADBEEF;
                                phba->lpfc_injerr_wref_cnt--;
                                phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
-                               rc = 1;
+                               rc = BG_ERR_TGT;
 
                                lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-                                       "9081 BLKGRD: Injecting reftag error: "
+                                       "9078 BLKGRD: Injecting reftag error: "
                                        "write lba x%lx\n", (unsigned long)lba);
+                               break;
                        }
-               } else {
-                       if (phba->lpfc_injerr_rref_cnt) {
+               }
+               if (phba->lpfc_injerr_rref_cnt) {
+                       switch (op) {
+                       case SCSI_PROT_READ_INSERT:
+                               /*
+                                * For READ_INSERT, it doesn't make sense
+                                * to change the reftag.
+                                */
+                               break;
+                       case SCSI_PROT_READ_STRIP:
+                       case SCSI_PROT_READ_PASS:
+                               /*
+                                * For READ_STRIP and READ_PASS, force the
+                                * error on data being read off the wire. It
+                                * should force an IO error to the driver.
+                                */
                                *reftag = 0xDEADBEEF;
                                phba->lpfc_injerr_rref_cnt--;
                                phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
-                               rc = 1;
+                               rc = BG_ERR_INIT;
 
                                lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-                                       "9076 BLKGRD: Injecting reftag error: "
+                                       "9079 BLKGRD: Injecting reftag error: "
                                        "read lba x%lx\n", (unsigned long)lba);
+                               break;
                        }
                }
        }
 
        /* Should we change the Application Tag */
        if (apptag) {
-               /*
-                * If we are SCSI_PROT_WRITE_STRIP, the protection data is
-                * being stripped from the wire, thus it doesn't matter.
-                */
-               if ((op == SCSI_PROT_WRITE_PASS) ||
-                       (op == SCSI_PROT_WRITE_INSERT)) {
-                       if (phba->lpfc_injerr_wapp_cnt) {
+               if (phba->lpfc_injerr_wapp_cnt) {
+                       switch (op) {
+                       case SCSI_PROT_WRITE_PASS:
+                               if (blockoff && src) {
+                                       /* Insert error in middle of the IO */
+
+                                       lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                                       "9080 BLKGRD: Injecting apptag error: "
+                                       "write lba x%lx + x%x oldappTag x%x\n",
+                                       (unsigned long)lba, blockoff,
+                                       src->app_tag);
+
+                                       /*
+                                        * NOTE, this will change app tag in
+                                        * the memory location forever!
+                                        */
+                                       src->app_tag = 0xDEAD;
+                                       phba->lpfc_injerr_wapp_cnt--;
+                                       phba->lpfc_injerr_lba =
+                                               LPFC_INJERR_LBA_OFF;
+                                       rc = BG_ERR_CHECK;
+                                       break;
+                               }
+                               /* Drop thru */
+                       case SCSI_PROT_WRITE_STRIP:
+                               /*
+                                * For WRITE_STRIP and WRITE_PASS,
+                                * force the error on data
+                                * being copied from SLI-Host to SLI-Port.
+                                */
+                               *apptag = 0xDEAD;
+                               phba->lpfc_injerr_wapp_cnt--;
+                               phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+                               rc = BG_ERR_INIT;
 
+                               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                                       "0812 BLKGRD: Injecting apptag error: "
+                                       "write lba x%lx\n", (unsigned long)lba);
+                               break;
+                       case SCSI_PROT_WRITE_INSERT:
+                               /*
+                                * For WRITE_INSERT, force the
+                                * error to be sent on the wire. It should be
+                                * detected by the Target.
+                                */
                                /* DEAD will be the apptag on the wire */
                                *apptag = 0xDEAD;
                                phba->lpfc_injerr_wapp_cnt--;
                                phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
-                               rc = 1;
+                               rc = BG_ERR_TGT;
 
                                lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-                                       "9077 BLKGRD: Injecting apptag error: "
+                                       "0813 BLKGRD: Injecting apptag error: "
                                        "write lba x%lx\n", (unsigned long)lba);
+                               break;
                        }
-               } else {
-                       if (phba->lpfc_injerr_rapp_cnt) {
+               }
+               if (phba->lpfc_injerr_rapp_cnt) {
+                       switch (op) {
+                       case SCSI_PROT_READ_INSERT:
+                               /*
+                                * For READ_INSERT, it doesn't make sense
+                                * to change the apptag.
+                                */
+                               break;
+                       case SCSI_PROT_READ_STRIP:
+                       case SCSI_PROT_READ_PASS:
+                               /*
+                                * For READ_STRIP and READ_PASS, force the
+                                * error on data being read off the wire. It
+                                * should force an IO error to the driver.
+                                */
                                *apptag = 0xDEAD;
                                phba->lpfc_injerr_rapp_cnt--;
                                phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
-                               rc = 1;
+                               rc = BG_ERR_INIT;
 
                                lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-                                       "9078 BLKGRD: Injecting apptag error: "
+                                       "0814 BLKGRD: Injecting apptag error: "
                                        "read lba x%lx\n", (unsigned long)lba);
+                               break;
                        }
                }
        }
 
+
        /* Should we change the Guard Tag */
+       if (new_guard) {
+               if (phba->lpfc_injerr_wgrd_cnt) {
+                       switch (op) {
+                       case SCSI_PROT_WRITE_PASS:
+                               if (blockoff && src) {
+                                       /* Insert error in middle of the IO */
+
+                                       lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                                       "0815 BLKGRD: Injecting guard error: "
+                                       "write lba x%lx + x%x oldgrdTag x%x\n",
+                                       (unsigned long)lba, blockoff,
+                                       src->guard_tag);
 
-       /*
-        * If we are SCSI_PROT_WRITE_INSERT, the protection data is
-        * being on the wire is being fully generated on the HBA.
-        * The host cannot change it or force an error.
-        */
-       if (((op == SCSI_PROT_WRITE_STRIP) ||
-               (op == SCSI_PROT_WRITE_PASS)) &&
-               phba->lpfc_injerr_wgrd_cnt) {
-               if (sgpe) {
-                       src = (struct scsi_dif_tuple *)sg_virt(sgpe);
-                       /*
-                        * Just inject an error in the first
-                        * prot block.
-                        */
-                       lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-                               "9079 BLKGRD: Injecting guard error: "
-                               "write lba x%lx oldGuard x%x refTag x%x\n",
-                               (unsigned long)lba, src->guard_tag,
-                               src->ref_tag);
+                                       /*
+                                        * NOTE, this will change guard tag in
+                                        * the memory location forever!
+                                        */
+                                       src->guard_tag = 0xDEAD;
+                                       phba->lpfc_injerr_wgrd_cnt--;
+                                       phba->lpfc_injerr_lba =
+                                               LPFC_INJERR_LBA_OFF;
+                                       rc = BG_ERR_CHECK;
+                                       break;
+                               }
+                               /* Drop thru */
+                       case SCSI_PROT_WRITE_STRIP:
+                               /*
+                                * For WRITE_STRIP and WRITE_PASS,
+                                * force the error on data
+                                * being copied from SLI-Host to SLI-Port.
+                                */
+                               phba->lpfc_injerr_wgrd_cnt--;
+                               phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
 
-                       src->guard_tag = (uint16_t)new_guard;
-                       phba->lpfc_injerr_wgrd_cnt--;
-                       phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
-                       rc = 1;
+                               rc = BG_ERR_SWAP;
+                               /* Signals the caller to swap CRC->CSUM */
 
-               } else {
-                       blksize = lpfc_cmd_blksize(sc);
-                       /*
-                        * Jump past the first data block
-                        * and inject an error in the
-                        * prot data. The prot data is already
-                        * embedded after the regular data.
-                        */
-                       src = (struct scsi_dif_tuple *)
-                                       (sg_virt(sgde) + blksize);
+                               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                                       "0816 BLKGRD: Injecting guard error: "
+                                       "write lba x%lx\n", (unsigned long)lba);
+                               break;
+                       case SCSI_PROT_WRITE_INSERT:
+                               /*
+                                * For WRITE_INSERT, force the
+                                * error to be sent on the wire. It should be
+                                * detected by the Target.
+                                */
+                               phba->lpfc_injerr_wgrd_cnt--;
+                               phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
 
-                       lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-                               "9080 BLKGRD: Injecting guard error: "
-                               "write lba x%lx oldGuard x%x refTag x%x\n",
-                               (unsigned long)lba, src->guard_tag,
-                               src->ref_tag);
-
-                       src->guard_tag = (uint16_t)new_guard;
-                       phba->lpfc_injerr_wgrd_cnt--;
-                       phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
-                       rc = 1;
+                               rc = BG_ERR_SWAP;
+                               /* Signals the caller to swap CRC->CSUM */
+
+                               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                                       "0817 BLKGRD: Injecting guard error: "
+                                       "write lba x%lx\n", (unsigned long)lba);
+                               break;
+                       }
+               }
+               if (phba->lpfc_injerr_rgrd_cnt) {
+                       switch (op) {
+                       case SCSI_PROT_READ_INSERT:
+                               /*
+                                * For READ_INSERT, it doesn't make sense
+                                * to change the guard tag.
+                                */
+                               break;
+                       case SCSI_PROT_READ_STRIP:
+                       case SCSI_PROT_READ_PASS:
+                               /*
+                                * For READ_STRIP and READ_PASS, force the
+                                * error on data being read off the wire. It
+                                * should force an IO error to the driver.
+                                */
+                               *apptag = 0xDEAD;
+                               phba->lpfc_injerr_rgrd_cnt--;
+                               phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+
+                               rc = BG_ERR_SWAP;
+                               /* Signals the caller to swap CRC->CSUM */
+
+                               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                                       "0818 BLKGRD: Injecting guard error: "
+                                       "read lba x%lx\n", (unsigned long)lba);
+                       }
                }
        }
+
        return rc;
 }
 #endif
 
-/*
- * Given a scsi cmnd, determine the BlockGuard opcodes to be used with it
+/**
+ * lpfc_sc_to_bg_opcodes - Determine the BlockGuard opcodes to be used with
+ * the specified SCSI command.
+ * @phba: The Hba for which this call is being executed.
  * @sc: The SCSI command to examine
  * @txopt: (out) BlockGuard operation for transmitted data
  * @rxopt: (out) BlockGuard operation for received data
  *
  * Returns: zero on success; non-zero if tx and/or rx op cannot be determined
  *
- */
+ **/
 static int
 lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                uint8_t *txop, uint8_t *rxop)
@@ -1519,8 +1687,88 @@ lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
        return ret;
 }
 
-/*
- * This function sets up buffer list for protection groups of
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+/**
+ * lpfc_bg_err_opcodes - reDetermine the BlockGuard opcodes to be used with
+ * the specified SCSI command in order to force a guard tag error.
+ * @phba: The Hba for which this call is being executed.
+ * @sc: The SCSI command to examine
+ * @txopt: (out) BlockGuard operation for transmitted data
+ * @rxopt: (out) BlockGuard operation for received data
+ *
+ * Returns: zero on success; non-zero if tx and/or rx op cannot be determined
+ *
+ **/
+static int
+lpfc_bg_err_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
+               uint8_t *txop, uint8_t *rxop)
+{
+       uint8_t guard_type = scsi_host_get_guard(sc->device->host);
+       uint8_t ret = 0;
+
+       if (guard_type == SHOST_DIX_GUARD_IP) {
+               switch (scsi_get_prot_op(sc)) {
+               case SCSI_PROT_READ_INSERT:
+               case SCSI_PROT_WRITE_STRIP:
+                       *txop = BG_OP_IN_CRC_OUT_NODIF;
+                       *rxop = BG_OP_IN_NODIF_OUT_CRC;
+                       break;
+
+               case SCSI_PROT_READ_STRIP:
+               case SCSI_PROT_WRITE_INSERT:
+                       *txop = BG_OP_IN_NODIF_OUT_CSUM;
+                       *rxop = BG_OP_IN_CSUM_OUT_NODIF;
+                       break;
+
+               case SCSI_PROT_READ_PASS:
+               case SCSI_PROT_WRITE_PASS:
+                       *txop = BG_OP_IN_CRC_OUT_CRC;
+                       *rxop = BG_OP_IN_CRC_OUT_CRC;
+                       break;
+
+               case SCSI_PROT_NORMAL:
+               default:
+                       break;
+
+               }
+       } else {
+               switch (scsi_get_prot_op(sc)) {
+               case SCSI_PROT_READ_STRIP:
+               case SCSI_PROT_WRITE_INSERT:
+                       *txop = BG_OP_IN_NODIF_OUT_CSUM;
+                       *rxop = BG_OP_IN_CSUM_OUT_NODIF;
+                       break;
+
+               case SCSI_PROT_READ_PASS:
+               case SCSI_PROT_WRITE_PASS:
+                       *txop = BG_OP_IN_CSUM_OUT_CRC;
+                       *rxop = BG_OP_IN_CRC_OUT_CSUM;
+                       break;
+
+               case SCSI_PROT_READ_INSERT:
+               case SCSI_PROT_WRITE_STRIP:
+                       *txop = BG_OP_IN_CSUM_OUT_NODIF;
+                       *rxop = BG_OP_IN_NODIF_OUT_CSUM;
+                       break;
+
+               case SCSI_PROT_NORMAL:
+               default:
+                       break;
+               }
+       }
+
+       return ret;
+}
+#endif
+
+/**
+ * lpfc_bg_setup_bpl - Setup BlockGuard BPL with no protection data
+ * @phba: The Hba for which this call is being executed.
+ * @sc: pointer to scsi command we're working on
+ * @bpl: pointer to buffer list for protection groups
+ * @datacnt: number of segments of data that have been dma mapped
+ *
+ * This function sets up BPL buffer list for protection groups of
  * type LPFC_PG_TYPE_NO_DIF
  *
  * This is usually used when the HBA is instructed to generate
@@ -1539,12 +1787,11 @@ lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
  *                                |more Data BDE's ... (opt)|
  *                                +-------------------------+
  *
- * @sc: pointer to scsi command we're working on
- * @bpl: pointer to buffer list for protection groups
- * @datacnt: number of segments of data that have been dma mapped
  *
  * Note: Data s/g buffers have been dma mapped
- */
+ *
+ * Returns the number of BDEs added to the BPL.
+ **/
 static int
 lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                struct ulp_bde64 *bpl, int datasegcnt)
@@ -1555,6 +1802,8 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
        dma_addr_t physaddr;
        int i = 0, num_bde = 0, status;
        int datadir = sc->sc_data_direction;
+       uint32_t rc;
+       uint32_t checking = 1;
        uint32_t reftag;
        unsigned blksize;
        uint8_t txop, rxop;
@@ -1565,11 +1814,16 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
 
        /* extract some info from the scsi command for pde*/
        blksize = lpfc_cmd_blksize(sc);
-       reftag = scsi_get_lba(sc) & 0xffffffff;
+       reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */
 
 #ifdef CONFIG_SCSI_LPFC_DEBUG_FS
-       /* reftag is the only error we can inject here */
-       lpfc_bg_err_inject(phba, sc, &reftag, 0, 0);
+       rc = lpfc_bg_err_inject(phba, sc, &reftag, 0, 1);
+       if (rc) {
+               if (rc == BG_ERR_SWAP)
+                       lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
+               if (rc == BG_ERR_CHECK)
+                       checking = 0;
+       }
 #endif
 
        /* setup PDE5 with what we have */
@@ -1592,8 +1846,8 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
        bf_set(pde6_optx, pde6, txop);
        bf_set(pde6_oprx, pde6, rxop);
        if (datadir == DMA_FROM_DEVICE) {
-               bf_set(pde6_ce, pde6, 1);
-               bf_set(pde6_re, pde6, 1);
+               bf_set(pde6_ce, pde6, checking);
+               bf_set(pde6_re, pde6, checking);
        }
        bf_set(pde6_ai, pde6, 1);
        bf_set(pde6_ae, pde6, 0);
@@ -1627,9 +1881,16 @@ out:
        return num_bde;
 }
 
-/*
- * This function sets up buffer list for protection groups of
- * type LPFC_PG_TYPE_DIF_BUF
+/**
+ * lpfc_bg_setup_bpl_prot - Setup BlockGuard BPL with protection data
+ * @phba: The Hba for which this call is being executed.
+ * @sc: pointer to scsi command we're working on
+ * @bpl: pointer to buffer list for protection groups
+ * @datacnt: number of segments of data that have been dma mapped
+ * @protcnt: number of segment of protection data that have been dma mapped
+ *
+ * This function sets up BPL buffer list for protection groups of
+ * type LPFC_PG_TYPE_DIF
  *
  * This is usually used when DIFs are in their own buffers,
  * separate from the data. The HBA can then by instructed
@@ -1654,14 +1915,11 @@ out:
  *                                    |          ...            |
  *                                    +-------------------------+
  *
- * @sc: pointer to scsi command we're working on
- * @bpl: pointer to buffer list for protection groups
- * @datacnt: number of segments of data that have been dma mapped
- * @protcnt: number of segment of protection data that have been dma mapped
- *
  * Note: It is assumed that both data and protection s/g buffers have been
  *       mapped for DMA
- */
+ *
+ * Returns the number of BDEs added to the BPL.
+ **/
 static int
 lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                struct ulp_bde64 *bpl, int datacnt, int protcnt)
@@ -1681,6 +1939,8 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
        int datadir = sc->sc_data_direction;
        unsigned char pgdone = 0, alldone = 0;
        unsigned blksize;
+       uint32_t rc;
+       uint32_t checking = 1;
        uint32_t reftag;
        uint8_t txop, rxop;
        int num_bde = 0;
@@ -1690,7 +1950,355 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
 
        if (!sgpe || !sgde) {
                lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
-                               "9020 Invalid s/g entry: data=0x%p prot=0x%p\n",
+                               "9020 Invalid s/g entry: data=0x%p prot=0x%p\n",
+                               sgpe, sgde);
+               return 0;
+       }
+
+       status = lpfc_sc_to_bg_opcodes(phba, sc, &txop, &rxop);
+       if (status)
+               goto out;
+
+       /* extract some info from the scsi command */
+       blksize = lpfc_cmd_blksize(sc);
+       reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+       rc = lpfc_bg_err_inject(phba, sc, &reftag, 0, 1);
+       if (rc) {
+               if (rc == BG_ERR_SWAP)
+                       lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
+               if (rc == BG_ERR_CHECK)
+                       checking = 0;
+       }
+#endif
+
+       split_offset = 0;
+       do {
+               /* setup PDE5 with what we have */
+               pde5 = (struct lpfc_pde5 *) bpl;
+               memset(pde5, 0, sizeof(struct lpfc_pde5));
+               bf_set(pde5_type, pde5, LPFC_PDE5_DESCRIPTOR);
+
+               /* Endianness conversion if necessary for PDE5 */
+               pde5->word0 = cpu_to_le32(pde5->word0);
+               pde5->reftag = cpu_to_le32(reftag);
+
+               /* advance bpl and increment bde count */
+               num_bde++;
+               bpl++;
+               pde6 = (struct lpfc_pde6 *) bpl;
+
+               /* setup PDE6 with the rest of the info */
+               memset(pde6, 0, sizeof(struct lpfc_pde6));
+               bf_set(pde6_type, pde6, LPFC_PDE6_DESCRIPTOR);
+               bf_set(pde6_optx, pde6, txop);
+               bf_set(pde6_oprx, pde6, rxop);
+               bf_set(pde6_ce, pde6, checking);
+               bf_set(pde6_re, pde6, checking);
+               bf_set(pde6_ai, pde6, 1);
+               bf_set(pde6_ae, pde6, 0);
+               bf_set(pde6_apptagval, pde6, 0);
+
+               /* Endianness conversion if necessary for PDE6 */
+               pde6->word0 = cpu_to_le32(pde6->word0);
+               pde6->word1 = cpu_to_le32(pde6->word1);
+               pde6->word2 = cpu_to_le32(pde6->word2);
+
+               /* advance bpl and increment bde count */
+               num_bde++;
+               bpl++;
+
+               /* setup the first BDE that points to protection buffer */
+               protphysaddr = sg_dma_address(sgpe) + protgroup_offset;
+               protgroup_len = sg_dma_len(sgpe) - protgroup_offset;
+
+               /* must be integer multiple of the DIF block length */
+               BUG_ON(protgroup_len % 8);
+
+               pde7 = (struct lpfc_pde7 *) bpl;
+               memset(pde7, 0, sizeof(struct lpfc_pde7));
+               bf_set(pde7_type, pde7, LPFC_PDE7_DESCRIPTOR);
+
+               pde7->addrHigh = le32_to_cpu(putPaddrHigh(protphysaddr));
+               pde7->addrLow = le32_to_cpu(putPaddrLow(protphysaddr));
+
+               protgrp_blks = protgroup_len / 8;
+               protgrp_bytes = protgrp_blks * blksize;
+
+               /* check if this pde is crossing the 4K boundary; if so split */
+               if ((pde7->addrLow & 0xfff) + protgroup_len > 0x1000) {
+                       protgroup_remainder = 0x1000 - (pde7->addrLow & 0xfff);
+                       protgroup_offset += protgroup_remainder;
+                       protgrp_blks = protgroup_remainder / 8;
+                       protgrp_bytes = protgrp_blks * blksize;
+               } else {
+                       protgroup_offset = 0;
+                       curr_prot++;
+               }
+
+               num_bde++;
+
+               /* setup BDE's for data blocks associated with DIF data */
+               pgdone = 0;
+               subtotal = 0; /* total bytes processed for current prot grp */
+               while (!pgdone) {
+                       if (!sgde) {
+                               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                                       "9065 BLKGRD:%s Invalid data segment\n",
+                                               __func__);
+                               return 0;
+                       }
+                       bpl++;
+                       dataphysaddr = sg_dma_address(sgde) + split_offset;
+                       bpl->addrLow = le32_to_cpu(putPaddrLow(dataphysaddr));
+                       bpl->addrHigh = le32_to_cpu(putPaddrHigh(dataphysaddr));
+
+                       remainder = sg_dma_len(sgde) - split_offset;
+
+                       if ((subtotal + remainder) <= protgrp_bytes) {
+                               /* we can use this whole buffer */
+                               bpl->tus.f.bdeSize = remainder;
+                               split_offset = 0;
+
+                               if ((subtotal + remainder) == protgrp_bytes)
+                                       pgdone = 1;
+                       } else {
+                               /* must split this buffer with next prot grp */
+                               bpl->tus.f.bdeSize = protgrp_bytes - subtotal;
+                               split_offset += bpl->tus.f.bdeSize;
+                       }
+
+                       subtotal += bpl->tus.f.bdeSize;
+
+                       if (datadir == DMA_TO_DEVICE)
+                               bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+                       else
+                               bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
+                       bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
+                       num_bde++;
+                       curr_data++;
+
+                       if (split_offset)
+                               break;
+
+                       /* Move to the next s/g segment if possible */
+                       sgde = sg_next(sgde);
+
+               }
+
+               if (protgroup_offset) {
+                       /* update the reference tag */
+                       reftag += protgrp_blks;
+                       bpl++;
+                       continue;
+               }
+
+               /* are we done ? */
+               if (curr_prot == protcnt) {
+                       alldone = 1;
+               } else if (curr_prot < protcnt) {
+                       /* advance to next prot buffer */
+                       sgpe = sg_next(sgpe);
+                       bpl++;
+
+                       /* update the reference tag */
+                       reftag += protgrp_blks;
+               } else {
+                       /* if we're here, we have a bug */
+                       lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                               "9054 BLKGRD: bug in %s\n", __func__);
+               }
+
+       } while (!alldone);
+out:
+
+       return num_bde;
+}
+
+/**
+ * lpfc_bg_setup_sgl - Setup BlockGuard SGL with no protection data
+ * @phba: The Hba for which this call is being executed.
+ * @sc: pointer to scsi command we're working on
+ * @sgl: pointer to buffer list for protection groups
+ * @datacnt: number of segments of data that have been dma mapped
+ *
+ * This function sets up SGL buffer list for protection groups of
+ * type LPFC_PG_TYPE_NO_DIF
+ *
+ * This is usually used when the HBA is instructed to generate
+ * DIFs and insert them into data stream (or strip DIF from
+ * incoming data stream)
+ *
+ * The buffer list consists of just one protection group described
+ * below:
+ *                                +-------------------------+
+ *   start of prot group  -->     |         DI_SEED         |
+ *                                +-------------------------+
+ *                                |         Data SGE        |
+ *                                +-------------------------+
+ *                                |more Data SGE's ... (opt)|
+ *                                +-------------------------+
+ *
+ *
+ * Note: Data s/g buffers have been dma mapped
+ *
+ * Returns the number of SGEs added to the SGL.
+ **/
+static int
+lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
+               struct sli4_sge *sgl, int datasegcnt)
+{
+       struct scatterlist *sgde = NULL; /* s/g data entry */
+       struct sli4_sge_diseed *diseed = NULL;
+       dma_addr_t physaddr;
+       int i = 0, num_sge = 0, status;
+       int datadir = sc->sc_data_direction;
+       uint32_t reftag;
+       unsigned blksize;
+       uint8_t txop, rxop;
+       uint32_t rc;
+       uint32_t checking = 1;
+       uint32_t dma_len;
+       uint32_t dma_offset = 0;
+
+       status  = lpfc_sc_to_bg_opcodes(phba, sc, &txop, &rxop);
+       if (status)
+               goto out;
+
+       /* extract some info from the scsi command for pde*/
+       blksize = lpfc_cmd_blksize(sc);
+       reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+       rc = lpfc_bg_err_inject(phba, sc, &reftag, 0, 1);
+       if (rc) {
+               if (rc == BG_ERR_SWAP)
+                       lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
+               if (rc == BG_ERR_CHECK)
+                       checking = 0;
+       }
+#endif
+
+       /* setup DISEED with what we have */
+       diseed = (struct sli4_sge_diseed *) sgl;
+       memset(diseed, 0, sizeof(struct sli4_sge_diseed));
+       bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DISEED);
+
+       /* Endianness conversion if necessary */
+       diseed->ref_tag = cpu_to_le32(reftag);
+       diseed->ref_tag_tran = diseed->ref_tag;
+
+       /* setup DISEED with the rest of the info */
+       bf_set(lpfc_sli4_sge_dif_optx, diseed, txop);
+       bf_set(lpfc_sli4_sge_dif_oprx, diseed, rxop);
+       if (datadir == DMA_FROM_DEVICE) {
+               bf_set(lpfc_sli4_sge_dif_ce, diseed, checking);
+               bf_set(lpfc_sli4_sge_dif_re, diseed, checking);
+       }
+       bf_set(lpfc_sli4_sge_dif_ai, diseed, 1);
+       bf_set(lpfc_sli4_sge_dif_me, diseed, 0);
+
+       /* Endianness conversion if necessary for DISEED */
+       diseed->word2 = cpu_to_le32(diseed->word2);
+       diseed->word3 = cpu_to_le32(diseed->word3);
+
+       /* advance bpl and increment sge count */
+       num_sge++;
+       sgl++;
+
+       /* assumption: caller has already run dma_map_sg on command data */
+       scsi_for_each_sg(sc, sgde, datasegcnt, i) {
+               physaddr = sg_dma_address(sgde);
+               dma_len = sg_dma_len(sgde);
+               sgl->addr_lo = cpu_to_le32(putPaddrLow(physaddr));
+               sgl->addr_hi = cpu_to_le32(putPaddrHigh(physaddr));
+               if ((i + 1) == datasegcnt)
+                       bf_set(lpfc_sli4_sge_last, sgl, 1);
+               else
+                       bf_set(lpfc_sli4_sge_last, sgl, 0);
+               bf_set(lpfc_sli4_sge_offset, sgl, dma_offset);
+               bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DATA);
+
+               sgl->sge_len = cpu_to_le32(dma_len);
+               dma_offset += dma_len;
+
+               sgl++;
+               num_sge++;
+       }
+
+out:
+       return num_sge;
+}
+
+/**
+ * lpfc_bg_setup_sgl_prot - Setup BlockGuard SGL with protection data
+ * @phba: The Hba for which this call is being executed.
+ * @sc: pointer to scsi command we're working on
+ * @sgl: pointer to buffer list for protection groups
+ * @datacnt: number of segments of data that have been dma mapped
+ * @protcnt: number of segment of protection data that have been dma mapped
+ *
+ * This function sets up SGL buffer list for protection groups of
+ * type LPFC_PG_TYPE_DIF
+ *
+ * This is usually used when DIFs are in their own buffers,
+ * separate from the data. The HBA can then by instructed
+ * to place the DIFs in the outgoing stream.  For read operations,
+ * The HBA could extract the DIFs and place it in DIF buffers.
+ *
+ * The buffer list for this type consists of one or more of the
+ * protection groups described below:
+ *                                    +-------------------------+
+ *   start of first prot group  -->   |         DISEED          |
+ *                                    +-------------------------+
+ *                                    |      DIF (Prot SGE)     |
+ *                                    +-------------------------+
+ *                                    |        Data SGE         |
+ *                                    +-------------------------+
+ *                                    |more Data SGE's ... (opt)|
+ *                                    +-------------------------+
+ *   start of new  prot group  -->    |         DISEED          |
+ *                                    +-------------------------+
+ *                                    |          ...            |
+ *                                    +-------------------------+
+ *
+ * Note: It is assumed that both data and protection s/g buffers have been
+ *       mapped for DMA
+ *
+ * Returns the number of SGEs added to the SGL.
+ **/
+static int
+lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
+               struct sli4_sge *sgl, int datacnt, int protcnt)
+{
+       struct scatterlist *sgde = NULL; /* s/g data entry */
+       struct scatterlist *sgpe = NULL; /* s/g prot entry */
+       struct sli4_sge_diseed *diseed = NULL;
+       dma_addr_t dataphysaddr, protphysaddr;
+       unsigned short curr_data = 0, curr_prot = 0;
+       unsigned int split_offset;
+       unsigned int protgroup_len, protgroup_offset = 0, protgroup_remainder;
+       unsigned int protgrp_blks, protgrp_bytes;
+       unsigned int remainder, subtotal;
+       int status;
+       unsigned char pgdone = 0, alldone = 0;
+       unsigned blksize;
+       uint32_t reftag;
+       uint8_t txop, rxop;
+       uint32_t dma_len;
+       uint32_t rc;
+       uint32_t checking = 1;
+       uint32_t dma_offset = 0;
+       int num_sge = 0;
+
+       sgpe = scsi_prot_sglist(sc);
+       sgde = scsi_sglist(sc);
+
+       if (!sgpe || !sgde) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+                               "9082 Invalid s/g entry: data=0x%p prot=0x%p\n",
                                sgpe, sgde);
                return 0;
        }
@@ -1701,48 +2309,44 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
 
        /* extract some info from the scsi command */
        blksize = lpfc_cmd_blksize(sc);
-       reftag = scsi_get_lba(sc) & 0xffffffff;
+       reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */
 
 #ifdef CONFIG_SCSI_LPFC_DEBUG_FS
-       /* reftag / guard tag are the only errors we can inject here */
-       lpfc_bg_err_inject(phba, sc, &reftag, 0, 0xDEAD);
+       rc = lpfc_bg_err_inject(phba, sc, &reftag, 0, 1);
+       if (rc) {
+               if (rc == BG_ERR_SWAP)
+                       lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
+               if (rc == BG_ERR_CHECK)
+                       checking = 0;
+       }
 #endif
 
        split_offset = 0;
        do {
-               /* setup PDE5 with what we have */
-               pde5 = (struct lpfc_pde5 *) bpl;
-               memset(pde5, 0, sizeof(struct lpfc_pde5));
-               bf_set(pde5_type, pde5, LPFC_PDE5_DESCRIPTOR);
-
-               /* Endianness conversion if necessary for PDE5 */
-               pde5->word0 = cpu_to_le32(pde5->word0);
-               pde5->reftag = cpu_to_le32(reftag);
-
-               /* advance bpl and increment bde count */
-               num_bde++;
-               bpl++;
-               pde6 = (struct lpfc_pde6 *) bpl;
-
-               /* setup PDE6 with the rest of the info */
-               memset(pde6, 0, sizeof(struct lpfc_pde6));
-               bf_set(pde6_type, pde6, LPFC_PDE6_DESCRIPTOR);
-               bf_set(pde6_optx, pde6, txop);
-               bf_set(pde6_oprx, pde6, rxop);
-               bf_set(pde6_ce, pde6, 1);
-               bf_set(pde6_re, pde6, 1);
-               bf_set(pde6_ai, pde6, 1);
-               bf_set(pde6_ae, pde6, 0);
-               bf_set(pde6_apptagval, pde6, 0);
-
-               /* Endianness conversion if necessary for PDE6 */
-               pde6->word0 = cpu_to_le32(pde6->word0);
-               pde6->word1 = cpu_to_le32(pde6->word1);
-               pde6->word2 = cpu_to_le32(pde6->word2);
-
-               /* advance bpl and increment bde count */
-               num_bde++;
-               bpl++;
+               /* setup DISEED with what we have */
+               diseed = (struct sli4_sge_diseed *) sgl;
+               memset(diseed, 0, sizeof(struct sli4_sge_diseed));
+               bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DISEED);
+
+               /* Endianness conversion if necessary */
+               diseed->ref_tag = cpu_to_le32(reftag);
+               diseed->ref_tag_tran = diseed->ref_tag;
+
+               /* setup DISEED with the rest of the info */
+               bf_set(lpfc_sli4_sge_dif_optx, diseed, txop);
+               bf_set(lpfc_sli4_sge_dif_oprx, diseed, rxop);
+               bf_set(lpfc_sli4_sge_dif_ce, diseed, checking);
+               bf_set(lpfc_sli4_sge_dif_re, diseed, checking);
+               bf_set(lpfc_sli4_sge_dif_ai, diseed, 1);
+               bf_set(lpfc_sli4_sge_dif_me, diseed, 0);
+
+               /* Endianness conversion if necessary for DISEED */
+               diseed->word2 = cpu_to_le32(diseed->word2);
+               diseed->word3 = cpu_to_le32(diseed->word3);
+
+               /* advance sgl and increment bde count */
+               num_sge++;
+               sgl++;
 
                /* setup the first BDE that points to protection buffer */
                protphysaddr = sg_dma_address(sgpe) + protgroup_offset;
@@ -1751,19 +2355,19 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                /* must be integer multiple of the DIF block length */
                BUG_ON(protgroup_len % 8);
 
-               pde7 = (struct lpfc_pde7 *) bpl;
-               memset(pde7, 0, sizeof(struct lpfc_pde7));
-               bf_set(pde7_type, pde7, LPFC_PDE7_DESCRIPTOR);
-
-               pde7->addrHigh = le32_to_cpu(putPaddrHigh(protphysaddr));
-               pde7->addrLow = le32_to_cpu(putPaddrLow(protphysaddr));
+               /* Now setup DIF SGE */
+               sgl->word2 = 0;
+               bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DIF);
+               sgl->addr_hi = le32_to_cpu(putPaddrHigh(protphysaddr));
+               sgl->addr_lo = le32_to_cpu(putPaddrLow(protphysaddr));
+               sgl->word2 = cpu_to_le32(sgl->word2);
 
                protgrp_blks = protgroup_len / 8;
                protgrp_bytes = protgrp_blks * blksize;
 
-               /* check if this pde is crossing the 4K boundary; if so split */
-               if ((pde7->addrLow & 0xfff) + protgroup_len > 0x1000) {
-                       protgroup_remainder = 0x1000 - (pde7->addrLow & 0xfff);
+               /* check if DIF SGE is crossing the 4K boundary; if so split */
+               if ((sgl->addr_lo & 0xfff) + protgroup_len > 0x1000) {
+                       protgroup_remainder = 0x1000 - (sgl->addr_lo & 0xfff);
                        protgroup_offset += protgroup_remainder;
                        protgrp_blks = protgroup_remainder / 8;
                        protgrp_bytes = protgrp_blks * blksize;
@@ -1772,47 +2376,48 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                        curr_prot++;
                }
 
-               num_bde++;
+               num_sge++;
 
-               /* setup BDE's for data blocks associated with DIF data */
+               /* setup SGE's for data blocks associated with DIF data */
                pgdone = 0;
                subtotal = 0; /* total bytes processed for current prot grp */
                while (!pgdone) {
                        if (!sgde) {
                                lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-                                       "9065 BLKGRD:%s Invalid data segment\n",
+                                       "9086 BLKGRD:%s Invalid data segment\n",
                                                __func__);
                                return 0;
                        }
-                       bpl++;
+                       sgl++;
                        dataphysaddr = sg_dma_address(sgde) + split_offset;
-                       bpl->addrLow = le32_to_cpu(putPaddrLow(dataphysaddr));
-                       bpl->addrHigh = le32_to_cpu(putPaddrHigh(dataphysaddr));
 
                        remainder = sg_dma_len(sgde) - split_offset;
 
                        if ((subtotal + remainder) <= protgrp_bytes) {
                                /* we can use this whole buffer */
-                               bpl->tus.f.bdeSize = remainder;
+                               dma_len = remainder;
                                split_offset = 0;
 
                                if ((subtotal + remainder) == protgrp_bytes)
                                        pgdone = 1;
                        } else {
                                /* must split this buffer with next prot grp */
-                               bpl->tus.f.bdeSize = protgrp_bytes - subtotal;
-                               split_offset += bpl->tus.f.bdeSize;
+                               dma_len = protgrp_bytes - subtotal;
+                               split_offset += dma_len;
                        }
 
-                       subtotal += bpl->tus.f.bdeSize;
+                       subtotal += dma_len;
 
-                       if (datadir == DMA_TO_DEVICE)
-                               bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
-                       else
-                               bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
-                       bpl->tus.w = le32_to_cpu(bpl->tus.w);
+                       sgl->addr_lo = cpu_to_le32(putPaddrLow(dataphysaddr));
+                       sgl->addr_hi = cpu_to_le32(putPaddrHigh(dataphysaddr));
+                       bf_set(lpfc_sli4_sge_last, sgl, 0);
+                       bf_set(lpfc_sli4_sge_offset, sgl, dma_offset);
+                       bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DATA);
 
-                       num_bde++;
+                       sgl->sge_len = cpu_to_le32(dma_len);
+                       dma_offset += dma_len;
+
+                       num_sge++;
                        curr_data++;
 
                        if (split_offset)
@@ -1820,45 +2425,50 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
 
                        /* Move to the next s/g segment if possible */
                        sgde = sg_next(sgde);
-
                }
 
                if (protgroup_offset) {
                        /* update the reference tag */
                        reftag += protgrp_blks;
-                       bpl++;
+                       sgl++;
                        continue;
                }
 
                /* are we done ? */
                if (curr_prot == protcnt) {
+                       bf_set(lpfc_sli4_sge_last, sgl, 1);
                        alldone = 1;
                } else if (curr_prot < protcnt) {
                        /* advance to next prot buffer */
                        sgpe = sg_next(sgpe);
-                       bpl++;
+                       sgl++;
 
                        /* update the reference tag */
                        reftag += protgrp_blks;
                } else {
                        /* if we're here, we have a bug */
                        lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-                               "9054 BLKGRD: bug in %s\n", __func__);
+                               "9085 BLKGRD: bug in %s\n", __func__);
                }
 
        } while (!alldone);
+
 out:
 
-       return num_bde;
+       return num_sge;
 }
 
-/*
+/**
+ * lpfc_prot_group_type - Get prtotection group type of SCSI command
+ * @phba: The Hba for which this call is being executed.
+ * @sc: pointer to scsi command we're working on
+ *
  * Given a SCSI command that supports DIF, determine composition of protection
  * groups involved in setting up buffer lists
  *
- * Returns:
- *                           for DIF (for both read and write)
- * */
+ * Returns: Protection group type (with or without DIF)
+ *
+ **/
 static int
 lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc)
 {
@@ -1885,13 +2495,17 @@ lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc)
        return ret;
 }
 
-/*
+/**
+ * lpfc_bg_scsi_prep_dma_buf_s3 - DMA mapping for scsi buffer to SLI3 IF spec
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be prep'ed.
+ *
  * This is the protection/DIF aware version of
  * lpfc_scsi_prep_dma_buf(). It may be a good idea to combine the
  * two functions eventually, but for now, it's here
- */
+ **/
 static int
-lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
+lpfc_bg_scsi_prep_dma_buf_s3(struct lpfc_hba *phba,
                struct lpfc_scsi_buf *lpfc_cmd)
 {
        struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
@@ -2147,7 +2761,21 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
                cmd->sense_buffer[8] = 0;     /* Information descriptor type */
                cmd->sense_buffer[9] = 0xa;   /* Additional descriptor length */
                cmd->sense_buffer[10] = 0x80; /* Validity bit */
-               bghm /= cmd->device->sector_size;
+
+               /* bghm is a "on the wire" FC frame based count */
+               switch (scsi_get_prot_op(cmd)) {
+               case SCSI_PROT_READ_INSERT:
+               case SCSI_PROT_WRITE_STRIP:
+                       bghm /= cmd->device->sector_size;
+                       break;
+               case SCSI_PROT_READ_STRIP:
+               case SCSI_PROT_WRITE_INSERT:
+               case SCSI_PROT_READ_PASS:
+               case SCSI_PROT_WRITE_PASS:
+                       bghm /= (cmd->device->sector_size +
+                               sizeof(struct scsi_dif_tuple));
+                       break;
+               }
 
                failing_sector = scsi_get_lba(cmd);
                failing_sector += bghm;
@@ -2291,6 +2919,180 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
        return 0;
 }
 
+/**
+ * lpfc_bg_scsi_adjust_dl - Adjust SCSI data length for BlockGuard
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be adjusted.
+ *
+ * Adjust the data length to account for how much data
+ * is actually on the wire.
+ *
+ * returns the adjusted data length
+ **/
+static int
+lpfc_bg_scsi_adjust_dl(struct lpfc_hba *phba,
+               struct lpfc_scsi_buf *lpfc_cmd)
+{
+       struct scsi_cmnd *sc = lpfc_cmd->pCmd;
+       int diflen, fcpdl;
+       unsigned blksize;
+
+       fcpdl = scsi_bufflen(sc);
+
+       /* Check if there is protection data on the wire */
+       if (sc->sc_data_direction == DMA_FROM_DEVICE) {
+               /* Read */
+               if (scsi_get_prot_op(sc) ==  SCSI_PROT_READ_INSERT)
+                       return fcpdl;
+
+       } else {
+               /* Write */
+               if (scsi_get_prot_op(sc) ==  SCSI_PROT_WRITE_STRIP)
+                       return fcpdl;
+       }
+
+       /* If protection data on the wire, adjust the count accordingly */
+       blksize = lpfc_cmd_blksize(sc);
+       diflen = (fcpdl / blksize) * 8;
+       fcpdl += diflen;
+       return fcpdl;
+}
+
+/**
+ * lpfc_bg_scsi_prep_dma_buf_s4 - DMA mapping for scsi buffer to SLI4 IF spec
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be mapped.
+ *
+ * This is the protection/DIF aware version of
+ * lpfc_scsi_prep_dma_buf(). It may be a good idea to combine the
+ * two functions eventually, but for now, it's here
+ **/
+static int
+lpfc_bg_scsi_prep_dma_buf_s4(struct lpfc_hba *phba,
+               struct lpfc_scsi_buf *lpfc_cmd)
+{
+       struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
+       struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
+       struct sli4_sge *sgl = (struct sli4_sge *)(lpfc_cmd->fcp_bpl);
+       IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
+       uint32_t num_bde = 0;
+       int datasegcnt, protsegcnt, datadir = scsi_cmnd->sc_data_direction;
+       int prot_group_type = 0;
+       int fcpdl;
+
+       /*
+        * Start the lpfc command prep by bumping the sgl beyond fcp_cmnd
+        *  fcp_rsp regions to the first data bde entry
+        */
+       if (scsi_sg_count(scsi_cmnd)) {
+               /*
+                * The driver stores the segment count returned from pci_map_sg
+                * because this a count of dma-mappings used to map the use_sg
+                * pages.  They are not guaranteed to be the same for those
+                * architectures that implement an IOMMU.
+                */
+               datasegcnt = dma_map_sg(&phba->pcidev->dev,
+                                       scsi_sglist(scsi_cmnd),
+                                       scsi_sg_count(scsi_cmnd), datadir);
+               if (unlikely(!datasegcnt))
+                       return 1;
+
+               sgl += 1;
+               /* clear the last flag in the fcp_rsp map entry */
+               sgl->word2 = le32_to_cpu(sgl->word2);
+               bf_set(lpfc_sli4_sge_last, sgl, 0);
+               sgl->word2 = cpu_to_le32(sgl->word2);
+
+               sgl += 1;
+               lpfc_cmd->seg_cnt = datasegcnt;
+               if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                                       "9087 BLKGRD: %s: Too many sg segments"
+                                       " from dma_map_sg.  Config %d, seg_cnt"
+                                       " %d\n",
+                                       __func__, phba->cfg_sg_seg_cnt,
+                                       lpfc_cmd->seg_cnt);
+                       scsi_dma_unmap(scsi_cmnd);
+                       return 1;
+               }
+
+               prot_group_type = lpfc_prot_group_type(phba, scsi_cmnd);
+
+               switch (prot_group_type) {
+               case LPFC_PG_TYPE_NO_DIF:
+                       num_bde = lpfc_bg_setup_sgl(phba, scsi_cmnd, sgl,
+                                       datasegcnt);
+                       /* we should have 2 or more entries in buffer list */
+                       if (num_bde < 2)
+                               goto err;
+                       break;
+               case LPFC_PG_TYPE_DIF_BUF:{
+                       /*
+                        * This type indicates that protection buffers are
+                        * passed to the driver, so that needs to be prepared
+                        * for DMA
+                        */
+                       protsegcnt = dma_map_sg(&phba->pcidev->dev,
+                                       scsi_prot_sglist(scsi_cmnd),
+                                       scsi_prot_sg_count(scsi_cmnd), datadir);
+                       if (unlikely(!protsegcnt)) {
+                               scsi_dma_unmap(scsi_cmnd);
+                               return 1;
+                       }
+
+                       lpfc_cmd->prot_seg_cnt = protsegcnt;
+                       if (lpfc_cmd->prot_seg_cnt
+                           > phba->cfg_prot_sg_seg_cnt) {
+                               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                                       "9088 BLKGRD: %s: Too many prot sg "
+                                       "segments from dma_map_sg.  Config %d,"
+                                               "prot_seg_cnt %d\n", __func__,
+                                               phba->cfg_prot_sg_seg_cnt,
+                                               lpfc_cmd->prot_seg_cnt);
+                               dma_unmap_sg(&phba->pcidev->dev,
+                                            scsi_prot_sglist(scsi_cmnd),
+                                            scsi_prot_sg_count(scsi_cmnd),
+                                            datadir);
+                               scsi_dma_unmap(scsi_cmnd);
+                               return 1;
+                       }
+
+                       num_bde = lpfc_bg_setup_sgl_prot(phba, scsi_cmnd, sgl,
+                                       datasegcnt, protsegcnt);
+                       /* we should have 3 or more entries in buffer list */
+                       if (num_bde < 3)
+                               goto err;
+                       break;
+               }
+               case LPFC_PG_TYPE_INVALID:
+               default:
+                       lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+                                       "9083 Unexpected protection group %i\n",
+                                       prot_group_type);
+                       return 1;
+               }
+       }
+
+       fcpdl = lpfc_bg_scsi_adjust_dl(phba, lpfc_cmd);
+
+       fcp_cmnd->fcpDl = be32_to_cpu(fcpdl);
+
+       /*
+        * Due to difference in data length between DIF/non-DIF paths,
+        * we need to set word 4 of IOCB here
+        */
+       iocb_cmd->un.fcpi.fcpi_parm = fcpdl;
+       lpfc_cmd->cur_iocbq.iocb_flag |= LPFC_IO_DIF;
+
+       return 0;
+err:
+       lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+                       "9084 Could not setup all needed BDE's"
+                       "prot_group_type=%d, num_bde=%d\n",
+                       prot_group_type, num_bde);
+       return 1;
+}
+
 /**
  * lpfc_scsi_prep_dma_buf - Wrapper function for DMA mapping of scsi buffer
  * @phba: The Hba for which this call is being executed.
@@ -2309,6 +3111,25 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
        return phba->lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
 }
 
+/**
+ * lpfc_bg_scsi_prep_dma_buf - Wrapper function for DMA mapping of scsi buffer
+ * using BlockGuard.
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be mapped.
+ *
+ * This routine wraps the actual DMA mapping function pointer from the
+ * lpfc_hba struct.
+ *
+ * Return codes:
+ *     1 - Error
+ *     0 - Success
+ **/
+static inline int
+lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
+{
+       return phba->lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd);
+}
+
 /**
  * lpfc_send_scsi_error_event - Posts an event when there is SCSI error
  * @phba: Pointer to hba context object.
@@ -3072,12 +3893,14 @@ lpfc_scsi_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
        case LPFC_PCI_DEV_LP:
                phba->lpfc_new_scsi_buf = lpfc_new_scsi_buf_s3;
                phba->lpfc_scsi_prep_dma_buf = lpfc_scsi_prep_dma_buf_s3;
+               phba->lpfc_bg_scsi_prep_dma_buf = lpfc_bg_scsi_prep_dma_buf_s3;
                phba->lpfc_release_scsi_buf = lpfc_release_scsi_buf_s3;
                phba->lpfc_get_scsi_buf = lpfc_get_scsi_buf_s3;
                break;
        case LPFC_PCI_DEV_OC:
                phba->lpfc_new_scsi_buf = lpfc_new_scsi_buf_s4;
                phba->lpfc_scsi_prep_dma_buf = lpfc_scsi_prep_dma_buf_s4;
+               phba->lpfc_bg_scsi_prep_dma_buf = lpfc_bg_scsi_prep_dma_buf_s4;
                phba->lpfc_release_scsi_buf = lpfc_release_scsi_buf_s4;
                phba->lpfc_get_scsi_buf = lpfc_get_scsi_buf_s4;
                break;
@@ -3250,8 +4073,7 @@ lpfc_queuecommand_lck(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
        ndlp = rdata->pnode;
 
        if ((scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) &&
-               (!(phba->sli3_options & LPFC_SLI3_BG_ENABLED) ||
-               (phba->sli_rev == LPFC_SLI_REV4))) {
+               (!(phba->sli3_options & LPFC_SLI3_BG_ENABLED))) {
 
                lpfc_printf_log(phba, KERN_ERR, LOG_BG,
                                "9058 BLKGRD: ERROR: rcvd protected cmd:%02x"
index 1a391e2df3b3d46a8ee5976ff66e089df2cf72e6..73da1c039b9d5cb05790d16a3be80c3014d493ba 100644 (file)
@@ -7839,12 +7839,16 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
                bf_set(wqe_lnk, &wqe->fcp_iwrite.wqe_com, iocbq->iocb.ulpXS);
                /* Always open the exchange */
                bf_set(wqe_xc, &wqe->fcp_iwrite.wqe_com, 0);
-               bf_set(wqe_dbde, &wqe->fcp_iwrite.wqe_com, 1);
                bf_set(wqe_iod, &wqe->fcp_iwrite.wqe_com, LPFC_WQE_IOD_WRITE);
                bf_set(wqe_lenloc, &wqe->fcp_iwrite.wqe_com,
                       LPFC_WQE_LENLOC_WORD4);
                bf_set(wqe_ebde_cnt, &wqe->fcp_iwrite.wqe_com, 0);
                bf_set(wqe_pu, &wqe->fcp_iwrite.wqe_com, iocbq->iocb.ulpPU);
+               if (iocbq->iocb_flag & LPFC_IO_DIF) {
+                       iocbq->iocb_flag &= ~LPFC_IO_DIF;
+                       bf_set(wqe_dif, &wqe->generic.wqe_com, 1);
+               }
+               bf_set(wqe_dbde, &wqe->fcp_iwrite.wqe_com, 1);
                break;
        case CMD_FCP_IREAD64_CR:
                /* word3 iocb=iotag wqe=payload_offset_len */
@@ -7858,12 +7862,16 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
                bf_set(wqe_lnk, &wqe->fcp_iread.wqe_com, iocbq->iocb.ulpXS);
                /* Always open the exchange */
                bf_set(wqe_xc, &wqe->fcp_iread.wqe_com, 0);
-               bf_set(wqe_dbde, &wqe->fcp_iread.wqe_com, 1);
                bf_set(wqe_iod, &wqe->fcp_iread.wqe_com, LPFC_WQE_IOD_READ);
                bf_set(wqe_lenloc, &wqe->fcp_iread.wqe_com,
                       LPFC_WQE_LENLOC_WORD4);
                bf_set(wqe_ebde_cnt, &wqe->fcp_iread.wqe_com, 0);
                bf_set(wqe_pu, &wqe->fcp_iread.wqe_com, iocbq->iocb.ulpPU);
+               if (iocbq->iocb_flag & LPFC_IO_DIF) {
+                       iocbq->iocb_flag &= ~LPFC_IO_DIF;
+                       bf_set(wqe_dif, &wqe->generic.wqe_com, 1);
+               }
+               bf_set(wqe_dbde, &wqe->fcp_iread.wqe_com, 1);
                break;
        case CMD_FCP_ICMND64_CR:
                /* word3 iocb=IO_TAG wqe=reserved */
@@ -10672,12 +10680,14 @@ lpfc_sli4_iocb_param_transfer(struct lpfc_hba *phba,
                              struct lpfc_wcqe_complete *wcqe)
 {
        unsigned long iflags;
+       uint32_t status;
        size_t offset = offsetof(struct lpfc_iocbq, iocb);
 
        memcpy((char *)pIocbIn + offset, (char *)pIocbOut + offset,
               sizeof(struct lpfc_iocbq) - offset);
        /* Map WCQE parameters into irspiocb parameters */
-       pIocbIn->iocb.ulpStatus = bf_get(lpfc_wcqe_c_status, wcqe);
+       status = bf_get(lpfc_wcqe_c_status, wcqe);
+       pIocbIn->iocb.ulpStatus = (status & LPFC_IOCB_STATUS_MASK);
        if (pIocbOut->iocb_flag & LPFC_IO_FCP)
                if (pIocbIn->iocb.ulpStatus == IOSTAT_FCP_RSP_ERROR)
                        pIocbIn->iocb.un.fcpi.fcpi_parm =
@@ -10690,6 +10700,44 @@ lpfc_sli4_iocb_param_transfer(struct lpfc_hba *phba,
                pIocbIn->iocb.un.genreq64.bdl.bdeSize = wcqe->total_data_placed;
        }
 
+       /* Convert BG errors for completion status */
+       if (status == CQE_STATUS_DI_ERROR) {
+               pIocbIn->iocb.ulpStatus = IOSTAT_LOCAL_REJECT;
+
+               if (bf_get(lpfc_wcqe_c_bg_edir, wcqe))
+                       pIocbIn->iocb.un.ulpWord[4] = IOERR_RX_DMA_FAILED;
+               else
+                       pIocbIn->iocb.un.ulpWord[4] = IOERR_TX_DMA_FAILED;
+
+               pIocbIn->iocb.unsli3.sli3_bg.bgstat = 0;
+               if (bf_get(lpfc_wcqe_c_bg_ge, wcqe)) /* Guard Check failed */
+                       pIocbIn->iocb.unsli3.sli3_bg.bgstat |=
+                               BGS_GUARD_ERR_MASK;
+               if (bf_get(lpfc_wcqe_c_bg_ae, wcqe)) /* App Tag Check failed */
+                       pIocbIn->iocb.unsli3.sli3_bg.bgstat |=
+                               BGS_APPTAG_ERR_MASK;
+               if (bf_get(lpfc_wcqe_c_bg_re, wcqe)) /* Ref Tag Check failed */
+                       pIocbIn->iocb.unsli3.sli3_bg.bgstat |=
+                               BGS_REFTAG_ERR_MASK;
+
+               /* Check to see if there was any good data before the error */
+               if (bf_get(lpfc_wcqe_c_bg_tdpv, wcqe)) {
+                       pIocbIn->iocb.unsli3.sli3_bg.bgstat |=
+                               BGS_HI_WATER_MARK_PRESENT_MASK;
+                       pIocbIn->iocb.unsli3.sli3_bg.bghm =
+                               wcqe->total_data_placed;
+               }
+
+               /*
+               * Set ALL the error bits to indicate we don't know what
+               * type of error it is.
+               */
+               if (!pIocbIn->iocb.unsli3.sli3_bg.bgstat)
+                       pIocbIn->iocb.unsli3.sli3_bg.bgstat |=
+                               (BGS_REFTAG_ERR_MASK | BGS_APPTAG_ERR_MASK |
+                               BGS_GUARD_ERR_MASK);
+       }
+
        /* Pick up HBA exchange busy condition */
        if (bf_get(lpfc_wcqe_c_xb, wcqe)) {
                spin_lock_irqsave(&phba->hbalock, iflags);
index 29c13b63e323563bf30009ed4205419de3a77775..3290b8e7ab655ef2bb9bb0d99dc39492832d0b20 100644 (file)
@@ -69,6 +69,7 @@ struct lpfc_iocbq {
 #define LPFC_USE_FCPWQIDX      0x80    /* Submit to specified FCPWQ index */
 #define DSS_SECURITY_OP                0x100   /* security IO */
 #define LPFC_IO_ON_Q           0x200   /* The IO is still on the TXCMPLQ */
+#define LPFC_IO_DIF            0x400   /* T10 DIF IO */
 
 #define LPFC_FIP_ELS_ID_MASK   0xc000  /* ELS_ID range 0-3, non-shifted mask */
 #define LPFC_FIP_ELS_ID_SHIFT  14