brintos

brintos / linux-shallow public Read only

0
0
Text · 16.8 KiB · 01d81ae Raw
616 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */3 4#include <linux/kernel.h>5#include <linux/list.h>6#include <linux/netdevice.h>7 8#include "spectrum_mr_tcam.h"9#include "reg.h"10#include "spectrum.h"11#include "core_acl_flex_actions.h"12#include "spectrum_mr.h"13 14struct mlxsw_sp_mr_tcam {15	void *priv;16};17 18/* This struct maps to one RIGR2 register entry */19struct mlxsw_sp_mr_erif_sublist {20	struct list_head list;21	u32 rigr2_kvdl_index;22	int num_erifs;23	u16 erif_indices[MLXSW_REG_RIGR2_MAX_ERIFS];24	bool synced;25};26 27struct mlxsw_sp_mr_tcam_erif_list {28	struct list_head erif_sublists;29	u32 kvdl_index;30};31 32static bool33mlxsw_sp_mr_erif_sublist_full(struct mlxsw_sp *mlxsw_sp,34			      struct mlxsw_sp_mr_erif_sublist *erif_sublist)35{36	int erif_list_entries = MLXSW_CORE_RES_GET(mlxsw_sp->core,37						   MC_ERIF_LIST_ENTRIES);38 39	return erif_sublist->num_erifs == erif_list_entries;40}41 42static void43mlxsw_sp_mr_erif_list_init(struct mlxsw_sp_mr_tcam_erif_list *erif_list)44{45	INIT_LIST_HEAD(&erif_list->erif_sublists);46}47 48static struct mlxsw_sp_mr_erif_sublist *49mlxsw_sp_mr_erif_sublist_create(struct mlxsw_sp *mlxsw_sp,50				struct mlxsw_sp_mr_tcam_erif_list *erif_list)51{52	struct mlxsw_sp_mr_erif_sublist *erif_sublist;53	int err;54 55	erif_sublist = kzalloc(sizeof(*erif_sublist), GFP_KERNEL);56	if (!erif_sublist)57		return ERR_PTR(-ENOMEM);58	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_MCRIGR,59				  1, &erif_sublist->rigr2_kvdl_index);60	if (err) {61		kfree(erif_sublist);62		return ERR_PTR(err);63	}64 65	list_add_tail(&erif_sublist->list, &erif_list->erif_sublists);66	return erif_sublist;67}68 69static void70mlxsw_sp_mr_erif_sublist_destroy(struct mlxsw_sp *mlxsw_sp,71				 struct mlxsw_sp_mr_erif_sublist *erif_sublist)72{73	list_del(&erif_sublist->list);74	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_MCRIGR,75			   1, erif_sublist->rigr2_kvdl_index);76	kfree(erif_sublist);77}78 79static int80mlxsw_sp_mr_erif_list_add(struct mlxsw_sp *mlxsw_sp,81			  struct mlxsw_sp_mr_tcam_erif_list *erif_list,82			  u16 erif_index)83{84	struct mlxsw_sp_mr_erif_sublist *sublist;85 86	/* If either there is no erif_entry or the last one is full, allocate a87	 * new one.88	 */89	if (list_empty(&erif_list->erif_sublists)) {90		sublist = mlxsw_sp_mr_erif_sublist_create(mlxsw_sp, erif_list);91		if (IS_ERR(sublist))92			return PTR_ERR(sublist);93		erif_list->kvdl_index = sublist->rigr2_kvdl_index;94	} else {95		sublist = list_last_entry(&erif_list->erif_sublists,96					  struct mlxsw_sp_mr_erif_sublist,97					  list);98		sublist->synced = false;99		if (mlxsw_sp_mr_erif_sublist_full(mlxsw_sp, sublist)) {100			sublist = mlxsw_sp_mr_erif_sublist_create(mlxsw_sp,101								  erif_list);102			if (IS_ERR(sublist))103				return PTR_ERR(sublist);104		}105	}106 107	/* Add the eRIF to the last entry's last index */108	sublist->erif_indices[sublist->num_erifs++] = erif_index;109	return 0;110}111 112static void113mlxsw_sp_mr_erif_list_flush(struct mlxsw_sp *mlxsw_sp,114			    struct mlxsw_sp_mr_tcam_erif_list *erif_list)115{116	struct mlxsw_sp_mr_erif_sublist *erif_sublist, *tmp;117 118	list_for_each_entry_safe(erif_sublist, tmp, &erif_list->erif_sublists,119				 list)120		mlxsw_sp_mr_erif_sublist_destroy(mlxsw_sp, erif_sublist);121}122 123static int124mlxsw_sp_mr_erif_list_commit(struct mlxsw_sp *mlxsw_sp,125			     struct mlxsw_sp_mr_tcam_erif_list *erif_list)126{127	struct mlxsw_sp_mr_erif_sublist *curr_sublist;128	char rigr2_pl[MLXSW_REG_RIGR2_LEN];129	int err;130	int i;131 132	list_for_each_entry(curr_sublist, &erif_list->erif_sublists, list) {133		if (curr_sublist->synced)134			continue;135 136		/* If the sublist is not the last one, pack the next index */137		if (list_is_last(&curr_sublist->list,138				 &erif_list->erif_sublists)) {139			mlxsw_reg_rigr2_pack(rigr2_pl,140					     curr_sublist->rigr2_kvdl_index,141					     false, 0);142		} else {143			struct mlxsw_sp_mr_erif_sublist *next_sublist;144 145			next_sublist = list_next_entry(curr_sublist, list);146			mlxsw_reg_rigr2_pack(rigr2_pl,147					     curr_sublist->rigr2_kvdl_index,148					     true,149					     next_sublist->rigr2_kvdl_index);150		}151 152		/* Pack all the erifs */153		for (i = 0; i < curr_sublist->num_erifs; i++) {154			u16 erif_index = curr_sublist->erif_indices[i];155 156			mlxsw_reg_rigr2_erif_entry_pack(rigr2_pl, i, true,157							erif_index);158		}159 160		/* Write the entry */161		err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rigr2),162				      rigr2_pl);163		if (err)164			/* No need of a rollback here because this165			 * hardware entry should not be pointed yet.166			 */167			return err;168		curr_sublist->synced = true;169	}170	return 0;171}172 173static void mlxsw_sp_mr_erif_list_move(struct mlxsw_sp_mr_tcam_erif_list *to,174				       struct mlxsw_sp_mr_tcam_erif_list *from)175{176	list_splice(&from->erif_sublists, &to->erif_sublists);177	to->kvdl_index = from->kvdl_index;178}179 180struct mlxsw_sp_mr_tcam_route {181	struct mlxsw_sp_mr_tcam_erif_list erif_list;182	struct mlxsw_afa_block *afa_block;183	u32 counter_index;184	enum mlxsw_sp_mr_route_action action;185	struct mlxsw_sp_mr_route_key key;186	u16 irif_index;187	u16 min_mtu;188	void *priv;189};190 191static struct mlxsw_afa_block *192mlxsw_sp_mr_tcam_afa_block_create(struct mlxsw_sp *mlxsw_sp,193				  enum mlxsw_sp_mr_route_action route_action,194				  u16 irif_index, u32 counter_index,195				  u16 min_mtu,196				  struct mlxsw_sp_mr_tcam_erif_list *erif_list)197{198	struct mlxsw_afa_block *afa_block;199	int err;200 201	afa_block = mlxsw_afa_block_create(mlxsw_sp->afa);202	if (IS_ERR(afa_block))203		return afa_block;204 205	err = mlxsw_afa_block_append_allocated_counter(afa_block,206						       counter_index);207	if (err)208		goto err;209 210	switch (route_action) {211	case MLXSW_SP_MR_ROUTE_ACTION_TRAP:212		err = mlxsw_afa_block_append_trap(afa_block,213						  MLXSW_TRAP_ID_ACL1);214		if (err)215			goto err;216		break;217	case MLXSW_SP_MR_ROUTE_ACTION_TRAP_AND_FORWARD:218	case MLXSW_SP_MR_ROUTE_ACTION_FORWARD:219		/* If we are about to append a multicast router action, commit220		 * the erif_list.221		 */222		err = mlxsw_sp_mr_erif_list_commit(mlxsw_sp, erif_list);223		if (err)224			goto err;225 226		err = mlxsw_afa_block_append_mcrouter(afa_block, irif_index,227						      min_mtu, false,228						      erif_list->kvdl_index);229		if (err)230			goto err;231 232		if (route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP_AND_FORWARD) {233			err = mlxsw_afa_block_append_trap_and_forward(afa_block,234								      MLXSW_TRAP_ID_ACL2);235			if (err)236				goto err;237		}238		break;239	default:240		err = -EINVAL;241		goto err;242	}243 244	err = mlxsw_afa_block_commit(afa_block);245	if (err)246		goto err;247	return afa_block;248err:249	mlxsw_afa_block_destroy(afa_block);250	return ERR_PTR(err);251}252 253static void254mlxsw_sp_mr_tcam_afa_block_destroy(struct mlxsw_afa_block *afa_block)255{256	mlxsw_afa_block_destroy(afa_block);257}258 259static int260mlxsw_sp_mr_tcam_erif_populate(struct mlxsw_sp *mlxsw_sp,261			       struct mlxsw_sp_mr_tcam_erif_list *erif_list,262			       struct mlxsw_sp_mr_route_info *route_info)263{264	int err;265	int i;266 267	for (i = 0; i < route_info->erif_num; i++) {268		u16 erif_index = route_info->erif_indices[i];269 270		err = mlxsw_sp_mr_erif_list_add(mlxsw_sp, erif_list,271						erif_index);272		if (err)273			return err;274	}275	return 0;276}277 278static int279mlxsw_sp_mr_tcam_route_create(struct mlxsw_sp *mlxsw_sp, void *priv,280			      void *route_priv,281			      struct mlxsw_sp_mr_route_params *route_params)282{283	const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;284	struct mlxsw_sp_mr_tcam_route *route = route_priv;285	struct mlxsw_sp_mr_tcam *mr_tcam = priv;286	int err;287 288	route->key = route_params->key;289	route->irif_index = route_params->value.irif_index;290	route->min_mtu = route_params->value.min_mtu;291	route->action = route_params->value.route_action;292 293	/* Create the egress RIFs list */294	mlxsw_sp_mr_erif_list_init(&route->erif_list);295	err = mlxsw_sp_mr_tcam_erif_populate(mlxsw_sp, &route->erif_list,296					     &route_params->value);297	if (err)298		goto err_erif_populate;299 300	/* Create the flow counter */301	err = mlxsw_sp_flow_counter_alloc(mlxsw_sp, &route->counter_index);302	if (err)303		goto err_counter_alloc;304 305	/* Create the flexible action block */306	route->afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp,307							     route->action,308							     route->irif_index,309							     route->counter_index,310							     route->min_mtu,311							     &route->erif_list);312	if (IS_ERR(route->afa_block)) {313		err = PTR_ERR(route->afa_block);314		goto err_afa_block_create;315	}316 317	route->priv = kzalloc(ops->route_priv_size, GFP_KERNEL);318	if (!route->priv) {319		err = -ENOMEM;320		goto err_route_priv_alloc;321	}322 323	/* Write the route to the TCAM */324	err = ops->route_create(mlxsw_sp, mr_tcam->priv, route->priv,325				&route->key, route->afa_block,326				route_params->prio);327	if (err)328		goto err_route_create;329	return 0;330 331err_route_create:332	kfree(route->priv);333err_route_priv_alloc:334	mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);335err_afa_block_create:336	mlxsw_sp_flow_counter_free(mlxsw_sp, route->counter_index);337err_erif_populate:338err_counter_alloc:339	mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &route->erif_list);340	return err;341}342 343static void mlxsw_sp_mr_tcam_route_destroy(struct mlxsw_sp *mlxsw_sp,344					   void *priv, void *route_priv)345{346	const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;347	struct mlxsw_sp_mr_tcam_route *route = route_priv;348	struct mlxsw_sp_mr_tcam *mr_tcam = priv;349 350	ops->route_destroy(mlxsw_sp, mr_tcam->priv, route->priv, &route->key);351	kfree(route->priv);352	mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);353	mlxsw_sp_flow_counter_free(mlxsw_sp, route->counter_index);354	mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &route->erif_list);355}356 357static int mlxsw_sp_mr_tcam_route_stats(struct mlxsw_sp *mlxsw_sp,358					void *route_priv, u64 *packets,359					u64 *bytes)360{361	struct mlxsw_sp_mr_tcam_route *route = route_priv;362 363	return mlxsw_sp_flow_counter_get(mlxsw_sp, route->counter_index,364					 false, packets, bytes);365}366 367static int368mlxsw_sp_mr_tcam_route_action_update(struct mlxsw_sp *mlxsw_sp,369				     void *route_priv,370				     enum mlxsw_sp_mr_route_action route_action)371{372	const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;373	struct mlxsw_sp_mr_tcam_route *route = route_priv;374	struct mlxsw_afa_block *afa_block;375	int err;376 377	/* Create a new flexible action block */378	afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp, route_action,379						      route->irif_index,380						      route->counter_index,381						      route->min_mtu,382						      &route->erif_list);383	if (IS_ERR(afa_block))384		return PTR_ERR(afa_block);385 386	/* Update the TCAM route entry */387	err = ops->route_update(mlxsw_sp, route->priv, &route->key, afa_block);388	if (err)389		goto err;390 391	/* Delete the old one */392	mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);393	route->afa_block = afa_block;394	route->action = route_action;395	return 0;396err:397	mlxsw_sp_mr_tcam_afa_block_destroy(afa_block);398	return err;399}400 401static int mlxsw_sp_mr_tcam_route_min_mtu_update(struct mlxsw_sp *mlxsw_sp,402						 void *route_priv, u16 min_mtu)403{404	const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;405	struct mlxsw_sp_mr_tcam_route *route = route_priv;406	struct mlxsw_afa_block *afa_block;407	int err;408 409	/* Create a new flexible action block */410	afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp,411						      route->action,412						      route->irif_index,413						      route->counter_index,414						      min_mtu,415						      &route->erif_list);416	if (IS_ERR(afa_block))417		return PTR_ERR(afa_block);418 419	/* Update the TCAM route entry */420	err = ops->route_update(mlxsw_sp, route->priv, &route->key, afa_block);421	if (err)422		goto err;423 424	/* Delete the old one */425	mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);426	route->afa_block = afa_block;427	route->min_mtu = min_mtu;428	return 0;429err:430	mlxsw_sp_mr_tcam_afa_block_destroy(afa_block);431	return err;432}433 434static int mlxsw_sp_mr_tcam_route_irif_update(struct mlxsw_sp *mlxsw_sp,435					      void *route_priv, u16 irif_index)436{437	struct mlxsw_sp_mr_tcam_route *route = route_priv;438 439	if (route->action != MLXSW_SP_MR_ROUTE_ACTION_TRAP)440		return -EINVAL;441	route->irif_index = irif_index;442	return 0;443}444 445static int mlxsw_sp_mr_tcam_route_erif_add(struct mlxsw_sp *mlxsw_sp,446					   void *route_priv, u16 erif_index)447{448	struct mlxsw_sp_mr_tcam_route *route = route_priv;449	int err;450 451	err = mlxsw_sp_mr_erif_list_add(mlxsw_sp, &route->erif_list,452					erif_index);453	if (err)454		return err;455 456	/* Commit the action only if the route action is not TRAP */457	if (route->action != MLXSW_SP_MR_ROUTE_ACTION_TRAP)458		return mlxsw_sp_mr_erif_list_commit(mlxsw_sp,459						    &route->erif_list);460	return 0;461}462 463static int mlxsw_sp_mr_tcam_route_erif_del(struct mlxsw_sp *mlxsw_sp,464					   void *route_priv, u16 erif_index)465{466	const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;467	struct mlxsw_sp_mr_tcam_route *route = route_priv;468	struct mlxsw_sp_mr_erif_sublist *erif_sublist;469	struct mlxsw_sp_mr_tcam_erif_list erif_list;470	struct mlxsw_afa_block *afa_block;471	int err;472	int i;473 474	/* Create a copy of the original erif_list without the deleted entry */475	mlxsw_sp_mr_erif_list_init(&erif_list);476	list_for_each_entry(erif_sublist, &route->erif_list.erif_sublists, list) {477		for (i = 0; i < erif_sublist->num_erifs; i++) {478			u16 curr_erif = erif_sublist->erif_indices[i];479 480			if (curr_erif == erif_index)481				continue;482			err = mlxsw_sp_mr_erif_list_add(mlxsw_sp, &erif_list,483							curr_erif);484			if (err)485				goto err_erif_list_add;486		}487	}488 489	/* Create the flexible action block pointing to the new erif_list */490	afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp, route->action,491						      route->irif_index,492						      route->counter_index,493						      route->min_mtu,494						      &erif_list);495	if (IS_ERR(afa_block)) {496		err = PTR_ERR(afa_block);497		goto err_afa_block_create;498	}499 500	/* Update the TCAM route entry */501	err = ops->route_update(mlxsw_sp, route->priv, &route->key, afa_block);502	if (err)503		goto err_route_write;504 505	mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);506	mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &route->erif_list);507	route->afa_block = afa_block;508	mlxsw_sp_mr_erif_list_move(&route->erif_list, &erif_list);509	return 0;510 511err_route_write:512	mlxsw_sp_mr_tcam_afa_block_destroy(afa_block);513err_afa_block_create:514err_erif_list_add:515	mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &erif_list);516	return err;517}518 519static int520mlxsw_sp_mr_tcam_route_update(struct mlxsw_sp *mlxsw_sp, void *route_priv,521			      struct mlxsw_sp_mr_route_info *route_info)522{523	const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;524	struct mlxsw_sp_mr_tcam_route *route = route_priv;525	struct mlxsw_sp_mr_tcam_erif_list erif_list;526	struct mlxsw_afa_block *afa_block;527	int err;528 529	/* Create a new erif_list */530	mlxsw_sp_mr_erif_list_init(&erif_list);531	err = mlxsw_sp_mr_tcam_erif_populate(mlxsw_sp, &erif_list, route_info);532	if (err)533		goto err_erif_populate;534 535	/* Create the flexible action block pointing to the new erif_list */536	afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp,537						      route_info->route_action,538						      route_info->irif_index,539						      route->counter_index,540						      route_info->min_mtu,541						      &erif_list);542	if (IS_ERR(afa_block)) {543		err = PTR_ERR(afa_block);544		goto err_afa_block_create;545	}546 547	/* Update the TCAM route entry */548	err = ops->route_update(mlxsw_sp, route->priv, &route->key, afa_block);549	if (err)550		goto err_route_write;551 552	mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);553	mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &route->erif_list);554	route->afa_block = afa_block;555	mlxsw_sp_mr_erif_list_move(&route->erif_list, &erif_list);556	route->action = route_info->route_action;557	route->irif_index = route_info->irif_index;558	route->min_mtu = route_info->min_mtu;559	return 0;560 561err_route_write:562	mlxsw_sp_mr_tcam_afa_block_destroy(afa_block);563err_afa_block_create:564err_erif_populate:565	mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &erif_list);566	return err;567}568 569static int mlxsw_sp_mr_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv)570{571	const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;572	struct mlxsw_sp_mr_tcam *mr_tcam = priv;573	int err;574 575	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MC_ERIF_LIST_ENTRIES))576		return -EIO;577 578	mr_tcam->priv = kzalloc(ops->priv_size, GFP_KERNEL);579	if (!mr_tcam->priv)580		return -ENOMEM;581 582	err = ops->init(mlxsw_sp, mr_tcam->priv);583	if (err)584		goto err_init;585	return 0;586 587err_init:588	kfree(mr_tcam->priv);589	return err;590}591 592static void mlxsw_sp_mr_tcam_fini(struct mlxsw_sp *mlxsw_sp, void *priv)593{594	const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;595	struct mlxsw_sp_mr_tcam *mr_tcam = priv;596 597	ops->fini(mr_tcam->priv);598	kfree(mr_tcam->priv);599}600 601const struct mlxsw_sp_mr_ops mlxsw_sp_mr_tcam_ops = {602	.priv_size = sizeof(struct mlxsw_sp_mr_tcam),603	.route_priv_size = sizeof(struct mlxsw_sp_mr_tcam_route),604	.init = mlxsw_sp_mr_tcam_init,605	.route_create = mlxsw_sp_mr_tcam_route_create,606	.route_update = mlxsw_sp_mr_tcam_route_update,607	.route_stats = mlxsw_sp_mr_tcam_route_stats,608	.route_action_update = mlxsw_sp_mr_tcam_route_action_update,609	.route_min_mtu_update = mlxsw_sp_mr_tcam_route_min_mtu_update,610	.route_irif_update = mlxsw_sp_mr_tcam_route_irif_update,611	.route_erif_add = mlxsw_sp_mr_tcam_route_erif_add,612	.route_erif_del = mlxsw_sp_mr_tcam_route_erif_del,613	.route_destroy = mlxsw_sp_mr_tcam_route_destroy,614	.fini = mlxsw_sp_mr_tcam_fini,615};616