diff options
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_scsi.c')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_scsi.c | 245 |
1 files changed, 214 insertions, 31 deletions
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index eadd241eeff1..2e1e54e5c3ae 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -21,6 +21,7 @@ #include <linux/pci.h> #include <linux/slab.h> #include <linux/interrupt.h> +#include <linux/export.h> #include <linux/delay.h> #include <asm/unaligned.h> @@ -58,6 +59,13 @@ static char *dif_op_str[] = { "SCSI_PROT_READ_PASS", "SCSI_PROT_WRITE_PASS", }; + +struct scsi_dif_tuple { + __be16 guard_tag; /* Checksum */ + __be16 app_tag; /* Opaque storage */ + __be32 ref_tag; /* Target LBA or indirect LBA */ +}; + static void lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb); static void @@ -1263,6 +1271,174 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) return 0; } +static inline unsigned +lpfc_cmd_blksize(struct scsi_cmnd *sc) +{ + return sc->device->sector_size; +} + +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS +/* + * Given a scsi cmnd, determine the BlockGuard tags to be used with it + * @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 + */ +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; + uint32_t op = scsi_get_prot_op(sc); + uint32_t blksize; + uint32_t numblks; + sector_t lba; + int rc = 0; + + if (op == SCSI_PROT_NORMAL) + return 0; + + lba = scsi_get_lba(sc); + if (phba->lpfc_injerr_lba != LPFC_INJERR_LBA_OFF) { + blksize = lpfc_cmd_blksize(sc); + numblks = (scsi_bufflen(sc) + blksize - 1) / blksize; + + /* Make sure we have the right LBA if one is specified */ + if ((phba->lpfc_injerr_lba < lba) || + (phba->lpfc_injerr_lba >= (lba + numblks))) + return 0; + } + + 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) { + + /* 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; + + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9081 BLKGRD: Injecting reftag error: " + "write lba x%lx\n", (unsigned long)lba); + } + } else { + if (phba->lpfc_injerr_rref_cnt) { + *reftag = 0xDEADBEEF; + phba->lpfc_injerr_rref_cnt--; + phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF; + rc = 1; + + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9076 BLKGRD: Injecting reftag error: " + "read lba x%lx\n", (unsigned long)lba); + } + } + } + + /* 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) { + + /* 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; + + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9077 BLKGRD: Injecting apptag error: " + "write lba x%lx\n", (unsigned long)lba); + } + } else { + if (phba->lpfc_injerr_rapp_cnt) { + *apptag = 0xDEAD; + phba->lpfc_injerr_rapp_cnt--; + phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF; + rc = 1; + + lpfc_printf_log(phba, KERN_ERR, LOG_BG, + "9078 BLKGRD: Injecting apptag error: " + "read lba x%lx\n", (unsigned long)lba); + } + } + } + + /* Should we change the 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); + + src->guard_tag = (uint16_t)new_guard; + phba->lpfc_injerr_wgrd_cnt--; + phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF; + rc = 1; + + } 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, + "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; + } + } + return rc; +} +#endif + /* * Given a scsi cmnd, determine the BlockGuard opcodes to be used with it * @sc: The SCSI command to examine @@ -1341,18 +1517,6 @@ lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc, return ret; } -struct scsi_dif_tuple { - __be16 guard_tag; /* Checksum */ - __be16 app_tag; /* Opaque storage */ - __be32 ref_tag; /* Target LBA or indirect LBA */ -}; - -static inline unsigned -lpfc_cmd_blksize(struct scsi_cmnd *sc) -{ - return sc->device->sector_size; -} - /* * This function sets up buffer list for protection groups of * type LPFC_PG_TYPE_NO_DIF @@ -1401,6 +1565,11 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc, blksize = lpfc_cmd_blksize(sc); reftag = scsi_get_lba(sc) & 0xffffffff; +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + /* reftag is the only error we can inject here */ + lpfc_bg_err_inject(phba, sc, &reftag, 0, 0); +#endif + /* setup PDE5 with what we have */ pde5 = (struct lpfc_pde5 *) bpl; memset(pde5, 0, sizeof(struct lpfc_pde5)); @@ -1532,6 +1701,11 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, blksize = lpfc_cmd_blksize(sc); reftag = scsi_get_lba(sc) & 0xffffffff; +#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); +#endif + split_offset = 0; do { /* setup PDE5 with what we have */ @@ -1671,7 +1845,6 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, } } while (!alldone); - out: return num_bde; @@ -2075,6 +2248,7 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) 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->word2 = cpu_to_le32(sgl->word2); sgl->sge_len = cpu_to_le32(dma_len); dma_offset += dma_len; @@ -2325,8 +2499,9 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, } lp = (uint32_t *)cmnd->sense_buffer; - if (!scsi_status && (resp_info & RESID_UNDER)) - logit = LOG_FCP; + if (!scsi_status && (resp_info & RESID_UNDER) && + vport->cfg_log_verbose & LOG_FCP_UNDER) + logit = LOG_FCP_UNDER; lpfc_printf_vlog(vport, KERN_WARNING, logit, "9024 FCP command x%x failed: x%x SNS x%x x%x " @@ -2342,7 +2517,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, if (resp_info & RESID_UNDER) { scsi_set_resid(cmnd, be32_to_cpu(fcprsp->rspResId)); - lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, + lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP_UNDER, "9025 FCP Read Underrun, expected %d, " "residual %d Data: x%x x%x x%x\n", be32_to_cpu(fcpcmd->fcpDl), @@ -2449,6 +2624,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, struct lpfc_fast_path_event *fast_path_evt; struct Scsi_Host *shost; uint32_t queue_depth, scsi_id; + uint32_t logit = LOG_FCP; /* Sanity check on return of outstanding command */ if (!(lpfc_cmd->pCmd)) @@ -2470,16 +2646,22 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, lpfc_cmd->status = IOSTAT_DRIVER_REJECT; else if (lpfc_cmd->status >= IOSTAT_CNT) lpfc_cmd->status = IOSTAT_DEFAULT; - - lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, - "9030 FCP cmd x%x failed <%d/%d> " - "status: x%x result: x%x Data: x%x x%x\n", - cmd->cmnd[0], - cmd->device ? cmd->device->id : 0xffff, - cmd->device ? cmd->device->lun : 0xffff, - lpfc_cmd->status, lpfc_cmd->result, - pIocbOut->iocb.ulpContext, - lpfc_cmd->cur_iocbq.iocb.ulpIoTag); + if (lpfc_cmd->status == IOSTAT_FCP_RSP_ERROR + && !lpfc_cmd->fcp_rsp->rspStatus3 + && (lpfc_cmd->fcp_rsp->rspStatus2 & RESID_UNDER) + && !(phba->cfg_log_verbose & LOG_FCP_UNDER)) + logit = 0; + else + logit = LOG_FCP | LOG_FCP_UNDER; + lpfc_printf_vlog(vport, KERN_WARNING, logit, + "9030 FCP cmd x%x failed <%d/%d> " + "status: x%x result: x%x Data: x%x x%x\n", + cmd->cmnd[0], + cmd->device ? cmd->device->id : 0xffff, + cmd->device ? cmd->device->lun : 0xffff, + lpfc_cmd->status, lpfc_cmd->result, + pIocbOut->iocb.ulpContext, + lpfc_cmd->cur_iocbq.iocb.ulpIoTag); switch (lpfc_cmd->status) { case IOSTAT_FCP_RSP_ERROR: @@ -3056,8 +3238,9 @@ lpfc_queuecommand_lck(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) } ndlp = rdata->pnode; - if (!(phba->sli3_options & LPFC_SLI3_BG_ENABLED) && - scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) { + if ((scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) && + (!(phba->sli3_options & LPFC_SLI3_BG_ENABLED) || + (phba->sli_rev == LPFC_SLI_REV4))) { lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9058 BLKGRD: ERROR: rcvd protected cmd:%02x" @@ -3691,9 +3874,9 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd) fc_host_post_vendor_event(shost, fc_get_event_number(), sizeof(scsi_event), (char *)&scsi_event, LPFC_NL_VENDOR_ID); - ret = fc_block_scsi_eh(cmnd); - if (ret) - return ret; + status = fc_block_scsi_eh(cmnd); + if (status) + return status; /* * Since the driver manages a single bus device, reset all |