forked from Minki/linux
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 */
|
||||
};
|
||||
|
||||
/* 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"
|
||||
|
||||
#define FW_VERSION(chip) ( \
|
||||
@ -780,6 +808,10 @@ struct adapter {
|
||||
struct work_struct db_drop_task;
|
||||
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;
|
||||
bool use_bd; /* Use SGE Back Door intfc for reading SGE Contexts */
|
||||
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
|
||||
};
|
||||
|
||||
/* 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 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 },
|
||||
{ "clk", &clk_debugfs_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 },
|
||||
{ "mbox1", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 1 },
|
||||
{ "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;
|
||||
}
|
||||
|
||||
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 */
|
||||
adapter->flags |= DEV_ENABLED;
|
||||
|
||||
@ -5167,6 +5177,7 @@ sriov:
|
||||
if (adapter->workq)
|
||||
destroy_workqueue(adapter->workq);
|
||||
|
||||
kfree(adapter->mbox_log);
|
||||
kfree(adapter);
|
||||
out_unmap_bar0:
|
||||
iounmap(regs);
|
||||
@ -5233,6 +5244,7 @@ static void remove_one(struct pci_dev *pdev)
|
||||
adapter->flags &= ~DEV_ENABLED;
|
||||
}
|
||||
pci_release_regions(pdev);
|
||||
kfree(adapter->mbox_log);
|
||||
synchronize_rcu();
|
||||
kfree(adapter);
|
||||
} 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));
|
||||
}
|
||||
|
||||
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,
|
||||
"mbox %d: %llx %llx %llx %llx %llx %llx %llx %llx\n", mbox,
|
||||
(unsigned long long)t4_read_reg64(adap, data_reg),
|
||||
(unsigned long long)t4_read_reg64(adap, data_reg + 8),
|
||||
(unsigned long long)t4_read_reg64(adap, data_reg + 16),
|
||||
(unsigned long long)t4_read_reg64(adap, data_reg + 24),
|
||||
(unsigned long long)t4_read_reg64(adap, data_reg + 32),
|
||||
(unsigned long long)t4_read_reg64(adap, data_reg + 40),
|
||||
(unsigned long long)t4_read_reg64(adap, data_reg + 48),
|
||||
(unsigned long long)t4_read_reg64(adap, data_reg + 56));
|
||||
struct mbox_cmd_log *log = adapter->mbox_log;
|
||||
struct mbox_cmd *entry;
|
||||
int i;
|
||||
|
||||
entry = mbox_cmd_log_entry(log, log->cursor++);
|
||||
if (log->cursor == log->size)
|
||||
log->cursor = 0;
|
||||
|
||||
for (i = 0; i < size / 8; i++)
|
||||
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
|
||||
};
|
||||
|
||||
u16 access = 0;
|
||||
u16 execute = 0;
|
||||
u32 v;
|
||||
u64 res;
|
||||
int i, ms, delay_idx;
|
||||
int i, ms, delay_idx, ret;
|
||||
const __be64 *p = cmd;
|
||||
u32 data_reg = PF_REG(mbox, CIM_PF_MAILBOX_DATA_A);
|
||||
u32 ctl_reg = PF_REG(mbox, CIM_PF_MAILBOX_CTRL_A);
|
||||
__be64 cmd_rpl[MBOX_LEN / 8];
|
||||
|
||||
if ((size & 15) || size > MBOX_LEN)
|
||||
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++)
|
||||
v = MBOWNER_G(t4_read_reg(adap, ctl_reg));
|
||||
|
||||
if (v != MBOX_OWNER_DRV)
|
||||
return v ? -EBUSY : -ETIMEDOUT;
|
||||
if (v != MBOX_OWNER_DRV) {
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
fw_asrt(adap, data_reg);
|
||||
res = FW_CMD_RETVAL_V(EIO);
|
||||
} 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);
|
||||
|
||||
execute = i + ms;
|
||||
t4_record_mbox(adap, cmd_rpl,
|
||||
MBOX_LEN, access, execute);
|
||||
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",
|
||||
*(const u8 *)cmd, mbox);
|
||||
t4_report_fw_error(adap);
|
||||
return -ETIMEDOUT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int t4_wr_mbox_meat(struct adapter *adap, int mbox, const void *cmd, int size,
|
||||
|
Loading…
Reference in New Issue
Block a user