162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2020-2021 Marvell International Ltd. All rights reserved */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/rhashtable.h>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include "prestera_acl.h"
762306a36Sopenharmony_ci#include "prestera_flow.h"
862306a36Sopenharmony_ci#include "prestera_hw.h"
962306a36Sopenharmony_ci#include "prestera.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define ACL_KEYMASK_SIZE	\
1262306a36Sopenharmony_ci	(sizeof(__be32) * __PRESTERA_ACL_RULE_MATCH_TYPE_MAX)
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistruct prestera_acl {
1562306a36Sopenharmony_ci	struct prestera_switch *sw;
1662306a36Sopenharmony_ci	struct list_head vtcam_list;
1762306a36Sopenharmony_ci	struct list_head rules;
1862306a36Sopenharmony_ci	struct rhashtable ruleset_ht;
1962306a36Sopenharmony_ci	struct rhashtable acl_rule_entry_ht;
2062306a36Sopenharmony_ci	struct idr uid;
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct prestera_acl_ruleset_ht_key {
2462306a36Sopenharmony_ci	struct prestera_flow_block *block;
2562306a36Sopenharmony_ci	u32 chain_index;
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct prestera_acl_rule_entry {
2962306a36Sopenharmony_ci	struct rhash_head ht_node;
3062306a36Sopenharmony_ci	struct prestera_acl_rule_entry_key key;
3162306a36Sopenharmony_ci	u32 hw_id;
3262306a36Sopenharmony_ci	u32 vtcam_id;
3362306a36Sopenharmony_ci	struct {
3462306a36Sopenharmony_ci		struct {
3562306a36Sopenharmony_ci			u8 valid:1;
3662306a36Sopenharmony_ci		} accept, drop, trap;
3762306a36Sopenharmony_ci		struct {
3862306a36Sopenharmony_ci			u8 valid:1;
3962306a36Sopenharmony_ci			struct prestera_acl_action_police i;
4062306a36Sopenharmony_ci		} police;
4162306a36Sopenharmony_ci		struct {
4262306a36Sopenharmony_ci			struct prestera_acl_action_jump i;
4362306a36Sopenharmony_ci			u8 valid:1;
4462306a36Sopenharmony_ci		} jump;
4562306a36Sopenharmony_ci		struct {
4662306a36Sopenharmony_ci			u32 id;
4762306a36Sopenharmony_ci			struct prestera_counter_block *block;
4862306a36Sopenharmony_ci		} counter;
4962306a36Sopenharmony_ci	};
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistruct prestera_acl_ruleset {
5362306a36Sopenharmony_ci	struct rhash_head ht_node; /* Member of acl HT */
5462306a36Sopenharmony_ci	struct prestera_acl_ruleset_ht_key ht_key;
5562306a36Sopenharmony_ci	struct rhashtable rule_ht;
5662306a36Sopenharmony_ci	struct prestera_acl *acl;
5762306a36Sopenharmony_ci	struct {
5862306a36Sopenharmony_ci		u32 min;
5962306a36Sopenharmony_ci		u32 max;
6062306a36Sopenharmony_ci	} prio;
6162306a36Sopenharmony_ci	unsigned long rule_count;
6262306a36Sopenharmony_ci	refcount_t refcount;
6362306a36Sopenharmony_ci	void *keymask;
6462306a36Sopenharmony_ci	u32 vtcam_id;
6562306a36Sopenharmony_ci	u32 index;
6662306a36Sopenharmony_ci	u16 pcl_id;
6762306a36Sopenharmony_ci	bool offload;
6862306a36Sopenharmony_ci	bool ingress;
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistruct prestera_acl_vtcam {
7262306a36Sopenharmony_ci	struct list_head list;
7362306a36Sopenharmony_ci	__be32 keymask[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX];
7462306a36Sopenharmony_ci	refcount_t refcount;
7562306a36Sopenharmony_ci	u32 id;
7662306a36Sopenharmony_ci	bool is_keymask_set;
7762306a36Sopenharmony_ci	u8 lookup;
7862306a36Sopenharmony_ci	u8 direction;
7962306a36Sopenharmony_ci};
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic const struct rhashtable_params prestera_acl_ruleset_ht_params = {
8262306a36Sopenharmony_ci	.key_len = sizeof(struct prestera_acl_ruleset_ht_key),
8362306a36Sopenharmony_ci	.key_offset = offsetof(struct prestera_acl_ruleset, ht_key),
8462306a36Sopenharmony_ci	.head_offset = offsetof(struct prestera_acl_ruleset, ht_node),
8562306a36Sopenharmony_ci	.automatic_shrinking = true,
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic const struct rhashtable_params prestera_acl_rule_ht_params = {
8962306a36Sopenharmony_ci	.key_len = sizeof(unsigned long),
9062306a36Sopenharmony_ci	.key_offset = offsetof(struct prestera_acl_rule, cookie),
9162306a36Sopenharmony_ci	.head_offset = offsetof(struct prestera_acl_rule, ht_node),
9262306a36Sopenharmony_ci	.automatic_shrinking = true,
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic const struct rhashtable_params __prestera_acl_rule_entry_ht_params = {
9662306a36Sopenharmony_ci	.key_offset  = offsetof(struct prestera_acl_rule_entry, key),
9762306a36Sopenharmony_ci	.head_offset = offsetof(struct prestera_acl_rule_entry, ht_node),
9862306a36Sopenharmony_ci	.key_len     = sizeof(struct prestera_acl_rule_entry_key),
9962306a36Sopenharmony_ci	.automatic_shrinking = true,
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ciint prestera_acl_chain_to_client(u32 chain_index, bool ingress, u32 *client)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	static const u32 ingress_client_map[] = {
10562306a36Sopenharmony_ci		PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_0,
10662306a36Sopenharmony_ci		PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_1,
10762306a36Sopenharmony_ci		PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_2
10862306a36Sopenharmony_ci	};
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (!ingress) {
11162306a36Sopenharmony_ci		/* prestera supports only one chain on egress */
11262306a36Sopenharmony_ci		if (chain_index > 0)
11362306a36Sopenharmony_ci			return -EINVAL;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		*client = PRESTERA_HW_COUNTER_CLIENT_EGRESS_LOOKUP;
11662306a36Sopenharmony_ci		return 0;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (chain_index >= ARRAY_SIZE(ingress_client_map))
12062306a36Sopenharmony_ci		return -EINVAL;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	*client = ingress_client_map[chain_index];
12362306a36Sopenharmony_ci	return 0;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic bool prestera_acl_chain_is_supported(u32 chain_index, bool ingress)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	if (!ingress)
12962306a36Sopenharmony_ci		/* prestera supports only one chain on egress */
13062306a36Sopenharmony_ci		return chain_index == 0;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	return (chain_index & ~PRESTERA_ACL_CHAIN_MASK) == 0;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic struct prestera_acl_ruleset *
13662306a36Sopenharmony_ciprestera_acl_ruleset_create(struct prestera_acl *acl,
13762306a36Sopenharmony_ci			    struct prestera_flow_block *block,
13862306a36Sopenharmony_ci			    u32 chain_index)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct prestera_acl_ruleset *ruleset;
14162306a36Sopenharmony_ci	u32 uid = 0;
14262306a36Sopenharmony_ci	int err;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (!prestera_acl_chain_is_supported(chain_index, block->ingress))
14562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	ruleset = kzalloc(sizeof(*ruleset), GFP_KERNEL);
14862306a36Sopenharmony_ci	if (!ruleset)
14962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	ruleset->acl = acl;
15262306a36Sopenharmony_ci	ruleset->ingress = block->ingress;
15362306a36Sopenharmony_ci	ruleset->ht_key.block = block;
15462306a36Sopenharmony_ci	ruleset->ht_key.chain_index = chain_index;
15562306a36Sopenharmony_ci	refcount_set(&ruleset->refcount, 1);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	err = rhashtable_init(&ruleset->rule_ht, &prestera_acl_rule_ht_params);
15862306a36Sopenharmony_ci	if (err)
15962306a36Sopenharmony_ci		goto err_rhashtable_init;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	err = idr_alloc_u32(&acl->uid, NULL, &uid, U8_MAX, GFP_KERNEL);
16262306a36Sopenharmony_ci	if (err)
16362306a36Sopenharmony_ci		goto err_ruleset_create;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/* make pcl-id based on uid */
16662306a36Sopenharmony_ci	ruleset->pcl_id = PRESTERA_ACL_PCL_ID_MAKE((u8)uid, chain_index);
16762306a36Sopenharmony_ci	ruleset->index = uid;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	ruleset->prio.min = UINT_MAX;
17062306a36Sopenharmony_ci	ruleset->prio.max = 0;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node,
17362306a36Sopenharmony_ci				     prestera_acl_ruleset_ht_params);
17462306a36Sopenharmony_ci	if (err)
17562306a36Sopenharmony_ci		goto err_ruleset_ht_insert;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return ruleset;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cierr_ruleset_ht_insert:
18062306a36Sopenharmony_ci	idr_remove(&acl->uid, uid);
18162306a36Sopenharmony_cierr_ruleset_create:
18262306a36Sopenharmony_ci	rhashtable_destroy(&ruleset->rule_ht);
18362306a36Sopenharmony_cierr_rhashtable_init:
18462306a36Sopenharmony_ci	kfree(ruleset);
18562306a36Sopenharmony_ci	return ERR_PTR(err);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ciint prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset *ruleset,
18962306a36Sopenharmony_ci				     void *keymask)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	ruleset->keymask = kmemdup(keymask, ACL_KEYMASK_SIZE, GFP_KERNEL);
19262306a36Sopenharmony_ci	if (!ruleset->keymask)
19362306a36Sopenharmony_ci		return -ENOMEM;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return 0;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ciint prestera_acl_ruleset_offload(struct prestera_acl_ruleset *ruleset)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct prestera_acl_iface iface;
20162306a36Sopenharmony_ci	u32 vtcam_id;
20262306a36Sopenharmony_ci	int dir;
20362306a36Sopenharmony_ci	int err;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	dir = ruleset->ingress ?
20662306a36Sopenharmony_ci		PRESTERA_HW_VTCAM_DIR_INGRESS : PRESTERA_HW_VTCAM_DIR_EGRESS;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (ruleset->offload)
20962306a36Sopenharmony_ci		return -EEXIST;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	err = prestera_acl_vtcam_id_get(ruleset->acl,
21262306a36Sopenharmony_ci					ruleset->ht_key.chain_index,
21362306a36Sopenharmony_ci					dir,
21462306a36Sopenharmony_ci					ruleset->keymask, &vtcam_id);
21562306a36Sopenharmony_ci	if (err)
21662306a36Sopenharmony_ci		goto err_vtcam_create;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (ruleset->ht_key.chain_index) {
21962306a36Sopenharmony_ci		/* for chain > 0, bind iface index to pcl-id to be able
22062306a36Sopenharmony_ci		 * to jump from any other ruleset to this one using the index.
22162306a36Sopenharmony_ci		 */
22262306a36Sopenharmony_ci		iface.index = ruleset->index;
22362306a36Sopenharmony_ci		iface.type = PRESTERA_ACL_IFACE_TYPE_INDEX;
22462306a36Sopenharmony_ci		err = prestera_hw_vtcam_iface_bind(ruleset->acl->sw, &iface,
22562306a36Sopenharmony_ci						   vtcam_id, ruleset->pcl_id);
22662306a36Sopenharmony_ci		if (err)
22762306a36Sopenharmony_ci			goto err_ruleset_bind;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	ruleset->vtcam_id = vtcam_id;
23162306a36Sopenharmony_ci	ruleset->offload = true;
23262306a36Sopenharmony_ci	return 0;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cierr_ruleset_bind:
23562306a36Sopenharmony_ci	prestera_acl_vtcam_id_put(ruleset->acl, ruleset->vtcam_id);
23662306a36Sopenharmony_cierr_vtcam_create:
23762306a36Sopenharmony_ci	return err;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic void prestera_acl_ruleset_destroy(struct prestera_acl_ruleset *ruleset)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	struct prestera_acl *acl = ruleset->acl;
24362306a36Sopenharmony_ci	u8 uid = ruleset->pcl_id & PRESTERA_ACL_KEYMASK_PCL_ID_USER;
24462306a36Sopenharmony_ci	int err;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node,
24762306a36Sopenharmony_ci			       prestera_acl_ruleset_ht_params);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (ruleset->offload) {
25062306a36Sopenharmony_ci		if (ruleset->ht_key.chain_index) {
25162306a36Sopenharmony_ci			struct prestera_acl_iface iface = {
25262306a36Sopenharmony_ci				.type = PRESTERA_ACL_IFACE_TYPE_INDEX,
25362306a36Sopenharmony_ci				.index = ruleset->index
25462306a36Sopenharmony_ci			};
25562306a36Sopenharmony_ci			err = prestera_hw_vtcam_iface_unbind(acl->sw, &iface,
25662306a36Sopenharmony_ci							     ruleset->vtcam_id);
25762306a36Sopenharmony_ci			WARN_ON(err);
25862306a36Sopenharmony_ci		}
25962306a36Sopenharmony_ci		WARN_ON(prestera_acl_vtcam_id_put(acl, ruleset->vtcam_id));
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	idr_remove(&acl->uid, uid);
26362306a36Sopenharmony_ci	rhashtable_destroy(&ruleset->rule_ht);
26462306a36Sopenharmony_ci	kfree(ruleset->keymask);
26562306a36Sopenharmony_ci	kfree(ruleset);
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic struct prestera_acl_ruleset *
26962306a36Sopenharmony_ci__prestera_acl_ruleset_lookup(struct prestera_acl *acl,
27062306a36Sopenharmony_ci			      struct prestera_flow_block *block,
27162306a36Sopenharmony_ci			      u32 chain_index)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct prestera_acl_ruleset_ht_key ht_key;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	memset(&ht_key, 0, sizeof(ht_key));
27662306a36Sopenharmony_ci	ht_key.block = block;
27762306a36Sopenharmony_ci	ht_key.chain_index = chain_index;
27862306a36Sopenharmony_ci	return rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key,
27962306a36Sopenharmony_ci				      prestera_acl_ruleset_ht_params);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistruct prestera_acl_ruleset *
28362306a36Sopenharmony_ciprestera_acl_ruleset_lookup(struct prestera_acl *acl,
28462306a36Sopenharmony_ci			    struct prestera_flow_block *block,
28562306a36Sopenharmony_ci			    u32 chain_index)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct prestera_acl_ruleset *ruleset;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	ruleset = __prestera_acl_ruleset_lookup(acl, block, chain_index);
29062306a36Sopenharmony_ci	if (!ruleset)
29162306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	refcount_inc(&ruleset->refcount);
29462306a36Sopenharmony_ci	return ruleset;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistruct prestera_acl_ruleset *
29862306a36Sopenharmony_ciprestera_acl_ruleset_get(struct prestera_acl *acl,
29962306a36Sopenharmony_ci			 struct prestera_flow_block *block,
30062306a36Sopenharmony_ci			 u32 chain_index)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	struct prestera_acl_ruleset *ruleset;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	ruleset = __prestera_acl_ruleset_lookup(acl, block, chain_index);
30562306a36Sopenharmony_ci	if (ruleset) {
30662306a36Sopenharmony_ci		refcount_inc(&ruleset->refcount);
30762306a36Sopenharmony_ci		return ruleset;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	return prestera_acl_ruleset_create(acl, block, chain_index);
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_civoid prestera_acl_ruleset_put(struct prestera_acl_ruleset *ruleset)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	if (!refcount_dec_and_test(&ruleset->refcount))
31662306a36Sopenharmony_ci		return;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	prestera_acl_ruleset_destroy(ruleset);
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ciint prestera_acl_ruleset_bind(struct prestera_acl_ruleset *ruleset,
32262306a36Sopenharmony_ci			      struct prestera_port *port)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	struct prestera_acl_iface iface = {
32562306a36Sopenharmony_ci		.type = PRESTERA_ACL_IFACE_TYPE_PORT,
32662306a36Sopenharmony_ci		.port = port
32762306a36Sopenharmony_ci	};
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return prestera_hw_vtcam_iface_bind(port->sw, &iface, ruleset->vtcam_id,
33062306a36Sopenharmony_ci					    ruleset->pcl_id);
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ciint prestera_acl_ruleset_unbind(struct prestera_acl_ruleset *ruleset,
33462306a36Sopenharmony_ci				struct prestera_port *port)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct prestera_acl_iface iface = {
33762306a36Sopenharmony_ci		.type = PRESTERA_ACL_IFACE_TYPE_PORT,
33862306a36Sopenharmony_ci		.port = port
33962306a36Sopenharmony_ci	};
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	return prestera_hw_vtcam_iface_unbind(port->sw, &iface,
34262306a36Sopenharmony_ci					      ruleset->vtcam_id);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic int prestera_acl_ruleset_block_bind(struct prestera_acl_ruleset *ruleset,
34662306a36Sopenharmony_ci					   struct prestera_flow_block *block)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct prestera_flow_block_binding *binding;
34962306a36Sopenharmony_ci	int err;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	block->ruleset_zero = ruleset;
35262306a36Sopenharmony_ci	list_for_each_entry(binding, &block->binding_list, list) {
35362306a36Sopenharmony_ci		err = prestera_acl_ruleset_bind(ruleset, binding->port);
35462306a36Sopenharmony_ci		if (err)
35562306a36Sopenharmony_ci			goto rollback;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci	return 0;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cirollback:
36062306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(binding, &block->binding_list,
36162306a36Sopenharmony_ci					     list)
36262306a36Sopenharmony_ci		err = prestera_acl_ruleset_unbind(ruleset, binding->port);
36362306a36Sopenharmony_ci	block->ruleset_zero = NULL;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return err;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic void
36962306a36Sopenharmony_ciprestera_acl_ruleset_block_unbind(struct prestera_acl_ruleset *ruleset,
37062306a36Sopenharmony_ci				  struct prestera_flow_block *block)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct prestera_flow_block_binding *binding;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	list_for_each_entry(binding, &block->binding_list, list)
37562306a36Sopenharmony_ci		prestera_acl_ruleset_unbind(ruleset, binding->port);
37662306a36Sopenharmony_ci	block->ruleset_zero = NULL;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic void
38062306a36Sopenharmony_ciprestera_acl_ruleset_prio_refresh(struct prestera_acl *acl,
38162306a36Sopenharmony_ci				  struct prestera_acl_ruleset *ruleset)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct prestera_acl_rule *rule;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	ruleset->prio.min = UINT_MAX;
38662306a36Sopenharmony_ci	ruleset->prio.max = 0;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	list_for_each_entry(rule, &acl->rules, list) {
38962306a36Sopenharmony_ci		if (ruleset->ingress != rule->ruleset->ingress)
39062306a36Sopenharmony_ci			continue;
39162306a36Sopenharmony_ci		if (ruleset->ht_key.chain_index != rule->chain_index)
39262306a36Sopenharmony_ci			continue;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		ruleset->prio.min = min(ruleset->prio.min, rule->priority);
39562306a36Sopenharmony_ci		ruleset->prio.max = max(ruleset->prio.max, rule->priority);
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_civoid
40062306a36Sopenharmony_ciprestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule, u16 pcl_id)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct prestera_acl_match *r_match = &rule->re_key.match;
40362306a36Sopenharmony_ci	__be16 pcl_id_mask = htons(PRESTERA_ACL_KEYMASK_PCL_ID);
40462306a36Sopenharmony_ci	__be16 pcl_id_key = htons(pcl_id);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	rule_match_set(r_match->key, PCL_ID, pcl_id_key);
40762306a36Sopenharmony_ci	rule_match_set(r_match->mask, PCL_ID, pcl_id_mask);
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistruct prestera_acl_rule *
41162306a36Sopenharmony_ciprestera_acl_rule_lookup(struct prestera_acl_ruleset *ruleset,
41262306a36Sopenharmony_ci			 unsigned long cookie)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	return rhashtable_lookup_fast(&ruleset->rule_ht, &cookie,
41562306a36Sopenharmony_ci				      prestera_acl_rule_ht_params);
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ciu32 prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset *ruleset)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	return ruleset->index;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_civoid prestera_acl_ruleset_prio_get(struct prestera_acl_ruleset *ruleset,
42462306a36Sopenharmony_ci				   u32 *prio_min, u32 *prio_max)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	*prio_min = ruleset->prio.min;
42762306a36Sopenharmony_ci	*prio_max = ruleset->prio.max;
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cibool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	return ruleset->offload;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistruct prestera_acl_rule *
43662306a36Sopenharmony_ciprestera_acl_rule_create(struct prestera_acl_ruleset *ruleset,
43762306a36Sopenharmony_ci			 unsigned long cookie, u32 chain_index)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	struct prestera_acl_rule *rule;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	rule = kzalloc(sizeof(*rule), GFP_KERNEL);
44262306a36Sopenharmony_ci	if (!rule)
44362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	rule->ruleset = ruleset;
44662306a36Sopenharmony_ci	rule->cookie = cookie;
44762306a36Sopenharmony_ci	rule->chain_index = chain_index;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	refcount_inc(&ruleset->refcount);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	return rule;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_civoid prestera_acl_rule_priority_set(struct prestera_acl_rule *rule,
45562306a36Sopenharmony_ci				    u32 priority)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	rule->priority = priority;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_civoid prestera_acl_rule_destroy(struct prestera_acl_rule *rule)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	if (rule->jump_ruleset)
46362306a36Sopenharmony_ci		/* release ruleset kept by jump action */
46462306a36Sopenharmony_ci		prestera_acl_ruleset_put(rule->jump_ruleset);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	prestera_acl_ruleset_put(rule->ruleset);
46762306a36Sopenharmony_ci	kfree(rule);
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic void prestera_acl_ruleset_prio_update(struct prestera_acl_ruleset *ruleset,
47162306a36Sopenharmony_ci					     u32 prio)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	ruleset->prio.min = min(ruleset->prio.min, prio);
47462306a36Sopenharmony_ci	ruleset->prio.max = max(ruleset->prio.max, prio);
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ciint prestera_acl_rule_add(struct prestera_switch *sw,
47862306a36Sopenharmony_ci			  struct prestera_acl_rule *rule)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	int err;
48162306a36Sopenharmony_ci	struct prestera_acl_ruleset *ruleset = rule->ruleset;
48262306a36Sopenharmony_ci	struct prestera_flow_block *block = ruleset->ht_key.block;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	/* try to add rule to hash table first */
48562306a36Sopenharmony_ci	err = rhashtable_insert_fast(&ruleset->rule_ht, &rule->ht_node,
48662306a36Sopenharmony_ci				     prestera_acl_rule_ht_params);
48762306a36Sopenharmony_ci	if (err)
48862306a36Sopenharmony_ci		goto err_ht_insert;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	prestera_acl_rule_keymask_pcl_id_set(rule, ruleset->pcl_id);
49162306a36Sopenharmony_ci	rule->re_arg.vtcam_id = ruleset->vtcam_id;
49262306a36Sopenharmony_ci	rule->re_key.prio = rule->priority;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	rule->re = prestera_acl_rule_entry_find(sw->acl, &rule->re_key);
49562306a36Sopenharmony_ci	err = WARN_ON(rule->re) ? -EEXIST : 0;
49662306a36Sopenharmony_ci	if (err)
49762306a36Sopenharmony_ci		goto err_rule_add;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	rule->re = prestera_acl_rule_entry_create(sw->acl, &rule->re_key,
50062306a36Sopenharmony_ci						  &rule->re_arg);
50162306a36Sopenharmony_ci	err = !rule->re ? -EINVAL : 0;
50262306a36Sopenharmony_ci	if (err)
50362306a36Sopenharmony_ci		goto err_rule_add;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	/* bind the block (all ports) to chain index 0, rest of
50662306a36Sopenharmony_ci	 * the chains are bound to goto action
50762306a36Sopenharmony_ci	 */
50862306a36Sopenharmony_ci	if (!ruleset->ht_key.chain_index && !ruleset->rule_count) {
50962306a36Sopenharmony_ci		err = prestera_acl_ruleset_block_bind(ruleset, block);
51062306a36Sopenharmony_ci		if (err)
51162306a36Sopenharmony_ci			goto err_acl_block_bind;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	list_add_tail(&rule->list, &sw->acl->rules);
51562306a36Sopenharmony_ci	ruleset->rule_count++;
51662306a36Sopenharmony_ci	prestera_acl_ruleset_prio_update(ruleset, rule->priority);
51762306a36Sopenharmony_ci	return 0;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cierr_acl_block_bind:
52062306a36Sopenharmony_ci	prestera_acl_rule_entry_destroy(sw->acl, rule->re);
52162306a36Sopenharmony_cierr_rule_add:
52262306a36Sopenharmony_ci	rule->re = NULL;
52362306a36Sopenharmony_ci	rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
52462306a36Sopenharmony_ci			       prestera_acl_rule_ht_params);
52562306a36Sopenharmony_cierr_ht_insert:
52662306a36Sopenharmony_ci	return err;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_civoid prestera_acl_rule_del(struct prestera_switch *sw,
53062306a36Sopenharmony_ci			   struct prestera_acl_rule *rule)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	struct prestera_acl_ruleset *ruleset = rule->ruleset;
53362306a36Sopenharmony_ci	struct prestera_flow_block *block = ruleset->ht_key.block;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
53662306a36Sopenharmony_ci			       prestera_acl_rule_ht_params);
53762306a36Sopenharmony_ci	ruleset->rule_count--;
53862306a36Sopenharmony_ci	list_del(&rule->list);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	prestera_acl_rule_entry_destroy(sw->acl, rule->re);
54162306a36Sopenharmony_ci	prestera_acl_ruleset_prio_refresh(sw->acl, ruleset);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	/* unbind block (all ports) */
54462306a36Sopenharmony_ci	if (!ruleset->ht_key.chain_index && !ruleset->rule_count)
54562306a36Sopenharmony_ci		prestera_acl_ruleset_block_unbind(ruleset, block);
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ciint prestera_acl_rule_get_stats(struct prestera_acl *acl,
54962306a36Sopenharmony_ci				struct prestera_acl_rule *rule,
55062306a36Sopenharmony_ci				u64 *packets, u64 *bytes, u64 *last_use)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	u64 current_packets;
55362306a36Sopenharmony_ci	u64 current_bytes;
55462306a36Sopenharmony_ci	int err;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	err = prestera_counter_stats_get(acl->sw->counter,
55762306a36Sopenharmony_ci					 rule->re->counter.block,
55862306a36Sopenharmony_ci					 rule->re->counter.id,
55962306a36Sopenharmony_ci					 &current_packets, &current_bytes);
56062306a36Sopenharmony_ci	if (err)
56162306a36Sopenharmony_ci		return err;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	*packets = current_packets;
56462306a36Sopenharmony_ci	*bytes = current_bytes;
56562306a36Sopenharmony_ci	*last_use = jiffies;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	return 0;
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_cistruct prestera_acl_rule_entry *
57162306a36Sopenharmony_ciprestera_acl_rule_entry_find(struct prestera_acl *acl,
57262306a36Sopenharmony_ci			     struct prestera_acl_rule_entry_key *key)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	return rhashtable_lookup_fast(&acl->acl_rule_entry_ht, key,
57562306a36Sopenharmony_ci				      __prestera_acl_rule_entry_ht_params);
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic int __prestera_acl_rule_entry2hw_del(struct prestera_switch *sw,
57962306a36Sopenharmony_ci					    struct prestera_acl_rule_entry *e)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	return prestera_hw_vtcam_rule_del(sw, e->vtcam_id, e->hw_id);
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic int __prestera_acl_rule_entry2hw_add(struct prestera_switch *sw,
58562306a36Sopenharmony_ci					    struct prestera_acl_rule_entry *e)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	struct prestera_acl_hw_action_info act_hw[PRESTERA_ACL_RULE_ACTION_MAX];
58862306a36Sopenharmony_ci	int act_num;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	memset(&act_hw, 0, sizeof(act_hw));
59162306a36Sopenharmony_ci	act_num = 0;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	/* accept */
59462306a36Sopenharmony_ci	if (e->accept.valid) {
59562306a36Sopenharmony_ci		act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_ACCEPT;
59662306a36Sopenharmony_ci		act_num++;
59762306a36Sopenharmony_ci	}
59862306a36Sopenharmony_ci	/* drop */
59962306a36Sopenharmony_ci	if (e->drop.valid) {
60062306a36Sopenharmony_ci		act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_DROP;
60162306a36Sopenharmony_ci		act_num++;
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci	/* trap */
60462306a36Sopenharmony_ci	if (e->trap.valid) {
60562306a36Sopenharmony_ci		act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_TRAP;
60662306a36Sopenharmony_ci		act_num++;
60762306a36Sopenharmony_ci	}
60862306a36Sopenharmony_ci	/* police */
60962306a36Sopenharmony_ci	if (e->police.valid) {
61062306a36Sopenharmony_ci		act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_POLICE;
61162306a36Sopenharmony_ci		act_hw[act_num].police = e->police.i;
61262306a36Sopenharmony_ci		act_num++;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci	/* jump */
61562306a36Sopenharmony_ci	if (e->jump.valid) {
61662306a36Sopenharmony_ci		act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_JUMP;
61762306a36Sopenharmony_ci		act_hw[act_num].jump = e->jump.i;
61862306a36Sopenharmony_ci		act_num++;
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci	/* counter */
62162306a36Sopenharmony_ci	if (e->counter.block) {
62262306a36Sopenharmony_ci		act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_COUNT;
62362306a36Sopenharmony_ci		act_hw[act_num].count.id = e->counter.id;
62462306a36Sopenharmony_ci		act_num++;
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	return prestera_hw_vtcam_rule_add(sw, e->vtcam_id, e->key.prio,
62862306a36Sopenharmony_ci					  e->key.match.key, e->key.match.mask,
62962306a36Sopenharmony_ci					  act_hw, act_num, &e->hw_id);
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic void
63362306a36Sopenharmony_ci__prestera_acl_rule_entry_act_destruct(struct prestera_switch *sw,
63462306a36Sopenharmony_ci				       struct prestera_acl_rule_entry *e)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	/* counter */
63762306a36Sopenharmony_ci	prestera_counter_put(sw->counter, e->counter.block, e->counter.id);
63862306a36Sopenharmony_ci	/* police */
63962306a36Sopenharmony_ci	if (e->police.valid)
64062306a36Sopenharmony_ci		prestera_hw_policer_release(sw, e->police.i.id);
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_civoid prestera_acl_rule_entry_destroy(struct prestera_acl *acl,
64462306a36Sopenharmony_ci				     struct prestera_acl_rule_entry *e)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	int ret;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	rhashtable_remove_fast(&acl->acl_rule_entry_ht, &e->ht_node,
64962306a36Sopenharmony_ci			       __prestera_acl_rule_entry_ht_params);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	ret = __prestera_acl_rule_entry2hw_del(acl->sw, e);
65262306a36Sopenharmony_ci	WARN_ON(ret && ret != -ENODEV);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	__prestera_acl_rule_entry_act_destruct(acl->sw, e);
65562306a36Sopenharmony_ci	kfree(e);
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_cistatic int
65962306a36Sopenharmony_ci__prestera_acl_rule_entry_act_construct(struct prestera_switch *sw,
66062306a36Sopenharmony_ci					struct prestera_acl_rule_entry *e,
66162306a36Sopenharmony_ci					struct prestera_acl_rule_entry_arg *arg)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	int err;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	/* accept */
66662306a36Sopenharmony_ci	e->accept.valid = arg->accept.valid;
66762306a36Sopenharmony_ci	/* drop */
66862306a36Sopenharmony_ci	e->drop.valid = arg->drop.valid;
66962306a36Sopenharmony_ci	/* trap */
67062306a36Sopenharmony_ci	e->trap.valid = arg->trap.valid;
67162306a36Sopenharmony_ci	/* jump */
67262306a36Sopenharmony_ci	e->jump.valid = arg->jump.valid;
67362306a36Sopenharmony_ci	e->jump.i = arg->jump.i;
67462306a36Sopenharmony_ci	/* police */
67562306a36Sopenharmony_ci	if (arg->police.valid) {
67662306a36Sopenharmony_ci		u8 type = arg->police.ingress ? PRESTERA_POLICER_TYPE_INGRESS :
67762306a36Sopenharmony_ci						PRESTERA_POLICER_TYPE_EGRESS;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci		err = prestera_hw_policer_create(sw, type, &e->police.i.id);
68062306a36Sopenharmony_ci		if (err)
68162306a36Sopenharmony_ci			goto err_out;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		err = prestera_hw_policer_sr_tcm_set(sw, e->police.i.id,
68462306a36Sopenharmony_ci						     arg->police.rate,
68562306a36Sopenharmony_ci						     arg->police.burst);
68662306a36Sopenharmony_ci		if (err) {
68762306a36Sopenharmony_ci			prestera_hw_policer_release(sw, e->police.i.id);
68862306a36Sopenharmony_ci			goto err_out;
68962306a36Sopenharmony_ci		}
69062306a36Sopenharmony_ci		e->police.valid = arg->police.valid;
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci	/* counter */
69362306a36Sopenharmony_ci	if (arg->count.valid) {
69462306a36Sopenharmony_ci		err = prestera_counter_get(sw->counter, arg->count.client,
69562306a36Sopenharmony_ci					   &e->counter.block,
69662306a36Sopenharmony_ci					   &e->counter.id);
69762306a36Sopenharmony_ci		if (err)
69862306a36Sopenharmony_ci			goto err_out;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	return 0;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_cierr_out:
70462306a36Sopenharmony_ci	__prestera_acl_rule_entry_act_destruct(sw, e);
70562306a36Sopenharmony_ci	return -EINVAL;
70662306a36Sopenharmony_ci}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_cistruct prestera_acl_rule_entry *
70962306a36Sopenharmony_ciprestera_acl_rule_entry_create(struct prestera_acl *acl,
71062306a36Sopenharmony_ci			       struct prestera_acl_rule_entry_key *key,
71162306a36Sopenharmony_ci			       struct prestera_acl_rule_entry_arg *arg)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	struct prestera_acl_rule_entry *e;
71462306a36Sopenharmony_ci	int err;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	e = kzalloc(sizeof(*e), GFP_KERNEL);
71762306a36Sopenharmony_ci	if (!e)
71862306a36Sopenharmony_ci		goto err_kzalloc;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	memcpy(&e->key, key, sizeof(*key));
72162306a36Sopenharmony_ci	e->vtcam_id = arg->vtcam_id;
72262306a36Sopenharmony_ci	err = __prestera_acl_rule_entry_act_construct(acl->sw, e, arg);
72362306a36Sopenharmony_ci	if (err)
72462306a36Sopenharmony_ci		goto err_act_construct;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	err = __prestera_acl_rule_entry2hw_add(acl->sw, e);
72762306a36Sopenharmony_ci	if (err)
72862306a36Sopenharmony_ci		goto err_hw_add;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	err = rhashtable_insert_fast(&acl->acl_rule_entry_ht, &e->ht_node,
73162306a36Sopenharmony_ci				     __prestera_acl_rule_entry_ht_params);
73262306a36Sopenharmony_ci	if (err)
73362306a36Sopenharmony_ci		goto err_ht_insert;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	return e;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cierr_ht_insert:
73862306a36Sopenharmony_ci	WARN_ON(__prestera_acl_rule_entry2hw_del(acl->sw, e));
73962306a36Sopenharmony_cierr_hw_add:
74062306a36Sopenharmony_ci	__prestera_acl_rule_entry_act_destruct(acl->sw, e);
74162306a36Sopenharmony_cierr_act_construct:
74262306a36Sopenharmony_ci	kfree(e);
74362306a36Sopenharmony_cierr_kzalloc:
74462306a36Sopenharmony_ci	return NULL;
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_cistatic int __prestera_acl_vtcam_id_try_fit(struct prestera_acl *acl, u8 lookup,
74862306a36Sopenharmony_ci					   void *keymask, u32 *vtcam_id)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	struct prestera_acl_vtcam *vtcam;
75162306a36Sopenharmony_ci	int i;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	list_for_each_entry(vtcam, &acl->vtcam_list, list) {
75462306a36Sopenharmony_ci		if (lookup != vtcam->lookup)
75562306a36Sopenharmony_ci			continue;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci		if (!keymask && !vtcam->is_keymask_set)
75862306a36Sopenharmony_ci			goto vtcam_found;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci		if (!(keymask && vtcam->is_keymask_set))
76162306a36Sopenharmony_ci			continue;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci		/* try to fit with vtcam keymask */
76462306a36Sopenharmony_ci		for (i = 0; i < __PRESTERA_ACL_RULE_MATCH_TYPE_MAX; i++) {
76562306a36Sopenharmony_ci			__be32 __keymask = ((__be32 *)keymask)[i];
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci			if (!__keymask)
76862306a36Sopenharmony_ci				/* vtcam keymask in not interested */
76962306a36Sopenharmony_ci				continue;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci			if (__keymask & ~vtcam->keymask[i])
77262306a36Sopenharmony_ci				/* keymask does not fit the vtcam keymask */
77362306a36Sopenharmony_ci				break;
77462306a36Sopenharmony_ci		}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci		if (i == __PRESTERA_ACL_RULE_MATCH_TYPE_MAX)
77762306a36Sopenharmony_ci			/* keymask fits vtcam keymask, return it */
77862306a36Sopenharmony_ci			goto vtcam_found;
77962306a36Sopenharmony_ci	}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	/* nothing is found */
78262306a36Sopenharmony_ci	return -ENOENT;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_civtcam_found:
78562306a36Sopenharmony_ci	refcount_inc(&vtcam->refcount);
78662306a36Sopenharmony_ci	*vtcam_id = vtcam->id;
78762306a36Sopenharmony_ci	return 0;
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ciint prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup, u8 dir,
79162306a36Sopenharmony_ci			      void *keymask, u32 *vtcam_id)
79262306a36Sopenharmony_ci{
79362306a36Sopenharmony_ci	struct prestera_acl_vtcam *vtcam;
79462306a36Sopenharmony_ci	u32 new_vtcam_id;
79562306a36Sopenharmony_ci	int err;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	/* find the vtcam that suits keymask. We do not expect to have
79862306a36Sopenharmony_ci	 * a big number of vtcams, so, the list type for vtcam list is
79962306a36Sopenharmony_ci	 * fine for now
80062306a36Sopenharmony_ci	 */
80162306a36Sopenharmony_ci	list_for_each_entry(vtcam, &acl->vtcam_list, list) {
80262306a36Sopenharmony_ci		if (lookup != vtcam->lookup ||
80362306a36Sopenharmony_ci		    dir != vtcam->direction)
80462306a36Sopenharmony_ci			continue;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci		if (!keymask && !vtcam->is_keymask_set) {
80762306a36Sopenharmony_ci			refcount_inc(&vtcam->refcount);
80862306a36Sopenharmony_ci			goto vtcam_found;
80962306a36Sopenharmony_ci		}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci		if (keymask && vtcam->is_keymask_set &&
81262306a36Sopenharmony_ci		    !memcmp(keymask, vtcam->keymask, sizeof(vtcam->keymask))) {
81362306a36Sopenharmony_ci			refcount_inc(&vtcam->refcount);
81462306a36Sopenharmony_ci			goto vtcam_found;
81562306a36Sopenharmony_ci		}
81662306a36Sopenharmony_ci	}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	/* vtcam not found, try to create new one */
81962306a36Sopenharmony_ci	vtcam = kzalloc(sizeof(*vtcam), GFP_KERNEL);
82062306a36Sopenharmony_ci	if (!vtcam)
82162306a36Sopenharmony_ci		return -ENOMEM;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	err = prestera_hw_vtcam_create(acl->sw, lookup, keymask, &new_vtcam_id,
82462306a36Sopenharmony_ci				       dir);
82562306a36Sopenharmony_ci	if (err) {
82662306a36Sopenharmony_ci		kfree(vtcam);
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci		/* cannot create new, try to fit into existing vtcam */
82962306a36Sopenharmony_ci		if (__prestera_acl_vtcam_id_try_fit(acl, lookup,
83062306a36Sopenharmony_ci						    keymask, &new_vtcam_id))
83162306a36Sopenharmony_ci			return err;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci		*vtcam_id = new_vtcam_id;
83462306a36Sopenharmony_ci		return 0;
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	vtcam->direction = dir;
83862306a36Sopenharmony_ci	vtcam->id = new_vtcam_id;
83962306a36Sopenharmony_ci	vtcam->lookup = lookup;
84062306a36Sopenharmony_ci	if (keymask) {
84162306a36Sopenharmony_ci		memcpy(vtcam->keymask, keymask, sizeof(vtcam->keymask));
84262306a36Sopenharmony_ci		vtcam->is_keymask_set = true;
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci	refcount_set(&vtcam->refcount, 1);
84562306a36Sopenharmony_ci	list_add_rcu(&vtcam->list, &acl->vtcam_list);
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_civtcam_found:
84862306a36Sopenharmony_ci	*vtcam_id = vtcam->id;
84962306a36Sopenharmony_ci	return 0;
85062306a36Sopenharmony_ci}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ciint prestera_acl_vtcam_id_put(struct prestera_acl *acl, u32 vtcam_id)
85362306a36Sopenharmony_ci{
85462306a36Sopenharmony_ci	struct prestera_acl_vtcam *vtcam;
85562306a36Sopenharmony_ci	int err;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	list_for_each_entry(vtcam, &acl->vtcam_list, list) {
85862306a36Sopenharmony_ci		if (vtcam_id != vtcam->id)
85962306a36Sopenharmony_ci			continue;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci		if (!refcount_dec_and_test(&vtcam->refcount))
86262306a36Sopenharmony_ci			return 0;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci		err = prestera_hw_vtcam_destroy(acl->sw, vtcam->id);
86562306a36Sopenharmony_ci		if (err && err != -ENODEV) {
86662306a36Sopenharmony_ci			refcount_set(&vtcam->refcount, 1);
86762306a36Sopenharmony_ci			return err;
86862306a36Sopenharmony_ci		}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci		list_del(&vtcam->list);
87162306a36Sopenharmony_ci		kfree(vtcam);
87262306a36Sopenharmony_ci		return 0;
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	return -ENOENT;
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ciint prestera_acl_init(struct prestera_switch *sw)
87962306a36Sopenharmony_ci{
88062306a36Sopenharmony_ci	struct prestera_acl *acl;
88162306a36Sopenharmony_ci	int err;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	acl = kzalloc(sizeof(*acl), GFP_KERNEL);
88462306a36Sopenharmony_ci	if (!acl)
88562306a36Sopenharmony_ci		return -ENOMEM;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	acl->sw = sw;
88862306a36Sopenharmony_ci	INIT_LIST_HEAD(&acl->rules);
88962306a36Sopenharmony_ci	INIT_LIST_HEAD(&acl->vtcam_list);
89062306a36Sopenharmony_ci	idr_init(&acl->uid);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	err = rhashtable_init(&acl->acl_rule_entry_ht,
89362306a36Sopenharmony_ci			      &__prestera_acl_rule_entry_ht_params);
89462306a36Sopenharmony_ci	if (err)
89562306a36Sopenharmony_ci		goto err_acl_rule_entry_ht_init;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	err = rhashtable_init(&acl->ruleset_ht,
89862306a36Sopenharmony_ci			      &prestera_acl_ruleset_ht_params);
89962306a36Sopenharmony_ci	if (err)
90062306a36Sopenharmony_ci		goto err_ruleset_ht_init;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	sw->acl = acl;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	return 0;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_cierr_ruleset_ht_init:
90762306a36Sopenharmony_ci	rhashtable_destroy(&acl->acl_rule_entry_ht);
90862306a36Sopenharmony_cierr_acl_rule_entry_ht_init:
90962306a36Sopenharmony_ci	kfree(acl);
91062306a36Sopenharmony_ci	return err;
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_civoid prestera_acl_fini(struct prestera_switch *sw)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct prestera_acl *acl = sw->acl;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	WARN_ON(!idr_is_empty(&acl->uid));
91862306a36Sopenharmony_ci	idr_destroy(&acl->uid);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	WARN_ON(!list_empty(&acl->vtcam_list));
92162306a36Sopenharmony_ci	WARN_ON(!list_empty(&acl->rules));
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	rhashtable_destroy(&acl->ruleset_ht);
92462306a36Sopenharmony_ci	rhashtable_destroy(&acl->acl_rule_entry_ht);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	kfree(acl);
92762306a36Sopenharmony_ci}
928