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