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

prestera_acl.c (20995B)


      1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
      2/* Copyright (c) 2020-2021 Marvell International Ltd. All rights reserved */
      3
      4#include <linux/rhashtable.h>
      5
      6#include "prestera_acl.h"
      7#include "prestera_flow.h"
      8#include "prestera_hw.h"
      9#include "prestera.h"
     10
     11#define ACL_KEYMASK_SIZE	\
     12	(sizeof(__be32) * __PRESTERA_ACL_RULE_MATCH_TYPE_MAX)
     13
     14struct prestera_acl {
     15	struct prestera_switch *sw;
     16	struct list_head vtcam_list;
     17	struct list_head rules;
     18	struct rhashtable ruleset_ht;
     19	struct rhashtable acl_rule_entry_ht;
     20	struct idr uid;
     21};
     22
     23struct prestera_acl_ruleset_ht_key {
     24	struct prestera_flow_block *block;
     25	u32 chain_index;
     26};
     27
     28struct prestera_acl_rule_entry {
     29	struct rhash_head ht_node;
     30	struct prestera_acl_rule_entry_key key;
     31	u32 hw_id;
     32	u32 vtcam_id;
     33	struct {
     34		struct {
     35			u8 valid:1;
     36		} accept, drop, trap;
     37		struct {
     38			u8 valid:1;
     39			struct prestera_acl_action_police i;
     40		} police;
     41		struct {
     42			struct prestera_acl_action_jump i;
     43			u8 valid:1;
     44		} jump;
     45		struct {
     46			u32 id;
     47			struct prestera_counter_block *block;
     48		} counter;
     49	};
     50};
     51
     52struct prestera_acl_ruleset {
     53	struct rhash_head ht_node; /* Member of acl HT */
     54	struct prestera_acl_ruleset_ht_key ht_key;
     55	struct rhashtable rule_ht;
     56	struct prestera_acl *acl;
     57	unsigned long rule_count;
     58	refcount_t refcount;
     59	void *keymask;
     60	u32 vtcam_id;
     61	u32 index;
     62	u16 pcl_id;
     63	bool offload;
     64};
     65
     66struct prestera_acl_vtcam {
     67	struct list_head list;
     68	__be32 keymask[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX];
     69	refcount_t refcount;
     70	u32 id;
     71	bool is_keymask_set;
     72	u8 lookup;
     73};
     74
     75static const struct rhashtable_params prestera_acl_ruleset_ht_params = {
     76	.key_len = sizeof(struct prestera_acl_ruleset_ht_key),
     77	.key_offset = offsetof(struct prestera_acl_ruleset, ht_key),
     78	.head_offset = offsetof(struct prestera_acl_ruleset, ht_node),
     79	.automatic_shrinking = true,
     80};
     81
     82static const struct rhashtable_params prestera_acl_rule_ht_params = {
     83	.key_len = sizeof(unsigned long),
     84	.key_offset = offsetof(struct prestera_acl_rule, cookie),
     85	.head_offset = offsetof(struct prestera_acl_rule, ht_node),
     86	.automatic_shrinking = true,
     87};
     88
     89static const struct rhashtable_params __prestera_acl_rule_entry_ht_params = {
     90	.key_offset  = offsetof(struct prestera_acl_rule_entry, key),
     91	.head_offset = offsetof(struct prestera_acl_rule_entry, ht_node),
     92	.key_len     = sizeof(struct prestera_acl_rule_entry_key),
     93	.automatic_shrinking = true,
     94};
     95
     96int prestera_acl_chain_to_client(u32 chain_index, u32 *client)
     97{
     98	static const u32 client_map[] = {
     99		PRESTERA_HW_COUNTER_CLIENT_LOOKUP_0,
    100		PRESTERA_HW_COUNTER_CLIENT_LOOKUP_1,
    101		PRESTERA_HW_COUNTER_CLIENT_LOOKUP_2
    102	};
    103
    104	if (chain_index >= ARRAY_SIZE(client_map))
    105		return -EINVAL;
    106
    107	*client = client_map[chain_index];
    108	return 0;
    109}
    110
    111static bool prestera_acl_chain_is_supported(u32 chain_index)
    112{
    113	return (chain_index & ~PRESTERA_ACL_CHAIN_MASK) == 0;
    114}
    115
    116static struct prestera_acl_ruleset *
    117prestera_acl_ruleset_create(struct prestera_acl *acl,
    118			    struct prestera_flow_block *block,
    119			    u32 chain_index)
    120{
    121	struct prestera_acl_ruleset *ruleset;
    122	u32 uid = 0;
    123	int err;
    124
    125	if (!prestera_acl_chain_is_supported(chain_index))
    126		return ERR_PTR(-EINVAL);
    127
    128	ruleset = kzalloc(sizeof(*ruleset), GFP_KERNEL);
    129	if (!ruleset)
    130		return ERR_PTR(-ENOMEM);
    131
    132	ruleset->acl = acl;
    133	ruleset->ht_key.block = block;
    134	ruleset->ht_key.chain_index = chain_index;
    135	refcount_set(&ruleset->refcount, 1);
    136
    137	err = rhashtable_init(&ruleset->rule_ht, &prestera_acl_rule_ht_params);
    138	if (err)
    139		goto err_rhashtable_init;
    140
    141	err = idr_alloc_u32(&acl->uid, NULL, &uid, U8_MAX, GFP_KERNEL);
    142	if (err)
    143		goto err_ruleset_create;
    144
    145	/* make pcl-id based on uid */
    146	ruleset->pcl_id = PRESTERA_ACL_PCL_ID_MAKE((u8)uid, chain_index);
    147	ruleset->index = uid;
    148
    149	err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node,
    150				     prestera_acl_ruleset_ht_params);
    151	if (err)
    152		goto err_ruleset_ht_insert;
    153
    154	return ruleset;
    155
    156err_ruleset_ht_insert:
    157	idr_remove(&acl->uid, uid);
    158err_ruleset_create:
    159	rhashtable_destroy(&ruleset->rule_ht);
    160err_rhashtable_init:
    161	kfree(ruleset);
    162	return ERR_PTR(err);
    163}
    164
    165void prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset *ruleset,
    166				      void *keymask)
    167{
    168	ruleset->keymask = kmemdup(keymask, ACL_KEYMASK_SIZE, GFP_KERNEL);
    169}
    170
    171int prestera_acl_ruleset_offload(struct prestera_acl_ruleset *ruleset)
    172{
    173	struct prestera_acl_iface iface;
    174	u32 vtcam_id;
    175	int err;
    176
    177	if (ruleset->offload)
    178		return -EEXIST;
    179
    180	err = prestera_acl_vtcam_id_get(ruleset->acl,
    181					ruleset->ht_key.chain_index,
    182					ruleset->keymask, &vtcam_id);
    183	if (err)
    184		goto err_vtcam_create;
    185
    186	if (ruleset->ht_key.chain_index) {
    187		/* for chain > 0, bind iface index to pcl-id to be able
    188		 * to jump from any other ruleset to this one using the index.
    189		 */
    190		iface.index = ruleset->index;
    191		iface.type = PRESTERA_ACL_IFACE_TYPE_INDEX;
    192		err = prestera_hw_vtcam_iface_bind(ruleset->acl->sw, &iface,
    193						   vtcam_id, ruleset->pcl_id);
    194		if (err)
    195			goto err_ruleset_bind;
    196	}
    197
    198	ruleset->vtcam_id = vtcam_id;
    199	ruleset->offload = true;
    200	return 0;
    201
    202err_ruleset_bind:
    203	prestera_acl_vtcam_id_put(ruleset->acl, ruleset->vtcam_id);
    204err_vtcam_create:
    205	return err;
    206}
    207
    208static void prestera_acl_ruleset_destroy(struct prestera_acl_ruleset *ruleset)
    209{
    210	struct prestera_acl *acl = ruleset->acl;
    211	u8 uid = ruleset->pcl_id & PRESTERA_ACL_KEYMASK_PCL_ID_USER;
    212	int err;
    213
    214	rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node,
    215			       prestera_acl_ruleset_ht_params);
    216
    217	if (ruleset->offload) {
    218		if (ruleset->ht_key.chain_index) {
    219			struct prestera_acl_iface iface = {
    220				.type = PRESTERA_ACL_IFACE_TYPE_INDEX,
    221				.index = ruleset->index
    222			};
    223			err = prestera_hw_vtcam_iface_unbind(acl->sw, &iface,
    224							     ruleset->vtcam_id);
    225			WARN_ON(err);
    226		}
    227		WARN_ON(prestera_acl_vtcam_id_put(acl, ruleset->vtcam_id));
    228	}
    229
    230	idr_remove(&acl->uid, uid);
    231	rhashtable_destroy(&ruleset->rule_ht);
    232	kfree(ruleset->keymask);
    233	kfree(ruleset);
    234}
    235
    236static struct prestera_acl_ruleset *
    237__prestera_acl_ruleset_lookup(struct prestera_acl *acl,
    238			      struct prestera_flow_block *block,
    239			      u32 chain_index)
    240{
    241	struct prestera_acl_ruleset_ht_key ht_key;
    242
    243	memset(&ht_key, 0, sizeof(ht_key));
    244	ht_key.block = block;
    245	ht_key.chain_index = chain_index;
    246	return rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key,
    247				      prestera_acl_ruleset_ht_params);
    248}
    249
    250struct prestera_acl_ruleset *
    251prestera_acl_ruleset_lookup(struct prestera_acl *acl,
    252			    struct prestera_flow_block *block,
    253			    u32 chain_index)
    254{
    255	struct prestera_acl_ruleset *ruleset;
    256
    257	ruleset = __prestera_acl_ruleset_lookup(acl, block, chain_index);
    258	if (!ruleset)
    259		return ERR_PTR(-ENOENT);
    260
    261	refcount_inc(&ruleset->refcount);
    262	return ruleset;
    263}
    264
    265struct prestera_acl_ruleset *
    266prestera_acl_ruleset_get(struct prestera_acl *acl,
    267			 struct prestera_flow_block *block,
    268			 u32 chain_index)
    269{
    270	struct prestera_acl_ruleset *ruleset;
    271
    272	ruleset = __prestera_acl_ruleset_lookup(acl, block, chain_index);
    273	if (ruleset) {
    274		refcount_inc(&ruleset->refcount);
    275		return ruleset;
    276	}
    277
    278	return prestera_acl_ruleset_create(acl, block, chain_index);
    279}
    280
    281void prestera_acl_ruleset_put(struct prestera_acl_ruleset *ruleset)
    282{
    283	if (!refcount_dec_and_test(&ruleset->refcount))
    284		return;
    285
    286	prestera_acl_ruleset_destroy(ruleset);
    287}
    288
    289int prestera_acl_ruleset_bind(struct prestera_acl_ruleset *ruleset,
    290			      struct prestera_port *port)
    291{
    292	struct prestera_acl_iface iface = {
    293		.type = PRESTERA_ACL_IFACE_TYPE_PORT,
    294		.port = port
    295	};
    296
    297	return prestera_hw_vtcam_iface_bind(port->sw, &iface, ruleset->vtcam_id,
    298					    ruleset->pcl_id);
    299}
    300
    301int prestera_acl_ruleset_unbind(struct prestera_acl_ruleset *ruleset,
    302				struct prestera_port *port)
    303{
    304	struct prestera_acl_iface iface = {
    305		.type = PRESTERA_ACL_IFACE_TYPE_PORT,
    306		.port = port
    307	};
    308
    309	return prestera_hw_vtcam_iface_unbind(port->sw, &iface,
    310					      ruleset->vtcam_id);
    311}
    312
    313static int prestera_acl_ruleset_block_bind(struct prestera_acl_ruleset *ruleset,
    314					   struct prestera_flow_block *block)
    315{
    316	struct prestera_flow_block_binding *binding;
    317	int err;
    318
    319	block->ruleset_zero = ruleset;
    320	list_for_each_entry(binding, &block->binding_list, list) {
    321		err = prestera_acl_ruleset_bind(ruleset, binding->port);
    322		if (err)
    323			goto rollback;
    324	}
    325	return 0;
    326
    327rollback:
    328	list_for_each_entry_continue_reverse(binding, &block->binding_list,
    329					     list)
    330		err = prestera_acl_ruleset_unbind(ruleset, binding->port);
    331	block->ruleset_zero = NULL;
    332
    333	return err;
    334}
    335
    336static void
    337prestera_acl_ruleset_block_unbind(struct prestera_acl_ruleset *ruleset,
    338				  struct prestera_flow_block *block)
    339{
    340	struct prestera_flow_block_binding *binding;
    341
    342	list_for_each_entry(binding, &block->binding_list, list)
    343		prestera_acl_ruleset_unbind(ruleset, binding->port);
    344	block->ruleset_zero = NULL;
    345}
    346
    347void
    348prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule, u16 pcl_id)
    349{
    350	struct prestera_acl_match *r_match = &rule->re_key.match;
    351	__be16 pcl_id_mask = htons(PRESTERA_ACL_KEYMASK_PCL_ID);
    352	__be16 pcl_id_key = htons(pcl_id);
    353
    354	rule_match_set(r_match->key, PCL_ID, pcl_id_key);
    355	rule_match_set(r_match->mask, PCL_ID, pcl_id_mask);
    356}
    357
    358struct prestera_acl_rule *
    359prestera_acl_rule_lookup(struct prestera_acl_ruleset *ruleset,
    360			 unsigned long cookie)
    361{
    362	return rhashtable_lookup_fast(&ruleset->rule_ht, &cookie,
    363				      prestera_acl_rule_ht_params);
    364}
    365
    366u32 prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset *ruleset)
    367{
    368	return ruleset->index;
    369}
    370
    371bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset)
    372{
    373	return ruleset->offload;
    374}
    375
    376struct prestera_acl_rule *
    377prestera_acl_rule_create(struct prestera_acl_ruleset *ruleset,
    378			 unsigned long cookie, u32 chain_index)
    379{
    380	struct prestera_acl_rule *rule;
    381
    382	rule = kzalloc(sizeof(*rule), GFP_KERNEL);
    383	if (!rule)
    384		return ERR_PTR(-ENOMEM);
    385
    386	rule->ruleset = ruleset;
    387	rule->cookie = cookie;
    388	rule->chain_index = chain_index;
    389
    390	refcount_inc(&ruleset->refcount);
    391
    392	return rule;
    393}
    394
    395void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule,
    396				    u32 priority)
    397{
    398	rule->priority = priority;
    399}
    400
    401void prestera_acl_rule_destroy(struct prestera_acl_rule *rule)
    402{
    403	if (rule->jump_ruleset)
    404		/* release ruleset kept by jump action */
    405		prestera_acl_ruleset_put(rule->jump_ruleset);
    406
    407	prestera_acl_ruleset_put(rule->ruleset);
    408	kfree(rule);
    409}
    410
    411int prestera_acl_rule_add(struct prestera_switch *sw,
    412			  struct prestera_acl_rule *rule)
    413{
    414	int err;
    415	struct prestera_acl_ruleset *ruleset = rule->ruleset;
    416	struct prestera_flow_block *block = ruleset->ht_key.block;
    417
    418	/* try to add rule to hash table first */
    419	err = rhashtable_insert_fast(&ruleset->rule_ht, &rule->ht_node,
    420				     prestera_acl_rule_ht_params);
    421	if (err)
    422		goto err_ht_insert;
    423
    424	prestera_acl_rule_keymask_pcl_id_set(rule, ruleset->pcl_id);
    425	rule->re_arg.vtcam_id = ruleset->vtcam_id;
    426	rule->re_key.prio = rule->priority;
    427
    428	rule->re = prestera_acl_rule_entry_find(sw->acl, &rule->re_key);
    429	err = WARN_ON(rule->re) ? -EEXIST : 0;
    430	if (err)
    431		goto err_rule_add;
    432
    433	rule->re = prestera_acl_rule_entry_create(sw->acl, &rule->re_key,
    434						  &rule->re_arg);
    435	err = !rule->re ? -EINVAL : 0;
    436	if (err)
    437		goto err_rule_add;
    438
    439	/* bind the block (all ports) to chain index 0, rest of
    440	 * the chains are bound to goto action
    441	 */
    442	if (!ruleset->ht_key.chain_index && !ruleset->rule_count) {
    443		err = prestera_acl_ruleset_block_bind(ruleset, block);
    444		if (err)
    445			goto err_acl_block_bind;
    446	}
    447
    448	list_add_tail(&rule->list, &sw->acl->rules);
    449	ruleset->rule_count++;
    450	return 0;
    451
    452err_acl_block_bind:
    453	prestera_acl_rule_entry_destroy(sw->acl, rule->re);
    454err_rule_add:
    455	rule->re = NULL;
    456	rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
    457			       prestera_acl_rule_ht_params);
    458err_ht_insert:
    459	return err;
    460}
    461
    462void prestera_acl_rule_del(struct prestera_switch *sw,
    463			   struct prestera_acl_rule *rule)
    464{
    465	struct prestera_acl_ruleset *ruleset = rule->ruleset;
    466	struct prestera_flow_block *block = ruleset->ht_key.block;
    467
    468	rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
    469			       prestera_acl_rule_ht_params);
    470	ruleset->rule_count--;
    471	list_del(&rule->list);
    472
    473	prestera_acl_rule_entry_destroy(sw->acl, rule->re);
    474
    475	/* unbind block (all ports) */
    476	if (!ruleset->ht_key.chain_index && !ruleset->rule_count)
    477		prestera_acl_ruleset_block_unbind(ruleset, block);
    478}
    479
    480int prestera_acl_rule_get_stats(struct prestera_acl *acl,
    481				struct prestera_acl_rule *rule,
    482				u64 *packets, u64 *bytes, u64 *last_use)
    483{
    484	u64 current_packets;
    485	u64 current_bytes;
    486	int err;
    487
    488	err = prestera_counter_stats_get(acl->sw->counter,
    489					 rule->re->counter.block,
    490					 rule->re->counter.id,
    491					 &current_packets, &current_bytes);
    492	if (err)
    493		return err;
    494
    495	*packets = current_packets;
    496	*bytes = current_bytes;
    497	*last_use = jiffies;
    498
    499	return 0;
    500}
    501
    502struct prestera_acl_rule_entry *
    503prestera_acl_rule_entry_find(struct prestera_acl *acl,
    504			     struct prestera_acl_rule_entry_key *key)
    505{
    506	return rhashtable_lookup_fast(&acl->acl_rule_entry_ht, key,
    507				      __prestera_acl_rule_entry_ht_params);
    508}
    509
    510static int __prestera_acl_rule_entry2hw_del(struct prestera_switch *sw,
    511					    struct prestera_acl_rule_entry *e)
    512{
    513	return prestera_hw_vtcam_rule_del(sw, e->vtcam_id, e->hw_id);
    514}
    515
    516static int __prestera_acl_rule_entry2hw_add(struct prestera_switch *sw,
    517					    struct prestera_acl_rule_entry *e)
    518{
    519	struct prestera_acl_hw_action_info act_hw[PRESTERA_ACL_RULE_ACTION_MAX];
    520	int act_num;
    521
    522	memset(&act_hw, 0, sizeof(act_hw));
    523	act_num = 0;
    524
    525	/* accept */
    526	if (e->accept.valid) {
    527		act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_ACCEPT;
    528		act_num++;
    529	}
    530	/* drop */
    531	if (e->drop.valid) {
    532		act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_DROP;
    533		act_num++;
    534	}
    535	/* trap */
    536	if (e->trap.valid) {
    537		act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_TRAP;
    538		act_num++;
    539	}
    540	/* police */
    541	if (e->police.valid) {
    542		act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_POLICE;
    543		act_hw[act_num].police = e->police.i;
    544		act_num++;
    545	}
    546	/* jump */
    547	if (e->jump.valid) {
    548		act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_JUMP;
    549		act_hw[act_num].jump = e->jump.i;
    550		act_num++;
    551	}
    552	/* counter */
    553	if (e->counter.block) {
    554		act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_COUNT;
    555		act_hw[act_num].count.id = e->counter.id;
    556		act_num++;
    557	}
    558
    559	return prestera_hw_vtcam_rule_add(sw, e->vtcam_id, e->key.prio,
    560					  e->key.match.key, e->key.match.mask,
    561					  act_hw, act_num, &e->hw_id);
    562}
    563
    564static void
    565__prestera_acl_rule_entry_act_destruct(struct prestera_switch *sw,
    566				       struct prestera_acl_rule_entry *e)
    567{
    568	/* counter */
    569	prestera_counter_put(sw->counter, e->counter.block, e->counter.id);
    570	/* police */
    571	if (e->police.valid)
    572		prestera_hw_policer_release(sw, e->police.i.id);
    573}
    574
    575void prestera_acl_rule_entry_destroy(struct prestera_acl *acl,
    576				     struct prestera_acl_rule_entry *e)
    577{
    578	int ret;
    579
    580	rhashtable_remove_fast(&acl->acl_rule_entry_ht, &e->ht_node,
    581			       __prestera_acl_rule_entry_ht_params);
    582
    583	ret = __prestera_acl_rule_entry2hw_del(acl->sw, e);
    584	WARN_ON(ret && ret != -ENODEV);
    585
    586	__prestera_acl_rule_entry_act_destruct(acl->sw, e);
    587	kfree(e);
    588}
    589
    590static int
    591__prestera_acl_rule_entry_act_construct(struct prestera_switch *sw,
    592					struct prestera_acl_rule_entry *e,
    593					struct prestera_acl_rule_entry_arg *arg)
    594{
    595	int err;
    596
    597	/* accept */
    598	e->accept.valid = arg->accept.valid;
    599	/* drop */
    600	e->drop.valid = arg->drop.valid;
    601	/* trap */
    602	e->trap.valid = arg->trap.valid;
    603	/* jump */
    604	e->jump.valid = arg->jump.valid;
    605	e->jump.i = arg->jump.i;
    606	/* police */
    607	if (arg->police.valid) {
    608		u8 type = arg->police.ingress ? PRESTERA_POLICER_TYPE_INGRESS :
    609						PRESTERA_POLICER_TYPE_EGRESS;
    610
    611		err = prestera_hw_policer_create(sw, type, &e->police.i.id);
    612		if (err)
    613			goto err_out;
    614
    615		err = prestera_hw_policer_sr_tcm_set(sw, e->police.i.id,
    616						     arg->police.rate,
    617						     arg->police.burst);
    618		if (err) {
    619			prestera_hw_policer_release(sw, e->police.i.id);
    620			goto err_out;
    621		}
    622		e->police.valid = arg->police.valid;
    623	}
    624	/* counter */
    625	if (arg->count.valid) {
    626		err = prestera_counter_get(sw->counter, arg->count.client,
    627					   &e->counter.block,
    628					   &e->counter.id);
    629		if (err)
    630			goto err_out;
    631	}
    632
    633	return 0;
    634
    635err_out:
    636	__prestera_acl_rule_entry_act_destruct(sw, e);
    637	return -EINVAL;
    638}
    639
    640struct prestera_acl_rule_entry *
    641prestera_acl_rule_entry_create(struct prestera_acl *acl,
    642			       struct prestera_acl_rule_entry_key *key,
    643			       struct prestera_acl_rule_entry_arg *arg)
    644{
    645	struct prestera_acl_rule_entry *e;
    646	int err;
    647
    648	e = kzalloc(sizeof(*e), GFP_KERNEL);
    649	if (!e)
    650		goto err_kzalloc;
    651
    652	memcpy(&e->key, key, sizeof(*key));
    653	e->vtcam_id = arg->vtcam_id;
    654	err = __prestera_acl_rule_entry_act_construct(acl->sw, e, arg);
    655	if (err)
    656		goto err_act_construct;
    657
    658	err = __prestera_acl_rule_entry2hw_add(acl->sw, e);
    659	if (err)
    660		goto err_hw_add;
    661
    662	err = rhashtable_insert_fast(&acl->acl_rule_entry_ht, &e->ht_node,
    663				     __prestera_acl_rule_entry_ht_params);
    664	if (err)
    665		goto err_ht_insert;
    666
    667	return e;
    668
    669err_ht_insert:
    670	WARN_ON(__prestera_acl_rule_entry2hw_del(acl->sw, e));
    671err_hw_add:
    672	__prestera_acl_rule_entry_act_destruct(acl->sw, e);
    673err_act_construct:
    674	kfree(e);
    675err_kzalloc:
    676	return NULL;
    677}
    678
    679static int __prestera_acl_vtcam_id_try_fit(struct prestera_acl *acl, u8 lookup,
    680					   void *keymask, u32 *vtcam_id)
    681{
    682	struct prestera_acl_vtcam *vtcam;
    683	int i;
    684
    685	list_for_each_entry(vtcam, &acl->vtcam_list, list) {
    686		if (lookup != vtcam->lookup)
    687			continue;
    688
    689		if (!keymask && !vtcam->is_keymask_set)
    690			goto vtcam_found;
    691
    692		if (!(keymask && vtcam->is_keymask_set))
    693			continue;
    694
    695		/* try to fit with vtcam keymask */
    696		for (i = 0; i < __PRESTERA_ACL_RULE_MATCH_TYPE_MAX; i++) {
    697			__be32 __keymask = ((__be32 *)keymask)[i];
    698
    699			if (!__keymask)
    700				/* vtcam keymask in not interested */
    701				continue;
    702
    703			if (__keymask & ~vtcam->keymask[i])
    704				/* keymask does not fit the vtcam keymask */
    705				break;
    706		}
    707
    708		if (i == __PRESTERA_ACL_RULE_MATCH_TYPE_MAX)
    709			/* keymask fits vtcam keymask, return it */
    710			goto vtcam_found;
    711	}
    712
    713	/* nothing is found */
    714	return -ENOENT;
    715
    716vtcam_found:
    717	refcount_inc(&vtcam->refcount);
    718	*vtcam_id = vtcam->id;
    719	return 0;
    720}
    721
    722int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup,
    723			      void *keymask, u32 *vtcam_id)
    724{
    725	struct prestera_acl_vtcam *vtcam;
    726	u32 new_vtcam_id;
    727	int err;
    728
    729	/* find the vtcam that suits keymask. We do not expect to have
    730	 * a big number of vtcams, so, the list type for vtcam list is
    731	 * fine for now
    732	 */
    733	list_for_each_entry(vtcam, &acl->vtcam_list, list) {
    734		if (lookup != vtcam->lookup)
    735			continue;
    736
    737		if (!keymask && !vtcam->is_keymask_set) {
    738			refcount_inc(&vtcam->refcount);
    739			goto vtcam_found;
    740		}
    741
    742		if (keymask && vtcam->is_keymask_set &&
    743		    !memcmp(keymask, vtcam->keymask, sizeof(vtcam->keymask))) {
    744			refcount_inc(&vtcam->refcount);
    745			goto vtcam_found;
    746		}
    747	}
    748
    749	/* vtcam not found, try to create new one */
    750	vtcam = kzalloc(sizeof(*vtcam), GFP_KERNEL);
    751	if (!vtcam)
    752		return -ENOMEM;
    753
    754	err = prestera_hw_vtcam_create(acl->sw, lookup, keymask, &new_vtcam_id,
    755				       PRESTERA_HW_VTCAM_DIR_INGRESS);
    756	if (err) {
    757		kfree(vtcam);
    758
    759		/* cannot create new, try to fit into existing vtcam */
    760		if (__prestera_acl_vtcam_id_try_fit(acl, lookup,
    761						    keymask, &new_vtcam_id))
    762			return err;
    763
    764		*vtcam_id = new_vtcam_id;
    765		return 0;
    766	}
    767
    768	vtcam->id = new_vtcam_id;
    769	vtcam->lookup = lookup;
    770	if (keymask) {
    771		memcpy(vtcam->keymask, keymask, sizeof(vtcam->keymask));
    772		vtcam->is_keymask_set = true;
    773	}
    774	refcount_set(&vtcam->refcount, 1);
    775	list_add_rcu(&vtcam->list, &acl->vtcam_list);
    776
    777vtcam_found:
    778	*vtcam_id = vtcam->id;
    779	return 0;
    780}
    781
    782int prestera_acl_vtcam_id_put(struct prestera_acl *acl, u32 vtcam_id)
    783{
    784	struct prestera_acl_vtcam *vtcam;
    785	int err;
    786
    787	list_for_each_entry(vtcam, &acl->vtcam_list, list) {
    788		if (vtcam_id != vtcam->id)
    789			continue;
    790
    791		if (!refcount_dec_and_test(&vtcam->refcount))
    792			return 0;
    793
    794		err = prestera_hw_vtcam_destroy(acl->sw, vtcam->id);
    795		if (err && err != -ENODEV) {
    796			refcount_set(&vtcam->refcount, 1);
    797			return err;
    798		}
    799
    800		list_del(&vtcam->list);
    801		kfree(vtcam);
    802		return 0;
    803	}
    804
    805	return -ENOENT;
    806}
    807
    808int prestera_acl_init(struct prestera_switch *sw)
    809{
    810	struct prestera_acl *acl;
    811	int err;
    812
    813	acl = kzalloc(sizeof(*acl), GFP_KERNEL);
    814	if (!acl)
    815		return -ENOMEM;
    816
    817	acl->sw = sw;
    818	INIT_LIST_HEAD(&acl->rules);
    819	INIT_LIST_HEAD(&acl->vtcam_list);
    820	idr_init(&acl->uid);
    821
    822	err = rhashtable_init(&acl->acl_rule_entry_ht,
    823			      &__prestera_acl_rule_entry_ht_params);
    824	if (err)
    825		goto err_acl_rule_entry_ht_init;
    826
    827	err = rhashtable_init(&acl->ruleset_ht,
    828			      &prestera_acl_ruleset_ht_params);
    829	if (err)
    830		goto err_ruleset_ht_init;
    831
    832	sw->acl = acl;
    833
    834	return 0;
    835
    836err_ruleset_ht_init:
    837	rhashtable_destroy(&acl->acl_rule_entry_ht);
    838err_acl_rule_entry_ht_init:
    839	kfree(acl);
    840	return err;
    841}
    842
    843void prestera_acl_fini(struct prestera_switch *sw)
    844{
    845	struct prestera_acl *acl = sw->acl;
    846
    847	WARN_ON(!idr_is_empty(&acl->uid));
    848	idr_destroy(&acl->uid);
    849
    850	WARN_ON(!list_empty(&acl->vtcam_list));
    851	WARN_ON(!list_empty(&acl->rules));
    852
    853	rhashtable_destroy(&acl->ruleset_ht);
    854	rhashtable_destroy(&acl->acl_rule_entry_ht);
    855
    856	kfree(acl);
    857}