diff options
Diffstat (limited to 'net/dsa')
-rw-r--r-- | net/dsa/slave.c | 88 |
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 | ||
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 | |||
669 | static void dsa_slave_get_ethtool_stats(struct net_device *dev, | 741 | static 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 | ||
896 | static struct ethtool_ops dsa_cpu_port_ethtool_ops; | ||
897 | |||
824 | static const struct net_device_ops dsa_slave_netdev_ops = { | 898 | static 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; |