162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * net/dsa/slave.c - Slave device handling 462306a36Sopenharmony_ci * Copyright (c) 2008-2009 Marvell Semiconductor 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/list.h> 862306a36Sopenharmony_ci#include <linux/etherdevice.h> 962306a36Sopenharmony_ci#include <linux/netdevice.h> 1062306a36Sopenharmony_ci#include <linux/phy.h> 1162306a36Sopenharmony_ci#include <linux/phy_fixed.h> 1262306a36Sopenharmony_ci#include <linux/phylink.h> 1362306a36Sopenharmony_ci#include <linux/of_net.h> 1462306a36Sopenharmony_ci#include <linux/of_mdio.h> 1562306a36Sopenharmony_ci#include <linux/mdio.h> 1662306a36Sopenharmony_ci#include <net/rtnetlink.h> 1762306a36Sopenharmony_ci#include <net/pkt_cls.h> 1862306a36Sopenharmony_ci#include <net/selftests.h> 1962306a36Sopenharmony_ci#include <net/tc_act/tc_mirred.h> 2062306a36Sopenharmony_ci#include <linux/if_bridge.h> 2162306a36Sopenharmony_ci#include <linux/if_hsr.h> 2262306a36Sopenharmony_ci#include <net/dcbnl.h> 2362306a36Sopenharmony_ci#include <linux/netpoll.h> 2462306a36Sopenharmony_ci#include <linux/string.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "dsa.h" 2762306a36Sopenharmony_ci#include "port.h" 2862306a36Sopenharmony_ci#include "master.h" 2962306a36Sopenharmony_ci#include "netlink.h" 3062306a36Sopenharmony_ci#include "slave.h" 3162306a36Sopenharmony_ci#include "switch.h" 3262306a36Sopenharmony_ci#include "tag.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct dsa_switchdev_event_work { 3562306a36Sopenharmony_ci struct net_device *dev; 3662306a36Sopenharmony_ci struct net_device *orig_dev; 3762306a36Sopenharmony_ci struct work_struct work; 3862306a36Sopenharmony_ci unsigned long event; 3962306a36Sopenharmony_ci /* Specific for SWITCHDEV_FDB_ADD_TO_DEVICE and 4062306a36Sopenharmony_ci * SWITCHDEV_FDB_DEL_TO_DEVICE 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci unsigned char addr[ETH_ALEN]; 4362306a36Sopenharmony_ci u16 vid; 4462306a36Sopenharmony_ci bool host_addr; 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cienum dsa_standalone_event { 4862306a36Sopenharmony_ci DSA_UC_ADD, 4962306a36Sopenharmony_ci DSA_UC_DEL, 5062306a36Sopenharmony_ci DSA_MC_ADD, 5162306a36Sopenharmony_ci DSA_MC_DEL, 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct dsa_standalone_event_work { 5562306a36Sopenharmony_ci struct work_struct work; 5662306a36Sopenharmony_ci struct net_device *dev; 5762306a36Sopenharmony_ci enum dsa_standalone_event event; 5862306a36Sopenharmony_ci unsigned char addr[ETH_ALEN]; 5962306a36Sopenharmony_ci u16 vid; 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct dsa_host_vlan_rx_filtering_ctx { 6362306a36Sopenharmony_ci struct net_device *dev; 6462306a36Sopenharmony_ci const unsigned char *addr; 6562306a36Sopenharmony_ci enum dsa_standalone_event event; 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic bool dsa_switch_supports_uc_filtering(struct dsa_switch *ds) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci return ds->ops->port_fdb_add && ds->ops->port_fdb_del && 7162306a36Sopenharmony_ci ds->fdb_isolation && !ds->vlan_filtering_is_global && 7262306a36Sopenharmony_ci !ds->needs_standalone_vlan_filtering; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic bool dsa_switch_supports_mc_filtering(struct dsa_switch *ds) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci return ds->ops->port_mdb_add && ds->ops->port_mdb_del && 7862306a36Sopenharmony_ci ds->fdb_isolation && !ds->vlan_filtering_is_global && 7962306a36Sopenharmony_ci !ds->needs_standalone_vlan_filtering; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void dsa_slave_standalone_event_work(struct work_struct *work) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct dsa_standalone_event_work *standalone_work = 8562306a36Sopenharmony_ci container_of(work, struct dsa_standalone_event_work, work); 8662306a36Sopenharmony_ci const unsigned char *addr = standalone_work->addr; 8762306a36Sopenharmony_ci struct net_device *dev = standalone_work->dev; 8862306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 8962306a36Sopenharmony_ci struct switchdev_obj_port_mdb mdb; 9062306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 9162306a36Sopenharmony_ci u16 vid = standalone_work->vid; 9262306a36Sopenharmony_ci int err; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci switch (standalone_work->event) { 9562306a36Sopenharmony_ci case DSA_UC_ADD: 9662306a36Sopenharmony_ci err = dsa_port_standalone_host_fdb_add(dp, addr, vid); 9762306a36Sopenharmony_ci if (err) { 9862306a36Sopenharmony_ci dev_err(ds->dev, 9962306a36Sopenharmony_ci "port %d failed to add %pM vid %d to fdb: %d\n", 10062306a36Sopenharmony_ci dp->index, addr, vid, err); 10162306a36Sopenharmony_ci break; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci case DSA_UC_DEL: 10662306a36Sopenharmony_ci err = dsa_port_standalone_host_fdb_del(dp, addr, vid); 10762306a36Sopenharmony_ci if (err) { 10862306a36Sopenharmony_ci dev_err(ds->dev, 10962306a36Sopenharmony_ci "port %d failed to delete %pM vid %d from fdb: %d\n", 11062306a36Sopenharmony_ci dp->index, addr, vid, err); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci break; 11462306a36Sopenharmony_ci case DSA_MC_ADD: 11562306a36Sopenharmony_ci ether_addr_copy(mdb.addr, addr); 11662306a36Sopenharmony_ci mdb.vid = vid; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci err = dsa_port_standalone_host_mdb_add(dp, &mdb); 11962306a36Sopenharmony_ci if (err) { 12062306a36Sopenharmony_ci dev_err(ds->dev, 12162306a36Sopenharmony_ci "port %d failed to add %pM vid %d to mdb: %d\n", 12262306a36Sopenharmony_ci dp->index, addr, vid, err); 12362306a36Sopenharmony_ci break; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci case DSA_MC_DEL: 12762306a36Sopenharmony_ci ether_addr_copy(mdb.addr, addr); 12862306a36Sopenharmony_ci mdb.vid = vid; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci err = dsa_port_standalone_host_mdb_del(dp, &mdb); 13162306a36Sopenharmony_ci if (err) { 13262306a36Sopenharmony_ci dev_err(ds->dev, 13362306a36Sopenharmony_ci "port %d failed to delete %pM vid %d from mdb: %d\n", 13462306a36Sopenharmony_ci dp->index, addr, vid, err); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci kfree(standalone_work); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int dsa_slave_schedule_standalone_work(struct net_device *dev, 14462306a36Sopenharmony_ci enum dsa_standalone_event event, 14562306a36Sopenharmony_ci const unsigned char *addr, 14662306a36Sopenharmony_ci u16 vid) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct dsa_standalone_event_work *standalone_work; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci standalone_work = kzalloc(sizeof(*standalone_work), GFP_ATOMIC); 15162306a36Sopenharmony_ci if (!standalone_work) 15262306a36Sopenharmony_ci return -ENOMEM; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci INIT_WORK(&standalone_work->work, dsa_slave_standalone_event_work); 15562306a36Sopenharmony_ci standalone_work->event = event; 15662306a36Sopenharmony_ci standalone_work->dev = dev; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci ether_addr_copy(standalone_work->addr, addr); 15962306a36Sopenharmony_ci standalone_work->vid = vid; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci dsa_schedule_work(&standalone_work->work); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int dsa_slave_host_vlan_rx_filtering(void *arg, int vid) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct dsa_host_vlan_rx_filtering_ctx *ctx = arg; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return dsa_slave_schedule_standalone_work(ctx->dev, ctx->event, 17162306a36Sopenharmony_ci ctx->addr, vid); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int dsa_slave_vlan_for_each(struct net_device *dev, 17562306a36Sopenharmony_ci int (*cb)(void *arg, int vid), void *arg) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 17862306a36Sopenharmony_ci struct dsa_vlan *v; 17962306a36Sopenharmony_ci int err; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci lockdep_assert_held(&dev->addr_list_lock); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci err = cb(arg, 0); 18462306a36Sopenharmony_ci if (err) 18562306a36Sopenharmony_ci return err; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci list_for_each_entry(v, &dp->user_vlans, list) { 18862306a36Sopenharmony_ci err = cb(arg, v->vid); 18962306a36Sopenharmony_ci if (err) 19062306a36Sopenharmony_ci return err; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int dsa_slave_sync_uc(struct net_device *dev, 19762306a36Sopenharmony_ci const unsigned char *addr) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct net_device *master = dsa_slave_to_master(dev); 20062306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 20162306a36Sopenharmony_ci struct dsa_host_vlan_rx_filtering_ctx ctx = { 20262306a36Sopenharmony_ci .dev = dev, 20362306a36Sopenharmony_ci .addr = addr, 20462306a36Sopenharmony_ci .event = DSA_UC_ADD, 20562306a36Sopenharmony_ci }; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci dev_uc_add(master, addr); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (!dsa_switch_supports_uc_filtering(dp->ds)) 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return dsa_slave_vlan_for_each(dev, dsa_slave_host_vlan_rx_filtering, 21362306a36Sopenharmony_ci &ctx); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int dsa_slave_unsync_uc(struct net_device *dev, 21762306a36Sopenharmony_ci const unsigned char *addr) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct net_device *master = dsa_slave_to_master(dev); 22062306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 22162306a36Sopenharmony_ci struct dsa_host_vlan_rx_filtering_ctx ctx = { 22262306a36Sopenharmony_ci .dev = dev, 22362306a36Sopenharmony_ci .addr = addr, 22462306a36Sopenharmony_ci .event = DSA_UC_DEL, 22562306a36Sopenharmony_ci }; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci dev_uc_del(master, addr); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (!dsa_switch_supports_uc_filtering(dp->ds)) 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return dsa_slave_vlan_for_each(dev, dsa_slave_host_vlan_rx_filtering, 23362306a36Sopenharmony_ci &ctx); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int dsa_slave_sync_mc(struct net_device *dev, 23762306a36Sopenharmony_ci const unsigned char *addr) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct net_device *master = dsa_slave_to_master(dev); 24062306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 24162306a36Sopenharmony_ci struct dsa_host_vlan_rx_filtering_ctx ctx = { 24262306a36Sopenharmony_ci .dev = dev, 24362306a36Sopenharmony_ci .addr = addr, 24462306a36Sopenharmony_ci .event = DSA_MC_ADD, 24562306a36Sopenharmony_ci }; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci dev_mc_add(master, addr); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (!dsa_switch_supports_mc_filtering(dp->ds)) 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return dsa_slave_vlan_for_each(dev, dsa_slave_host_vlan_rx_filtering, 25362306a36Sopenharmony_ci &ctx); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int dsa_slave_unsync_mc(struct net_device *dev, 25762306a36Sopenharmony_ci const unsigned char *addr) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct net_device *master = dsa_slave_to_master(dev); 26062306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 26162306a36Sopenharmony_ci struct dsa_host_vlan_rx_filtering_ctx ctx = { 26262306a36Sopenharmony_ci .dev = dev, 26362306a36Sopenharmony_ci .addr = addr, 26462306a36Sopenharmony_ci .event = DSA_MC_DEL, 26562306a36Sopenharmony_ci }; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci dev_mc_del(master, addr); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (!dsa_switch_supports_mc_filtering(dp->ds)) 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return dsa_slave_vlan_for_each(dev, dsa_slave_host_vlan_rx_filtering, 27362306a36Sopenharmony_ci &ctx); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_civoid dsa_slave_sync_ha(struct net_device *dev) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 27962306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 28062306a36Sopenharmony_ci struct netdev_hw_addr *ha; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci netif_addr_lock_bh(dev); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci netdev_for_each_synced_mc_addr(ha, dev) 28562306a36Sopenharmony_ci dsa_slave_sync_mc(dev, ha->addr); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci netdev_for_each_synced_uc_addr(ha, dev) 28862306a36Sopenharmony_ci dsa_slave_sync_uc(dev, ha->addr); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci netif_addr_unlock_bh(dev); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (dsa_switch_supports_uc_filtering(ds) || 29362306a36Sopenharmony_ci dsa_switch_supports_mc_filtering(ds)) 29462306a36Sopenharmony_ci dsa_flush_workqueue(); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_civoid dsa_slave_unsync_ha(struct net_device *dev) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 30062306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 30162306a36Sopenharmony_ci struct netdev_hw_addr *ha; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci netif_addr_lock_bh(dev); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci netdev_for_each_synced_uc_addr(ha, dev) 30662306a36Sopenharmony_ci dsa_slave_unsync_uc(dev, ha->addr); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci netdev_for_each_synced_mc_addr(ha, dev) 30962306a36Sopenharmony_ci dsa_slave_unsync_mc(dev, ha->addr); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci netif_addr_unlock_bh(dev); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (dsa_switch_supports_uc_filtering(ds) || 31462306a36Sopenharmony_ci dsa_switch_supports_mc_filtering(ds)) 31562306a36Sopenharmony_ci dsa_flush_workqueue(); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/* slave mii_bus handling ***************************************************/ 31962306a36Sopenharmony_cistatic int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct dsa_switch *ds = bus->priv; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (ds->phys_mii_mask & (1 << addr)) 32462306a36Sopenharmony_ci return ds->ops->phy_read(ds, addr, reg); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return 0xffff; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic int dsa_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct dsa_switch *ds = bus->priv; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (ds->phys_mii_mask & (1 << addr)) 33462306a36Sopenharmony_ci return ds->ops->phy_write(ds, addr, reg, val); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_civoid dsa_slave_mii_bus_init(struct dsa_switch *ds) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci ds->slave_mii_bus->priv = (void *)ds; 34262306a36Sopenharmony_ci ds->slave_mii_bus->name = "dsa slave smi"; 34362306a36Sopenharmony_ci ds->slave_mii_bus->read = dsa_slave_phy_read; 34462306a36Sopenharmony_ci ds->slave_mii_bus->write = dsa_slave_phy_write; 34562306a36Sopenharmony_ci snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "dsa-%d.%d", 34662306a36Sopenharmony_ci ds->dst->index, ds->index); 34762306a36Sopenharmony_ci ds->slave_mii_bus->parent = ds->dev; 34862306a36Sopenharmony_ci ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci/* slave device handling ****************************************************/ 35362306a36Sopenharmony_cistatic int dsa_slave_get_iflink(const struct net_device *dev) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci return dsa_slave_to_master(dev)->ifindex; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic int dsa_slave_open(struct net_device *dev) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct net_device *master = dsa_slave_to_master(dev); 36162306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 36262306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 36362306a36Sopenharmony_ci int err; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci err = dev_open(master, NULL); 36662306a36Sopenharmony_ci if (err < 0) { 36762306a36Sopenharmony_ci netdev_err(dev, "failed to open master %s\n", master->name); 36862306a36Sopenharmony_ci goto out; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (dsa_switch_supports_uc_filtering(ds)) { 37262306a36Sopenharmony_ci err = dsa_port_standalone_host_fdb_add(dp, dev->dev_addr, 0); 37362306a36Sopenharmony_ci if (err) 37462306a36Sopenharmony_ci goto out; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) { 37862306a36Sopenharmony_ci err = dev_uc_add(master, dev->dev_addr); 37962306a36Sopenharmony_ci if (err < 0) 38062306a36Sopenharmony_ci goto del_host_addr; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci err = dsa_port_enable_rt(dp, dev->phydev); 38462306a36Sopenharmony_ci if (err) 38562306a36Sopenharmony_ci goto del_unicast; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cidel_unicast: 39062306a36Sopenharmony_ci if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) 39162306a36Sopenharmony_ci dev_uc_del(master, dev->dev_addr); 39262306a36Sopenharmony_cidel_host_addr: 39362306a36Sopenharmony_ci if (dsa_switch_supports_uc_filtering(ds)) 39462306a36Sopenharmony_ci dsa_port_standalone_host_fdb_del(dp, dev->dev_addr, 0); 39562306a36Sopenharmony_ciout: 39662306a36Sopenharmony_ci return err; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int dsa_slave_close(struct net_device *dev) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct net_device *master = dsa_slave_to_master(dev); 40262306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 40362306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci dsa_port_disable_rt(dp); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) 40862306a36Sopenharmony_ci dev_uc_del(master, dev->dev_addr); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (dsa_switch_supports_uc_filtering(ds)) 41162306a36Sopenharmony_ci dsa_port_standalone_host_fdb_del(dp, dev->dev_addr, 0); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return 0; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic void dsa_slave_manage_host_flood(struct net_device *dev) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci bool mc = dev->flags & (IFF_PROMISC | IFF_ALLMULTI); 41962306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 42062306a36Sopenharmony_ci bool uc = dev->flags & IFF_PROMISC; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci dsa_port_set_host_flood(dp, uc, mc); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic void dsa_slave_change_rx_flags(struct net_device *dev, int change) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct net_device *master = dsa_slave_to_master(dev); 42862306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 42962306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (change & IFF_ALLMULTI) 43262306a36Sopenharmony_ci dev_set_allmulti(master, 43362306a36Sopenharmony_ci dev->flags & IFF_ALLMULTI ? 1 : -1); 43462306a36Sopenharmony_ci if (change & IFF_PROMISC) 43562306a36Sopenharmony_ci dev_set_promiscuity(master, 43662306a36Sopenharmony_ci dev->flags & IFF_PROMISC ? 1 : -1); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (dsa_switch_supports_uc_filtering(ds) && 43962306a36Sopenharmony_ci dsa_switch_supports_mc_filtering(ds)) 44062306a36Sopenharmony_ci dsa_slave_manage_host_flood(dev); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic void dsa_slave_set_rx_mode(struct net_device *dev) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci __dev_mc_sync(dev, dsa_slave_sync_mc, dsa_slave_unsync_mc); 44662306a36Sopenharmony_ci __dev_uc_sync(dev, dsa_slave_sync_uc, dsa_slave_unsync_uc); 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int dsa_slave_set_mac_address(struct net_device *dev, void *a) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct net_device *master = dsa_slave_to_master(dev); 45262306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 45362306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 45462306a36Sopenharmony_ci struct sockaddr *addr = a; 45562306a36Sopenharmony_ci int err; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 45862306a36Sopenharmony_ci return -EADDRNOTAVAIL; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* If the port is down, the address isn't synced yet to hardware or 46162306a36Sopenharmony_ci * to the DSA master, so there is nothing to change. 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_ci if (!(dev->flags & IFF_UP)) 46462306a36Sopenharmony_ci goto out_change_dev_addr; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (dsa_switch_supports_uc_filtering(ds)) { 46762306a36Sopenharmony_ci err = dsa_port_standalone_host_fdb_add(dp, addr->sa_data, 0); 46862306a36Sopenharmony_ci if (err) 46962306a36Sopenharmony_ci return err; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (!ether_addr_equal(addr->sa_data, master->dev_addr)) { 47362306a36Sopenharmony_ci err = dev_uc_add(master, addr->sa_data); 47462306a36Sopenharmony_ci if (err < 0) 47562306a36Sopenharmony_ci goto del_unicast; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) 47962306a36Sopenharmony_ci dev_uc_del(master, dev->dev_addr); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (dsa_switch_supports_uc_filtering(ds)) 48262306a36Sopenharmony_ci dsa_port_standalone_host_fdb_del(dp, dev->dev_addr, 0); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ciout_change_dev_addr: 48562306a36Sopenharmony_ci eth_hw_addr_set(dev, addr->sa_data); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return 0; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cidel_unicast: 49062306a36Sopenharmony_ci if (dsa_switch_supports_uc_filtering(ds)) 49162306a36Sopenharmony_ci dsa_port_standalone_host_fdb_del(dp, addr->sa_data, 0); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci return err; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistruct dsa_slave_dump_ctx { 49762306a36Sopenharmony_ci struct net_device *dev; 49862306a36Sopenharmony_ci struct sk_buff *skb; 49962306a36Sopenharmony_ci struct netlink_callback *cb; 50062306a36Sopenharmony_ci int idx; 50162306a36Sopenharmony_ci}; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic int 50462306a36Sopenharmony_cidsa_slave_port_fdb_do_dump(const unsigned char *addr, u16 vid, 50562306a36Sopenharmony_ci bool is_static, void *data) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci struct dsa_slave_dump_ctx *dump = data; 50862306a36Sopenharmony_ci u32 portid = NETLINK_CB(dump->cb->skb).portid; 50962306a36Sopenharmony_ci u32 seq = dump->cb->nlh->nlmsg_seq; 51062306a36Sopenharmony_ci struct nlmsghdr *nlh; 51162306a36Sopenharmony_ci struct ndmsg *ndm; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (dump->idx < dump->cb->args[2]) 51462306a36Sopenharmony_ci goto skip; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH, 51762306a36Sopenharmony_ci sizeof(*ndm), NLM_F_MULTI); 51862306a36Sopenharmony_ci if (!nlh) 51962306a36Sopenharmony_ci return -EMSGSIZE; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci ndm = nlmsg_data(nlh); 52262306a36Sopenharmony_ci ndm->ndm_family = AF_BRIDGE; 52362306a36Sopenharmony_ci ndm->ndm_pad1 = 0; 52462306a36Sopenharmony_ci ndm->ndm_pad2 = 0; 52562306a36Sopenharmony_ci ndm->ndm_flags = NTF_SELF; 52662306a36Sopenharmony_ci ndm->ndm_type = 0; 52762306a36Sopenharmony_ci ndm->ndm_ifindex = dump->dev->ifindex; 52862306a36Sopenharmony_ci ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr)) 53162306a36Sopenharmony_ci goto nla_put_failure; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid)) 53462306a36Sopenharmony_ci goto nla_put_failure; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci nlmsg_end(dump->skb, nlh); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ciskip: 53962306a36Sopenharmony_ci dump->idx++; 54062306a36Sopenharmony_ci return 0; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cinla_put_failure: 54362306a36Sopenharmony_ci nlmsg_cancel(dump->skb, nlh); 54462306a36Sopenharmony_ci return -EMSGSIZE; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic int 54862306a36Sopenharmony_cidsa_slave_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, 54962306a36Sopenharmony_ci struct net_device *dev, struct net_device *filter_dev, 55062306a36Sopenharmony_ci int *idx) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 55362306a36Sopenharmony_ci struct dsa_slave_dump_ctx dump = { 55462306a36Sopenharmony_ci .dev = dev, 55562306a36Sopenharmony_ci .skb = skb, 55662306a36Sopenharmony_ci .cb = cb, 55762306a36Sopenharmony_ci .idx = *idx, 55862306a36Sopenharmony_ci }; 55962306a36Sopenharmony_ci int err; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci err = dsa_port_fdb_dump(dp, dsa_slave_port_fdb_do_dump, &dump); 56262306a36Sopenharmony_ci *idx = dump.idx; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return err; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct dsa_slave_priv *p = netdev_priv(dev); 57062306a36Sopenharmony_ci struct dsa_switch *ds = p->dp->ds; 57162306a36Sopenharmony_ci int port = p->dp->index; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* Pass through to switch driver if it supports timestamping */ 57462306a36Sopenharmony_ci switch (cmd) { 57562306a36Sopenharmony_ci case SIOCGHWTSTAMP: 57662306a36Sopenharmony_ci if (ds->ops->port_hwtstamp_get) 57762306a36Sopenharmony_ci return ds->ops->port_hwtstamp_get(ds, port, ifr); 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci case SIOCSHWTSTAMP: 58062306a36Sopenharmony_ci if (ds->ops->port_hwtstamp_set) 58162306a36Sopenharmony_ci return ds->ops->port_hwtstamp_set(ds, port, ifr); 58262306a36Sopenharmony_ci break; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return phylink_mii_ioctl(p->dp->pl, ifr, cmd); 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic int dsa_slave_port_attr_set(struct net_device *dev, const void *ctx, 58962306a36Sopenharmony_ci const struct switchdev_attr *attr, 59062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 59362306a36Sopenharmony_ci int ret; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (ctx && ctx != dp) 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci switch (attr->id) { 59962306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_STP_STATE: 60062306a36Sopenharmony_ci if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev)) 60162306a36Sopenharmony_ci return -EOPNOTSUPP; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci ret = dsa_port_set_state(dp, attr->u.stp_state, true); 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_MST_STATE: 60662306a36Sopenharmony_ci if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev)) 60762306a36Sopenharmony_ci return -EOPNOTSUPP; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci ret = dsa_port_set_mst_state(dp, &attr->u.mst_state, extack); 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: 61262306a36Sopenharmony_ci if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev)) 61362306a36Sopenharmony_ci return -EOPNOTSUPP; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci ret = dsa_port_vlan_filtering(dp, attr->u.vlan_filtering, 61662306a36Sopenharmony_ci extack); 61762306a36Sopenharmony_ci break; 61862306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: 61962306a36Sopenharmony_ci if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev)) 62062306a36Sopenharmony_ci return -EOPNOTSUPP; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci ret = dsa_port_ageing_time(dp, attr->u.ageing_time); 62362306a36Sopenharmony_ci break; 62462306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_MST: 62562306a36Sopenharmony_ci if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev)) 62662306a36Sopenharmony_ci return -EOPNOTSUPP; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci ret = dsa_port_mst_enable(dp, attr->u.mst, extack); 62962306a36Sopenharmony_ci break; 63062306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: 63162306a36Sopenharmony_ci if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev)) 63262306a36Sopenharmony_ci return -EOPNOTSUPP; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags, 63562306a36Sopenharmony_ci extack); 63662306a36Sopenharmony_ci break; 63762306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: 63862306a36Sopenharmony_ci if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev)) 63962306a36Sopenharmony_ci return -EOPNOTSUPP; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, extack); 64262306a36Sopenharmony_ci break; 64362306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_VLAN_MSTI: 64462306a36Sopenharmony_ci if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev)) 64562306a36Sopenharmony_ci return -EOPNOTSUPP; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci ret = dsa_port_vlan_msti(dp, &attr->u.vlan_msti); 64862306a36Sopenharmony_ci break; 64962306a36Sopenharmony_ci default: 65062306a36Sopenharmony_ci ret = -EOPNOTSUPP; 65162306a36Sopenharmony_ci break; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci return ret; 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci/* Must be called under rcu_read_lock() */ 65862306a36Sopenharmony_cistatic int 65962306a36Sopenharmony_cidsa_slave_vlan_check_for_8021q_uppers(struct net_device *slave, 66062306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct net_device *upper_dev; 66362306a36Sopenharmony_ci struct list_head *iter; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci netdev_for_each_upper_dev_rcu(slave, upper_dev, iter) { 66662306a36Sopenharmony_ci u16 vid; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (!is_vlan_dev(upper_dev)) 66962306a36Sopenharmony_ci continue; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci vid = vlan_dev_vlan_id(upper_dev); 67262306a36Sopenharmony_ci if (vid == vlan->vid) 67362306a36Sopenharmony_ci return -EBUSY; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci return 0; 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic int dsa_slave_vlan_add(struct net_device *dev, 68062306a36Sopenharmony_ci const struct switchdev_obj *obj, 68162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 68462306a36Sopenharmony_ci struct switchdev_obj_port_vlan *vlan; 68562306a36Sopenharmony_ci int err; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (dsa_port_skip_vlan_configuration(dp)) { 68862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "skipping configuration of VLAN"); 68962306a36Sopenharmony_ci return 0; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci /* Deny adding a bridge VLAN when there is already an 802.1Q upper with 69562306a36Sopenharmony_ci * the same VID. 69662306a36Sopenharmony_ci */ 69762306a36Sopenharmony_ci if (br_vlan_enabled(dsa_port_bridge_dev_get(dp))) { 69862306a36Sopenharmony_ci rcu_read_lock(); 69962306a36Sopenharmony_ci err = dsa_slave_vlan_check_for_8021q_uppers(dev, vlan); 70062306a36Sopenharmony_ci rcu_read_unlock(); 70162306a36Sopenharmony_ci if (err) { 70262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 70362306a36Sopenharmony_ci "Port already has a VLAN upper with this VID"); 70462306a36Sopenharmony_ci return err; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci return dsa_port_vlan_add(dp, vlan, extack); 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci/* Offload a VLAN installed on the bridge or on a foreign interface by 71262306a36Sopenharmony_ci * installing it as a VLAN towards the CPU port. 71362306a36Sopenharmony_ci */ 71462306a36Sopenharmony_cistatic int dsa_slave_host_vlan_add(struct net_device *dev, 71562306a36Sopenharmony_ci const struct switchdev_obj *obj, 71662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 71962306a36Sopenharmony_ci struct switchdev_obj_port_vlan vlan; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci /* Do nothing if this is a software bridge */ 72262306a36Sopenharmony_ci if (!dp->bridge) 72362306a36Sopenharmony_ci return -EOPNOTSUPP; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (dsa_port_skip_vlan_configuration(dp)) { 72662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "skipping configuration of VLAN"); 72762306a36Sopenharmony_ci return 0; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci vlan = *SWITCHDEV_OBJ_PORT_VLAN(obj); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* Even though drivers often handle CPU membership in special ways, 73362306a36Sopenharmony_ci * it doesn't make sense to program a PVID, so clear this flag. 73462306a36Sopenharmony_ci */ 73562306a36Sopenharmony_ci vlan.flags &= ~BRIDGE_VLAN_INFO_PVID; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return dsa_port_host_vlan_add(dp, &vlan, extack); 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic int dsa_slave_port_obj_add(struct net_device *dev, const void *ctx, 74162306a36Sopenharmony_ci const struct switchdev_obj *obj, 74262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 74562306a36Sopenharmony_ci int err; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (ctx && ctx != dp) 74862306a36Sopenharmony_ci return 0; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci switch (obj->id) { 75162306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_MDB: 75262306a36Sopenharmony_ci if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) 75362306a36Sopenharmony_ci return -EOPNOTSUPP; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); 75662306a36Sopenharmony_ci break; 75762306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_HOST_MDB: 75862306a36Sopenharmony_ci if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev)) 75962306a36Sopenharmony_ci return -EOPNOTSUPP; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci err = dsa_port_bridge_host_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); 76262306a36Sopenharmony_ci break; 76362306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 76462306a36Sopenharmony_ci if (dsa_port_offloads_bridge_port(dp, obj->orig_dev)) 76562306a36Sopenharmony_ci err = dsa_slave_vlan_add(dev, obj, extack); 76662306a36Sopenharmony_ci else 76762306a36Sopenharmony_ci err = dsa_slave_host_vlan_add(dev, obj, extack); 76862306a36Sopenharmony_ci break; 76962306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_MRP: 77062306a36Sopenharmony_ci if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev)) 77162306a36Sopenharmony_ci return -EOPNOTSUPP; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci err = dsa_port_mrp_add(dp, SWITCHDEV_OBJ_MRP(obj)); 77462306a36Sopenharmony_ci break; 77562306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_RING_ROLE_MRP: 77662306a36Sopenharmony_ci if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev)) 77762306a36Sopenharmony_ci return -EOPNOTSUPP; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci err = dsa_port_mrp_add_ring_role(dp, 78062306a36Sopenharmony_ci SWITCHDEV_OBJ_RING_ROLE_MRP(obj)); 78162306a36Sopenharmony_ci break; 78262306a36Sopenharmony_ci default: 78362306a36Sopenharmony_ci err = -EOPNOTSUPP; 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci return err; 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic int dsa_slave_vlan_del(struct net_device *dev, 79162306a36Sopenharmony_ci const struct switchdev_obj *obj) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 79462306a36Sopenharmony_ci struct switchdev_obj_port_vlan *vlan; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (dsa_port_skip_vlan_configuration(dp)) 79762306a36Sopenharmony_ci return 0; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci return dsa_port_vlan_del(dp, vlan); 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistatic int dsa_slave_host_vlan_del(struct net_device *dev, 80562306a36Sopenharmony_ci const struct switchdev_obj *obj) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 80862306a36Sopenharmony_ci struct switchdev_obj_port_vlan *vlan; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci /* Do nothing if this is a software bridge */ 81162306a36Sopenharmony_ci if (!dp->bridge) 81262306a36Sopenharmony_ci return -EOPNOTSUPP; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (dsa_port_skip_vlan_configuration(dp)) 81562306a36Sopenharmony_ci return 0; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci return dsa_port_host_vlan_del(dp, vlan); 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic int dsa_slave_port_obj_del(struct net_device *dev, const void *ctx, 82362306a36Sopenharmony_ci const struct switchdev_obj *obj) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 82662306a36Sopenharmony_ci int err; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (ctx && ctx != dp) 82962306a36Sopenharmony_ci return 0; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci switch (obj->id) { 83262306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_MDB: 83362306a36Sopenharmony_ci if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) 83462306a36Sopenharmony_ci return -EOPNOTSUPP; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); 83762306a36Sopenharmony_ci break; 83862306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_HOST_MDB: 83962306a36Sopenharmony_ci if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev)) 84062306a36Sopenharmony_ci return -EOPNOTSUPP; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci err = dsa_port_bridge_host_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); 84362306a36Sopenharmony_ci break; 84462306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 84562306a36Sopenharmony_ci if (dsa_port_offloads_bridge_port(dp, obj->orig_dev)) 84662306a36Sopenharmony_ci err = dsa_slave_vlan_del(dev, obj); 84762306a36Sopenharmony_ci else 84862306a36Sopenharmony_ci err = dsa_slave_host_vlan_del(dev, obj); 84962306a36Sopenharmony_ci break; 85062306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_MRP: 85162306a36Sopenharmony_ci if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev)) 85262306a36Sopenharmony_ci return -EOPNOTSUPP; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci err = dsa_port_mrp_del(dp, SWITCHDEV_OBJ_MRP(obj)); 85562306a36Sopenharmony_ci break; 85662306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_RING_ROLE_MRP: 85762306a36Sopenharmony_ci if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev)) 85862306a36Sopenharmony_ci return -EOPNOTSUPP; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci err = dsa_port_mrp_del_ring_role(dp, 86162306a36Sopenharmony_ci SWITCHDEV_OBJ_RING_ROLE_MRP(obj)); 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci default: 86462306a36Sopenharmony_ci err = -EOPNOTSUPP; 86562306a36Sopenharmony_ci break; 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci return err; 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic inline netdev_tx_t dsa_slave_netpoll_send_skb(struct net_device *dev, 87262306a36Sopenharmony_ci struct sk_buff *skb) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 87562306a36Sopenharmony_ci struct dsa_slave_priv *p = netdev_priv(dev); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci return netpoll_send_skb(p->netpoll, skb); 87862306a36Sopenharmony_ci#else 87962306a36Sopenharmony_ci BUG(); 88062306a36Sopenharmony_ci return NETDEV_TX_OK; 88162306a36Sopenharmony_ci#endif 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cistatic void dsa_skb_tx_timestamp(struct dsa_slave_priv *p, 88562306a36Sopenharmony_ci struct sk_buff *skb) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci struct dsa_switch *ds = p->dp->ds; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) 89062306a36Sopenharmony_ci return; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (!ds->ops->port_txtstamp) 89362306a36Sopenharmony_ci return; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci ds->ops->port_txtstamp(ds, p->dp->index, skb); 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cinetdev_tx_t dsa_enqueue_skb(struct sk_buff *skb, struct net_device *dev) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci /* SKB for netpoll still need to be mangled with the protocol-specific 90162306a36Sopenharmony_ci * tag to be successfully transmitted 90262306a36Sopenharmony_ci */ 90362306a36Sopenharmony_ci if (unlikely(netpoll_tx_running(dev))) 90462306a36Sopenharmony_ci return dsa_slave_netpoll_send_skb(dev, skb); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci /* Queue the SKB for transmission on the parent interface, but 90762306a36Sopenharmony_ci * do not modify its EtherType 90862306a36Sopenharmony_ci */ 90962306a36Sopenharmony_ci skb->dev = dsa_slave_to_master(dev); 91062306a36Sopenharmony_ci dev_queue_xmit(skb); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci return NETDEV_TX_OK; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dsa_enqueue_skb); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic int dsa_realloc_skb(struct sk_buff *skb, struct net_device *dev) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci int needed_headroom = dev->needed_headroom; 91962306a36Sopenharmony_ci int needed_tailroom = dev->needed_tailroom; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci /* For tail taggers, we need to pad short frames ourselves, to ensure 92262306a36Sopenharmony_ci * that the tail tag does not fail at its role of being at the end of 92362306a36Sopenharmony_ci * the packet, once the master interface pads the frame. Account for 92462306a36Sopenharmony_ci * that pad length here, and pad later. 92562306a36Sopenharmony_ci */ 92662306a36Sopenharmony_ci if (unlikely(needed_tailroom && skb->len < ETH_ZLEN)) 92762306a36Sopenharmony_ci needed_tailroom += ETH_ZLEN - skb->len; 92862306a36Sopenharmony_ci /* skb_headroom() returns unsigned int... */ 92962306a36Sopenharmony_ci needed_headroom = max_t(int, needed_headroom - skb_headroom(skb), 0); 93062306a36Sopenharmony_ci needed_tailroom = max_t(int, needed_tailroom - skb_tailroom(skb), 0); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (likely(!needed_headroom && !needed_tailroom && !skb_cloned(skb))) 93362306a36Sopenharmony_ci /* No reallocation needed, yay! */ 93462306a36Sopenharmony_ci return 0; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci return pskb_expand_head(skb, needed_headroom, needed_tailroom, 93762306a36Sopenharmony_ci GFP_ATOMIC); 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev) 94162306a36Sopenharmony_ci{ 94262306a36Sopenharmony_ci struct dsa_slave_priv *p = netdev_priv(dev); 94362306a36Sopenharmony_ci struct sk_buff *nskb; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci dev_sw_netstats_tx_add(dev, 1, skb->len); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci memset(skb->cb, 0, sizeof(skb->cb)); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci /* Handle tx timestamp if any */ 95062306a36Sopenharmony_ci dsa_skb_tx_timestamp(p, skb); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (dsa_realloc_skb(skb, dev)) { 95362306a36Sopenharmony_ci dev_kfree_skb_any(skb); 95462306a36Sopenharmony_ci return NETDEV_TX_OK; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* needed_tailroom should still be 'warm' in the cache line from 95862306a36Sopenharmony_ci * dsa_realloc_skb(), which has also ensured that padding is safe. 95962306a36Sopenharmony_ci */ 96062306a36Sopenharmony_ci if (dev->needed_tailroom) 96162306a36Sopenharmony_ci eth_skb_pad(skb); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* Transmit function may have to reallocate the original SKB, 96462306a36Sopenharmony_ci * in which case it must have freed it. Only free it here on error. 96562306a36Sopenharmony_ci */ 96662306a36Sopenharmony_ci nskb = p->xmit(skb, dev); 96762306a36Sopenharmony_ci if (!nskb) { 96862306a36Sopenharmony_ci kfree_skb(skb); 96962306a36Sopenharmony_ci return NETDEV_TX_OK; 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci return dsa_enqueue_skb(nskb, dev); 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci/* ethtool operations *******************************************************/ 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cistatic void dsa_slave_get_drvinfo(struct net_device *dev, 97862306a36Sopenharmony_ci struct ethtool_drvinfo *drvinfo) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci strscpy(drvinfo->driver, "dsa", sizeof(drvinfo->driver)); 98162306a36Sopenharmony_ci strscpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); 98262306a36Sopenharmony_ci strscpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info)); 98362306a36Sopenharmony_ci} 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_cistatic int dsa_slave_get_regs_len(struct net_device *dev) 98662306a36Sopenharmony_ci{ 98762306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 98862306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (ds->ops->get_regs_len) 99162306a36Sopenharmony_ci return ds->ops->get_regs_len(ds, dp->index); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci return -EOPNOTSUPP; 99462306a36Sopenharmony_ci} 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_cistatic void 99762306a36Sopenharmony_cidsa_slave_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *_p) 99862306a36Sopenharmony_ci{ 99962306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 100062306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci if (ds->ops->get_regs) 100362306a36Sopenharmony_ci ds->ops->get_regs(ds, dp->index, regs, _p); 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic int dsa_slave_nway_reset(struct net_device *dev) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci return phylink_ethtool_nway_reset(dp->pl); 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistatic int dsa_slave_get_eeprom_len(struct net_device *dev) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 101662306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci if (ds->cd && ds->cd->eeprom_len) 101962306a36Sopenharmony_ci return ds->cd->eeprom_len; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (ds->ops->get_eeprom_len) 102262306a36Sopenharmony_ci return ds->ops->get_eeprom_len(ds); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci return 0; 102562306a36Sopenharmony_ci} 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic int dsa_slave_get_eeprom(struct net_device *dev, 102862306a36Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *data) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 103162306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci if (ds->ops->get_eeprom) 103462306a36Sopenharmony_ci return ds->ops->get_eeprom(ds, eeprom, data); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci return -EOPNOTSUPP; 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_cistatic int dsa_slave_set_eeprom(struct net_device *dev, 104062306a36Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *data) 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 104362306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci if (ds->ops->set_eeprom) 104662306a36Sopenharmony_ci return ds->ops->set_eeprom(ds, eeprom, data); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci return -EOPNOTSUPP; 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_cistatic void dsa_slave_get_strings(struct net_device *dev, 105262306a36Sopenharmony_ci uint32_t stringset, uint8_t *data) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 105562306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci if (stringset == ETH_SS_STATS) { 105862306a36Sopenharmony_ci int len = ETH_GSTRING_LEN; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci strscpy_pad(data, "tx_packets", len); 106162306a36Sopenharmony_ci strscpy_pad(data + len, "tx_bytes", len); 106262306a36Sopenharmony_ci strscpy_pad(data + 2 * len, "rx_packets", len); 106362306a36Sopenharmony_ci strscpy_pad(data + 3 * len, "rx_bytes", len); 106462306a36Sopenharmony_ci if (ds->ops->get_strings) 106562306a36Sopenharmony_ci ds->ops->get_strings(ds, dp->index, stringset, 106662306a36Sopenharmony_ci data + 4 * len); 106762306a36Sopenharmony_ci } else if (stringset == ETH_SS_TEST) { 106862306a36Sopenharmony_ci net_selftest_get_strings(data); 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci} 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_cistatic void dsa_slave_get_ethtool_stats(struct net_device *dev, 107462306a36Sopenharmony_ci struct ethtool_stats *stats, 107562306a36Sopenharmony_ci uint64_t *data) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 107862306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 107962306a36Sopenharmony_ci struct pcpu_sw_netstats *s; 108062306a36Sopenharmony_ci unsigned int start; 108162306a36Sopenharmony_ci int i; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci for_each_possible_cpu(i) { 108462306a36Sopenharmony_ci u64 tx_packets, tx_bytes, rx_packets, rx_bytes; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci s = per_cpu_ptr(dev->tstats, i); 108762306a36Sopenharmony_ci do { 108862306a36Sopenharmony_ci start = u64_stats_fetch_begin(&s->syncp); 108962306a36Sopenharmony_ci tx_packets = u64_stats_read(&s->tx_packets); 109062306a36Sopenharmony_ci tx_bytes = u64_stats_read(&s->tx_bytes); 109162306a36Sopenharmony_ci rx_packets = u64_stats_read(&s->rx_packets); 109262306a36Sopenharmony_ci rx_bytes = u64_stats_read(&s->rx_bytes); 109362306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&s->syncp, start)); 109462306a36Sopenharmony_ci data[0] += tx_packets; 109562306a36Sopenharmony_ci data[1] += tx_bytes; 109662306a36Sopenharmony_ci data[2] += rx_packets; 109762306a36Sopenharmony_ci data[3] += rx_bytes; 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci if (ds->ops->get_ethtool_stats) 110062306a36Sopenharmony_ci ds->ops->get_ethtool_stats(ds, dp->index, data + 4); 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_cistatic int dsa_slave_get_sset_count(struct net_device *dev, int sset) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 110662306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci if (sset == ETH_SS_STATS) { 110962306a36Sopenharmony_ci int count = 0; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci if (ds->ops->get_sset_count) { 111262306a36Sopenharmony_ci count = ds->ops->get_sset_count(ds, dp->index, sset); 111362306a36Sopenharmony_ci if (count < 0) 111462306a36Sopenharmony_ci return count; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci return count + 4; 111862306a36Sopenharmony_ci } else if (sset == ETH_SS_TEST) { 111962306a36Sopenharmony_ci return net_selftest_get_count(); 112062306a36Sopenharmony_ci } 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci return -EOPNOTSUPP; 112362306a36Sopenharmony_ci} 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_cistatic void dsa_slave_get_eth_phy_stats(struct net_device *dev, 112662306a36Sopenharmony_ci struct ethtool_eth_phy_stats *phy_stats) 112762306a36Sopenharmony_ci{ 112862306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 112962306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci if (ds->ops->get_eth_phy_stats) 113262306a36Sopenharmony_ci ds->ops->get_eth_phy_stats(ds, dp->index, phy_stats); 113362306a36Sopenharmony_ci} 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_cistatic void dsa_slave_get_eth_mac_stats(struct net_device *dev, 113662306a36Sopenharmony_ci struct ethtool_eth_mac_stats *mac_stats) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 113962306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci if (ds->ops->get_eth_mac_stats) 114262306a36Sopenharmony_ci ds->ops->get_eth_mac_stats(ds, dp->index, mac_stats); 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_cistatic void 114662306a36Sopenharmony_cidsa_slave_get_eth_ctrl_stats(struct net_device *dev, 114762306a36Sopenharmony_ci struct ethtool_eth_ctrl_stats *ctrl_stats) 114862306a36Sopenharmony_ci{ 114962306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 115062306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci if (ds->ops->get_eth_ctrl_stats) 115362306a36Sopenharmony_ci ds->ops->get_eth_ctrl_stats(ds, dp->index, ctrl_stats); 115462306a36Sopenharmony_ci} 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_cistatic void 115762306a36Sopenharmony_cidsa_slave_get_rmon_stats(struct net_device *dev, 115862306a36Sopenharmony_ci struct ethtool_rmon_stats *rmon_stats, 115962306a36Sopenharmony_ci const struct ethtool_rmon_hist_range **ranges) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 116262306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci if (ds->ops->get_rmon_stats) 116562306a36Sopenharmony_ci ds->ops->get_rmon_stats(ds, dp->index, rmon_stats, ranges); 116662306a36Sopenharmony_ci} 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_cistatic void dsa_slave_net_selftest(struct net_device *ndev, 116962306a36Sopenharmony_ci struct ethtool_test *etest, u64 *buf) 117062306a36Sopenharmony_ci{ 117162306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(ndev); 117262306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci if (ds->ops->self_test) { 117562306a36Sopenharmony_ci ds->ops->self_test(ds, dp->index, etest, buf); 117662306a36Sopenharmony_ci return; 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci net_selftest(ndev, etest, buf); 118062306a36Sopenharmony_ci} 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_cistatic int dsa_slave_get_mm(struct net_device *dev, 118362306a36Sopenharmony_ci struct ethtool_mm_state *state) 118462306a36Sopenharmony_ci{ 118562306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 118662306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci if (!ds->ops->get_mm) 118962306a36Sopenharmony_ci return -EOPNOTSUPP; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci return ds->ops->get_mm(ds, dp->index, state); 119262306a36Sopenharmony_ci} 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_cistatic int dsa_slave_set_mm(struct net_device *dev, struct ethtool_mm_cfg *cfg, 119562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 119862306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci if (!ds->ops->set_mm) 120162306a36Sopenharmony_ci return -EOPNOTSUPP; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci return ds->ops->set_mm(ds, dp->index, cfg, extack); 120462306a36Sopenharmony_ci} 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_cistatic void dsa_slave_get_mm_stats(struct net_device *dev, 120762306a36Sopenharmony_ci struct ethtool_mm_stats *stats) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 121062306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci if (ds->ops->get_mm_stats) 121362306a36Sopenharmony_ci ds->ops->get_mm_stats(ds, dp->index, stats); 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic void dsa_slave_get_wol(struct net_device *dev, struct ethtool_wolinfo *w) 121762306a36Sopenharmony_ci{ 121862306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 121962306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci phylink_ethtool_get_wol(dp->pl, w); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci if (ds->ops->get_wol) 122462306a36Sopenharmony_ci ds->ops->get_wol(ds, dp->index, w); 122562306a36Sopenharmony_ci} 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_cistatic int dsa_slave_set_wol(struct net_device *dev, struct ethtool_wolinfo *w) 122862306a36Sopenharmony_ci{ 122962306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 123062306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 123162306a36Sopenharmony_ci int ret = -EOPNOTSUPP; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci phylink_ethtool_set_wol(dp->pl, w); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci if (ds->ops->set_wol) 123662306a36Sopenharmony_ci ret = ds->ops->set_wol(ds, dp->index, w); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci return ret; 123962306a36Sopenharmony_ci} 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_cistatic int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e) 124262306a36Sopenharmony_ci{ 124362306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 124462306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 124562306a36Sopenharmony_ci int ret; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci /* Port's PHY and MAC both need to be EEE capable */ 124862306a36Sopenharmony_ci if (!dev->phydev || !dp->pl) 124962306a36Sopenharmony_ci return -ENODEV; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci if (!ds->ops->set_mac_eee) 125262306a36Sopenharmony_ci return -EOPNOTSUPP; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci ret = ds->ops->set_mac_eee(ds, dp->index, e); 125562306a36Sopenharmony_ci if (ret) 125662306a36Sopenharmony_ci return ret; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci return phylink_ethtool_set_eee(dp->pl, e); 125962306a36Sopenharmony_ci} 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_cistatic int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e) 126262306a36Sopenharmony_ci{ 126362306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 126462306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 126562306a36Sopenharmony_ci int ret; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci /* Port's PHY and MAC both need to be EEE capable */ 126862306a36Sopenharmony_ci if (!dev->phydev || !dp->pl) 126962306a36Sopenharmony_ci return -ENODEV; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci if (!ds->ops->get_mac_eee) 127262306a36Sopenharmony_ci return -EOPNOTSUPP; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci ret = ds->ops->get_mac_eee(ds, dp->index, e); 127562306a36Sopenharmony_ci if (ret) 127662306a36Sopenharmony_ci return ret; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci return phylink_ethtool_get_eee(dp->pl, e); 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_cistatic int dsa_slave_get_link_ksettings(struct net_device *dev, 128262306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 128362306a36Sopenharmony_ci{ 128462306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci return phylink_ethtool_ksettings_get(dp->pl, cmd); 128762306a36Sopenharmony_ci} 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_cistatic int dsa_slave_set_link_ksettings(struct net_device *dev, 129062306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 129162306a36Sopenharmony_ci{ 129262306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci return phylink_ethtool_ksettings_set(dp->pl, cmd); 129562306a36Sopenharmony_ci} 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_cistatic void dsa_slave_get_pause_stats(struct net_device *dev, 129862306a36Sopenharmony_ci struct ethtool_pause_stats *pause_stats) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 130162306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci if (ds->ops->get_pause_stats) 130462306a36Sopenharmony_ci ds->ops->get_pause_stats(ds, dp->index, pause_stats); 130562306a36Sopenharmony_ci} 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_cistatic void dsa_slave_get_pauseparam(struct net_device *dev, 130862306a36Sopenharmony_ci struct ethtool_pauseparam *pause) 130962306a36Sopenharmony_ci{ 131062306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci phylink_ethtool_get_pauseparam(dp->pl, pause); 131362306a36Sopenharmony_ci} 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_cistatic int dsa_slave_set_pauseparam(struct net_device *dev, 131662306a36Sopenharmony_ci struct ethtool_pauseparam *pause) 131762306a36Sopenharmony_ci{ 131862306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci return phylink_ethtool_set_pauseparam(dp->pl, pause); 132162306a36Sopenharmony_ci} 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 132462306a36Sopenharmony_cistatic int dsa_slave_netpoll_setup(struct net_device *dev, 132562306a36Sopenharmony_ci struct netpoll_info *ni) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci struct net_device *master = dsa_slave_to_master(dev); 132862306a36Sopenharmony_ci struct dsa_slave_priv *p = netdev_priv(dev); 132962306a36Sopenharmony_ci struct netpoll *netpoll; 133062306a36Sopenharmony_ci int err = 0; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL); 133362306a36Sopenharmony_ci if (!netpoll) 133462306a36Sopenharmony_ci return -ENOMEM; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci err = __netpoll_setup(netpoll, master); 133762306a36Sopenharmony_ci if (err) { 133862306a36Sopenharmony_ci kfree(netpoll); 133962306a36Sopenharmony_ci goto out; 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci p->netpoll = netpoll; 134362306a36Sopenharmony_ciout: 134462306a36Sopenharmony_ci return err; 134562306a36Sopenharmony_ci} 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_cistatic void dsa_slave_netpoll_cleanup(struct net_device *dev) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci struct dsa_slave_priv *p = netdev_priv(dev); 135062306a36Sopenharmony_ci struct netpoll *netpoll = p->netpoll; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci if (!netpoll) 135362306a36Sopenharmony_ci return; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci p->netpoll = NULL; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci __netpoll_free(netpoll); 135862306a36Sopenharmony_ci} 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_cistatic void dsa_slave_poll_controller(struct net_device *dev) 136162306a36Sopenharmony_ci{ 136262306a36Sopenharmony_ci} 136362306a36Sopenharmony_ci#endif 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_cistatic struct dsa_mall_tc_entry * 136662306a36Sopenharmony_cidsa_slave_mall_tc_entry_find(struct net_device *dev, unsigned long cookie) 136762306a36Sopenharmony_ci{ 136862306a36Sopenharmony_ci struct dsa_slave_priv *p = netdev_priv(dev); 136962306a36Sopenharmony_ci struct dsa_mall_tc_entry *mall_tc_entry; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci list_for_each_entry(mall_tc_entry, &p->mall_tc_list, list) 137262306a36Sopenharmony_ci if (mall_tc_entry->cookie == cookie) 137362306a36Sopenharmony_ci return mall_tc_entry; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci return NULL; 137662306a36Sopenharmony_ci} 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_cistatic int 137962306a36Sopenharmony_cidsa_slave_add_cls_matchall_mirred(struct net_device *dev, 138062306a36Sopenharmony_ci struct tc_cls_matchall_offload *cls, 138162306a36Sopenharmony_ci bool ingress) 138262306a36Sopenharmony_ci{ 138362306a36Sopenharmony_ci struct netlink_ext_ack *extack = cls->common.extack; 138462306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 138562306a36Sopenharmony_ci struct dsa_slave_priv *p = netdev_priv(dev); 138662306a36Sopenharmony_ci struct dsa_mall_mirror_tc_entry *mirror; 138762306a36Sopenharmony_ci struct dsa_mall_tc_entry *mall_tc_entry; 138862306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 138962306a36Sopenharmony_ci struct flow_action_entry *act; 139062306a36Sopenharmony_ci struct dsa_port *to_dp; 139162306a36Sopenharmony_ci int err; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci if (!ds->ops->port_mirror_add) 139462306a36Sopenharmony_ci return -EOPNOTSUPP; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci if (!flow_action_basic_hw_stats_check(&cls->rule->action, 139762306a36Sopenharmony_ci cls->common.extack)) 139862306a36Sopenharmony_ci return -EOPNOTSUPP; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci act = &cls->rule->action.entries[0]; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci if (!act->dev) 140362306a36Sopenharmony_ci return -EINVAL; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci if (!dsa_slave_dev_check(act->dev)) 140662306a36Sopenharmony_ci return -EOPNOTSUPP; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci mall_tc_entry = kzalloc(sizeof(*mall_tc_entry), GFP_KERNEL); 140962306a36Sopenharmony_ci if (!mall_tc_entry) 141062306a36Sopenharmony_ci return -ENOMEM; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci mall_tc_entry->cookie = cls->cookie; 141362306a36Sopenharmony_ci mall_tc_entry->type = DSA_PORT_MALL_MIRROR; 141462306a36Sopenharmony_ci mirror = &mall_tc_entry->mirror; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci to_dp = dsa_slave_to_port(act->dev); 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci mirror->to_local_port = to_dp->index; 141962306a36Sopenharmony_ci mirror->ingress = ingress; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci err = ds->ops->port_mirror_add(ds, dp->index, mirror, ingress, extack); 142262306a36Sopenharmony_ci if (err) { 142362306a36Sopenharmony_ci kfree(mall_tc_entry); 142462306a36Sopenharmony_ci return err; 142562306a36Sopenharmony_ci } 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci list_add_tail(&mall_tc_entry->list, &p->mall_tc_list); 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci return err; 143062306a36Sopenharmony_ci} 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_cistatic int 143362306a36Sopenharmony_cidsa_slave_add_cls_matchall_police(struct net_device *dev, 143462306a36Sopenharmony_ci struct tc_cls_matchall_offload *cls, 143562306a36Sopenharmony_ci bool ingress) 143662306a36Sopenharmony_ci{ 143762306a36Sopenharmony_ci struct netlink_ext_ack *extack = cls->common.extack; 143862306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 143962306a36Sopenharmony_ci struct dsa_slave_priv *p = netdev_priv(dev); 144062306a36Sopenharmony_ci struct dsa_mall_policer_tc_entry *policer; 144162306a36Sopenharmony_ci struct dsa_mall_tc_entry *mall_tc_entry; 144262306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 144362306a36Sopenharmony_ci struct flow_action_entry *act; 144462306a36Sopenharmony_ci int err; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci if (!ds->ops->port_policer_add) { 144762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 144862306a36Sopenharmony_ci "Policing offload not implemented"); 144962306a36Sopenharmony_ci return -EOPNOTSUPP; 145062306a36Sopenharmony_ci } 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci if (!ingress) { 145362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 145462306a36Sopenharmony_ci "Only supported on ingress qdisc"); 145562306a36Sopenharmony_ci return -EOPNOTSUPP; 145662306a36Sopenharmony_ci } 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci if (!flow_action_basic_hw_stats_check(&cls->rule->action, 145962306a36Sopenharmony_ci cls->common.extack)) 146062306a36Sopenharmony_ci return -EOPNOTSUPP; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci list_for_each_entry(mall_tc_entry, &p->mall_tc_list, list) { 146362306a36Sopenharmony_ci if (mall_tc_entry->type == DSA_PORT_MALL_POLICER) { 146462306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 146562306a36Sopenharmony_ci "Only one port policer allowed"); 146662306a36Sopenharmony_ci return -EEXIST; 146762306a36Sopenharmony_ci } 146862306a36Sopenharmony_ci } 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci act = &cls->rule->action.entries[0]; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci mall_tc_entry = kzalloc(sizeof(*mall_tc_entry), GFP_KERNEL); 147362306a36Sopenharmony_ci if (!mall_tc_entry) 147462306a36Sopenharmony_ci return -ENOMEM; 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci mall_tc_entry->cookie = cls->cookie; 147762306a36Sopenharmony_ci mall_tc_entry->type = DSA_PORT_MALL_POLICER; 147862306a36Sopenharmony_ci policer = &mall_tc_entry->policer; 147962306a36Sopenharmony_ci policer->rate_bytes_per_sec = act->police.rate_bytes_ps; 148062306a36Sopenharmony_ci policer->burst = act->police.burst; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci err = ds->ops->port_policer_add(ds, dp->index, policer); 148362306a36Sopenharmony_ci if (err) { 148462306a36Sopenharmony_ci kfree(mall_tc_entry); 148562306a36Sopenharmony_ci return err; 148662306a36Sopenharmony_ci } 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci list_add_tail(&mall_tc_entry->list, &p->mall_tc_list); 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci return err; 149162306a36Sopenharmony_ci} 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_cistatic int dsa_slave_add_cls_matchall(struct net_device *dev, 149462306a36Sopenharmony_ci struct tc_cls_matchall_offload *cls, 149562306a36Sopenharmony_ci bool ingress) 149662306a36Sopenharmony_ci{ 149762306a36Sopenharmony_ci int err = -EOPNOTSUPP; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci if (cls->common.protocol == htons(ETH_P_ALL) && 150062306a36Sopenharmony_ci flow_offload_has_one_action(&cls->rule->action) && 150162306a36Sopenharmony_ci cls->rule->action.entries[0].id == FLOW_ACTION_MIRRED) 150262306a36Sopenharmony_ci err = dsa_slave_add_cls_matchall_mirred(dev, cls, ingress); 150362306a36Sopenharmony_ci else if (flow_offload_has_one_action(&cls->rule->action) && 150462306a36Sopenharmony_ci cls->rule->action.entries[0].id == FLOW_ACTION_POLICE) 150562306a36Sopenharmony_ci err = dsa_slave_add_cls_matchall_police(dev, cls, ingress); 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci return err; 150862306a36Sopenharmony_ci} 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_cistatic void dsa_slave_del_cls_matchall(struct net_device *dev, 151162306a36Sopenharmony_ci struct tc_cls_matchall_offload *cls) 151262306a36Sopenharmony_ci{ 151362306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 151462306a36Sopenharmony_ci struct dsa_mall_tc_entry *mall_tc_entry; 151562306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci mall_tc_entry = dsa_slave_mall_tc_entry_find(dev, cls->cookie); 151862306a36Sopenharmony_ci if (!mall_tc_entry) 151962306a36Sopenharmony_ci return; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci list_del(&mall_tc_entry->list); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci switch (mall_tc_entry->type) { 152462306a36Sopenharmony_ci case DSA_PORT_MALL_MIRROR: 152562306a36Sopenharmony_ci if (ds->ops->port_mirror_del) 152662306a36Sopenharmony_ci ds->ops->port_mirror_del(ds, dp->index, 152762306a36Sopenharmony_ci &mall_tc_entry->mirror); 152862306a36Sopenharmony_ci break; 152962306a36Sopenharmony_ci case DSA_PORT_MALL_POLICER: 153062306a36Sopenharmony_ci if (ds->ops->port_policer_del) 153162306a36Sopenharmony_ci ds->ops->port_policer_del(ds, dp->index); 153262306a36Sopenharmony_ci break; 153362306a36Sopenharmony_ci default: 153462306a36Sopenharmony_ci WARN_ON(1); 153562306a36Sopenharmony_ci } 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci kfree(mall_tc_entry); 153862306a36Sopenharmony_ci} 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_cistatic int dsa_slave_setup_tc_cls_matchall(struct net_device *dev, 154162306a36Sopenharmony_ci struct tc_cls_matchall_offload *cls, 154262306a36Sopenharmony_ci bool ingress) 154362306a36Sopenharmony_ci{ 154462306a36Sopenharmony_ci if (cls->common.chain_index) 154562306a36Sopenharmony_ci return -EOPNOTSUPP; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci switch (cls->command) { 154862306a36Sopenharmony_ci case TC_CLSMATCHALL_REPLACE: 154962306a36Sopenharmony_ci return dsa_slave_add_cls_matchall(dev, cls, ingress); 155062306a36Sopenharmony_ci case TC_CLSMATCHALL_DESTROY: 155162306a36Sopenharmony_ci dsa_slave_del_cls_matchall(dev, cls); 155262306a36Sopenharmony_ci return 0; 155362306a36Sopenharmony_ci default: 155462306a36Sopenharmony_ci return -EOPNOTSUPP; 155562306a36Sopenharmony_ci } 155662306a36Sopenharmony_ci} 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_cistatic int dsa_slave_add_cls_flower(struct net_device *dev, 155962306a36Sopenharmony_ci struct flow_cls_offload *cls, 156062306a36Sopenharmony_ci bool ingress) 156162306a36Sopenharmony_ci{ 156262306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 156362306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 156462306a36Sopenharmony_ci int port = dp->index; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci if (!ds->ops->cls_flower_add) 156762306a36Sopenharmony_ci return -EOPNOTSUPP; 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci return ds->ops->cls_flower_add(ds, port, cls, ingress); 157062306a36Sopenharmony_ci} 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_cistatic int dsa_slave_del_cls_flower(struct net_device *dev, 157362306a36Sopenharmony_ci struct flow_cls_offload *cls, 157462306a36Sopenharmony_ci bool ingress) 157562306a36Sopenharmony_ci{ 157662306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 157762306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 157862306a36Sopenharmony_ci int port = dp->index; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci if (!ds->ops->cls_flower_del) 158162306a36Sopenharmony_ci return -EOPNOTSUPP; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci return ds->ops->cls_flower_del(ds, port, cls, ingress); 158462306a36Sopenharmony_ci} 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_cistatic int dsa_slave_stats_cls_flower(struct net_device *dev, 158762306a36Sopenharmony_ci struct flow_cls_offload *cls, 158862306a36Sopenharmony_ci bool ingress) 158962306a36Sopenharmony_ci{ 159062306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 159162306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 159262306a36Sopenharmony_ci int port = dp->index; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if (!ds->ops->cls_flower_stats) 159562306a36Sopenharmony_ci return -EOPNOTSUPP; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci return ds->ops->cls_flower_stats(ds, port, cls, ingress); 159862306a36Sopenharmony_ci} 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_cistatic int dsa_slave_setup_tc_cls_flower(struct net_device *dev, 160162306a36Sopenharmony_ci struct flow_cls_offload *cls, 160262306a36Sopenharmony_ci bool ingress) 160362306a36Sopenharmony_ci{ 160462306a36Sopenharmony_ci switch (cls->command) { 160562306a36Sopenharmony_ci case FLOW_CLS_REPLACE: 160662306a36Sopenharmony_ci return dsa_slave_add_cls_flower(dev, cls, ingress); 160762306a36Sopenharmony_ci case FLOW_CLS_DESTROY: 160862306a36Sopenharmony_ci return dsa_slave_del_cls_flower(dev, cls, ingress); 160962306a36Sopenharmony_ci case FLOW_CLS_STATS: 161062306a36Sopenharmony_ci return dsa_slave_stats_cls_flower(dev, cls, ingress); 161162306a36Sopenharmony_ci default: 161262306a36Sopenharmony_ci return -EOPNOTSUPP; 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci} 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_cistatic int dsa_slave_setup_tc_block_cb(enum tc_setup_type type, void *type_data, 161762306a36Sopenharmony_ci void *cb_priv, bool ingress) 161862306a36Sopenharmony_ci{ 161962306a36Sopenharmony_ci struct net_device *dev = cb_priv; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci if (!tc_can_offload(dev)) 162262306a36Sopenharmony_ci return -EOPNOTSUPP; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci switch (type) { 162562306a36Sopenharmony_ci case TC_SETUP_CLSMATCHALL: 162662306a36Sopenharmony_ci return dsa_slave_setup_tc_cls_matchall(dev, type_data, ingress); 162762306a36Sopenharmony_ci case TC_SETUP_CLSFLOWER: 162862306a36Sopenharmony_ci return dsa_slave_setup_tc_cls_flower(dev, type_data, ingress); 162962306a36Sopenharmony_ci default: 163062306a36Sopenharmony_ci return -EOPNOTSUPP; 163162306a36Sopenharmony_ci } 163262306a36Sopenharmony_ci} 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_cistatic int dsa_slave_setup_tc_block_cb_ig(enum tc_setup_type type, 163562306a36Sopenharmony_ci void *type_data, void *cb_priv) 163662306a36Sopenharmony_ci{ 163762306a36Sopenharmony_ci return dsa_slave_setup_tc_block_cb(type, type_data, cb_priv, true); 163862306a36Sopenharmony_ci} 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_cistatic int dsa_slave_setup_tc_block_cb_eg(enum tc_setup_type type, 164162306a36Sopenharmony_ci void *type_data, void *cb_priv) 164262306a36Sopenharmony_ci{ 164362306a36Sopenharmony_ci return dsa_slave_setup_tc_block_cb(type, type_data, cb_priv, false); 164462306a36Sopenharmony_ci} 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_cistatic LIST_HEAD(dsa_slave_block_cb_list); 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_cistatic int dsa_slave_setup_tc_block(struct net_device *dev, 164962306a36Sopenharmony_ci struct flow_block_offload *f) 165062306a36Sopenharmony_ci{ 165162306a36Sopenharmony_ci struct flow_block_cb *block_cb; 165262306a36Sopenharmony_ci flow_setup_cb_t *cb; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) 165562306a36Sopenharmony_ci cb = dsa_slave_setup_tc_block_cb_ig; 165662306a36Sopenharmony_ci else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) 165762306a36Sopenharmony_ci cb = dsa_slave_setup_tc_block_cb_eg; 165862306a36Sopenharmony_ci else 165962306a36Sopenharmony_ci return -EOPNOTSUPP; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci f->driver_block_list = &dsa_slave_block_cb_list; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci switch (f->command) { 166462306a36Sopenharmony_ci case FLOW_BLOCK_BIND: 166562306a36Sopenharmony_ci if (flow_block_cb_is_busy(cb, dev, &dsa_slave_block_cb_list)) 166662306a36Sopenharmony_ci return -EBUSY; 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci block_cb = flow_block_cb_alloc(cb, dev, dev, NULL); 166962306a36Sopenharmony_ci if (IS_ERR(block_cb)) 167062306a36Sopenharmony_ci return PTR_ERR(block_cb); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci flow_block_cb_add(block_cb, f); 167362306a36Sopenharmony_ci list_add_tail(&block_cb->driver_list, &dsa_slave_block_cb_list); 167462306a36Sopenharmony_ci return 0; 167562306a36Sopenharmony_ci case FLOW_BLOCK_UNBIND: 167662306a36Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, cb, dev); 167762306a36Sopenharmony_ci if (!block_cb) 167862306a36Sopenharmony_ci return -ENOENT; 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci flow_block_cb_remove(block_cb, f); 168162306a36Sopenharmony_ci list_del(&block_cb->driver_list); 168262306a36Sopenharmony_ci return 0; 168362306a36Sopenharmony_ci default: 168462306a36Sopenharmony_ci return -EOPNOTSUPP; 168562306a36Sopenharmony_ci } 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_cistatic int dsa_slave_setup_ft_block(struct dsa_switch *ds, int port, 168962306a36Sopenharmony_ci void *type_data) 169062306a36Sopenharmony_ci{ 169162306a36Sopenharmony_ci struct net_device *master = dsa_port_to_master(dsa_to_port(ds, port)); 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci if (!master->netdev_ops->ndo_setup_tc) 169462306a36Sopenharmony_ci return -EOPNOTSUPP; 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci return master->netdev_ops->ndo_setup_tc(master, TC_SETUP_FT, type_data); 169762306a36Sopenharmony_ci} 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_cistatic int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type, 170062306a36Sopenharmony_ci void *type_data) 170162306a36Sopenharmony_ci{ 170262306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 170362306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci switch (type) { 170662306a36Sopenharmony_ci case TC_SETUP_BLOCK: 170762306a36Sopenharmony_ci return dsa_slave_setup_tc_block(dev, type_data); 170862306a36Sopenharmony_ci case TC_SETUP_FT: 170962306a36Sopenharmony_ci return dsa_slave_setup_ft_block(ds, dp->index, type_data); 171062306a36Sopenharmony_ci default: 171162306a36Sopenharmony_ci break; 171262306a36Sopenharmony_ci } 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci if (!ds->ops->port_setup_tc) 171562306a36Sopenharmony_ci return -EOPNOTSUPP; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci return ds->ops->port_setup_tc(ds, dp->index, type, type_data); 171862306a36Sopenharmony_ci} 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_cistatic int dsa_slave_get_rxnfc(struct net_device *dev, 172162306a36Sopenharmony_ci struct ethtool_rxnfc *nfc, u32 *rule_locs) 172262306a36Sopenharmony_ci{ 172362306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 172462306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci if (!ds->ops->get_rxnfc) 172762306a36Sopenharmony_ci return -EOPNOTSUPP; 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci return ds->ops->get_rxnfc(ds, dp->index, nfc, rule_locs); 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_cistatic int dsa_slave_set_rxnfc(struct net_device *dev, 173362306a36Sopenharmony_ci struct ethtool_rxnfc *nfc) 173462306a36Sopenharmony_ci{ 173562306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 173662306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci if (!ds->ops->set_rxnfc) 173962306a36Sopenharmony_ci return -EOPNOTSUPP; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci return ds->ops->set_rxnfc(ds, dp->index, nfc); 174262306a36Sopenharmony_ci} 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_cistatic int dsa_slave_get_ts_info(struct net_device *dev, 174562306a36Sopenharmony_ci struct ethtool_ts_info *ts) 174662306a36Sopenharmony_ci{ 174762306a36Sopenharmony_ci struct dsa_slave_priv *p = netdev_priv(dev); 174862306a36Sopenharmony_ci struct dsa_switch *ds = p->dp->ds; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci if (!ds->ops->get_ts_info) 175162306a36Sopenharmony_ci return -EOPNOTSUPP; 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci return ds->ops->get_ts_info(ds, p->dp->index, ts); 175462306a36Sopenharmony_ci} 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_cistatic int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto, 175762306a36Sopenharmony_ci u16 vid) 175862306a36Sopenharmony_ci{ 175962306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 176062306a36Sopenharmony_ci struct switchdev_obj_port_vlan vlan = { 176162306a36Sopenharmony_ci .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 176262306a36Sopenharmony_ci .vid = vid, 176362306a36Sopenharmony_ci /* This API only allows programming tagged, non-PVID VIDs */ 176462306a36Sopenharmony_ci .flags = 0, 176562306a36Sopenharmony_ci }; 176662306a36Sopenharmony_ci struct netlink_ext_ack extack = {0}; 176762306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 176862306a36Sopenharmony_ci struct netdev_hw_addr *ha; 176962306a36Sopenharmony_ci struct dsa_vlan *v; 177062306a36Sopenharmony_ci int ret; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci /* User port... */ 177362306a36Sopenharmony_ci ret = dsa_port_vlan_add(dp, &vlan, &extack); 177462306a36Sopenharmony_ci if (ret) { 177562306a36Sopenharmony_ci if (extack._msg) 177662306a36Sopenharmony_ci netdev_err(dev, "%s\n", extack._msg); 177762306a36Sopenharmony_ci return ret; 177862306a36Sopenharmony_ci } 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci /* And CPU port... */ 178162306a36Sopenharmony_ci ret = dsa_port_host_vlan_add(dp, &vlan, &extack); 178262306a36Sopenharmony_ci if (ret) { 178362306a36Sopenharmony_ci if (extack._msg) 178462306a36Sopenharmony_ci netdev_err(dev, "CPU port %d: %s\n", dp->cpu_dp->index, 178562306a36Sopenharmony_ci extack._msg); 178662306a36Sopenharmony_ci return ret; 178762306a36Sopenharmony_ci } 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci if (!dsa_switch_supports_uc_filtering(ds) && 179062306a36Sopenharmony_ci !dsa_switch_supports_mc_filtering(ds)) 179162306a36Sopenharmony_ci return 0; 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci v = kzalloc(sizeof(*v), GFP_KERNEL); 179462306a36Sopenharmony_ci if (!v) { 179562306a36Sopenharmony_ci ret = -ENOMEM; 179662306a36Sopenharmony_ci goto rollback; 179762306a36Sopenharmony_ci } 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci netif_addr_lock_bh(dev); 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci v->vid = vid; 180262306a36Sopenharmony_ci list_add_tail(&v->list, &dp->user_vlans); 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci if (dsa_switch_supports_mc_filtering(ds)) { 180562306a36Sopenharmony_ci netdev_for_each_synced_mc_addr(ha, dev) { 180662306a36Sopenharmony_ci dsa_slave_schedule_standalone_work(dev, DSA_MC_ADD, 180762306a36Sopenharmony_ci ha->addr, vid); 180862306a36Sopenharmony_ci } 180962306a36Sopenharmony_ci } 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci if (dsa_switch_supports_uc_filtering(ds)) { 181262306a36Sopenharmony_ci netdev_for_each_synced_uc_addr(ha, dev) { 181362306a36Sopenharmony_ci dsa_slave_schedule_standalone_work(dev, DSA_UC_ADD, 181462306a36Sopenharmony_ci ha->addr, vid); 181562306a36Sopenharmony_ci } 181662306a36Sopenharmony_ci } 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci netif_addr_unlock_bh(dev); 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci dsa_flush_workqueue(); 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci return 0; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_cirollback: 182562306a36Sopenharmony_ci dsa_port_host_vlan_del(dp, &vlan); 182662306a36Sopenharmony_ci dsa_port_vlan_del(dp, &vlan); 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci return ret; 182962306a36Sopenharmony_ci} 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_cistatic int dsa_slave_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, 183262306a36Sopenharmony_ci u16 vid) 183362306a36Sopenharmony_ci{ 183462306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 183562306a36Sopenharmony_ci struct switchdev_obj_port_vlan vlan = { 183662306a36Sopenharmony_ci .vid = vid, 183762306a36Sopenharmony_ci /* This API only allows programming tagged, non-PVID VIDs */ 183862306a36Sopenharmony_ci .flags = 0, 183962306a36Sopenharmony_ci }; 184062306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 184162306a36Sopenharmony_ci struct netdev_hw_addr *ha; 184262306a36Sopenharmony_ci struct dsa_vlan *v; 184362306a36Sopenharmony_ci int err; 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci err = dsa_port_vlan_del(dp, &vlan); 184662306a36Sopenharmony_ci if (err) 184762306a36Sopenharmony_ci return err; 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci err = dsa_port_host_vlan_del(dp, &vlan); 185062306a36Sopenharmony_ci if (err) 185162306a36Sopenharmony_ci return err; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci if (!dsa_switch_supports_uc_filtering(ds) && 185462306a36Sopenharmony_ci !dsa_switch_supports_mc_filtering(ds)) 185562306a36Sopenharmony_ci return 0; 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci netif_addr_lock_bh(dev); 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci v = dsa_vlan_find(&dp->user_vlans, &vlan); 186062306a36Sopenharmony_ci if (!v) { 186162306a36Sopenharmony_ci netif_addr_unlock_bh(dev); 186262306a36Sopenharmony_ci return -ENOENT; 186362306a36Sopenharmony_ci } 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci list_del(&v->list); 186662306a36Sopenharmony_ci kfree(v); 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci if (dsa_switch_supports_mc_filtering(ds)) { 186962306a36Sopenharmony_ci netdev_for_each_synced_mc_addr(ha, dev) { 187062306a36Sopenharmony_ci dsa_slave_schedule_standalone_work(dev, DSA_MC_DEL, 187162306a36Sopenharmony_ci ha->addr, vid); 187262306a36Sopenharmony_ci } 187362306a36Sopenharmony_ci } 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci if (dsa_switch_supports_uc_filtering(ds)) { 187662306a36Sopenharmony_ci netdev_for_each_synced_uc_addr(ha, dev) { 187762306a36Sopenharmony_ci dsa_slave_schedule_standalone_work(dev, DSA_UC_DEL, 187862306a36Sopenharmony_ci ha->addr, vid); 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci } 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci netif_addr_unlock_bh(dev); 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci dsa_flush_workqueue(); 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci return 0; 188762306a36Sopenharmony_ci} 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_cistatic int dsa_slave_restore_vlan(struct net_device *vdev, int vid, void *arg) 189062306a36Sopenharmony_ci{ 189162306a36Sopenharmony_ci __be16 proto = vdev ? vlan_dev_vlan_proto(vdev) : htons(ETH_P_8021Q); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci return dsa_slave_vlan_rx_add_vid(arg, proto, vid); 189462306a36Sopenharmony_ci} 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_cistatic int dsa_slave_clear_vlan(struct net_device *vdev, int vid, void *arg) 189762306a36Sopenharmony_ci{ 189862306a36Sopenharmony_ci __be16 proto = vdev ? vlan_dev_vlan_proto(vdev) : htons(ETH_P_8021Q); 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci return dsa_slave_vlan_rx_kill_vid(arg, proto, vid); 190162306a36Sopenharmony_ci} 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci/* Keep the VLAN RX filtering list in sync with the hardware only if VLAN 190462306a36Sopenharmony_ci * filtering is enabled. The baseline is that only ports that offload a 190562306a36Sopenharmony_ci * VLAN-aware bridge are VLAN-aware, and standalone ports are VLAN-unaware, 190662306a36Sopenharmony_ci * but there are exceptions for quirky hardware. 190762306a36Sopenharmony_ci * 190862306a36Sopenharmony_ci * If ds->vlan_filtering_is_global = true, then standalone ports which share 190962306a36Sopenharmony_ci * the same switch with other ports that offload a VLAN-aware bridge are also 191062306a36Sopenharmony_ci * inevitably VLAN-aware. 191162306a36Sopenharmony_ci * 191262306a36Sopenharmony_ci * To summarize, a DSA switch port offloads: 191362306a36Sopenharmony_ci * 191462306a36Sopenharmony_ci * - If standalone (this includes software bridge, software LAG): 191562306a36Sopenharmony_ci * - if ds->needs_standalone_vlan_filtering = true, OR if 191662306a36Sopenharmony_ci * (ds->vlan_filtering_is_global = true AND there are bridges spanning 191762306a36Sopenharmony_ci * this switch chip which have vlan_filtering=1) 191862306a36Sopenharmony_ci * - the 8021q upper VLANs 191962306a36Sopenharmony_ci * - else (standalone VLAN filtering is not needed, VLAN filtering is not 192062306a36Sopenharmony_ci * global, or it is, but no port is under a VLAN-aware bridge): 192162306a36Sopenharmony_ci * - no VLAN (any 8021q upper is a software VLAN) 192262306a36Sopenharmony_ci * 192362306a36Sopenharmony_ci * - If under a vlan_filtering=0 bridge which it offload: 192462306a36Sopenharmony_ci * - if ds->configure_vlan_while_not_filtering = true (default): 192562306a36Sopenharmony_ci * - the bridge VLANs. These VLANs are committed to hardware but inactive. 192662306a36Sopenharmony_ci * - else (deprecated): 192762306a36Sopenharmony_ci * - no VLAN. The bridge VLANs are not restored when VLAN awareness is 192862306a36Sopenharmony_ci * enabled, so this behavior is broken and discouraged. 192962306a36Sopenharmony_ci * 193062306a36Sopenharmony_ci * - If under a vlan_filtering=1 bridge which it offload: 193162306a36Sopenharmony_ci * - the bridge VLANs 193262306a36Sopenharmony_ci * - the 8021q upper VLANs 193362306a36Sopenharmony_ci */ 193462306a36Sopenharmony_ciint dsa_slave_manage_vlan_filtering(struct net_device *slave, 193562306a36Sopenharmony_ci bool vlan_filtering) 193662306a36Sopenharmony_ci{ 193762306a36Sopenharmony_ci int err; 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci if (vlan_filtering) { 194062306a36Sopenharmony_ci slave->features |= NETIF_F_HW_VLAN_CTAG_FILTER; 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci err = vlan_for_each(slave, dsa_slave_restore_vlan, slave); 194362306a36Sopenharmony_ci if (err) { 194462306a36Sopenharmony_ci vlan_for_each(slave, dsa_slave_clear_vlan, slave); 194562306a36Sopenharmony_ci slave->features &= ~NETIF_F_HW_VLAN_CTAG_FILTER; 194662306a36Sopenharmony_ci return err; 194762306a36Sopenharmony_ci } 194862306a36Sopenharmony_ci } else { 194962306a36Sopenharmony_ci err = vlan_for_each(slave, dsa_slave_clear_vlan, slave); 195062306a36Sopenharmony_ci if (err) 195162306a36Sopenharmony_ci return err; 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci slave->features &= ~NETIF_F_HW_VLAN_CTAG_FILTER; 195462306a36Sopenharmony_ci } 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci return 0; 195762306a36Sopenharmony_ci} 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_cistruct dsa_hw_port { 196062306a36Sopenharmony_ci struct list_head list; 196162306a36Sopenharmony_ci struct net_device *dev; 196262306a36Sopenharmony_ci int old_mtu; 196362306a36Sopenharmony_ci}; 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_cistatic int dsa_hw_port_list_set_mtu(struct list_head *hw_port_list, int mtu) 196662306a36Sopenharmony_ci{ 196762306a36Sopenharmony_ci const struct dsa_hw_port *p; 196862306a36Sopenharmony_ci int err; 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci list_for_each_entry(p, hw_port_list, list) { 197162306a36Sopenharmony_ci if (p->dev->mtu == mtu) 197262306a36Sopenharmony_ci continue; 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci err = dev_set_mtu(p->dev, mtu); 197562306a36Sopenharmony_ci if (err) 197662306a36Sopenharmony_ci goto rollback; 197762306a36Sopenharmony_ci } 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci return 0; 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_cirollback: 198262306a36Sopenharmony_ci list_for_each_entry_continue_reverse(p, hw_port_list, list) { 198362306a36Sopenharmony_ci if (p->dev->mtu == p->old_mtu) 198462306a36Sopenharmony_ci continue; 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci if (dev_set_mtu(p->dev, p->old_mtu)) 198762306a36Sopenharmony_ci netdev_err(p->dev, "Failed to restore MTU\n"); 198862306a36Sopenharmony_ci } 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci return err; 199162306a36Sopenharmony_ci} 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_cistatic void dsa_hw_port_list_free(struct list_head *hw_port_list) 199462306a36Sopenharmony_ci{ 199562306a36Sopenharmony_ci struct dsa_hw_port *p, *n; 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci list_for_each_entry_safe(p, n, hw_port_list, list) 199862306a36Sopenharmony_ci kfree(p); 199962306a36Sopenharmony_ci} 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci/* Make the hardware datapath to/from @dev limited to a common MTU */ 200262306a36Sopenharmony_cistatic void dsa_bridge_mtu_normalization(struct dsa_port *dp) 200362306a36Sopenharmony_ci{ 200462306a36Sopenharmony_ci struct list_head hw_port_list; 200562306a36Sopenharmony_ci struct dsa_switch_tree *dst; 200662306a36Sopenharmony_ci int min_mtu = ETH_MAX_MTU; 200762306a36Sopenharmony_ci struct dsa_port *other_dp; 200862306a36Sopenharmony_ci int err; 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci if (!dp->ds->mtu_enforcement_ingress) 201162306a36Sopenharmony_ci return; 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci if (!dp->bridge) 201462306a36Sopenharmony_ci return; 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci INIT_LIST_HEAD(&hw_port_list); 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci /* Populate the list of ports that are part of the same bridge 201962306a36Sopenharmony_ci * as the newly added/modified port 202062306a36Sopenharmony_ci */ 202162306a36Sopenharmony_ci list_for_each_entry(dst, &dsa_tree_list, list) { 202262306a36Sopenharmony_ci list_for_each_entry(other_dp, &dst->ports, list) { 202362306a36Sopenharmony_ci struct dsa_hw_port *hw_port; 202462306a36Sopenharmony_ci struct net_device *slave; 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci if (other_dp->type != DSA_PORT_TYPE_USER) 202762306a36Sopenharmony_ci continue; 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci if (!dsa_port_bridge_same(dp, other_dp)) 203062306a36Sopenharmony_ci continue; 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci if (!other_dp->ds->mtu_enforcement_ingress) 203362306a36Sopenharmony_ci continue; 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci slave = other_dp->slave; 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci if (min_mtu > slave->mtu) 203862306a36Sopenharmony_ci min_mtu = slave->mtu; 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci hw_port = kzalloc(sizeof(*hw_port), GFP_KERNEL); 204162306a36Sopenharmony_ci if (!hw_port) 204262306a36Sopenharmony_ci goto out; 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci hw_port->dev = slave; 204562306a36Sopenharmony_ci hw_port->old_mtu = slave->mtu; 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci list_add(&hw_port->list, &hw_port_list); 204862306a36Sopenharmony_ci } 204962306a36Sopenharmony_ci } 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci /* Attempt to configure the entire hardware bridge to the newly added 205262306a36Sopenharmony_ci * interface's MTU first, regardless of whether the intention of the 205362306a36Sopenharmony_ci * user was to raise or lower it. 205462306a36Sopenharmony_ci */ 205562306a36Sopenharmony_ci err = dsa_hw_port_list_set_mtu(&hw_port_list, dp->slave->mtu); 205662306a36Sopenharmony_ci if (!err) 205762306a36Sopenharmony_ci goto out; 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci /* Clearly that didn't work out so well, so just set the minimum MTU on 206062306a36Sopenharmony_ci * all hardware bridge ports now. If this fails too, then all ports will 206162306a36Sopenharmony_ci * still have their old MTU rolled back anyway. 206262306a36Sopenharmony_ci */ 206362306a36Sopenharmony_ci dsa_hw_port_list_set_mtu(&hw_port_list, min_mtu); 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ciout: 206662306a36Sopenharmony_ci dsa_hw_port_list_free(&hw_port_list); 206762306a36Sopenharmony_ci} 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ciint dsa_slave_change_mtu(struct net_device *dev, int new_mtu) 207062306a36Sopenharmony_ci{ 207162306a36Sopenharmony_ci struct net_device *master = dsa_slave_to_master(dev); 207262306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 207362306a36Sopenharmony_ci struct dsa_port *cpu_dp = dp->cpu_dp; 207462306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 207562306a36Sopenharmony_ci struct dsa_port *other_dp; 207662306a36Sopenharmony_ci int largest_mtu = 0; 207762306a36Sopenharmony_ci int new_master_mtu; 207862306a36Sopenharmony_ci int old_master_mtu; 207962306a36Sopenharmony_ci int mtu_limit; 208062306a36Sopenharmony_ci int overhead; 208162306a36Sopenharmony_ci int cpu_mtu; 208262306a36Sopenharmony_ci int err; 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci if (!ds->ops->port_change_mtu) 208562306a36Sopenharmony_ci return -EOPNOTSUPP; 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci dsa_tree_for_each_user_port(other_dp, ds->dst) { 208862306a36Sopenharmony_ci int slave_mtu; 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci /* During probe, this function will be called for each slave 209162306a36Sopenharmony_ci * device, while not all of them have been allocated. That's 209262306a36Sopenharmony_ci * ok, it doesn't change what the maximum is, so ignore it. 209362306a36Sopenharmony_ci */ 209462306a36Sopenharmony_ci if (!other_dp->slave) 209562306a36Sopenharmony_ci continue; 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci /* Pretend that we already applied the setting, which we 209862306a36Sopenharmony_ci * actually haven't (still haven't done all integrity checks) 209962306a36Sopenharmony_ci */ 210062306a36Sopenharmony_ci if (dp == other_dp) 210162306a36Sopenharmony_ci slave_mtu = new_mtu; 210262306a36Sopenharmony_ci else 210362306a36Sopenharmony_ci slave_mtu = other_dp->slave->mtu; 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci if (largest_mtu < slave_mtu) 210662306a36Sopenharmony_ci largest_mtu = slave_mtu; 210762306a36Sopenharmony_ci } 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci overhead = dsa_tag_protocol_overhead(cpu_dp->tag_ops); 211062306a36Sopenharmony_ci mtu_limit = min_t(int, master->max_mtu, dev->max_mtu + overhead); 211162306a36Sopenharmony_ci old_master_mtu = master->mtu; 211262306a36Sopenharmony_ci new_master_mtu = largest_mtu + overhead; 211362306a36Sopenharmony_ci if (new_master_mtu > mtu_limit) 211462306a36Sopenharmony_ci return -ERANGE; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci /* If the master MTU isn't over limit, there's no need to check the CPU 211762306a36Sopenharmony_ci * MTU, since that surely isn't either. 211862306a36Sopenharmony_ci */ 211962306a36Sopenharmony_ci cpu_mtu = largest_mtu; 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci /* Start applying stuff */ 212262306a36Sopenharmony_ci if (new_master_mtu != old_master_mtu) { 212362306a36Sopenharmony_ci err = dev_set_mtu(master, new_master_mtu); 212462306a36Sopenharmony_ci if (err < 0) 212562306a36Sopenharmony_ci goto out_master_failed; 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci /* We only need to propagate the MTU of the CPU port to 212862306a36Sopenharmony_ci * upstream switches, so emit a notifier which updates them. 212962306a36Sopenharmony_ci */ 213062306a36Sopenharmony_ci err = dsa_port_mtu_change(cpu_dp, cpu_mtu); 213162306a36Sopenharmony_ci if (err) 213262306a36Sopenharmony_ci goto out_cpu_failed; 213362306a36Sopenharmony_ci } 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci err = ds->ops->port_change_mtu(ds, dp->index, new_mtu); 213662306a36Sopenharmony_ci if (err) 213762306a36Sopenharmony_ci goto out_port_failed; 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci dev->mtu = new_mtu; 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci dsa_bridge_mtu_normalization(dp); 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci return 0; 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ciout_port_failed: 214662306a36Sopenharmony_ci if (new_master_mtu != old_master_mtu) 214762306a36Sopenharmony_ci dsa_port_mtu_change(cpu_dp, old_master_mtu - overhead); 214862306a36Sopenharmony_ciout_cpu_failed: 214962306a36Sopenharmony_ci if (new_master_mtu != old_master_mtu) 215062306a36Sopenharmony_ci dev_set_mtu(master, old_master_mtu); 215162306a36Sopenharmony_ciout_master_failed: 215262306a36Sopenharmony_ci return err; 215362306a36Sopenharmony_ci} 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_cistatic int __maybe_unused 215662306a36Sopenharmony_cidsa_slave_dcbnl_set_default_prio(struct net_device *dev, struct dcb_app *app) 215762306a36Sopenharmony_ci{ 215862306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 215962306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 216062306a36Sopenharmony_ci unsigned long mask, new_prio; 216162306a36Sopenharmony_ci int err, port = dp->index; 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci if (!ds->ops->port_set_default_prio) 216462306a36Sopenharmony_ci return -EOPNOTSUPP; 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci err = dcb_ieee_setapp(dev, app); 216762306a36Sopenharmony_ci if (err) 216862306a36Sopenharmony_ci return err; 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci mask = dcb_ieee_getapp_mask(dev, app); 217162306a36Sopenharmony_ci new_prio = __fls(mask); 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci err = ds->ops->port_set_default_prio(ds, port, new_prio); 217462306a36Sopenharmony_ci if (err) { 217562306a36Sopenharmony_ci dcb_ieee_delapp(dev, app); 217662306a36Sopenharmony_ci return err; 217762306a36Sopenharmony_ci } 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci return 0; 218062306a36Sopenharmony_ci} 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_cistatic int __maybe_unused 218362306a36Sopenharmony_cidsa_slave_dcbnl_add_dscp_prio(struct net_device *dev, struct dcb_app *app) 218462306a36Sopenharmony_ci{ 218562306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 218662306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 218762306a36Sopenharmony_ci unsigned long mask, new_prio; 218862306a36Sopenharmony_ci int err, port = dp->index; 218962306a36Sopenharmony_ci u8 dscp = app->protocol; 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci if (!ds->ops->port_add_dscp_prio) 219262306a36Sopenharmony_ci return -EOPNOTSUPP; 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci if (dscp >= 64) { 219562306a36Sopenharmony_ci netdev_err(dev, "DSCP APP entry with protocol value %u is invalid\n", 219662306a36Sopenharmony_ci dscp); 219762306a36Sopenharmony_ci return -EINVAL; 219862306a36Sopenharmony_ci } 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci err = dcb_ieee_setapp(dev, app); 220162306a36Sopenharmony_ci if (err) 220262306a36Sopenharmony_ci return err; 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci mask = dcb_ieee_getapp_mask(dev, app); 220562306a36Sopenharmony_ci new_prio = __fls(mask); 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci err = ds->ops->port_add_dscp_prio(ds, port, dscp, new_prio); 220862306a36Sopenharmony_ci if (err) { 220962306a36Sopenharmony_ci dcb_ieee_delapp(dev, app); 221062306a36Sopenharmony_ci return err; 221162306a36Sopenharmony_ci } 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci return 0; 221462306a36Sopenharmony_ci} 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_cistatic int __maybe_unused dsa_slave_dcbnl_ieee_setapp(struct net_device *dev, 221762306a36Sopenharmony_ci struct dcb_app *app) 221862306a36Sopenharmony_ci{ 221962306a36Sopenharmony_ci switch (app->selector) { 222062306a36Sopenharmony_ci case IEEE_8021QAZ_APP_SEL_ETHERTYPE: 222162306a36Sopenharmony_ci switch (app->protocol) { 222262306a36Sopenharmony_ci case 0: 222362306a36Sopenharmony_ci return dsa_slave_dcbnl_set_default_prio(dev, app); 222462306a36Sopenharmony_ci default: 222562306a36Sopenharmony_ci return -EOPNOTSUPP; 222662306a36Sopenharmony_ci } 222762306a36Sopenharmony_ci break; 222862306a36Sopenharmony_ci case IEEE_8021QAZ_APP_SEL_DSCP: 222962306a36Sopenharmony_ci return dsa_slave_dcbnl_add_dscp_prio(dev, app); 223062306a36Sopenharmony_ci default: 223162306a36Sopenharmony_ci return -EOPNOTSUPP; 223262306a36Sopenharmony_ci } 223362306a36Sopenharmony_ci} 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_cistatic int __maybe_unused 223662306a36Sopenharmony_cidsa_slave_dcbnl_del_default_prio(struct net_device *dev, struct dcb_app *app) 223762306a36Sopenharmony_ci{ 223862306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 223962306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 224062306a36Sopenharmony_ci unsigned long mask, new_prio; 224162306a36Sopenharmony_ci int err, port = dp->index; 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci if (!ds->ops->port_set_default_prio) 224462306a36Sopenharmony_ci return -EOPNOTSUPP; 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci err = dcb_ieee_delapp(dev, app); 224762306a36Sopenharmony_ci if (err) 224862306a36Sopenharmony_ci return err; 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_ci mask = dcb_ieee_getapp_mask(dev, app); 225162306a36Sopenharmony_ci new_prio = mask ? __fls(mask) : 0; 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci err = ds->ops->port_set_default_prio(ds, port, new_prio); 225462306a36Sopenharmony_ci if (err) { 225562306a36Sopenharmony_ci dcb_ieee_setapp(dev, app); 225662306a36Sopenharmony_ci return err; 225762306a36Sopenharmony_ci } 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci return 0; 226062306a36Sopenharmony_ci} 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_cistatic int __maybe_unused 226362306a36Sopenharmony_cidsa_slave_dcbnl_del_dscp_prio(struct net_device *dev, struct dcb_app *app) 226462306a36Sopenharmony_ci{ 226562306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 226662306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 226762306a36Sopenharmony_ci int err, port = dp->index; 226862306a36Sopenharmony_ci u8 dscp = app->protocol; 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci if (!ds->ops->port_del_dscp_prio) 227162306a36Sopenharmony_ci return -EOPNOTSUPP; 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci err = dcb_ieee_delapp(dev, app); 227462306a36Sopenharmony_ci if (err) 227562306a36Sopenharmony_ci return err; 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci err = ds->ops->port_del_dscp_prio(ds, port, dscp, app->priority); 227862306a36Sopenharmony_ci if (err) { 227962306a36Sopenharmony_ci dcb_ieee_setapp(dev, app); 228062306a36Sopenharmony_ci return err; 228162306a36Sopenharmony_ci } 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_ci return 0; 228462306a36Sopenharmony_ci} 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_cistatic int __maybe_unused dsa_slave_dcbnl_ieee_delapp(struct net_device *dev, 228762306a36Sopenharmony_ci struct dcb_app *app) 228862306a36Sopenharmony_ci{ 228962306a36Sopenharmony_ci switch (app->selector) { 229062306a36Sopenharmony_ci case IEEE_8021QAZ_APP_SEL_ETHERTYPE: 229162306a36Sopenharmony_ci switch (app->protocol) { 229262306a36Sopenharmony_ci case 0: 229362306a36Sopenharmony_ci return dsa_slave_dcbnl_del_default_prio(dev, app); 229462306a36Sopenharmony_ci default: 229562306a36Sopenharmony_ci return -EOPNOTSUPP; 229662306a36Sopenharmony_ci } 229762306a36Sopenharmony_ci break; 229862306a36Sopenharmony_ci case IEEE_8021QAZ_APP_SEL_DSCP: 229962306a36Sopenharmony_ci return dsa_slave_dcbnl_del_dscp_prio(dev, app); 230062306a36Sopenharmony_ci default: 230162306a36Sopenharmony_ci return -EOPNOTSUPP; 230262306a36Sopenharmony_ci } 230362306a36Sopenharmony_ci} 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci/* Pre-populate the DCB application priority table with the priorities 230662306a36Sopenharmony_ci * configured during switch setup, which we read from hardware here. 230762306a36Sopenharmony_ci */ 230862306a36Sopenharmony_cistatic int dsa_slave_dcbnl_init(struct net_device *dev) 230962306a36Sopenharmony_ci{ 231062306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 231162306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 231262306a36Sopenharmony_ci int port = dp->index; 231362306a36Sopenharmony_ci int err; 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci if (ds->ops->port_get_default_prio) { 231662306a36Sopenharmony_ci int prio = ds->ops->port_get_default_prio(ds, port); 231762306a36Sopenharmony_ci struct dcb_app app = { 231862306a36Sopenharmony_ci .selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE, 231962306a36Sopenharmony_ci .protocol = 0, 232062306a36Sopenharmony_ci .priority = prio, 232162306a36Sopenharmony_ci }; 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci if (prio < 0) 232462306a36Sopenharmony_ci return prio; 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_ci err = dcb_ieee_setapp(dev, &app); 232762306a36Sopenharmony_ci if (err) 232862306a36Sopenharmony_ci return err; 232962306a36Sopenharmony_ci } 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci if (ds->ops->port_get_dscp_prio) { 233262306a36Sopenharmony_ci int protocol; 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci for (protocol = 0; protocol < 64; protocol++) { 233562306a36Sopenharmony_ci struct dcb_app app = { 233662306a36Sopenharmony_ci .selector = IEEE_8021QAZ_APP_SEL_DSCP, 233762306a36Sopenharmony_ci .protocol = protocol, 233862306a36Sopenharmony_ci }; 233962306a36Sopenharmony_ci int prio; 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci prio = ds->ops->port_get_dscp_prio(ds, port, protocol); 234262306a36Sopenharmony_ci if (prio == -EOPNOTSUPP) 234362306a36Sopenharmony_ci continue; 234462306a36Sopenharmony_ci if (prio < 0) 234562306a36Sopenharmony_ci return prio; 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci app.priority = prio; 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_ci err = dcb_ieee_setapp(dev, &app); 235062306a36Sopenharmony_ci if (err) 235162306a36Sopenharmony_ci return err; 235262306a36Sopenharmony_ci } 235362306a36Sopenharmony_ci } 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci return 0; 235662306a36Sopenharmony_ci} 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_cistatic const struct ethtool_ops dsa_slave_ethtool_ops = { 235962306a36Sopenharmony_ci .get_drvinfo = dsa_slave_get_drvinfo, 236062306a36Sopenharmony_ci .get_regs_len = dsa_slave_get_regs_len, 236162306a36Sopenharmony_ci .get_regs = dsa_slave_get_regs, 236262306a36Sopenharmony_ci .nway_reset = dsa_slave_nway_reset, 236362306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 236462306a36Sopenharmony_ci .get_eeprom_len = dsa_slave_get_eeprom_len, 236562306a36Sopenharmony_ci .get_eeprom = dsa_slave_get_eeprom, 236662306a36Sopenharmony_ci .set_eeprom = dsa_slave_set_eeprom, 236762306a36Sopenharmony_ci .get_strings = dsa_slave_get_strings, 236862306a36Sopenharmony_ci .get_ethtool_stats = dsa_slave_get_ethtool_stats, 236962306a36Sopenharmony_ci .get_sset_count = dsa_slave_get_sset_count, 237062306a36Sopenharmony_ci .get_eth_phy_stats = dsa_slave_get_eth_phy_stats, 237162306a36Sopenharmony_ci .get_eth_mac_stats = dsa_slave_get_eth_mac_stats, 237262306a36Sopenharmony_ci .get_eth_ctrl_stats = dsa_slave_get_eth_ctrl_stats, 237362306a36Sopenharmony_ci .get_rmon_stats = dsa_slave_get_rmon_stats, 237462306a36Sopenharmony_ci .set_wol = dsa_slave_set_wol, 237562306a36Sopenharmony_ci .get_wol = dsa_slave_get_wol, 237662306a36Sopenharmony_ci .set_eee = dsa_slave_set_eee, 237762306a36Sopenharmony_ci .get_eee = dsa_slave_get_eee, 237862306a36Sopenharmony_ci .get_link_ksettings = dsa_slave_get_link_ksettings, 237962306a36Sopenharmony_ci .set_link_ksettings = dsa_slave_set_link_ksettings, 238062306a36Sopenharmony_ci .get_pause_stats = dsa_slave_get_pause_stats, 238162306a36Sopenharmony_ci .get_pauseparam = dsa_slave_get_pauseparam, 238262306a36Sopenharmony_ci .set_pauseparam = dsa_slave_set_pauseparam, 238362306a36Sopenharmony_ci .get_rxnfc = dsa_slave_get_rxnfc, 238462306a36Sopenharmony_ci .set_rxnfc = dsa_slave_set_rxnfc, 238562306a36Sopenharmony_ci .get_ts_info = dsa_slave_get_ts_info, 238662306a36Sopenharmony_ci .self_test = dsa_slave_net_selftest, 238762306a36Sopenharmony_ci .get_mm = dsa_slave_get_mm, 238862306a36Sopenharmony_ci .set_mm = dsa_slave_set_mm, 238962306a36Sopenharmony_ci .get_mm_stats = dsa_slave_get_mm_stats, 239062306a36Sopenharmony_ci}; 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_cistatic const struct dcbnl_rtnl_ops __maybe_unused dsa_slave_dcbnl_ops = { 239362306a36Sopenharmony_ci .ieee_setapp = dsa_slave_dcbnl_ieee_setapp, 239462306a36Sopenharmony_ci .ieee_delapp = dsa_slave_dcbnl_ieee_delapp, 239562306a36Sopenharmony_ci}; 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_cistatic void dsa_slave_get_stats64(struct net_device *dev, 239862306a36Sopenharmony_ci struct rtnl_link_stats64 *s) 239962306a36Sopenharmony_ci{ 240062306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 240162306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci if (ds->ops->get_stats64) 240462306a36Sopenharmony_ci ds->ops->get_stats64(ds, dp->index, s); 240562306a36Sopenharmony_ci else 240662306a36Sopenharmony_ci dev_get_tstats64(dev, s); 240762306a36Sopenharmony_ci} 240862306a36Sopenharmony_ci 240962306a36Sopenharmony_cistatic int dsa_slave_fill_forward_path(struct net_device_path_ctx *ctx, 241062306a36Sopenharmony_ci struct net_device_path *path) 241162306a36Sopenharmony_ci{ 241262306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(ctx->dev); 241362306a36Sopenharmony_ci struct net_device *master = dsa_port_to_master(dp); 241462306a36Sopenharmony_ci struct dsa_port *cpu_dp = dp->cpu_dp; 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci path->dev = ctx->dev; 241762306a36Sopenharmony_ci path->type = DEV_PATH_DSA; 241862306a36Sopenharmony_ci path->dsa.proto = cpu_dp->tag_ops->proto; 241962306a36Sopenharmony_ci path->dsa.port = dp->index; 242062306a36Sopenharmony_ci ctx->dev = master; 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci return 0; 242362306a36Sopenharmony_ci} 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_cistatic const struct net_device_ops dsa_slave_netdev_ops = { 242662306a36Sopenharmony_ci .ndo_open = dsa_slave_open, 242762306a36Sopenharmony_ci .ndo_stop = dsa_slave_close, 242862306a36Sopenharmony_ci .ndo_start_xmit = dsa_slave_xmit, 242962306a36Sopenharmony_ci .ndo_change_rx_flags = dsa_slave_change_rx_flags, 243062306a36Sopenharmony_ci .ndo_set_rx_mode = dsa_slave_set_rx_mode, 243162306a36Sopenharmony_ci .ndo_set_mac_address = dsa_slave_set_mac_address, 243262306a36Sopenharmony_ci .ndo_fdb_dump = dsa_slave_fdb_dump, 243362306a36Sopenharmony_ci .ndo_eth_ioctl = dsa_slave_ioctl, 243462306a36Sopenharmony_ci .ndo_get_iflink = dsa_slave_get_iflink, 243562306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 243662306a36Sopenharmony_ci .ndo_netpoll_setup = dsa_slave_netpoll_setup, 243762306a36Sopenharmony_ci .ndo_netpoll_cleanup = dsa_slave_netpoll_cleanup, 243862306a36Sopenharmony_ci .ndo_poll_controller = dsa_slave_poll_controller, 243962306a36Sopenharmony_ci#endif 244062306a36Sopenharmony_ci .ndo_setup_tc = dsa_slave_setup_tc, 244162306a36Sopenharmony_ci .ndo_get_stats64 = dsa_slave_get_stats64, 244262306a36Sopenharmony_ci .ndo_vlan_rx_add_vid = dsa_slave_vlan_rx_add_vid, 244362306a36Sopenharmony_ci .ndo_vlan_rx_kill_vid = dsa_slave_vlan_rx_kill_vid, 244462306a36Sopenharmony_ci .ndo_change_mtu = dsa_slave_change_mtu, 244562306a36Sopenharmony_ci .ndo_fill_forward_path = dsa_slave_fill_forward_path, 244662306a36Sopenharmony_ci}; 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_cistatic struct device_type dsa_type = { 244962306a36Sopenharmony_ci .name = "dsa", 245062306a36Sopenharmony_ci}; 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_civoid dsa_port_phylink_mac_change(struct dsa_switch *ds, int port, bool up) 245362306a36Sopenharmony_ci{ 245462306a36Sopenharmony_ci const struct dsa_port *dp = dsa_to_port(ds, port); 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci if (dp->pl) 245762306a36Sopenharmony_ci phylink_mac_change(dp->pl, up); 245862306a36Sopenharmony_ci} 245962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dsa_port_phylink_mac_change); 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_cistatic void dsa_slave_phylink_fixed_state(struct phylink_config *config, 246262306a36Sopenharmony_ci struct phylink_link_state *state) 246362306a36Sopenharmony_ci{ 246462306a36Sopenharmony_ci struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 246562306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci /* No need to check that this operation is valid, the callback would 246862306a36Sopenharmony_ci * not be called if it was not. 246962306a36Sopenharmony_ci */ 247062306a36Sopenharmony_ci ds->ops->phylink_fixed_state(ds, dp->index, state); 247162306a36Sopenharmony_ci} 247262306a36Sopenharmony_ci 247362306a36Sopenharmony_ci/* slave device setup *******************************************************/ 247462306a36Sopenharmony_cistatic int dsa_slave_phy_connect(struct net_device *slave_dev, int addr, 247562306a36Sopenharmony_ci u32 flags) 247662306a36Sopenharmony_ci{ 247762306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(slave_dev); 247862306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci slave_dev->phydev = mdiobus_get_phy(ds->slave_mii_bus, addr); 248162306a36Sopenharmony_ci if (!slave_dev->phydev) { 248262306a36Sopenharmony_ci netdev_err(slave_dev, "no phy at %d\n", addr); 248362306a36Sopenharmony_ci return -ENODEV; 248462306a36Sopenharmony_ci } 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci slave_dev->phydev->dev_flags |= flags; 248762306a36Sopenharmony_ci 248862306a36Sopenharmony_ci return phylink_connect_phy(dp->pl, slave_dev->phydev); 248962306a36Sopenharmony_ci} 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_cistatic int dsa_slave_phy_setup(struct net_device *slave_dev) 249262306a36Sopenharmony_ci{ 249362306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(slave_dev); 249462306a36Sopenharmony_ci struct device_node *port_dn = dp->dn; 249562306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 249662306a36Sopenharmony_ci u32 phy_flags = 0; 249762306a36Sopenharmony_ci int ret; 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci dp->pl_config.dev = &slave_dev->dev; 250062306a36Sopenharmony_ci dp->pl_config.type = PHYLINK_NETDEV; 250162306a36Sopenharmony_ci 250262306a36Sopenharmony_ci /* The get_fixed_state callback takes precedence over polling the 250362306a36Sopenharmony_ci * link GPIO in PHYLINK (see phylink_get_fixed_state). Only set 250462306a36Sopenharmony_ci * this if the switch provides such a callback. 250562306a36Sopenharmony_ci */ 250662306a36Sopenharmony_ci if (ds->ops->phylink_fixed_state) { 250762306a36Sopenharmony_ci dp->pl_config.get_fixed_state = dsa_slave_phylink_fixed_state; 250862306a36Sopenharmony_ci dp->pl_config.poll_fixed_state = true; 250962306a36Sopenharmony_ci } 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_ci ret = dsa_port_phylink_create(dp); 251262306a36Sopenharmony_ci if (ret) 251362306a36Sopenharmony_ci return ret; 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci if (ds->ops->get_phy_flags) 251662306a36Sopenharmony_ci phy_flags = ds->ops->get_phy_flags(ds, dp->index); 251762306a36Sopenharmony_ci 251862306a36Sopenharmony_ci ret = phylink_of_phy_connect(dp->pl, port_dn, phy_flags); 251962306a36Sopenharmony_ci if (ret == -ENODEV && ds->slave_mii_bus) { 252062306a36Sopenharmony_ci /* We could not connect to a designated PHY or SFP, so try to 252162306a36Sopenharmony_ci * use the switch internal MDIO bus instead 252262306a36Sopenharmony_ci */ 252362306a36Sopenharmony_ci ret = dsa_slave_phy_connect(slave_dev, dp->index, phy_flags); 252462306a36Sopenharmony_ci } 252562306a36Sopenharmony_ci if (ret) { 252662306a36Sopenharmony_ci netdev_err(slave_dev, "failed to connect to PHY: %pe\n", 252762306a36Sopenharmony_ci ERR_PTR(ret)); 252862306a36Sopenharmony_ci dsa_port_phylink_destroy(dp); 252962306a36Sopenharmony_ci } 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_ci return ret; 253262306a36Sopenharmony_ci} 253362306a36Sopenharmony_ci 253462306a36Sopenharmony_civoid dsa_slave_setup_tagger(struct net_device *slave) 253562306a36Sopenharmony_ci{ 253662306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(slave); 253762306a36Sopenharmony_ci struct net_device *master = dsa_port_to_master(dp); 253862306a36Sopenharmony_ci struct dsa_slave_priv *p = netdev_priv(slave); 253962306a36Sopenharmony_ci const struct dsa_port *cpu_dp = dp->cpu_dp; 254062306a36Sopenharmony_ci const struct dsa_switch *ds = dp->ds; 254162306a36Sopenharmony_ci 254262306a36Sopenharmony_ci slave->needed_headroom = cpu_dp->tag_ops->needed_headroom; 254362306a36Sopenharmony_ci slave->needed_tailroom = cpu_dp->tag_ops->needed_tailroom; 254462306a36Sopenharmony_ci /* Try to save one extra realloc later in the TX path (in the master) 254562306a36Sopenharmony_ci * by also inheriting the master's needed headroom and tailroom. 254662306a36Sopenharmony_ci * The 8021q driver also does this. 254762306a36Sopenharmony_ci */ 254862306a36Sopenharmony_ci slave->needed_headroom += master->needed_headroom; 254962306a36Sopenharmony_ci slave->needed_tailroom += master->needed_tailroom; 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_ci p->xmit = cpu_dp->tag_ops->xmit; 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ci slave->features = master->vlan_features | NETIF_F_HW_TC; 255462306a36Sopenharmony_ci slave->hw_features |= NETIF_F_HW_TC; 255562306a36Sopenharmony_ci slave->features |= NETIF_F_LLTX; 255662306a36Sopenharmony_ci if (slave->needed_tailroom) 255762306a36Sopenharmony_ci slave->features &= ~(NETIF_F_SG | NETIF_F_FRAGLIST); 255862306a36Sopenharmony_ci if (ds->needs_standalone_vlan_filtering) 255962306a36Sopenharmony_ci slave->features |= NETIF_F_HW_VLAN_CTAG_FILTER; 256062306a36Sopenharmony_ci} 256162306a36Sopenharmony_ci 256262306a36Sopenharmony_ciint dsa_slave_suspend(struct net_device *slave_dev) 256362306a36Sopenharmony_ci{ 256462306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(slave_dev); 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci if (!netif_running(slave_dev)) 256762306a36Sopenharmony_ci return 0; 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_ci netif_device_detach(slave_dev); 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_ci rtnl_lock(); 257262306a36Sopenharmony_ci phylink_stop(dp->pl); 257362306a36Sopenharmony_ci rtnl_unlock(); 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci return 0; 257662306a36Sopenharmony_ci} 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ciint dsa_slave_resume(struct net_device *slave_dev) 257962306a36Sopenharmony_ci{ 258062306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(slave_dev); 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci if (!netif_running(slave_dev)) 258362306a36Sopenharmony_ci return 0; 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_ci netif_device_attach(slave_dev); 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ci rtnl_lock(); 258862306a36Sopenharmony_ci phylink_start(dp->pl); 258962306a36Sopenharmony_ci rtnl_unlock(); 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_ci return 0; 259262306a36Sopenharmony_ci} 259362306a36Sopenharmony_ci 259462306a36Sopenharmony_ciint dsa_slave_create(struct dsa_port *port) 259562306a36Sopenharmony_ci{ 259662306a36Sopenharmony_ci struct net_device *master = dsa_port_to_master(port); 259762306a36Sopenharmony_ci struct dsa_switch *ds = port->ds; 259862306a36Sopenharmony_ci struct net_device *slave_dev; 259962306a36Sopenharmony_ci struct dsa_slave_priv *p; 260062306a36Sopenharmony_ci const char *name; 260162306a36Sopenharmony_ci int assign_type; 260262306a36Sopenharmony_ci int ret; 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_ci if (!ds->num_tx_queues) 260562306a36Sopenharmony_ci ds->num_tx_queues = 1; 260662306a36Sopenharmony_ci 260762306a36Sopenharmony_ci if (port->name) { 260862306a36Sopenharmony_ci name = port->name; 260962306a36Sopenharmony_ci assign_type = NET_NAME_PREDICTABLE; 261062306a36Sopenharmony_ci } else { 261162306a36Sopenharmony_ci name = "eth%d"; 261262306a36Sopenharmony_ci assign_type = NET_NAME_ENUM; 261362306a36Sopenharmony_ci } 261462306a36Sopenharmony_ci 261562306a36Sopenharmony_ci slave_dev = alloc_netdev_mqs(sizeof(struct dsa_slave_priv), name, 261662306a36Sopenharmony_ci assign_type, ether_setup, 261762306a36Sopenharmony_ci ds->num_tx_queues, 1); 261862306a36Sopenharmony_ci if (slave_dev == NULL) 261962306a36Sopenharmony_ci return -ENOMEM; 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci slave_dev->rtnl_link_ops = &dsa_link_ops; 262262306a36Sopenharmony_ci slave_dev->ethtool_ops = &dsa_slave_ethtool_ops; 262362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_DCB) 262462306a36Sopenharmony_ci slave_dev->dcbnl_ops = &dsa_slave_dcbnl_ops; 262562306a36Sopenharmony_ci#endif 262662306a36Sopenharmony_ci if (!is_zero_ether_addr(port->mac)) 262762306a36Sopenharmony_ci eth_hw_addr_set(slave_dev, port->mac); 262862306a36Sopenharmony_ci else 262962306a36Sopenharmony_ci eth_hw_addr_inherit(slave_dev, master); 263062306a36Sopenharmony_ci slave_dev->priv_flags |= IFF_NO_QUEUE; 263162306a36Sopenharmony_ci if (dsa_switch_supports_uc_filtering(ds)) 263262306a36Sopenharmony_ci slave_dev->priv_flags |= IFF_UNICAST_FLT; 263362306a36Sopenharmony_ci slave_dev->netdev_ops = &dsa_slave_netdev_ops; 263462306a36Sopenharmony_ci if (ds->ops->port_max_mtu) 263562306a36Sopenharmony_ci slave_dev->max_mtu = ds->ops->port_max_mtu(ds, port->index); 263662306a36Sopenharmony_ci SET_NETDEV_DEVTYPE(slave_dev, &dsa_type); 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_ci SET_NETDEV_DEV(slave_dev, port->ds->dev); 263962306a36Sopenharmony_ci SET_NETDEV_DEVLINK_PORT(slave_dev, &port->devlink_port); 264062306a36Sopenharmony_ci slave_dev->dev.of_node = port->dn; 264162306a36Sopenharmony_ci slave_dev->vlan_features = master->vlan_features; 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ci p = netdev_priv(slave_dev); 264462306a36Sopenharmony_ci slave_dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); 264562306a36Sopenharmony_ci if (!slave_dev->tstats) { 264662306a36Sopenharmony_ci free_netdev(slave_dev); 264762306a36Sopenharmony_ci return -ENOMEM; 264862306a36Sopenharmony_ci } 264962306a36Sopenharmony_ci 265062306a36Sopenharmony_ci ret = gro_cells_init(&p->gcells, slave_dev); 265162306a36Sopenharmony_ci if (ret) 265262306a36Sopenharmony_ci goto out_free; 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci p->dp = port; 265562306a36Sopenharmony_ci INIT_LIST_HEAD(&p->mall_tc_list); 265662306a36Sopenharmony_ci port->slave = slave_dev; 265762306a36Sopenharmony_ci dsa_slave_setup_tagger(slave_dev); 265862306a36Sopenharmony_ci 265962306a36Sopenharmony_ci netif_carrier_off(slave_dev); 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_ci ret = dsa_slave_phy_setup(slave_dev); 266262306a36Sopenharmony_ci if (ret) { 266362306a36Sopenharmony_ci netdev_err(slave_dev, 266462306a36Sopenharmony_ci "error %d setting up PHY for tree %d, switch %d, port %d\n", 266562306a36Sopenharmony_ci ret, ds->dst->index, ds->index, port->index); 266662306a36Sopenharmony_ci goto out_gcells; 266762306a36Sopenharmony_ci } 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci rtnl_lock(); 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_ci ret = dsa_slave_change_mtu(slave_dev, ETH_DATA_LEN); 267262306a36Sopenharmony_ci if (ret && ret != -EOPNOTSUPP) 267362306a36Sopenharmony_ci dev_warn(ds->dev, "nonfatal error %d setting MTU to %d on port %d\n", 267462306a36Sopenharmony_ci ret, ETH_DATA_LEN, port->index); 267562306a36Sopenharmony_ci 267662306a36Sopenharmony_ci ret = register_netdevice(slave_dev); 267762306a36Sopenharmony_ci if (ret) { 267862306a36Sopenharmony_ci netdev_err(master, "error %d registering interface %s\n", 267962306a36Sopenharmony_ci ret, slave_dev->name); 268062306a36Sopenharmony_ci rtnl_unlock(); 268162306a36Sopenharmony_ci goto out_phy; 268262306a36Sopenharmony_ci } 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_DCB)) { 268562306a36Sopenharmony_ci ret = dsa_slave_dcbnl_init(slave_dev); 268662306a36Sopenharmony_ci if (ret) { 268762306a36Sopenharmony_ci netdev_err(slave_dev, 268862306a36Sopenharmony_ci "failed to initialize DCB: %pe\n", 268962306a36Sopenharmony_ci ERR_PTR(ret)); 269062306a36Sopenharmony_ci rtnl_unlock(); 269162306a36Sopenharmony_ci goto out_unregister; 269262306a36Sopenharmony_ci } 269362306a36Sopenharmony_ci } 269462306a36Sopenharmony_ci 269562306a36Sopenharmony_ci ret = netdev_upper_dev_link(master, slave_dev, NULL); 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_ci rtnl_unlock(); 269862306a36Sopenharmony_ci 269962306a36Sopenharmony_ci if (ret) 270062306a36Sopenharmony_ci goto out_unregister; 270162306a36Sopenharmony_ci 270262306a36Sopenharmony_ci return 0; 270362306a36Sopenharmony_ci 270462306a36Sopenharmony_ciout_unregister: 270562306a36Sopenharmony_ci unregister_netdev(slave_dev); 270662306a36Sopenharmony_ciout_phy: 270762306a36Sopenharmony_ci rtnl_lock(); 270862306a36Sopenharmony_ci phylink_disconnect_phy(p->dp->pl); 270962306a36Sopenharmony_ci rtnl_unlock(); 271062306a36Sopenharmony_ci dsa_port_phylink_destroy(p->dp); 271162306a36Sopenharmony_ciout_gcells: 271262306a36Sopenharmony_ci gro_cells_destroy(&p->gcells); 271362306a36Sopenharmony_ciout_free: 271462306a36Sopenharmony_ci free_percpu(slave_dev->tstats); 271562306a36Sopenharmony_ci free_netdev(slave_dev); 271662306a36Sopenharmony_ci port->slave = NULL; 271762306a36Sopenharmony_ci return ret; 271862306a36Sopenharmony_ci} 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_civoid dsa_slave_destroy(struct net_device *slave_dev) 272162306a36Sopenharmony_ci{ 272262306a36Sopenharmony_ci struct net_device *master = dsa_slave_to_master(slave_dev); 272362306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(slave_dev); 272462306a36Sopenharmony_ci struct dsa_slave_priv *p = netdev_priv(slave_dev); 272562306a36Sopenharmony_ci 272662306a36Sopenharmony_ci netif_carrier_off(slave_dev); 272762306a36Sopenharmony_ci rtnl_lock(); 272862306a36Sopenharmony_ci netdev_upper_dev_unlink(master, slave_dev); 272962306a36Sopenharmony_ci unregister_netdevice(slave_dev); 273062306a36Sopenharmony_ci phylink_disconnect_phy(dp->pl); 273162306a36Sopenharmony_ci rtnl_unlock(); 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_ci dsa_port_phylink_destroy(dp); 273462306a36Sopenharmony_ci gro_cells_destroy(&p->gcells); 273562306a36Sopenharmony_ci free_percpu(slave_dev->tstats); 273662306a36Sopenharmony_ci free_netdev(slave_dev); 273762306a36Sopenharmony_ci} 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_ciint dsa_slave_change_master(struct net_device *dev, struct net_device *master, 274062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 274162306a36Sopenharmony_ci{ 274262306a36Sopenharmony_ci struct net_device *old_master = dsa_slave_to_master(dev); 274362306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 274462306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 274562306a36Sopenharmony_ci struct net_device *upper; 274662306a36Sopenharmony_ci struct list_head *iter; 274762306a36Sopenharmony_ci int err; 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_ci if (master == old_master) 275062306a36Sopenharmony_ci return 0; 275162306a36Sopenharmony_ci 275262306a36Sopenharmony_ci if (!ds->ops->port_change_master) { 275362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 275462306a36Sopenharmony_ci "Driver does not support changing DSA master"); 275562306a36Sopenharmony_ci return -EOPNOTSUPP; 275662306a36Sopenharmony_ci } 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_ci if (!netdev_uses_dsa(master)) { 275962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 276062306a36Sopenharmony_ci "Interface not eligible as DSA master"); 276162306a36Sopenharmony_ci return -EOPNOTSUPP; 276262306a36Sopenharmony_ci } 276362306a36Sopenharmony_ci 276462306a36Sopenharmony_ci netdev_for_each_upper_dev_rcu(master, upper, iter) { 276562306a36Sopenharmony_ci if (dsa_slave_dev_check(upper)) 276662306a36Sopenharmony_ci continue; 276762306a36Sopenharmony_ci if (netif_is_bridge_master(upper)) 276862306a36Sopenharmony_ci continue; 276962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Cannot join master with unknown uppers"); 277062306a36Sopenharmony_ci return -EOPNOTSUPP; 277162306a36Sopenharmony_ci } 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci /* Since we allow live-changing the DSA master, plus we auto-open the 277462306a36Sopenharmony_ci * DSA master when the user port opens => we need to ensure that the 277562306a36Sopenharmony_ci * new DSA master is open too. 277662306a36Sopenharmony_ci */ 277762306a36Sopenharmony_ci if (dev->flags & IFF_UP) { 277862306a36Sopenharmony_ci err = dev_open(master, extack); 277962306a36Sopenharmony_ci if (err) 278062306a36Sopenharmony_ci return err; 278162306a36Sopenharmony_ci } 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_ci netdev_upper_dev_unlink(old_master, dev); 278462306a36Sopenharmony_ci 278562306a36Sopenharmony_ci err = netdev_upper_dev_link(master, dev, extack); 278662306a36Sopenharmony_ci if (err) 278762306a36Sopenharmony_ci goto out_revert_old_master_unlink; 278862306a36Sopenharmony_ci 278962306a36Sopenharmony_ci err = dsa_port_change_master(dp, master, extack); 279062306a36Sopenharmony_ci if (err) 279162306a36Sopenharmony_ci goto out_revert_master_link; 279262306a36Sopenharmony_ci 279362306a36Sopenharmony_ci /* Update the MTU of the new CPU port through cross-chip notifiers */ 279462306a36Sopenharmony_ci err = dsa_slave_change_mtu(dev, dev->mtu); 279562306a36Sopenharmony_ci if (err && err != -EOPNOTSUPP) { 279662306a36Sopenharmony_ci netdev_warn(dev, 279762306a36Sopenharmony_ci "nonfatal error updating MTU with new master: %pe\n", 279862306a36Sopenharmony_ci ERR_PTR(err)); 279962306a36Sopenharmony_ci } 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci /* If the port doesn't have its own MAC address and relies on the DSA 280262306a36Sopenharmony_ci * master's one, inherit it again from the new DSA master. 280362306a36Sopenharmony_ci */ 280462306a36Sopenharmony_ci if (is_zero_ether_addr(dp->mac)) 280562306a36Sopenharmony_ci eth_hw_addr_inherit(dev, master); 280662306a36Sopenharmony_ci 280762306a36Sopenharmony_ci return 0; 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_ciout_revert_master_link: 281062306a36Sopenharmony_ci netdev_upper_dev_unlink(master, dev); 281162306a36Sopenharmony_ciout_revert_old_master_unlink: 281262306a36Sopenharmony_ci netdev_upper_dev_link(old_master, dev, NULL); 281362306a36Sopenharmony_ci return err; 281462306a36Sopenharmony_ci} 281562306a36Sopenharmony_ci 281662306a36Sopenharmony_cibool dsa_slave_dev_check(const struct net_device *dev) 281762306a36Sopenharmony_ci{ 281862306a36Sopenharmony_ci return dev->netdev_ops == &dsa_slave_netdev_ops; 281962306a36Sopenharmony_ci} 282062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dsa_slave_dev_check); 282162306a36Sopenharmony_ci 282262306a36Sopenharmony_cistatic int dsa_slave_changeupper(struct net_device *dev, 282362306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info) 282462306a36Sopenharmony_ci{ 282562306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 282662306a36Sopenharmony_ci struct netlink_ext_ack *extack; 282762306a36Sopenharmony_ci int err = NOTIFY_DONE; 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci if (!dsa_slave_dev_check(dev)) 283062306a36Sopenharmony_ci return err; 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_ci extack = netdev_notifier_info_to_extack(&info->info); 283362306a36Sopenharmony_ci 283462306a36Sopenharmony_ci if (netif_is_bridge_master(info->upper_dev)) { 283562306a36Sopenharmony_ci if (info->linking) { 283662306a36Sopenharmony_ci err = dsa_port_bridge_join(dp, info->upper_dev, extack); 283762306a36Sopenharmony_ci if (!err) 283862306a36Sopenharmony_ci dsa_bridge_mtu_normalization(dp); 283962306a36Sopenharmony_ci if (err == -EOPNOTSUPP) { 284062306a36Sopenharmony_ci NL_SET_ERR_MSG_WEAK_MOD(extack, 284162306a36Sopenharmony_ci "Offloading not supported"); 284262306a36Sopenharmony_ci err = 0; 284362306a36Sopenharmony_ci } 284462306a36Sopenharmony_ci err = notifier_from_errno(err); 284562306a36Sopenharmony_ci } else { 284662306a36Sopenharmony_ci dsa_port_bridge_leave(dp, info->upper_dev); 284762306a36Sopenharmony_ci err = NOTIFY_OK; 284862306a36Sopenharmony_ci } 284962306a36Sopenharmony_ci } else if (netif_is_lag_master(info->upper_dev)) { 285062306a36Sopenharmony_ci if (info->linking) { 285162306a36Sopenharmony_ci err = dsa_port_lag_join(dp, info->upper_dev, 285262306a36Sopenharmony_ci info->upper_info, extack); 285362306a36Sopenharmony_ci if (err == -EOPNOTSUPP) { 285462306a36Sopenharmony_ci NL_SET_ERR_MSG_WEAK_MOD(extack, 285562306a36Sopenharmony_ci "Offloading not supported"); 285662306a36Sopenharmony_ci err = 0; 285762306a36Sopenharmony_ci } 285862306a36Sopenharmony_ci err = notifier_from_errno(err); 285962306a36Sopenharmony_ci } else { 286062306a36Sopenharmony_ci dsa_port_lag_leave(dp, info->upper_dev); 286162306a36Sopenharmony_ci err = NOTIFY_OK; 286262306a36Sopenharmony_ci } 286362306a36Sopenharmony_ci } else if (is_hsr_master(info->upper_dev)) { 286462306a36Sopenharmony_ci if (info->linking) { 286562306a36Sopenharmony_ci err = dsa_port_hsr_join(dp, info->upper_dev); 286662306a36Sopenharmony_ci if (err == -EOPNOTSUPP) { 286762306a36Sopenharmony_ci NL_SET_ERR_MSG_WEAK_MOD(extack, 286862306a36Sopenharmony_ci "Offloading not supported"); 286962306a36Sopenharmony_ci err = 0; 287062306a36Sopenharmony_ci } 287162306a36Sopenharmony_ci err = notifier_from_errno(err); 287262306a36Sopenharmony_ci } else { 287362306a36Sopenharmony_ci dsa_port_hsr_leave(dp, info->upper_dev); 287462306a36Sopenharmony_ci err = NOTIFY_OK; 287562306a36Sopenharmony_ci } 287662306a36Sopenharmony_ci } 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci return err; 287962306a36Sopenharmony_ci} 288062306a36Sopenharmony_ci 288162306a36Sopenharmony_cistatic int dsa_slave_prechangeupper(struct net_device *dev, 288262306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info) 288362306a36Sopenharmony_ci{ 288462306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 288562306a36Sopenharmony_ci 288662306a36Sopenharmony_ci if (!dsa_slave_dev_check(dev)) 288762306a36Sopenharmony_ci return NOTIFY_DONE; 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ci if (netif_is_bridge_master(info->upper_dev) && !info->linking) 289062306a36Sopenharmony_ci dsa_port_pre_bridge_leave(dp, info->upper_dev); 289162306a36Sopenharmony_ci else if (netif_is_lag_master(info->upper_dev) && !info->linking) 289262306a36Sopenharmony_ci dsa_port_pre_lag_leave(dp, info->upper_dev); 289362306a36Sopenharmony_ci /* dsa_port_pre_hsr_leave is not yet necessary since hsr cannot be 289462306a36Sopenharmony_ci * meaningfully enslaved to a bridge yet 289562306a36Sopenharmony_ci */ 289662306a36Sopenharmony_ci 289762306a36Sopenharmony_ci return NOTIFY_DONE; 289862306a36Sopenharmony_ci} 289962306a36Sopenharmony_ci 290062306a36Sopenharmony_cistatic int 290162306a36Sopenharmony_cidsa_slave_lag_changeupper(struct net_device *dev, 290262306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info) 290362306a36Sopenharmony_ci{ 290462306a36Sopenharmony_ci struct net_device *lower; 290562306a36Sopenharmony_ci struct list_head *iter; 290662306a36Sopenharmony_ci int err = NOTIFY_DONE; 290762306a36Sopenharmony_ci struct dsa_port *dp; 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci if (!netif_is_lag_master(dev)) 291062306a36Sopenharmony_ci return err; 291162306a36Sopenharmony_ci 291262306a36Sopenharmony_ci netdev_for_each_lower_dev(dev, lower, iter) { 291362306a36Sopenharmony_ci if (!dsa_slave_dev_check(lower)) 291462306a36Sopenharmony_ci continue; 291562306a36Sopenharmony_ci 291662306a36Sopenharmony_ci dp = dsa_slave_to_port(lower); 291762306a36Sopenharmony_ci if (!dp->lag) 291862306a36Sopenharmony_ci /* Software LAG */ 291962306a36Sopenharmony_ci continue; 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_ci err = dsa_slave_changeupper(lower, info); 292262306a36Sopenharmony_ci if (notifier_to_errno(err)) 292362306a36Sopenharmony_ci break; 292462306a36Sopenharmony_ci } 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_ci return err; 292762306a36Sopenharmony_ci} 292862306a36Sopenharmony_ci 292962306a36Sopenharmony_ci/* Same as dsa_slave_lag_changeupper() except that it calls 293062306a36Sopenharmony_ci * dsa_slave_prechangeupper() 293162306a36Sopenharmony_ci */ 293262306a36Sopenharmony_cistatic int 293362306a36Sopenharmony_cidsa_slave_lag_prechangeupper(struct net_device *dev, 293462306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info) 293562306a36Sopenharmony_ci{ 293662306a36Sopenharmony_ci struct net_device *lower; 293762306a36Sopenharmony_ci struct list_head *iter; 293862306a36Sopenharmony_ci int err = NOTIFY_DONE; 293962306a36Sopenharmony_ci struct dsa_port *dp; 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_ci if (!netif_is_lag_master(dev)) 294262306a36Sopenharmony_ci return err; 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_ci netdev_for_each_lower_dev(dev, lower, iter) { 294562306a36Sopenharmony_ci if (!dsa_slave_dev_check(lower)) 294662306a36Sopenharmony_ci continue; 294762306a36Sopenharmony_ci 294862306a36Sopenharmony_ci dp = dsa_slave_to_port(lower); 294962306a36Sopenharmony_ci if (!dp->lag) 295062306a36Sopenharmony_ci /* Software LAG */ 295162306a36Sopenharmony_ci continue; 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_ci err = dsa_slave_prechangeupper(lower, info); 295462306a36Sopenharmony_ci if (notifier_to_errno(err)) 295562306a36Sopenharmony_ci break; 295662306a36Sopenharmony_ci } 295762306a36Sopenharmony_ci 295862306a36Sopenharmony_ci return err; 295962306a36Sopenharmony_ci} 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_cistatic int 296262306a36Sopenharmony_cidsa_prevent_bridging_8021q_upper(struct net_device *dev, 296362306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info) 296462306a36Sopenharmony_ci{ 296562306a36Sopenharmony_ci struct netlink_ext_ack *ext_ack; 296662306a36Sopenharmony_ci struct net_device *slave, *br; 296762306a36Sopenharmony_ci struct dsa_port *dp; 296862306a36Sopenharmony_ci 296962306a36Sopenharmony_ci ext_ack = netdev_notifier_info_to_extack(&info->info); 297062306a36Sopenharmony_ci 297162306a36Sopenharmony_ci if (!is_vlan_dev(dev)) 297262306a36Sopenharmony_ci return NOTIFY_DONE; 297362306a36Sopenharmony_ci 297462306a36Sopenharmony_ci slave = vlan_dev_real_dev(dev); 297562306a36Sopenharmony_ci if (!dsa_slave_dev_check(slave)) 297662306a36Sopenharmony_ci return NOTIFY_DONE; 297762306a36Sopenharmony_ci 297862306a36Sopenharmony_ci dp = dsa_slave_to_port(slave); 297962306a36Sopenharmony_ci br = dsa_port_bridge_dev_get(dp); 298062306a36Sopenharmony_ci if (!br) 298162306a36Sopenharmony_ci return NOTIFY_DONE; 298262306a36Sopenharmony_ci 298362306a36Sopenharmony_ci /* Deny enslaving a VLAN device into a VLAN-aware bridge */ 298462306a36Sopenharmony_ci if (br_vlan_enabled(br) && 298562306a36Sopenharmony_ci netif_is_bridge_master(info->upper_dev) && info->linking) { 298662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(ext_ack, 298762306a36Sopenharmony_ci "Cannot enslave VLAN device into VLAN aware bridge"); 298862306a36Sopenharmony_ci return notifier_from_errno(-EINVAL); 298962306a36Sopenharmony_ci } 299062306a36Sopenharmony_ci 299162306a36Sopenharmony_ci return NOTIFY_DONE; 299262306a36Sopenharmony_ci} 299362306a36Sopenharmony_ci 299462306a36Sopenharmony_cistatic int 299562306a36Sopenharmony_cidsa_slave_check_8021q_upper(struct net_device *dev, 299662306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info) 299762306a36Sopenharmony_ci{ 299862306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 299962306a36Sopenharmony_ci struct net_device *br = dsa_port_bridge_dev_get(dp); 300062306a36Sopenharmony_ci struct bridge_vlan_info br_info; 300162306a36Sopenharmony_ci struct netlink_ext_ack *extack; 300262306a36Sopenharmony_ci int err = NOTIFY_DONE; 300362306a36Sopenharmony_ci u16 vid; 300462306a36Sopenharmony_ci 300562306a36Sopenharmony_ci if (!br || !br_vlan_enabled(br)) 300662306a36Sopenharmony_ci return NOTIFY_DONE; 300762306a36Sopenharmony_ci 300862306a36Sopenharmony_ci extack = netdev_notifier_info_to_extack(&info->info); 300962306a36Sopenharmony_ci vid = vlan_dev_vlan_id(info->upper_dev); 301062306a36Sopenharmony_ci 301162306a36Sopenharmony_ci /* br_vlan_get_info() returns -EINVAL or -ENOENT if the 301262306a36Sopenharmony_ci * device, respectively the VID is not found, returning 301362306a36Sopenharmony_ci * 0 means success, which is a failure for us here. 301462306a36Sopenharmony_ci */ 301562306a36Sopenharmony_ci err = br_vlan_get_info(br, vid, &br_info); 301662306a36Sopenharmony_ci if (err == 0) { 301762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 301862306a36Sopenharmony_ci "This VLAN is already configured by the bridge"); 301962306a36Sopenharmony_ci return notifier_from_errno(-EBUSY); 302062306a36Sopenharmony_ci } 302162306a36Sopenharmony_ci 302262306a36Sopenharmony_ci return NOTIFY_DONE; 302362306a36Sopenharmony_ci} 302462306a36Sopenharmony_ci 302562306a36Sopenharmony_cistatic int 302662306a36Sopenharmony_cidsa_slave_prechangeupper_sanity_check(struct net_device *dev, 302762306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info) 302862306a36Sopenharmony_ci{ 302962306a36Sopenharmony_ci struct dsa_switch *ds; 303062306a36Sopenharmony_ci struct dsa_port *dp; 303162306a36Sopenharmony_ci int err; 303262306a36Sopenharmony_ci 303362306a36Sopenharmony_ci if (!dsa_slave_dev_check(dev)) 303462306a36Sopenharmony_ci return dsa_prevent_bridging_8021q_upper(dev, info); 303562306a36Sopenharmony_ci 303662306a36Sopenharmony_ci dp = dsa_slave_to_port(dev); 303762306a36Sopenharmony_ci ds = dp->ds; 303862306a36Sopenharmony_ci 303962306a36Sopenharmony_ci if (ds->ops->port_prechangeupper) { 304062306a36Sopenharmony_ci err = ds->ops->port_prechangeupper(ds, dp->index, info); 304162306a36Sopenharmony_ci if (err) 304262306a36Sopenharmony_ci return notifier_from_errno(err); 304362306a36Sopenharmony_ci } 304462306a36Sopenharmony_ci 304562306a36Sopenharmony_ci if (is_vlan_dev(info->upper_dev)) 304662306a36Sopenharmony_ci return dsa_slave_check_8021q_upper(dev, info); 304762306a36Sopenharmony_ci 304862306a36Sopenharmony_ci return NOTIFY_DONE; 304962306a36Sopenharmony_ci} 305062306a36Sopenharmony_ci 305162306a36Sopenharmony_ci/* To be eligible as a DSA master, a LAG must have all lower interfaces be 305262306a36Sopenharmony_ci * eligible DSA masters. Additionally, all LAG slaves must be DSA masters of 305362306a36Sopenharmony_ci * switches in the same switch tree. 305462306a36Sopenharmony_ci */ 305562306a36Sopenharmony_cistatic int dsa_lag_master_validate(struct net_device *lag_dev, 305662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 305762306a36Sopenharmony_ci{ 305862306a36Sopenharmony_ci struct net_device *lower1, *lower2; 305962306a36Sopenharmony_ci struct list_head *iter1, *iter2; 306062306a36Sopenharmony_ci 306162306a36Sopenharmony_ci netdev_for_each_lower_dev(lag_dev, lower1, iter1) { 306262306a36Sopenharmony_ci netdev_for_each_lower_dev(lag_dev, lower2, iter2) { 306362306a36Sopenharmony_ci if (!netdev_uses_dsa(lower1) || 306462306a36Sopenharmony_ci !netdev_uses_dsa(lower2)) { 306562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 306662306a36Sopenharmony_ci "All LAG ports must be eligible as DSA masters"); 306762306a36Sopenharmony_ci return notifier_from_errno(-EINVAL); 306862306a36Sopenharmony_ci } 306962306a36Sopenharmony_ci 307062306a36Sopenharmony_ci if (lower1 == lower2) 307162306a36Sopenharmony_ci continue; 307262306a36Sopenharmony_ci 307362306a36Sopenharmony_ci if (!dsa_port_tree_same(lower1->dsa_ptr, 307462306a36Sopenharmony_ci lower2->dsa_ptr)) { 307562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 307662306a36Sopenharmony_ci "LAG contains DSA masters of disjoint switch trees"); 307762306a36Sopenharmony_ci return notifier_from_errno(-EINVAL); 307862306a36Sopenharmony_ci } 307962306a36Sopenharmony_ci } 308062306a36Sopenharmony_ci } 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_ci return NOTIFY_DONE; 308362306a36Sopenharmony_ci} 308462306a36Sopenharmony_ci 308562306a36Sopenharmony_cistatic int 308662306a36Sopenharmony_cidsa_master_prechangeupper_sanity_check(struct net_device *master, 308762306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info) 308862306a36Sopenharmony_ci{ 308962306a36Sopenharmony_ci struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(&info->info); 309062306a36Sopenharmony_ci 309162306a36Sopenharmony_ci if (!netdev_uses_dsa(master)) 309262306a36Sopenharmony_ci return NOTIFY_DONE; 309362306a36Sopenharmony_ci 309462306a36Sopenharmony_ci if (!info->linking) 309562306a36Sopenharmony_ci return NOTIFY_DONE; 309662306a36Sopenharmony_ci 309762306a36Sopenharmony_ci /* Allow DSA switch uppers */ 309862306a36Sopenharmony_ci if (dsa_slave_dev_check(info->upper_dev)) 309962306a36Sopenharmony_ci return NOTIFY_DONE; 310062306a36Sopenharmony_ci 310162306a36Sopenharmony_ci /* Allow bridge uppers of DSA masters, subject to further 310262306a36Sopenharmony_ci * restrictions in dsa_bridge_prechangelower_sanity_check() 310362306a36Sopenharmony_ci */ 310462306a36Sopenharmony_ci if (netif_is_bridge_master(info->upper_dev)) 310562306a36Sopenharmony_ci return NOTIFY_DONE; 310662306a36Sopenharmony_ci 310762306a36Sopenharmony_ci /* Allow LAG uppers, subject to further restrictions in 310862306a36Sopenharmony_ci * dsa_lag_master_prechangelower_sanity_check() 310962306a36Sopenharmony_ci */ 311062306a36Sopenharmony_ci if (netif_is_lag_master(info->upper_dev)) 311162306a36Sopenharmony_ci return dsa_lag_master_validate(info->upper_dev, extack); 311262306a36Sopenharmony_ci 311362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 311462306a36Sopenharmony_ci "DSA master cannot join unknown upper interfaces"); 311562306a36Sopenharmony_ci return notifier_from_errno(-EBUSY); 311662306a36Sopenharmony_ci} 311762306a36Sopenharmony_ci 311862306a36Sopenharmony_cistatic int 311962306a36Sopenharmony_cidsa_lag_master_prechangelower_sanity_check(struct net_device *dev, 312062306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info) 312162306a36Sopenharmony_ci{ 312262306a36Sopenharmony_ci struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(&info->info); 312362306a36Sopenharmony_ci struct net_device *lag_dev = info->upper_dev; 312462306a36Sopenharmony_ci struct net_device *lower; 312562306a36Sopenharmony_ci struct list_head *iter; 312662306a36Sopenharmony_ci 312762306a36Sopenharmony_ci if (!netdev_uses_dsa(lag_dev) || !netif_is_lag_master(lag_dev)) 312862306a36Sopenharmony_ci return NOTIFY_DONE; 312962306a36Sopenharmony_ci 313062306a36Sopenharmony_ci if (!info->linking) 313162306a36Sopenharmony_ci return NOTIFY_DONE; 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci if (!netdev_uses_dsa(dev)) { 313462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 313562306a36Sopenharmony_ci "Only DSA masters can join a LAG DSA master"); 313662306a36Sopenharmony_ci return notifier_from_errno(-EINVAL); 313762306a36Sopenharmony_ci } 313862306a36Sopenharmony_ci 313962306a36Sopenharmony_ci netdev_for_each_lower_dev(lag_dev, lower, iter) { 314062306a36Sopenharmony_ci if (!dsa_port_tree_same(dev->dsa_ptr, lower->dsa_ptr)) { 314162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 314262306a36Sopenharmony_ci "Interface is DSA master for a different switch tree than this LAG"); 314362306a36Sopenharmony_ci return notifier_from_errno(-EINVAL); 314462306a36Sopenharmony_ci } 314562306a36Sopenharmony_ci 314662306a36Sopenharmony_ci break; 314762306a36Sopenharmony_ci } 314862306a36Sopenharmony_ci 314962306a36Sopenharmony_ci return NOTIFY_DONE; 315062306a36Sopenharmony_ci} 315162306a36Sopenharmony_ci 315262306a36Sopenharmony_ci/* Don't allow bridging of DSA masters, since the bridge layer rx_handler 315362306a36Sopenharmony_ci * prevents the DSA fake ethertype handler to be invoked, so we don't get the 315462306a36Sopenharmony_ci * chance to strip off and parse the DSA switch tag protocol header (the bridge 315562306a36Sopenharmony_ci * layer just returns RX_HANDLER_CONSUMED, stopping RX processing for these 315662306a36Sopenharmony_ci * frames). 315762306a36Sopenharmony_ci * The only case where that would not be an issue is when bridging can already 315862306a36Sopenharmony_ci * be offloaded, such as when the DSA master is itself a DSA or plain switchdev 315962306a36Sopenharmony_ci * port, and is bridged only with other ports from the same hardware device. 316062306a36Sopenharmony_ci */ 316162306a36Sopenharmony_cistatic int 316262306a36Sopenharmony_cidsa_bridge_prechangelower_sanity_check(struct net_device *new_lower, 316362306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info) 316462306a36Sopenharmony_ci{ 316562306a36Sopenharmony_ci struct net_device *br = info->upper_dev; 316662306a36Sopenharmony_ci struct netlink_ext_ack *extack; 316762306a36Sopenharmony_ci struct net_device *lower; 316862306a36Sopenharmony_ci struct list_head *iter; 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_ci if (!netif_is_bridge_master(br)) 317162306a36Sopenharmony_ci return NOTIFY_DONE; 317262306a36Sopenharmony_ci 317362306a36Sopenharmony_ci if (!info->linking) 317462306a36Sopenharmony_ci return NOTIFY_DONE; 317562306a36Sopenharmony_ci 317662306a36Sopenharmony_ci extack = netdev_notifier_info_to_extack(&info->info); 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ci netdev_for_each_lower_dev(br, lower, iter) { 317962306a36Sopenharmony_ci if (!netdev_uses_dsa(new_lower) && !netdev_uses_dsa(lower)) 318062306a36Sopenharmony_ci continue; 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci if (!netdev_port_same_parent_id(lower, new_lower)) { 318362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 318462306a36Sopenharmony_ci "Cannot do software bridging with a DSA master"); 318562306a36Sopenharmony_ci return notifier_from_errno(-EINVAL); 318662306a36Sopenharmony_ci } 318762306a36Sopenharmony_ci } 318862306a36Sopenharmony_ci 318962306a36Sopenharmony_ci return NOTIFY_DONE; 319062306a36Sopenharmony_ci} 319162306a36Sopenharmony_ci 319262306a36Sopenharmony_cistatic void dsa_tree_migrate_ports_from_lag_master(struct dsa_switch_tree *dst, 319362306a36Sopenharmony_ci struct net_device *lag_dev) 319462306a36Sopenharmony_ci{ 319562306a36Sopenharmony_ci struct net_device *new_master = dsa_tree_find_first_master(dst); 319662306a36Sopenharmony_ci struct dsa_port *dp; 319762306a36Sopenharmony_ci int err; 319862306a36Sopenharmony_ci 319962306a36Sopenharmony_ci dsa_tree_for_each_user_port(dp, dst) { 320062306a36Sopenharmony_ci if (dsa_port_to_master(dp) != lag_dev) 320162306a36Sopenharmony_ci continue; 320262306a36Sopenharmony_ci 320362306a36Sopenharmony_ci err = dsa_slave_change_master(dp->slave, new_master, NULL); 320462306a36Sopenharmony_ci if (err) { 320562306a36Sopenharmony_ci netdev_err(dp->slave, 320662306a36Sopenharmony_ci "failed to restore master to %s: %pe\n", 320762306a36Sopenharmony_ci new_master->name, ERR_PTR(err)); 320862306a36Sopenharmony_ci } 320962306a36Sopenharmony_ci } 321062306a36Sopenharmony_ci} 321162306a36Sopenharmony_ci 321262306a36Sopenharmony_cistatic int dsa_master_lag_join(struct net_device *master, 321362306a36Sopenharmony_ci struct net_device *lag_dev, 321462306a36Sopenharmony_ci struct netdev_lag_upper_info *uinfo, 321562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 321662306a36Sopenharmony_ci{ 321762306a36Sopenharmony_ci struct dsa_port *cpu_dp = master->dsa_ptr; 321862306a36Sopenharmony_ci struct dsa_switch_tree *dst = cpu_dp->dst; 321962306a36Sopenharmony_ci struct dsa_port *dp; 322062306a36Sopenharmony_ci int err; 322162306a36Sopenharmony_ci 322262306a36Sopenharmony_ci err = dsa_master_lag_setup(lag_dev, cpu_dp, uinfo, extack); 322362306a36Sopenharmony_ci if (err) 322462306a36Sopenharmony_ci return err; 322562306a36Sopenharmony_ci 322662306a36Sopenharmony_ci dsa_tree_for_each_user_port(dp, dst) { 322762306a36Sopenharmony_ci if (dsa_port_to_master(dp) != master) 322862306a36Sopenharmony_ci continue; 322962306a36Sopenharmony_ci 323062306a36Sopenharmony_ci err = dsa_slave_change_master(dp->slave, lag_dev, extack); 323162306a36Sopenharmony_ci if (err) 323262306a36Sopenharmony_ci goto restore; 323362306a36Sopenharmony_ci } 323462306a36Sopenharmony_ci 323562306a36Sopenharmony_ci return 0; 323662306a36Sopenharmony_ci 323762306a36Sopenharmony_cirestore: 323862306a36Sopenharmony_ci dsa_tree_for_each_user_port_continue_reverse(dp, dst) { 323962306a36Sopenharmony_ci if (dsa_port_to_master(dp) != lag_dev) 324062306a36Sopenharmony_ci continue; 324162306a36Sopenharmony_ci 324262306a36Sopenharmony_ci err = dsa_slave_change_master(dp->slave, master, NULL); 324362306a36Sopenharmony_ci if (err) { 324462306a36Sopenharmony_ci netdev_err(dp->slave, 324562306a36Sopenharmony_ci "failed to restore master to %s: %pe\n", 324662306a36Sopenharmony_ci master->name, ERR_PTR(err)); 324762306a36Sopenharmony_ci } 324862306a36Sopenharmony_ci } 324962306a36Sopenharmony_ci 325062306a36Sopenharmony_ci dsa_master_lag_teardown(lag_dev, master->dsa_ptr); 325162306a36Sopenharmony_ci 325262306a36Sopenharmony_ci return err; 325362306a36Sopenharmony_ci} 325462306a36Sopenharmony_ci 325562306a36Sopenharmony_cistatic void dsa_master_lag_leave(struct net_device *master, 325662306a36Sopenharmony_ci struct net_device *lag_dev) 325762306a36Sopenharmony_ci{ 325862306a36Sopenharmony_ci struct dsa_port *dp, *cpu_dp = lag_dev->dsa_ptr; 325962306a36Sopenharmony_ci struct dsa_switch_tree *dst = cpu_dp->dst; 326062306a36Sopenharmony_ci struct dsa_port *new_cpu_dp = NULL; 326162306a36Sopenharmony_ci struct net_device *lower; 326262306a36Sopenharmony_ci struct list_head *iter; 326362306a36Sopenharmony_ci 326462306a36Sopenharmony_ci netdev_for_each_lower_dev(lag_dev, lower, iter) { 326562306a36Sopenharmony_ci if (netdev_uses_dsa(lower)) { 326662306a36Sopenharmony_ci new_cpu_dp = lower->dsa_ptr; 326762306a36Sopenharmony_ci break; 326862306a36Sopenharmony_ci } 326962306a36Sopenharmony_ci } 327062306a36Sopenharmony_ci 327162306a36Sopenharmony_ci if (new_cpu_dp) { 327262306a36Sopenharmony_ci /* Update the CPU port of the user ports still under the LAG 327362306a36Sopenharmony_ci * so that dsa_port_to_master() continues to work properly 327462306a36Sopenharmony_ci */ 327562306a36Sopenharmony_ci dsa_tree_for_each_user_port(dp, dst) 327662306a36Sopenharmony_ci if (dsa_port_to_master(dp) == lag_dev) 327762306a36Sopenharmony_ci dp->cpu_dp = new_cpu_dp; 327862306a36Sopenharmony_ci 327962306a36Sopenharmony_ci /* Update the index of the virtual CPU port to match the lowest 328062306a36Sopenharmony_ci * physical CPU port 328162306a36Sopenharmony_ci */ 328262306a36Sopenharmony_ci lag_dev->dsa_ptr = new_cpu_dp; 328362306a36Sopenharmony_ci wmb(); 328462306a36Sopenharmony_ci } else { 328562306a36Sopenharmony_ci /* If the LAG DSA master has no ports left, migrate back all 328662306a36Sopenharmony_ci * user ports to the first physical CPU port 328762306a36Sopenharmony_ci */ 328862306a36Sopenharmony_ci dsa_tree_migrate_ports_from_lag_master(dst, lag_dev); 328962306a36Sopenharmony_ci } 329062306a36Sopenharmony_ci 329162306a36Sopenharmony_ci /* This DSA master has left its LAG in any case, so let 329262306a36Sopenharmony_ci * the CPU port leave the hardware LAG as well 329362306a36Sopenharmony_ci */ 329462306a36Sopenharmony_ci dsa_master_lag_teardown(lag_dev, master->dsa_ptr); 329562306a36Sopenharmony_ci} 329662306a36Sopenharmony_ci 329762306a36Sopenharmony_cistatic int dsa_master_changeupper(struct net_device *dev, 329862306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info) 329962306a36Sopenharmony_ci{ 330062306a36Sopenharmony_ci struct netlink_ext_ack *extack; 330162306a36Sopenharmony_ci int err = NOTIFY_DONE; 330262306a36Sopenharmony_ci 330362306a36Sopenharmony_ci if (!netdev_uses_dsa(dev)) 330462306a36Sopenharmony_ci return err; 330562306a36Sopenharmony_ci 330662306a36Sopenharmony_ci extack = netdev_notifier_info_to_extack(&info->info); 330762306a36Sopenharmony_ci 330862306a36Sopenharmony_ci if (netif_is_lag_master(info->upper_dev)) { 330962306a36Sopenharmony_ci if (info->linking) { 331062306a36Sopenharmony_ci err = dsa_master_lag_join(dev, info->upper_dev, 331162306a36Sopenharmony_ci info->upper_info, extack); 331262306a36Sopenharmony_ci err = notifier_from_errno(err); 331362306a36Sopenharmony_ci } else { 331462306a36Sopenharmony_ci dsa_master_lag_leave(dev, info->upper_dev); 331562306a36Sopenharmony_ci err = NOTIFY_OK; 331662306a36Sopenharmony_ci } 331762306a36Sopenharmony_ci } 331862306a36Sopenharmony_ci 331962306a36Sopenharmony_ci return err; 332062306a36Sopenharmony_ci} 332162306a36Sopenharmony_ci 332262306a36Sopenharmony_cistatic int dsa_slave_netdevice_event(struct notifier_block *nb, 332362306a36Sopenharmony_ci unsigned long event, void *ptr) 332462306a36Sopenharmony_ci{ 332562306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 332662306a36Sopenharmony_ci 332762306a36Sopenharmony_ci switch (event) { 332862306a36Sopenharmony_ci case NETDEV_PRECHANGEUPPER: { 332962306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info = ptr; 333062306a36Sopenharmony_ci int err; 333162306a36Sopenharmony_ci 333262306a36Sopenharmony_ci err = dsa_slave_prechangeupper_sanity_check(dev, info); 333362306a36Sopenharmony_ci if (notifier_to_errno(err)) 333462306a36Sopenharmony_ci return err; 333562306a36Sopenharmony_ci 333662306a36Sopenharmony_ci err = dsa_master_prechangeupper_sanity_check(dev, info); 333762306a36Sopenharmony_ci if (notifier_to_errno(err)) 333862306a36Sopenharmony_ci return err; 333962306a36Sopenharmony_ci 334062306a36Sopenharmony_ci err = dsa_lag_master_prechangelower_sanity_check(dev, info); 334162306a36Sopenharmony_ci if (notifier_to_errno(err)) 334262306a36Sopenharmony_ci return err; 334362306a36Sopenharmony_ci 334462306a36Sopenharmony_ci err = dsa_bridge_prechangelower_sanity_check(dev, info); 334562306a36Sopenharmony_ci if (notifier_to_errno(err)) 334662306a36Sopenharmony_ci return err; 334762306a36Sopenharmony_ci 334862306a36Sopenharmony_ci err = dsa_slave_prechangeupper(dev, ptr); 334962306a36Sopenharmony_ci if (notifier_to_errno(err)) 335062306a36Sopenharmony_ci return err; 335162306a36Sopenharmony_ci 335262306a36Sopenharmony_ci err = dsa_slave_lag_prechangeupper(dev, ptr); 335362306a36Sopenharmony_ci if (notifier_to_errno(err)) 335462306a36Sopenharmony_ci return err; 335562306a36Sopenharmony_ci 335662306a36Sopenharmony_ci break; 335762306a36Sopenharmony_ci } 335862306a36Sopenharmony_ci case NETDEV_CHANGEUPPER: { 335962306a36Sopenharmony_ci int err; 336062306a36Sopenharmony_ci 336162306a36Sopenharmony_ci err = dsa_slave_changeupper(dev, ptr); 336262306a36Sopenharmony_ci if (notifier_to_errno(err)) 336362306a36Sopenharmony_ci return err; 336462306a36Sopenharmony_ci 336562306a36Sopenharmony_ci err = dsa_slave_lag_changeupper(dev, ptr); 336662306a36Sopenharmony_ci if (notifier_to_errno(err)) 336762306a36Sopenharmony_ci return err; 336862306a36Sopenharmony_ci 336962306a36Sopenharmony_ci err = dsa_master_changeupper(dev, ptr); 337062306a36Sopenharmony_ci if (notifier_to_errno(err)) 337162306a36Sopenharmony_ci return err; 337262306a36Sopenharmony_ci 337362306a36Sopenharmony_ci break; 337462306a36Sopenharmony_ci } 337562306a36Sopenharmony_ci case NETDEV_CHANGELOWERSTATE: { 337662306a36Sopenharmony_ci struct netdev_notifier_changelowerstate_info *info = ptr; 337762306a36Sopenharmony_ci struct dsa_port *dp; 337862306a36Sopenharmony_ci int err = 0; 337962306a36Sopenharmony_ci 338062306a36Sopenharmony_ci if (dsa_slave_dev_check(dev)) { 338162306a36Sopenharmony_ci dp = dsa_slave_to_port(dev); 338262306a36Sopenharmony_ci 338362306a36Sopenharmony_ci err = dsa_port_lag_change(dp, info->lower_state_info); 338462306a36Sopenharmony_ci } 338562306a36Sopenharmony_ci 338662306a36Sopenharmony_ci /* Mirror LAG port events on DSA masters that are in 338762306a36Sopenharmony_ci * a LAG towards their respective switch CPU ports 338862306a36Sopenharmony_ci */ 338962306a36Sopenharmony_ci if (netdev_uses_dsa(dev)) { 339062306a36Sopenharmony_ci dp = dev->dsa_ptr; 339162306a36Sopenharmony_ci 339262306a36Sopenharmony_ci err = dsa_port_lag_change(dp, info->lower_state_info); 339362306a36Sopenharmony_ci } 339462306a36Sopenharmony_ci 339562306a36Sopenharmony_ci return notifier_from_errno(err); 339662306a36Sopenharmony_ci } 339762306a36Sopenharmony_ci case NETDEV_CHANGE: 339862306a36Sopenharmony_ci case NETDEV_UP: { 339962306a36Sopenharmony_ci /* Track state of master port. 340062306a36Sopenharmony_ci * DSA driver may require the master port (and indirectly 340162306a36Sopenharmony_ci * the tagger) to be available for some special operation. 340262306a36Sopenharmony_ci */ 340362306a36Sopenharmony_ci if (netdev_uses_dsa(dev)) { 340462306a36Sopenharmony_ci struct dsa_port *cpu_dp = dev->dsa_ptr; 340562306a36Sopenharmony_ci struct dsa_switch_tree *dst = cpu_dp->ds->dst; 340662306a36Sopenharmony_ci 340762306a36Sopenharmony_ci /* Track when the master port is UP */ 340862306a36Sopenharmony_ci dsa_tree_master_oper_state_change(dst, dev, 340962306a36Sopenharmony_ci netif_oper_up(dev)); 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_ci /* Track when the master port is ready and can accept 341262306a36Sopenharmony_ci * packet. 341362306a36Sopenharmony_ci * NETDEV_UP event is not enough to flag a port as ready. 341462306a36Sopenharmony_ci * We also have to wait for linkwatch_do_dev to dev_activate 341562306a36Sopenharmony_ci * and emit a NETDEV_CHANGE event. 341662306a36Sopenharmony_ci * We check if a master port is ready by checking if the dev 341762306a36Sopenharmony_ci * have a qdisc assigned and is not noop. 341862306a36Sopenharmony_ci */ 341962306a36Sopenharmony_ci dsa_tree_master_admin_state_change(dst, dev, 342062306a36Sopenharmony_ci !qdisc_tx_is_noop(dev)); 342162306a36Sopenharmony_ci 342262306a36Sopenharmony_ci return NOTIFY_OK; 342362306a36Sopenharmony_ci } 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci return NOTIFY_DONE; 342662306a36Sopenharmony_ci } 342762306a36Sopenharmony_ci case NETDEV_GOING_DOWN: { 342862306a36Sopenharmony_ci struct dsa_port *dp, *cpu_dp; 342962306a36Sopenharmony_ci struct dsa_switch_tree *dst; 343062306a36Sopenharmony_ci LIST_HEAD(close_list); 343162306a36Sopenharmony_ci 343262306a36Sopenharmony_ci if (!netdev_uses_dsa(dev)) 343362306a36Sopenharmony_ci return NOTIFY_DONE; 343462306a36Sopenharmony_ci 343562306a36Sopenharmony_ci cpu_dp = dev->dsa_ptr; 343662306a36Sopenharmony_ci dst = cpu_dp->ds->dst; 343762306a36Sopenharmony_ci 343862306a36Sopenharmony_ci dsa_tree_master_admin_state_change(dst, dev, false); 343962306a36Sopenharmony_ci 344062306a36Sopenharmony_ci list_for_each_entry(dp, &dst->ports, list) { 344162306a36Sopenharmony_ci if (!dsa_port_is_user(dp)) 344262306a36Sopenharmony_ci continue; 344362306a36Sopenharmony_ci 344462306a36Sopenharmony_ci if (dp->cpu_dp != cpu_dp) 344562306a36Sopenharmony_ci continue; 344662306a36Sopenharmony_ci 344762306a36Sopenharmony_ci list_add(&dp->slave->close_list, &close_list); 344862306a36Sopenharmony_ci } 344962306a36Sopenharmony_ci 345062306a36Sopenharmony_ci dev_close_many(&close_list, true); 345162306a36Sopenharmony_ci 345262306a36Sopenharmony_ci return NOTIFY_OK; 345362306a36Sopenharmony_ci } 345462306a36Sopenharmony_ci default: 345562306a36Sopenharmony_ci break; 345662306a36Sopenharmony_ci } 345762306a36Sopenharmony_ci 345862306a36Sopenharmony_ci return NOTIFY_DONE; 345962306a36Sopenharmony_ci} 346062306a36Sopenharmony_ci 346162306a36Sopenharmony_cistatic void 346262306a36Sopenharmony_cidsa_fdb_offload_notify(struct dsa_switchdev_event_work *switchdev_work) 346362306a36Sopenharmony_ci{ 346462306a36Sopenharmony_ci struct switchdev_notifier_fdb_info info = {}; 346562306a36Sopenharmony_ci 346662306a36Sopenharmony_ci info.addr = switchdev_work->addr; 346762306a36Sopenharmony_ci info.vid = switchdev_work->vid; 346862306a36Sopenharmony_ci info.offloaded = true; 346962306a36Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, 347062306a36Sopenharmony_ci switchdev_work->orig_dev, &info.info, NULL); 347162306a36Sopenharmony_ci} 347262306a36Sopenharmony_ci 347362306a36Sopenharmony_cistatic void dsa_slave_switchdev_event_work(struct work_struct *work) 347462306a36Sopenharmony_ci{ 347562306a36Sopenharmony_ci struct dsa_switchdev_event_work *switchdev_work = 347662306a36Sopenharmony_ci container_of(work, struct dsa_switchdev_event_work, work); 347762306a36Sopenharmony_ci const unsigned char *addr = switchdev_work->addr; 347862306a36Sopenharmony_ci struct net_device *dev = switchdev_work->dev; 347962306a36Sopenharmony_ci u16 vid = switchdev_work->vid; 348062306a36Sopenharmony_ci struct dsa_switch *ds; 348162306a36Sopenharmony_ci struct dsa_port *dp; 348262306a36Sopenharmony_ci int err; 348362306a36Sopenharmony_ci 348462306a36Sopenharmony_ci dp = dsa_slave_to_port(dev); 348562306a36Sopenharmony_ci ds = dp->ds; 348662306a36Sopenharmony_ci 348762306a36Sopenharmony_ci switch (switchdev_work->event) { 348862306a36Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_DEVICE: 348962306a36Sopenharmony_ci if (switchdev_work->host_addr) 349062306a36Sopenharmony_ci err = dsa_port_bridge_host_fdb_add(dp, addr, vid); 349162306a36Sopenharmony_ci else if (dp->lag) 349262306a36Sopenharmony_ci err = dsa_port_lag_fdb_add(dp, addr, vid); 349362306a36Sopenharmony_ci else 349462306a36Sopenharmony_ci err = dsa_port_fdb_add(dp, addr, vid); 349562306a36Sopenharmony_ci if (err) { 349662306a36Sopenharmony_ci dev_err(ds->dev, 349762306a36Sopenharmony_ci "port %d failed to add %pM vid %d to fdb: %d\n", 349862306a36Sopenharmony_ci dp->index, addr, vid, err); 349962306a36Sopenharmony_ci break; 350062306a36Sopenharmony_ci } 350162306a36Sopenharmony_ci dsa_fdb_offload_notify(switchdev_work); 350262306a36Sopenharmony_ci break; 350362306a36Sopenharmony_ci 350462306a36Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_DEVICE: 350562306a36Sopenharmony_ci if (switchdev_work->host_addr) 350662306a36Sopenharmony_ci err = dsa_port_bridge_host_fdb_del(dp, addr, vid); 350762306a36Sopenharmony_ci else if (dp->lag) 350862306a36Sopenharmony_ci err = dsa_port_lag_fdb_del(dp, addr, vid); 350962306a36Sopenharmony_ci else 351062306a36Sopenharmony_ci err = dsa_port_fdb_del(dp, addr, vid); 351162306a36Sopenharmony_ci if (err) { 351262306a36Sopenharmony_ci dev_err(ds->dev, 351362306a36Sopenharmony_ci "port %d failed to delete %pM vid %d from fdb: %d\n", 351462306a36Sopenharmony_ci dp->index, addr, vid, err); 351562306a36Sopenharmony_ci } 351662306a36Sopenharmony_ci 351762306a36Sopenharmony_ci break; 351862306a36Sopenharmony_ci } 351962306a36Sopenharmony_ci 352062306a36Sopenharmony_ci kfree(switchdev_work); 352162306a36Sopenharmony_ci} 352262306a36Sopenharmony_ci 352362306a36Sopenharmony_cistatic bool dsa_foreign_dev_check(const struct net_device *dev, 352462306a36Sopenharmony_ci const struct net_device *foreign_dev) 352562306a36Sopenharmony_ci{ 352662306a36Sopenharmony_ci const struct dsa_port *dp = dsa_slave_to_port(dev); 352762306a36Sopenharmony_ci struct dsa_switch_tree *dst = dp->ds->dst; 352862306a36Sopenharmony_ci 352962306a36Sopenharmony_ci if (netif_is_bridge_master(foreign_dev)) 353062306a36Sopenharmony_ci return !dsa_tree_offloads_bridge_dev(dst, foreign_dev); 353162306a36Sopenharmony_ci 353262306a36Sopenharmony_ci if (netif_is_bridge_port(foreign_dev)) 353362306a36Sopenharmony_ci return !dsa_tree_offloads_bridge_port(dst, foreign_dev); 353462306a36Sopenharmony_ci 353562306a36Sopenharmony_ci /* Everything else is foreign */ 353662306a36Sopenharmony_ci return true; 353762306a36Sopenharmony_ci} 353862306a36Sopenharmony_ci 353962306a36Sopenharmony_cistatic int dsa_slave_fdb_event(struct net_device *dev, 354062306a36Sopenharmony_ci struct net_device *orig_dev, 354162306a36Sopenharmony_ci unsigned long event, const void *ctx, 354262306a36Sopenharmony_ci const struct switchdev_notifier_fdb_info *fdb_info) 354362306a36Sopenharmony_ci{ 354462306a36Sopenharmony_ci struct dsa_switchdev_event_work *switchdev_work; 354562306a36Sopenharmony_ci struct dsa_port *dp = dsa_slave_to_port(dev); 354662306a36Sopenharmony_ci bool host_addr = fdb_info->is_local; 354762306a36Sopenharmony_ci struct dsa_switch *ds = dp->ds; 354862306a36Sopenharmony_ci 354962306a36Sopenharmony_ci if (ctx && ctx != dp) 355062306a36Sopenharmony_ci return 0; 355162306a36Sopenharmony_ci 355262306a36Sopenharmony_ci if (!dp->bridge) 355362306a36Sopenharmony_ci return 0; 355462306a36Sopenharmony_ci 355562306a36Sopenharmony_ci if (switchdev_fdb_is_dynamically_learned(fdb_info)) { 355662306a36Sopenharmony_ci if (dsa_port_offloads_bridge_port(dp, orig_dev)) 355762306a36Sopenharmony_ci return 0; 355862306a36Sopenharmony_ci 355962306a36Sopenharmony_ci /* FDB entries learned by the software bridge or by foreign 356062306a36Sopenharmony_ci * bridge ports should be installed as host addresses only if 356162306a36Sopenharmony_ci * the driver requests assisted learning. 356262306a36Sopenharmony_ci */ 356362306a36Sopenharmony_ci if (!ds->assisted_learning_on_cpu_port) 356462306a36Sopenharmony_ci return 0; 356562306a36Sopenharmony_ci } 356662306a36Sopenharmony_ci 356762306a36Sopenharmony_ci /* Also treat FDB entries on foreign interfaces bridged with us as host 356862306a36Sopenharmony_ci * addresses. 356962306a36Sopenharmony_ci */ 357062306a36Sopenharmony_ci if (dsa_foreign_dev_check(dev, orig_dev)) 357162306a36Sopenharmony_ci host_addr = true; 357262306a36Sopenharmony_ci 357362306a36Sopenharmony_ci /* Check early that we're not doing work in vain. 357462306a36Sopenharmony_ci * Host addresses on LAG ports still require regular FDB ops, 357562306a36Sopenharmony_ci * since the CPU port isn't in a LAG. 357662306a36Sopenharmony_ci */ 357762306a36Sopenharmony_ci if (dp->lag && !host_addr) { 357862306a36Sopenharmony_ci if (!ds->ops->lag_fdb_add || !ds->ops->lag_fdb_del) 357962306a36Sopenharmony_ci return -EOPNOTSUPP; 358062306a36Sopenharmony_ci } else { 358162306a36Sopenharmony_ci if (!ds->ops->port_fdb_add || !ds->ops->port_fdb_del) 358262306a36Sopenharmony_ci return -EOPNOTSUPP; 358362306a36Sopenharmony_ci } 358462306a36Sopenharmony_ci 358562306a36Sopenharmony_ci switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); 358662306a36Sopenharmony_ci if (!switchdev_work) 358762306a36Sopenharmony_ci return -ENOMEM; 358862306a36Sopenharmony_ci 358962306a36Sopenharmony_ci netdev_dbg(dev, "%s FDB entry towards %s, addr %pM vid %d%s\n", 359062306a36Sopenharmony_ci event == SWITCHDEV_FDB_ADD_TO_DEVICE ? "Adding" : "Deleting", 359162306a36Sopenharmony_ci orig_dev->name, fdb_info->addr, fdb_info->vid, 359262306a36Sopenharmony_ci host_addr ? " as host address" : ""); 359362306a36Sopenharmony_ci 359462306a36Sopenharmony_ci INIT_WORK(&switchdev_work->work, dsa_slave_switchdev_event_work); 359562306a36Sopenharmony_ci switchdev_work->event = event; 359662306a36Sopenharmony_ci switchdev_work->dev = dev; 359762306a36Sopenharmony_ci switchdev_work->orig_dev = orig_dev; 359862306a36Sopenharmony_ci 359962306a36Sopenharmony_ci ether_addr_copy(switchdev_work->addr, fdb_info->addr); 360062306a36Sopenharmony_ci switchdev_work->vid = fdb_info->vid; 360162306a36Sopenharmony_ci switchdev_work->host_addr = host_addr; 360262306a36Sopenharmony_ci 360362306a36Sopenharmony_ci dsa_schedule_work(&switchdev_work->work); 360462306a36Sopenharmony_ci 360562306a36Sopenharmony_ci return 0; 360662306a36Sopenharmony_ci} 360762306a36Sopenharmony_ci 360862306a36Sopenharmony_ci/* Called under rcu_read_lock() */ 360962306a36Sopenharmony_cistatic int dsa_slave_switchdev_event(struct notifier_block *unused, 361062306a36Sopenharmony_ci unsigned long event, void *ptr) 361162306a36Sopenharmony_ci{ 361262306a36Sopenharmony_ci struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 361362306a36Sopenharmony_ci int err; 361462306a36Sopenharmony_ci 361562306a36Sopenharmony_ci switch (event) { 361662306a36Sopenharmony_ci case SWITCHDEV_PORT_ATTR_SET: 361762306a36Sopenharmony_ci err = switchdev_handle_port_attr_set(dev, ptr, 361862306a36Sopenharmony_ci dsa_slave_dev_check, 361962306a36Sopenharmony_ci dsa_slave_port_attr_set); 362062306a36Sopenharmony_ci return notifier_from_errno(err); 362162306a36Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_DEVICE: 362262306a36Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_DEVICE: 362362306a36Sopenharmony_ci err = switchdev_handle_fdb_event_to_device(dev, event, ptr, 362462306a36Sopenharmony_ci dsa_slave_dev_check, 362562306a36Sopenharmony_ci dsa_foreign_dev_check, 362662306a36Sopenharmony_ci dsa_slave_fdb_event); 362762306a36Sopenharmony_ci return notifier_from_errno(err); 362862306a36Sopenharmony_ci default: 362962306a36Sopenharmony_ci return NOTIFY_DONE; 363062306a36Sopenharmony_ci } 363162306a36Sopenharmony_ci 363262306a36Sopenharmony_ci return NOTIFY_OK; 363362306a36Sopenharmony_ci} 363462306a36Sopenharmony_ci 363562306a36Sopenharmony_cistatic int dsa_slave_switchdev_blocking_event(struct notifier_block *unused, 363662306a36Sopenharmony_ci unsigned long event, void *ptr) 363762306a36Sopenharmony_ci{ 363862306a36Sopenharmony_ci struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 363962306a36Sopenharmony_ci int err; 364062306a36Sopenharmony_ci 364162306a36Sopenharmony_ci switch (event) { 364262306a36Sopenharmony_ci case SWITCHDEV_PORT_OBJ_ADD: 364362306a36Sopenharmony_ci err = switchdev_handle_port_obj_add_foreign(dev, ptr, 364462306a36Sopenharmony_ci dsa_slave_dev_check, 364562306a36Sopenharmony_ci dsa_foreign_dev_check, 364662306a36Sopenharmony_ci dsa_slave_port_obj_add); 364762306a36Sopenharmony_ci return notifier_from_errno(err); 364862306a36Sopenharmony_ci case SWITCHDEV_PORT_OBJ_DEL: 364962306a36Sopenharmony_ci err = switchdev_handle_port_obj_del_foreign(dev, ptr, 365062306a36Sopenharmony_ci dsa_slave_dev_check, 365162306a36Sopenharmony_ci dsa_foreign_dev_check, 365262306a36Sopenharmony_ci dsa_slave_port_obj_del); 365362306a36Sopenharmony_ci return notifier_from_errno(err); 365462306a36Sopenharmony_ci case SWITCHDEV_PORT_ATTR_SET: 365562306a36Sopenharmony_ci err = switchdev_handle_port_attr_set(dev, ptr, 365662306a36Sopenharmony_ci dsa_slave_dev_check, 365762306a36Sopenharmony_ci dsa_slave_port_attr_set); 365862306a36Sopenharmony_ci return notifier_from_errno(err); 365962306a36Sopenharmony_ci } 366062306a36Sopenharmony_ci 366162306a36Sopenharmony_ci return NOTIFY_DONE; 366262306a36Sopenharmony_ci} 366362306a36Sopenharmony_ci 366462306a36Sopenharmony_cistatic struct notifier_block dsa_slave_nb __read_mostly = { 366562306a36Sopenharmony_ci .notifier_call = dsa_slave_netdevice_event, 366662306a36Sopenharmony_ci}; 366762306a36Sopenharmony_ci 366862306a36Sopenharmony_cistruct notifier_block dsa_slave_switchdev_notifier = { 366962306a36Sopenharmony_ci .notifier_call = dsa_slave_switchdev_event, 367062306a36Sopenharmony_ci}; 367162306a36Sopenharmony_ci 367262306a36Sopenharmony_cistruct notifier_block dsa_slave_switchdev_blocking_notifier = { 367362306a36Sopenharmony_ci .notifier_call = dsa_slave_switchdev_blocking_event, 367462306a36Sopenharmony_ci}; 367562306a36Sopenharmony_ci 367662306a36Sopenharmony_ciint dsa_slave_register_notifier(void) 367762306a36Sopenharmony_ci{ 367862306a36Sopenharmony_ci struct notifier_block *nb; 367962306a36Sopenharmony_ci int err; 368062306a36Sopenharmony_ci 368162306a36Sopenharmony_ci err = register_netdevice_notifier(&dsa_slave_nb); 368262306a36Sopenharmony_ci if (err) 368362306a36Sopenharmony_ci return err; 368462306a36Sopenharmony_ci 368562306a36Sopenharmony_ci err = register_switchdev_notifier(&dsa_slave_switchdev_notifier); 368662306a36Sopenharmony_ci if (err) 368762306a36Sopenharmony_ci goto err_switchdev_nb; 368862306a36Sopenharmony_ci 368962306a36Sopenharmony_ci nb = &dsa_slave_switchdev_blocking_notifier; 369062306a36Sopenharmony_ci err = register_switchdev_blocking_notifier(nb); 369162306a36Sopenharmony_ci if (err) 369262306a36Sopenharmony_ci goto err_switchdev_blocking_nb; 369362306a36Sopenharmony_ci 369462306a36Sopenharmony_ci return 0; 369562306a36Sopenharmony_ci 369662306a36Sopenharmony_cierr_switchdev_blocking_nb: 369762306a36Sopenharmony_ci unregister_switchdev_notifier(&dsa_slave_switchdev_notifier); 369862306a36Sopenharmony_cierr_switchdev_nb: 369962306a36Sopenharmony_ci unregister_netdevice_notifier(&dsa_slave_nb); 370062306a36Sopenharmony_ci return err; 370162306a36Sopenharmony_ci} 370262306a36Sopenharmony_ci 370362306a36Sopenharmony_civoid dsa_slave_unregister_notifier(void) 370462306a36Sopenharmony_ci{ 370562306a36Sopenharmony_ci struct notifier_block *nb; 370662306a36Sopenharmony_ci int err; 370762306a36Sopenharmony_ci 370862306a36Sopenharmony_ci nb = &dsa_slave_switchdev_blocking_notifier; 370962306a36Sopenharmony_ci err = unregister_switchdev_blocking_notifier(nb); 371062306a36Sopenharmony_ci if (err) 371162306a36Sopenharmony_ci pr_err("DSA: failed to unregister switchdev blocking notifier (%d)\n", err); 371262306a36Sopenharmony_ci 371362306a36Sopenharmony_ci err = unregister_switchdev_notifier(&dsa_slave_switchdev_notifier); 371462306a36Sopenharmony_ci if (err) 371562306a36Sopenharmony_ci pr_err("DSA: failed to unregister switchdev notifier (%d)\n", err); 371662306a36Sopenharmony_ci 371762306a36Sopenharmony_ci err = unregister_netdevice_notifier(&dsa_slave_nb); 371862306a36Sopenharmony_ci if (err) 371962306a36Sopenharmony_ci pr_err("DSA: failed to unregister slave notifier (%d)\n", err); 372062306a36Sopenharmony_ci} 3721