summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladimir Oltean <vladimir.oltean@nxp.com>2022-02-21 14:01:30 +0200
committerVladimir Oltean <vladimir.oltean@nxp.com>2022-05-04 15:24:54 +0200
commitecbc3ac1c34f67fb4bb7f1513692b746dc16338d (patch)
treea0671183d13d75feadb69dc9cba0566c7f53bfcf
parent9bd7c70091f1b21c21578d7d8eade5df41e05b93 (diff)
net: switchdev: avoid infinite recursion from LAG to bridge with port object handler
The logic from switchdev_handle_port_obj_add_foreign() is directly adapted from switchdev_handle_fdb_event_to_device(), which already detects events on foreign interfaces and reoffloads them towards the switchdev neighbors. However, when we have a simple br0 <-> bond0 <-> swp0 topology and the switchdev_handle_port_obj_add_foreign() gets called on bond0, we get stuck into an infinite recursion: 1. bond0 does not pass check_cb(), so we attempt to find switchdev neighbor interfaces. For that, we recursively call __switchdev_handle_port_obj_add() for bond0's bridge, br0. 2. __switchdev_handle_port_obj_add() recurses through br0's lowers, essentially calling __switchdev_handle_port_obj_add() for bond0 3. Go to step 1. This happens because switchdev_handle_fdb_event_to_device() and switchdev_handle_port_obj_add_foreign() are not exactly the same. The FDB event helper special-cases LAG interfaces with its lag_mod_cb(), so this is why we don't end up in an infinite loop - because it doesn't attempt to treat LAG interfaces as potentially foreign bridge ports. The problem is solved by looking ahead through the bridge's lowers to see whether there is any switchdev interface that is foreign to the @dev we are currently processing. This stops the recursion described above at step 1: __switchdev_handle_port_obj_add(bond0) will not create another call to __switchdev_handle_port_obj_add(br0). Going one step upper should only happen when we're starting from a bridge port that has been determined to be "foreign" to the switchdev driver that passes the foreign_dev_check_cb(). Fixes: c4076cdd21f8 ("net: switchdev: introduce switchdev_handle_port_obj_{add,del} for foreign interfaces") Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> Signed-off-by: David S. Miller <davem@davemloft.net> (cherry picked from commit acd8df5880d7c80b0317dce8df3e65b6a6825c88) Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
-rw-r--r--net/switchdev/switchdev.c16
1 files changed, 12 insertions, 4 deletions
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index f9cec7f28358..7e946bde18be 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -563,7 +563,7 @@ static int __switchdev_handle_port_obj_add(struct net_device *dev,
struct netlink_ext_ack *extack))
{
struct switchdev_notifier_info *info = &port_obj_info->info;
- struct net_device *br, *lower_dev;
+ struct net_device *br, *lower_dev, *switchdev;
struct netlink_ext_ack *extack;
struct list_head *iter;
int err = -EOPNOTSUPP;
@@ -613,7 +613,11 @@ static int __switchdev_handle_port_obj_add(struct net_device *dev,
if (!br || !netif_is_bridge_master(br))
return err;
- if (!switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb))
+ switchdev = switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb);
+ if (!switchdev)
+ return err;
+
+ if (!foreign_dev_check_cb(switchdev, dev))
return err;
return __switchdev_handle_port_obj_add(br, port_obj_info, check_cb,
@@ -673,7 +677,7 @@ static int __switchdev_handle_port_obj_del(struct net_device *dev,
const struct switchdev_obj *obj))
{
struct switchdev_notifier_info *info = &port_obj_info->info;
- struct net_device *br, *lower_dev;
+ struct net_device *br, *lower_dev, *switchdev;
struct list_head *iter;
int err = -EOPNOTSUPP;
@@ -720,7 +724,11 @@ static int __switchdev_handle_port_obj_del(struct net_device *dev,
if (!br || !netif_is_bridge_master(br))
return err;
- if (!switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb))
+ switchdev = switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb);
+ if (!switchdev)
+ return err;
+
+ if (!foreign_dev_check_cb(switchdev, dev))
return err;
return __switchdev_handle_port_obj_del(br, port_obj_info, check_cb,