Commit 67125b02 authored by Hiral Patel's avatar Hiral Patel Committed by James Bottomley
Browse files

[SCSI] fnic: Fnic Statistics Collection



This feature gathers active and cumulative per fnic stats for io,
abort, terminate, reset, vlan discovery path and it also includes
various important stats for debugging issues. It also provided
debugfs and ioctl interface for user to retrieve these stats.
It also provides functionality to reset cumulative stats through
user interface.

Signed-off-by: default avatarHiral Patel <hiralpat@cisco.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent 441fbd25
......@@ -27,6 +27,7 @@
#include "fnic_io.h"
#include "fnic_res.h"
#include "fnic_trace.h"
#include "fnic_stats.h"
#include "vnic_dev.h"
#include "vnic_wq.h"
#include "vnic_rq.h"
......@@ -232,6 +233,13 @@ struct fnic {
unsigned int wq_count;
unsigned int cq_count;
struct dentry *fnic_stats_debugfs_host;
struct dentry *fnic_stats_debugfs_file;
struct dentry *fnic_reset_debugfs_file;
unsigned int reset_stats;
atomic64_t io_cmpl_skip;
struct fnic_stats fnic_stats;
u32 vlan_hw_insert:1; /* let hw insert the tag */
u32 in_remove:1; /* fnic device in removal */
u32 stop_rx_link_events:1; /* stop proc. rx frames, link events */
......
......@@ -23,6 +23,58 @@
static struct dentry *fnic_trace_debugfs_root;
static struct dentry *fnic_trace_debugfs_file;
static struct dentry *fnic_trace_enable;
static struct dentry *fnic_stats_debugfs_root;
/*
* fnic_debugfs_init - Initialize debugfs for fnic debug logging
*
* Description:
* When Debugfs is configured this routine sets up the fnic debugfs
* file system. If not already created, this routine will create the
* fnic directory and statistics directory for trace buffer and
* stats logging.
*/
int fnic_debugfs_init(void)
{
int rc = -1;
fnic_trace_debugfs_root = debugfs_create_dir("fnic", NULL);
if (!fnic_trace_debugfs_root) {
printk(KERN_DEBUG "Cannot create debugfs root\n");
return rc;
}
if (!fnic_trace_debugfs_root) {
printk(KERN_DEBUG
"fnic root directory doesn't exist in debugfs\n");
return rc;
}
fnic_stats_debugfs_root = debugfs_create_dir("statistics",
fnic_trace_debugfs_root);
if (!fnic_stats_debugfs_root) {
printk(KERN_DEBUG "Cannot create Statistics directory\n");
return rc;
}
rc = 0;
return rc;
}
/*
* fnic_debugfs_terminate - Tear down debugfs infrastructure
*
* Description:
* When Debugfs is configured this routine removes debugfs file system
* elements that are specific to fnic.
*/
void fnic_debugfs_terminate(void)
{
debugfs_remove(fnic_stats_debugfs_root);
fnic_stats_debugfs_root = NULL;
debugfs_remove(fnic_trace_debugfs_root);
fnic_trace_debugfs_root = NULL;
}
/*
* fnic_trace_ctrl_open - Open the trace_enable file
......@@ -241,16 +293,16 @@ static const struct file_operations fnic_trace_debugfs_fops = {
* Description:
* When Debugfs is configured this routine sets up the fnic debugfs
* file system. If not already created, this routine will create the
* fnic directory. It will create file trace to log fnic trace buffer
* output into debugfs and it will also create file trace_enable to
* control enable/disable of trace logging into trace buffer.
* create file trace to log fnic trace buffer output into debugfs and
* it will also create file trace_enable to control enable/disable of
* trace logging into trace buffer.
*/
int fnic_trace_debugfs_init(void)
{
int rc = -1;
fnic_trace_debugfs_root = debugfs_create_dir("fnic", NULL);
if (!fnic_trace_debugfs_root) {
printk(KERN_DEBUG "Cannot create debugfs root\n");
printk(KERN_DEBUG
"FNIC Debugfs root directory doesn't exist\n");
return rc;
}
fnic_trace_enable = debugfs_create_file("tracing_enable",
......@@ -259,8 +311,8 @@ int fnic_trace_debugfs_init(void)
NULL, &fnic_trace_ctrl_fops);
if (!fnic_trace_enable) {
printk(KERN_DEBUG "Cannot create trace_enable file"
" under debugfs");
printk(KERN_DEBUG
"Cannot create trace_enable file under debugfs\n");
return rc;
}
......@@ -271,7 +323,8 @@ int fnic_trace_debugfs_init(void)
&fnic_trace_debugfs_fops);
if (!fnic_trace_debugfs_file) {
printk(KERN_DEBUG "Cannot create trace file under debugfs");
printk(KERN_DEBUG
"Cannot create trace file under debugfs\n");
return rc;
}
rc = 0;
......@@ -295,8 +348,323 @@ void fnic_trace_debugfs_terminate(void)
debugfs_remove(fnic_trace_enable);
fnic_trace_enable = NULL;
}
if (fnic_trace_debugfs_root) {
debugfs_remove(fnic_trace_debugfs_root);
fnic_trace_debugfs_root = NULL;
}
/*
* fnic_reset_stats_open - Open the reset_stats file
* @inode: The inode pointer.
* @file: The file pointer to attach the stats reset flag.
*
* Description:
* This routine opens a debugsfs file reset_stats and stores i_private data
* to debug structure to retrieve later for while performing other
* file oprations.
*
* Returns:
* This function returns zero if successful.
*/
static int fnic_reset_stats_open(struct inode *inode, struct file *file)
{
struct stats_debug_info *debug;
debug = kzalloc(sizeof(struct stats_debug_info), GFP_KERNEL);
if (!debug)
return -ENOMEM;
debug->i_private = inode->i_private;
file->private_data = debug;
return 0;
}
/*
* fnic_reset_stats_read - Read a reset_stats debugfs file
* @filp: The file pointer to read from.
* @ubuf: The buffer to copy the data to.
* @cnt: The number of bytes to read.
* @ppos: The position in the file to start reading from.
*
* Description:
* This routine reads value of variable reset_stats
* and stores into local @buf. It will start reading file at @ppos and
* copy up to @cnt of data to @ubuf from @buf.
*
* Returns:
* This function returns the amount of data that was read.
*/
static ssize_t fnic_reset_stats_read(struct file *file,
char __user *ubuf,
size_t cnt, loff_t *ppos)
{
struct stats_debug_info *debug = file->private_data;
struct fnic *fnic = (struct fnic *)debug->i_private;
char buf[64];
int len;
len = sprintf(buf, "%u\n", fnic->reset_stats);
return simple_read_from_buffer(ubuf, cnt, ppos, buf, len);
}
/*
* fnic_reset_stats_write - Write to reset_stats debugfs file
* @filp: The file pointer to write from.
* @ubuf: The buffer to copy the data from.
* @cnt: The number of bytes to write.
* @ppos: The position in the file to start writing to.
*
* Description:
* This routine writes data from user buffer @ubuf to buffer @buf and
* resets cumulative stats of fnic.
*
* Returns:
* This function returns the amount of data that was written.
*/
static ssize_t fnic_reset_stats_write(struct file *file,
const char __user *ubuf,
size_t cnt, loff_t *ppos)
{
struct stats_debug_info *debug = file->private_data;
struct fnic *fnic = (struct fnic *)debug->i_private;
struct fnic_stats *stats = &fnic->fnic_stats;
u64 *io_stats_p = (u64 *)&stats->io_stats;
u64 *fw_stats_p = (u64 *)&stats->fw_stats;
char buf[64];
unsigned long val;
int ret;
if (cnt >= sizeof(buf))
return -EINVAL;
if (copy_from_user(&buf, ubuf, cnt))
return -EFAULT;
buf[cnt] = 0;
ret = kstrtoul(buf, 10, &val);
if (ret < 0)
return ret;
fnic->reset_stats = val;
if (fnic->reset_stats) {
/* Skip variable is used to avoid descrepancies to Num IOs
* and IO Completions stats. Skip incrementing No IO Compls
* for pending active IOs after reset stats
*/
atomic64_set(&fnic->io_cmpl_skip,
atomic64_read(&stats->io_stats.active_ios));
memset(&stats->abts_stats, 0, sizeof(struct abort_stats));
memset(&stats->term_stats, 0,
sizeof(struct terminate_stats));
memset(&stats->reset_stats, 0, sizeof(struct reset_stats));
memset(&stats->misc_stats, 0, sizeof(struct misc_stats));
memset(&stats->vlan_stats, 0, sizeof(struct vlan_stats));
memset(io_stats_p+1, 0,
sizeof(struct io_path_stats) - sizeof(u64));
memset(fw_stats_p+1, 0,
sizeof(struct fw_stats) - sizeof(u64));
}
(*ppos)++;
return cnt;
}
/*
* fnic_reset_stats_release - Release the buffer used to store
* debugfs file data
* @inode: The inode pointer
* @file: The file pointer that contains the buffer to release
*
* Description:
* This routine frees the buffer that was allocated when the debugfs
* file was opened.
*
* Returns:
* This function returns zero.
*/
static int fnic_reset_stats_release(struct inode *inode,
struct file *file)
{
struct stats_debug_info *debug = file->private_data;
kfree(debug);
return 0;
}
/*
* fnic_stats_debugfs_open - Open the stats file for specific host
* and get fnic stats.
* @inode: The inode pointer.
* @file: The file pointer to attach the specific host statistics.
*
* Description:
* This routine opens a debugsfs file stats of specific host and print
* fnic stats.
*
* Returns:
* This function returns zero if successful.
*/
static int fnic_stats_debugfs_open(struct inode *inode,
struct file *file)
{
struct fnic *fnic = inode->i_private;
struct fnic_stats *fnic_stats = &fnic->fnic_stats;
struct stats_debug_info *debug;
int buf_size = 2 * PAGE_SIZE;
debug = kzalloc(sizeof(struct stats_debug_info), GFP_KERNEL);
if (!debug)
return -ENOMEM;
debug->debug_buffer = vmalloc(buf_size);
if (!debug->debug_buffer) {
kfree(debug);
return -ENOMEM;
}
debug->buf_size = buf_size;
memset((void *)debug->debug_buffer, 0, buf_size);
debug->buffer_len = fnic_get_stats_data(debug, fnic_stats);
file->private_data = debug;
return 0;
}
/*
* fnic_stats_debugfs_read - Read a debugfs file
* @file: The file pointer to read from.
* @ubuf: The buffer to copy the data to.
* @nbytes: The number of bytes to read.
* @pos: The position in the file to start reading from.
*
* Description:
* This routine reads data from the buffer indicated in the private_data
* field of @file. It will start reading at @pos and copy up to @nbytes of
* data to @ubuf.
*
* Returns:
* This function returns the amount of data that was read (this could be
* less than @nbytes if the end of the file was reached).
*/
static ssize_t fnic_stats_debugfs_read(struct file *file,
char __user *ubuf,
size_t nbytes,
loff_t *pos)
{
struct stats_debug_info *debug = file->private_data;
int rc = 0;
rc = simple_read_from_buffer(ubuf, nbytes, pos,
debug->debug_buffer,
debug->buffer_len);
return rc;
}
/*
* fnic_stats_stats_release - Release the buffer used to store
* debugfs file data
* @inode: The inode pointer
* @file: The file pointer that contains the buffer to release
*
* Description:
* This routine frees the buffer that was allocated when the debugfs
* file was opened.
*
* Returns:
* This function returns zero.
*/
static int fnic_stats_debugfs_release(struct inode *inode,
struct file *file)
{
struct stats_debug_info *debug = file->private_data;
vfree(debug->debug_buffer);
kfree(debug);
return 0;
}
static const struct file_operations fnic_stats_debugfs_fops = {
.owner = THIS_MODULE,
.open = fnic_stats_debugfs_open,
.read = fnic_stats_debugfs_read,
.release = fnic_stats_debugfs_release,
};
static const struct file_operations fnic_reset_debugfs_fops = {
.owner = THIS_MODULE,
.open = fnic_reset_stats_open,
.read = fnic_reset_stats_read,
.write = fnic_reset_stats_write,
.release = fnic_reset_stats_release,
};
/*
* fnic_stats_init - Initialize stats struct and create stats file per fnic
*
* Description:
* When Debugfs is configured this routine sets up the stats file per fnic
* It will create file stats and reset_stats under statistics/host# directory
* to log per fnic stats.
*/
int fnic_stats_debugfs_init(struct fnic *fnic)
{
int rc = -1;
char name[16];
snprintf(name, sizeof(name), "host%d", fnic->lport->host->host_no);
if (!fnic_stats_debugfs_root) {
printk(KERN_DEBUG "fnic_stats root doesn't exist\n");
return rc;
}
fnic->fnic_stats_debugfs_host = debugfs_create_dir(name,
fnic_stats_debugfs_root);
if (!fnic->fnic_stats_debugfs_host) {
printk(KERN_DEBUG "Cannot create host directory\n");
return rc;
}
fnic->fnic_stats_debugfs_file = debugfs_create_file("stats",
S_IFREG|S_IRUGO|S_IWUSR,
fnic->fnic_stats_debugfs_host,
fnic,
&fnic_stats_debugfs_fops);
if (!fnic->fnic_stats_debugfs_file) {
printk(KERN_DEBUG "Cannot create host stats file\n");
return rc;
}
fnic->fnic_reset_debugfs_file = debugfs_create_file("reset_stats",
S_IFREG|S_IRUGO|S_IWUSR,
fnic->fnic_stats_debugfs_host,
fnic,
&fnic_reset_debugfs_fops);
if (!fnic->fnic_reset_debugfs_file) {
printk(KERN_DEBUG "Cannot create host stats file\n");
return rc;
}
rc = 0;
return rc;
}
/*
* fnic_stats_debugfs_remove - Tear down debugfs infrastructure of stats
*
* Description:
* When Debugfs is configured this routine removes debugfs file system
* elements that are specific to fnic stats.
*/
void fnic_stats_debugfs_remove(struct fnic *fnic)
{
if (!fnic)
return;
debugfs_remove(fnic->fnic_stats_debugfs_file);
fnic->fnic_stats_debugfs_file = NULL;
debugfs_remove(fnic->fnic_reset_debugfs_file);
fnic->fnic_reset_debugfs_file = NULL;
debugfs_remove(fnic->fnic_stats_debugfs_host);
fnic->fnic_stats_debugfs_host = NULL;
}
......@@ -302,6 +302,7 @@ static inline int is_fnic_fip_flogi_reject(struct fcoe_ctlr *fip,
static void fnic_fcoe_send_vlan_req(struct fnic *fnic)
{
struct fcoe_ctlr *fip = &fnic->ctlr;
struct fnic_stats *fnic_stats = &fnic->fnic_stats;
struct sk_buff *skb;
char *eth_fr;
int fr_len;
......@@ -337,6 +338,7 @@ static void fnic_fcoe_send_vlan_req(struct fnic *fnic)
vlan->desc.wwnn.fd_desc.fip_dtype = FIP_DT_NAME;
vlan->desc.wwnn.fd_desc.fip_dlen = sizeof(vlan->desc.wwnn) / FIP_BPW;
put_unaligned_be64(fip->lp->wwnn, &vlan->desc.wwnn.fd_wwn);
atomic64_inc(&fnic_stats->vlan_stats.vlan_disc_reqs);
skb_put(skb, sizeof(*vlan));
skb->protocol = htons(ETH_P_FIP);
......@@ -354,6 +356,7 @@ static void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct sk_buff *skb)
struct fcoe_ctlr *fip = &fnic->ctlr;
struct fip_header *fiph;
struct fip_desc *desc;
struct fnic_stats *fnic_stats = &fnic->fnic_stats;
u16 vid;
size_t rlen;
size_t dlen;
......@@ -402,6 +405,7 @@ static void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct sk_buff *skb)
/* any VLAN descriptors present ? */
if (list_empty(&fnic->vlans)) {
/* retry from timer */
atomic64_inc(&fnic_stats->vlan_stats.resp_withno_vlanID);
FNIC_FCS_DBG(KERN_INFO, fnic->lport->host,
"No VLAN descriptors in FIP VLAN response\n");
spin_unlock_irqrestore(&fnic->vlans_lock, flags);
......@@ -533,6 +537,7 @@ drop:
void fnic_handle_fip_frame(struct work_struct *work)
{
struct fnic *fnic = container_of(work, struct fnic, fip_frame_work);
struct fnic_stats *fnic_stats = &fnic->fnic_stats;
unsigned long flags;
struct sk_buff *skb;
struct ethhdr *eh;
......@@ -567,6 +572,8 @@ void fnic_handle_fip_frame(struct work_struct *work)
* fcf's & restart from scratch
*/
if (is_fnic_fip_flogi_reject(&fnic->ctlr, skb)) {
atomic64_inc(
&fnic_stats->vlan_stats.flogi_rejects);
shost_printk(KERN_INFO, fnic->lport->host,
"Trigger a Link down - VLAN Disc\n");
fcoe_ctlr_link_down(&fnic->ctlr);
......@@ -753,6 +760,7 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
struct fnic *fnic = vnic_dev_priv(rq->vdev);
struct sk_buff *skb;
struct fc_frame *fp;
struct fnic_stats *fnic_stats = &fnic->fnic_stats;
unsigned int eth_hdrs_stripped;
u8 type, color, eop, sop, ingress_port, vlan_stripped;
u8 fcoe = 0, fcoe_sof, fcoe_eof;
......@@ -803,6 +811,7 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
eth_hdrs_stripped = 0;
skb_trim(skb, bytes_written);
if (!fcs_ok) {
atomic64_inc(&fnic_stats->misc_stats.frame_errors);
FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
"fcs error. dropping packet.\n");
goto drop;
......@@ -818,6 +827,7 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
}
if (!fcs_ok || packet_error || !fcoe_fc_crc_ok || fcoe_enc_error) {
atomic64_inc(&fnic_stats->misc_stats.frame_errors);
FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
"fnic rq_cmpl fcoe x%x fcsok x%x"
" pkterr x%x fcoe_fc_crc_ok x%x, fcoe_enc_err"
......@@ -1205,6 +1215,7 @@ void fnic_handle_fip_timer(struct fnic *fnic)
{
unsigned long flags;
struct fcoe_vlan *vlan;
struct fnic_stats *fnic_stats = &fnic->fnic_stats;
u64 sol_time;
spin_lock_irqsave(&fnic->fnic_lock, flags);
......@@ -1273,6 +1284,7 @@ void fnic_handle_fip_timer(struct fnic *fnic)
vlan->state = FIP_VLAN_SENT; /* sent now */
}
spin_unlock_irqrestore(&fnic->vlans_lock, flags);
atomic64_inc(&fnic_stats->vlan_stats.sol_expiry_count);
vlan->sol_count++;
sol_time = jiffies + msecs_to_jiffies
(FCOE_CTLR_START_DELAY);
......
......@@ -37,6 +37,9 @@ static irqreturn_t fnic_isr_legacy(int irq, void *data)
if (!pba)
return IRQ_NONE;
fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
if (pba & (1 << FNIC_INTX_NOTIFY)) {
vnic_intr_return_all_credits(&fnic->intr[FNIC_INTX_NOTIFY]);
fnic_handle_link_event(fnic);
......@@ -66,6 +69,9 @@ static irqreturn_t fnic_isr_msi(int irq, void *data)
struct fnic *fnic = data;
unsigned long work_done = 0;
fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
work_done += fnic_wq_copy_cmpl_handler(fnic, -1);
work_done += fnic_wq_cmpl_handler(fnic, -1);
work_done += fnic_rq_cmpl_handler(fnic, -1);
......@@ -83,6 +89,9 @@ static irqreturn_t fnic_isr_msix_rq(int irq, void *data)
struct fnic *fnic = data;
unsigned long rq_work_done = 0;
fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
rq_work_done = fnic_rq_cmpl_handler(fnic, -1);
vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_RQ],
rq_work_done,
......@@ -97,6 +106,9 @@ static irqreturn_t fnic_isr_msix_wq(int irq, void *data)
struct fnic *fnic = data;
unsigned long wq_work_done = 0;
fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
wq_work_done = fnic_wq_cmpl_handler(fnic, -1);
vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ],
wq_work_done,
......@@ -110,6 +122,9 @@ static irqreturn_t fnic_isr_msix_wq_copy(int irq, void *data)
struct fnic *fnic = data;
unsigned long wq_copy_work_done = 0;
fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, -1);
vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ_COPY],
wq_copy_work_done,
......@@ -122,6 +137,9 @@ static irqreturn_t fnic_isr_msix_err_notify(int irq, void *data)
{
struct fnic *fnic = data;
fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
vnic_intr_return_all_credits(&fnic->intr[FNIC_MSIX_ERR_NOTIFY]);
fnic_log_q_error(fnic);
fnic_handle_link_event(fnic);
......
......@@ -556,6 +556,13 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
host->transportt = fnic_fc_transport;
err = fnic_stats_debugfs_init(fnic);
if (err) {
shost_printk(KERN_ERR, fnic->lport->host,
"Failed to initialize debugfs for stats\n");
fnic_stats_debugfs_remove(fnic);
}
/* Setup PCI resources */
pci_set_drvdata(pdev, fnic);
......@@ -917,6 +924,7 @@ err_out_release_regions:
err_out_disable_device:
pci_disable_device(pdev);
err_out_free_hba:
fnic_stats_debugfs_remove(fnic);
scsi_host_put(lp->host);
err_out:
return err;
......@@ -969,6 +977,7 @@ static void fnic_remove(struct pci_dev *pdev)
fcoe_ctlr_destroy(&fnic->ctlr);
fc_lport_destroy(lp);