#include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_fs.h>
+
+#include <linux/nvme-fc-driver.h>
 
 #include "lpfc_hw4.h"
 #include "lpfc_hw.h"
 #include "lpfc_sli4.h"
 #include "lpfc_nl.h"
 #include "lpfc_disc.h"
-#include "lpfc_scsi.h"
 #include "lpfc.h"
+#include "lpfc_scsi.h"
+#include "lpfc_nvme.h"
 #include "lpfc_logmsg.h"
 #include "lpfc_crtn.h"
 #include "lpfc_vport.h"
 MODULE_PARM_DESC(lpfc_debugfs_max_slow_ring_trc,
        "Set debugfs slow ring trace depth");
 
+/* This MUST be a power of 2 */
+static int lpfc_debugfs_max_nvmeio_trc;
+module_param(lpfc_debugfs_max_nvmeio_trc, int, 0444);
+MODULE_PARM_DESC(lpfc_debugfs_max_nvmeio_trc,
+                "Set debugfs NVME IO trace depth");
+
 static int lpfc_debugfs_mask_disc_trc;
 module_param(lpfc_debugfs_mask_disc_trc, int, S_IRUGO);
 MODULE_PARM_DESC(lpfc_debugfs_mask_disc_trc,
        struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
        struct lpfc_nodelist *ndlp;
        unsigned char *statep;
+       struct nvme_fc_local_port *localport;
+       struct lpfc_nvme_lport *lport;
+       struct lpfc_nvme_rport *rport;
+       struct nvme_fc_remote_port *nrport;
 
        cnt = (LPFC_NODELIST_SIZE / LPFC_NODELIST_ENTRY_SIZE);
 
        }
        spin_unlock_irq(shost->host_lock);
 
+       len += snprintf(buf + len, size - len,
+                               "\nNVME Lport/Rport Entries ...\n");
+
+       localport = vport->localport;
+       if (!localport)
+               goto out_exit;
+
+       spin_lock_irq(shost->host_lock);
+       lport = (struct lpfc_nvme_lport *)localport->private;
+
+       /* Port state is only one of two values for now. */
+       if (localport->port_id)
+               statep = "ONLINE";
+       else
+               statep = "UNKNOWN ";
+
+       len += snprintf(buf + len, size - len,
+                       "Lport DID x%06x PortState %s\n",
+                       localport->port_id, statep);
+
+       len += snprintf(buf + len, size - len, "\tRport List:\n");
+       list_for_each_entry(rport, &lport->rport_list, list) {
+               /* local short-hand pointer. */
+               nrport = rport->remoteport;
+
+               /* Port state is only one of two values for now. */
+               switch (nrport->port_state) {
+               case FC_OBJSTATE_ONLINE:
+                       statep = "ONLINE";
+                       break;
+               case FC_OBJSTATE_UNKNOWN:
+                       statep = "UNKNOWN ";
+                       break;
+               default:
+                       statep = "UNSUPPORTED";
+                       break;
+               }
+
+               /* Tab in to show lport ownership. */
+               len += snprintf(buf + len, size - len,
+                               "\t%s Port ID:x%06x ",
+                               statep, nrport->port_id);
+               len += snprintf(buf + len, size - len, "WWPN x%llx ",
+                               nrport->port_name);
+               len += snprintf(buf + len, size - len, "WWNN x%llx ",
+                               nrport->node_name);
+               switch (nrport->port_role) {
+               case FC_PORT_ROLE_NVME_INITIATOR:
+                       len +=  snprintf(buf + len, size - len,
+                                        "NVME INITIATOR ");
+                       break;
+               case FC_PORT_ROLE_NVME_TARGET:
+                       len +=  snprintf(buf + len, size - len,
+                                        "NVME TARGET ");
+                       break;
+               case FC_PORT_ROLE_NVME_DISCOVERY:
+                       len +=  snprintf(buf + len, size - len,
+                                        "NVME DISCOVERY ");
+                       break;
+               default:
+                       len +=  snprintf(buf + len, size - len,
+                                        "UNKNOWN ROLE x%x",
+                                        nrport->port_role);
+                       break;
+               }
+
+               /* Terminate the string. */
+               len +=  snprintf(buf + len, size - len, "\n");
+       }
+
+       spin_unlock_irq(shost->host_lock);
+ out_exit:
+       return len;
+}
+
+/**
+ * lpfc_debugfs_nvmestat_data - Dump target node list to a buffer
+ * @vport: The vport to gather target node info from.
+ * @buf: The buffer to dump log into.
+ * @size: The maximum amount of data to process.
+ *
+ * Description:
+ * This routine dumps the NVME statistics associated with @vport
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into @buf and will
+ * not exceed @size.
+ **/
+static int
+lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size)
+{
+       struct lpfc_hba   *phba = vport->phba;
+       int len = 0;
+
+       if (phba->nvmet_support == 0) {
+               if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME))
+                       return len;
+
+               len += snprintf(buf + len, size - len,
+                               "\nNVME Lport Statistics\n");
+
+               len += snprintf(buf + len, size - len,
+                               "LS: Xmt %016llx Cmpl %016llx\n",
+                               phba->fc4NvmeLsRequests,
+                               phba->fc4NvmeLsCmpls);
+
+               len += snprintf(buf + len, size - len,
+                               "FCP: Rd %016llx Wr %016llx IO %016llx\n",
+                               phba->fc4NvmeInputRequests,
+                               phba->fc4NvmeOutputRequests,
+                               phba->fc4NvmeControlRequests);
+
+               len += snprintf(buf + len, size - len,
+                               "    Cmpl %016llx\n", phba->fc4NvmeIoCmpls);
+       }
+
+       return len;
+}
+
+
+/**
+ * lpfc_debugfs_nvmektime_data - Dump target node list to a buffer
+ * @vport: The vport to gather target node info from.
+ * @buf: The buffer to dump log into.
+ * @size: The maximum amount of data to process.
+ *
+ * Description:
+ * This routine dumps the NVME statistics associated with @vport
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into @buf and will
+ * not exceed @size.
+ **/
+static int
+lpfc_debugfs_nvmektime_data(struct lpfc_vport *vport, char *buf, int size)
+{
+       struct lpfc_hba   *phba = vport->phba;
+       int len = 0;
+
+       if (phba->nvmet_support == 0) {
+               /* NVME Initiator */
+               len += snprintf(buf + len, PAGE_SIZE - len,
+                               "ktime %s: Total Samples: %lld\n",
+                               (phba->ktime_on ?  "Enabled" : "Disabled"),
+                               phba->ktime_data_samples);
+               if (phba->ktime_data_samples == 0)
+                       return len;
+
+               len += snprintf(
+                       buf + len, PAGE_SIZE - len,
+                       "Segment 1: Last NVME Cmd cmpl "
+                       "done -to- Start of next NVME cnd (in driver)\n");
+               len += snprintf(
+                       buf + len, PAGE_SIZE - len,
+                       "avg:%08lld min:%08lld max %08lld\n",
+                       phba->ktime_seg1_total /
+                       phba->ktime_data_samples,
+                       phba->ktime_seg1_min,
+                       phba->ktime_seg1_max);
+               len += snprintf(
+                       buf + len, PAGE_SIZE - len,
+                       "Segment 2: Driver start of NVME cmd "
+                       "-to- Firmware WQ doorbell\n");
+               len += snprintf(
+                       buf + len, PAGE_SIZE - len,
+                       "avg:%08lld min:%08lld max %08lld\n",
+                       phba->ktime_seg2_total /
+                       phba->ktime_data_samples,
+                       phba->ktime_seg2_min,
+                       phba->ktime_seg2_max);
+               len += snprintf(
+                       buf + len, PAGE_SIZE - len,
+                       "Segment 3: Firmware WQ doorbell -to- "
+                       "MSI-X ISR cmpl\n");
+               len += snprintf(
+                       buf + len, PAGE_SIZE - len,
+                       "avg:%08lld min:%08lld max %08lld\n",
+                       phba->ktime_seg3_total /
+                       phba->ktime_data_samples,
+                       phba->ktime_seg3_min,
+                       phba->ktime_seg3_max);
+               len += snprintf(
+                       buf + len, PAGE_SIZE - len,
+                       "Segment 4: MSI-X ISR cmpl -to- "
+                       "NVME cmpl done\n");
+               len += snprintf(
+                       buf + len, PAGE_SIZE - len,
+                       "avg:%08lld min:%08lld max %08lld\n",
+                       phba->ktime_seg4_total /
+                       phba->ktime_data_samples,
+                       phba->ktime_seg4_min,
+                       phba->ktime_seg4_max);
+               len += snprintf(
+                       buf + len, PAGE_SIZE - len,
+                       "Total IO avg time: %08lld\n",
+                       ((phba->ktime_seg1_total +
+                       phba->ktime_seg2_total  +
+                       phba->ktime_seg3_total +
+                       phba->ktime_seg4_total) /
+                       phba->ktime_data_samples));
+               return len;
+       }
+
+       return len;
+}
+
+/**
+ * lpfc_debugfs_nvmeio_trc_data - Dump NVME IO trace list to a buffer
+ * @phba: The phba to gather target node info from.
+ * @buf: The buffer to dump log into.
+ * @size: The maximum amount of data to process.
+ *
+ * Description:
+ * This routine dumps the NVME IO trace associated with @phba
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into @buf and will
+ * not exceed @size.
+ **/
+static int
+lpfc_debugfs_nvmeio_trc_data(struct lpfc_hba *phba, char *buf, int size)
+{
+       struct lpfc_debugfs_nvmeio_trc *dtp;
+       int i, state, index, skip;
+       int len = 0;
+
+       state = phba->nvmeio_trc_on;
+
+       index = (atomic_read(&phba->nvmeio_trc_cnt) + 1) &
+               (phba->nvmeio_trc_size - 1);
+       skip = phba->nvmeio_trc_output_idx;
+
+       len += snprintf(buf + len, size - len,
+                       "%s IO Trace %s: next_idx %d skip %d size %d\n",
+                       (phba->nvmet_support ? "NVME" : "NVMET"),
+                       (state ? "Enabled" : "Disabled"),
+                       index, skip, phba->nvmeio_trc_size);
+
+       if (!phba->nvmeio_trc || state)
+               return len;
+
+       /* trace MUST bhe off to continue */
+
+       for (i = index; i < phba->nvmeio_trc_size; i++) {
+               if (skip) {
+                       skip--;
+                       continue;
+               }
+               dtp = phba->nvmeio_trc + i;
+               phba->nvmeio_trc_output_idx++;
+
+               if (!dtp->fmt)
+                       continue;
+
+               len +=  snprintf(buf + len, size - len, dtp->fmt,
+                       dtp->data1, dtp->data2, dtp->data3);
+
+               if (phba->nvmeio_trc_output_idx >= phba->nvmeio_trc_size) {
+                       phba->nvmeio_trc_output_idx = 0;
+                       len += snprintf(buf + len, size - len,
+                                       "Trace Complete\n");
+                       goto out;
+               }
+
+               if (len >= (size - LPFC_DEBUG_OUT_LINE_SZ)) {
+                       len += snprintf(buf + len, size - len,
+                                       "Trace Continue (%d of %d)\n",
+                                       phba->nvmeio_trc_output_idx,
+                                       phba->nvmeio_trc_size);
+                       goto out;
+               }
+       }
+       for (i = 0; i < index; i++) {
+               if (skip) {
+                       skip--;
+                       continue;
+               }
+               dtp = phba->nvmeio_trc + i;
+               phba->nvmeio_trc_output_idx++;
+
+               if (!dtp->fmt)
+                       continue;
+
+               len +=  snprintf(buf + len, size - len, dtp->fmt,
+                       dtp->data1, dtp->data2, dtp->data3);
+
+               if (phba->nvmeio_trc_output_idx >= phba->nvmeio_trc_size) {
+                       phba->nvmeio_trc_output_idx = 0;
+                       len += snprintf(buf + len, size - len,
+                                       "Trace Complete\n");
+                       goto out;
+               }
+
+               if (len >= (size - LPFC_DEBUG_OUT_LINE_SZ)) {
+                       len += snprintf(buf + len, size - len,
+                                       "Trace Continue (%d of %d)\n",
+                                       phba->nvmeio_trc_output_idx,
+                                       phba->nvmeio_trc_size);
+                       goto out;
+               }
+       }
+
+       len += snprintf(buf + len, size - len,
+                       "Trace Done\n");
+out:
+       return len;
+}
+
+/**
+ * lpfc_debugfs_cpucheck_data - Dump target node list to a buffer
+ * @vport: The vport to gather target node info from.
+ * @buf: The buffer to dump log into.
+ * @size: The maximum amount of data to process.
+ *
+ * Description:
+ * This routine dumps the NVME statistics associated with @vport
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into @buf and will
+ * not exceed @size.
+ **/
+static int
+lpfc_debugfs_cpucheck_data(struct lpfc_vport *vport, char *buf, int size)
+{
+       struct lpfc_hba   *phba = vport->phba;
+       int i;
+       int len = 0;
+       uint32_t tot_xmt = 0;
+       uint32_t tot_cmpl = 0;
+
+       if (phba->nvmet_support == 0) {
+               /* NVME Initiator */
+               len += snprintf(buf + len, PAGE_SIZE - len,
+                               "CPUcheck %s\n",
+                               (phba->cpucheck_on & LPFC_CHECK_NVME_IO ?
+                                       "Enabled" : "Disabled"));
+               for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
+                       if (i >= LPFC_CHECK_CPU_CNT)
+                               break;
+                       len += snprintf(buf + len, PAGE_SIZE - len,
+                                       "%02d: xmit x%08x cmpl x%08x\n",
+                                       i, phba->cpucheck_xmt_io[i],
+                                       phba->cpucheck_cmpl_io[i]);
+                       tot_xmt += phba->cpucheck_xmt_io[i];
+                       tot_cmpl += phba->cpucheck_cmpl_io[i];
+               }
+               len += snprintf(buf + len, PAGE_SIZE - len,
+                               "tot:xmit x%08x cmpl x%08x\n",
+                               tot_xmt, tot_cmpl);
+               return len;
+       }
+
        return len;
 }
 
        return;
 }
 
+/**
+ * lpfc_debugfs_nvme_trc - Store NVME/NVMET trace log
+ * @phba: The phba to associate this trace string with for retrieval.
+ * @fmt: Format string to be displayed when dumping the log.
+ * @data1: 1st data parameter to be applied to @fmt.
+ * @data2: 2nd data parameter to be applied to @fmt.
+ * @data3: 3rd data parameter to be applied to @fmt.
+ *
+ * Description:
+ * This routine is used by the driver code to add a debugfs log entry to the
+ * nvme trace buffer associated with @phba. @fmt, @data1, @data2, and
+ * @data3 are used like printf when displaying the log.
+ **/
+inline void
+lpfc_debugfs_nvme_trc(struct lpfc_hba *phba, char *fmt,
+                     uint16_t data1, uint16_t data2, uint32_t data3)
+{
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+       struct lpfc_debugfs_nvmeio_trc *dtp;
+       int index;
+
+       if (!phba->nvmeio_trc_on || !phba->nvmeio_trc)
+               return;
+
+       index = atomic_inc_return(&phba->nvmeio_trc_cnt) &
+               (phba->nvmeio_trc_size - 1);
+       dtp = phba->nvmeio_trc + index;
+       dtp->fmt = fmt;
+       dtp->data1 = data1;
+       dtp->data2 = data2;
+       dtp->data3 = data3;
+#endif
+}
+
 #ifdef CONFIG_SCSI_LPFC_DEBUG_FS
 /**
  * lpfc_debugfs_disc_trc_open - Open the discovery trace log
        return 0;
 }
 
+
+static int
+lpfc_debugfs_nvmestat_open(struct inode *inode, struct file *file)
+{
+       struct lpfc_vport *vport = inode->i_private;
+       struct lpfc_debug *debug;
+       int rc = -ENOMEM;
+
+       debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+       if (!debug)
+               goto out;
+
+        /* Round to page boundary */
+       debug->buffer = kmalloc(LPFC_NVMESTAT_SIZE, GFP_KERNEL);
+       if (!debug->buffer) {
+               kfree(debug);
+               goto out;
+       }
+
+       debug->len = lpfc_debugfs_nvmestat_data(vport, debug->buffer,
+               LPFC_NVMESTAT_SIZE);
+
+       debug->i_private = inode->i_private;
+       file->private_data = debug;
+
+       rc = 0;
+out:
+       return rc;
+}
+
+static int
+lpfc_debugfs_nvmektime_open(struct inode *inode, struct file *file)
+{
+       struct lpfc_vport *vport = inode->i_private;
+       struct lpfc_debug *debug;
+       int rc = -ENOMEM;
+
+       debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+       if (!debug)
+               goto out;
+
+        /* Round to page boundary */
+       debug->buffer = kmalloc(LPFC_NVMEKTIME_SIZE, GFP_KERNEL);
+       if (!debug->buffer) {
+               kfree(debug);
+               goto out;
+       }
+
+       debug->len = lpfc_debugfs_nvmektime_data(vport, debug->buffer,
+               LPFC_NVMEKTIME_SIZE);
+
+       debug->i_private = inode->i_private;
+       file->private_data = debug;
+
+       rc = 0;
+out:
+       return rc;
+}
+
+static ssize_t
+lpfc_debugfs_nvmektime_write(struct file *file, const char __user *buf,
+                            size_t nbytes, loff_t *ppos)
+{
+       struct lpfc_debug *debug = file->private_data;
+       struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private;
+       struct lpfc_hba   *phba = vport->phba;
+       char mybuf[64];
+       char *pbuf;
+
+       if (nbytes > 64)
+               nbytes = 64;
+
+       /* Protect copy from user */
+       if (!access_ok(VERIFY_READ, buf, nbytes))
+               return -EFAULT;
+
+       memset(mybuf, 0, sizeof(mybuf));
+
+       if (copy_from_user(mybuf, buf, nbytes))
+               return -EFAULT;
+       pbuf = &mybuf[0];
+
+       if ((strncmp(pbuf, "on", sizeof("on") - 1) == 0)) {
+               phba->ktime_data_samples = 0;
+               phba->ktime_status_samples = 0;
+               phba->ktime_seg1_total = 0;
+               phba->ktime_seg1_max = 0;
+               phba->ktime_seg1_min = 0xffffffff;
+               phba->ktime_seg2_total = 0;
+               phba->ktime_seg2_max = 0;
+               phba->ktime_seg2_min = 0xffffffff;
+               phba->ktime_seg3_total = 0;
+               phba->ktime_seg3_max = 0;
+               phba->ktime_seg3_min = 0xffffffff;
+               phba->ktime_seg4_total = 0;
+               phba->ktime_seg4_max = 0;
+               phba->ktime_seg4_min = 0xffffffff;
+               phba->ktime_seg5_total = 0;
+               phba->ktime_seg5_max = 0;
+               phba->ktime_seg5_min = 0xffffffff;
+               phba->ktime_seg6_total = 0;
+               phba->ktime_seg6_max = 0;
+               phba->ktime_seg6_min = 0xffffffff;
+               phba->ktime_seg7_total = 0;
+               phba->ktime_seg7_max = 0;
+               phba->ktime_seg7_min = 0xffffffff;
+               phba->ktime_seg8_total = 0;
+               phba->ktime_seg8_max = 0;
+               phba->ktime_seg8_min = 0xffffffff;
+               phba->ktime_seg9_total = 0;
+               phba->ktime_seg9_max = 0;
+               phba->ktime_seg9_min = 0xffffffff;
+               phba->ktime_seg10_total = 0;
+               phba->ktime_seg10_max = 0;
+               phba->ktime_seg10_min = 0xffffffff;
+
+               phba->ktime_on = 1;
+               return strlen(pbuf);
+       } else if ((strncmp(pbuf, "off",
+                  sizeof("off") - 1) == 0)) {
+               phba->ktime_on = 0;
+               return strlen(pbuf);
+       } else if ((strncmp(pbuf, "zero",
+                  sizeof("zero") - 1) == 0)) {
+               phba->ktime_data_samples = 0;
+               phba->ktime_status_samples = 0;
+               phba->ktime_seg1_total = 0;
+               phba->ktime_seg1_max = 0;
+               phba->ktime_seg1_min = 0xffffffff;
+               phba->ktime_seg2_total = 0;
+               phba->ktime_seg2_max = 0;
+               phba->ktime_seg2_min = 0xffffffff;
+               phba->ktime_seg3_total = 0;
+               phba->ktime_seg3_max = 0;
+               phba->ktime_seg3_min = 0xffffffff;
+               phba->ktime_seg4_total = 0;
+               phba->ktime_seg4_max = 0;
+               phba->ktime_seg4_min = 0xffffffff;
+               phba->ktime_seg5_total = 0;
+               phba->ktime_seg5_max = 0;
+               phba->ktime_seg5_min = 0xffffffff;
+               phba->ktime_seg6_total = 0;
+               phba->ktime_seg6_max = 0;
+               phba->ktime_seg6_min = 0xffffffff;
+               phba->ktime_seg7_total = 0;
+               phba->ktime_seg7_max = 0;
+               phba->ktime_seg7_min = 0xffffffff;
+               phba->ktime_seg8_total = 0;
+               phba->ktime_seg8_max = 0;
+               phba->ktime_seg8_min = 0xffffffff;
+               phba->ktime_seg9_total = 0;
+               phba->ktime_seg9_max = 0;
+               phba->ktime_seg9_min = 0xffffffff;
+               phba->ktime_seg10_total = 0;
+               phba->ktime_seg10_max = 0;
+               phba->ktime_seg10_min = 0xffffffff;
+               return strlen(pbuf);
+       }
+       return -EINVAL;
+}
+
+static int
+lpfc_debugfs_nvmeio_trc_open(struct inode *inode, struct file *file)
+{
+       struct lpfc_hba *phba = inode->i_private;
+       struct lpfc_debug *debug;
+       int rc = -ENOMEM;
+
+       debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+       if (!debug)
+               goto out;
+
+        /* Round to page boundary */
+       debug->buffer = kmalloc(LPFC_NVMEIO_TRC_SIZE, GFP_KERNEL);
+       if (!debug->buffer) {
+               kfree(debug);
+               goto out;
+       }
+
+       debug->len = lpfc_debugfs_nvmeio_trc_data(phba, debug->buffer,
+               LPFC_NVMEIO_TRC_SIZE);
+
+       debug->i_private = inode->i_private;
+       file->private_data = debug;
+
+       rc = 0;
+out:
+       return rc;
+}
+
+static ssize_t
+lpfc_debugfs_nvmeio_trc_write(struct file *file, const char __user *buf,
+                             size_t nbytes, loff_t *ppos)
+{
+       struct lpfc_debug *debug = file->private_data;
+       struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+       int i;
+       unsigned long sz;
+       char mybuf[64];
+       char *pbuf;
+
+       if (nbytes > 64)
+               nbytes = 64;
+
+       /* Protect copy from user */
+       if (!access_ok(VERIFY_READ, buf, nbytes))
+               return -EFAULT;
+
+       memset(mybuf, 0, sizeof(mybuf));
+
+       if (copy_from_user(mybuf, buf, nbytes))
+               return -EFAULT;
+       pbuf = &mybuf[0];
+
+       if ((strncmp(pbuf, "off", sizeof("off") - 1) == 0)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0570 nvmeio_trc_off\n");
+               phba->nvmeio_trc_output_idx = 0;
+               phba->nvmeio_trc_on = 0;
+               return strlen(pbuf);
+       } else if ((strncmp(pbuf, "on", sizeof("on") - 1) == 0)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0571 nvmeio_trc_on\n");
+               phba->nvmeio_trc_output_idx = 0;
+               phba->nvmeio_trc_on = 1;
+               return strlen(pbuf);
+       }
+
+       /* We must be off to allocate the trace buffer */
+       if (phba->nvmeio_trc_on != 0)
+               return -EINVAL;
+
+       /* If not on or off, the parameter is the trace buffer size */
+       i = kstrtoul(pbuf, 0, &sz);
+       if (i)
+               return -EINVAL;
+       phba->nvmeio_trc_size = (uint32_t)sz;
+
+       /* It must be a power of 2 - round down */
+       i = 0;
+       while (sz > 1) {
+               sz = sz >> 1;
+               i++;
+       }
+       sz = (1 << i);
+       if (phba->nvmeio_trc_size != sz)
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0572 nvmeio_trc_size changed to %ld\n",
+                               sz);
+       phba->nvmeio_trc_size = (uint32_t)sz;
+
+       /* If one previously exists, free it */
+       kfree(phba->nvmeio_trc);
+
+       /* Allocate new trace buffer and initialize */
+       phba->nvmeio_trc = kmalloc((sizeof(struct lpfc_debugfs_nvmeio_trc) *
+                                   sz), GFP_KERNEL);
+       if (!phba->nvmeio_trc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0573 Cannot create debugfs "
+                               "nvmeio_trc buffer\n");
+               return -ENOMEM;
+       }
+       memset(phba->nvmeio_trc, 0,
+              (sizeof(struct lpfc_debugfs_nvmeio_trc) * sz));
+       atomic_set(&phba->nvmeio_trc_cnt, 0);
+       phba->nvmeio_trc_on = 0;
+       phba->nvmeio_trc_output_idx = 0;
+
+       return strlen(pbuf);
+}
+
+static int
+lpfc_debugfs_cpucheck_open(struct inode *inode, struct file *file)
+{
+       struct lpfc_vport *vport = inode->i_private;
+       struct lpfc_debug *debug;
+       int rc = -ENOMEM;
+
+       debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+       if (!debug)
+               goto out;
+
+        /* Round to page boundary */
+       debug->buffer = kmalloc(LPFC_CPUCHECK_SIZE, GFP_KERNEL);
+       if (!debug->buffer) {
+               kfree(debug);
+               goto out;
+       }
+
+       debug->len = lpfc_debugfs_cpucheck_data(vport, debug->buffer,
+               LPFC_NVMEKTIME_SIZE);
+
+       debug->i_private = inode->i_private;
+       file->private_data = debug;
+
+       rc = 0;
+out:
+       return rc;
+}
+
+static ssize_t
+lpfc_debugfs_cpucheck_write(struct file *file, const char __user *buf,
+                           size_t nbytes, loff_t *ppos)
+{
+       struct lpfc_debug *debug = file->private_data;
+       struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private;
+       struct lpfc_hba   *phba = vport->phba;
+       char mybuf[64];
+       char *pbuf;
+       int i;
+
+       if (nbytes > 64)
+               nbytes = 64;
+
+       /* Protect copy from user */
+       if (!access_ok(VERIFY_READ, buf, nbytes))
+               return -EFAULT;
+
+       memset(mybuf, 0, sizeof(mybuf));
+
+       if (copy_from_user(mybuf, buf, nbytes))
+               return -EFAULT;
+       pbuf = &mybuf[0];
+
+       if ((strncmp(pbuf, "on", sizeof("on") - 1) == 0)) {
+               phba->cpucheck_on |= LPFC_CHECK_NVME_IO;
+               return strlen(pbuf);
+       } else if ((strncmp(pbuf, "rcv",
+                  sizeof("rcv") - 1) == 0)) {
+               return -EINVAL;
+       } else if ((strncmp(pbuf, "off",
+                  sizeof("off") - 1) == 0)) {
+               phba->cpucheck_on = LPFC_CHECK_OFF;
+               return strlen(pbuf);
+       } else if ((strncmp(pbuf, "zero",
+                  sizeof("zero") - 1) == 0)) {
+               for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
+                       if (i >= LPFC_CHECK_CPU_CNT)
+                               break;
+                       phba->cpucheck_rcv_io[i] = 0;
+                       phba->cpucheck_xmt_io[i] = 0;
+                       phba->cpucheck_cmpl_io[i] = 0;
+                       phba->cpucheck_ccmpl_io[i] = 0;
+               }
+               return strlen(pbuf);
+       }
+       return -EINVAL;
+}
+
 /*
  * ---------------------------------
  * iDiag debugfs file access methods
                        idiag.ptr_private = phba->sli4_hba.nvmels_cq;
                        goto pass_check;
                }
+               /* NVME LS complete queue */
+               if (phba->sli4_hba.nvmels_cq &&
+                   phba->sli4_hba.nvmels_cq->queue_id == queid) {
+                       /* Sanity check */
+                       rc = lpfc_idiag_que_param_check(
+                                       phba->sli4_hba.nvmels_cq, index, count);
+                       if (rc)
+                               goto error_out;
+                       idiag.ptr_private = phba->sli4_hba.nvmels_cq;
+                       goto pass_check;
+               }
                /* FCP complete queue */
                if (phba->sli4_hba.fcp_cq) {
                        for (qidx = 0; qidx < phba->cfg_fcp_io_channel;
                                }
                        }
                }
+               /* NVME complete queue */
+               if (phba->sli4_hba.nvme_cq) {
+                       qidx = 0;
+                       do {
+                               if (phba->sli4_hba.nvme_cq[qidx] &&
+                                   phba->sli4_hba.nvme_cq[qidx]->queue_id ==
+                                   queid) {
+                                       /* Sanity check */
+                                       rc = lpfc_idiag_que_param_check(
+                                               phba->sli4_hba.nvme_cq[qidx],
+                                               index, count);
+                                       if (rc)
+                                               goto error_out;
+                                       idiag.ptr_private =
+                                               phba->sli4_hba.nvme_cq[qidx];
+                                       goto pass_check;
+                               }
+                       } while (++qidx < phba->cfg_nvme_io_channel);
+               }
                goto error_out;
                break;
        case LPFC_IDIAG_MQ:
                        idiag.ptr_private = phba->sli4_hba.nvmels_wq;
                        goto pass_check;
                }
+               /* NVME LS work queue */
+               if (phba->sli4_hba.nvmels_wq &&
+                   phba->sli4_hba.nvmels_wq->queue_id == queid) {
+                       /* Sanity check */
+                       rc = lpfc_idiag_que_param_check(
+                                       phba->sli4_hba.nvmels_wq, index, count);
+                       if (rc)
+                               goto error_out;
+                       idiag.ptr_private = phba->sli4_hba.nvmels_wq;
+                       goto pass_check;
+               }
                /* FCP work queue */
                if (phba->sli4_hba.fcp_wq) {
                        for (qidx = 0; qidx < phba->cfg_fcp_io_channel;
                                }
                        }
                }
+
+               /* NVME work queues */
+               if (phba->sli4_hba.nvme_wq) {
+                       for (qidx = 0; qidx < phba->cfg_nvme_io_channel;
+                               qidx++) {
+                               if (!phba->sli4_hba.nvme_wq[qidx])
+                                       continue;
+                               if (phba->sli4_hba.nvme_wq[qidx]->queue_id ==
+                                   queid) {
+                                       /* Sanity check */
+                                       rc = lpfc_idiag_que_param_check(
+                                               phba->sli4_hba.nvme_wq[qidx],
+                                               index, count);
+                                       if (rc)
+                                               goto error_out;
+                                       idiag.ptr_private =
+                                               phba->sli4_hba.nvme_wq[qidx];
+                                       goto pass_check;
+                               }
+                       }
+               }
                goto error_out;
                break;
        case LPFC_IDIAG_RQ:
        .release =      lpfc_debugfs_release,
 };
 
+#undef lpfc_debugfs_op_nvmestat
+static const struct file_operations lpfc_debugfs_op_nvmestat = {
+       .owner =        THIS_MODULE,
+       .open =         lpfc_debugfs_nvmestat_open,
+       .llseek =       lpfc_debugfs_lseek,
+       .read =         lpfc_debugfs_read,
+       .release =      lpfc_debugfs_release,
+};
+
+#undef lpfc_debugfs_op_nvmektime
+static const struct file_operations lpfc_debugfs_op_nvmektime = {
+       .owner =        THIS_MODULE,
+       .open =         lpfc_debugfs_nvmektime_open,
+       .llseek =       lpfc_debugfs_lseek,
+       .read =         lpfc_debugfs_read,
+       .write =        lpfc_debugfs_nvmektime_write,
+       .release =      lpfc_debugfs_release,
+};
+
+#undef lpfc_debugfs_op_nvmeio_trc
+static const struct file_operations lpfc_debugfs_op_nvmeio_trc = {
+       .owner =        THIS_MODULE,
+       .open =         lpfc_debugfs_nvmeio_trc_open,
+       .llseek =       lpfc_debugfs_lseek,
+       .read =         lpfc_debugfs_read,
+       .write =        lpfc_debugfs_nvmeio_trc_write,
+       .release =      lpfc_debugfs_release,
+};
+
+#undef lpfc_debugfs_op_cpucheck
+static const struct file_operations lpfc_debugfs_op_cpucheck = {
+       .owner =        THIS_MODULE,
+       .open =         lpfc_debugfs_cpucheck_open,
+       .llseek =       lpfc_debugfs_lseek,
+       .read =         lpfc_debugfs_read,
+       .write =        lpfc_debugfs_cpucheck_write,
+       .release =      lpfc_debugfs_release,
+};
+
 #undef lpfc_debugfs_op_dumpData
 static const struct file_operations lpfc_debugfs_op_dumpData = {
        .owner =        THIS_MODULE,
                                lpfc_debugfs_max_slow_ring_trc));
                }
 
+               snprintf(name, sizeof(name), "nvmeio_trc");
+               phba->debug_nvmeio_trc =
+                       debugfs_create_file(name, 0644,
+                                           phba->hba_debugfs_root,
+                                           phba, &lpfc_debugfs_op_nvmeio_trc);
+               if (!phba->debug_nvmeio_trc) {
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+                                        "0574 No create debugfs nvmeio_trc\n");
+                       goto debug_failed;
+               }
+
+               atomic_set(&phba->nvmeio_trc_cnt, 0);
+               if (lpfc_debugfs_max_nvmeio_trc) {
+                       num = lpfc_debugfs_max_nvmeio_trc - 1;
+                       if (num & lpfc_debugfs_max_disc_trc) {
+                               /* Change to be a power of 2 */
+                               num = lpfc_debugfs_max_nvmeio_trc;
+                               i = 0;
+                               while (num > 1) {
+                                       num = num >> 1;
+                                       i++;
+                               }
+                               lpfc_debugfs_max_nvmeio_trc = (1 << i);
+                               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                               "0575 lpfc_debugfs_max_nvmeio_trc "
+                                               "changed to %d\n",
+                                               lpfc_debugfs_max_nvmeio_trc);
+                       }
+                       phba->nvmeio_trc_size = lpfc_debugfs_max_nvmeio_trc;
+
+                       /* Allocate trace buffer and initialize */
+                       phba->nvmeio_trc = kmalloc(
+                               (sizeof(struct lpfc_debugfs_nvmeio_trc) *
+                               phba->nvmeio_trc_size), GFP_KERNEL);
+
+                       if (!phba->nvmeio_trc) {
+                               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                               "0576 Cannot create debugfs "
+                                               "nvmeio_trc buffer\n");
+                               goto nvmeio_off;
+                       }
+                       memset(phba->nvmeio_trc, 0,
+                              (sizeof(struct lpfc_debugfs_nvmeio_trc) *
+                              phba->nvmeio_trc_size));
+                       phba->nvmeio_trc_on = 1;
+                       phba->nvmeio_trc_output_idx = 0;
+                       phba->nvmeio_trc = NULL;
+               } else {
+nvmeio_off:
+                       phba->nvmeio_trc_size = 0;
+                       phba->nvmeio_trc_on = 0;
+                       phba->nvmeio_trc_output_idx = 0;
+                       phba->nvmeio_trc = NULL;
+               }
        }
 
        snprintf(name, sizeof(name), "vport%d", vport->vpi);
                goto debug_failed;
        }
 
+       snprintf(name, sizeof(name), "nvmestat");
+       vport->debug_nvmestat =
+               debugfs_create_file(name, 0644,
+                                   vport->vport_debugfs_root,
+                                   vport, &lpfc_debugfs_op_nvmestat);
+       if (!vport->debug_nvmestat) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+                                "0811 Cannot create debugfs nvmestat\n");
+               goto debug_failed;
+       }
+
+       snprintf(name, sizeof(name), "nvmektime");
+       vport->debug_nvmektime =
+               debugfs_create_file(name, 0644,
+                                   vport->vport_debugfs_root,
+                                   vport, &lpfc_debugfs_op_nvmektime);
+       if (!vport->debug_nvmektime) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+                                "0815 Cannot create debugfs nvmektime\n");
+               goto debug_failed;
+       }
+
+       snprintf(name, sizeof(name), "cpucheck");
+       vport->debug_cpucheck =
+               debugfs_create_file(name, 0644,
+                                   vport->vport_debugfs_root,
+                                   vport, &lpfc_debugfs_op_cpucheck);
+       if (!vport->debug_cpucheck) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+                                "0819 Cannot create debugfs cpucheck\n");
+               goto debug_failed;
+       }
+
        /*
         * The following section is for additional directories/files for the
         * physical port.
                kfree(vport->disc_trc);
                vport->disc_trc = NULL;
        }
-       if (vport->debug_disc_trc) {
-               debugfs_remove(vport->debug_disc_trc); /* discovery_trace */
-               vport->debug_disc_trc = NULL;
-       }
-       if (vport->debug_nodelist) {
-               debugfs_remove(vport->debug_nodelist); /* nodelist */
-               vport->debug_nodelist = NULL;
-       }
+
+       debugfs_remove(vport->debug_disc_trc); /* discovery_trace */
+       vport->debug_disc_trc = NULL;
+
+       debugfs_remove(vport->debug_nodelist); /* nodelist */
+       vport->debug_nodelist = NULL;
+
+       debugfs_remove(vport->debug_nvmestat); /* nvmestat */
+       vport->debug_nvmestat = NULL;
+
+       debugfs_remove(vport->debug_nvmektime); /* nvmektime */
+       vport->debug_nvmektime = NULL;
+
+       debugfs_remove(vport->debug_cpucheck); /* cpucheck */
+       vport->debug_cpucheck = NULL;
+
        if (vport->vport_debugfs_root) {
                debugfs_remove(vport->vport_debugfs_root); /* vportX */
                vport->vport_debugfs_root = NULL;
                atomic_dec(&phba->debugfs_vport_count);
        }
+
        if (atomic_read(&phba->debugfs_vport_count) == 0) {
 
-               if (phba->debug_hbqinfo) {
-                       debugfs_remove(phba->debug_hbqinfo); /* hbqinfo */
-                       phba->debug_hbqinfo = NULL;
-               }
-               if (phba->debug_dumpHBASlim) {
-                       debugfs_remove(phba->debug_dumpHBASlim); /* HBASlim */
-                       phba->debug_dumpHBASlim = NULL;
-               }
-               if (phba->debug_dumpHostSlim) {
-                       debugfs_remove(phba->debug_dumpHostSlim); /* HostSlim */
-                       phba->debug_dumpHostSlim = NULL;
-               }
-               if (phba->debug_dumpData) {
-                       debugfs_remove(phba->debug_dumpData); /* dumpData */
-                       phba->debug_dumpData = NULL;
-               }
+               debugfs_remove(phba->debug_hbqinfo); /* hbqinfo */
+               phba->debug_hbqinfo = NULL;
 
-               if (phba->debug_dumpDif) {
-                       debugfs_remove(phba->debug_dumpDif); /* dumpDif */
-                       phba->debug_dumpDif = NULL;
-               }
-               if (phba->debug_InjErrLBA) {
-                       debugfs_remove(phba->debug_InjErrLBA); /* InjErrLBA */
-                       phba->debug_InjErrLBA = NULL;
-               }
-               if (phba->debug_InjErrNPortID) {         /* InjErrNPortID */
-                       debugfs_remove(phba->debug_InjErrNPortID);
-                       phba->debug_InjErrNPortID = NULL;
-               }
-               if (phba->debug_InjErrWWPN) {
-                       debugfs_remove(phba->debug_InjErrWWPN); /* InjErrWWPN */
-                       phba->debug_InjErrWWPN = NULL;
-               }
-               if (phba->debug_writeGuard) {
-                       debugfs_remove(phba->debug_writeGuard); /* writeGuard */
-                       phba->debug_writeGuard = NULL;
-               }
-               if (phba->debug_writeApp) {
-                       debugfs_remove(phba->debug_writeApp); /* writeApp */
-                       phba->debug_writeApp = NULL;
-               }
-               if (phba->debug_writeRef) {
-                       debugfs_remove(phba->debug_writeRef); /* writeRef */
-                       phba->debug_writeRef = NULL;
-               }
-               if (phba->debug_readGuard) {
-                       debugfs_remove(phba->debug_readGuard); /* readGuard */
-                       phba->debug_readGuard = NULL;
-               }
-               if (phba->debug_readApp) {
-                       debugfs_remove(phba->debug_readApp); /* readApp */
-                       phba->debug_readApp = NULL;
-               }
-               if (phba->debug_readRef) {
-                       debugfs_remove(phba->debug_readRef); /* readRef */
-                       phba->debug_readRef = NULL;
-               }
+               debugfs_remove(phba->debug_dumpHBASlim); /* HBASlim */
+               phba->debug_dumpHBASlim = NULL;
+
+               debugfs_remove(phba->debug_dumpHostSlim); /* HostSlim */
+               phba->debug_dumpHostSlim = NULL;
+
+               debugfs_remove(phba->debug_dumpData); /* dumpData */
+               phba->debug_dumpData = NULL;
+
+               debugfs_remove(phba->debug_dumpDif); /* dumpDif */
+               phba->debug_dumpDif = NULL;
+
+               debugfs_remove(phba->debug_InjErrLBA); /* InjErrLBA */
+               phba->debug_InjErrLBA = NULL;
+
+               debugfs_remove(phba->debug_InjErrNPortID);
+               phba->debug_InjErrNPortID = NULL;
+
+               debugfs_remove(phba->debug_InjErrWWPN); /* InjErrWWPN */
+               phba->debug_InjErrWWPN = NULL;
+
+               debugfs_remove(phba->debug_writeGuard); /* writeGuard */
+               phba->debug_writeGuard = NULL;
+
+               debugfs_remove(phba->debug_writeApp); /* writeApp */
+               phba->debug_writeApp = NULL;
+
+               debugfs_remove(phba->debug_writeRef); /* writeRef */
+               phba->debug_writeRef = NULL;
+
+               debugfs_remove(phba->debug_readGuard); /* readGuard */
+               phba->debug_readGuard = NULL;
+
+               debugfs_remove(phba->debug_readApp); /* readApp */
+               phba->debug_readApp = NULL;
+
+               debugfs_remove(phba->debug_readRef); /* readRef */
+               phba->debug_readRef = NULL;
 
                if (phba->slow_ring_trc) {
                        kfree(phba->slow_ring_trc);
                        phba->slow_ring_trc = NULL;
                }
-               if (phba->debug_slow_ring_trc) {
-                       /* slow_ring_trace */
-                       debugfs_remove(phba->debug_slow_ring_trc);
-                       phba->debug_slow_ring_trc = NULL;
-               }
+
+               /* slow_ring_trace */
+               debugfs_remove(phba->debug_slow_ring_trc);
+               phba->debug_slow_ring_trc = NULL;
+
+               debugfs_remove(phba->debug_nvmeio_trc);
+               phba->debug_nvmeio_trc = NULL;
+
+               kfree(phba->nvmeio_trc);
+               phba->nvmeio_trc = NULL;
 
                /*
                 * iDiag release
                 */
                if (phba->sli_rev == LPFC_SLI_REV4) {
-                       if (phba->idiag_ext_acc) {
-                               /* iDiag extAcc */
-                               debugfs_remove(phba->idiag_ext_acc);
-                               phba->idiag_ext_acc = NULL;
-                       }
-                       if (phba->idiag_mbx_acc) {
-                               /* iDiag mbxAcc */
-                               debugfs_remove(phba->idiag_mbx_acc);
-                               phba->idiag_mbx_acc = NULL;
-                       }
-                       if (phba->idiag_ctl_acc) {
-                               /* iDiag ctlAcc */
-                               debugfs_remove(phba->idiag_ctl_acc);
-                               phba->idiag_ctl_acc = NULL;
-                       }
-                       if (phba->idiag_drb_acc) {
-                               /* iDiag drbAcc */
-                               debugfs_remove(phba->idiag_drb_acc);
-                               phba->idiag_drb_acc = NULL;
-                       }
-                       if (phba->idiag_que_acc) {
-                               /* iDiag queAcc */
-                               debugfs_remove(phba->idiag_que_acc);
-                               phba->idiag_que_acc = NULL;
-                       }
-                       if (phba->idiag_que_info) {
-                               /* iDiag queInfo */
-                               debugfs_remove(phba->idiag_que_info);
-                               phba->idiag_que_info = NULL;
-                       }
-                       if (phba->idiag_bar_acc) {
-                               /* iDiag barAcc */
-                               debugfs_remove(phba->idiag_bar_acc);
-                               phba->idiag_bar_acc = NULL;
-                       }
-                       if (phba->idiag_pci_cfg) {
-                               /* iDiag pciCfg */
-                               debugfs_remove(phba->idiag_pci_cfg);
-                               phba->idiag_pci_cfg = NULL;
-                       }
+                       /* iDiag extAcc */
+                       debugfs_remove(phba->idiag_ext_acc);
+                       phba->idiag_ext_acc = NULL;
+
+                       /* iDiag mbxAcc */
+                       debugfs_remove(phba->idiag_mbx_acc);
+                       phba->idiag_mbx_acc = NULL;
+
+                       /* iDiag ctlAcc */
+                       debugfs_remove(phba->idiag_ctl_acc);
+                       phba->idiag_ctl_acc = NULL;
+
+                       /* iDiag drbAcc */
+                       debugfs_remove(phba->idiag_drb_acc);
+                       phba->idiag_drb_acc = NULL;
+
+                       /* iDiag queAcc */
+                       debugfs_remove(phba->idiag_que_acc);
+                       phba->idiag_que_acc = NULL;
+
+                       /* iDiag queInfo */
+                       debugfs_remove(phba->idiag_que_info);
+                       phba->idiag_que_info = NULL;
+
+                       /* iDiag barAcc */
+                       debugfs_remove(phba->idiag_bar_acc);
+                       phba->idiag_bar_acc = NULL;
+
+                       /* iDiag pciCfg */
+                       debugfs_remove(phba->idiag_pci_cfg);
+                       phba->idiag_pci_cfg = NULL;
 
                        /* Finally remove the iDiag debugfs root */
-                       if (phba->idiag_root) {
-                               /* iDiag root */
-                               debugfs_remove(phba->idiag_root);
-                               phba->idiag_root = NULL;
-                       }
+                       debugfs_remove(phba->idiag_root);
+                       phba->idiag_root = NULL;
                }
 
                if (phba->hba_debugfs_root) {
                        atomic_dec(&lpfc_debugfs_hba_count);
                }
 
-               if (atomic_read(&lpfc_debugfs_hba_count) == 0) {
-                       debugfs_remove(lpfc_debugfs_root); /* lpfc */
-                       lpfc_debugfs_root = NULL;
-               }
+               debugfs_remove(lpfc_debugfs_root); /* lpfc */
+               lpfc_debugfs_root = NULL;
        }
 #endif
        return;