brintos

brintos / linux-shallow public Read only

0
0
Text · 30.1 KiB · 69cd689 Raw
1073 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */3 4#include <linux/mutex.h>5#include <linux/rhashtable.h>6#include <net/ipv6.h>7 8#include "spectrum_mr.h"9#include "spectrum_router.h"10 11struct mlxsw_sp_mr {12	const struct mlxsw_sp_mr_ops *mr_ops;13	void *catchall_route_priv;14	struct delayed_work stats_update_dw;15	struct list_head table_list;16	struct mutex table_list_lock; /* Protects table_list */17#define MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL 5000 /* ms */18	unsigned long priv[];19	/* priv has to be always the last item */20};21 22struct mlxsw_sp_mr_vif;23struct mlxsw_sp_mr_vif_ops {24	bool (*is_regular)(const struct mlxsw_sp_mr_vif *vif);25};26 27struct mlxsw_sp_mr_vif {28	struct net_device *dev;29	const struct mlxsw_sp_rif *rif;30	unsigned long vif_flags;31 32	/* A list of route_vif_entry structs that point to routes that the VIF33	 * instance is used as one of the egress VIFs34	 */35	struct list_head route_evif_list;36 37	/* A list of route_vif_entry structs that point to routes that the VIF38	 * instance is used as an ingress VIF39	 */40	struct list_head route_ivif_list;41 42	/* Protocol specific operations for a VIF */43	const struct mlxsw_sp_mr_vif_ops *ops;44};45 46struct mlxsw_sp_mr_route_vif_entry {47	struct list_head vif_node;48	struct list_head route_node;49	struct mlxsw_sp_mr_vif *mr_vif;50	struct mlxsw_sp_mr_route *mr_route;51};52 53struct mlxsw_sp_mr_table;54struct mlxsw_sp_mr_table_ops {55	bool (*is_route_valid)(const struct mlxsw_sp_mr_table *mr_table,56			       const struct mr_mfc *mfc);57	void (*key_create)(struct mlxsw_sp_mr_table *mr_table,58			   struct mlxsw_sp_mr_route_key *key,59			   struct mr_mfc *mfc);60	bool (*is_route_starg)(const struct mlxsw_sp_mr_table *mr_table,61			       const struct mlxsw_sp_mr_route *mr_route);62};63 64struct mlxsw_sp_mr_table {65	struct list_head node;66	enum mlxsw_sp_l3proto proto;67	struct mlxsw_sp *mlxsw_sp;68	u32 vr_id;69	struct mlxsw_sp_mr_vif vifs[MAXVIFS];70	struct list_head route_list;71	struct mutex route_list_lock; /* Protects route_list */72	struct rhashtable route_ht;73	const struct mlxsw_sp_mr_table_ops *ops;74	char catchall_route_priv[];75	/* catchall_route_priv has to be always the last item */76};77 78struct mlxsw_sp_mr_route {79	struct list_head node;80	struct rhash_head ht_node;81	struct mlxsw_sp_mr_route_key key;82	enum mlxsw_sp_mr_route_action route_action;83	u16 min_mtu;84	struct mr_mfc *mfc;85	void *route_priv;86	const struct mlxsw_sp_mr_table *mr_table;87	/* A list of route_vif_entry structs that point to the egress VIFs */88	struct list_head evif_list;89	/* A route_vif_entry struct that point to the ingress VIF */90	struct mlxsw_sp_mr_route_vif_entry ivif;91};92 93static const struct rhashtable_params mlxsw_sp_mr_route_ht_params = {94	.key_len = sizeof(struct mlxsw_sp_mr_route_key),95	.key_offset = offsetof(struct mlxsw_sp_mr_route, key),96	.head_offset = offsetof(struct mlxsw_sp_mr_route, ht_node),97	.automatic_shrinking = true,98};99 100static bool mlxsw_sp_mr_vif_valid(const struct mlxsw_sp_mr_vif *vif)101{102	return vif->ops->is_regular(vif) && vif->dev && vif->rif;103}104 105static bool mlxsw_sp_mr_vif_exists(const struct mlxsw_sp_mr_vif *vif)106{107	return vif->dev;108}109 110static bool111mlxsw_sp_mr_route_ivif_in_evifs(const struct mlxsw_sp_mr_route *mr_route)112{113	vifi_t ivif = mr_route->mfc->mfc_parent;114 115	return mr_route->mfc->mfc_un.res.ttls[ivif] != 255;116}117 118static int119mlxsw_sp_mr_route_valid_evifs_num(const struct mlxsw_sp_mr_route *mr_route)120{121	struct mlxsw_sp_mr_route_vif_entry *rve;122	int valid_evifs;123 124	valid_evifs = 0;125	list_for_each_entry(rve, &mr_route->evif_list, route_node)126		if (mlxsw_sp_mr_vif_valid(rve->mr_vif))127			valid_evifs++;128	return valid_evifs;129}130 131static enum mlxsw_sp_mr_route_action132mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)133{134	struct mlxsw_sp_mr_route_vif_entry *rve;135 136	/* If the ingress port is not regular and resolved, trap the route */137	if (!mlxsw_sp_mr_vif_valid(mr_route->ivif.mr_vif))138		return MLXSW_SP_MR_ROUTE_ACTION_TRAP;139 140	/* The kernel does not match a (*,G) route that the ingress interface is141	 * not one of the egress interfaces, so trap these kind of routes.142	 */143	if (mr_route->mr_table->ops->is_route_starg(mr_route->mr_table,144						    mr_route) &&145	    !mlxsw_sp_mr_route_ivif_in_evifs(mr_route))146		return MLXSW_SP_MR_ROUTE_ACTION_TRAP;147 148	/* If the route has no valid eVIFs, trap it. */149	if (!mlxsw_sp_mr_route_valid_evifs_num(mr_route))150		return MLXSW_SP_MR_ROUTE_ACTION_TRAP;151 152	/* If one of the eVIFs has no RIF, trap-and-forward the route as there153	 * is some more routing to do in software too.154	 */155	list_for_each_entry(rve, &mr_route->evif_list, route_node)156		if (mlxsw_sp_mr_vif_exists(rve->mr_vif) && !rve->mr_vif->rif)157			return MLXSW_SP_MR_ROUTE_ACTION_TRAP_AND_FORWARD;158 159	return MLXSW_SP_MR_ROUTE_ACTION_FORWARD;160}161 162static enum mlxsw_sp_mr_route_prio163mlxsw_sp_mr_route_prio(const struct mlxsw_sp_mr_route *mr_route)164{165	return mr_route->mr_table->ops->is_route_starg(mr_route->mr_table,166						       mr_route) ?167		MLXSW_SP_MR_ROUTE_PRIO_STARG : MLXSW_SP_MR_ROUTE_PRIO_SG;168}169 170static int mlxsw_sp_mr_route_evif_link(struct mlxsw_sp_mr_route *mr_route,171				       struct mlxsw_sp_mr_vif *mr_vif)172{173	struct mlxsw_sp_mr_route_vif_entry *rve;174 175	rve = kzalloc(sizeof(*rve), GFP_KERNEL);176	if (!rve)177		return -ENOMEM;178	rve->mr_route = mr_route;179	rve->mr_vif = mr_vif;180	list_add_tail(&rve->route_node, &mr_route->evif_list);181	list_add_tail(&rve->vif_node, &mr_vif->route_evif_list);182	return 0;183}184 185static void186mlxsw_sp_mr_route_evif_unlink(struct mlxsw_sp_mr_route_vif_entry *rve)187{188	list_del(&rve->route_node);189	list_del(&rve->vif_node);190	kfree(rve);191}192 193static void mlxsw_sp_mr_route_ivif_link(struct mlxsw_sp_mr_route *mr_route,194					struct mlxsw_sp_mr_vif *mr_vif)195{196	mr_route->ivif.mr_route = mr_route;197	mr_route->ivif.mr_vif = mr_vif;198	list_add_tail(&mr_route->ivif.vif_node, &mr_vif->route_ivif_list);199}200 201static void mlxsw_sp_mr_route_ivif_unlink(struct mlxsw_sp_mr_route *mr_route)202{203	list_del(&mr_route->ivif.vif_node);204}205 206static int207mlxsw_sp_mr_route_info_create(struct mlxsw_sp_mr_table *mr_table,208			      struct mlxsw_sp_mr_route *mr_route,209			      struct mlxsw_sp_mr_route_info *route_info)210{211	struct mlxsw_sp_mr_route_vif_entry *rve;212	u16 *erif_indices;213	u16 irif_index;214	u16 erif = 0;215 216	erif_indices = kmalloc_array(MAXVIFS, sizeof(*erif_indices),217				     GFP_KERNEL);218	if (!erif_indices)219		return -ENOMEM;220 221	list_for_each_entry(rve, &mr_route->evif_list, route_node) {222		if (mlxsw_sp_mr_vif_valid(rve->mr_vif)) {223			u16 rifi = mlxsw_sp_rif_index(rve->mr_vif->rif);224 225			erif_indices[erif++] = rifi;226		}227	}228 229	if (mlxsw_sp_mr_vif_valid(mr_route->ivif.mr_vif))230		irif_index = mlxsw_sp_rif_index(mr_route->ivif.mr_vif->rif);231	else232		irif_index = 0;233 234	route_info->irif_index = irif_index;235	route_info->erif_indices = erif_indices;236	route_info->min_mtu = mr_route->min_mtu;237	route_info->route_action = mr_route->route_action;238	route_info->erif_num = erif;239	return 0;240}241 242static void243mlxsw_sp_mr_route_info_destroy(struct mlxsw_sp_mr_route_info *route_info)244{245	kfree(route_info->erif_indices);246}247 248static int mlxsw_sp_mr_route_write(struct mlxsw_sp_mr_table *mr_table,249				   struct mlxsw_sp_mr_route *mr_route,250				   bool replace)251{252	struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;253	struct mlxsw_sp_mr_route_info route_info;254	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;255	int err;256 257	err = mlxsw_sp_mr_route_info_create(mr_table, mr_route, &route_info);258	if (err)259		return err;260 261	if (!replace) {262		struct mlxsw_sp_mr_route_params route_params;263 264		mr_route->route_priv = kzalloc(mr->mr_ops->route_priv_size,265					       GFP_KERNEL);266		if (!mr_route->route_priv) {267			err = -ENOMEM;268			goto out;269		}270 271		route_params.key = mr_route->key;272		route_params.value = route_info;273		route_params.prio = mlxsw_sp_mr_route_prio(mr_route);274		err = mr->mr_ops->route_create(mlxsw_sp, mr->priv,275					       mr_route->route_priv,276					       &route_params);277		if (err)278			kfree(mr_route->route_priv);279	} else {280		err = mr->mr_ops->route_update(mlxsw_sp, mr_route->route_priv,281					       &route_info);282	}283out:284	mlxsw_sp_mr_route_info_destroy(&route_info);285	return err;286}287 288static void mlxsw_sp_mr_route_erase(struct mlxsw_sp_mr_table *mr_table,289				    struct mlxsw_sp_mr_route *mr_route)290{291	struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;292	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;293 294	mr->mr_ops->route_destroy(mlxsw_sp, mr->priv, mr_route->route_priv);295	kfree(mr_route->route_priv);296}297 298static struct mlxsw_sp_mr_route *299mlxsw_sp_mr_route_create(struct mlxsw_sp_mr_table *mr_table,300			 struct mr_mfc *mfc)301{302	struct mlxsw_sp_mr_route_vif_entry *rve, *tmp;303	struct mlxsw_sp_mr_route *mr_route;304	int err = 0;305	int i;306 307	/* Allocate and init a new route and fill it with parameters */308	mr_route = kzalloc(sizeof(*mr_route), GFP_KERNEL);309	if (!mr_route)310		return ERR_PTR(-ENOMEM);311	INIT_LIST_HEAD(&mr_route->evif_list);312 313	/* Find min_mtu and link iVIF and eVIFs */314	mr_route->min_mtu = ETH_MAX_MTU;315	mr_cache_hold(mfc);316	mr_route->mfc = mfc;317	mr_table->ops->key_create(mr_table, &mr_route->key, mr_route->mfc);318 319	mr_route->mr_table = mr_table;320	for (i = 0; i < MAXVIFS; i++) {321		if (mfc->mfc_un.res.ttls[i] != 255) {322			err = mlxsw_sp_mr_route_evif_link(mr_route,323							  &mr_table->vifs[i]);324			if (err)325				goto err;326			if (mr_table->vifs[i].dev &&327			    mr_table->vifs[i].dev->mtu < mr_route->min_mtu)328				mr_route->min_mtu = mr_table->vifs[i].dev->mtu;329		}330	}331	mlxsw_sp_mr_route_ivif_link(mr_route,332				    &mr_table->vifs[mfc->mfc_parent]);333 334	mr_route->route_action = mlxsw_sp_mr_route_action(mr_route);335	return mr_route;336err:337	mr_cache_put(mfc);338	list_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)339		mlxsw_sp_mr_route_evif_unlink(rve);340	kfree(mr_route);341	return ERR_PTR(err);342}343 344static void mlxsw_sp_mr_route_destroy(struct mlxsw_sp_mr_table *mr_table,345				      struct mlxsw_sp_mr_route *mr_route)346{347	struct mlxsw_sp_mr_route_vif_entry *rve, *tmp;348 349	mlxsw_sp_mr_route_ivif_unlink(mr_route);350	mr_cache_put(mr_route->mfc);351	list_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)352		mlxsw_sp_mr_route_evif_unlink(rve);353	kfree(mr_route);354}355 356static void mlxsw_sp_mr_mfc_offload_set(struct mlxsw_sp_mr_route *mr_route,357					bool offload)358{359	if (offload)360		mr_route->mfc->mfc_flags |= MFC_OFFLOAD;361	else362		mr_route->mfc->mfc_flags &= ~MFC_OFFLOAD;363}364 365static void mlxsw_sp_mr_mfc_offload_update(struct mlxsw_sp_mr_route *mr_route)366{367	bool offload;368 369	offload = mr_route->route_action != MLXSW_SP_MR_ROUTE_ACTION_TRAP;370	mlxsw_sp_mr_mfc_offload_set(mr_route, offload);371}372 373static void __mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,374				    struct mlxsw_sp_mr_route *mr_route)375{376	WARN_ON_ONCE(!mutex_is_locked(&mr_table->route_list_lock));377 378	mlxsw_sp_mr_mfc_offload_set(mr_route, false);379	rhashtable_remove_fast(&mr_table->route_ht, &mr_route->ht_node,380			       mlxsw_sp_mr_route_ht_params);381	list_del(&mr_route->node);382	mlxsw_sp_mr_route_erase(mr_table, mr_route);383	mlxsw_sp_mr_route_destroy(mr_table, mr_route);384}385 386int mlxsw_sp_mr_route_add(struct mlxsw_sp_mr_table *mr_table,387			  struct mr_mfc *mfc, bool replace)388{389	struct mlxsw_sp_mr_route *mr_orig_route = NULL;390	struct mlxsw_sp_mr_route *mr_route;391	int err;392 393	if (!mr_table->ops->is_route_valid(mr_table, mfc))394		return -EINVAL;395 396	/* Create a new route */397	mr_route = mlxsw_sp_mr_route_create(mr_table, mfc);398	if (IS_ERR(mr_route))399		return PTR_ERR(mr_route);400 401	/* Find any route with a matching key */402	mr_orig_route = rhashtable_lookup_fast(&mr_table->route_ht,403					       &mr_route->key,404					       mlxsw_sp_mr_route_ht_params);405	if (replace) {406		/* On replace case, make the route point to the new route_priv.407		 */408		if (WARN_ON(!mr_orig_route)) {409			err = -ENOENT;410			goto err_no_orig_route;411		}412		mr_route->route_priv = mr_orig_route->route_priv;413	} else if (mr_orig_route) {414		/* On non replace case, if another route with the same key was415		 * found, abort, as duplicate routes are used for proxy routes.416		 */417		dev_warn(mr_table->mlxsw_sp->bus_info->dev,418			 "Offloading proxy routes is not supported.\n");419		err = -EINVAL;420		goto err_duplicate_route;421	}422 423	/* Write the route to the hardware */424	err = mlxsw_sp_mr_route_write(mr_table, mr_route, replace);425	if (err)426		goto err_mr_route_write;427 428	/* Put it in the table data-structures */429	mutex_lock(&mr_table->route_list_lock);430	list_add_tail(&mr_route->node, &mr_table->route_list);431	mutex_unlock(&mr_table->route_list_lock);432	err = rhashtable_insert_fast(&mr_table->route_ht,433				     &mr_route->ht_node,434				     mlxsw_sp_mr_route_ht_params);435	if (err)436		goto err_rhashtable_insert;437 438	/* Destroy the original route */439	if (replace) {440		rhashtable_remove_fast(&mr_table->route_ht,441				       &mr_orig_route->ht_node,442				       mlxsw_sp_mr_route_ht_params);443		list_del(&mr_orig_route->node);444		mlxsw_sp_mr_route_destroy(mr_table, mr_orig_route);445	}446 447	mlxsw_sp_mr_mfc_offload_update(mr_route);448	return 0;449 450err_rhashtable_insert:451	mutex_lock(&mr_table->route_list_lock);452	list_del(&mr_route->node);453	mutex_unlock(&mr_table->route_list_lock);454	mlxsw_sp_mr_route_erase(mr_table, mr_route);455err_mr_route_write:456err_no_orig_route:457err_duplicate_route:458	mlxsw_sp_mr_route_destroy(mr_table, mr_route);459	return err;460}461 462void mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,463			   struct mr_mfc *mfc)464{465	struct mlxsw_sp_mr_route *mr_route;466	struct mlxsw_sp_mr_route_key key;467 468	mr_table->ops->key_create(mr_table, &key, mfc);469	mr_route = rhashtable_lookup_fast(&mr_table->route_ht, &key,470					  mlxsw_sp_mr_route_ht_params);471	if (mr_route) {472		mutex_lock(&mr_table->route_list_lock);473		__mlxsw_sp_mr_route_del(mr_table, mr_route);474		mutex_unlock(&mr_table->route_list_lock);475	}476}477 478/* Should be called after the VIF struct is updated */479static int480mlxsw_sp_mr_route_ivif_resolve(struct mlxsw_sp_mr_table *mr_table,481			       struct mlxsw_sp_mr_route_vif_entry *rve)482{483	struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;484	enum mlxsw_sp_mr_route_action route_action;485	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;486	u16 irif_index;487	int err;488 489	route_action = mlxsw_sp_mr_route_action(rve->mr_route);490	if (route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP)491		return 0;492 493	/* rve->mr_vif->rif is guaranteed to be valid at this stage */494	irif_index = mlxsw_sp_rif_index(rve->mr_vif->rif);495	err = mr->mr_ops->route_irif_update(mlxsw_sp, rve->mr_route->route_priv,496					    irif_index);497	if (err)498		return err;499 500	err = mr->mr_ops->route_action_update(mlxsw_sp,501					      rve->mr_route->route_priv,502					      route_action);503	if (err)504		/* No need to rollback here because the iRIF change only takes505		 * place after the action has been updated.506		 */507		return err;508 509	rve->mr_route->route_action = route_action;510	mlxsw_sp_mr_mfc_offload_update(rve->mr_route);511	return 0;512}513 514static void515mlxsw_sp_mr_route_ivif_unresolve(struct mlxsw_sp_mr_table *mr_table,516				 struct mlxsw_sp_mr_route_vif_entry *rve)517{518	struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;519	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;520 521	mr->mr_ops->route_action_update(mlxsw_sp, rve->mr_route->route_priv,522					MLXSW_SP_MR_ROUTE_ACTION_TRAP);523	rve->mr_route->route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP;524	mlxsw_sp_mr_mfc_offload_update(rve->mr_route);525}526 527/* Should be called after the RIF struct is updated */528static int529mlxsw_sp_mr_route_evif_resolve(struct mlxsw_sp_mr_table *mr_table,530			       struct mlxsw_sp_mr_route_vif_entry *rve)531{532	struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;533	enum mlxsw_sp_mr_route_action route_action;534	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;535	u16 erif_index = 0;536	int err;537 538	/* Add the eRIF */539	if (mlxsw_sp_mr_vif_valid(rve->mr_vif)) {540		erif_index = mlxsw_sp_rif_index(rve->mr_vif->rif);541		err = mr->mr_ops->route_erif_add(mlxsw_sp,542						 rve->mr_route->route_priv,543						 erif_index);544		if (err)545			return err;546	}547 548	/* Update the route action, as the new eVIF can be a tunnel or a pimreg549	 * device which will require updating the action.550	 */551	route_action = mlxsw_sp_mr_route_action(rve->mr_route);552	if (route_action != rve->mr_route->route_action) {553		err = mr->mr_ops->route_action_update(mlxsw_sp,554						      rve->mr_route->route_priv,555						      route_action);556		if (err)557			goto err_route_action_update;558	}559 560	/* Update the minimum MTU */561	if (rve->mr_vif->dev->mtu < rve->mr_route->min_mtu) {562		rve->mr_route->min_mtu = rve->mr_vif->dev->mtu;563		err = mr->mr_ops->route_min_mtu_update(mlxsw_sp,564						       rve->mr_route->route_priv,565						       rve->mr_route->min_mtu);566		if (err)567			goto err_route_min_mtu_update;568	}569 570	rve->mr_route->route_action = route_action;571	mlxsw_sp_mr_mfc_offload_update(rve->mr_route);572	return 0;573 574err_route_min_mtu_update:575	if (route_action != rve->mr_route->route_action)576		mr->mr_ops->route_action_update(mlxsw_sp,577						rve->mr_route->route_priv,578						rve->mr_route->route_action);579err_route_action_update:580	if (mlxsw_sp_mr_vif_valid(rve->mr_vif))581		mr->mr_ops->route_erif_del(mlxsw_sp, rve->mr_route->route_priv,582					   erif_index);583	return err;584}585 586/* Should be called before the RIF struct is updated */587static void588mlxsw_sp_mr_route_evif_unresolve(struct mlxsw_sp_mr_table *mr_table,589				 struct mlxsw_sp_mr_route_vif_entry *rve)590{591	struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;592	enum mlxsw_sp_mr_route_action route_action;593	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;594	u16 rifi;595 596	/* If the unresolved RIF was not valid, no need to delete it */597	if (!mlxsw_sp_mr_vif_valid(rve->mr_vif))598		return;599 600	/* Update the route action: if there is only one valid eVIF in the601	 * route, set the action to trap as the VIF deletion will lead to zero602	 * valid eVIFs. On any other case, use the mlxsw_sp_mr_route_action to603	 * determine the route action.604	 */605	if (mlxsw_sp_mr_route_valid_evifs_num(rve->mr_route) == 1)606		route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP;607	else608		route_action = mlxsw_sp_mr_route_action(rve->mr_route);609	if (route_action != rve->mr_route->route_action)610		mr->mr_ops->route_action_update(mlxsw_sp,611						rve->mr_route->route_priv,612						route_action);613 614	/* Delete the erif from the route */615	rifi = mlxsw_sp_rif_index(rve->mr_vif->rif);616	mr->mr_ops->route_erif_del(mlxsw_sp, rve->mr_route->route_priv, rifi);617	rve->mr_route->route_action = route_action;618	mlxsw_sp_mr_mfc_offload_update(rve->mr_route);619}620 621static int mlxsw_sp_mr_vif_resolve(struct mlxsw_sp_mr_table *mr_table,622				   struct net_device *dev,623				   struct mlxsw_sp_mr_vif *mr_vif,624				   unsigned long vif_flags,625				   const struct mlxsw_sp_rif *rif)626{627	struct mlxsw_sp_mr_route_vif_entry *irve, *erve;628	int err;629 630	/* Update the VIF */631	mr_vif->dev = dev;632	mr_vif->rif = rif;633	mr_vif->vif_flags = vif_flags;634 635	/* Update all routes where this VIF is used as an unresolved iRIF */636	list_for_each_entry(irve, &mr_vif->route_ivif_list, vif_node) {637		err = mlxsw_sp_mr_route_ivif_resolve(mr_table, irve);638		if (err)639			goto err_irif_unresolve;640	}641 642	/* Update all routes where this VIF is used as an unresolved eRIF */643	list_for_each_entry(erve, &mr_vif->route_evif_list, vif_node) {644		err = mlxsw_sp_mr_route_evif_resolve(mr_table, erve);645		if (err)646			goto err_erif_unresolve;647	}648	return 0;649 650err_erif_unresolve:651	list_for_each_entry_continue_reverse(erve, &mr_vif->route_evif_list,652					     vif_node)653		mlxsw_sp_mr_route_evif_unresolve(mr_table, erve);654err_irif_unresolve:655	list_for_each_entry_continue_reverse(irve, &mr_vif->route_ivif_list,656					     vif_node)657		mlxsw_sp_mr_route_ivif_unresolve(mr_table, irve);658	mr_vif->rif = NULL;659	return err;660}661 662static void mlxsw_sp_mr_vif_unresolve(struct mlxsw_sp_mr_table *mr_table,663				      struct net_device *dev,664				      struct mlxsw_sp_mr_vif *mr_vif)665{666	struct mlxsw_sp_mr_route_vif_entry *rve;667 668	/* Update all routes where this VIF is used as an unresolved eRIF */669	list_for_each_entry(rve, &mr_vif->route_evif_list, vif_node)670		mlxsw_sp_mr_route_evif_unresolve(mr_table, rve);671 672	/* Update all routes where this VIF is used as an unresolved iRIF */673	list_for_each_entry(rve, &mr_vif->route_ivif_list, vif_node)674		mlxsw_sp_mr_route_ivif_unresolve(mr_table, rve);675 676	/* Update the VIF */677	mr_vif->dev = dev;678	mr_vif->rif = NULL;679}680 681int mlxsw_sp_mr_vif_add(struct mlxsw_sp_mr_table *mr_table,682			struct net_device *dev, vifi_t vif_index,683			unsigned long vif_flags, const struct mlxsw_sp_rif *rif)684{685	struct mlxsw_sp_mr_vif *mr_vif = &mr_table->vifs[vif_index];686 687	if (WARN_ON(vif_index >= MAXVIFS))688		return -EINVAL;689	if (mr_vif->dev)690		return -EEXIST;691	return mlxsw_sp_mr_vif_resolve(mr_table, dev, mr_vif, vif_flags, rif);692}693 694void mlxsw_sp_mr_vif_del(struct mlxsw_sp_mr_table *mr_table, vifi_t vif_index)695{696	struct mlxsw_sp_mr_vif *mr_vif = &mr_table->vifs[vif_index];697 698	if (WARN_ON(vif_index >= MAXVIFS))699		return;700	if (WARN_ON(!mr_vif->dev))701		return;702	mlxsw_sp_mr_vif_unresolve(mr_table, NULL, mr_vif);703}704 705static struct mlxsw_sp_mr_vif *706mlxsw_sp_mr_dev_vif_lookup(struct mlxsw_sp_mr_table *mr_table,707			   const struct mlxsw_sp_rif *rif)708{709	vifi_t vif_index;710 711	for (vif_index = 0; vif_index < MAXVIFS; vif_index++)712		if (mlxsw_sp_rif_dev_is(rif, mr_table->vifs[vif_index].dev))713			return &mr_table->vifs[vif_index];714	return NULL;715}716 717int mlxsw_sp_mr_rif_add(struct mlxsw_sp_mr_table *mr_table,718			const struct mlxsw_sp_rif *rif)719{720	struct mlxsw_sp_mr_vif *mr_vif;721 722	if (!mlxsw_sp_rif_has_dev(rif))723		return 0;724 725	mr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif);726	if (!mr_vif)727		return 0;728	return mlxsw_sp_mr_vif_resolve(mr_table, mr_vif->dev, mr_vif,729				       mr_vif->vif_flags, rif);730}731 732void mlxsw_sp_mr_rif_del(struct mlxsw_sp_mr_table *mr_table,733			 const struct mlxsw_sp_rif *rif)734{735	struct mlxsw_sp_mr_vif *mr_vif;736 737	if (!mlxsw_sp_rif_has_dev(rif))738		return;739 740	mr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif);741	if (!mr_vif)742		return;743	mlxsw_sp_mr_vif_unresolve(mr_table, mr_vif->dev, mr_vif);744}745 746void mlxsw_sp_mr_rif_mtu_update(struct mlxsw_sp_mr_table *mr_table,747				const struct mlxsw_sp_rif *rif, int mtu)748{749	struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;750	struct mlxsw_sp_mr_route_vif_entry *rve;751	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;752	struct mlxsw_sp_mr_vif *mr_vif;753 754	if (!mlxsw_sp_rif_has_dev(rif))755		return;756 757	/* Search for a VIF that use that RIF */758	mr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif);759	if (!mr_vif)760		return;761 762	/* Update all the routes that uses that VIF as eVIF */763	list_for_each_entry(rve, &mr_vif->route_evif_list, vif_node) {764		if (mtu < rve->mr_route->min_mtu) {765			rve->mr_route->min_mtu = mtu;766			mr->mr_ops->route_min_mtu_update(mlxsw_sp,767							 rve->mr_route->route_priv,768							 mtu);769		}770	}771}772 773/* Protocol specific functions */774static bool775mlxsw_sp_mr_route4_validate(const struct mlxsw_sp_mr_table *mr_table,776			    const struct mr_mfc *c)777{778	struct mfc_cache *mfc = (struct mfc_cache *) c;779 780	/* If the route is a (*,*) route, abort, as these kind of routes are781	 * used for proxy routes.782	 */783	if (mfc->mfc_origin == htonl(INADDR_ANY) &&784	    mfc->mfc_mcastgrp == htonl(INADDR_ANY)) {785		dev_warn(mr_table->mlxsw_sp->bus_info->dev,786			 "Offloading proxy routes is not supported.\n");787		return false;788	}789	return true;790}791 792static void mlxsw_sp_mr_route4_key(struct mlxsw_sp_mr_table *mr_table,793				   struct mlxsw_sp_mr_route_key *key,794				   struct mr_mfc *c)795{796	const struct mfc_cache *mfc = (struct mfc_cache *) c;797	bool starg;798 799	starg = (mfc->mfc_origin == htonl(INADDR_ANY));800 801	memset(key, 0, sizeof(*key));802	key->vrid = mr_table->vr_id;803	key->proto = MLXSW_SP_L3_PROTO_IPV4;804	key->group.addr4 = mfc->mfc_mcastgrp;805	key->group_mask.addr4 = htonl(0xffffffff);806	key->source.addr4 = mfc->mfc_origin;807	key->source_mask.addr4 = htonl(starg ? 0 : 0xffffffff);808}809 810static bool mlxsw_sp_mr_route4_starg(const struct mlxsw_sp_mr_table *mr_table,811				     const struct mlxsw_sp_mr_route *mr_route)812{813	return mr_route->key.source_mask.addr4 == htonl(INADDR_ANY);814}815 816static bool mlxsw_sp_mr_vif4_is_regular(const struct mlxsw_sp_mr_vif *vif)817{818	return !(vif->vif_flags & (VIFF_TUNNEL | VIFF_REGISTER));819}820 821static bool822mlxsw_sp_mr_route6_validate(const struct mlxsw_sp_mr_table *mr_table,823			    const struct mr_mfc *c)824{825	struct mfc6_cache *mfc = (struct mfc6_cache *) c;826 827	/* If the route is a (*,*) route, abort, as these kind of routes are828	 * used for proxy routes.829	 */830	if (ipv6_addr_any(&mfc->mf6c_origin) &&831	    ipv6_addr_any(&mfc->mf6c_mcastgrp)) {832		dev_warn(mr_table->mlxsw_sp->bus_info->dev,833			 "Offloading proxy routes is not supported.\n");834		return false;835	}836	return true;837}838 839static void mlxsw_sp_mr_route6_key(struct mlxsw_sp_mr_table *mr_table,840				   struct mlxsw_sp_mr_route_key *key,841				   struct mr_mfc *c)842{843	const struct mfc6_cache *mfc = (struct mfc6_cache *) c;844 845	memset(key, 0, sizeof(*key));846	key->vrid = mr_table->vr_id;847	key->proto = MLXSW_SP_L3_PROTO_IPV6;848	key->group.addr6 = mfc->mf6c_mcastgrp;849	memset(&key->group_mask.addr6, 0xff, sizeof(key->group_mask.addr6));850	key->source.addr6 = mfc->mf6c_origin;851	if (!ipv6_addr_any(&mfc->mf6c_origin))852		memset(&key->source_mask.addr6, 0xff,853		       sizeof(key->source_mask.addr6));854}855 856static bool mlxsw_sp_mr_route6_starg(const struct mlxsw_sp_mr_table *mr_table,857				     const struct mlxsw_sp_mr_route *mr_route)858{859	return ipv6_addr_any(&mr_route->key.source_mask.addr6);860}861 862static bool mlxsw_sp_mr_vif6_is_regular(const struct mlxsw_sp_mr_vif *vif)863{864	return !(vif->vif_flags & MIFF_REGISTER);865}866 867static struct868mlxsw_sp_mr_vif_ops mlxsw_sp_mr_vif_ops_arr[] = {869	{870		.is_regular = mlxsw_sp_mr_vif4_is_regular,871	},872	{873		.is_regular = mlxsw_sp_mr_vif6_is_regular,874	},875};876 877static struct878mlxsw_sp_mr_table_ops mlxsw_sp_mr_table_ops_arr[] = {879	{880		.is_route_valid = mlxsw_sp_mr_route4_validate,881		.key_create = mlxsw_sp_mr_route4_key,882		.is_route_starg = mlxsw_sp_mr_route4_starg,883	},884	{885		.is_route_valid = mlxsw_sp_mr_route6_validate,886		.key_create = mlxsw_sp_mr_route6_key,887		.is_route_starg = mlxsw_sp_mr_route6_starg,888	},889 890};891 892struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,893						   u32 vr_id,894						   enum mlxsw_sp_l3proto proto)895{896	struct mlxsw_sp_mr_route_params catchall_route_params = {897		.prio = MLXSW_SP_MR_ROUTE_PRIO_CATCHALL,898		.key = {899			.vrid = vr_id,900			.proto = proto,901		},902		.value = {903			.route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP,904		}905	};906	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;907	struct mlxsw_sp_mr_table *mr_table;908	int err;909	int i;910 911	mr_table = kzalloc(sizeof(*mr_table) + mr->mr_ops->route_priv_size,912			   GFP_KERNEL);913	if (!mr_table)914		return ERR_PTR(-ENOMEM);915 916	mr_table->vr_id = vr_id;917	mr_table->mlxsw_sp = mlxsw_sp;918	mr_table->proto = proto;919	mr_table->ops = &mlxsw_sp_mr_table_ops_arr[proto];920	INIT_LIST_HEAD(&mr_table->route_list);921	mutex_init(&mr_table->route_list_lock);922 923	err = rhashtable_init(&mr_table->route_ht,924			      &mlxsw_sp_mr_route_ht_params);925	if (err)926		goto err_route_rhashtable_init;927 928	for (i = 0; i < MAXVIFS; i++) {929		INIT_LIST_HEAD(&mr_table->vifs[i].route_evif_list);930		INIT_LIST_HEAD(&mr_table->vifs[i].route_ivif_list);931		mr_table->vifs[i].ops = &mlxsw_sp_mr_vif_ops_arr[proto];932	}933 934	err = mr->mr_ops->route_create(mlxsw_sp, mr->priv,935				       mr_table->catchall_route_priv,936				       &catchall_route_params);937	if (err)938		goto err_ops_route_create;939	mutex_lock(&mr->table_list_lock);940	list_add_tail(&mr_table->node, &mr->table_list);941	mutex_unlock(&mr->table_list_lock);942	return mr_table;943 944err_ops_route_create:945	rhashtable_destroy(&mr_table->route_ht);946err_route_rhashtable_init:947	mutex_destroy(&mr_table->route_list_lock);948	kfree(mr_table);949	return ERR_PTR(err);950}951 952void mlxsw_sp_mr_table_destroy(struct mlxsw_sp_mr_table *mr_table)953{954	struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;955	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;956 957	WARN_ON(!mlxsw_sp_mr_table_empty(mr_table));958	mutex_lock(&mr->table_list_lock);959	list_del(&mr_table->node);960	mutex_unlock(&mr->table_list_lock);961	mr->mr_ops->route_destroy(mlxsw_sp, mr->priv,962				  &mr_table->catchall_route_priv);963	rhashtable_destroy(&mr_table->route_ht);964	mutex_destroy(&mr_table->route_list_lock);965	kfree(mr_table);966}967 968void mlxsw_sp_mr_table_flush(struct mlxsw_sp_mr_table *mr_table)969{970	struct mlxsw_sp_mr_route *mr_route, *tmp;971	int i;972 973	mutex_lock(&mr_table->route_list_lock);974	list_for_each_entry_safe(mr_route, tmp, &mr_table->route_list, node)975		__mlxsw_sp_mr_route_del(mr_table, mr_route);976	mutex_unlock(&mr_table->route_list_lock);977 978	for (i = 0; i < MAXVIFS; i++) {979		mr_table->vifs[i].dev = NULL;980		mr_table->vifs[i].rif = NULL;981	}982}983 984bool mlxsw_sp_mr_table_empty(const struct mlxsw_sp_mr_table *mr_table)985{986	int i;987 988	for (i = 0; i < MAXVIFS; i++)989		if (mr_table->vifs[i].dev)990			return false;991	return list_empty(&mr_table->route_list);992}993 994static void mlxsw_sp_mr_route_stats_update(struct mlxsw_sp *mlxsw_sp,995					   struct mlxsw_sp_mr_route *mr_route)996{997	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;998	u64 packets, bytes;999 1000	if (mr_route->route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP)1001		return;1002 1003	mr->mr_ops->route_stats(mlxsw_sp, mr_route->route_priv, &packets,1004				&bytes);1005 1006	if (mr_route->mfc->mfc_un.res.pkt != packets)1007		mr_route->mfc->mfc_un.res.lastuse = jiffies;1008	mr_route->mfc->mfc_un.res.pkt = packets;1009	mr_route->mfc->mfc_un.res.bytes = bytes;1010}1011 1012static void mlxsw_sp_mr_stats_update(struct work_struct *work)1013{1014	struct mlxsw_sp_mr *mr = container_of(work, struct mlxsw_sp_mr,1015					      stats_update_dw.work);1016	struct mlxsw_sp_mr_table *mr_table;1017	struct mlxsw_sp_mr_route *mr_route;1018	unsigned long interval;1019 1020	mutex_lock(&mr->table_list_lock);1021	list_for_each_entry(mr_table, &mr->table_list, node) {1022		mutex_lock(&mr_table->route_list_lock);1023		list_for_each_entry(mr_route, &mr_table->route_list, node)1024			mlxsw_sp_mr_route_stats_update(mr_table->mlxsw_sp,1025						       mr_route);1026		mutex_unlock(&mr_table->route_list_lock);1027	}1028	mutex_unlock(&mr->table_list_lock);1029 1030	interval = msecs_to_jiffies(MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL);1031	mlxsw_core_schedule_dw(&mr->stats_update_dw, interval);1032}1033 1034int mlxsw_sp_mr_init(struct mlxsw_sp *mlxsw_sp,1035		     const struct mlxsw_sp_mr_ops *mr_ops)1036{1037	struct mlxsw_sp_mr *mr;1038	unsigned long interval;1039	int err;1040 1041	mr = kzalloc(sizeof(*mr) + mr_ops->priv_size, GFP_KERNEL);1042	if (!mr)1043		return -ENOMEM;1044	mr->mr_ops = mr_ops;1045	mlxsw_sp->mr = mr;1046	INIT_LIST_HEAD(&mr->table_list);1047	mutex_init(&mr->table_list_lock);1048 1049	err = mr_ops->init(mlxsw_sp, mr->priv);1050	if (err)1051		goto err;1052 1053	/* Create the delayed work for counter updates */1054	INIT_DELAYED_WORK(&mr->stats_update_dw, mlxsw_sp_mr_stats_update);1055	interval = msecs_to_jiffies(MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL);1056	mlxsw_core_schedule_dw(&mr->stats_update_dw, interval);1057	return 0;1058err:1059	mutex_destroy(&mr->table_list_lock);1060	kfree(mr);1061	return err;1062}1063 1064void mlxsw_sp_mr_fini(struct mlxsw_sp *mlxsw_sp)1065{1066	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;1067 1068	cancel_delayed_work_sync(&mr->stats_update_dw);1069	mr->mr_ops->fini(mlxsw_sp, mr->priv);1070	mutex_destroy(&mr->table_list_lock);1071	kfree(mr);1072}1073