162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Handling of a master device, switching frames via its switch fabric CPU port 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2017 Savoir-faire Linux Inc. 662306a36Sopenharmony_ci * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/ethtool.h> 1062306a36Sopenharmony_ci#include <linux/netdevice.h> 1162306a36Sopenharmony_ci#include <linux/netlink.h> 1262306a36Sopenharmony_ci#include <net/dsa.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "dsa.h" 1562306a36Sopenharmony_ci#include "master.h" 1662306a36Sopenharmony_ci#include "port.h" 1762306a36Sopenharmony_ci#include "tag.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic int dsa_master_get_regs_len(struct net_device *dev) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 2262306a36Sopenharmony_ci const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 2362306a36Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 2462306a36Sopenharmony_ci int port = cpu_dp->index; 2562306a36Sopenharmony_ci int ret = 0; 2662306a36Sopenharmony_ci int len; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (ops->get_regs_len) { 2962306a36Sopenharmony_ci len = ops->get_regs_len(dev); 3062306a36Sopenharmony_ci if (len < 0) 3162306a36Sopenharmony_ci return len; 3262306a36Sopenharmony_ci ret += len; 3362306a36Sopenharmony_ci } 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci ret += sizeof(struct ethtool_drvinfo); 3662306a36Sopenharmony_ci ret += sizeof(struct ethtool_regs); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (ds->ops->get_regs_len) { 3962306a36Sopenharmony_ci len = ds->ops->get_regs_len(ds, port); 4062306a36Sopenharmony_ci if (len < 0) 4162306a36Sopenharmony_ci return len; 4262306a36Sopenharmony_ci ret += len; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci return ret; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void dsa_master_get_regs(struct net_device *dev, 4962306a36Sopenharmony_ci struct ethtool_regs *regs, void *data) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 5262306a36Sopenharmony_ci const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 5362306a36Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 5462306a36Sopenharmony_ci struct ethtool_drvinfo *cpu_info; 5562306a36Sopenharmony_ci struct ethtool_regs *cpu_regs; 5662306a36Sopenharmony_ci int port = cpu_dp->index; 5762306a36Sopenharmony_ci int len; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (ops->get_regs_len && ops->get_regs) { 6062306a36Sopenharmony_ci len = ops->get_regs_len(dev); 6162306a36Sopenharmony_ci if (len < 0) 6262306a36Sopenharmony_ci return; 6362306a36Sopenharmony_ci regs->len = len; 6462306a36Sopenharmony_ci ops->get_regs(dev, regs, data); 6562306a36Sopenharmony_ci data += regs->len; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci cpu_info = (struct ethtool_drvinfo *)data; 6962306a36Sopenharmony_ci strscpy(cpu_info->driver, "dsa", sizeof(cpu_info->driver)); 7062306a36Sopenharmony_ci data += sizeof(*cpu_info); 7162306a36Sopenharmony_ci cpu_regs = (struct ethtool_regs *)data; 7262306a36Sopenharmony_ci data += sizeof(*cpu_regs); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (ds->ops->get_regs_len && ds->ops->get_regs) { 7562306a36Sopenharmony_ci len = ds->ops->get_regs_len(ds, port); 7662306a36Sopenharmony_ci if (len < 0) 7762306a36Sopenharmony_ci return; 7862306a36Sopenharmony_ci cpu_regs->len = len; 7962306a36Sopenharmony_ci ds->ops->get_regs(ds, port, cpu_regs, data); 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void dsa_master_get_ethtool_stats(struct net_device *dev, 8462306a36Sopenharmony_ci struct ethtool_stats *stats, 8562306a36Sopenharmony_ci uint64_t *data) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 8862306a36Sopenharmony_ci const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 8962306a36Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 9062306a36Sopenharmony_ci int port = cpu_dp->index; 9162306a36Sopenharmony_ci int count = 0; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (ops->get_sset_count && ops->get_ethtool_stats) { 9462306a36Sopenharmony_ci count = ops->get_sset_count(dev, ETH_SS_STATS); 9562306a36Sopenharmony_ci ops->get_ethtool_stats(dev, stats, data); 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (ds->ops->get_ethtool_stats) 9962306a36Sopenharmony_ci ds->ops->get_ethtool_stats(ds, port, data + count); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void dsa_master_get_ethtool_phy_stats(struct net_device *dev, 10362306a36Sopenharmony_ci struct ethtool_stats *stats, 10462306a36Sopenharmony_ci uint64_t *data) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 10762306a36Sopenharmony_ci const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 10862306a36Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 10962306a36Sopenharmony_ci int port = cpu_dp->index; 11062306a36Sopenharmony_ci int count = 0; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (dev->phydev && !ops->get_ethtool_phy_stats) { 11362306a36Sopenharmony_ci count = phy_ethtool_get_sset_count(dev->phydev); 11462306a36Sopenharmony_ci if (count >= 0) 11562306a36Sopenharmony_ci phy_ethtool_get_stats(dev->phydev, stats, data); 11662306a36Sopenharmony_ci } else if (ops->get_sset_count && ops->get_ethtool_phy_stats) { 11762306a36Sopenharmony_ci count = ops->get_sset_count(dev, ETH_SS_PHY_STATS); 11862306a36Sopenharmony_ci ops->get_ethtool_phy_stats(dev, stats, data); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (count < 0) 12262306a36Sopenharmony_ci count = 0; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (ds->ops->get_ethtool_phy_stats) 12562306a36Sopenharmony_ci ds->ops->get_ethtool_phy_stats(ds, port, data + count); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int dsa_master_get_sset_count(struct net_device *dev, int sset) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 13162306a36Sopenharmony_ci const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 13262306a36Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 13362306a36Sopenharmony_ci int count = 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (sset == ETH_SS_PHY_STATS && dev->phydev && 13662306a36Sopenharmony_ci !ops->get_ethtool_phy_stats) 13762306a36Sopenharmony_ci count = phy_ethtool_get_sset_count(dev->phydev); 13862306a36Sopenharmony_ci else if (ops->get_sset_count) 13962306a36Sopenharmony_ci count = ops->get_sset_count(dev, sset); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (count < 0) 14262306a36Sopenharmony_ci count = 0; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (ds->ops->get_sset_count) 14562306a36Sopenharmony_ci count += ds->ops->get_sset_count(ds, cpu_dp->index, sset); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return count; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic void dsa_master_get_strings(struct net_device *dev, uint32_t stringset, 15162306a36Sopenharmony_ci uint8_t *data) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 15462306a36Sopenharmony_ci const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 15562306a36Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 15662306a36Sopenharmony_ci int port = cpu_dp->index; 15762306a36Sopenharmony_ci int len = ETH_GSTRING_LEN; 15862306a36Sopenharmony_ci int mcount = 0, count, i; 15962306a36Sopenharmony_ci uint8_t pfx[4]; 16062306a36Sopenharmony_ci uint8_t *ndata; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci snprintf(pfx, sizeof(pfx), "p%.2d", port); 16362306a36Sopenharmony_ci /* We do not want to be NULL-terminated, since this is a prefix */ 16462306a36Sopenharmony_ci pfx[sizeof(pfx) - 1] = '_'; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (stringset == ETH_SS_PHY_STATS && dev->phydev && 16762306a36Sopenharmony_ci !ops->get_ethtool_phy_stats) { 16862306a36Sopenharmony_ci mcount = phy_ethtool_get_sset_count(dev->phydev); 16962306a36Sopenharmony_ci if (mcount < 0) 17062306a36Sopenharmony_ci mcount = 0; 17162306a36Sopenharmony_ci else 17262306a36Sopenharmony_ci phy_ethtool_get_strings(dev->phydev, data); 17362306a36Sopenharmony_ci } else if (ops->get_sset_count && ops->get_strings) { 17462306a36Sopenharmony_ci mcount = ops->get_sset_count(dev, stringset); 17562306a36Sopenharmony_ci if (mcount < 0) 17662306a36Sopenharmony_ci mcount = 0; 17762306a36Sopenharmony_ci ops->get_strings(dev, stringset, data); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (ds->ops->get_strings) { 18162306a36Sopenharmony_ci ndata = data + mcount * len; 18262306a36Sopenharmony_ci /* This function copies ETH_GSTRINGS_LEN bytes, we will mangle 18362306a36Sopenharmony_ci * the output after to prepend our CPU port prefix we 18462306a36Sopenharmony_ci * constructed earlier 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci ds->ops->get_strings(ds, port, stringset, ndata); 18762306a36Sopenharmony_ci count = ds->ops->get_sset_count(ds, port, stringset); 18862306a36Sopenharmony_ci if (count < 0) 18962306a36Sopenharmony_ci return; 19062306a36Sopenharmony_ci for (i = 0; i < count; i++) { 19162306a36Sopenharmony_ci memmove(ndata + (i * len + sizeof(pfx)), 19262306a36Sopenharmony_ci ndata + i * len, len - sizeof(pfx)); 19362306a36Sopenharmony_ci memcpy(ndata + i * len, pfx, sizeof(pfx)); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/* Deny PTP operations on master if there is at least one switch in the tree 19962306a36Sopenharmony_ci * that is PTP capable. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ciint __dsa_master_hwtstamp_validate(struct net_device *dev, 20262306a36Sopenharmony_ci const struct kernel_hwtstamp_config *config, 20362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 20662306a36Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 20762306a36Sopenharmony_ci struct dsa_switch_tree *dst; 20862306a36Sopenharmony_ci struct dsa_port *dp; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci dst = ds->dst; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci list_for_each_entry(dp, &dst->ports, list) { 21362306a36Sopenharmony_ci if (dsa_port_supports_hwtstamp(dp)) { 21462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 21562306a36Sopenharmony_ci "HW timestamping not allowed on DSA master when switch supports the operation"); 21662306a36Sopenharmony_ci return -EBUSY; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int dsa_master_ethtool_setup(struct net_device *dev) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 22662306a36Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 22762306a36Sopenharmony_ci struct ethtool_ops *ops; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (netif_is_lag_master(dev)) 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci ops = devm_kzalloc(ds->dev, sizeof(*ops), GFP_KERNEL); 23362306a36Sopenharmony_ci if (!ops) 23462306a36Sopenharmony_ci return -ENOMEM; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci cpu_dp->orig_ethtool_ops = dev->ethtool_ops; 23762306a36Sopenharmony_ci if (cpu_dp->orig_ethtool_ops) 23862306a36Sopenharmony_ci memcpy(ops, cpu_dp->orig_ethtool_ops, sizeof(*ops)); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci ops->get_regs_len = dsa_master_get_regs_len; 24162306a36Sopenharmony_ci ops->get_regs = dsa_master_get_regs; 24262306a36Sopenharmony_ci ops->get_sset_count = dsa_master_get_sset_count; 24362306a36Sopenharmony_ci ops->get_ethtool_stats = dsa_master_get_ethtool_stats; 24462306a36Sopenharmony_ci ops->get_strings = dsa_master_get_strings; 24562306a36Sopenharmony_ci ops->get_ethtool_phy_stats = dsa_master_get_ethtool_phy_stats; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci dev->ethtool_ops = ops; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void dsa_master_ethtool_teardown(struct net_device *dev) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (netif_is_lag_master(dev)) 25762306a36Sopenharmony_ci return; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci dev->ethtool_ops = cpu_dp->orig_ethtool_ops; 26062306a36Sopenharmony_ci cpu_dp->orig_ethtool_ops = NULL; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci/* Keep the master always promiscuous if the tagging protocol requires that 26462306a36Sopenharmony_ci * (garbles MAC DA) or if it doesn't support unicast filtering, case in which 26562306a36Sopenharmony_ci * it would revert to promiscuous mode as soon as we call dev_uc_add() on it 26662306a36Sopenharmony_ci * anyway. 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_cistatic void dsa_master_set_promiscuity(struct net_device *dev, int inc) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci const struct dsa_device_ops *ops = dev->dsa_ptr->tag_ops; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if ((dev->priv_flags & IFF_UNICAST_FLT) && !ops->promisc_on_master) 27362306a36Sopenharmony_ci return; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci ASSERT_RTNL(); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci dev_set_promiscuity(dev, inc); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic ssize_t tagging_show(struct device *d, struct device_attribute *attr, 28162306a36Sopenharmony_ci char *buf) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct net_device *dev = to_net_dev(d); 28462306a36Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", 28762306a36Sopenharmony_ci dsa_tag_protocol_to_str(cpu_dp->tag_ops)); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic ssize_t tagging_store(struct device *d, struct device_attribute *attr, 29162306a36Sopenharmony_ci const char *buf, size_t count) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci const struct dsa_device_ops *new_tag_ops, *old_tag_ops; 29462306a36Sopenharmony_ci const char *end = strchrnul(buf, '\n'), *name; 29562306a36Sopenharmony_ci struct net_device *dev = to_net_dev(d); 29662306a36Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 29762306a36Sopenharmony_ci size_t len = end - buf; 29862306a36Sopenharmony_ci int err; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* Empty string passed */ 30162306a36Sopenharmony_ci if (!len) 30262306a36Sopenharmony_ci return -ENOPROTOOPT; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci name = kstrndup(buf, len, GFP_KERNEL); 30562306a36Sopenharmony_ci if (!name) 30662306a36Sopenharmony_ci return -ENOMEM; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci old_tag_ops = cpu_dp->tag_ops; 30962306a36Sopenharmony_ci new_tag_ops = dsa_tag_driver_get_by_name(name); 31062306a36Sopenharmony_ci kfree(name); 31162306a36Sopenharmony_ci /* Bad tagger name? */ 31262306a36Sopenharmony_ci if (IS_ERR(new_tag_ops)) 31362306a36Sopenharmony_ci return PTR_ERR(new_tag_ops); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (new_tag_ops == old_tag_ops) 31662306a36Sopenharmony_ci /* Drop the temporarily held duplicate reference, since 31762306a36Sopenharmony_ci * the DSA switch tree uses this tagger. 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci goto out; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci err = dsa_tree_change_tag_proto(cpu_dp->ds->dst, new_tag_ops, 32262306a36Sopenharmony_ci old_tag_ops); 32362306a36Sopenharmony_ci if (err) { 32462306a36Sopenharmony_ci /* On failure the old tagger is restored, so we don't need the 32562306a36Sopenharmony_ci * driver for the new one. 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_ci dsa_tag_driver_put(new_tag_ops); 32862306a36Sopenharmony_ci return err; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* On success we no longer need the module for the old tagging protocol 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_ciout: 33462306a36Sopenharmony_ci dsa_tag_driver_put(old_tag_ops); 33562306a36Sopenharmony_ci return count; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_cistatic DEVICE_ATTR_RW(tagging); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic struct attribute *dsa_slave_attrs[] = { 34062306a36Sopenharmony_ci &dev_attr_tagging.attr, 34162306a36Sopenharmony_ci NULL 34262306a36Sopenharmony_ci}; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic const struct attribute_group dsa_group = { 34562306a36Sopenharmony_ci .name = "dsa", 34662306a36Sopenharmony_ci .attrs = dsa_slave_attrs, 34762306a36Sopenharmony_ci}; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void dsa_master_reset_mtu(struct net_device *dev) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci int err; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci err = dev_set_mtu(dev, ETH_DATA_LEN); 35462306a36Sopenharmony_ci if (err) 35562306a36Sopenharmony_ci netdev_dbg(dev, 35662306a36Sopenharmony_ci "Unable to reset MTU to exclude DSA overheads\n"); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ciint dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci const struct dsa_device_ops *tag_ops = cpu_dp->tag_ops; 36262306a36Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 36362306a36Sopenharmony_ci struct device_link *consumer_link; 36462306a36Sopenharmony_ci int mtu, ret; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci mtu = ETH_DATA_LEN + dsa_tag_protocol_overhead(tag_ops); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* The DSA master must use SET_NETDEV_DEV for this to work. */ 36962306a36Sopenharmony_ci if (!netif_is_lag_master(dev)) { 37062306a36Sopenharmony_ci consumer_link = device_link_add(ds->dev, dev->dev.parent, 37162306a36Sopenharmony_ci DL_FLAG_AUTOREMOVE_CONSUMER); 37262306a36Sopenharmony_ci if (!consumer_link) 37362306a36Sopenharmony_ci netdev_err(dev, 37462306a36Sopenharmony_ci "Failed to create a device link to DSA switch %s\n", 37562306a36Sopenharmony_ci dev_name(ds->dev)); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* The switch driver may not implement ->port_change_mtu(), case in 37962306a36Sopenharmony_ci * which dsa_slave_change_mtu() will not update the master MTU either, 38062306a36Sopenharmony_ci * so we need to do that here. 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_ci ret = dev_set_mtu(dev, mtu); 38362306a36Sopenharmony_ci if (ret) 38462306a36Sopenharmony_ci netdev_warn(dev, "error %d setting MTU to %d to include DSA overhead\n", 38562306a36Sopenharmony_ci ret, mtu); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* If we use a tagging format that doesn't have an ethertype 38862306a36Sopenharmony_ci * field, make sure that all packets from this point on get 38962306a36Sopenharmony_ci * sent to the tag format's receive function. 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_ci wmb(); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci dev->dsa_ptr = cpu_dp; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci dsa_master_set_promiscuity(dev, 1); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci ret = dsa_master_ethtool_setup(dev); 39862306a36Sopenharmony_ci if (ret) 39962306a36Sopenharmony_ci goto out_err_reset_promisc; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci ret = sysfs_create_group(&dev->dev.kobj, &dsa_group); 40262306a36Sopenharmony_ci if (ret) 40362306a36Sopenharmony_ci goto out_err_ethtool_teardown; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci return ret; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ciout_err_ethtool_teardown: 40862306a36Sopenharmony_ci dsa_master_ethtool_teardown(dev); 40962306a36Sopenharmony_ciout_err_reset_promisc: 41062306a36Sopenharmony_ci dsa_master_set_promiscuity(dev, -1); 41162306a36Sopenharmony_ci return ret; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_civoid dsa_master_teardown(struct net_device *dev) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci sysfs_remove_group(&dev->dev.kobj, &dsa_group); 41762306a36Sopenharmony_ci dsa_master_ethtool_teardown(dev); 41862306a36Sopenharmony_ci dsa_master_reset_mtu(dev); 41962306a36Sopenharmony_ci dsa_master_set_promiscuity(dev, -1); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci dev->dsa_ptr = NULL; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* If we used a tagging format that doesn't have an ethertype 42462306a36Sopenharmony_ci * field, make sure that all packets from this point get sent 42562306a36Sopenharmony_ci * without the tag and go through the regular receive path. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_ci wmb(); 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ciint dsa_master_lag_setup(struct net_device *lag_dev, struct dsa_port *cpu_dp, 43162306a36Sopenharmony_ci struct netdev_lag_upper_info *uinfo, 43262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci bool master_setup = false; 43562306a36Sopenharmony_ci int err; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (!netdev_uses_dsa(lag_dev)) { 43862306a36Sopenharmony_ci err = dsa_master_setup(lag_dev, cpu_dp); 43962306a36Sopenharmony_ci if (err) 44062306a36Sopenharmony_ci return err; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci master_setup = true; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci err = dsa_port_lag_join(cpu_dp, lag_dev, uinfo, extack); 44662306a36Sopenharmony_ci if (err) { 44762306a36Sopenharmony_ci NL_SET_ERR_MSG_WEAK_MOD(extack, "CPU port failed to join LAG"); 44862306a36Sopenharmony_ci goto out_master_teardown; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ciout_master_teardown: 45462306a36Sopenharmony_ci if (master_setup) 45562306a36Sopenharmony_ci dsa_master_teardown(lag_dev); 45662306a36Sopenharmony_ci return err; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci/* Tear down a master if there isn't any other user port on it, 46062306a36Sopenharmony_ci * optionally also destroying LAG information. 46162306a36Sopenharmony_ci */ 46262306a36Sopenharmony_civoid dsa_master_lag_teardown(struct net_device *lag_dev, 46362306a36Sopenharmony_ci struct dsa_port *cpu_dp) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct net_device *upper; 46662306a36Sopenharmony_ci struct list_head *iter; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci dsa_port_lag_leave(cpu_dp, lag_dev); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci netdev_for_each_upper_dev_rcu(lag_dev, upper, iter) 47162306a36Sopenharmony_ci if (dsa_slave_dev_check(upper)) 47262306a36Sopenharmony_ci return; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci dsa_master_teardown(lag_dev); 47562306a36Sopenharmony_ci} 476