/******************************************************************* * 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. *
*******************************************************************/
#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.
*/ staticint 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 */ staticint 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 */ staticint 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 */ staticint 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");
/** * 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.
**/ staticint
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;
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.
**/ staticint
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;
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;
}
staticint 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.
**/ staticint
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);
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;
}
staticint 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.
**/ staticint
lpfc_debugfs_commonxripools_data(struct lpfc_hba *phba, char *buf, int size)
{ struct lpfc_sli4_hdw_queue *qp; int len = 0; int i, out; unsignedlong 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];
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.
**/ staticint
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
/** * 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.
**/ staticint
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];
lpfc_debugfs_last_lock++; if (lpfc_debugfs_last_lock >= phba->cfg_hdw_queue)
lpfc_debugfs_last_lock = 0;
}
return len;
} #endif
staticint 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.
**/ staticint
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;
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.
**/ staticint
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);
}
/** * 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.
**/ staticint
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; unsignedchar *statep; unsignedlong iflags; struct nvme_fc_local_port *localport; struct nvme_fc_remote_port *nrport = NULL; struct lpfc_nvme_rport *rport;
/* 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;
}
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.
**/ staticint
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));
/** * 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.
**/ staticint
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);
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;
/** * 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.
**/ staticint
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.
**/ staticint
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;
/** * 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.
**/ staticint
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};
/* 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;
}
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.
**/ inlinevoid
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;
/** * 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.
**/ inlinevoid
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;
/** * 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.
**/ inlinevoid
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;
#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.
**/ staticint
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;
/** * 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.
**/ staticint
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;
/** * 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.
**/ staticint
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;
}
/** * 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.
**/ staticint
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;
}
#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.
**/ staticint
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;
}
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.