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

Commit 4caef463 authored by Yuval Mintz's avatar Yuval Mintz Committed by David S. Miller
Browse files

mlxsw: spectrum_mr: Convert into using mr_mfc



Current multicast routing logic in driver assumes it's always meant to
deal with IPv4 multicast routes, leaving several placeholders for
later IPv6 support [currently usually WARN()].

This patch changes the driver's internal multicast route struct into
holding a common mr_mfc instead of the IPv4 mfc_cache.
The various placeholders are grouped into 2:
  - Functions that require only the common bits; These remain and the
    restriction for IPv4-only is lifted.
  - Function that require IPv4-specifics - for handling these functions
    we add sets of operations that encapsulate the protocol differences

Signed-off-by: default avatarYuval Mintz <yuvalm@mellanox.com>
Signed-off-by: default avatarIdo Schimmel <idosch@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9742f866
Loading
Loading
Loading
Loading
+108 −84
Original line number Original line Diff line number Diff line
@@ -47,6 +47,11 @@ struct mlxsw_sp_mr {
	/* priv has to be always the last item */
	/* priv has to be always the last item */
};
};


struct mlxsw_sp_mr_vif;
struct mlxsw_sp_mr_vif_ops {
	bool (*is_regular)(const struct mlxsw_sp_mr_vif *vif);
};

struct mlxsw_sp_mr_vif {
struct mlxsw_sp_mr_vif {
	struct net_device *dev;
	struct net_device *dev;
	const struct mlxsw_sp_rif *rif;
	const struct mlxsw_sp_rif *rif;
@@ -61,6 +66,9 @@ struct mlxsw_sp_mr_vif {
	 * instance is used as an ingress VIF
	 * instance is used as an ingress VIF
	 */
	 */
	struct list_head route_ivif_list;
	struct list_head route_ivif_list;

	/* Protocol specific operations for a VIF */
	const struct mlxsw_sp_mr_vif_ops *ops;
};
};


struct mlxsw_sp_mr_route_vif_entry {
struct mlxsw_sp_mr_route_vif_entry {
@@ -70,6 +78,17 @@ struct mlxsw_sp_mr_route_vif_entry {
	struct mlxsw_sp_mr_route *mr_route;
	struct mlxsw_sp_mr_route *mr_route;
};
};


struct mlxsw_sp_mr_table;
struct mlxsw_sp_mr_table_ops {
	bool (*is_route_valid)(const struct mlxsw_sp_mr_table *mr_table,
			       const struct mr_mfc *mfc);
	void (*key_create)(struct mlxsw_sp_mr_table *mr_table,
			   struct mlxsw_sp_mr_route_key *key,
			   struct mr_mfc *mfc);
	bool (*is_route_starg)(const struct mlxsw_sp_mr_table *mr_table,
			       const struct mlxsw_sp_mr_route *mr_route);
};

struct mlxsw_sp_mr_table {
struct mlxsw_sp_mr_table {
	struct list_head node;
	struct list_head node;
	enum mlxsw_sp_l3proto proto;
	enum mlxsw_sp_l3proto proto;
@@ -78,6 +97,7 @@ struct mlxsw_sp_mr_table {
	struct mlxsw_sp_mr_vif vifs[MAXVIFS];
	struct mlxsw_sp_mr_vif vifs[MAXVIFS];
	struct list_head route_list;
	struct list_head route_list;
	struct rhashtable route_ht;
	struct rhashtable route_ht;
	const struct mlxsw_sp_mr_table_ops *ops;
	char catchall_route_priv[0];
	char catchall_route_priv[0];
	/* catchall_route_priv has to be always the last item */
	/* catchall_route_priv has to be always the last item */
};
};
@@ -88,7 +108,7 @@ struct mlxsw_sp_mr_route {
	struct mlxsw_sp_mr_route_key key;
	struct mlxsw_sp_mr_route_key key;
	enum mlxsw_sp_mr_route_action route_action;
	enum mlxsw_sp_mr_route_action route_action;
	u16 min_mtu;
	u16 min_mtu;
	struct mfc_cache *mfc4;
	struct mr_mfc *mfc;
	void *route_priv;
	void *route_priv;
	const struct mlxsw_sp_mr_table *mr_table;
	const struct mlxsw_sp_mr_table *mr_table;
	/* A list of route_vif_entry structs that point to the egress VIFs */
	/* A list of route_vif_entry structs that point to the egress VIFs */
@@ -104,14 +124,9 @@ static const struct rhashtable_params mlxsw_sp_mr_route_ht_params = {
	.automatic_shrinking = true,
	.automatic_shrinking = true,
};
};


static bool mlxsw_sp_mr_vif_regular(const struct mlxsw_sp_mr_vif *vif)
{
	return !(vif->vif_flags & (VIFF_TUNNEL | VIFF_REGISTER));
}

static bool mlxsw_sp_mr_vif_valid(const struct mlxsw_sp_mr_vif *vif)
static bool mlxsw_sp_mr_vif_valid(const struct mlxsw_sp_mr_vif *vif)
{
{
	return mlxsw_sp_mr_vif_regular(vif) && vif->dev && vif->rif;
	return vif->ops->is_regular(vif) && vif->dev && vif->rif;
}
}


static bool mlxsw_sp_mr_vif_exists(const struct mlxsw_sp_mr_vif *vif)
static bool mlxsw_sp_mr_vif_exists(const struct mlxsw_sp_mr_vif *vif)
@@ -122,18 +137,9 @@ static bool mlxsw_sp_mr_vif_exists(const struct mlxsw_sp_mr_vif *vif)
static bool
static bool
mlxsw_sp_mr_route_ivif_in_evifs(const struct mlxsw_sp_mr_route *mr_route)
mlxsw_sp_mr_route_ivif_in_evifs(const struct mlxsw_sp_mr_route *mr_route)
{
{
	vifi_t ivif;
	vifi_t ivif = mr_route->mfc->mfc_parent;


	switch (mr_route->mr_table->proto) {
	return mr_route->mfc->mfc_un.res.ttls[ivif] != 255;
	case MLXSW_SP_L3_PROTO_IPV4:
		ivif = mr_route->mfc4->_c.mfc_parent;
		return mr_route->mfc4->_c.mfc_un.res.ttls[ivif] != 255;
	case MLXSW_SP_L3_PROTO_IPV6:
		/* fall through */
	default:
		WARN_ON_ONCE(1);
	}
	return false;
}
}


static int
static int
@@ -149,19 +155,6 @@ mlxsw_sp_mr_route_valid_evifs_num(const struct mlxsw_sp_mr_route *mr_route)
	return valid_evifs;
	return valid_evifs;
}
}


static bool mlxsw_sp_mr_route_starg(const struct mlxsw_sp_mr_route *mr_route)
{
	switch (mr_route->mr_table->proto) {
	case MLXSW_SP_L3_PROTO_IPV4:
		return mr_route->key.source_mask.addr4 == htonl(INADDR_ANY);
	case MLXSW_SP_L3_PROTO_IPV6:
		/* fall through */
	default:
		WARN_ON_ONCE(1);
	}
	return false;
}

static enum mlxsw_sp_mr_route_action
static enum mlxsw_sp_mr_route_action
mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)
mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)
{
{
@@ -174,7 +167,8 @@ mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)
	/* The kernel does not match a (*,G) route that the ingress interface is
	/* The kernel does not match a (*,G) route that the ingress interface is
	 * not one of the egress interfaces, so trap these kind of routes.
	 * not one of the egress interfaces, so trap these kind of routes.
	 */
	 */
	if (mlxsw_sp_mr_route_starg(mr_route) &&
	if (mr_route->mr_table->ops->is_route_starg(mr_route->mr_table,
						    mr_route) &&
	    !mlxsw_sp_mr_route_ivif_in_evifs(mr_route))
	    !mlxsw_sp_mr_route_ivif_in_evifs(mr_route))
		return MLXSW_SP_MR_ROUTE_ACTION_TRAP;
		return MLXSW_SP_MR_ROUTE_ACTION_TRAP;


@@ -195,25 +189,11 @@ mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)
static enum mlxsw_sp_mr_route_prio
static enum mlxsw_sp_mr_route_prio
mlxsw_sp_mr_route_prio(const struct mlxsw_sp_mr_route *mr_route)
mlxsw_sp_mr_route_prio(const struct mlxsw_sp_mr_route *mr_route)
{
{
	return mlxsw_sp_mr_route_starg(mr_route) ?
	return mr_route->mr_table->ops->is_route_starg(mr_route->mr_table,
						       mr_route) ?
		MLXSW_SP_MR_ROUTE_PRIO_STARG : MLXSW_SP_MR_ROUTE_PRIO_SG;
		MLXSW_SP_MR_ROUTE_PRIO_STARG : MLXSW_SP_MR_ROUTE_PRIO_SG;
}
}


static void mlxsw_sp_mr_route4_key(struct mlxsw_sp_mr_table *mr_table,
				   struct mlxsw_sp_mr_route_key *key,
				   const struct mfc_cache *mfc)
{
	bool starg = (mfc->mfc_origin == htonl(INADDR_ANY));

	memset(key, 0, sizeof(*key));
	key->vrid = mr_table->vr_id;
	key->proto = mr_table->proto;
	key->group.addr4 = mfc->mfc_mcastgrp;
	key->group_mask.addr4 = htonl(0xffffffff);
	key->source.addr4 = mfc->mfc_origin;
	key->source_mask.addr4 = htonl(starg ? 0 : 0xffffffff);
}

static int mlxsw_sp_mr_route_evif_link(struct mlxsw_sp_mr_route *mr_route,
static int mlxsw_sp_mr_route_evif_link(struct mlxsw_sp_mr_route *mr_route,
				       struct mlxsw_sp_mr_vif *mr_vif)
				       struct mlxsw_sp_mr_vif *mr_vif)
{
{
@@ -356,12 +336,13 @@ mlxsw_sp_mr_route4_create(struct mlxsw_sp_mr_table *mr_table,
	if (!mr_route)
	if (!mr_route)
		return ERR_PTR(-ENOMEM);
		return ERR_PTR(-ENOMEM);
	INIT_LIST_HEAD(&mr_route->evif_list);
	INIT_LIST_HEAD(&mr_route->evif_list);
	mlxsw_sp_mr_route4_key(mr_table, &mr_route->key, mfc);


	/* Find min_mtu and link iVIF and eVIFs */
	/* Find min_mtu and link iVIF and eVIFs */
	mr_route->min_mtu = ETH_MAX_MTU;
	mr_route->min_mtu = ETH_MAX_MTU;
	mr_cache_hold(&mfc->_c);
	mr_cache_hold(&mfc->_c);
	mr_route->mfc4 = mfc;
	mr_route->mfc = &mfc->_c;
	mr_table->ops->key_create(mr_table, &mr_route->key, mr_route->mfc);

	mr_route->mr_table = mr_table;
	mr_route->mr_table = mr_table;
	for (i = 0; i < MAXVIFS; i++) {
	for (i = 0; i < MAXVIFS; i++) {
		if (mfc->_c.mfc_un.res.ttls[i] != 255) {
		if (mfc->_c.mfc_un.res.ttls[i] != 255) {
@@ -393,7 +374,7 @@ static void mlxsw_sp_mr_route4_destroy(struct mlxsw_sp_mr_table *mr_table,
	struct mlxsw_sp_mr_route_vif_entry *rve, *tmp;
	struct mlxsw_sp_mr_route_vif_entry *rve, *tmp;


	mlxsw_sp_mr_route_ivif_unlink(mr_route);
	mlxsw_sp_mr_route_ivif_unlink(mr_route);
	mr_cache_put((struct mr_mfc *)mr_route->mfc4);
	mr_cache_put((struct mr_mfc *)mr_route->mfc);
	list_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)
	list_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)
		mlxsw_sp_mr_route_evif_unlink(rve);
		mlxsw_sp_mr_route_evif_unlink(rve);
	kfree(mr_route);
	kfree(mr_route);
@@ -416,18 +397,10 @@ static void mlxsw_sp_mr_route_destroy(struct mlxsw_sp_mr_table *mr_table,
static void mlxsw_sp_mr_mfc_offload_set(struct mlxsw_sp_mr_route *mr_route,
static void mlxsw_sp_mr_mfc_offload_set(struct mlxsw_sp_mr_route *mr_route,
					bool offload)
					bool offload)
{
{
	switch (mr_route->mr_table->proto) {
	case MLXSW_SP_L3_PROTO_IPV4:
	if (offload)
	if (offload)
			mr_route->mfc4->_c.mfc_flags |= MFC_OFFLOAD;
		mr_route->mfc->mfc_flags |= MFC_OFFLOAD;
	else
	else
			mr_route->mfc4->_c.mfc_flags &= ~MFC_OFFLOAD;
		mr_route->mfc->mfc_flags &= ~MFC_OFFLOAD;
		break;
	case MLXSW_SP_L3_PROTO_IPV6:
		/* fall through */
	default:
		WARN_ON_ONCE(1);
	}
}
}


static void mlxsw_sp_mr_mfc_offload_update(struct mlxsw_sp_mr_route *mr_route)
static void mlxsw_sp_mr_mfc_offload_update(struct mlxsw_sp_mr_route *mr_route)
@@ -456,15 +429,8 @@ int mlxsw_sp_mr_route4_add(struct mlxsw_sp_mr_table *mr_table,
	struct mlxsw_sp_mr_route *mr_route;
	struct mlxsw_sp_mr_route *mr_route;
	int err;
	int err;


	/* If the route is a (*,*) route, abort, as these kind of routes are
	if (!mr_table->ops->is_route_valid(mr_table, &mfc->_c))
	 * used for proxy routes.
	 */
	if (mfc->mfc_origin == htonl(INADDR_ANY) &&
	    mfc->mfc_mcastgrp == htonl(INADDR_ANY)) {
		dev_warn(mr_table->mlxsw_sp->bus_info->dev,
			 "Offloading proxy routes is not supported.\n");
		return -EINVAL;
		return -EINVAL;
	}


	/* Create a new route */
	/* Create a new route */
	mr_route = mlxsw_sp_mr_route4_create(mr_table, mfc);
	mr_route = mlxsw_sp_mr_route4_create(mr_table, mfc);
@@ -535,7 +501,7 @@ void mlxsw_sp_mr_route4_del(struct mlxsw_sp_mr_table *mr_table,
	struct mlxsw_sp_mr_route *mr_route;
	struct mlxsw_sp_mr_route *mr_route;
	struct mlxsw_sp_mr_route_key key;
	struct mlxsw_sp_mr_route_key key;


	mlxsw_sp_mr_route4_key(mr_table, &key, mfc);
	mr_table->ops->key_create(mr_table, &key, &mfc->_c);
	mr_route = rhashtable_lookup_fast(&mr_table->route_ht, &key,
	mr_route = rhashtable_lookup_fast(&mr_table->route_ht, &key,
					  mlxsw_sp_mr_route_ht_params);
					  mlxsw_sp_mr_route_ht_params);
	if (mr_route)
	if (mr_route)
@@ -840,6 +806,70 @@ void mlxsw_sp_mr_rif_mtu_update(struct mlxsw_sp_mr_table *mr_table,
	}
	}
}
}


/* Protocol specific functions */
static bool
mlxsw_sp_mr_route4_validate(const struct mlxsw_sp_mr_table *mr_table,
			    const struct mr_mfc *c)
{
	struct mfc_cache *mfc = (struct mfc_cache *) c;

	/* If the route is a (*,*) route, abort, as these kind of routes are
	 * used for proxy routes.
	 */
	if (mfc->mfc_origin == htonl(INADDR_ANY) &&
	    mfc->mfc_mcastgrp == htonl(INADDR_ANY)) {
		dev_warn(mr_table->mlxsw_sp->bus_info->dev,
			 "Offloading proxy routes is not supported.\n");
		return false;
	}
	return true;
}

static void mlxsw_sp_mr_route4_key(struct mlxsw_sp_mr_table *mr_table,
				   struct mlxsw_sp_mr_route_key *key,
				   struct mr_mfc *c)
{
	const struct mfc_cache *mfc = (struct mfc_cache *) c;
	bool starg;

	starg = (mfc->mfc_origin == htonl(INADDR_ANY));

	memset(key, 0, sizeof(*key));
	key->vrid = mr_table->vr_id;
	key->proto = MLXSW_SP_L3_PROTO_IPV4;
	key->group.addr4 = mfc->mfc_mcastgrp;
	key->group_mask.addr4 = htonl(0xffffffff);
	key->source.addr4 = mfc->mfc_origin;
	key->source_mask.addr4 = htonl(starg ? 0 : 0xffffffff);
}

static bool mlxsw_sp_mr_route4_starg(const struct mlxsw_sp_mr_table *mr_table,
				     const struct mlxsw_sp_mr_route *mr_route)
{
	return mr_route->key.source_mask.addr4 == htonl(INADDR_ANY);
}

static bool mlxsw_sp_mr_vif4_is_regular(const struct mlxsw_sp_mr_vif *vif)
{
	return !(vif->vif_flags & (VIFF_TUNNEL | VIFF_REGISTER));
}

static struct
mlxsw_sp_mr_vif_ops mlxsw_sp_mr_vif_ops_arr[] = {
	{
		.is_regular = mlxsw_sp_mr_vif4_is_regular,
	},
};

static struct
mlxsw_sp_mr_table_ops mlxsw_sp_mr_table_ops_arr[] = {
	{
		.is_route_valid = mlxsw_sp_mr_route4_validate,
		.key_create = mlxsw_sp_mr_route4_key,
		.is_route_starg = mlxsw_sp_mr_route4_starg,
	},
};

struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,
						   u32 vr_id,
						   u32 vr_id,
						   enum mlxsw_sp_l3proto proto)
						   enum mlxsw_sp_l3proto proto)
@@ -867,6 +897,7 @@ struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,
	mr_table->vr_id = vr_id;
	mr_table->vr_id = vr_id;
	mr_table->mlxsw_sp = mlxsw_sp;
	mr_table->mlxsw_sp = mlxsw_sp;
	mr_table->proto = proto;
	mr_table->proto = proto;
	mr_table->ops = &mlxsw_sp_mr_table_ops_arr[proto];
	INIT_LIST_HEAD(&mr_table->route_list);
	INIT_LIST_HEAD(&mr_table->route_list);


	err = rhashtable_init(&mr_table->route_ht,
	err = rhashtable_init(&mr_table->route_ht,
@@ -877,6 +908,7 @@ struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,
	for (i = 0; i < MAXVIFS; i++) {
	for (i = 0; i < MAXVIFS; i++) {
		INIT_LIST_HEAD(&mr_table->vifs[i].route_evif_list);
		INIT_LIST_HEAD(&mr_table->vifs[i].route_evif_list);
		INIT_LIST_HEAD(&mr_table->vifs[i].route_ivif_list);
		INIT_LIST_HEAD(&mr_table->vifs[i].route_ivif_list);
		mr_table->vifs[i].ops = &mlxsw_sp_mr_vif_ops_arr[proto];
	}
	}


	err = mr->mr_ops->route_create(mlxsw_sp, mr->priv,
	err = mr->mr_ops->route_create(mlxsw_sp, mr->priv,
@@ -943,18 +975,10 @@ static void mlxsw_sp_mr_route_stats_update(struct mlxsw_sp *mlxsw_sp,
	mr->mr_ops->route_stats(mlxsw_sp, mr_route->route_priv, &packets,
	mr->mr_ops->route_stats(mlxsw_sp, mr_route->route_priv, &packets,
				&bytes);
				&bytes);


	switch (mr_route->mr_table->proto) {
	if (mr_route->mfc->mfc_un.res.pkt != packets)
	case MLXSW_SP_L3_PROTO_IPV4:
		mr_route->mfc->mfc_un.res.lastuse = jiffies;
		if (mr_route->mfc4->_c.mfc_un.res.pkt != packets)
	mr_route->mfc->mfc_un.res.pkt = packets;
			mr_route->mfc4->_c.mfc_un.res.lastuse = jiffies;
	mr_route->mfc->mfc_un.res.bytes = bytes;
		mr_route->mfc4->_c.mfc_un.res.pkt = packets;
		mr_route->mfc4->_c.mfc_un.res.bytes = bytes;
		break;
	case MLXSW_SP_L3_PROTO_IPV6:
		/* fall through */
	default:
		WARN_ON_ONCE(1);
	}
}
}


static void mlxsw_sp_mr_stats_update(struct work_struct *work)
static void mlxsw_sp_mr_stats_update(struct work_struct *work)