aboutsummaryrefslogtreecommitdiffstats
path: root/net/dsa/slave.c
diff options
context:
space:
mode:
authorFlorian Fainelli <f.fainelli@gmail.com>2016-04-27 14:45:14 -0400
committerDavid S. Miller <davem@davemloft.net>2016-04-28 17:16:17 -0400
commitbadf3ada60ab8f76f9488dc8f5c0c57f70682f5a (patch)
tree7abbe5f6b670ab233e1ef0cc7c0be4eea75c0951 /net/dsa/slave.c
parent0cef6a4c34b56a9a6894f2dad2fad4be789990e1 (diff)
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 <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/dsa/slave.c')
-rw-r--r--net/dsa/slave.c88
1 files changed, 88 insertions, 0 deletions
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 3b6750f5e68b..5ea8a40c8d33 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -666,6 +666,78 @@ static void dsa_slave_get_strings(struct net_device *dev,
666 } 666 }
667} 667}
668 668
669static 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
688static 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
703static 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
669static void dsa_slave_get_ethtool_stats(struct net_device *dev, 741static void dsa_slave_get_ethtool_stats(struct net_device *dev,
670 struct ethtool_stats *stats, 742 struct ethtool_stats *stats,
671 uint64_t *data) 743 uint64_t *data)
@@ -821,6 +893,8 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
821 .get_eee = dsa_slave_get_eee, 893 .get_eee = dsa_slave_get_eee,
822}; 894};
823 895
896static struct ethtool_ops dsa_cpu_port_ethtool_ops;
897
824static const struct net_device_ops dsa_slave_netdev_ops = { 898static const struct net_device_ops dsa_slave_netdev_ops = {
825 .ndo_open = dsa_slave_open, 899 .ndo_open = dsa_slave_open,
826 .ndo_stop = dsa_slave_close, 900 .ndo_stop = dsa_slave_close,
@@ -1038,6 +1112,7 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
1038 int port, char *name) 1112 int port, char *name)
1039{ 1113{
1040 struct net_device *master = ds->dst->master_netdev; 1114 struct net_device *master = ds->dst->master_netdev;
1115 struct dsa_switch_tree *dst = ds->dst;
1041 struct net_device *slave_dev; 1116 struct net_device *slave_dev;
1042 struct dsa_slave_priv *p; 1117 struct dsa_slave_priv *p;
1043 int ret; 1118 int ret;
@@ -1049,6 +1124,19 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
1049 1124
1050 slave_dev->features = master->vlan_features; 1125 slave_dev->features = master->vlan_features;
1051 slave_dev->ethtool_ops = &dsa_slave_ethtool_ops; 1126 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 }
1052 eth_hw_addr_inherit(slave_dev, master); 1140 eth_hw_addr_inherit(slave_dev, master);
1053 slave_dev->priv_flags |= IFF_NO_QUEUE; 1141 slave_dev->priv_flags |= IFF_NO_QUEUE;
1054 slave_dev->netdev_ops = &dsa_slave_netdev_ops; 1142 slave_dev->netdev_ops = &dsa_slave_netdev_ops;