cxgb4vf: Add support to enable logging of firmware mailbox commands for VF

Add new /sys/kernel/debug/ support to dump firmware mailbox commands
and replies for debugging purpose.

Based on original work by Casey Leedom <leedom@chelsio.com>

Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Hariprasad Shenai 2016-04-28 13:23:19 +05:30 committed by David S. Miller
parent 7f080c3f2f
commit ae7b757622
4 changed files with 192 additions and 25 deletions

View File

@ -387,6 +387,10 @@ struct adapter {
/* various locks */ /* various locks */
spinlock_t stats_lock; spinlock_t stats_lock;
/* support for mailbox command/reply logging */
#define T4VF_OS_LOG_MBOX_CMDS 256
struct mbox_cmd_log *mbox_log;
/* list of MAC addresses in MPS Hash */ /* list of MAC addresses in MPS Hash */
struct list_head mac_hlist; struct list_head mac_hlist;
}; };

View File

@ -1703,6 +1703,105 @@ static const struct ethtool_ops cxgb4vf_ethtool_ops = {
* ================================================ * ================================================
*/ */
/*
* Show Firmware Mailbox Command/Reply Log
*
* Note that we don't do any locking when dumping the Firmware Mailbox Log so
* it's possible that we can catch things during a log update and therefore
* see partially corrupted log entries. But i9t's probably Good Enough(tm).
* If we ever decide that we want to make sure that we're dumping a coherent
* log, we'd need to perform locking in the mailbox logging and in
* mboxlog_open() where we'd need to grab the entire mailbox log in one go
* like we do for the Firmware Device Log. But as stated above, meh ...
*/
static int mboxlog_show(struct seq_file *seq, void *v)
{
struct adapter *adapter = seq->private;
struct mbox_cmd_log *log = adapter->mbox_log;
struct mbox_cmd *entry;
int entry_idx, i;
if (v == SEQ_START_TOKEN) {
seq_printf(seq,
"%10s %15s %5s %5s %s\n",
"Seq#", "Tstamp", "Atime", "Etime",
"Command/Reply");
return 0;
}
entry_idx = log->cursor + ((uintptr_t)v - 2);
if (entry_idx >= log->size)
entry_idx -= log->size;
entry = mbox_cmd_log_entry(log, entry_idx);
/* skip over unused entries */
if (entry->timestamp == 0)
return 0;
seq_printf(seq, "%10u %15llu %5d %5d",
entry->seqno, entry->timestamp,
entry->access, entry->execute);
for (i = 0; i < MBOX_LEN / 8; i++) {
u64 flit = entry->cmd[i];
u32 hi = (u32)(flit >> 32);
u32 lo = (u32)flit;
seq_printf(seq, " %08x %08x", hi, lo);
}
seq_puts(seq, "\n");
return 0;
}
static inline void *mboxlog_get_idx(struct seq_file *seq, loff_t pos)
{
struct adapter *adapter = seq->private;
struct mbox_cmd_log *log = adapter->mbox_log;
return ((pos <= log->size) ? (void *)(uintptr_t)(pos + 1) : NULL);
}
static void *mboxlog_start(struct seq_file *seq, loff_t *pos)
{
return *pos ? mboxlog_get_idx(seq, *pos) : SEQ_START_TOKEN;
}
static void *mboxlog_next(struct seq_file *seq, void *v, loff_t *pos)
{
++*pos;
return mboxlog_get_idx(seq, *pos);
}
static void mboxlog_stop(struct seq_file *seq, void *v)
{
}
static const struct seq_operations mboxlog_seq_ops = {
.start = mboxlog_start,
.next = mboxlog_next,
.stop = mboxlog_stop,
.show = mboxlog_show
};
static int mboxlog_open(struct inode *inode, struct file *file)
{
int res = seq_open(file, &mboxlog_seq_ops);
if (!res) {
struct seq_file *seq = file->private_data;
seq->private = inode->i_private;
}
return res;
}
static const struct file_operations mboxlog_fops = {
.owner = THIS_MODULE,
.open = mboxlog_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
/* /*
* Show SGE Queue Set information. We display QPL Queues Sets per line. * Show SGE Queue Set information. We display QPL Queues Sets per line.
*/ */
@ -2122,6 +2221,7 @@ struct cxgb4vf_debugfs_entry {
}; };
static struct cxgb4vf_debugfs_entry debugfs_files[] = { static struct cxgb4vf_debugfs_entry debugfs_files[] = {
{ "mboxlog", S_IRUGO, &mboxlog_fops },
{ "sge_qinfo", S_IRUGO, &sge_qinfo_debugfs_fops }, { "sge_qinfo", S_IRUGO, &sge_qinfo_debugfs_fops },
{ "sge_qstats", S_IRUGO, &sge_qstats_proc_fops }, { "sge_qstats", S_IRUGO, &sge_qstats_proc_fops },
{ "resources", S_IRUGO, &resources_proc_fops }, { "resources", S_IRUGO, &resources_proc_fops },
@ -2664,6 +2764,16 @@ static int cxgb4vf_pci_probe(struct pci_dev *pdev,
adapter->pdev = pdev; adapter->pdev = pdev;
adapter->pdev_dev = &pdev->dev; adapter->pdev_dev = &pdev->dev;
adapter->mbox_log = kzalloc(sizeof(*adapter->mbox_log) +
(sizeof(struct mbox_cmd) *
T4VF_OS_LOG_MBOX_CMDS),
GFP_KERNEL);
if (!adapter->mbox_log) {
err = -ENOMEM;
goto err_free_adapter;
}
adapter->mbox_log->size = T4VF_OS_LOG_MBOX_CMDS;
/* /*
* Initialize SMP data synchronization resources. * Initialize SMP data synchronization resources.
*/ */
@ -2913,6 +3023,7 @@ static int cxgb4vf_pci_probe(struct pci_dev *pdev,
iounmap(adapter->regs); iounmap(adapter->regs);
err_free_adapter: err_free_adapter:
kfree(adapter->mbox_log);
kfree(adapter); kfree(adapter);
err_release_regions: err_release_regions:
@ -2982,6 +3093,7 @@ static void cxgb4vf_pci_remove(struct pci_dev *pdev)
iounmap(adapter->regs); iounmap(adapter->regs);
if (!is_t4(adapter->params.chip)) if (!is_t4(adapter->params.chip))
iounmap(adapter->bar2); iounmap(adapter->bar2);
kfree(adapter->mbox_log);
kfree(adapter); kfree(adapter);
} }

View File

@ -36,6 +36,7 @@
#ifndef __T4VF_COMMON_H__ #ifndef __T4VF_COMMON_H__
#define __T4VF_COMMON_H__ #define __T4VF_COMMON_H__
#include "../cxgb4/t4_hw.h"
#include "../cxgb4/t4fw_api.h" #include "../cxgb4/t4fw_api.h"
#define CHELSIO_CHIP_CODE(version, revision) (((version) << 4) | (revision)) #define CHELSIO_CHIP_CODE(version, revision) (((version) << 4) | (revision))
@ -227,6 +228,34 @@ struct adapter_params {
u8 nports; /* # of Ethernet "ports" */ u8 nports; /* # of Ethernet "ports" */
}; };
/* Firmware Mailbox Command/Reply log. All values are in Host-Endian format.
* The access and execute times are signed in order to accommodate negative
* error returns.
*/
struct mbox_cmd {
u64 cmd[MBOX_LEN / 8]; /* a Firmware Mailbox Command/Reply */
u64 timestamp; /* OS-dependent timestamp */
u32 seqno; /* sequence number */
s16 access; /* time (ms) to access mailbox */
s16 execute; /* time (ms) to execute */
};
struct mbox_cmd_log {
unsigned int size; /* number of entries in the log */
unsigned int cursor; /* next position in the log to write */
u32 seqno; /* next sequence number */
/* variable length mailbox command log starts here */
};
/* Given a pointer to a Firmware Mailbox Command Log and a log entry index,
* return a pointer to the specified entry.
*/
static inline struct mbox_cmd *mbox_cmd_log_entry(struct mbox_cmd_log *log,
unsigned int entry_idx)
{
return &((struct mbox_cmd *)&(log)[1])[entry_idx];
}
#include "adapter.h" #include "adapter.h"
#ifndef PCI_VENDOR_ID_CHELSIO #ifndef PCI_VENDOR_ID_CHELSIO

View File

@ -76,21 +76,33 @@ static void get_mbox_rpl(struct adapter *adapter, __be64 *rpl, int size,
*rpl++ = cpu_to_be64(t4_read_reg64(adapter, mbox_data)); *rpl++ = cpu_to_be64(t4_read_reg64(adapter, mbox_data));
} }
/* /**
* Dump contents of mailbox with a leading tag. * t4vf_record_mbox - record a Firmware Mailbox Command/Reply in the log
* @adapter: the adapter
* @cmd: the Firmware Mailbox Command or Reply
* @size: command length in bytes
* @access: the time (ms) needed to access the Firmware Mailbox
* @execute: the time (ms) the command spent being executed
*/ */
static void dump_mbox(struct adapter *adapter, const char *tag, u32 mbox_data) static void t4vf_record_mbox(struct adapter *adapter, const __be64 *cmd,
int size, int access, int execute)
{ {
dev_err(adapter->pdev_dev, struct mbox_cmd_log *log = adapter->mbox_log;
"mbox %s: %llx %llx %llx %llx %llx %llx %llx %llx\n", tag, struct mbox_cmd *entry;
(unsigned long long)t4_read_reg64(adapter, mbox_data + 0), int i;
(unsigned long long)t4_read_reg64(adapter, mbox_data + 8),
(unsigned long long)t4_read_reg64(adapter, mbox_data + 16), entry = mbox_cmd_log_entry(log, log->cursor++);
(unsigned long long)t4_read_reg64(adapter, mbox_data + 24), if (log->cursor == log->size)
(unsigned long long)t4_read_reg64(adapter, mbox_data + 32), log->cursor = 0;
(unsigned long long)t4_read_reg64(adapter, mbox_data + 40),
(unsigned long long)t4_read_reg64(adapter, mbox_data + 48), for (i = 0; i < size / 8; i++)
(unsigned long long)t4_read_reg64(adapter, mbox_data + 56)); entry->cmd[i] = be64_to_cpu(cmd[i]);
while (i < MBOX_LEN / 8)
entry->cmd[i++] = 0;
entry->timestamp = jiffies;
entry->seqno = log->seqno++;
entry->access = access;
entry->execute = execute;
} }
/** /**
@ -120,10 +132,13 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size,
1, 1, 3, 5, 10, 10, 20, 50, 100 1, 1, 3, 5, 10, 10, 20, 50, 100
}; };
u16 access = 0, execute = 0;
u32 v, mbox_data; u32 v, mbox_data;
int i, ms, delay_idx; int i, ms, delay_idx, ret;
const __be64 *p; const __be64 *p;
u32 mbox_ctl = T4VF_CIM_BASE_ADDR + CIM_VF_EXT_MAILBOX_CTRL; u32 mbox_ctl = T4VF_CIM_BASE_ADDR + CIM_VF_EXT_MAILBOX_CTRL;
u32 cmd_op = FW_CMD_OP_G(be32_to_cpu(((struct fw_cmd_hdr *)cmd)->hi));
__be64 cmd_rpl[MBOX_LEN / 8];
/* In T6, mailbox size is changed to 128 bytes to avoid /* In T6, mailbox size is changed to 128 bytes to avoid
* invalidating the entire prefetch buffer. * invalidating the entire prefetch buffer.
@ -148,8 +163,11 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size,
v = MBOWNER_G(t4_read_reg(adapter, mbox_ctl)); v = MBOWNER_G(t4_read_reg(adapter, mbox_ctl));
for (i = 0; v == MBOX_OWNER_NONE && i < 3; i++) for (i = 0; v == MBOX_OWNER_NONE && i < 3; i++)
v = MBOWNER_G(t4_read_reg(adapter, mbox_ctl)); v = MBOWNER_G(t4_read_reg(adapter, mbox_ctl));
if (v != MBOX_OWNER_DRV) if (v != MBOX_OWNER_DRV) {
return v == MBOX_OWNER_FW ? -EBUSY : -ETIMEDOUT; ret = (v == MBOX_OWNER_FW) ? -EBUSY : -ETIMEDOUT;
t4vf_record_mbox(adapter, cmd, size, access, ret);
return ret;
}
/* /*
* Write the command array into the Mailbox Data register array and * Write the command array into the Mailbox Data register array and
@ -164,6 +182,8 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size,
* Data registers before doing the write to the VF Mailbox Control * Data registers before doing the write to the VF Mailbox Control
* register. * register.
*/ */
if (cmd_op != FW_VI_STATS_CMD)
t4vf_record_mbox(adapter, cmd, size, access, 0);
for (i = 0, p = cmd; i < size; i += 8) for (i = 0, p = cmd; i < size; i += 8)
t4_write_reg64(adapter, mbox_data + i, be64_to_cpu(*p++)); t4_write_reg64(adapter, mbox_data + i, be64_to_cpu(*p++));
t4_read_reg(adapter, mbox_data); /* flush write */ t4_read_reg(adapter, mbox_data); /* flush write */
@ -209,31 +229,33 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size,
* We return the (negated) firmware command return * We return the (negated) firmware command return
* code (this depends on FW_SUCCESS == 0). * code (this depends on FW_SUCCESS == 0).
*/ */
get_mbox_rpl(adapter, cmd_rpl, size, mbox_data);
/* return value in low-order little-endian word */ /* return value in low-order little-endian word */
v = t4_read_reg(adapter, mbox_data); v = be64_to_cpu(cmd_rpl[0]);
if (FW_CMD_RETVAL_G(v))
dump_mbox(adapter, "FW Error", mbox_data);
if (rpl) { if (rpl) {
/* request bit in high-order BE word */ /* request bit in high-order BE word */
WARN_ON((be32_to_cpu(*(const __be32 *)cmd) WARN_ON((be32_to_cpu(*(const __be32 *)cmd)
& FW_CMD_REQUEST_F) == 0); & FW_CMD_REQUEST_F) == 0);
get_mbox_rpl(adapter, rpl, size, mbox_data); memcpy(rpl, cmd_rpl, size);
WARN_ON((be32_to_cpu(*(__be32 *)rpl) WARN_ON((be32_to_cpu(*(__be32 *)rpl)
& FW_CMD_REQUEST_F) != 0); & FW_CMD_REQUEST_F) != 0);
} }
t4_write_reg(adapter, mbox_ctl, t4_write_reg(adapter, mbox_ctl,
MBOWNER_V(MBOX_OWNER_NONE)); MBOWNER_V(MBOX_OWNER_NONE));
execute = i + ms;
if (cmd_op != FW_VI_STATS_CMD)
t4vf_record_mbox(adapter, cmd_rpl, size, access,
execute);
return -FW_CMD_RETVAL_G(v); return -FW_CMD_RETVAL_G(v);
} }
} }
/* /* We timed out. Return the error ... */
* We timed out. Return the error ... ret = -ETIMEDOUT;
*/ t4vf_record_mbox(adapter, cmd, size, access, ret);
dump_mbox(adapter, "FW Timeout", mbox_data); return ret;
return -ETIMEDOUT;
} }
#define ADVERT_MASK (FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G |\ #define ADVERT_MASK (FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G |\