This patch updates the lpfc driver such that it ties into the NVME FC
transport to add host (initiator) support.
It currently is an unoptimized NVME implementation.

The patch:
-Replaces stubs so that the local FC port is registered with the
 NVME FC transport as a "nvme local port"
-Replaces stubs in the discovery code so that nvme targets are
 registered with the NVME FC transport as a "nvme remote ports"
-Adds the lpfc_nvme.c file which contains all the entrypoints to support
 the NVME FC transport host interfaces. Supports sending of FC-4 LS and
 FCP IO requests to the base driver


Signed-off-by: James Smart <james.sm...@broadcom.com>
---
 drivers/scsi/lpfc/Makefile       |    3 +-
 drivers/scsi/lpfc/lpfc_crtn.h    |    3 +
 drivers/scsi/lpfc/lpfc_hbadisc.c |   26 +-
 drivers/scsi/lpfc/lpfc_init.c    |   15 +-
 drivers/scsi/lpfc/lpfc_nvme.c    | 1447 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 1489 insertions(+), 5 deletions(-)
 create mode 100644 drivers/scsi/lpfc/lpfc_nvme.c

diff --git a/drivers/scsi/lpfc/Makefile b/drivers/scsi/lpfc/Makefile
index 9606a13..cd7e1fc 100644
--- a/drivers/scsi/lpfc/Makefile
+++ b/drivers/scsi/lpfc/Makefile
@@ -30,4 +30,5 @@ obj-$(CONFIG_SCSI_LPFC) := lpfc.o
 
 lpfc-objs := lpfc_mem.o lpfc_sli.o lpfc_ct.o lpfc_els.o \
        lpfc_hbadisc.o  lpfc_init.o lpfc_mbox.o lpfc_nportdisc.o   \
-       lpfc_scsi.o lpfc_attr.o lpfc_vport.o lpfc_debugfs.o lpfc_bsg.o
+       lpfc_scsi.o lpfc_attr.o lpfc_vport.o lpfc_debugfs.o lpfc_bsg.o \
+       lpfc_nvme.o
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index bfac2c6..ab2b4eb 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -514,3 +514,6 @@ void lpfc_release_scsi_buf(struct lpfc_hba *, struct 
lpfc_scsi_buf *);
 struct lpfc_scsi_buf *lpfc_get_scsi_buf(struct lpfc_hba *,
                                        struct lpfc_nodelist *);
 
+/* NVME interfaces. */
+int lpfc_create_nvme_lport(struct lpfc_vport *);
+void lpfc_destroy_nvme_lport(struct lpfc_nvme *);
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index 2061636..e213cf0f7 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -4060,6 +4060,7 @@ lpfc_register_nvme_port(struct lpfc_vport *vport, struct 
lpfc_nodelist *ndlp)
        struct lpfc_nvme *pnvme = vport->pnvme;
        struct lpfc_nvme_lport *lport;
        struct lpfc_nvme_rport *rport;
+       struct nvme_fc_remote_port *remote_port;
        struct nvme_fc_port_info rpinfo;
        struct lpfc_nodelist *fndlp;
 
@@ -4152,7 +4153,30 @@ regit:
                else
                        rpinfo.fabric_name = lport->localport->fabric_name;
 
-               /* TODO: bind with nvme layer - register remote nvme port */
+               ret = nvme_fc_register_remoteport(lport->localport, &rpinfo,
+                                                 &remote_port);
+               if (!ret) {
+                       rport = remote_port->private;
+                       rport->remoteport = remote_port;
+                       rport->lport = lport;
+                       rport->ndlp = ndlp;
+                       ndlp->nrport = rport;
+                       INIT_LIST_HEAD(&rport->list);
+                       list_add_tail(&rport->list, &lport->rport_list);
+                       lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME | LOG_NODE,
+                                        "6031 Binding new rport to "
+                                        "lport %p using fabricName 0x%llx "
+                                        "Rport WWNN 0x%llx, Rport WWPN 0x%llx "
+                                        "DID x%06x Role x%x\n",
+                                        lport,
+                                        lport->localport->fabric_name,
+                                        rpinfo.node_name, rpinfo.port_name,
+                                        rpinfo.port_id, rpinfo.port_role);
+               } else
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME | LOG_NODE,
+                                        "6031 RemotePort Registration failed "
+                                        "err: %d, DID x%06x\n",
+                                        ret, ndlp->nlp_DID);
        } else {
                ret = -EINVAL;
                lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 53d3e75..1b786e7 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -5377,8 +5377,10 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
        if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
            LPFC_SLI_INTF_IF_TYPE_2) {
                rc = lpfc_pci_function_reset(phba);
-               if (unlikely(rc))
+               if (unlikely(rc)) {
+                       rc = -ENODEV;
                        goto out_free_mem;
+               }
                phba->temp_sensor_support = 1;
        }
 
@@ -10787,7 +10789,14 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const 
struct pci_device_id *pid)
                /* Create NVME binding with nvme_fc_transport. This
                 * ensures the vport is initialized.
                 */
-               /* TODO: bind with nvme layer */
+               error = lpfc_create_nvme_lport(vport);
+               if (error) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "6004 NVME registration failed, "
+                                       "error x%x\n",
+                                       error);
+                       goto out_disable_intr;
+               }
        }
 
        /* check for firmware upgrade or downgrade */
@@ -10858,8 +10867,8 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev)
        lpfc_destroy_vport_work_array(phba, vports);
 
        if (phba->cfg_nvme_io_channel) {
-               /* TODO: unbind with nvme layer */
                /* The nvme pointer is invalid post call. */
+               lpfc_destroy_nvme_lport(vport->pnvme);
                vport->pnvme = NULL;
        }
 
diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c
new file mode 100644
index 0000000..1b3ab8e
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_nvme.c
@@ -0,0 +1,1447 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Fibre Channel Host Bus Adapters.                                *
+ * Copyright (C) 2004-2016 Emulex.  All rights reserved.           *
+ * EMULEX and SLI are trademarks of Emulex.                        *
+ * www.emulex.com                                                  *
+ * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of version 2 of the GNU General       *
+ * Public License as published by the Free Software Foundation.    *
+ * This program is distributed in the hope that it will be useful. *
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND          *
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,  *
+ * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE      *
+ * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
+ * TO BE LEGALLY INVALID.  See the GNU General Public License for  *
+ * more details, a copy of which can be found in the file COPYING  *
+ * included with this package.                                     *
+ ********************************************************************/
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/unaligned.h>
+#include <linux/crc-t10dif.h>
+#include <net/checksum.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_fs.h>
+
+#include <linux/nvme-fc-driver.h>
+
+#include "lpfc_version.h"
+#include "lpfc_hw4.h"
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
+#include "lpfc_nl.h"
+#include "lpfc_disc.h"
+#include "lpfc_nvme.h"
+#include "lpfc.h"
+#include "lpfc_scsi.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_crtn.h"
+#include "lpfc_vport.h"
+
+/* NVME initiator-based functions */
+
+/**
+ * lpfc_nvme_create_hw_queue -
+ * @lpfc_pnvme: Pointer to the driver's nvme instance data
+ * @qidx: An cpu index used to affinitize IO queues and MSIX vectors.
+ * @handle: An opaque driver handle used in follow-up calls.
+ *
+ * Driver registers this routine to preallocate and initialize
+ * any internal data structures to bind the @qidx to its internal
+ * IO queues.
+ *
+ * Return value :
+ *   0 - Success
+ *   TODO:  What are the failure codes.
+ **/
+static int
+lpfc_nvme_create_hw_queue(struct nvme_fc_local_port *pnvme_lport,
+                      unsigned int qnum, u16 qsize,
+                      void **handle)
+{
+       uint32_t cpu = 0;
+       uint32_t qidx;
+       struct lpfc_nvme_lport *lport;
+       struct lpfc_vport *vport;
+       struct lpfc_nvme_qhandle *qhandle;
+
+       lport = (struct lpfc_nvme_lport *) pnvme_lport->private;
+       vport = lport->pnvme->vport;
+       qidx = 0;  /* Hardcode for now */
+
+       lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME,
+                        "6000 ENTER.  lpfc_pnvme %p, qidx x%x running "
+                        "cpu %d\n",
+                        lport, qidx, smp_processor_id());
+
+       /* Display all online CPUs. */
+       for_each_present_cpu(cpu) {
+               if (cpu_online(cpu)) {
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME,
+                                        "9999 CPU %d online\n",
+                                        cpu);
+                       if (cpu == qidx) {
+                               qhandle = kzalloc(
+                                         sizeof(struct lpfc_nvme_qhandle),
+                                         GFP_KERNEL);
+                               if (qhandle == NULL)
+                                       return -ENOMEM;
+
+                               qhandle->cpu_id = qidx;
+                               qhandle->wq_id = vport->last_fcp_wqidx;
+                               vport->last_fcp_wqidx =
+                                       (vport->last_fcp_wqidx + 1) %
+                                       vport->phba->cfg_nvme_io_channel;
+                               lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME,
+                                                "6073 Binding qidx %d to "
+                                                "fcp_wqidx %d in qhandle %p\n",
+                                                qidx, vport->last_fcp_wqidx,
+                                                qhandle);
+                               handle = (void **)&qhandle;
+                               return 0;
+                       }
+               } else
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME,
+                                        "9999 CPU %d offline\n",
+                                        cpu);
+       }
+
+       /* Stub in routine and return 0 for now. */
+       return -EINVAL;
+}
+
+/**
+ * lpfc_nvme_delete_hw_queue -
+ * @lpfc_pnvme: Pointer to the driver's nvme instance data
+ * @qidx: An cpu index used to affinitize IO queues and MSIX vectors.
+ * @handle: An opaque driver handle from lpfc_nvme_create_hw_queue
+ *
+ * Driver registers this routine to free
+ * any internal data structures to bind the @qidx to its internal
+ * IO queues.
+ *
+ * Return value :
+ *   0 - Success
+ *   TODO:  What are the failure codes.
+ **/
+static void
+lpfc_nvme_delete_hw_queue(struct nvme_fc_local_port *pnvme_lport,
+                      unsigned int qidx,
+                      void *handle)
+{
+       struct lpfc_nvme_lport *lport;
+       struct lpfc_vport *vport;
+
+       lport = (struct lpfc_nvme_lport *) pnvme_lport->private;
+       vport = lport->pnvme->vport;
+
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
+                       "6001 ENTER.  lpfc_pnvme %p, qidx x%xi qhandle %p\n",
+                       lport, qidx, handle);
+       kfree(handle);
+}
+
+static void
+lpfc_nvme_cmpl_gen_req(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
+                      struct lpfc_wcqe_complete *wcqe)
+{
+       struct lpfc_vport *vport = cmdwqe->vport;
+       uint32_t status;
+       struct nvmefc_ls_req *pnvme_lsreq;
+       struct lpfc_dmabuf *buf_ptr;
+       struct lpfc_nodelist *ndlp;
+
+       pnvme_lsreq = (struct nvmefc_ls_req *)cmdwqe->context2;
+       status = bf_get(lpfc_wcqe_c_status, wcqe) & LPFC_IOCB_STATUS_MASK;
+       ndlp = (struct lpfc_nodelist *)cmdwqe->context1;
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
+                        "6047 nvme cmpl Enter "
+                        "Data %p DID %x Xri: %x status %x cmd:%p lsreg:%p "
+                        "bmp:%p ndlp:%p\n",
+                        pnvme_lsreq, ndlp ? ndlp->nlp_DID : 0,
+                        cmdwqe->sli4_xritag, status,
+                        cmdwqe, pnvme_lsreq, cmdwqe->context3, ndlp);
+
+       if (cmdwqe->context3) {
+               buf_ptr = (struct lpfc_dmabuf *)cmdwqe->context3;
+               lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
+               kfree(buf_ptr);
+               cmdwqe->context3 = NULL;
+       }
+       if (pnvme_lsreq->done)
+               pnvme_lsreq->done(pnvme_lsreq, status);
+       else
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME,
+                                "6046 nvme cmpl without done call back? "
+                                "Data %p DID %x Xri: %x status %x\n",
+                               pnvme_lsreq, ndlp ? ndlp->nlp_DID : 0,
+                               cmdwqe->sli4_xritag, status);
+       if (ndlp) {
+               lpfc_nlp_put(ndlp);
+               cmdwqe->context1 = NULL;
+       }
+       lpfc_sli_release_iocbq(phba, cmdwqe);
+}
+
+static int
+lpfc_nvme_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
+                 struct lpfc_dmabuf *inp,
+                struct nvmefc_ls_req *pnvme_lsreq,
+            void (*cmpl)(struct lpfc_hba *, struct lpfc_iocbq *,
+                          struct lpfc_wcqe_complete *),
+            struct lpfc_nodelist *ndlp, uint32_t num_entry,
+            uint32_t tmo, uint8_t retry)
+{
+       struct lpfc_hba  *phba = vport->phba;
+       union lpfc_wqe *wqe;
+       struct lpfc_iocbq *genwqe;
+       struct ulp_bde64 *bpl;
+       struct ulp_bde64 bde;
+       int i, rc, xmit_len, first_len;
+
+       /* Allocate buffer for  command WQE */
+       genwqe = lpfc_sli_get_iocbq(phba);
+       if (genwqe == NULL)
+               return 1;
+
+       wqe = &genwqe->wqe;
+       memset(wqe, 0, sizeof(union lpfc_wqe));
+
+       genwqe->context3 = (uint8_t *)bmp;
+       genwqe->iocb_flag |= LPFC_IO_NVME_LS;
+
+       /* Save for completion so we can release these resources */
+       genwqe->context1 = lpfc_nlp_get(ndlp);
+       genwqe->context2 = (uint8_t *)pnvme_lsreq;
+       /* Fill in payload, bp points to frame payload */
+
+       if (!tmo)
+               /* FC spec states we need 3 * ratov for CT requests */
+               tmo = (3 * phba->fc_ratov);
+
+       /* For this command calculate the xmit length of the request bde. */
+       xmit_len = 0;
+       first_len = 0;
+       bpl = (struct ulp_bde64 *)bmp->virt;
+       for (i = 0; i < num_entry; i++) {
+               bde.tus.w = bpl[i].tus.w;
+               if (bde.tus.f.bdeFlags != BUFF_TYPE_BDE_64)
+                       break;
+               xmit_len += bde.tus.f.bdeSize;
+               if (i == 0)
+                       first_len = xmit_len;
+       }
+
+       genwqe->rsvd2 = num_entry;
+       genwqe->hba_wqidx = 0;
+
+       /* Words 0 - 2 */
+       wqe->generic.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+       wqe->generic.bde.tus.f.bdeSize = first_len;
+       wqe->generic.bde.addrLow = bpl[0].addrLow;
+       wqe->generic.bde.addrHigh = bpl[0].addrHigh;
+
+       /* Word 3 */
+       wqe->gen_req.request_payload_len = first_len;
+
+       /* Word 4 */
+
+       /* Word 5 */
+       bf_set(wqe_dfctl, &wqe->gen_req.wge_ctl, 0);
+       bf_set(wqe_si, &wqe->gen_req.wge_ctl, 1);
+       bf_set(wqe_la, &wqe->gen_req.wge_ctl, 1);
+       bf_set(wqe_rctl, &wqe->gen_req.wge_ctl, FC_RCTL_DD_UNSOL_CTL);
+       bf_set(wqe_type, &wqe->gen_req.wge_ctl, LPFC_FC4_TYPE_NVME);
+
+       /* Word 6 */
+       bf_set(wqe_ctxt_tag, &wqe->gen_req.wqe_com,
+              phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
+       bf_set(wqe_xri_tag, &wqe->gen_req.wqe_com, genwqe->sli4_xritag);
+
+       /* Word 7 */
+       bf_set(wqe_tmo, &wqe->gen_req.wqe_com, (vport->phba->fc_ratov-1));
+       bf_set(wqe_class, &wqe->gen_req.wqe_com, CLASS3);
+       bf_set(wqe_cmnd, &wqe->gen_req.wqe_com, CMD_GEN_REQUEST64_WQE);
+       bf_set(wqe_ct, &wqe->gen_req.wqe_com, SLI4_CT_RPI);
+
+       /* Word 8 */
+       wqe->gen_req.wqe_com.abort_tag = genwqe->iotag;
+
+       /* Word 9 */
+       bf_set(wqe_reqtag, &wqe->gen_req.wqe_com, genwqe->iotag);
+
+       /* Word 10 */
+       bf_set(wqe_dbde, &wqe->gen_req.wqe_com, 1);
+       bf_set(wqe_iod, &wqe->gen_req.wqe_com, LPFC_WQE_IOD_READ);
+       bf_set(wqe_qosd, &wqe->gen_req.wqe_com, 1);
+       bf_set(wqe_lenloc, &wqe->gen_req.wqe_com, LPFC_WQE_LENLOC_NONE);
+       bf_set(wqe_ebde_cnt, &wqe->gen_req.wqe_com, 0);
+
+       /* Word 11 */
+       bf_set(wqe_cqid, &wqe->gen_req.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
+       bf_set(wqe_cmd_type, &wqe->gen_req.wqe_com, OTHER_COMMAND);
+
+
+       /* Issue GEN REQ WQE for NPORT <did> */
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+                        "6050 Issue GEN REQ WQE to NPORT x%x "
+                        "Data: x%x x%x wq:%p lsreq:%p bmp:%p xmit:%d 1st:%d\n",
+                        ndlp->nlp_DID, genwqe->iotag,
+                        vport->port_state,
+                       genwqe, pnvme_lsreq, bmp, xmit_len, first_len);
+       genwqe->wqe_cmpl = cmpl;
+       genwqe->iocb_cmpl = NULL;
+       genwqe->drvrTimeout = tmo + LPFC_DRVR_TIMEOUT;
+       genwqe->vport = vport;
+       genwqe->retry = retry;
+
+       rc = lpfc_sli_issue_wqe(phba, LPFC_ELS_RING, genwqe);
+       if (rc == WQE_ERROR) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+                                "6045 Issue GEN REQ WQE to NPORT x%x "
+                                "Data: x%x x%x\n",
+                                ndlp->nlp_DID, genwqe->iotag,
+                                vport->port_state);
+               lpfc_sli_release_iocbq(phba, genwqe);
+               return 1;
+       }
+       return 0;
+}
+
+/**
+ * lpfc_nvme_ls_req - Issue an Link Service request
+ * @lpfc_pnvme: Pointer to the driver's nvme instance data
+ * @lpfc_nvme_lport: Pointer to the driver's local port data
+ * @lpfc_nvme_rport: Pointer to the rport getting the @lpfc_nvme_ereq
+ *
+ * Driver registers this routine to handle any link service request
+ * from the nvme_fc transport to a remote nvme-aware port.
+ *
+ * Return value :
+ *   0 - Success
+ *   TODO: What are the failure codes.
+ **/
+static int
+lpfc_nvme_ls_req(struct nvme_fc_local_port *pnvme_lport,
+                struct nvme_fc_remote_port *pnvme_rport,
+                struct nvmefc_ls_req *pnvme_lsreq)
+{
+       int ret = 0;
+       struct lpfc_nvme_lport *lport;
+       struct lpfc_vport *vport;
+       struct lpfc_nodelist *ndlp;
+       struct ulp_bde64 *bpl;
+       struct lpfc_dmabuf *bmp;
+
+       /* there are two dma buf in the request, actually there is one and
+       ** the second one is just the start address + cmd size.
+       ** Before calling lpfc_nvme_gen_req these buffers need to be wrapped
+       ** in a lpfc_dmabuf struct. When freeing we just free the wrapper
+       ** because the nvem layer owns the data bufs.
+       ** We do not have to break these packets open, we don't care what is in
+       ** them. And we do not have to look at the resonse data, we only care
+       ** that we got a response. All of the caring is going to happen in the
+       ** nvme-fc layer.
+       */
+
+       lport = (struct lpfc_nvme_lport *) pnvme_lport->private;
+       vport = lport->pnvme->vport;
+
+       ndlp = lpfc_findnode_did(vport, pnvme_rport->port_id);
+       if (!ndlp) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME,
+                                "6043 Could not find node for DID %x\n",
+                                pnvme_rport->port_id);
+               return 1;
+       }
+       bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+       if (!bmp) {
+
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME,
+                                "6044 Could not find node for DID %x\n",
+                                pnvme_rport->port_id);
+               return 2;
+       }
+       INIT_LIST_HEAD(&bmp->list);
+       bmp->virt = lpfc_mbuf_alloc(vport->phba, MEM_PRI, &(bmp->phys));
+       if (!bmp->virt) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME,
+                                "6042 Could not find node for DID %x\n",
+                                pnvme_rport->port_id);
+               kfree(bmp);
+               return 3;
+       }
+       bpl = (struct ulp_bde64 *)bmp->virt;
+       bpl->addrHigh = le32_to_cpu(putPaddrHigh(pnvme_lsreq->rqstdma));
+       bpl->addrLow = le32_to_cpu(putPaddrLow(pnvme_lsreq->rqstdma));
+       bpl->tus.f.bdeFlags = 0;
+       bpl->tus.f.bdeSize = pnvme_lsreq->rqstlen;
+       bpl->tus.w = le32_to_cpu(bpl->tus.w);
+       bpl++;
+
+       bpl->addrHigh = le32_to_cpu(putPaddrHigh(pnvme_lsreq->rspdma));
+       bpl->addrLow = le32_to_cpu(putPaddrLow(pnvme_lsreq->rspdma));
+       bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
+       bpl->tus.f.bdeSize = pnvme_lsreq->rsplen;
+       bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
+       /* Expand print to include key fields. */
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
+                        "6051 ENTER.  lport %p, rport %p lsreq%p rqstlen:%d "
+                        "rsplen:%d %llux %llux\n",
+                        pnvme_lport, pnvme_rport,
+                        pnvme_lsreq, pnvme_lsreq->rqstlen,
+                        pnvme_lsreq->rsplen, pnvme_lsreq->rqstdma,
+                        pnvme_lsreq->rspdma);
+
+       /* Hardcode the wait to 30 seconds.  Connections are failing otherwise.
+        * This code allows it all to work.
+        */
+       ret = lpfc_nvme_gen_req(vport, bmp, pnvme_lsreq->rqstaddr,
+                               pnvme_lsreq, lpfc_nvme_cmpl_gen_req,
+                               ndlp, 2, 30, 0);
+       if (ret != WQE_SUCCESS) {
+               lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
+                                "6052 EXIT. issue ls wqe failed lport %p, "
+                                "rport %p lsreq%p Status %x DID %x\n",
+                                pnvme_lport, pnvme_rport, pnvme_lsreq,
+                                ret, ndlp->nlp_DID);
+               lpfc_mbuf_free(vport->phba, bmp->virt, bmp->phys);
+               kfree(bmp);
+               return ret;
+       }
+
+       /* Stub in routine and return 0 for now. */
+       return ret;
+}
+
+/**
+ * lpfc_nvme_ls_abort - Issue an Link Service request
+ * @lpfc_pnvme: Pointer to the driver's nvme instance data
+ * @lpfc_nvme_lport: Pointer to the driver's local port data
+ * @lpfc_nvme_rport: Pointer to the rport getting the @lpfc_nvme_ereq
+ *
+ * Driver registers this routine to handle any link service request
+ * from the nvme_fc transport to a remote nvme-aware port.
+ *
+ * Return value :
+ *   0 - Success
+ *   TODO: What are the failure codes.
+ **/
+static void
+lpfc_nvme_ls_abort(struct nvme_fc_local_port *pnvme_lport,
+                  struct nvme_fc_remote_port *pnvme_rport,
+                  struct nvmefc_ls_req *pnvme_lsreq)
+{
+       struct lpfc_nvme_lport *lport;
+       struct lpfc_vport *vport;
+       struct lpfc_nodelist *ndlp;
+
+       lport = (struct lpfc_nvme_lport *) pnvme_lport->private;
+       vport = lport->pnvme->vport;
+
+       ndlp = lpfc_findnode_did(vport, pnvme_rport->port_id);
+       if (!ndlp) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME,
+                                "6043 Could not find node for DID %x\n",
+                                pnvme_rport->port_id);
+               return;
+       }
+
+       /* Expand print to include key fields. */
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
+                        "6006 ENTER.  lport %p, rport %p lsreq %p rqstlen:%d "
+                        "rsplen:%d %llux %llux\n",
+                        pnvme_lport, pnvme_rport,
+                        pnvme_lsreq, pnvme_lsreq->rqstlen,
+                        pnvme_lsreq->rsplen, pnvme_lsreq->rqstdma,
+                        pnvme_lsreq->rspdma);
+}
+
+/* Fix up the existing sgls for NVME IO. */
+static void
+lpfc_nvme_adj_fcp_sgls(struct lpfc_vport *vport,
+                      struct lpfc_scsi_buf *psb,
+                      struct nvmefc_fcp_req *nCmd)
+{
+       struct sli4_sge *sgl;
+       union lpfc_wqe128 *wqe128;
+       uint32_t *wptr, *dptr;
+
+       /*
+        * Adjust the FCP_CMD and FCP_RSP DMA data and sge_len to
+        * match NVME.  NVME sends 96 bytes. Also, use the
+        * nvme commands command and response dma addresses
+        * rather than the virtual memory to ease the restore
+        * operation.
+        */
+       sgl = (struct sli4_sge *)psb->fcp_bpl;
+       sgl->addr_hi = cpu_to_le32(putPaddrHigh(nCmd->cmddma));
+       sgl->addr_lo = cpu_to_le32(putPaddrLow(nCmd->cmddma));
+       sgl->word2 = le32_to_cpu(sgl->word2);
+       bf_set(lpfc_sli4_sge_last, sgl, 0);
+       sgl->word2 = cpu_to_le32(sgl->word2);
+       sgl->sge_len = cpu_to_le32(nCmd->cmdlen);
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME | LOG_FCP,
+                        "6063 Reconfig fcp_cmd to len %d bytes "
+                        "from cmddma 0x%llx\n",
+                        sgl->sge_len, nCmd->cmddma);
+       sgl++;
+
+       /* Setup the physical region for the FCP RSP */
+       sgl->addr_hi = cpu_to_le32(putPaddrHigh(nCmd->rspdma));
+       sgl->addr_lo = cpu_to_le32(putPaddrLow(nCmd->rspdma));
+       sgl->word2 = le32_to_cpu(sgl->word2);
+       bf_set(lpfc_sli4_sge_last, sgl, 1);
+       sgl->word2 = cpu_to_le32(sgl->word2);
+       sgl->sge_len = cpu_to_le32(nCmd->rsplen);
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME | LOG_FCP,
+                        "6066 Reconfig fcp_rsp to len %d bytes "
+                        "from rspdma 0x%llx\n",
+                        sgl->sge_len, nCmd->rspdma);
+
+       /*
+        * Get a local pointer to the built-in wqe and correct
+        * the fcp_cmd size to match NVME's 96 bytes and fix
+        * the dma address.
+        */
+
+       /* 128 byte wqe support here */
+       wqe128 = (union lpfc_wqe128 *)&psb->cur_iocbq.wqe;
+
+       /* Word 0-2 - NVME CMND IU (embedded payload) */
+       wqe128->generic.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_IMMED;
+       wqe128->generic.bde.tus.f.bdeSize = 60;
+       wqe128->generic.bde.addrHigh = 0;
+       wqe128->generic.bde.addrLow =  64;  /* Word 16 */
+
+       /* Word 10 */
+       bf_set(wqe_nvme, &wqe128->fcp_icmd.wqe_com, 1);
+       bf_set(wqe_wqes, &wqe128->fcp_icmd.wqe_com, 1);
+
+       /*
+        * Embed the payload in the last half of the WQE
+        * WQE words 16-30 get the NVME CMD IU payload
+        *
+        * WQE words 16-18 get payload Words 4-6
+        * WQE words 19-20 get payload Words 8-9
+        * WQE words 21-30 get payload Words 14-23
+        */
+       wptr = &wqe128->words[16];  /* WQE ptr */
+       dptr = (uint32_t *)nCmd->cmdaddr;  /* payload ptr */
+       dptr += 4;              /* Skip Words 0-3 in payload */
+       *wptr++ = *dptr++;      /* Word 4 */
+       *wptr++ = *dptr++;      /* Word 5 */
+       *wptr++ = *dptr++;      /* Word 6 */
+       dptr++;                 /* Skip Word 7 in payload */
+       *wptr++ = *dptr++;      /* Word 8 */
+       *wptr++ = *dptr++;      /* Word 9 */
+       dptr += 4;              /* Skip Words 10-13 in payload */
+       *wptr++ = *dptr++;      /* Word 14 */
+       *wptr++ = *dptr++;      /* Word 15 */
+       *wptr++ = *dptr++;      /* Word 16 */
+       *wptr++ = *dptr++;      /* Word 17 */
+       *wptr++ = *dptr++;      /* Word 18 */
+       *wptr++ = *dptr++;      /* Word 19 */
+       *wptr++ = *dptr++;      /* Word 20 */
+       *wptr++ = *dptr++;      /* Word 21 */
+       *wptr++ = *dptr++;      /* Word 22 */
+       *wptr = *dptr;          /* Word 23 */
+}
+
+/* Restore the psb fcp_cmd and fcp_rsp regions for fcp io. */
+static void
+lpfc_nvme_restore_fcp_sgls(struct lpfc_vport *vport,
+                          struct lpfc_scsi_buf *psb)
+{
+       struct sli4_sge *sgl;
+       dma_addr_t pdma_phys_fcp_cmd;
+       dma_addr_t pdma_phys_fcp_rsp;
+       dma_addr_t pdma_phys_bpl;
+       union lpfc_wqe *wqe;
+       int sgl_size;
+
+       sgl_size = vport->phba->cfg_sg_dma_buf_size -
+               (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp));
+
+       /* Just restore what lpfc_new_scsi_buf setup. */
+       psb->fcp_bpl = psb->data;
+       psb->fcp_cmnd = (psb->data + sgl_size);
+       psb->fcp_rsp = (struct fcp_rsp *)((uint8_t *)psb->fcp_cmnd +
+                                         sizeof(struct fcp_cmnd));
+
+       /* Initialize local short-hand pointers. */
+       sgl = (struct sli4_sge *)psb->fcp_bpl;
+       pdma_phys_bpl = psb->dma_handle;
+       pdma_phys_fcp_cmd = (psb->dma_handle + sgl_size);
+       pdma_phys_fcp_rsp = pdma_phys_fcp_cmd + sizeof(struct fcp_cmnd);
+
+       /*
+        * The first two bdes are the FCP_CMD and FCP_RSP.
+        * The balance are sg list bdes. Initialize the
+        * first two and leave the rest for queuecommand.
+        */
+       sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_cmd));
+       sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_cmd));
+       sgl->word2 = le32_to_cpu(sgl->word2);
+       bf_set(lpfc_sli4_sge_last, sgl, 0);
+       sgl->word2 = cpu_to_le32(sgl->word2);
+       sgl->sge_len = cpu_to_le32(sizeof(struct fcp_cmnd));
+       sgl++;
+
+       /* Setup the physical region for the FCP RSP */
+       sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_rsp));
+       sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_rsp));
+       sgl->word2 = le32_to_cpu(sgl->word2);
+       bf_set(lpfc_sli4_sge_last, sgl, 1);
+       sgl->word2 = cpu_to_le32(sgl->word2);
+       sgl->sge_len = cpu_to_le32(sizeof(struct fcp_rsp));
+
+       /*
+        * Get a local pointer to the built-in wqe and correct
+        * the fcp_cmd size to match NVME's 96 bytes and fix
+        * the dma address.
+        */
+       wqe = &psb->cur_iocbq.wqe;
+       wqe->generic.bde.tus.f.bdeSize = sizeof(struct fcp_cmnd);
+       wqe->generic.bde.addrLow = putPaddrLow(pdma_phys_fcp_cmd);
+       wqe->generic.bde.addrHigh = putPaddrHigh(pdma_phys_fcp_cmd);
+}
+
+/**
+ * lpfc_nvme_io_cmd_wqe_cmpl - Complete an NVME-over-FCP IO
+ * @lpfc_pnvme: Pointer to the driver's nvme instance data
+ * @lpfc_nvme_lport: Pointer to the driver's local port data
+ * @lpfc_nvme_rport: Pointer to the rport getting the @lpfc_nvme_ereq
+ *
+ * Driver registers this routine as it io request handler.  This
+ * routine issues an fcp WQE with data from the @lpfc_nvme_fcpreq
+ * data structure to the rport indicated in @lpfc_nvme_rport.
+ *
+ * Return value :
+ *   0 - Success
+ *   TODO: What are the failure codes.
+ **/
+static void
+lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn,
+                         struct lpfc_wcqe_complete *wcqe)
+{
+       struct lpfc_scsi_buf *lpfc_cmd =
+               (struct lpfc_scsi_buf *)pwqeIn->context1;
+       struct lpfc_vport *vport = pwqeIn->vport;
+       struct nvmefc_fcp_req *nCmd;
+       struct lpfc_nvme_rport *rport;
+       struct lpfc_nodelist *ndlp;
+       unsigned long flags;
+       uint32_t code, len = 0;
+       uint16_t cid, sqhd;
+       uint32_t *ptr;
+
+       /* Sanity check on return of outstanding command */
+       if (!lpfc_cmd || !lpfc_cmd->nvmeCmd || !lpfc_cmd->nrport) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME,
+                                "6071 Completion pointers bad on wqe %p.\n",
+                                wcqe);
+               return;
+       }
+
+       nCmd = lpfc_cmd->nvmeCmd;
+       rport = lpfc_cmd->nrport;
+
+       /*
+        * Catch race where our node has transitioned, but the
+        * transport is still transitioning.
+        */
+       ndlp = rport->ndlp;
+       if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME,
+                                "6061 rport %p, ndlp %p, DID x%06x ndlp not 
ready.\n",
+                                rport, ndlp, rport->remoteport->port_id);
+
+               ndlp = lpfc_findnode_did(vport, rport->remoteport->port_id);
+               if (!ndlp) {
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME,
+                                        "6062 Ignoring NVME cmpl.  No ndlp\n");
+                       goto out_err;
+               }
+       }
+
+       code = bf_get(lpfc_wcqe_c_code, wcqe);
+       if (code == CQE_CODE_NVME_ERSP) {
+               /* For this type of CQE, we need to rebuild the rsp */
+
+               /*
+                * Get Command Id from cmd to plug into response. This
+                * code is not needed in the next NVME Transport drop.
+                */
+               ptr = (uint32_t *)nCmd->cmdaddr;/* to be removed */
+               ptr += 8;                       /* to be removed */
+               code = *ptr;                    /* to be removed */
+               code = le32_to_cpu(code);       /* to be removed */
+               cid = (code >> 16) & 0xffff;    /* to be removed */
+
+               /*
+                * RSN is in CQE word 2
+                * SQHD is in CQE Word 3 bits 15:0
+                * NOTE: information in CQE is Little Endian
+                */
+               ptr = (uint32_t *)wcqe;
+               sqhd = (uint16_t)(*(ptr+3) & 0xffff);
+
+               /* Build response */
+               ptr = (uint32_t *)nCmd->rspaddr;
+               *ptr++ = cpu_to_be32(8);  /* ERSP IU Length */
+               *ptr++ = cpu_to_be32(wcqe->parameter); /* RSN */
+               *ptr++ = 0;              /* Word 2 - reserved */
+               *ptr++ = 0;              /* Word 3 - reserved */
+               *ptr++ = 0;              /* Word 4 */
+               *ptr++ = 0;              /* Word 5 */
+               /* SQ ID is 0, SQHD from CQE */
+               *ptr++ = cpu_to_be32(sqhd);
+
+               /* Cmd ID from cmd payload */
+               *ptr = cid;     /* to be removed */
+
+               lpfc_cmd->status = IOSTAT_SUCCESS;
+               lpfc_cmd->result = 0;
+       } else {
+               lpfc_cmd->status = (bf_get(lpfc_wcqe_c_status, wcqe) &
+                           LPFC_IOCB_STATUS_MASK);
+               lpfc_cmd->result = wcqe->parameter;
+       }
+
+       /* For NVME, the only failure path that results in an
+        * IO error is when the adapter rejects it.  All other
+        * conditions are a success case and resolved by the
+        * transport.
+        */
+       if ((lpfc_cmd->status == IOSTAT_SUCCESS) ||
+           (lpfc_cmd->status == IOSTAT_FCP_RSP_ERROR)) {
+               nCmd->transferred_length = wcqe->total_data_placed;
+               nCmd->rcv_rsplen = 0;
+               if (lpfc_cmd->status == IOSTAT_FCP_RSP_ERROR)
+                       nCmd->rcv_rsplen = wcqe->parameter;
+               nCmd->status = 0;
+       } else
+               goto out_err;
+
+       lpfc_printf_vlog(vport, KERN_WARNING, LOG_NVME | LOG_FCP,
+                        "6059 NVME cmd %p completion "
+                        "io status: x%x rcv_rsplen: x%x "
+                        "sid: x%06x did: x%06x oxid: x%x "
+                        "total data placed x%x\n",
+                        nCmd, lpfc_cmd->status, nCmd->rcv_rsplen,
+                        vport->fc_myDID,
+                        (ndlp) ? ndlp->nlp_DID : 0,
+                        lpfc_cmd->cur_iocbq.sli4_xritag,
+                        nCmd->transferred_length);
+
+       /* pick up SLI4 exhange busy condition */
+       if (bf_get(lpfc_wcqe_c_xb, wcqe))
+               lpfc_cmd->flags |= LPFC_SBUF_XBUSY;
+       else
+               lpfc_cmd->flags &= ~LPFC_SBUF_XBUSY;
+
+       if (ndlp && NLP_CHK_NODE_ACT(ndlp))
+               atomic_dec(&ndlp->cmd_pending);
+
+       /* Update stats and complete the IO.  There is
+        * no need for dma unprep because the nvme_transport
+        * owns the dma address.
+        */
+       nCmd->done(nCmd);
+
+       spin_lock_irqsave(&phba->hbalock, flags);
+       lpfc_cmd->nvmeCmd = NULL;
+       lpfc_cmd->nrport = NULL;
+       spin_unlock_irqrestore(&phba->hbalock, flags);
+
+       goto out_cleanup;
+
+ out_err:
+       /* The lpfc_cmd is valid, but the ndlp may not be - don't
+        * touch it.
+        */
+       lpfc_cmd->result = wcqe->parameter;
+       nCmd->transferred_length = 0;
+       nCmd->rcv_rsplen = nCmd->rsplen;
+       nCmd->status = -EINVAL;
+       len = wcqe->parameter;
+       if (wcqe->parameter == 0)
+               len = nCmd->rsplen;
+
+       lpfc_printf_vlog(vport, KERN_WARNING, LOG_NVME | LOG_FCP,
+                        "6072 NVME Completion Error: status x%x, result x%x "
+                        "returning %d, rsplen %d.\n", lpfc_cmd->status,
+                        lpfc_cmd->result, nCmd->status,
+                        nCmd->rsplen);
+
+ out_cleanup:
+       lpfc_nvme_restore_fcp_sgls(vport, lpfc_cmd);
+       lpfc_release_scsi_buf(phba, lpfc_cmd);
+}
+
+
+/**
+ * lpfc_nvme_prep_io_cmd - Issue an NVME-over-FCP IO
+ * @lpfc_pnvme: Pointer to the driver's nvme instance data
+ * @lpfc_nvme_lport: Pointer to the driver's local port data
+ * @lpfc_nvme_rport: Pointer to the rport getting the @lpfc_nvme_ereq
+ * @lpfc_nvme_fcreq: IO request from nvme fc to driver.
+ * @hw_queue_handle: Driver-returned handle in lpfc_nvme_create_hw_queue
+ *
+ * Driver registers this routine as it io request handler.  This
+ * routine issues an fcp WQE with data from the @lpfc_nvme_fcpreq
+ * data structure to the rport indicated in @lpfc_nvme_rport.
+ *
+ * Return value :
+ *   0 - Success
+ *   TODO: What are the failure codes.
+ **/
+static int
+lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport,
+                     struct lpfc_scsi_buf *lpfc_cmd,
+                     struct lpfc_nodelist *pnode)
+{
+       struct lpfc_hba *phba = vport->phba;
+       struct nvmefc_fcp_req *nCmd = lpfc_cmd->nvmeCmd;
+       struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
+       union lpfc_wqe *wqe = &lpfc_cmd->cur_iocbq.wqe;
+       struct lpfc_iocbq *pwqeq = &(lpfc_cmd->cur_iocbq);
+
+       if (!pnode || !NLP_CHK_NODE_ACT(pnode))
+               return -EINVAL;
+
+       /*
+        * There are three possibilities here - use scatter-gather segment, use
+        * the single mapping, or neither.  Start the lpfc command prep by
+        * bumping the bpl beyond the fcp_cmnd and fcp_rsp regions to the first
+        * data bde entry.
+        */
+       wqe->generic.wqe_com.word7 = 0;
+       wqe->generic.wqe_com.word10 = 0;
+
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME | LOG_MISC,
+                        "6055 Prep NVME IO: sg_cnt %d, flags x%x\n",
+                        nCmd->sg_cnt, nCmd->io_dir);
+       if (nCmd->sg_cnt) {
+               if (nCmd->io_dir == NVMEFC_FCP_WRITE) {
+                       /* Word 3 */
+                       /* Add the FCP_CMD and FCP_RSP sizes for the offset */
+                       bf_set(payload_offset_len, &wqe->fcp_iwrite,
+                              sizeof(struct fcp_cmnd) +
+                              sizeof(struct fcp_rsp));
+
+                       /* Word 7 */
+                       bf_set(wqe_cmnd, &wqe->generic.wqe_com,
+                              CMD_FCP_IWRITE64_WQE);
+                       bf_set(wqe_pu, &wqe->generic.wqe_com,
+                              PARM_READ_CHECK);
+
+                       /* Word 10 */
+                       bf_set(wqe_iod, &wqe->fcp_iwrite.wqe_com,
+                              LPFC_WQE_IOD_WRITE);
+                       bf_set(wqe_lenloc, &wqe->fcp_iwrite.wqe_com,
+                              LPFC_WQE_LENLOC_WORD4);
+                       bf_set(wqe_ebde_cnt, &wqe->fcp_iwrite.wqe_com, 0);
+                       bf_set(wqe_dbde, &wqe->fcp_iwrite.wqe_com, 1);
+
+                       /* Word 11 */
+                       bf_set(wqe_cmd_type, &wqe->generic.wqe_com,
+                              NVME_WRITE_CMD);
+
+                       fcp_cmnd->fcpCntl3 = WRITE_DATA;
+                       phba->fc4OutputRequests++;
+               } else {
+                       /* Read IO.  Set up Word 3. */
+                       /* Add the FCP_CMD and FCP_RSP sizes for the offset */
+                       bf_set(payload_offset_len, &wqe->fcp_iread,
+                              sizeof(struct fcp_cmnd) +
+                              sizeof(struct fcp_rsp));
+
+                       /* Word 7 */
+                       bf_set(wqe_cmnd, &wqe->generic.wqe_com,
+                              CMD_FCP_IREAD64_WQE);
+                       bf_set(wqe_pu, &wqe->generic.wqe_com,
+                              PARM_READ_CHECK);
+
+                       /* Word 10 */
+                       bf_set(wqe_iod, &wqe->fcp_iread.wqe_com,
+                              LPFC_WQE_IOD_READ);
+                       bf_set(wqe_lenloc, &wqe->fcp_iread.wqe_com,
+                              LPFC_WQE_LENLOC_WORD4);
+                       bf_set(wqe_ebde_cnt, &wqe->fcp_iread.wqe_com, 0);
+                       bf_set(wqe_dbde, &wqe->fcp_iread.wqe_com, 1);
+
+                       /* Word 11 */
+                       bf_set(wqe_cmd_type, &wqe->generic.wqe_com,
+                              NVME_READ_CMD);
+
+                       fcp_cmnd->fcpCntl3 = READ_DATA;
+                       phba->fc4InputRequests++;
+               }
+       } else {
+               /* Word 4 */
+               wqe->fcp_icmd.rsrvd4 = 0;
+
+               /* Word 7 */
+               bf_set(wqe_cmnd, &wqe->generic.wqe_com, CMD_FCP_ICMND64_WQE);
+               bf_set(wqe_pu, &wqe->generic.wqe_com, 0);
+
+               /* Word 10 */
+               bf_set(wqe_dbde, &wqe->fcp_icmd.wqe_com, 1);
+               bf_set(wqe_iod, &wqe->fcp_icmd.wqe_com, LPFC_WQE_IOD_WRITE);
+               bf_set(wqe_qosd, &wqe->fcp_icmd.wqe_com, 1);
+               bf_set(wqe_lenloc, &wqe->fcp_icmd.wqe_com,
+                      LPFC_WQE_LENLOC_NONE);
+               bf_set(wqe_ebde_cnt, &wqe->fcp_icmd.wqe_com, 0);
+
+               /* Word 11 */
+               bf_set(wqe_cmd_type, &wqe->generic.wqe_com, NVME_READ_CMD);
+
+               fcp_cmnd->fcpCntl3 = 0;
+               phba->fc4ControlRequests++;
+       }
+
+       /*
+        * Finish initializing those WQE fields that are independent
+        * of the scsi_cmnd request_buffer
+        */
+
+       /* Word 6 */
+       bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com,
+              phba->sli4_hba.rpi_ids[pnode->nlp_rpi]);
+       bf_set(wqe_xri_tag, &wqe->generic.wqe_com, pwqeq->sli4_xritag);
+
+       /* Word 7:  Set erp to 0 for NVME.  */
+       bf_set(wqe_erp, &wqe->generic.wqe_com, 0);
+
+       /* Preserve Class data in the ndlp. */
+       bf_set(wqe_class, &wqe->generic.wqe_com,
+              (pnode->nlp_fcp_info & 0x0f));
+
+       /* NVME upper layers will time things out, if needed */
+       bf_set(wqe_tmo, &wqe->generic.wqe_com, 0);
+
+       /* Word 8 */
+       wqe->generic.wqe_com.abort_tag = pwqeq->iotag;
+
+       /* Word 9 */
+       bf_set(wqe_reqtag, &wqe->generic.wqe_com, pwqeq->iotag);
+
+       /* Word 11 */
+       bf_set(wqe_cqid, &wqe->generic.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
+
+       pwqeq->context1 = lpfc_cmd;
+       if (pwqeq->wqe_cmpl == NULL)
+               pwqeq->wqe_cmpl = lpfc_nvme_io_cmd_wqe_cmpl;
+       pwqeq->iocb_cmpl = NULL;
+       pwqeq->vport = vport;
+       pwqeq->iocb_flag |= LPFC_IO_NVME;
+       return 0;
+}
+
+
+/**
+ * lpfc_nvme_prep_io_dma - Issue an NVME-over-FCP IO
+ * @lpfc_pnvme: Pointer to the driver's nvme instance data
+ * @lpfc_nvme_lport: Pointer to the driver's local port data
+ * @lpfc_nvme_rport: Pointer to the rport getting the @lpfc_nvme_ereq
+ * @lpfc_nvme_fcreq: IO request from nvme fc to driver.
+ * @hw_queue_handle: Driver-returned handle in lpfc_nvme_create_hw_queue
+ *
+ * Driver registers this routine as it io request handler.  This
+ * routine issues an fcp WQE with data from the @lpfc_nvme_fcpreq
+ * data structure to the rport indicated in @lpfc_nvme_rport.
+ *
+ * Return value :
+ *   0 - Success
+ *   TODO: What are the failure codes.
+ **/
+static int
+lpfc_nvme_prep_io_dma(struct lpfc_vport *vport,
+                     struct lpfc_scsi_buf *lpfc_cmd)
+{
+       struct lpfc_hba *phba = vport->phba;
+       struct nvmefc_fcp_req *nCmd = lpfc_cmd->nvmeCmd;
+       struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
+       union lpfc_wqe *wqe_cmd = &lpfc_cmd->cur_iocbq.wqe;
+       struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl;
+       struct scatterlist *data_sg;
+       struct sli4_sge *first_data_sgl;
+       dma_addr_t physaddr;
+       uint32_t num_bde = 0;
+       uint32_t dma_len;
+       uint32_t dma_offset = 0;
+       int nseg, i;
+
+       /* Fix up the command and response DMA stuff. */
+       lpfc_nvme_adj_fcp_sgls(vport, lpfc_cmd, nCmd);
+
+       /*
+        * There are three possibilities here - use scatter-gather segment, use
+        * the single mapping, or neither.  Start the lpfc command prep by
+        * bumping the bpl beyond the fcp_cmnd and fcp_rsp regions to the first
+        * data bde entry.
+        */
+       if (nCmd->sg_cnt) {
+               /*
+                * Jump over the fcp_cmd and fcp_rsp.  The fix routine
+                * has already adjusted for this.
+                */
+               sgl += 2;
+
+               first_data_sgl = sgl;
+               lpfc_cmd->seg_cnt = nCmd->sg_cnt;
+               if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_NVME,
+                                       "6058 Too many sg segments from "
+                                       "NVME Transport.  Max %d, "
+                                       "nvmeIO sg_cnt %d\n",
+                                       phba->cfg_sg_seg_cnt,
+                                       lpfc_cmd->seg_cnt);
+                       lpfc_cmd->seg_cnt = 0;
+                       return 1;
+               }
+
+               /*
+                * The driver established a maximum scatter-gather segment count
+                * during probe that limits the number of sg elements in any
+                * single scsi command.  Just run through the seg_cnt and format
+                * the sge's.
+                */
+               nseg = nCmd->sg_cnt;
+               data_sg = nCmd->first_sgl;
+               for (i = 0; i < nseg; i++) {
+                       if (data_sg == NULL) {
+                               lpfc_printf_log(phba, KERN_ERR, LOG_NVME,
+                                       "9999 Segment count mismatch: %d "
+                                       "nvmeIO sg_cnt: %d\n", i, nseg);
+                               lpfc_cmd->seg_cnt = 0;
+                               return 1;
+                       }
+                       physaddr = data_sg->dma_address;
+                       dma_len = data_sg->length;
+                       sgl->addr_lo = cpu_to_le32(putPaddrLow(physaddr));
+                       sgl->addr_hi = cpu_to_le32(putPaddrHigh(physaddr));
+                       sgl->word2 = le32_to_cpu(sgl->word2);
+                       if ((num_bde + 1) == nseg)
+                               bf_set(lpfc_sli4_sge_last, sgl, 1);
+                       else
+                               bf_set(lpfc_sli4_sge_last, sgl, 0);
+                       bf_set(lpfc_sli4_sge_offset, sgl, dma_offset);
+                       bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DATA);
+                       sgl->word2 = cpu_to_le32(sgl->word2);
+                       sgl->sge_len = cpu_to_le32(dma_len);
+
+                       lpfc_printf_log(phba, KERN_INFO, LOG_NVME | LOG_FCP,
+                                       "9999 Set DMA seg: addr x%llx, "
+                                       "len x%x, seg %d of %d\n",
+                                       physaddr, dma_len, i, nseg);
+                       dma_offset += dma_len;
+                       data_sg = sg_next(data_sg);
+                       sgl++;
+               }
+       } else {
+               /* For this clause to be valid, the payload_length
+                * and sg_cnt must zero.
+                */
+               if (nCmd->payload_length != 0) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_NVME | LOG_FCP,
+                                       "9999 NVME DMA Prep Err: sg_cnt %d "
+                                       "payload_length x%x\n",
+                                       nCmd->sg_cnt, nCmd->payload_length);
+                       return 1;
+               }
+       }
+
+       /*
+        * Finish initializing those WQE fields that are dependent on the
+        * scsi_cmnd request_buffer.
+        */
+       fcp_cmnd->fcpDl = cpu_to_be32(nCmd->payload_length);
+
+       /*
+        * Due to difference in data length between DIF/non-DIF paths,
+        * we need to set word 4 of WQE here
+        */
+       wqe_cmd->fcp_iread.total_xfer_len = nCmd->payload_length;
+       return 0;
+}
+
+/**
+ * lpfc_nvme_fcp_io_submit - Issue an NVME-over-FCP IO
+ * @lpfc_pnvme: Pointer to the driver's nvme instance data
+ * @lpfc_nvme_lport: Pointer to the driver's local port data
+ * @lpfc_nvme_rport: Pointer to the rport getting the @lpfc_nvme_ereq
+ * @lpfc_nvme_fcreq: IO request from nvme fc to driver.
+ * @hw_queue_handle: Driver-returned handle in lpfc_nvme_create_hw_queue
+ *
+ * Driver registers this routine as it io request handler.  This
+ * routine issues an fcp WQE with data from the @lpfc_nvme_fcpreq
+ * data structure to the rport
+ indicated in @lpfc_nvme_rport.
+ *
+ * Return value :
+ *   0 - Success
+ *   TODO: What are the failure codes.
+ **/
+static int
+lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport,
+                       struct nvme_fc_remote_port *pnvme_rport,
+                       void *hw_queue_handle,
+                       struct nvmefc_fcp_req *pnvme_fcreq)
+{
+       int ret = 0;
+       struct lpfc_nvme_lport *lport;
+       struct lpfc_vport *vport;
+       struct lpfc_hba *phba;
+       struct lpfc_nodelist *ndlp;
+       struct lpfc_scsi_buf *lpfc_cmd;
+       struct lpfc_nvme_rport *rport;
+
+       lport = (struct lpfc_nvme_lport *)pnvme_lport->private;
+       rport = (struct lpfc_nvme_rport *)pnvme_rport->private;
+       vport = lport->pnvme->vport;
+       phba = vport->phba;
+
+       /* Announce entry to new IO submit field. */
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
+                        "6002 ENTER.  Issue IO to rport %p, DID x%06x "
+                        "on lport %p Data: %p %p\n",
+                        pnvme_rport, pnvme_rport->port_id, pnvme_lport,
+                        pnvme_fcreq, hw_queue_handle);
+
+       /*
+        * Catch race where our node has transitioned, but the
+        * transport is still transitioning.
+        */
+       ndlp = rport->ndlp;
+       if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME,
+                                "6053 rport %p, ndlp %p, DID x%06x ndlp not 
ready.\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,
+                                        "9999 Could not find node for DID 
%x\n",
+                                        pnvme_rport->port_id);
+                       ret = -ENODEV;
+                       goto out_fail;
+               }
+       }
+
+       /* The remote node has to be ready for IO or it's an error. */
+       if ((ndlp->nlp_state != NLP_STE_MAPPED_NODE) &&
+           !(ndlp->nlp_type & NLP_NVME_TARGET)) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME,
+                                "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);
+               ret = -ENODEV;
+               goto out_fail;
+
+       }
+
+       /* The node is shared with FCP IO, make sure the IO pending count does
+        * not exceed the programmed depth.
+        */
+       if (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth) {
+               ret = -EAGAIN;
+               goto out_fail;
+       }
+
+       /* For the prototype, the driver is reusing the lpfc_scsi_buf. */
+       lpfc_cmd = lpfc_get_scsi_buf(phba, ndlp);
+       if (lpfc_cmd == NULL) {
+               lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME | LOG_MISC,
+                                "6065 driver's buffer pool is empty, "
+                                "IO failed\n");
+               ret = -ENOMEM;
+               goto out_fail;
+       }
+
+       /*
+        * Store the data needed by the driver to issue and complete the IO.
+        * Do not let the IO hang out forever.  There is no midlayer issuing
+        * an abort so inform the FW of the maximum IO pending time.
+        */
+       lpfc_cmd->nvmeCmd = pnvme_fcreq;
+       lpfc_cmd->nrport = rport;
+
+       lpfc_cmd->start_time = jiffies;
+       lpfc_cmd->cur_iocbq.wqe_cmpl = NULL;
+
+       lpfc_nvme_prep_io_cmd(vport, lpfc_cmd, ndlp);
+       ret = lpfc_nvme_prep_io_dma(vport, lpfc_cmd);
+       if (ret) {
+               ret = -ENOMEM;
+               goto out_free_scsi_buf;
+       }
+
+       atomic_inc(&ndlp->cmd_pending);
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP | LOG_NVME,
+                        "9999 Issuing NVME IO to rport %p, "
+                        "DID x%06x on lport %p Data: %p x%llx\n",
+                        pnvme_rport, pnvme_rport->port_id, pnvme_lport,
+                        pnvme_fcreq, (uint64_t) hw_queue_handle);
+
+       ret = lpfc_sli_issue_wqe(phba, LPFC_FCP_RING, &lpfc_cmd->cur_iocbq);
+       if (ret) {
+               atomic_dec(&ndlp->cmd_pending);
+               lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP | LOG_NVME,
+                                "6056 FCP could not issue WQE err %x "
+                                "sid: x%x did: x%x oxid: x%x\n",
+                                ret, vport->fc_myDID, ndlp->nlp_DID,
+                                lpfc_cmd->cur_iocbq.sli4_xritag);
+               ret = -EINVAL;
+               goto out_free_scsi_buf;
+       }
+       return 0;
+
+ out_free_scsi_buf:
+       lpfc_nvme_restore_fcp_sgls(vport, lpfc_cmd);
+       lpfc_release_scsi_buf(phba, lpfc_cmd);
+ out_fail:
+       return ret;
+}
+
+/**
+ * lpfc_nvme_fcp_abort - Issue an NVME-over-FCP ABTS
+ * @lpfc_pnvme: Pointer to the driver's nvme instance data
+ * @lpfc_nvme_lport: Pointer to the driver's local port data
+ * @lpfc_nvme_rport: Pointer to the rport getting the @lpfc_nvme_ereq
+ * @lpfc_nvme_fcreq: IO request from nvme fc to driver.
+ * @hw_queue_handle: Driver-returned handle in lpfc_nvme_create_hw_queue
+ *
+ * Driver registers this routine as it io abort handler.  This
+ * routine issues an fcp WQE with data from the @lpfc_nvme_fcpreq
+ * data structure to the rport
+ indicated in @lpfc_nvme_rport.
+ *
+ * Return value :
+ *   0 - Success
+ *   TODO: What are the failure codes.
+ **/
+static void
+lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,
+                       struct nvme_fc_remote_port *pnvme_rport,
+                       void *hw_queue_handle,
+                       struct nvmefc_fcp_req *pnvme_fcreq)
+{
+       struct lpfc_nvme_lport *lport;
+       struct lpfc_vport *vport;
+       struct lpfc_hba *phba;
+       struct lpfc_nodelist *ndlp;
+       struct lpfc_nvme_rport *rport;
+
+       lport = (struct lpfc_nvme_lport *)pnvme_lport->private;
+       rport = (struct lpfc_nvme_rport *)pnvme_rport->private;
+       vport = lport->pnvme->vport;
+       phba = vport->phba;
+
+       /* Announce entry to new IO submit field. */
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
+                        "6002 ENTER.  Issue IO to rport %p, DID x%06x "
+                        "on lport %p Data: %p x%llx\n",
+                        pnvme_rport, pnvme_rport->port_id, pnvme_lport,
+                        pnvme_fcreq, (uint64_t) hw_queue_handle);
+
+       /*
+        * Catch race where our node has transitioned, but the
+        * transport is still transitioning.
+        */
+       ndlp = rport->ndlp;
+       if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME,
+                                "6053 rport %p, ndlp %p, DID x%06x ndlp not 
ready.\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,
+                                        "9999 Could not find node for DID 
%x\n",
+                                        pnvme_rport->port_id);
+                       goto out_fail;
+               }
+       }
+
+       /* The remote node has to be ready for IO or it's an error. */
+       if ((ndlp->nlp_state != NLP_STE_MAPPED_NODE) &&
+           !(ndlp->nlp_type & NLP_NVME_TARGET)) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME,
+                                "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);
+               goto out_fail;
+
+       }
+
+ out_fail:
+       return;
+}
+
+/* Declare and initialization an instance of the FC NVME template. */
+static struct nvme_fc_port_template lpfc_nvme_template = {
+       /* initiator-based functions */
+       .create_queue = lpfc_nvme_create_hw_queue,
+       .delete_queue = lpfc_nvme_delete_hw_queue,
+       .ls_req       = lpfc_nvme_ls_req,
+       .fcp_io       = lpfc_nvme_fcp_io_submit,
+       .ls_abort     = lpfc_nvme_ls_abort,
+       .fcp_abort    = lpfc_nvme_fcp_abort,
+
+       /* TBD.  Set max_hw_queues for now.  */
+       .max_hw_queues = 1,     /* LPFC_HBA_IO_CHAN_MAX, */
+       .max_sgl_segments = 16,
+       .max_dif_sgl_segments = 16,
+       .dma_boundary = 0xFFFFFFFF,
+
+       /* Sizes of additional private data for data structures.
+        * No use for the last two sizes at this time.
+        */
+       .local_priv_sz = sizeof(struct lpfc_nvme_lport),
+       .remote_priv_sz = sizeof(struct lpfc_nvme_rport),
+       .lsrqst_priv_sz = 0,
+       .fcprqst_priv_sz = 0,
+};
+
+/**
+ * lpfc_create_nvme_lport - Create/Bind an nvme localport instance.
+ * @pvport - the lpfc_vport instance requesting a localport.
+ *
+ * This routine is invoked to create an nvme localport instance to bind
+ * to the nvme_fc_transport.  It is called once during driver load
+ * like lpfc_create_shost after all other services are initialized.
+ * It requires a vport, vpi, and wwns at call time.  Other localport
+ * parameters are modified as the driver's FCID and the Fabric WWN
+ * are established.
+ *
+ * Return codes
+ *      0 - successful
+ *      -ENOMEM - no heap memory available
+ *      other values - from nvme registration upcall
+ **/
+int
+lpfc_create_nvme_lport(struct lpfc_vport *vport)
+{
+       struct nvme_fc_port_info nfcp_info;
+       struct nvme_fc_local_port *localport;
+       struct lpfc_nvme_lport *lport;
+       struct lpfc_nvme *pnvme = vport->pnvme;
+       int len, ret = 0;
+
+       /* Allocate memory for the NVME instance. */
+       pnvme = kzalloc(sizeof(struct lpfc_nvme), GFP_KERNEL);
+       if (!pnvme) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME,
+                                "6003 Failed to allocate nvme struct\n");
+               return -ENOMEM;
+       }
+
+       /* Complete initializing the nvme instance including back pointers. */
+       vport->pnvme = pnvme;
+       pnvme->vport = vport;
+       pnvme->lpfc_nvme_state = LPFC_NVME_INIT;
+       pnvme->lpfc_nvme_conn_state = LPFC_NVME_CONN_NONE;
+       INIT_LIST_HEAD(&pnvme->lport_list);
+
+       /* Initialize this localport instance.  The vport wwn usage ensures
+        * that NPIV is accounted for.
+        */
+       memset(&nfcp_info, 0, sizeof(struct nvme_fc_port_info));
+       nfcp_info.port_role = FC_PORT_ROLE_NVME_INITIATOR;
+       nfcp_info.node_name = wwn_to_u64(vport->fc_nodename.u.wwn);
+       nfcp_info.port_name = wwn_to_u64(vport->fc_portname.u.wwn);
+
+       /* localport is allocated from the stack, but the registration
+        * call allocates heap memory as well as the private area.
+        */
+       ret = nvme_fc_register_localport(&nfcp_info, &lpfc_nvme_template,
+                                        &vport->phba->pcidev->dev, &localport);
+       if (!ret) {
+               lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
+                                "6005 Successfully registered local "
+                                "NVME port num %d, localP %p, lport priv %p\n",
+                                localport->port_num, localport,
+                                localport->private);
+
+               /* Private is our lport size declared in the template. */
+               lport = (struct lpfc_nvme_lport *) localport->private;
+               lport->localport = localport;
+               lport->pnvme = pnvme;
+               INIT_LIST_HEAD(&lport->list);
+               INIT_LIST_HEAD(&lport->rport_list);
+               list_add_tail(&lport->list, &pnvme->lport_list);
+       }
+       len  = lpfc_new_scsi_buf(vport, 32);
+       vport->phba->total_scsi_bufs += len;
+       return ret;
+}
+
+/**
+ * lpfc_destroy_nvme_lport - Destroy lpfc_nvme bound to nvme transport.
+ * @pnvme: pointer to lpfc nvme data structure.
+ *
+ * This routine is invoked to destroy all lports bound to the phba.
+ * The lport memory was allocated by the nvme fc transport and is
+ * released there.  This routine ensures all rports bound to the
+ * lport have been disconnected.
+ *
+ **/
+void
+lpfc_destroy_nvme_lport(struct lpfc_nvme *pnvme)
+{
+       struct lpfc_nvme_lport *lport, *lport_next;
+       int ret;
+
+       lpfc_printf_vlog(pnvme->vport, KERN_INFO, LOG_NVME,
+                        "6007 Destroying NVME lport %p\n",
+                        pnvme);
+
+       list_for_each_entry_safe(lport, lport_next, &pnvme->lport_list, list) {
+               if (!list_empty(&lport->rport_list)) {
+                       lpfc_printf_vlog(pnvme->vport, KERN_ERR, LOG_NVME,
+                                        "6008 lport %p rport list not empty.  "
+                                        "Fail destroy.\n",
+                                        lport);
+                       return;
+               }
+               /*
+                * lport's rport list is clear.  Unregister lport and
+                * release resources.
+                */
+               list_del(&lport->list);
+               ret = nvme_fc_unregister_localport(lport->localport);
+               if (ret == 0)
+                       lpfc_printf_vlog(pnvme->vport,
+                                        KERN_INFO, LOG_NVME,
+                                        "6009 Unregistered lport "
+                                        "Success\n");
+               else
+                       lpfc_printf_vlog(pnvme->vport,
+                                        KERN_INFO, LOG_NVME,
+                                        "6010 Unregistered lport "
+                                        "Failed, status x%x\n",
+                                        ret);
+       }
+
+       /* All lports are unregistered.  Safe to free nvme memory. */
+       kfree(pnvme);
+}
+
-- 
2.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to