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