Commit eb600204 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'mlxsw-dedicated-router-notification-block'



Ido Schimmel says:

====================
mlxsw: A dedicated notifier block for router code

Petr says:

Currently all netdevice events are handled in the centralized notifier
handler maintained by spectrum.c. Since a number of events are involving
router code, spectrum.c needs to dispatch them to spectrum_router.c. The
spectrum module therefore needs to know more about the router code than it
should have, and there is are several API points through which the two
modules communicate.

In this patchset, move bulk of the router-related event handling to the
router code. Some of the knowledge has to stay: spectrum.c cannot veto
events that the router supports, and vice versa. But beyond that, the two
can ignore each other's details, which leads to more focused and simpler
code.

As a side effect, this fixes L3 HW stats support on tunnel netdevices.

The patch set progresses as follows:

- In patch #1, change spectrum code to not bounce L3 enslavement, which the
  router code supports.

- In patch #2, add a new do-nothing notifier block to the router code.

- In patches #3-#6, move router-specific event handling to the router
  module. In patch #7, clean up a comment.

- In patch #8, use the advantage that all router event handling is in the
  router code and clean up taking router lock.

- mlxsw supports L3 HW stats on tunnels as of this patchset. Patches #9 and
  #10 therefore add a selftest for L3 HW stats support on tunnels.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 9f88af22 813f97a2
Loading
Loading
Loading
Loading
+18 −48
Original line number Diff line number Diff line
@@ -3122,9 +3122,8 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
		}
	}

	/* Initialize netdevice notifier after router and SPAN is initialized,
	 * so that the event handler can use router structures and call SPAN
	 * respin.
	/* Initialize netdevice notifier after SPAN is initialized, so that the
	 * event handler can call SPAN respin.
	 */
	mlxsw_sp->netdevice_nb.notifier_call = mlxsw_sp_netdevice_event;
	err = register_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp),
@@ -4525,7 +4524,8 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
		    !netif_is_lag_master(upper_dev) &&
		    !netif_is_bridge_master(upper_dev) &&
		    !netif_is_ovs_master(upper_dev) &&
		    !netif_is_macvlan(upper_dev)) {
		    !netif_is_macvlan(upper_dev) &&
		    !netif_is_l3_master(upper_dev)) {
			NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
			return -EINVAL;
		}
@@ -4724,7 +4724,8 @@ static int mlxsw_sp_netdevice_port_vlan_event(struct net_device *vlan_dev,
	case NETDEV_PRECHANGEUPPER:
		upper_dev = info->upper_dev;
		if (!netif_is_bridge_master(upper_dev) &&
		    !netif_is_macvlan(upper_dev)) {
		    !netif_is_macvlan(upper_dev) &&
		    !netif_is_l3_master(upper_dev)) {
			NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
			return -EINVAL;
		}
@@ -4763,9 +4764,6 @@ static int mlxsw_sp_netdevice_port_vlan_event(struct net_device *vlan_dev,
		} else if (netif_is_macvlan(upper_dev)) {
			if (!info->linking)
				mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
		} else {
			err = -EINVAL;
			WARN_ON(1);
		}
		break;
	}
@@ -4813,7 +4811,8 @@ static int mlxsw_sp_netdevice_bridge_vlan_event(struct net_device *vlan_dev,
	switch (event) {
	case NETDEV_PRECHANGEUPPER:
		upper_dev = info->upper_dev;
		if (!netif_is_macvlan(upper_dev)) {
		if (!netif_is_macvlan(upper_dev) &&
		    !netif_is_l3_master(upper_dev)) {
			NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
			return -EOPNOTSUPP;
		}
@@ -4874,7 +4873,9 @@ 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) && !netif_is_macvlan(upper_dev)) {
		if (!is_vlan_dev(upper_dev) &&
		    !netif_is_macvlan(upper_dev) &&
		    !netif_is_l3_master(upper_dev)) {
			NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
			return -EOPNOTSUPP;
		}
@@ -4918,25 +4919,20 @@ static int mlxsw_sp_netdevice_macvlan_event(struct net_device *macvlan_dev,
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(macvlan_dev);
	struct netdev_notifier_changeupper_info *info = ptr;
	struct netlink_ext_ack *extack;
	struct net_device *upper_dev;

	if (!mlxsw_sp || event != NETDEV_PRECHANGEUPPER)
		return 0;

	extack = netdev_notifier_info_to_extack(&info->info);
	upper_dev = info->upper_dev;

	/* VRF enslavement is handled in mlxsw_sp_netdevice_vrf_event() */
	if (!netif_is_l3_master(upper_dev)) {
		NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");

		return -EOPNOTSUPP;
	}

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);
	return 0;
}

static int mlxsw_sp_netdevice_vxlan_event(struct mlxsw_sp *mlxsw_sp,
@@ -5007,22 +5003,6 @@ static int mlxsw_sp_netdevice_vxlan_event(struct mlxsw_sp *mlxsw_sp,
	return 0;
}

static bool mlxsw_sp_netdevice_event_is_router(unsigned long event)
{
	switch (event) {
	case NETDEV_PRE_CHANGEADDR:
	case NETDEV_CHANGEADDR:
	case NETDEV_CHANGEMTU:
	case NETDEV_OFFLOAD_XSTATS_ENABLE:
	case NETDEV_OFFLOAD_XSTATS_DISABLE:
	case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
	case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
		return true;
	default:
		return false;
	}
}

static int mlxsw_sp_netdevice_event(struct notifier_block *nb,
				    unsigned long event, void *ptr)
{
@@ -5041,16 +5021,6 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *nb,

	if (netif_is_vxlan(dev))
		err = mlxsw_sp_netdevice_vxlan_event(mlxsw_sp, dev, event, ptr);
	if (mlxsw_sp_netdev_is_ipip_ol(mlxsw_sp, dev))
		err = mlxsw_sp_netdevice_ipip_ol_event(mlxsw_sp, dev,
						       event, ptr);
	else if (mlxsw_sp_netdev_is_ipip_ul(mlxsw_sp, dev))
		err = mlxsw_sp_netdevice_ipip_ul_event(mlxsw_sp, dev,
						       event, ptr);
	else if (mlxsw_sp_netdevice_event_is_router(event))
		err = mlxsw_sp_netdevice_router_port_event(dev, event, ptr);
	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))
		err = mlxsw_sp_netdevice_port_event(dev, dev, event, ptr);
	else if (netif_is_lag_master(dev))
+0 −17
Original line number Diff line number Diff line
@@ -718,29 +718,12 @@ union mlxsw_sp_l3addr {
int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp,
			 struct netlink_ext_ack *extack);
void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp);
int mlxsw_sp_netdevice_router_port_event(struct net_device *dev,
					 unsigned long event, void *ptr);
void mlxsw_sp_rif_macvlan_del(struct mlxsw_sp *mlxsw_sp,
			      const struct net_device *macvlan_dev);
int mlxsw_sp_inetaddr_valid_event(struct notifier_block *unused,
				  unsigned long event, void *ptr);
int mlxsw_sp_inet6addr_valid_event(struct notifier_block *unused,
				   unsigned long event, void *ptr);
int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
				 struct netdev_notifier_changeupper_info *info);
bool mlxsw_sp_netdev_is_ipip_ol(const struct mlxsw_sp *mlxsw_sp,
				const struct net_device *dev);
bool mlxsw_sp_netdev_is_ipip_ul(struct mlxsw_sp *mlxsw_sp,
				const struct net_device *dev);
int mlxsw_sp_netdevice_ipip_ol_event(struct mlxsw_sp *mlxsw_sp,
				     struct net_device *l3_dev,
				     unsigned long event,
				     struct netdev_notifier_info *info);
int
mlxsw_sp_netdevice_ipip_ul_event(struct mlxsw_sp *mlxsw_sp,
				 struct net_device *l3_dev,
				 unsigned long event,
				 struct netdev_notifier_info *info);
int
mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
			       struct net_device *l3_dev,
+115 −45
Original line number Diff line number Diff line
@@ -1530,7 +1530,7 @@ static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp,
	return false;
}

bool mlxsw_sp_netdev_is_ipip_ol(const struct mlxsw_sp *mlxsw_sp,
static bool mlxsw_sp_netdev_is_ipip_ol(const struct mlxsw_sp *mlxsw_sp,
				       const struct net_device *dev)
{
	return mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL);
@@ -1575,16 +1575,10 @@ mlxsw_sp_ipip_entry_find_by_ul_dev(const struct mlxsw_sp *mlxsw_sp,
	return NULL;
}

bool mlxsw_sp_netdev_is_ipip_ul(struct mlxsw_sp *mlxsw_sp,
static bool mlxsw_sp_netdev_is_ipip_ul(struct mlxsw_sp *mlxsw_sp,
				       const struct net_device *dev)
{
	bool is_ipip_ul;

	mutex_lock(&mlxsw_sp->router->lock);
	is_ipip_ul = mlxsw_sp_ipip_entry_find_by_ul_dev(mlxsw_sp, dev, NULL);
	mutex_unlock(&mlxsw_sp->router->lock);

	return is_ipip_ul;
	return mlxsw_sp_ipip_entry_find_by_ul_dev(mlxsw_sp, dev, NULL);
}

static bool mlxsw_sp_netdevice_ipip_can_offload(struct mlxsw_sp *mlxsw_sp,
@@ -1960,7 +1954,7 @@ static void mlxsw_sp_ipip_demote_tunnel_by_ul_netdev(struct mlxsw_sp *mlxsw_sp,
	}
}

int mlxsw_sp_netdevice_ipip_ol_event(struct mlxsw_sp *mlxsw_sp,
static int mlxsw_sp_netdevice_ipip_ol_event(struct mlxsw_sp *mlxsw_sp,
					    struct net_device *ol_dev,
					    unsigned long event,
					    struct netdev_notifier_info *info)
@@ -1969,7 +1963,6 @@ int mlxsw_sp_netdevice_ipip_ol_event(struct mlxsw_sp *mlxsw_sp,
	struct netlink_ext_ack *extack;
	int err = 0;

	mutex_lock(&mlxsw_sp->router->lock);
	switch (event) {
	case NETDEV_REGISTER:
		err = mlxsw_sp_netdevice_ipip_ol_reg_event(mlxsw_sp, ol_dev);
@@ -2000,7 +1993,6 @@ int mlxsw_sp_netdevice_ipip_ol_event(struct mlxsw_sp *mlxsw_sp,
		err = mlxsw_sp_netdevice_ipip_ol_update_mtu(mlxsw_sp, ol_dev);
		break;
	}
	mutex_unlock(&mlxsw_sp->router->lock);
	return err;
}

@@ -2038,16 +2030,15 @@ __mlxsw_sp_netdevice_ipip_ul_event(struct mlxsw_sp *mlxsw_sp,
	return 0;
}

int
static int
mlxsw_sp_netdevice_ipip_ul_event(struct mlxsw_sp *mlxsw_sp,
				 struct net_device *ul_dev,
				 unsigned long event,
				 struct netdev_notifier_info *info)
{
	struct mlxsw_sp_ipip_entry *ipip_entry = NULL;
	int err = 0;
	int err;

	mutex_lock(&mlxsw_sp->router->lock);
	while ((ipip_entry = mlxsw_sp_ipip_entry_find_by_ul_dev(mlxsw_sp,
								ul_dev,
								ipip_entry))) {
@@ -2060,7 +2051,7 @@ mlxsw_sp_netdevice_ipip_ul_event(struct mlxsw_sp *mlxsw_sp,
		if (err) {
			mlxsw_sp_ipip_demote_tunnel_by_ul_netdev(mlxsw_sp,
								 ul_dev);
			break;
			return err;
		}

		if (demote_this) {
@@ -2077,9 +2068,8 @@ mlxsw_sp_netdevice_ipip_ul_event(struct mlxsw_sp *mlxsw_sp,
			ipip_entry = prev;
		}
	}
	mutex_unlock(&mlxsw_sp->router->lock);

	return err;
	return 0;
}

int mlxsw_sp_router_nve_promote_decap(struct mlxsw_sp *mlxsw_sp, u32 ul_tb_id,
@@ -9378,6 +9368,19 @@ static int mlxsw_sp_router_port_pre_changeaddr_event(struct mlxsw_sp_rif *rif,
	return -ENOBUFS;
}

static bool mlxsw_sp_is_offload_xstats_event(unsigned long event)
{
	switch (event) {
	case NETDEV_OFFLOAD_XSTATS_ENABLE:
	case NETDEV_OFFLOAD_XSTATS_DISABLE:
	case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
	case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
		return true;
	}

	return false;
}

static int
mlxsw_sp_router_port_offload_xstats_cmd(struct mlxsw_sp_rif *rif,
					unsigned long event,
@@ -9407,45 +9410,60 @@ mlxsw_sp_router_port_offload_xstats_cmd(struct mlxsw_sp_rif *rif,
	return 0;
}

int mlxsw_sp_netdevice_router_port_event(struct net_device *dev,
static int
mlxsw_sp_netdevice_offload_xstats_cmd(struct mlxsw_sp *mlxsw_sp,
				      struct net_device *dev,
				      unsigned long event,
				      struct netdev_notifier_offload_xstats_info *info)
{
	struct mlxsw_sp_rif *rif;

	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
	if (!rif)
		return 0;

	return mlxsw_sp_router_port_offload_xstats_cmd(rif, event, info);
}

static bool mlxsw_sp_is_router_event(unsigned long event)
{
	switch (event) {
	case NETDEV_PRE_CHANGEADDR:
	case NETDEV_CHANGEADDR:
	case NETDEV_CHANGEMTU:
		return true;
	default:
		return false;
	}
}

static int mlxsw_sp_netdevice_router_port_event(struct net_device *dev,
						unsigned long event, void *ptr)
{
	struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr);
	struct mlxsw_sp *mlxsw_sp;
	struct mlxsw_sp_rif *rif;
	int err = 0;

	mlxsw_sp = mlxsw_sp_lower_get(dev);
	if (!mlxsw_sp)
		return 0;

	mutex_lock(&mlxsw_sp->router->lock);
	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
	if (!rif)
		goto out;
		return 0;

	switch (event) {
	case NETDEV_CHANGEMTU:
	case NETDEV_CHANGEADDR:
		err = mlxsw_sp_router_port_change_event(mlxsw_sp, rif, extack);
		break;
		return mlxsw_sp_router_port_change_event(mlxsw_sp, rif, extack);
	case NETDEV_PRE_CHANGEADDR:
		err = mlxsw_sp_router_port_pre_changeaddr_event(rif, ptr);
		break;
	case NETDEV_OFFLOAD_XSTATS_ENABLE:
	case NETDEV_OFFLOAD_XSTATS_DISABLE:
	case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
	case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
		err = mlxsw_sp_router_port_offload_xstats_cmd(rif, event, ptr);
		break;
		return mlxsw_sp_router_port_pre_changeaddr_event(rif, ptr);
	default:
		WARN_ON_ONCE(1);
		break;
	}

out:
	mutex_unlock(&mlxsw_sp->router->lock);
	return err;
	return 0;
}

static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
@@ -9476,7 +9494,17 @@ static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
	__mlxsw_sp_inetaddr_event(mlxsw_sp, l3_dev, NETDEV_DOWN, NULL);
}

int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
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_vrf_event(struct net_device *l3_dev, unsigned long event,
			     struct netdev_notifier_changeupper_info *info)
{
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
@@ -9488,7 +9516,6 @@ int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
	if (!mlxsw_sp || netif_is_macvlan(l3_dev))
		return 0;

	mutex_lock(&mlxsw_sp->router->lock);
	switch (event) {
	case NETDEV_PRECHANGEUPPER:
		break;
@@ -9503,11 +9530,42 @@ int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
		}
		break;
	}
	mutex_unlock(&mlxsw_sp->router->lock);

	return err;
}

static int mlxsw_sp_router_netdevice_event(struct notifier_block *nb,
					   unsigned long event, void *ptr)
{
	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
	struct mlxsw_sp_router *router;
	struct mlxsw_sp *mlxsw_sp;
	int err = 0;

	router = container_of(nb, struct mlxsw_sp_router, netdevice_nb);
	mlxsw_sp = router->mlxsw_sp;

	mutex_lock(&mlxsw_sp->router->lock);

	if (mlxsw_sp_is_offload_xstats_event(event))
		err = mlxsw_sp_netdevice_offload_xstats_cmd(mlxsw_sp, dev,
							    event, ptr);
	else if (mlxsw_sp_netdev_is_ipip_ol(mlxsw_sp, dev))
		err = mlxsw_sp_netdevice_ipip_ol_event(mlxsw_sp, dev,
						       event, ptr);
	else if (mlxsw_sp_netdev_is_ipip_ul(mlxsw_sp, dev))
		err = mlxsw_sp_netdevice_ipip_ul_event(mlxsw_sp, dev,
						       event, ptr);
	else if (mlxsw_sp_is_router_event(event))
		err = mlxsw_sp_netdevice_router_port_event(dev, event, ptr);
	else if (mlxsw_sp_is_vrf_event(event, ptr))
		err = mlxsw_sp_netdevice_vrf_event(dev, event, ptr);

	mutex_unlock(&mlxsw_sp->router->lock);

	return notifier_from_errno(err);
}

static int __mlxsw_sp_rif_macvlan_flush(struct net_device *dev,
					struct netdev_nested_priv *priv)
{
@@ -10692,8 +10750,18 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp,
	if (err)
		goto err_register_fib_notifier;

	mlxsw_sp->router->netdevice_nb.notifier_call =
		mlxsw_sp_router_netdevice_event;
	err = register_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp),
					      &mlxsw_sp->router->netdevice_nb);
	if (err)
		goto err_register_netdev_notifier;

	return 0;

err_register_netdev_notifier:
	unregister_fib_notifier(mlxsw_sp_net(mlxsw_sp),
				&mlxsw_sp->router->fib_nb);
err_register_fib_notifier:
	unregister_nexthop_notifier(mlxsw_sp_net(mlxsw_sp),
				    &mlxsw_sp->router->nexthop_nb);
@@ -10741,6 +10809,8 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp,

void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
{
	unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp),
					  &mlxsw_sp->router->netdevice_nb);
	unregister_fib_notifier(mlxsw_sp_net(mlxsw_sp),
				&mlxsw_sp->router->fib_nb);
	unregister_nexthop_notifier(mlxsw_sp_net(mlxsw_sp),
+1 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ struct mlxsw_sp_router {
	struct notifier_block netevent_nb;
	struct notifier_block inetaddr_nb;
	struct notifier_block inet6addr_nb;
	struct notifier_block netdevice_nb;
	const struct mlxsw_sp_rif_ops **rif_ops_arr;
	const struct mlxsw_sp_ipip_ops **ipip_ops_arr;
	struct mlxsw_sp_router_nve_decap nve_decap_config;
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ TEST_PROGS = bridge_igmp.sh \
	gre_multipath_nh.sh \
	gre_multipath.sh \
	hw_stats_l3.sh \
	hw_stats_l3_gre.sh \
	ip6_forward_instats_vrf.sh \
	ip6gre_custom_multipath_hash.sh \
	ip6gre_flat_key.sh \
Loading