dpaa2-eth: add debugfs statistics
authorIoana Radulescu <ruxandra.radulescu@nxp.com>
Fri, 18 Jan 2019 16:16:00 +0000 (16:16 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sat, 19 Jan 2019 18:28:43 +0000 (10:28 -0800)
Export detailed driver counters through debugfs.

Statistics already available in ethtool are presented in a
structured manner. Includes per-core, per-FQ and per-channel statistics.

Also transition from module_fsl_mc_driver to explicit module_init/exit
in order to create the debugfs directory besides registering the driver.

Signed-off-by: Ioana Radulescu <ruxandra.radulescu@nxp.com>
Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/freescale/dpaa2/Makefile
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c [new file with mode: 0644]
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.h [new file with mode: 0644]
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h

index 2f424e0..d1e78cd 100644 (file)
@@ -7,6 +7,7 @@ obj-$(CONFIG_FSL_DPAA2_ETH)             += fsl-dpaa2-eth.o
 obj-$(CONFIG_FSL_DPAA2_PTP_CLOCK)      += fsl-dpaa2-ptp.o
 
 fsl-dpaa2-eth-objs     := dpaa2-eth.o dpaa2-ethtool.o dpni.o
+fsl-dpaa2-eth-${CONFIG_DEBUG_FS} += dpaa2-eth-debugfs.o
 fsl-dpaa2-ptp-objs     := dpaa2-ptp.o dprtc.o
 
 # Needed by the tracing framework
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c
new file mode 100644 (file)
index 0000000..a027f4a
--- /dev/null
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* Copyright 2015 Freescale Semiconductor Inc.
+ * Copyright 2018-2019 NXP
+ */
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include "dpaa2-eth.h"
+#include "dpaa2-eth-debugfs.h"
+
+#define DPAA2_ETH_DBG_ROOT "dpaa2-eth"
+
+static struct dentry *dpaa2_dbg_root;
+
+static int dpaa2_dbg_cpu_show(struct seq_file *file, void *offset)
+{
+       struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)file->private;
+       struct rtnl_link_stats64 *stats;
+       struct dpaa2_eth_drv_stats *extras;
+       int i;
+
+       seq_printf(file, "Per-CPU stats for %s\n", priv->net_dev->name);
+       seq_printf(file, "%s%16s%16s%16s%16s%16s%16s%16s%16s%16s\n",
+                  "CPU", "Rx", "Rx Err", "Rx SG", "Tx", "Tx Err", "Tx conf",
+                  "Tx SG", "Tx realloc", "Enq busy");
+
+       for_each_online_cpu(i) {
+               stats = per_cpu_ptr(priv->percpu_stats, i);
+               extras = per_cpu_ptr(priv->percpu_extras, i);
+               seq_printf(file, "%3d%16llu%16llu%16llu%16llu%16llu%16llu%16llu%16llu%16llu\n",
+                          i,
+                          stats->rx_packets,
+                          stats->rx_errors,
+                          extras->rx_sg_frames,
+                          stats->tx_packets,
+                          stats->tx_errors,
+                          extras->tx_conf_frames,
+                          extras->tx_sg_frames,
+                          extras->tx_reallocs,
+                          extras->tx_portal_busy);
+       }
+
+       return 0;
+}
+
+static int dpaa2_dbg_cpu_open(struct inode *inode, struct file *file)
+{
+       int err;
+       struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)inode->i_private;
+
+       err = single_open(file, dpaa2_dbg_cpu_show, priv);
+       if (err < 0)
+               netdev_err(priv->net_dev, "single_open() failed\n");
+
+       return err;
+}
+
+static const struct file_operations dpaa2_dbg_cpu_ops = {
+       .open = dpaa2_dbg_cpu_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static char *fq_type_to_str(struct dpaa2_eth_fq *fq)
+{
+       switch (fq->type) {
+       case DPAA2_RX_FQ:
+               return "Rx";
+       case DPAA2_TX_CONF_FQ:
+               return "Tx conf";
+       default:
+               return "N/A";
+       }
+}
+
+static int dpaa2_dbg_fqs_show(struct seq_file *file, void *offset)
+{
+       struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)file->private;
+       struct dpaa2_eth_fq *fq;
+       u32 fcnt, bcnt;
+       int i, err;
+
+       seq_printf(file, "FQ stats for %s:\n", priv->net_dev->name);
+       seq_printf(file, "%s%16s%16s%16s%16s\n",
+                  "VFQID", "CPU", "Type", "Frames", "Pending frames");
+
+       for (i = 0; i <  priv->num_fqs; i++) {
+               fq = &priv->fq[i];
+               err = dpaa2_io_query_fq_count(NULL, fq->fqid, &fcnt, &bcnt);
+               if (err)
+                       fcnt = 0;
+
+               seq_printf(file, "%5d%16d%16s%16llu%16u\n",
+                          fq->fqid,
+                          fq->target_cpu,
+                          fq_type_to_str(fq),
+                          fq->stats.frames,
+                          fcnt);
+       }
+
+       return 0;
+}
+
+static int dpaa2_dbg_fqs_open(struct inode *inode, struct file *file)
+{
+       int err;
+       struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)inode->i_private;
+
+       err = single_open(file, dpaa2_dbg_fqs_show, priv);
+       if (err < 0)
+               netdev_err(priv->net_dev, "single_open() failed\n");
+
+       return err;
+}
+
+static const struct file_operations dpaa2_dbg_fq_ops = {
+       .open = dpaa2_dbg_fqs_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static int dpaa2_dbg_ch_show(struct seq_file *file, void *offset)
+{
+       struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)file->private;
+       struct dpaa2_eth_channel *ch;
+       int i;
+
+       seq_printf(file, "Channel stats for %s:\n", priv->net_dev->name);
+       seq_printf(file, "%s%16s%16s%16s%16s\n",
+                  "CHID", "CPU", "Deq busy", "CDANs", "Buf count");
+
+       for (i = 0; i < priv->num_channels; i++) {
+               ch = priv->channel[i];
+               seq_printf(file, "%4d%16d%16llu%16llu%16d\n",
+                          ch->ch_id,
+                          ch->nctx.desired_cpu,
+                          ch->stats.dequeue_portal_busy,
+                          ch->stats.cdan,
+                          ch->buf_count);
+       }
+
+       return 0;
+}
+
+static int dpaa2_dbg_ch_open(struct inode *inode, struct file *file)
+{
+       int err;
+       struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)inode->i_private;
+
+       err = single_open(file, dpaa2_dbg_ch_show, priv);
+       if (err < 0)
+               netdev_err(priv->net_dev, "single_open() failed\n");
+
+       return err;
+}
+
+static const struct file_operations dpaa2_dbg_ch_ops = {
+       .open = dpaa2_dbg_ch_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+void dpaa2_dbg_add(struct dpaa2_eth_priv *priv)
+{
+       if (!dpaa2_dbg_root)
+               return;
+
+       /* Create a directory for the interface */
+       priv->dbg.dir = debugfs_create_dir(priv->net_dev->name,
+                                          dpaa2_dbg_root);
+       if (!priv->dbg.dir) {
+               netdev_err(priv->net_dev, "debugfs_create_dir() failed\n");
+               return;
+       }
+
+       /* per-cpu stats file */
+       priv->dbg.cpu_stats = debugfs_create_file("cpu_stats", 0444,
+                                                 priv->dbg.dir, priv,
+                                                 &dpaa2_dbg_cpu_ops);
+       if (!priv->dbg.cpu_stats) {
+               netdev_err(priv->net_dev, "debugfs_create_file() failed\n");
+               goto err_cpu_stats;
+       }
+
+       /* per-fq stats file */
+       priv->dbg.fq_stats = debugfs_create_file("fq_stats", 0444,
+                                                priv->dbg.dir, priv,
+                                                &dpaa2_dbg_fq_ops);
+       if (!priv->dbg.fq_stats) {
+               netdev_err(priv->net_dev, "debugfs_create_file() failed\n");
+               goto err_fq_stats;
+       }
+
+       /* per-fq stats file */
+       priv->dbg.ch_stats = debugfs_create_file("ch_stats", 0444,
+                                                priv->dbg.dir, priv,
+                                                &dpaa2_dbg_ch_ops);
+       if (!priv->dbg.fq_stats) {
+               netdev_err(priv->net_dev, "debugfs_create_file() failed\n");
+               goto err_ch_stats;
+       }
+
+       return;
+
+err_ch_stats:
+       debugfs_remove(priv->dbg.fq_stats);
+err_fq_stats:
+       debugfs_remove(priv->dbg.cpu_stats);
+err_cpu_stats:
+       debugfs_remove(priv->dbg.dir);
+}
+
+void dpaa2_dbg_remove(struct dpaa2_eth_priv *priv)
+{
+       debugfs_remove(priv->dbg.fq_stats);
+       debugfs_remove(priv->dbg.ch_stats);
+       debugfs_remove(priv->dbg.cpu_stats);
+       debugfs_remove(priv->dbg.dir);
+}
+
+void dpaa2_eth_dbg_init(void)
+{
+       dpaa2_dbg_root = debugfs_create_dir(DPAA2_ETH_DBG_ROOT, NULL);
+       if (!dpaa2_dbg_root) {
+               pr_err("DPAA2-ETH: debugfs create failed\n");
+               return;
+       }
+
+       pr_debug("DPAA2-ETH: debugfs created\n");
+}
+
+void dpaa2_eth_dbg_exit(void)
+{
+       debugfs_remove(dpaa2_dbg_root);
+}
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.h
new file mode 100644 (file)
index 0000000..4f63de9
--- /dev/null
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/* Copyright 2015 Freescale Semiconductor Inc.
+ * Copyright 2018-2019 NXP
+ */
+#ifndef DPAA2_ETH_DEBUGFS_H
+#define DPAA2_ETH_DEBUGFS_H
+
+#include <linux/dcache.h>
+
+struct dpaa2_eth_priv;
+
+struct dpaa2_debugfs {
+       struct dentry *dir;
+       struct dentry *fq_stats;
+       struct dentry *ch_stats;
+       struct dentry *cpu_stats;
+};
+
+#ifdef CONFIG_DEBUG_FS
+void dpaa2_eth_dbg_init(void);
+void dpaa2_eth_dbg_exit(void);
+void dpaa2_dbg_add(struct dpaa2_eth_priv *priv);
+void dpaa2_dbg_remove(struct dpaa2_eth_priv *priv);
+#else
+static inline void dpaa2_eth_dbg_init(void) {}
+static inline void dpaa2_eth_dbg_exit(void) {}
+static inline void dpaa2_dbg_add(struct dpaa2_eth_priv *priv) {}
+static inline void dpaa2_dbg_remove(struct dpaa2_eth_priv *priv) {}
+#endif /* CONFIG_DEBUG_FS */
+
+#endif /* DPAA2_ETH_DEBUGFS_H */
index ecf9218..04925c7 100644 (file)
@@ -3083,6 +3083,10 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev)
                goto err_netdev_reg;
        }
 
+#ifdef CONFIG_DEBUG_FS
+       dpaa2_dbg_add(priv);
+#endif
+
        dev_info(dev, "Probed interface %s\n", net_dev->name);
        return 0;
 
@@ -3126,6 +3130,9 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev)
        net_dev = dev_get_drvdata(dev);
        priv = netdev_priv(net_dev);
 
+#ifdef CONFIG_DEBUG_FS
+       dpaa2_dbg_remove(priv);
+#endif
        unregister_netdev(net_dev);
 
        if (priv->do_link_poll)
@@ -3170,4 +3177,25 @@ static struct fsl_mc_driver dpaa2_eth_driver = {
        .match_id_table = dpaa2_eth_match_id_table
 };
 
-module_fsl_mc_driver(dpaa2_eth_driver);
+static int __init dpaa2_eth_driver_init(void)
+{
+       int err;
+
+       dpaa2_eth_dbg_init();
+       err = fsl_mc_driver_register(&dpaa2_eth_driver);
+       if (err) {
+               dpaa2_eth_dbg_exit();
+               return err;
+       }
+
+       return 0;
+}
+
+static void __exit dpaa2_eth_driver_exit(void)
+{
+       dpaa2_eth_dbg_exit();
+       fsl_mc_driver_unregister(&dpaa2_eth_driver);
+}
+
+module_init(dpaa2_eth_driver_init);
+module_exit(dpaa2_eth_driver_exit);
index 308aae6..31fe486 100644 (file)
@@ -16,6 +16,7 @@
 #include "dpni-cmd.h"
 
 #include "dpaa2-eth-trace.h"
+#include "dpaa2-eth-debugfs.h"
 
 #define DPAA2_WRIOP_VERSION(x, y, z) ((x) << 10 | (y) << 5 | (z) << 0)
 
@@ -365,6 +366,9 @@ struct dpaa2_eth_priv {
        struct dpaa2_eth_cls_rule *cls_rules;
        u8 rx_cls_enabled;
        struct bpf_prog *xdp_prog;
+#ifdef CONFIG_DEBUG_FS
+       struct dpaa2_debugfs dbg;
+#endif
 };
 
 #define DPAA2_RXH_SUPPORTED    (RXH_L2DA | RXH_VLAN | RXH_L3_PROTO \