diff options
Diffstat (limited to 'drivers/scsi/lpfc')
-rw-r--r-- | drivers/scsi/lpfc/lpfc.h | 9 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_attr.c | 124 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_bsg.c | 20 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_ct.c | 10 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_debugfs.c | 98 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_els.c | 1 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_hbadisc.c | 7 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_hw4.h | 7 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_init.c | 153 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_nportdisc.c | 35 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_nvme.c | 238 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_nvme.h | 17 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_nvmet.h | 6 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_scsi.c | 33 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_scsi.h | 2 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_sli.c | 116 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_version.h | 4 |
17 files changed, 612 insertions, 268 deletions
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 6c0d351c0d0d..20b249a649dd 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -64,8 +64,6 @@ struct lpfc_sli2_slim; #define LPFC_IOCB_LIST_CNT 2250 /* list of IOCBs for fast-path usage. */ #define LPFC_Q_RAMP_UP_INTERVAL 120 /* lun q_depth ramp up interval */ #define LPFC_VNAME_LEN 100 /* vport symbolic name length */ -#define LPFC_TGTQ_INTERVAL 40000 /* Min amount of time between tgt - queue depth change in millisecs */ #define LPFC_TGTQ_RAMPUP_PCENT 5 /* Target queue rampup in percentage */ #define LPFC_MIN_TGT_QDEPTH 10 #define LPFC_MAX_TGT_QDEPTH 0xFFFF @@ -784,6 +782,7 @@ struct lpfc_hba { uint32_t cfg_nvme_oas; uint32_t cfg_nvme_embed_cmd; uint32_t cfg_nvme_io_channel; + uint32_t cfg_nvmet_mrq_post; uint32_t cfg_nvmet_mrq; uint32_t cfg_enable_nvmet; uint32_t cfg_nvme_enable_fb; @@ -922,12 +921,6 @@ struct lpfc_hba { atomic_t fc4ScsiOutputRequests; atomic_t fc4ScsiControlRequests; atomic_t fc4ScsiIoCmpls; - atomic_t fc4NvmeInputRequests; - atomic_t fc4NvmeOutputRequests; - atomic_t fc4NvmeControlRequests; - atomic_t fc4NvmeIoCmpls; - atomic_t fc4NvmeLsRequests; - atomic_t fc4NvmeLsCmpls; uint64_t bg_guard_err_cnt; uint64_t bg_apptag_err_cnt; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 2ac1d21c553f..729d343861f4 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * @@ -149,10 +149,14 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, struct lpfc_nvmet_tgtport *tgtp; struct nvme_fc_local_port *localport; struct lpfc_nvme_lport *lport; + struct lpfc_nvme_rport *rport; struct lpfc_nodelist *ndlp; struct nvme_fc_remote_port *nrport; - uint64_t data1, data2, data3, tot; + struct lpfc_nvme_ctrl_stat *cstat; + uint64_t data1, data2, data3; + uint64_t totin, totout, tot; char *statep; + int i; int len = 0; if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) { @@ -293,6 +297,13 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, len = snprintf(buf, PAGE_SIZE, "NVME Initiator Enabled\n"); spin_lock_irq(shost->host_lock); + len += snprintf(buf + len, PAGE_SIZE - len, + "XRI Dist lpfc%d Total %d NVME %d SCSI %d ELS %d\n", + phba->brd_no, + phba->sli4_hba.max_cfg_param.max_xri, + phba->sli4_hba.nvme_xri_max, + phba->sli4_hba.scsi_xri_max, + lpfc_sli4_get_els_iocb_cnt(phba)); /* Port state is only one of two values for now. */ if (localport->port_id) @@ -309,11 +320,14 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, localport->port_id, statep); list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { - if (!ndlp->nrport) + rport = lpfc_ndlp_get_nrport(ndlp); + if (!rport) continue; /* local short-hand pointer. */ - nrport = ndlp->nrport->remoteport; + nrport = rport->remoteport; + if (!nrport) + continue; /* Port state is only one of two values for now. */ switch (nrport->port_state) { @@ -364,11 +378,14 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, } spin_unlock_irq(shost->host_lock); + if (!lport) + return len; + len += snprintf(buf + len, PAGE_SIZE - len, "\nNVME Statistics\n"); len += snprintf(buf+len, PAGE_SIZE-len, "LS: Xmt %010x Cmpl %010x Abort %08x\n", - atomic_read(&phba->fc4NvmeLsRequests), - atomic_read(&phba->fc4NvmeLsCmpls), + atomic_read(&lport->fc4NvmeLsRequests), + atomic_read(&lport->fc4NvmeLsCmpls), atomic_read(&lport->xmt_ls_abort)); len += snprintf(buf + len, PAGE_SIZE - len, @@ -377,28 +394,33 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, atomic_read(&lport->cmpl_ls_xb), atomic_read(&lport->cmpl_ls_err)); - tot = atomic_read(&phba->fc4NvmeIoCmpls); - data1 = atomic_read(&phba->fc4NvmeInputRequests); - data2 = atomic_read(&phba->fc4NvmeOutputRequests); - data3 = atomic_read(&phba->fc4NvmeControlRequests); + totin = 0; + totout = 0; + for (i = 0; i < phba->cfg_nvme_io_channel; i++) { + cstat = &lport->cstat[i]; + tot = atomic_read(&cstat->fc4NvmeIoCmpls); + totin += tot; + data1 = atomic_read(&cstat->fc4NvmeInputRequests); + data2 = atomic_read(&cstat->fc4NvmeOutputRequests); + data3 = atomic_read(&cstat->fc4NvmeControlRequests); + totout += (data1 + data2 + data3); + } len += snprintf(buf+len, PAGE_SIZE-len, - "FCP: Rd %016llx Wr %016llx IO %016llx\n", - data1, data2, data3); + "Total FCP Cmpl %016llx Issue %016llx " + "OutIO %016llx\n", + totin, totout, totout - totin); len += snprintf(buf+len, PAGE_SIZE-len, - " noxri %08x nondlp %08x qdepth %08x " - "wqerr %08x\n", + " abort %08x noxri %08x nondlp %08x qdepth %08x " + "wqerr %08x err %08x\n", + atomic_read(&lport->xmt_fcp_abort), atomic_read(&lport->xmt_fcp_noxri), atomic_read(&lport->xmt_fcp_bad_ndlp), atomic_read(&lport->xmt_fcp_qdepth), + atomic_read(&lport->xmt_fcp_err), atomic_read(&lport->xmt_fcp_wqerr)); len += snprintf(buf + len, PAGE_SIZE - len, - " Cmpl %016llx Outstanding %016llx Abort %08x\n", - tot, ((data1 + data2 + data3) - tot), - atomic_read(&lport->xmt_fcp_abort)); - - len += snprintf(buf + len, PAGE_SIZE - len, "FCP CMPL: xb %08x Err %08x\n", atomic_read(&lport->cmpl_fcp_xb), atomic_read(&lport->cmpl_fcp_err)); @@ -3280,6 +3302,9 @@ lpfc_update_rport_devloss_tmo(struct lpfc_vport *vport) { struct Scsi_Host *shost; struct lpfc_nodelist *ndlp; +#if (IS_ENABLED(CONFIG_NVME_FC)) + struct lpfc_nvme_rport *rport; +#endif shost = lpfc_shost_from_vport(vport); spin_lock_irq(shost->host_lock); @@ -3289,8 +3314,9 @@ lpfc_update_rport_devloss_tmo(struct lpfc_vport *vport) if (ndlp->rport) ndlp->rport->dev_loss_tmo = vport->cfg_devloss_tmo; #if (IS_ENABLED(CONFIG_NVME_FC)) - if (ndlp->nrport) - nvme_fc_set_remoteport_devloss(ndlp->nrport->remoteport, + rport = lpfc_ndlp_get_nrport(ndlp); + if (rport) + nvme_fc_set_remoteport_devloss(rport->remoteport, vport->cfg_devloss_tmo); #endif } @@ -3414,6 +3440,15 @@ LPFC_ATTR_R(nvmet_mrq, "Specify number of RQ pairs for processing NVMET cmds"); /* + * lpfc_nvmet_mrq_post: Specify number of RQ buffer to initially post + * to each NVMET RQ. Range 64 to 2048, default is 512. + */ +LPFC_ATTR_R(nvmet_mrq_post, + LPFC_NVMET_RQE_DEF_POST, LPFC_NVMET_RQE_MIN_POST, + LPFC_NVMET_RQE_DEF_COUNT, + "Specify number of RQ buffers to initially post"); + +/* * lpfc_enable_fc4_type: Defines what FC4 types are supported. * Supported Values: 1 - register just FCP * 3 - register both FCP and NVME @@ -3469,8 +3504,49 @@ LPFC_VPORT_ATTR_R(lun_queue_depth, 30, 1, 512, # tgt_queue_depth: This parameter is used to limit the number of outstanding # commands per target port. Value range is [10,65535]. Default value is 65535. */ -LPFC_VPORT_ATTR_RW(tgt_queue_depth, 65535, 10, 65535, - "Max number of FCP commands we can queue to a specific target port"); +static uint lpfc_tgt_queue_depth = LPFC_MAX_TGT_QDEPTH; +module_param(lpfc_tgt_queue_depth, uint, 0444); +MODULE_PARM_DESC(lpfc_tgt_queue_depth, "Set max Target queue depth"); +lpfc_vport_param_show(tgt_queue_depth); +lpfc_vport_param_init(tgt_queue_depth, LPFC_MAX_TGT_QDEPTH, + LPFC_MIN_TGT_QDEPTH, LPFC_MAX_TGT_QDEPTH); + +/** + * lpfc_tgt_queue_depth_store: Sets an attribute value. + * @phba: pointer the the adapter structure. + * @val: integer attribute value. + * + * Description: Sets the parameter to the new value. + * + * Returns: + * zero on success + * -EINVAL if val is invalid + */ +static int +lpfc_tgt_queue_depth_set(struct lpfc_vport *vport, uint val) +{ + struct Scsi_Host *shost = lpfc_shost_from_vport(vport); + struct lpfc_nodelist *ndlp; + + if (!lpfc_rangecheck(val, LPFC_MIN_TGT_QDEPTH, LPFC_MAX_TGT_QDEPTH)) + return -EINVAL; + + if (val == vport->cfg_tgt_queue_depth) + return 0; + + spin_lock_irq(shost->host_lock); + vport->cfg_tgt_queue_depth = val; + + /* Next loop thru nodelist and change cmd_qdepth */ + list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) + ndlp->cmd_qdepth = vport->cfg_tgt_queue_depth; + + spin_unlock_irq(shost->host_lock); + return 0; +} + +lpfc_vport_param_store(tgt_queue_depth); +static DEVICE_ATTR_RW(lpfc_tgt_queue_depth); /* # hba_queue_depth: This parameter is used to limit the number of outstanding @@ -5302,6 +5378,7 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_suppress_rsp, &dev_attr_lpfc_nvme_io_channel, &dev_attr_lpfc_nvmet_mrq, + &dev_attr_lpfc_nvmet_mrq_post, &dev_attr_lpfc_nvme_enable_fb, &dev_attr_lpfc_nvmet_fb_size, &dev_attr_lpfc_enable_bg, @@ -6352,6 +6429,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) lpfc_enable_fc4_type_init(phba, lpfc_enable_fc4_type); lpfc_nvmet_mrq_init(phba, lpfc_nvmet_mrq); + lpfc_nvmet_mrq_post_init(phba, lpfc_nvmet_mrq_post); /* Initialize first burst. Target vs Initiator are different. */ lpfc_nvme_enable_fb_init(phba, lpfc_nvme_enable_fb); diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index 0f174ca80f67..edb1a18a6414 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -3621,7 +3621,7 @@ lpfc_bsg_issue_mbox_ext_handle_job(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) bsg_reply->result = 0; lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, - "2937 SLI_CONFIG ext-buffer maibox command " + "2937 SLI_CONFIG ext-buffer mailbox command " "(x%x/x%x) complete bsg job done, bsize:%d\n", phba->mbox_ext_buf_ctx.nembType, phba->mbox_ext_buf_ctx.mboxType, size); @@ -3632,7 +3632,7 @@ lpfc_bsg_issue_mbox_ext_handle_job(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) phba->mbox_ext_buf_ctx.mbx_dmabuf, 0); } else { lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, - "2938 SLI_CONFIG ext-buffer maibox " + "2938 SLI_CONFIG ext-buffer mailbox " "command (x%x/x%x) failure, rc:x%x\n", phba->mbox_ext_buf_ctx.nembType, phba->mbox_ext_buf_ctx.mboxType, rc); @@ -3666,7 +3666,7 @@ lpfc_bsg_issue_read_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) pmboxq->u.mb.mbxStatus = MBXERR_ERROR; lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, - "2939 SLI_CONFIG ext-buffer rd maibox command " + "2939 SLI_CONFIG ext-buffer rd mailbox command " "complete, ctxState:x%x, mbxStatus:x%x\n", phba->mbox_ext_buf_ctx.state, pmboxq->u.mb.mbxStatus); @@ -3706,7 +3706,7 @@ lpfc_bsg_issue_write_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) pmboxq->u.mb.mbxStatus = MBXERR_ERROR; lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, - "2940 SLI_CONFIG ext-buffer wr maibox command " + "2940 SLI_CONFIG ext-buffer wr mailbox command " "complete, ctxState:x%x, mbxStatus:x%x\n", phba->mbox_ext_buf_ctx.state, pmboxq->u.mb.mbxStatus); @@ -3988,12 +3988,12 @@ lpfc_bsg_sli_cfg_read_cmd_ext(struct lpfc_hba *phba, struct bsg_job *job, if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) { lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, "2947 Issued SLI_CONFIG ext-buffer " - "maibox command, rc:x%x\n", rc); + "mailbox command, rc:x%x\n", rc); return SLI_CONFIG_HANDLED; } lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, "2948 Failed to issue SLI_CONFIG ext-buffer " - "maibox command, rc:x%x\n", rc); + "mailbox command, rc:x%x\n", rc); rc = -EPIPE; job_error: @@ -4147,12 +4147,12 @@ lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct bsg_job *job, if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) { lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, "2955 Issued SLI_CONFIG ext-buffer " - "maibox command, rc:x%x\n", rc); + "mailbox command, rc:x%x\n", rc); return SLI_CONFIG_HANDLED; } lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, "2956 Failed to issue SLI_CONFIG ext-buffer " - "maibox command, rc:x%x\n", rc); + "mailbox command, rc:x%x\n", rc); rc = -EPIPE; goto job_error; } @@ -4492,12 +4492,12 @@ lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct bsg_job *job, if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) { lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, "2969 Issued SLI_CONFIG ext-buffer " - "maibox command, rc:x%x\n", rc); + "mailbox command, rc:x%x\n", rc); return SLI_CONFIG_HANDLED; } lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, "2970 Failed to issue SLI_CONFIG ext-buffer " - "maibox command, rc:x%x\n", rc); + "mailbox command, rc:x%x\n", rc); rc = -EPIPE; goto job_error; } diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 0617c8ea88c6..d4a200ae5a6f 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -471,6 +471,11 @@ lpfc_prep_node_fc4type(struct lpfc_vport *vport, uint32_t Did, uint8_t fc4_type) "Parse GID_FTrsp: did:x%x flg:x%x x%x", Did, ndlp->nlp_flag, vport->fc_flag); + /* Don't assume the rport is always the previous + * FC4 type. + */ + ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME); + /* By default, the driver expects to support FCP FC4 */ if (fc4_type == FC_TYPE_FCP) ndlp->nlp_fc4_type |= NLP_FC4_FCP; @@ -691,6 +696,11 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, vport->fc_flag &= ~FC_RSCN_DEFERRED; spin_unlock_irq(shost->host_lock); + /* This is a GID_FT completing so the gidft_inp counter was + * incremented before the GID_FT was issued to the wire. + */ + vport->gidft_inp--; + /* * Skip processing the NS response * Re-issue the NS cmd diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index fb0dc2aeed91..9df0c051349f 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2007-2015 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * @@ -544,7 +544,7 @@ static int lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) { int len = 0; - int cnt; + int i, iocnt, outio, cnt; struct Scsi_Host *shost = lpfc_shost_from_vport(vport); struct lpfc_hba *phba = vport->phba; struct lpfc_nodelist *ndlp; @@ -552,12 +552,15 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) struct nvme_fc_local_port *localport; struct lpfc_nvmet_tgtport *tgtp; struct nvme_fc_remote_port *nrport; + struct lpfc_nvme_rport *rport; cnt = (LPFC_NODELIST_SIZE / LPFC_NODELIST_ENTRY_SIZE); + outio = 0; len += snprintf(buf+len, size-len, "\nFCP Nodelist Entries ...\n"); spin_lock_irq(shost->host_lock); list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { + iocnt = 0; if (!cnt) { len += snprintf(buf+len, size-len, "Missing Nodelist Entries\n"); @@ -585,9 +588,11 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) break; case NLP_STE_UNMAPPED_NODE: statep = "UNMAP "; + iocnt = 1; break; case NLP_STE_MAPPED_NODE: statep = "MAPPED"; + iocnt = 1; break; case NLP_STE_NPR_NODE: statep = "NPR "; @@ -614,8 +619,10 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) len += snprintf(buf+len, size-len, "UNKNOWN_TYPE "); if (ndlp->nlp_type & NLP_FC_NODE) len += snprintf(buf+len, size-len, "FC_NODE "); - if (ndlp->nlp_type & NLP_FABRIC) + if (ndlp->nlp_type & NLP_FABRIC) { len += snprintf(buf+len, size-len, "FABRIC "); + iocnt = 0; + } if (ndlp->nlp_type & NLP_FCP_TARGET) len += snprintf(buf+len, size-len, "FCP_TGT sid:%d ", ndlp->nlp_sid); @@ -632,10 +639,20 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) ndlp->nlp_usg_map); len += snprintf(buf+len, size-len, "refcnt:%x", kref_read(&ndlp->kref)); + if (iocnt) { + i = atomic_read(&ndlp->cmd_pending); + len += snprintf(buf + len, size - len, + " OutIO:x%x Qdepth x%x", + i, ndlp->cmd_qdepth); + outio += i; + } len += snprintf(buf+len, size-len, "\n"); } spin_unlock_irq(shost->host_lock); + len += snprintf(buf + len, size - len, + "\nOutstanding IO x%x\n", outio); + if (phba->nvmet_support && phba->targetport && (vport == phba->pport)) { tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; len += snprintf(buf + len, size - len, @@ -679,10 +696,13 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) len += snprintf(buf + len, size - len, "\tRport List:\n"); list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { /* local short-hand pointer. */ - if (!ndlp->nrport) + rport = lpfc_ndlp_get_nrport(ndlp); + if (!rport) continue; - nrport = ndlp->nrport->remoteport; + nrport = rport->remoteport; + if (!nrport) + continue; /* Port state is only one of two values for now. */ switch (nrport->port_state) { @@ -751,10 +771,12 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) struct lpfc_nvmet_tgtport *tgtp; struct lpfc_nvmet_rcv_ctx *ctxp, *next_ctxp; struct nvme_fc_local_port *localport; + struct lpfc_nvme_ctrl_stat *cstat; struct lpfc_nvme_lport *lport; - uint64_t tot, data1, data2, data3; + uint64_t data1, data2, data3; + uint64_t tot, totin, totout; + int cnt, i, maxch; int len = 0; - int cnt; if (phba->nvmet_support) { if (!phba->targetport) @@ -880,33 +902,52 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) return len; + localport = vport->localport; + if (!localport) + return len; + lport = (struct lpfc_nvme_lport *)localport->private; + if (!lport) + return len; + len += snprintf(buf + len, size - len, "\nNVME Lport Statistics\n"); len += snprintf(buf + len, size - len, "LS: Xmt %016x Cmpl %016x\n", - atomic_read(&phba->fc4NvmeLsRequests), - atomic_read(&phba->fc4NvmeLsCmpls)); - - tot = atomic_read(&phba->fc4NvmeIoCmpls); - data1 = atomic_read(&phba->fc4NvmeInputRequests); - data2 = atomic_read(&phba->fc4NvmeOutputRequests); - data3 = atomic_read(&phba->fc4NvmeControlRequests); + atomic_read(&lport->fc4NvmeLsRequests), + atomic_read(&lport->fc4NvmeLsCmpls)); - len += snprintf(buf + len, size - len, - "FCP: Rd %016llx Wr %016llx IO %016llx\n", - data1, data2, data3); - - len += snprintf(buf + len, size - len, - " Cmpl %016llx Outstanding %016llx\n", - tot, (data1 + data2 + data3) - tot); + if (phba->cfg_nvme_io_channel < 32) + maxch = phba->cfg_nvme_io_channel; + else + maxch = 32; + totin = 0; + totout = 0; + for (i = 0; i < phba->cfg_nvme_io_channel; i++) { + cstat = &lport->cstat[i]; + tot = atomic_read(&cstat->fc4NvmeIoCmpls); + totin += tot; + data1 = atomic_read(&cstat->fc4NvmeInputRequests); + data2 = atomic_read(&cstat->fc4NvmeOutputRequests); + data3 = atomic_read(&cstat->fc4NvmeControlRequests); + totout += (data1 + data2 + data3); + + /* Limit to 32, debugfs display buffer limitation */ + if (i >= 32) + continue; - localport = vport->localport; - if (!localport) - return len; - lport = (struct lpfc_nvme_lport *)localport->private; - if (!lport) - return len; + len += snprintf(buf + len, PAGE_SIZE - len, + "FCP (%d): Rd %016llx Wr %016llx " + "IO %016llx ", + i, data1, data2, data3); + len += snprintf(buf + len, PAGE_SIZE - len, + "Cmpl %016llx OutIO %016llx\n", + tot, ((data1 + data2 + data3) - tot)); + } + len += snprintf(buf + len, PAGE_SIZE - len, + "Total FCP Cmpl %016llx Issue %016llx " + "OutIO %016llx\n", + totin, totout, totout - totin); len += snprintf(buf + len, size - len, "LS Xmt Err: Abrt %08x Err %08x " @@ -918,11 +959,12 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) len += snprintf(buf + len, size - len, "FCP Xmt Err: noxri %06x nondlp %06x " - "qdepth %06x wqerr %06x Abrt %06x\n", + "qdepth %06x wqerr %06x err %06x Abrt %06x\n", atomic_read(&lport->xmt_fcp_noxri), atomic_read(&lport->xmt_fcp_bad_ndlp), atomic_read(&lport->xmt_fcp_qdepth), atomic_read(&lport->xmt_fcp_wqerr), + atomic_read(&lport->xmt_fcp_err), atomic_read(&lport->xmt_fcp_abort)); len += snprintf(buf + len, size - len, diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 74895e62aaea..6d84a10fef07 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -6268,7 +6268,6 @@ lpfc_els_handle_rscn(struct lpfc_vport *vport) * flush the RSCN. Otherwise, the outstanding requests * need to complete. */ - vport->gidft_inp = 0; if (lpfc_issue_gidft(vport) > 0) return 1; } else { diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 3e7712cd6c9a..2fef54fab86d 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -708,8 +708,7 @@ lpfc_work_done(struct lpfc_hba *phba) HA_RXMASK)); } } - if ((phba->sli_rev == LPFC_SLI_REV4) && - (!list_empty(&pring->txq))) + if (phba->sli_rev == LPFC_SLI_REV4) lpfc_drain_txq(phba); /* * Turn on Ring interrupts @@ -3876,10 +3875,6 @@ int lpfc_issue_gidft(struct lpfc_vport *vport) { struct lpfc_hba *phba = vport->phba; - struct lpfc_nodelist *ndlp; - - list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) - ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME); /* Good status, issue CT Request to NameServer */ if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 98b80559c215..f43f0bacb77a 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2009-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * @@ -566,6 +566,7 @@ struct lpfc_register { /* The following BAR0 register sets are defined for if_type 0 and 2 UCNAs. */ #define LPFC_SLI_INTF 0x0058 +#define LPFC_SLI_ASIC_VER 0x009C #define LPFC_CTL_PORT_SEM_OFFSET 0x400 #define lpfc_port_smphr_perr_SHIFT 31 @@ -3912,6 +3913,7 @@ struct lpfc_acqe_link { #define LPFC_ASYNC_LINK_FAULT_NONE 0x0 #define LPFC_ASYNC_LINK_FAULT_LOCAL 0x1 #define LPFC_ASYNC_LINK_FAULT_REMOTE 0x2 +#define LPFC_ASYNC_LINK_FAULT_LR_LRR 0x3 #define lpfc_acqe_logical_link_speed_SHIFT 16 #define lpfc_acqe_logical_link_speed_MASK 0x0000FFFF #define lpfc_acqe_logical_link_speed_WORD word1 @@ -4616,6 +4618,9 @@ union lpfc_wqe128 { struct send_frame_wqe send_frame; }; +#define MAGIC_NUMER_G6 0xFEAA0003 +#define MAGIC_NUMER_G7 0xFEAA0005 + struct lpfc_grp_hdr { uint32_t size; uint32_t magic_number; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 7887468c71b4..7ae343b14630 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * @@ -1266,6 +1266,9 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba) uint64_t tot, data1, data2, data3; struct lpfc_nvmet_tgtport *tgtp; struct lpfc_register reg_data; + struct nvme_fc_local_port *localport; + struct lpfc_nvme_lport *lport; + struct lpfc_nvme_ctrl_stat *cstat; void __iomem *eqdreg = phba->sli4_hba.u.if_type2.EQDregaddr; vports = lpfc_create_vport_work_array(phba); @@ -1299,14 +1302,25 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba) tot += atomic_read(&tgtp->xmt_fcp_release); tot = atomic_read(&tgtp->rcv_fcp_cmd_in) - tot; } else { - tot = atomic_read(&phba->fc4NvmeIoCmpls); - data1 = atomic_read( - &phba->fc4NvmeInputRequests); - data2 = atomic_read( - &phba->fc4NvmeOutputRequests); - data3 = atomic_read( - &phba->fc4NvmeControlRequests); - tot = (data1 + data2 + data3) - tot; + localport = phba->pport->localport; + if (!localport || !localport->private) + goto skip_eqdelay; + lport = (struct lpfc_nvme_lport *) + localport->private; + tot = 0; + for (i = 0; + i < phba->cfg_nvme_io_channel; i++) { + cstat = &lport->cstat[i]; + data1 = atomic_read( + &cstat->fc4NvmeInputRequests); + data2 = atomic_read( + &cstat->fc4NvmeOutputRequests); + data3 = atomic_read( + &cstat->fc4NvmeControlRequests); + tot += (data1 + data2 + data3); + tot -= atomic_read( + &cstat->fc4NvmeIoCmpls); + } } } @@ -4265,32 +4279,24 @@ lpfc_sli4_fcf_redisc_wait_tmo(struct timer_list *t) * @phba: pointer to lpfc hba data structure. * @acqe_link: pointer to the async link completion queue entry. * - * This routine is to parse the SLI4 link-attention link fault code and - * translate it into the base driver's read link attention mailbox command - * status. - * - * Return: Link-attention status in terms of base driver's coding. + * This routine is to parse the SLI4 link-attention link fault code. **/ -static uint16_t +static void lpfc_sli4_parse_latt_fault(struct lpfc_hba *phba, struct lpfc_acqe_link *acqe_link) { - uint16_t latt_fault; - switch (bf_get(lpfc_acqe_link_fault, acqe_link)) { case LPFC_ASYNC_LINK_FAULT_NONE: case LPFC_ASYNC_LINK_FAULT_LOCAL: case LPFC_ASYNC_LINK_FAULT_REMOTE: - latt_fault = 0; + case LPFC_ASYNC_LINK_FAULT_LR_LRR: break; default: lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0398 Invalid link fault code: x%x\n", + "0398 Unknown link fault code: x%x\n", bf_get(lpfc_acqe_link_fault, acqe_link)); - latt_fault = MBXERR_ERROR; break; } - return latt_fault; } /** @@ -4565,9 +4571,12 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba, * the READ_TOPOLOGY completion routine to continue without actually * sending the READ_TOPOLOGY mailbox command to the port. */ - /* Parse and translate status field */ + /* Initialize completion status */ mb = &pmb->u.mb; - mb->mbxStatus = lpfc_sli4_parse_latt_fault(phba, acqe_link); + mb->mbxStatus = MBX_SUCCESS; + + /* Parse port fault information field */ + lpfc_sli4_parse_latt_fault(phba, acqe_link); /* Parse and translate link attention fields */ la = (struct lpfc_mbx_read_top *) &pmb->u.mb.un.varReadTop; @@ -4695,10 +4704,12 @@ lpfc_sli4_async_fc_evt(struct lpfc_hba *phba, struct lpfc_acqe_fc_la *acqe_fc) break; } - /* Parse and translate status field */ + /* Initialize completion status */ mb = &pmb->u.mb; - mb->mbxStatus = lpfc_sli4_parse_latt_fault(phba, - (void *)acqe_fc); + mb->mbxStatus = MBX_SUCCESS; + + /* Parse port fault information field */ + lpfc_sli4_parse_latt_fault(phba, (void *)acqe_fc); /* Parse and translate link attention fields */ la = (struct lpfc_mbx_read_top *)&pmb->u.mb.un.varReadTop; @@ -5103,7 +5114,7 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba, if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_DISCOVERY, - "2772 Issue FCF rediscover mabilbox " + "2772 Issue FCF rediscover mailbox " "command failed, fail through to FCF " "dead event\n"); spin_lock_irq(&phba->hbalock); @@ -5195,7 +5206,7 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba, lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_DISCOVERY, "2774 Issue FCF rediscover " - "mabilbox command failed, " + "mailbox command failed, " "through to CVL event\n"); spin_lock_irq(&phba->hbalock); phba->fcf.fcf_flag &= ~FCF_ACVL_DISC; @@ -5839,6 +5850,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) int fof_vectors = 0; int extra; uint64_t wwn; + u32 if_type; + u32 if_fam; phba->sli4_hba.num_online_cpu = num_online_cpus(); phba->sli4_hba.num_present_cpu = lpfc_present_cpu; @@ -6160,15 +6173,28 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) */ rc = lpfc_get_sli4_parameters(phba, mboxq); if (rc) { + if_type = bf_get(lpfc_sli_intf_if_type, + &phba->sli4_hba.sli_intf); + if_fam = bf_get(lpfc_sli_intf_sli_family, + &phba->sli4_hba.sli_intf); if (phba->sli4_hba.extents_in_use && phba->sli4_hba.rpi_hdrs_in_use) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2999 Unsupported SLI4 Parameters " "Extents and RPI headers enabled.\n"); + if (if_type == LPFC_SLI_INTF_IF_TYPE_0 && + if_fam == LPFC_SLI_INTF_FAMILY_BE2) { + mempool_free(mboxq, phba->mbox_mem_pool); + rc = -EIO; + goto out_free_bsmbx; + } + } + if (!(if_type == LPFC_SLI_INTF_IF_TYPE_0 && + if_fam == LPFC_SLI_INTF_FAMILY_BE2)) { + mempool_free(mboxq, phba->mbox_mem_pool); + rc = -EIO; + goto out_free_bsmbx; } - mempool_free(mboxq, phba->mbox_mem_pool); - rc = -EIO; - goto out_free_bsmbx; } mempool_free(mboxq, phba->mbox_mem_pool); @@ -6406,8 +6432,11 @@ lpfc_setup_driver_resource_phase2(struct lpfc_hba *phba) return error; } - /* workqueue for deferred irq use */ - phba->wq = alloc_workqueue("lpfc_wq", WQ_MEM_RECLAIM, 0); + /* The lpfc_wq workqueue for deferred irq use, is only used for SLI4 */ + if (phba->sli_rev == LPFC_SLI_REV4) + phba->wq = alloc_workqueue("lpfc_wq", WQ_MEM_RECLAIM, 0); + else + phba->wq = NULL; return 0; } @@ -6430,7 +6459,8 @@ lpfc_unset_driver_resource_phase2(struct lpfc_hba *phba) } /* Stop kernel worker thread */ - kthread_stop(phba->worker_thread); + if (phba->worker_thread) + kthread_stop(phba->worker_thread); } /** @@ -6895,12 +6925,6 @@ lpfc_create_shost(struct lpfc_hba *phba) atomic_set(&phba->fc4ScsiOutputRequests, 0); atomic_set(&phba->fc4ScsiControlRequests, 0); atomic_set(&phba->fc4ScsiIoCmpls, 0); - atomic_set(&phba->fc4NvmeInputRequests, 0); - atomic_set(&phba->fc4NvmeOutputRequests, 0); - atomic_set(&phba->fc4NvmeControlRequests, 0); - atomic_set(&phba->fc4NvmeIoCmpls, 0); - atomic_set(&phba->fc4NvmeLsRequests, 0); - atomic_set(&phba->fc4NvmeLsCmpls, 0); vport = lpfc_create_port(phba, phba->brd_no, &phba->pcidev->dev); if (!vport) return -ENODEV; @@ -7781,6 +7805,40 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) phba->sli4_hba.max_cfg_param.max_wq, phba->sli4_hba.max_cfg_param.max_rq); + /* + * Calculate NVME queue resources based on how + * many WQ/CQs are available. + */ + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + length = phba->sli4_hba.max_cfg_param.max_wq; + if (phba->sli4_hba.max_cfg_param.max_cq < + phba->sli4_hba.max_cfg_param.max_wq) + length = phba->sli4_hba.max_cfg_param.max_cq; + + /* + * Whats left after this can go toward NVME. + * The minus 6 accounts for ELS, NVME LS, MBOX + * fof plus a couple extra. When configured for + * NVMET, FCP io channel WQs are not created. + */ + length -= 6; + if (!phba->nvmet_support) + length -= phba->cfg_fcp_io_channel; + + if (phba->cfg_nvme_io_channel > length) { + lpfc_printf_log( + phba, KERN_ERR, LOG_SLI, + "2005 Reducing NVME IO channel to %d: " + "WQ %d CQ %d NVMEIO %d FCPIO %d\n", + length, + phba->sli4_hba.max_cfg_param.max_wq, + phba->sli4_hba.max_cfg_param.max_cq, + phba->cfg_nvme_io_channel, + phba->cfg_fcp_io_channel); + + phba->cfg_nvme_io_channel = length; + } + } } if (rc) @@ -10533,6 +10591,7 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) struct lpfc_pc_sli4_params *sli4_params; uint32_t mbox_tmo; int length; + bool exp_wqcq_pages = true; struct lpfc_sli4_parameters *mbx_sli4_parameters; /* @@ -10659,8 +10718,15 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) phba->nvme_support, phba->nvme_embed_pbde, phba->cfg_nvme_embed_cmd, phba->cfg_suppress_rsp); + if ((bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) == + LPFC_SLI_INTF_IF_TYPE_2) && + (bf_get(lpfc_sli_intf_sli_family, &phba->sli4_hba.sli_intf) == + LPFC_SLI_INTF_FAMILY_LNCR_A0)) + exp_wqcq_pages = false; + if ((bf_get(cfg_cqpsize, mbx_sli4_parameters) & LPFC_CQ_16K_PAGE_SZ) && (bf_get(cfg_wqpsize, mbx_sli4_parameters) & LPFC_WQ_16K_PAGE_SZ) && + exp_wqcq_pages && (sli4_params->wqsize & LPFC_WQ_SZ128_SUPPORT)) phba->enab_exp_wqcq_pages = 1; else @@ -11322,7 +11388,11 @@ lpfc_log_write_firmware_error(struct lpfc_hba *phba, uint32_t offset, uint32_t magic_number, uint32_t ftype, uint32_t fid, uint32_t fsize, const struct firmware *fw) { - if (offset == ADD_STATUS_FW_NOT_SUPPORTED) + if ((offset == ADD_STATUS_FW_NOT_SUPPORTED) || + (phba->pcidev->device == PCI_DEVICE_ID_LANCER_G6_FC && + magic_number != MAGIC_NUMER_G6) || + (phba->pcidev->device == PCI_DEVICE_ID_LANCER_G7_FC && + magic_number != MAGIC_NUMER_G7)) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3030 This firmware version is not supported on " "this HBA model. Device:%x Magic:%x Type:%x " @@ -11719,6 +11789,7 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev) lpfc_nvme_free(phba); lpfc_free_iocb_list(phba); + lpfc_unset_driver_resource_phase2(phba); lpfc_sli4_driver_resource_unset(phba); /* Unmap adapter Control and Doorbell registers */ diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 022060636ae1..1a803975bcbc 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -1936,31 +1936,14 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, goto out; } - /* When the rport rejected the FCP PRLI as unsupported. - * This should only happen in Pt2Pt so an NVME PRLI - * should be outstanding still. - */ - if (npr && ndlp->nlp_flag & NLP_FCP_PRLI_RJT) { + /* Adjust the nlp_type accordingly if the PRLI failed */ + if (npr) ndlp->nlp_fc4_type &= ~NLP_FC4_FCP; - goto out_err; - } - - /* The LS Req had some error. Don't let this be a - * target. - */ - if ((ndlp->fc4_prli_sent == 1) && - (ndlp->nlp_state == NLP_STE_PRLI_ISSUE) && - (ndlp->nlp_type & (NLP_FCP_TARGET | NLP_FCP_INITIATOR))) - /* The FCP PRLI completed successfully but - * the NVME PRLI failed. Since they are sent in - * succession, allow the FCP to complete. - */ - goto out_err; + if (nvpr) + ndlp->nlp_fc4_type &= ~NLP_FC4_NVME; - ndlp->nlp_prev_state = NLP_STE_PRLI_ISSUE; - ndlp->nlp_type |= NLP_FCP_INITIATOR; - lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); - return ndlp->nlp_state; + /* We can't set the DSM state till BOTH PRLIs complete */ + goto out_err; } if (npr && (npr->acceptRspCode == PRLI_REQ_EXECUTED) && @@ -1999,6 +1982,12 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, if (bf_get_be32(prli_disc, nvpr)) ndlp->nlp_type |= NLP_NVME_DISCOVERY; + /* This node is an NVME target. Adjust the command + * queue depth on this node to not exceed the available + * xris. + */ + ndlp->cmd_qdepth = phba->sli4_hba.nvme_xri_max; + /* * If prli_fba is set, the Target supports FirstBurst. * If prli_fb_sz is 0, the FirstBurst size is unlimited, diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 378dca40ca20..76a5a99605aa 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * @@ -334,7 +334,14 @@ lpfc_nvme_remoteport_delete(struct nvme_fc_remote_port *remoteport) "6146 remoteport delete of remoteport %p\n", remoteport); spin_lock_irq(&vport->phba->hbalock); - ndlp->nrport = NULL; + + /* The register rebind might have occurred before the delete + * downcall. Guard against this race. + */ + if (ndlp->upcall_flags & NLP_WAIT_FOR_UNREG) { + ndlp->nrport = NULL; + ndlp->upcall_flags &= ~NLP_WAIT_FOR_UNREG; + } spin_unlock_irq(&vport->phba->hbalock); /* Remove original register reference. The host transport @@ -357,15 +364,19 @@ lpfc_nvme_cmpl_gen_req(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, struct lpfc_dmabuf *buf_ptr; struct lpfc_nodelist *ndlp; - atomic_inc(&vport->phba->fc4NvmeLsCmpls); - pnvme_lsreq = (struct nvmefc_ls_req *)cmdwqe->context2; status = bf_get(lpfc_wcqe_c_status, wcqe) & LPFC_IOCB_STATUS_MASK; - if (status) { + + if (vport->localport) { lport = (struct lpfc_nvme_lport *)vport->localport->private; - if (bf_get(lpfc_wcqe_c_xb, wcqe)) - atomic_inc(&lport->cmpl_ls_xb); - atomic_inc(&lport->cmpl_ls_err); + if (lport) { + atomic_inc(&lport->fc4NvmeLsCmpls); + if (status) { + if (bf_get(lpfc_wcqe_c_xb, wcqe)) + atomic_inc(&lport->cmpl_ls_xb); + atomic_inc(&lport->cmpl_ls_err); + } + } } ndlp = (struct lpfc_nodelist *)cmdwqe->context1; @@ -570,6 +581,9 @@ lpfc_nvme_ls_req(struct nvme_fc_local_port *pnvme_lport, lport = (struct lpfc_nvme_lport *)pnvme_lport->private; rport = (struct lpfc_nvme_rport *)pnvme_rport->private; + if (unlikely(!lport) || unlikely(!rport)) + return -EINVAL; + vport = lport->vport; if (vport->load_flag & FC_UNLOADING) @@ -639,7 +653,7 @@ lpfc_nvme_ls_req(struct nvme_fc_local_port *pnvme_lport, pnvme_lsreq->rsplen, &pnvme_lsreq->rqstdma, &pnvme_lsreq->rspdma); - atomic_inc(&vport->phba->fc4NvmeLsRequests); + atomic_inc(&lport->fc4NvmeLsRequests); /* Hardcode the wait to 30 seconds. Connections are failing otherwise. * This code allows it all to work. @@ -690,6 +704,8 @@ lpfc_nvme_ls_abort(struct nvme_fc_local_port *pnvme_lport, struct lpfc_iocbq *wqe, *next_wqe; lport = (struct lpfc_nvme_lport *)pnvme_lport->private; + if (unlikely(!lport)) + return; vport = lport->vport; phba = vport->phba; @@ -949,28 +965,48 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, struct lpfc_nodelist *ndlp; struct lpfc_nvme_fcpreq_priv *freqpriv; struct lpfc_nvme_lport *lport; + struct lpfc_nvme_ctrl_stat *cstat; unsigned long flags; - uint32_t code, status; + uint32_t code, status, idx; uint16_t cid, sqhd, data; uint32_t *ptr; /* Sanity check on return of outstanding command */ if (!lpfc_ncmd || !lpfc_ncmd->nvmeCmd || !lpfc_ncmd->nrport) { + if (!lpfc_ncmd) { + lpfc_printf_vlog(vport, KERN_ERR, + LOG_NODE | LOG_NVME_IOERR, + "6071 Null lpfc_ncmd pointer. No " + "release, skip completion\n"); + return; + } + lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR, - "6071 Completion pointers bad on wqe %p.\n", - wcqe); + "6066 Missing cmpl ptrs: lpfc_ncmd %p, " + "nvmeCmd %p nrport %p\n", + lpfc_ncmd, lpfc_ncmd->nvmeCmd, + lpfc_ncmd->nrport); + + /* Release the lpfc_ncmd regardless of the missing elements. */ + lpfc_release_nvme_buf(phba, lpfc_ncmd); return; } - atomic_inc(&phba->fc4NvmeIoCmpls); - nCmd = lpfc_ncmd->nvmeCmd; rport = lpfc_ncmd->nrport; status = bf_get(lpfc_wcqe_c_status, wcqe); - if (status) { + + if (vport->localport) { lport = (struct lpfc_nvme_lport *)vport->localport->private; - if (bf_get(lpfc_wcqe_c_xb, wcqe)) - atomic_inc(&lport->cmpl_fcp_xb); - atomic_inc(&lport->cmpl_fcp_err); + if (lport) { + idx = lpfc_ncmd->cur_iocbq.hba_wqidx; + cstat = &lport->cstat[idx]; + atomic_inc(&cstat->fc4NvmeIoCmpls); + if (status) { + if (bf_get(lpfc_wcqe_c_xb, wcqe)) + atomic_inc(&lport->cmpl_fcp_xb); + atomic_inc(&lport->cmpl_fcp_err); + } + } } lpfc_nvmeio_data(phba, "NVME FCP CMPL: xri x%x stat x%x parm x%x\n", @@ -1163,7 +1199,8 @@ out_err: static int lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport, struct lpfc_nvme_buf *lpfc_ncmd, - struct lpfc_nodelist *pnode) + struct lpfc_nodelist *pnode, + struct lpfc_nvme_ctrl_stat *cstat) { struct lpfc_hba *phba = vport->phba; struct nvmefc_fcp_req *nCmd = lpfc_ncmd->nvmeCmd; @@ -1201,7 +1238,7 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport, } else { wqe->fcp_iwrite.initial_xfer_len = 0; } - atomic_inc(&phba->fc4NvmeOutputRequests); + atomic_inc(&cstat->fc4NvmeOutputRequests); } else { /* From the iread template, initialize words 7 - 11 */ memcpy(&wqe->words[7], @@ -1214,13 +1251,13 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport, /* Word 5 */ wqe->fcp_iread.rsrvd5 = 0; - atomic_inc(&phba->fc4NvmeInputRequests); + atomic_inc(&cstat->fc4NvmeInputRequests); } } else { /* From the icmnd template, initialize words 4 - 11 */ memcpy(&wqe->words[4], &lpfc_icmnd_cmd_template.words[4], sizeof(uint32_t) * 8); - atomic_inc(&phba->fc4NvmeControlRequests); + atomic_inc(&cstat->fc4NvmeControlRequests); } /* * Finish initializing those WQE fields that are independent @@ -1400,7 +1437,9 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, { int ret = 0; int expedite = 0; + int idx; struct lpfc_nvme_lport *lport; + struct lpfc_nvme_ctrl_stat *cstat; struct lpfc_vport *vport; struct lpfc_hba *phba; struct lpfc_nodelist *ndlp; @@ -1425,9 +1464,10 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, vport = lport->vport; if (unlikely(!hw_queue_handle)) { - lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_ABTS, - "6129 Fail Abort, NULL hw_queue_handle\n"); - ret = -EINVAL; + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, + "6117 Fail IO, NULL hw_queue_handle\n"); + atomic_inc(&lport->xmt_fcp_err); + ret = -EBUSY; goto out_fail; } @@ -1439,12 +1479,18 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, } if (vport->load_flag & FC_UNLOADING) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, + "6124 Fail IO, Driver unload\n"); + atomic_inc(&lport->xmt_fcp_err); ret = -ENODEV; goto out_fail; } freqpriv = pnvme_fcreq->private; if (unlikely(!freqpriv)) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, + "6158 Fail IO, NULL request data\n"); + atomic_inc(&lport->xmt_fcp_err); ret = -EINVAL; goto out_fail; } @@ -1462,32 +1508,26 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, */ ndlp = rport->ndlp; if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR, - "6053 rport %p, ndlp %p, DID x%06x " - "ndlp not ready.\n", + lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_NVME_IOERR, + "6053 Fail IO, ndlp not ready: rport %p " + "ndlp %p, DID x%06x\n", rport, ndlp, pnvme_rport->port_id); - - ndlp = lpfc_findnode_did(vport, pnvme_rport->port_id); - if (!ndlp) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR, - "6066 Missing node for DID %x\n", - pnvme_rport->port_id); - atomic_inc(&lport->xmt_fcp_bad_ndlp); - ret = -ENODEV; - goto out_fail; - } + atomic_inc(&lport->xmt_fcp_err); + ret = -EBUSY; + goto out_fail; } /* The remote node has to be a mapped target or it's an error. */ if ((ndlp->nlp_type & NLP_NVME_TARGET) && (ndlp->nlp_state != NLP_STE_MAPPED_NODE)) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR, - "6036 rport %p, DID x%06x not ready for " - "IO. State x%x, Type x%x\n", - rport, pnvme_rport->port_id, - ndlp->nlp_state, ndlp->nlp_type); + lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_NVME_IOERR, + "6036 Fail IO, DID x%06x not ready for " + "IO. State x%x, Type x%x Flg x%x\n", + pnvme_rport->port_id, + ndlp->nlp_state, ndlp->nlp_type, + ndlp->upcall_flags); atomic_inc(&lport->xmt_fcp_bad_ndlp); - ret = -ENODEV; + ret = -EBUSY; goto out_fail; } @@ -1508,6 +1548,12 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, */ if ((atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth) && !expedite) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, + "6174 Fail IO, ndlp qdepth exceeded: " + "idx %d DID %x pend %d qdepth %d\n", + lpfc_queue_info->index, ndlp->nlp_DID, + atomic_read(&ndlp->cmd_pending), + ndlp->cmd_qdepth); atomic_inc(&lport->xmt_fcp_qdepth); ret = -EBUSY; goto out_fail; @@ -1517,8 +1563,9 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, if (lpfc_ncmd == NULL) { atomic_inc(&lport->xmt_fcp_noxri); lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, - "6065 driver's buffer pool is empty, " - "IO failed\n"); + "6065 Fail IO, driver buffer pool is empty: " + "idx %d DID %x\n", + lpfc_queue_info->index, ndlp->nlp_DID); ret = -EBUSY; goto out_fail; } @@ -1543,15 +1590,6 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, lpfc_ncmd->ndlp = ndlp; lpfc_ncmd->start_time = jiffies; - lpfc_nvme_prep_io_cmd(vport, lpfc_ncmd, ndlp); - ret = lpfc_nvme_prep_io_dma(vport, lpfc_ncmd); - if (ret) { - ret = -ENOMEM; - goto out_free_nvme_buf; - } - - atomic_inc(&ndlp->cmd_pending); - /* * Issue the IO on the WQ indicated by index in the hw_queue_handle. * This identfier was create in our hardware queue create callback @@ -1560,7 +1598,23 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, * index to use and that they have affinitized a CPU to this hardware * queue. A hardware queue maps to a driver MSI-X vector/EQ/CQ/WQ. */ - lpfc_ncmd->cur_iocbq.hba_wqidx = lpfc_queue_info->index; + idx = lpfc_queue_info->index; + lpfc_ncmd->cur_iocbq.hba_wqidx = idx; + cstat = &lport->cstat[idx]; + + lpfc_nvme_prep_io_cmd(vport, lpfc_ncmd, ndlp, cstat); + ret = lpfc_nvme_prep_io_dma(vport, lpfc_ncmd); + if (ret) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, + "6175 Fail IO, Prep DMA: " + "idx %d DID %x\n", + lpfc_queue_info->index, ndlp->nlp_DID); + atomic_inc(&lport->xmt_fcp_err); + ret = -ENOMEM; + goto out_free_nvme_buf; + } + + atomic_inc(&ndlp->cmd_pending); lpfc_nvmeio_data(phba, "NVME FCP XMIT: xri x%x idx %d to %06x\n", lpfc_ncmd->cur_iocbq.sli4_xritag, @@ -1571,7 +1625,7 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, atomic_inc(&lport->xmt_fcp_wqerr); atomic_dec(&ndlp->cmd_pending); lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, - "6113 FCP could not issue WQE err %x " + "6113 Fail IO, Could not issue WQE err %x " "sid: x%x did: x%x oxid: x%x\n", ret, vport->fc_myDID, ndlp->nlp_DID, lpfc_ncmd->cur_iocbq.sli4_xritag); @@ -1605,11 +1659,11 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, out_free_nvme_buf: if (lpfc_ncmd->nvmeCmd->sg_cnt) { if (lpfc_ncmd->nvmeCmd->io_dir == NVMEFC_FCP_WRITE) - atomic_dec(&phba->fc4NvmeOutputRequests); + atomic_dec(&cstat->fc4NvmeOutputRequests); else - atomic_dec(&phba->fc4NvmeInputRequests); + atomic_dec(&cstat->fc4NvmeInputRequests); } else - atomic_dec(&phba->fc4NvmeControlRequests); + atomic_dec(&cstat->fc4NvmeControlRequests); lpfc_release_nvme_buf(phba, lpfc_ncmd); out_fail: return ret; @@ -2390,7 +2444,8 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) struct nvme_fc_port_info nfcp_info; struct nvme_fc_local_port *localport; struct lpfc_nvme_lport *lport; - int len; + struct lpfc_nvme_ctrl_stat *cstat; + int len, i; /* Initialize this localport instance. The vport wwn usage ensures * that NPIV is accounted for. @@ -2414,6 +2469,11 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) lpfc_nvme_template.max_sgl_segments = phba->cfg_nvme_seg_cnt + 1; lpfc_nvme_template.max_hw_queues = phba->cfg_nvme_io_channel; + cstat = kmalloc((sizeof(struct lpfc_nvme_ctrl_stat) * + phba->cfg_nvme_io_channel), GFP_KERNEL); + if (!cstat) + return -ENOMEM; + /* localport is allocated from the stack, but the registration * call allocates heap memory as well as the private area. */ @@ -2436,11 +2496,13 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) lport = (struct lpfc_nvme_lport *)localport->private; vport->localport = localport; lport->vport = vport; + lport->cstat = cstat; vport->nvmei_support = 1; atomic_set(&lport->xmt_fcp_noxri, 0); atomic_set(&lport->xmt_fcp_bad_ndlp, 0); atomic_set(&lport->xmt_fcp_qdepth, 0); + atomic_set(&lport->xmt_fcp_err, 0); atomic_set(&lport->xmt_fcp_wqerr, 0); atomic_set(&lport->xmt_fcp_abort, 0); atomic_set(&lport->xmt_ls_abort, 0); @@ -2449,6 +2511,16 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) atomic_set(&lport->cmpl_fcp_err, 0); atomic_set(&lport->cmpl_ls_xb, 0); atomic_set(&lport->cmpl_ls_err, 0); + atomic_set(&lport->fc4NvmeLsRequests, 0); + atomic_set(&lport->fc4NvmeLsCmpls, 0); + + for (i = 0; i < phba->cfg_nvme_io_channel; i++) { + cstat = &lport->cstat[i]; + atomic_set(&cstat->fc4NvmeInputRequests, 0); + atomic_set(&cstat->fc4NvmeOutputRequests, 0); + atomic_set(&cstat->fc4NvmeControlRequests, 0); + atomic_set(&cstat->fc4NvmeIoCmpls, 0); + } /* Don't post more new bufs if repost already recovered * the nvme sgls. @@ -2458,6 +2530,8 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) phba->sli4_hba.nvme_xri_max); vport->phba->total_nvme_bufs += len; } + } else { + kfree(cstat); } return ret; @@ -2520,6 +2594,7 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport) #if (IS_ENABLED(CONFIG_NVME_FC)) struct nvme_fc_local_port *localport; struct lpfc_nvme_lport *lport; + struct lpfc_nvme_ctrl_stat *cstat; int ret; if (vport->nvmei_support == 0) @@ -2528,6 +2603,7 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport) localport = vport->localport; vport->localport = NULL; lport = (struct lpfc_nvme_lport *)localport->private; + cstat = lport->cstat; lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME, "6011 Destroying NVME localport %p\n", @@ -2543,6 +2619,7 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport) * indefinitely or succeeds */ lpfc_nvme_lport_unreg_wait(vport, lport); + kfree(cstat); /* Regardless of the unregister upcall response, clear * nvmei_support. All rports are unregistered and the @@ -2607,6 +2684,7 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) struct nvme_fc_local_port *localport; struct lpfc_nvme_lport *lport; struct lpfc_nvme_rport *rport; + struct lpfc_nvme_rport *oldrport; struct nvme_fc_remote_port *remote_port; struct nvme_fc_port_info rpinfo; struct lpfc_nodelist *prev_ndlp; @@ -2639,7 +2717,9 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) rpinfo.port_name = wwn_to_u64(ndlp->nlp_portname.u.wwn); rpinfo.node_name = wwn_to_u64(ndlp->nlp_nodename.u.wwn); - if (!ndlp->nrport) + + oldrport = lpfc_ndlp_get_nrport(ndlp); + if (!oldrport) lpfc_nlp_get(ndlp); ret = nvme_fc_register_remoteport(localport, &rpinfo, &remote_port); @@ -2648,9 +2728,15 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) * a resume of the existing rport. Else this is a * new rport. */ + /* Guard against an unregister/reregister + * race that leaves the WAIT flag set. + */ + spin_lock_irq(&vport->phba->hbalock); + ndlp->upcall_flags &= ~NLP_WAIT_FOR_UNREG; + spin_unlock_irq(&vport->phba->hbalock); rport = remote_port->private; - if (ndlp->nrport) { - if (ndlp->nrport == remote_port->private) { + if (oldrport) { + if (oldrport == remote_port->private) { /* Same remoteport. Just reuse. */ lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NVME_DISC, @@ -2674,11 +2760,20 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) */ spin_lock_irq(&vport->phba->hbalock); ndlp->nrport = NULL; + ndlp->upcall_flags &= ~NLP_WAIT_FOR_UNREG; spin_unlock_irq(&vport->phba->hbalock); rport->ndlp = NULL; rport->remoteport = NULL; - if (prev_ndlp) - lpfc_nlp_put(ndlp); + + /* Reference only removed if previous NDLP is no longer + * active. It might be just a swap and removing the + * reference would cause a premature cleanup. + */ + if (prev_ndlp && prev_ndlp != ndlp) { + if ((!NLP_CHK_NODE_ACT(prev_ndlp)) || + (!prev_ndlp->nrport)) + lpfc_nlp_put(prev_ndlp); + } } /* Clean bind the rport to the ndlp. */ @@ -2746,7 +2841,7 @@ lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) if (!lport) goto input_err; - rport = ndlp->nrport; + rport = lpfc_ndlp_get_nrport(ndlp); if (!rport) goto input_err; @@ -2767,6 +2862,15 @@ lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) * The transport will update it. */ ndlp->upcall_flags |= NLP_WAIT_FOR_UNREG; + + /* Don't let the host nvme transport keep sending keep-alives + * on this remoteport. Vport is unloading, no recovery. The + * return values is ignored. The upcall is a courtesy to the + * transport. + */ + if (vport->load_flag & FC_UNLOADING) + (void)nvme_fc_set_remoteport_devloss(remoteport, 0); + ret = nvme_fc_unregister_remoteport(remoteport); if (ret != 0) { lpfc_nlp_put(ndlp); diff --git a/drivers/scsi/lpfc/lpfc_nvme.h b/drivers/scsi/lpfc/lpfc_nvme.h index 9216653e0441..04bd463dd043 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.h +++ b/drivers/scsi/lpfc/lpfc_nvme.h @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * @@ -30,21 +30,36 @@ #define LPFC_NVME_FB_SHIFT 9 #define LPFC_NVME_MAX_FB (1 << 20) /* 1M */ +#define lpfc_ndlp_get_nrport(ndlp) \ + ((!ndlp->nrport || (ndlp->upcall_flags & NLP_WAIT_FOR_UNREG)) \ + ? NULL : ndlp->nrport) + struct lpfc_nvme_qhandle { uint32_t index; /* WQ index to use */ uint32_t qidx; /* queue index passed to create */ uint32_t cpu_id; /* current cpu id at time of create */ }; +struct lpfc_nvme_ctrl_stat { + atomic_t fc4NvmeInputRequests; + atomic_t fc4NvmeOutputRequests; + atomic_t fc4NvmeControlRequests; + atomic_t fc4NvmeIoCmpls; +}; + /* Declare nvme-based local and remote port definitions. */ struct lpfc_nvme_lport { struct lpfc_vport *vport; struct completion lport_unreg_done; /* Add stats counters here */ + struct lpfc_nvme_ctrl_stat *cstat; + atomic_t fc4NvmeLsRequests; + atomic_t fc4NvmeLsCmpls; atomic_t xmt_fcp_noxri; atomic_t xmt_fcp_bad_ndlp; atomic_t xmt_fcp_qdepth; atomic_t xmt_fcp_wqerr; + atomic_t xmt_fcp_err; atomic_t xmt_fcp_abort; atomic_t xmt_ls_abort; atomic_t xmt_ls_err; diff --git a/drivers/scsi/lpfc/lpfc_nvmet.h b/drivers/scsi/lpfc/lpfc_nvmet.h index c1bcef3f103c..81f520abfd64 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.h +++ b/drivers/scsi/lpfc/lpfc_nvmet.h @@ -22,8 +22,10 @@ ********************************************************************/ #define LPFC_NVMET_DEFAULT_SEGS (64 + 1) /* 256K IOs */ -#define LPFC_NVMET_RQE_DEF_COUNT 512 -#define LPFC_NVMET_SUCCESS_LEN 12 +#define LPFC_NVMET_RQE_MIN_POST 128 +#define LPFC_NVMET_RQE_DEF_POST 512 +#define LPFC_NVMET_RQE_DEF_COUNT 2048 +#define LPFC_NVMET_SUCCESS_LEN 12 #define LPFC_NVMET_MRQ_OFF 0xffff #define LPFC_NVMET_MRQ_AUTO 0 diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 050f04418f5f..a94fb9f8bb44 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * @@ -1021,7 +1021,7 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) if (lpfc_test_rrq_active(phba, ndlp, lpfc_cmd->cur_iocbq.sli4_lxritag)) continue; - list_del(&lpfc_cmd->list); + list_del_init(&lpfc_cmd->list); found = 1; break; } @@ -1036,7 +1036,7 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) if (lpfc_test_rrq_active( phba, ndlp, lpfc_cmd->cur_iocbq.sli4_lxritag)) continue; - list_del(&lpfc_cmd->list); + list_del_init(&lpfc_cmd->list); found = 1; break; } @@ -3983,9 +3983,6 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, } #endif - if (pnode && NLP_CHK_NODE_ACT(pnode)) - atomic_dec(&pnode->cmd_pending); - if (lpfc_cmd->status) { if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT && (lpfc_cmd->result & IOERR_DRVR_MASK)) @@ -4125,6 +4122,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, msecs_to_jiffies(vport->cfg_max_scsicmpl_time))) { spin_lock_irqsave(shost->host_lock, flags); if (pnode && NLP_CHK_NODE_ACT(pnode)) { + atomic_dec(&pnode->cmd_pending); if (pnode->cmd_qdepth > atomic_read(&pnode->cmd_pending) && (atomic_read(&pnode->cmd_pending) > @@ -4138,16 +4136,8 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, } spin_unlock_irqrestore(shost->host_lock, flags); } else if (pnode && NLP_CHK_NODE_ACT(pnode)) { - if ((pnode->cmd_qdepth != vport->cfg_tgt_queue_depth) && - time_after(jiffies, pnode->last_change_time + - msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) { - spin_lock_irqsave(shost->host_lock, flags); - pnode->cmd_qdepth = vport->cfg_tgt_queue_depth; - pnode->last_change_time = jiffies; - spin_unlock_irqrestore(shost->host_lock, flags); - } + atomic_dec(&pnode->cmd_pending); } - lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); spin_lock_irqsave(&phba->hbalock, flags); @@ -4591,6 +4581,8 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) ndlp->nlp_portname.u.wwn[7]); goto out_tgt_busy; } + atomic_inc(&ndlp->cmd_pending); + lpfc_cmd = lpfc_get_scsi_buf(phba, ndlp); if (lpfc_cmd == NULL) { lpfc_rampdown_queue_depth(phba); @@ -4643,11 +4635,9 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) 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); if (err) { - atomic_dec(&ndlp->cmd_pending); lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, "3376 FCP could not issue IOCB err %x" "FCP cmd x%x <%d/%llu> " @@ -4691,6 +4681,7 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); lpfc_release_scsi_buf(phba, lpfc_cmd); out_host_busy: + atomic_dec(&ndlp->cmd_pending); return SCSI_MLQUEUE_HOST_BUSY; out_tgt_busy: @@ -4725,7 +4716,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) int ret = SUCCESS, status = 0; struct lpfc_sli_ring *pring_s4; int ret_val; - unsigned long flags, iflags; + unsigned long flags; DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waitq); status = fc_block_scsi_eh(cmnd); @@ -4825,16 +4816,16 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) abtsiocb->iocb_cmpl = lpfc_sli_abort_fcp_cmpl; abtsiocb->vport = vport; if (phba->sli_rev == LPFC_SLI_REV4) { - pring_s4 = lpfc_sli4_calc_ring(phba, iocb); + pring_s4 = lpfc_sli4_calc_ring(phba, abtsiocb); if (pring_s4 == NULL) { ret = FAILED; goto out_unlock; } /* Note: both hbalock and ring_lock must be set here */ - spin_lock_irqsave(&pring_s4->ring_lock, iflags); + spin_lock(&pring_s4->ring_lock); ret_val = __lpfc_sli_issue_iocb(phba, pring_s4->ringno, abtsiocb, 0); - spin_unlock_irqrestore(&pring_s4->ring_lock, iflags); + spin_unlock(&pring_s4->ring_lock); } else { ret_val = __lpfc_sli_issue_iocb(phba, LPFC_FCP_RING, abtsiocb, 0); diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h index 8e38e0204c47..c38e4da71f5f 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.h +++ b/drivers/scsi/lpfc/lpfc_scsi.h @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index cb17e2b2be81..4b70d53acb72 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * @@ -96,6 +96,34 @@ lpfc_get_iocb_from_iocbq(struct lpfc_iocbq *iocbq) return &iocbq->iocb; } +#if defined(CONFIG_64BIT) && defined(__LITTLE_ENDIAN) +/** + * lpfc_sli4_pcimem_bcopy - SLI4 memory copy function + * @srcp: Source memory pointer. + * @destp: Destination memory pointer. + * @cnt: Number of words required to be copied. + * Must be a multiple of sizeof(uint64_t) + * + * This function is used for copying data between driver memory + * and the SLI WQ. This function also changes the endianness + * of each word if native endianness is different from SLI + * endianness. This function can be called with or without + * lock. + **/ +void +lpfc_sli4_pcimem_bcopy(void *srcp, void *destp, uint32_t cnt) +{ + uint64_t *src = srcp; + uint64_t *dest = destp; + int i; + + for (i = 0; i < (int)cnt; i += sizeof(uint64_t)) + *dest++ = *src++; +} +#else +#define lpfc_sli4_pcimem_bcopy(a, b, c) lpfc_sli_pcimem_bcopy(a, b, c) +#endif + /** * lpfc_sli4_wq_put - Put a Work Queue Entry on an Work Queue * @q: The Work Queue to operate on. @@ -137,7 +165,7 @@ lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe128 *wqe) bf_set(wqe_wqec, &wqe->generic.wqe_com, 0); if (q->phba->sli3_options & LPFC_SLI4_PHWQ_ENABLED) bf_set(wqe_wqid, &wqe->generic.wqe_com, q->queue_id); - lpfc_sli_pcimem_bcopy(wqe, temp_wqe, q->entry_size); + lpfc_sli4_pcimem_bcopy(wqe, temp_wqe, q->entry_size); if (q->dpp_enable && q->phba->cfg_enable_dpp) { /* write to DPP aperture taking advatage of Combined Writes */ tmp = (uint8_t *)temp_wqe; @@ -240,7 +268,7 @@ lpfc_sli4_mq_put(struct lpfc_queue *q, struct lpfc_mqe *mqe) /* If the host has not yet processed the next entry then we are done */ if (((q->host_index + 1) % q->entry_count) == q->hba_index) return -ENOMEM; - lpfc_sli_pcimem_bcopy(mqe, temp_mqe, q->entry_size); + lpfc_sli4_pcimem_bcopy(mqe, temp_mqe, q->entry_size); /* Save off the mailbox pointer for completion */ q->phba->mbox = (MAILBOX_t *)temp_mqe; @@ -663,8 +691,8 @@ lpfc_sli4_rq_put(struct lpfc_queue *hq, struct lpfc_queue *dq, /* If the host has not yet processed the next entry then we are done */ if (((hq_put_index + 1) % hq->entry_count) == hq->hba_index) return -EBUSY; - lpfc_sli_pcimem_bcopy(hrqe, temp_hrqe, hq->entry_size); - lpfc_sli_pcimem_bcopy(drqe, temp_drqe, dq->entry_size); + lpfc_sli4_pcimem_bcopy(hrqe, temp_hrqe, hq->entry_size); + lpfc_sli4_pcimem_bcopy(drqe, temp_drqe, dq->entry_size); /* Update the host index to point to the next slot */ hq->host_index = ((hq_put_index + 1) % hq->entry_count); @@ -7199,7 +7227,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) lpfc_post_rq_buffer( phba, phba->sli4_hba.nvmet_mrq_hdr[i], phba->sli4_hba.nvmet_mrq_data[i], - LPFC_NVMET_RQE_DEF_COUNT, i); + phba->cfg_nvmet_mrq_post, i); } } @@ -8185,8 +8213,8 @@ lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) */ mbx_cmnd = bf_get(lpfc_mqe_command, mb); memset(phba->sli4_hba.bmbx.avirt, 0, sizeof(struct lpfc_bmbx_create)); - lpfc_sli_pcimem_bcopy(mb, phba->sli4_hba.bmbx.avirt, - sizeof(struct lpfc_mqe)); + lpfc_sli4_pcimem_bcopy(mb, phba->sli4_hba.bmbx.avirt, + sizeof(struct lpfc_mqe)); /* Post the high mailbox dma address to the port and wait for ready. */ dma_address = &phba->sli4_hba.bmbx.dma_address; @@ -8210,11 +8238,11 @@ lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) * If so, update the mailbox status so that the upper layers * can complete the request normally. */ - lpfc_sli_pcimem_bcopy(phba->sli4_hba.bmbx.avirt, mb, - sizeof(struct lpfc_mqe)); + lpfc_sli4_pcimem_bcopy(phba->sli4_hba.bmbx.avirt, mb, + sizeof(struct lpfc_mqe)); mbox_rgn = (struct lpfc_bmbx_create *) phba->sli4_hba.bmbx.avirt; - lpfc_sli_pcimem_bcopy(&mbox_rgn->mcqe, &mboxq->mcqe, - sizeof(struct lpfc_mcqe)); + lpfc_sli4_pcimem_bcopy(&mbox_rgn->mcqe, &mboxq->mcqe, + sizeof(struct lpfc_mcqe)); mcqe_status = bf_get(lpfc_mcqe_status, &mbox_rgn->mcqe); /* * When the CQE status indicates a failure and the mailbox status @@ -11300,11 +11328,11 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, unsigned long iflags; struct lpfc_sli_ring *pring_s4; - spin_lock_irq(&phba->hbalock); + spin_lock_irqsave(&phba->hbalock, iflags); /* all I/Os are in process of being flushed */ if (phba->hba_flag & HBA_FCP_IOQ_FLUSH) { - spin_unlock_irq(&phba->hbalock); + spin_unlock_irqrestore(&phba->hbalock, iflags); return 0; } sum = 0; @@ -11366,14 +11394,14 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, iocbq->iocb_flag |= LPFC_DRIVER_ABORTED; if (phba->sli_rev == LPFC_SLI_REV4) { - pring_s4 = lpfc_sli4_calc_ring(phba, iocbq); - if (pring_s4 == NULL) + pring_s4 = lpfc_sli4_calc_ring(phba, abtsiocbq); + if (!pring_s4) continue; /* Note: both hbalock and ring_lock must be set here */ - spin_lock_irqsave(&pring_s4->ring_lock, iflags); + spin_lock(&pring_s4->ring_lock); ret_val = __lpfc_sli_issue_iocb(phba, pring_s4->ringno, abtsiocbq, 0); - spin_unlock_irqrestore(&pring_s4->ring_lock, iflags); + spin_unlock(&pring_s4->ring_lock); } else { ret_val = __lpfc_sli_issue_iocb(phba, pring->ringno, abtsiocbq, 0); @@ -11385,7 +11413,7 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, else sum++; } - spin_unlock_irq(&phba->hbalock); + spin_unlock_irqrestore(&phba->hbalock, iflags); return sum; } @@ -12830,7 +12858,7 @@ lpfc_sli4_sp_handle_mbox_event(struct lpfc_hba *phba, struct lpfc_mcqe *mcqe) /* Move mbox data to caller's mailbox region, do endian swapping */ if (pmb->mbox_cmpl && mbox) - lpfc_sli_pcimem_bcopy(mbox, mqe, sizeof(struct lpfc_mqe)); + lpfc_sli4_pcimem_bcopy(mbox, mqe, sizeof(struct lpfc_mqe)); /* * For mcqe errors, conditionally move a modified error code to @@ -12913,7 +12941,7 @@ lpfc_sli4_sp_handle_mcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe) bool workposted; /* Copy the mailbox MCQE and convert endian order as needed */ - lpfc_sli_pcimem_bcopy(cqe, &mcqe, sizeof(struct lpfc_mcqe)); + lpfc_sli4_pcimem_bcopy(cqe, &mcqe, sizeof(struct lpfc_mcqe)); /* Invoke the proper event handling routine */ if (!bf_get(lpfc_trailer_async, &mcqe)) @@ -12944,6 +12972,17 @@ lpfc_sli4_sp_handle_els_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, int txcmplq_cnt = 0; int fcp_txcmplq_cnt = 0; + /* Check for response status */ + if (unlikely(bf_get(lpfc_wcqe_c_status, wcqe))) { + /* Log the error status */ + lpfc_printf_log(phba, KERN_INFO, LOG_SLI, + "0357 ELS CQE error: status=x%x: " + "CQE: %08x %08x %08x %08x\n", + bf_get(lpfc_wcqe_c_status, wcqe), + wcqe->word0, wcqe->total_data_placed, + wcqe->parameter, wcqe->word3); + } + /* Get an irspiocbq for later ELS response processing use */ irspiocbq = lpfc_sli_get_iocbq(phba); if (!irspiocbq) { @@ -13173,7 +13212,7 @@ lpfc_sli4_sp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq, bool workposted = false; /* Copy the work queue CQE and convert endian order if needed */ - lpfc_sli_pcimem_bcopy(cqe, &cqevt, sizeof(struct lpfc_cqe)); + lpfc_sli4_pcimem_bcopy(cqe, &cqevt, sizeof(struct lpfc_cqe)); /* Check and process for different type of WCQE and dispatch */ switch (bf_get(lpfc_cqe_code, &cqevt)) { @@ -13364,14 +13403,12 @@ lpfc_sli4_fp_handle_fcp_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, phba->lpfc_rampdown_queue_depth(phba); /* Log the error status */ - lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, - "0373 FCP complete error: status=x%x, " - "hw_status=x%x, total_data_specified=%d, " - "parameter=x%x, word3=x%x\n", + lpfc_printf_log(phba, KERN_INFO, LOG_SLI, + "0373 FCP CQE error: status=x%x: " + "CQE: %08x %08x %08x %08x\n", bf_get(lpfc_wcqe_c_status, wcqe), - bf_get(lpfc_wcqe_c_hw_status, wcqe), - wcqe->total_data_placed, wcqe->parameter, - wcqe->word3); + wcqe->word0, wcqe->total_data_placed, + wcqe->parameter, wcqe->word3); } /* Look up the FCP command IOCB and create pseudo response IOCB */ @@ -13581,7 +13618,7 @@ lpfc_sli4_fp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq, bool workposted = false; /* Copy the work queue CQE and convert endian order if needed */ - lpfc_sli_pcimem_bcopy(cqe, &wcqe, sizeof(struct lpfc_cqe)); + lpfc_sli4_pcimem_bcopy(cqe, &wcqe, sizeof(struct lpfc_cqe)); /* Check and process for different type of WCQE and dispatch */ switch (bf_get(lpfc_wcqe_c_code, &wcqe)) { @@ -19032,9 +19069,22 @@ lpfc_drain_txq(struct lpfc_hba *phba) struct lpfc_sglq *sglq; union lpfc_wqe128 wqe; uint32_t txq_cnt = 0; + struct lpfc_queue *wq; - pring = lpfc_phba_elsring(phba); - if (unlikely(!pring)) + if (phba->link_flag & LS_MDS_LOOPBACK) { + /* MDS WQE are posted only to first WQ*/ + wq = phba->sli4_hba.fcp_wq[0]; + if (unlikely(!wq)) + return 0; + pring = wq->pring; + } else { + wq = phba->sli4_hba.els_wq; + if (unlikely(!wq)) + return 0; + pring = lpfc_phba_elsring(phba); + } + + if (unlikely(!pring) || list_empty(&pring->txq)) return 0; spin_lock_irqsave(&pring->ring_lock, iflags); @@ -19075,7 +19125,7 @@ lpfc_drain_txq(struct lpfc_hba *phba) fail_msg = "to convert bpl to sgl"; else if (lpfc_sli4_iocb2wqe(phba, piocbq, &wqe)) fail_msg = "to convert iocb to wqe"; - else if (lpfc_sli4_wq_put(phba->sli4_hba.els_wq, &wqe)) + else if (lpfc_sli4_wq_put(wq, &wqe)) fail_msg = " - Wq is full"; else lpfc_sli_ringtxcmpl_put(phba, pring, piocbq); diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index e8b089abbfb3..18c23afcf46b 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * @@ -20,7 +20,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "12.0.0.1" +#define LPFC_DRIVER_VERSION "12.0.0.4" #define LPFC_DRIVER_NAME "lpfc" /* Used for SLI 2/3 */ |