18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Handling of a master device, switching frames via its switch fabric CPU port 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2017 Savoir-faire Linux Inc. 68c2ecf20Sopenharmony_ci * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "dsa_priv.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistatic int dsa_master_get_regs_len(struct net_device *dev) 128c2ecf20Sopenharmony_ci{ 138c2ecf20Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 148c2ecf20Sopenharmony_ci const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 158c2ecf20Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 168c2ecf20Sopenharmony_ci int port = cpu_dp->index; 178c2ecf20Sopenharmony_ci int ret = 0; 188c2ecf20Sopenharmony_ci int len; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci if (ops->get_regs_len) { 218c2ecf20Sopenharmony_ci len = ops->get_regs_len(dev); 228c2ecf20Sopenharmony_ci if (len < 0) 238c2ecf20Sopenharmony_ci return len; 248c2ecf20Sopenharmony_ci ret += len; 258c2ecf20Sopenharmony_ci } 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci ret += sizeof(struct ethtool_drvinfo); 288c2ecf20Sopenharmony_ci ret += sizeof(struct ethtool_regs); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci if (ds->ops->get_regs_len) { 318c2ecf20Sopenharmony_ci len = ds->ops->get_regs_len(ds, port); 328c2ecf20Sopenharmony_ci if (len < 0) 338c2ecf20Sopenharmony_ci return len; 348c2ecf20Sopenharmony_ci ret += len; 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return ret; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic void dsa_master_get_regs(struct net_device *dev, 418c2ecf20Sopenharmony_ci struct ethtool_regs *regs, void *data) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 448c2ecf20Sopenharmony_ci const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 458c2ecf20Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 468c2ecf20Sopenharmony_ci struct ethtool_drvinfo *cpu_info; 478c2ecf20Sopenharmony_ci struct ethtool_regs *cpu_regs; 488c2ecf20Sopenharmony_ci int port = cpu_dp->index; 498c2ecf20Sopenharmony_ci int len; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (ops->get_regs_len && ops->get_regs) { 528c2ecf20Sopenharmony_ci len = ops->get_regs_len(dev); 538c2ecf20Sopenharmony_ci if (len < 0) 548c2ecf20Sopenharmony_ci return; 558c2ecf20Sopenharmony_ci regs->len = len; 568c2ecf20Sopenharmony_ci ops->get_regs(dev, regs, data); 578c2ecf20Sopenharmony_ci data += regs->len; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci cpu_info = (struct ethtool_drvinfo *)data; 618c2ecf20Sopenharmony_ci strlcpy(cpu_info->driver, "dsa", sizeof(cpu_info->driver)); 628c2ecf20Sopenharmony_ci data += sizeof(*cpu_info); 638c2ecf20Sopenharmony_ci cpu_regs = (struct ethtool_regs *)data; 648c2ecf20Sopenharmony_ci data += sizeof(*cpu_regs); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (ds->ops->get_regs_len && ds->ops->get_regs) { 678c2ecf20Sopenharmony_ci len = ds->ops->get_regs_len(ds, port); 688c2ecf20Sopenharmony_ci if (len < 0) 698c2ecf20Sopenharmony_ci return; 708c2ecf20Sopenharmony_ci cpu_regs->len = len; 718c2ecf20Sopenharmony_ci ds->ops->get_regs(ds, port, cpu_regs, data); 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic void dsa_master_get_ethtool_stats(struct net_device *dev, 768c2ecf20Sopenharmony_ci struct ethtool_stats *stats, 778c2ecf20Sopenharmony_ci uint64_t *data) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 808c2ecf20Sopenharmony_ci const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 818c2ecf20Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 828c2ecf20Sopenharmony_ci int port = cpu_dp->index; 838c2ecf20Sopenharmony_ci int count = 0; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (ops->get_sset_count && ops->get_ethtool_stats) { 868c2ecf20Sopenharmony_ci count = ops->get_sset_count(dev, ETH_SS_STATS); 878c2ecf20Sopenharmony_ci ops->get_ethtool_stats(dev, stats, data); 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (ds->ops->get_ethtool_stats) 918c2ecf20Sopenharmony_ci ds->ops->get_ethtool_stats(ds, port, data + count); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void dsa_master_get_ethtool_phy_stats(struct net_device *dev, 958c2ecf20Sopenharmony_ci struct ethtool_stats *stats, 968c2ecf20Sopenharmony_ci uint64_t *data) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 998c2ecf20Sopenharmony_ci const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 1008c2ecf20Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 1018c2ecf20Sopenharmony_ci int port = cpu_dp->index; 1028c2ecf20Sopenharmony_ci int count = 0; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (dev->phydev && !ops->get_ethtool_phy_stats) { 1058c2ecf20Sopenharmony_ci count = phy_ethtool_get_sset_count(dev->phydev); 1068c2ecf20Sopenharmony_ci if (count >= 0) 1078c2ecf20Sopenharmony_ci phy_ethtool_get_stats(dev->phydev, stats, data); 1088c2ecf20Sopenharmony_ci } else if (ops->get_sset_count && ops->get_ethtool_phy_stats) { 1098c2ecf20Sopenharmony_ci count = ops->get_sset_count(dev, ETH_SS_PHY_STATS); 1108c2ecf20Sopenharmony_ci ops->get_ethtool_phy_stats(dev, stats, data); 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (count < 0) 1148c2ecf20Sopenharmony_ci count = 0; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (ds->ops->get_ethtool_phy_stats) 1178c2ecf20Sopenharmony_ci ds->ops->get_ethtool_phy_stats(ds, port, data + count); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int dsa_master_get_sset_count(struct net_device *dev, int sset) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 1238c2ecf20Sopenharmony_ci const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 1248c2ecf20Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 1258c2ecf20Sopenharmony_ci int count = 0; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (sset == ETH_SS_PHY_STATS && dev->phydev && 1288c2ecf20Sopenharmony_ci !ops->get_ethtool_phy_stats) 1298c2ecf20Sopenharmony_ci count = phy_ethtool_get_sset_count(dev->phydev); 1308c2ecf20Sopenharmony_ci else if (ops->get_sset_count) 1318c2ecf20Sopenharmony_ci count = ops->get_sset_count(dev, sset); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (count < 0) 1348c2ecf20Sopenharmony_ci count = 0; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (ds->ops->get_sset_count) 1378c2ecf20Sopenharmony_ci count += ds->ops->get_sset_count(ds, cpu_dp->index, sset); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return count; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void dsa_master_get_strings(struct net_device *dev, uint32_t stringset, 1438c2ecf20Sopenharmony_ci uint8_t *data) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 1468c2ecf20Sopenharmony_ci const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 1478c2ecf20Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 1488c2ecf20Sopenharmony_ci int port = cpu_dp->index; 1498c2ecf20Sopenharmony_ci int len = ETH_GSTRING_LEN; 1508c2ecf20Sopenharmony_ci int mcount = 0, count, i; 1518c2ecf20Sopenharmony_ci uint8_t pfx[4]; 1528c2ecf20Sopenharmony_ci uint8_t *ndata; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci snprintf(pfx, sizeof(pfx), "p%.2d", port); 1558c2ecf20Sopenharmony_ci /* We do not want to be NULL-terminated, since this is a prefix */ 1568c2ecf20Sopenharmony_ci pfx[sizeof(pfx) - 1] = '_'; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (stringset == ETH_SS_PHY_STATS && dev->phydev && 1598c2ecf20Sopenharmony_ci !ops->get_ethtool_phy_stats) { 1608c2ecf20Sopenharmony_ci mcount = phy_ethtool_get_sset_count(dev->phydev); 1618c2ecf20Sopenharmony_ci if (mcount < 0) 1628c2ecf20Sopenharmony_ci mcount = 0; 1638c2ecf20Sopenharmony_ci else 1648c2ecf20Sopenharmony_ci phy_ethtool_get_strings(dev->phydev, data); 1658c2ecf20Sopenharmony_ci } else if (ops->get_sset_count && ops->get_strings) { 1668c2ecf20Sopenharmony_ci mcount = ops->get_sset_count(dev, stringset); 1678c2ecf20Sopenharmony_ci if (mcount < 0) 1688c2ecf20Sopenharmony_ci mcount = 0; 1698c2ecf20Sopenharmony_ci ops->get_strings(dev, stringset, data); 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (ds->ops->get_strings) { 1738c2ecf20Sopenharmony_ci ndata = data + mcount * len; 1748c2ecf20Sopenharmony_ci /* This function copies ETH_GSTRINGS_LEN bytes, we will mangle 1758c2ecf20Sopenharmony_ci * the output after to prepend our CPU port prefix we 1768c2ecf20Sopenharmony_ci * constructed earlier 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_ci ds->ops->get_strings(ds, port, stringset, ndata); 1798c2ecf20Sopenharmony_ci count = ds->ops->get_sset_count(ds, port, stringset); 1808c2ecf20Sopenharmony_ci if (count < 0) 1818c2ecf20Sopenharmony_ci return; 1828c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 1838c2ecf20Sopenharmony_ci memmove(ndata + (i * len + sizeof(pfx)), 1848c2ecf20Sopenharmony_ci ndata + i * len, len - sizeof(pfx)); 1858c2ecf20Sopenharmony_ci memcpy(ndata + i * len, pfx, sizeof(pfx)); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int dsa_master_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 1938c2ecf20Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 1948c2ecf20Sopenharmony_ci struct dsa_switch_tree *dst; 1958c2ecf20Sopenharmony_ci int err = -EOPNOTSUPP; 1968c2ecf20Sopenharmony_ci struct dsa_port *dp; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci dst = ds->dst; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci switch (cmd) { 2018c2ecf20Sopenharmony_ci case SIOCGHWTSTAMP: 2028c2ecf20Sopenharmony_ci case SIOCSHWTSTAMP: 2038c2ecf20Sopenharmony_ci /* Deny PTP operations on master if there is at least one 2048c2ecf20Sopenharmony_ci * switch in the tree that is PTP capable. 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci list_for_each_entry(dp, &dst->ports, list) 2078c2ecf20Sopenharmony_ci if (dp->ds->ops->port_hwtstamp_get || 2088c2ecf20Sopenharmony_ci dp->ds->ops->port_hwtstamp_set) 2098c2ecf20Sopenharmony_ci return -EBUSY; 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (dev->netdev_ops->ndo_do_ioctl) 2148c2ecf20Sopenharmony_ci err = dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return err; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic const struct dsa_netdevice_ops dsa_netdev_ops = { 2208c2ecf20Sopenharmony_ci .ndo_do_ioctl = dsa_master_ioctl, 2218c2ecf20Sopenharmony_ci}; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic int dsa_master_ethtool_setup(struct net_device *dev) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 2268c2ecf20Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 2278c2ecf20Sopenharmony_ci struct ethtool_ops *ops; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci ops = devm_kzalloc(ds->dev, sizeof(*ops), GFP_KERNEL); 2308c2ecf20Sopenharmony_ci if (!ops) 2318c2ecf20Sopenharmony_ci return -ENOMEM; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci cpu_dp->orig_ethtool_ops = dev->ethtool_ops; 2348c2ecf20Sopenharmony_ci if (cpu_dp->orig_ethtool_ops) 2358c2ecf20Sopenharmony_ci memcpy(ops, cpu_dp->orig_ethtool_ops, sizeof(*ops)); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci ops->get_regs_len = dsa_master_get_regs_len; 2388c2ecf20Sopenharmony_ci ops->get_regs = dsa_master_get_regs; 2398c2ecf20Sopenharmony_ci ops->get_sset_count = dsa_master_get_sset_count; 2408c2ecf20Sopenharmony_ci ops->get_ethtool_stats = dsa_master_get_ethtool_stats; 2418c2ecf20Sopenharmony_ci ops->get_strings = dsa_master_get_strings; 2428c2ecf20Sopenharmony_ci ops->get_ethtool_phy_stats = dsa_master_get_ethtool_phy_stats; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci dev->ethtool_ops = ops; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic void dsa_master_ethtool_teardown(struct net_device *dev) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci dev->ethtool_ops = cpu_dp->orig_ethtool_ops; 2548c2ecf20Sopenharmony_ci cpu_dp->orig_ethtool_ops = NULL; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic void dsa_netdev_ops_set(struct net_device *dev, 2588c2ecf20Sopenharmony_ci const struct dsa_netdevice_ops *ops) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci dev->dsa_ptr->netdev_ops = ops; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic void dsa_master_set_promiscuity(struct net_device *dev, int inc) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci const struct dsa_device_ops *ops = dev->dsa_ptr->tag_ops; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (!ops->promisc_on_master) 2688c2ecf20Sopenharmony_ci return; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci rtnl_lock(); 2718c2ecf20Sopenharmony_ci dev_set_promiscuity(dev, inc); 2728c2ecf20Sopenharmony_ci rtnl_unlock(); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic ssize_t tagging_show(struct device *d, struct device_attribute *attr, 2768c2ecf20Sopenharmony_ci char *buf) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct net_device *dev = to_net_dev(d); 2798c2ecf20Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", 2828c2ecf20Sopenharmony_ci dsa_tag_protocol_to_str(cpu_dp->tag_ops)); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(tagging); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic struct attribute *dsa_slave_attrs[] = { 2878c2ecf20Sopenharmony_ci &dev_attr_tagging.attr, 2888c2ecf20Sopenharmony_ci NULL 2898c2ecf20Sopenharmony_ci}; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic const struct attribute_group dsa_group = { 2928c2ecf20Sopenharmony_ci .name = "dsa", 2938c2ecf20Sopenharmony_ci .attrs = dsa_slave_attrs, 2948c2ecf20Sopenharmony_ci}; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic void dsa_master_reset_mtu(struct net_device *dev) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci int err; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci rtnl_lock(); 3018c2ecf20Sopenharmony_ci err = dev_set_mtu(dev, ETH_DATA_LEN); 3028c2ecf20Sopenharmony_ci if (err) 3038c2ecf20Sopenharmony_ci netdev_dbg(dev, 3048c2ecf20Sopenharmony_ci "Unable to reset MTU to exclude DSA overheads\n"); 3058c2ecf20Sopenharmony_ci rtnl_unlock(); 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic struct lock_class_key dsa_master_addr_list_lock_key; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ciint dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct dsa_switch *ds = cpu_dp->ds; 3138c2ecf20Sopenharmony_ci struct device_link *consumer_link; 3148c2ecf20Sopenharmony_ci int ret; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* The DSA master must use SET_NETDEV_DEV for this to work. */ 3178c2ecf20Sopenharmony_ci consumer_link = device_link_add(ds->dev, dev->dev.parent, 3188c2ecf20Sopenharmony_ci DL_FLAG_AUTOREMOVE_CONSUMER); 3198c2ecf20Sopenharmony_ci if (!consumer_link) 3208c2ecf20Sopenharmony_ci netdev_err(dev, 3218c2ecf20Sopenharmony_ci "Failed to create a device link to DSA switch %s\n", 3228c2ecf20Sopenharmony_ci dev_name(ds->dev)); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci rtnl_lock(); 3258c2ecf20Sopenharmony_ci ret = dev_set_mtu(dev, ETH_DATA_LEN + cpu_dp->tag_ops->overhead); 3268c2ecf20Sopenharmony_ci rtnl_unlock(); 3278c2ecf20Sopenharmony_ci if (ret) 3288c2ecf20Sopenharmony_ci netdev_warn(dev, "error %d setting MTU to include DSA overhead\n", 3298c2ecf20Sopenharmony_ci ret); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* If we use a tagging format that doesn't have an ethertype 3328c2ecf20Sopenharmony_ci * field, make sure that all packets from this point on get 3338c2ecf20Sopenharmony_ci * sent to the tag format's receive function. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ci wmb(); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci dev->dsa_ptr = cpu_dp; 3388c2ecf20Sopenharmony_ci lockdep_set_class(&dev->addr_list_lock, 3398c2ecf20Sopenharmony_ci &dsa_master_addr_list_lock_key); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci dsa_master_set_promiscuity(dev, 1); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci ret = dsa_master_ethtool_setup(dev); 3448c2ecf20Sopenharmony_ci if (ret) 3458c2ecf20Sopenharmony_ci goto out_err_reset_promisc; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci dsa_netdev_ops_set(dev, &dsa_netdev_ops); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci ret = sysfs_create_group(&dev->dev.kobj, &dsa_group); 3508c2ecf20Sopenharmony_ci if (ret) 3518c2ecf20Sopenharmony_ci goto out_err_ndo_teardown; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return ret; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ciout_err_ndo_teardown: 3568c2ecf20Sopenharmony_ci dsa_netdev_ops_set(dev, NULL); 3578c2ecf20Sopenharmony_ci dsa_master_ethtool_teardown(dev); 3588c2ecf20Sopenharmony_ciout_err_reset_promisc: 3598c2ecf20Sopenharmony_ci dsa_master_set_promiscuity(dev, -1); 3608c2ecf20Sopenharmony_ci return ret; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_civoid dsa_master_teardown(struct net_device *dev) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci sysfs_remove_group(&dev->dev.kobj, &dsa_group); 3668c2ecf20Sopenharmony_ci dsa_netdev_ops_set(dev, NULL); 3678c2ecf20Sopenharmony_ci dsa_master_ethtool_teardown(dev); 3688c2ecf20Sopenharmony_ci dsa_master_reset_mtu(dev); 3698c2ecf20Sopenharmony_ci dsa_master_set_promiscuity(dev, -1); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci dev->dsa_ptr = NULL; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* If we used a tagging format that doesn't have an ethertype 3748c2ecf20Sopenharmony_ci * field, make sure that all packets from this point get sent 3758c2ecf20Sopenharmony_ci * without the tag and go through the regular receive path. 3768c2ecf20Sopenharmony_ci */ 3778c2ecf20Sopenharmony_ci wmb(); 3788c2ecf20Sopenharmony_ci} 379