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 
prestera_acl_chain_to_client(u32 chain_index,u32 * client)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 
prestera_acl_chain_is_supported(u32 chain_index)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 *
prestera_acl_ruleset_create(struct prestera_acl * acl,struct prestera_flow_block * block,u32 chain_index)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 
prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset * ruleset,void * keymask)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 
prestera_acl_ruleset_offload(struct prestera_acl_ruleset * ruleset)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 
prestera_acl_ruleset_destroy(struct prestera_acl_ruleset * ruleset)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 *
__prestera_acl_ruleset_lookup(struct prestera_acl * acl,struct prestera_flow_block * block,u32 chain_index)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 *
prestera_acl_ruleset_lookup(struct prestera_acl * acl,struct prestera_flow_block * block,u32 chain_index)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 *
prestera_acl_ruleset_get(struct prestera_acl * acl,struct prestera_flow_block * block,u32 chain_index)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 
prestera_acl_ruleset_put(struct prestera_acl_ruleset * ruleset)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 
prestera_acl_ruleset_bind(struct prestera_acl_ruleset * ruleset,struct prestera_port * port)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 
prestera_acl_ruleset_unbind(struct prestera_acl_ruleset * ruleset,struct prestera_port * port)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 
prestera_acl_ruleset_block_bind(struct prestera_acl_ruleset * ruleset,struct prestera_flow_block * block)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
prestera_acl_ruleset_block_unbind(struct prestera_acl_ruleset * ruleset,struct prestera_flow_block * block)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
prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule * rule,u16 pcl_id)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 *
prestera_acl_rule_lookup(struct prestera_acl_ruleset * ruleset,unsigned long cookie)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 
prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset * ruleset)366 u32 prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset *ruleset)
367 {
368 	return ruleset->index;
369 }
370 
prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset * ruleset)371 bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset)
372 {
373 	return ruleset->offload;
374 }
375 
376 struct prestera_acl_rule *
prestera_acl_rule_create(struct prestera_acl_ruleset * ruleset,unsigned long cookie,u32 chain_index)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 
prestera_acl_rule_priority_set(struct prestera_acl_rule * rule,u32 priority)395 void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule,
396 				    u32 priority)
397 {
398 	rule->priority = priority;
399 }
400 
prestera_acl_rule_destroy(struct prestera_acl_rule * rule)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 
prestera_acl_rule_add(struct prestera_switch * sw,struct prestera_acl_rule * rule)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 
prestera_acl_rule_del(struct prestera_switch * sw,struct prestera_acl_rule * rule)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 
prestera_acl_rule_get_stats(struct prestera_acl * acl,struct prestera_acl_rule * rule,u64 * packets,u64 * bytes,u64 * last_use)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 *
prestera_acl_rule_entry_find(struct prestera_acl * acl,struct prestera_acl_rule_entry_key * key)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 
__prestera_acl_rule_entry2hw_del(struct prestera_switch * sw,struct prestera_acl_rule_entry * e)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 
__prestera_acl_rule_entry2hw_add(struct prestera_switch * sw,struct prestera_acl_rule_entry * e)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
__prestera_acl_rule_entry_act_destruct(struct prestera_switch * sw,struct prestera_acl_rule_entry * e)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 
prestera_acl_rule_entry_destroy(struct prestera_acl * acl,struct prestera_acl_rule_entry * e)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
__prestera_acl_rule_entry_act_construct(struct prestera_switch * sw,struct prestera_acl_rule_entry * e,struct prestera_acl_rule_entry_arg * arg)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 *
prestera_acl_rule_entry_create(struct prestera_acl * acl,struct prestera_acl_rule_entry_key * key,struct prestera_acl_rule_entry_arg * arg)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 
__prestera_acl_vtcam_id_try_fit(struct prestera_acl * acl,u8 lookup,void * keymask,u32 * vtcam_id)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 
prestera_acl_vtcam_id_get(struct prestera_acl * acl,u8 lookup,void * keymask,u32 * vtcam_id)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 
prestera_acl_vtcam_id_put(struct prestera_acl * acl,u32 vtcam_id)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 
prestera_acl_init(struct prestera_switch * sw)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 
prestera_acl_fini(struct prestera_switch * sw)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 }
858