diff options
author | Florian Fainelli <f.fainelli@gmail.com> | 2017-06-15 13:15:53 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-06-16 12:43:48 -0400 |
commit | 484c01720d84c65e996a6adc4ba8ce6a811879df (patch) | |
tree | 32ba1a9e17d2722f7187891b8680432a8c355c49 /drivers/net/dsa/dsa_loop.c | |
parent | 3407dc8ed1a7528e8792c86d9ebc124aa5fa629f (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.c | 79 |
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 | ||
30 | struct dsa_loop_mib_entry { | ||
31 | char name[ETH_GSTRING_LEN]; | ||
32 | unsigned long val; | ||
33 | }; | ||
34 | |||
35 | enum 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 | |||
43 | static 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 | |||
50 | struct 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 | ||
31 | struct dsa_loop_priv { | 56 | struct 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 | ||
48 | static int dsa_loop_setup(struct dsa_switch *ds) | 74 | static 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 | ||
88 | static int dsa_loop_get_sset_count(struct dsa_switch *ds) | ||
89 | { | ||
90 | return __DSA_LOOP_CNT_MAX; | ||
91 | } | ||
92 | |||
93 | static 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 | |||
103 | static 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 | |||
55 | static int dsa_loop_set_addr(struct dsa_switch *ds, u8 *addr) | 113 | static 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 | ||
72 | static int dsa_loop_phy_write(struct dsa_switch *ds, int port, | 137 | static 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 | ||
83 | static int dsa_loop_port_bridge_join(struct dsa_switch *ds, int port, | 155 | static 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, | |||
225 | static struct dsa_switch_ops dsa_loop_driver = { | 297 | static 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, |