diff options
author | James Smart <james.smart@emulex.com> | 2012-01-18 16:25:09 -0500 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-02-19 08:08:52 -0600 |
commit | acd6859b084d1e1b3ec8bc9befe6532223260d33 (patch) | |
tree | e06983f9b080226622fa06c071f3223caac4c7fd /drivers/scsi/lpfc | |
parent | 6b5151fd7baec6812fece993ddd7a2cf9fd0125f (diff) |
[SCSI] lpfc 8.3.29: T10 Diff fixes and enhancements
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>
Diffstat (limited to 'drivers/scsi/lpfc')
-rw-r--r-- | drivers/scsi/lpfc/lpfc.h | 13 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_debugfs.c | 19 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_hw4.h | 23 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_init.c | 10 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_scsi.c | 1054 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_sli.c | 54 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_sli.h | 1 |
7 files changed, 1049 insertions, 125 deletions
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 825f9307417..5fc044ff656 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -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; diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 3587a3fe8fc..22e17be04d8 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -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; diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index cc19bc1b5ac..9e2b9b227e1 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -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 diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index f3ad7349f5d..d9628770f11 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -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) diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index c60f5d0b386..efc055b6bac 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -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; @@ -1701,11 +1961,16 @@ 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; @@ -1729,8 +1994,8 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, 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_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); @@ -1852,13 +2117,358 @@ 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; + } + + 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 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; + protgroup_len = sg_dma_len(sgpe) - protgroup_offset; + + /* must be integer multiple of the DIF block length */ + BUG_ON(protgroup_len % 8); + + /* 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 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; + } else { + protgroup_offset = 0; + curr_prot++; + } + + num_sge++; + + /* 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, + "9086 BLKGRD:%s Invalid data segment\n", + __func__); + return 0; + } + sgl++; + dataphysaddr = sg_dma_address(sgde) + split_offset; + + remainder = sg_dma_len(sgde) - split_offset; + + if ((subtotal + remainder) <= protgrp_bytes) { + /* we can use this whole buffer */ + dma_len = remainder; + split_offset = 0; + + if ((subtotal + remainder) == protgrp_bytes) + pgdone = 1; + } else { + /* must split this buffer with next prot grp */ + dma_len = protgrp_bytes - subtotal; + split_offset += dma_len; + } + + subtotal += dma_len; + + 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); + + sgl->sge_len = cpu_to_le32(dma_len); + dma_offset += dma_len; + + num_sge++; + 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; + 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); + 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, + "9085 BLKGRD: bug in %s\n", __func__); + } + + } while (!alldone); + +out: + + 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; @@ -2292,6 +2920,180 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) } /** + * 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. * @lpfc_cmd: The scsi buffer which is going to be mapped. @@ -2310,6 +3112,25 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *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. * @vport: Pointer to vport 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" diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 1a391e2df3b..73da1c039b9 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -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); diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h index 29c13b63e32..3290b8e7ab6 100644 --- a/drivers/scsi/lpfc/lpfc_sli.h +++ b/drivers/scsi/lpfc/lpfc_sli.h @@ -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 |