cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

spectrum_acl_tcam.c (53912B)


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