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 <lee...@chelsio.com>

Signed-off-by: Hariprasad Shenai <haripra...@chelsio.com>
---
 drivers/net/ethernet/chelsio/cxgb4/cxgb4.h         | 32 +++++++
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c | 99 ++++++++++++++++++++++
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c    | 12 +++
 drivers/net/ethernet/chelsio/cxgb4/t4_hw.c         | 69 ++++++++++-----
 4 files changed, 192 insertions(+), 20 deletions(-)

diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h 
b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index 6af5242e6d21..b4fceb92479f 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -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
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c 
b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
index 9506c5cd11b9..91fb50850fff 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
@@ -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 },
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c 
b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 4f627f3edb98..d7f40436f319 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -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
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c 
b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index 7907d85efa4c..49bcbf16c9ca 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
@@ -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,
-- 
2.3.4

Reply via email to