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