Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b1e45526 authored by Ido Schimmel's avatar Ido Schimmel Committed by David S. Miller
Browse files

mlxsw: spectrum_router: Simplify VRF enslavement



When a netdev is enslaved to a VRF master, its router interface (RIF)
needs to be destroyed (if exists) and a new one created using the
corresponding virtual router (VR).

>From the driver's perspective, the above is equivalent to an inetaddr
event sent for this netdev. Therefore, when a port netdev (or its
uppers) are enslaved to a VRF master, call the same function that
would've been called had a NETDEV_UP was sent for this netdev in the
inetaddr notification chain.

This patch also fixes a bug when a LAG netdev with an existing RIF is
enslaved to a VRF. Before this patch, each LAG port would drop the
reference on the RIF, but would re-join the same one (in the wrong VR)
soon after. With this patch, the corresponding RIF is first destroyed
and a new one is created using the correct VR.

Fixes: 7179eb5a ("mlxsw: spectrum_router: Add support for VRFs")
Signed-off-by: default avatarIdo Schimmel <idosch@mellanox.com>
Reviewed-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent cedf90c0
Loading
Loading
Loading
Loading
+13 −64
Original line number Original line Diff line number Diff line
@@ -4106,7 +4106,6 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev,
		if (!is_vlan_dev(upper_dev) &&
		if (!is_vlan_dev(upper_dev) &&
		    !netif_is_lag_master(upper_dev) &&
		    !netif_is_lag_master(upper_dev) &&
		    !netif_is_bridge_master(upper_dev) &&
		    !netif_is_bridge_master(upper_dev) &&
		    !netif_is_l3_master(upper_dev) &&
		    !netif_is_ovs_master(upper_dev))
		    !netif_is_ovs_master(upper_dev))
			return -EINVAL;
			return -EINVAL;
		if (!info->linking)
		if (!info->linking)
@@ -4151,11 +4150,6 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev,
			else
			else
				mlxsw_sp_port_lag_leave(mlxsw_sp_port,
				mlxsw_sp_port_lag_leave(mlxsw_sp_port,
							upper_dev);
							upper_dev);
		} else if (netif_is_l3_master(upper_dev)) {
			if (info->linking)
				err = mlxsw_sp_port_vrf_join(mlxsw_sp_port);
			else
				mlxsw_sp_port_vrf_leave(mlxsw_sp_port);
		} else if (netif_is_ovs_master(upper_dev)) {
		} else if (netif_is_ovs_master(upper_dev)) {
			if (info->linking)
			if (info->linking)
				err = mlxsw_sp_port_ovs_join(mlxsw_sp_port);
				err = mlxsw_sp_port_ovs_join(mlxsw_sp_port);
@@ -4275,7 +4269,7 @@ static int mlxsw_sp_netdevice_bridge_event(struct net_device *br_dev,
	switch (event) {
	switch (event) {
	case NETDEV_PRECHANGEUPPER:
	case NETDEV_PRECHANGEUPPER:
		upper_dev = info->upper_dev;
		upper_dev = info->upper_dev;
		if (!is_vlan_dev(upper_dev) && !netif_is_l3_master(upper_dev))
		if (!is_vlan_dev(upper_dev))
			return -EINVAL;
			return -EINVAL;
		if (is_vlan_dev(upper_dev) &&
		if (is_vlan_dev(upper_dev) &&
		    br_dev != mlxsw_sp->master_bridge.dev)
		    br_dev != mlxsw_sp->master_bridge.dev)
@@ -4290,12 +4284,6 @@ static int mlxsw_sp_netdevice_bridge_event(struct net_device *br_dev,
			else
			else
				mlxsw_sp_master_bridge_vlan_unlink(mlxsw_sp,
				mlxsw_sp_master_bridge_vlan_unlink(mlxsw_sp,
								   upper_dev);
								   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 {
		} else {
			err = -EINVAL;
			err = -EINVAL;
			WARN_ON(1);
			WARN_ON(1);
@@ -4529,8 +4517,7 @@ static int mlxsw_sp_netdevice_vport_event(struct net_device *dev,
	switch (event) {
	switch (event) {
	case NETDEV_PRECHANGEUPPER:
	case NETDEV_PRECHANGEUPPER:
		upper_dev = info->upper_dev;
		upper_dev = info->upper_dev;
		if (!netif_is_bridge_master(upper_dev) &&
		if (!netif_is_bridge_master(upper_dev))
		    !netif_is_l3_master(upper_dev))
			return -EINVAL;
			return -EINVAL;
		if (!info->linking)
		if (!info->linking)
			break;
			break;
@@ -4550,11 +4537,6 @@ static int mlxsw_sp_netdevice_vport_event(struct net_device *dev,
								 upper_dev);
								 upper_dev);
			else
			else
				mlxsw_sp_vport_bridge_leave(mlxsw_sp_vport);
				mlxsw_sp_vport_bridge_leave(mlxsw_sp_vport);
		} else if (netif_is_l3_master(upper_dev)) {
			if (info->linking)
				err = mlxsw_sp_vport_vrf_join(mlxsw_sp_vport);
			else
				mlxsw_sp_vport_vrf_leave(mlxsw_sp_vport);
		} else {
		} else {
			err = -EINVAL;
			err = -EINVAL;
			WARN_ON(1);
			WARN_ON(1);
@@ -4585,47 +4567,6 @@ static int mlxsw_sp_netdevice_lag_vport_event(struct net_device *lag_dev,
	return 0;
	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,
static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev,
					 unsigned long event, void *ptr)
					 unsigned long event, void *ptr)
{
{
@@ -4638,13 +4579,19 @@ static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev,
	else if (netif_is_lag_master(real_dev))
	else if (netif_is_lag_master(real_dev))
		return mlxsw_sp_netdevice_lag_vport_event(real_dev, event, ptr,
		return mlxsw_sp_netdevice_lag_vport_event(real_dev, event, ptr,
							  vid);
							  vid);
	else if (netif_is_bridge_master(real_dev))
		return mlxsw_sp_netdevice_bridge_vlan_event(vlan_dev, event,
							    ptr);


	return 0;
	return 0;
}
}


static bool mlxsw_sp_is_vrf_event(unsigned long event, void *ptr)
{
	struct netdev_notifier_changeupper_info *info = ptr;

	if (event != NETDEV_PRECHANGEUPPER && event != NETDEV_CHANGEUPPER)
		return false;
	return netif_is_l3_master(info->upper_dev);
}

static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
				    unsigned long event, void *ptr)
				    unsigned long event, void *ptr)
{
{
@@ -4653,6 +4600,8 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused,


	if (event == NETDEV_CHANGEADDR || event == NETDEV_CHANGEMTU)
	if (event == NETDEV_CHANGEADDR || event == NETDEV_CHANGEMTU)
		err = mlxsw_sp_netdevice_router_port_event(dev);
		err = mlxsw_sp_netdevice_router_port_event(dev);
	else if (mlxsw_sp_is_vrf_event(event, ptr))
		err = mlxsw_sp_netdevice_vrf_event(dev, event, ptr);
	else if (mlxsw_sp_port_dev_check(dev))
	else if (mlxsw_sp_port_dev_check(dev))
		err = mlxsw_sp_netdevice_port_event(dev, event, ptr);
		err = mlxsw_sp_netdevice_port_event(dev, event, ptr);
	else if (netif_is_lag_master(dev))
	else if (netif_is_lag_master(dev))
+2 −8
Original line number Original line Diff line number Diff line
@@ -576,14 +576,8 @@ int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
			    unsigned long event, void *ptr);
			    unsigned long event, void *ptr);
void mlxsw_sp_rif_bridge_destroy(struct mlxsw_sp *mlxsw_sp,
void mlxsw_sp_rif_bridge_destroy(struct mlxsw_sp *mlxsw_sp,
				 struct mlxsw_sp_rif *rif);
				 struct mlxsw_sp_rif *rif);
int mlxsw_sp_vport_vrf_join(struct mlxsw_sp_port *mlxsw_sp_vport);
int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
void mlxsw_sp_vport_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_vport);
				 struct netdev_notifier_changeupper_info *info);
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,
int mlxsw_sp_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, unsigned int entry_count,
			u32 *p_entry_index);
			u32 *p_entry_index);
+48 −59
Original line number Original line Diff line number Diff line
@@ -3345,6 +3345,21 @@ static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
	return 0;
	return 0;
}
}


static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
				     unsigned long event)
{
	if (mlxsw_sp_port_dev_check(dev))
		return mlxsw_sp_inetaddr_port_event(dev, event);
	else if (netif_is_lag_master(dev))
		return mlxsw_sp_inetaddr_lag_event(dev, event);
	else if (netif_is_bridge_master(dev))
		return mlxsw_sp_inetaddr_bridge_event(dev, dev, event);
	else if (is_vlan_dev(dev))
		return mlxsw_sp_inetaddr_vlan_event(dev, event);
	else
		return 0;
}

int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
			    unsigned long event, void *ptr)
			    unsigned long event, void *ptr)
{
{
@@ -3362,15 +3377,7 @@ int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
	if (!mlxsw_sp_rif_should_config(rif, ifa->ifa_dev, event))
	if (!mlxsw_sp_rif_should_config(rif, ifa->ifa_dev, event))
		goto out;
		goto out;


	if (mlxsw_sp_port_dev_check(dev))
	err = __mlxsw_sp_inetaddr_event(dev, event);
		err = mlxsw_sp_inetaddr_port_event(dev, event);
	else if (netif_is_lag_master(dev))
		err = mlxsw_sp_inetaddr_lag_event(dev, event);
	else if (netif_is_bridge_master(dev))
		err = mlxsw_sp_inetaddr_bridge_event(dev, dev, event);
	else if (is_vlan_dev(dev))
		err = mlxsw_sp_inetaddr_vlan_event(dev, event);

out:
out:
	return notifier_from_errno(err);
	return notifier_from_errno(err);
}
}
@@ -3433,71 +3440,53 @@ int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
	return err;
	return err;
}
}


int mlxsw_sp_vport_vrf_join(struct mlxsw_sp_port *mlxsw_sp_vport)
static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
				  struct net_device *l3_dev)
{
{
	struct mlxsw_sp_fid *f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport);
	struct mlxsw_sp_rif *rif;
	struct net_device *dev = mlxsw_sp_vport->dev;


	/* In case vPort already has a RIF, then we need to drop it.
	/* If netdev is already associated with a RIF, then we need to
	 * A new one will be created using the VRF's VR.
	 * destroy it and create a new one with the new virtual router ID.
	 */
	 */
	if (f && f->rif)
	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
		mlxsw_sp_vport_rif_sp_leave(mlxsw_sp_vport);
	if (rif)

		__mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
	return mlxsw_sp_vport_rif_sp_join(mlxsw_sp_vport, dev);
}

void mlxsw_sp_vport_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_vport)
{
	mlxsw_sp_vport_rif_sp_leave(mlxsw_sp_vport);
}

int mlxsw_sp_port_vrf_join(struct mlxsw_sp_port *mlxsw_sp_port)
{
	struct mlxsw_sp_port *mlxsw_sp_vport;

	mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, 1);
	if (WARN_ON(!mlxsw_sp_vport))
		return -EINVAL;


	return mlxsw_sp_vport_vrf_join(mlxsw_sp_vport);
	return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
}
}


void mlxsw_sp_port_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_port)
static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
				    struct net_device *l3_dev)
{
{
	struct mlxsw_sp_port *mlxsw_sp_vport;
	struct mlxsw_sp_rif *rif;


	mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, 1);
	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
	if (WARN_ON(!mlxsw_sp_vport))
	if (!rif)
		return;
		return;

	__mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
	mlxsw_sp_vport_vrf_leave(mlxsw_sp_vport);
}
}


int mlxsw_sp_bridge_vrf_join(struct mlxsw_sp *mlxsw_sp,
int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
			     struct net_device *l3_dev)
				 struct netdev_notifier_changeupper_info *info)
{
{
	struct mlxsw_sp_fid *f;
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);

	int err = 0;
	f = mlxsw_sp_bridge_fid_get(mlxsw_sp, l3_dev);
	if (WARN_ON(!f))
		return -EINVAL;


	if (f->rif)
	if (!mlxsw_sp)
		mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->rif);
		return 0;


	return mlxsw_sp_rif_bridge_create(mlxsw_sp, l3_dev, f);
	switch (event) {
	case NETDEV_PRECHANGEUPPER:
		return 0;
	case NETDEV_CHANGEUPPER:
		if (info->linking)
			err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
		else
			mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
		break;
	}
	}


void mlxsw_sp_bridge_vrf_leave(struct mlxsw_sp *mlxsw_sp,
	return err;
			       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->rif);
}
}


static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)