xref: /openbmc/linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c (revision 060f35a317ef09101b128f399dce7ed13d019461)
19948a064SJiri Pirko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
29948a064SJiri Pirko /* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
3c011ec1bSYotam Gigi 
4c366de85SIdo Schimmel #include <linux/mutex.h>
5c011ec1bSYotam Gigi #include <linux/rhashtable.h>
66981e104SYuval Mintz #include <net/ipv6.h>
7c011ec1bSYotam Gigi 
8c011ec1bSYotam Gigi #include "spectrum_mr.h"
9c011ec1bSYotam Gigi #include "spectrum_router.h"
10c011ec1bSYotam Gigi 
11c011ec1bSYotam Gigi struct mlxsw_sp_mr {
12c011ec1bSYotam Gigi 	const struct mlxsw_sp_mr_ops *mr_ops;
13c011ec1bSYotam Gigi 	void *catchall_route_priv;
14c011ec1bSYotam Gigi 	struct delayed_work stats_update_dw;
15c011ec1bSYotam Gigi 	struct list_head table_list;
16c366de85SIdo Schimmel 	struct mutex table_list_lock; /* Protects table_list */
17c011ec1bSYotam Gigi #define MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL 5000 /* ms */
1842c435a2SIdo Schimmel 	unsigned long priv[];
19c011ec1bSYotam Gigi 	/* priv has to be always the last item */
20c011ec1bSYotam Gigi };
21c011ec1bSYotam Gigi 
224caef463SYuval Mintz struct mlxsw_sp_mr_vif;
234caef463SYuval Mintz struct mlxsw_sp_mr_vif_ops {
244caef463SYuval Mintz 	bool (*is_regular)(const struct mlxsw_sp_mr_vif *vif);
254caef463SYuval Mintz };
264caef463SYuval Mintz 
27c011ec1bSYotam Gigi struct mlxsw_sp_mr_vif {
28c011ec1bSYotam Gigi 	struct net_device *dev;
29c011ec1bSYotam Gigi 	const struct mlxsw_sp_rif *rif;
30c011ec1bSYotam Gigi 	unsigned long vif_flags;
31c011ec1bSYotam Gigi 
32c011ec1bSYotam Gigi 	/* A list of route_vif_entry structs that point to routes that the VIF
33c011ec1bSYotam Gigi 	 * instance is used as one of the egress VIFs
34c011ec1bSYotam Gigi 	 */
35c011ec1bSYotam Gigi 	struct list_head route_evif_list;
36c011ec1bSYotam Gigi 
37c011ec1bSYotam Gigi 	/* A list of route_vif_entry structs that point to routes that the VIF
38c011ec1bSYotam Gigi 	 * instance is used as an ingress VIF
39c011ec1bSYotam Gigi 	 */
40c011ec1bSYotam Gigi 	struct list_head route_ivif_list;
414caef463SYuval Mintz 
424caef463SYuval Mintz 	/* Protocol specific operations for a VIF */
434caef463SYuval Mintz 	const struct mlxsw_sp_mr_vif_ops *ops;
44c011ec1bSYotam Gigi };
45c011ec1bSYotam Gigi 
46c011ec1bSYotam Gigi struct mlxsw_sp_mr_route_vif_entry {
47c011ec1bSYotam Gigi 	struct list_head vif_node;
48c011ec1bSYotam Gigi 	struct list_head route_node;
49c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_vif *mr_vif;
50c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route *mr_route;
51c011ec1bSYotam Gigi };
52c011ec1bSYotam Gigi 
534caef463SYuval Mintz struct mlxsw_sp_mr_table;
544caef463SYuval Mintz struct mlxsw_sp_mr_table_ops {
554caef463SYuval Mintz 	bool (*is_route_valid)(const struct mlxsw_sp_mr_table *mr_table,
564caef463SYuval Mintz 			       const struct mr_mfc *mfc);
574caef463SYuval Mintz 	void (*key_create)(struct mlxsw_sp_mr_table *mr_table,
584caef463SYuval Mintz 			   struct mlxsw_sp_mr_route_key *key,
594caef463SYuval Mintz 			   struct mr_mfc *mfc);
604caef463SYuval Mintz 	bool (*is_route_starg)(const struct mlxsw_sp_mr_table *mr_table,
614caef463SYuval Mintz 			       const struct mlxsw_sp_mr_route *mr_route);
624caef463SYuval Mintz };
634caef463SYuval Mintz 
64c011ec1bSYotam Gigi struct mlxsw_sp_mr_table {
65c011ec1bSYotam Gigi 	struct list_head node;
66c011ec1bSYotam Gigi 	enum mlxsw_sp_l3proto proto;
67c011ec1bSYotam Gigi 	struct mlxsw_sp *mlxsw_sp;
68c011ec1bSYotam Gigi 	u32 vr_id;
69c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_vif vifs[MAXVIFS];
70c011ec1bSYotam Gigi 	struct list_head route_list;
71f38656d0SIdo Schimmel 	struct mutex route_list_lock; /* Protects route_list */
72c011ec1bSYotam Gigi 	struct rhashtable route_ht;
734caef463SYuval Mintz 	const struct mlxsw_sp_mr_table_ops *ops;
74e99f8e7fSGustavo A. R. Silva 	char catchall_route_priv[];
75c011ec1bSYotam Gigi 	/* catchall_route_priv has to be always the last item */
76c011ec1bSYotam Gigi };
77c011ec1bSYotam Gigi 
78c011ec1bSYotam Gigi struct mlxsw_sp_mr_route {
79c011ec1bSYotam Gigi 	struct list_head node;
80c011ec1bSYotam Gigi 	struct rhash_head ht_node;
81c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route_key key;
82c011ec1bSYotam Gigi 	enum mlxsw_sp_mr_route_action route_action;
83c011ec1bSYotam Gigi 	u16 min_mtu;
844caef463SYuval Mintz 	struct mr_mfc *mfc;
85c011ec1bSYotam Gigi 	void *route_priv;
86c011ec1bSYotam Gigi 	const struct mlxsw_sp_mr_table *mr_table;
87c011ec1bSYotam Gigi 	/* A list of route_vif_entry structs that point to the egress VIFs */
88c011ec1bSYotam Gigi 	struct list_head evif_list;
89c011ec1bSYotam Gigi 	/* A route_vif_entry struct that point to the ingress VIF */
90c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route_vif_entry ivif;
91c011ec1bSYotam Gigi };
92c011ec1bSYotam Gigi 
93c011ec1bSYotam Gigi static const struct rhashtable_params mlxsw_sp_mr_route_ht_params = {
94c011ec1bSYotam Gigi 	.key_len = sizeof(struct mlxsw_sp_mr_route_key),
95c011ec1bSYotam Gigi 	.key_offset = offsetof(struct mlxsw_sp_mr_route, key),
96c011ec1bSYotam Gigi 	.head_offset = offsetof(struct mlxsw_sp_mr_route, ht_node),
97c011ec1bSYotam Gigi 	.automatic_shrinking = true,
98c011ec1bSYotam Gigi };
99c011ec1bSYotam Gigi 
mlxsw_sp_mr_vif_valid(const struct mlxsw_sp_mr_vif * vif)100c011ec1bSYotam Gigi static bool mlxsw_sp_mr_vif_valid(const struct mlxsw_sp_mr_vif *vif)
101c011ec1bSYotam Gigi {
1024caef463SYuval Mintz 	return vif->ops->is_regular(vif) && vif->dev && vif->rif;
103c011ec1bSYotam Gigi }
104c011ec1bSYotam Gigi 
mlxsw_sp_mr_vif_exists(const struct mlxsw_sp_mr_vif * vif)105f60c2549SYotam Gigi static bool mlxsw_sp_mr_vif_exists(const struct mlxsw_sp_mr_vif *vif)
106c011ec1bSYotam Gigi {
107f60c2549SYotam Gigi 	return vif->dev;
108c011ec1bSYotam Gigi }
109c011ec1bSYotam Gigi 
110c011ec1bSYotam Gigi static bool
mlxsw_sp_mr_route_ivif_in_evifs(const struct mlxsw_sp_mr_route * mr_route)111c011ec1bSYotam Gigi mlxsw_sp_mr_route_ivif_in_evifs(const struct mlxsw_sp_mr_route *mr_route)
112c011ec1bSYotam Gigi {
1134caef463SYuval Mintz 	vifi_t ivif = mr_route->mfc->mfc_parent;
114c011ec1bSYotam Gigi 
1154caef463SYuval Mintz 	return mr_route->mfc->mfc_un.res.ttls[ivif] != 255;
116c011ec1bSYotam Gigi }
117c011ec1bSYotam Gigi 
118c011ec1bSYotam Gigi static int
mlxsw_sp_mr_route_valid_evifs_num(const struct mlxsw_sp_mr_route * mr_route)119c011ec1bSYotam Gigi mlxsw_sp_mr_route_valid_evifs_num(const struct mlxsw_sp_mr_route *mr_route)
120c011ec1bSYotam Gigi {
121c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route_vif_entry *rve;
122c011ec1bSYotam Gigi 	int valid_evifs;
123c011ec1bSYotam Gigi 
124c011ec1bSYotam Gigi 	valid_evifs = 0;
125c011ec1bSYotam Gigi 	list_for_each_entry(rve, &mr_route->evif_list, route_node)
126c011ec1bSYotam Gigi 		if (mlxsw_sp_mr_vif_valid(rve->mr_vif))
127c011ec1bSYotam Gigi 			valid_evifs++;
128c011ec1bSYotam Gigi 	return valid_evifs;
129c011ec1bSYotam Gigi }
130c011ec1bSYotam Gigi 
131c011ec1bSYotam Gigi static enum mlxsw_sp_mr_route_action
mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route * mr_route)132c011ec1bSYotam Gigi mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)
133c011ec1bSYotam Gigi {
134c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route_vif_entry *rve;
135c011ec1bSYotam Gigi 
136c011ec1bSYotam Gigi 	/* If the ingress port is not regular and resolved, trap the route */
137c011ec1bSYotam Gigi 	if (!mlxsw_sp_mr_vif_valid(mr_route->ivif.mr_vif))
138c011ec1bSYotam Gigi 		return MLXSW_SP_MR_ROUTE_ACTION_TRAP;
139c011ec1bSYotam Gigi 
140c011ec1bSYotam Gigi 	/* The kernel does not match a (*,G) route that the ingress interface is
141c011ec1bSYotam Gigi 	 * not one of the egress interfaces, so trap these kind of routes.
142c011ec1bSYotam Gigi 	 */
1434caef463SYuval Mintz 	if (mr_route->mr_table->ops->is_route_starg(mr_route->mr_table,
1444caef463SYuval Mintz 						    mr_route) &&
145c011ec1bSYotam Gigi 	    !mlxsw_sp_mr_route_ivif_in_evifs(mr_route))
146c011ec1bSYotam Gigi 		return MLXSW_SP_MR_ROUTE_ACTION_TRAP;
147c011ec1bSYotam Gigi 
148c011ec1bSYotam Gigi 	/* If the route has no valid eVIFs, trap it. */
149c011ec1bSYotam Gigi 	if (!mlxsw_sp_mr_route_valid_evifs_num(mr_route))
150c011ec1bSYotam Gigi 		return MLXSW_SP_MR_ROUTE_ACTION_TRAP;
151c011ec1bSYotam Gigi 
152f60c2549SYotam Gigi 	/* If one of the eVIFs has no RIF, trap-and-forward the route as there
153f60c2549SYotam Gigi 	 * is some more routing to do in software too.
154c011ec1bSYotam Gigi 	 */
155f60c2549SYotam Gigi 	list_for_each_entry(rve, &mr_route->evif_list, route_node)
156f60c2549SYotam Gigi 		if (mlxsw_sp_mr_vif_exists(rve->mr_vif) && !rve->mr_vif->rif)
157f60c2549SYotam Gigi 			return MLXSW_SP_MR_ROUTE_ACTION_TRAP_AND_FORWARD;
158f60c2549SYotam Gigi 
159c011ec1bSYotam Gigi 	return MLXSW_SP_MR_ROUTE_ACTION_FORWARD;
160c011ec1bSYotam Gigi }
161c011ec1bSYotam Gigi 
162c011ec1bSYotam Gigi static enum mlxsw_sp_mr_route_prio
mlxsw_sp_mr_route_prio(const struct mlxsw_sp_mr_route * mr_route)163c011ec1bSYotam Gigi mlxsw_sp_mr_route_prio(const struct mlxsw_sp_mr_route *mr_route)
164c011ec1bSYotam Gigi {
1654caef463SYuval Mintz 	return mr_route->mr_table->ops->is_route_starg(mr_route->mr_table,
1664caef463SYuval Mintz 						       mr_route) ?
167c011ec1bSYotam Gigi 		MLXSW_SP_MR_ROUTE_PRIO_STARG : MLXSW_SP_MR_ROUTE_PRIO_SG;
168c011ec1bSYotam Gigi }
169c011ec1bSYotam Gigi 
mlxsw_sp_mr_route_evif_link(struct mlxsw_sp_mr_route * mr_route,struct mlxsw_sp_mr_vif * mr_vif)170c011ec1bSYotam Gigi static int mlxsw_sp_mr_route_evif_link(struct mlxsw_sp_mr_route *mr_route,
171c011ec1bSYotam Gigi 				       struct mlxsw_sp_mr_vif *mr_vif)
172c011ec1bSYotam Gigi {
173c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route_vif_entry *rve;
174c011ec1bSYotam Gigi 
175c011ec1bSYotam Gigi 	rve = kzalloc(sizeof(*rve), GFP_KERNEL);
176c011ec1bSYotam Gigi 	if (!rve)
177c011ec1bSYotam Gigi 		return -ENOMEM;
178c011ec1bSYotam Gigi 	rve->mr_route = mr_route;
179c011ec1bSYotam Gigi 	rve->mr_vif = mr_vif;
180c011ec1bSYotam Gigi 	list_add_tail(&rve->route_node, &mr_route->evif_list);
181c011ec1bSYotam Gigi 	list_add_tail(&rve->vif_node, &mr_vif->route_evif_list);
182c011ec1bSYotam Gigi 	return 0;
183c011ec1bSYotam Gigi }
184c011ec1bSYotam Gigi 
185c011ec1bSYotam Gigi static void
mlxsw_sp_mr_route_evif_unlink(struct mlxsw_sp_mr_route_vif_entry * rve)186c011ec1bSYotam Gigi mlxsw_sp_mr_route_evif_unlink(struct mlxsw_sp_mr_route_vif_entry *rve)
187c011ec1bSYotam Gigi {
188c011ec1bSYotam Gigi 	list_del(&rve->route_node);
189c011ec1bSYotam Gigi 	list_del(&rve->vif_node);
190c011ec1bSYotam Gigi 	kfree(rve);
191c011ec1bSYotam Gigi }
192c011ec1bSYotam Gigi 
mlxsw_sp_mr_route_ivif_link(struct mlxsw_sp_mr_route * mr_route,struct mlxsw_sp_mr_vif * mr_vif)193c011ec1bSYotam Gigi static void mlxsw_sp_mr_route_ivif_link(struct mlxsw_sp_mr_route *mr_route,
194c011ec1bSYotam Gigi 					struct mlxsw_sp_mr_vif *mr_vif)
195c011ec1bSYotam Gigi {
196c011ec1bSYotam Gigi 	mr_route->ivif.mr_route = mr_route;
197c011ec1bSYotam Gigi 	mr_route->ivif.mr_vif = mr_vif;
198c011ec1bSYotam Gigi 	list_add_tail(&mr_route->ivif.vif_node, &mr_vif->route_ivif_list);
199c011ec1bSYotam Gigi }
200c011ec1bSYotam Gigi 
mlxsw_sp_mr_route_ivif_unlink(struct mlxsw_sp_mr_route * mr_route)201c011ec1bSYotam Gigi static void mlxsw_sp_mr_route_ivif_unlink(struct mlxsw_sp_mr_route *mr_route)
202c011ec1bSYotam Gigi {
203c011ec1bSYotam Gigi 	list_del(&mr_route->ivif.vif_node);
204c011ec1bSYotam Gigi }
205c011ec1bSYotam Gigi 
206c011ec1bSYotam Gigi static int
mlxsw_sp_mr_route_info_create(struct mlxsw_sp_mr_table * mr_table,struct mlxsw_sp_mr_route * mr_route,struct mlxsw_sp_mr_route_info * route_info)207c011ec1bSYotam Gigi mlxsw_sp_mr_route_info_create(struct mlxsw_sp_mr_table *mr_table,
208c011ec1bSYotam Gigi 			      struct mlxsw_sp_mr_route *mr_route,
209c011ec1bSYotam Gigi 			      struct mlxsw_sp_mr_route_info *route_info)
210c011ec1bSYotam Gigi {
211c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route_vif_entry *rve;
212c011ec1bSYotam Gigi 	u16 *erif_indices;
213c011ec1bSYotam Gigi 	u16 irif_index;
214c011ec1bSYotam Gigi 	u16 erif = 0;
215c011ec1bSYotam Gigi 
216c011ec1bSYotam Gigi 	erif_indices = kmalloc_array(MAXVIFS, sizeof(*erif_indices),
217c011ec1bSYotam Gigi 				     GFP_KERNEL);
218c011ec1bSYotam Gigi 	if (!erif_indices)
219c011ec1bSYotam Gigi 		return -ENOMEM;
220c011ec1bSYotam Gigi 
221c011ec1bSYotam Gigi 	list_for_each_entry(rve, &mr_route->evif_list, route_node) {
222c011ec1bSYotam Gigi 		if (mlxsw_sp_mr_vif_valid(rve->mr_vif)) {
223c011ec1bSYotam Gigi 			u16 rifi = mlxsw_sp_rif_index(rve->mr_vif->rif);
224c011ec1bSYotam Gigi 
225c011ec1bSYotam Gigi 			erif_indices[erif++] = rifi;
226c011ec1bSYotam Gigi 		}
227c011ec1bSYotam Gigi 	}
228c011ec1bSYotam Gigi 
229c011ec1bSYotam Gigi 	if (mlxsw_sp_mr_vif_valid(mr_route->ivif.mr_vif))
230c011ec1bSYotam Gigi 		irif_index = mlxsw_sp_rif_index(mr_route->ivif.mr_vif->rif);
231c011ec1bSYotam Gigi 	else
232c011ec1bSYotam Gigi 		irif_index = 0;
233c011ec1bSYotam Gigi 
234c011ec1bSYotam Gigi 	route_info->irif_index = irif_index;
235c011ec1bSYotam Gigi 	route_info->erif_indices = erif_indices;
236c011ec1bSYotam Gigi 	route_info->min_mtu = mr_route->min_mtu;
237c011ec1bSYotam Gigi 	route_info->route_action = mr_route->route_action;
238c011ec1bSYotam Gigi 	route_info->erif_num = erif;
239c011ec1bSYotam Gigi 	return 0;
240c011ec1bSYotam Gigi }
241c011ec1bSYotam Gigi 
242c011ec1bSYotam Gigi static void
mlxsw_sp_mr_route_info_destroy(struct mlxsw_sp_mr_route_info * route_info)243c011ec1bSYotam Gigi mlxsw_sp_mr_route_info_destroy(struct mlxsw_sp_mr_route_info *route_info)
244c011ec1bSYotam Gigi {
245c011ec1bSYotam Gigi 	kfree(route_info->erif_indices);
246c011ec1bSYotam Gigi }
247c011ec1bSYotam Gigi 
mlxsw_sp_mr_route_write(struct mlxsw_sp_mr_table * mr_table,struct mlxsw_sp_mr_route * mr_route,bool replace)248c011ec1bSYotam Gigi static int mlxsw_sp_mr_route_write(struct mlxsw_sp_mr_table *mr_table,
249c011ec1bSYotam Gigi 				   struct mlxsw_sp_mr_route *mr_route,
250c011ec1bSYotam Gigi 				   bool replace)
251c011ec1bSYotam Gigi {
252c011ec1bSYotam Gigi 	struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;
253c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route_info route_info;
254c011ec1bSYotam Gigi 	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
255c011ec1bSYotam Gigi 	int err;
256c011ec1bSYotam Gigi 
257c011ec1bSYotam Gigi 	err = mlxsw_sp_mr_route_info_create(mr_table, mr_route, &route_info);
258c011ec1bSYotam Gigi 	if (err)
259c011ec1bSYotam Gigi 		return err;
260c011ec1bSYotam Gigi 
261c011ec1bSYotam Gigi 	if (!replace) {
262c011ec1bSYotam Gigi 		struct mlxsw_sp_mr_route_params route_params;
263c011ec1bSYotam Gigi 
264c011ec1bSYotam Gigi 		mr_route->route_priv = kzalloc(mr->mr_ops->route_priv_size,
265c011ec1bSYotam Gigi 					       GFP_KERNEL);
266c011ec1bSYotam Gigi 		if (!mr_route->route_priv) {
267c011ec1bSYotam Gigi 			err = -ENOMEM;
268c011ec1bSYotam Gigi 			goto out;
269c011ec1bSYotam Gigi 		}
270c011ec1bSYotam Gigi 
271c011ec1bSYotam Gigi 		route_params.key = mr_route->key;
272c011ec1bSYotam Gigi 		route_params.value = route_info;
273c011ec1bSYotam Gigi 		route_params.prio = mlxsw_sp_mr_route_prio(mr_route);
274c011ec1bSYotam Gigi 		err = mr->mr_ops->route_create(mlxsw_sp, mr->priv,
275c011ec1bSYotam Gigi 					       mr_route->route_priv,
276c011ec1bSYotam Gigi 					       &route_params);
277c011ec1bSYotam Gigi 		if (err)
278c011ec1bSYotam Gigi 			kfree(mr_route->route_priv);
279c011ec1bSYotam Gigi 	} else {
280c011ec1bSYotam Gigi 		err = mr->mr_ops->route_update(mlxsw_sp, mr_route->route_priv,
281c011ec1bSYotam Gigi 					       &route_info);
282c011ec1bSYotam Gigi 	}
283c011ec1bSYotam Gigi out:
284c011ec1bSYotam Gigi 	mlxsw_sp_mr_route_info_destroy(&route_info);
285c011ec1bSYotam Gigi 	return err;
286c011ec1bSYotam Gigi }
287c011ec1bSYotam Gigi 
mlxsw_sp_mr_route_erase(struct mlxsw_sp_mr_table * mr_table,struct mlxsw_sp_mr_route * mr_route)288c011ec1bSYotam Gigi static void mlxsw_sp_mr_route_erase(struct mlxsw_sp_mr_table *mr_table,
289c011ec1bSYotam Gigi 				    struct mlxsw_sp_mr_route *mr_route)
290c011ec1bSYotam Gigi {
291c011ec1bSYotam Gigi 	struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;
292c011ec1bSYotam Gigi 	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
293c011ec1bSYotam Gigi 
294c011ec1bSYotam Gigi 	mr->mr_ops->route_destroy(mlxsw_sp, mr->priv, mr_route->route_priv);
295c011ec1bSYotam Gigi 	kfree(mr_route->route_priv);
296c011ec1bSYotam Gigi }
297c011ec1bSYotam Gigi 
298c011ec1bSYotam Gigi static struct mlxsw_sp_mr_route *
mlxsw_sp_mr_route_create(struct mlxsw_sp_mr_table * mr_table,struct mr_mfc * mfc)299eb35da0cSYuval Mintz mlxsw_sp_mr_route_create(struct mlxsw_sp_mr_table *mr_table,
300eb35da0cSYuval Mintz 			 struct mr_mfc *mfc)
301c011ec1bSYotam Gigi {
302c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route_vif_entry *rve, *tmp;
303c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route *mr_route;
30445bfbc01SColin Ian King 	int err = 0;
305c011ec1bSYotam Gigi 	int i;
306c011ec1bSYotam Gigi 
307c011ec1bSYotam Gigi 	/* Allocate and init a new route and fill it with parameters */
308c011ec1bSYotam Gigi 	mr_route = kzalloc(sizeof(*mr_route), GFP_KERNEL);
309c011ec1bSYotam Gigi 	if (!mr_route)
310c011ec1bSYotam Gigi 		return ERR_PTR(-ENOMEM);
311c011ec1bSYotam Gigi 	INIT_LIST_HEAD(&mr_route->evif_list);
312c011ec1bSYotam Gigi 
313c011ec1bSYotam Gigi 	/* Find min_mtu and link iVIF and eVIFs */
314c011ec1bSYotam Gigi 	mr_route->min_mtu = ETH_MAX_MTU;
315eb35da0cSYuval Mintz 	mr_cache_hold(mfc);
316eb35da0cSYuval Mintz 	mr_route->mfc = mfc;
3174caef463SYuval Mintz 	mr_table->ops->key_create(mr_table, &mr_route->key, mr_route->mfc);
3184caef463SYuval Mintz 
319c011ec1bSYotam Gigi 	mr_route->mr_table = mr_table;
320c011ec1bSYotam Gigi 	for (i = 0; i < MAXVIFS; i++) {
321eb35da0cSYuval Mintz 		if (mfc->mfc_un.res.ttls[i] != 255) {
322c011ec1bSYotam Gigi 			err = mlxsw_sp_mr_route_evif_link(mr_route,
323c011ec1bSYotam Gigi 							  &mr_table->vifs[i]);
324c011ec1bSYotam Gigi 			if (err)
325c011ec1bSYotam Gigi 				goto err;
326c011ec1bSYotam Gigi 			if (mr_table->vifs[i].dev &&
327c011ec1bSYotam Gigi 			    mr_table->vifs[i].dev->mtu < mr_route->min_mtu)
328c011ec1bSYotam Gigi 				mr_route->min_mtu = mr_table->vifs[i].dev->mtu;
329c011ec1bSYotam Gigi 		}
330c011ec1bSYotam Gigi 	}
331494fff56SYuval Mintz 	mlxsw_sp_mr_route_ivif_link(mr_route,
332eb35da0cSYuval Mintz 				    &mr_table->vifs[mfc->mfc_parent]);
333c011ec1bSYotam Gigi 
334c011ec1bSYotam Gigi 	mr_route->route_action = mlxsw_sp_mr_route_action(mr_route);
335c011ec1bSYotam Gigi 	return mr_route;
336c011ec1bSYotam Gigi err:
337eb35da0cSYuval Mintz 	mr_cache_put(mfc);
338c011ec1bSYotam Gigi 	list_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)
339c011ec1bSYotam Gigi 		mlxsw_sp_mr_route_evif_unlink(rve);
340c011ec1bSYotam Gigi 	kfree(mr_route);
341c011ec1bSYotam Gigi 	return ERR_PTR(err);
342c011ec1bSYotam Gigi }
343c011ec1bSYotam Gigi 
mlxsw_sp_mr_route_destroy(struct mlxsw_sp_mr_table * mr_table,struct mlxsw_sp_mr_route * mr_route)344eb35da0cSYuval Mintz static void mlxsw_sp_mr_route_destroy(struct mlxsw_sp_mr_table *mr_table,
345c011ec1bSYotam Gigi 				      struct mlxsw_sp_mr_route *mr_route)
346c011ec1bSYotam Gigi {
347c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route_vif_entry *rve, *tmp;
348c011ec1bSYotam Gigi 
349c011ec1bSYotam Gigi 	mlxsw_sp_mr_route_ivif_unlink(mr_route);
350eb35da0cSYuval Mintz 	mr_cache_put(mr_route->mfc);
351c011ec1bSYotam Gigi 	list_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)
352c011ec1bSYotam Gigi 		mlxsw_sp_mr_route_evif_unlink(rve);
353c011ec1bSYotam Gigi 	kfree(mr_route);
354c011ec1bSYotam Gigi }
355c011ec1bSYotam Gigi 
mlxsw_sp_mr_mfc_offload_set(struct mlxsw_sp_mr_route * mr_route,bool offload)356c011ec1bSYotam Gigi static void mlxsw_sp_mr_mfc_offload_set(struct mlxsw_sp_mr_route *mr_route,
357c011ec1bSYotam Gigi 					bool offload)
358c011ec1bSYotam Gigi {
359c011ec1bSYotam Gigi 	if (offload)
3604caef463SYuval Mintz 		mr_route->mfc->mfc_flags |= MFC_OFFLOAD;
361c011ec1bSYotam Gigi 	else
3624caef463SYuval Mintz 		mr_route->mfc->mfc_flags &= ~MFC_OFFLOAD;
363c011ec1bSYotam Gigi }
364c011ec1bSYotam Gigi 
mlxsw_sp_mr_mfc_offload_update(struct mlxsw_sp_mr_route * mr_route)365c011ec1bSYotam Gigi static void mlxsw_sp_mr_mfc_offload_update(struct mlxsw_sp_mr_route *mr_route)
366c011ec1bSYotam Gigi {
367c011ec1bSYotam Gigi 	bool offload;
368c011ec1bSYotam Gigi 
369c011ec1bSYotam Gigi 	offload = mr_route->route_action != MLXSW_SP_MR_ROUTE_ACTION_TRAP;
370c011ec1bSYotam Gigi 	mlxsw_sp_mr_mfc_offload_set(mr_route, offload);
371c011ec1bSYotam Gigi }
372c011ec1bSYotam Gigi 
__mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table * mr_table,struct mlxsw_sp_mr_route * mr_route)373c011ec1bSYotam Gigi static void __mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,
374c011ec1bSYotam Gigi 				    struct mlxsw_sp_mr_route *mr_route)
375c011ec1bSYotam Gigi {
376f38656d0SIdo Schimmel 	WARN_ON_ONCE(!mutex_is_locked(&mr_table->route_list_lock));
377f38656d0SIdo Schimmel 
378c011ec1bSYotam Gigi 	mlxsw_sp_mr_mfc_offload_set(mr_route, false);
379c011ec1bSYotam Gigi 	rhashtable_remove_fast(&mr_table->route_ht, &mr_route->ht_node,
380c011ec1bSYotam Gigi 			       mlxsw_sp_mr_route_ht_params);
381c011ec1bSYotam Gigi 	list_del(&mr_route->node);
3823e3c8dafSIdo Schimmel 	mlxsw_sp_mr_route_erase(mr_table, mr_route);
383c011ec1bSYotam Gigi 	mlxsw_sp_mr_route_destroy(mr_table, mr_route);
384c011ec1bSYotam Gigi }
385c011ec1bSYotam Gigi 
mlxsw_sp_mr_route_add(struct mlxsw_sp_mr_table * mr_table,struct mr_mfc * mfc,bool replace)386eb35da0cSYuval Mintz int mlxsw_sp_mr_route_add(struct mlxsw_sp_mr_table *mr_table,
387eb35da0cSYuval Mintz 			  struct mr_mfc *mfc, bool replace)
388c011ec1bSYotam Gigi {
389c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route *mr_orig_route = NULL;
390c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route *mr_route;
391c011ec1bSYotam Gigi 	int err;
392c011ec1bSYotam Gigi 
393eb35da0cSYuval Mintz 	if (!mr_table->ops->is_route_valid(mr_table, mfc))
394c011ec1bSYotam Gigi 		return -EINVAL;
395c011ec1bSYotam Gigi 
396c011ec1bSYotam Gigi 	/* Create a new route */
397eb35da0cSYuval Mintz 	mr_route = mlxsw_sp_mr_route_create(mr_table, mfc);
398c011ec1bSYotam Gigi 	if (IS_ERR(mr_route))
399c011ec1bSYotam Gigi 		return PTR_ERR(mr_route);
400c011ec1bSYotam Gigi 
401c011ec1bSYotam Gigi 	/* Find any route with a matching key */
402c011ec1bSYotam Gigi 	mr_orig_route = rhashtable_lookup_fast(&mr_table->route_ht,
403c011ec1bSYotam Gigi 					       &mr_route->key,
404c011ec1bSYotam Gigi 					       mlxsw_sp_mr_route_ht_params);
405c011ec1bSYotam Gigi 	if (replace) {
406c011ec1bSYotam Gigi 		/* On replace case, make the route point to the new route_priv.
407c011ec1bSYotam Gigi 		 */
408c011ec1bSYotam Gigi 		if (WARN_ON(!mr_orig_route)) {
409c011ec1bSYotam Gigi 			err = -ENOENT;
410c011ec1bSYotam Gigi 			goto err_no_orig_route;
411c011ec1bSYotam Gigi 		}
412c011ec1bSYotam Gigi 		mr_route->route_priv = mr_orig_route->route_priv;
413c011ec1bSYotam Gigi 	} else if (mr_orig_route) {
414c011ec1bSYotam Gigi 		/* On non replace case, if another route with the same key was
415c011ec1bSYotam Gigi 		 * found, abort, as duplicate routes are used for proxy routes.
416c011ec1bSYotam Gigi 		 */
417c011ec1bSYotam Gigi 		dev_warn(mr_table->mlxsw_sp->bus_info->dev,
418c011ec1bSYotam Gigi 			 "Offloading proxy routes is not supported.\n");
419c011ec1bSYotam Gigi 		err = -EINVAL;
420c011ec1bSYotam Gigi 		goto err_duplicate_route;
421c011ec1bSYotam Gigi 	}
422c011ec1bSYotam Gigi 
4233e3c8dafSIdo Schimmel 	/* Write the route to the hardware */
4243e3c8dafSIdo Schimmel 	err = mlxsw_sp_mr_route_write(mr_table, mr_route, replace);
4253e3c8dafSIdo Schimmel 	if (err)
4263e3c8dafSIdo Schimmel 		goto err_mr_route_write;
4273e3c8dafSIdo Schimmel 
428c011ec1bSYotam Gigi 	/* Put it in the table data-structures */
429f38656d0SIdo Schimmel 	mutex_lock(&mr_table->route_list_lock);
430c011ec1bSYotam Gigi 	list_add_tail(&mr_route->node, &mr_table->route_list);
431f38656d0SIdo Schimmel 	mutex_unlock(&mr_table->route_list_lock);
432c011ec1bSYotam Gigi 	err = rhashtable_insert_fast(&mr_table->route_ht,
433c011ec1bSYotam Gigi 				     &mr_route->ht_node,
434c011ec1bSYotam Gigi 				     mlxsw_sp_mr_route_ht_params);
435c011ec1bSYotam Gigi 	if (err)
436c011ec1bSYotam Gigi 		goto err_rhashtable_insert;
437c011ec1bSYotam Gigi 
438c011ec1bSYotam Gigi 	/* Destroy the original route */
439c011ec1bSYotam Gigi 	if (replace) {
440c011ec1bSYotam Gigi 		rhashtable_remove_fast(&mr_table->route_ht,
441c011ec1bSYotam Gigi 				       &mr_orig_route->ht_node,
442c011ec1bSYotam Gigi 				       mlxsw_sp_mr_route_ht_params);
443c011ec1bSYotam Gigi 		list_del(&mr_orig_route->node);
444eb35da0cSYuval Mintz 		mlxsw_sp_mr_route_destroy(mr_table, mr_orig_route);
445c011ec1bSYotam Gigi 	}
446c011ec1bSYotam Gigi 
447c011ec1bSYotam Gigi 	mlxsw_sp_mr_mfc_offload_update(mr_route);
448c011ec1bSYotam Gigi 	return 0;
449c011ec1bSYotam Gigi 
450c011ec1bSYotam Gigi err_rhashtable_insert:
451f38656d0SIdo Schimmel 	mutex_lock(&mr_table->route_list_lock);
452c011ec1bSYotam Gigi 	list_del(&mr_route->node);
453f38656d0SIdo Schimmel 	mutex_unlock(&mr_table->route_list_lock);
4543e3c8dafSIdo Schimmel 	mlxsw_sp_mr_route_erase(mr_table, mr_route);
4553e3c8dafSIdo Schimmel err_mr_route_write:
456c011ec1bSYotam Gigi err_no_orig_route:
457c011ec1bSYotam Gigi err_duplicate_route:
458eb35da0cSYuval Mintz 	mlxsw_sp_mr_route_destroy(mr_table, mr_route);
459c011ec1bSYotam Gigi 	return err;
460c011ec1bSYotam Gigi }
461c011ec1bSYotam Gigi 
mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table * mr_table,struct mr_mfc * mfc)462eb35da0cSYuval Mintz void mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,
463eb35da0cSYuval Mintz 			   struct mr_mfc *mfc)
464c011ec1bSYotam Gigi {
465c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route *mr_route;
466c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route_key key;
467c011ec1bSYotam Gigi 
468eb35da0cSYuval Mintz 	mr_table->ops->key_create(mr_table, &key, mfc);
469c011ec1bSYotam Gigi 	mr_route = rhashtable_lookup_fast(&mr_table->route_ht, &key,
470c011ec1bSYotam Gigi 					  mlxsw_sp_mr_route_ht_params);
471f38656d0SIdo Schimmel 	if (mr_route) {
472f38656d0SIdo Schimmel 		mutex_lock(&mr_table->route_list_lock);
473c011ec1bSYotam Gigi 		__mlxsw_sp_mr_route_del(mr_table, mr_route);
474f38656d0SIdo Schimmel 		mutex_unlock(&mr_table->route_list_lock);
475f38656d0SIdo Schimmel 	}
476c011ec1bSYotam Gigi }
477c011ec1bSYotam Gigi 
478c011ec1bSYotam Gigi /* Should be called after the VIF struct is updated */
479c011ec1bSYotam Gigi static int
mlxsw_sp_mr_route_ivif_resolve(struct mlxsw_sp_mr_table * mr_table,struct mlxsw_sp_mr_route_vif_entry * rve)480c011ec1bSYotam Gigi mlxsw_sp_mr_route_ivif_resolve(struct mlxsw_sp_mr_table *mr_table,
481c011ec1bSYotam Gigi 			       struct mlxsw_sp_mr_route_vif_entry *rve)
482c011ec1bSYotam Gigi {
483c011ec1bSYotam Gigi 	struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;
484c011ec1bSYotam Gigi 	enum mlxsw_sp_mr_route_action route_action;
485c011ec1bSYotam Gigi 	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
486c011ec1bSYotam Gigi 	u16 irif_index;
487c011ec1bSYotam Gigi 	int err;
488c011ec1bSYotam Gigi 
489c011ec1bSYotam Gigi 	route_action = mlxsw_sp_mr_route_action(rve->mr_route);
490c011ec1bSYotam Gigi 	if (route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP)
491c011ec1bSYotam Gigi 		return 0;
492c011ec1bSYotam Gigi 
493c011ec1bSYotam Gigi 	/* rve->mr_vif->rif is guaranteed to be valid at this stage */
494c011ec1bSYotam Gigi 	irif_index = mlxsw_sp_rif_index(rve->mr_vif->rif);
495c011ec1bSYotam Gigi 	err = mr->mr_ops->route_irif_update(mlxsw_sp, rve->mr_route->route_priv,
496c011ec1bSYotam Gigi 					    irif_index);
497c011ec1bSYotam Gigi 	if (err)
498c011ec1bSYotam Gigi 		return err;
499c011ec1bSYotam Gigi 
500c011ec1bSYotam Gigi 	err = mr->mr_ops->route_action_update(mlxsw_sp,
501c011ec1bSYotam Gigi 					      rve->mr_route->route_priv,
502c011ec1bSYotam Gigi 					      route_action);
503c011ec1bSYotam Gigi 	if (err)
504c011ec1bSYotam Gigi 		/* No need to rollback here because the iRIF change only takes
505c011ec1bSYotam Gigi 		 * place after the action has been updated.
506c011ec1bSYotam Gigi 		 */
507c011ec1bSYotam Gigi 		return err;
508c011ec1bSYotam Gigi 
509c011ec1bSYotam Gigi 	rve->mr_route->route_action = route_action;
510c011ec1bSYotam Gigi 	mlxsw_sp_mr_mfc_offload_update(rve->mr_route);
511c011ec1bSYotam Gigi 	return 0;
512c011ec1bSYotam Gigi }
513c011ec1bSYotam Gigi 
514c011ec1bSYotam Gigi static void
mlxsw_sp_mr_route_ivif_unresolve(struct mlxsw_sp_mr_table * mr_table,struct mlxsw_sp_mr_route_vif_entry * rve)515c011ec1bSYotam Gigi mlxsw_sp_mr_route_ivif_unresolve(struct mlxsw_sp_mr_table *mr_table,
516c011ec1bSYotam Gigi 				 struct mlxsw_sp_mr_route_vif_entry *rve)
517c011ec1bSYotam Gigi {
518c011ec1bSYotam Gigi 	struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;
519c011ec1bSYotam Gigi 	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
520c011ec1bSYotam Gigi 
521c011ec1bSYotam Gigi 	mr->mr_ops->route_action_update(mlxsw_sp, rve->mr_route->route_priv,
522c011ec1bSYotam Gigi 					MLXSW_SP_MR_ROUTE_ACTION_TRAP);
523c011ec1bSYotam Gigi 	rve->mr_route->route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP;
524c011ec1bSYotam Gigi 	mlxsw_sp_mr_mfc_offload_update(rve->mr_route);
525c011ec1bSYotam Gigi }
526c011ec1bSYotam Gigi 
527c011ec1bSYotam Gigi /* Should be called after the RIF struct is updated */
528c011ec1bSYotam Gigi static int
mlxsw_sp_mr_route_evif_resolve(struct mlxsw_sp_mr_table * mr_table,struct mlxsw_sp_mr_route_vif_entry * rve)529c011ec1bSYotam Gigi mlxsw_sp_mr_route_evif_resolve(struct mlxsw_sp_mr_table *mr_table,
530c011ec1bSYotam Gigi 			       struct mlxsw_sp_mr_route_vif_entry *rve)
531c011ec1bSYotam Gigi {
532c011ec1bSYotam Gigi 	struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;
533c011ec1bSYotam Gigi 	enum mlxsw_sp_mr_route_action route_action;
534c011ec1bSYotam Gigi 	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
535c011ec1bSYotam Gigi 	u16 erif_index = 0;
536c011ec1bSYotam Gigi 	int err;
537c011ec1bSYotam Gigi 
538cbaf3f6aSIdo Schimmel 	/* Add the eRIF */
539cbaf3f6aSIdo Schimmel 	if (mlxsw_sp_mr_vif_valid(rve->mr_vif)) {
540cbaf3f6aSIdo Schimmel 		erif_index = mlxsw_sp_rif_index(rve->mr_vif->rif);
541cbaf3f6aSIdo Schimmel 		err = mr->mr_ops->route_erif_add(mlxsw_sp,
542cbaf3f6aSIdo Schimmel 						 rve->mr_route->route_priv,
543cbaf3f6aSIdo Schimmel 						 erif_index);
544cbaf3f6aSIdo Schimmel 		if (err)
545cbaf3f6aSIdo Schimmel 			return err;
546cbaf3f6aSIdo Schimmel 	}
547cbaf3f6aSIdo Schimmel 
548c011ec1bSYotam Gigi 	/* Update the route action, as the new eVIF can be a tunnel or a pimreg
549c011ec1bSYotam Gigi 	 * device which will require updating the action.
550c011ec1bSYotam Gigi 	 */
551c011ec1bSYotam Gigi 	route_action = mlxsw_sp_mr_route_action(rve->mr_route);
552c011ec1bSYotam Gigi 	if (route_action != rve->mr_route->route_action) {
553c011ec1bSYotam Gigi 		err = mr->mr_ops->route_action_update(mlxsw_sp,
554c011ec1bSYotam Gigi 						      rve->mr_route->route_priv,
555c011ec1bSYotam Gigi 						      route_action);
556c011ec1bSYotam Gigi 		if (err)
557cbaf3f6aSIdo Schimmel 			goto err_route_action_update;
558c011ec1bSYotam Gigi 	}
559c011ec1bSYotam Gigi 
560c011ec1bSYotam Gigi 	/* Update the minimum MTU */
561c011ec1bSYotam Gigi 	if (rve->mr_vif->dev->mtu < rve->mr_route->min_mtu) {
562c011ec1bSYotam Gigi 		rve->mr_route->min_mtu = rve->mr_vif->dev->mtu;
563c011ec1bSYotam Gigi 		err = mr->mr_ops->route_min_mtu_update(mlxsw_sp,
564c011ec1bSYotam Gigi 						       rve->mr_route->route_priv,
565c011ec1bSYotam Gigi 						       rve->mr_route->min_mtu);
566c011ec1bSYotam Gigi 		if (err)
567c011ec1bSYotam Gigi 			goto err_route_min_mtu_update;
568c011ec1bSYotam Gigi 	}
569c011ec1bSYotam Gigi 
570c011ec1bSYotam Gigi 	rve->mr_route->route_action = route_action;
571c011ec1bSYotam Gigi 	mlxsw_sp_mr_mfc_offload_update(rve->mr_route);
572c011ec1bSYotam Gigi 	return 0;
573c011ec1bSYotam Gigi 
574c011ec1bSYotam Gigi err_route_min_mtu_update:
575c011ec1bSYotam Gigi 	if (route_action != rve->mr_route->route_action)
576c011ec1bSYotam Gigi 		mr->mr_ops->route_action_update(mlxsw_sp,
577c011ec1bSYotam Gigi 						rve->mr_route->route_priv,
578c011ec1bSYotam Gigi 						rve->mr_route->route_action);
579cbaf3f6aSIdo Schimmel err_route_action_update:
580cbaf3f6aSIdo Schimmel 	if (mlxsw_sp_mr_vif_valid(rve->mr_vif))
581cbaf3f6aSIdo Schimmel 		mr->mr_ops->route_erif_del(mlxsw_sp, rve->mr_route->route_priv,
582cbaf3f6aSIdo Schimmel 					   erif_index);
583c011ec1bSYotam Gigi 	return err;
584c011ec1bSYotam Gigi }
585c011ec1bSYotam Gigi 
586c011ec1bSYotam Gigi /* Should be called before the RIF struct is updated */
587c011ec1bSYotam Gigi static void
mlxsw_sp_mr_route_evif_unresolve(struct mlxsw_sp_mr_table * mr_table,struct mlxsw_sp_mr_route_vif_entry * rve)588c011ec1bSYotam Gigi mlxsw_sp_mr_route_evif_unresolve(struct mlxsw_sp_mr_table *mr_table,
589c011ec1bSYotam Gigi 				 struct mlxsw_sp_mr_route_vif_entry *rve)
590c011ec1bSYotam Gigi {
591c011ec1bSYotam Gigi 	struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;
592c011ec1bSYotam Gigi 	enum mlxsw_sp_mr_route_action route_action;
593c011ec1bSYotam Gigi 	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
594c011ec1bSYotam Gigi 	u16 rifi;
595c011ec1bSYotam Gigi 
596c011ec1bSYotam Gigi 	/* If the unresolved RIF was not valid, no need to delete it */
597c011ec1bSYotam Gigi 	if (!mlxsw_sp_mr_vif_valid(rve->mr_vif))
598c011ec1bSYotam Gigi 		return;
599c011ec1bSYotam Gigi 
600c011ec1bSYotam Gigi 	/* Update the route action: if there is only one valid eVIF in the
601c011ec1bSYotam Gigi 	 * route, set the action to trap as the VIF deletion will lead to zero
602c011ec1bSYotam Gigi 	 * valid eVIFs. On any other case, use the mlxsw_sp_mr_route_action to
603c011ec1bSYotam Gigi 	 * determine the route action.
604c011ec1bSYotam Gigi 	 */
605c011ec1bSYotam Gigi 	if (mlxsw_sp_mr_route_valid_evifs_num(rve->mr_route) == 1)
606c011ec1bSYotam Gigi 		route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP;
607c011ec1bSYotam Gigi 	else
608c011ec1bSYotam Gigi 		route_action = mlxsw_sp_mr_route_action(rve->mr_route);
609c011ec1bSYotam Gigi 	if (route_action != rve->mr_route->route_action)
610c011ec1bSYotam Gigi 		mr->mr_ops->route_action_update(mlxsw_sp,
611c011ec1bSYotam Gigi 						rve->mr_route->route_priv,
612c011ec1bSYotam Gigi 						route_action);
613c011ec1bSYotam Gigi 
614c011ec1bSYotam Gigi 	/* Delete the erif from the route */
615c011ec1bSYotam Gigi 	rifi = mlxsw_sp_rif_index(rve->mr_vif->rif);
616c011ec1bSYotam Gigi 	mr->mr_ops->route_erif_del(mlxsw_sp, rve->mr_route->route_priv, rifi);
617c011ec1bSYotam Gigi 	rve->mr_route->route_action = route_action;
618c011ec1bSYotam Gigi 	mlxsw_sp_mr_mfc_offload_update(rve->mr_route);
619c011ec1bSYotam Gigi }
620c011ec1bSYotam Gigi 
mlxsw_sp_mr_vif_resolve(struct mlxsw_sp_mr_table * mr_table,struct net_device * dev,struct mlxsw_sp_mr_vif * mr_vif,unsigned long vif_flags,const struct mlxsw_sp_rif * rif)621c011ec1bSYotam Gigi static int mlxsw_sp_mr_vif_resolve(struct mlxsw_sp_mr_table *mr_table,
622c011ec1bSYotam Gigi 				   struct net_device *dev,
623c011ec1bSYotam Gigi 				   struct mlxsw_sp_mr_vif *mr_vif,
624c011ec1bSYotam Gigi 				   unsigned long vif_flags,
625c011ec1bSYotam Gigi 				   const struct mlxsw_sp_rif *rif)
626c011ec1bSYotam Gigi {
627c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route_vif_entry *irve, *erve;
628c011ec1bSYotam Gigi 	int err;
629c011ec1bSYotam Gigi 
630c011ec1bSYotam Gigi 	/* Update the VIF */
631c011ec1bSYotam Gigi 	mr_vif->dev = dev;
632c011ec1bSYotam Gigi 	mr_vif->rif = rif;
633c011ec1bSYotam Gigi 	mr_vif->vif_flags = vif_flags;
634c011ec1bSYotam Gigi 
635c011ec1bSYotam Gigi 	/* Update all routes where this VIF is used as an unresolved iRIF */
636c011ec1bSYotam Gigi 	list_for_each_entry(irve, &mr_vif->route_ivif_list, vif_node) {
637c011ec1bSYotam Gigi 		err = mlxsw_sp_mr_route_ivif_resolve(mr_table, irve);
638c011ec1bSYotam Gigi 		if (err)
639c011ec1bSYotam Gigi 			goto err_irif_unresolve;
640c011ec1bSYotam Gigi 	}
641c011ec1bSYotam Gigi 
642c011ec1bSYotam Gigi 	/* Update all routes where this VIF is used as an unresolved eRIF */
643c011ec1bSYotam Gigi 	list_for_each_entry(erve, &mr_vif->route_evif_list, vif_node) {
644c011ec1bSYotam Gigi 		err = mlxsw_sp_mr_route_evif_resolve(mr_table, erve);
645c011ec1bSYotam Gigi 		if (err)
646c011ec1bSYotam Gigi 			goto err_erif_unresolve;
647c011ec1bSYotam Gigi 	}
648c011ec1bSYotam Gigi 	return 0;
649c011ec1bSYotam Gigi 
650c011ec1bSYotam Gigi err_erif_unresolve:
651f6bf1bafSIdo Schimmel 	list_for_each_entry_continue_reverse(erve, &mr_vif->route_evif_list,
652c011ec1bSYotam Gigi 					     vif_node)
653c011ec1bSYotam Gigi 		mlxsw_sp_mr_route_evif_unresolve(mr_table, erve);
654c011ec1bSYotam Gigi err_irif_unresolve:
655f6bf1bafSIdo Schimmel 	list_for_each_entry_continue_reverse(irve, &mr_vif->route_ivif_list,
656c011ec1bSYotam Gigi 					     vif_node)
657c011ec1bSYotam Gigi 		mlxsw_sp_mr_route_ivif_unresolve(mr_table, irve);
658c011ec1bSYotam Gigi 	mr_vif->rif = NULL;
659c011ec1bSYotam Gigi 	return err;
660c011ec1bSYotam Gigi }
661c011ec1bSYotam Gigi 
mlxsw_sp_mr_vif_unresolve(struct mlxsw_sp_mr_table * mr_table,struct net_device * dev,struct mlxsw_sp_mr_vif * mr_vif)662c011ec1bSYotam Gigi static void mlxsw_sp_mr_vif_unresolve(struct mlxsw_sp_mr_table *mr_table,
663c011ec1bSYotam Gigi 				      struct net_device *dev,
664c011ec1bSYotam Gigi 				      struct mlxsw_sp_mr_vif *mr_vif)
665c011ec1bSYotam Gigi {
666c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route_vif_entry *rve;
667c011ec1bSYotam Gigi 
668c011ec1bSYotam Gigi 	/* Update all routes where this VIF is used as an unresolved eRIF */
669c011ec1bSYotam Gigi 	list_for_each_entry(rve, &mr_vif->route_evif_list, vif_node)
670c011ec1bSYotam Gigi 		mlxsw_sp_mr_route_evif_unresolve(mr_table, rve);
671c011ec1bSYotam Gigi 
672c011ec1bSYotam Gigi 	/* Update all routes where this VIF is used as an unresolved iRIF */
673c011ec1bSYotam Gigi 	list_for_each_entry(rve, &mr_vif->route_ivif_list, vif_node)
674c011ec1bSYotam Gigi 		mlxsw_sp_mr_route_ivif_unresolve(mr_table, rve);
675c011ec1bSYotam Gigi 
676c011ec1bSYotam Gigi 	/* Update the VIF */
677c011ec1bSYotam Gigi 	mr_vif->dev = dev;
678c011ec1bSYotam Gigi 	mr_vif->rif = NULL;
679c011ec1bSYotam Gigi }
680c011ec1bSYotam Gigi 
mlxsw_sp_mr_vif_add(struct mlxsw_sp_mr_table * mr_table,struct net_device * dev,vifi_t vif_index,unsigned long vif_flags,const struct mlxsw_sp_rif * rif)681c011ec1bSYotam Gigi int mlxsw_sp_mr_vif_add(struct mlxsw_sp_mr_table *mr_table,
682c011ec1bSYotam Gigi 			struct net_device *dev, vifi_t vif_index,
683c011ec1bSYotam Gigi 			unsigned long vif_flags, const struct mlxsw_sp_rif *rif)
684c011ec1bSYotam Gigi {
685c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_vif *mr_vif = &mr_table->vifs[vif_index];
686c011ec1bSYotam Gigi 
687c011ec1bSYotam Gigi 	if (WARN_ON(vif_index >= MAXVIFS))
688c011ec1bSYotam Gigi 		return -EINVAL;
689c011ec1bSYotam Gigi 	if (mr_vif->dev)
690c011ec1bSYotam Gigi 		return -EEXIST;
691c011ec1bSYotam Gigi 	return mlxsw_sp_mr_vif_resolve(mr_table, dev, mr_vif, vif_flags, rif);
692c011ec1bSYotam Gigi }
693c011ec1bSYotam Gigi 
mlxsw_sp_mr_vif_del(struct mlxsw_sp_mr_table * mr_table,vifi_t vif_index)694c011ec1bSYotam Gigi void mlxsw_sp_mr_vif_del(struct mlxsw_sp_mr_table *mr_table, vifi_t vif_index)
695c011ec1bSYotam Gigi {
696c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_vif *mr_vif = &mr_table->vifs[vif_index];
697c011ec1bSYotam Gigi 
698c011ec1bSYotam Gigi 	if (WARN_ON(vif_index >= MAXVIFS))
699c011ec1bSYotam Gigi 		return;
700c011ec1bSYotam Gigi 	if (WARN_ON(!mr_vif->dev))
701c011ec1bSYotam Gigi 		return;
702c011ec1bSYotam Gigi 	mlxsw_sp_mr_vif_unresolve(mr_table, NULL, mr_vif);
703c011ec1bSYotam Gigi }
704c011ec1bSYotam Gigi 
7056a30dc29SYotam Gigi static struct mlxsw_sp_mr_vif *
mlxsw_sp_mr_dev_vif_lookup(struct mlxsw_sp_mr_table * mr_table,const struct mlxsw_sp_rif * rif)706c011ec1bSYotam Gigi mlxsw_sp_mr_dev_vif_lookup(struct mlxsw_sp_mr_table *mr_table,
7075374a50fSPetr Machata 			   const struct mlxsw_sp_rif *rif)
708c011ec1bSYotam Gigi {
709c011ec1bSYotam Gigi 	vifi_t vif_index;
710c011ec1bSYotam Gigi 
711c011ec1bSYotam Gigi 	for (vif_index = 0; vif_index < MAXVIFS; vif_index++)
7125374a50fSPetr Machata 		if (mlxsw_sp_rif_dev_is(rif, mr_table->vifs[vif_index].dev))
713c011ec1bSYotam Gigi 			return &mr_table->vifs[vif_index];
714c011ec1bSYotam Gigi 	return NULL;
715c011ec1bSYotam Gigi }
716c011ec1bSYotam Gigi 
mlxsw_sp_mr_rif_add(struct mlxsw_sp_mr_table * mr_table,const struct mlxsw_sp_rif * rif)717c011ec1bSYotam Gigi int mlxsw_sp_mr_rif_add(struct mlxsw_sp_mr_table *mr_table,
718c011ec1bSYotam Gigi 			const struct mlxsw_sp_rif *rif)
719c011ec1bSYotam Gigi {
720c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_vif *mr_vif;
721c011ec1bSYotam Gigi 
7220255f748SPetr Machata 	if (!mlxsw_sp_rif_has_dev(rif))
723c011ec1bSYotam Gigi 		return 0;
724c011ec1bSYotam Gigi 
7255374a50fSPetr Machata 	mr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif);
726c011ec1bSYotam Gigi 	if (!mr_vif)
727c011ec1bSYotam Gigi 		return 0;
728c011ec1bSYotam Gigi 	return mlxsw_sp_mr_vif_resolve(mr_table, mr_vif->dev, mr_vif,
729c011ec1bSYotam Gigi 				       mr_vif->vif_flags, rif);
730c011ec1bSYotam Gigi }
731c011ec1bSYotam Gigi 
mlxsw_sp_mr_rif_del(struct mlxsw_sp_mr_table * mr_table,const struct mlxsw_sp_rif * rif)732c011ec1bSYotam Gigi void mlxsw_sp_mr_rif_del(struct mlxsw_sp_mr_table *mr_table,
733c011ec1bSYotam Gigi 			 const struct mlxsw_sp_rif *rif)
734c011ec1bSYotam Gigi {
735c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_vif *mr_vif;
736c011ec1bSYotam Gigi 
7370255f748SPetr Machata 	if (!mlxsw_sp_rif_has_dev(rif))
738c011ec1bSYotam Gigi 		return;
739c011ec1bSYotam Gigi 
7405374a50fSPetr Machata 	mr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif);
741c011ec1bSYotam Gigi 	if (!mr_vif)
742c011ec1bSYotam Gigi 		return;
743c011ec1bSYotam Gigi 	mlxsw_sp_mr_vif_unresolve(mr_table, mr_vif->dev, mr_vif);
744c011ec1bSYotam Gigi }
745c011ec1bSYotam Gigi 
mlxsw_sp_mr_rif_mtu_update(struct mlxsw_sp_mr_table * mr_table,const struct mlxsw_sp_rif * rif,int mtu)746c011ec1bSYotam Gigi void mlxsw_sp_mr_rif_mtu_update(struct mlxsw_sp_mr_table *mr_table,
747c011ec1bSYotam Gigi 				const struct mlxsw_sp_rif *rif, int mtu)
748c011ec1bSYotam Gigi {
749c011ec1bSYotam Gigi 	struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;
750c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route_vif_entry *rve;
751c011ec1bSYotam Gigi 	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
752c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_vif *mr_vif;
753c011ec1bSYotam Gigi 
7540255f748SPetr Machata 	if (!mlxsw_sp_rif_has_dev(rif))
755c011ec1bSYotam Gigi 		return;
756c011ec1bSYotam Gigi 
757c011ec1bSYotam Gigi 	/* Search for a VIF that use that RIF */
7585374a50fSPetr Machata 	mr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif);
759c011ec1bSYotam Gigi 	if (!mr_vif)
760c011ec1bSYotam Gigi 		return;
761c011ec1bSYotam Gigi 
762c011ec1bSYotam Gigi 	/* Update all the routes that uses that VIF as eVIF */
763c011ec1bSYotam Gigi 	list_for_each_entry(rve, &mr_vif->route_evif_list, vif_node) {
764c011ec1bSYotam Gigi 		if (mtu < rve->mr_route->min_mtu) {
765c011ec1bSYotam Gigi 			rve->mr_route->min_mtu = mtu;
766c011ec1bSYotam Gigi 			mr->mr_ops->route_min_mtu_update(mlxsw_sp,
767c011ec1bSYotam Gigi 							 rve->mr_route->route_priv,
768c011ec1bSYotam Gigi 							 mtu);
769c011ec1bSYotam Gigi 		}
770c011ec1bSYotam Gigi 	}
771c011ec1bSYotam Gigi }
772c011ec1bSYotam Gigi 
7734caef463SYuval Mintz /* Protocol specific functions */
7744caef463SYuval Mintz static bool
mlxsw_sp_mr_route4_validate(const struct mlxsw_sp_mr_table * mr_table,const struct mr_mfc * c)7754caef463SYuval Mintz mlxsw_sp_mr_route4_validate(const struct mlxsw_sp_mr_table *mr_table,
7764caef463SYuval Mintz 			    const struct mr_mfc *c)
7774caef463SYuval Mintz {
7784caef463SYuval Mintz 	struct mfc_cache *mfc = (struct mfc_cache *) c;
7794caef463SYuval Mintz 
7804caef463SYuval Mintz 	/* If the route is a (*,*) route, abort, as these kind of routes are
7814caef463SYuval Mintz 	 * used for proxy routes.
7824caef463SYuval Mintz 	 */
7834caef463SYuval Mintz 	if (mfc->mfc_origin == htonl(INADDR_ANY) &&
7844caef463SYuval Mintz 	    mfc->mfc_mcastgrp == htonl(INADDR_ANY)) {
7854caef463SYuval Mintz 		dev_warn(mr_table->mlxsw_sp->bus_info->dev,
7864caef463SYuval Mintz 			 "Offloading proxy routes is not supported.\n");
7874caef463SYuval Mintz 		return false;
7884caef463SYuval Mintz 	}
7894caef463SYuval Mintz 	return true;
7904caef463SYuval Mintz }
7914caef463SYuval Mintz 
mlxsw_sp_mr_route4_key(struct mlxsw_sp_mr_table * mr_table,struct mlxsw_sp_mr_route_key * key,struct mr_mfc * c)7924caef463SYuval Mintz static void mlxsw_sp_mr_route4_key(struct mlxsw_sp_mr_table *mr_table,
7934caef463SYuval Mintz 				   struct mlxsw_sp_mr_route_key *key,
7944caef463SYuval Mintz 				   struct mr_mfc *c)
7954caef463SYuval Mintz {
7964caef463SYuval Mintz 	const struct mfc_cache *mfc = (struct mfc_cache *) c;
7974caef463SYuval Mintz 	bool starg;
7984caef463SYuval Mintz 
7994caef463SYuval Mintz 	starg = (mfc->mfc_origin == htonl(INADDR_ANY));
8004caef463SYuval Mintz 
8014caef463SYuval Mintz 	memset(key, 0, sizeof(*key));
8024caef463SYuval Mintz 	key->vrid = mr_table->vr_id;
8034caef463SYuval Mintz 	key->proto = MLXSW_SP_L3_PROTO_IPV4;
8044caef463SYuval Mintz 	key->group.addr4 = mfc->mfc_mcastgrp;
8054caef463SYuval Mintz 	key->group_mask.addr4 = htonl(0xffffffff);
8064caef463SYuval Mintz 	key->source.addr4 = mfc->mfc_origin;
8074caef463SYuval Mintz 	key->source_mask.addr4 = htonl(starg ? 0 : 0xffffffff);
8084caef463SYuval Mintz }
8094caef463SYuval Mintz 
mlxsw_sp_mr_route4_starg(const struct mlxsw_sp_mr_table * mr_table,const struct mlxsw_sp_mr_route * mr_route)8104caef463SYuval Mintz static bool mlxsw_sp_mr_route4_starg(const struct mlxsw_sp_mr_table *mr_table,
8114caef463SYuval Mintz 				     const struct mlxsw_sp_mr_route *mr_route)
8124caef463SYuval Mintz {
8134caef463SYuval Mintz 	return mr_route->key.source_mask.addr4 == htonl(INADDR_ANY);
8144caef463SYuval Mintz }
8154caef463SYuval Mintz 
mlxsw_sp_mr_vif4_is_regular(const struct mlxsw_sp_mr_vif * vif)8164caef463SYuval Mintz static bool mlxsw_sp_mr_vif4_is_regular(const struct mlxsw_sp_mr_vif *vif)
8174caef463SYuval Mintz {
8184caef463SYuval Mintz 	return !(vif->vif_flags & (VIFF_TUNNEL | VIFF_REGISTER));
8194caef463SYuval Mintz }
8204caef463SYuval Mintz 
8216981e104SYuval Mintz static bool
mlxsw_sp_mr_route6_validate(const struct mlxsw_sp_mr_table * mr_table,const struct mr_mfc * c)8226981e104SYuval Mintz mlxsw_sp_mr_route6_validate(const struct mlxsw_sp_mr_table *mr_table,
8236981e104SYuval Mintz 			    const struct mr_mfc *c)
8246981e104SYuval Mintz {
8256981e104SYuval Mintz 	struct mfc6_cache *mfc = (struct mfc6_cache *) c;
8266981e104SYuval Mintz 
8276981e104SYuval Mintz 	/* If the route is a (*,*) route, abort, as these kind of routes are
8286981e104SYuval Mintz 	 * used for proxy routes.
8296981e104SYuval Mintz 	 */
8306981e104SYuval Mintz 	if (ipv6_addr_any(&mfc->mf6c_origin) &&
8316981e104SYuval Mintz 	    ipv6_addr_any(&mfc->mf6c_mcastgrp)) {
8326981e104SYuval Mintz 		dev_warn(mr_table->mlxsw_sp->bus_info->dev,
8336981e104SYuval Mintz 			 "Offloading proxy routes is not supported.\n");
8346981e104SYuval Mintz 		return false;
8356981e104SYuval Mintz 	}
8366981e104SYuval Mintz 	return true;
8376981e104SYuval Mintz }
8386981e104SYuval Mintz 
mlxsw_sp_mr_route6_key(struct mlxsw_sp_mr_table * mr_table,struct mlxsw_sp_mr_route_key * key,struct mr_mfc * c)8396981e104SYuval Mintz static void mlxsw_sp_mr_route6_key(struct mlxsw_sp_mr_table *mr_table,
8406981e104SYuval Mintz 				   struct mlxsw_sp_mr_route_key *key,
8416981e104SYuval Mintz 				   struct mr_mfc *c)
8426981e104SYuval Mintz {
8436981e104SYuval Mintz 	const struct mfc6_cache *mfc = (struct mfc6_cache *) c;
8446981e104SYuval Mintz 
8456981e104SYuval Mintz 	memset(key, 0, sizeof(*key));
8466981e104SYuval Mintz 	key->vrid = mr_table->vr_id;
8476981e104SYuval Mintz 	key->proto = MLXSW_SP_L3_PROTO_IPV6;
8486981e104SYuval Mintz 	key->group.addr6 = mfc->mf6c_mcastgrp;
8496981e104SYuval Mintz 	memset(&key->group_mask.addr6, 0xff, sizeof(key->group_mask.addr6));
8506981e104SYuval Mintz 	key->source.addr6 = mfc->mf6c_origin;
8516981e104SYuval Mintz 	if (!ipv6_addr_any(&mfc->mf6c_origin))
8526981e104SYuval Mintz 		memset(&key->source_mask.addr6, 0xff,
8536981e104SYuval Mintz 		       sizeof(key->source_mask.addr6));
8546981e104SYuval Mintz }
8556981e104SYuval Mintz 
mlxsw_sp_mr_route6_starg(const struct mlxsw_sp_mr_table * mr_table,const struct mlxsw_sp_mr_route * mr_route)8566981e104SYuval Mintz static bool mlxsw_sp_mr_route6_starg(const struct mlxsw_sp_mr_table *mr_table,
8576981e104SYuval Mintz 				     const struct mlxsw_sp_mr_route *mr_route)
8586981e104SYuval Mintz {
8596981e104SYuval Mintz 	return ipv6_addr_any(&mr_route->key.source_mask.addr6);
8606981e104SYuval Mintz }
8616981e104SYuval Mintz 
mlxsw_sp_mr_vif6_is_regular(const struct mlxsw_sp_mr_vif * vif)8626981e104SYuval Mintz static bool mlxsw_sp_mr_vif6_is_regular(const struct mlxsw_sp_mr_vif *vif)
8636981e104SYuval Mintz {
8646981e104SYuval Mintz 	return !(vif->vif_flags & MIFF_REGISTER);
8656981e104SYuval Mintz }
8666981e104SYuval Mintz 
8674caef463SYuval Mintz static struct
8684caef463SYuval Mintz mlxsw_sp_mr_vif_ops mlxsw_sp_mr_vif_ops_arr[] = {
8694caef463SYuval Mintz 	{
8704caef463SYuval Mintz 		.is_regular = mlxsw_sp_mr_vif4_is_regular,
8714caef463SYuval Mintz 	},
8726981e104SYuval Mintz 	{
8736981e104SYuval Mintz 		.is_regular = mlxsw_sp_mr_vif6_is_regular,
8746981e104SYuval Mintz 	},
8754caef463SYuval Mintz };
8764caef463SYuval Mintz 
8774caef463SYuval Mintz static struct
8784caef463SYuval Mintz mlxsw_sp_mr_table_ops mlxsw_sp_mr_table_ops_arr[] = {
8794caef463SYuval Mintz 	{
8804caef463SYuval Mintz 		.is_route_valid = mlxsw_sp_mr_route4_validate,
8814caef463SYuval Mintz 		.key_create = mlxsw_sp_mr_route4_key,
8824caef463SYuval Mintz 		.is_route_starg = mlxsw_sp_mr_route4_starg,
8834caef463SYuval Mintz 	},
8846981e104SYuval Mintz 	{
8856981e104SYuval Mintz 		.is_route_valid = mlxsw_sp_mr_route6_validate,
8866981e104SYuval Mintz 		.key_create = mlxsw_sp_mr_route6_key,
8876981e104SYuval Mintz 		.is_route_starg = mlxsw_sp_mr_route6_starg,
8886981e104SYuval Mintz 	},
8896981e104SYuval Mintz 
8904caef463SYuval Mintz };
8914caef463SYuval Mintz 
mlxsw_sp_mr_table_create(struct mlxsw_sp * mlxsw_sp,u32 vr_id,enum mlxsw_sp_l3proto proto)892c011ec1bSYotam Gigi struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,
893c011ec1bSYotam Gigi 						   u32 vr_id,
894c011ec1bSYotam Gigi 						   enum mlxsw_sp_l3proto proto)
895c011ec1bSYotam Gigi {
896c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route_params catchall_route_params = {
897c011ec1bSYotam Gigi 		.prio = MLXSW_SP_MR_ROUTE_PRIO_CATCHALL,
898c011ec1bSYotam Gigi 		.key = {
899c011ec1bSYotam Gigi 			.vrid = vr_id,
900a3b66866SYuval Mintz 			.proto = proto,
901c011ec1bSYotam Gigi 		},
902c011ec1bSYotam Gigi 		.value = {
903c011ec1bSYotam Gigi 			.route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP,
904c011ec1bSYotam Gigi 		}
905c011ec1bSYotam Gigi 	};
906c011ec1bSYotam Gigi 	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
907c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_table *mr_table;
908c011ec1bSYotam Gigi 	int err;
909c011ec1bSYotam Gigi 	int i;
910c011ec1bSYotam Gigi 
911c011ec1bSYotam Gigi 	mr_table = kzalloc(sizeof(*mr_table) + mr->mr_ops->route_priv_size,
912c011ec1bSYotam Gigi 			   GFP_KERNEL);
913c011ec1bSYotam Gigi 	if (!mr_table)
914c011ec1bSYotam Gigi 		return ERR_PTR(-ENOMEM);
915c011ec1bSYotam Gigi 
916c011ec1bSYotam Gigi 	mr_table->vr_id = vr_id;
917c011ec1bSYotam Gigi 	mr_table->mlxsw_sp = mlxsw_sp;
918c011ec1bSYotam Gigi 	mr_table->proto = proto;
9194caef463SYuval Mintz 	mr_table->ops = &mlxsw_sp_mr_table_ops_arr[proto];
920c011ec1bSYotam Gigi 	INIT_LIST_HEAD(&mr_table->route_list);
921f38656d0SIdo Schimmel 	mutex_init(&mr_table->route_list_lock);
922c011ec1bSYotam Gigi 
923c011ec1bSYotam Gigi 	err = rhashtable_init(&mr_table->route_ht,
924c011ec1bSYotam Gigi 			      &mlxsw_sp_mr_route_ht_params);
925c011ec1bSYotam Gigi 	if (err)
926c011ec1bSYotam Gigi 		goto err_route_rhashtable_init;
927c011ec1bSYotam Gigi 
928c011ec1bSYotam Gigi 	for (i = 0; i < MAXVIFS; i++) {
929c011ec1bSYotam Gigi 		INIT_LIST_HEAD(&mr_table->vifs[i].route_evif_list);
930c011ec1bSYotam Gigi 		INIT_LIST_HEAD(&mr_table->vifs[i].route_ivif_list);
9314caef463SYuval Mintz 		mr_table->vifs[i].ops = &mlxsw_sp_mr_vif_ops_arr[proto];
932c011ec1bSYotam Gigi 	}
933c011ec1bSYotam Gigi 
934c011ec1bSYotam Gigi 	err = mr->mr_ops->route_create(mlxsw_sp, mr->priv,
935c011ec1bSYotam Gigi 				       mr_table->catchall_route_priv,
936c011ec1bSYotam Gigi 				       &catchall_route_params);
937c011ec1bSYotam Gigi 	if (err)
938c011ec1bSYotam Gigi 		goto err_ops_route_create;
939c366de85SIdo Schimmel 	mutex_lock(&mr->table_list_lock);
940c011ec1bSYotam Gigi 	list_add_tail(&mr_table->node, &mr->table_list);
941c366de85SIdo Schimmel 	mutex_unlock(&mr->table_list_lock);
942c011ec1bSYotam Gigi 	return mr_table;
943c011ec1bSYotam Gigi 
944c011ec1bSYotam Gigi err_ops_route_create:
945c011ec1bSYotam Gigi 	rhashtable_destroy(&mr_table->route_ht);
946c011ec1bSYotam Gigi err_route_rhashtable_init:
947f38656d0SIdo Schimmel 	mutex_destroy(&mr_table->route_list_lock);
948c011ec1bSYotam Gigi 	kfree(mr_table);
949c011ec1bSYotam Gigi 	return ERR_PTR(err);
950c011ec1bSYotam Gigi }
951c011ec1bSYotam Gigi 
mlxsw_sp_mr_table_destroy(struct mlxsw_sp_mr_table * mr_table)952c011ec1bSYotam Gigi void mlxsw_sp_mr_table_destroy(struct mlxsw_sp_mr_table *mr_table)
953c011ec1bSYotam Gigi {
954c011ec1bSYotam Gigi 	struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;
955c011ec1bSYotam Gigi 	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
956c011ec1bSYotam Gigi 
957c011ec1bSYotam Gigi 	WARN_ON(!mlxsw_sp_mr_table_empty(mr_table));
958c366de85SIdo Schimmel 	mutex_lock(&mr->table_list_lock);
959c011ec1bSYotam Gigi 	list_del(&mr_table->node);
960c366de85SIdo Schimmel 	mutex_unlock(&mr->table_list_lock);
961c011ec1bSYotam Gigi 	mr->mr_ops->route_destroy(mlxsw_sp, mr->priv,
962c011ec1bSYotam Gigi 				  &mr_table->catchall_route_priv);
963c011ec1bSYotam Gigi 	rhashtable_destroy(&mr_table->route_ht);
964f38656d0SIdo Schimmel 	mutex_destroy(&mr_table->route_list_lock);
965c011ec1bSYotam Gigi 	kfree(mr_table);
966c011ec1bSYotam Gigi }
967c011ec1bSYotam Gigi 
mlxsw_sp_mr_table_flush(struct mlxsw_sp_mr_table * mr_table)968c011ec1bSYotam Gigi void mlxsw_sp_mr_table_flush(struct mlxsw_sp_mr_table *mr_table)
969c011ec1bSYotam Gigi {
970c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route *mr_route, *tmp;
971c011ec1bSYotam Gigi 	int i;
972c011ec1bSYotam Gigi 
973f38656d0SIdo Schimmel 	mutex_lock(&mr_table->route_list_lock);
974c011ec1bSYotam Gigi 	list_for_each_entry_safe(mr_route, tmp, &mr_table->route_list, node)
975c011ec1bSYotam Gigi 		__mlxsw_sp_mr_route_del(mr_table, mr_route);
976f38656d0SIdo Schimmel 	mutex_unlock(&mr_table->route_list_lock);
977c011ec1bSYotam Gigi 
978c011ec1bSYotam Gigi 	for (i = 0; i < MAXVIFS; i++) {
979c011ec1bSYotam Gigi 		mr_table->vifs[i].dev = NULL;
980c011ec1bSYotam Gigi 		mr_table->vifs[i].rif = NULL;
981c011ec1bSYotam Gigi 	}
982c011ec1bSYotam Gigi }
983c011ec1bSYotam Gigi 
mlxsw_sp_mr_table_empty(const struct mlxsw_sp_mr_table * mr_table)984c011ec1bSYotam Gigi bool mlxsw_sp_mr_table_empty(const struct mlxsw_sp_mr_table *mr_table)
985c011ec1bSYotam Gigi {
986c011ec1bSYotam Gigi 	int i;
987c011ec1bSYotam Gigi 
988c011ec1bSYotam Gigi 	for (i = 0; i < MAXVIFS; i++)
989c011ec1bSYotam Gigi 		if (mr_table->vifs[i].dev)
990c011ec1bSYotam Gigi 			return false;
991c011ec1bSYotam Gigi 	return list_empty(&mr_table->route_list);
992c011ec1bSYotam Gigi }
993c011ec1bSYotam Gigi 
mlxsw_sp_mr_route_stats_update(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mr_route * mr_route)994c011ec1bSYotam Gigi static void mlxsw_sp_mr_route_stats_update(struct mlxsw_sp *mlxsw_sp,
995c011ec1bSYotam Gigi 					   struct mlxsw_sp_mr_route *mr_route)
996c011ec1bSYotam Gigi {
997c011ec1bSYotam Gigi 	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
998c011ec1bSYotam Gigi 	u64 packets, bytes;
999c011ec1bSYotam Gigi 
1000c011ec1bSYotam Gigi 	if (mr_route->route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP)
1001c011ec1bSYotam Gigi 		return;
1002c011ec1bSYotam Gigi 
1003c011ec1bSYotam Gigi 	mr->mr_ops->route_stats(mlxsw_sp, mr_route->route_priv, &packets,
1004c011ec1bSYotam Gigi 				&bytes);
1005c011ec1bSYotam Gigi 
1006*5960f4d8SEric Dumazet 	if (atomic_long_read(&mr_route->mfc->mfc_un.res.pkt) != packets)
1007*5960f4d8SEric Dumazet 		WRITE_ONCE(mr_route->mfc->mfc_un.res.lastuse, jiffies);
1008*5960f4d8SEric Dumazet 	atomic_long_set(&mr_route->mfc->mfc_un.res.pkt, packets);
1009*5960f4d8SEric Dumazet 	atomic_long_set(&mr_route->mfc->mfc_un.res.bytes, bytes);
1010c011ec1bSYotam Gigi }
1011c011ec1bSYotam Gigi 
mlxsw_sp_mr_stats_update(struct work_struct * work)1012c011ec1bSYotam Gigi static void mlxsw_sp_mr_stats_update(struct work_struct *work)
1013c011ec1bSYotam Gigi {
1014c011ec1bSYotam Gigi 	struct mlxsw_sp_mr *mr = container_of(work, struct mlxsw_sp_mr,
1015c011ec1bSYotam Gigi 					      stats_update_dw.work);
1016c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_table *mr_table;
1017c011ec1bSYotam Gigi 	struct mlxsw_sp_mr_route *mr_route;
1018c011ec1bSYotam Gigi 	unsigned long interval;
1019c011ec1bSYotam Gigi 
1020c366de85SIdo Schimmel 	mutex_lock(&mr->table_list_lock);
1021f38656d0SIdo Schimmel 	list_for_each_entry(mr_table, &mr->table_list, node) {
1022f38656d0SIdo Schimmel 		mutex_lock(&mr_table->route_list_lock);
1023c011ec1bSYotam Gigi 		list_for_each_entry(mr_route, &mr_table->route_list, node)
1024c011ec1bSYotam Gigi 			mlxsw_sp_mr_route_stats_update(mr_table->mlxsw_sp,
1025c011ec1bSYotam Gigi 						       mr_route);
1026f38656d0SIdo Schimmel 		mutex_unlock(&mr_table->route_list_lock);
1027f38656d0SIdo Schimmel 	}
1028c366de85SIdo Schimmel 	mutex_unlock(&mr->table_list_lock);
1029c011ec1bSYotam Gigi 
1030c011ec1bSYotam Gigi 	interval = msecs_to_jiffies(MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL);
1031c011ec1bSYotam Gigi 	mlxsw_core_schedule_dw(&mr->stats_update_dw, interval);
1032c011ec1bSYotam Gigi }
1033c011ec1bSYotam Gigi 
mlxsw_sp_mr_init(struct mlxsw_sp * mlxsw_sp,const struct mlxsw_sp_mr_ops * mr_ops)1034c011ec1bSYotam Gigi int mlxsw_sp_mr_init(struct mlxsw_sp *mlxsw_sp,
1035c011ec1bSYotam Gigi 		     const struct mlxsw_sp_mr_ops *mr_ops)
1036c011ec1bSYotam Gigi {
1037c011ec1bSYotam Gigi 	struct mlxsw_sp_mr *mr;
1038c011ec1bSYotam Gigi 	unsigned long interval;
1039c011ec1bSYotam Gigi 	int err;
1040c011ec1bSYotam Gigi 
1041c011ec1bSYotam Gigi 	mr = kzalloc(sizeof(*mr) + mr_ops->priv_size, GFP_KERNEL);
1042c011ec1bSYotam Gigi 	if (!mr)
1043c011ec1bSYotam Gigi 		return -ENOMEM;
1044c011ec1bSYotam Gigi 	mr->mr_ops = mr_ops;
1045c011ec1bSYotam Gigi 	mlxsw_sp->mr = mr;
1046c011ec1bSYotam Gigi 	INIT_LIST_HEAD(&mr->table_list);
1047c366de85SIdo Schimmel 	mutex_init(&mr->table_list_lock);
1048c011ec1bSYotam Gigi 
1049c011ec1bSYotam Gigi 	err = mr_ops->init(mlxsw_sp, mr->priv);
1050c011ec1bSYotam Gigi 	if (err)
1051c011ec1bSYotam Gigi 		goto err;
1052c011ec1bSYotam Gigi 
1053c011ec1bSYotam Gigi 	/* Create the delayed work for counter updates */
1054c011ec1bSYotam Gigi 	INIT_DELAYED_WORK(&mr->stats_update_dw, mlxsw_sp_mr_stats_update);
1055c011ec1bSYotam Gigi 	interval = msecs_to_jiffies(MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL);
1056c011ec1bSYotam Gigi 	mlxsw_core_schedule_dw(&mr->stats_update_dw, interval);
1057c011ec1bSYotam Gigi 	return 0;
1058c011ec1bSYotam Gigi err:
1059c366de85SIdo Schimmel 	mutex_destroy(&mr->table_list_lock);
1060c011ec1bSYotam Gigi 	kfree(mr);
1061c011ec1bSYotam Gigi 	return err;
1062c011ec1bSYotam Gigi }
1063c011ec1bSYotam Gigi 
mlxsw_sp_mr_fini(struct mlxsw_sp * mlxsw_sp)1064c011ec1bSYotam Gigi void mlxsw_sp_mr_fini(struct mlxsw_sp *mlxsw_sp)
1065c011ec1bSYotam Gigi {
1066c011ec1bSYotam Gigi 	struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
1067c011ec1bSYotam Gigi 
1068c011ec1bSYotam Gigi 	cancel_delayed_work_sync(&mr->stats_update_dw);
10698fae4392SJiri Pirko 	mr->mr_ops->fini(mlxsw_sp, mr->priv);
1070c366de85SIdo Schimmel 	mutex_destroy(&mr->table_list_lock);
1071c011ec1bSYotam Gigi 	kfree(mr);
1072c011ec1bSYotam Gigi }
1073