diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 659df3225a24..28019f8b70f7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -4111,7 +4111,7 @@ static int mlxsw_sp_netdevice_bridge_event(struct net_device *br_dev, switch (event) { case NETDEV_PRECHANGEUPPER: upper_dev = info->upper_dev; - if (!is_vlan_dev(upper_dev)) + if (!is_vlan_dev(upper_dev) && !netif_is_l3_master(upper_dev)) return -EINVAL; if (is_vlan_dev(upper_dev) && br_dev != mlxsw_sp->master_bridge.dev) @@ -4126,6 +4126,12 @@ static int mlxsw_sp_netdevice_bridge_event(struct net_device *br_dev, else mlxsw_sp_master_bridge_vlan_unlink(mlxsw_sp, upper_dev); + } else if (netif_is_l3_master(upper_dev)) { + if (info->linking) + err = mlxsw_sp_bridge_vrf_join(mlxsw_sp, + br_dev); + else + mlxsw_sp_bridge_vrf_leave(mlxsw_sp, br_dev); } else { err = -EINVAL; WARN_ON(1); @@ -4415,6 +4421,47 @@ static int mlxsw_sp_netdevice_lag_vport_event(struct net_device *lag_dev, return 0; } +static int mlxsw_sp_netdevice_bridge_vlan_event(struct net_device *vlan_dev, + unsigned long event, void *ptr) +{ + struct netdev_notifier_changeupper_info *info; + struct mlxsw_sp *mlxsw_sp; + int err = 0; + + mlxsw_sp = mlxsw_sp_lower_get(vlan_dev); + if (!mlxsw_sp) + return 0; + + info = ptr; + + switch (event) { + case NETDEV_PRECHANGEUPPER: + /* VLAN devices are only allowed on top of the + * VLAN-aware bridge. + */ + if (WARN_ON(vlan_dev_real_dev(vlan_dev) != + mlxsw_sp->master_bridge.dev)) + return -EINVAL; + if (!netif_is_l3_master(info->upper_dev)) + return -EINVAL; + break; + case NETDEV_CHANGEUPPER: + if (netif_is_l3_master(info->upper_dev)) { + if (info->linking) + err = mlxsw_sp_bridge_vrf_join(mlxsw_sp, + vlan_dev); + else + mlxsw_sp_bridge_vrf_leave(mlxsw_sp, vlan_dev); + } else { + err = -EINVAL; + WARN_ON(1); + } + break; + } + + return err; +} + static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev, unsigned long event, void *ptr) { @@ -4427,6 +4474,9 @@ static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev, else if (netif_is_lag_master(real_dev)) return mlxsw_sp_netdevice_lag_vport_event(real_dev, event, ptr, vid); + else if (netif_is_bridge_master(real_dev)) + return mlxsw_sp_netdevice_bridge_vlan_event(vlan_dev, event, + ptr); return 0; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 60004d97e631..0e223f6983c5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -582,6 +582,10 @@ int mlxsw_sp_vport_vrf_join(struct mlxsw_sp_port *mlxsw_sp_vport); void mlxsw_sp_vport_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_vport); int mlxsw_sp_port_vrf_join(struct mlxsw_sp_port *mlxsw_sp_port); void mlxsw_sp_port_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_port); +int mlxsw_sp_bridge_vrf_join(struct mlxsw_sp *mlxsw_sp, + struct net_device *l3_dev); +void mlxsw_sp_bridge_vrf_leave(struct mlxsw_sp *mlxsw_sp, + struct net_device *l3_dev); int mlxsw_sp_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, unsigned int entry_count); void mlxsw_sp_kvdl_free(struct mlxsw_sp *mlxsw_sp, int entry_index); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index e26268805fcc..c89f4b4d70b1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3267,6 +3267,32 @@ void mlxsw_sp_port_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_port) mlxsw_sp_vport_vrf_leave(mlxsw_sp_vport); } +int mlxsw_sp_bridge_vrf_join(struct mlxsw_sp *mlxsw_sp, + struct net_device *l3_dev) +{ + struct mlxsw_sp_fid *f; + + f = mlxsw_sp_bridge_fid_get(mlxsw_sp, l3_dev); + if (WARN_ON(!f)) + return -EINVAL; + + if (f->r) + mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r); + + return mlxsw_sp_rif_bridge_create(mlxsw_sp, l3_dev, f); +} + +void mlxsw_sp_bridge_vrf_leave(struct mlxsw_sp *mlxsw_sp, + struct net_device *l3_dev) +{ + struct mlxsw_sp_fid *f; + + f = mlxsw_sp_bridge_fid_get(mlxsw_sp, l3_dev); + if (WARN_ON(!f)) + return; + mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r); +} + static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb) { struct mlxsw_sp *mlxsw_sp = container_of(nb, struct mlxsw_sp, fib_nb);