From: James Smart <james.smart@emulex.com>
To: linux-scsi@vger.kernel.org
Subject: [PATCH 15/21] lpfc: Implement support for wire-only DIF devices
Date: Fri, 3 Apr 2015 17:13:00 -0400 [thread overview]
Message-ID: <1428095580.6933.44.camel@myfc17> (raw)
Implement support for wire-only DIF devices
This patch adds the ability to support auto-enablement of wire-only DIF
(DIF is generated on TX to target, stripped on RX; OS unaware DIF in use)
for a select set of devices. Currently, there is only 1 device vendor
supported: 3PARdata. When the feature is enabled, Inquiry commands are
trapped, the vendor matched, and DIF enablement checked. In 3Par's case,
there's a vendor specific check to see if the LUN supports DIF.
If supported, DIF will be enabled on a per-lun basis. The driver will
trap READS/WRITEs from the OS, check for LUN DIF enablement, and if set,
turns on write-only DIF.
Signed-off-by: Dick Kennedy <dick.kennedy@emulex.com>
Signed-off-by: James Smart <james.smart@emulex.com>
---
drivers/scsi/lpfc/lpfc.h | 41 ++++
drivers/scsi/lpfc/lpfc_attr.c | 65 ++++++-
drivers/scsi/lpfc/lpfc_crtn.h | 3 +-
drivers/scsi/lpfc/lpfc_hbadisc.c | 3 +
drivers/scsi/lpfc/lpfc_init.c | 24 ++-
drivers/scsi/lpfc/lpfc_mbox.c | 4 +-
drivers/scsi/lpfc/lpfc_scsi.c | 410 ++++++++++++++++++++++++++++++++++++---
drivers/scsi/lpfc/lpfc_scsi.h | 7 +-
drivers/scsi/lpfc/lpfc_sli.c | 36 ++--
9 files changed, 532 insertions(+), 61 deletions(-)
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 922e59d..e192c2d 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -295,6 +295,42 @@ enum hba_state {
LPFC_HBA_ERROR = -1
};
+/* Structure used to identify all devices controlled
+ * by the External DIF logic.
+ */
+struct lpfc_external_dif_support {
+ struct list_head listentry;
+ struct lpfc_name portName;
+ uint64_t lun;
+ uint16_t sid;
+ uint8_t dif_info;
+ uint8_t reserved1;
+};
+
+/* Struct used to identify External DIF vendor specific info */
+struct lpfc_vendor_dif {
+ uint8_t length;
+ uint8_t version;
+ uint8_t dif_info;
+#define LPFC_FDIF_ATO 0x01
+#define LPFC_FDIF_REFCHK 0x02
+#define LPFC_FDIF_APPCHK 0x04
+#define LPFC_FDIF_GRDCHK 0x08
+#define LPFC_FDIF_SPTMASK 0x70
+#define LPFC_FDIF_PROTECT 0x80
+ uint8_t reserved1;
+};
+
+/* Defines used to identify a External DIF device */
+#define LPFC_INQ_VID_OFFSET 8
+#define LPFC_INQ_VDIF_OFFSET 168
+#define LPFC_INQ_FDIF_SZ (LPFC_INQ_VDIF_OFFSET + 4)
+#define LPFC_INQ_FDIF_VENDOR "3PARdata" /* Vendor Identification */
+#define LPFC_INQ_FDIF_VERSION 1
+#define LPFC_INQ_FDIF_SIZE 2
+#define LPFC_FDIF_CDB_PROTECT 0x20 /* Set RD/WR PROTECT = 001 */
+
+
struct lpfc_vport {
struct lpfc_hba *phba;
struct list_head listentry;
@@ -441,6 +477,10 @@ struct lpfc_vport {
unsigned long rcv_buffer_time_stamp;
uint32_t vport_flag;
#define STATIC_VPORT 1
+
+ /* Used to discover External DIF devices */
+ struct list_head external_dif_list;
+ spinlock_t external_dif_lock; /* lock for external_dif_list */
};
struct hbq_s {
@@ -739,6 +779,7 @@ struct lpfc_hba {
#define OAS_LUN_VALID 0x04
uint32_t cfg_XLanePriority;
uint32_t cfg_enable_bg;
+ uint32_t cfg_external_dif;
uint32_t cfg_hostmem_hgp;
uint32_t cfg_log_verbose;
uint32_t cfg_aer_support;
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index faf0e8c..891e2d1 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -135,16 +135,29 @@ lpfc_bg_info_show(struct device *dev, struct device_attribute *attr,
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
+ int len = 0;
- if (phba->cfg_enable_bg)
+ if (phba->cfg_enable_bg) {
if (phba->sli3_options & LPFC_SLI3_BG_ENABLED)
- return snprintf(buf, PAGE_SIZE, "BlockGuard Enabled\n");
+ len += snprintf(buf, PAGE_SIZE,
+ "BlockGuard Enabled\n");
else
- return snprintf(buf, PAGE_SIZE,
+ len += snprintf(buf, PAGE_SIZE,
"BlockGuard Not Supported\n");
- else
- return snprintf(buf, PAGE_SIZE,
+ } else {
+ len += snprintf(buf, PAGE_SIZE,
"BlockGuard Disabled\n");
+ }
+
+ if (phba->cfg_external_dif) {
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED)
+ len += snprintf(buf + len, PAGE_SIZE,
+ "External DIF Enabled\n");
+ else
+ len += snprintf(buf + len, PAGE_SIZE,
+ "External DIF Not Supported\n");
+ }
+ return len;
}
static ssize_t
@@ -4681,6 +4694,30 @@ LPFC_ATTR_R(EnableXLane, 0, 0, 1, "Enable Express Lane Feature.");
*/
LPFC_ATTR_RW(XLanePriority, 0, 0x0, 0x7f, "CS_CTL for Express Lane Feature.");
+
+/*
+ * For T10 DIF / protection data support, the driver supports 4 modes
+ * of operation.
+ *
+ * Mode 1: (lpfc_enable_bg=1 lpfc_external_dif=1)
+ * All normal T10 DIF devices are supported.
+ * External DIF devices are supported.
+ *
+ * Mode 2: (lpfc_enable_bg=0 lpfc_external_dif=1)
+ * If you don't want to have the extra overhead of the upper SCSI Layer
+ * supporting T10-DIF, but you still want to support External DIF devices.
+ * Normal T10 DIF devices are NOT supported.
+ * External DIF devices are supported.
+ *
+ * Mode 3: (lpfc_enable_bg=1 lpfc_external_dif=0)
+ * All normal T10 DIF devices are supported.
+ * External DIF devices are NOT supported.
+ *
+ * Mode 4: (lpfc_enable_bg=0 lpfc_external_dif=1)
+ * No normal T10-DIF and no external DIF devices supported,
+ * This would be the driver default values for these module parameters.
+ */
+
/*
# lpfc_enable_bg: Enable BlockGuard (Emulex's Implementation of T10-DIF)
# 0 = BlockGuard disabled (default)
@@ -4690,6 +4727,15 @@ LPFC_ATTR_RW(XLanePriority, 0, 0x0, 0x7f, "CS_CTL for Express Lane Feature.");
LPFC_ATTR_R(enable_bg, 0, 0, 1, "Enable BlockGuard Support");
/*
+# lpfc_external_dif: Enable External DIF support on select devices
+# 0 = External DIF disabled (default)
+# 1 = External DIF enabled
+# Value range is [0,1]. Default value is 0.
+*/
+LPFC_ATTR_R(external_dif, 0, 0, 1,
+ "External T10-DIF Support, on select devices");
+
+/*
# lpfc_fcp_look_ahead: Look ahead for completions in FCP start routine
# 0 = disabled (default)
# 1 = enabled
@@ -4703,7 +4749,7 @@ unsigned int lpfc_fcp_look_ahead = LPFC_LOOK_AHEAD_OFF;
# lpfc_prot_mask: i
# - Bit mask of host protection capabilities used to register with the
# SCSI mid-layer
-# - Only meaningful if BG is turned on (lpfc_enable_bg=1).
+# - Only meaningful if BG is turned on, lpfc_enable_bg = 1
# - Allows you to ultimately specify which profiles to use
# - Default will result in registering capabilities for all profiles.
# - SHOST_DIF_TYPE1_PROTECTION 1
@@ -4726,6 +4772,7 @@ MODULE_PARM_DESC(lpfc_prot_mask, "host protection mask");
# - Bit mask of protection guard types to register with the SCSI mid-layer
# - Guard types are currently either 1) T10-DIF CRC 2) IP checksum
# - Allows you to ultimately specify which profiles to use
+# - Only meaningful if BG is turned on, lpfc_enable_bg = 1
# - Default will result in registering capabilities for all guard types
#
*/
@@ -4837,6 +4884,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_fcp_cpu_map,
&dev_attr_lpfc_fcp_io_channel,
&dev_attr_lpfc_enable_bg,
+ &dev_attr_lpfc_external_dif,
&dev_attr_lpfc_soft_wwnn,
&dev_attr_lpfc_soft_wwpn,
&dev_attr_lpfc_soft_wwn_enable,
@@ -5839,6 +5887,11 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
phba->cfg_oas_lun_status = 0;
phba->cfg_oas_flags = 0;
lpfc_enable_bg_init(phba, lpfc_enable_bg);
+ lpfc_external_dif_init(phba, lpfc_external_dif);
+
+ if (phba->cfg_enable_bg || phba->cfg_external_dif)
+ phba->sli3_options |= LPFC_SLI3_BG_ENABLED;
+
if (phba->sli_rev == LPFC_SLI_REV4)
phba->cfg_poll = 0;
else
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index dd01ea8..a7b4fc7 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -366,7 +366,8 @@ extern int lpfc_delay_discovery;
int lpfc_vport_symbolic_node_name(struct lpfc_vport *, char *, size_t);
int lpfc_vport_symbolic_port_name(struct lpfc_vport *, char *, size_t);
void lpfc_terminate_rport_io(struct fc_rport *);
-void lpfc_dev_loss_tmo_callbk(struct fc_rport *rport);
+void lpfc_dev_loss_tmo_callbk(struct fc_rport *);
+void lpfc_external_dif_cleanup(struct lpfc_vport *, struct lpfc_name *);
struct lpfc_vport *lpfc_create_port(struct lpfc_hba *, int, struct device *);
int lpfc_vport_disable(struct fc_vport *fc_vport, bool disable);
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index 2a51df7..17da14e 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -143,6 +143,9 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
return;
}
+ /* Cleanup all External DIF devices that match this rports WWPN */
+ lpfc_external_dif_cleanup(vport, &ndlp->nlp_portname);
+
if (ndlp->nlp_state == NLP_STE_MAPPED_NODE)
return;
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 166b2c7..3d154518 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -154,8 +154,6 @@ lpfc_config_port_prep(struct lpfc_hba *phba)
sizeof(phba->wwpn));
}
- phba->sli3_options = 0x0;
-
/* Setup and issue mailbox READ REV command */
lpfc_read_rev(phba, pmb);
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
@@ -2523,6 +2521,7 @@ lpfc_cleanup(struct lpfc_vport *vport)
{
struct lpfc_hba *phba = vport->phba;
struct lpfc_nodelist *ndlp, *next_ndlp;
+ struct lpfc_external_dif_support *dp, *next_dp;
int i = 0;
if (phba->link_state > LPFC_LINK_DOWN)
@@ -2600,6 +2599,15 @@ lpfc_cleanup(struct lpfc_vport *vport)
msleep(10);
}
lpfc_cleanup_vports_rrqs(vport, NULL);
+
+ /* Cleanup any discovered External DIF devices for this vport */
+ list_for_each_entry_safe(dp, next_dp, &vport->external_dif_list,
+ listentry) {
+ spin_lock_irq(&vport->external_dif_lock);
+ list_del(&dp->listentry);
+ spin_unlock_irq(&vport->external_dif_lock);
+ kfree(dp);
+ }
}
/**
@@ -3348,6 +3356,10 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
if (error)
goto out_put_shost;
+ /* Initialize objects used to discover External DIF devices */
+ INIT_LIST_HEAD(&vport->external_dif_list);
+ spin_lock_init(&vport->external_dif_lock);
+
spin_lock_irq(&phba->hbalock);
list_add_tail(&vport->listentry, &phba->port_list);
spin_unlock_irq(&phba->hbalock);
@@ -4991,7 +5003,7 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba)
lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt;
/* There are going to be 2 reserved BDEs: 1 FCP cmnd + 1 FCP rsp */
- if (phba->cfg_enable_bg) {
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
/*
* The scsi_buf for a T10-DIF I/O will hold the FCP cmnd,
* the FCP rsp, and a BDE for each. Sice we have no control
@@ -5184,7 +5196,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
* used to create the sg_dma_buf_pool must be dynamically calculated.
*/
- if (phba->cfg_enable_bg) {
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
/*
* The scsi_buf for a T10-DIF I/O will hold the FCP cmnd,
* the FCP rsp, and a SGE for each. Sice we have no control
@@ -6285,7 +6297,9 @@ lpfc_post_init_setup(struct lpfc_hba *phba)
*/
shost = pci_get_drvdata(phba->pcidev);
shost->can_queue = phba->cfg_hba_queue_depth - 10;
- if (phba->sli3_options & LPFC_SLI3_BG_ENABLED)
+
+ /* Setup T10-DIF interface with SCSI Layer API */
+ if (phba->cfg_enable_bg)
lpfc_setup_bg(phba, shost);
lpfc_host_attrib_init(shost);
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index 06241f5..702283e 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -1280,7 +1280,7 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
/* If HBA supports SLI=3 ask for it */
if (phba->sli_rev == LPFC_SLI_REV3 && phba->vpd.sli3Feat.cerbm) {
- if (phba->cfg_enable_bg)
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED)
mb->un.varCfgPort.cbg = 1; /* configure BlockGuard */
if (phba->cfg_enable_dss)
mb->un.varCfgPort.cdss = 1; /* Configure Security */
@@ -2071,7 +2071,7 @@ lpfc_request_features(struct lpfc_hba *phba, struct lpfcMboxq *mboxq)
bf_set(lpfc_mbx_rq_ftr_rq_perfh, &mboxq->u.mqe.un.req_ftrs, 1);
/* Enable DIF (block guard) only if configured to do so. */
- if (phba->cfg_enable_bg)
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED)
bf_set(lpfc_mbx_rq_ftr_rq_dif, &mboxq->u.mqe.un.req_ftrs, 1);
/* Enable NPIV only if configured to do so. */
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index 5612ba6..9ed5f44 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -80,6 +80,9 @@ lpfc_rport_data_from_scsi_device(struct scsi_device *sdev)
}
static void
+lpfc_external_dif(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
+ uint8_t *cdb_ptr);
+static void
lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb);
static void
lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb);
@@ -564,7 +567,7 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba,
&phba->sli4_hba.lpfc_abts_scsi_buf_list, list) {
if (psb->cur_iocbq.sli4_xritag == xri) {
list_del(&psb->list);
- psb->exch_busy = 0;
+ psb->flags &= ~LPFC_SBUF_XBUSY;
psb->status = IOSTAT_SUCCESS;
spin_unlock(
&phba->sli4_hba.abts_scsi_buf_list_lock);
@@ -596,7 +599,7 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba,
if (iocbq->sli4_xritag != xri)
continue;
psb = container_of(iocbq, struct lpfc_scsi_buf, cur_iocbq);
- psb->exch_busy = 0;
+ psb->flags &= ~LPFC_SBUF_XBUSY;
spin_unlock_irqrestore(&phba->hbalock, iflag);
if (!list_empty(&pring->txq))
lpfc_worker_wake_up(phba);
@@ -683,10 +686,10 @@ lpfc_sli4_post_scsi_sgl_list(struct lpfc_hba *phba,
psb->cur_iocbq.sli4_xritag);
if (status) {
/* failure, put on abort scsi list */
- psb->exch_busy = 1;
+ psb->flags |= LPFC_SBUF_XBUSY;
} else {
/* success, put on SCSI buffer list */
- psb->exch_busy = 0;
+ psb->flags &= ~LPFC_SBUF_XBUSY;
psb->status = IOSTAT_SUCCESS;
num_posted++;
}
@@ -716,10 +719,10 @@ lpfc_sli4_post_scsi_sgl_list(struct lpfc_hba *phba,
struct lpfc_scsi_buf, list);
if (status) {
/* failure, put on abort scsi list */
- psb->exch_busy = 1;
+ psb->flags |= LPFC_SBUF_XBUSY;
} else {
/* success, put on SCSI buffer list */
- psb->exch_busy = 0;
+ psb->flags &= ~LPFC_SBUF_XBUSY;
psb->status = IOSTAT_SUCCESS;
num_posted++;
}
@@ -833,7 +836,8 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc)
* 4K Page alignment is CRITICAL to BlockGuard, double check
* to be sure.
*/
- if (phba->cfg_enable_bg && (((unsigned long)(psb->data) &
+ if ((phba->sli3_options & LPFC_SLI3_BG_ENABLED) &&
+ (((unsigned long)(psb->data) &
(unsigned long)(SLI4_PAGE_SIZE - 1)) != 0)) {
pci_pool_free(phba->lpfc_scsi_dma_buf_pool,
psb->data, psb->dma_handle);
@@ -1097,7 +1101,7 @@ lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
psb->nonsg_phys = 0;
psb->prot_seg_cnt = 0;
- if (psb->exch_busy) {
+ if (psb->flags & LPFC_SBUF_XBUSY) {
spin_lock_irqsave(&phba->sli4_hba.abts_scsi_buf_list_lock,
iflag);
psb->pCmd = NULL;
@@ -1125,7 +1129,7 @@ lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
static void
lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
{
-
+ psb->flags &= ~(LPFC_SBUF_NORMAL_DIF | LPFC_SBUF_PASS_DIF);
phba->lpfc_release_scsi_buf(phba, psb);
}
@@ -1267,6 +1271,38 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
return 0;
}
+/**
+ * lpfc_scsi_get_prot_op - Gets the SCSI defined protection data operation
+ * @sc: The SCSI Layer structure for the IO in question.
+ *
+ * This routine calls the SCSI Layer to get the protectio data operation
+ * associated with the specified IO. Then, if this is an IO effected by an
+ * External DIF device, the protection operation is adjusted accordingly.
+ *
+ * Returns the SCSI defined protection data operation
+ **/
+uint32_t
+lpfc_scsi_get_prot_op(struct scsi_cmnd *sc)
+{
+ struct lpfc_scsi_buf *lpfc_cmd;
+ uint32_t op = scsi_get_prot_op(sc);
+
+ lpfc_cmd = (struct lpfc_scsi_buf *)sc->host_scribble;
+ if (lpfc_cmd->flags & LPFC_SBUF_NORMAL_DIF) {
+ if (sc->sc_data_direction == DMA_FROM_DEVICE)
+ op = SCSI_PROT_READ_STRIP;
+ else if (sc->sc_data_direction == DMA_TO_DEVICE)
+ op = SCSI_PROT_WRITE_INSERT;
+ } else if (lpfc_cmd->flags & LPFC_SBUF_PASS_DIF) {
+ if (sc->sc_data_direction == DMA_FROM_DEVICE)
+ op = SCSI_PROT_READ_PASS;
+ else if (sc->sc_data_direction == DMA_TO_DEVICE)
+ op = SCSI_PROT_WRITE_PASS;
+ }
+ return op;
+}
+
+
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
/* Return if if error injection is detected by Initiator */
@@ -1298,7 +1334,7 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
struct scsi_dif_tuple *src = NULL;
struct lpfc_nodelist *ndlp;
struct lpfc_rport_data *rdata;
- uint32_t op = scsi_get_prot_op(sc);
+ uint32_t op = lpfc_scsi_get_prot_op(sc);
uint32_t blksize;
uint32_t numblks;
sector_t lba;
@@ -1702,7 +1738,7 @@ lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
uint8_t ret = 0;
if (lpfc_cmd_guard_csum(sc)) {
- switch (scsi_get_prot_op(sc)) {
+ switch (lpfc_scsi_get_prot_op(sc)) {
case SCSI_PROT_READ_INSERT:
case SCSI_PROT_WRITE_STRIP:
*rxop = BG_OP_IN_NODIF_OUT_CSUM;
@@ -1725,13 +1761,13 @@ lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
default:
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
"9063 BLKGRD: Bad op/guard:%d/IP combination\n",
- scsi_get_prot_op(sc));
+ lpfc_scsi_get_prot_op(sc));
ret = 1;
break;
}
} else {
- switch (scsi_get_prot_op(sc)) {
+ switch (lpfc_scsi_get_prot_op(sc)) {
case SCSI_PROT_READ_STRIP:
case SCSI_PROT_WRITE_INSERT:
*rxop = BG_OP_IN_CRC_OUT_NODIF;
@@ -1754,7 +1790,7 @@ lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
default:
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
"9075 BLKGRD: Bad op/guard:%d/CRC combination\n",
- scsi_get_prot_op(sc));
+ lpfc_scsi_get_prot_op(sc));
ret = 1;
break;
}
@@ -1782,7 +1818,7 @@ lpfc_bg_err_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
uint8_t ret = 0;
if (lpfc_cmd_guard_csum(sc)) {
- switch (scsi_get_prot_op(sc)) {
+ switch (lpfc_scsi_get_prot_op(sc)) {
case SCSI_PROT_READ_INSERT:
case SCSI_PROT_WRITE_STRIP:
*rxop = BG_OP_IN_NODIF_OUT_CRC;
@@ -1807,7 +1843,7 @@ lpfc_bg_err_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
}
} else {
- switch (scsi_get_prot_op(sc)) {
+ switch (lpfc_scsi_get_prot_op(sc)) {
case SCSI_PROT_READ_STRIP:
case SCSI_PROT_WRITE_INSERT:
*rxop = BG_OP_IN_CSUM_OUT_NODIF;
@@ -2628,7 +2664,7 @@ static int
lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc)
{
int ret = LPFC_PG_TYPE_INVALID;
- unsigned char op = scsi_get_prot_op(sc);
+ unsigned char op = lpfc_scsi_get_prot_op(sc);
switch (op) {
case SCSI_PROT_READ_STRIP:
@@ -2673,12 +2709,12 @@ lpfc_bg_scsi_adjust_dl(struct lpfc_hba *phba,
/* Check if there is protection data on the wire */
if (sc->sc_data_direction == DMA_FROM_DEVICE) {
/* Read check for protection data */
- if (scsi_get_prot_op(sc) == SCSI_PROT_READ_INSERT)
+ if (lpfc_scsi_get_prot_op(sc) == SCSI_PROT_READ_INSERT)
return fcpdl;
} else {
/* Write check for protection data */
- if (scsi_get_prot_op(sc) == SCSI_PROT_WRITE_STRIP)
+ if (lpfc_scsi_get_prot_op(sc) == SCSI_PROT_WRITE_STRIP)
return fcpdl;
}
@@ -2895,7 +2931,7 @@ lpfc_calc_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
guard_tag = 0;
/* First check to see if there is protection data to examine */
- prot = scsi_get_prot_op(cmd);
+ prot = lpfc_scsi_get_prot_op(cmd);
if ((prot == SCSI_PROT_READ_STRIP) ||
(prot == SCSI_PROT_WRITE_INSERT) ||
(prot == SCSI_PROT_NORMAL))
@@ -3177,7 +3213,7 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
cmd->sense_buffer[10] = 0x80; /* Validity bit */
/* bghm is a "on the wire" FC frame based count */
- switch (scsi_get_prot_op(cmd)) {
+ switch (lpfc_scsi_get_prot_op(cmd)) {
case SCSI_PROT_READ_INSERT:
case SCSI_PROT_WRITE_STRIP:
bghm /= cmd->device->sector_size;
@@ -3459,7 +3495,7 @@ lpfc_bg_scsi_prep_dma_buf_s4(struct lpfc_hba *phba,
}
}
- switch (scsi_get_prot_op(scsi_cmnd)) {
+ switch (lpfc_scsi_get_prot_op(scsi_cmnd)) {
case SCSI_PROT_WRITE_STRIP:
case SCSI_PROT_READ_STRIP:
lpfc_cmd->cur_iocbq.iocb_flag |= LPFC_IO_DIF_STRIP;
@@ -3693,6 +3729,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
uint32_t host_status = DID_OK;
uint32_t rsplen = 0;
uint32_t logit = LOG_FCP | LOG_FCP_ERROR;
+ uint8_t asc, ascq;
/*
@@ -3840,7 +3877,16 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
scsi_set_resid(cmnd, scsi_bufflen(cmnd));
}
- out:
+out:
+ if (vport->phba->cfg_external_dif &&
+ (lpfc_cmd->flags & (LPFC_SBUF_NORMAL_DIF | LPFC_SBUF_PASS_DIF))) {
+ asc = cmnd->sense_buffer[12];
+ ascq = cmnd->sense_buffer[13];
+ /* Check for LOGICAL BLOCK GUARD CHECK / REF TAG failed */
+ if ((scsi_status == SAM_STAT_CHECK_CONDITION) &&
+ (asc == 0x10) && ((ascq == 1) || (ascq == 3)))
+ host_status = DID_ERROR; /* Convert to retryable err */
+ }
cmnd->result = ScsiResult(host_status, scsi_status);
lpfc_send_scsi_error_event(vport->phba, vport, lpfc_cmd, rsp_iocb);
}
@@ -3882,7 +3928,10 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
lpfc_cmd->result = (pIocbOut->iocb.un.ulpWord[4] & IOERR_PARAM_MASK);
lpfc_cmd->status = pIocbOut->iocb.ulpStatus;
/* pick up SLI4 exhange busy status from HBA */
- lpfc_cmd->exch_busy = pIocbOut->iocb_flag & LPFC_EXCHANGE_BUSY;
+ if (pIocbOut->iocb_flag & LPFC_EXCHANGE_BUSY)
+ lpfc_cmd->flags |= LPFC_SBUF_XBUSY;
+ else
+ lpfc_cmd->flags &= ~LPFC_SBUF_XBUSY;
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
if (lpfc_cmd->prot_data_type) {
@@ -4001,7 +4050,8 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
if ((lpfc_cmd->result == IOERR_RX_DMA_FAILED ||
lpfc_cmd->result == IOERR_TX_DMA_FAILED) &&
pIocbOut->iocb.unsli3.sli3_bg.bgstat) {
- if (scsi_get_prot_op(cmd) != SCSI_PROT_NORMAL) {
+ if (lpfc_scsi_get_prot_op(cmd) !=
+ SCSI_PROT_NORMAL) {
/*
* This is a response for a BG enabled
* cmd. Parse BG error
@@ -4184,6 +4234,10 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
memset(ptr, 0, (LPFC_FCP_CDB_LEN - scsi_cmnd->cmd_len));
}
+ /* Check if we want to make this IO an External DIF device */
+ if (vport->phba->cfg_external_dif)
+ lpfc_external_dif(vport, lpfc_cmd, &fcp_cmnd->fcpCdb[0]);
+
fcp_cmnd->fcpCntl1 = SIMPLE_Q;
sli4 = (phba->sli_rev == LPFC_SLI_REV4);
@@ -4241,7 +4295,8 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
piocbq->iocb.ulpClass = (pnode->nlp_fcp_info & 0x0f);
piocbq->context1 = lpfc_cmd;
- piocbq->iocb_cmpl = lpfc_scsi_cmd_iocb_cmpl;
+ if (piocbq->iocb_cmpl == NULL)
+ piocbq->iocb_cmpl = lpfc_scsi_cmd_iocb_cmpl;
piocbq->iocb.ulpTimeout = lpfc_cmd->timeout;
piocbq->vport = vport;
}
@@ -4481,6 +4536,296 @@ void lpfc_poll_timeout(unsigned long ptr)
}
/**
+ * lpfc_external_dif_cleanup - Clean up a specific External DIF device
+ * @vport: The virtual port for which this call is being executed.
+ * @pname WWPN to match
+ *
+ * This routine scans the discovered External DIF devices for the vport
+ * for a match using the targets WWPN. All luns matching that WWPN will be
+ * removed. This routine is called when dev_loss for a target is envoked.
+ **/
+void
+lpfc_external_dif_cleanup(struct lpfc_vport *vport, struct lpfc_name *pname)
+{
+ struct lpfc_external_dif_support *dp, *next_dp;
+ unsigned long flags;
+ uint8_t *name;
+
+ spin_lock_irqsave(&vport->external_dif_lock, flags);
+ list_for_each_entry_safe(dp, next_dp, &vport->external_dif_list,
+ listentry) {
+ name = (uint8_t *)&dp->portName;
+ if (memcmp(name, (uint8_t *)pname,
+ sizeof(struct lpfc_name)) == 0) {
+ list_del(&dp->listentry);
+ lpfc_printf_log(vport->phba, KERN_WARNING, LOG_BG,
+ "0701 Remove External DIF device "
+ "scsi_id x%x: lun_id x%llx: WWPN "
+ "%02x:%02x:%02x:%02x:"
+ "%02x:%02x:%02x:%02x\n",
+ dp->sid, dp->lun,
+ *name, *(name+1), *(name+2), *(name+3),
+ *(name+4), *(name+5), *(name+6),
+ *(name+7));
+ kfree(dp);
+ }
+ }
+ spin_unlock_irqrestore(&vport->external_dif_lock, flags);
+}
+
+/**
+ * lpfc_external_dif_match - Look up a specific External DIF device
+ * @vport: The virtual port for which this call is being executed.
+ * @lun: lun id used to specify the desired External DIF device
+ * @sid: SCSI id used to specify the desired External DIF device
+ *
+ * This routine scans the discovered External DIF devices for the vport
+ * for a match using the lun/sid criteria.
+ *
+ * Return code :
+ * NULL - device not found
+ * dp - struct lpfc_external_dif_support of matching device
+ **/
+struct lpfc_external_dif_support *
+lpfc_external_dif_match(struct lpfc_vport *vport, uint64_t lun, uint32_t sid)
+{
+ struct lpfc_external_dif_support *dp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vport->external_dif_lock, flags);
+ list_for_each_entry(dp, &vport->external_dif_list, listentry) {
+ if ((dp->sid == sid) && (dp->lun == lun)) {
+ spin_unlock_irqrestore(&vport->external_dif_lock,
+ flags);
+ return dp;
+ }
+ }
+ spin_unlock_irqrestore(&vport->external_dif_lock, flags);
+ return NULL;
+}
+
+/**
+ * lpfc_external_dif_cmpl - IOCB completion routine for a External DIF IO
+ * @phba: The Hba for which this call is being executed.
+ * @pIocbIn: The command IOCBQ for the scsi cmnd.
+ * @pIocbOut: The response IOCBQ for the scsi cmnd.
+ *
+ * This routine processes the External DIF SCSi command cmpl before calling the
+ * normal SCSI cmpl routine (lpfc_scsi_cmd_iocb_cmpl). There are 2 types of
+ * External DIF completions, INQUIRY and READ/WRITE SCSI commands.
+ *
+ * We use INQUIRY to discover External DIF devices. An External DIF device does
+ * not advertise itself as T10-DIF capable using standard bits in the INQUIRY
+ * and READ_CAPACITY commands. Instead, it uses some vendor specific
+ * information in the standard INQUIRY command to turn on this feature.
+ *
+ * For READ/WRITE IOs we convert the IO back into a normal IO so it can be
+ * completed to the SCSI layer. The SCSI layer is unaware the IO was actually
+ * transmitted on the wire in T10 DIF Type 1 format.
+ **/
+static void
+lpfc_external_dif_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
+ struct lpfc_iocbq *pIocbOut)
+{
+ struct lpfc_scsi_buf *lpfc_cmd =
+ (struct lpfc_scsi_buf *)pIocbIn->context1;
+ struct lpfc_vport *vport = pIocbIn->vport;
+ struct fcp_rsp *fcprsp = lpfc_cmd->fcp_rsp;
+ uint32_t resp_info = fcprsp->rspStatus2;
+ struct scsi_cmnd *cmnd = lpfc_cmd->pCmd;
+ uint32_t status = pIocbOut->iocb.ulpStatus;
+ struct lpfc_rport_data *rdata;
+ struct lpfc_nodelist *pnode;
+ struct lpfc_external_dif_support *dp;
+ struct lpfc_vendor_dif *vendor_dif_infop;
+ struct fcp_cmnd *fcpcmd;
+ struct scsi_device *sdev;
+ struct scatterlist *sgde;
+ unsigned long flags;
+ uint8_t *data_inq;
+ uint8_t *name;
+ uint32_t cnt;
+
+ if (status) {
+ if ((status != IOSTAT_FCP_RSP_ERROR) ||
+ !(resp_info & RESID_UNDER))
+ goto out;
+ }
+
+ /* Only success and RESID_UNDER make it here */
+ switch (cmnd->cmnd[0]) {
+ case INQUIRY:
+ fcpcmd = lpfc_cmd->fcp_cmnd;
+ sgde = scsi_sglist(cmnd);
+ data_inq = (uint8_t *)sg_virt(sgde);
+
+ /* Make sure the INQUIRY payload has our
+ * vendor specific info included.
+ */
+ cnt = be32_to_cpu(fcpcmd->fcpDl) -
+ be32_to_cpu(fcprsp->rspResId);
+ if (cnt < LPFC_INQ_FDIF_SZ)
+ break;
+
+ /* Jump to T10 Vendor Identification field */
+ data_inq += LPFC_INQ_VID_OFFSET;
+ if ((memcmp(data_inq, LPFC_INQ_FDIF_VENDOR,
+ sizeof(LPFC_INQ_FDIF_VENDOR) != 0)))
+ break;
+
+ sdev = cmnd->device;
+ if (lpfc_external_dif_match(vport, sdev->lun, sdev->id))
+ break; /* device already exists */
+
+ /* Jump to Vendor specific DIF info */
+ vendor_dif_infop = (struct lpfc_vendor_dif *)(data_inq +
+ (LPFC_INQ_VDIF_OFFSET - LPFC_INQ_VID_OFFSET));
+
+ /* Check to see if External DIF protection is enabled and we
+ * are version 1. Currently we only support DIF Type 1
+ * (GRD_CHK / REF_CHK)
+ */
+ if ((vendor_dif_infop->length != LPFC_INQ_FDIF_SIZE) ||
+ (vendor_dif_infop->version != LPFC_INQ_FDIF_VERSION) ||
+ (vendor_dif_infop->dif_info != (LPFC_FDIF_PROTECT |
+ LPFC_FDIF_REFCHK | LPFC_FDIF_GRDCHK))) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "0709 External DIF Vendor info error "
+ "Data: %02x %02x %02x\n",
+ vendor_dif_infop->length,
+ vendor_dif_infop->version,
+ vendor_dif_infop->dif_info);
+ break;
+ }
+
+ /* New External DIF device found */
+ dp = kmalloc(sizeof(struct lpfc_external_dif_support),
+ GFP_ATOMIC);
+ if (!dp)
+ break;
+ dp->lun = sdev->lun;
+ dp->sid = sdev->id;
+ dp->dif_info = vendor_dif_infop->dif_info;
+
+ rdata = lpfc_cmd->rdata;
+ pnode = rdata->pnode;
+ memcpy(&dp->portName, &pnode->nlp_portname,
+ sizeof(struct lpfc_name));
+
+ spin_lock_irqsave(&vport->external_dif_lock, flags);
+ list_add_tail(&dp->listentry, &vport->external_dif_list);
+ spin_unlock_irqrestore(&vport->external_dif_lock, flags);
+
+ name = (uint8_t *)&pnode->nlp_portname;
+ lpfc_printf_log(phba, KERN_WARNING, LOG_BG,
+ "0712 Discovered External DIF device NPortId "
+ "x%x: scsi_id x%x: lun_id x%llx: WWPN "
+ "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ pnode->nlp_DID, dp->sid, dp->lun,
+ *name, *(name+1), *(name+2), *(name+3),
+ *(name+4), *(name+5), *(name+6), *(name+7));
+ break;
+ case READ_10:
+ case READ_12:
+ case READ_16:
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_16:
+ case WRITE_SAME:
+ case WRITE_SAME_16:
+ case WRITE_VERIFY:
+ break;
+ default:
+ break;
+ }
+out:
+ lpfc_scsi_cmd_iocb_cmpl(phba, pIocbIn, pIocbOut);
+}
+
+/**
+ * lpfc_external_dif - Check to see if we want to process this IO as a External DIF
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
+ *
+ * This routine will selectively force normal IOs to be processed as a
+ * READ_STRIP / WRITE_INSERT T10-DIF IO. The upper SCSI Layer will be unaware
+ * that the IO is going to be transmitted on the wire with T10-DIF protection
+ * data. This routine also diverts INQUIRY command cmpletions so they can be
+ * used to scan for External DIF devices.
+ **/
+static void
+lpfc_external_dif(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
+ uint8_t *cdb_ptr)
+{
+ struct scsi_cmnd *cmnd = lpfc_cmd->pCmd;
+ struct lpfc_iocbq *piocbq = &(lpfc_cmd->cur_iocbq);
+ struct scsi_device *sdev;
+
+ switch (scsi_get_prot_op(cmnd)) {
+ case SCSI_PROT_NORMAL:
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ break;
+ default:
+ return;
+ }
+
+ switch (cdb_ptr[0]) {
+ case INQUIRY:
+ /* We are only interested in page 0 */
+ if ((cdb_ptr[1] != 0) || (cdb_ptr[2] != 0))
+ return;
+
+ /* Divert cmpl to check for a External DIF device */
+ piocbq->iocb_cmpl = lpfc_external_dif_cmpl;
+ return;
+ case READ_10:
+ case READ_12:
+ case READ_16:
+ /* Is this a Force DIF device */
+ sdev = cmnd->device;
+ if (!lpfc_external_dif_match(vport, sdev->lun, sdev->id))
+ return;
+
+ /* This is an IO for a External DIF device, so set the
+ * appropriate bits so we send protection data on the wire.
+ */
+ cdb_ptr[1] |= LPFC_FDIF_CDB_PROTECT; /* Set RDPROTECT = 001 */
+ piocbq->iocb_cmpl = lpfc_external_dif_cmpl;
+ break;
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_16:
+ case WRITE_SAME:
+ case WRITE_SAME_16:
+ case WRITE_VERIFY:
+ /* Is this a Force DIF device */
+ sdev = cmnd->device;
+ if (!lpfc_external_dif_match(vport, sdev->lun, sdev->id))
+ return;
+
+ /* This is an IO for a External DIF device, so set the
+ * appropriate bits so we send protection data on the wire.
+ */
+ cdb_ptr[1] |= LPFC_FDIF_CDB_PROTECT; /* Set WRPROTECT = 001 */
+ piocbq->iocb_cmpl = lpfc_external_dif_cmpl;
+ break;
+ default:
+ return;
+ }
+
+ switch (scsi_get_prot_op(cmnd)) {
+ case SCSI_PROT_NORMAL:
+ lpfc_cmd->flags |= LPFC_SBUF_NORMAL_DIF;
+ break;
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ lpfc_cmd->flags |= LPFC_SBUF_PASS_DIF;
+ break;
+ }
+}
+
+/**
* lpfc_queuecommand - scsi_host_template queuecommand entry point
* @cmnd: Pointer to scsi_cmnd data structure.
* @done: Pointer to done routine.
@@ -4551,10 +4896,13 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
lpfc_cmd->rdata = rdata;
lpfc_cmd->timeout = 0;
lpfc_cmd->start_time = jiffies;
+ lpfc_cmd->cur_iocbq.iocb_cmpl = NULL;
cmnd->host_scribble = (unsigned char *)lpfc_cmd;
- if (scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
- if (vport->phba->cfg_enable_bg) {
+ lpfc_scsi_prep_cmnd(vport, lpfc_cmd, ndlp);
+
+ if (lpfc_scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
lpfc_printf_vlog(vport,
KERN_INFO, LOG_SCSI_CMD,
"9033 BLKGRD: rcvd %s cmd:x%x "
@@ -4567,7 +4915,7 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
}
err = lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd);
} else {
- if (vport->phba->cfg_enable_bg) {
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
lpfc_printf_vlog(vport,
KERN_INFO, LOG_SCSI_CMD,
"9038 BLKGRD: rcvd PROT_NORMAL cmd: "
@@ -4583,8 +4931,6 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
if (err)
goto out_host_busy_free_buf;
- lpfc_scsi_prep_cmnd(vport, lpfc_cmd, ndlp);
-
atomic_inc(&ndlp->cmd_pending);
err = lpfc_sli_issue_iocb(phba, LPFC_FCP_RING,
&lpfc_cmd->cur_iocbq, SLI_IOCB_RET_IOCB);
@@ -5109,7 +5455,7 @@ static int
lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
{
struct Scsi_Host *shost = cmnd->device->host;
- struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
struct lpfc_rport_data *rdata;
struct lpfc_nodelist *pnode;
unsigned tgt_id = cmnd->device->id;
diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h
index 0389ac1..bb9a455 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.h
+++ b/drivers/scsi/lpfc/lpfc_scsi.h
@@ -134,7 +134,12 @@ struct lpfc_scsi_buf {
uint32_t timeout;
- uint16_t exch_busy; /* SLI4 hba reported XB on complete WCQE */
+ uint16_t flags;
+#define LPFC_SBUF_XBUSY 0x1 /* SLI4 hba reported XB on WCQE cmpl */
+ /* External DIF device IO conversions */
+#define LPFC_SBUF_NORMAL_DIF 0x2 /* normal mode to insert/strip */
+#define LPFC_SBUF_PASS_DIF 0x4 /* insert/strip mode to passthru */
+
uint16_t status; /* From IOCB Word 7- ulpStatus */
uint32_t result; /* From IOCB Word 4. */
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index c76c2a1..066428b 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -4526,7 +4526,6 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
phba->sli3_options &= ~(LPFC_SLI3_NPIV_ENABLED |
LPFC_SLI3_HBQ_ENABLED |
LPFC_SLI3_CRP_ENABLED |
- LPFC_SLI3_BG_ENABLED |
LPFC_SLI3_DSS_ENABLED);
if (rc != MBX_SUCCESS) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
@@ -4592,13 +4591,20 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
phba->hbq_get = phba->mbox->us.s3_pgp.hbq_get;
phba->port_gp = phba->mbox->us.s3_pgp.port;
- if (phba->cfg_enable_bg) {
- if (pmb->u.mb.un.varCfgPort.gbg)
- phba->sli3_options |= LPFC_SLI3_BG_ENABLED;
- else
+ /*
+ * If the port cannot support the host's requested features
+ * then turn off the global config parameters to disable the
+ * feature in the driver. This is not a fatal error.
+ */
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
+ if (pmb->u.mb.un.varCfgPort.gbg == 0) {
+ phba->cfg_enable_bg = 0;
+ phba->cfg_external_dif = 0;
+ phba->sli3_options &= ~LPFC_SLI3_BG_ENABLED;
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0443 Adapter did not grant "
"BlockGuard\n");
+ }
}
} else {
phba->hbq_get = NULL;
@@ -4689,7 +4695,6 @@ lpfc_sli_hba_setup(struct lpfc_hba *phba)
} else {
phba->iocb_cmd_size = SLI2_IOCB_CMD_SIZE;
phba->iocb_rsp_size = SLI2_IOCB_RSP_SIZE;
- phba->sli3_options = 0;
}
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
@@ -6407,12 +6412,13 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
* then turn off the global config parameters to disable the
* feature in the driver. This is not a fatal error.
*/
- phba->sli3_options &= ~LPFC_SLI3_BG_ENABLED;
- if (phba->cfg_enable_bg) {
- if (bf_get(lpfc_mbx_rq_ftr_rsp_dif, &mqe->un.req_ftrs))
- phba->sli3_options |= LPFC_SLI3_BG_ENABLED;
- else
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
+ if (!(bf_get(lpfc_mbx_rq_ftr_rsp_dif, &mqe->un.req_ftrs))) {
+ phba->cfg_enable_bg = 0;
+ phba->cfg_external_dif = 0;
+ phba->sli3_options &= ~LPFC_SLI3_BG_ENABLED;
ftr_rsp++;
+ }
}
if (phba->max_vpi && phba->cfg_enable_npiv &&
@@ -6425,8 +6431,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
"x%x x%x x%x\n", mqe->un.req_ftrs.word2,
mqe->un.req_ftrs.word3, phba->cfg_enable_bg,
phba->cfg_enable_npiv, phba->max_vpi);
- if (!(bf_get(lpfc_mbx_rq_ftr_rsp_dif, &mqe->un.req_ftrs)))
- phba->cfg_enable_bg = 0;
+
if (!(bf_get(lpfc_mbx_rq_ftr_rsp_npiv, &mqe->un.req_ftrs)))
phba->cfg_enable_npiv = 0;
}
@@ -10326,7 +10331,10 @@ lpfc_sli_wake_iocb_wait(struct lpfc_hba *phba,
!(cmdiocbq->iocb_flag & LPFC_IO_LIBDFC)) {
lpfc_cmd = container_of(cmdiocbq, struct lpfc_scsi_buf,
cur_iocbq);
- lpfc_cmd->exch_busy = rspiocbq->iocb_flag & LPFC_EXCHANGE_BUSY;
+ if (rspiocbq->iocb_flag & LPFC_EXCHANGE_BUSY)
+ lpfc_cmd->flags |= LPFC_SBUF_XBUSY;
+ else
+ lpfc_cmd->flags &= ~LPFC_SBUF_XBUSY;
}
pdone_q = cmdiocbq->context_un.wait_queue;
--
1.7.11.7
next reply other threads:[~2015-04-03 21:13 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-04-03 21:13 James Smart [this message]
2015-04-05 11:12 ` [PATCH 15/21] lpfc: Implement support for wire-only DIF devices Sebastian Herbszt
2015-04-05 16:06 ` Christoph Hellwig
2015-04-07 23:13 ` Martin K. Petersen
2015-04-09 20:58 ` James Smart
2015-04-09 21:20 ` Martin K. Petersen
-- strict thread matches above, loose matches on Subject: below --
2015-02-05 19:25 James Smart
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1428095580.6933.44.camel@myfc17 \
--to=james.smart@emulex.com \
--cc=linux-scsi@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.