18b474a9fSSerhiy Boiko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
247327e19SVolodymyr Mytnyk /* Copyright (c) 2020-2021 Marvell International Ltd. All rights reserved */
38b474a9fSSerhiy Boiko
48b474a9fSSerhiy Boiko #include <linux/rhashtable.h>
58b474a9fSSerhiy Boiko
68b474a9fSSerhiy Boiko #include "prestera_acl.h"
747327e19SVolodymyr Mytnyk #include "prestera_flow.h"
847327e19SVolodymyr Mytnyk #include "prestera_hw.h"
947327e19SVolodymyr Mytnyk #include "prestera.h"
1047327e19SVolodymyr Mytnyk
1147327e19SVolodymyr Mytnyk #define ACL_KEYMASK_SIZE \
1247327e19SVolodymyr Mytnyk (sizeof(__be32) * __PRESTERA_ACL_RULE_MATCH_TYPE_MAX)
138b474a9fSSerhiy Boiko
148b474a9fSSerhiy Boiko struct prestera_acl {
158b474a9fSSerhiy Boiko struct prestera_switch *sw;
1647327e19SVolodymyr Mytnyk struct list_head vtcam_list;
178b474a9fSSerhiy Boiko struct list_head rules;
1847327e19SVolodymyr Mytnyk struct rhashtable ruleset_ht;
1947327e19SVolodymyr Mytnyk struct rhashtable acl_rule_entry_ht;
2047327e19SVolodymyr Mytnyk struct idr uid;
2147327e19SVolodymyr Mytnyk };
2247327e19SVolodymyr Mytnyk
2347327e19SVolodymyr Mytnyk struct prestera_acl_ruleset_ht_key {
2447327e19SVolodymyr Mytnyk struct prestera_flow_block *block;
25fa5d824cSVolodymyr Mytnyk u32 chain_index;
2647327e19SVolodymyr Mytnyk };
2747327e19SVolodymyr Mytnyk
2847327e19SVolodymyr Mytnyk struct prestera_acl_rule_entry {
2947327e19SVolodymyr Mytnyk struct rhash_head ht_node;
3047327e19SVolodymyr Mytnyk struct prestera_acl_rule_entry_key key;
3147327e19SVolodymyr Mytnyk u32 hw_id;
3247327e19SVolodymyr Mytnyk u32 vtcam_id;
3347327e19SVolodymyr Mytnyk struct {
3447327e19SVolodymyr Mytnyk struct {
3547327e19SVolodymyr Mytnyk u8 valid:1;
3647327e19SVolodymyr Mytnyk } accept, drop, trap;
37adefefe5SVolodymyr Mytnyk struct {
38dde2daa0SVolodymyr Mytnyk u8 valid:1;
39dde2daa0SVolodymyr Mytnyk struct prestera_acl_action_police i;
40dde2daa0SVolodymyr Mytnyk } police;
41dde2daa0SVolodymyr Mytnyk struct {
42fa5d824cSVolodymyr Mytnyk struct prestera_acl_action_jump i;
43fa5d824cSVolodymyr Mytnyk u8 valid:1;
44fa5d824cSVolodymyr Mytnyk } jump;
45fa5d824cSVolodymyr Mytnyk struct {
46adefefe5SVolodymyr Mytnyk u32 id;
47adefefe5SVolodymyr Mytnyk struct prestera_counter_block *block;
48adefefe5SVolodymyr Mytnyk } counter;
4947327e19SVolodymyr Mytnyk };
508b474a9fSSerhiy Boiko };
518b474a9fSSerhiy Boiko
528b474a9fSSerhiy Boiko struct prestera_acl_ruleset {
5347327e19SVolodymyr Mytnyk struct rhash_head ht_node; /* Member of acl HT */
5447327e19SVolodymyr Mytnyk struct prestera_acl_ruleset_ht_key ht_key;
558b474a9fSSerhiy Boiko struct rhashtable rule_ht;
5647327e19SVolodymyr Mytnyk struct prestera_acl *acl;
5744af9571SMaksym Glubokiy struct {
5844af9571SMaksym Glubokiy u32 min;
5944af9571SMaksym Glubokiy u32 max;
6044af9571SMaksym Glubokiy } prio;
6147327e19SVolodymyr Mytnyk unsigned long rule_count;
6247327e19SVolodymyr Mytnyk refcount_t refcount;
6347327e19SVolodymyr Mytnyk void *keymask;
6447327e19SVolodymyr Mytnyk u32 vtcam_id;
65fa5d824cSVolodymyr Mytnyk u32 index;
6647327e19SVolodymyr Mytnyk u16 pcl_id;
6747327e19SVolodymyr Mytnyk bool offload;
68702e7014SMaksym Glubokiy bool ingress;
698b474a9fSSerhiy Boiko };
708b474a9fSSerhiy Boiko
7147327e19SVolodymyr Mytnyk struct prestera_acl_vtcam {
728b474a9fSSerhiy Boiko struct list_head list;
7347327e19SVolodymyr Mytnyk __be32 keymask[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX];
7447327e19SVolodymyr Mytnyk refcount_t refcount;
758b474a9fSSerhiy Boiko u32 id;
7647327e19SVolodymyr Mytnyk bool is_keymask_set;
7747327e19SVolodymyr Mytnyk u8 lookup;
78702e7014SMaksym Glubokiy u8 direction;
7947327e19SVolodymyr Mytnyk };
8047327e19SVolodymyr Mytnyk
8147327e19SVolodymyr Mytnyk static const struct rhashtable_params prestera_acl_ruleset_ht_params = {
8247327e19SVolodymyr Mytnyk .key_len = sizeof(struct prestera_acl_ruleset_ht_key),
8347327e19SVolodymyr Mytnyk .key_offset = offsetof(struct prestera_acl_ruleset, ht_key),
8447327e19SVolodymyr Mytnyk .head_offset = offsetof(struct prestera_acl_ruleset, ht_node),
8547327e19SVolodymyr Mytnyk .automatic_shrinking = true,
868b474a9fSSerhiy Boiko };
878b474a9fSSerhiy Boiko
888b474a9fSSerhiy Boiko static const struct rhashtable_params prestera_acl_rule_ht_params = {
898b474a9fSSerhiy Boiko .key_len = sizeof(unsigned long),
908b474a9fSSerhiy Boiko .key_offset = offsetof(struct prestera_acl_rule, cookie),
918b474a9fSSerhiy Boiko .head_offset = offsetof(struct prestera_acl_rule, ht_node),
928b474a9fSSerhiy Boiko .automatic_shrinking = true,
938b474a9fSSerhiy Boiko };
948b474a9fSSerhiy Boiko
9547327e19SVolodymyr Mytnyk static const struct rhashtable_params __prestera_acl_rule_entry_ht_params = {
9647327e19SVolodymyr Mytnyk .key_offset = offsetof(struct prestera_acl_rule_entry, key),
9747327e19SVolodymyr Mytnyk .head_offset = offsetof(struct prestera_acl_rule_entry, ht_node),
9847327e19SVolodymyr Mytnyk .key_len = sizeof(struct prestera_acl_rule_entry_key),
9947327e19SVolodymyr Mytnyk .automatic_shrinking = true,
10047327e19SVolodymyr Mytnyk };
10147327e19SVolodymyr Mytnyk
prestera_acl_chain_to_client(u32 chain_index,bool ingress,u32 * client)102702e7014SMaksym Glubokiy int prestera_acl_chain_to_client(u32 chain_index, bool ingress, u32 *client)
103fa5d824cSVolodymyr Mytnyk {
104702e7014SMaksym Glubokiy static const u32 ingress_client_map[] = {
105702e7014SMaksym Glubokiy PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_0,
106702e7014SMaksym Glubokiy PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_1,
107702e7014SMaksym Glubokiy PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_2
108fa5d824cSVolodymyr Mytnyk };
109fa5d824cSVolodymyr Mytnyk
110702e7014SMaksym Glubokiy if (!ingress) {
111702e7014SMaksym Glubokiy /* prestera supports only one chain on egress */
112702e7014SMaksym Glubokiy if (chain_index > 0)
113fa5d824cSVolodymyr Mytnyk return -EINVAL;
114fa5d824cSVolodymyr Mytnyk
115702e7014SMaksym Glubokiy *client = PRESTERA_HW_COUNTER_CLIENT_EGRESS_LOOKUP;
116fa5d824cSVolodymyr Mytnyk return 0;
117fa5d824cSVolodymyr Mytnyk }
118fa5d824cSVolodymyr Mytnyk
119702e7014SMaksym Glubokiy if (chain_index >= ARRAY_SIZE(ingress_client_map))
120702e7014SMaksym Glubokiy return -EINVAL;
121702e7014SMaksym Glubokiy
122702e7014SMaksym Glubokiy *client = ingress_client_map[chain_index];
123702e7014SMaksym Glubokiy return 0;
124702e7014SMaksym Glubokiy }
125702e7014SMaksym Glubokiy
prestera_acl_chain_is_supported(u32 chain_index,bool ingress)126702e7014SMaksym Glubokiy static bool prestera_acl_chain_is_supported(u32 chain_index, bool ingress)
127fa5d824cSVolodymyr Mytnyk {
128702e7014SMaksym Glubokiy if (!ingress)
129702e7014SMaksym Glubokiy /* prestera supports only one chain on egress */
130702e7014SMaksym Glubokiy return chain_index == 0;
131702e7014SMaksym Glubokiy
132fa5d824cSVolodymyr Mytnyk return (chain_index & ~PRESTERA_ACL_CHAIN_MASK) == 0;
133fa5d824cSVolodymyr Mytnyk }
134fa5d824cSVolodymyr Mytnyk
1358b474a9fSSerhiy Boiko static struct prestera_acl_ruleset *
prestera_acl_ruleset_create(struct prestera_acl * acl,struct prestera_flow_block * block,u32 chain_index)13647327e19SVolodymyr Mytnyk prestera_acl_ruleset_create(struct prestera_acl *acl,
137fa5d824cSVolodymyr Mytnyk struct prestera_flow_block *block,
138fa5d824cSVolodymyr Mytnyk u32 chain_index)
1398b474a9fSSerhiy Boiko {
1408b474a9fSSerhiy Boiko struct prestera_acl_ruleset *ruleset;
141604ba230SVolodymyr Mytnyk u32 uid = 0;
1428b474a9fSSerhiy Boiko int err;
1438b474a9fSSerhiy Boiko
144702e7014SMaksym Glubokiy if (!prestera_acl_chain_is_supported(chain_index, block->ingress))
145fa5d824cSVolodymyr Mytnyk return ERR_PTR(-EINVAL);
146fa5d824cSVolodymyr Mytnyk
1478b474a9fSSerhiy Boiko ruleset = kzalloc(sizeof(*ruleset), GFP_KERNEL);
1488b474a9fSSerhiy Boiko if (!ruleset)
1498b474a9fSSerhiy Boiko return ERR_PTR(-ENOMEM);
1508b474a9fSSerhiy Boiko
15147327e19SVolodymyr Mytnyk ruleset->acl = acl;
152702e7014SMaksym Glubokiy ruleset->ingress = block->ingress;
15347327e19SVolodymyr Mytnyk ruleset->ht_key.block = block;
154fa5d824cSVolodymyr Mytnyk ruleset->ht_key.chain_index = chain_index;
15547327e19SVolodymyr Mytnyk refcount_set(&ruleset->refcount, 1);
15647327e19SVolodymyr Mytnyk
1578b474a9fSSerhiy Boiko err = rhashtable_init(&ruleset->rule_ht, &prestera_acl_rule_ht_params);
1588b474a9fSSerhiy Boiko if (err)
1598b474a9fSSerhiy Boiko goto err_rhashtable_init;
1608b474a9fSSerhiy Boiko
16147327e19SVolodymyr Mytnyk err = idr_alloc_u32(&acl->uid, NULL, &uid, U8_MAX, GFP_KERNEL);
1628b474a9fSSerhiy Boiko if (err)
1638b474a9fSSerhiy Boiko goto err_ruleset_create;
1648b474a9fSSerhiy Boiko
16547327e19SVolodymyr Mytnyk /* make pcl-id based on uid */
166fa5d824cSVolodymyr Mytnyk ruleset->pcl_id = PRESTERA_ACL_PCL_ID_MAKE((u8)uid, chain_index);
167fa5d824cSVolodymyr Mytnyk ruleset->index = uid;
168fa5d824cSVolodymyr Mytnyk
16944af9571SMaksym Glubokiy ruleset->prio.min = UINT_MAX;
17044af9571SMaksym Glubokiy ruleset->prio.max = 0;
17144af9571SMaksym Glubokiy
17247327e19SVolodymyr Mytnyk err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node,
17347327e19SVolodymyr Mytnyk prestera_acl_ruleset_ht_params);
17447327e19SVolodymyr Mytnyk if (err)
17547327e19SVolodymyr Mytnyk goto err_ruleset_ht_insert;
1768b474a9fSSerhiy Boiko
1778b474a9fSSerhiy Boiko return ruleset;
1788b474a9fSSerhiy Boiko
17947327e19SVolodymyr Mytnyk err_ruleset_ht_insert:
18047327e19SVolodymyr Mytnyk idr_remove(&acl->uid, uid);
1818b474a9fSSerhiy Boiko err_ruleset_create:
1828b474a9fSSerhiy Boiko rhashtable_destroy(&ruleset->rule_ht);
1838b474a9fSSerhiy Boiko err_rhashtable_init:
1848b474a9fSSerhiy Boiko kfree(ruleset);
1858b474a9fSSerhiy Boiko return ERR_PTR(err);
1868b474a9fSSerhiy Boiko }
1878b474a9fSSerhiy Boiko
prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset * ruleset,void * keymask)188*9e6fd874SJiasheng Jiang int prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset *ruleset,
189604ba230SVolodymyr Mytnyk void *keymask)
190604ba230SVolodymyr Mytnyk {
191604ba230SVolodymyr Mytnyk ruleset->keymask = kmemdup(keymask, ACL_KEYMASK_SIZE, GFP_KERNEL);
192*9e6fd874SJiasheng Jiang if (!ruleset->keymask)
193*9e6fd874SJiasheng Jiang return -ENOMEM;
194*9e6fd874SJiasheng Jiang
195*9e6fd874SJiasheng Jiang return 0;
196604ba230SVolodymyr Mytnyk }
197604ba230SVolodymyr Mytnyk
prestera_acl_ruleset_offload(struct prestera_acl_ruleset * ruleset)19847327e19SVolodymyr Mytnyk int prestera_acl_ruleset_offload(struct prestera_acl_ruleset *ruleset)
19947327e19SVolodymyr Mytnyk {
200fa5d824cSVolodymyr Mytnyk struct prestera_acl_iface iface;
20147327e19SVolodymyr Mytnyk u32 vtcam_id;
202702e7014SMaksym Glubokiy int dir;
20347327e19SVolodymyr Mytnyk int err;
20447327e19SVolodymyr Mytnyk
205702e7014SMaksym Glubokiy dir = ruleset->ingress ?
206702e7014SMaksym Glubokiy PRESTERA_HW_VTCAM_DIR_INGRESS : PRESTERA_HW_VTCAM_DIR_EGRESS;
207702e7014SMaksym Glubokiy
20847327e19SVolodymyr Mytnyk if (ruleset->offload)
20947327e19SVolodymyr Mytnyk return -EEXIST;
21047327e19SVolodymyr Mytnyk
211fa5d824cSVolodymyr Mytnyk err = prestera_acl_vtcam_id_get(ruleset->acl,
212fa5d824cSVolodymyr Mytnyk ruleset->ht_key.chain_index,
213702e7014SMaksym Glubokiy dir,
21447327e19SVolodymyr Mytnyk ruleset->keymask, &vtcam_id);
21547327e19SVolodymyr Mytnyk if (err)
216fa5d824cSVolodymyr Mytnyk goto err_vtcam_create;
217fa5d824cSVolodymyr Mytnyk
218fa5d824cSVolodymyr Mytnyk if (ruleset->ht_key.chain_index) {
219fa5d824cSVolodymyr Mytnyk /* for chain > 0, bind iface index to pcl-id to be able
220fa5d824cSVolodymyr Mytnyk * to jump from any other ruleset to this one using the index.
221fa5d824cSVolodymyr Mytnyk */
222fa5d824cSVolodymyr Mytnyk iface.index = ruleset->index;
223fa5d824cSVolodymyr Mytnyk iface.type = PRESTERA_ACL_IFACE_TYPE_INDEX;
224fa5d824cSVolodymyr Mytnyk err = prestera_hw_vtcam_iface_bind(ruleset->acl->sw, &iface,
225fa5d824cSVolodymyr Mytnyk vtcam_id, ruleset->pcl_id);
226fa5d824cSVolodymyr Mytnyk if (err)
227fa5d824cSVolodymyr Mytnyk goto err_ruleset_bind;
228fa5d824cSVolodymyr Mytnyk }
22947327e19SVolodymyr Mytnyk
23047327e19SVolodymyr Mytnyk ruleset->vtcam_id = vtcam_id;
23147327e19SVolodymyr Mytnyk ruleset->offload = true;
23247327e19SVolodymyr Mytnyk return 0;
233fa5d824cSVolodymyr Mytnyk
234fa5d824cSVolodymyr Mytnyk err_ruleset_bind:
235fa5d824cSVolodymyr Mytnyk prestera_acl_vtcam_id_put(ruleset->acl, ruleset->vtcam_id);
236fa5d824cSVolodymyr Mytnyk err_vtcam_create:
237fa5d824cSVolodymyr Mytnyk return err;
23847327e19SVolodymyr Mytnyk }
23947327e19SVolodymyr Mytnyk
prestera_acl_ruleset_destroy(struct prestera_acl_ruleset * ruleset)2408b474a9fSSerhiy Boiko static void prestera_acl_ruleset_destroy(struct prestera_acl_ruleset *ruleset)
2418b474a9fSSerhiy Boiko {
24247327e19SVolodymyr Mytnyk struct prestera_acl *acl = ruleset->acl;
24347327e19SVolodymyr Mytnyk u8 uid = ruleset->pcl_id & PRESTERA_ACL_KEYMASK_PCL_ID_USER;
244fa5d824cSVolodymyr Mytnyk int err;
24547327e19SVolodymyr Mytnyk
24647327e19SVolodymyr Mytnyk rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node,
24747327e19SVolodymyr Mytnyk prestera_acl_ruleset_ht_params);
24847327e19SVolodymyr Mytnyk
249fa5d824cSVolodymyr Mytnyk if (ruleset->offload) {
250fa5d824cSVolodymyr Mytnyk if (ruleset->ht_key.chain_index) {
251fa5d824cSVolodymyr Mytnyk struct prestera_acl_iface iface = {
252fa5d824cSVolodymyr Mytnyk .type = PRESTERA_ACL_IFACE_TYPE_INDEX,
253fa5d824cSVolodymyr Mytnyk .index = ruleset->index
254fa5d824cSVolodymyr Mytnyk };
255fa5d824cSVolodymyr Mytnyk err = prestera_hw_vtcam_iface_unbind(acl->sw, &iface,
256fa5d824cSVolodymyr Mytnyk ruleset->vtcam_id);
257fa5d824cSVolodymyr Mytnyk WARN_ON(err);
258fa5d824cSVolodymyr Mytnyk }
25947327e19SVolodymyr Mytnyk WARN_ON(prestera_acl_vtcam_id_put(acl, ruleset->vtcam_id));
260fa5d824cSVolodymyr Mytnyk }
26147327e19SVolodymyr Mytnyk
26247327e19SVolodymyr Mytnyk idr_remove(&acl->uid, uid);
2638b474a9fSSerhiy Boiko rhashtable_destroy(&ruleset->rule_ht);
26447327e19SVolodymyr Mytnyk kfree(ruleset->keymask);
2658b474a9fSSerhiy Boiko kfree(ruleset);
2668b474a9fSSerhiy Boiko }
2678b474a9fSSerhiy Boiko
26847327e19SVolodymyr Mytnyk static struct prestera_acl_ruleset *
__prestera_acl_ruleset_lookup(struct prestera_acl * acl,struct prestera_flow_block * block,u32 chain_index)26947327e19SVolodymyr Mytnyk __prestera_acl_ruleset_lookup(struct prestera_acl *acl,
270fa5d824cSVolodymyr Mytnyk struct prestera_flow_block *block,
271fa5d824cSVolodymyr Mytnyk u32 chain_index)
2728b474a9fSSerhiy Boiko {
27347327e19SVolodymyr Mytnyk struct prestera_acl_ruleset_ht_key ht_key;
2748b474a9fSSerhiy Boiko
27547327e19SVolodymyr Mytnyk memset(&ht_key, 0, sizeof(ht_key));
27647327e19SVolodymyr Mytnyk ht_key.block = block;
277fa5d824cSVolodymyr Mytnyk ht_key.chain_index = chain_index;
27847327e19SVolodymyr Mytnyk return rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key,
27947327e19SVolodymyr Mytnyk prestera_acl_ruleset_ht_params);
2808b474a9fSSerhiy Boiko }
2818b474a9fSSerhiy Boiko
28247327e19SVolodymyr Mytnyk struct prestera_acl_ruleset *
prestera_acl_ruleset_lookup(struct prestera_acl * acl,struct prestera_flow_block * block,u32 chain_index)28347327e19SVolodymyr Mytnyk prestera_acl_ruleset_lookup(struct prestera_acl *acl,
284fa5d824cSVolodymyr Mytnyk struct prestera_flow_block *block,
285fa5d824cSVolodymyr Mytnyk u32 chain_index)
2868b474a9fSSerhiy Boiko {
28747327e19SVolodymyr Mytnyk struct prestera_acl_ruleset *ruleset;
28847327e19SVolodymyr Mytnyk
289fa5d824cSVolodymyr Mytnyk ruleset = __prestera_acl_ruleset_lookup(acl, block, chain_index);
29047327e19SVolodymyr Mytnyk if (!ruleset)
29147327e19SVolodymyr Mytnyk return ERR_PTR(-ENOENT);
29247327e19SVolodymyr Mytnyk
29347327e19SVolodymyr Mytnyk refcount_inc(&ruleset->refcount);
29447327e19SVolodymyr Mytnyk return ruleset;
2958b474a9fSSerhiy Boiko }
2968b474a9fSSerhiy Boiko
29747327e19SVolodymyr Mytnyk struct prestera_acl_ruleset *
prestera_acl_ruleset_get(struct prestera_acl * acl,struct prestera_flow_block * block,u32 chain_index)29847327e19SVolodymyr Mytnyk prestera_acl_ruleset_get(struct prestera_acl *acl,
299fa5d824cSVolodymyr Mytnyk struct prestera_flow_block *block,
300fa5d824cSVolodymyr Mytnyk u32 chain_index)
30147327e19SVolodymyr Mytnyk {
30247327e19SVolodymyr Mytnyk struct prestera_acl_ruleset *ruleset;
30347327e19SVolodymyr Mytnyk
304fa5d824cSVolodymyr Mytnyk ruleset = __prestera_acl_ruleset_lookup(acl, block, chain_index);
30547327e19SVolodymyr Mytnyk if (ruleset) {
30647327e19SVolodymyr Mytnyk refcount_inc(&ruleset->refcount);
30747327e19SVolodymyr Mytnyk return ruleset;
30847327e19SVolodymyr Mytnyk }
30947327e19SVolodymyr Mytnyk
310fa5d824cSVolodymyr Mytnyk return prestera_acl_ruleset_create(acl, block, chain_index);
31147327e19SVolodymyr Mytnyk }
31247327e19SVolodymyr Mytnyk
prestera_acl_ruleset_put(struct prestera_acl_ruleset * ruleset)31347327e19SVolodymyr Mytnyk void prestera_acl_ruleset_put(struct prestera_acl_ruleset *ruleset)
31447327e19SVolodymyr Mytnyk {
31547327e19SVolodymyr Mytnyk if (!refcount_dec_and_test(&ruleset->refcount))
31647327e19SVolodymyr Mytnyk return;
31747327e19SVolodymyr Mytnyk
31847327e19SVolodymyr Mytnyk prestera_acl_ruleset_destroy(ruleset);
31947327e19SVolodymyr Mytnyk }
32047327e19SVolodymyr Mytnyk
prestera_acl_ruleset_bind(struct prestera_acl_ruleset * ruleset,struct prestera_port * port)32147327e19SVolodymyr Mytnyk int prestera_acl_ruleset_bind(struct prestera_acl_ruleset *ruleset,
3228b474a9fSSerhiy Boiko struct prestera_port *port)
3238b474a9fSSerhiy Boiko {
32447327e19SVolodymyr Mytnyk struct prestera_acl_iface iface = {
32547327e19SVolodymyr Mytnyk .type = PRESTERA_ACL_IFACE_TYPE_PORT,
32647327e19SVolodymyr Mytnyk .port = port
32747327e19SVolodymyr Mytnyk };
3288b474a9fSSerhiy Boiko
32947327e19SVolodymyr Mytnyk return prestera_hw_vtcam_iface_bind(port->sw, &iface, ruleset->vtcam_id,
33047327e19SVolodymyr Mytnyk ruleset->pcl_id);
3318b474a9fSSerhiy Boiko }
3328b474a9fSSerhiy Boiko
prestera_acl_ruleset_unbind(struct prestera_acl_ruleset * ruleset,struct prestera_port * port)33347327e19SVolodymyr Mytnyk int prestera_acl_ruleset_unbind(struct prestera_acl_ruleset *ruleset,
3348b474a9fSSerhiy Boiko struct prestera_port *port)
3358b474a9fSSerhiy Boiko {
33647327e19SVolodymyr Mytnyk struct prestera_acl_iface iface = {
33747327e19SVolodymyr Mytnyk .type = PRESTERA_ACL_IFACE_TYPE_PORT,
33847327e19SVolodymyr Mytnyk .port = port
33947327e19SVolodymyr Mytnyk };
34047327e19SVolodymyr Mytnyk
34147327e19SVolodymyr Mytnyk return prestera_hw_vtcam_iface_unbind(port->sw, &iface,
34247327e19SVolodymyr Mytnyk ruleset->vtcam_id);
34347327e19SVolodymyr Mytnyk }
34447327e19SVolodymyr Mytnyk
prestera_acl_ruleset_block_bind(struct prestera_acl_ruleset * ruleset,struct prestera_flow_block * block)34547327e19SVolodymyr Mytnyk static int prestera_acl_ruleset_block_bind(struct prestera_acl_ruleset *ruleset,
34647327e19SVolodymyr Mytnyk struct prestera_flow_block *block)
34747327e19SVolodymyr Mytnyk {
3488b474a9fSSerhiy Boiko struct prestera_flow_block_binding *binding;
3498b474a9fSSerhiy Boiko int err;
3508b474a9fSSerhiy Boiko
35147327e19SVolodymyr Mytnyk block->ruleset_zero = ruleset;
35247327e19SVolodymyr Mytnyk list_for_each_entry(binding, &block->binding_list, list) {
35347327e19SVolodymyr Mytnyk err = prestera_acl_ruleset_bind(ruleset, binding->port);
3548b474a9fSSerhiy Boiko if (err)
35547327e19SVolodymyr Mytnyk goto rollback;
35647327e19SVolodymyr Mytnyk }
3578b474a9fSSerhiy Boiko return 0;
3588b474a9fSSerhiy Boiko
35947327e19SVolodymyr Mytnyk rollback:
36047327e19SVolodymyr Mytnyk list_for_each_entry_continue_reverse(binding, &block->binding_list,
36147327e19SVolodymyr Mytnyk list)
36247327e19SVolodymyr Mytnyk err = prestera_acl_ruleset_unbind(ruleset, binding->port);
36347327e19SVolodymyr Mytnyk block->ruleset_zero = NULL;
36447327e19SVolodymyr Mytnyk
3658b474a9fSSerhiy Boiko return err;
3668b474a9fSSerhiy Boiko }
3678b474a9fSSerhiy Boiko
36847327e19SVolodymyr Mytnyk static void
prestera_acl_ruleset_block_unbind(struct prestera_acl_ruleset * ruleset,struct prestera_flow_block * block)36947327e19SVolodymyr Mytnyk prestera_acl_ruleset_block_unbind(struct prestera_acl_ruleset *ruleset,
37047327e19SVolodymyr Mytnyk struct prestera_flow_block *block)
3718b474a9fSSerhiy Boiko {
3728b474a9fSSerhiy Boiko struct prestera_flow_block_binding *binding;
3738b474a9fSSerhiy Boiko
37447327e19SVolodymyr Mytnyk list_for_each_entry(binding, &block->binding_list, list)
37547327e19SVolodymyr Mytnyk prestera_acl_ruleset_unbind(ruleset, binding->port);
37647327e19SVolodymyr Mytnyk block->ruleset_zero = NULL;
3778b474a9fSSerhiy Boiko }
3788b474a9fSSerhiy Boiko
37944af9571SMaksym Glubokiy static void
prestera_acl_ruleset_prio_refresh(struct prestera_acl * acl,struct prestera_acl_ruleset * ruleset)38044af9571SMaksym Glubokiy prestera_acl_ruleset_prio_refresh(struct prestera_acl *acl,
38144af9571SMaksym Glubokiy struct prestera_acl_ruleset *ruleset)
38244af9571SMaksym Glubokiy {
38344af9571SMaksym Glubokiy struct prestera_acl_rule *rule;
38444af9571SMaksym Glubokiy
38544af9571SMaksym Glubokiy ruleset->prio.min = UINT_MAX;
38644af9571SMaksym Glubokiy ruleset->prio.max = 0;
38744af9571SMaksym Glubokiy
38844af9571SMaksym Glubokiy list_for_each_entry(rule, &acl->rules, list) {
38944af9571SMaksym Glubokiy if (ruleset->ingress != rule->ruleset->ingress)
39044af9571SMaksym Glubokiy continue;
39144af9571SMaksym Glubokiy if (ruleset->ht_key.chain_index != rule->chain_index)
39244af9571SMaksym Glubokiy continue;
39344af9571SMaksym Glubokiy
39444af9571SMaksym Glubokiy ruleset->prio.min = min(ruleset->prio.min, rule->priority);
39544af9571SMaksym Glubokiy ruleset->prio.max = max(ruleset->prio.max, rule->priority);
39644af9571SMaksym Glubokiy }
39744af9571SMaksym Glubokiy }
39844af9571SMaksym Glubokiy
39947327e19SVolodymyr Mytnyk void
prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule * rule,u16 pcl_id)40047327e19SVolodymyr Mytnyk prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule, u16 pcl_id)
4018b474a9fSSerhiy Boiko {
40247327e19SVolodymyr Mytnyk struct prestera_acl_match *r_match = &rule->re_key.match;
40347327e19SVolodymyr Mytnyk __be16 pcl_id_mask = htons(PRESTERA_ACL_KEYMASK_PCL_ID);
40447327e19SVolodymyr Mytnyk __be16 pcl_id_key = htons(pcl_id);
4058b474a9fSSerhiy Boiko
40647327e19SVolodymyr Mytnyk rule_match_set(r_match->key, PCL_ID, pcl_id_key);
40747327e19SVolodymyr Mytnyk rule_match_set(r_match->mask, PCL_ID, pcl_id_mask);
4088b474a9fSSerhiy Boiko }
4098b474a9fSSerhiy Boiko
4108b474a9fSSerhiy Boiko struct prestera_acl_rule *
prestera_acl_rule_lookup(struct prestera_acl_ruleset * ruleset,unsigned long cookie)4118b474a9fSSerhiy Boiko prestera_acl_rule_lookup(struct prestera_acl_ruleset *ruleset,
4128b474a9fSSerhiy Boiko unsigned long cookie)
4138b474a9fSSerhiy Boiko {
4148b474a9fSSerhiy Boiko return rhashtable_lookup_fast(&ruleset->rule_ht, &cookie,
4158b474a9fSSerhiy Boiko prestera_acl_rule_ht_params);
4168b474a9fSSerhiy Boiko }
4178b474a9fSSerhiy Boiko
prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset * ruleset)418fa5d824cSVolodymyr Mytnyk u32 prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset *ruleset)
419fa5d824cSVolodymyr Mytnyk {
420fa5d824cSVolodymyr Mytnyk return ruleset->index;
421fa5d824cSVolodymyr Mytnyk }
422fa5d824cSVolodymyr Mytnyk
prestera_acl_ruleset_prio_get(struct prestera_acl_ruleset * ruleset,u32 * prio_min,u32 * prio_max)42344af9571SMaksym Glubokiy void prestera_acl_ruleset_prio_get(struct prestera_acl_ruleset *ruleset,
42444af9571SMaksym Glubokiy u32 *prio_min, u32 *prio_max)
42544af9571SMaksym Glubokiy {
42644af9571SMaksym Glubokiy *prio_min = ruleset->prio.min;
42744af9571SMaksym Glubokiy *prio_max = ruleset->prio.max;
42844af9571SMaksym Glubokiy }
42944af9571SMaksym Glubokiy
prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset * ruleset)43047327e19SVolodymyr Mytnyk bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset)
43147327e19SVolodymyr Mytnyk {
43247327e19SVolodymyr Mytnyk return ruleset->offload;
43347327e19SVolodymyr Mytnyk }
43447327e19SVolodymyr Mytnyk
4358b474a9fSSerhiy Boiko struct prestera_acl_rule *
prestera_acl_rule_create(struct prestera_acl_ruleset * ruleset,unsigned long cookie,u32 chain_index)43647327e19SVolodymyr Mytnyk prestera_acl_rule_create(struct prestera_acl_ruleset *ruleset,
437fa5d824cSVolodymyr Mytnyk unsigned long cookie, u32 chain_index)
4388b474a9fSSerhiy Boiko {
4398b474a9fSSerhiy Boiko struct prestera_acl_rule *rule;
4408b474a9fSSerhiy Boiko
4418b474a9fSSerhiy Boiko rule = kzalloc(sizeof(*rule), GFP_KERNEL);
4428b474a9fSSerhiy Boiko if (!rule)
4438b474a9fSSerhiy Boiko return ERR_PTR(-ENOMEM);
4448b474a9fSSerhiy Boiko
44547327e19SVolodymyr Mytnyk rule->ruleset = ruleset;
4468b474a9fSSerhiy Boiko rule->cookie = cookie;
447fa5d824cSVolodymyr Mytnyk rule->chain_index = chain_index;
44847327e19SVolodymyr Mytnyk
44947327e19SVolodymyr Mytnyk refcount_inc(&ruleset->refcount);
4508b474a9fSSerhiy Boiko
4518b474a9fSSerhiy Boiko return rule;
4528b474a9fSSerhiy Boiko }
4538b474a9fSSerhiy Boiko
prestera_acl_rule_priority_set(struct prestera_acl_rule * rule,u32 priority)4548b474a9fSSerhiy Boiko void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule,
4558b474a9fSSerhiy Boiko u32 priority)
4568b474a9fSSerhiy Boiko {
4578b474a9fSSerhiy Boiko rule->priority = priority;
4588b474a9fSSerhiy Boiko }
4598b474a9fSSerhiy Boiko
prestera_acl_rule_destroy(struct prestera_acl_rule * rule)4608b474a9fSSerhiy Boiko void prestera_acl_rule_destroy(struct prestera_acl_rule *rule)
4618b474a9fSSerhiy Boiko {
462fa5d824cSVolodymyr Mytnyk if (rule->jump_ruleset)
463fa5d824cSVolodymyr Mytnyk /* release ruleset kept by jump action */
464fa5d824cSVolodymyr Mytnyk prestera_acl_ruleset_put(rule->jump_ruleset);
465fa5d824cSVolodymyr Mytnyk
46647327e19SVolodymyr Mytnyk prestera_acl_ruleset_put(rule->ruleset);
4678b474a9fSSerhiy Boiko kfree(rule);
4688b474a9fSSerhiy Boiko }
4698b474a9fSSerhiy Boiko
prestera_acl_ruleset_prio_update(struct prestera_acl_ruleset * ruleset,u32 prio)47044af9571SMaksym Glubokiy static void prestera_acl_ruleset_prio_update(struct prestera_acl_ruleset *ruleset,
47144af9571SMaksym Glubokiy u32 prio)
47244af9571SMaksym Glubokiy {
47344af9571SMaksym Glubokiy ruleset->prio.min = min(ruleset->prio.min, prio);
47444af9571SMaksym Glubokiy ruleset->prio.max = max(ruleset->prio.max, prio);
47544af9571SMaksym Glubokiy }
47644af9571SMaksym Glubokiy
prestera_acl_rule_add(struct prestera_switch * sw,struct prestera_acl_rule * rule)4778b474a9fSSerhiy Boiko int prestera_acl_rule_add(struct prestera_switch *sw,
4788b474a9fSSerhiy Boiko struct prestera_acl_rule *rule)
4798b474a9fSSerhiy Boiko {
4808b474a9fSSerhiy Boiko int err;
48147327e19SVolodymyr Mytnyk struct prestera_acl_ruleset *ruleset = rule->ruleset;
48247327e19SVolodymyr Mytnyk struct prestera_flow_block *block = ruleset->ht_key.block;
4838b474a9fSSerhiy Boiko
4848b474a9fSSerhiy Boiko /* try to add rule to hash table first */
48547327e19SVolodymyr Mytnyk err = rhashtable_insert_fast(&ruleset->rule_ht, &rule->ht_node,
4868b474a9fSSerhiy Boiko prestera_acl_rule_ht_params);
4878b474a9fSSerhiy Boiko if (err)
48847327e19SVolodymyr Mytnyk goto err_ht_insert;
4898b474a9fSSerhiy Boiko
49047327e19SVolodymyr Mytnyk prestera_acl_rule_keymask_pcl_id_set(rule, ruleset->pcl_id);
49147327e19SVolodymyr Mytnyk rule->re_arg.vtcam_id = ruleset->vtcam_id;
49247327e19SVolodymyr Mytnyk rule->re_key.prio = rule->priority;
49347327e19SVolodymyr Mytnyk
49447327e19SVolodymyr Mytnyk rule->re = prestera_acl_rule_entry_find(sw->acl, &rule->re_key);
49547327e19SVolodymyr Mytnyk err = WARN_ON(rule->re) ? -EEXIST : 0;
4968b474a9fSSerhiy Boiko if (err)
4978b474a9fSSerhiy Boiko goto err_rule_add;
4988b474a9fSSerhiy Boiko
49947327e19SVolodymyr Mytnyk rule->re = prestera_acl_rule_entry_create(sw->acl, &rule->re_key,
50047327e19SVolodymyr Mytnyk &rule->re_arg);
50147327e19SVolodymyr Mytnyk err = !rule->re ? -EINVAL : 0;
50247327e19SVolodymyr Mytnyk if (err)
50347327e19SVolodymyr Mytnyk goto err_rule_add;
50447327e19SVolodymyr Mytnyk
505fa5d824cSVolodymyr Mytnyk /* bind the block (all ports) to chain index 0, rest of
506fa5d824cSVolodymyr Mytnyk * the chains are bound to goto action
507fa5d824cSVolodymyr Mytnyk */
508fa5d824cSVolodymyr Mytnyk if (!ruleset->ht_key.chain_index && !ruleset->rule_count) {
50947327e19SVolodymyr Mytnyk err = prestera_acl_ruleset_block_bind(ruleset, block);
51047327e19SVolodymyr Mytnyk if (err)
51147327e19SVolodymyr Mytnyk goto err_acl_block_bind;
51247327e19SVolodymyr Mytnyk }
5138b474a9fSSerhiy Boiko
5148b474a9fSSerhiy Boiko list_add_tail(&rule->list, &sw->acl->rules);
51547327e19SVolodymyr Mytnyk ruleset->rule_count++;
51644af9571SMaksym Glubokiy prestera_acl_ruleset_prio_update(ruleset, rule->priority);
5178b474a9fSSerhiy Boiko return 0;
5188b474a9fSSerhiy Boiko
51947327e19SVolodymyr Mytnyk err_acl_block_bind:
52047327e19SVolodymyr Mytnyk prestera_acl_rule_entry_destroy(sw->acl, rule->re);
5218b474a9fSSerhiy Boiko err_rule_add:
52247327e19SVolodymyr Mytnyk rule->re = NULL;
52347327e19SVolodymyr Mytnyk rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
5248b474a9fSSerhiy Boiko prestera_acl_rule_ht_params);
52547327e19SVolodymyr Mytnyk err_ht_insert:
5268b474a9fSSerhiy Boiko return err;
5278b474a9fSSerhiy Boiko }
5288b474a9fSSerhiy Boiko
prestera_acl_rule_del(struct prestera_switch * sw,struct prestera_acl_rule * rule)5298b474a9fSSerhiy Boiko void prestera_acl_rule_del(struct prestera_switch *sw,
5308b474a9fSSerhiy Boiko struct prestera_acl_rule *rule)
5318b474a9fSSerhiy Boiko {
53247327e19SVolodymyr Mytnyk struct prestera_acl_ruleset *ruleset = rule->ruleset;
53347327e19SVolodymyr Mytnyk struct prestera_flow_block *block = ruleset->ht_key.block;
53447327e19SVolodymyr Mytnyk
53547327e19SVolodymyr Mytnyk rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
5368b474a9fSSerhiy Boiko prestera_acl_rule_ht_params);
53747327e19SVolodymyr Mytnyk ruleset->rule_count--;
5388b474a9fSSerhiy Boiko list_del(&rule->list);
53947327e19SVolodymyr Mytnyk
54047327e19SVolodymyr Mytnyk prestera_acl_rule_entry_destroy(sw->acl, rule->re);
54144af9571SMaksym Glubokiy prestera_acl_ruleset_prio_refresh(sw->acl, ruleset);
54247327e19SVolodymyr Mytnyk
54347327e19SVolodymyr Mytnyk /* unbind block (all ports) */
544fa5d824cSVolodymyr Mytnyk if (!ruleset->ht_key.chain_index && !ruleset->rule_count)
54547327e19SVolodymyr Mytnyk prestera_acl_ruleset_block_unbind(ruleset, block);
5468b474a9fSSerhiy Boiko }
5478b474a9fSSerhiy Boiko
prestera_acl_rule_get_stats(struct prestera_acl * acl,struct prestera_acl_rule * rule,u64 * packets,u64 * bytes,u64 * last_use)54847327e19SVolodymyr Mytnyk int prestera_acl_rule_get_stats(struct prestera_acl *acl,
5498b474a9fSSerhiy Boiko struct prestera_acl_rule *rule,
5508b474a9fSSerhiy Boiko u64 *packets, u64 *bytes, u64 *last_use)
5518b474a9fSSerhiy Boiko {
552adefefe5SVolodymyr Mytnyk u64 current_packets;
553adefefe5SVolodymyr Mytnyk u64 current_bytes;
554adefefe5SVolodymyr Mytnyk int err;
555adefefe5SVolodymyr Mytnyk
556adefefe5SVolodymyr Mytnyk err = prestera_counter_stats_get(acl->sw->counter,
557adefefe5SVolodymyr Mytnyk rule->re->counter.block,
558adefefe5SVolodymyr Mytnyk rule->re->counter.id,
559adefefe5SVolodymyr Mytnyk ¤t_packets, ¤t_bytes);
560adefefe5SVolodymyr Mytnyk if (err)
561adefefe5SVolodymyr Mytnyk return err;
562adefefe5SVolodymyr Mytnyk
563adefefe5SVolodymyr Mytnyk *packets = current_packets;
564adefefe5SVolodymyr Mytnyk *bytes = current_bytes;
5658b474a9fSSerhiy Boiko *last_use = jiffies;
5668b474a9fSSerhiy Boiko
5678b474a9fSSerhiy Boiko return 0;
5688b474a9fSSerhiy Boiko }
5698b474a9fSSerhiy Boiko
57047327e19SVolodymyr Mytnyk struct prestera_acl_rule_entry *
prestera_acl_rule_entry_find(struct prestera_acl * acl,struct prestera_acl_rule_entry_key * key)57147327e19SVolodymyr Mytnyk prestera_acl_rule_entry_find(struct prestera_acl *acl,
57247327e19SVolodymyr Mytnyk struct prestera_acl_rule_entry_key *key)
57347327e19SVolodymyr Mytnyk {
574f6882b8fSYang Yingliang return rhashtable_lookup_fast(&acl->acl_rule_entry_ht, key,
57547327e19SVolodymyr Mytnyk __prestera_acl_rule_entry_ht_params);
57647327e19SVolodymyr Mytnyk }
57747327e19SVolodymyr Mytnyk
__prestera_acl_rule_entry2hw_del(struct prestera_switch * sw,struct prestera_acl_rule_entry * e)57847327e19SVolodymyr Mytnyk static int __prestera_acl_rule_entry2hw_del(struct prestera_switch *sw,
57947327e19SVolodymyr Mytnyk struct prestera_acl_rule_entry *e)
58047327e19SVolodymyr Mytnyk {
58147327e19SVolodymyr Mytnyk return prestera_hw_vtcam_rule_del(sw, e->vtcam_id, e->hw_id);
58247327e19SVolodymyr Mytnyk }
58347327e19SVolodymyr Mytnyk
__prestera_acl_rule_entry2hw_add(struct prestera_switch * sw,struct prestera_acl_rule_entry * e)58447327e19SVolodymyr Mytnyk static int __prestera_acl_rule_entry2hw_add(struct prestera_switch *sw,
58547327e19SVolodymyr Mytnyk struct prestera_acl_rule_entry *e)
58647327e19SVolodymyr Mytnyk {
58747327e19SVolodymyr Mytnyk struct prestera_acl_hw_action_info act_hw[PRESTERA_ACL_RULE_ACTION_MAX];
58847327e19SVolodymyr Mytnyk int act_num;
58947327e19SVolodymyr Mytnyk
59047327e19SVolodymyr Mytnyk memset(&act_hw, 0, sizeof(act_hw));
59147327e19SVolodymyr Mytnyk act_num = 0;
59247327e19SVolodymyr Mytnyk
59347327e19SVolodymyr Mytnyk /* accept */
59447327e19SVolodymyr Mytnyk if (e->accept.valid) {
59547327e19SVolodymyr Mytnyk act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_ACCEPT;
59647327e19SVolodymyr Mytnyk act_num++;
59747327e19SVolodymyr Mytnyk }
59847327e19SVolodymyr Mytnyk /* drop */
59947327e19SVolodymyr Mytnyk if (e->drop.valid) {
60047327e19SVolodymyr Mytnyk act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_DROP;
60147327e19SVolodymyr Mytnyk act_num++;
60247327e19SVolodymyr Mytnyk }
60347327e19SVolodymyr Mytnyk /* trap */
60447327e19SVolodymyr Mytnyk if (e->trap.valid) {
60547327e19SVolodymyr Mytnyk act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_TRAP;
60647327e19SVolodymyr Mytnyk act_num++;
60747327e19SVolodymyr Mytnyk }
608dde2daa0SVolodymyr Mytnyk /* police */
609dde2daa0SVolodymyr Mytnyk if (e->police.valid) {
610dde2daa0SVolodymyr Mytnyk act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_POLICE;
611dde2daa0SVolodymyr Mytnyk act_hw[act_num].police = e->police.i;
612dde2daa0SVolodymyr Mytnyk act_num++;
613dde2daa0SVolodymyr Mytnyk }
614fa5d824cSVolodymyr Mytnyk /* jump */
615fa5d824cSVolodymyr Mytnyk if (e->jump.valid) {
616fa5d824cSVolodymyr Mytnyk act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_JUMP;
617fa5d824cSVolodymyr Mytnyk act_hw[act_num].jump = e->jump.i;
618fa5d824cSVolodymyr Mytnyk act_num++;
619fa5d824cSVolodymyr Mytnyk }
620adefefe5SVolodymyr Mytnyk /* counter */
621adefefe5SVolodymyr Mytnyk if (e->counter.block) {
622adefefe5SVolodymyr Mytnyk act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_COUNT;
623adefefe5SVolodymyr Mytnyk act_hw[act_num].count.id = e->counter.id;
624adefefe5SVolodymyr Mytnyk act_num++;
625adefefe5SVolodymyr Mytnyk }
62647327e19SVolodymyr Mytnyk
62747327e19SVolodymyr Mytnyk return prestera_hw_vtcam_rule_add(sw, e->vtcam_id, e->key.prio,
62847327e19SVolodymyr Mytnyk e->key.match.key, e->key.match.mask,
62947327e19SVolodymyr Mytnyk act_hw, act_num, &e->hw_id);
63047327e19SVolodymyr Mytnyk }
63147327e19SVolodymyr Mytnyk
63247327e19SVolodymyr Mytnyk static void
__prestera_acl_rule_entry_act_destruct(struct prestera_switch * sw,struct prestera_acl_rule_entry * e)63347327e19SVolodymyr Mytnyk __prestera_acl_rule_entry_act_destruct(struct prestera_switch *sw,
63447327e19SVolodymyr Mytnyk struct prestera_acl_rule_entry *e)
63547327e19SVolodymyr Mytnyk {
636adefefe5SVolodymyr Mytnyk /* counter */
637adefefe5SVolodymyr Mytnyk prestera_counter_put(sw->counter, e->counter.block, e->counter.id);
638dde2daa0SVolodymyr Mytnyk /* police */
639dde2daa0SVolodymyr Mytnyk if (e->police.valid)
640dde2daa0SVolodymyr Mytnyk prestera_hw_policer_release(sw, e->police.i.id);
64147327e19SVolodymyr Mytnyk }
64247327e19SVolodymyr Mytnyk
prestera_acl_rule_entry_destroy(struct prestera_acl * acl,struct prestera_acl_rule_entry * e)64347327e19SVolodymyr Mytnyk void prestera_acl_rule_entry_destroy(struct prestera_acl *acl,
64447327e19SVolodymyr Mytnyk struct prestera_acl_rule_entry *e)
64547327e19SVolodymyr Mytnyk {
64647327e19SVolodymyr Mytnyk int ret;
64747327e19SVolodymyr Mytnyk
64847327e19SVolodymyr Mytnyk rhashtable_remove_fast(&acl->acl_rule_entry_ht, &e->ht_node,
64947327e19SVolodymyr Mytnyk __prestera_acl_rule_entry_ht_params);
65047327e19SVolodymyr Mytnyk
65147327e19SVolodymyr Mytnyk ret = __prestera_acl_rule_entry2hw_del(acl->sw, e);
65247327e19SVolodymyr Mytnyk WARN_ON(ret && ret != -ENODEV);
65347327e19SVolodymyr Mytnyk
65447327e19SVolodymyr Mytnyk __prestera_acl_rule_entry_act_destruct(acl->sw, e);
65547327e19SVolodymyr Mytnyk kfree(e);
65647327e19SVolodymyr Mytnyk }
65747327e19SVolodymyr Mytnyk
65847327e19SVolodymyr Mytnyk static int
__prestera_acl_rule_entry_act_construct(struct prestera_switch * sw,struct prestera_acl_rule_entry * e,struct prestera_acl_rule_entry_arg * arg)65947327e19SVolodymyr Mytnyk __prestera_acl_rule_entry_act_construct(struct prestera_switch *sw,
66047327e19SVolodymyr Mytnyk struct prestera_acl_rule_entry *e,
66147327e19SVolodymyr Mytnyk struct prestera_acl_rule_entry_arg *arg)
66247327e19SVolodymyr Mytnyk {
663dde2daa0SVolodymyr Mytnyk int err;
664dde2daa0SVolodymyr Mytnyk
66547327e19SVolodymyr Mytnyk /* accept */
66647327e19SVolodymyr Mytnyk e->accept.valid = arg->accept.valid;
66747327e19SVolodymyr Mytnyk /* drop */
66847327e19SVolodymyr Mytnyk e->drop.valid = arg->drop.valid;
66947327e19SVolodymyr Mytnyk /* trap */
67047327e19SVolodymyr Mytnyk e->trap.valid = arg->trap.valid;
671fa5d824cSVolodymyr Mytnyk /* jump */
672fa5d824cSVolodymyr Mytnyk e->jump.valid = arg->jump.valid;
673fa5d824cSVolodymyr Mytnyk e->jump.i = arg->jump.i;
674dde2daa0SVolodymyr Mytnyk /* police */
675dde2daa0SVolodymyr Mytnyk if (arg->police.valid) {
676dde2daa0SVolodymyr Mytnyk u8 type = arg->police.ingress ? PRESTERA_POLICER_TYPE_INGRESS :
677dde2daa0SVolodymyr Mytnyk PRESTERA_POLICER_TYPE_EGRESS;
678dde2daa0SVolodymyr Mytnyk
679dde2daa0SVolodymyr Mytnyk err = prestera_hw_policer_create(sw, type, &e->police.i.id);
680dde2daa0SVolodymyr Mytnyk if (err)
681dde2daa0SVolodymyr Mytnyk goto err_out;
682dde2daa0SVolodymyr Mytnyk
683dde2daa0SVolodymyr Mytnyk err = prestera_hw_policer_sr_tcm_set(sw, e->police.i.id,
684dde2daa0SVolodymyr Mytnyk arg->police.rate,
685dde2daa0SVolodymyr Mytnyk arg->police.burst);
686dde2daa0SVolodymyr Mytnyk if (err) {
687dde2daa0SVolodymyr Mytnyk prestera_hw_policer_release(sw, e->police.i.id);
688dde2daa0SVolodymyr Mytnyk goto err_out;
689dde2daa0SVolodymyr Mytnyk }
690dde2daa0SVolodymyr Mytnyk e->police.valid = arg->police.valid;
691dde2daa0SVolodymyr Mytnyk }
692adefefe5SVolodymyr Mytnyk /* counter */
693adefefe5SVolodymyr Mytnyk if (arg->count.valid) {
694adefefe5SVolodymyr Mytnyk err = prestera_counter_get(sw->counter, arg->count.client,
695adefefe5SVolodymyr Mytnyk &e->counter.block,
696adefefe5SVolodymyr Mytnyk &e->counter.id);
697adefefe5SVolodymyr Mytnyk if (err)
698adefefe5SVolodymyr Mytnyk goto err_out;
699adefefe5SVolodymyr Mytnyk }
70047327e19SVolodymyr Mytnyk
70147327e19SVolodymyr Mytnyk return 0;
702adefefe5SVolodymyr Mytnyk
703adefefe5SVolodymyr Mytnyk err_out:
704adefefe5SVolodymyr Mytnyk __prestera_acl_rule_entry_act_destruct(sw, e);
705adefefe5SVolodymyr Mytnyk return -EINVAL;
70647327e19SVolodymyr Mytnyk }
70747327e19SVolodymyr Mytnyk
70847327e19SVolodymyr Mytnyk struct prestera_acl_rule_entry *
prestera_acl_rule_entry_create(struct prestera_acl * acl,struct prestera_acl_rule_entry_key * key,struct prestera_acl_rule_entry_arg * arg)70947327e19SVolodymyr Mytnyk prestera_acl_rule_entry_create(struct prestera_acl *acl,
71047327e19SVolodymyr Mytnyk struct prestera_acl_rule_entry_key *key,
71147327e19SVolodymyr Mytnyk struct prestera_acl_rule_entry_arg *arg)
71247327e19SVolodymyr Mytnyk {
71347327e19SVolodymyr Mytnyk struct prestera_acl_rule_entry *e;
71447327e19SVolodymyr Mytnyk int err;
71547327e19SVolodymyr Mytnyk
71647327e19SVolodymyr Mytnyk e = kzalloc(sizeof(*e), GFP_KERNEL);
71747327e19SVolodymyr Mytnyk if (!e)
71847327e19SVolodymyr Mytnyk goto err_kzalloc;
71947327e19SVolodymyr Mytnyk
72047327e19SVolodymyr Mytnyk memcpy(&e->key, key, sizeof(*key));
72147327e19SVolodymyr Mytnyk e->vtcam_id = arg->vtcam_id;
72247327e19SVolodymyr Mytnyk err = __prestera_acl_rule_entry_act_construct(acl->sw, e, arg);
72347327e19SVolodymyr Mytnyk if (err)
72447327e19SVolodymyr Mytnyk goto err_act_construct;
72547327e19SVolodymyr Mytnyk
72647327e19SVolodymyr Mytnyk err = __prestera_acl_rule_entry2hw_add(acl->sw, e);
72747327e19SVolodymyr Mytnyk if (err)
72847327e19SVolodymyr Mytnyk goto err_hw_add;
72947327e19SVolodymyr Mytnyk
73047327e19SVolodymyr Mytnyk err = rhashtable_insert_fast(&acl->acl_rule_entry_ht, &e->ht_node,
73147327e19SVolodymyr Mytnyk __prestera_acl_rule_entry_ht_params);
73247327e19SVolodymyr Mytnyk if (err)
73347327e19SVolodymyr Mytnyk goto err_ht_insert;
73447327e19SVolodymyr Mytnyk
73547327e19SVolodymyr Mytnyk return e;
73647327e19SVolodymyr Mytnyk
73747327e19SVolodymyr Mytnyk err_ht_insert:
73847327e19SVolodymyr Mytnyk WARN_ON(__prestera_acl_rule_entry2hw_del(acl->sw, e));
73947327e19SVolodymyr Mytnyk err_hw_add:
74047327e19SVolodymyr Mytnyk __prestera_acl_rule_entry_act_destruct(acl->sw, e);
74147327e19SVolodymyr Mytnyk err_act_construct:
74247327e19SVolodymyr Mytnyk kfree(e);
74347327e19SVolodymyr Mytnyk err_kzalloc:
74447327e19SVolodymyr Mytnyk return NULL;
74547327e19SVolodymyr Mytnyk }
74647327e19SVolodymyr Mytnyk
__prestera_acl_vtcam_id_try_fit(struct prestera_acl * acl,u8 lookup,void * keymask,u32 * vtcam_id)747604ba230SVolodymyr Mytnyk static int __prestera_acl_vtcam_id_try_fit(struct prestera_acl *acl, u8 lookup,
748604ba230SVolodymyr Mytnyk void *keymask, u32 *vtcam_id)
749604ba230SVolodymyr Mytnyk {
750604ba230SVolodymyr Mytnyk struct prestera_acl_vtcam *vtcam;
751604ba230SVolodymyr Mytnyk int i;
752604ba230SVolodymyr Mytnyk
753604ba230SVolodymyr Mytnyk list_for_each_entry(vtcam, &acl->vtcam_list, list) {
754604ba230SVolodymyr Mytnyk if (lookup != vtcam->lookup)
755604ba230SVolodymyr Mytnyk continue;
756604ba230SVolodymyr Mytnyk
757604ba230SVolodymyr Mytnyk if (!keymask && !vtcam->is_keymask_set)
758604ba230SVolodymyr Mytnyk goto vtcam_found;
759604ba230SVolodymyr Mytnyk
760604ba230SVolodymyr Mytnyk if (!(keymask && vtcam->is_keymask_set))
761604ba230SVolodymyr Mytnyk continue;
762604ba230SVolodymyr Mytnyk
763604ba230SVolodymyr Mytnyk /* try to fit with vtcam keymask */
764604ba230SVolodymyr Mytnyk for (i = 0; i < __PRESTERA_ACL_RULE_MATCH_TYPE_MAX; i++) {
765604ba230SVolodymyr Mytnyk __be32 __keymask = ((__be32 *)keymask)[i];
766604ba230SVolodymyr Mytnyk
767604ba230SVolodymyr Mytnyk if (!__keymask)
768604ba230SVolodymyr Mytnyk /* vtcam keymask in not interested */
769604ba230SVolodymyr Mytnyk continue;
770604ba230SVolodymyr Mytnyk
771604ba230SVolodymyr Mytnyk if (__keymask & ~vtcam->keymask[i])
772604ba230SVolodymyr Mytnyk /* keymask does not fit the vtcam keymask */
773604ba230SVolodymyr Mytnyk break;
774604ba230SVolodymyr Mytnyk }
775604ba230SVolodymyr Mytnyk
776604ba230SVolodymyr Mytnyk if (i == __PRESTERA_ACL_RULE_MATCH_TYPE_MAX)
777604ba230SVolodymyr Mytnyk /* keymask fits vtcam keymask, return it */
778604ba230SVolodymyr Mytnyk goto vtcam_found;
779604ba230SVolodymyr Mytnyk }
780604ba230SVolodymyr Mytnyk
781604ba230SVolodymyr Mytnyk /* nothing is found */
782604ba230SVolodymyr Mytnyk return -ENOENT;
783604ba230SVolodymyr Mytnyk
784604ba230SVolodymyr Mytnyk vtcam_found:
785604ba230SVolodymyr Mytnyk refcount_inc(&vtcam->refcount);
786604ba230SVolodymyr Mytnyk *vtcam_id = vtcam->id;
787604ba230SVolodymyr Mytnyk return 0;
788604ba230SVolodymyr Mytnyk }
789604ba230SVolodymyr Mytnyk
prestera_acl_vtcam_id_get(struct prestera_acl * acl,u8 lookup,u8 dir,void * keymask,u32 * vtcam_id)790702e7014SMaksym Glubokiy int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup, u8 dir,
79147327e19SVolodymyr Mytnyk void *keymask, u32 *vtcam_id)
79247327e19SVolodymyr Mytnyk {
79347327e19SVolodymyr Mytnyk struct prestera_acl_vtcam *vtcam;
79447327e19SVolodymyr Mytnyk u32 new_vtcam_id;
79547327e19SVolodymyr Mytnyk int err;
79647327e19SVolodymyr Mytnyk
79747327e19SVolodymyr Mytnyk /* find the vtcam that suits keymask. We do not expect to have
79847327e19SVolodymyr Mytnyk * a big number of vtcams, so, the list type for vtcam list is
79947327e19SVolodymyr Mytnyk * fine for now
80047327e19SVolodymyr Mytnyk */
80147327e19SVolodymyr Mytnyk list_for_each_entry(vtcam, &acl->vtcam_list, list) {
802702e7014SMaksym Glubokiy if (lookup != vtcam->lookup ||
803702e7014SMaksym Glubokiy dir != vtcam->direction)
80447327e19SVolodymyr Mytnyk continue;
80547327e19SVolodymyr Mytnyk
80647327e19SVolodymyr Mytnyk if (!keymask && !vtcam->is_keymask_set) {
80747327e19SVolodymyr Mytnyk refcount_inc(&vtcam->refcount);
80847327e19SVolodymyr Mytnyk goto vtcam_found;
80947327e19SVolodymyr Mytnyk }
81047327e19SVolodymyr Mytnyk
81147327e19SVolodymyr Mytnyk if (keymask && vtcam->is_keymask_set &&
81247327e19SVolodymyr Mytnyk !memcmp(keymask, vtcam->keymask, sizeof(vtcam->keymask))) {
81347327e19SVolodymyr Mytnyk refcount_inc(&vtcam->refcount);
81447327e19SVolodymyr Mytnyk goto vtcam_found;
81547327e19SVolodymyr Mytnyk }
81647327e19SVolodymyr Mytnyk }
81747327e19SVolodymyr Mytnyk
81847327e19SVolodymyr Mytnyk /* vtcam not found, try to create new one */
81947327e19SVolodymyr Mytnyk vtcam = kzalloc(sizeof(*vtcam), GFP_KERNEL);
82047327e19SVolodymyr Mytnyk if (!vtcam)
82147327e19SVolodymyr Mytnyk return -ENOMEM;
82247327e19SVolodymyr Mytnyk
82347327e19SVolodymyr Mytnyk err = prestera_hw_vtcam_create(acl->sw, lookup, keymask, &new_vtcam_id,
824702e7014SMaksym Glubokiy dir);
82547327e19SVolodymyr Mytnyk if (err) {
82647327e19SVolodymyr Mytnyk kfree(vtcam);
827604ba230SVolodymyr Mytnyk
828604ba230SVolodymyr Mytnyk /* cannot create new, try to fit into existing vtcam */
829604ba230SVolodymyr Mytnyk if (__prestera_acl_vtcam_id_try_fit(acl, lookup,
830604ba230SVolodymyr Mytnyk keymask, &new_vtcam_id))
83147327e19SVolodymyr Mytnyk return err;
832604ba230SVolodymyr Mytnyk
833604ba230SVolodymyr Mytnyk *vtcam_id = new_vtcam_id;
834604ba230SVolodymyr Mytnyk return 0;
83547327e19SVolodymyr Mytnyk }
83647327e19SVolodymyr Mytnyk
837702e7014SMaksym Glubokiy vtcam->direction = dir;
83847327e19SVolodymyr Mytnyk vtcam->id = new_vtcam_id;
83947327e19SVolodymyr Mytnyk vtcam->lookup = lookup;
84047327e19SVolodymyr Mytnyk if (keymask) {
84147327e19SVolodymyr Mytnyk memcpy(vtcam->keymask, keymask, sizeof(vtcam->keymask));
84247327e19SVolodymyr Mytnyk vtcam->is_keymask_set = true;
84347327e19SVolodymyr Mytnyk }
84447327e19SVolodymyr Mytnyk refcount_set(&vtcam->refcount, 1);
84547327e19SVolodymyr Mytnyk list_add_rcu(&vtcam->list, &acl->vtcam_list);
84647327e19SVolodymyr Mytnyk
84747327e19SVolodymyr Mytnyk vtcam_found:
84847327e19SVolodymyr Mytnyk *vtcam_id = vtcam->id;
84947327e19SVolodymyr Mytnyk return 0;
85047327e19SVolodymyr Mytnyk }
85147327e19SVolodymyr Mytnyk
prestera_acl_vtcam_id_put(struct prestera_acl * acl,u32 vtcam_id)85247327e19SVolodymyr Mytnyk int prestera_acl_vtcam_id_put(struct prestera_acl *acl, u32 vtcam_id)
85347327e19SVolodymyr Mytnyk {
85447327e19SVolodymyr Mytnyk struct prestera_acl_vtcam *vtcam;
85547327e19SVolodymyr Mytnyk int err;
85647327e19SVolodymyr Mytnyk
85747327e19SVolodymyr Mytnyk list_for_each_entry(vtcam, &acl->vtcam_list, list) {
85847327e19SVolodymyr Mytnyk if (vtcam_id != vtcam->id)
85947327e19SVolodymyr Mytnyk continue;
86047327e19SVolodymyr Mytnyk
86147327e19SVolodymyr Mytnyk if (!refcount_dec_and_test(&vtcam->refcount))
86247327e19SVolodymyr Mytnyk return 0;
86347327e19SVolodymyr Mytnyk
86447327e19SVolodymyr Mytnyk err = prestera_hw_vtcam_destroy(acl->sw, vtcam->id);
86547327e19SVolodymyr Mytnyk if (err && err != -ENODEV) {
86647327e19SVolodymyr Mytnyk refcount_set(&vtcam->refcount, 1);
86747327e19SVolodymyr Mytnyk return err;
86847327e19SVolodymyr Mytnyk }
86947327e19SVolodymyr Mytnyk
87047327e19SVolodymyr Mytnyk list_del(&vtcam->list);
87147327e19SVolodymyr Mytnyk kfree(vtcam);
87247327e19SVolodymyr Mytnyk return 0;
87347327e19SVolodymyr Mytnyk }
87447327e19SVolodymyr Mytnyk
87547327e19SVolodymyr Mytnyk return -ENOENT;
87647327e19SVolodymyr Mytnyk }
87747327e19SVolodymyr Mytnyk
prestera_acl_init(struct prestera_switch * sw)8788b474a9fSSerhiy Boiko int prestera_acl_init(struct prestera_switch *sw)
8798b474a9fSSerhiy Boiko {
8808b474a9fSSerhiy Boiko struct prestera_acl *acl;
88147327e19SVolodymyr Mytnyk int err;
8828b474a9fSSerhiy Boiko
8838b474a9fSSerhiy Boiko acl = kzalloc(sizeof(*acl), GFP_KERNEL);
8848b474a9fSSerhiy Boiko if (!acl)
8858b474a9fSSerhiy Boiko return -ENOMEM;
8868b474a9fSSerhiy Boiko
8878b474a9fSSerhiy Boiko acl->sw = sw;
88847327e19SVolodymyr Mytnyk INIT_LIST_HEAD(&acl->rules);
88947327e19SVolodymyr Mytnyk INIT_LIST_HEAD(&acl->vtcam_list);
89047327e19SVolodymyr Mytnyk idr_init(&acl->uid);
89147327e19SVolodymyr Mytnyk
89247327e19SVolodymyr Mytnyk err = rhashtable_init(&acl->acl_rule_entry_ht,
89347327e19SVolodymyr Mytnyk &__prestera_acl_rule_entry_ht_params);
89447327e19SVolodymyr Mytnyk if (err)
89547327e19SVolodymyr Mytnyk goto err_acl_rule_entry_ht_init;
89647327e19SVolodymyr Mytnyk
89747327e19SVolodymyr Mytnyk err = rhashtable_init(&acl->ruleset_ht,
89847327e19SVolodymyr Mytnyk &prestera_acl_ruleset_ht_params);
89947327e19SVolodymyr Mytnyk if (err)
90047327e19SVolodymyr Mytnyk goto err_ruleset_ht_init;
90147327e19SVolodymyr Mytnyk
90247327e19SVolodymyr Mytnyk sw->acl = acl;
9038b474a9fSSerhiy Boiko
9048b474a9fSSerhiy Boiko return 0;
90547327e19SVolodymyr Mytnyk
90647327e19SVolodymyr Mytnyk err_ruleset_ht_init:
90747327e19SVolodymyr Mytnyk rhashtable_destroy(&acl->acl_rule_entry_ht);
90847327e19SVolodymyr Mytnyk err_acl_rule_entry_ht_init:
90947327e19SVolodymyr Mytnyk kfree(acl);
91047327e19SVolodymyr Mytnyk return err;
9118b474a9fSSerhiy Boiko }
9128b474a9fSSerhiy Boiko
prestera_acl_fini(struct prestera_switch * sw)9138b474a9fSSerhiy Boiko void prestera_acl_fini(struct prestera_switch *sw)
9148b474a9fSSerhiy Boiko {
9158b474a9fSSerhiy Boiko struct prestera_acl *acl = sw->acl;
9168b474a9fSSerhiy Boiko
91747327e19SVolodymyr Mytnyk WARN_ON(!idr_is_empty(&acl->uid));
91847327e19SVolodymyr Mytnyk idr_destroy(&acl->uid);
91947327e19SVolodymyr Mytnyk
92047327e19SVolodymyr Mytnyk WARN_ON(!list_empty(&acl->vtcam_list));
9218b474a9fSSerhiy Boiko WARN_ON(!list_empty(&acl->rules));
92247327e19SVolodymyr Mytnyk
92347327e19SVolodymyr Mytnyk rhashtable_destroy(&acl->ruleset_ht);
92447327e19SVolodymyr Mytnyk rhashtable_destroy(&acl->acl_rule_entry_ht);
92547327e19SVolodymyr Mytnyk
9268b474a9fSSerhiy Boiko kfree(acl);
9278b474a9fSSerhiy Boiko }
928