Skip to content

Commit badf3ad

Browse files
ffainellidavem330
authored andcommitted
net: dsa: Provide CPU port statistics to master netdev
This patch overloads the DSA master netdev, aka CPU Ethernet MAC to also include switch-side statistics, which is useful for debugging purposes, when the switch is not properly connected to the Ethernet MAC (duplex mismatch, (RG)MII electrical issues etc.). We accomplish this by retaining the original copy of the master netdev's ethtool_ops, and just overload the 3 operations we care about: get_sset_count, get_strings and get_ethtool_stats so as to intercept these calls and call into the original master_netdev ethtool_ops, plus our own. We take this approach as opposed to providing a set of DSA helper functions that would retrive the CPU port's statistics, because the entire purpose of DSA is to allow unmodified Ethernet MAC drivers to be used as CPU conduit interfaces, therefore, statistics overlay in such drivers would simply not scale. The new ethtool -S <iface> output would therefore look like this now: <iface> statistics p<2 digits cpu port number>_<switch MIB counter names> Signed-off-by: Florian Fainelli <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 0cef6a4 commit badf3ad

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed

include/net/dsa.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ struct dsa_switch_tree {
110110
struct net_device *orig_dev);
111111
enum dsa_tag_protocol tag_protocol;
112112

113+
/*
114+
* Original copy of the master netdev ethtool_ops
115+
*/
116+
struct ethtool_ops master_ethtool_ops;
117+
113118
/*
114119
* The switch and port to which the CPU is attached.
115120
*/

net/dsa/slave.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,78 @@ static void dsa_slave_get_strings(struct net_device *dev,
666666
}
667667
}
668668

669+
static void dsa_cpu_port_get_ethtool_stats(struct net_device *dev,
670+
struct ethtool_stats *stats,
671+
uint64_t *data)
672+
{
673+
struct dsa_switch_tree *dst = dev->dsa_ptr;
674+
struct dsa_switch *ds = dst->ds[0];
675+
s8 cpu_port = dst->cpu_port;
676+
int count = 0;
677+
678+
if (dst->master_ethtool_ops.get_sset_count) {
679+
count = dst->master_ethtool_ops.get_sset_count(dev,
680+
ETH_SS_STATS);
681+
dst->master_ethtool_ops.get_ethtool_stats(dev, stats, data);
682+
}
683+
684+
if (ds->drv->get_ethtool_stats)
685+
ds->drv->get_ethtool_stats(ds, cpu_port, data + count);
686+
}
687+
688+
static int dsa_cpu_port_get_sset_count(struct net_device *dev, int sset)
689+
{
690+
struct dsa_switch_tree *dst = dev->dsa_ptr;
691+
struct dsa_switch *ds = dst->ds[0];
692+
int count = 0;
693+
694+
if (dst->master_ethtool_ops.get_sset_count)
695+
count += dst->master_ethtool_ops.get_sset_count(dev, sset);
696+
697+
if (sset == ETH_SS_STATS && ds->drv->get_sset_count)
698+
count += ds->drv->get_sset_count(ds);
699+
700+
return count;
701+
}
702+
703+
static void dsa_cpu_port_get_strings(struct net_device *dev,
704+
uint32_t stringset, uint8_t *data)
705+
{
706+
struct dsa_switch_tree *dst = dev->dsa_ptr;
707+
struct dsa_switch *ds = dst->ds[0];
708+
s8 cpu_port = dst->cpu_port;
709+
int len = ETH_GSTRING_LEN;
710+
int mcount = 0, count;
711+
unsigned int i;
712+
uint8_t pfx[4];
713+
uint8_t *ndata;
714+
715+
snprintf(pfx, sizeof(pfx), "p%.2d", cpu_port);
716+
/* We do not want to be NULL-terminated, since this is a prefix */
717+
pfx[sizeof(pfx) - 1] = '_';
718+
719+
if (dst->master_ethtool_ops.get_sset_count) {
720+
mcount = dst->master_ethtool_ops.get_sset_count(dev,
721+
ETH_SS_STATS);
722+
dst->master_ethtool_ops.get_strings(dev, stringset, data);
723+
}
724+
725+
if (stringset == ETH_SS_STATS && ds->drv->get_strings) {
726+
ndata = data + mcount * len;
727+
/* This function copies ETH_GSTRINGS_LEN bytes, we will mangle
728+
* the output after to prepend our CPU port prefix we
729+
* constructed earlier
730+
*/
731+
ds->drv->get_strings(ds, cpu_port, ndata);
732+
count = ds->drv->get_sset_count(ds);
733+
for (i = 0; i < count; i++) {
734+
memmove(ndata + (i * len + sizeof(pfx)),
735+
ndata + i * len, len - sizeof(pfx));
736+
memcpy(ndata + i * len, pfx, sizeof(pfx));
737+
}
738+
}
739+
}
740+
669741
static void dsa_slave_get_ethtool_stats(struct net_device *dev,
670742
struct ethtool_stats *stats,
671743
uint64_t *data)
@@ -821,6 +893,8 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
821893
.get_eee = dsa_slave_get_eee,
822894
};
823895

896+
static struct ethtool_ops dsa_cpu_port_ethtool_ops;
897+
824898
static const struct net_device_ops dsa_slave_netdev_ops = {
825899
.ndo_open = dsa_slave_open,
826900
.ndo_stop = dsa_slave_close,
@@ -1038,6 +1112,7 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
10381112
int port, char *name)
10391113
{
10401114
struct net_device *master = ds->dst->master_netdev;
1115+
struct dsa_switch_tree *dst = ds->dst;
10411116
struct net_device *slave_dev;
10421117
struct dsa_slave_priv *p;
10431118
int ret;
@@ -1049,6 +1124,19 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
10491124

10501125
slave_dev->features = master->vlan_features;
10511126
slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
1127+
if (master->ethtool_ops != &dsa_cpu_port_ethtool_ops) {
1128+
memcpy(&dst->master_ethtool_ops, master->ethtool_ops,
1129+
sizeof(struct ethtool_ops));
1130+
memcpy(&dsa_cpu_port_ethtool_ops, &dst->master_ethtool_ops,
1131+
sizeof(struct ethtool_ops));
1132+
dsa_cpu_port_ethtool_ops.get_sset_count =
1133+
dsa_cpu_port_get_sset_count;
1134+
dsa_cpu_port_ethtool_ops.get_ethtool_stats =
1135+
dsa_cpu_port_get_ethtool_stats;
1136+
dsa_cpu_port_ethtool_ops.get_strings =
1137+
dsa_cpu_port_get_strings;
1138+
master->ethtool_ops = &dsa_cpu_port_ethtool_ops;
1139+
}
10521140
eth_hw_addr_inherit(slave_dev, master);
10531141
slave_dev->priv_flags |= IFF_NO_QUEUE;
10541142
slave_dev->netdev_ops = &dsa_slave_netdev_ops;

0 commit comments

Comments
 (0)