mlxsw: spectrum_router: Replay IP NETDEV_UP on device enslavement
authorPetr Machata <petrm@nvidia.com>
Wed, 19 Jul 2023 11:01:30 +0000 (13:01 +0200)
committerDavid S. Miller <davem@davemloft.net>
Fri, 21 Jul 2023 07:54:05 +0000 (08:54 +0100)
Enslaving of front panel ports (and their uppers) to netdevices that
already have uppers is currently forbidden. When this is permitted, any
uppers with IP addresses need to have the NETDEV_UP inetaddr event
replayed, so that any RIFs are created.

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
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h
drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c

index 4346cc7..0dcd988 100644 (file)
@@ -4469,8 +4469,14 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port,
        if (err)
                goto err_router_join;
 
+       err = mlxsw_sp_netdevice_enslavement_replay(mlxsw_sp, lag_dev, extack);
+       if (err)
+               goto err_replay;
+
        return 0;
 
+err_replay:
+       mlxsw_sp_router_port_leave_lag(mlxsw_sp_port, lag_dev);
 err_router_join:
        lag->ref_count--;
        mlxsw_sp_port->lagged = 0;
index 9263a91..18d1bcb 100644 (file)
@@ -9781,6 +9781,97 @@ mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
        return err;
 }
 
+struct mlxsw_sp_router_replay_inetaddr_up {
+       struct mlxsw_sp *mlxsw_sp;
+       struct netlink_ext_ack *extack;
+       unsigned int done;
+};
+
+static int mlxsw_sp_router_replay_inetaddr_up(struct net_device *dev,
+                                             struct netdev_nested_priv *priv)
+{
+       struct mlxsw_sp_router_replay_inetaddr_up *ctx = priv->data;
+       struct mlxsw_sp_crif *crif;
+       int err;
+
+       if (mlxsw_sp_dev_addr_list_empty(dev))
+               return 0;
+
+       crif = mlxsw_sp_crif_lookup(ctx->mlxsw_sp->router, dev);
+       if (!crif || crif->rif)
+               return 0;
+
+       if (!mlxsw_sp_rif_should_config(crif->rif, dev, NETDEV_UP))
+               return 0;
+
+       err = __mlxsw_sp_inetaddr_event(ctx->mlxsw_sp, dev, NETDEV_UP,
+                                       false, ctx->extack);
+       if (err)
+               return err;
+
+       ctx->done++;
+       return 0;
+}
+
+static int mlxsw_sp_router_unreplay_inetaddr_up(struct net_device *dev,
+                                               struct netdev_nested_priv *priv)
+{
+       struct mlxsw_sp_router_replay_inetaddr_up *ctx = priv->data;
+       struct mlxsw_sp_crif *crif;
+
+       if (!ctx->done)
+               return 0;
+
+       if (mlxsw_sp_dev_addr_list_empty(dev))
+               return 0;
+
+       crif = mlxsw_sp_crif_lookup(ctx->mlxsw_sp->router, dev);
+       if (!crif || !crif->rif)
+               return 0;
+
+       /* We are rolling back NETDEV_UP, so ask for that. */
+       if (!mlxsw_sp_rif_should_config(crif->rif, dev, NETDEV_UP))
+               return 0;
+
+       __mlxsw_sp_inetaddr_event(ctx->mlxsw_sp, dev, NETDEV_DOWN, false, NULL);
+
+       ctx->done--;
+       return 0;
+}
+
+int mlxsw_sp_netdevice_enslavement_replay(struct mlxsw_sp *mlxsw_sp,
+                                         struct net_device *upper_dev,
+                                         struct netlink_ext_ack *extack)
+{
+       struct mlxsw_sp_router_replay_inetaddr_up ctx = {
+               .mlxsw_sp = mlxsw_sp,
+               .extack = extack,
+       };
+       struct netdev_nested_priv priv = {
+               .data = &ctx,
+       };
+       int err;
+
+       err = mlxsw_sp_router_replay_inetaddr_up(upper_dev, &priv);
+       if (err)
+               return err;
+
+       err = netdev_walk_all_upper_dev_rcu(upper_dev,
+                                           mlxsw_sp_router_replay_inetaddr_up,
+                                           &priv);
+       if (err)
+               goto err_replay_up;
+
+       return 0;
+
+err_replay_up:
+       netdev_walk_all_upper_dev_rcu(upper_dev,
+                                     mlxsw_sp_router_unreplay_inetaddr_up,
+                                     &priv);
+       mlxsw_sp_router_unreplay_inetaddr_up(upper_dev, &priv);
+       return err;
+}
+
 static int
 mlxsw_sp_port_vid_router_join_existing(struct mlxsw_sp_port *mlxsw_sp_port,
                                       u16 vid, struct net_device *dev,
@@ -9857,6 +9948,26 @@ err_router_join_dev:
        return err;
 }
 
+static void
+__mlxsw_sp_router_port_leave_lag(struct mlxsw_sp_port *mlxsw_sp_port,
+                                struct net_device *lag_dev)
+{
+       u16 default_vid = MLXSW_SP_DEFAULT_VID;
+       struct net_device *upper_dev;
+       struct list_head *iter;
+       u16 vid;
+
+       netdev_for_each_upper_dev_rcu(lag_dev, upper_dev, iter) {
+               if (!is_vlan_dev(upper_dev))
+                       continue;
+
+               vid = vlan_dev_vlan_id(upper_dev);
+               mlxsw_sp_port_vid_router_leave(mlxsw_sp_port, vid, upper_dev);
+       }
+
+       mlxsw_sp_port_vid_router_leave(mlxsw_sp_port, default_vid, lag_dev);
+}
+
 int mlxsw_sp_router_port_join_lag(struct mlxsw_sp_port *mlxsw_sp_port,
                                  struct net_device *lag_dev,
                                  struct netlink_ext_ack *extack)
@@ -9870,6 +9981,14 @@ int mlxsw_sp_router_port_join_lag(struct mlxsw_sp_port *mlxsw_sp_port,
        return err;
 }
 
+void mlxsw_sp_router_port_leave_lag(struct mlxsw_sp_port *mlxsw_sp_port,
+                                   struct net_device *lag_dev)
+{
+       mutex_lock(&mlxsw_sp_port->mlxsw_sp->router->lock);
+       __mlxsw_sp_router_port_leave_lag(mlxsw_sp_port, lag_dev);
+       mutex_unlock(&mlxsw_sp_port->mlxsw_sp->router->lock);
+}
+
 static int mlxsw_sp_router_netdevice_event(struct notifier_block *nb,
                                           unsigned long event, void *ptr)
 {
index 7424222..eed04fb 100644 (file)
@@ -178,5 +178,10 @@ int mlxsw_sp_router_bridge_vlan_add(struct mlxsw_sp *mlxsw_sp,
 int mlxsw_sp_router_port_join_lag(struct mlxsw_sp_port *mlxsw_sp_port,
                                  struct net_device *lag_dev,
                                  struct netlink_ext_ack *extack);
+void mlxsw_sp_router_port_leave_lag(struct mlxsw_sp_port *mlxsw_sp_port,
+                                   struct net_device *lag_dev);
+int mlxsw_sp_netdevice_enslavement_replay(struct mlxsw_sp *mlxsw_sp,
+                                         struct net_device *upper_dev,
+                                         struct netlink_ext_ack *extack);
 
 #endif /* _MLXSW_ROUTER_H_*/
index 982eae6..dffb67c 100644 (file)
@@ -2894,8 +2894,15 @@ int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port,
        if (err)
                goto err_port_join;
 
+       err = mlxsw_sp_netdevice_enslavement_replay(mlxsw_sp, br_dev, extack);
+       if (err)
+               goto err_replay;
+
        return 0;
 
+err_replay:
+       bridge_device->ops->port_leave(bridge_device, bridge_port,
+                                      mlxsw_sp_port);
 err_port_join:
        mlxsw_sp_bridge_port_put(mlxsw_sp->bridge, bridge_port);
        return err;