aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/dsa/dsa_loop.c
diff options
context:
space:
mode:
authorFlorian Fainelli <f.fainelli@gmail.com>2017-06-15 13:15:53 -0400
committerDavid S. Miller <davem@davemloft.net>2017-06-16 12:43:48 -0400
commit484c01720d84c65e996a6adc4ba8ce6a811879df (patch)
tree32ba1a9e17d2722f7187891b8680432a8c355c49 /drivers/net/dsa/dsa_loop.c
parent3407dc8ed1a7528e8792c86d9ebc124aa5fa629f (diff)
net: dsa: loop: Implement ethtool statistics
When a DSA driver implements ethtool statistics, we also override the master network device's ethtool statistics with the CPU port's statistics and this has proven to be a possible source of bugs in the past. Enhance the dsa_loop.c driver to provide statistics under the forme of ok/error reads and writes from the per-port PHY read/writes. Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/dsa/dsa_loop.c')
-rw-r--r--drivers/net/dsa/dsa_loop.c79
1 files changed, 77 insertions, 2 deletions
diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c
index fb888593c2e9..fdd8f3872102 100644
--- a/drivers/net/dsa/dsa_loop.c
+++ b/drivers/net/dsa/dsa_loop.c
@@ -14,6 +14,7 @@
14#include <linux/phy.h> 14#include <linux/phy.h>
15#include <linux/phy_fixed.h> 15#include <linux/phy_fixed.h>
16#include <linux/export.h> 16#include <linux/export.h>
17#include <linux/ethtool.h>
17#include <linux/workqueue.h> 18#include <linux/workqueue.h>
18#include <linux/module.h> 19#include <linux/module.h>
19#include <linux/if_bridge.h> 20#include <linux/if_bridge.h>
@@ -26,6 +27,30 @@ struct dsa_loop_vlan {
26 u16 untagged; 27 u16 untagged;
27}; 28};
28 29
30struct dsa_loop_mib_entry {
31 char name[ETH_GSTRING_LEN];
32 unsigned long val;
33};
34
35enum dsa_loop_mib_counters {
36 DSA_LOOP_PHY_READ_OK,
37 DSA_LOOP_PHY_READ_ERR,
38 DSA_LOOP_PHY_WRITE_OK,
39 DSA_LOOP_PHY_WRITE_ERR,
40 __DSA_LOOP_CNT_MAX,
41};
42
43static struct dsa_loop_mib_entry dsa_loop_mibs[] = {
44 [DSA_LOOP_PHY_READ_OK] = { "phy_read_ok", },
45 [DSA_LOOP_PHY_READ_ERR] = { "phy_read_err", },
46 [DSA_LOOP_PHY_WRITE_OK] = { "phy_write_ok", },
47 [DSA_LOOP_PHY_WRITE_ERR] = { "phy_write_err", },
48};
49
50struct dsa_loop_port {
51 struct dsa_loop_mib_entry mib[__DSA_LOOP_CNT_MAX];
52};
53
29#define DSA_LOOP_VLANS 5 54#define DSA_LOOP_VLANS 5
30 55
31struct dsa_loop_priv { 56struct dsa_loop_priv {
@@ -33,6 +58,7 @@ struct dsa_loop_priv {
33 unsigned int port_base; 58 unsigned int port_base;
34 struct dsa_loop_vlan vlans[DSA_LOOP_VLANS]; 59 struct dsa_loop_vlan vlans[DSA_LOOP_VLANS];
35 struct net_device *netdev; 60 struct net_device *netdev;
61 struct dsa_loop_port ports[DSA_MAX_PORTS];
36 u16 pvid; 62 u16 pvid;
37}; 63};
38 64
@@ -47,11 +73,43 @@ static enum dsa_tag_protocol dsa_loop_get_protocol(struct dsa_switch *ds)
47 73
48static int dsa_loop_setup(struct dsa_switch *ds) 74static int dsa_loop_setup(struct dsa_switch *ds)
49{ 75{
76 struct dsa_loop_priv *ps = ds->priv;
77 unsigned int i;
78
79 for (i = 0; i < ds->num_ports; i++)
80 memcpy(ps->ports[i].mib, dsa_loop_mibs,
81 sizeof(dsa_loop_mibs));
82
50 dev_dbg(ds->dev, "%s\n", __func__); 83 dev_dbg(ds->dev, "%s\n", __func__);
51 84
52 return 0; 85 return 0;
53} 86}
54 87
88static int dsa_loop_get_sset_count(struct dsa_switch *ds)
89{
90 return __DSA_LOOP_CNT_MAX;
91}
92
93static void dsa_loop_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
94{
95 struct dsa_loop_priv *ps = ds->priv;
96 unsigned int i;
97
98 for (i = 0; i < __DSA_LOOP_CNT_MAX; i++)
99 memcpy(data + i * ETH_GSTRING_LEN,
100 ps->ports[port].mib[i].name, ETH_GSTRING_LEN);
101}
102
103static void dsa_loop_get_ethtool_stats(struct dsa_switch *ds, int port,
104 uint64_t *data)
105{
106 struct dsa_loop_priv *ps = ds->priv;
107 unsigned int i;
108
109 for (i = 0; i < __DSA_LOOP_CNT_MAX; i++)
110 data[i] = ps->ports[port].mib[i].val;
111}
112
55static int dsa_loop_set_addr(struct dsa_switch *ds, u8 *addr) 113static int dsa_loop_set_addr(struct dsa_switch *ds, u8 *addr)
56{ 114{
57 dev_dbg(ds->dev, "%s\n", __func__); 115 dev_dbg(ds->dev, "%s\n", __func__);
@@ -63,10 +121,17 @@ static int dsa_loop_phy_read(struct dsa_switch *ds, int port, int regnum)
63{ 121{
64 struct dsa_loop_priv *ps = ds->priv; 122 struct dsa_loop_priv *ps = ds->priv;
65 struct mii_bus *bus = ps->bus; 123 struct mii_bus *bus = ps->bus;
124 int ret;
66 125
67 dev_dbg(ds->dev, "%s\n", __func__); 126 dev_dbg(ds->dev, "%s\n", __func__);
68 127
69 return mdiobus_read_nested(bus, ps->port_base + port, regnum); 128 ret = mdiobus_read_nested(bus, ps->port_base + port, regnum);
129 if (ret < 0)
130 ps->ports[port].mib[DSA_LOOP_PHY_READ_ERR].val++;
131 else
132 ps->ports[port].mib[DSA_LOOP_PHY_READ_OK].val++;
133
134 return ret;
70} 135}
71 136
72static int dsa_loop_phy_write(struct dsa_switch *ds, int port, 137static int dsa_loop_phy_write(struct dsa_switch *ds, int port,
@@ -74,10 +139,17 @@ static int dsa_loop_phy_write(struct dsa_switch *ds, int port,
74{ 139{
75 struct dsa_loop_priv *ps = ds->priv; 140 struct dsa_loop_priv *ps = ds->priv;
76 struct mii_bus *bus = ps->bus; 141 struct mii_bus *bus = ps->bus;
142 int ret;
77 143
78 dev_dbg(ds->dev, "%s\n", __func__); 144 dev_dbg(ds->dev, "%s\n", __func__);
79 145
80 return mdiobus_write_nested(bus, ps->port_base + port, regnum, value); 146 ret = mdiobus_write_nested(bus, ps->port_base + port, regnum, value);
147 if (ret < 0)
148 ps->ports[port].mib[DSA_LOOP_PHY_WRITE_ERR].val++;
149 else
150 ps->ports[port].mib[DSA_LOOP_PHY_WRITE_OK].val++;
151
152 return ret;
81} 153}
82 154
83static int dsa_loop_port_bridge_join(struct dsa_switch *ds, int port, 155static int dsa_loop_port_bridge_join(struct dsa_switch *ds, int port,
@@ -225,6 +297,9 @@ static int dsa_loop_port_vlan_dump(struct dsa_switch *ds, int port,
225static struct dsa_switch_ops dsa_loop_driver = { 297static struct dsa_switch_ops dsa_loop_driver = {
226 .get_tag_protocol = dsa_loop_get_protocol, 298 .get_tag_protocol = dsa_loop_get_protocol,
227 .setup = dsa_loop_setup, 299 .setup = dsa_loop_setup,
300 .get_strings = dsa_loop_get_strings,
301 .get_ethtool_stats = dsa_loop_get_ethtool_stats,
302 .get_sset_count = dsa_loop_get_sset_count,
228 .set_addr = dsa_loop_set_addr, 303 .set_addr = dsa_loop_set_addr,
229 .phy_read = dsa_loop_phy_read, 304 .phy_read = dsa_loop_phy_read,
230 .phy_write = dsa_loop_phy_write, 305 .phy_write = dsa_loop_phy_write,