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