mlxsw: spectrum: Permit enslavement to netdevices with uppers
authorPetr Machata <petrm@nvidia.com>
Wed, 19 Jul 2023 11:01:32 +0000 (13:01 +0200)
committerDavid S. Miller <davem@davemloft.net>
Fri, 21 Jul 2023 07:54:06 +0000 (08:54 +0100)
Enslaving of front panel ports (and their uppers) to netdevices that
already have uppers is currently forbidden. In the previous patches, a
number of replays have been added. Those ensure that various bits of state,
such as next hops or switchdev objects, are offloaded when they become
relevant due to a mlxsw lower being introduced into the topology.

However the act of actually, for example, enslaving a front-panel port to
a bridge with uppers, has been vetoed so far. In this patch, remove the
vetoes and permit the operation.

mlxsw currently validates creation of "interesting" uppers. Thus creating
VLAN netdevices on top of 802.1ad bridges is forbidden if the bridge has an
mlxsw lower, but permitted in general. This validation code never gets run
when a port is introduced as a lower of an existing netdevice structure.

Thus when enslaving an mlxsw netdevice to netdevices with uppers, invoke
the PRECHANGEUPPER event handler for each netdevice above the one that the
front panel port is being enslaved to. This way the tower of netdevices
above the attachment point is validated.

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 b955511..f0f6af3 100644 (file)
@@ -4735,6 +4735,58 @@ static bool mlxsw_sp_bridge_vxlan_is_valid(struct net_device *br_dev,
        return true;
 }
 
+static bool mlxsw_sp_netdev_is_master(struct net_device *upper_dev,
+                                     struct net_device *dev)
+{
+       return upper_dev == netdev_master_upper_dev_get(dev);
+}
+
+static int __mlxsw_sp_netdevice_event(struct mlxsw_sp *mlxsw_sp,
+                                     unsigned long event, void *ptr,
+                                     bool process_foreign);
+
+static int mlxsw_sp_netdevice_validate_uppers(struct mlxsw_sp *mlxsw_sp,
+                                             struct net_device *dev,
+                                             struct netlink_ext_ack *extack)
+{
+       struct net_device *upper_dev;
+       struct list_head *iter;
+       int err;
+
+       netdev_for_each_upper_dev_rcu(dev, upper_dev, iter) {
+               struct netdev_notifier_changeupper_info info = {
+                       .info = {
+                               .dev = dev,
+                               .extack = extack,
+                       },
+                       .master = mlxsw_sp_netdev_is_master(upper_dev, dev),
+                       .upper_dev = upper_dev,
+                       .linking = true,
+
+                       /* upper_info is relevant for LAG devices. But we would
+                        * only need this if LAG were a valid upper above
+                        * another upper (e.g. a bridge that is a member of a
+                        * LAG), and that is never a valid configuration. So we
+                        * can keep this as NULL.
+                        */
+                       .upper_info = NULL,
+               };
+
+               err = __mlxsw_sp_netdevice_event(mlxsw_sp,
+                                                NETDEV_PRECHANGEUPPER,
+                                                &info, true);
+               if (err)
+                       return err;
+
+               err = mlxsw_sp_netdevice_validate_uppers(mlxsw_sp, upper_dev,
+                                                        extack);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
 static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
                                               struct net_device *dev,
                                               unsigned long event, void *ptr,
@@ -4776,8 +4828,11 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
                    (!netif_is_bridge_master(upper_dev) ||
                     !mlxsw_sp_bridge_device_is_offloaded(mlxsw_sp,
                                                          upper_dev))) {
-                       NL_SET_ERR_MSG_MOD(extack, "Enslaving a port to a device that already has an upper device is not supported");
-                       return -EINVAL;
+                       err = mlxsw_sp_netdevice_validate_uppers(mlxsw_sp,
+                                                                upper_dev,
+                                                                extack);
+                       if (err)
+                               return err;
                }
                if (netif_is_lag_master(upper_dev) &&
                    !mlxsw_sp_master_lag_check(mlxsw_sp, upper_dev,
@@ -5008,8 +5063,11 @@ static int mlxsw_sp_netdevice_port_vlan_event(struct net_device *vlan_dev,
                    (!netif_is_bridge_master(upper_dev) ||
                     !mlxsw_sp_bridge_device_is_offloaded(mlxsw_sp,
                                                          upper_dev))) {
-                       NL_SET_ERR_MSG_MOD(extack, "Enslaving a port to a device that already has an upper device is not supported");
-                       return -EINVAL;
+                       err = mlxsw_sp_netdevice_validate_uppers(mlxsw_sp,
+                                                                upper_dev,
+                                                                extack);
+                       if (err)
+                               return err;
                }
                break;
        case NETDEV_CHANGEUPPER: