summaryrefslogtreecommitdiff
path: root/net/dsa/port.c
diff options
context:
space:
mode:
authorVladimir Oltean <vladimir.oltean@nxp.com>2021-06-27 14:54:29 +0300
committerDavid S. Miller <davem@davemloft.net>2021-06-29 00:09:03 +0300
commit7491894532341cff11babd1fe3bd68537166bcc4 (patch)
tree9ba8efa46dfb690c3076eb2d2c810484ba88cc03 /net/dsa/port.c
parent4ede74e73b5b540b2a20bb6d5ad4d69348ba51fc (diff)
downloadlinux-7491894532341cff11babd1fe3bd68537166bcc4.tar.xz
net: dsa: replay a deletion of switchdev objects for ports leaving a bridged LAG
When a DSA switch port leaves a bonding interface that is under a bridge, there might be dangling switchdev objects on that port left behind, because the bridge is not aware that its lower interface (the bond) changed state in any way. Call the bridge replay helpers with adding=false before changing dp->bridge_dev to NULL, because we need to simulate to dsa_slave_port_obj_del() that these notifications were emitted by the bridge. We add this hook to the NETDEV_PRECHANGEUPPER event handler, because we are calling into switchdev (and the __switchdev_handle_port_obj_del fanout helpers expect the upper/lower adjacency lists to still be valid) and PRECHANGEUPPER is the last moment in time when they still are. Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/dsa/port.c')
-rw-r--r--net/dsa/port.c45
1 files changed, 43 insertions, 2 deletions
diff --git a/net/dsa/port.c b/net/dsa/port.c
index 4e58d07ececd..46089dd2b2ec 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -212,7 +212,33 @@ static int dsa_port_switchdev_sync(struct dsa_port *dp,
return 0;
}
-static void dsa_port_switchdev_unsync(struct dsa_port *dp)
+static int dsa_port_switchdev_unsync_objs(struct dsa_port *dp,
+ struct net_device *br,
+ struct netlink_ext_ack *extack)
+{
+ struct net_device *brport_dev = dsa_port_to_bridge_port(dp);
+ int err;
+
+ /* Delete the switchdev objects left on this port */
+ err = br_mdb_replay(br, brport_dev, dp, false,
+ &dsa_slave_switchdev_blocking_notifier, extack);
+ if (err && err != -EOPNOTSUPP)
+ return err;
+
+ err = br_fdb_replay(br, brport_dev, dp, false,
+ &dsa_slave_switchdev_notifier);
+ if (err && err != -EOPNOTSUPP)
+ return err;
+
+ err = br_vlan_replay(br, brport_dev, dp, false,
+ &dsa_slave_switchdev_blocking_notifier, extack);
+ if (err && err != -EOPNOTSUPP)
+ return err;
+
+ return 0;
+}
+
+static void dsa_port_switchdev_unsync_attrs(struct dsa_port *dp)
{
/* Configure the port for standalone mode (no address learning,
* flood everything).
@@ -278,6 +304,12 @@ out_rollback:
return err;
}
+int dsa_port_pre_bridge_leave(struct dsa_port *dp, struct net_device *br,
+ struct netlink_ext_ack *extack)
+{
+ return dsa_port_switchdev_unsync_objs(dp, br, extack);
+}
+
void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
{
struct dsa_notifier_bridge_info info = {
@@ -297,7 +329,7 @@ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
if (err)
pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n");
- dsa_port_switchdev_unsync(dp);
+ dsa_port_switchdev_unsync_attrs(dp);
}
int dsa_port_lag_change(struct dsa_port *dp,
@@ -365,6 +397,15 @@ err_lag_join:
return err;
}
+int dsa_port_pre_lag_leave(struct dsa_port *dp, struct net_device *lag,
+ struct netlink_ext_ack *extack)
+{
+ if (dp->bridge_dev)
+ return dsa_port_pre_bridge_leave(dp, dp->bridge_dev, extack);
+
+ return 0;
+}
+
void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag)
{
struct dsa_notifier_lag_info info = {