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)
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);
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)
{
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;
}
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);
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);