mlxsw: spectrum_router: Replay IP NETDEV_UP on device deslavement
authorPetr Machata <petrm@nvidia.com>
Wed, 19 Jul 2023 11:01:31 +0000 (13:01 +0200)
committerDavid S. Miller <davem@davemloft.net>
Fri, 21 Jul 2023 07:54:06 +0000 (08:54 +0100)
When a netdevice is removed from a bridge or a LAG, and it has an IP
address, it should join the router and gain a RIF. Do that by replaying
address addition event on the netdevice.

When handling deslavement of LAG or its upper from a bridge device, the
replay should be done after all the lowers of the LAG have left the bridge.
Thus these scenarios are handled by passing replay_deslavement of false,
and by invoking, after the lowers have been processed, a new helper,
mlxsw_sp_netdevice_post_lag_event(), which does the per-LAG / -upper
handling, and in particular invokes the replay.

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

index 0dcd988..b955511 100644 (file)
@@ -4838,15 +4838,20 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
        case NETDEV_CHANGEUPPER:
                upper_dev = info->upper_dev;
                if (netif_is_bridge_master(upper_dev)) {
-                       if (info->linking)
+                       if (info->linking) {
                                err = mlxsw_sp_port_bridge_join(mlxsw_sp_port,
                                                                lower_dev,
                                                                upper_dev,
                                                                extack);
-                       else
+                       } else {
                                mlxsw_sp_port_bridge_leave(mlxsw_sp_port,
                                                           lower_dev,
                                                           upper_dev);
+                               if (!replay_deslavement)
+                                       break;
+                               mlxsw_sp_netdevice_deslavement_replay(mlxsw_sp,
+                                                                     lower_dev);
+                       }
                } else if (netif_is_lag_master(upper_dev)) {
                        if (info->linking) {
                                err = mlxsw_sp_port_lag_join(mlxsw_sp_port,
@@ -4855,6 +4860,8 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
                                mlxsw_sp_port_lag_col_dist_disable(mlxsw_sp_port);
                                mlxsw_sp_port_lag_leave(mlxsw_sp_port,
                                                        upper_dev);
+                               mlxsw_sp_netdevice_deslavement_replay(mlxsw_sp,
+                                                                     dev);
                        }
                } else if (netif_is_ovs_master(upper_dev)) {
                        if (info->linking)
@@ -4924,6 +4931,30 @@ static int mlxsw_sp_netdevice_port_event(struct net_device *lower_dev,
        return 0;
 }
 
+/* Called for LAG or its upper VLAN after the per-LAG-lower processing was done,
+ * to do any per-LAG / per-LAG-upper processing.
+ */
+static int mlxsw_sp_netdevice_post_lag_event(struct net_device *dev,
+                                            unsigned long event,
+                                            void *ptr)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(dev);
+       struct netdev_notifier_changeupper_info *info = ptr;
+
+       if (!mlxsw_sp)
+               return 0;
+
+       switch (event) {
+       case NETDEV_CHANGEUPPER:
+               if (info->linking)
+                       break;
+               if (netif_is_bridge_master(info->upper_dev))
+                       mlxsw_sp_netdevice_deslavement_replay(mlxsw_sp, dev);
+               break;
+       }
+       return 0;
+}
+
 static int mlxsw_sp_netdevice_lag_event(struct net_device *lag_dev,
                                        unsigned long event, void *ptr)
 {
@@ -4940,7 +4971,7 @@ static int mlxsw_sp_netdevice_lag_event(struct net_device *lag_dev,
                }
        }
 
-       return 0;
+       return mlxsw_sp_netdevice_post_lag_event(lag_dev, event, ptr);
 }
 
 static int mlxsw_sp_netdevice_port_vlan_event(struct net_device *vlan_dev,
@@ -4984,15 +5015,20 @@ static int mlxsw_sp_netdevice_port_vlan_event(struct net_device *vlan_dev,
        case NETDEV_CHANGEUPPER:
                upper_dev = info->upper_dev;
                if (netif_is_bridge_master(upper_dev)) {
-                       if (info->linking)
+                       if (info->linking) {
                                err = mlxsw_sp_port_bridge_join(mlxsw_sp_port,
                                                                vlan_dev,
                                                                upper_dev,
                                                                extack);
-                       else
+                       } else {
                                mlxsw_sp_port_bridge_leave(mlxsw_sp_port,
                                                           vlan_dev,
                                                           upper_dev);
+                               if (!replay_deslavement)
+                                       break;
+                               mlxsw_sp_netdevice_deslavement_replay(mlxsw_sp,
+                                                                     vlan_dev);
+                       }
                } else if (netif_is_macvlan(upper_dev)) {
                        if (!info->linking)
                                mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
@@ -5022,7 +5058,7 @@ static int mlxsw_sp_netdevice_lag_port_vlan_event(struct net_device *vlan_dev,
                }
        }
 
-       return 0;
+       return mlxsw_sp_netdevice_post_lag_event(vlan_dev, event, ptr);
 }
 
 static int mlxsw_sp_netdevice_bridge_vlan_event(struct mlxsw_sp *mlxsw_sp,
index 18d1bcb..57f0faa 100644 (file)
@@ -9785,12 +9785,14 @@ struct mlxsw_sp_router_replay_inetaddr_up {
        struct mlxsw_sp *mlxsw_sp;
        struct netlink_ext_ack *extack;
        unsigned int done;
+       bool deslavement;
 };
 
 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;
+       bool nomaster = ctx->deslavement;
        struct mlxsw_sp_crif *crif;
        int err;
 
@@ -9805,7 +9807,7 @@ static int mlxsw_sp_router_replay_inetaddr_up(struct net_device *dev,
                return 0;
 
        err = __mlxsw_sp_inetaddr_event(ctx->mlxsw_sp, dev, NETDEV_UP,
-                                       false, ctx->extack);
+                                       nomaster, ctx->extack);
        if (err)
                return err;
 
@@ -9817,6 +9819,7 @@ 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;
+       bool nomaster = ctx->deslavement;
        struct mlxsw_sp_crif *crif;
 
        if (!ctx->done)
@@ -9833,7 +9836,8 @@ static int mlxsw_sp_router_unreplay_inetaddr_up(struct net_device *dev,
        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);
+       __mlxsw_sp_inetaddr_event(ctx->mlxsw_sp, dev, NETDEV_DOWN, nomaster,
+                                 NULL);
 
        ctx->done--;
        return 0;
@@ -9846,6 +9850,7 @@ int mlxsw_sp_netdevice_enslavement_replay(struct mlxsw_sp *mlxsw_sp,
        struct mlxsw_sp_router_replay_inetaddr_up ctx = {
                .mlxsw_sp = mlxsw_sp,
                .extack = extack,
+               .deslavement = false,
        };
        struct netdev_nested_priv priv = {
                .data = &ctx,
@@ -9872,6 +9877,20 @@ err_replay_up:
        return err;
 }
 
+void mlxsw_sp_netdevice_deslavement_replay(struct mlxsw_sp *mlxsw_sp,
+                                          struct net_device *dev)
+{
+       struct mlxsw_sp_router_replay_inetaddr_up ctx = {
+               .mlxsw_sp = mlxsw_sp,
+               .deslavement = true,
+       };
+       struct netdev_nested_priv priv = {
+               .data = &ctx,
+       };
+
+       mlxsw_sp_router_replay_inetaddr_up(dev, &priv);
+}
+
 static int
 mlxsw_sp_port_vid_router_join_existing(struct mlxsw_sp_port *mlxsw_sp_port,
                                       u16 vid, struct net_device *dev,
index eed04fb..ed3b628 100644 (file)
@@ -183,5 +183,7 @@ void mlxsw_sp_router_port_leave_lag(struct mlxsw_sp_port *mlxsw_sp_port,
 int mlxsw_sp_netdevice_enslavement_replay(struct mlxsw_sp *mlxsw_sp,
                                          struct net_device *upper_dev,
                                          struct netlink_ext_ack *extack);
+void mlxsw_sp_netdevice_deslavement_replay(struct mlxsw_sp *mlxsw_sp,
+                                          struct net_device *dev);
 
 #endif /* _MLXSW_ROUTER_H_*/