1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2f2f23566SVivien Didelot /* 3f2f23566SVivien Didelot * Handling of a master device, switching frames via its switch fabric CPU port 4f2f23566SVivien Didelot * 5f2f23566SVivien Didelot * Copyright (c) 2017 Savoir-faire Linux Inc. 6f2f23566SVivien Didelot * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 7f2f23566SVivien Didelot */ 8f2f23566SVivien Didelot 9f2f23566SVivien Didelot #include "dsa_priv.h" 10f2f23566SVivien Didelot 11f2f23566SVivien Didelot static void dsa_master_get_ethtool_stats(struct net_device *dev, 12f2f23566SVivien Didelot struct ethtool_stats *stats, 13f2f23566SVivien Didelot uint64_t *data) 14f2f23566SVivien Didelot { 152f657a60SVivien Didelot struct dsa_port *cpu_dp = dev->dsa_ptr; 167ec764eeSVivien Didelot const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 177ec764eeSVivien Didelot struct dsa_switch *ds = cpu_dp->ds; 187ec764eeSVivien Didelot int port = cpu_dp->index; 19f2f23566SVivien Didelot int count = 0; 20f2f23566SVivien Didelot 211d1e79f1SFlorian Fainelli if (ops->get_sset_count && ops->get_ethtool_stats) { 22f2f23566SVivien Didelot count = ops->get_sset_count(dev, ETH_SS_STATS); 23f2f23566SVivien Didelot ops->get_ethtool_stats(dev, stats, data); 24f2f23566SVivien Didelot } 25f2f23566SVivien Didelot 26f2f23566SVivien Didelot if (ds->ops->get_ethtool_stats) 277ec764eeSVivien Didelot ds->ops->get_ethtool_stats(ds, port, data + count); 28f2f23566SVivien Didelot } 29f2f23566SVivien Didelot 30cf963573SFlorian Fainelli static void dsa_master_get_ethtool_phy_stats(struct net_device *dev, 31cf963573SFlorian Fainelli struct ethtool_stats *stats, 32cf963573SFlorian Fainelli uint64_t *data) 33cf963573SFlorian Fainelli { 34cf963573SFlorian Fainelli struct dsa_port *cpu_dp = dev->dsa_ptr; 35cf963573SFlorian Fainelli const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 36cf963573SFlorian Fainelli struct dsa_switch *ds = cpu_dp->ds; 37cf963573SFlorian Fainelli int port = cpu_dp->index; 38cf963573SFlorian Fainelli int count = 0; 39cf963573SFlorian Fainelli 40cf963573SFlorian Fainelli if (dev->phydev && !ops->get_ethtool_phy_stats) { 41cf963573SFlorian Fainelli count = phy_ethtool_get_sset_count(dev->phydev); 42cf963573SFlorian Fainelli if (count >= 0) 43cf963573SFlorian Fainelli phy_ethtool_get_stats(dev->phydev, stats, data); 44cf963573SFlorian Fainelli } else if (ops->get_sset_count && ops->get_ethtool_phy_stats) { 45cf963573SFlorian Fainelli count = ops->get_sset_count(dev, ETH_SS_PHY_STATS); 46cf963573SFlorian Fainelli ops->get_ethtool_phy_stats(dev, stats, data); 47cf963573SFlorian Fainelli } 48cf963573SFlorian Fainelli 49cf963573SFlorian Fainelli if (count < 0) 50cf963573SFlorian Fainelli count = 0; 51cf963573SFlorian Fainelli 52cf963573SFlorian Fainelli if (ds->ops->get_ethtool_phy_stats) 53cf963573SFlorian Fainelli ds->ops->get_ethtool_phy_stats(ds, port, data + count); 54cf963573SFlorian Fainelli } 55cf963573SFlorian Fainelli 56f2f23566SVivien Didelot static int dsa_master_get_sset_count(struct net_device *dev, int sset) 57f2f23566SVivien Didelot { 582f657a60SVivien Didelot struct dsa_port *cpu_dp = dev->dsa_ptr; 597ec764eeSVivien Didelot const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 607ec764eeSVivien Didelot struct dsa_switch *ds = cpu_dp->ds; 61f2f23566SVivien Didelot int count = 0; 62f2f23566SVivien Didelot 63cf963573SFlorian Fainelli if (sset == ETH_SS_PHY_STATS && dev->phydev && 64cf963573SFlorian Fainelli !ops->get_ethtool_phy_stats) 65cf963573SFlorian Fainelli count = phy_ethtool_get_sset_count(dev->phydev); 66cf963573SFlorian Fainelli else if (ops->get_sset_count) 6789f09048SFlorian Fainelli count = ops->get_sset_count(dev, sset); 68cf963573SFlorian Fainelli 6989f09048SFlorian Fainelli if (count < 0) 7089f09048SFlorian Fainelli count = 0; 71f2f23566SVivien Didelot 7289f09048SFlorian Fainelli if (ds->ops->get_sset_count) 7389f09048SFlorian Fainelli count += ds->ops->get_sset_count(ds, cpu_dp->index, sset); 74f2f23566SVivien Didelot 75f2f23566SVivien Didelot return count; 76f2f23566SVivien Didelot } 77f2f23566SVivien Didelot 78f2f23566SVivien Didelot static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset, 79f2f23566SVivien Didelot uint8_t *data) 80f2f23566SVivien Didelot { 812f657a60SVivien Didelot struct dsa_port *cpu_dp = dev->dsa_ptr; 827ec764eeSVivien Didelot const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 837ec764eeSVivien Didelot struct dsa_switch *ds = cpu_dp->ds; 847ec764eeSVivien Didelot int port = cpu_dp->index; 85f2f23566SVivien Didelot int len = ETH_GSTRING_LEN; 86f2f23566SVivien Didelot int mcount = 0, count; 87f2f23566SVivien Didelot unsigned int i; 88f2f23566SVivien Didelot uint8_t pfx[4]; 89f2f23566SVivien Didelot uint8_t *ndata; 90f2f23566SVivien Didelot 917ec764eeSVivien Didelot snprintf(pfx, sizeof(pfx), "p%.2d", port); 92f2f23566SVivien Didelot /* We do not want to be NULL-terminated, since this is a prefix */ 93f2f23566SVivien Didelot pfx[sizeof(pfx) - 1] = '_'; 94f2f23566SVivien Didelot 95cf963573SFlorian Fainelli if (stringset == ETH_SS_PHY_STATS && dev->phydev && 96cf963573SFlorian Fainelli !ops->get_ethtool_phy_stats) { 97cf963573SFlorian Fainelli mcount = phy_ethtool_get_sset_count(dev->phydev); 98cf963573SFlorian Fainelli if (mcount < 0) 99cf963573SFlorian Fainelli mcount = 0; 100cf963573SFlorian Fainelli else 101cf963573SFlorian Fainelli phy_ethtool_get_strings(dev->phydev, data); 102cf963573SFlorian Fainelli } else if (ops->get_sset_count && ops->get_strings) { 10389f09048SFlorian Fainelli mcount = ops->get_sset_count(dev, stringset); 10489f09048SFlorian Fainelli if (mcount < 0) 10589f09048SFlorian Fainelli mcount = 0; 106f2f23566SVivien Didelot ops->get_strings(dev, stringset, data); 107f2f23566SVivien Didelot } 108f2f23566SVivien Didelot 10989f09048SFlorian Fainelli if (ds->ops->get_strings) { 110f2f23566SVivien Didelot ndata = data + mcount * len; 111f2f23566SVivien Didelot /* This function copies ETH_GSTRINGS_LEN bytes, we will mangle 112f2f23566SVivien Didelot * the output after to prepend our CPU port prefix we 113f2f23566SVivien Didelot * constructed earlier 114f2f23566SVivien Didelot */ 11589f09048SFlorian Fainelli ds->ops->get_strings(ds, port, stringset, ndata); 11689f09048SFlorian Fainelli count = ds->ops->get_sset_count(ds, port, stringset); 117f2f23566SVivien Didelot for (i = 0; i < count; i++) { 118f2f23566SVivien Didelot memmove(ndata + (i * len + sizeof(pfx)), 119f2f23566SVivien Didelot ndata + i * len, len - sizeof(pfx)); 120f2f23566SVivien Didelot memcpy(ndata + i * len, pfx, sizeof(pfx)); 121f2f23566SVivien Didelot } 122f2f23566SVivien Didelot } 123f2f23566SVivien Didelot } 124f2f23566SVivien Didelot 125da7b9e9bSFlorian Fainelli static int dsa_master_get_phys_port_name(struct net_device *dev, 126da7b9e9bSFlorian Fainelli char *name, size_t len) 127da7b9e9bSFlorian Fainelli { 128da7b9e9bSFlorian Fainelli struct dsa_port *cpu_dp = dev->dsa_ptr; 129da7b9e9bSFlorian Fainelli 130da7b9e9bSFlorian Fainelli if (snprintf(name, len, "p%d", cpu_dp->index) >= len) 131da7b9e9bSFlorian Fainelli return -EINVAL; 132da7b9e9bSFlorian Fainelli 133da7b9e9bSFlorian Fainelli return 0; 134da7b9e9bSFlorian Fainelli } 135da7b9e9bSFlorian Fainelli 13617a22fcfSVivien Didelot static int dsa_master_ethtool_setup(struct net_device *dev) 137f2f23566SVivien Didelot { 1382f657a60SVivien Didelot struct dsa_port *cpu_dp = dev->dsa_ptr; 1397ec764eeSVivien Didelot struct dsa_switch *ds = cpu_dp->ds; 140f2f23566SVivien Didelot struct ethtool_ops *ops; 141f2f23566SVivien Didelot 142f2f23566SVivien Didelot ops = devm_kzalloc(ds->dev, sizeof(*ops), GFP_KERNEL); 143f2f23566SVivien Didelot if (!ops) 144f2f23566SVivien Didelot return -ENOMEM; 145f2f23566SVivien Didelot 1467ec764eeSVivien Didelot cpu_dp->orig_ethtool_ops = dev->ethtool_ops; 1477ec764eeSVivien Didelot if (cpu_dp->orig_ethtool_ops) 1487ec764eeSVivien Didelot memcpy(ops, cpu_dp->orig_ethtool_ops, sizeof(*ops)); 149f2f23566SVivien Didelot 150f2f23566SVivien Didelot ops->get_sset_count = dsa_master_get_sset_count; 151f2f23566SVivien Didelot ops->get_ethtool_stats = dsa_master_get_ethtool_stats; 152f2f23566SVivien Didelot ops->get_strings = dsa_master_get_strings; 153cf963573SFlorian Fainelli ops->get_ethtool_phy_stats = dsa_master_get_ethtool_phy_stats; 154f2f23566SVivien Didelot 155f2f23566SVivien Didelot dev->ethtool_ops = ops; 156f2f23566SVivien Didelot 157f2f23566SVivien Didelot return 0; 158f2f23566SVivien Didelot } 159f2f23566SVivien Didelot 16017a22fcfSVivien Didelot static void dsa_master_ethtool_teardown(struct net_device *dev) 161f2f23566SVivien Didelot { 1622f657a60SVivien Didelot struct dsa_port *cpu_dp = dev->dsa_ptr; 163f2f23566SVivien Didelot 1647ec764eeSVivien Didelot dev->ethtool_ops = cpu_dp->orig_ethtool_ops; 1657ec764eeSVivien Didelot cpu_dp->orig_ethtool_ops = NULL; 166f2f23566SVivien Didelot } 16717a22fcfSVivien Didelot 168da7b9e9bSFlorian Fainelli static int dsa_master_ndo_setup(struct net_device *dev) 169da7b9e9bSFlorian Fainelli { 170da7b9e9bSFlorian Fainelli struct dsa_port *cpu_dp = dev->dsa_ptr; 171da7b9e9bSFlorian Fainelli struct dsa_switch *ds = cpu_dp->ds; 172da7b9e9bSFlorian Fainelli struct net_device_ops *ops; 173da7b9e9bSFlorian Fainelli 174da7b9e9bSFlorian Fainelli if (dev->netdev_ops->ndo_get_phys_port_name) 175da7b9e9bSFlorian Fainelli return 0; 176da7b9e9bSFlorian Fainelli 177da7b9e9bSFlorian Fainelli ops = devm_kzalloc(ds->dev, sizeof(*ops), GFP_KERNEL); 178da7b9e9bSFlorian Fainelli if (!ops) 179da7b9e9bSFlorian Fainelli return -ENOMEM; 180da7b9e9bSFlorian Fainelli 181da7b9e9bSFlorian Fainelli cpu_dp->orig_ndo_ops = dev->netdev_ops; 182da7b9e9bSFlorian Fainelli if (cpu_dp->orig_ndo_ops) 183da7b9e9bSFlorian Fainelli memcpy(ops, cpu_dp->orig_ndo_ops, sizeof(*ops)); 184da7b9e9bSFlorian Fainelli 185da7b9e9bSFlorian Fainelli ops->ndo_get_phys_port_name = dsa_master_get_phys_port_name; 186da7b9e9bSFlorian Fainelli 187da7b9e9bSFlorian Fainelli dev->netdev_ops = ops; 188da7b9e9bSFlorian Fainelli 189da7b9e9bSFlorian Fainelli return 0; 190da7b9e9bSFlorian Fainelli } 191da7b9e9bSFlorian Fainelli 192da7b9e9bSFlorian Fainelli static void dsa_master_ndo_teardown(struct net_device *dev) 193da7b9e9bSFlorian Fainelli { 194da7b9e9bSFlorian Fainelli struct dsa_port *cpu_dp = dev->dsa_ptr; 195da7b9e9bSFlorian Fainelli 196da7b9e9bSFlorian Fainelli dev->netdev_ops = cpu_dp->orig_ndo_ops; 197da7b9e9bSFlorian Fainelli cpu_dp->orig_ndo_ops = NULL; 198da7b9e9bSFlorian Fainelli } 199da7b9e9bSFlorian Fainelli 200a3d7e01dSFlorian Fainelli static ssize_t tagging_show(struct device *d, struct device_attribute *attr, 201a3d7e01dSFlorian Fainelli char *buf) 202a3d7e01dSFlorian Fainelli { 203a3d7e01dSFlorian Fainelli struct net_device *dev = to_net_dev(d); 204a3d7e01dSFlorian Fainelli struct dsa_port *cpu_dp = dev->dsa_ptr; 205a3d7e01dSFlorian Fainelli 206a3d7e01dSFlorian Fainelli return sprintf(buf, "%s\n", 207a3d7e01dSFlorian Fainelli dsa_tag_protocol_to_str(cpu_dp->tag_ops)); 208a3d7e01dSFlorian Fainelli } 209a3d7e01dSFlorian Fainelli static DEVICE_ATTR_RO(tagging); 210a3d7e01dSFlorian Fainelli 211a3d7e01dSFlorian Fainelli static struct attribute *dsa_slave_attrs[] = { 212a3d7e01dSFlorian Fainelli &dev_attr_tagging.attr, 213a3d7e01dSFlorian Fainelli NULL 214a3d7e01dSFlorian Fainelli }; 215a3d7e01dSFlorian Fainelli 216a3d7e01dSFlorian Fainelli static const struct attribute_group dsa_group = { 217a3d7e01dSFlorian Fainelli .name = "dsa", 218a3d7e01dSFlorian Fainelli .attrs = dsa_slave_attrs, 219a3d7e01dSFlorian Fainelli }; 220a3d7e01dSFlorian Fainelli 221a60956edSAndrew Lunn static void dsa_master_set_mtu(struct net_device *dev, struct dsa_port *cpu_dp) 222dc0fe7d4SAndrew Lunn { 223dc0fe7d4SAndrew Lunn unsigned int mtu = ETH_DATA_LEN + cpu_dp->tag_ops->overhead; 224dc0fe7d4SAndrew Lunn int err; 225dc0fe7d4SAndrew Lunn 226dc0fe7d4SAndrew Lunn rtnl_lock(); 227dc0fe7d4SAndrew Lunn if (mtu <= dev->max_mtu) { 228dc0fe7d4SAndrew Lunn err = dev_set_mtu(dev, mtu); 229dc0fe7d4SAndrew Lunn if (err) 230dc0fe7d4SAndrew Lunn netdev_dbg(dev, "Unable to set MTU to include for DSA overheads\n"); 231dc0fe7d4SAndrew Lunn } 232dc0fe7d4SAndrew Lunn rtnl_unlock(); 233dc0fe7d4SAndrew Lunn } 234dc0fe7d4SAndrew Lunn 23591ba4795SAndrew Lunn static void dsa_master_reset_mtu(struct net_device *dev) 23691ba4795SAndrew Lunn { 23791ba4795SAndrew Lunn int err; 23891ba4795SAndrew Lunn 23991ba4795SAndrew Lunn rtnl_lock(); 24091ba4795SAndrew Lunn err = dev_set_mtu(dev, ETH_DATA_LEN); 24191ba4795SAndrew Lunn if (err) 24291ba4795SAndrew Lunn netdev_dbg(dev, 24391ba4795SAndrew Lunn "Unable to reset MTU to exclude DSA overheads\n"); 24491ba4795SAndrew Lunn rtnl_unlock(); 24591ba4795SAndrew Lunn } 24691ba4795SAndrew Lunn 247c8101f77SMarc Zyngier static struct lock_class_key dsa_master_addr_list_lock_key; 248c8101f77SMarc Zyngier 24917a22fcfSVivien Didelot int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp) 25017a22fcfSVivien Didelot { 251a3d7e01dSFlorian Fainelli int ret; 252a3d7e01dSFlorian Fainelli 253dc0fe7d4SAndrew Lunn dsa_master_set_mtu(dev, cpu_dp); 254dc0fe7d4SAndrew Lunn 25517a22fcfSVivien Didelot /* If we use a tagging format that doesn't have an ethertype 25617a22fcfSVivien Didelot * field, make sure that all packets from this point on get 25717a22fcfSVivien Didelot * sent to the tag format's receive function. 25817a22fcfSVivien Didelot */ 25917a22fcfSVivien Didelot wmb(); 26017a22fcfSVivien Didelot 26117a22fcfSVivien Didelot dev->dsa_ptr = cpu_dp; 262c8101f77SMarc Zyngier lockdep_set_class(&dev->addr_list_lock, 263c8101f77SMarc Zyngier &dsa_master_addr_list_lock_key); 26417a22fcfSVivien Didelot 265a3d7e01dSFlorian Fainelli ret = dsa_master_ethtool_setup(dev); 266a3d7e01dSFlorian Fainelli if (ret) 267a3d7e01dSFlorian Fainelli return ret; 268a3d7e01dSFlorian Fainelli 269da7b9e9bSFlorian Fainelli ret = dsa_master_ndo_setup(dev); 270da7b9e9bSFlorian Fainelli if (ret) 271da7b9e9bSFlorian Fainelli goto out_err_ethtool_teardown; 272da7b9e9bSFlorian Fainelli 273a3d7e01dSFlorian Fainelli ret = sysfs_create_group(&dev->dev.kobj, &dsa_group); 274a3d7e01dSFlorian Fainelli if (ret) 275da7b9e9bSFlorian Fainelli goto out_err_ndo_teardown; 276a3d7e01dSFlorian Fainelli 277a3d7e01dSFlorian Fainelli return ret; 278da7b9e9bSFlorian Fainelli 279da7b9e9bSFlorian Fainelli out_err_ndo_teardown: 280da7b9e9bSFlorian Fainelli dsa_master_ndo_teardown(dev); 281da7b9e9bSFlorian Fainelli out_err_ethtool_teardown: 282da7b9e9bSFlorian Fainelli dsa_master_ethtool_teardown(dev); 283da7b9e9bSFlorian Fainelli return ret; 28417a22fcfSVivien Didelot } 28517a22fcfSVivien Didelot 28617a22fcfSVivien Didelot void dsa_master_teardown(struct net_device *dev) 28717a22fcfSVivien Didelot { 288a3d7e01dSFlorian Fainelli sysfs_remove_group(&dev->dev.kobj, &dsa_group); 289da7b9e9bSFlorian Fainelli dsa_master_ndo_teardown(dev); 29017a22fcfSVivien Didelot dsa_master_ethtool_teardown(dev); 29191ba4795SAndrew Lunn dsa_master_reset_mtu(dev); 29217a22fcfSVivien Didelot 29317a22fcfSVivien Didelot dev->dsa_ptr = NULL; 29417a22fcfSVivien Didelot 29517a22fcfSVivien Didelot /* If we used a tagging format that doesn't have an ethertype 29617a22fcfSVivien Didelot * field, make sure that all packets from this point get sent 29717a22fcfSVivien Didelot * without the tag and go through the regular receive path. 29817a22fcfSVivien Didelot */ 29917a22fcfSVivien Didelot wmb(); 30017a22fcfSVivien Didelot } 301