|
@@ -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"
|