brintos

brintos / linux-shallow public Read only

0
0
Text · 55.1 KiB · b1d08e9 Raw
1953 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/slab.h>6#include <linux/errno.h>7#include <linux/bitops.h>8#include <linux/list.h>9#include <linux/rhashtable.h>10#include <linux/netdevice.h>11#include <linux/mutex.h>12#include <linux/refcount.h>13#include <linux/idr.h>14#include <net/devlink.h>15#include <trace/events/mlxsw.h>16 17#include "reg.h"18#include "core.h"19#include "resources.h"20#include "spectrum.h"21#include "spectrum_acl_tcam.h"22#include "core_acl_flex_keys.h"23 24size_t mlxsw_sp_acl_tcam_priv_size(struct mlxsw_sp *mlxsw_sp)25{26	const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;27 28	return ops->priv_size;29}30 31#define MLXSW_SP_ACL_TCAM_VREGION_REHASH_INTRVL_DFLT 5000 /* ms */32#define MLXSW_SP_ACL_TCAM_VREGION_REHASH_INTRVL_MIN 3000 /* ms */33#define MLXSW_SP_ACL_TCAM_VREGION_REHASH_CREDITS 100 /* number of entries */34 35int mlxsw_sp_acl_tcam_priority_get(struct mlxsw_sp *mlxsw_sp,36				   struct mlxsw_sp_acl_rule_info *rulei,37				   u32 *priority, bool fillup_priority)38{39	u64 max_priority;40 41	if (!fillup_priority) {42		*priority = 0;43		return 0;44	}45 46	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, KVD_SIZE))47		return -EIO;48 49	/* Priority range is 1..cap_kvd_size-1. */50	max_priority = MLXSW_CORE_RES_GET(mlxsw_sp->core, KVD_SIZE) - 1;51	if (rulei->priority >= max_priority)52		return -EINVAL;53 54	/* Unlike in TC, in HW, higher number means higher priority. */55	*priority = max_priority - rulei->priority;56	return 0;57}58 59static int mlxsw_sp_acl_tcam_region_id_get(struct mlxsw_sp_acl_tcam *tcam,60					   u16 *p_id)61{62	int id;63 64	id = ida_alloc_max(&tcam->used_regions, tcam->max_regions - 1,65			   GFP_KERNEL);66	if (id < 0)67		return id;68 69	*p_id = id;70 71	return 0;72}73 74static void mlxsw_sp_acl_tcam_region_id_put(struct mlxsw_sp_acl_tcam *tcam,75					    u16 id)76{77	ida_free(&tcam->used_regions, id);78}79 80static int mlxsw_sp_acl_tcam_group_id_get(struct mlxsw_sp_acl_tcam *tcam,81					  u16 *p_id)82{83	int id;84 85	id = ida_alloc_max(&tcam->used_groups, tcam->max_groups - 1,86			   GFP_KERNEL);87	if (id < 0)88		return id;89 90	*p_id = id;91 92	return 0;93}94 95static void mlxsw_sp_acl_tcam_group_id_put(struct mlxsw_sp_acl_tcam *tcam,96					   u16 id)97{98	ida_free(&tcam->used_groups, id);99}100 101struct mlxsw_sp_acl_tcam_pattern {102	const enum mlxsw_afk_element *elements;103	unsigned int elements_count;104};105 106struct mlxsw_sp_acl_tcam_group {107	struct mlxsw_sp_acl_tcam *tcam;108	u16 id;109	struct mutex lock; /* guards region list updates */110	struct list_head region_list;111	unsigned int region_count;112};113 114struct mlxsw_sp_acl_tcam_vgroup {115	struct mlxsw_sp_acl_tcam_group group;116	struct list_head vregion_list;117	struct rhashtable vchunk_ht;118	const struct mlxsw_sp_acl_tcam_pattern *patterns;119	unsigned int patterns_count;120	bool tmplt_elusage_set;121	struct mlxsw_afk_element_usage tmplt_elusage;122	bool vregion_rehash_enabled;123	unsigned int *p_min_prio;124	unsigned int *p_max_prio;125};126 127struct mlxsw_sp_acl_tcam_rehash_ctx {128	void *hints_priv;129	bool this_is_rollback;130	struct mlxsw_sp_acl_tcam_vchunk *current_vchunk; /* vchunk being131							  * currently migrated.132							  */133	struct mlxsw_sp_acl_tcam_ventry *start_ventry; /* ventry to start134							* migration from in135							* a vchunk being136							* currently migrated.137							*/138	struct mlxsw_sp_acl_tcam_ventry *stop_ventry; /* ventry to stop139						       * migration at140						       * a vchunk being141						       * currently migrated.142						       */143};144 145struct mlxsw_sp_acl_tcam_vregion {146	struct mutex lock; /* Protects consistency of region, region2 pointers147			    * and vchunk_list.148			    */149	struct mlxsw_sp_acl_tcam_region *region;150	struct mlxsw_sp_acl_tcam_region *region2; /* Used during migration */151	struct list_head list; /* Member of a TCAM group */152	struct list_head tlist; /* Member of a TCAM */153	struct list_head vchunk_list; /* List of vchunks under this vregion */154	struct mlxsw_afk_key_info *key_info;155	struct mlxsw_sp_acl_tcam *tcam;156	struct mlxsw_sp_acl_tcam_vgroup *vgroup;157	struct {158		struct delayed_work dw;159		struct mlxsw_sp_acl_tcam_rehash_ctx ctx;160	} rehash;161	struct mlxsw_sp *mlxsw_sp;162	refcount_t ref_count;163};164 165struct mlxsw_sp_acl_tcam_vchunk;166 167struct mlxsw_sp_acl_tcam_chunk {168	struct mlxsw_sp_acl_tcam_vchunk *vchunk;169	struct mlxsw_sp_acl_tcam_region *region;170	unsigned long priv[];171	/* priv has to be always the last item */172};173 174struct mlxsw_sp_acl_tcam_vchunk {175	struct mlxsw_sp_acl_tcam_chunk *chunk;176	struct mlxsw_sp_acl_tcam_chunk *chunk2; /* Used during migration */177	struct list_head list; /* Member of a TCAM vregion */178	struct rhash_head ht_node; /* Member of a chunk HT */179	struct list_head ventry_list;180	unsigned int priority; /* Priority within the vregion and group */181	struct mlxsw_sp_acl_tcam_vgroup *vgroup;182	struct mlxsw_sp_acl_tcam_vregion *vregion;183	refcount_t ref_count;184};185 186struct mlxsw_sp_acl_tcam_entry {187	struct mlxsw_sp_acl_tcam_ventry *ventry;188	struct mlxsw_sp_acl_tcam_chunk *chunk;189	unsigned long priv[];190	/* priv has to be always the last item */191};192 193struct mlxsw_sp_acl_tcam_ventry {194	struct mlxsw_sp_acl_tcam_entry *entry;195	struct list_head list; /* Member of a TCAM vchunk */196	struct mlxsw_sp_acl_tcam_vchunk *vchunk;197	struct mlxsw_sp_acl_rule_info *rulei;198};199 200static const struct rhashtable_params mlxsw_sp_acl_tcam_vchunk_ht_params = {201	.key_len = sizeof(unsigned int),202	.key_offset = offsetof(struct mlxsw_sp_acl_tcam_vchunk, priority),203	.head_offset = offsetof(struct mlxsw_sp_acl_tcam_vchunk, ht_node),204	.automatic_shrinking = true,205};206 207static int mlxsw_sp_acl_tcam_group_update(struct mlxsw_sp *mlxsw_sp,208					  struct mlxsw_sp_acl_tcam_group *group)209{210	struct mlxsw_sp_acl_tcam_region *region;211	char pagt_pl[MLXSW_REG_PAGT_LEN];212	int acl_index = 0;213 214	mlxsw_reg_pagt_pack(pagt_pl, group->id);215	list_for_each_entry(region, &group->region_list, list) {216		bool multi = false;217 218		/* Check if the next entry in the list has the same vregion. */219		if (region->list.next != &group->region_list &&220		    list_next_entry(region, list)->vregion == region->vregion)221			multi = true;222		mlxsw_reg_pagt_acl_id_pack(pagt_pl, acl_index++,223					   region->id, multi);224	}225	mlxsw_reg_pagt_size_set(pagt_pl, acl_index);226	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pagt), pagt_pl);227}228 229static int230mlxsw_sp_acl_tcam_group_add(struct mlxsw_sp_acl_tcam *tcam,231			    struct mlxsw_sp_acl_tcam_group *group)232{233	int err;234 235	group->tcam = tcam;236	INIT_LIST_HEAD(&group->region_list);237 238	err = mlxsw_sp_acl_tcam_group_id_get(tcam, &group->id);239	if (err)240		return err;241 242	mutex_init(&group->lock);243 244	return 0;245}246 247static void mlxsw_sp_acl_tcam_group_del(struct mlxsw_sp_acl_tcam_group *group)248{249	struct mlxsw_sp_acl_tcam *tcam = group->tcam;250 251	mutex_destroy(&group->lock);252	mlxsw_sp_acl_tcam_group_id_put(tcam, group->id);253	WARN_ON(!list_empty(&group->region_list));254}255 256static int257mlxsw_sp_acl_tcam_vgroup_add(struct mlxsw_sp *mlxsw_sp,258			     struct mlxsw_sp_acl_tcam *tcam,259			     struct mlxsw_sp_acl_tcam_vgroup *vgroup,260			     const struct mlxsw_sp_acl_tcam_pattern *patterns,261			     unsigned int patterns_count,262			     struct mlxsw_afk_element_usage *tmplt_elusage,263			     bool vregion_rehash_enabled,264			     unsigned int *p_min_prio,265			     unsigned int *p_max_prio)266{267	int err;268 269	vgroup->patterns = patterns;270	vgroup->patterns_count = patterns_count;271	vgroup->vregion_rehash_enabled = vregion_rehash_enabled;272	vgroup->p_min_prio = p_min_prio;273	vgroup->p_max_prio = p_max_prio;274 275	if (tmplt_elusage) {276		vgroup->tmplt_elusage_set = true;277		memcpy(&vgroup->tmplt_elusage, tmplt_elusage,278		       sizeof(vgroup->tmplt_elusage));279	}280	INIT_LIST_HEAD(&vgroup->vregion_list);281 282	err = mlxsw_sp_acl_tcam_group_add(tcam, &vgroup->group);283	if (err)284		return err;285 286	err = rhashtable_init(&vgroup->vchunk_ht,287			      &mlxsw_sp_acl_tcam_vchunk_ht_params);288	if (err)289		goto err_rhashtable_init;290 291	return 0;292 293err_rhashtable_init:294	mlxsw_sp_acl_tcam_group_del(&vgroup->group);295	return err;296}297 298static void299mlxsw_sp_acl_tcam_vgroup_del(struct mlxsw_sp_acl_tcam_vgroup *vgroup)300{301	rhashtable_destroy(&vgroup->vchunk_ht);302	mlxsw_sp_acl_tcam_group_del(&vgroup->group);303	WARN_ON(!list_empty(&vgroup->vregion_list));304}305 306static int307mlxsw_sp_acl_tcam_group_bind(struct mlxsw_sp *mlxsw_sp,308			     struct mlxsw_sp_acl_tcam_group *group,309			     struct mlxsw_sp_port *mlxsw_sp_port,310			     bool ingress)311{312	char ppbt_pl[MLXSW_REG_PPBT_LEN];313 314	mlxsw_reg_ppbt_pack(ppbt_pl, ingress ? MLXSW_REG_PXBT_E_IACL :315					       MLXSW_REG_PXBT_E_EACL,316			    MLXSW_REG_PXBT_OP_BIND, mlxsw_sp_port->local_port,317			    group->id);318	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppbt), ppbt_pl);319}320 321static void322mlxsw_sp_acl_tcam_group_unbind(struct mlxsw_sp *mlxsw_sp,323			       struct mlxsw_sp_acl_tcam_group *group,324			       struct mlxsw_sp_port *mlxsw_sp_port,325			       bool ingress)326{327	char ppbt_pl[MLXSW_REG_PPBT_LEN];328 329	mlxsw_reg_ppbt_pack(ppbt_pl, ingress ? MLXSW_REG_PXBT_E_IACL :330					       MLXSW_REG_PXBT_E_EACL,331			    MLXSW_REG_PXBT_OP_UNBIND, mlxsw_sp_port->local_port,332			    group->id);333	mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppbt), ppbt_pl);334}335 336static u16337mlxsw_sp_acl_tcam_group_id(struct mlxsw_sp_acl_tcam_group *group)338{339	return group->id;340}341 342static unsigned int343mlxsw_sp_acl_tcam_vregion_prio(struct mlxsw_sp_acl_tcam_vregion *vregion)344{345	struct mlxsw_sp_acl_tcam_vchunk *vchunk;346 347	if (list_empty(&vregion->vchunk_list))348		return 0;349	/* As a priority of a vregion, return priority of the first vchunk */350	vchunk = list_first_entry(&vregion->vchunk_list,351				  typeof(*vchunk), list);352	return vchunk->priority;353}354 355static unsigned int356mlxsw_sp_acl_tcam_vregion_max_prio(struct mlxsw_sp_acl_tcam_vregion *vregion)357{358	struct mlxsw_sp_acl_tcam_vchunk *vchunk;359 360	if (list_empty(&vregion->vchunk_list))361		return 0;362	vchunk = list_last_entry(&vregion->vchunk_list,363				 typeof(*vchunk), list);364	return vchunk->priority;365}366 367static void368mlxsw_sp_acl_tcam_vgroup_prio_update(struct mlxsw_sp_acl_tcam_vgroup *vgroup)369{370	struct mlxsw_sp_acl_tcam_vregion *vregion;371 372	if (list_empty(&vgroup->vregion_list))373		return;374	vregion = list_first_entry(&vgroup->vregion_list,375				   typeof(*vregion), list);376	*vgroup->p_min_prio = mlxsw_sp_acl_tcam_vregion_prio(vregion);377	vregion = list_last_entry(&vgroup->vregion_list,378				  typeof(*vregion), list);379	*vgroup->p_max_prio = mlxsw_sp_acl_tcam_vregion_max_prio(vregion);380}381 382static int383mlxsw_sp_acl_tcam_group_region_attach(struct mlxsw_sp *mlxsw_sp,384				      struct mlxsw_sp_acl_tcam_group *group,385				      struct mlxsw_sp_acl_tcam_region *region,386				      unsigned int priority,387				      struct mlxsw_sp_acl_tcam_region *next_region)388{389	struct mlxsw_sp_acl_tcam_region *region2;390	struct list_head *pos;391	int err;392 393	mutex_lock(&group->lock);394	if (group->region_count == group->tcam->max_group_size) {395		err = -ENOBUFS;396		goto err_region_count_check;397	}398 399	if (next_region) {400		/* If the next region is defined, place the new one401		 * before it. The next one is a sibling.402		 */403		pos = &next_region->list;404	} else {405		/* Position the region inside the list according to priority */406		list_for_each(pos, &group->region_list) {407			region2 = list_entry(pos, typeof(*region2), list);408			if (mlxsw_sp_acl_tcam_vregion_prio(region2->vregion) >409			    priority)410				break;411		}412	}413	list_add_tail(&region->list, pos);414	region->group = group;415 416	err = mlxsw_sp_acl_tcam_group_update(mlxsw_sp, group);417	if (err)418		goto err_group_update;419 420	group->region_count++;421	mutex_unlock(&group->lock);422	return 0;423 424err_group_update:425	list_del(&region->list);426err_region_count_check:427	mutex_unlock(&group->lock);428	return err;429}430 431static void432mlxsw_sp_acl_tcam_group_region_detach(struct mlxsw_sp *mlxsw_sp,433				      struct mlxsw_sp_acl_tcam_region *region)434{435	struct mlxsw_sp_acl_tcam_group *group = region->group;436 437	mutex_lock(&group->lock);438	list_del(&region->list);439	group->region_count--;440	mlxsw_sp_acl_tcam_group_update(mlxsw_sp, group);441	mutex_unlock(&group->lock);442}443 444static int445mlxsw_sp_acl_tcam_vgroup_vregion_attach(struct mlxsw_sp *mlxsw_sp,446					struct mlxsw_sp_acl_tcam_vgroup *vgroup,447					struct mlxsw_sp_acl_tcam_vregion *vregion,448					unsigned int priority)449{450	struct mlxsw_sp_acl_tcam_vregion *vregion2;451	struct list_head *pos;452	int err;453 454	/* Position the vregion inside the list according to priority */455	list_for_each(pos, &vgroup->vregion_list) {456		vregion2 = list_entry(pos, typeof(*vregion2), list);457		if (mlxsw_sp_acl_tcam_vregion_prio(vregion2) > priority)458			break;459	}460	list_add_tail(&vregion->list, pos);461 462	err = mlxsw_sp_acl_tcam_group_region_attach(mlxsw_sp, &vgroup->group,463						    vregion->region,464						    priority, NULL);465	if (err)466		goto err_region_attach;467 468	return 0;469 470err_region_attach:471	list_del(&vregion->list);472	return err;473}474 475static void476mlxsw_sp_acl_tcam_vgroup_vregion_detach(struct mlxsw_sp *mlxsw_sp,477					struct mlxsw_sp_acl_tcam_vregion *vregion)478{479	list_del(&vregion->list);480	if (vregion->region2)481		mlxsw_sp_acl_tcam_group_region_detach(mlxsw_sp,482						      vregion->region2);483	mlxsw_sp_acl_tcam_group_region_detach(mlxsw_sp, vregion->region);484}485 486static struct mlxsw_sp_acl_tcam_vregion *487mlxsw_sp_acl_tcam_vgroup_vregion_find(struct mlxsw_sp_acl_tcam_vgroup *vgroup,488				      unsigned int priority,489				      struct mlxsw_afk_element_usage *elusage,490				      bool *p_need_split)491{492	struct mlxsw_sp_acl_tcam_vregion *vregion, *vregion2;493	struct list_head *pos;494	bool issubset;495 496	list_for_each(pos, &vgroup->vregion_list) {497		vregion = list_entry(pos, typeof(*vregion), list);498 499		/* First, check if the requested priority does not rather belong500		 * under some of the next vregions.501		 */502		if (pos->next != &vgroup->vregion_list) { /* not last */503			vregion2 = list_entry(pos->next, typeof(*vregion2),504					      list);505			if (priority >=506			    mlxsw_sp_acl_tcam_vregion_prio(vregion2))507				continue;508		}509 510		issubset = mlxsw_afk_key_info_subset(vregion->key_info,511						     elusage);512 513		/* If requested element usage would not fit and the priority514		 * is lower than the currently inspected vregion we cannot515		 * use this region, so return NULL to indicate new vregion has516		 * to be created.517		 */518		if (!issubset &&519		    priority < mlxsw_sp_acl_tcam_vregion_prio(vregion))520			return NULL;521 522		/* If requested element usage would not fit and the priority523		 * is higher than the currently inspected vregion we cannot524		 * use this vregion. There is still some hope that the next525		 * vregion would be the fit. So let it be processed and526		 * eventually break at the check right above this.527		 */528		if (!issubset &&529		    priority > mlxsw_sp_acl_tcam_vregion_max_prio(vregion))530			continue;531 532		/* Indicate if the vregion needs to be split in order to add533		 * the requested priority. Split is needed when requested534		 * element usage won't fit into the found vregion.535		 */536		*p_need_split = !issubset;537		return vregion;538	}539	return NULL; /* New vregion has to be created. */540}541 542static void543mlxsw_sp_acl_tcam_vgroup_use_patterns(struct mlxsw_sp_acl_tcam_vgroup *vgroup,544				      struct mlxsw_afk_element_usage *elusage,545				      struct mlxsw_afk_element_usage *out)546{547	const struct mlxsw_sp_acl_tcam_pattern *pattern;548	int i;549 550	/* In case the template is set, we don't have to look up the pattern551	 * and just use the template.552	 */553	if (vgroup->tmplt_elusage_set) {554		memcpy(out, &vgroup->tmplt_elusage, sizeof(*out));555		WARN_ON(!mlxsw_afk_element_usage_subset(elusage, out));556		return;557	}558 559	for (i = 0; i < vgroup->patterns_count; i++) {560		pattern = &vgroup->patterns[i];561		mlxsw_afk_element_usage_fill(out, pattern->elements,562					     pattern->elements_count);563		if (mlxsw_afk_element_usage_subset(elusage, out))564			return;565	}566	memcpy(out, elusage, sizeof(*out));567}568 569static int570mlxsw_sp_acl_tcam_region_alloc(struct mlxsw_sp *mlxsw_sp,571			       struct mlxsw_sp_acl_tcam_region *region)572{573	struct mlxsw_afk_key_info *key_info = region->key_info;574	char ptar_pl[MLXSW_REG_PTAR_LEN];575	unsigned int encodings_count;576	int i;577	int err;578 579	mlxsw_reg_ptar_pack(ptar_pl, MLXSW_REG_PTAR_OP_ALLOC,580			    region->key_type,581			    MLXSW_SP_ACL_TCAM_REGION_BASE_COUNT,582			    region->id, region->tcam_region_info);583	encodings_count = mlxsw_afk_key_info_blocks_count_get(key_info);584	for (i = 0; i < encodings_count; i++) {585		u16 encoding;586 587		encoding = mlxsw_afk_key_info_block_encoding_get(key_info, i);588		mlxsw_reg_ptar_key_id_pack(ptar_pl, i, encoding);589	}590	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptar), ptar_pl);591	if (err)592		return err;593	mlxsw_reg_ptar_unpack(ptar_pl, region->tcam_region_info);594	return 0;595}596 597static void598mlxsw_sp_acl_tcam_region_free(struct mlxsw_sp *mlxsw_sp,599			      struct mlxsw_sp_acl_tcam_region *region)600{601	char ptar_pl[MLXSW_REG_PTAR_LEN];602 603	mlxsw_reg_ptar_pack(ptar_pl, MLXSW_REG_PTAR_OP_FREE,604			    region->key_type, 0, region->id,605			    region->tcam_region_info);606	mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptar), ptar_pl);607}608 609static int610mlxsw_sp_acl_tcam_region_enable(struct mlxsw_sp *mlxsw_sp,611				struct mlxsw_sp_acl_tcam_region *region)612{613	char pacl_pl[MLXSW_REG_PACL_LEN];614 615	mlxsw_reg_pacl_pack(pacl_pl, region->id, true,616			    region->tcam_region_info);617	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pacl), pacl_pl);618}619 620static void621mlxsw_sp_acl_tcam_region_disable(struct mlxsw_sp *mlxsw_sp,622				 struct mlxsw_sp_acl_tcam_region *region)623{624	char pacl_pl[MLXSW_REG_PACL_LEN];625 626	mlxsw_reg_pacl_pack(pacl_pl, region->id, false,627			    region->tcam_region_info);628	mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pacl), pacl_pl);629}630 631static struct mlxsw_sp_acl_tcam_region *632mlxsw_sp_acl_tcam_region_create(struct mlxsw_sp *mlxsw_sp,633				struct mlxsw_sp_acl_tcam *tcam,634				struct mlxsw_sp_acl_tcam_vregion *vregion,635				void *hints_priv)636{637	const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;638	struct mlxsw_sp_acl_tcam_region *region;639	int err;640 641	region = kzalloc(sizeof(*region) + ops->region_priv_size, GFP_KERNEL);642	if (!region)643		return ERR_PTR(-ENOMEM);644	region->mlxsw_sp = mlxsw_sp;645	region->vregion = vregion;646	region->key_info = vregion->key_info;647 648	err = mlxsw_sp_acl_tcam_region_id_get(tcam, &region->id);649	if (err)650		goto err_region_id_get;651 652	err = ops->region_associate(mlxsw_sp, region);653	if (err)654		goto err_tcam_region_associate;655 656	region->key_type = ops->key_type;657	err = mlxsw_sp_acl_tcam_region_alloc(mlxsw_sp, region);658	if (err)659		goto err_tcam_region_alloc;660 661	err = mlxsw_sp_acl_tcam_region_enable(mlxsw_sp, region);662	if (err)663		goto err_tcam_region_enable;664 665	err = ops->region_init(mlxsw_sp, region->priv, tcam->priv,666			       region, hints_priv);667	if (err)668		goto err_tcam_region_init;669 670	return region;671 672err_tcam_region_init:673	mlxsw_sp_acl_tcam_region_disable(mlxsw_sp, region);674err_tcam_region_enable:675	mlxsw_sp_acl_tcam_region_free(mlxsw_sp, region);676err_tcam_region_alloc:677err_tcam_region_associate:678	mlxsw_sp_acl_tcam_region_id_put(tcam, region->id);679err_region_id_get:680	kfree(region);681	return ERR_PTR(err);682}683 684static void685mlxsw_sp_acl_tcam_region_destroy(struct mlxsw_sp *mlxsw_sp,686				 struct mlxsw_sp_acl_tcam_region *region)687{688	struct mlxsw_sp_acl_tcam *tcam = mlxsw_sp_acl_to_tcam(mlxsw_sp->acl);689	const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;690 691	ops->region_fini(mlxsw_sp, region->priv);692	mlxsw_sp_acl_tcam_region_disable(mlxsw_sp, region);693	mlxsw_sp_acl_tcam_region_free(mlxsw_sp, region);694	mlxsw_sp_acl_tcam_region_id_put(tcam, region->id);695	kfree(region);696}697 698static void699mlxsw_sp_acl_tcam_vregion_rehash_work_schedule(struct mlxsw_sp_acl_tcam_vregion *vregion)700{701	unsigned long interval = vregion->tcam->vregion_rehash_intrvl;702 703	if (!interval)704		return;705	mlxsw_core_schedule_dw(&vregion->rehash.dw,706			       msecs_to_jiffies(interval));707}708 709static void710mlxsw_sp_acl_tcam_vregion_rehash(struct mlxsw_sp *mlxsw_sp,711				 struct mlxsw_sp_acl_tcam_vregion *vregion,712				 int *credits);713 714static void mlxsw_sp_acl_tcam_vregion_rehash_work(struct work_struct *work)715{716	struct mlxsw_sp_acl_tcam_vregion *vregion =717		container_of(work, struct mlxsw_sp_acl_tcam_vregion,718			     rehash.dw.work);719	int credits = MLXSW_SP_ACL_TCAM_VREGION_REHASH_CREDITS;720 721	mutex_lock(&vregion->lock);722	mlxsw_sp_acl_tcam_vregion_rehash(vregion->mlxsw_sp, vregion, &credits);723	mutex_unlock(&vregion->lock);724	if (credits < 0)725		/* Rehash gone out of credits so it was interrupted.726		 * Schedule the work as soon as possible to continue.727		 */728		mlxsw_core_schedule_dw(&vregion->rehash.dw, 0);729	else730		mlxsw_sp_acl_tcam_vregion_rehash_work_schedule(vregion);731}732 733static void734mlxsw_sp_acl_tcam_rehash_ctx_vchunk_reset(struct mlxsw_sp_acl_tcam_rehash_ctx *ctx)735{736	/* The entry markers are relative to the current chunk and therefore737	 * needs to be reset together with the chunk marker.738	 */739	ctx->current_vchunk = NULL;740	ctx->start_ventry = NULL;741	ctx->stop_ventry = NULL;742}743 744static void745mlxsw_sp_acl_tcam_rehash_ctx_vchunk_changed(struct mlxsw_sp_acl_tcam_vchunk *vchunk)746{747	struct mlxsw_sp_acl_tcam_vregion *vregion = vchunk->vregion;748 749	/* If a rule was added or deleted from vchunk which is currently750	 * under rehash migration, we have to reset the ventry pointers751	 * to make sure all rules are properly migrated.752	 */753	if (vregion->rehash.ctx.current_vchunk == vchunk) {754		vregion->rehash.ctx.start_ventry = NULL;755		vregion->rehash.ctx.stop_ventry = NULL;756	}757}758 759static void760mlxsw_sp_acl_tcam_rehash_ctx_vregion_changed(struct mlxsw_sp_acl_tcam_vregion *vregion)761{762	/* If a chunk was added or deleted from vregion we have to reset763	 * the current chunk pointer to make sure all chunks764	 * are properly migrated.765	 */766	mlxsw_sp_acl_tcam_rehash_ctx_vchunk_reset(&vregion->rehash.ctx);767}768 769static struct mlxsw_sp_acl_tcam_vregion *770mlxsw_sp_acl_tcam_vregion_create(struct mlxsw_sp *mlxsw_sp,771				 struct mlxsw_sp_acl_tcam_vgroup *vgroup,772				 unsigned int priority,773				 struct mlxsw_afk_element_usage *elusage)774{775	const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;776	struct mlxsw_afk *afk = mlxsw_sp_acl_afk(mlxsw_sp->acl);777	struct mlxsw_sp_acl_tcam *tcam = vgroup->group.tcam;778	struct mlxsw_sp_acl_tcam_vregion *vregion;779	int err;780 781	vregion = kzalloc(sizeof(*vregion), GFP_KERNEL);782	if (!vregion)783		return ERR_PTR(-ENOMEM);784	INIT_LIST_HEAD(&vregion->vchunk_list);785	mutex_init(&vregion->lock);786	vregion->tcam = tcam;787	vregion->mlxsw_sp = mlxsw_sp;788	vregion->vgroup = vgroup;789	refcount_set(&vregion->ref_count, 1);790 791	vregion->key_info = mlxsw_afk_key_info_get(afk, elusage);792	if (IS_ERR(vregion->key_info)) {793		err = PTR_ERR(vregion->key_info);794		goto err_key_info_get;795	}796 797	vregion->region = mlxsw_sp_acl_tcam_region_create(mlxsw_sp, tcam,798							  vregion, NULL);799	if (IS_ERR(vregion->region)) {800		err = PTR_ERR(vregion->region);801		goto err_region_create;802	}803 804	err = mlxsw_sp_acl_tcam_vgroup_vregion_attach(mlxsw_sp, vgroup, vregion,805						      priority);806	if (err)807		goto err_vgroup_vregion_attach;808 809	if (vgroup->vregion_rehash_enabled && ops->region_rehash_hints_get) {810		/* Create the delayed work for vregion periodic rehash */811		INIT_DELAYED_WORK(&vregion->rehash.dw,812				  mlxsw_sp_acl_tcam_vregion_rehash_work);813		mlxsw_sp_acl_tcam_vregion_rehash_work_schedule(vregion);814		mutex_lock(&tcam->lock);815		list_add_tail(&vregion->tlist, &tcam->vregion_list);816		mutex_unlock(&tcam->lock);817	}818 819	return vregion;820 821err_vgroup_vregion_attach:822	mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, vregion->region);823err_region_create:824	mlxsw_afk_key_info_put(vregion->key_info);825err_key_info_get:826	kfree(vregion);827	return ERR_PTR(err);828}829 830static void831mlxsw_sp_acl_tcam_vregion_destroy(struct mlxsw_sp *mlxsw_sp,832				  struct mlxsw_sp_acl_tcam_vregion *vregion)833{834	const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;835	struct mlxsw_sp_acl_tcam_vgroup *vgroup = vregion->vgroup;836	struct mlxsw_sp_acl_tcam *tcam = vregion->tcam;837 838	if (vgroup->vregion_rehash_enabled && ops->region_rehash_hints_get) {839		struct mlxsw_sp_acl_tcam_rehash_ctx *ctx = &vregion->rehash.ctx;840 841		mutex_lock(&tcam->lock);842		list_del(&vregion->tlist);843		mutex_unlock(&tcam->lock);844		if (cancel_delayed_work_sync(&vregion->rehash.dw) &&845		    ctx->hints_priv)846			ops->region_rehash_hints_put(ctx->hints_priv);847	}848	mlxsw_sp_acl_tcam_vgroup_vregion_detach(mlxsw_sp, vregion);849	if (vregion->region2)850		mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, vregion->region2);851	mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, vregion->region);852	mlxsw_afk_key_info_put(vregion->key_info);853	mutex_destroy(&vregion->lock);854	kfree(vregion);855}856 857static struct mlxsw_sp_acl_tcam_vregion *858mlxsw_sp_acl_tcam_vregion_get(struct mlxsw_sp *mlxsw_sp,859			      struct mlxsw_sp_acl_tcam_vgroup *vgroup,860			      unsigned int priority,861			      struct mlxsw_afk_element_usage *elusage)862{863	struct mlxsw_afk_element_usage vregion_elusage;864	struct mlxsw_sp_acl_tcam_vregion *vregion;865	bool need_split;866 867	vregion = mlxsw_sp_acl_tcam_vgroup_vregion_find(vgroup, priority,868							elusage, &need_split);869	if (vregion) {870		if (need_split) {871			/* According to priority, new vchunk should belong to872			 * an existing vregion. However, this vchunk needs873			 * elements that vregion does not contain. We need874			 * to split the existing vregion into two and create875			 * a new vregion for the new vchunk in between.876			 * This is not supported now.877			 */878			return ERR_PTR(-EOPNOTSUPP);879		}880		refcount_inc(&vregion->ref_count);881		return vregion;882	}883 884	mlxsw_sp_acl_tcam_vgroup_use_patterns(vgroup, elusage,885					      &vregion_elusage);886 887	return mlxsw_sp_acl_tcam_vregion_create(mlxsw_sp, vgroup, priority,888						&vregion_elusage);889}890 891static void892mlxsw_sp_acl_tcam_vregion_put(struct mlxsw_sp *mlxsw_sp,893			      struct mlxsw_sp_acl_tcam_vregion *vregion)894{895	if (!refcount_dec_and_test(&vregion->ref_count))896		return;897	mlxsw_sp_acl_tcam_vregion_destroy(mlxsw_sp, vregion);898}899 900static struct mlxsw_sp_acl_tcam_chunk *901mlxsw_sp_acl_tcam_chunk_create(struct mlxsw_sp *mlxsw_sp,902			       struct mlxsw_sp_acl_tcam_vchunk *vchunk,903			       struct mlxsw_sp_acl_tcam_region *region)904{905	const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;906	struct mlxsw_sp_acl_tcam_chunk *chunk;907 908	chunk = kzalloc(sizeof(*chunk) + ops->chunk_priv_size, GFP_KERNEL);909	if (!chunk)910		return ERR_PTR(-ENOMEM);911	chunk->vchunk = vchunk;912	chunk->region = region;913 914	ops->chunk_init(region->priv, chunk->priv, vchunk->priority);915	return chunk;916}917 918static void919mlxsw_sp_acl_tcam_chunk_destroy(struct mlxsw_sp *mlxsw_sp,920				struct mlxsw_sp_acl_tcam_chunk *chunk)921{922	const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;923 924	ops->chunk_fini(chunk->priv);925	kfree(chunk);926}927 928static struct mlxsw_sp_acl_tcam_vchunk *929mlxsw_sp_acl_tcam_vchunk_create(struct mlxsw_sp *mlxsw_sp,930				struct mlxsw_sp_acl_tcam_vgroup *vgroup,931				unsigned int priority,932				struct mlxsw_afk_element_usage *elusage)933{934	struct mlxsw_sp_acl_tcam_vchunk *vchunk, *vchunk2;935	struct mlxsw_sp_acl_tcam_vregion *vregion;936	struct list_head *pos;937	int err;938 939	if (priority == MLXSW_SP_ACL_TCAM_CATCHALL_PRIO)940		return ERR_PTR(-EINVAL);941 942	vchunk = kzalloc(sizeof(*vchunk), GFP_KERNEL);943	if (!vchunk)944		return ERR_PTR(-ENOMEM);945	INIT_LIST_HEAD(&vchunk->ventry_list);946	vchunk->priority = priority;947	vchunk->vgroup = vgroup;948	refcount_set(&vchunk->ref_count, 1);949 950	vregion = mlxsw_sp_acl_tcam_vregion_get(mlxsw_sp, vgroup,951						priority, elusage);952	if (IS_ERR(vregion)) {953		err = PTR_ERR(vregion);954		goto err_vregion_get;955	}956 957	vchunk->vregion = vregion;958 959	err = rhashtable_insert_fast(&vgroup->vchunk_ht, &vchunk->ht_node,960				     mlxsw_sp_acl_tcam_vchunk_ht_params);961	if (err)962		goto err_rhashtable_insert;963 964	mutex_lock(&vregion->lock);965	vchunk->chunk = mlxsw_sp_acl_tcam_chunk_create(mlxsw_sp, vchunk,966						       vchunk->vregion->region);967	if (IS_ERR(vchunk->chunk)) {968		mutex_unlock(&vregion->lock);969		err = PTR_ERR(vchunk->chunk);970		goto err_chunk_create;971	}972 973	mlxsw_sp_acl_tcam_rehash_ctx_vregion_changed(vregion);974 975	/* Position the vchunk inside the list according to priority */976	list_for_each(pos, &vregion->vchunk_list) {977		vchunk2 = list_entry(pos, typeof(*vchunk2), list);978		if (vchunk2->priority > priority)979			break;980	}981	list_add_tail(&vchunk->list, pos);982	mutex_unlock(&vregion->lock);983	mlxsw_sp_acl_tcam_vgroup_prio_update(vgroup);984 985	return vchunk;986 987err_chunk_create:988	rhashtable_remove_fast(&vgroup->vchunk_ht, &vchunk->ht_node,989			       mlxsw_sp_acl_tcam_vchunk_ht_params);990err_rhashtable_insert:991	mlxsw_sp_acl_tcam_vregion_put(mlxsw_sp, vregion);992err_vregion_get:993	kfree(vchunk);994	return ERR_PTR(err);995}996 997static void998mlxsw_sp_acl_tcam_vchunk_destroy(struct mlxsw_sp *mlxsw_sp,999				 struct mlxsw_sp_acl_tcam_vchunk *vchunk)1000{1001	struct mlxsw_sp_acl_tcam_vregion *vregion = vchunk->vregion;1002	struct mlxsw_sp_acl_tcam_vgroup *vgroup = vchunk->vgroup;1003 1004	mutex_lock(&vregion->lock);1005	mlxsw_sp_acl_tcam_rehash_ctx_vregion_changed(vregion);1006	list_del(&vchunk->list);1007	if (vchunk->chunk2)1008		mlxsw_sp_acl_tcam_chunk_destroy(mlxsw_sp, vchunk->chunk2);1009	mlxsw_sp_acl_tcam_chunk_destroy(mlxsw_sp, vchunk->chunk);1010	mutex_unlock(&vregion->lock);1011	rhashtable_remove_fast(&vgroup->vchunk_ht, &vchunk->ht_node,1012			       mlxsw_sp_acl_tcam_vchunk_ht_params);1013	mlxsw_sp_acl_tcam_vregion_put(mlxsw_sp, vchunk->vregion);1014	kfree(vchunk);1015	mlxsw_sp_acl_tcam_vgroup_prio_update(vgroup);1016}1017 1018static struct mlxsw_sp_acl_tcam_vchunk *1019mlxsw_sp_acl_tcam_vchunk_get(struct mlxsw_sp *mlxsw_sp,1020			     struct mlxsw_sp_acl_tcam_vgroup *vgroup,1021			     unsigned int priority,1022			     struct mlxsw_afk_element_usage *elusage)1023{1024	struct mlxsw_sp_acl_tcam_vchunk *vchunk;1025 1026	vchunk = rhashtable_lookup_fast(&vgroup->vchunk_ht, &priority,1027					mlxsw_sp_acl_tcam_vchunk_ht_params);1028	if (vchunk) {1029		if (WARN_ON(!mlxsw_afk_key_info_subset(vchunk->vregion->key_info,1030						       elusage)))1031			return ERR_PTR(-EINVAL);1032		refcount_inc(&vchunk->ref_count);1033		return vchunk;1034	}1035	return mlxsw_sp_acl_tcam_vchunk_create(mlxsw_sp, vgroup,1036					       priority, elusage);1037}1038 1039static void1040mlxsw_sp_acl_tcam_vchunk_put(struct mlxsw_sp *mlxsw_sp,1041			     struct mlxsw_sp_acl_tcam_vchunk *vchunk)1042{1043	if (!refcount_dec_and_test(&vchunk->ref_count))1044		return;1045	mlxsw_sp_acl_tcam_vchunk_destroy(mlxsw_sp, vchunk);1046}1047 1048static struct mlxsw_sp_acl_tcam_entry *1049mlxsw_sp_acl_tcam_entry_create(struct mlxsw_sp *mlxsw_sp,1050			       struct mlxsw_sp_acl_tcam_ventry *ventry,1051			       struct mlxsw_sp_acl_tcam_chunk *chunk)1052{1053	const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;1054	struct mlxsw_sp_acl_tcam_entry *entry;1055	int err;1056 1057	entry = kzalloc(sizeof(*entry) + ops->entry_priv_size, GFP_KERNEL);1058	if (!entry)1059		return ERR_PTR(-ENOMEM);1060	entry->ventry = ventry;1061	entry->chunk = chunk;1062 1063	err = ops->entry_add(mlxsw_sp, chunk->region->priv, chunk->priv,1064			     entry->priv, ventry->rulei);1065	if (err)1066		goto err_entry_add;1067 1068	return entry;1069 1070err_entry_add:1071	kfree(entry);1072	return ERR_PTR(err);1073}1074 1075static void mlxsw_sp_acl_tcam_entry_destroy(struct mlxsw_sp *mlxsw_sp,1076					    struct mlxsw_sp_acl_tcam_entry *entry)1077{1078	const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;1079 1080	ops->entry_del(mlxsw_sp, entry->chunk->region->priv,1081		       entry->chunk->priv, entry->priv);1082	kfree(entry);1083}1084 1085static int1086mlxsw_sp_acl_tcam_entry_action_replace(struct mlxsw_sp *mlxsw_sp,1087				       struct mlxsw_sp_acl_tcam_region *region,1088				       struct mlxsw_sp_acl_tcam_entry *entry,1089				       struct mlxsw_sp_acl_rule_info *rulei)1090{1091	const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;1092 1093	return ops->entry_action_replace(mlxsw_sp, region->priv,1094					 entry->priv, rulei);1095}1096 1097static int1098mlxsw_sp_acl_tcam_entry_activity_get(struct mlxsw_sp *mlxsw_sp,1099				     struct mlxsw_sp_acl_tcam_entry *entry,1100				     bool *activity)1101{1102	const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;1103 1104	return ops->entry_activity_get(mlxsw_sp, entry->chunk->region->priv,1105				       entry->priv, activity);1106}1107 1108static int mlxsw_sp_acl_tcam_ventry_add(struct mlxsw_sp *mlxsw_sp,1109					struct mlxsw_sp_acl_tcam_vgroup *vgroup,1110					struct mlxsw_sp_acl_tcam_ventry *ventry,1111					struct mlxsw_sp_acl_rule_info *rulei)1112{1113	struct mlxsw_sp_acl_tcam_vregion *vregion;1114	struct mlxsw_sp_acl_tcam_vchunk *vchunk;1115	int err;1116 1117	vchunk = mlxsw_sp_acl_tcam_vchunk_get(mlxsw_sp, vgroup, rulei->priority,1118					      &rulei->values.elusage);1119	if (IS_ERR(vchunk))1120		return PTR_ERR(vchunk);1121 1122	ventry->vchunk = vchunk;1123	ventry->rulei = rulei;1124	vregion = vchunk->vregion;1125 1126	mutex_lock(&vregion->lock);1127	ventry->entry = mlxsw_sp_acl_tcam_entry_create(mlxsw_sp, ventry,1128						       vchunk->chunk);1129	if (IS_ERR(ventry->entry)) {1130		mutex_unlock(&vregion->lock);1131		err = PTR_ERR(ventry->entry);1132		goto err_entry_create;1133	}1134 1135	list_add_tail(&ventry->list, &vchunk->ventry_list);1136	mlxsw_sp_acl_tcam_rehash_ctx_vchunk_changed(vchunk);1137	mutex_unlock(&vregion->lock);1138 1139	return 0;1140 1141err_entry_create:1142	mlxsw_sp_acl_tcam_vchunk_put(mlxsw_sp, vchunk);1143	return err;1144}1145 1146static void mlxsw_sp_acl_tcam_ventry_del(struct mlxsw_sp *mlxsw_sp,1147					 struct mlxsw_sp_acl_tcam_ventry *ventry)1148{1149	struct mlxsw_sp_acl_tcam_vchunk *vchunk = ventry->vchunk;1150	struct mlxsw_sp_acl_tcam_vregion *vregion = vchunk->vregion;1151 1152	mutex_lock(&vregion->lock);1153	mlxsw_sp_acl_tcam_rehash_ctx_vchunk_changed(vchunk);1154	list_del(&ventry->list);1155	mlxsw_sp_acl_tcam_entry_destroy(mlxsw_sp, ventry->entry);1156	mutex_unlock(&vregion->lock);1157	mlxsw_sp_acl_tcam_vchunk_put(mlxsw_sp, vchunk);1158}1159 1160static int1161mlxsw_sp_acl_tcam_ventry_action_replace(struct mlxsw_sp *mlxsw_sp,1162					struct mlxsw_sp_acl_tcam_ventry *ventry,1163					struct mlxsw_sp_acl_rule_info *rulei)1164{1165	struct mlxsw_sp_acl_tcam_vchunk *vchunk = ventry->vchunk;1166 1167	return mlxsw_sp_acl_tcam_entry_action_replace(mlxsw_sp,1168						      vchunk->vregion->region,1169						      ventry->entry, rulei);1170}1171 1172static int1173mlxsw_sp_acl_tcam_ventry_activity_get(struct mlxsw_sp *mlxsw_sp,1174				      struct mlxsw_sp_acl_tcam_ventry *ventry,1175				      bool *activity)1176{1177	struct mlxsw_sp_acl_tcam_vregion *vregion = ventry->vchunk->vregion;1178	int err;1179 1180	mutex_lock(&vregion->lock);1181	err = mlxsw_sp_acl_tcam_entry_activity_get(mlxsw_sp, ventry->entry,1182						   activity);1183	mutex_unlock(&vregion->lock);1184	return err;1185}1186 1187static int1188mlxsw_sp_acl_tcam_ventry_migrate(struct mlxsw_sp *mlxsw_sp,1189				 struct mlxsw_sp_acl_tcam_ventry *ventry,1190				 struct mlxsw_sp_acl_tcam_chunk *chunk,1191				 int *credits)1192{1193	struct mlxsw_sp_acl_tcam_entry *new_entry;1194 1195	/* First check if the entry is not already where we want it to be. */1196	if (ventry->entry->chunk == chunk)1197		return 0;1198 1199	if (--(*credits) < 0)1200		return 0;1201 1202	new_entry = mlxsw_sp_acl_tcam_entry_create(mlxsw_sp, ventry, chunk);1203	if (IS_ERR(new_entry))1204		return PTR_ERR(new_entry);1205	mlxsw_sp_acl_tcam_entry_destroy(mlxsw_sp, ventry->entry);1206	ventry->entry = new_entry;1207	return 0;1208}1209 1210static int1211mlxsw_sp_acl_tcam_vchunk_migrate_start(struct mlxsw_sp *mlxsw_sp,1212				       struct mlxsw_sp_acl_tcam_vchunk *vchunk,1213				       struct mlxsw_sp_acl_tcam_region *region,1214				       struct mlxsw_sp_acl_tcam_rehash_ctx *ctx)1215{1216	struct mlxsw_sp_acl_tcam_chunk *new_chunk;1217 1218	WARN_ON(vchunk->chunk2);1219 1220	new_chunk = mlxsw_sp_acl_tcam_chunk_create(mlxsw_sp, vchunk, region);1221	if (IS_ERR(new_chunk))1222		return PTR_ERR(new_chunk);1223	vchunk->chunk2 = vchunk->chunk;1224	vchunk->chunk = new_chunk;1225	ctx->current_vchunk = vchunk;1226	ctx->start_ventry = NULL;1227	ctx->stop_ventry = NULL;1228	return 0;1229}1230 1231static void1232mlxsw_sp_acl_tcam_vchunk_migrate_end(struct mlxsw_sp *mlxsw_sp,1233				     struct mlxsw_sp_acl_tcam_vchunk *vchunk,1234				     struct mlxsw_sp_acl_tcam_rehash_ctx *ctx)1235{1236	mlxsw_sp_acl_tcam_chunk_destroy(mlxsw_sp, vchunk->chunk2);1237	vchunk->chunk2 = NULL;1238	mlxsw_sp_acl_tcam_rehash_ctx_vchunk_reset(ctx);1239}1240 1241static int1242mlxsw_sp_acl_tcam_vchunk_migrate_one(struct mlxsw_sp *mlxsw_sp,1243				     struct mlxsw_sp_acl_tcam_vchunk *vchunk,1244				     struct mlxsw_sp_acl_tcam_region *region,1245				     struct mlxsw_sp_acl_tcam_rehash_ctx *ctx,1246				     int *credits)1247{1248	struct mlxsw_sp_acl_tcam_ventry *ventry;1249	int err;1250 1251	if (vchunk->chunk->region != region) {1252		err = mlxsw_sp_acl_tcam_vchunk_migrate_start(mlxsw_sp, vchunk,1253							     region, ctx);1254		if (err)1255			return err;1256	} else if (!vchunk->chunk2) {1257		/* The chunk is already as it should be, nothing to do. */1258		return 0;1259	}1260 1261	if (list_empty(&vchunk->ventry_list))1262		goto out;1263 1264	/* If the migration got interrupted, we have the ventry to start from1265	 * stored in context.1266	 */1267	if (ctx->start_ventry)1268		ventry = ctx->start_ventry;1269	else1270		ventry = list_first_entry(&vchunk->ventry_list,1271					  typeof(*ventry), list);1272 1273	WARN_ON(ventry->vchunk != vchunk);1274 1275	list_for_each_entry_from(ventry, &vchunk->ventry_list, list) {1276		/* During rollback, once we reach the ventry that failed1277		 * to migrate, we are done.1278		 */1279		if (ventry == ctx->stop_ventry)1280			break;1281 1282		err = mlxsw_sp_acl_tcam_ventry_migrate(mlxsw_sp, ventry,1283						       vchunk->chunk, credits);1284		if (err) {1285			if (ctx->this_is_rollback) {1286				/* Save the ventry which we ended with and try1287				 * to continue later on.1288				 */1289				ctx->start_ventry = ventry;1290				return err;1291			}1292			/* Swap the chunk and chunk2 pointers so the follow-up1293			 * rollback call will see the original chunk pointer1294			 * in vchunk->chunk.1295			 */1296			swap(vchunk->chunk, vchunk->chunk2);1297			/* The rollback has to be done from beginning of the1298			 * chunk, that is why we have to null the start_ventry.1299			 * However, we know where to stop the rollback,1300			 * at the current ventry.1301			 */1302			ctx->start_ventry = NULL;1303			ctx->stop_ventry = ventry;1304			return err;1305		} else if (*credits < 0) {1306			/* We are out of credits, the rest of the ventries1307			 * will be migrated later. Save the ventry1308			 * which we ended with.1309			 */1310			ctx->start_ventry = ventry;1311			return 0;1312		}1313	}1314 1315out:1316	mlxsw_sp_acl_tcam_vchunk_migrate_end(mlxsw_sp, vchunk, ctx);1317	return 0;1318}1319 1320static int1321mlxsw_sp_acl_tcam_vchunk_migrate_all(struct mlxsw_sp *mlxsw_sp,1322				     struct mlxsw_sp_acl_tcam_vregion *vregion,1323				     struct mlxsw_sp_acl_tcam_rehash_ctx *ctx,1324				     int *credits)1325{1326	struct mlxsw_sp_acl_tcam_vchunk *vchunk;1327	int err;1328 1329	if (list_empty(&vregion->vchunk_list))1330		return 0;1331 1332	/* If the migration got interrupted, we have the vchunk1333	 * we are working on stored in context.1334	 */1335	if (ctx->current_vchunk)1336		vchunk = ctx->current_vchunk;1337	else1338		vchunk = list_first_entry(&vregion->vchunk_list,1339					  typeof(*vchunk), list);1340 1341	list_for_each_entry_from(vchunk, &vregion->vchunk_list, list) {1342		err = mlxsw_sp_acl_tcam_vchunk_migrate_one(mlxsw_sp, vchunk,1343							   vregion->region,1344							   ctx, credits);1345		if (err || *credits < 0)1346			return err;1347	}1348	return 0;1349}1350 1351static int1352mlxsw_sp_acl_tcam_vregion_migrate(struct mlxsw_sp *mlxsw_sp,1353				  struct mlxsw_sp_acl_tcam_vregion *vregion,1354				  struct mlxsw_sp_acl_tcam_rehash_ctx *ctx,1355				  int *credits)1356{1357	int err, err2;1358 1359	trace_mlxsw_sp_acl_tcam_vregion_migrate(mlxsw_sp, vregion);1360	err = mlxsw_sp_acl_tcam_vchunk_migrate_all(mlxsw_sp, vregion,1361						   ctx, credits);1362	if (err) {1363		if (ctx->this_is_rollback)1364			return err;1365		/* In case migration was not successful, we need to swap1366		 * so the original region pointer is assigned again1367		 * to vregion->region.1368		 */1369		swap(vregion->region, vregion->region2);1370		mlxsw_sp_acl_tcam_rehash_ctx_vchunk_reset(ctx);1371		ctx->this_is_rollback = true;1372		err2 = mlxsw_sp_acl_tcam_vchunk_migrate_all(mlxsw_sp, vregion,1373							    ctx, credits);1374		if (err2) {1375			trace_mlxsw_sp_acl_tcam_vregion_rehash_rollback_failed(mlxsw_sp,1376									       vregion);1377			dev_err(mlxsw_sp->bus_info->dev, "Failed to rollback during vregion migration fail\n");1378			/* Let the rollback to be continued later on. */1379		}1380	}1381	trace_mlxsw_sp_acl_tcam_vregion_migrate_end(mlxsw_sp, vregion);1382	return err;1383}1384 1385static bool1386mlxsw_sp_acl_tcam_vregion_rehash_in_progress(const struct mlxsw_sp_acl_tcam_rehash_ctx *ctx)1387{1388	return ctx->hints_priv;1389}1390 1391static int1392mlxsw_sp_acl_tcam_vregion_rehash_start(struct mlxsw_sp *mlxsw_sp,1393				       struct mlxsw_sp_acl_tcam_vregion *vregion,1394				       struct mlxsw_sp_acl_tcam_rehash_ctx *ctx)1395{1396	const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;1397	unsigned int priority = mlxsw_sp_acl_tcam_vregion_prio(vregion);1398	struct mlxsw_sp_acl_tcam_region *new_region;1399	void *hints_priv;1400	int err;1401 1402	trace_mlxsw_sp_acl_tcam_vregion_rehash(mlxsw_sp, vregion);1403 1404	hints_priv = ops->region_rehash_hints_get(vregion->region->priv);1405	if (IS_ERR(hints_priv))1406		return PTR_ERR(hints_priv);1407 1408	new_region = mlxsw_sp_acl_tcam_region_create(mlxsw_sp, vregion->tcam,1409						     vregion, hints_priv);1410	if (IS_ERR(new_region)) {1411		err = PTR_ERR(new_region);1412		goto err_region_create;1413	}1414 1415	/* vregion->region contains the pointer to the new region1416	 * we are going to migrate to.1417	 */1418	vregion->region2 = vregion->region;1419	vregion->region = new_region;1420	err = mlxsw_sp_acl_tcam_group_region_attach(mlxsw_sp,1421						    vregion->region2->group,1422						    new_region, priority,1423						    vregion->region2);1424	if (err)1425		goto err_group_region_attach;1426 1427	ctx->hints_priv = hints_priv;1428	ctx->this_is_rollback = false;1429	mlxsw_sp_acl_tcam_rehash_ctx_vchunk_reset(ctx);1430 1431	return 0;1432 1433err_group_region_attach:1434	vregion->region = vregion->region2;1435	vregion->region2 = NULL;1436	mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, new_region);1437err_region_create:1438	ops->region_rehash_hints_put(hints_priv);1439	return err;1440}1441 1442static void1443mlxsw_sp_acl_tcam_vregion_rehash_end(struct mlxsw_sp *mlxsw_sp,1444				     struct mlxsw_sp_acl_tcam_vregion *vregion,1445				     struct mlxsw_sp_acl_tcam_rehash_ctx *ctx)1446{1447	struct mlxsw_sp_acl_tcam_region *unused_region = vregion->region2;1448	const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;1449 1450	vregion->region2 = NULL;1451	mlxsw_sp_acl_tcam_group_region_detach(mlxsw_sp, unused_region);1452	mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, unused_region);1453	ops->region_rehash_hints_put(ctx->hints_priv);1454	ctx->hints_priv = NULL;1455}1456 1457static void1458mlxsw_sp_acl_tcam_vregion_rehash(struct mlxsw_sp *mlxsw_sp,1459				 struct mlxsw_sp_acl_tcam_vregion *vregion,1460				 int *credits)1461{1462	struct mlxsw_sp_acl_tcam_rehash_ctx *ctx = &vregion->rehash.ctx;1463	int err;1464 1465	/* Check if the previous rehash work was interrupted1466	 * which means we have to continue it now.1467	 * If not, start a new rehash.1468	 */1469	if (!mlxsw_sp_acl_tcam_vregion_rehash_in_progress(ctx)) {1470		err = mlxsw_sp_acl_tcam_vregion_rehash_start(mlxsw_sp,1471							     vregion, ctx);1472		if (err) {1473			if (err != -EAGAIN)1474				dev_err(mlxsw_sp->bus_info->dev, "Failed get rehash hints\n");1475			return;1476		}1477	}1478 1479	err = mlxsw_sp_acl_tcam_vregion_migrate(mlxsw_sp, vregion,1480						ctx, credits);1481	if (err) {1482		dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to migrate vregion\n");1483		return;1484	}1485 1486	if (*credits >= 0)1487		mlxsw_sp_acl_tcam_vregion_rehash_end(mlxsw_sp, vregion, ctx);1488}1489 1490static int1491mlxsw_sp_acl_tcam_region_rehash_intrvl_get(struct devlink *devlink, u32 id,1492					   struct devlink_param_gset_ctx *ctx)1493{1494	struct mlxsw_core *mlxsw_core = devlink_priv(devlink);1495	struct mlxsw_sp_acl_tcam *tcam;1496	struct mlxsw_sp *mlxsw_sp;1497 1498	mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);1499	tcam = mlxsw_sp_acl_to_tcam(mlxsw_sp->acl);1500	ctx->val.vu32 = tcam->vregion_rehash_intrvl;1501 1502	return 0;1503}1504 1505static int1506mlxsw_sp_acl_tcam_region_rehash_intrvl_set(struct devlink *devlink, u32 id,1507					   struct devlink_param_gset_ctx *ctx,1508					   struct netlink_ext_ack *extack)1509{1510	struct mlxsw_core *mlxsw_core = devlink_priv(devlink);1511	struct mlxsw_sp_acl_tcam_vregion *vregion;1512	struct mlxsw_sp_acl_tcam *tcam;1513	struct mlxsw_sp *mlxsw_sp;1514	u32 val = ctx->val.vu32;1515 1516	if (val < MLXSW_SP_ACL_TCAM_VREGION_REHASH_INTRVL_MIN && val)1517		return -EINVAL;1518 1519	mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);1520	tcam = mlxsw_sp_acl_to_tcam(mlxsw_sp->acl);1521	tcam->vregion_rehash_intrvl = val;1522	mutex_lock(&tcam->lock);1523	list_for_each_entry(vregion, &tcam->vregion_list, tlist) {1524		if (val)1525			mlxsw_core_schedule_dw(&vregion->rehash.dw, 0);1526		else1527			cancel_delayed_work_sync(&vregion->rehash.dw);1528	}1529	mutex_unlock(&tcam->lock);1530	return 0;1531}1532 1533static const struct devlink_param mlxsw_sp_acl_tcam_rehash_params[] = {1534	DEVLINK_PARAM_DRIVER(MLXSW_DEVLINK_PARAM_ID_ACL_REGION_REHASH_INTERVAL,1535			     "acl_region_rehash_interval",1536			     DEVLINK_PARAM_TYPE_U32,1537			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),1538			     mlxsw_sp_acl_tcam_region_rehash_intrvl_get,1539			     mlxsw_sp_acl_tcam_region_rehash_intrvl_set,1540			     NULL),1541};1542 1543static int mlxsw_sp_acl_tcam_rehash_params_register(struct mlxsw_sp *mlxsw_sp)1544{1545	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);1546 1547	if (!mlxsw_sp->acl_tcam_ops->region_rehash_hints_get)1548		return 0;1549 1550	return devl_params_register(devlink, mlxsw_sp_acl_tcam_rehash_params,1551				    ARRAY_SIZE(mlxsw_sp_acl_tcam_rehash_params));1552}1553 1554static void1555mlxsw_sp_acl_tcam_rehash_params_unregister(struct mlxsw_sp *mlxsw_sp)1556{1557	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);1558 1559	if (!mlxsw_sp->acl_tcam_ops->region_rehash_hints_get)1560		return;1561 1562	devl_params_unregister(devlink, mlxsw_sp_acl_tcam_rehash_params,1563			       ARRAY_SIZE(mlxsw_sp_acl_tcam_rehash_params));1564}1565 1566int mlxsw_sp_acl_tcam_init(struct mlxsw_sp *mlxsw_sp,1567			   struct mlxsw_sp_acl_tcam *tcam)1568{1569	const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;1570	u64 max_tcam_regions;1571	u64 max_regions;1572	u64 max_groups;1573	int err;1574 1575	mutex_init(&tcam->lock);1576	tcam->vregion_rehash_intrvl =1577			MLXSW_SP_ACL_TCAM_VREGION_REHASH_INTRVL_DFLT;1578	INIT_LIST_HEAD(&tcam->vregion_list);1579 1580	err = mlxsw_sp_acl_tcam_rehash_params_register(mlxsw_sp);1581	if (err)1582		goto err_rehash_params_register;1583 1584	max_tcam_regions = MLXSW_CORE_RES_GET(mlxsw_sp->core,1585					      ACL_MAX_TCAM_REGIONS);1586	max_regions = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_REGIONS);1587 1588	/* Use 1:1 mapping between ACL region and TCAM region */1589	if (max_tcam_regions < max_regions)1590		max_regions = max_tcam_regions;1591 1592	ida_init(&tcam->used_regions);1593	tcam->max_regions = max_regions;1594 1595	max_groups = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_GROUPS);1596	ida_init(&tcam->used_groups);1597	tcam->max_groups = max_groups;1598	tcam->max_group_size = MLXSW_CORE_RES_GET(mlxsw_sp->core,1599						  ACL_MAX_GROUP_SIZE);1600	tcam->max_group_size = min_t(unsigned int, tcam->max_group_size,1601				     MLXSW_REG_PAGT_ACL_MAX_NUM);1602 1603	err = ops->init(mlxsw_sp, tcam->priv, tcam);1604	if (err)1605		goto err_tcam_init;1606 1607	return 0;1608 1609err_tcam_init:1610	ida_destroy(&tcam->used_groups);1611	ida_destroy(&tcam->used_regions);1612	mlxsw_sp_acl_tcam_rehash_params_unregister(mlxsw_sp);1613err_rehash_params_register:1614	mutex_destroy(&tcam->lock);1615	return err;1616}1617 1618void mlxsw_sp_acl_tcam_fini(struct mlxsw_sp *mlxsw_sp,1619			    struct mlxsw_sp_acl_tcam *tcam)1620{1621	const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;1622 1623	ops->fini(mlxsw_sp, tcam->priv);1624	ida_destroy(&tcam->used_groups);1625	ida_destroy(&tcam->used_regions);1626	mlxsw_sp_acl_tcam_rehash_params_unregister(mlxsw_sp);1627	mutex_destroy(&tcam->lock);1628}1629 1630static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv4[] = {1631	MLXSW_AFK_ELEMENT_SRC_SYS_PORT,1632	MLXSW_AFK_ELEMENT_DMAC_32_47,1633	MLXSW_AFK_ELEMENT_DMAC_0_31,1634	MLXSW_AFK_ELEMENT_SMAC_32_47,1635	MLXSW_AFK_ELEMENT_SMAC_0_31,1636	MLXSW_AFK_ELEMENT_ETHERTYPE,1637	MLXSW_AFK_ELEMENT_IP_PROTO,1638	MLXSW_AFK_ELEMENT_SRC_IP_0_31,1639	MLXSW_AFK_ELEMENT_DST_IP_0_31,1640	MLXSW_AFK_ELEMENT_DST_L4_PORT,1641	MLXSW_AFK_ELEMENT_SRC_L4_PORT,1642	MLXSW_AFK_ELEMENT_VID,1643	MLXSW_AFK_ELEMENT_PCP,1644	MLXSW_AFK_ELEMENT_TCP_FLAGS,1645	MLXSW_AFK_ELEMENT_IP_TTL_,1646	MLXSW_AFK_ELEMENT_IP_ECN,1647	MLXSW_AFK_ELEMENT_IP_DSCP,1648};1649 1650static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv6[] = {1651	MLXSW_AFK_ELEMENT_ETHERTYPE,1652	MLXSW_AFK_ELEMENT_IP_PROTO,1653	MLXSW_AFK_ELEMENT_SRC_IP_96_127,1654	MLXSW_AFK_ELEMENT_SRC_IP_64_95,1655	MLXSW_AFK_ELEMENT_SRC_IP_32_63,1656	MLXSW_AFK_ELEMENT_SRC_IP_0_31,1657	MLXSW_AFK_ELEMENT_DST_IP_96_127,1658	MLXSW_AFK_ELEMENT_DST_IP_64_95,1659	MLXSW_AFK_ELEMENT_DST_IP_32_63,1660	MLXSW_AFK_ELEMENT_DST_IP_0_31,1661	MLXSW_AFK_ELEMENT_DST_L4_PORT,1662	MLXSW_AFK_ELEMENT_SRC_L4_PORT,1663};1664 1665static const struct mlxsw_sp_acl_tcam_pattern mlxsw_sp_acl_tcam_patterns[] = {1666	{1667		.elements = mlxsw_sp_acl_tcam_pattern_ipv4,1668		.elements_count = ARRAY_SIZE(mlxsw_sp_acl_tcam_pattern_ipv4),1669	},1670	{1671		.elements = mlxsw_sp_acl_tcam_pattern_ipv6,1672		.elements_count = ARRAY_SIZE(mlxsw_sp_acl_tcam_pattern_ipv6),1673	},1674};1675 1676#define MLXSW_SP_ACL_TCAM_PATTERNS_COUNT \1677	ARRAY_SIZE(mlxsw_sp_acl_tcam_patterns)1678 1679struct mlxsw_sp_acl_tcam_flower_ruleset {1680	struct mlxsw_sp_acl_tcam_vgroup vgroup;1681};1682 1683struct mlxsw_sp_acl_tcam_flower_rule {1684	struct mlxsw_sp_acl_tcam_ventry ventry;1685};1686 1687static int1688mlxsw_sp_acl_tcam_flower_ruleset_add(struct mlxsw_sp *mlxsw_sp,1689				     struct mlxsw_sp_acl_tcam *tcam,1690				     void *ruleset_priv,1691				     struct mlxsw_afk_element_usage *tmplt_elusage,1692				     unsigned int *p_min_prio,1693				     unsigned int *p_max_prio)1694{1695	struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;1696 1697	return mlxsw_sp_acl_tcam_vgroup_add(mlxsw_sp, tcam, &ruleset->vgroup,1698					    mlxsw_sp_acl_tcam_patterns,1699					    MLXSW_SP_ACL_TCAM_PATTERNS_COUNT,1700					    tmplt_elusage, true,1701					    p_min_prio, p_max_prio);1702}1703 1704static void1705mlxsw_sp_acl_tcam_flower_ruleset_del(struct mlxsw_sp *mlxsw_sp,1706				     void *ruleset_priv)1707{1708	struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;1709 1710	mlxsw_sp_acl_tcam_vgroup_del(&ruleset->vgroup);1711}1712 1713static int1714mlxsw_sp_acl_tcam_flower_ruleset_bind(struct mlxsw_sp *mlxsw_sp,1715				      void *ruleset_priv,1716				      struct mlxsw_sp_port *mlxsw_sp_port,1717				      bool ingress)1718{1719	struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;1720 1721	return mlxsw_sp_acl_tcam_group_bind(mlxsw_sp, &ruleset->vgroup.group,1722					    mlxsw_sp_port, ingress);1723}1724 1725static void1726mlxsw_sp_acl_tcam_flower_ruleset_unbind(struct mlxsw_sp *mlxsw_sp,1727					void *ruleset_priv,1728					struct mlxsw_sp_port *mlxsw_sp_port,1729					bool ingress)1730{1731	struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;1732 1733	mlxsw_sp_acl_tcam_group_unbind(mlxsw_sp, &ruleset->vgroup.group,1734				       mlxsw_sp_port, ingress);1735}1736 1737static u161738mlxsw_sp_acl_tcam_flower_ruleset_group_id(void *ruleset_priv)1739{1740	struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;1741 1742	return mlxsw_sp_acl_tcam_group_id(&ruleset->vgroup.group);1743}1744 1745static int1746mlxsw_sp_acl_tcam_flower_rule_add(struct mlxsw_sp *mlxsw_sp,1747				  void *ruleset_priv, void *rule_priv,1748				  struct mlxsw_sp_acl_rule_info *rulei)1749{1750	struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;1751	struct mlxsw_sp_acl_tcam_flower_rule *rule = rule_priv;1752 1753	return mlxsw_sp_acl_tcam_ventry_add(mlxsw_sp, &ruleset->vgroup,1754					    &rule->ventry, rulei);1755}1756 1757static void1758mlxsw_sp_acl_tcam_flower_rule_del(struct mlxsw_sp *mlxsw_sp, void *rule_priv)1759{1760	struct mlxsw_sp_acl_tcam_flower_rule *rule = rule_priv;1761 1762	mlxsw_sp_acl_tcam_ventry_del(mlxsw_sp, &rule->ventry);1763}1764 1765static int1766mlxsw_sp_acl_tcam_flower_rule_action_replace(struct mlxsw_sp *mlxsw_sp,1767					     void *rule_priv,1768					     struct mlxsw_sp_acl_rule_info *rulei)1769{1770	return -EOPNOTSUPP;1771}1772 1773static int1774mlxsw_sp_acl_tcam_flower_rule_activity_get(struct mlxsw_sp *mlxsw_sp,1775					   void *rule_priv, bool *activity)1776{1777	struct mlxsw_sp_acl_tcam_flower_rule *rule = rule_priv;1778 1779	return mlxsw_sp_acl_tcam_ventry_activity_get(mlxsw_sp, &rule->ventry,1780						     activity);1781}1782 1783static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_flower_ops = {1784	.ruleset_priv_size	= sizeof(struct mlxsw_sp_acl_tcam_flower_ruleset),1785	.ruleset_add		= mlxsw_sp_acl_tcam_flower_ruleset_add,1786	.ruleset_del		= mlxsw_sp_acl_tcam_flower_ruleset_del,1787	.ruleset_bind		= mlxsw_sp_acl_tcam_flower_ruleset_bind,1788	.ruleset_unbind		= mlxsw_sp_acl_tcam_flower_ruleset_unbind,1789	.ruleset_group_id	= mlxsw_sp_acl_tcam_flower_ruleset_group_id,1790	.rule_priv_size		= sizeof(struct mlxsw_sp_acl_tcam_flower_rule),1791	.rule_add		= mlxsw_sp_acl_tcam_flower_rule_add,1792	.rule_del		= mlxsw_sp_acl_tcam_flower_rule_del,1793	.rule_action_replace	= mlxsw_sp_acl_tcam_flower_rule_action_replace,1794	.rule_activity_get	= mlxsw_sp_acl_tcam_flower_rule_activity_get,1795};1796 1797struct mlxsw_sp_acl_tcam_mr_ruleset {1798	struct mlxsw_sp_acl_tcam_vchunk *vchunk;1799	struct mlxsw_sp_acl_tcam_vgroup vgroup;1800};1801 1802struct mlxsw_sp_acl_tcam_mr_rule {1803	struct mlxsw_sp_acl_tcam_ventry ventry;1804};1805 1806static int1807mlxsw_sp_acl_tcam_mr_ruleset_add(struct mlxsw_sp *mlxsw_sp,1808				 struct mlxsw_sp_acl_tcam *tcam,1809				 void *ruleset_priv,1810				 struct mlxsw_afk_element_usage *tmplt_elusage,1811				 unsigned int *p_min_prio,1812				 unsigned int *p_max_prio)1813{1814	struct mlxsw_sp_acl_tcam_mr_ruleset *ruleset = ruleset_priv;1815	int err;1816 1817	err = mlxsw_sp_acl_tcam_vgroup_add(mlxsw_sp, tcam, &ruleset->vgroup,1818					   mlxsw_sp_acl_tcam_patterns,1819					   MLXSW_SP_ACL_TCAM_PATTERNS_COUNT,1820					   tmplt_elusage, false,1821					   p_min_prio, p_max_prio);1822	if (err)1823		return err;1824 1825	/* For most of the TCAM clients it would make sense to take a tcam chunk1826	 * only when the first rule is written. This is not the case for1827	 * multicast router as it is required to bind the multicast router to a1828	 * specific ACL Group ID which must exist in HW before multicast router1829	 * is initialized.1830	 */1831	ruleset->vchunk = mlxsw_sp_acl_tcam_vchunk_get(mlxsw_sp,1832						       &ruleset->vgroup, 1,1833						       tmplt_elusage);1834	if (IS_ERR(ruleset->vchunk)) {1835		err = PTR_ERR(ruleset->vchunk);1836		goto err_chunk_get;1837	}1838 1839	return 0;1840 1841err_chunk_get:1842	mlxsw_sp_acl_tcam_vgroup_del(&ruleset->vgroup);1843	return err;1844}1845 1846static void1847mlxsw_sp_acl_tcam_mr_ruleset_del(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv)1848{1849	struct mlxsw_sp_acl_tcam_mr_ruleset *ruleset = ruleset_priv;1850 1851	mlxsw_sp_acl_tcam_vchunk_put(mlxsw_sp, ruleset->vchunk);1852	mlxsw_sp_acl_tcam_vgroup_del(&ruleset->vgroup);1853}1854 1855static int1856mlxsw_sp_acl_tcam_mr_ruleset_bind(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv,1857				  struct mlxsw_sp_port *mlxsw_sp_port,1858				  bool ingress)1859{1860	/* Binding is done when initializing multicast router */1861	return 0;1862}1863 1864static void1865mlxsw_sp_acl_tcam_mr_ruleset_unbind(struct mlxsw_sp *mlxsw_sp,1866				    void *ruleset_priv,1867				    struct mlxsw_sp_port *mlxsw_sp_port,1868				    bool ingress)1869{1870}1871 1872static u161873mlxsw_sp_acl_tcam_mr_ruleset_group_id(void *ruleset_priv)1874{1875	struct mlxsw_sp_acl_tcam_mr_ruleset *ruleset = ruleset_priv;1876 1877	return mlxsw_sp_acl_tcam_group_id(&ruleset->vgroup.group);1878}1879 1880static int1881mlxsw_sp_acl_tcam_mr_rule_add(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv,1882			      void *rule_priv,1883			      struct mlxsw_sp_acl_rule_info *rulei)1884{1885	struct mlxsw_sp_acl_tcam_mr_ruleset *ruleset = ruleset_priv;1886	struct mlxsw_sp_acl_tcam_mr_rule *rule = rule_priv;1887 1888	return mlxsw_sp_acl_tcam_ventry_add(mlxsw_sp, &ruleset->vgroup,1889					   &rule->ventry, rulei);1890}1891 1892static void1893mlxsw_sp_acl_tcam_mr_rule_del(struct mlxsw_sp *mlxsw_sp, void *rule_priv)1894{1895	struct mlxsw_sp_acl_tcam_mr_rule *rule = rule_priv;1896 1897	mlxsw_sp_acl_tcam_ventry_del(mlxsw_sp, &rule->ventry);1898}1899 1900static int1901mlxsw_sp_acl_tcam_mr_rule_action_replace(struct mlxsw_sp *mlxsw_sp,1902					 void *rule_priv,1903					 struct mlxsw_sp_acl_rule_info *rulei)1904{1905	struct mlxsw_sp_acl_tcam_mr_rule *rule = rule_priv;1906 1907	return mlxsw_sp_acl_tcam_ventry_action_replace(mlxsw_sp, &rule->ventry,1908						       rulei);1909}1910 1911static int1912mlxsw_sp_acl_tcam_mr_rule_activity_get(struct mlxsw_sp *mlxsw_sp,1913				       void *rule_priv, bool *activity)1914{1915	*activity = false;1916 1917	return 0;1918}1919 1920static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_mr_ops = {1921	.ruleset_priv_size	= sizeof(struct mlxsw_sp_acl_tcam_mr_ruleset),1922	.ruleset_add		= mlxsw_sp_acl_tcam_mr_ruleset_add,1923	.ruleset_del		= mlxsw_sp_acl_tcam_mr_ruleset_del,1924	.ruleset_bind		= mlxsw_sp_acl_tcam_mr_ruleset_bind,1925	.ruleset_unbind		= mlxsw_sp_acl_tcam_mr_ruleset_unbind,1926	.ruleset_group_id	= mlxsw_sp_acl_tcam_mr_ruleset_group_id,1927	.rule_priv_size		= sizeof(struct mlxsw_sp_acl_tcam_mr_rule),1928	.rule_add		= mlxsw_sp_acl_tcam_mr_rule_add,1929	.rule_del		= mlxsw_sp_acl_tcam_mr_rule_del,1930	.rule_action_replace	= mlxsw_sp_acl_tcam_mr_rule_action_replace,1931	.rule_activity_get	= mlxsw_sp_acl_tcam_mr_rule_activity_get,1932};1933 1934static const struct mlxsw_sp_acl_profile_ops *1935mlxsw_sp_acl_tcam_profile_ops_arr[] = {1936	[MLXSW_SP_ACL_PROFILE_FLOWER] = &mlxsw_sp_acl_tcam_flower_ops,1937	[MLXSW_SP_ACL_PROFILE_MR] = &mlxsw_sp_acl_tcam_mr_ops,1938};1939 1940const struct mlxsw_sp_acl_profile_ops *1941mlxsw_sp_acl_tcam_profile_ops(struct mlxsw_sp *mlxsw_sp,1942			      enum mlxsw_sp_acl_profile profile)1943{1944	const struct mlxsw_sp_acl_profile_ops *ops;1945 1946	if (WARN_ON(profile >= ARRAY_SIZE(mlxsw_sp_acl_tcam_profile_ops_arr)))1947		return NULL;1948	ops = mlxsw_sp_acl_tcam_profile_ops_arr[profile];1949	if (WARN_ON(!ops))1950		return NULL;1951	return ops;1952}1953