cxgb4: Add support to enable logging of firmware mailbox commands
Add new /sys/kernel/debug/ support to dump a firmware mailbox command issued 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:
parent
482f13aa5e
commit
7f080c3f2f
@ -359,6 +359,34 @@ struct sge_idma_monitor_state {
|
|||||||
unsigned int idma_warn[2]; /* time to warning in HZ */
|
unsigned int idma_warn[2]; /* time to warning in HZ */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* 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 "t4fw_api.h"
|
#include "t4fw_api.h"
|
||||||
|
|
||||||
#define FW_VERSION(chip) ( \
|
#define FW_VERSION(chip) ( \
|
||||||
@ -780,6 +808,10 @@ struct adapter {
|
|||||||
struct work_struct db_drop_task;
|
struct work_struct db_drop_task;
|
||||||
bool tid_release_task_busy;
|
bool tid_release_task_busy;
|
||||||
|
|
||||||
|
/* support for mailbox command/reply logging */
|
||||||
|
#define T4_OS_LOG_MBOX_CMDS 256
|
||||||
|
struct mbox_cmd_log *mbox_log;
|
||||||
|
|
||||||
struct dentry *debugfs_root;
|
struct dentry *debugfs_root;
|
||||||
bool use_bd; /* Use SGE Back Door intfc for reading SGE Contexts */
|
bool use_bd; /* Use SGE Back Door intfc for reading SGE Contexts */
|
||||||
bool trace_rss; /* 1 implies that different RSS flit per filter is
|
bool trace_rss; /* 1 implies that different RSS flit per filter is
|
||||||
|
@ -1152,6 +1152,104 @@ static const struct file_operations devlog_fops = {
|
|||||||
.release = seq_release_private
|
.release = seq_release_private
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* 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 it'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.
|
||||||
|
*/
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
static int mbox_show(struct seq_file *seq, void *v)
|
static int mbox_show(struct seq_file *seq, void *v)
|
||||||
{
|
{
|
||||||
static const char * const owner[] = { "none", "FW", "driver",
|
static const char * const owner[] = { "none", "FW", "driver",
|
||||||
@ -3129,6 +3227,7 @@ int t4_setup_debugfs(struct adapter *adap)
|
|||||||
{ "cim_qcfg", &cim_qcfg_fops, S_IRUSR, 0 },
|
{ "cim_qcfg", &cim_qcfg_fops, S_IRUSR, 0 },
|
||||||
{ "clk", &clk_debugfs_fops, S_IRUSR, 0 },
|
{ "clk", &clk_debugfs_fops, S_IRUSR, 0 },
|
||||||
{ "devlog", &devlog_fops, S_IRUSR, 0 },
|
{ "devlog", &devlog_fops, S_IRUSR, 0 },
|
||||||
|
{ "mboxlog", &mboxlog_fops, S_IRUSR, 0 },
|
||||||
{ "mbox0", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 0 },
|
{ "mbox0", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 0 },
|
||||||
{ "mbox1", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 1 },
|
{ "mbox1", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 1 },
|
||||||
{ "mbox2", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 2 },
|
{ "mbox2", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 2 },
|
||||||
|
@ -4909,6 +4909,16 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||||||
goto out_free_adapter;
|
goto out_free_adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adapter->mbox_log = kzalloc(sizeof(*adapter->mbox_log) +
|
||||||
|
(sizeof(struct mbox_cmd) *
|
||||||
|
T4_OS_LOG_MBOX_CMDS),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!adapter->mbox_log) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out_free_adapter;
|
||||||
|
}
|
||||||
|
adapter->mbox_log->size = T4_OS_LOG_MBOX_CMDS;
|
||||||
|
|
||||||
/* PCI device has been enabled */
|
/* PCI device has been enabled */
|
||||||
adapter->flags |= DEV_ENABLED;
|
adapter->flags |= DEV_ENABLED;
|
||||||
|
|
||||||
@ -5167,6 +5177,7 @@ sriov:
|
|||||||
if (adapter->workq)
|
if (adapter->workq)
|
||||||
destroy_workqueue(adapter->workq);
|
destroy_workqueue(adapter->workq);
|
||||||
|
|
||||||
|
kfree(adapter->mbox_log);
|
||||||
kfree(adapter);
|
kfree(adapter);
|
||||||
out_unmap_bar0:
|
out_unmap_bar0:
|
||||||
iounmap(regs);
|
iounmap(regs);
|
||||||
@ -5233,6 +5244,7 @@ static void remove_one(struct pci_dev *pdev)
|
|||||||
adapter->flags &= ~DEV_ENABLED;
|
adapter->flags &= ~DEV_ENABLED;
|
||||||
}
|
}
|
||||||
pci_release_regions(pdev);
|
pci_release_regions(pdev);
|
||||||
|
kfree(adapter->mbox_log);
|
||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
kfree(adapter);
|
kfree(adapter);
|
||||||
} else
|
} else
|
||||||
|
@ -224,18 +224,34 @@ static void fw_asrt(struct adapter *adap, u32 mbox_addr)
|
|||||||
be32_to_cpu(asrt.u.assert.x), be32_to_cpu(asrt.u.assert.y));
|
be32_to_cpu(asrt.u.assert.x), be32_to_cpu(asrt.u.assert.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_mbox(struct adapter *adap, int mbox, u32 data_reg)
|
/**
|
||||||
|
* t4_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 t4_record_mbox(struct adapter *adapter,
|
||||||
|
const __be64 *cmd, unsigned int size,
|
||||||
|
int access, int execute)
|
||||||
{
|
{
|
||||||
dev_err(adap->pdev_dev,
|
struct mbox_cmd_log *log = adapter->mbox_log;
|
||||||
"mbox %d: %llx %llx %llx %llx %llx %llx %llx %llx\n", mbox,
|
struct mbox_cmd *entry;
|
||||||
(unsigned long long)t4_read_reg64(adap, data_reg),
|
int i;
|
||||||
(unsigned long long)t4_read_reg64(adap, data_reg + 8),
|
|
||||||
(unsigned long long)t4_read_reg64(adap, data_reg + 16),
|
entry = mbox_cmd_log_entry(log, log->cursor++);
|
||||||
(unsigned long long)t4_read_reg64(adap, data_reg + 24),
|
if (log->cursor == log->size)
|
||||||
(unsigned long long)t4_read_reg64(adap, data_reg + 32),
|
log->cursor = 0;
|
||||||
(unsigned long long)t4_read_reg64(adap, data_reg + 40),
|
|
||||||
(unsigned long long)t4_read_reg64(adap, data_reg + 48),
|
for (i = 0; i < size / 8; i++)
|
||||||
(unsigned long long)t4_read_reg64(adap, data_reg + 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -268,12 +284,15 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd,
|
|||||||
1, 1, 3, 5, 10, 10, 20, 50, 100, 200
|
1, 1, 3, 5, 10, 10, 20, 50, 100, 200
|
||||||
};
|
};
|
||||||
|
|
||||||
|
u16 access = 0;
|
||||||
|
u16 execute = 0;
|
||||||
u32 v;
|
u32 v;
|
||||||
u64 res;
|
u64 res;
|
||||||
int i, ms, delay_idx;
|
int i, ms, delay_idx, ret;
|
||||||
const __be64 *p = cmd;
|
const __be64 *p = cmd;
|
||||||
u32 data_reg = PF_REG(mbox, CIM_PF_MAILBOX_DATA_A);
|
u32 data_reg = PF_REG(mbox, CIM_PF_MAILBOX_DATA_A);
|
||||||
u32 ctl_reg = PF_REG(mbox, CIM_PF_MAILBOX_CTRL_A);
|
u32 ctl_reg = PF_REG(mbox, CIM_PF_MAILBOX_CTRL_A);
|
||||||
|
__be64 cmd_rpl[MBOX_LEN / 8];
|
||||||
|
|
||||||
if ((size & 15) || size > MBOX_LEN)
|
if ((size & 15) || size > MBOX_LEN)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -289,9 +308,14 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd,
|
|||||||
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(adap, ctl_reg));
|
v = MBOWNER_G(t4_read_reg(adap, ctl_reg));
|
||||||
|
|
||||||
if (v != MBOX_OWNER_DRV)
|
if (v != MBOX_OWNER_DRV) {
|
||||||
return v ? -EBUSY : -ETIMEDOUT;
|
ret = (v == MBOX_OWNER_FW) ? -EBUSY : -ETIMEDOUT;
|
||||||
|
t4_record_mbox(adap, cmd, MBOX_LEN, access, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy in the new mailbox command and send it on its way ... */
|
||||||
|
t4_record_mbox(adap, cmd, MBOX_LEN, access, 0);
|
||||||
for (i = 0; i < size; i += 8)
|
for (i = 0; i < size; i += 8)
|
||||||
t4_write_reg64(adap, data_reg + i, be64_to_cpu(*p++));
|
t4_write_reg64(adap, data_reg + i, be64_to_cpu(*p++));
|
||||||
|
|
||||||
@ -317,26 +341,31 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = t4_read_reg64(adap, data_reg);
|
get_mbox_rpl(adap, cmd_rpl, MBOX_LEN / 8, data_reg);
|
||||||
|
res = be64_to_cpu(cmd_rpl[0]);
|
||||||
|
|
||||||
if (FW_CMD_OP_G(res >> 32) == FW_DEBUG_CMD) {
|
if (FW_CMD_OP_G(res >> 32) == FW_DEBUG_CMD) {
|
||||||
fw_asrt(adap, data_reg);
|
fw_asrt(adap, data_reg);
|
||||||
res = FW_CMD_RETVAL_V(EIO);
|
res = FW_CMD_RETVAL_V(EIO);
|
||||||
} else if (rpl) {
|
} else if (rpl) {
|
||||||
get_mbox_rpl(adap, rpl, size / 8, data_reg);
|
memcpy(rpl, cmd_rpl, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FW_CMD_RETVAL_G((int)res))
|
|
||||||
dump_mbox(adap, mbox, data_reg);
|
|
||||||
t4_write_reg(adap, ctl_reg, 0);
|
t4_write_reg(adap, ctl_reg, 0);
|
||||||
|
|
||||||
|
execute = i + ms;
|
||||||
|
t4_record_mbox(adap, cmd_rpl,
|
||||||
|
MBOX_LEN, access, execute);
|
||||||
return -FW_CMD_RETVAL_G((int)res);
|
return -FW_CMD_RETVAL_G((int)res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dump_mbox(adap, mbox, data_reg);
|
ret = -ETIMEDOUT;
|
||||||
|
t4_record_mbox(adap, cmd, MBOX_LEN, access, ret);
|
||||||
dev_err(adap->pdev_dev, "command %#x in mailbox %d timed out\n",
|
dev_err(adap->pdev_dev, "command %#x in mailbox %d timed out\n",
|
||||||
*(const u8 *)cmd, mbox);
|
*(const u8 *)cmd, mbox);
|
||||||
t4_report_fw_error(adap);
|
t4_report_fw_error(adap);
|
||||||
return -ETIMEDOUT;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int t4_wr_mbox_meat(struct adapter *adap, int mbox, const void *cmd, int size,
|
int t4_wr_mbox_meat(struct adapter *adap, int mbox, const void *cmd, int size,
|
||||||
|
Loading…
Reference in New Issue
Block a user