Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/scsi/lpfc/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 189 kB image not shown  

Quelle  lpfc_debugfs.c   Sprache: C

 
/*******************************************************************
 * This file is part of the Emulex Linux Device Driver for         *
 * Fibre Channel Host Bus Adapters.                                *
 * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
 * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.  *
 * Copyright (C) 2007-2015 Emulex.  All rights reserved.           *
 * EMULEX and SLI are trademarks of Emulex.                        *
 * www.broadcom.com                                                *
 *                                                                 *
 * 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/blkdev.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/idr.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/ctype.h>
#include <linux/vmalloc.h>

#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/fc/fc_fs.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.h"
#include "lpfc_scsi.h"
#include "lpfc_nvme.h"
#include "lpfc_logmsg.h"
#include "lpfc_crtn.h"
#include "lpfc_vport.h"
#include "lpfc_version.h"
#include "lpfc_compat.h"
#include "lpfc_debugfs.h"
#include "lpfc_bsg.h"

#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
/*
 * debugfs interface
 *
 * To access this interface the user should:
 * # mount -t debugfs none /sys/kernel/debug
 *
 * The lpfc debugfs directory hierarchy is:
 * /sys/kernel/debug/lpfc/fnX/vportY
 * where X is the lpfc hba function unique_id
 * where Y is the vport VPI on that hba
 *
 * Debugging services available per vport:
 * discovery_trace
 * This is an ACSII readable file that contains a trace of the last
 * lpfc_debugfs_max_disc_trc events that happened on a specific vport.
 * See lpfc_debugfs.h for different categories of  discovery events.
 * To enable the discovery trace, the following module parameters must be set:
 * lpfc_debugfs_enable=1         Turns on lpfc debugfs filesystem support
 * lpfc_debugfs_max_disc_trc=X   Where X is the event trace depth for
 *                               EACH vport. X MUST also be a power of 2.
 * lpfc_debugfs_mask_disc_trc=Y  Where Y is an event mask as defined in
 *                               lpfc_debugfs.h .
 *
 * slow_ring_trace
 * This is an ACSII readable file that contains a trace of the last
 * lpfc_debugfs_max_slow_ring_trc events that happened on a specific HBA.
 * To enable the slow ring trace, the following module parameters must be set:
 * lpfc_debugfs_enable=1         Turns on lpfc debugfs filesystem support
 * lpfc_debugfs_max_slow_ring_trc=X   Where X is the event trace depth for
 *                               the HBA. X MUST also be a power of 2.
 */

static int lpfc_debugfs_enable = 1;
module_param(lpfc_debugfs_enable, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_debugfs_enable, "Enable debugfs services");

/* This MUST be a power of 2 */
static int lpfc_debugfs_max_disc_trc;
module_param(lpfc_debugfs_max_disc_trc, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_debugfs_max_disc_trc,
 "Set debugfs discovery trace depth");

/* This MUST be a power of 2 */
static int lpfc_debugfs_max_slow_ring_trc;
module_param(lpfc_debugfs_max_slow_ring_trc, int, S_IRUGO);
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,
 "Set debugfs discovery trace mask");

#include <linux/debugfs.h>

static atomic_t lpfc_debugfs_seq_trc_cnt = ATOMIC_INIT(0);
static unsigned long lpfc_debugfs_start_time = 0L;

/* iDiag */
static struct lpfc_idiag idiag;

/**
 * lpfc_debugfs_disc_trc_data - Dump discovery logging to a buffer
 * @vport: The vport to gather the log info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine gathers the lpfc discovery debugfs data from the @vport and
 * dumps it to @buf up to @size number of bytes. It will start at the next entry
 * in the log and process the log until the end of the buffer. Then it will
 * gather from the beginning of the log and process until the current entry.
 *
 * Notes:
 * Discovery logging will be disabled while while this routine dumps the log.
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/

static int
lpfc_debugfs_disc_trc_data(struct lpfc_vport *vport, char *buf, int size)
{
 int i, index, len, enable;
 uint32_t ms;
 struct lpfc_debugfs_trc *dtp;
 char *buffer;

 buffer = kmalloc(LPFC_DEBUG_TRC_ENTRY_SIZE, GFP_KERNEL);
 if (!buffer)
  return 0;

 enable = lpfc_debugfs_enable;
 lpfc_debugfs_enable = 0;

 len = 0;
 index = (atomic_read(&vport->disc_trc_cnt) + 1) &
  (lpfc_debugfs_max_disc_trc - 1);
 for (i = index; i < lpfc_debugfs_max_disc_trc; i++) {
  dtp = vport->disc_trc + i;
  if (!dtp->fmt)
   continue;
  ms = jiffies_to_msecs(dtp->jif - lpfc_debugfs_start_time);
  snprintf(buffer,
   LPFC_DEBUG_TRC_ENTRY_SIZE, "%010d:%010d ms:%s\n",
   dtp->seq_cnt, ms, dtp->fmt);
  len +=  scnprintf(buf+len, size-len, buffer,
   dtp->data1, dtp->data2, dtp->data3);
 }
 for (i = 0; i < index; i++) {
  dtp = vport->disc_trc + i;
  if (!dtp->fmt)
   continue;
  ms = jiffies_to_msecs(dtp->jif - lpfc_debugfs_start_time);
  snprintf(buffer,
   LPFC_DEBUG_TRC_ENTRY_SIZE, "%010d:%010d ms:%s\n",
   dtp->seq_cnt, ms, dtp->fmt);
  len +=  scnprintf(buf+len, size-len, buffer,
   dtp->data1, dtp->data2, dtp->data3);
 }

 lpfc_debugfs_enable = enable;
 kfree(buffer);

 return len;
}

/**
 * lpfc_debugfs_slow_ring_trc_data - Dump slow ring logging to a buffer
 * @phba: The HBA to gather the log info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine gathers the lpfc slow ring debugfs data from the @phba and
 * dumps it to @buf up to @size number of bytes. It will start at the next entry
 * in the log and process the log until the end of the buffer. Then it will
 * gather from the beginning of the log and process until the current entry.
 *
 * Notes:
 * Slow ring logging will be disabled while while this routine dumps the log.
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/

static int
lpfc_debugfs_slow_ring_trc_data(struct lpfc_hba *phba, char *buf, int size)
{
 int i, index, len, enable;
 uint32_t ms;
 struct lpfc_debugfs_trc *dtp;
 char *buffer;

 buffer = kmalloc(LPFC_DEBUG_TRC_ENTRY_SIZE, GFP_KERNEL);
 if (!buffer)
  return 0;

 enable = lpfc_debugfs_enable;
 lpfc_debugfs_enable = 0;

 len = 0;
 index = (atomic_read(&phba->slow_ring_trc_cnt) + 1) &
  (lpfc_debugfs_max_slow_ring_trc - 1);
 for (i = index; i < lpfc_debugfs_max_slow_ring_trc; i++) {
  dtp = phba->slow_ring_trc + i;
  if (!dtp->fmt)
   continue;
  ms = jiffies_to_msecs(dtp->jif - lpfc_debugfs_start_time);
  snprintf(buffer,
   LPFC_DEBUG_TRC_ENTRY_SIZE, "%010d:%010d ms:%s\n",
   dtp->seq_cnt, ms, dtp->fmt);
  len +=  scnprintf(buf+len, size-len, buffer,
   dtp->data1, dtp->data2, dtp->data3);
 }
 for (i = 0; i < index; i++) {
  dtp = phba->slow_ring_trc + i;
  if (!dtp->fmt)
   continue;
  ms = jiffies_to_msecs(dtp->jif - lpfc_debugfs_start_time);
  snprintf(buffer,
   LPFC_DEBUG_TRC_ENTRY_SIZE, "%010d:%010d ms:%s\n",
   dtp->seq_cnt, ms, dtp->fmt);
  len +=  scnprintf(buf+len, size-len, buffer,
   dtp->data1, dtp->data2, dtp->data3);
 }

 lpfc_debugfs_enable = enable;
 kfree(buffer);

 return len;
}

static int lpfc_debugfs_last_hbq = -1;

/**
 * lpfc_debugfs_hbqinfo_data - Dump host buffer queue info to a buffer
 * @phba: The HBA to gather host buffer info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine dumps the host buffer queue info from the @phba to @buf up to
 * @size number of bytes. A header that describes the current hbq state will be
 * dumped to @buf first and then info on each hbq entry will be dumped to @buf
 * until @size bytes have been dumped or all the hbq info has been dumped.
 *
 * Notes:
 * This routine will rotate through each configured HBQ each time called.
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/

static int
lpfc_debugfs_hbqinfo_data(struct lpfc_hba *phba, char *buf, int size)
{
 int len = 0;
 int i, j, found, posted, low;
 uint32_t phys, raw_index, getidx;
 struct lpfc_hbq_init *hip;
 struct hbq_s *hbqs;
 struct lpfc_hbq_entry *hbqe;
 struct lpfc_dmabuf *d_buf;
 struct hbq_dmabuf *hbq_buf;

 if (phba->sli_rev != 3)
  return 0;

 spin_lock_irq(&phba->hbalock);

 /* toggle between multiple hbqs, if any */
 i = lpfc_sli_hbq_count();
 if (i > 1) {
   lpfc_debugfs_last_hbq++;
   if (lpfc_debugfs_last_hbq >= i)
   lpfc_debugfs_last_hbq = 0;
 }
 else
  lpfc_debugfs_last_hbq = 0;

 i = lpfc_debugfs_last_hbq;

 len +=  scnprintf(buf+len, size-len, "HBQ %d Info\n", i);

 hbqs =  &phba->hbqs[i];
 posted = 0;
 list_for_each_entry(d_buf, &hbqs->hbq_buffer_list, list)
  posted++;

 hip =  lpfc_hbq_defs[i];
 len +=  scnprintf(buf+len, size-len,
  "idx:%d prof:%d rn:%d bufcnt:%d icnt:%d acnt:%d posted %d\n",
  hip->hbq_index, hip->profile, hip->rn,
  hip->buffer_count, hip->init_count, hip->add_count, posted);

 raw_index = phba->hbq_get[i];
 getidx = le32_to_cpu(raw_index);
 len +=  scnprintf(buf+len, size-len,
  "entries:%d bufcnt:%d Put:%d nPut:%d localGet:%d hbaGet:%d\n",
  hbqs->entry_count, hbqs->buffer_count, hbqs->hbqPutIdx,
  hbqs->next_hbqPutIdx, hbqs->local_hbqGetIdx, getidx);

 hbqe = (struct lpfc_hbq_entry *) phba->hbqs[i].hbq_virt;
 for (j=0; j<hbqs->entry_count; j++) {
  len +=  scnprintf(buf+len, size-len,
   "%03d: %08x %04x %05x ", j,
   le32_to_cpu(hbqe->bde.addrLow),
   le32_to_cpu(hbqe->bde.tus.w),
   le32_to_cpu(hbqe->buffer_tag));
  i = 0;
  found = 0;

  /* First calculate if slot has an associated posted buffer */
  low = hbqs->hbqPutIdx - posted;
  if (low >= 0) {
   if ((j >= hbqs->hbqPutIdx) || (j < low)) {
    len +=  scnprintf(buf + len, size - len,
      "Unused\n");
    goto skipit;
   }
  }
  else {
   if ((j >= hbqs->hbqPutIdx) &&
    (j < (hbqs->entry_count+low))) {
    len +=  scnprintf(buf + len, size - len,
      "Unused\n");
    goto skipit;
   }
  }

  /* Get the Buffer info for the posted buffer */
  list_for_each_entry(d_buf, &hbqs->hbq_buffer_list, list) {
   hbq_buf = container_of(d_buf, struct hbq_dmabuf, dbuf);
   phys = ((uint64_t)hbq_buf->dbuf.phys & 0xffffffff);
   if (phys == le32_to_cpu(hbqe->bde.addrLow)) {
    len +=  scnprintf(buf+len, size-len,
     "Buf%d: x%px %06x\n", i,
     hbq_buf->dbuf.virt, hbq_buf->tag);
    found = 1;
    break;
   }
   i++;
  }
  if (!found) {
   len +=  scnprintf(buf+len, size-len, "No DMAinfo?\n");
  }
skipit:
  hbqe++;
  if (len > LPFC_HBQINFO_SIZE - 54)
   break;
 }
 spin_unlock_irq(&phba->hbalock);
 return len;
}

static int lpfc_debugfs_last_xripool;

/**
 * lpfc_debugfs_commonxripools_data - Dump Hardware Queue info to a buffer
 * @phba: The HBA to gather host buffer info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine dumps the Hardware Queue info from the @phba to @buf up to
 * @size number of bytes. A header that describes the current hdwq state will be
 * dumped to @buf first and then info on each hdwq entry will be dumped to @buf
 * until @size bytes have been dumped or all the hdwq info has been dumped.
 *
 * Notes:
 * This routine will rotate through each configured Hardware Queue each
 * time called.
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/

static int
lpfc_debugfs_commonxripools_data(struct lpfc_hba *phba, char *buf, int size)
{
 struct lpfc_sli4_hdw_queue *qp;
 int len = 0;
 int i, out;
 unsigned long iflag;

 for (i = 0; i < phba->cfg_hdw_queue; i++) {
  if (len > (LPFC_DUMP_MULTIXRIPOOL_SIZE - 80))
   break;
  qp = &phba->sli4_hba.hdwq[lpfc_debugfs_last_xripool];

  len += scnprintf(buf + len, size - len, "HdwQ %d Info ", i);
  spin_lock_irqsave(&qp->abts_io_buf_list_lock, iflag);
  spin_lock(&qp->io_buf_list_get_lock);
  spin_lock(&qp->io_buf_list_put_lock);
  out = qp->total_io_bufs - (qp->get_io_bufs + qp->put_io_bufs +
   qp->abts_scsi_io_bufs + qp->abts_nvme_io_bufs);
  len += scnprintf(buf + len, size - len,
     "tot:%d get:%d put:%d mt:%d "
     "ABTS scsi:%d nvme:%d Out:%d\n",
   qp->total_io_bufs, qp->get_io_bufs, qp->put_io_bufs,
   qp->empty_io_bufs, qp->abts_scsi_io_bufs,
   qp->abts_nvme_io_bufs, out);
  spin_unlock(&qp->io_buf_list_put_lock);
  spin_unlock(&qp->io_buf_list_get_lock);
  spin_unlock_irqrestore(&qp->abts_io_buf_list_lock, iflag);

  lpfc_debugfs_last_xripool++;
  if (lpfc_debugfs_last_xripool >= phba->cfg_hdw_queue)
   lpfc_debugfs_last_xripool = 0;
 }

 return len;
}

/**
 * lpfc_debugfs_multixripools_data - Display multi-XRI pools information
 * @phba: The HBA to gather host buffer info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine displays current multi-XRI pools information including XRI
 * count in public, private and txcmplq. It also displays current high and
 * low watermark.
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/

static int
lpfc_debugfs_multixripools_data(struct lpfc_hba *phba, char *buf, int size)
{
 u32 i;
 u32 hwq_count;
 struct lpfc_sli4_hdw_queue *qp;
 struct lpfc_multixri_pool *multixri_pool;
 struct lpfc_pvt_pool *pvt_pool;
 struct lpfc_pbl_pool *pbl_pool;
 u32 txcmplq_cnt;
 char tmp[LPFC_DEBUG_OUT_LINE_SZ] = {0};

 if (phba->sli_rev != LPFC_SLI_REV4)
  return 0;

 if (!phba->sli4_hba.hdwq)
  return 0;

 if (!phba->cfg_xri_rebalancing) {
  i = lpfc_debugfs_commonxripools_data(phba, buf, size);
  return i;
 }

 /*
 * Pbl: Current number of free XRIs in public pool
 * Pvt: Current number of free XRIs in private pool
 * Busy: Current number of outstanding XRIs
 * HWM: Current high watermark
 * pvt_empty: Incremented by 1 when IO submission fails (no xri)
 * pbl_empty: Incremented by 1 when all pbl_pool are empty during
 *            IO submission
 */

 scnprintf(tmp, sizeof(tmp),
    "HWQ: Pbl Pvt Busy HWM | pvt_empty pbl_empty ");
 if (strlcat(buf, tmp, size) >= size)
  return strnlen(buf, size);

#ifdef LPFC_MXP_STAT
 /*
 * MAXH: Max high watermark seen so far
 * above_lmt: Incremented by 1 if xri_owned > xri_limit during
 *            IO submission
 * below_lmt: Incremented by 1 if xri_owned <= xri_limit  during
 *            IO submission
 * locPbl_hit: Incremented by 1 if successfully get a batch of XRI from
 *             local pbl_pool
 * othPbl_hit: Incremented by 1 if successfully get a batch of XRI from
 *             other pbl_pool
 */

 scnprintf(tmp, sizeof(tmp),
    "MAXH above_lmt below_lmt locPbl_hit othPbl_hit");
 if (strlcat(buf, tmp, size) >= size)
  return strnlen(buf, size);

 /*
 * sPbl: snapshot of Pbl 15 sec after stat gets cleared
 * sPvt: snapshot of Pvt 15 sec after stat gets cleared
 * sBusy: snapshot of Busy 15 sec after stat gets cleared
 */

 scnprintf(tmp, sizeof(tmp),
    " | sPbl sPvt sBusy");
 if (strlcat(buf, tmp, size) >= size)
  return strnlen(buf, size);
#endif

 scnprintf(tmp, sizeof(tmp), "\n");
 if (strlcat(buf, tmp, size) >= size)
  return strnlen(buf, size);

 hwq_count = phba->cfg_hdw_queue;
 for (i = 0; i < hwq_count; i++) {
  qp = &phba->sli4_hba.hdwq[i];
  multixri_pool = qp->p_multixri_pool;
  if (!multixri_pool)
   continue;
  pbl_pool = &multixri_pool->pbl_pool;
  pvt_pool = &multixri_pool->pvt_pool;
  txcmplq_cnt = qp->io_wq->pring->txcmplq_cnt;

  scnprintf(tmp, sizeof(tmp),
     "%03d: %4d %4d %4d %4d | %10d %10d ",
     i, pbl_pool->count, pvt_pool->count,
     txcmplq_cnt, pvt_pool->high_watermark,
     qp->empty_io_bufs, multixri_pool->pbl_empty_count);
  if (strlcat(buf, tmp, size) >= size)
   break;

#ifdef LPFC_MXP_STAT
  scnprintf(tmp, sizeof(tmp),
     "%4d %10d %10d %10d %10d",
     multixri_pool->stat_max_hwm,
     multixri_pool->above_limit_count,
     multixri_pool->below_limit_count,
     multixri_pool->local_pbl_hit_count,
     multixri_pool->other_pbl_hit_count);
  if (strlcat(buf, tmp, size) >= size)
   break;

  scnprintf(tmp, sizeof(tmp),
     " | %4d %4d %5d",
     multixri_pool->stat_pbl_count,
     multixri_pool->stat_pvt_count,
     multixri_pool->stat_busy_count);
  if (strlcat(buf, tmp, size) >= size)
   break;
#endif

  scnprintf(tmp, sizeof(tmp), "\n");
  if (strlcat(buf, tmp, size) >= size)
   break;
 }
 return strnlen(buf, size);
}


#ifdef LPFC_HDWQ_LOCK_STAT
static int lpfc_debugfs_last_lock;

/**
 * lpfc_debugfs_lockstat_data - Dump Hardware Queue info to a buffer
 * @phba: The HBA to gather host buffer info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine dumps the Hardware Queue info from the @phba to @buf up to
 * @size number of bytes. A header that describes the current hdwq state will be
 * dumped to @buf first and then info on each hdwq entry will be dumped to @buf
 * until @size bytes have been dumped or all the hdwq info has been dumped.
 *
 * Notes:
 * This routine will rotate through each configured Hardware Queue each
 * time called.
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/

static int
lpfc_debugfs_lockstat_data(struct lpfc_hba *phba, char *buf, int size)
{
 struct lpfc_sli4_hdw_queue *qp;
 int len = 0;
 int i;

 if (phba->sli_rev != LPFC_SLI_REV4)
  return 0;

 if (!phba->sli4_hba.hdwq)
  return 0;

 for (i = 0; i < phba->cfg_hdw_queue; i++) {
  if (len > (LPFC_HDWQINFO_SIZE - 100))
   break;
  qp = &phba->sli4_hba.hdwq[lpfc_debugfs_last_lock];

  len += scnprintf(buf + len, size - len, "HdwQ %03d Lock ", i);
  if (phba->cfg_xri_rebalancing) {
   len += scnprintf(buf + len, size - len,
      "get_pvt:%d mv_pvt:%d "
      "mv2pub:%d mv2pvt:%d "
      "put_pvt:%d put_pub:%d wq:%d\n",
      qp->lock_conflict.alloc_pvt_pool,
      qp->lock_conflict.mv_from_pvt_pool,
      qp->lock_conflict.mv_to_pub_pool,
      qp->lock_conflict.mv_to_pvt_pool,
      qp->lock_conflict.free_pvt_pool,
      qp->lock_conflict.free_pub_pool,
      qp->lock_conflict.wq_access);
  } else {
   len += scnprintf(buf + len, size - len,
      "get:%d put:%d free:%d wq:%d\n",
      qp->lock_conflict.alloc_xri_get,
      qp->lock_conflict.alloc_xri_put,
      qp->lock_conflict.free_xri,
      qp->lock_conflict.wq_access);
  }

  lpfc_debugfs_last_lock++;
  if (lpfc_debugfs_last_lock >= phba->cfg_hdw_queue)
   lpfc_debugfs_last_lock = 0;
 }

 return len;
}
#endif

static int lpfc_debugfs_last_hba_slim_off;

/**
 * lpfc_debugfs_dumpHBASlim_data - Dump HBA SLIM info to a buffer
 * @phba: The HBA to gather SLIM info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine dumps the current contents of HBA SLIM for the HBA associated
 * with @phba to @buf up to @size bytes of data. This is the raw HBA SLIM data.
 *
 * Notes:
 * This routine will only dump up to 1024 bytes of data each time called and
 * should be called multiple times to dump the entire HBA SLIM.
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/

static int
lpfc_debugfs_dumpHBASlim_data(struct lpfc_hba *phba, char *buf, int size)
{
 int len = 0;
 int i, off;
 uint32_t *ptr;
 char *buffer;

 buffer = kmalloc(1024, GFP_KERNEL);
 if (!buffer)
  return 0;

 off = 0;
 spin_lock_irq(&phba->hbalock);

 len +=  scnprintf(buf+len, size-len, "HBA SLIM\n");
 lpfc_memcpy_from_slim(buffer,
  phba->MBslimaddr + lpfc_debugfs_last_hba_slim_off, 1024);

 ptr = (uint32_t *)&buffer[0];
 off = lpfc_debugfs_last_hba_slim_off;

 /* Set it up for the next time */
 lpfc_debugfs_last_hba_slim_off += 1024;
 if (lpfc_debugfs_last_hba_slim_off >= 4096)
  lpfc_debugfs_last_hba_slim_off = 0;

 i = 1024;
 while (i > 0) {
  len +=  scnprintf(buf+len, size-len,
  "%08x: %08x %08x %08x %08x %08x %08x %08x %08x\n",
  off, *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4),
  *(ptr+5), *(ptr+6), *(ptr+7));
  ptr += 8;
  i -= (8 * sizeof(uint32_t));
  off += (8 * sizeof(uint32_t));
 }

 spin_unlock_irq(&phba->hbalock);
 kfree(buffer);

 return len;
}

/**
 * lpfc_debugfs_dumpHostSlim_data - Dump host SLIM info to a buffer
 * @phba: The HBA to gather Host SLIM info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine dumps the current contents of host SLIM for the host associated
 * with @phba to @buf up to @size bytes of data. The dump will contain the
 * Mailbox, PCB, Rings, and Registers that are located in host memory.
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/

static int
lpfc_debugfs_dumpHostSlim_data(struct lpfc_hba *phba, char *buf, int size)
{
 int len = 0;
 int i, off;
 uint32_t word0, word1, word2, word3;
 uint32_t *ptr;
 struct lpfc_pgp *pgpp;
 struct lpfc_sli *psli = &phba->sli;
 struct lpfc_sli_ring *pring;

 off = 0;
 spin_lock_irq(&phba->hbalock);

 len +=  scnprintf(buf+len, size-len, "SLIM Mailbox\n");
 ptr = (uint32_t *)phba->slim2p.virt;
 i = sizeof(MAILBOX_t);
 while (i > 0) {
  len +=  scnprintf(buf+len, size-len,
  "%08x: %08x %08x %08x %08x %08x %08x %08x %08x\n",
  off, *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4),
  *(ptr+5), *(ptr+6), *(ptr+7));
  ptr += 8;
  i -= (8 * sizeof(uint32_t));
  off += (8 * sizeof(uint32_t));
 }

 len +=  scnprintf(buf+len, size-len, "SLIM PCB\n");
 ptr = (uint32_t *)phba->pcb;
 i = sizeof(PCB_t);
 while (i > 0) {
  len +=  scnprintf(buf+len, size-len,
  "%08x: %08x %08x %08x %08x %08x %08x %08x %08x\n",
  off, *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4),
  *(ptr+5), *(ptr+6), *(ptr+7));
  ptr += 8;
  i -= (8 * sizeof(uint32_t));
  off += (8 * sizeof(uint32_t));
 }

 if (phba->sli_rev <= LPFC_SLI_REV3) {
  for (i = 0; i < 4; i++) {
   pgpp = &phba->port_gp[i];
   pring = &psli->sli3_ring[i];
   len +=  scnprintf(buf+len, size-len,
      "Ring %d: CMD GetInx:%d "
      "(Max:%d Next:%d "
      "Local:%d flg:x%x) "
      "RSP PutInx:%d Max:%d\n",
      i, pgpp->cmdGetInx,
      pring->sli.sli3.numCiocb,
      pring->sli.sli3.next_cmdidx,
      pring->sli.sli3.local_getidx,
      pring->flag, pgpp->rspPutInx,
      pring->sli.sli3.numRiocb);
  }

  word0 = readl(phba->HAregaddr);
  word1 = readl(phba->CAregaddr);
  word2 = readl(phba->HSregaddr);
  word3 = readl(phba->HCregaddr);
  len +=  scnprintf(buf+len, size-len, "HA:%08x CA:%08x HS:%08x "
     "HC:%08x\n", word0, word1, word2, word3);
 }
 spin_unlock_irq(&phba->hbalock);
 return len;
}

/**
 * lpfc_debugfs_nodelist_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 current target node list associated with @vport to
 * @buf up to @size bytes of data. Each node entry in the dump will contain a
 * node state, DID, WWPN, WWNN, RPI, flags, type, and other useful fields.
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/

static int
lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size)
{
 int len = 0;
 int i, iocnt, outio, cnt;
 struct lpfc_hba  *phba = vport->phba;
 struct lpfc_nodelist *ndlp;
 unsigned char *statep;
 unsigned long iflags;
 struct nvme_fc_local_port *localport;
 struct nvme_fc_remote_port *nrport = NULL;
 struct lpfc_nvme_rport *rport;

 cnt = (LPFC_NODELIST_SIZE / LPFC_NODELIST_ENTRY_SIZE);
 outio = 0;

 len += scnprintf(buf+len, size-len, "\nFCP Nodelist Entries ...\n");
 spin_lock_irqsave(&vport->fc_nodes_list_lock, iflags);
 list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
  iocnt = 0;
  if (!cnt) {
   len +=  scnprintf(buf+len, size-len,
    "Missing Nodelist Entries\n");
   break;
  }
  cnt--;
  switch (ndlp->nlp_state) {
  case NLP_STE_UNUSED_NODE:
   statep = "UNUSED";
   break;
  case NLP_STE_PLOGI_ISSUE:
   statep = "PLOGI ";
   break;
  case NLP_STE_ADISC_ISSUE:
   statep = "ADISC ";
   break;
  case NLP_STE_REG_LOGIN_ISSUE:
   statep = "REGLOG";
   break;
  case NLP_STE_PRLI_ISSUE:
   statep = "PRLI ";
   break;
  case NLP_STE_LOGO_ISSUE:
   statep = "LOGO ";
   break;
  case NLP_STE_UNMAPPED_NODE:
   statep = "UNMAP ";
   iocnt = 1;
   break;
  case NLP_STE_MAPPED_NODE:
   statep = "MAPPED";
   iocnt = 1;
   break;
  case NLP_STE_NPR_NODE:
   statep = "NPR ";
   break;
  default:
   statep = "UNKNOWN";
  }
  len += scnprintf(buf+len, size-len, "%s DID:x%06x ",
    statep, ndlp->nlp_DID);
  len += scnprintf(buf+len, size-len,
    "WWPN x%016llx ",
    wwn_to_u64(ndlp->nlp_portname.u.wwn));
  len += scnprintf(buf+len, size-len,
    "WWNN x%016llx ",
    wwn_to_u64(ndlp->nlp_nodename.u.wwn));
  len += scnprintf(buf+len, size-len, "RPI:x%04x ",
     ndlp->nlp_rpi);
  len += scnprintf(buf+len, size-len, "flag:x%08lx ",
     ndlp->nlp_flag);
  if (!ndlp->nlp_type)
   len += scnprintf(buf+len, size-len, "UNKNOWN_TYPE ");
  if (ndlp->nlp_type & NLP_FC_NODE)
   len += scnprintf(buf+len, size-len, "FC_NODE ");
  if (ndlp->nlp_type & NLP_FABRIC) {
   len += scnprintf(buf+len, size-len, "FABRIC ");
   iocnt = 0;
  }
  if (ndlp->nlp_type & NLP_FCP_TARGET)
   len += scnprintf(buf+len, size-len, "FCP_TGT sid:%d ",
    ndlp->nlp_sid);
  if (ndlp->nlp_type & NLP_FCP_INITIATOR)
   len += scnprintf(buf+len, size-len, "FCP_INITIATOR ");
  if (ndlp->nlp_type & NLP_NVME_TARGET)
   len += scnprintf(buf + len,
     size - len, "NVME_TGT sid:%d ",
     NLP_NO_SID);
  if (ndlp->nlp_type & NLP_NVME_INITIATOR)
   len += scnprintf(buf + len,
     size - len, "NVME_INITIATOR ");
  len += scnprintf(buf+len, size-len, "refcnt:%d",
   kref_read(&ndlp->kref));
  if (iocnt) {
   i = atomic_read(&ndlp->cmd_pending);
   len += scnprintf(buf + len, size - len,
     " OutIO:x%x Qdepth x%x",
     i, ndlp->cmd_qdepth);
   outio += i;
  }
  len += scnprintf(buf+len, size-len, " xpt:x%x",
     ndlp->fc4_xpt_flags);
  if (ndlp->nlp_defer_did != NLP_EVT_NOTHING_PENDING)
   len += scnprintf(buf+len, size-len, " defer:%x",
      ndlp->nlp_defer_did);
  len +=  scnprintf(buf+len, size-len, "\n");
 }
 spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags);

 len += scnprintf(buf + len, size - len,
   "\nOutstanding IO x%x\n",  outio);

 if (phba->nvmet_support && phba->targetport && (vport == phba->pport)) {
  len += scnprintf(buf + len, size - len,
    "\nNVME Targetport Entry ...\n");

  /* Port state is only one of two values for now. */
  if (phba->targetport->port_id)
   statep = "REGISTERED";
  else
   statep = "INIT";
  len += scnprintf(buf + len, size - len,
    "TGT WWNN x%llx WWPN x%llx State %s\n",
    wwn_to_u64(vport->fc_nodename.u.wwn),
    wwn_to_u64(vport->fc_portname.u.wwn),
    statep);
  len += scnprintf(buf + len, size - len,
    " Targetport DID x%06x\n",
    phba->targetport->port_id);
  goto out_exit;
 }

 len += scnprintf(buf + len, size - len,
    "\nNVME Lport/Rport Entries ...\n");

 localport = vport->localport;
 if (!localport)
  goto out_exit;

 /* Port state is only one of two values for now. */
 if (localport->port_id)
  statep = "ONLINE";
 else
  statep = "UNKNOWN ";

 len += scnprintf(buf + len, size - len,
   "Lport DID x%06x PortState %s\n",
   localport->port_id, statep);

 len += scnprintf(buf + len, size - len, "\tRport List:\n");
 spin_lock_irqsave(&vport->fc_nodes_list_lock, iflags);
 list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
  /* local short-hand pointer. */
  spin_lock(&ndlp->lock);
  rport = lpfc_ndlp_get_nrport(ndlp);
  if (rport)
   nrport = rport->remoteport;
  else
   nrport = NULL;
  spin_unlock(&ndlp->lock);
  if (!nrport)
   continue;

  /* 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 += scnprintf(buf + len, size - len,
    "\t%s Port ID:x%06x ",
    statep, nrport->port_id);
  len += scnprintf(buf + len, size - len, "WWPN x%llx ",
    nrport->port_name);
  len += scnprintf(buf + len, size - len, "WWNN x%llx ",
    nrport->node_name);

  /* An NVME rport can have multiple roles. */
  if (nrport->port_role & FC_PORT_ROLE_NVME_INITIATOR)
   len +=  scnprintf(buf + len, size - len,
      "INITIATOR ");
  if (nrport->port_role & FC_PORT_ROLE_NVME_TARGET)
   len +=  scnprintf(buf + len, size - len,
      "TARGET ");
  if (nrport->port_role & FC_PORT_ROLE_NVME_DISCOVERY)
   len +=  scnprintf(buf + len, size - len,
      "DISCSRVC ");
  if (nrport->port_role & ~(FC_PORT_ROLE_NVME_INITIATOR |
       FC_PORT_ROLE_NVME_TARGET |
       FC_PORT_ROLE_NVME_DISCOVERY))
   len +=  scnprintf(buf + len, size - len,
      "UNKNOWN ROLE x%x",
      nrport->port_role);
  /* Terminate the string. */
  len +=  scnprintf(buf + len, size - len, "\n");
 }
 spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags);
 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;
 struct lpfc_nvmet_tgtport *tgtp;
 struct lpfc_async_xchg_ctx *ctxp, *next_ctxp;
 struct nvme_fc_local_port *localport;
 struct lpfc_fc4_ctrl_stat *cstat;
 struct lpfc_nvme_lport *lport;
 uint64_t data1, data2, data3;
 uint64_t tot, totin, totout;
 int cnt, i;
 int len = 0;

 if (phba->nvmet_support) {
  if (!phba->targetport)
   return len;
  tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
  len += scnprintf(buf + len, size - len,
    "\nNVME Targetport Statistics\n");

  len += scnprintf(buf + len, size - len,
    "LS: Rcv %08x Drop %08x Abort %08x\n",
    atomic_read(&tgtp->rcv_ls_req_in),
    atomic_read(&tgtp->rcv_ls_req_drop),
    atomic_read(&tgtp->xmt_ls_abort));
  if (atomic_read(&tgtp->rcv_ls_req_in) !=
      atomic_read(&tgtp->rcv_ls_req_out)) {
   len += scnprintf(buf + len, size - len,
     "Rcv LS: in %08x != out %08x\n",
     atomic_read(&tgtp->rcv_ls_req_in),
     atomic_read(&tgtp->rcv_ls_req_out));
  }

  len += scnprintf(buf + len, size - len,
    "LS: Xmt %08x Drop %08x Cmpl %08x\n",
    atomic_read(&tgtp->xmt_ls_rsp),
    atomic_read(&tgtp->xmt_ls_drop),
    atomic_read(&tgtp->xmt_ls_rsp_cmpl));

  len += scnprintf(buf + len, size - len,
    "LS: RSP Abort %08x xb %08x Err %08x\n",
    atomic_read(&tgtp->xmt_ls_rsp_aborted),
    atomic_read(&tgtp->xmt_ls_rsp_xb_set),
    atomic_read(&tgtp->xmt_ls_rsp_error));

  len += scnprintf(buf + len, size - len,
    "FCP: Rcv %08x Defer %08x Release %08x "
    "Drop %08x\n",
    atomic_read(&tgtp->rcv_fcp_cmd_in),
    atomic_read(&tgtp->rcv_fcp_cmd_defer),
    atomic_read(&tgtp->xmt_fcp_release),
    atomic_read(&tgtp->rcv_fcp_cmd_drop));

  if (atomic_read(&tgtp->rcv_fcp_cmd_in) !=
      atomic_read(&tgtp->rcv_fcp_cmd_out)) {
   len += scnprintf(buf + len, size - len,
     "Rcv FCP: in %08x != out %08x\n",
     atomic_read(&tgtp->rcv_fcp_cmd_in),
     atomic_read(&tgtp->rcv_fcp_cmd_out));
  }

  len += scnprintf(buf + len, size - len,
    "FCP Rsp: read %08x readrsp %08x "
    "write %08x rsp %08x\n",
    atomic_read(&tgtp->xmt_fcp_read),
    atomic_read(&tgtp->xmt_fcp_read_rsp),
    atomic_read(&tgtp->xmt_fcp_write),
    atomic_read(&tgtp->xmt_fcp_rsp));

  len += scnprintf(buf + len, size - len,
    "FCP Rsp Cmpl: %08x err %08x drop %08x\n",
    atomic_read(&tgtp->xmt_fcp_rsp_cmpl),
    atomic_read(&tgtp->xmt_fcp_rsp_error),
    atomic_read(&tgtp->xmt_fcp_rsp_drop));

  len += scnprintf(buf + len, size - len,
    "FCP Rsp Abort: %08x xb %08x xricqe %08x\n",
    atomic_read(&tgtp->xmt_fcp_rsp_aborted),
    atomic_read(&tgtp->xmt_fcp_rsp_xb_set),
    atomic_read(&tgtp->xmt_fcp_xri_abort_cqe));

  len += scnprintf(buf + len, size - len,
    "ABORT: Xmt %08x Cmpl %08x\n",
    atomic_read(&tgtp->xmt_fcp_abort),
    atomic_read(&tgtp->xmt_fcp_abort_cmpl));

  len += scnprintf(buf + len, size - len,
    "ABORT: Sol %08x Usol %08x Err %08x Cmpl %08x",
    atomic_read(&tgtp->xmt_abort_sol),
    atomic_read(&tgtp->xmt_abort_unsol),
    atomic_read(&tgtp->xmt_abort_rsp),
    atomic_read(&tgtp->xmt_abort_rsp_error));

  len +=  scnprintf(buf + len, size - len, "\n");

  cnt = 0;
  spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
  list_for_each_entry_safe(ctxp, next_ctxp,
    &phba->sli4_hba.lpfc_abts_nvmet_ctx_list,
    list) {
   cnt++;
  }
  spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
  if (cnt) {
   len += scnprintf(buf + len, size - len,
     "ABORT: %d ctx entries\n", cnt);
   spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
   list_for_each_entry_safe(ctxp, next_ctxp,
        &phba->sli4_hba.lpfc_abts_nvmet_ctx_list,
        list) {
    if (len >= (size - LPFC_DEBUG_OUT_LINE_SZ))
     break;
    len += scnprintf(buf + len, size - len,
      "Entry: oxid %x state %x "
      "flag %x\n",
      ctxp->oxid, ctxp->state,
      ctxp->flag);
   }
   spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
  }

  /* Calculate outstanding IOs */
  tot = atomic_read(&tgtp->rcv_fcp_cmd_drop);
  tot += atomic_read(&tgtp->xmt_fcp_release);
  tot = atomic_read(&tgtp->rcv_fcp_cmd_in) - tot;

  len += scnprintf(buf + len, size - len,
    "IO_CTX: %08x WAIT: cur %08x tot %08x\n"
    "CTX Outstanding %08llx\n",
    phba->sli4_hba.nvmet_xri_cnt,
    phba->sli4_hba.nvmet_io_wait_cnt,
    phba->sli4_hba.nvmet_io_wait_total,
    tot);
 } else {
  if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_NVME))
   return len;

  localport = vport->localport;
  if (!localport)
   return len;
  lport = (struct lpfc_nvme_lport *)localport->private;
  if (!lport)
   return len;

  len += scnprintf(buf + len, size - len,
    "\nNVME HDWQ Statistics\n");

  len += scnprintf(buf + len, size - len,
    "LS: Xmt %016x Cmpl %016x\n",
    atomic_read(&lport->fc4NvmeLsRequests),
    atomic_read(&lport->fc4NvmeLsCmpls));

  totin = 0;
  totout = 0;
  for (i = 0; i < phba->cfg_hdw_queue; i++) {
   cstat = &phba->sli4_hba.hdwq[i].nvme_cstat;
   tot = cstat->io_cmpls;
   totin += tot;
   data1 = cstat->input_requests;
   data2 = cstat->output_requests;
   data3 = cstat->control_requests;
   totout += (data1 + data2 + data3);

   /* Limit to 32, debugfs display buffer limitation */
   if (i >= 32)
    continue;

   len += scnprintf(buf + len, PAGE_SIZE - len,
     "HDWQ (%d): Rd %016llx Wr %016llx "
     "IO %016llx ",
     i, data1, data2, data3);
   len += scnprintf(buf + len, PAGE_SIZE - len,
     "Cmpl %016llx OutIO %016llx\n",
     tot, ((data1 + data2 + data3) - tot));
  }
  len += scnprintf(buf + len, PAGE_SIZE - len,
    "Total FCP Cmpl %016llx Issue %016llx "
    "OutIO %016llx\n",
    totin, totout, totout - totin);

  len += scnprintf(buf + len, size - len,
    "LS Xmt Err: Abrt %08x Err %08x "
    "Cmpl Err: xb %08x Err %08x\n",
    atomic_read(&lport->xmt_ls_abort),
    atomic_read(&lport->xmt_ls_err),
    atomic_read(&lport->cmpl_ls_xb),
    atomic_read(&lport->cmpl_ls_err));

  len += scnprintf(buf + len, size - len,
    "FCP Xmt Err: noxri %06x nondlp %06x "
    "qdepth %06x wqerr %06x err %06x Abrt %06x\n",
    atomic_read(&lport->xmt_fcp_noxri),
    atomic_read(&lport->xmt_fcp_bad_ndlp),
    atomic_read(&lport->xmt_fcp_qdepth),
    atomic_read(&lport->xmt_fcp_wqerr),
    atomic_read(&lport->xmt_fcp_err),
    atomic_read(&lport->xmt_fcp_abort));

  len += scnprintf(buf + len, size - len,
    "FCP Cmpl Err: xb %08x Err %08x\n",
    atomic_read(&lport->cmpl_fcp_xb),
    atomic_read(&lport->cmpl_fcp_err));

 }

 return len;
}

/**
 * lpfc_debugfs_scsistat_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 SCSI 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_scsistat_data(struct lpfc_vport *vport, char *buf, int size)
{
 int len;
 struct lpfc_hba *phba = vport->phba;
 struct lpfc_fc4_ctrl_stat *cstat;
 u64 data1, data2, data3;
 u64 tot, totin, totout;
 int i;
 char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0};

 if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_FCP) ||
     (phba->sli_rev != LPFC_SLI_REV4))
  return 0;

 scnprintf(buf, size, "SCSI HDWQ Statistics\n");

 totin = 0;
 totout = 0;
 for (i = 0; i < phba->cfg_hdw_queue; i++) {
  cstat = &phba->sli4_hba.hdwq[i].scsi_cstat;
  tot = cstat->io_cmpls;
  totin += tot;
  data1 = cstat->input_requests;
  data2 = cstat->output_requests;
  data3 = cstat->control_requests;
  totout += (data1 + data2 + data3);

  scnprintf(tmp, sizeof(tmp), "HDWQ (%d): Rd %016llx Wr %016llx "
     "IO %016llx ", i, data1, data2, data3);
  if (strlcat(buf, tmp, size) >= size)
   goto buffer_done;

  scnprintf(tmp, sizeof(tmp), "Cmpl %016llx OutIO %016llx\n",
     tot, ((data1 + data2 + data3) - tot));
  if (strlcat(buf, tmp, size) >= size)
   goto buffer_done;
 }
 scnprintf(tmp, sizeof(tmp), "Total FCP Cmpl %016llx Issue %016llx "
    "OutIO %016llx\n", totin, totout, totout - totin);
 strlcat(buf, tmp, size);

buffer_done:
 len = strnlen(buf, size);

 return len;
}

void
lpfc_io_ktime(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd)
{
 uint64_t seg1, seg2, seg3, seg4;
 uint64_t segsum;

 if (!lpfc_cmd->ts_last_cmd ||
     !lpfc_cmd->ts_cmd_start ||
     !lpfc_cmd->ts_cmd_wqput ||
     !lpfc_cmd->ts_isr_cmpl ||
     !lpfc_cmd->ts_data_io)
  return;

 if (lpfc_cmd->ts_data_io < lpfc_cmd->ts_cmd_start)
  return;
 if (lpfc_cmd->ts_cmd_start < lpfc_cmd->ts_last_cmd)
  return;
 if (lpfc_cmd->ts_cmd_wqput < lpfc_cmd->ts_cmd_start)
  return;
 if (lpfc_cmd->ts_isr_cmpl < lpfc_cmd->ts_cmd_wqput)
  return;
 if (lpfc_cmd->ts_data_io < lpfc_cmd->ts_isr_cmpl)
  return;
 /*
 * Segment 1 - Time from Last FCP command cmpl is handed
 * off to NVME Layer to start of next command.
 * Segment 2 - Time from Driver receives a IO cmd start
 * from NVME Layer to WQ put is done on IO cmd.
 * Segment 3 - Time from Driver WQ put is done on IO cmd
 * to MSI-X ISR for IO cmpl.
 * Segment 4 - Time from MSI-X ISR for IO cmpl to when
 * cmpl is handled off to the NVME Layer.
 */

 seg1 = lpfc_cmd->ts_cmd_start - lpfc_cmd->ts_last_cmd;
 if (seg1 > 5000000)  /* 5 ms - for sequential IOs only */
  seg1 = 0;

 /* Calculate times relative to start of IO */
 seg2 = (lpfc_cmd->ts_cmd_wqput - lpfc_cmd->ts_cmd_start);
 segsum = seg2;
 seg3 = lpfc_cmd->ts_isr_cmpl - lpfc_cmd->ts_cmd_start;
 if (segsum > seg3)
  return;
 seg3 -= segsum;
 segsum += seg3;

 seg4 = lpfc_cmd->ts_data_io - lpfc_cmd->ts_cmd_start;
 if (segsum > seg4)
  return;
 seg4 -= segsum;

 phba->ktime_data_samples++;
 phba->ktime_seg1_total += seg1;
 if (seg1 < phba->ktime_seg1_min)
  phba->ktime_seg1_min = seg1;
 else if (seg1 > phba->ktime_seg1_max)
  phba->ktime_seg1_max = seg1;
 phba->ktime_seg2_total += seg2;
 if (seg2 < phba->ktime_seg2_min)
  phba->ktime_seg2_min = seg2;
 else if (seg2 > phba->ktime_seg2_max)
  phba->ktime_seg2_max = seg2;
 phba->ktime_seg3_total += seg3;
 if (seg3 < phba->ktime_seg3_min)
  phba->ktime_seg3_min = seg3;
 else if (seg3 > phba->ktime_seg3_max)
  phba->ktime_seg3_max = seg3;
 phba->ktime_seg4_total += seg4;
 if (seg4 < phba->ktime_seg4_min)
  phba->ktime_seg4_min = seg4;
 else if (seg4 > phba->ktime_seg4_max)
  phba->ktime_seg4_max = seg4;

 lpfc_cmd->ts_last_cmd = 0;
 lpfc_cmd->ts_cmd_start = 0;
 lpfc_cmd->ts_cmd_wqput  = 0;
 lpfc_cmd->ts_isr_cmpl = 0;
 lpfc_cmd->ts_data_io = 0;
}

/**
 * lpfc_debugfs_ioktime_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_ioktime_data(struct lpfc_vport *vport, char *buf, int size)
{
 struct lpfc_hba   *phba = vport->phba;
 int len = 0;

 if (phba->nvmet_support == 0) {
  /* Initiator */
  len += scnprintf(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 += scnprintf(
   buf + len, PAGE_SIZE - len,
   "Segment 1: Last Cmd cmpl "
   "done -to- Start of next Cmd (in driver)\n");
  len += scnprintf(
   buf + len, PAGE_SIZE - len,
   "avg:%08lld min:%08lld max %08lld\n",
   div_u64(phba->ktime_seg1_total,
    phba->ktime_data_samples),
   phba->ktime_seg1_min,
   phba->ktime_seg1_max);
  len += scnprintf(
   buf + len, PAGE_SIZE - len,
   "Segment 2: Driver start of Cmd "
   "-to- Firmware WQ doorbell\n");
  len += scnprintf(
   buf + len, PAGE_SIZE - len,
   "avg:%08lld min:%08lld max %08lld\n",
   div_u64(phba->ktime_seg2_total,
    phba->ktime_data_samples),
   phba->ktime_seg2_min,
   phba->ktime_seg2_max);
  len += scnprintf(
   buf + len, PAGE_SIZE - len,
   "Segment 3: Firmware WQ doorbell -to- "
   "MSI-X ISR cmpl\n");
  len += scnprintf(
   buf + len, PAGE_SIZE - len,
   "avg:%08lld min:%08lld max %08lld\n",
   div_u64(phba->ktime_seg3_total,
    phba->ktime_data_samples),
   phba->ktime_seg3_min,
   phba->ktime_seg3_max);
  len += scnprintf(
   buf + len, PAGE_SIZE - len,
   "Segment 4: MSI-X ISR cmpl -to- "
   "Cmd cmpl done\n");
  len += scnprintf(
   buf + len, PAGE_SIZE - len,
   "avg:%08lld min:%08lld max %08lld\n",
   div_u64(phba->ktime_seg4_total,
    phba->ktime_data_samples),
   phba->ktime_seg4_min,
   phba->ktime_seg4_max);
  len += scnprintf(
   buf + len, PAGE_SIZE - len,
   "Total IO avg time: %08lld\n",
   div_u64(phba->ktime_seg1_total +
   phba->ktime_seg2_total  +
   phba->ktime_seg3_total +
   phba->ktime_seg4_total,
   phba->ktime_data_samples));
  return len;
 }

 /* NVME Target */
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "ktime %s: Total Samples: %lld %lld\n",
   (phba->ktime_on ? "Enabled" : "Disabled"),
   phba->ktime_data_samples,
   phba->ktime_status_samples);
 if (phba->ktime_data_samples == 0)
  return len;

 len += scnprintf(buf + len, PAGE_SIZE-len,
   "Segment 1: MSI-X ISR Rcv cmd -to- "
   "cmd pass to NVME Layer\n");
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "avg:%08lld min:%08lld max %08lld\n",
   div_u64(phba->ktime_seg1_total,
    phba->ktime_data_samples),
   phba->ktime_seg1_min,
   phba->ktime_seg1_max);
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "Segment 2: cmd pass to NVME Layer- "
   "-to- Driver rcv cmd OP (action)\n");
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "avg:%08lld min:%08lld max %08lld\n",
   div_u64(phba->ktime_seg2_total,
    phba->ktime_data_samples),
   phba->ktime_seg2_min,
   phba->ktime_seg2_max);
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "Segment 3: Driver rcv cmd OP -to- "
   "Firmware WQ doorbell: cmd\n");
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "avg:%08lld min:%08lld max %08lld\n",
   div_u64(phba->ktime_seg3_total,
    phba->ktime_data_samples),
   phba->ktime_seg3_min,
   phba->ktime_seg3_max);
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "Segment 4: Firmware WQ doorbell: cmd "
   "-to- MSI-X ISR for cmd cmpl\n");
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "avg:%08lld min:%08lld max %08lld\n",
   div_u64(phba->ktime_seg4_total,
    phba->ktime_data_samples),
   phba->ktime_seg4_min,
   phba->ktime_seg4_max);
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "Segment 5: MSI-X ISR for cmd cmpl "
   "-to- NVME layer passed cmd done\n");
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "avg:%08lld min:%08lld max %08lld\n",
   div_u64(phba->ktime_seg5_total,
    phba->ktime_data_samples),
   phba->ktime_seg5_min,
   phba->ktime_seg5_max);

 if (phba->ktime_status_samples == 0) {
  len += scnprintf(buf + len, PAGE_SIZE-len,
    "Total: cmd received by MSI-X ISR "
    "-to- cmd completed on wire\n");
  len += scnprintf(buf + len, PAGE_SIZE-len,
    "avg:%08lld min:%08lld "
    "max %08lld\n",
    div_u64(phba->ktime_seg10_total,
     phba->ktime_data_samples),
    phba->ktime_seg10_min,
    phba->ktime_seg10_max);
  return len;
 }

 len += scnprintf(buf + len, PAGE_SIZE-len,
   "Segment 6: NVME layer passed cmd done "
   "-to- Driver rcv rsp status OP\n");
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "avg:%08lld min:%08lld max %08lld\n",
   div_u64(phba->ktime_seg6_total,
    phba->ktime_status_samples),
   phba->ktime_seg6_min,
   phba->ktime_seg6_max);
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "Segment 7: Driver rcv rsp status OP "
   "-to- Firmware WQ doorbell: status\n");
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "avg:%08lld min:%08lld max %08lld\n",
   div_u64(phba->ktime_seg7_total,
    phba->ktime_status_samples),
   phba->ktime_seg7_min,
   phba->ktime_seg7_max);
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "Segment 8: Firmware WQ doorbell: status"
   " -to- MSI-X ISR for status cmpl\n");
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "avg:%08lld min:%08lld max %08lld\n",
   div_u64(phba->ktime_seg8_total,
    phba->ktime_status_samples),
   phba->ktime_seg8_min,
   phba->ktime_seg8_max);
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "Segment 9: MSI-X ISR for status cmpl "
   "-to- NVME layer passed status done\n");
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "avg:%08lld min:%08lld max %08lld\n",
   div_u64(phba->ktime_seg9_total,
    phba->ktime_status_samples),
   phba->ktime_seg9_min,
   phba->ktime_seg9_max);
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "Total: cmd received by MSI-X ISR -to- "
   "cmd completed on wire\n");
 len += scnprintf(buf + len, PAGE_SIZE-len,
   "avg:%08lld min:%08lld max %08lld\n",
   div_u64(phba->ktime_seg10_total,
    phba->ktime_status_samples),
   phba->ktime_seg10_min,
   phba->ktime_seg10_max);
 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 += scnprintf(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 +=  scnprintf(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 += scnprintf(buf + len, size - len,
     "Trace Complete\n");
   goto out;
  }

  if (len >= (size - LPFC_DEBUG_OUT_LINE_SZ)) {
   len += scnprintf(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 +=  scnprintf(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 += scnprintf(buf + len, size - len,
     "Trace Complete\n");
   goto out;
  }

  if (len >= (size - LPFC_DEBUG_OUT_LINE_SZ)) {
   len += scnprintf(buf + len, size - len,
     "Trace Continue (%d of %d)\n",
     phba->nvmeio_trc_output_idx,
     phba->nvmeio_trc_size);
   goto out;
  }
 }

 len += scnprintf(buf + len, size - len,
   "Trace Done\n");
out:
 return len;
}

/**
 * lpfc_debugfs_hdwqstat_data - Dump I/O stats 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 + SCSI 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_hdwqstat_data(struct lpfc_vport *vport, char *buf, int size)
{
 struct lpfc_hba   *phba = vport->phba;
 struct lpfc_hdwq_stat *c_stat;
 int i, j, len;
 uint32_t tot_xmt;
 uint32_t tot_rcv;
 uint32_t tot_cmpl;
 char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0};

 scnprintf(tmp, sizeof(tmp), "HDWQ Stats:\n\n");
 if (strlcat(buf, tmp, size) >= size)
  goto buffer_done;

 scnprintf(tmp, sizeof(tmp), "(NVME Accounting: %s) ",
    (phba->hdwqstat_on &
    (LPFC_CHECK_NVME_IO | LPFC_CHECK_NVMET_IO) ?
    "Enabled" : "Disabled"));
 if (strlcat(buf, tmp, size) >= size)
  goto buffer_done;

 scnprintf(tmp, sizeof(tmp), "(SCSI Accounting: %s) ",
    (phba->hdwqstat_on & LPFC_CHECK_SCSI_IO ?
    "Enabled" : "Disabled"));
 if (strlcat(buf, tmp, size) >= size)
  goto buffer_done;

 scnprintf(tmp, sizeof(tmp), "\n\n");
 if (strlcat(buf, tmp, size) >= size)
  goto buffer_done;

 for (i = 0; i < phba->cfg_hdw_queue; i++) {
  tot_rcv = 0;
  tot_xmt = 0;
  tot_cmpl = 0;

  for_each_present_cpu(j) {
   c_stat = per_cpu_ptr(phba->sli4_hba.c_stat, j);

   /* Only display for this HDWQ */
   if (i != c_stat->hdwq_no)
    continue;

   /* Only display non-zero counters */
   if (!c_stat->xmt_io && !c_stat->cmpl_io &&
       !c_stat->rcv_io)
    continue;

   if (!tot_xmt && !tot_cmpl && !tot_rcv) {
    /* Print HDWQ string only the first time */
    scnprintf(tmp, sizeof(tmp), "[HDWQ %d]:\t", i);
    if (strlcat(buf, tmp, size) >= size)
     goto buffer_done;
   }

   tot_xmt += c_stat->xmt_io;
   tot_cmpl += c_stat->cmpl_io;
   if (phba->nvmet_support)
    tot_rcv += c_stat->rcv_io;

   scnprintf(tmp, sizeof(tmp), "| [CPU %d]: ", j);
   if (strlcat(buf, tmp, size) >= size)
    goto buffer_done;

   if (phba->nvmet_support) {
    scnprintf(tmp, sizeof(tmp),
       "XMT 0x%x CMPL 0x%x RCV 0x%x |",
       c_stat->xmt_io, c_stat->cmpl_io,
       c_stat->rcv_io);
    if (strlcat(buf, tmp, size) >= size)
     goto buffer_done;
   } else {
    scnprintf(tmp, sizeof(tmp),
       "XMT 0x%x CMPL 0x%x |",
       c_stat->xmt_io, c_stat->cmpl_io);
    if (strlcat(buf, tmp, size) >= size)
     goto buffer_done;
   }
  }

  /* Check if nothing to display */
  if (!tot_xmt && !tot_cmpl && !tot_rcv)
   continue;

  scnprintf(tmp, sizeof(tmp), "\t->\t[HDWQ Total: ");
  if (strlcat(buf, tmp, size) >= size)
   goto buffer_done;

  if (phba->nvmet_support) {
   scnprintf(tmp, sizeof(tmp),
      "XMT 0x%x CMPL 0x%x RCV 0x%x]\n\n",
      tot_xmt, tot_cmpl, tot_rcv);
   if (strlcat(buf, tmp, size) >= size)
    goto buffer_done;
  } else {
   scnprintf(tmp, sizeof(tmp),
      "XMT 0x%x CMPL 0x%x]\n\n",
      tot_xmt, tot_cmpl);
   if (strlcat(buf, tmp, size) >= size)
    goto buffer_done;
  }
 }

buffer_done:
 len = strnlen(buf, size);
 return len;
}

#endif

/**
 * lpfc_debugfs_disc_trc - Store discovery trace log
 * @vport: The vport to associate this trace string with for retrieval.
 * @mask: Log entry classification.
 * @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
 * discovery trace buffer associated with @vport. Only entries with a @mask that
 * match the current debugfs discovery mask will be saved. Entries that do not
 * match will be thrown away. @fmt, @data1, @data2, and @data3 are used like
 * printf when displaying the log.
 **/

inline void
lpfc_debugfs_disc_trc(struct lpfc_vport *vport, int mask, char *fmt,
 uint32_t data1, uint32_t data2, uint32_t data3)
{
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
 struct lpfc_debugfs_trc *dtp;
 int index;

 if (!(lpfc_debugfs_mask_disc_trc & mask))
  return;

 if (!lpfc_debugfs_enable || !lpfc_debugfs_max_disc_trc ||
  !vport || !vport->disc_trc)
  return;

 index = atomic_inc_return(&vport->disc_trc_cnt) &
  (lpfc_debugfs_max_disc_trc - 1);
 dtp = vport->disc_trc + index;
 dtp->fmt = fmt;
 dtp->data1 = data1;
 dtp->data2 = data2;
 dtp->data3 = data3;
 dtp->seq_cnt = atomic_inc_return(&lpfc_debugfs_seq_trc_cnt);
 dtp->jif = jiffies;
#endif
 return;
}

/**
 * lpfc_debugfs_slow_ring_trc - Store slow ring 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
 * discovery trace buffer associated with @vport. @fmt, @data1, @data2, and
 * @data3 are used like printf when displaying the log.
 **/

inline void
lpfc_debugfs_slow_ring_trc(struct lpfc_hba *phba, char *fmt,
 uint32_t data1, uint32_t data2, uint32_t data3)
{
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
 struct lpfc_debugfs_trc *dtp;
 int index;

 if (!lpfc_debugfs_enable || !lpfc_debugfs_max_slow_ring_trc ||
  !phba || !phba->slow_ring_trc)
  return;

 index = atomic_inc_return(&phba->slow_ring_trc_cnt) &
  (lpfc_debugfs_max_slow_ring_trc - 1);
 dtp = phba->slow_ring_trc + index;
 dtp->fmt = fmt;
 dtp->data1 = data1;
 dtp->data2 = data2;
 dtp->data3 = data3;
 dtp->seq_cnt = atomic_inc_return(&lpfc_debugfs_seq_trc_cnt);
 dtp->jif = jiffies;
#endif
 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
 * @inode: The inode pointer that contains a vport pointer.
 * @file: The file pointer to attach the log output.
 *
 * Description:
 * This routine is the entry point for the debugfs open file operation. It gets
 * the vport from the i_private field in @inode, allocates the necessary buffer
 * for the log, fills the buffer from the in-memory log for this vport, and then
 * returns a pointer to that log in the private_data field in @file.
 *
 * Returns:
 * This function returns zero if successful. On error it will return a negative
 * error value.
 **/

static int
lpfc_debugfs_disc_trc_open(struct inode *inode, struct file *file)
{
 struct lpfc_vport *vport = inode->i_private;
 struct lpfc_debug *debug;
 int size;
 int rc = -ENOMEM;

 if (!lpfc_debugfs_max_disc_trc) {
  rc = -ENOSPC;
  goto out;
 }

 debug = kmalloc(sizeof(*debug), GFP_KERNEL);
 if (!debug)
  goto out;

 /* Round to page boundary */
 size =  (lpfc_debugfs_max_disc_trc * LPFC_DEBUG_TRC_ENTRY_SIZE);
 size = PAGE_ALIGN(size);

 debug->buffer = kmalloc(size, GFP_KERNEL);
 if (!debug->buffer) {
  kfree(debug);
  goto out;
 }

 debug->len = lpfc_debugfs_disc_trc_data(vport, debug->buffer, size);
 file->private_data = debug;

 rc = 0;
out:
 return rc;
}

/**
 * lpfc_debugfs_slow_ring_trc_open - Open the Slow Ring trace log
 * @inode: The inode pointer that contains a vport pointer.
 * @file: The file pointer to attach the log output.
 *
 * Description:
 * This routine is the entry point for the debugfs open file operation. It gets
 * the vport from the i_private field in @inode, allocates the necessary buffer
 * for the log, fills the buffer from the in-memory log for this vport, and then
 * returns a pointer to that log in the private_data field in @file.
 *
 * Returns:
 * This function returns zero if successful. On error it will return a negative
 * error value.
 **/

static int
lpfc_debugfs_slow_ring_trc_open(struct inode *inode, struct file *file)
{
 struct lpfc_hba *phba = inode->i_private;
 struct lpfc_debug *debug;
 int size;
 int rc = -ENOMEM;

 if (!lpfc_debugfs_max_slow_ring_trc) {
  rc = -ENOSPC;
  goto out;
 }

 debug = kmalloc(sizeof(*debug), GFP_KERNEL);
 if (!debug)
  goto out;

 /* Round to page boundary */
 size =  (lpfc_debugfs_max_slow_ring_trc * LPFC_DEBUG_TRC_ENTRY_SIZE);
 size = PAGE_ALIGN(size);

 debug->buffer = kmalloc(size, GFP_KERNEL);
 if (!debug->buffer) {
  kfree(debug);
  goto out;
 }

 debug->len = lpfc_debugfs_slow_ring_trc_data(phba, debug->buffer, size);
 file->private_data = debug;

 rc = 0;
out:
 return rc;
}

/**
 * lpfc_debugfs_hbqinfo_open - Open the hbqinfo debugfs buffer
 * @inode: The inode pointer that contains a vport pointer.
 * @file: The file pointer to attach the log output.
 *
 * Description:
 * This routine is the entry point for the debugfs open file operation. It gets
 * the vport from the i_private field in @inode, allocates the necessary buffer
 * for the log, fills the buffer from the in-memory log for this vport, and then
 * returns a pointer to that log in the private_data field in @file.
 *
 * Returns:
 * This function returns zero if successful. On error it will return a negative
 * error value.
 **/

static int
lpfc_debugfs_hbqinfo_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_HBQINFO_SIZE, GFP_KERNEL);
 if (!debug->buffer) {
  kfree(debug);
  goto out;
 }

 debug->len = lpfc_debugfs_hbqinfo_data(phba, debug->buffer,
  LPFC_HBQINFO_SIZE);
 file->private_data = debug;

 rc = 0;
out:
 return rc;
}

/**
 * lpfc_debugfs_multixripools_open - Open the multixripool debugfs buffer
 * @inode: The inode pointer that contains a hba pointer.
 * @file: The file pointer to attach the log output.
 *
 * Description:
 * This routine is the entry point for the debugfs open file operation. It gets
 * the hba from the i_private field in @inode, allocates the necessary buffer
 * for the log, fills the buffer from the in-memory log for this hba, and then
 * returns a pointer to that log in the private_data field in @file.
 *
 * Returns:
 * This function returns zero if successful. On error it will return a negative
 * error value.
 **/

static int
lpfc_debugfs_multixripools_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 = kzalloc(LPFC_DUMP_MULTIXRIPOOL_SIZE, GFP_KERNEL);
 if (!debug->buffer) {
  kfree(debug);
  goto out;
 }

 debug->len = lpfc_debugfs_multixripools_data(
  phba, debug->buffer, LPFC_DUMP_MULTIXRIPOOL_SIZE);

 debug->i_private = inode->i_private;
 file->private_data = debug;

 rc = 0;
out:
 return rc;
}

#ifdef LPFC_HDWQ_LOCK_STAT
/**
 * lpfc_debugfs_lockstat_open - Open the lockstat debugfs buffer
 * @inode: The inode pointer that contains a vport pointer.
 * @file: The file pointer to attach the log output.
 *
 * Description:
 * This routine is the entry point for the debugfs open file operation. It gets
 * the vport from the i_private field in @inode, allocates the necessary buffer
 * for the log, fills the buffer from the in-memory log for this vport, and then
 * returns a pointer to that log in the private_data field in @file.
 *
 * Returns:
 * This function returns zero if successful. On error it will return a negative
 * error value.
 **/

static int
lpfc_debugfs_lockstat_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_HDWQINFO_SIZE, GFP_KERNEL);
 if (!debug->buffer) {
  kfree(debug);
  goto out;
 }

 debug->len = lpfc_debugfs_lockstat_data(phba, debug->buffer,
  LPFC_HBQINFO_SIZE);
 file->private_data = debug;

 rc = 0;
out:
 return rc;
}

static ssize_t
lpfc_debugfs_lockstat_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;
 struct lpfc_sli4_hdw_queue *qp;
 char mybuf[64];
 char *pbuf;
 int i;
 size_t bsize;

 memset(mybuf, 0, sizeof(mybuf));

 bsize = min(nbytes, (sizeof(mybuf) - 1));

 if (copy_from_user(mybuf, buf, bsize))
  return -EFAULT;
 pbuf = &mybuf[0];

 if ((strncmp(pbuf, "reset", strlen("reset")) == 0) ||
     (strncmp(pbuf, "zero", strlen("zero")) == 0)) {
  for (i = 0; i < phba->cfg_hdw_queue; i++) {
   qp = &phba->sli4_hba.hdwq[i];
   qp->lock_conflict.alloc_xri_get = 0;
   qp->lock_conflict.alloc_xri_put = 0;
   qp->lock_conflict.free_xri = 0;
   qp->lock_conflict.wq_access = 0;
   qp->lock_conflict.alloc_pvt_pool = 0;
   qp->lock_conflict.mv_from_pvt_pool = 0;
   qp->lock_conflict.mv_to_pub_pool = 0;
   qp->lock_conflict.mv_to_pvt_pool = 0;
   qp->lock_conflict.free_pvt_pool = 0;
   qp->lock_conflict.free_pub_pool = 0;
   qp->lock_conflict.wq_access = 0;
  }
 }
 return bsize;
}
#endif

static int lpfc_debugfs_ras_log_data(struct lpfc_hba *phba,
         char *buffer, int size)
{
 int copied = 0;
 struct lpfc_dmabuf *dmabuf, *next;

 memset(buffer, 0, size);

 spin_lock_irq(&phba->ras_fwlog_lock);
 if (phba->ras_fwlog.state != ACTIVE) {
  spin_unlock_irq(&phba->ras_fwlog_lock);
  return -EINVAL;
 }
 spin_unlock_irq(&phba->ras_fwlog_lock);

 list_for_each_entry_safe(dmabuf, next,
     &phba->ras_fwlog.fwlog_buff_list, list) {
  /* Check if copying will go over size and a '\0' char */
  if ((copied + LPFC_RAS_MAX_ENTRY_SIZE) >= (size - 1)) {
   memcpy(buffer + copied, dmabuf->virt,
          size - copied - 1);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=86 G=90

¤ Dauer der Verarbeitung: 0.34 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.