OSDN Git Service

Merge branch 'dsa-mv88e6xxx-debugfs'
authorDavid S. Miller <davem@davemloft.net>
Tue, 23 Jun 2015 13:33:51 +0000 (06:33 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 23 Jun 2015 13:33:51 +0000 (06:33 -0700)
Andrew Lunn says:

====================
debugfs for mv88e6xxx

This patchset adds some debugfs files for seeing into a mv88e6xxx
family of switch chips.

DB   T/P  Vec State Addr
003  Port 008   7   00:22:02:00:18:44
003  Port 008   6   80:ee:73:83:60:27
005  Port 020   7   94:10:3e:80:bc:f3
0f8  Port 001   6   8e:25:13:53:44:de

This walks all possible entries, so is a bit slow, but is always
correct.

Target Port
   0   15
   1   15
   2   15
   3   15
   4   15
   5   15
   6   15
   7   15
   8   15
   9   15
-->snip<--
  31   15

A rather boring example, since i only have one switch here. But this shows
the routing between multiple switches.

    GLOBAL GLOBAL2   0    1    2    3    4    5    6
 0:  c804       0  1e4f 100f 100f 1e4f 1e0f  e07  e07
 1:    fe       0     3    3    3    3    3 c03e c03f
 2:     0    ffff     0    0    0    0    0    0    0
 3:     0    ffff  1721 1721 1721 1721 1721 1721 1721
 4:  6000     258   433  431  431  433  433 373f  433
 5:     0      ff     0    0    0    0    0    0    0
 6:  c000    1f0f  2026 2025 2023 3020 4020 501f 6020
 7:     0    707f     0    0    0    0    0    0    0
 8:     0    7800  2080 2080 2080 2080 2080 2080 2080
 9:     0    1600     1    1    1    1    1    1    1
 a:   148       0     0    0    0    0    0    0    0
 b:  4000    1000     1    2    4    8   10   20   40
 c:     0      7f     0    0    0    0    0    0    0
 d:  ffff     5f3     0    0    0    0    0    0    0
 e:  ffff       6     0    0    0    0    0    0    0
 f:  ffff     f00  dada dada dada dada dada dada dada
10:     0       0     0    0    0    0    0    0    0
11:     0       0     0    0    0    0    0    0    0
12:  5555       0     0    0    0    0    0    0    0
13:  5555       0    1a    0    0 1df0    0 1e07    0
14:  aaaa     400     0    0    0    0    0    0    0
15:  aaaa       0     0    0    0    0    0    0    0
16:  ffff       0  6011 6011 6011 6011   33   33    0
17:  ffff       0     0    0    0    0    0    0    0
18:  fa41    1844  3210 3210 3210 3210 3210 3210 3210
19:     0     1e1  7654 7654 7654 7654 7654 7654 7654
1a:  5550       0     0    0    0    0    0    0    0
1b:   1fb    f869  8000 8000 8000 8000 8000 8000 8000
1c:     0       0     0    0    0    0    0    0    0
1d:   c00       0     0    0    0    0    0    0    0
1e:     0       0     0    0    0    0    0    0    0
1f:     0       0     0    0    0    0    0    0    0

All the switch registers which are directly accessible.

      Statistic       Port  0  Port  1  Port  2  Port  3  Port  4  Port  5  Port 6
     in_good_octets:     2176        0        0  4263711        0   499540       0
      in_bad_octets:    46050        0        0    50196        0        0       0
         in_unicast:        0        0        0     7693        0     7691       0
      in_broadcasts:        0        0        0        0        0        3       0
      in_multicasts:       34        0        0        0        0       27       0
           in_pause:        0        0        0        0        0        0       0
       in_undersize:        0        0        0        0        0        0       0
       in_fragments:       45        0        0        2        0        0       0
        in_oversize:        0        0        0        0        0        0       0
          in_jabber:        0        0        0        0        0        0       0
        in_rx_error:        0        0        0        0        0        0       0
       in_fcs_error:      159        0        0       37        0        0       0
         out_octets:      808        0        0   496608      336  4267159       0
        out_unicast:        0        0        0     7691        0     7693       0
     out_broadcasts:        1        0        0        3        0        0       0
     out_multicasts:        9        0        0        6        4       34       0
          out_pause:        0        0        0        0        0        0       0
          excessive:        0        0        0        0        0        0       0
         collisions:        0        0        0        0        0        0       0
           deferred:        0        0        0        0        0        0       0
             single:        0        0        0        0        0        0       0
           multiple:        0        0        0        0        0        0       0
      out_fcs_error:        0        0        0        0        0        0       0
               late:        0        0        0        0        0        0       0
       hist_64bytes:       36        0        0     7577        0     7574       0
   hist_65_127bytes:       53        0        0      241        4      298       0
  hist_128_255bytes:       50        0        0       12        0       10       0
  hist_256_511bytes:       43        0        0        8        0        2       0
 hist_512_1023bytes:       18        0        0     7573        0     7564       0
hist_1024_max_bytes:        3        0        0       19        0        0       0
     sw_in_discards:        0        0        0        0        0        0       0
     sw_in_filtered:        0        0        0        0        0        0       0
    sw_out_filtered:       34        0        0     7693        0     7721       0

Of particular interest here is that you get to see all ports,
including the CPU port and any DSA ports. You cannot get statistics
for these ports via ethtool.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dsa/mv88e6xxx.c
drivers/net/dsa/mv88e6xxx.h

index 39530fa..c938d7c 100644 (file)
@@ -8,6 +8,7 @@
  * (at your option) any later version.
  */
 
+#include <linux/debugfs.h>
 #include <linux/delay.h>
 #include <linux/etherdevice.h>
 #include <linux/if_bridge.h>
@@ -16,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/netdevice.h>
 #include <linux/phy.h>
+#include <linux/seq_file.h>
 #include <net/dsa.h>
 #include "mv88e6xxx.h"
 
@@ -679,6 +681,40 @@ static void _mv88e6xxx_get_strings(struct dsa_switch *ds,
        }
 }
 
+static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds,
+                                           int stat,
+                                           struct mv88e6xxx_hw_stat *stats,
+                                           int port)
+{
+       struct mv88e6xxx_hw_stat *s = stats + stat;
+       u32 low;
+       u32 high = 0;
+       int ret;
+       u64 value;
+
+       if (s->reg >= 0x100) {
+               ret = _mv88e6xxx_reg_read(ds, REG_PORT(port),
+                                         s->reg - 0x100);
+               if (ret < 0)
+                       return UINT64_MAX;
+
+               low = ret;
+               if (s->sizeof_stat == 4) {
+                       ret = _mv88e6xxx_reg_read(ds, REG_PORT(port),
+                                                 s->reg - 0x100 + 1);
+                       if (ret < 0)
+                               return UINT64_MAX;
+                       high = ret;
+               }
+       } else {
+               _mv88e6xxx_stats_read(ds, s->reg, &low);
+               if (s->sizeof_stat == 8)
+                       _mv88e6xxx_stats_read(ds, s->reg + 1, &high);
+       }
+       value = (((u64)high) << 16) | low;
+       return value;
+}
+
 static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
                                         int nr_stats,
                                         struct mv88e6xxx_hw_stat *stats,
@@ -697,34 +733,9 @@ static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
        }
 
        /* Read each of the counters. */
-       for (i = 0; i < nr_stats; i++) {
-               struct mv88e6xxx_hw_stat *s = stats + i;
-               u32 low;
-               u32 high = 0;
+       for (i = 0; i < nr_stats; i++)
+               data[i] = _mv88e6xxx_get_ethtool_stat(ds, i, stats, port);
 
-               if (s->reg >= 0x100) {
-                       ret = _mv88e6xxx_reg_read(ds, REG_PORT(port),
-                                                 s->reg - 0x100);
-                       if (ret < 0)
-                               goto error;
-                       low = ret;
-                       if (s->sizeof_stat == 4) {
-                               ret = _mv88e6xxx_reg_read(ds, REG_PORT(port),
-                                                         s->reg - 0x100 + 1);
-                               if (ret < 0)
-                                       goto error;
-                               high = ret;
-                       }
-                       data[i] = (((u64)high) << 16) | low;
-                       continue;
-               }
-               _mv88e6xxx_stats_read(ds, s->reg, &low);
-               if (s->sizeof_stat == 8)
-                       _mv88e6xxx_stats_read(ds, s->reg + 1, &high);
-
-               data[i] = (((u64)high) << 32) | low;
-       }
-error:
        mutex_unlock(&ps->smi_mutex);
 }
 
@@ -890,6 +901,13 @@ static int _mv88e6xxx_atu_wait(struct dsa_switch *ds)
                               GLOBAL_ATU_OP_BUSY);
 }
 
+/* Must be called with SMI lock held */
+static int _mv88e6xxx_scratch_wait(struct dsa_switch *ds)
+{
+       return _mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_SCRATCH_MISC,
+                              GLOBAL2_SCRATCH_BUSY);
+}
+
 /* Must be called with SMI mutex held */
 static int _mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr,
                                        int regnum)
@@ -1601,9 +1619,267 @@ int mv88e6xxx_setup_ports(struct dsa_switch *ds)
        return 0;
 }
 
+static int mv88e6xxx_regs_show(struct seq_file *s, void *p)
+{
+       struct dsa_switch *ds = s->private;
+
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+       int reg, port;
+
+       seq_puts(s, "    GLOBAL GLOBAL2 ");
+       for (port = 0 ; port < ps->num_ports; port++)
+               seq_printf(s, " %2d  ", port);
+       seq_puts(s, "\n");
+
+       for (reg = 0; reg < 32; reg++) {
+               seq_printf(s, "%2x: ", reg);
+               seq_printf(s, " %4x    %4x  ",
+                          mv88e6xxx_reg_read(ds, REG_GLOBAL, reg),
+                          mv88e6xxx_reg_read(ds, REG_GLOBAL2, reg));
+
+               for (port = 0 ; port < ps->num_ports; port++)
+                       seq_printf(s, "%4x ",
+                                  mv88e6xxx_reg_read(ds, REG_PORT(port), reg));
+               seq_puts(s, "\n");
+       }
+
+       return 0;
+}
+
+static int mv88e6xxx_regs_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mv88e6xxx_regs_show, inode->i_private);
+}
+
+static const struct file_operations mv88e6xxx_regs_fops = {
+       .open   = mv88e6xxx_regs_open,
+       .read   = seq_read,
+       .llseek = no_llseek,
+       .release = single_release,
+       .owner  = THIS_MODULE,
+};
+
+static void mv88e6xxx_atu_show_header(struct seq_file *s)
+{
+       seq_puts(s, "DB   T/P  Vec State Addr\n");
+}
+
+static void mv88e6xxx_atu_show_entry(struct seq_file *s, int dbnum,
+                                    unsigned char *addr, int data)
+{
+       bool trunk = !!(data & GLOBAL_ATU_DATA_TRUNK);
+       int portvec = ((data & GLOBAL_ATU_DATA_PORT_VECTOR_MASK) >>
+                      GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT);
+       int state = data & GLOBAL_ATU_DATA_STATE_MASK;
+
+       seq_printf(s, "%03x %5s %10pb   %x   %pM\n",
+                  dbnum, (trunk ? "Trunk" : "Port"), &portvec, state, addr);
+}
+
+static int mv88e6xxx_atu_show_db(struct seq_file *s, struct dsa_switch *ds,
+                                int dbnum)
+{
+       unsigned char bcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+       unsigned char addr[6];
+       int ret, data, state;
+
+       ret = __mv88e6xxx_write_addr(ds, bcast);
+       if (ret < 0)
+               return ret;
+
+       do {
+               ret = _mv88e6xxx_atu_cmd(ds, dbnum, GLOBAL_ATU_OP_GET_NEXT_DB);
+               if (ret < 0)
+                       return ret;
+               data = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_DATA);
+               if (data < 0)
+                       return data;
+
+               state = data & GLOBAL_ATU_DATA_STATE_MASK;
+               if (state == GLOBAL_ATU_DATA_STATE_UNUSED)
+                       break;
+               ret = __mv88e6xxx_read_addr(ds, addr);
+               if (ret < 0)
+                       return ret;
+               mv88e6xxx_atu_show_entry(s, dbnum, addr, data);
+       } while (state != GLOBAL_ATU_DATA_STATE_UNUSED);
+
+       return 0;
+}
+
+static int mv88e6xxx_atu_show(struct seq_file *s, void *p)
+{
+       struct dsa_switch *ds = s->private;
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+       int dbnum;
+
+       mv88e6xxx_atu_show_header(s);
+
+       for (dbnum = 0; dbnum < 255; dbnum++) {
+               mutex_lock(&ps->smi_mutex);
+               mv88e6xxx_atu_show_db(s, ds, dbnum);
+               mutex_unlock(&ps->smi_mutex);
+       }
+
+       return 0;
+}
+
+static int mv88e6xxx_atu_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mv88e6xxx_atu_show, inode->i_private);
+}
+
+static const struct file_operations mv88e6xxx_atu_fops = {
+       .open   = mv88e6xxx_atu_open,
+       .read   = seq_read,
+       .llseek = no_llseek,
+       .release = single_release,
+       .owner  = THIS_MODULE,
+};
+
+static void mv88e6xxx_stats_show_header(struct seq_file *s,
+                                       struct mv88e6xxx_priv_state *ps)
+{
+       int port;
+
+       seq_puts(s, "      Statistic       ");
+       for (port = 0 ; port < ps->num_ports; port++)
+               seq_printf(s, "Port %2d  ", port);
+       seq_puts(s, "\n");
+}
+
+static int mv88e6xxx_stats_show(struct seq_file *s, void *p)
+{
+       struct dsa_switch *ds = s->private;
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+       struct mv88e6xxx_hw_stat *stats = mv88e6xxx_hw_stats;
+       int port, stat, max_stats;
+       uint64_t value;
+
+       if (have_sw_in_discards(ds))
+               max_stats = ARRAY_SIZE(mv88e6xxx_hw_stats);
+       else
+               max_stats = ARRAY_SIZE(mv88e6xxx_hw_stats) - 3;
+
+       mv88e6xxx_stats_show_header(s, ps);
+
+       mutex_lock(&ps->smi_mutex);
+
+       for (stat = 0; stat < max_stats; stat++) {
+               seq_printf(s, "%19s: ", stats[stat].string);
+               for (port = 0 ; port < ps->num_ports; port++) {
+                       _mv88e6xxx_stats_snapshot(ds, port);
+                       value = _mv88e6xxx_get_ethtool_stat(ds, stat, stats,
+                                                           port);
+                       seq_printf(s, "%8llu ", value);
+               }
+               seq_puts(s, "\n");
+       }
+       mutex_unlock(&ps->smi_mutex);
+
+       return 0;
+}
+
+static int mv88e6xxx_stats_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mv88e6xxx_stats_show, inode->i_private);
+}
+
+static const struct file_operations mv88e6xxx_stats_fops = {
+       .open   = mv88e6xxx_stats_open,
+       .read   = seq_read,
+       .llseek = no_llseek,
+       .release = single_release,
+       .owner  = THIS_MODULE,
+};
+
+static int mv88e6xxx_device_map_show(struct seq_file *s, void *p)
+{
+       struct dsa_switch *ds = s->private;
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+       int target, ret;
+
+       seq_puts(s, "Target Port\n");
+
+       mutex_lock(&ps->smi_mutex);
+       for (target = 0; target < 32; target++) {
+               ret = _mv88e6xxx_reg_write(
+                       ds, REG_GLOBAL2, GLOBAL2_DEVICE_MAPPING,
+                       target << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT);
+               if (ret < 0)
+                       goto out;
+               ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL2,
+                                         GLOBAL2_DEVICE_MAPPING);
+               seq_printf(s, "  %2d   %2d\n", target,
+                          ret & GLOBAL2_DEVICE_MAPPING_PORT_MASK);
+       }
+out:
+       mutex_unlock(&ps->smi_mutex);
+
+       return 0;
+}
+
+static int mv88e6xxx_device_map_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mv88e6xxx_device_map_show, inode->i_private);
+}
+
+static const struct file_operations mv88e6xxx_device_map_fops = {
+       .open   = mv88e6xxx_device_map_open,
+       .read   = seq_read,
+       .llseek = no_llseek,
+       .release = single_release,
+       .owner  = THIS_MODULE,
+};
+
+static int mv88e6xxx_scratch_show(struct seq_file *s, void *p)
+{
+       struct dsa_switch *ds = s->private;
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+       int reg, ret;
+
+       seq_puts(s, "Register Value\n");
+
+       mutex_lock(&ps->smi_mutex);
+       for (reg = 0; reg < 0x80; reg++) {
+               ret = _mv88e6xxx_reg_write(
+                       ds, REG_GLOBAL2, GLOBAL2_SCRATCH_MISC,
+                       reg << GLOBAL2_SCRATCH_REGISTER_SHIFT);
+               if (ret < 0)
+                       goto out;
+
+               ret = _mv88e6xxx_scratch_wait(ds);
+               if (ret < 0)
+                       goto out;
+
+               ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL2,
+                                         GLOBAL2_SCRATCH_MISC);
+               seq_printf(s, "  %2x   %2x\n", reg,
+                          ret & GLOBAL2_SCRATCH_VALUE_MASK);
+       }
+out:
+       mutex_unlock(&ps->smi_mutex);
+
+       return 0;
+}
+
+static int mv88e6xxx_scratch_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mv88e6xxx_scratch_show, inode->i_private);
+}
+
+static const struct file_operations mv88e6xxx_scratch_fops = {
+       .open   = mv88e6xxx_scratch_open,
+       .read   = seq_read,
+       .llseek = no_llseek,
+       .release = single_release,
+       .owner  = THIS_MODULE,
+};
+
 int mv88e6xxx_setup_common(struct dsa_switch *ds)
 {
        struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+       char *name;
 
        mutex_init(&ps->smi_mutex);
 
@@ -1613,6 +1889,24 @@ int mv88e6xxx_setup_common(struct dsa_switch *ds)
 
        INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work);
 
+       name = kasprintf(GFP_KERNEL, "dsa%d", ds->index);
+       ps->dbgfs = debugfs_create_dir(name, NULL);
+       kfree(name);
+
+       debugfs_create_file("regs", S_IRUGO, ps->dbgfs, ds,
+                           &mv88e6xxx_regs_fops);
+
+       debugfs_create_file("atu", S_IRUGO, ps->dbgfs, ds,
+                           &mv88e6xxx_atu_fops);
+
+       debugfs_create_file("stats", S_IRUGO, ps->dbgfs, ds,
+                           &mv88e6xxx_stats_fops);
+
+       debugfs_create_file("device_map", S_IRUGO, ps->dbgfs, ds,
+                           &mv88e6xxx_device_map_fops);
+
+       debugfs_create_file("scratch", S_IRUGO, ps->dbgfs, ds,
+                           &mv88e6xxx_scratch_fops);
        return 0;
 }
 
index e10ccdb..a650b26 100644 (file)
 #ifndef __MV88E6XXX_H
 #define __MV88E6XXX_H
 
+#ifndef UINT64_MAX
+#define UINT64_MAX             (u64)(~((u64)0))
+#endif
+
 #define SMI_CMD                        0x00
 #define SMI_CMD_BUSY           BIT(15)
 #define SMI_CMD_CLAUSE_22      BIT(12)
 #define GLOBAL_ATU_OP_FLUSH_NON_STATIC_DB ((6 << 12) | GLOBAL_ATU_OP_BUSY)
 #define GLOBAL_ATU_OP_GET_CLR_VIOLATION          ((7 << 12) | GLOBAL_ATU_OP_BUSY)
 #define GLOBAL_ATU_DATA                0x0c
+#define GLOBAL_ATU_DATA_TRUNK                  BIT(15)
+#define GLOBAL_ATU_DATA_PORT_VECTOR_MASK       0x3ff0
+#define GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT      4
 #define GLOBAL_ATU_DATA_STATE_MASK             0x0f
 #define GLOBAL_ATU_DATA_STATE_UNUSED           0x00
 #define GLOBAL_ATU_DATA_STATE_UC_MGMT          0x0d
 #define GLOBAL2_DEVICE_MAPPING 0x06
 #define GLOBAL2_DEVICE_MAPPING_UPDATE          BIT(15)
 #define GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT    8
+#define GLOBAL2_DEVICE_MAPPING_PORT_MASK       0x0f
 #define GLOBAL2_TRUNK_MASK     0x07
 #define GLOBAL2_TRUNK_MASK_UPDATE              BIT(15)
 #define GLOBAL2_TRUNK_MASK_NUM_SHIFT           12
 #define GLOBAL2_SMI_OP_45_READ_DATA    ((2 << 10) | GLOBAL2_SMI_OP_BUSY)
 #define GLOBAL2_SMI_DATA       0x19
 #define GLOBAL2_SCRATCH_MISC   0x1a
+#define GLOBAL2_SCRATCH_BUSY           BIT(15)
+#define GLOBAL2_SCRATCH_REGISTER_SHIFT 8
+#define GLOBAL2_SCRATCH_VALUE_MASK     0xff
 #define GLOBAL2_WDOG_CONTROL   0x1b
 #define GLOBAL2_QOS_WEIGHT     0x1c
 #define GLOBAL2_MISC           0x1d
@@ -339,6 +350,8 @@ struct mv88e6xxx_priv_state {
        u8 port_state[DSA_MAX_PORTS];
 
        struct work_struct bridge_work;
+
+       struct dentry *dbgfs;
 };
 
 struct mv88e6xxx_hw_stat {