xref: /openbmc/linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr_tcam.c (revision ead5d1f4d877e92c051e1a1ade623d0d30e71619)
19948a064SJiri Pirko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
29948a064SJiri Pirko /* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
30e14c777SYotam Gigi 
40e14c777SYotam Gigi #include <linux/kernel.h>
50e14c777SYotam Gigi #include <linux/list.h>
60e14c777SYotam Gigi #include <linux/netdevice.h>
70e14c777SYotam Gigi 
8ea00aa3aSYotam Gigi #include "spectrum_mr_tcam.h"
90e14c777SYotam Gigi #include "reg.h"
100e14c777SYotam Gigi #include "spectrum.h"
110e14c777SYotam Gigi #include "core_acl_flex_actions.h"
120e14c777SYotam Gigi #include "spectrum_mr.h"
130e14c777SYotam Gigi 
140e14c777SYotam Gigi struct mlxsw_sp_mr_tcam {
158fae4392SJiri Pirko 	void *priv;
160e14c777SYotam Gigi };
170e14c777SYotam Gigi 
180e14c777SYotam Gigi /* This struct maps to one RIGR2 register entry */
190e14c777SYotam Gigi struct mlxsw_sp_mr_erif_sublist {
200e14c777SYotam Gigi 	struct list_head list;
210e14c777SYotam Gigi 	u32 rigr2_kvdl_index;
220e14c777SYotam Gigi 	int num_erifs;
230e14c777SYotam Gigi 	u16 erif_indices[MLXSW_REG_RIGR2_MAX_ERIFS];
240e14c777SYotam Gigi 	bool synced;
250e14c777SYotam Gigi };
260e14c777SYotam Gigi 
270e14c777SYotam Gigi struct mlxsw_sp_mr_tcam_erif_list {
280e14c777SYotam Gigi 	struct list_head erif_sublists;
290e14c777SYotam Gigi 	u32 kvdl_index;
300e14c777SYotam Gigi };
310e14c777SYotam Gigi 
320e14c777SYotam Gigi static bool
mlxsw_sp_mr_erif_sublist_full(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mr_erif_sublist * erif_sublist)330e14c777SYotam Gigi mlxsw_sp_mr_erif_sublist_full(struct mlxsw_sp *mlxsw_sp,
340e14c777SYotam Gigi 			      struct mlxsw_sp_mr_erif_sublist *erif_sublist)
350e14c777SYotam Gigi {
360e14c777SYotam Gigi 	int erif_list_entries = MLXSW_CORE_RES_GET(mlxsw_sp->core,
370e14c777SYotam Gigi 						   MC_ERIF_LIST_ENTRIES);
380e14c777SYotam Gigi 
390e14c777SYotam Gigi 	return erif_sublist->num_erifs == erif_list_entries;
400e14c777SYotam Gigi }
410e14c777SYotam Gigi 
420e14c777SYotam Gigi static void
mlxsw_sp_mr_erif_list_init(struct mlxsw_sp_mr_tcam_erif_list * erif_list)430e14c777SYotam Gigi mlxsw_sp_mr_erif_list_init(struct mlxsw_sp_mr_tcam_erif_list *erif_list)
440e14c777SYotam Gigi {
450e14c777SYotam Gigi 	INIT_LIST_HEAD(&erif_list->erif_sublists);
460e14c777SYotam Gigi }
470e14c777SYotam Gigi 
480e14c777SYotam Gigi static struct mlxsw_sp_mr_erif_sublist *
mlxsw_sp_mr_erif_sublist_create(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mr_tcam_erif_list * erif_list)490e14c777SYotam Gigi mlxsw_sp_mr_erif_sublist_create(struct mlxsw_sp *mlxsw_sp,
500e14c777SYotam Gigi 				struct mlxsw_sp_mr_tcam_erif_list *erif_list)
510e14c777SYotam Gigi {
520e14c777SYotam Gigi 	struct mlxsw_sp_mr_erif_sublist *erif_sublist;
530e14c777SYotam Gigi 	int err;
540e14c777SYotam Gigi 
550e14c777SYotam Gigi 	erif_sublist = kzalloc(sizeof(*erif_sublist), GFP_KERNEL);
560e14c777SYotam Gigi 	if (!erif_sublist)
570e14c777SYotam Gigi 		return ERR_PTR(-ENOMEM);
584b6b1869SJiri Pirko 	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_MCRIGR,
594b6b1869SJiri Pirko 				  1, &erif_sublist->rigr2_kvdl_index);
600e14c777SYotam Gigi 	if (err) {
610e14c777SYotam Gigi 		kfree(erif_sublist);
620e14c777SYotam Gigi 		return ERR_PTR(err);
630e14c777SYotam Gigi 	}
640e14c777SYotam Gigi 
650e14c777SYotam Gigi 	list_add_tail(&erif_sublist->list, &erif_list->erif_sublists);
660e14c777SYotam Gigi 	return erif_sublist;
670e14c777SYotam Gigi }
680e14c777SYotam Gigi 
690e14c777SYotam Gigi static void
mlxsw_sp_mr_erif_sublist_destroy(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mr_erif_sublist * erif_sublist)700e14c777SYotam Gigi mlxsw_sp_mr_erif_sublist_destroy(struct mlxsw_sp *mlxsw_sp,
710e14c777SYotam Gigi 				 struct mlxsw_sp_mr_erif_sublist *erif_sublist)
720e14c777SYotam Gigi {
730e14c777SYotam Gigi 	list_del(&erif_sublist->list);
744b6b1869SJiri Pirko 	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_MCRIGR,
750304c005SJiri Pirko 			   1, erif_sublist->rigr2_kvdl_index);
760e14c777SYotam Gigi 	kfree(erif_sublist);
770e14c777SYotam Gigi }
780e14c777SYotam Gigi 
790e14c777SYotam Gigi static int
mlxsw_sp_mr_erif_list_add(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mr_tcam_erif_list * erif_list,u16 erif_index)800e14c777SYotam Gigi mlxsw_sp_mr_erif_list_add(struct mlxsw_sp *mlxsw_sp,
810e14c777SYotam Gigi 			  struct mlxsw_sp_mr_tcam_erif_list *erif_list,
820e14c777SYotam Gigi 			  u16 erif_index)
830e14c777SYotam Gigi {
840e14c777SYotam Gigi 	struct mlxsw_sp_mr_erif_sublist *sublist;
850e14c777SYotam Gigi 
860e14c777SYotam Gigi 	/* If either there is no erif_entry or the last one is full, allocate a
870e14c777SYotam Gigi 	 * new one.
880e14c777SYotam Gigi 	 */
890e14c777SYotam Gigi 	if (list_empty(&erif_list->erif_sublists)) {
900e14c777SYotam Gigi 		sublist = mlxsw_sp_mr_erif_sublist_create(mlxsw_sp, erif_list);
910e14c777SYotam Gigi 		if (IS_ERR(sublist))
920e14c777SYotam Gigi 			return PTR_ERR(sublist);
930e14c777SYotam Gigi 		erif_list->kvdl_index = sublist->rigr2_kvdl_index;
940e14c777SYotam Gigi 	} else {
950e14c777SYotam Gigi 		sublist = list_last_entry(&erif_list->erif_sublists,
960e14c777SYotam Gigi 					  struct mlxsw_sp_mr_erif_sublist,
970e14c777SYotam Gigi 					  list);
980e14c777SYotam Gigi 		sublist->synced = false;
990e14c777SYotam Gigi 		if (mlxsw_sp_mr_erif_sublist_full(mlxsw_sp, sublist)) {
1000e14c777SYotam Gigi 			sublist = mlxsw_sp_mr_erif_sublist_create(mlxsw_sp,
1010e14c777SYotam Gigi 								  erif_list);
1020e14c777SYotam Gigi 			if (IS_ERR(sublist))
1030e14c777SYotam Gigi 				return PTR_ERR(sublist);
1040e14c777SYotam Gigi 		}
1050e14c777SYotam Gigi 	}
1060e14c777SYotam Gigi 
1070e14c777SYotam Gigi 	/* Add the eRIF to the last entry's last index */
1080e14c777SYotam Gigi 	sublist->erif_indices[sublist->num_erifs++] = erif_index;
1090e14c777SYotam Gigi 	return 0;
1100e14c777SYotam Gigi }
1110e14c777SYotam Gigi 
1120e14c777SYotam Gigi static void
mlxsw_sp_mr_erif_list_flush(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mr_tcam_erif_list * erif_list)1130e14c777SYotam Gigi mlxsw_sp_mr_erif_list_flush(struct mlxsw_sp *mlxsw_sp,
1140e14c777SYotam Gigi 			    struct mlxsw_sp_mr_tcam_erif_list *erif_list)
1150e14c777SYotam Gigi {
1160e14c777SYotam Gigi 	struct mlxsw_sp_mr_erif_sublist *erif_sublist, *tmp;
1170e14c777SYotam Gigi 
1180e14c777SYotam Gigi 	list_for_each_entry_safe(erif_sublist, tmp, &erif_list->erif_sublists,
1190e14c777SYotam Gigi 				 list)
1200e14c777SYotam Gigi 		mlxsw_sp_mr_erif_sublist_destroy(mlxsw_sp, erif_sublist);
1210e14c777SYotam Gigi }
1220e14c777SYotam Gigi 
1230e14c777SYotam Gigi static int
mlxsw_sp_mr_erif_list_commit(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mr_tcam_erif_list * erif_list)1240e14c777SYotam Gigi mlxsw_sp_mr_erif_list_commit(struct mlxsw_sp *mlxsw_sp,
1250e14c777SYotam Gigi 			     struct mlxsw_sp_mr_tcam_erif_list *erif_list)
1260e14c777SYotam Gigi {
1270e14c777SYotam Gigi 	struct mlxsw_sp_mr_erif_sublist *curr_sublist;
1280e14c777SYotam Gigi 	char rigr2_pl[MLXSW_REG_RIGR2_LEN];
1290e14c777SYotam Gigi 	int err;
1300e14c777SYotam Gigi 	int i;
1310e14c777SYotam Gigi 
1320e14c777SYotam Gigi 	list_for_each_entry(curr_sublist, &erif_list->erif_sublists, list) {
1330e14c777SYotam Gigi 		if (curr_sublist->synced)
1340e14c777SYotam Gigi 			continue;
1350e14c777SYotam Gigi 
1360e14c777SYotam Gigi 		/* If the sublist is not the last one, pack the next index */
1370e14c777SYotam Gigi 		if (list_is_last(&curr_sublist->list,
1380e14c777SYotam Gigi 				 &erif_list->erif_sublists)) {
1390e14c777SYotam Gigi 			mlxsw_reg_rigr2_pack(rigr2_pl,
1400e14c777SYotam Gigi 					     curr_sublist->rigr2_kvdl_index,
1410e14c777SYotam Gigi 					     false, 0);
1420e14c777SYotam Gigi 		} else {
1430e14c777SYotam Gigi 			struct mlxsw_sp_mr_erif_sublist *next_sublist;
1440e14c777SYotam Gigi 
1450e14c777SYotam Gigi 			next_sublist = list_next_entry(curr_sublist, list);
1460e14c777SYotam Gigi 			mlxsw_reg_rigr2_pack(rigr2_pl,
1470e14c777SYotam Gigi 					     curr_sublist->rigr2_kvdl_index,
1480e14c777SYotam Gigi 					     true,
1490e14c777SYotam Gigi 					     next_sublist->rigr2_kvdl_index);
1500e14c777SYotam Gigi 		}
1510e14c777SYotam Gigi 
1520e14c777SYotam Gigi 		/* Pack all the erifs */
1530e14c777SYotam Gigi 		for (i = 0; i < curr_sublist->num_erifs; i++) {
1540e14c777SYotam Gigi 			u16 erif_index = curr_sublist->erif_indices[i];
1550e14c777SYotam Gigi 
1560e14c777SYotam Gigi 			mlxsw_reg_rigr2_erif_entry_pack(rigr2_pl, i, true,
1570e14c777SYotam Gigi 							erif_index);
1580e14c777SYotam Gigi 		}
1590e14c777SYotam Gigi 
1600e14c777SYotam Gigi 		/* Write the entry */
1610e14c777SYotam Gigi 		err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rigr2),
1620e14c777SYotam Gigi 				      rigr2_pl);
1630e14c777SYotam Gigi 		if (err)
1640e14c777SYotam Gigi 			/* No need of a rollback here because this
1650e14c777SYotam Gigi 			 * hardware entry should not be pointed yet.
1660e14c777SYotam Gigi 			 */
1670e14c777SYotam Gigi 			return err;
1680e14c777SYotam Gigi 		curr_sublist->synced = true;
1690e14c777SYotam Gigi 	}
1700e14c777SYotam Gigi 	return 0;
1710e14c777SYotam Gigi }
1720e14c777SYotam Gigi 
mlxsw_sp_mr_erif_list_move(struct mlxsw_sp_mr_tcam_erif_list * to,struct mlxsw_sp_mr_tcam_erif_list * from)1730e14c777SYotam Gigi static void mlxsw_sp_mr_erif_list_move(struct mlxsw_sp_mr_tcam_erif_list *to,
1740e14c777SYotam Gigi 				       struct mlxsw_sp_mr_tcam_erif_list *from)
1750e14c777SYotam Gigi {
1760e14c777SYotam Gigi 	list_splice(&from->erif_sublists, &to->erif_sublists);
1770e14c777SYotam Gigi 	to->kvdl_index = from->kvdl_index;
1780e14c777SYotam Gigi }
1790e14c777SYotam Gigi 
1800e14c777SYotam Gigi struct mlxsw_sp_mr_tcam_route {
1810e14c777SYotam Gigi 	struct mlxsw_sp_mr_tcam_erif_list erif_list;
1820e14c777SYotam Gigi 	struct mlxsw_afa_block *afa_block;
1830e14c777SYotam Gigi 	u32 counter_index;
1840e14c777SYotam Gigi 	enum mlxsw_sp_mr_route_action action;
1850e14c777SYotam Gigi 	struct mlxsw_sp_mr_route_key key;
1860e14c777SYotam Gigi 	u16 irif_index;
1870e14c777SYotam Gigi 	u16 min_mtu;
1888fae4392SJiri Pirko 	void *priv;
1890e14c777SYotam Gigi };
1900e14c777SYotam Gigi 
1910e14c777SYotam Gigi static struct mlxsw_afa_block *
mlxsw_sp_mr_tcam_afa_block_create(struct mlxsw_sp * mlxsw_sp,enum mlxsw_sp_mr_route_action route_action,u16 irif_index,u32 counter_index,u16 min_mtu,struct mlxsw_sp_mr_tcam_erif_list * erif_list)1920e14c777SYotam Gigi mlxsw_sp_mr_tcam_afa_block_create(struct mlxsw_sp *mlxsw_sp,
1930e14c777SYotam Gigi 				  enum mlxsw_sp_mr_route_action route_action,
1940e14c777SYotam Gigi 				  u16 irif_index, u32 counter_index,
1950e14c777SYotam Gigi 				  u16 min_mtu,
1960e14c777SYotam Gigi 				  struct mlxsw_sp_mr_tcam_erif_list *erif_list)
1970e14c777SYotam Gigi {
1980e14c777SYotam Gigi 	struct mlxsw_afa_block *afa_block;
1990e14c777SYotam Gigi 	int err;
2000e14c777SYotam Gigi 
2010e14c777SYotam Gigi 	afa_block = mlxsw_afa_block_create(mlxsw_sp->afa);
202*c391eb83SDan Carpenter 	if (IS_ERR(afa_block))
203*c391eb83SDan Carpenter 		return afa_block;
2040e14c777SYotam Gigi 
205c18c1e18SJiri Pirko 	err = mlxsw_afa_block_append_allocated_counter(afa_block,
206c18c1e18SJiri Pirko 						       counter_index);
2070e14c777SYotam Gigi 	if (err)
2080e14c777SYotam Gigi 		goto err;
2090e14c777SYotam Gigi 
2100e14c777SYotam Gigi 	switch (route_action) {
2110e14c777SYotam Gigi 	case MLXSW_SP_MR_ROUTE_ACTION_TRAP:
2120e14c777SYotam Gigi 		err = mlxsw_afa_block_append_trap(afa_block,
2130e14c777SYotam Gigi 						  MLXSW_TRAP_ID_ACL1);
2140e14c777SYotam Gigi 		if (err)
2150e14c777SYotam Gigi 			goto err;
2160e14c777SYotam Gigi 		break;
217607feadeSYotam Gigi 	case MLXSW_SP_MR_ROUTE_ACTION_TRAP_AND_FORWARD:
2180e14c777SYotam Gigi 	case MLXSW_SP_MR_ROUTE_ACTION_FORWARD:
2190e14c777SYotam Gigi 		/* If we are about to append a multicast router action, commit
2200e14c777SYotam Gigi 		 * the erif_list.
2210e14c777SYotam Gigi 		 */
2220e14c777SYotam Gigi 		err = mlxsw_sp_mr_erif_list_commit(mlxsw_sp, erif_list);
2230e14c777SYotam Gigi 		if (err)
2240e14c777SYotam Gigi 			goto err;
2250e14c777SYotam Gigi 
2260e14c777SYotam Gigi 		err = mlxsw_afa_block_append_mcrouter(afa_block, irif_index,
2270e14c777SYotam Gigi 						      min_mtu, false,
2280e14c777SYotam Gigi 						      erif_list->kvdl_index);
2290e14c777SYotam Gigi 		if (err)
2300e14c777SYotam Gigi 			goto err;
231607feadeSYotam Gigi 
232607feadeSYotam Gigi 		if (route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP_AND_FORWARD) {
233607feadeSYotam Gigi 			err = mlxsw_afa_block_append_trap_and_forward(afa_block,
234607feadeSYotam Gigi 								      MLXSW_TRAP_ID_ACL2);
235607feadeSYotam Gigi 			if (err)
236607feadeSYotam Gigi 				goto err;
237607feadeSYotam Gigi 		}
2380e14c777SYotam Gigi 		break;
2390e14c777SYotam Gigi 	default:
2400e14c777SYotam Gigi 		err = -EINVAL;
2410e14c777SYotam Gigi 		goto err;
2420e14c777SYotam Gigi 	}
2430e14c777SYotam Gigi 
2440e14c777SYotam Gigi 	err = mlxsw_afa_block_commit(afa_block);
2450e14c777SYotam Gigi 	if (err)
2460e14c777SYotam Gigi 		goto err;
2470e14c777SYotam Gigi 	return afa_block;
2480e14c777SYotam Gigi err:
2490e14c777SYotam Gigi 	mlxsw_afa_block_destroy(afa_block);
2500e14c777SYotam Gigi 	return ERR_PTR(err);
2510e14c777SYotam Gigi }
2520e14c777SYotam Gigi 
2530e14c777SYotam Gigi static void
mlxsw_sp_mr_tcam_afa_block_destroy(struct mlxsw_afa_block * afa_block)2540e14c777SYotam Gigi mlxsw_sp_mr_tcam_afa_block_destroy(struct mlxsw_afa_block *afa_block)
2550e14c777SYotam Gigi {
2560e14c777SYotam Gigi 	mlxsw_afa_block_destroy(afa_block);
2570e14c777SYotam Gigi }
2580e14c777SYotam Gigi 
2590e14c777SYotam Gigi static int
mlxsw_sp_mr_tcam_erif_populate(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mr_tcam_erif_list * erif_list,struct mlxsw_sp_mr_route_info * route_info)2600e14c777SYotam Gigi mlxsw_sp_mr_tcam_erif_populate(struct mlxsw_sp *mlxsw_sp,
2610e14c777SYotam Gigi 			       struct mlxsw_sp_mr_tcam_erif_list *erif_list,
2620e14c777SYotam Gigi 			       struct mlxsw_sp_mr_route_info *route_info)
2630e14c777SYotam Gigi {
2640e14c777SYotam Gigi 	int err;
2650e14c777SYotam Gigi 	int i;
2660e14c777SYotam Gigi 
2670e14c777SYotam Gigi 	for (i = 0; i < route_info->erif_num; i++) {
2680e14c777SYotam Gigi 		u16 erif_index = route_info->erif_indices[i];
2690e14c777SYotam Gigi 
2700e14c777SYotam Gigi 		err = mlxsw_sp_mr_erif_list_add(mlxsw_sp, erif_list,
2710e14c777SYotam Gigi 						erif_index);
2720e14c777SYotam Gigi 		if (err)
2730e14c777SYotam Gigi 			return err;
2740e14c777SYotam Gigi 	}
2750e14c777SYotam Gigi 	return 0;
2760e14c777SYotam Gigi }
2770e14c777SYotam Gigi 
2780e14c777SYotam Gigi static int
mlxsw_sp_mr_tcam_route_create(struct mlxsw_sp * mlxsw_sp,void * priv,void * route_priv,struct mlxsw_sp_mr_route_params * route_params)2790e14c777SYotam Gigi mlxsw_sp_mr_tcam_route_create(struct mlxsw_sp *mlxsw_sp, void *priv,
2800e14c777SYotam Gigi 			      void *route_priv,
2810e14c777SYotam Gigi 			      struct mlxsw_sp_mr_route_params *route_params)
2820e14c777SYotam Gigi {
2838fae4392SJiri Pirko 	const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
2840e14c777SYotam Gigi 	struct mlxsw_sp_mr_tcam_route *route = route_priv;
2850e14c777SYotam Gigi 	struct mlxsw_sp_mr_tcam *mr_tcam = priv;
2860e14c777SYotam Gigi 	int err;
2870e14c777SYotam Gigi 
2880e14c777SYotam Gigi 	route->key = route_params->key;
2890e14c777SYotam Gigi 	route->irif_index = route_params->value.irif_index;
2900e14c777SYotam Gigi 	route->min_mtu = route_params->value.min_mtu;
2910e14c777SYotam Gigi 	route->action = route_params->value.route_action;
2920e14c777SYotam Gigi 
2930e14c777SYotam Gigi 	/* Create the egress RIFs list */
2940e14c777SYotam Gigi 	mlxsw_sp_mr_erif_list_init(&route->erif_list);
2950e14c777SYotam Gigi 	err = mlxsw_sp_mr_tcam_erif_populate(mlxsw_sp, &route->erif_list,
2960e14c777SYotam Gigi 					     &route_params->value);
2970e14c777SYotam Gigi 	if (err)
2980e14c777SYotam Gigi 		goto err_erif_populate;
2990e14c777SYotam Gigi 
3000e14c777SYotam Gigi 	/* Create the flow counter */
3010e14c777SYotam Gigi 	err = mlxsw_sp_flow_counter_alloc(mlxsw_sp, &route->counter_index);
3020e14c777SYotam Gigi 	if (err)
3030e14c777SYotam Gigi 		goto err_counter_alloc;
3040e14c777SYotam Gigi 
3050e14c777SYotam Gigi 	/* Create the flexible action block */
3060e14c777SYotam Gigi 	route->afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp,
3070e14c777SYotam Gigi 							     route->action,
3080e14c777SYotam Gigi 							     route->irif_index,
3090e14c777SYotam Gigi 							     route->counter_index,
3100e14c777SYotam Gigi 							     route->min_mtu,
3110e14c777SYotam Gigi 							     &route->erif_list);
3120e14c777SYotam Gigi 	if (IS_ERR(route->afa_block)) {
3130e14c777SYotam Gigi 		err = PTR_ERR(route->afa_block);
3140e14c777SYotam Gigi 		goto err_afa_block_create;
3150e14c777SYotam Gigi 	}
3160e14c777SYotam Gigi 
3178fae4392SJiri Pirko 	route->priv = kzalloc(ops->route_priv_size, GFP_KERNEL);
3188fae4392SJiri Pirko 	if (!route->priv) {
3198fae4392SJiri Pirko 		err = -ENOMEM;
3208fae4392SJiri Pirko 		goto err_route_priv_alloc;
3218fae4392SJiri Pirko 	}
3220e14c777SYotam Gigi 
3230e14c777SYotam Gigi 	/* Write the route to the TCAM */
3248fae4392SJiri Pirko 	err = ops->route_create(mlxsw_sp, mr_tcam->priv, route->priv,
3258fae4392SJiri Pirko 				&route->key, route->afa_block,
3268fae4392SJiri Pirko 				route_params->prio);
3270e14c777SYotam Gigi 	if (err)
3288fae4392SJiri Pirko 		goto err_route_create;
3290e14c777SYotam Gigi 	return 0;
3300e14c777SYotam Gigi 
3318fae4392SJiri Pirko err_route_create:
3328fae4392SJiri Pirko 	kfree(route->priv);
3338fae4392SJiri Pirko err_route_priv_alloc:
3340e14c777SYotam Gigi 	mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
3350e14c777SYotam Gigi err_afa_block_create:
3360e14c777SYotam Gigi 	mlxsw_sp_flow_counter_free(mlxsw_sp, route->counter_index);
3370e14c777SYotam Gigi err_erif_populate:
3380e14c777SYotam Gigi err_counter_alloc:
3390e14c777SYotam Gigi 	mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &route->erif_list);
3400e14c777SYotam Gigi 	return err;
3410e14c777SYotam Gigi }
3420e14c777SYotam Gigi 
mlxsw_sp_mr_tcam_route_destroy(struct mlxsw_sp * mlxsw_sp,void * priv,void * route_priv)3430e14c777SYotam Gigi static void mlxsw_sp_mr_tcam_route_destroy(struct mlxsw_sp *mlxsw_sp,
3440e14c777SYotam Gigi 					   void *priv, void *route_priv)
3450e14c777SYotam Gigi {
3468fae4392SJiri Pirko 	const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
3470e14c777SYotam Gigi 	struct mlxsw_sp_mr_tcam_route *route = route_priv;
3480e14c777SYotam Gigi 	struct mlxsw_sp_mr_tcam *mr_tcam = priv;
3490e14c777SYotam Gigi 
3508fae4392SJiri Pirko 	ops->route_destroy(mlxsw_sp, mr_tcam->priv, route->priv, &route->key);
3518fae4392SJiri Pirko 	kfree(route->priv);
3520e14c777SYotam Gigi 	mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
3530e14c777SYotam Gigi 	mlxsw_sp_flow_counter_free(mlxsw_sp, route->counter_index);
3540e14c777SYotam Gigi 	mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &route->erif_list);
3550e14c777SYotam Gigi }
3560e14c777SYotam Gigi 
mlxsw_sp_mr_tcam_route_stats(struct mlxsw_sp * mlxsw_sp,void * route_priv,u64 * packets,u64 * bytes)3570e14c777SYotam Gigi static int mlxsw_sp_mr_tcam_route_stats(struct mlxsw_sp *mlxsw_sp,
3580e14c777SYotam Gigi 					void *route_priv, u64 *packets,
3590e14c777SYotam Gigi 					u64 *bytes)
3600e14c777SYotam Gigi {
3610e14c777SYotam Gigi 	struct mlxsw_sp_mr_tcam_route *route = route_priv;
3620e14c777SYotam Gigi 
3630e14c777SYotam Gigi 	return mlxsw_sp_flow_counter_get(mlxsw_sp, route->counter_index,
3640e14c777SYotam Gigi 					 packets, bytes);
3650e14c777SYotam Gigi }
3660e14c777SYotam Gigi 
3670e14c777SYotam Gigi static int
mlxsw_sp_mr_tcam_route_action_update(struct mlxsw_sp * mlxsw_sp,void * route_priv,enum mlxsw_sp_mr_route_action route_action)3680e14c777SYotam Gigi mlxsw_sp_mr_tcam_route_action_update(struct mlxsw_sp *mlxsw_sp,
3690e14c777SYotam Gigi 				     void *route_priv,
3700e14c777SYotam Gigi 				     enum mlxsw_sp_mr_route_action route_action)
3710e14c777SYotam Gigi {
3728fae4392SJiri Pirko 	const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
3730e14c777SYotam Gigi 	struct mlxsw_sp_mr_tcam_route *route = route_priv;
3740e14c777SYotam Gigi 	struct mlxsw_afa_block *afa_block;
3750e14c777SYotam Gigi 	int err;
3760e14c777SYotam Gigi 
3770e14c777SYotam Gigi 	/* Create a new flexible action block */
3780e14c777SYotam Gigi 	afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp, route_action,
3790e14c777SYotam Gigi 						      route->irif_index,
3800e14c777SYotam Gigi 						      route->counter_index,
3810e14c777SYotam Gigi 						      route->min_mtu,
3820e14c777SYotam Gigi 						      &route->erif_list);
3830e14c777SYotam Gigi 	if (IS_ERR(afa_block))
3840e14c777SYotam Gigi 		return PTR_ERR(afa_block);
3850e14c777SYotam Gigi 
3860e14c777SYotam Gigi 	/* Update the TCAM route entry */
3878fae4392SJiri Pirko 	err = ops->route_update(mlxsw_sp, route->priv, &route->key, afa_block);
3880e14c777SYotam Gigi 	if (err)
3890e14c777SYotam Gigi 		goto err;
3900e14c777SYotam Gigi 
3910e14c777SYotam Gigi 	/* Delete the old one */
3920e14c777SYotam Gigi 	mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
3930e14c777SYotam Gigi 	route->afa_block = afa_block;
3940e14c777SYotam Gigi 	route->action = route_action;
3950e14c777SYotam Gigi 	return 0;
3960e14c777SYotam Gigi err:
3970e14c777SYotam Gigi 	mlxsw_sp_mr_tcam_afa_block_destroy(afa_block);
3980e14c777SYotam Gigi 	return err;
3990e14c777SYotam Gigi }
4000e14c777SYotam Gigi 
mlxsw_sp_mr_tcam_route_min_mtu_update(struct mlxsw_sp * mlxsw_sp,void * route_priv,u16 min_mtu)4010e14c777SYotam Gigi static int mlxsw_sp_mr_tcam_route_min_mtu_update(struct mlxsw_sp *mlxsw_sp,
4020e14c777SYotam Gigi 						 void *route_priv, u16 min_mtu)
4030e14c777SYotam Gigi {
4048fae4392SJiri Pirko 	const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
4050e14c777SYotam Gigi 	struct mlxsw_sp_mr_tcam_route *route = route_priv;
4060e14c777SYotam Gigi 	struct mlxsw_afa_block *afa_block;
4070e14c777SYotam Gigi 	int err;
4080e14c777SYotam Gigi 
4090e14c777SYotam Gigi 	/* Create a new flexible action block */
4100e14c777SYotam Gigi 	afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp,
4110e14c777SYotam Gigi 						      route->action,
4120e14c777SYotam Gigi 						      route->irif_index,
4130e14c777SYotam Gigi 						      route->counter_index,
4140e14c777SYotam Gigi 						      min_mtu,
4150e14c777SYotam Gigi 						      &route->erif_list);
4160e14c777SYotam Gigi 	if (IS_ERR(afa_block))
4170e14c777SYotam Gigi 		return PTR_ERR(afa_block);
4180e14c777SYotam Gigi 
4190e14c777SYotam Gigi 	/* Update the TCAM route entry */
4208fae4392SJiri Pirko 	err = ops->route_update(mlxsw_sp, route->priv, &route->key, afa_block);
4210e14c777SYotam Gigi 	if (err)
4220e14c777SYotam Gigi 		goto err;
4230e14c777SYotam Gigi 
4240e14c777SYotam Gigi 	/* Delete the old one */
4250e14c777SYotam Gigi 	mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
4260e14c777SYotam Gigi 	route->afa_block = afa_block;
4270e14c777SYotam Gigi 	route->min_mtu = min_mtu;
4280e14c777SYotam Gigi 	return 0;
4290e14c777SYotam Gigi err:
4300e14c777SYotam Gigi 	mlxsw_sp_mr_tcam_afa_block_destroy(afa_block);
4310e14c777SYotam Gigi 	return err;
4320e14c777SYotam Gigi }
4330e14c777SYotam Gigi 
mlxsw_sp_mr_tcam_route_irif_update(struct mlxsw_sp * mlxsw_sp,void * route_priv,u16 irif_index)4340e14c777SYotam Gigi static int mlxsw_sp_mr_tcam_route_irif_update(struct mlxsw_sp *mlxsw_sp,
4350e14c777SYotam Gigi 					      void *route_priv, u16 irif_index)
4360e14c777SYotam Gigi {
4370e14c777SYotam Gigi 	struct mlxsw_sp_mr_tcam_route *route = route_priv;
4380e14c777SYotam Gigi 
4390e14c777SYotam Gigi 	if (route->action != MLXSW_SP_MR_ROUTE_ACTION_TRAP)
4400e14c777SYotam Gigi 		return -EINVAL;
4410e14c777SYotam Gigi 	route->irif_index = irif_index;
4420e14c777SYotam Gigi 	return 0;
4430e14c777SYotam Gigi }
4440e14c777SYotam Gigi 
mlxsw_sp_mr_tcam_route_erif_add(struct mlxsw_sp * mlxsw_sp,void * route_priv,u16 erif_index)4450e14c777SYotam Gigi static int mlxsw_sp_mr_tcam_route_erif_add(struct mlxsw_sp *mlxsw_sp,
4460e14c777SYotam Gigi 					   void *route_priv, u16 erif_index)
4470e14c777SYotam Gigi {
4480e14c777SYotam Gigi 	struct mlxsw_sp_mr_tcam_route *route = route_priv;
4490e14c777SYotam Gigi 	int err;
4500e14c777SYotam Gigi 
4510e14c777SYotam Gigi 	err = mlxsw_sp_mr_erif_list_add(mlxsw_sp, &route->erif_list,
4520e14c777SYotam Gigi 					erif_index);
4530e14c777SYotam Gigi 	if (err)
4540e14c777SYotam Gigi 		return err;
4550e14c777SYotam Gigi 
4560e14c777SYotam Gigi 	/* Commit the action only if the route action is not TRAP */
4570e14c777SYotam Gigi 	if (route->action != MLXSW_SP_MR_ROUTE_ACTION_TRAP)
4580e14c777SYotam Gigi 		return mlxsw_sp_mr_erif_list_commit(mlxsw_sp,
4590e14c777SYotam Gigi 						    &route->erif_list);
4600e14c777SYotam Gigi 	return 0;
4610e14c777SYotam Gigi }
4620e14c777SYotam Gigi 
mlxsw_sp_mr_tcam_route_erif_del(struct mlxsw_sp * mlxsw_sp,void * route_priv,u16 erif_index)4630e14c777SYotam Gigi static int mlxsw_sp_mr_tcam_route_erif_del(struct mlxsw_sp *mlxsw_sp,
4640e14c777SYotam Gigi 					   void *route_priv, u16 erif_index)
4650e14c777SYotam Gigi {
4668fae4392SJiri Pirko 	const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
4670e14c777SYotam Gigi 	struct mlxsw_sp_mr_tcam_route *route = route_priv;
4680e14c777SYotam Gigi 	struct mlxsw_sp_mr_erif_sublist *erif_sublist;
4690e14c777SYotam Gigi 	struct mlxsw_sp_mr_tcam_erif_list erif_list;
4700e14c777SYotam Gigi 	struct mlxsw_afa_block *afa_block;
4710e14c777SYotam Gigi 	int err;
4720e14c777SYotam Gigi 	int i;
4730e14c777SYotam Gigi 
4740e14c777SYotam Gigi 	/* Create a copy of the original erif_list without the deleted entry */
4750e14c777SYotam Gigi 	mlxsw_sp_mr_erif_list_init(&erif_list);
4760e14c777SYotam Gigi 	list_for_each_entry(erif_sublist, &route->erif_list.erif_sublists, list) {
4770e14c777SYotam Gigi 		for (i = 0; i < erif_sublist->num_erifs; i++) {
4780e14c777SYotam Gigi 			u16 curr_erif = erif_sublist->erif_indices[i];
4790e14c777SYotam Gigi 
4800e14c777SYotam Gigi 			if (curr_erif == erif_index)
4810e14c777SYotam Gigi 				continue;
4820e14c777SYotam Gigi 			err = mlxsw_sp_mr_erif_list_add(mlxsw_sp, &erif_list,
4830e14c777SYotam Gigi 							curr_erif);
4840e14c777SYotam Gigi 			if (err)
4850e14c777SYotam Gigi 				goto err_erif_list_add;
4860e14c777SYotam Gigi 		}
4870e14c777SYotam Gigi 	}
4880e14c777SYotam Gigi 
4890e14c777SYotam Gigi 	/* Create the flexible action block pointing to the new erif_list */
4900e14c777SYotam Gigi 	afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp, route->action,
4910e14c777SYotam Gigi 						      route->irif_index,
4920e14c777SYotam Gigi 						      route->counter_index,
4930e14c777SYotam Gigi 						      route->min_mtu,
4940e14c777SYotam Gigi 						      &erif_list);
4950e14c777SYotam Gigi 	if (IS_ERR(afa_block)) {
4960e14c777SYotam Gigi 		err = PTR_ERR(afa_block);
4970e14c777SYotam Gigi 		goto err_afa_block_create;
4980e14c777SYotam Gigi 	}
4990e14c777SYotam Gigi 
5000e14c777SYotam Gigi 	/* Update the TCAM route entry */
5018fae4392SJiri Pirko 	err = ops->route_update(mlxsw_sp, route->priv, &route->key, afa_block);
5020e14c777SYotam Gigi 	if (err)
5030e14c777SYotam Gigi 		goto err_route_write;
5040e14c777SYotam Gigi 
5050e14c777SYotam Gigi 	mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
5060e14c777SYotam Gigi 	mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &route->erif_list);
5070e14c777SYotam Gigi 	route->afa_block = afa_block;
5080e14c777SYotam Gigi 	mlxsw_sp_mr_erif_list_move(&route->erif_list, &erif_list);
5090e14c777SYotam Gigi 	return 0;
5100e14c777SYotam Gigi 
5110e14c777SYotam Gigi err_route_write:
5120e14c777SYotam Gigi 	mlxsw_sp_mr_tcam_afa_block_destroy(afa_block);
5130e14c777SYotam Gigi err_afa_block_create:
5140e14c777SYotam Gigi err_erif_list_add:
5150e14c777SYotam Gigi 	mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &erif_list);
5160e14c777SYotam Gigi 	return err;
5170e14c777SYotam Gigi }
5180e14c777SYotam Gigi 
5190e14c777SYotam Gigi static int
mlxsw_sp_mr_tcam_route_update(struct mlxsw_sp * mlxsw_sp,void * route_priv,struct mlxsw_sp_mr_route_info * route_info)5200e14c777SYotam Gigi mlxsw_sp_mr_tcam_route_update(struct mlxsw_sp *mlxsw_sp, void *route_priv,
5210e14c777SYotam Gigi 			      struct mlxsw_sp_mr_route_info *route_info)
5220e14c777SYotam Gigi {
5238fae4392SJiri Pirko 	const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
5240e14c777SYotam Gigi 	struct mlxsw_sp_mr_tcam_route *route = route_priv;
5250e14c777SYotam Gigi 	struct mlxsw_sp_mr_tcam_erif_list erif_list;
5260e14c777SYotam Gigi 	struct mlxsw_afa_block *afa_block;
5270e14c777SYotam Gigi 	int err;
5280e14c777SYotam Gigi 
5290e14c777SYotam Gigi 	/* Create a new erif_list */
5300e14c777SYotam Gigi 	mlxsw_sp_mr_erif_list_init(&erif_list);
5310e14c777SYotam Gigi 	err = mlxsw_sp_mr_tcam_erif_populate(mlxsw_sp, &erif_list, route_info);
5320e14c777SYotam Gigi 	if (err)
5330e14c777SYotam Gigi 		goto err_erif_populate;
5340e14c777SYotam Gigi 
5350e14c777SYotam Gigi 	/* Create the flexible action block pointing to the new erif_list */
5360e14c777SYotam Gigi 	afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp,
5370e14c777SYotam Gigi 						      route_info->route_action,
5380e14c777SYotam Gigi 						      route_info->irif_index,
5390e14c777SYotam Gigi 						      route->counter_index,
5400e14c777SYotam Gigi 						      route_info->min_mtu,
5410e14c777SYotam Gigi 						      &erif_list);
5420e14c777SYotam Gigi 	if (IS_ERR(afa_block)) {
5430e14c777SYotam Gigi 		err = PTR_ERR(afa_block);
5440e14c777SYotam Gigi 		goto err_afa_block_create;
5450e14c777SYotam Gigi 	}
5460e14c777SYotam Gigi 
5470e14c777SYotam Gigi 	/* Update the TCAM route entry */
5488fae4392SJiri Pirko 	err = ops->route_update(mlxsw_sp, route->priv, &route->key, afa_block);
5490e14c777SYotam Gigi 	if (err)
5500e14c777SYotam Gigi 		goto err_route_write;
5510e14c777SYotam Gigi 
5520e14c777SYotam Gigi 	mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
5530e14c777SYotam Gigi 	mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &route->erif_list);
5540e14c777SYotam Gigi 	route->afa_block = afa_block;
5550e14c777SYotam Gigi 	mlxsw_sp_mr_erif_list_move(&route->erif_list, &erif_list);
5560e14c777SYotam Gigi 	route->action = route_info->route_action;
5570e14c777SYotam Gigi 	route->irif_index = route_info->irif_index;
5580e14c777SYotam Gigi 	route->min_mtu = route_info->min_mtu;
5590e14c777SYotam Gigi 	return 0;
5600e14c777SYotam Gigi 
5610e14c777SYotam Gigi err_route_write:
5620e14c777SYotam Gigi 	mlxsw_sp_mr_tcam_afa_block_destroy(afa_block);
5630e14c777SYotam Gigi err_afa_block_create:
5640e14c777SYotam Gigi err_erif_populate:
5650e14c777SYotam Gigi 	mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &erif_list);
5660e14c777SYotam Gigi 	return err;
5670e14c777SYotam Gigi }
5680e14c777SYotam Gigi 
mlxsw_sp_mr_tcam_init(struct mlxsw_sp * mlxsw_sp,void * priv)5690e14c777SYotam Gigi static int mlxsw_sp_mr_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv)
5700e14c777SYotam Gigi {
5718fae4392SJiri Pirko 	const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
5720e14c777SYotam Gigi 	struct mlxsw_sp_mr_tcam *mr_tcam = priv;
5739742f866SYuval Mintz 	int err;
5740e14c777SYotam Gigi 
5758fae4392SJiri Pirko 	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MC_ERIF_LIST_ENTRIES))
5760e14c777SYotam Gigi 		return -EIO;
5770e14c777SYotam Gigi 
5788fae4392SJiri Pirko 	mr_tcam->priv = kzalloc(ops->priv_size, GFP_KERNEL);
5798fae4392SJiri Pirko 	if (!mr_tcam->priv)
5808fae4392SJiri Pirko 		return -ENOMEM;
5819742f866SYuval Mintz 
5828fae4392SJiri Pirko 	err = ops->init(mlxsw_sp, mr_tcam->priv);
5839742f866SYuval Mintz 	if (err)
5848fae4392SJiri Pirko 		goto err_init;
5859742f866SYuval Mintz 	return 0;
5869742f866SYuval Mintz 
5878fae4392SJiri Pirko err_init:
5888fae4392SJiri Pirko 	kfree(mr_tcam->priv);
5899742f866SYuval Mintz 	return err;
5900e14c777SYotam Gigi }
5910e14c777SYotam Gigi 
mlxsw_sp_mr_tcam_fini(struct mlxsw_sp * mlxsw_sp,void * priv)5928fae4392SJiri Pirko static void mlxsw_sp_mr_tcam_fini(struct mlxsw_sp *mlxsw_sp, void *priv)
5930e14c777SYotam Gigi {
5948fae4392SJiri Pirko 	const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
5950e14c777SYotam Gigi 	struct mlxsw_sp_mr_tcam *mr_tcam = priv;
5960e14c777SYotam Gigi 
5978fae4392SJiri Pirko 	ops->fini(mr_tcam->priv);
5988fae4392SJiri Pirko 	kfree(mr_tcam->priv);
5990e14c777SYotam Gigi }
6000e14c777SYotam Gigi 
6010e14c777SYotam Gigi const struct mlxsw_sp_mr_ops mlxsw_sp_mr_tcam_ops = {
6020e14c777SYotam Gigi 	.priv_size = sizeof(struct mlxsw_sp_mr_tcam),
6030e14c777SYotam Gigi 	.route_priv_size = sizeof(struct mlxsw_sp_mr_tcam_route),
6040e14c777SYotam Gigi 	.init = mlxsw_sp_mr_tcam_init,
6050e14c777SYotam Gigi 	.route_create = mlxsw_sp_mr_tcam_route_create,
6060e14c777SYotam Gigi 	.route_update = mlxsw_sp_mr_tcam_route_update,
6070e14c777SYotam Gigi 	.route_stats = mlxsw_sp_mr_tcam_route_stats,
6080e14c777SYotam Gigi 	.route_action_update = mlxsw_sp_mr_tcam_route_action_update,
6090e14c777SYotam Gigi 	.route_min_mtu_update = mlxsw_sp_mr_tcam_route_min_mtu_update,
6100e14c777SYotam Gigi 	.route_irif_update = mlxsw_sp_mr_tcam_route_irif_update,
6110e14c777SYotam Gigi 	.route_erif_add = mlxsw_sp_mr_tcam_route_erif_add,
6120e14c777SYotam Gigi 	.route_erif_del = mlxsw_sp_mr_tcam_route_erif_del,
6130e14c777SYotam Gigi 	.route_destroy = mlxsw_sp_mr_tcam_route_destroy,
6140e14c777SYotam Gigi 	.fini = mlxsw_sp_mr_tcam_fini,
6150e14c777SYotam Gigi };
616