12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2a40c175bSVivien Didelot /* 3a40c175bSVivien Didelot * Handling of a single switch port 4a40c175bSVivien Didelot * 5a40c175bSVivien Didelot * Copyright (c) 2017 Savoir-faire Linux Inc. 6a40c175bSVivien Didelot * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 7a40c175bSVivien Didelot */ 8a40c175bSVivien Didelot 9a40c175bSVivien Didelot #include <linux/if_bridge.h> 10cfbed329SVivien Didelot #include <linux/notifier.h> 1157ab1ca2SVivien Didelot #include <linux/of_mdio.h> 1257ab1ca2SVivien Didelot #include <linux/of_net.h> 13a40c175bSVivien Didelot 14a40c175bSVivien Didelot #include "dsa_priv.h" 15a40c175bSVivien Didelot 16886f8e26SVladimir Oltean /** 17886f8e26SVladimir Oltean * dsa_port_notify - Notify the switching fabric of changes to a port 18886f8e26SVladimir Oltean * @dp: port on which change occurred 19886f8e26SVladimir Oltean * @e: event, must be of type DSA_NOTIFIER_* 20886f8e26SVladimir Oltean * @v: event-specific value. 21886f8e26SVladimir Oltean * 22886f8e26SVladimir Oltean * Notify all switches in the DSA tree that this port's switch belongs to, 23886f8e26SVladimir Oltean * including this switch itself, of an event. Allows the other switches to 24886f8e26SVladimir Oltean * reconfigure themselves for cross-chip operations. Can also be used to 25886f8e26SVladimir Oltean * reconfigure ports without net_devices (CPU ports, DSA links) whenever 26886f8e26SVladimir Oltean * a user port's state changes. 27886f8e26SVladimir Oltean */ 28bb9f6031SAndrew Lunn static int dsa_port_notify(const struct dsa_port *dp, unsigned long e, void *v) 29cfbed329SVivien Didelot { 30886f8e26SVladimir Oltean return dsa_tree_notify(dp->ds->dst, e, v); 31cfbed329SVivien Didelot } 32cfbed329SVivien Didelot 339264e4adSVladimir Oltean static void dsa_port_notify_bridge_fdb_flush(const struct dsa_port *dp) 349264e4adSVladimir Oltean { 359264e4adSVladimir Oltean struct net_device *brport_dev = dsa_port_to_bridge_port(dp); 369264e4adSVladimir Oltean struct switchdev_notifier_fdb_info info = { 379264e4adSVladimir Oltean /* flush all VLANs */ 389264e4adSVladimir Oltean .vid = 0, 399264e4adSVladimir Oltean }; 409264e4adSVladimir Oltean 419264e4adSVladimir Oltean /* When the port becomes standalone it has already left the bridge. 429264e4adSVladimir Oltean * Don't notify the bridge in that case. 439264e4adSVladimir Oltean */ 449264e4adSVladimir Oltean if (!brport_dev) 459264e4adSVladimir Oltean return; 469264e4adSVladimir Oltean 479264e4adSVladimir Oltean call_switchdev_notifiers(SWITCHDEV_FDB_FLUSH_TO_BRIDGE, 489264e4adSVladimir Oltean brport_dev, &info.info, NULL); 499264e4adSVladimir Oltean } 509264e4adSVladimir Oltean 51045c45d1SVladimir Oltean static void dsa_port_fast_age(const struct dsa_port *dp) 52045c45d1SVladimir Oltean { 53045c45d1SVladimir Oltean struct dsa_switch *ds = dp->ds; 54045c45d1SVladimir Oltean 55045c45d1SVladimir Oltean if (!ds->ops->port_fast_age) 56045c45d1SVladimir Oltean return; 57045c45d1SVladimir Oltean 58045c45d1SVladimir Oltean ds->ops->port_fast_age(ds, dp->index); 599264e4adSVladimir Oltean 609264e4adSVladimir Oltean dsa_port_notify_bridge_fdb_flush(dp); 61045c45d1SVladimir Oltean } 62045c45d1SVladimir Oltean 63a4ffe09fSVladimir Oltean static bool dsa_port_can_configure_learning(struct dsa_port *dp) 64a4ffe09fSVladimir Oltean { 65a4ffe09fSVladimir Oltean struct switchdev_brport_flags flags = { 66a4ffe09fSVladimir Oltean .mask = BR_LEARNING, 67a4ffe09fSVladimir Oltean }; 68a4ffe09fSVladimir Oltean struct dsa_switch *ds = dp->ds; 69a4ffe09fSVladimir Oltean int err; 70a4ffe09fSVladimir Oltean 71a4ffe09fSVladimir Oltean if (!ds->ops->port_bridge_flags || !ds->ops->port_pre_bridge_flags) 72a4ffe09fSVladimir Oltean return false; 73a4ffe09fSVladimir Oltean 74a4ffe09fSVladimir Oltean err = ds->ops->port_pre_bridge_flags(ds, dp->index, flags, NULL); 75a4ffe09fSVladimir Oltean return !err; 76a4ffe09fSVladimir Oltean } 77a4ffe09fSVladimir Oltean 7839f32101SVladimir Oltean int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age) 79a40c175bSVivien Didelot { 80a40c175bSVivien Didelot struct dsa_switch *ds = dp->ds; 81a40c175bSVivien Didelot int port = dp->index; 82a40c175bSVivien Didelot 83bae33f2bSVladimir Oltean if (!ds->ops->port_stp_state_set) 84bae33f2bSVladimir Oltean return -EOPNOTSUPP; 85a40c175bSVivien Didelot 86a40c175bSVivien Didelot ds->ops->port_stp_state_set(ds, port, state); 87a40c175bSVivien Didelot 88a4ffe09fSVladimir Oltean if (!dsa_port_can_configure_learning(dp) || 89a4ffe09fSVladimir Oltean (do_fast_age && dp->learning)) { 90a40c175bSVivien Didelot /* Fast age FDB entries or flush appropriate forwarding database 91a40c175bSVivien Didelot * for the given port, if we are moving it from Learning or 92a40c175bSVivien Didelot * Forwarding state, to Disabled or Blocking or Listening state. 9339f32101SVladimir Oltean * Ports that were standalone before the STP state change don't 9439f32101SVladimir Oltean * need to fast age the FDB, since address learning is off in 9539f32101SVladimir Oltean * standalone mode. 96a40c175bSVivien Didelot */ 97a40c175bSVivien Didelot 98a40c175bSVivien Didelot if ((dp->stp_state == BR_STATE_LEARNING || 99a40c175bSVivien Didelot dp->stp_state == BR_STATE_FORWARDING) && 100a40c175bSVivien Didelot (state == BR_STATE_DISABLED || 101a40c175bSVivien Didelot state == BR_STATE_BLOCKING || 102a40c175bSVivien Didelot state == BR_STATE_LISTENING)) 103045c45d1SVladimir Oltean dsa_port_fast_age(dp); 104a40c175bSVivien Didelot } 105a40c175bSVivien Didelot 106a40c175bSVivien Didelot dp->stp_state = state; 107a40c175bSVivien Didelot 108a40c175bSVivien Didelot return 0; 109a40c175bSVivien Didelot } 110a40c175bSVivien Didelot 11139f32101SVladimir Oltean static void dsa_port_set_state_now(struct dsa_port *dp, u8 state, 11239f32101SVladimir Oltean bool do_fast_age) 113a40c175bSVivien Didelot { 114a40c175bSVivien Didelot int err; 115a40c175bSVivien Didelot 11639f32101SVladimir Oltean err = dsa_port_set_state(dp, state, do_fast_age); 117a40c175bSVivien Didelot if (err) 118a40c175bSVivien Didelot pr_err("DSA: failed to set STP state %u (%d)\n", state, err); 119a40c175bSVivien Didelot } 120cfbed329SVivien Didelot 1218640f8dcSRussell King int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy) 122fb8a6a2bSVivien Didelot { 123fb8a6a2bSVivien Didelot struct dsa_switch *ds = dp->ds; 124fb8a6a2bSVivien Didelot int port = dp->index; 125fb8a6a2bSVivien Didelot int err; 126fb8a6a2bSVivien Didelot 127fb8a6a2bSVivien Didelot if (ds->ops->port_enable) { 128fb8a6a2bSVivien Didelot err = ds->ops->port_enable(ds, port, phy); 129fb8a6a2bSVivien Didelot if (err) 130fb8a6a2bSVivien Didelot return err; 131fb8a6a2bSVivien Didelot } 132fb8a6a2bSVivien Didelot 133d3eed0e5SVladimir Oltean if (!dp->bridge) 13439f32101SVladimir Oltean dsa_port_set_state_now(dp, BR_STATE_FORWARDING, false); 135fb8a6a2bSVivien Didelot 1368640f8dcSRussell King if (dp->pl) 1378640f8dcSRussell King phylink_start(dp->pl); 1388640f8dcSRussell King 139fb8a6a2bSVivien Didelot return 0; 140fb8a6a2bSVivien Didelot } 141fb8a6a2bSVivien Didelot 1428640f8dcSRussell King int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy) 1438640f8dcSRussell King { 1448640f8dcSRussell King int err; 1458640f8dcSRussell King 1468640f8dcSRussell King rtnl_lock(); 1478640f8dcSRussell King err = dsa_port_enable_rt(dp, phy); 1488640f8dcSRussell King rtnl_unlock(); 1498640f8dcSRussell King 1508640f8dcSRussell King return err; 1518640f8dcSRussell King } 1528640f8dcSRussell King 1538640f8dcSRussell King void dsa_port_disable_rt(struct dsa_port *dp) 154fb8a6a2bSVivien Didelot { 155fb8a6a2bSVivien Didelot struct dsa_switch *ds = dp->ds; 156fb8a6a2bSVivien Didelot int port = dp->index; 157fb8a6a2bSVivien Didelot 1588640f8dcSRussell King if (dp->pl) 1598640f8dcSRussell King phylink_stop(dp->pl); 1608640f8dcSRussell King 161d3eed0e5SVladimir Oltean if (!dp->bridge) 16239f32101SVladimir Oltean dsa_port_set_state_now(dp, BR_STATE_DISABLED, false); 163fb8a6a2bSVivien Didelot 164fb8a6a2bSVivien Didelot if (ds->ops->port_disable) 16575104db0SAndrew Lunn ds->ops->port_disable(ds, port); 166fb8a6a2bSVivien Didelot } 167fb8a6a2bSVivien Didelot 1688640f8dcSRussell King void dsa_port_disable(struct dsa_port *dp) 1698640f8dcSRussell King { 1708640f8dcSRussell King rtnl_lock(); 1718640f8dcSRussell King dsa_port_disable_rt(dp); 1728640f8dcSRussell King rtnl_unlock(); 1738640f8dcSRussell King } 1748640f8dcSRussell King 1755961d6a1SVladimir Oltean static int dsa_port_inherit_brport_flags(struct dsa_port *dp, 1765961d6a1SVladimir Oltean struct netlink_ext_ack *extack) 1775e38c158SVladimir Oltean { 1785961d6a1SVladimir Oltean const unsigned long mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | 179b9e8b58fSHans Schultz BR_BCAST_FLOOD | BR_PORT_LOCKED; 1805961d6a1SVladimir Oltean struct net_device *brport_dev = dsa_port_to_bridge_port(dp); 1815961d6a1SVladimir Oltean int flag, err; 1825e38c158SVladimir Oltean 1835961d6a1SVladimir Oltean for_each_set_bit(flag, &mask, 32) { 1845961d6a1SVladimir Oltean struct switchdev_brport_flags flags = {0}; 1855e38c158SVladimir Oltean 1865961d6a1SVladimir Oltean flags.mask = BIT(flag); 1875e38c158SVladimir Oltean 1885961d6a1SVladimir Oltean if (br_port_flag_is_set(brport_dev, BIT(flag))) 1895961d6a1SVladimir Oltean flags.val = BIT(flag); 190e18f4c18SVladimir Oltean 1915961d6a1SVladimir Oltean err = dsa_port_bridge_flags(dp, flags, extack); 1925961d6a1SVladimir Oltean if (err && err != -EOPNOTSUPP) 1935961d6a1SVladimir Oltean return err; 1945e38c158SVladimir Oltean } 1955961d6a1SVladimir Oltean 1965961d6a1SVladimir Oltean return 0; 1975961d6a1SVladimir Oltean } 1985961d6a1SVladimir Oltean 1995961d6a1SVladimir Oltean static void dsa_port_clear_brport_flags(struct dsa_port *dp) 2005961d6a1SVladimir Oltean { 2015961d6a1SVladimir Oltean const unsigned long val = BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; 2025961d6a1SVladimir Oltean const unsigned long mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | 203b9e8b58fSHans Schultz BR_BCAST_FLOOD | BR_PORT_LOCKED; 2045961d6a1SVladimir Oltean int flag, err; 2055961d6a1SVladimir Oltean 2065961d6a1SVladimir Oltean for_each_set_bit(flag, &mask, 32) { 2075961d6a1SVladimir Oltean struct switchdev_brport_flags flags = {0}; 2085961d6a1SVladimir Oltean 2095961d6a1SVladimir Oltean flags.mask = BIT(flag); 2105961d6a1SVladimir Oltean flags.val = val & BIT(flag); 2115961d6a1SVladimir Oltean 2125961d6a1SVladimir Oltean err = dsa_port_bridge_flags(dp, flags, NULL); 2135961d6a1SVladimir Oltean if (err && err != -EOPNOTSUPP) 2145961d6a1SVladimir Oltean dev_err(dp->ds->dev, 2155961d6a1SVladimir Oltean "failed to clear bridge port flag %lu: %pe\n", 2165961d6a1SVladimir Oltean flags.val, ERR_PTR(err)); 2175961d6a1SVladimir Oltean } 2185961d6a1SVladimir Oltean } 2195961d6a1SVladimir Oltean 2204e51bf44SVladimir Oltean static int dsa_port_switchdev_sync_attrs(struct dsa_port *dp, 2215961d6a1SVladimir Oltean struct netlink_ext_ack *extack) 2225961d6a1SVladimir Oltean { 223010e269fSVladimir Oltean struct net_device *brport_dev = dsa_port_to_bridge_port(dp); 22436cbf39bSVladimir Oltean struct net_device *br = dsa_port_bridge_dev_get(dp); 2255961d6a1SVladimir Oltean int err; 2265961d6a1SVladimir Oltean 2275961d6a1SVladimir Oltean err = dsa_port_inherit_brport_flags(dp, extack); 2285961d6a1SVladimir Oltean if (err) 2295961d6a1SVladimir Oltean return err; 2305961d6a1SVladimir Oltean 23139f32101SVladimir Oltean err = dsa_port_set_state(dp, br_port_get_stp_state(brport_dev), false); 232010e269fSVladimir Oltean if (err && err != -EOPNOTSUPP) 233010e269fSVladimir Oltean return err; 234010e269fSVladimir Oltean 235010e269fSVladimir Oltean err = dsa_port_vlan_filtering(dp, br_vlan_enabled(br), extack); 236010e269fSVladimir Oltean if (err && err != -EOPNOTSUPP) 237010e269fSVladimir Oltean return err; 238010e269fSVladimir Oltean 239010e269fSVladimir Oltean err = dsa_port_ageing_time(dp, br_get_ageing_time(br)); 240010e269fSVladimir Oltean if (err && err != -EOPNOTSUPP) 241010e269fSVladimir Oltean return err; 242010e269fSVladimir Oltean 24374918945SVladimir Oltean return 0; 24474918945SVladimir Oltean } 24574918945SVladimir Oltean 24674918945SVladimir Oltean static void dsa_port_switchdev_unsync_attrs(struct dsa_port *dp) 2475961d6a1SVladimir Oltean { 2485961d6a1SVladimir Oltean /* Configure the port for standalone mode (no address learning, 2495961d6a1SVladimir Oltean * flood everything). 2505961d6a1SVladimir Oltean * The bridge only emits SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS events 2515961d6a1SVladimir Oltean * when the user requests it through netlink or sysfs, but not 2525961d6a1SVladimir Oltean * automatically at port join or leave, so we need to handle resetting 2535961d6a1SVladimir Oltean * the brport flags ourselves. But we even prefer it that way, because 2545961d6a1SVladimir Oltean * otherwise, some setups might never get the notification they need, 2555961d6a1SVladimir Oltean * for example, when a port leaves a LAG that offloads the bridge, 2565961d6a1SVladimir Oltean * it becomes standalone, but as far as the bridge is concerned, no 2575961d6a1SVladimir Oltean * port ever left. 2585961d6a1SVladimir Oltean */ 2595961d6a1SVladimir Oltean dsa_port_clear_brport_flags(dp); 2605961d6a1SVladimir Oltean 2615961d6a1SVladimir Oltean /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, 2625961d6a1SVladimir Oltean * so allow it to be in BR_STATE_FORWARDING to be kept functional 2635961d6a1SVladimir Oltean */ 26439f32101SVladimir Oltean dsa_port_set_state_now(dp, BR_STATE_FORWARDING, true); 265010e269fSVladimir Oltean 266010e269fSVladimir Oltean /* VLAN filtering is handled by dsa_switch_bridge_leave */ 267010e269fSVladimir Oltean 268010e269fSVladimir Oltean /* Ageing time may be global to the switch chip, so don't change it 269010e269fSVladimir Oltean * here because we have no good reason (or value) to change it to. 270010e269fSVladimir Oltean */ 2715e38c158SVladimir Oltean } 2725e38c158SVladimir Oltean 273947c8746SVladimir Oltean static int dsa_port_bridge_create(struct dsa_port *dp, 274947c8746SVladimir Oltean struct net_device *br, 275947c8746SVladimir Oltean struct netlink_ext_ack *extack) 276947c8746SVladimir Oltean { 277947c8746SVladimir Oltean struct dsa_switch *ds = dp->ds; 278d3eed0e5SVladimir Oltean struct dsa_bridge *bridge; 279947c8746SVladimir Oltean 280d3eed0e5SVladimir Oltean bridge = dsa_tree_bridge_find(ds->dst, br); 281d3eed0e5SVladimir Oltean if (bridge) { 282d3eed0e5SVladimir Oltean refcount_inc(&bridge->refcount); 283d3eed0e5SVladimir Oltean dp->bridge = bridge; 284947c8746SVladimir Oltean return 0; 285d3eed0e5SVladimir Oltean } 286947c8746SVladimir Oltean 287d3eed0e5SVladimir Oltean bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); 288d3eed0e5SVladimir Oltean if (!bridge) 289d3eed0e5SVladimir Oltean return -ENOMEM; 290d3eed0e5SVladimir Oltean 291d3eed0e5SVladimir Oltean refcount_set(&bridge->refcount, 1); 292d3eed0e5SVladimir Oltean 293d3eed0e5SVladimir Oltean bridge->dev = br; 294d3eed0e5SVladimir Oltean 295d3eed0e5SVladimir Oltean bridge->num = dsa_bridge_num_get(br, ds->max_num_bridges); 296d3eed0e5SVladimir Oltean if (ds->max_num_bridges && !bridge->num) { 297947c8746SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 298947c8746SVladimir Oltean "Range of offloadable bridges exceeded"); 299d3eed0e5SVladimir Oltean kfree(bridge); 300947c8746SVladimir Oltean return -EOPNOTSUPP; 301947c8746SVladimir Oltean } 302947c8746SVladimir Oltean 303d3eed0e5SVladimir Oltean dp->bridge = bridge; 304947c8746SVladimir Oltean 305947c8746SVladimir Oltean return 0; 306947c8746SVladimir Oltean } 307947c8746SVladimir Oltean 308947c8746SVladimir Oltean static void dsa_port_bridge_destroy(struct dsa_port *dp, 309947c8746SVladimir Oltean const struct net_device *br) 310947c8746SVladimir Oltean { 311d3eed0e5SVladimir Oltean struct dsa_bridge *bridge = dp->bridge; 312947c8746SVladimir Oltean 313d3eed0e5SVladimir Oltean dp->bridge = NULL; 314947c8746SVladimir Oltean 315d3eed0e5SVladimir Oltean if (!refcount_dec_and_test(&bridge->refcount)) 316d3eed0e5SVladimir Oltean return; 317947c8746SVladimir Oltean 318d3eed0e5SVladimir Oltean if (bridge->num) 319d3eed0e5SVladimir Oltean dsa_bridge_num_put(br, bridge->num); 320d3eed0e5SVladimir Oltean 321d3eed0e5SVladimir Oltean kfree(bridge); 322123abc06SVladimir Oltean } 323123abc06SVladimir Oltean 3242afc526aSVladimir Oltean int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br, 3252afc526aSVladimir Oltean struct netlink_ext_ack *extack) 326cfbed329SVivien Didelot { 327cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 328f66a6a69SVladimir Oltean .tree_index = dp->ds->dst->index, 329cfbed329SVivien Didelot .sw_index = dp->ds->index, 330cfbed329SVivien Didelot .port = dp->index, 331*06b9cce4SVladimir Oltean .extack = extack, 332cfbed329SVivien Didelot }; 3332f5dc00fSVladimir Oltean struct net_device *dev = dp->slave; 3342f5dc00fSVladimir Oltean struct net_device *brport_dev; 335cfbed329SVivien Didelot int err; 336cfbed329SVivien Didelot 337c1388063SRussell King /* Here the interface is already bridged. Reflect the current 338c1388063SRussell King * configuration so that drivers can program their chips accordingly. 339cfbed329SVivien Didelot */ 340947c8746SVladimir Oltean err = dsa_port_bridge_create(dp, br, extack); 341947c8746SVladimir Oltean if (err) 342947c8746SVladimir Oltean return err; 343cfbed329SVivien Didelot 3442f5dc00fSVladimir Oltean brport_dev = dsa_port_to_bridge_port(dp); 3452f5dc00fSVladimir Oltean 346d3eed0e5SVladimir Oltean info.bridge = *dp->bridge; 347f66a6a69SVladimir Oltean err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_JOIN, &info); 3485961d6a1SVladimir Oltean if (err) 3495961d6a1SVladimir Oltean goto out_rollback; 350cfbed329SVivien Didelot 351857fdd74SVladimir Oltean /* Drivers which support bridge TX forwarding should set this */ 352857fdd74SVladimir Oltean dp->bridge->tx_fwd_offload = info.tx_fwd_offload; 353123abc06SVladimir Oltean 3544e51bf44SVladimir Oltean err = switchdev_bridge_port_offload(brport_dev, dev, dp, 3554e51bf44SVladimir Oltean &dsa_slave_switchdev_notifier, 3564e51bf44SVladimir Oltean &dsa_slave_switchdev_blocking_notifier, 357857fdd74SVladimir Oltean dp->bridge->tx_fwd_offload, extack); 3585961d6a1SVladimir Oltean if (err) 3595961d6a1SVladimir Oltean goto out_rollback_unbridge; 3605961d6a1SVladimir Oltean 3614e51bf44SVladimir Oltean err = dsa_port_switchdev_sync_attrs(dp, extack); 3622f5dc00fSVladimir Oltean if (err) 3632f5dc00fSVladimir Oltean goto out_rollback_unoffload; 3642f5dc00fSVladimir Oltean 3655961d6a1SVladimir Oltean return 0; 3665961d6a1SVladimir Oltean 3672f5dc00fSVladimir Oltean out_rollback_unoffload: 3684e51bf44SVladimir Oltean switchdev_bridge_port_unoffload(brport_dev, dp, 3694e51bf44SVladimir Oltean &dsa_slave_switchdev_notifier, 3704e51bf44SVladimir Oltean &dsa_slave_switchdev_blocking_notifier); 3715961d6a1SVladimir Oltean out_rollback_unbridge: 3725961d6a1SVladimir Oltean dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info); 3735961d6a1SVladimir Oltean out_rollback: 374947c8746SVladimir Oltean dsa_port_bridge_destroy(dp, br); 375cfbed329SVivien Didelot return err; 376cfbed329SVivien Didelot } 377cfbed329SVivien Didelot 3784e51bf44SVladimir Oltean void dsa_port_pre_bridge_leave(struct dsa_port *dp, struct net_device *br) 37974918945SVladimir Oltean { 3802f5dc00fSVladimir Oltean struct net_device *brport_dev = dsa_port_to_bridge_port(dp); 3812f5dc00fSVladimir Oltean 38209dba21bSVladimir Oltean /* Don't try to unoffload something that is not offloaded */ 38309dba21bSVladimir Oltean if (!brport_dev) 38409dba21bSVladimir Oltean return; 38509dba21bSVladimir Oltean 3864e51bf44SVladimir Oltean switchdev_bridge_port_unoffload(brport_dev, dp, 3874e51bf44SVladimir Oltean &dsa_slave_switchdev_notifier, 3884e51bf44SVladimir Oltean &dsa_slave_switchdev_blocking_notifier); 389d7d0d423SVladimir Oltean 390d7d0d423SVladimir Oltean dsa_flush_workqueue(); 39174918945SVladimir Oltean } 39274918945SVladimir Oltean 393cfbed329SVivien Didelot void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) 394cfbed329SVivien Didelot { 395cfbed329SVivien Didelot struct dsa_notifier_bridge_info info = { 396f66a6a69SVladimir Oltean .tree_index = dp->ds->dst->index, 397cfbed329SVivien Didelot .sw_index = dp->ds->index, 398cfbed329SVivien Didelot .port = dp->index, 399cfbed329SVivien Didelot }; 400cfbed329SVivien Didelot int err; 401cfbed329SVivien Didelot 402342b6419SAlvin Šipraga /* If the port could not be offloaded to begin with, then 403342b6419SAlvin Šipraga * there is nothing to do. 404342b6419SAlvin Šipraga */ 405342b6419SAlvin Šipraga if (!dp->bridge) 406342b6419SAlvin Šipraga return; 407342b6419SAlvin Šipraga 408342b6419SAlvin Šipraga info.bridge = *dp->bridge; 409342b6419SAlvin Šipraga 410cfbed329SVivien Didelot /* Here the port is already unbridged. Reflect the current configuration 411cfbed329SVivien Didelot * so that drivers can program their chips accordingly. 412cfbed329SVivien Didelot */ 413947c8746SVladimir Oltean dsa_port_bridge_destroy(dp, br); 414cfbed329SVivien Didelot 415f66a6a69SVladimir Oltean err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info); 416cfbed329SVivien Didelot if (err) 417ab97462bSVladimir Oltean dev_err(dp->ds->dev, 418ab97462bSVladimir Oltean "port %d failed to notify DSA_NOTIFIER_BRIDGE_LEAVE: %pe\n", 419ab97462bSVladimir Oltean dp->index, ERR_PTR(err)); 420cfbed329SVivien Didelot 42174918945SVladimir Oltean dsa_port_switchdev_unsync_attrs(dp); 422cfbed329SVivien Didelot } 4234d61d304SVivien Didelot 424058102a6STobias Waldekranz int dsa_port_lag_change(struct dsa_port *dp, 425058102a6STobias Waldekranz struct netdev_lag_lower_state_info *linfo) 426058102a6STobias Waldekranz { 427058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 428058102a6STobias Waldekranz .sw_index = dp->ds->index, 429058102a6STobias Waldekranz .port = dp->index, 430058102a6STobias Waldekranz }; 431058102a6STobias Waldekranz bool tx_enabled; 432058102a6STobias Waldekranz 433dedd6a00SVladimir Oltean if (!dp->lag) 434058102a6STobias Waldekranz return 0; 435058102a6STobias Waldekranz 436058102a6STobias Waldekranz /* On statically configured aggregates (e.g. loadbalance 437058102a6STobias Waldekranz * without LACP) ports will always be tx_enabled, even if the 438058102a6STobias Waldekranz * link is down. Thus we require both link_up and tx_enabled 439058102a6STobias Waldekranz * in order to include it in the tx set. 440058102a6STobias Waldekranz */ 441058102a6STobias Waldekranz tx_enabled = linfo->link_up && linfo->tx_enabled; 442058102a6STobias Waldekranz 443058102a6STobias Waldekranz if (tx_enabled == dp->lag_tx_enabled) 444058102a6STobias Waldekranz return 0; 445058102a6STobias Waldekranz 446058102a6STobias Waldekranz dp->lag_tx_enabled = tx_enabled; 447058102a6STobias Waldekranz 448058102a6STobias Waldekranz return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info); 449058102a6STobias Waldekranz } 450058102a6STobias Waldekranz 451dedd6a00SVladimir Oltean static int dsa_port_lag_create(struct dsa_port *dp, 452dedd6a00SVladimir Oltean struct net_device *lag_dev) 453dedd6a00SVladimir Oltean { 454dedd6a00SVladimir Oltean struct dsa_switch *ds = dp->ds; 455dedd6a00SVladimir Oltean struct dsa_lag *lag; 456dedd6a00SVladimir Oltean 457dedd6a00SVladimir Oltean lag = dsa_tree_lag_find(ds->dst, lag_dev); 458dedd6a00SVladimir Oltean if (lag) { 459dedd6a00SVladimir Oltean refcount_inc(&lag->refcount); 460dedd6a00SVladimir Oltean dp->lag = lag; 461dedd6a00SVladimir Oltean return 0; 462dedd6a00SVladimir Oltean } 463dedd6a00SVladimir Oltean 464dedd6a00SVladimir Oltean lag = kzalloc(sizeof(*lag), GFP_KERNEL); 465dedd6a00SVladimir Oltean if (!lag) 466dedd6a00SVladimir Oltean return -ENOMEM; 467dedd6a00SVladimir Oltean 468dedd6a00SVladimir Oltean refcount_set(&lag->refcount, 1); 469e212fa7cSVladimir Oltean mutex_init(&lag->fdb_lock); 470e212fa7cSVladimir Oltean INIT_LIST_HEAD(&lag->fdbs); 471dedd6a00SVladimir Oltean lag->dev = lag_dev; 472dedd6a00SVladimir Oltean dsa_lag_map(ds->dst, lag); 473dedd6a00SVladimir Oltean dp->lag = lag; 474dedd6a00SVladimir Oltean 475dedd6a00SVladimir Oltean return 0; 476dedd6a00SVladimir Oltean } 477dedd6a00SVladimir Oltean 478dedd6a00SVladimir Oltean static void dsa_port_lag_destroy(struct dsa_port *dp) 479dedd6a00SVladimir Oltean { 480dedd6a00SVladimir Oltean struct dsa_lag *lag = dp->lag; 481dedd6a00SVladimir Oltean 482dedd6a00SVladimir Oltean dp->lag = NULL; 483dedd6a00SVladimir Oltean dp->lag_tx_enabled = false; 484dedd6a00SVladimir Oltean 485dedd6a00SVladimir Oltean if (!refcount_dec_and_test(&lag->refcount)) 486dedd6a00SVladimir Oltean return; 487dedd6a00SVladimir Oltean 488e212fa7cSVladimir Oltean WARN_ON(!list_empty(&lag->fdbs)); 489dedd6a00SVladimir Oltean dsa_lag_unmap(dp->ds->dst, lag); 490dedd6a00SVladimir Oltean kfree(lag); 491dedd6a00SVladimir Oltean } 492dedd6a00SVladimir Oltean 49346a76724SVladimir Oltean int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev, 4942afc526aSVladimir Oltean struct netdev_lag_upper_info *uinfo, 4952afc526aSVladimir Oltean struct netlink_ext_ack *extack) 496058102a6STobias Waldekranz { 497058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 498058102a6STobias Waldekranz .sw_index = dp->ds->index, 499058102a6STobias Waldekranz .port = dp->index, 500058102a6STobias Waldekranz .info = uinfo, 501058102a6STobias Waldekranz }; 502185c9a76SVladimir Oltean struct net_device *bridge_dev; 503058102a6STobias Waldekranz int err; 504058102a6STobias Waldekranz 505dedd6a00SVladimir Oltean err = dsa_port_lag_create(dp, lag_dev); 506dedd6a00SVladimir Oltean if (err) 507dedd6a00SVladimir Oltean goto err_lag_create; 508058102a6STobias Waldekranz 509dedd6a00SVladimir Oltean info.lag = *dp->lag; 510058102a6STobias Waldekranz err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info); 511185c9a76SVladimir Oltean if (err) 512185c9a76SVladimir Oltean goto err_lag_join; 513185c9a76SVladimir Oltean 51446a76724SVladimir Oltean bridge_dev = netdev_master_upper_dev_get(lag_dev); 515185c9a76SVladimir Oltean if (!bridge_dev || !netif_is_bridge_master(bridge_dev)) 516185c9a76SVladimir Oltean return 0; 517185c9a76SVladimir Oltean 5182afc526aSVladimir Oltean err = dsa_port_bridge_join(dp, bridge_dev, extack); 519185c9a76SVladimir Oltean if (err) 520185c9a76SVladimir Oltean goto err_bridge_join; 521185c9a76SVladimir Oltean 522185c9a76SVladimir Oltean return 0; 523185c9a76SVladimir Oltean 524185c9a76SVladimir Oltean err_bridge_join: 525185c9a76SVladimir Oltean dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); 526185c9a76SVladimir Oltean err_lag_join: 527dedd6a00SVladimir Oltean dsa_port_lag_destroy(dp); 528dedd6a00SVladimir Oltean err_lag_create: 529058102a6STobias Waldekranz return err; 530058102a6STobias Waldekranz } 531058102a6STobias Waldekranz 53246a76724SVladimir Oltean void dsa_port_pre_lag_leave(struct dsa_port *dp, struct net_device *lag_dev) 53374918945SVladimir Oltean { 53436cbf39bSVladimir Oltean struct net_device *br = dsa_port_bridge_dev_get(dp); 53536cbf39bSVladimir Oltean 53636cbf39bSVladimir Oltean if (br) 53736cbf39bSVladimir Oltean dsa_port_pre_bridge_leave(dp, br); 53874918945SVladimir Oltean } 53974918945SVladimir Oltean 54046a76724SVladimir Oltean void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag_dev) 541058102a6STobias Waldekranz { 54236cbf39bSVladimir Oltean struct net_device *br = dsa_port_bridge_dev_get(dp); 543058102a6STobias Waldekranz struct dsa_notifier_lag_info info = { 544058102a6STobias Waldekranz .sw_index = dp->ds->index, 545058102a6STobias Waldekranz .port = dp->index, 546058102a6STobias Waldekranz }; 547058102a6STobias Waldekranz int err; 548058102a6STobias Waldekranz 549dedd6a00SVladimir Oltean if (!dp->lag) 550058102a6STobias Waldekranz return; 551058102a6STobias Waldekranz 552058102a6STobias Waldekranz /* Port might have been part of a LAG that in turn was 553058102a6STobias Waldekranz * attached to a bridge. 554058102a6STobias Waldekranz */ 55536cbf39bSVladimir Oltean if (br) 55636cbf39bSVladimir Oltean dsa_port_bridge_leave(dp, br); 557058102a6STobias Waldekranz 558dedd6a00SVladimir Oltean info.lag = *dp->lag; 559dedd6a00SVladimir Oltean 560dedd6a00SVladimir Oltean dsa_port_lag_destroy(dp); 561058102a6STobias Waldekranz 562058102a6STobias Waldekranz err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); 563058102a6STobias Waldekranz if (err) 564ab97462bSVladimir Oltean dev_err(dp->ds->dev, 565ab97462bSVladimir Oltean "port %d failed to notify DSA_NOTIFIER_LAG_LEAVE: %pe\n", 566ab97462bSVladimir Oltean dp->index, ERR_PTR(err)); 567058102a6STobias Waldekranz } 568058102a6STobias Waldekranz 569adb256ebSVladimir Oltean /* Must be called under rcu_read_lock() */ 5708f5d16f6SVladimir Oltean static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp, 57189153ed6SVladimir Oltean bool vlan_filtering, 57289153ed6SVladimir Oltean struct netlink_ext_ack *extack) 5738f5d16f6SVladimir Oltean { 5748f5d16f6SVladimir Oltean struct dsa_switch *ds = dp->ds; 575d0004a02SVladimir Oltean struct dsa_port *other_dp; 576d0004a02SVladimir Oltean int err; 577adb256ebSVladimir Oltean 578adb256ebSVladimir Oltean /* VLAN awareness was off, so the question is "can we turn it on". 579adb256ebSVladimir Oltean * We may have had 8021q uppers, those need to go. Make sure we don't 580adb256ebSVladimir Oltean * enter an inconsistent state: deny changing the VLAN awareness state 581adb256ebSVladimir Oltean * as long as we have 8021q uppers. 582adb256ebSVladimir Oltean */ 58357d77986SVladimir Oltean if (vlan_filtering && dsa_port_is_user(dp)) { 58436cbf39bSVladimir Oltean struct net_device *br = dsa_port_bridge_dev_get(dp); 585adb256ebSVladimir Oltean struct net_device *upper_dev, *slave = dp->slave; 586adb256ebSVladimir Oltean struct list_head *iter; 587adb256ebSVladimir Oltean 588adb256ebSVladimir Oltean netdev_for_each_upper_dev_rcu(slave, upper_dev, iter) { 589adb256ebSVladimir Oltean struct bridge_vlan_info br_info; 590adb256ebSVladimir Oltean u16 vid; 591adb256ebSVladimir Oltean 592adb256ebSVladimir Oltean if (!is_vlan_dev(upper_dev)) 593adb256ebSVladimir Oltean continue; 594adb256ebSVladimir Oltean 595adb256ebSVladimir Oltean vid = vlan_dev_vlan_id(upper_dev); 596adb256ebSVladimir Oltean 597adb256ebSVladimir Oltean /* br_vlan_get_info() returns -EINVAL or -ENOENT if the 598adb256ebSVladimir Oltean * device, respectively the VID is not found, returning 599adb256ebSVladimir Oltean * 0 means success, which is a failure for us here. 600adb256ebSVladimir Oltean */ 601adb256ebSVladimir Oltean err = br_vlan_get_info(br, vid, &br_info); 602adb256ebSVladimir Oltean if (err == 0) { 60389153ed6SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 60489153ed6SVladimir Oltean "Must first remove VLAN uppers having VIDs also present in bridge"); 605adb256ebSVladimir Oltean return false; 606adb256ebSVladimir Oltean } 607adb256ebSVladimir Oltean } 608adb256ebSVladimir Oltean } 6098f5d16f6SVladimir Oltean 6108f5d16f6SVladimir Oltean if (!ds->vlan_filtering_is_global) 6118f5d16f6SVladimir Oltean return true; 6128f5d16f6SVladimir Oltean 6138f5d16f6SVladimir Oltean /* For cases where enabling/disabling VLAN awareness is global to the 6148f5d16f6SVladimir Oltean * switch, we need to handle the case where multiple bridges span 6158f5d16f6SVladimir Oltean * different ports of the same switch device and one of them has a 6168f5d16f6SVladimir Oltean * different setting than what is being requested. 6178f5d16f6SVladimir Oltean */ 618d0004a02SVladimir Oltean dsa_switch_for_each_port(other_dp, ds) { 61936cbf39bSVladimir Oltean struct net_device *other_br = dsa_port_bridge_dev_get(other_dp); 6208f5d16f6SVladimir Oltean 6218f5d16f6SVladimir Oltean /* If it's the same bridge, it also has same 6228f5d16f6SVladimir Oltean * vlan_filtering setting => no need to check 6238f5d16f6SVladimir Oltean */ 62436cbf39bSVladimir Oltean if (!other_br || other_br == dsa_port_bridge_dev_get(dp)) 6258f5d16f6SVladimir Oltean continue; 62636cbf39bSVladimir Oltean 62736cbf39bSVladimir Oltean if (br_vlan_enabled(other_br) != vlan_filtering) { 62889153ed6SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 62989153ed6SVladimir Oltean "VLAN filtering is a global setting"); 6308f5d16f6SVladimir Oltean return false; 6318f5d16f6SVladimir Oltean } 6328f5d16f6SVladimir Oltean } 6338f5d16f6SVladimir Oltean return true; 6348f5d16f6SVladimir Oltean } 6358f5d16f6SVladimir Oltean 63689153ed6SVladimir Oltean int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, 63789153ed6SVladimir Oltean struct netlink_ext_ack *extack) 6384d61d304SVivien Didelot { 63906cfb2dfSVladimir Oltean bool old_vlan_filtering = dsa_port_is_vlan_filtering(dp); 6404d61d304SVivien Didelot struct dsa_switch *ds = dp->ds; 641adb256ebSVladimir Oltean bool apply; 642bae33f2bSVladimir Oltean int err; 643adb256ebSVladimir Oltean 6448f5d16f6SVladimir Oltean if (!ds->ops->port_vlan_filtering) 645707ec383SVladimir Oltean return -EOPNOTSUPP; 6468f5d16f6SVladimir Oltean 647adb256ebSVladimir Oltean /* We are called from dsa_slave_switchdev_blocking_event(), 648adb256ebSVladimir Oltean * which is not under rcu_read_lock(), unlike 649adb256ebSVladimir Oltean * dsa_slave_switchdev_event(). 650adb256ebSVladimir Oltean */ 651adb256ebSVladimir Oltean rcu_read_lock(); 65289153ed6SVladimir Oltean apply = dsa_port_can_apply_vlan_filtering(dp, vlan_filtering, extack); 653adb256ebSVladimir Oltean rcu_read_unlock(); 654adb256ebSVladimir Oltean if (!apply) 6558f5d16f6SVladimir Oltean return -EINVAL; 656707ec383SVladimir Oltean 657ec9121e7SVladimir Oltean if (dsa_port_is_vlan_filtering(dp) == vlan_filtering) 658ec9121e7SVladimir Oltean return 0; 659ec9121e7SVladimir Oltean 66089153ed6SVladimir Oltean err = ds->ops->port_vlan_filtering(ds, dp->index, vlan_filtering, 66189153ed6SVladimir Oltean extack); 66233162e9aSVladimir Oltean if (err) 66333162e9aSVladimir Oltean return err; 6648f5d16f6SVladimir Oltean 66506cfb2dfSVladimir Oltean if (ds->vlan_filtering_is_global) { 666d0004a02SVladimir Oltean struct dsa_port *other_dp; 66706cfb2dfSVladimir Oltean 66814574676SVladimir Oltean ds->vlan_filtering = vlan_filtering; 66906cfb2dfSVladimir Oltean 670d0004a02SVladimir Oltean dsa_switch_for_each_user_port(other_dp, ds) { 671d0004a02SVladimir Oltean struct net_device *slave = dp->slave; 67206cfb2dfSVladimir Oltean 67306cfb2dfSVladimir Oltean /* We might be called in the unbind path, so not 67406cfb2dfSVladimir Oltean * all slave devices might still be registered. 67506cfb2dfSVladimir Oltean */ 67606cfb2dfSVladimir Oltean if (!slave) 67706cfb2dfSVladimir Oltean continue; 67806cfb2dfSVladimir Oltean 67906cfb2dfSVladimir Oltean err = dsa_slave_manage_vlan_filtering(slave, 68006cfb2dfSVladimir Oltean vlan_filtering); 68106cfb2dfSVladimir Oltean if (err) 68206cfb2dfSVladimir Oltean goto restore; 68306cfb2dfSVladimir Oltean } 68406cfb2dfSVladimir Oltean } else { 68533162e9aSVladimir Oltean dp->vlan_filtering = vlan_filtering; 6862e554a7aSVladimir Oltean 68706cfb2dfSVladimir Oltean err = dsa_slave_manage_vlan_filtering(dp->slave, 68806cfb2dfSVladimir Oltean vlan_filtering); 68906cfb2dfSVladimir Oltean if (err) 69006cfb2dfSVladimir Oltean goto restore; 69106cfb2dfSVladimir Oltean } 69206cfb2dfSVladimir Oltean 6934d61d304SVivien Didelot return 0; 69406cfb2dfSVladimir Oltean 69506cfb2dfSVladimir Oltean restore: 69606cfb2dfSVladimir Oltean ds->ops->port_vlan_filtering(ds, dp->index, old_vlan_filtering, NULL); 69706cfb2dfSVladimir Oltean 69806cfb2dfSVladimir Oltean if (ds->vlan_filtering_is_global) 69906cfb2dfSVladimir Oltean ds->vlan_filtering = old_vlan_filtering; 70006cfb2dfSVladimir Oltean else 70106cfb2dfSVladimir Oltean dp->vlan_filtering = old_vlan_filtering; 70206cfb2dfSVladimir Oltean 70306cfb2dfSVladimir Oltean return err; 7044d61d304SVivien Didelot } 705d87bd94eSVivien Didelot 70654a0ed0dSRussell King /* This enforces legacy behavior for switch drivers which assume they can't 70754a0ed0dSRussell King * receive VLAN configuration when enslaved to a bridge with vlan_filtering=0 70854a0ed0dSRussell King */ 70954a0ed0dSRussell King bool dsa_port_skip_vlan_configuration(struct dsa_port *dp) 71054a0ed0dSRussell King { 71136cbf39bSVladimir Oltean struct net_device *br = dsa_port_bridge_dev_get(dp); 71254a0ed0dSRussell King struct dsa_switch *ds = dp->ds; 71354a0ed0dSRussell King 71436cbf39bSVladimir Oltean if (!br) 71554a0ed0dSRussell King return false; 71654a0ed0dSRussell King 71736cbf39bSVladimir Oltean return !ds->configure_vlan_while_not_filtering && !br_vlan_enabled(br); 71854a0ed0dSRussell King } 71954a0ed0dSRussell King 720bae33f2bSVladimir Oltean int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock) 721d87bd94eSVivien Didelot { 722d87bd94eSVivien Didelot unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock); 723d87bd94eSVivien Didelot unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies); 724bae33f2bSVladimir Oltean struct dsa_notifier_ageing_time_info info; 725bae33f2bSVladimir Oltean int err; 726d87bd94eSVivien Didelot 727bae33f2bSVladimir Oltean info.ageing_time = ageing_time; 728bae33f2bSVladimir Oltean 729bae33f2bSVladimir Oltean err = dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); 730bae33f2bSVladimir Oltean if (err) 731bae33f2bSVladimir Oltean return err; 732d87bd94eSVivien Didelot 733d87bd94eSVivien Didelot dp->ageing_time = ageing_time; 734d87bd94eSVivien Didelot 73577b61365SVladimir Oltean return 0; 736d87bd94eSVivien Didelot } 737d1cffff0SVivien Didelot 738e18f4c18SVladimir Oltean int dsa_port_pre_bridge_flags(const struct dsa_port *dp, 739a8b659e7SVladimir Oltean struct switchdev_brport_flags flags, 740a8b659e7SVladimir Oltean struct netlink_ext_ack *extack) 741ea87005aSFlorian Fainelli { 742ea87005aSFlorian Fainelli struct dsa_switch *ds = dp->ds; 743ea87005aSFlorian Fainelli 744a8b659e7SVladimir Oltean if (!ds->ops->port_pre_bridge_flags) 745ea87005aSFlorian Fainelli return -EINVAL; 746ea87005aSFlorian Fainelli 747a8b659e7SVladimir Oltean return ds->ops->port_pre_bridge_flags(ds, dp->index, flags, extack); 748ea87005aSFlorian Fainelli } 749ea87005aSFlorian Fainelli 750045c45d1SVladimir Oltean int dsa_port_bridge_flags(struct dsa_port *dp, 751a8b659e7SVladimir Oltean struct switchdev_brport_flags flags, 752a8b659e7SVladimir Oltean struct netlink_ext_ack *extack) 75357652796SRussell King { 75457652796SRussell King struct dsa_switch *ds = dp->ds; 755045c45d1SVladimir Oltean int err; 75657652796SRussell King 757a8b659e7SVladimir Oltean if (!ds->ops->port_bridge_flags) 75870a7c484SOleksij Rempel return -EOPNOTSUPP; 75957652796SRussell King 760045c45d1SVladimir Oltean err = ds->ops->port_bridge_flags(ds, dp->index, flags, extack); 761045c45d1SVladimir Oltean if (err) 762045c45d1SVladimir Oltean return err; 763045c45d1SVladimir Oltean 764045c45d1SVladimir Oltean if (flags.mask & BR_LEARNING) { 765045c45d1SVladimir Oltean bool learning = flags.val & BR_LEARNING; 766045c45d1SVladimir Oltean 767045c45d1SVladimir Oltean if (learning == dp->learning) 768045c45d1SVladimir Oltean return 0; 769045c45d1SVladimir Oltean 770bee7c577SVladimir Oltean if ((dp->learning && !learning) && 771bee7c577SVladimir Oltean (dp->stp_state == BR_STATE_LEARNING || 772bee7c577SVladimir Oltean dp->stp_state == BR_STATE_FORWARDING)) 773045c45d1SVladimir Oltean dsa_port_fast_age(dp); 774045c45d1SVladimir Oltean 775045c45d1SVladimir Oltean dp->learning = learning; 776045c45d1SVladimir Oltean } 777045c45d1SVladimir Oltean 778045c45d1SVladimir Oltean return 0; 77957652796SRussell King } 78057652796SRussell King 781bfcb8132SVladimir Oltean int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu, 78288faba20SVladimir Oltean bool targeted_match) 783bfcb8132SVladimir Oltean { 784bfcb8132SVladimir Oltean struct dsa_notifier_mtu_info info = { 785bfcb8132SVladimir Oltean .sw_index = dp->ds->index, 78688faba20SVladimir Oltean .targeted_match = targeted_match, 787bfcb8132SVladimir Oltean .port = dp->index, 788bfcb8132SVladimir Oltean .mtu = new_mtu, 789bfcb8132SVladimir Oltean }; 790bfcb8132SVladimir Oltean 791bfcb8132SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_MTU, &info); 792bfcb8132SVladimir Oltean } 793bfcb8132SVladimir Oltean 7942acf4e6aSArkadi Sharshevsky int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, 7952acf4e6aSArkadi Sharshevsky u16 vid) 796d1cffff0SVivien Didelot { 797685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 798685fb6a4SVivien Didelot .sw_index = dp->ds->index, 799685fb6a4SVivien Didelot .port = dp->index, 8002acf4e6aSArkadi Sharshevsky .addr = addr, 8012acf4e6aSArkadi Sharshevsky .vid = vid, 802c2693363SVladimir Oltean .db = { 803c2693363SVladimir Oltean .type = DSA_DB_BRIDGE, 804c2693363SVladimir Oltean .bridge = *dp->bridge, 805c2693363SVladimir Oltean }, 806685fb6a4SVivien Didelot }; 807d1cffff0SVivien Didelot 808c2693363SVladimir Oltean /* Refcounting takes bridge.num as a key, and should be global for all 809c2693363SVladimir Oltean * bridges in the absence of FDB isolation, and per bridge otherwise. 810c2693363SVladimir Oltean * Force the bridge.num to zero here in the absence of FDB isolation. 811c2693363SVladimir Oltean */ 812c2693363SVladimir Oltean if (!dp->ds->fdb_isolation) 813c2693363SVladimir Oltean info.db.bridge.num = 0; 814c2693363SVladimir Oltean 815685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info); 816d1cffff0SVivien Didelot } 817d1cffff0SVivien Didelot 8182acf4e6aSArkadi Sharshevsky int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, 8192acf4e6aSArkadi Sharshevsky u16 vid) 820d1cffff0SVivien Didelot { 821685fb6a4SVivien Didelot struct dsa_notifier_fdb_info info = { 822685fb6a4SVivien Didelot .sw_index = dp->ds->index, 823685fb6a4SVivien Didelot .port = dp->index, 8242acf4e6aSArkadi Sharshevsky .addr = addr, 8252acf4e6aSArkadi Sharshevsky .vid = vid, 826c2693363SVladimir Oltean .db = { 827c2693363SVladimir Oltean .type = DSA_DB_BRIDGE, 828c2693363SVladimir Oltean .bridge = *dp->bridge, 829c2693363SVladimir Oltean }, 830685fb6a4SVivien Didelot }; 831d1cffff0SVivien Didelot 832c2693363SVladimir Oltean if (!dp->ds->fdb_isolation) 833c2693363SVladimir Oltean info.db.bridge.num = 0; 834c2693363SVladimir Oltean 835685fb6a4SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); 836d1cffff0SVivien Didelot } 837d1cffff0SVivien Didelot 8383dc80afcSVladimir Oltean int dsa_port_host_fdb_add(struct dsa_port *dp, const unsigned char *addr, 8393dc80afcSVladimir Oltean u16 vid) 8403dc80afcSVladimir Oltean { 8413dc80afcSVladimir Oltean struct dsa_notifier_fdb_info info = { 8423dc80afcSVladimir Oltean .sw_index = dp->ds->index, 8433dc80afcSVladimir Oltean .port = dp->index, 8443dc80afcSVladimir Oltean .addr = addr, 8453dc80afcSVladimir Oltean .vid = vid, 846c2693363SVladimir Oltean .db = { 847c2693363SVladimir Oltean .type = DSA_DB_BRIDGE, 848c2693363SVladimir Oltean .bridge = *dp->bridge, 849c2693363SVladimir Oltean }, 8503dc80afcSVladimir Oltean }; 85126ee7b06SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 85226ee7b06SVladimir Oltean int err; 85326ee7b06SVladimir Oltean 8548940e6b6SVladimir Oltean /* Avoid a call to __dev_set_promiscuity() on the master, which 8558940e6b6SVladimir Oltean * requires rtnl_lock(), since we can't guarantee that is held here, 8568940e6b6SVladimir Oltean * and we can't take it either. 8578940e6b6SVladimir Oltean */ 8588940e6b6SVladimir Oltean if (cpu_dp->master->priv_flags & IFF_UNICAST_FLT) { 85926ee7b06SVladimir Oltean err = dev_uc_add(cpu_dp->master, addr); 86026ee7b06SVladimir Oltean if (err) 86126ee7b06SVladimir Oltean return err; 8628940e6b6SVladimir Oltean } 8633dc80afcSVladimir Oltean 864c2693363SVladimir Oltean if (!dp->ds->fdb_isolation) 865c2693363SVladimir Oltean info.db.bridge.num = 0; 866c2693363SVladimir Oltean 8673dc80afcSVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_ADD, &info); 8683dc80afcSVladimir Oltean } 8693dc80afcSVladimir Oltean 8703dc80afcSVladimir Oltean int dsa_port_host_fdb_del(struct dsa_port *dp, const unsigned char *addr, 8713dc80afcSVladimir Oltean u16 vid) 8723dc80afcSVladimir Oltean { 8733dc80afcSVladimir Oltean struct dsa_notifier_fdb_info info = { 8743dc80afcSVladimir Oltean .sw_index = dp->ds->index, 8753dc80afcSVladimir Oltean .port = dp->index, 8763dc80afcSVladimir Oltean .addr = addr, 8773dc80afcSVladimir Oltean .vid = vid, 878c2693363SVladimir Oltean .db = { 879c2693363SVladimir Oltean .type = DSA_DB_BRIDGE, 880c2693363SVladimir Oltean .bridge = *dp->bridge, 881c2693363SVladimir Oltean }, 8823dc80afcSVladimir Oltean }; 88326ee7b06SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 88426ee7b06SVladimir Oltean int err; 88526ee7b06SVladimir Oltean 8868940e6b6SVladimir Oltean if (cpu_dp->master->priv_flags & IFF_UNICAST_FLT) { 88726ee7b06SVladimir Oltean err = dev_uc_del(cpu_dp->master, addr); 88826ee7b06SVladimir Oltean if (err) 88926ee7b06SVladimir Oltean return err; 8908940e6b6SVladimir Oltean } 8913dc80afcSVladimir Oltean 892c2693363SVladimir Oltean if (!dp->ds->fdb_isolation) 893c2693363SVladimir Oltean info.db.bridge.num = 0; 894c2693363SVladimir Oltean 8953dc80afcSVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_DEL, &info); 8963dc80afcSVladimir Oltean } 8973dc80afcSVladimir Oltean 898e212fa7cSVladimir Oltean int dsa_port_lag_fdb_add(struct dsa_port *dp, const unsigned char *addr, 899e212fa7cSVladimir Oltean u16 vid) 900e212fa7cSVladimir Oltean { 901e212fa7cSVladimir Oltean struct dsa_notifier_lag_fdb_info info = { 902e212fa7cSVladimir Oltean .lag = dp->lag, 903e212fa7cSVladimir Oltean .addr = addr, 904e212fa7cSVladimir Oltean .vid = vid, 905c2693363SVladimir Oltean .db = { 906c2693363SVladimir Oltean .type = DSA_DB_BRIDGE, 907c2693363SVladimir Oltean .bridge = *dp->bridge, 908c2693363SVladimir Oltean }, 909e212fa7cSVladimir Oltean }; 910e212fa7cSVladimir Oltean 911c2693363SVladimir Oltean if (!dp->ds->fdb_isolation) 912c2693363SVladimir Oltean info.db.bridge.num = 0; 913c2693363SVladimir Oltean 914e212fa7cSVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_LAG_FDB_ADD, &info); 915e212fa7cSVladimir Oltean } 916e212fa7cSVladimir Oltean 917e212fa7cSVladimir Oltean int dsa_port_lag_fdb_del(struct dsa_port *dp, const unsigned char *addr, 918e212fa7cSVladimir Oltean u16 vid) 919e212fa7cSVladimir Oltean { 920e212fa7cSVladimir Oltean struct dsa_notifier_lag_fdb_info info = { 921e212fa7cSVladimir Oltean .lag = dp->lag, 922e212fa7cSVladimir Oltean .addr = addr, 923e212fa7cSVladimir Oltean .vid = vid, 924c2693363SVladimir Oltean .db = { 925c2693363SVladimir Oltean .type = DSA_DB_BRIDGE, 926c2693363SVladimir Oltean .bridge = *dp->bridge, 927c2693363SVladimir Oltean }, 928e212fa7cSVladimir Oltean }; 929e212fa7cSVladimir Oltean 930c2693363SVladimir Oltean if (!dp->ds->fdb_isolation) 931c2693363SVladimir Oltean info.db.bridge.num = 0; 932c2693363SVladimir Oltean 933e212fa7cSVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_LAG_FDB_DEL, &info); 934e212fa7cSVladimir Oltean } 935e212fa7cSVladimir Oltean 936de40fc5dSVivien Didelot int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data) 937de40fc5dSVivien Didelot { 938de40fc5dSVivien Didelot struct dsa_switch *ds = dp->ds; 939de40fc5dSVivien Didelot int port = dp->index; 940de40fc5dSVivien Didelot 941de40fc5dSVivien Didelot if (!ds->ops->port_fdb_dump) 942de40fc5dSVivien Didelot return -EOPNOTSUPP; 943de40fc5dSVivien Didelot 944de40fc5dSVivien Didelot return ds->ops->port_fdb_dump(ds, port, cb, data); 945de40fc5dSVivien Didelot } 946de40fc5dSVivien Didelot 947bb9f6031SAndrew Lunn int dsa_port_mdb_add(const struct dsa_port *dp, 948ffb68fc5SVladimir Oltean const struct switchdev_obj_port_mdb *mdb) 9493a9afea3SVivien Didelot { 9508ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 9518ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 9528ae5bcdcSVivien Didelot .port = dp->index, 9538ae5bcdcSVivien Didelot .mdb = mdb, 954c2693363SVladimir Oltean .db = { 955c2693363SVladimir Oltean .type = DSA_DB_BRIDGE, 956c2693363SVladimir Oltean .bridge = *dp->bridge, 957c2693363SVladimir Oltean }, 9588ae5bcdcSVivien Didelot }; 9593a9afea3SVivien Didelot 960c2693363SVladimir Oltean if (!dp->ds->fdb_isolation) 961c2693363SVladimir Oltean info.db.bridge.num = 0; 962c2693363SVladimir Oltean 9638ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info); 9643a9afea3SVivien Didelot } 9653a9afea3SVivien Didelot 966bb9f6031SAndrew Lunn int dsa_port_mdb_del(const struct dsa_port *dp, 9673a9afea3SVivien Didelot const struct switchdev_obj_port_mdb *mdb) 9683a9afea3SVivien Didelot { 9698ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info info = { 9708ae5bcdcSVivien Didelot .sw_index = dp->ds->index, 9718ae5bcdcSVivien Didelot .port = dp->index, 9728ae5bcdcSVivien Didelot .mdb = mdb, 973c2693363SVladimir Oltean .db = { 974c2693363SVladimir Oltean .type = DSA_DB_BRIDGE, 975c2693363SVladimir Oltean .bridge = *dp->bridge, 976c2693363SVladimir Oltean }, 9778ae5bcdcSVivien Didelot }; 9783a9afea3SVivien Didelot 979c2693363SVladimir Oltean if (!dp->ds->fdb_isolation) 980c2693363SVladimir Oltean info.db.bridge.num = 0; 981c2693363SVladimir Oltean 9828ae5bcdcSVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); 9833a9afea3SVivien Didelot } 9843a9afea3SVivien Didelot 985b8e997c4SVladimir Oltean int dsa_port_host_mdb_add(const struct dsa_port *dp, 986b8e997c4SVladimir Oltean const struct switchdev_obj_port_mdb *mdb) 987b8e997c4SVladimir Oltean { 988b8e997c4SVladimir Oltean struct dsa_notifier_mdb_info info = { 989b8e997c4SVladimir Oltean .sw_index = dp->ds->index, 990b8e997c4SVladimir Oltean .port = dp->index, 991b8e997c4SVladimir Oltean .mdb = mdb, 992c2693363SVladimir Oltean .db = { 993c2693363SVladimir Oltean .type = DSA_DB_BRIDGE, 994c2693363SVladimir Oltean .bridge = *dp->bridge, 995c2693363SVladimir Oltean }, 996b8e997c4SVladimir Oltean }; 99726ee7b06SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 99826ee7b06SVladimir Oltean int err; 99926ee7b06SVladimir Oltean 100026ee7b06SVladimir Oltean err = dev_mc_add(cpu_dp->master, mdb->addr); 100126ee7b06SVladimir Oltean if (err) 100226ee7b06SVladimir Oltean return err; 1003b8e997c4SVladimir Oltean 1004c2693363SVladimir Oltean if (!dp->ds->fdb_isolation) 1005c2693363SVladimir Oltean info.db.bridge.num = 0; 1006c2693363SVladimir Oltean 1007b8e997c4SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_ADD, &info); 1008b8e997c4SVladimir Oltean } 1009b8e997c4SVladimir Oltean 1010b8e997c4SVladimir Oltean int dsa_port_host_mdb_del(const struct dsa_port *dp, 1011b8e997c4SVladimir Oltean const struct switchdev_obj_port_mdb *mdb) 1012b8e997c4SVladimir Oltean { 1013b8e997c4SVladimir Oltean struct dsa_notifier_mdb_info info = { 1014b8e997c4SVladimir Oltean .sw_index = dp->ds->index, 1015b8e997c4SVladimir Oltean .port = dp->index, 1016b8e997c4SVladimir Oltean .mdb = mdb, 1017c2693363SVladimir Oltean .db = { 1018c2693363SVladimir Oltean .type = DSA_DB_BRIDGE, 1019c2693363SVladimir Oltean .bridge = *dp->bridge, 1020c2693363SVladimir Oltean }, 1021b8e997c4SVladimir Oltean }; 102226ee7b06SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 102326ee7b06SVladimir Oltean int err; 102426ee7b06SVladimir Oltean 102526ee7b06SVladimir Oltean err = dev_mc_del(cpu_dp->master, mdb->addr); 102626ee7b06SVladimir Oltean if (err) 102726ee7b06SVladimir Oltean return err; 1028b8e997c4SVladimir Oltean 1029c2693363SVladimir Oltean if (!dp->ds->fdb_isolation) 1030c2693363SVladimir Oltean info.db.bridge.num = 0; 1031c2693363SVladimir Oltean 1032b8e997c4SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_DEL, &info); 1033b8e997c4SVladimir Oltean } 1034b8e997c4SVladimir Oltean 1035076e7133SVivien Didelot int dsa_port_vlan_add(struct dsa_port *dp, 103631046a5fSVladimir Oltean const struct switchdev_obj_port_vlan *vlan, 103731046a5fSVladimir Oltean struct netlink_ext_ack *extack) 1038076e7133SVivien Didelot { 1039d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 1040d0c627b8SVivien Didelot .sw_index = dp->ds->index, 1041d0c627b8SVivien Didelot .port = dp->index, 1042d0c627b8SVivien Didelot .vlan = vlan, 104331046a5fSVladimir Oltean .extack = extack, 1044d0c627b8SVivien Didelot }; 1045076e7133SVivien Didelot 1046d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); 1047076e7133SVivien Didelot } 1048076e7133SVivien Didelot 1049076e7133SVivien Didelot int dsa_port_vlan_del(struct dsa_port *dp, 1050076e7133SVivien Didelot const struct switchdev_obj_port_vlan *vlan) 1051076e7133SVivien Didelot { 1052d0c627b8SVivien Didelot struct dsa_notifier_vlan_info info = { 1053d0c627b8SVivien Didelot .sw_index = dp->ds->index, 1054d0c627b8SVivien Didelot .port = dp->index, 1055d0c627b8SVivien Didelot .vlan = vlan, 1056d0c627b8SVivien Didelot }; 1057076e7133SVivien Didelot 1058d0c627b8SVivien Didelot return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); 1059076e7133SVivien Didelot } 106057ab1ca2SVivien Didelot 1061134ef238SVladimir Oltean int dsa_port_host_vlan_add(struct dsa_port *dp, 1062134ef238SVladimir Oltean const struct switchdev_obj_port_vlan *vlan, 1063134ef238SVladimir Oltean struct netlink_ext_ack *extack) 1064134ef238SVladimir Oltean { 1065134ef238SVladimir Oltean struct dsa_notifier_vlan_info info = { 1066134ef238SVladimir Oltean .sw_index = dp->ds->index, 1067134ef238SVladimir Oltean .port = dp->index, 1068134ef238SVladimir Oltean .vlan = vlan, 1069134ef238SVladimir Oltean .extack = extack, 1070134ef238SVladimir Oltean }; 1071134ef238SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 1072134ef238SVladimir Oltean int err; 1073134ef238SVladimir Oltean 1074134ef238SVladimir Oltean err = dsa_port_notify(dp, DSA_NOTIFIER_HOST_VLAN_ADD, &info); 1075134ef238SVladimir Oltean if (err && err != -EOPNOTSUPP) 1076134ef238SVladimir Oltean return err; 1077134ef238SVladimir Oltean 1078134ef238SVladimir Oltean vlan_vid_add(cpu_dp->master, htons(ETH_P_8021Q), vlan->vid); 1079134ef238SVladimir Oltean 1080134ef238SVladimir Oltean return err; 1081134ef238SVladimir Oltean } 1082134ef238SVladimir Oltean 1083134ef238SVladimir Oltean int dsa_port_host_vlan_del(struct dsa_port *dp, 1084134ef238SVladimir Oltean const struct switchdev_obj_port_vlan *vlan) 1085134ef238SVladimir Oltean { 1086134ef238SVladimir Oltean struct dsa_notifier_vlan_info info = { 1087134ef238SVladimir Oltean .sw_index = dp->ds->index, 1088134ef238SVladimir Oltean .port = dp->index, 1089134ef238SVladimir Oltean .vlan = vlan, 1090134ef238SVladimir Oltean }; 1091134ef238SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp; 1092134ef238SVladimir Oltean int err; 1093134ef238SVladimir Oltean 1094134ef238SVladimir Oltean err = dsa_port_notify(dp, DSA_NOTIFIER_HOST_VLAN_DEL, &info); 1095134ef238SVladimir Oltean if (err && err != -EOPNOTSUPP) 1096134ef238SVladimir Oltean return err; 1097134ef238SVladimir Oltean 1098134ef238SVladimir Oltean vlan_vid_del(cpu_dp->master, htons(ETH_P_8021Q), vlan->vid); 1099134ef238SVladimir Oltean 1100134ef238SVladimir Oltean return err; 1101134ef238SVladimir Oltean } 1102134ef238SVladimir Oltean 1103c595c433SHoratiu Vultur int dsa_port_mrp_add(const struct dsa_port *dp, 1104c595c433SHoratiu Vultur const struct switchdev_obj_mrp *mrp) 1105c595c433SHoratiu Vultur { 1106cad69019SVladimir Oltean struct dsa_switch *ds = dp->ds; 1107c595c433SHoratiu Vultur 1108cad69019SVladimir Oltean if (!ds->ops->port_mrp_add) 1109cad69019SVladimir Oltean return -EOPNOTSUPP; 1110cad69019SVladimir Oltean 1111cad69019SVladimir Oltean return ds->ops->port_mrp_add(ds, dp->index, mrp); 1112c595c433SHoratiu Vultur } 1113c595c433SHoratiu Vultur 1114c595c433SHoratiu Vultur int dsa_port_mrp_del(const struct dsa_port *dp, 1115c595c433SHoratiu Vultur const struct switchdev_obj_mrp *mrp) 1116c595c433SHoratiu Vultur { 1117cad69019SVladimir Oltean struct dsa_switch *ds = dp->ds; 1118c595c433SHoratiu Vultur 1119cad69019SVladimir Oltean if (!ds->ops->port_mrp_del) 1120cad69019SVladimir Oltean return -EOPNOTSUPP; 1121cad69019SVladimir Oltean 1122cad69019SVladimir Oltean return ds->ops->port_mrp_del(ds, dp->index, mrp); 1123c595c433SHoratiu Vultur } 1124c595c433SHoratiu Vultur 1125c595c433SHoratiu Vultur int dsa_port_mrp_add_ring_role(const struct dsa_port *dp, 1126c595c433SHoratiu Vultur const struct switchdev_obj_ring_role_mrp *mrp) 1127c595c433SHoratiu Vultur { 1128cad69019SVladimir Oltean struct dsa_switch *ds = dp->ds; 1129c595c433SHoratiu Vultur 1130cad69019SVladimir Oltean if (!ds->ops->port_mrp_add_ring_role) 1131cad69019SVladimir Oltean return -EOPNOTSUPP; 1132cad69019SVladimir Oltean 1133cad69019SVladimir Oltean return ds->ops->port_mrp_add_ring_role(ds, dp->index, mrp); 1134c595c433SHoratiu Vultur } 1135c595c433SHoratiu Vultur 1136c595c433SHoratiu Vultur int dsa_port_mrp_del_ring_role(const struct dsa_port *dp, 1137c595c433SHoratiu Vultur const struct switchdev_obj_ring_role_mrp *mrp) 1138c595c433SHoratiu Vultur { 1139cad69019SVladimir Oltean struct dsa_switch *ds = dp->ds; 1140c595c433SHoratiu Vultur 1141cad69019SVladimir Oltean if (!ds->ops->port_mrp_del_ring_role) 1142cad69019SVladimir Oltean return -EOPNOTSUPP; 1143cad69019SVladimir Oltean 1144cad69019SVladimir Oltean return ds->ops->port_mrp_del_ring_role(ds, dp->index, mrp); 1145c595c433SHoratiu Vultur } 1146c595c433SHoratiu Vultur 114753da0ebaSVladimir Oltean void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp, 114853da0ebaSVladimir Oltean const struct dsa_device_ops *tag_ops) 114953da0ebaSVladimir Oltean { 115053da0ebaSVladimir Oltean cpu_dp->rcv = tag_ops->rcv; 115153da0ebaSVladimir Oltean cpu_dp->tag_ops = tag_ops; 115253da0ebaSVladimir Oltean } 115353da0ebaSVladimir Oltean 11546207a78cSFlorian Fainelli static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp) 11556207a78cSFlorian Fainelli { 11566207a78cSFlorian Fainelli struct device_node *phy_dn; 11576207a78cSFlorian Fainelli struct phy_device *phydev; 11586207a78cSFlorian Fainelli 11596207a78cSFlorian Fainelli phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0); 11606207a78cSFlorian Fainelli if (!phy_dn) 11616207a78cSFlorian Fainelli return NULL; 11626207a78cSFlorian Fainelli 11636207a78cSFlorian Fainelli phydev = of_phy_find_device(phy_dn); 11646207a78cSFlorian Fainelli if (!phydev) { 11656207a78cSFlorian Fainelli of_node_put(phy_dn); 11666207a78cSFlorian Fainelli return ERR_PTR(-EPROBE_DEFER); 11676207a78cSFlorian Fainelli } 11686207a78cSFlorian Fainelli 11699919a363SWen Yang of_node_put(phy_dn); 11706207a78cSFlorian Fainelli return phydev; 11716207a78cSFlorian Fainelli } 11726207a78cSFlorian Fainelli 11738ae67496SFlorian Fainelli static void dsa_port_phylink_validate(struct phylink_config *config, 117477373d49SIoana Ciornei unsigned long *supported, 117577373d49SIoana Ciornei struct phylink_link_state *state) 117677373d49SIoana Ciornei { 117777373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 117877373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 117977373d49SIoana Ciornei 11805938bce4SRussell King (Oracle) if (!ds->ops->phylink_validate) { 11815938bce4SRussell King (Oracle) if (config->mac_capabilities) 11825938bce4SRussell King (Oracle) phylink_generic_validate(config, supported, state); 118377373d49SIoana Ciornei return; 11845938bce4SRussell King (Oracle) } 118577373d49SIoana Ciornei 118677373d49SIoana Ciornei ds->ops->phylink_validate(ds, dp->index, supported, state); 118777373d49SIoana Ciornei } 118877373d49SIoana Ciornei 11898ae67496SFlorian Fainelli static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, 119077373d49SIoana Ciornei struct phylink_link_state *state) 119177373d49SIoana Ciornei { 119277373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 119377373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 119487615c96SRussell King int err; 119577373d49SIoana Ciornei 1196d46b7e4fSRussell King /* Only called for inband modes */ 1197d46b7e4fSRussell King if (!ds->ops->phylink_mac_link_state) { 1198d46b7e4fSRussell King state->link = 0; 1199d46b7e4fSRussell King return; 120077373d49SIoana Ciornei } 1201d46b7e4fSRussell King 120287615c96SRussell King err = ds->ops->phylink_mac_link_state(ds, dp->index, state); 120387615c96SRussell King if (err < 0) { 120487615c96SRussell King dev_err(ds->dev, "p%d: phylink_mac_link_state() failed: %d\n", 120587615c96SRussell King dp->index, err); 1206d46b7e4fSRussell King state->link = 0; 1207d46b7e4fSRussell King } 120887615c96SRussell King } 120977373d49SIoana Ciornei 1210bde01822SRussell King (Oracle) static struct phylink_pcs * 1211bde01822SRussell King (Oracle) dsa_port_phylink_mac_select_pcs(struct phylink_config *config, 1212bde01822SRussell King (Oracle) phy_interface_t interface) 1213bde01822SRussell King (Oracle) { 1214bde01822SRussell King (Oracle) struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 121510544570SRussell King (Oracle) struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP); 1216bde01822SRussell King (Oracle) struct dsa_switch *ds = dp->ds; 1217bde01822SRussell King (Oracle) 1218bde01822SRussell King (Oracle) if (ds->ops->phylink_mac_select_pcs) 1219bde01822SRussell King (Oracle) pcs = ds->ops->phylink_mac_select_pcs(ds, dp->index, interface); 1220bde01822SRussell King (Oracle) 1221bde01822SRussell King (Oracle) return pcs; 1222bde01822SRussell King (Oracle) } 1223bde01822SRussell King (Oracle) 12248ae67496SFlorian Fainelli static void dsa_port_phylink_mac_config(struct phylink_config *config, 122577373d49SIoana Ciornei unsigned int mode, 122677373d49SIoana Ciornei const struct phylink_link_state *state) 122777373d49SIoana Ciornei { 122877373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 122977373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 123077373d49SIoana Ciornei 123177373d49SIoana Ciornei if (!ds->ops->phylink_mac_config) 123277373d49SIoana Ciornei return; 123377373d49SIoana Ciornei 123477373d49SIoana Ciornei ds->ops->phylink_mac_config(ds, dp->index, mode, state); 123577373d49SIoana Ciornei } 123677373d49SIoana Ciornei 12378ae67496SFlorian Fainelli static void dsa_port_phylink_mac_an_restart(struct phylink_config *config) 123877373d49SIoana Ciornei { 123977373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 124077373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 124177373d49SIoana Ciornei 124277373d49SIoana Ciornei if (!ds->ops->phylink_mac_an_restart) 124377373d49SIoana Ciornei return; 124477373d49SIoana Ciornei 124577373d49SIoana Ciornei ds->ops->phylink_mac_an_restart(ds, dp->index); 124677373d49SIoana Ciornei } 124777373d49SIoana Ciornei 12488ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_down(struct phylink_config *config, 124977373d49SIoana Ciornei unsigned int mode, 125077373d49SIoana Ciornei phy_interface_t interface) 125177373d49SIoana Ciornei { 125277373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 12530e279218SIoana Ciornei struct phy_device *phydev = NULL; 125477373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 125577373d49SIoana Ciornei 125657d77986SVladimir Oltean if (dsa_port_is_user(dp)) 12570e279218SIoana Ciornei phydev = dp->slave->phydev; 12580e279218SIoana Ciornei 125977373d49SIoana Ciornei if (!ds->ops->phylink_mac_link_down) { 12600e279218SIoana Ciornei if (ds->ops->adjust_link && phydev) 12610e279218SIoana Ciornei ds->ops->adjust_link(ds, dp->index, phydev); 126277373d49SIoana Ciornei return; 126377373d49SIoana Ciornei } 126477373d49SIoana Ciornei 126577373d49SIoana Ciornei ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface); 126677373d49SIoana Ciornei } 126777373d49SIoana Ciornei 12688ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_up(struct phylink_config *config, 126991a208f2SRussell King struct phy_device *phydev, 127077373d49SIoana Ciornei unsigned int mode, 127177373d49SIoana Ciornei phy_interface_t interface, 127291a208f2SRussell King int speed, int duplex, 127391a208f2SRussell King bool tx_pause, bool rx_pause) 127477373d49SIoana Ciornei { 127577373d49SIoana Ciornei struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); 127677373d49SIoana Ciornei struct dsa_switch *ds = dp->ds; 127777373d49SIoana Ciornei 127877373d49SIoana Ciornei if (!ds->ops->phylink_mac_link_up) { 12790e279218SIoana Ciornei if (ds->ops->adjust_link && phydev) 12800e279218SIoana Ciornei ds->ops->adjust_link(ds, dp->index, phydev); 128177373d49SIoana Ciornei return; 128277373d49SIoana Ciornei } 128377373d49SIoana Ciornei 12845b502a7bSRussell King ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev, 12855b502a7bSRussell King speed, duplex, tx_pause, rx_pause); 128677373d49SIoana Ciornei } 128777373d49SIoana Ciornei 128821bd64bdSRussell King (Oracle) static const struct phylink_mac_ops dsa_port_phylink_mac_ops = { 128977373d49SIoana Ciornei .validate = dsa_port_phylink_validate, 1290bde01822SRussell King (Oracle) .mac_select_pcs = dsa_port_phylink_mac_select_pcs, 1291d46b7e4fSRussell King .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state, 129277373d49SIoana Ciornei .mac_config = dsa_port_phylink_mac_config, 129377373d49SIoana Ciornei .mac_an_restart = dsa_port_phylink_mac_an_restart, 129477373d49SIoana Ciornei .mac_link_down = dsa_port_phylink_mac_link_down, 129577373d49SIoana Ciornei .mac_link_up = dsa_port_phylink_mac_link_up, 129677373d49SIoana Ciornei }; 129777373d49SIoana Ciornei 129821bd64bdSRussell King (Oracle) int dsa_port_phylink_create(struct dsa_port *dp) 129921bd64bdSRussell King (Oracle) { 130021bd64bdSRussell King (Oracle) struct dsa_switch *ds = dp->ds; 130121bd64bdSRussell King (Oracle) phy_interface_t mode; 130221bd64bdSRussell King (Oracle) int err; 130321bd64bdSRussell King (Oracle) 130421bd64bdSRussell King (Oracle) err = of_get_phy_mode(dp->dn, &mode); 130521bd64bdSRussell King (Oracle) if (err) 130621bd64bdSRussell King (Oracle) mode = PHY_INTERFACE_MODE_NA; 130721bd64bdSRussell King (Oracle) 13080a9f0794SRussell King (Oracle) /* Presence of phylink_mac_link_state or phylink_mac_an_restart is 13090a9f0794SRussell King (Oracle) * an indicator of a legacy phylink driver. 13100a9f0794SRussell King (Oracle) */ 13110a9f0794SRussell King (Oracle) if (ds->ops->phylink_mac_link_state || 13120a9f0794SRussell King (Oracle) ds->ops->phylink_mac_an_restart) 13130a9f0794SRussell King (Oracle) dp->pl_config.legacy_pre_march2020 = true; 13140a9f0794SRussell King (Oracle) 1315072eea6cSRussell King (Oracle) if (ds->ops->phylink_get_caps) 1316072eea6cSRussell King (Oracle) ds->ops->phylink_get_caps(ds, dp->index, &dp->pl_config); 131721bd64bdSRussell King (Oracle) 131821bd64bdSRussell King (Oracle) dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn), 131921bd64bdSRussell King (Oracle) mode, &dsa_port_phylink_mac_ops); 132021bd64bdSRussell King (Oracle) if (IS_ERR(dp->pl)) { 132121bd64bdSRussell King (Oracle) pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl)); 132221bd64bdSRussell King (Oracle) return PTR_ERR(dp->pl); 132321bd64bdSRussell King (Oracle) } 132421bd64bdSRussell King (Oracle) 132521bd64bdSRussell King (Oracle) return 0; 132621bd64bdSRussell King (Oracle) } 132721bd64bdSRussell King (Oracle) 132833615367SSebastian Reichel static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable) 132933615367SSebastian Reichel { 133033615367SSebastian Reichel struct dsa_switch *ds = dp->ds; 133133615367SSebastian Reichel struct phy_device *phydev; 133233615367SSebastian Reichel int port = dp->index; 133333615367SSebastian Reichel int err = 0; 133433615367SSebastian Reichel 13356207a78cSFlorian Fainelli phydev = dsa_port_get_phy_device(dp); 13366207a78cSFlorian Fainelli if (!phydev) 133733615367SSebastian Reichel return 0; 133833615367SSebastian Reichel 13396207a78cSFlorian Fainelli if (IS_ERR(phydev)) 13406207a78cSFlorian Fainelli return PTR_ERR(phydev); 134133615367SSebastian Reichel 134233615367SSebastian Reichel if (enable) { 134333615367SSebastian Reichel err = genphy_resume(phydev); 134433615367SSebastian Reichel if (err < 0) 134533615367SSebastian Reichel goto err_put_dev; 134633615367SSebastian Reichel 134733615367SSebastian Reichel err = genphy_read_status(phydev); 134833615367SSebastian Reichel if (err < 0) 134933615367SSebastian Reichel goto err_put_dev; 135033615367SSebastian Reichel } else { 135133615367SSebastian Reichel err = genphy_suspend(phydev); 135233615367SSebastian Reichel if (err < 0) 135333615367SSebastian Reichel goto err_put_dev; 135433615367SSebastian Reichel } 135533615367SSebastian Reichel 135633615367SSebastian Reichel if (ds->ops->adjust_link) 135733615367SSebastian Reichel ds->ops->adjust_link(ds, port, phydev); 135833615367SSebastian Reichel 135933615367SSebastian Reichel dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev)); 136033615367SSebastian Reichel 136133615367SSebastian Reichel err_put_dev: 136233615367SSebastian Reichel put_device(&phydev->mdio.dev); 136333615367SSebastian Reichel return err; 136433615367SSebastian Reichel } 136533615367SSebastian Reichel 136633615367SSebastian Reichel static int dsa_port_fixed_link_register_of(struct dsa_port *dp) 136757ab1ca2SVivien Didelot { 136857ab1ca2SVivien Didelot struct device_node *dn = dp->dn; 136957ab1ca2SVivien Didelot struct dsa_switch *ds = dp->ds; 137057ab1ca2SVivien Didelot struct phy_device *phydev; 137157ab1ca2SVivien Didelot int port = dp->index; 13720c65b2b9SAndrew Lunn phy_interface_t mode; 137357ab1ca2SVivien Didelot int err; 137457ab1ca2SVivien Didelot 137557ab1ca2SVivien Didelot err = of_phy_register_fixed_link(dn); 137657ab1ca2SVivien Didelot if (err) { 137757ab1ca2SVivien Didelot dev_err(ds->dev, 137857ab1ca2SVivien Didelot "failed to register the fixed PHY of port %d\n", 137957ab1ca2SVivien Didelot port); 138057ab1ca2SVivien Didelot return err; 138157ab1ca2SVivien Didelot } 138257ab1ca2SVivien Didelot 138357ab1ca2SVivien Didelot phydev = of_phy_find_device(dn); 138457ab1ca2SVivien Didelot 13850c65b2b9SAndrew Lunn err = of_get_phy_mode(dn, &mode); 13860c65b2b9SAndrew Lunn if (err) 138757ab1ca2SVivien Didelot mode = PHY_INTERFACE_MODE_NA; 138857ab1ca2SVivien Didelot phydev->interface = mode; 138957ab1ca2SVivien Didelot 139057ab1ca2SVivien Didelot genphy_read_status(phydev); 139157ab1ca2SVivien Didelot 139257ab1ca2SVivien Didelot if (ds->ops->adjust_link) 139357ab1ca2SVivien Didelot ds->ops->adjust_link(ds, port, phydev); 139457ab1ca2SVivien Didelot 139557ab1ca2SVivien Didelot put_device(&phydev->mdio.dev); 139657ab1ca2SVivien Didelot 139757ab1ca2SVivien Didelot return 0; 139857ab1ca2SVivien Didelot } 139957ab1ca2SVivien Didelot 14000e279218SIoana Ciornei static int dsa_port_phylink_register(struct dsa_port *dp) 14010e279218SIoana Ciornei { 14020e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 14030e279218SIoana Ciornei struct device_node *port_dn = dp->dn; 14040c65b2b9SAndrew Lunn int err; 14050e279218SIoana Ciornei 14060e279218SIoana Ciornei dp->pl_config.dev = ds->dev; 14070e279218SIoana Ciornei dp->pl_config.type = PHYLINK_DEV; 14080e279218SIoana Ciornei 140921bd64bdSRussell King (Oracle) err = dsa_port_phylink_create(dp); 141021bd64bdSRussell King (Oracle) if (err) 141121bd64bdSRussell King (Oracle) return err; 14120e279218SIoana Ciornei 14130e279218SIoana Ciornei err = phylink_of_phy_connect(dp->pl, port_dn, 0); 14142131fba5SFlorian Fainelli if (err && err != -ENODEV) { 14150e279218SIoana Ciornei pr_err("could not attach to PHY: %d\n", err); 14160e279218SIoana Ciornei goto err_phy_connect; 14170e279218SIoana Ciornei } 14180e279218SIoana Ciornei 14190e279218SIoana Ciornei return 0; 14200e279218SIoana Ciornei 14210e279218SIoana Ciornei err_phy_connect: 14220e279218SIoana Ciornei phylink_destroy(dp->pl); 14230e279218SIoana Ciornei return err; 14240e279218SIoana Ciornei } 14250e279218SIoana Ciornei 142633615367SSebastian Reichel int dsa_port_link_register_of(struct dsa_port *dp) 142757ab1ca2SVivien Didelot { 14280e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 1429a20f9970SAndrew Lunn struct device_node *phy_np; 14303be98b2dSAndrew Lunn int port = dp->index; 14310e279218SIoana Ciornei 1432a20f9970SAndrew Lunn if (!ds->ops->adjust_link) { 1433a20f9970SAndrew Lunn phy_np = of_parse_phandle(dp->dn, "phy-handle", 0); 14343be98b2dSAndrew Lunn if (of_phy_is_fixed_link(dp->dn) || phy_np) { 14353be98b2dSAndrew Lunn if (ds->ops->phylink_mac_link_down) 14363be98b2dSAndrew Lunn ds->ops->phylink_mac_link_down(ds, port, 14373be98b2dSAndrew Lunn MLO_AN_FIXED, PHY_INTERFACE_MODE_NA); 14380e279218SIoana Ciornei return dsa_port_phylink_register(dp); 14393be98b2dSAndrew Lunn } 1440a20f9970SAndrew Lunn return 0; 1441a20f9970SAndrew Lunn } 14420e279218SIoana Ciornei 14430e279218SIoana Ciornei dev_warn(ds->dev, 14440e279218SIoana Ciornei "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!\n"); 14450e279218SIoana Ciornei 144633615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 144733615367SSebastian Reichel return dsa_port_fixed_link_register_of(dp); 144833615367SSebastian Reichel else 144933615367SSebastian Reichel return dsa_port_setup_phy_of(dp, true); 145033615367SSebastian Reichel } 145157ab1ca2SVivien Didelot 145233615367SSebastian Reichel void dsa_port_link_unregister_of(struct dsa_port *dp) 145333615367SSebastian Reichel { 14540e279218SIoana Ciornei struct dsa_switch *ds = dp->ds; 14550e279218SIoana Ciornei 1456a20f9970SAndrew Lunn if (!ds->ops->adjust_link && dp->pl) { 14570e279218SIoana Ciornei rtnl_lock(); 14580e279218SIoana Ciornei phylink_disconnect_phy(dp->pl); 14590e279218SIoana Ciornei rtnl_unlock(); 14600e279218SIoana Ciornei phylink_destroy(dp->pl); 1461a20f9970SAndrew Lunn dp->pl = NULL; 14620e279218SIoana Ciornei return; 14630e279218SIoana Ciornei } 14640e279218SIoana Ciornei 146533615367SSebastian Reichel if (of_phy_is_fixed_link(dp->dn)) 146633615367SSebastian Reichel of_phy_deregister_fixed_link(dp->dn); 146733615367SSebastian Reichel else 146833615367SSebastian Reichel dsa_port_setup_phy_of(dp, false); 146957ab1ca2SVivien Didelot } 1470cf963573SFlorian Fainelli 147118596f50SGeorge McCollister int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr) 147218596f50SGeorge McCollister { 1473a68dc7b9SVladimir Oltean struct dsa_switch *ds = dp->ds; 147418596f50SGeorge McCollister int err; 147518596f50SGeorge McCollister 1476a68dc7b9SVladimir Oltean if (!ds->ops->port_hsr_join) 1477a68dc7b9SVladimir Oltean return -EOPNOTSUPP; 1478a68dc7b9SVladimir Oltean 147918596f50SGeorge McCollister dp->hsr_dev = hsr; 148018596f50SGeorge McCollister 1481a68dc7b9SVladimir Oltean err = ds->ops->port_hsr_join(ds, dp->index, hsr); 148218596f50SGeorge McCollister if (err) 148318596f50SGeorge McCollister dp->hsr_dev = NULL; 148418596f50SGeorge McCollister 148518596f50SGeorge McCollister return err; 148618596f50SGeorge McCollister } 148718596f50SGeorge McCollister 148818596f50SGeorge McCollister void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr) 148918596f50SGeorge McCollister { 1490a68dc7b9SVladimir Oltean struct dsa_switch *ds = dp->ds; 149118596f50SGeorge McCollister int err; 149218596f50SGeorge McCollister 149318596f50SGeorge McCollister dp->hsr_dev = NULL; 149418596f50SGeorge McCollister 1495a68dc7b9SVladimir Oltean if (ds->ops->port_hsr_leave) { 1496a68dc7b9SVladimir Oltean err = ds->ops->port_hsr_leave(ds, dp->index, hsr); 149718596f50SGeorge McCollister if (err) 1498ab97462bSVladimir Oltean dev_err(dp->ds->dev, 1499a68dc7b9SVladimir Oltean "port %d failed to leave HSR %s: %pe\n", 1500a68dc7b9SVladimir Oltean dp->index, hsr->name, ERR_PTR(err)); 1501a68dc7b9SVladimir Oltean } 150218596f50SGeorge McCollister } 1503c64b9c05SVladimir Oltean 1504724395f4SVladimir Oltean int dsa_port_tag_8021q_vlan_add(struct dsa_port *dp, u16 vid, bool broadcast) 1505c64b9c05SVladimir Oltean { 1506c64b9c05SVladimir Oltean struct dsa_notifier_tag_8021q_vlan_info info = { 1507c64b9c05SVladimir Oltean .tree_index = dp->ds->dst->index, 1508c64b9c05SVladimir Oltean .sw_index = dp->ds->index, 1509c64b9c05SVladimir Oltean .port = dp->index, 1510c64b9c05SVladimir Oltean .vid = vid, 1511c64b9c05SVladimir Oltean }; 1512c64b9c05SVladimir Oltean 1513724395f4SVladimir Oltean if (broadcast) 1514c64b9c05SVladimir Oltean return dsa_broadcast(DSA_NOTIFIER_TAG_8021Q_VLAN_ADD, &info); 1515724395f4SVladimir Oltean 1516724395f4SVladimir Oltean return dsa_port_notify(dp, DSA_NOTIFIER_TAG_8021Q_VLAN_ADD, &info); 1517c64b9c05SVladimir Oltean } 1518c64b9c05SVladimir Oltean 1519724395f4SVladimir Oltean void dsa_port_tag_8021q_vlan_del(struct dsa_port *dp, u16 vid, bool broadcast) 1520c64b9c05SVladimir Oltean { 1521c64b9c05SVladimir Oltean struct dsa_notifier_tag_8021q_vlan_info info = { 1522c64b9c05SVladimir Oltean .tree_index = dp->ds->dst->index, 1523c64b9c05SVladimir Oltean .sw_index = dp->ds->index, 1524c64b9c05SVladimir Oltean .port = dp->index, 1525c64b9c05SVladimir Oltean .vid = vid, 1526c64b9c05SVladimir Oltean }; 1527c64b9c05SVladimir Oltean int err; 1528c64b9c05SVladimir Oltean 1529724395f4SVladimir Oltean if (broadcast) 1530c64b9c05SVladimir Oltean err = dsa_broadcast(DSA_NOTIFIER_TAG_8021Q_VLAN_DEL, &info); 1531724395f4SVladimir Oltean else 1532724395f4SVladimir Oltean err = dsa_port_notify(dp, DSA_NOTIFIER_TAG_8021Q_VLAN_DEL, &info); 1533c64b9c05SVladimir Oltean if (err) 1534ab97462bSVladimir Oltean dev_err(dp->ds->dev, 1535ab97462bSVladimir Oltean "port %d failed to notify tag_8021q VLAN %d deletion: %pe\n", 1536ab97462bSVladimir Oltean dp->index, vid, ERR_PTR(err)); 1537c64b9c05SVladimir Oltean } 1538