mlxsw: spectrum: On port enslavement to a LAG, join upper's bridges
authorPetr Machata <petrm@nvidia.com>
Wed, 19 Jul 2023 11:01:24 +0000 (13:01 +0200)
committerDavid S. Miller <davem@davemloft.net>
Fri, 21 Jul 2023 07:54:04 +0000 (08:54 +0100)
Currently it never happens that a netdevice that is already a bridge slave
would suddenly become mlxsw upper. The only case where this might be
possible as far as mlxsw is concerned, is with LAG netdevices. But if a LAG
already has an upper, enslaving mlxsw port to that LAG is forbidden. Thus
the only way to install a LAG between a bridge and a mlxsw port is by first
enslaving the port to the LAG, and then enslaving that LAG to a bridge.

However in the following patches, the requirement that ports be only
enslaved to masters without uppers, is going to be relaxed. It will
therefore be necessary to join bridges of LAG uppers. Without this replay,
the mlxsw bridge_port objects are not instantiated, which causes issues
later, as a lot of code relies on their presence.

Therefore in this patch, when the first mlxsw physical netdevice is
enslaved to a LAG, consider bridges upper to the LAG (both the direct
master, if any, and any bridge masters of VLAN uppers), and have the
relevant netdevices join their bridges.

Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Danielle Ratson <danieller@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mellanox/mlxsw/spectrum.c

index b9ceffe..9a6e1ce 100644 (file)
@@ -4337,6 +4337,88 @@ static int mlxsw_sp_port_lag_index_get(struct mlxsw_sp *mlxsw_sp,
        return -EBUSY;
 }
 
+static int mlxsw_sp_lag_uppers_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port,
+                                          struct net_device *lag_dev,
+                                          struct netlink_ext_ack *extack)
+{
+       struct net_device *upper_dev;
+       struct net_device *master;
+       struct list_head *iter;
+       int done = 0;
+       int err;
+
+       master = netdev_master_upper_dev_get(lag_dev);
+       if (master && netif_is_bridge_master(master)) {
+               err = mlxsw_sp_port_bridge_join(mlxsw_sp_port, lag_dev, master,
+                                               extack);
+               if (err)
+                       return err;
+       }
+
+       netdev_for_each_upper_dev_rcu(lag_dev, upper_dev, iter) {
+               if (!is_vlan_dev(upper_dev))
+                       continue;
+
+               master = netdev_master_upper_dev_get(upper_dev);
+               if (master && netif_is_bridge_master(master)) {
+                       err = mlxsw_sp_port_bridge_join(mlxsw_sp_port,
+                                                       upper_dev, master,
+                                                       extack);
+                       if (err)
+                               goto err_port_bridge_join;
+               }
+
+               ++done;
+       }
+
+       return 0;
+
+err_port_bridge_join:
+       netdev_for_each_upper_dev_rcu(lag_dev, upper_dev, iter) {
+               if (!is_vlan_dev(upper_dev))
+                       continue;
+
+               master = netdev_master_upper_dev_get(upper_dev);
+               if (!master || !netif_is_bridge_master(master))
+                       continue;
+
+               if (!done--)
+                       break;
+
+               mlxsw_sp_port_bridge_leave(mlxsw_sp_port, upper_dev, master);
+       }
+
+       master = netdev_master_upper_dev_get(lag_dev);
+       if (master && netif_is_bridge_master(master))
+               mlxsw_sp_port_bridge_leave(mlxsw_sp_port, lag_dev, master);
+
+       return err;
+}
+
+static void
+mlxsw_sp_lag_uppers_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port,
+                                struct net_device *lag_dev)
+{
+       struct net_device *upper_dev;
+       struct net_device *master;
+       struct list_head *iter;
+
+       netdev_for_each_upper_dev_rcu(lag_dev, upper_dev, iter) {
+               if (!is_vlan_dev(upper_dev))
+                       continue;
+
+               master = netdev_master_upper_dev_get(upper_dev);
+               if (!master)
+                       continue;
+
+               mlxsw_sp_port_bridge_leave(mlxsw_sp_port, upper_dev, master);
+       }
+
+       master = netdev_master_upper_dev_get(lag_dev);
+       if (master)
+               mlxsw_sp_port_bridge_leave(mlxsw_sp_port, lag_dev, master);
+}
+
 static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port,
                                  struct net_device *lag_dev,
                                  struct netlink_ext_ack *extack)
@@ -4361,6 +4443,12 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port,
        err = mlxsw_sp_port_lag_index_get(mlxsw_sp, lag_id, &port_index);
        if (err)
                return err;
+
+       err = mlxsw_sp_lag_uppers_bridge_join(mlxsw_sp_port, lag_dev,
+                                             extack);
+       if (err)
+               goto err_lag_uppers_bridge_join;
+
        err = mlxsw_sp_lag_col_port_add(mlxsw_sp_port, lag_id, port_index);
        if (err)
                goto err_col_port_add;
@@ -4390,6 +4478,8 @@ err_router_join:
                                     mlxsw_sp_port->local_port);
        mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id);
 err_col_port_add:
+       mlxsw_sp_lag_uppers_bridge_leave(mlxsw_sp_port, lag_dev);
+err_lag_uppers_bridge_join:
        if (!lag->ref_count)
                mlxsw_sp_lag_destroy(mlxsw_sp, lag_id);
        return err;