1 /*
2  * drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c
3  * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
4  * Copyright (c) 2017 Jiri Pirko <jiri@mellanox.com>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the names of the copyright holders nor the names of its
15  *    contributors may be used to endorse or promote products derived from
16  *    this software without specific prior written permission.
17  *
18  * Alternatively, this software may be distributed under the terms of the
19  * GNU General Public License ("GPL") version 2 as published by the Free
20  * Software Foundation.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include <linux/kernel.h>
36 #include <linux/slab.h>
37 #include <linux/errno.h>
38 #include <linux/list.h>
39 #include <linux/string.h>
40 #include <linux/rhashtable.h>
41 #include <linux/netdevice.h>
42 #include <net/tc_act/tc_vlan.h>
43 
44 #include "reg.h"
45 #include "core.h"
46 #include "resources.h"
47 #include "spectrum.h"
48 #include "core_acl_flex_keys.h"
49 #include "core_acl_flex_actions.h"
50 #include "spectrum_acl_flex_keys.h"
51 
52 struct mlxsw_sp_acl {
53 	struct mlxsw_sp *mlxsw_sp;
54 	struct mlxsw_afk *afk;
55 	struct mlxsw_afa *afa;
56 	struct mlxsw_sp_fid *dummy_fid;
57 	const struct mlxsw_sp_acl_ops *ops;
58 	struct rhashtable ruleset_ht;
59 	struct list_head rules;
60 	struct {
61 		struct delayed_work dw;
62 		unsigned long interval;	/* ms */
63 #define MLXSW_SP_ACL_RULE_ACTIVITY_UPDATE_PERIOD_MS 1000
64 	} rule_activity_update;
65 	unsigned long priv[0];
66 	/* priv has to be always the last item */
67 };
68 
69 struct mlxsw_afk *mlxsw_sp_acl_afk(struct mlxsw_sp_acl *acl)
70 {
71 	return acl->afk;
72 }
73 
74 struct mlxsw_sp_acl_ruleset_ht_key {
75 	struct net_device *dev; /* dev this ruleset is bound to */
76 	bool ingress;
77 	const struct mlxsw_sp_acl_profile_ops *ops;
78 };
79 
80 struct mlxsw_sp_acl_ruleset {
81 	struct rhash_head ht_node; /* Member of acl HT */
82 	struct mlxsw_sp_acl_ruleset_ht_key ht_key;
83 	struct rhashtable rule_ht;
84 	unsigned int ref_count;
85 	unsigned long priv[0];
86 	/* priv has to be always the last item */
87 };
88 
89 struct mlxsw_sp_acl_rule {
90 	struct rhash_head ht_node; /* Member of rule HT */
91 	struct list_head list;
92 	unsigned long cookie; /* HT key */
93 	struct mlxsw_sp_acl_ruleset *ruleset;
94 	struct mlxsw_sp_acl_rule_info *rulei;
95 	u64 last_used;
96 	u64 last_packets;
97 	u64 last_bytes;
98 	unsigned long priv[0];
99 	/* priv has to be always the last item */
100 };
101 
102 static const struct rhashtable_params mlxsw_sp_acl_ruleset_ht_params = {
103 	.key_len = sizeof(struct mlxsw_sp_acl_ruleset_ht_key),
104 	.key_offset = offsetof(struct mlxsw_sp_acl_ruleset, ht_key),
105 	.head_offset = offsetof(struct mlxsw_sp_acl_ruleset, ht_node),
106 	.automatic_shrinking = true,
107 };
108 
109 static const struct rhashtable_params mlxsw_sp_acl_rule_ht_params = {
110 	.key_len = sizeof(unsigned long),
111 	.key_offset = offsetof(struct mlxsw_sp_acl_rule, cookie),
112 	.head_offset = offsetof(struct mlxsw_sp_acl_rule, ht_node),
113 	.automatic_shrinking = true,
114 };
115 
116 struct mlxsw_sp_fid *mlxsw_sp_acl_dummy_fid(struct mlxsw_sp *mlxsw_sp)
117 {
118 	return mlxsw_sp->acl->dummy_fid;
119 }
120 
121 static struct mlxsw_sp_acl_ruleset *
122 mlxsw_sp_acl_ruleset_create(struct mlxsw_sp *mlxsw_sp,
123 			    const struct mlxsw_sp_acl_profile_ops *ops)
124 {
125 	struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
126 	struct mlxsw_sp_acl_ruleset *ruleset;
127 	size_t alloc_size;
128 	int err;
129 
130 	alloc_size = sizeof(*ruleset) + ops->ruleset_priv_size;
131 	ruleset = kzalloc(alloc_size, GFP_KERNEL);
132 	if (!ruleset)
133 		return ERR_PTR(-ENOMEM);
134 	ruleset->ref_count = 1;
135 	ruleset->ht_key.ops = ops;
136 
137 	err = rhashtable_init(&ruleset->rule_ht, &mlxsw_sp_acl_rule_ht_params);
138 	if (err)
139 		goto err_rhashtable_init;
140 
141 	err = ops->ruleset_add(mlxsw_sp, acl->priv, ruleset->priv);
142 	if (err)
143 		goto err_ops_ruleset_add;
144 
145 	return ruleset;
146 
147 err_ops_ruleset_add:
148 	rhashtable_destroy(&ruleset->rule_ht);
149 err_rhashtable_init:
150 	kfree(ruleset);
151 	return ERR_PTR(err);
152 }
153 
154 static void mlxsw_sp_acl_ruleset_destroy(struct mlxsw_sp *mlxsw_sp,
155 					 struct mlxsw_sp_acl_ruleset *ruleset)
156 {
157 	const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
158 
159 	ops->ruleset_del(mlxsw_sp, ruleset->priv);
160 	rhashtable_destroy(&ruleset->rule_ht);
161 	kfree(ruleset);
162 }
163 
164 static int mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp,
165 				     struct mlxsw_sp_acl_ruleset *ruleset,
166 				     struct net_device *dev, bool ingress)
167 {
168 	const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
169 	struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
170 	int err;
171 
172 	ruleset->ht_key.dev = dev;
173 	ruleset->ht_key.ingress = ingress;
174 	err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node,
175 				     mlxsw_sp_acl_ruleset_ht_params);
176 	if (err)
177 		return err;
178 	err = ops->ruleset_bind(mlxsw_sp, ruleset->priv, dev, ingress);
179 	if (err)
180 		goto err_ops_ruleset_bind;
181 	return 0;
182 
183 err_ops_ruleset_bind:
184 	rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node,
185 			       mlxsw_sp_acl_ruleset_ht_params);
186 	return err;
187 }
188 
189 static void mlxsw_sp_acl_ruleset_unbind(struct mlxsw_sp *mlxsw_sp,
190 					struct mlxsw_sp_acl_ruleset *ruleset)
191 {
192 	const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
193 	struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
194 
195 	ops->ruleset_unbind(mlxsw_sp, ruleset->priv);
196 	rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node,
197 			       mlxsw_sp_acl_ruleset_ht_params);
198 }
199 
200 static void mlxsw_sp_acl_ruleset_ref_inc(struct mlxsw_sp_acl_ruleset *ruleset)
201 {
202 	ruleset->ref_count++;
203 }
204 
205 static void mlxsw_sp_acl_ruleset_ref_dec(struct mlxsw_sp *mlxsw_sp,
206 					 struct mlxsw_sp_acl_ruleset *ruleset)
207 {
208 	if (--ruleset->ref_count)
209 		return;
210 	mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, ruleset);
211 	mlxsw_sp_acl_ruleset_destroy(mlxsw_sp, ruleset);
212 }
213 
214 struct mlxsw_sp_acl_ruleset *
215 mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp,
216 			 struct net_device *dev, bool ingress,
217 			 enum mlxsw_sp_acl_profile profile)
218 {
219 	const struct mlxsw_sp_acl_profile_ops *ops;
220 	struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
221 	struct mlxsw_sp_acl_ruleset_ht_key ht_key;
222 	struct mlxsw_sp_acl_ruleset *ruleset;
223 	int err;
224 
225 	ops = acl->ops->profile_ops(mlxsw_sp, profile);
226 	if (!ops)
227 		return ERR_PTR(-EINVAL);
228 
229 	memset(&ht_key, 0, sizeof(ht_key));
230 	ht_key.dev = dev;
231 	ht_key.ingress = ingress;
232 	ht_key.ops = ops;
233 	ruleset = rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key,
234 					 mlxsw_sp_acl_ruleset_ht_params);
235 	if (ruleset) {
236 		mlxsw_sp_acl_ruleset_ref_inc(ruleset);
237 		return ruleset;
238 	}
239 	ruleset = mlxsw_sp_acl_ruleset_create(mlxsw_sp, ops);
240 	if (IS_ERR(ruleset))
241 		return ruleset;
242 	err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, ruleset, dev, ingress);
243 	if (err)
244 		goto err_ruleset_bind;
245 	return ruleset;
246 
247 err_ruleset_bind:
248 	mlxsw_sp_acl_ruleset_destroy(mlxsw_sp, ruleset);
249 	return ERR_PTR(err);
250 }
251 
252 void mlxsw_sp_acl_ruleset_put(struct mlxsw_sp *mlxsw_sp,
253 			      struct mlxsw_sp_acl_ruleset *ruleset)
254 {
255 	mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset);
256 }
257 
258 static int
259 mlxsw_sp_acl_rulei_counter_alloc(struct mlxsw_sp *mlxsw_sp,
260 				 struct mlxsw_sp_acl_rule_info *rulei)
261 {
262 	int err;
263 
264 	err = mlxsw_sp_flow_counter_alloc(mlxsw_sp, &rulei->counter_index);
265 	if (err)
266 		return err;
267 	rulei->counter_valid = true;
268 	return 0;
269 }
270 
271 static void
272 mlxsw_sp_acl_rulei_counter_free(struct mlxsw_sp *mlxsw_sp,
273 				struct mlxsw_sp_acl_rule_info *rulei)
274 {
275 	rulei->counter_valid = false;
276 	mlxsw_sp_flow_counter_free(mlxsw_sp, rulei->counter_index);
277 }
278 
279 struct mlxsw_sp_acl_rule_info *
280 mlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl)
281 {
282 	struct mlxsw_sp_acl_rule_info *rulei;
283 	int err;
284 
285 	rulei = kzalloc(sizeof(*rulei), GFP_KERNEL);
286 	if (!rulei)
287 		return NULL;
288 	rulei->act_block = mlxsw_afa_block_create(acl->afa);
289 	if (IS_ERR(rulei->act_block)) {
290 		err = PTR_ERR(rulei->act_block);
291 		goto err_afa_block_create;
292 	}
293 	return rulei;
294 
295 err_afa_block_create:
296 	kfree(rulei);
297 	return ERR_PTR(err);
298 }
299 
300 void mlxsw_sp_acl_rulei_destroy(struct mlxsw_sp_acl_rule_info *rulei)
301 {
302 	mlxsw_afa_block_destroy(rulei->act_block);
303 	kfree(rulei);
304 }
305 
306 int mlxsw_sp_acl_rulei_commit(struct mlxsw_sp_acl_rule_info *rulei)
307 {
308 	return mlxsw_afa_block_commit(rulei->act_block);
309 }
310 
311 void mlxsw_sp_acl_rulei_priority(struct mlxsw_sp_acl_rule_info *rulei,
312 				 unsigned int priority)
313 {
314 	rulei->priority = priority;
315 }
316 
317 void mlxsw_sp_acl_rulei_keymask_u32(struct mlxsw_sp_acl_rule_info *rulei,
318 				    enum mlxsw_afk_element element,
319 				    u32 key_value, u32 mask_value)
320 {
321 	mlxsw_afk_values_add_u32(&rulei->values, element,
322 				 key_value, mask_value);
323 }
324 
325 void mlxsw_sp_acl_rulei_keymask_buf(struct mlxsw_sp_acl_rule_info *rulei,
326 				    enum mlxsw_afk_element element,
327 				    const char *key_value,
328 				    const char *mask_value, unsigned int len)
329 {
330 	mlxsw_afk_values_add_buf(&rulei->values, element,
331 				 key_value, mask_value, len);
332 }
333 
334 void mlxsw_sp_acl_rulei_act_continue(struct mlxsw_sp_acl_rule_info *rulei)
335 {
336 	mlxsw_afa_block_continue(rulei->act_block);
337 }
338 
339 void mlxsw_sp_acl_rulei_act_jump(struct mlxsw_sp_acl_rule_info *rulei,
340 				 u16 group_id)
341 {
342 	mlxsw_afa_block_jump(rulei->act_block, group_id);
343 }
344 
345 int mlxsw_sp_acl_rulei_act_drop(struct mlxsw_sp_acl_rule_info *rulei)
346 {
347 	return mlxsw_afa_block_append_drop(rulei->act_block);
348 }
349 
350 int mlxsw_sp_acl_rulei_act_trap(struct mlxsw_sp_acl_rule_info *rulei)
351 {
352 	return mlxsw_afa_block_append_trap(rulei->act_block);
353 }
354 
355 int mlxsw_sp_acl_rulei_act_fwd(struct mlxsw_sp *mlxsw_sp,
356 			       struct mlxsw_sp_acl_rule_info *rulei,
357 			       struct net_device *out_dev)
358 {
359 	struct mlxsw_sp_port *mlxsw_sp_port;
360 	u8 local_port;
361 	bool in_port;
362 
363 	if (out_dev) {
364 		if (!mlxsw_sp_port_dev_check(out_dev))
365 			return -EINVAL;
366 		mlxsw_sp_port = netdev_priv(out_dev);
367 		if (mlxsw_sp_port->mlxsw_sp != mlxsw_sp)
368 			return -EINVAL;
369 		local_port = mlxsw_sp_port->local_port;
370 		in_port = false;
371 	} else {
372 		/* If out_dev is NULL, the called wants to
373 		 * set forward to ingress port.
374 		 */
375 		local_port = 0;
376 		in_port = true;
377 	}
378 	return mlxsw_afa_block_append_fwd(rulei->act_block,
379 					  local_port, in_port);
380 }
381 
382 int mlxsw_sp_acl_rulei_act_vlan(struct mlxsw_sp *mlxsw_sp,
383 				struct mlxsw_sp_acl_rule_info *rulei,
384 				u32 action, u16 vid, u16 proto, u8 prio)
385 {
386 	u8 ethertype;
387 
388 	if (action == TCA_VLAN_ACT_MODIFY) {
389 		switch (proto) {
390 		case ETH_P_8021Q:
391 			ethertype = 0;
392 			break;
393 		case ETH_P_8021AD:
394 			ethertype = 1;
395 			break;
396 		default:
397 			dev_err(mlxsw_sp->bus_info->dev, "Unsupported VLAN protocol %#04x\n",
398 				proto);
399 			return -EINVAL;
400 		}
401 
402 		return mlxsw_afa_block_append_vlan_modify(rulei->act_block,
403 							  vid, prio, ethertype);
404 	} else {
405 		dev_err(mlxsw_sp->bus_info->dev, "Unsupported VLAN action\n");
406 		return -EINVAL;
407 	}
408 }
409 
410 int mlxsw_sp_acl_rulei_act_count(struct mlxsw_sp *mlxsw_sp,
411 				 struct mlxsw_sp_acl_rule_info *rulei)
412 {
413 	return mlxsw_afa_block_append_counter(rulei->act_block,
414 					      rulei->counter_index);
415 }
416 
417 int mlxsw_sp_acl_rulei_act_fid_set(struct mlxsw_sp *mlxsw_sp,
418 				   struct mlxsw_sp_acl_rule_info *rulei,
419 				   u16 fid)
420 {
421 	return mlxsw_afa_block_append_fid_set(rulei->act_block, fid);
422 }
423 
424 struct mlxsw_sp_acl_rule *
425 mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp,
426 			 struct mlxsw_sp_acl_ruleset *ruleset,
427 			 unsigned long cookie)
428 {
429 	const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
430 	struct mlxsw_sp_acl_rule *rule;
431 	int err;
432 
433 	mlxsw_sp_acl_ruleset_ref_inc(ruleset);
434 	rule = kzalloc(sizeof(*rule) + ops->rule_priv_size, GFP_KERNEL);
435 	if (!rule) {
436 		err = -ENOMEM;
437 		goto err_alloc;
438 	}
439 	rule->cookie = cookie;
440 	rule->ruleset = ruleset;
441 
442 	rule->rulei = mlxsw_sp_acl_rulei_create(mlxsw_sp->acl);
443 	if (IS_ERR(rule->rulei)) {
444 		err = PTR_ERR(rule->rulei);
445 		goto err_rulei_create;
446 	}
447 
448 	err = mlxsw_sp_acl_rulei_counter_alloc(mlxsw_sp, rule->rulei);
449 	if (err)
450 		goto err_counter_alloc;
451 	return rule;
452 
453 err_counter_alloc:
454 	mlxsw_sp_acl_rulei_destroy(rule->rulei);
455 err_rulei_create:
456 	kfree(rule);
457 err_alloc:
458 	mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset);
459 	return ERR_PTR(err);
460 }
461 
462 void mlxsw_sp_acl_rule_destroy(struct mlxsw_sp *mlxsw_sp,
463 			       struct mlxsw_sp_acl_rule *rule)
464 {
465 	struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
466 
467 	mlxsw_sp_acl_rulei_counter_free(mlxsw_sp, rule->rulei);
468 	mlxsw_sp_acl_rulei_destroy(rule->rulei);
469 	kfree(rule);
470 	mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset);
471 }
472 
473 int mlxsw_sp_acl_rule_add(struct mlxsw_sp *mlxsw_sp,
474 			  struct mlxsw_sp_acl_rule *rule)
475 {
476 	struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
477 	const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
478 	int err;
479 
480 	err = ops->rule_add(mlxsw_sp, ruleset->priv, rule->priv, rule->rulei);
481 	if (err)
482 		return err;
483 
484 	err = rhashtable_insert_fast(&ruleset->rule_ht, &rule->ht_node,
485 				     mlxsw_sp_acl_rule_ht_params);
486 	if (err)
487 		goto err_rhashtable_insert;
488 
489 	list_add_tail(&rule->list, &mlxsw_sp->acl->rules);
490 	return 0;
491 
492 err_rhashtable_insert:
493 	ops->rule_del(mlxsw_sp, rule->priv);
494 	return err;
495 }
496 
497 void mlxsw_sp_acl_rule_del(struct mlxsw_sp *mlxsw_sp,
498 			   struct mlxsw_sp_acl_rule *rule)
499 {
500 	struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
501 	const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
502 
503 	list_del(&rule->list);
504 	rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
505 			       mlxsw_sp_acl_rule_ht_params);
506 	ops->rule_del(mlxsw_sp, rule->priv);
507 }
508 
509 struct mlxsw_sp_acl_rule *
510 mlxsw_sp_acl_rule_lookup(struct mlxsw_sp *mlxsw_sp,
511 			 struct mlxsw_sp_acl_ruleset *ruleset,
512 			 unsigned long cookie)
513 {
514 	return rhashtable_lookup_fast(&ruleset->rule_ht, &cookie,
515 				       mlxsw_sp_acl_rule_ht_params);
516 }
517 
518 struct mlxsw_sp_acl_rule_info *
519 mlxsw_sp_acl_rule_rulei(struct mlxsw_sp_acl_rule *rule)
520 {
521 	return rule->rulei;
522 }
523 
524 static int mlxsw_sp_acl_rule_activity_update(struct mlxsw_sp *mlxsw_sp,
525 					     struct mlxsw_sp_acl_rule *rule)
526 {
527 	struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
528 	const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
529 	bool active;
530 	int err;
531 
532 	err = ops->rule_activity_get(mlxsw_sp, rule->priv, &active);
533 	if (err)
534 		return err;
535 	if (active)
536 		rule->last_used = jiffies;
537 	return 0;
538 }
539 
540 static int mlxsw_sp_acl_rules_activity_update(struct mlxsw_sp_acl *acl)
541 {
542 	struct mlxsw_sp_acl_rule *rule;
543 	int err;
544 
545 	/* Protect internal structures from changes */
546 	rtnl_lock();
547 	list_for_each_entry(rule, &acl->rules, list) {
548 		err = mlxsw_sp_acl_rule_activity_update(acl->mlxsw_sp,
549 							rule);
550 		if (err)
551 			goto err_rule_update;
552 	}
553 	rtnl_unlock();
554 	return 0;
555 
556 err_rule_update:
557 	rtnl_unlock();
558 	return err;
559 }
560 
561 static void mlxsw_sp_acl_rule_activity_work_schedule(struct mlxsw_sp_acl *acl)
562 {
563 	unsigned long interval = acl->rule_activity_update.interval;
564 
565 	mlxsw_core_schedule_dw(&acl->rule_activity_update.dw,
566 			       msecs_to_jiffies(interval));
567 }
568 
569 static void mlxsw_sp_acl_rul_activity_update_work(struct work_struct *work)
570 {
571 	struct mlxsw_sp_acl *acl = container_of(work, struct mlxsw_sp_acl,
572 						rule_activity_update.dw.work);
573 	int err;
574 
575 	err = mlxsw_sp_acl_rules_activity_update(acl);
576 	if (err)
577 		dev_err(acl->mlxsw_sp->bus_info->dev, "Could not update acl activity");
578 
579 	mlxsw_sp_acl_rule_activity_work_schedule(acl);
580 }
581 
582 int mlxsw_sp_acl_rule_get_stats(struct mlxsw_sp *mlxsw_sp,
583 				struct mlxsw_sp_acl_rule *rule,
584 				u64 *packets, u64 *bytes, u64 *last_use)
585 
586 {
587 	struct mlxsw_sp_acl_rule_info *rulei;
588 	u64 current_packets;
589 	u64 current_bytes;
590 	int err;
591 
592 	rulei = mlxsw_sp_acl_rule_rulei(rule);
593 	err = mlxsw_sp_flow_counter_get(mlxsw_sp, rulei->counter_index,
594 					&current_packets, &current_bytes);
595 	if (err)
596 		return err;
597 
598 	*packets = current_packets - rule->last_packets;
599 	*bytes = current_bytes - rule->last_bytes;
600 	*last_use = rule->last_used;
601 
602 	rule->last_bytes = current_bytes;
603 	rule->last_packets = current_packets;
604 
605 	return 0;
606 }
607 
608 #define MLXSW_SP_KDVL_ACT_EXT_SIZE 1
609 
610 static int mlxsw_sp_act_kvdl_set_add(void *priv, u32 *p_kvdl_index,
611 				     char *enc_actions, bool is_first)
612 {
613 	struct mlxsw_sp *mlxsw_sp = priv;
614 	char pefa_pl[MLXSW_REG_PEFA_LEN];
615 	u32 kvdl_index;
616 	int err;
617 
618 	/* The first action set of a TCAM entry is stored directly in TCAM,
619 	 * not KVD linear area.
620 	 */
621 	if (is_first)
622 		return 0;
623 
624 	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KDVL_ACT_EXT_SIZE,
625 				  &kvdl_index);
626 	if (err)
627 		return err;
628 	mlxsw_reg_pefa_pack(pefa_pl, kvdl_index, enc_actions);
629 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pefa), pefa_pl);
630 	if (err)
631 		goto err_pefa_write;
632 	*p_kvdl_index = kvdl_index;
633 	return 0;
634 
635 err_pefa_write:
636 	mlxsw_sp_kvdl_free(mlxsw_sp, kvdl_index);
637 	return err;
638 }
639 
640 static void mlxsw_sp_act_kvdl_set_del(void *priv, u32 kvdl_index,
641 				      bool is_first)
642 {
643 	struct mlxsw_sp *mlxsw_sp = priv;
644 
645 	if (is_first)
646 		return;
647 	mlxsw_sp_kvdl_free(mlxsw_sp, kvdl_index);
648 }
649 
650 static int mlxsw_sp_act_kvdl_fwd_entry_add(void *priv, u32 *p_kvdl_index,
651 					   u8 local_port)
652 {
653 	struct mlxsw_sp *mlxsw_sp = priv;
654 	char ppbs_pl[MLXSW_REG_PPBS_LEN];
655 	u32 kvdl_index;
656 	int err;
657 
658 	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, 1, &kvdl_index);
659 	if (err)
660 		return err;
661 	mlxsw_reg_ppbs_pack(ppbs_pl, kvdl_index, local_port);
662 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppbs), ppbs_pl);
663 	if (err)
664 		goto err_ppbs_write;
665 	*p_kvdl_index = kvdl_index;
666 	return 0;
667 
668 err_ppbs_write:
669 	mlxsw_sp_kvdl_free(mlxsw_sp, kvdl_index);
670 	return err;
671 }
672 
673 static void mlxsw_sp_act_kvdl_fwd_entry_del(void *priv, u32 kvdl_index)
674 {
675 	struct mlxsw_sp *mlxsw_sp = priv;
676 
677 	mlxsw_sp_kvdl_free(mlxsw_sp, kvdl_index);
678 }
679 
680 static const struct mlxsw_afa_ops mlxsw_sp_act_afa_ops = {
681 	.kvdl_set_add		= mlxsw_sp_act_kvdl_set_add,
682 	.kvdl_set_del		= mlxsw_sp_act_kvdl_set_del,
683 	.kvdl_fwd_entry_add	= mlxsw_sp_act_kvdl_fwd_entry_add,
684 	.kvdl_fwd_entry_del	= mlxsw_sp_act_kvdl_fwd_entry_del,
685 };
686 
687 int mlxsw_sp_acl_init(struct mlxsw_sp *mlxsw_sp)
688 {
689 	const struct mlxsw_sp_acl_ops *acl_ops = &mlxsw_sp_acl_tcam_ops;
690 	struct mlxsw_sp_fid *fid;
691 	struct mlxsw_sp_acl *acl;
692 	int err;
693 
694 	acl = kzalloc(sizeof(*acl) + acl_ops->priv_size, GFP_KERNEL);
695 	if (!acl)
696 		return -ENOMEM;
697 	mlxsw_sp->acl = acl;
698 	acl->mlxsw_sp = mlxsw_sp;
699 	acl->afk = mlxsw_afk_create(MLXSW_CORE_RES_GET(mlxsw_sp->core,
700 						       ACL_FLEX_KEYS),
701 				    mlxsw_sp_afk_blocks,
702 				    MLXSW_SP_AFK_BLOCKS_COUNT);
703 	if (!acl->afk) {
704 		err = -ENOMEM;
705 		goto err_afk_create;
706 	}
707 
708 	acl->afa = mlxsw_afa_create(MLXSW_CORE_RES_GET(mlxsw_sp->core,
709 						       ACL_ACTIONS_PER_SET),
710 				    &mlxsw_sp_act_afa_ops, mlxsw_sp);
711 	if (IS_ERR(acl->afa)) {
712 		err = PTR_ERR(acl->afa);
713 		goto err_afa_create;
714 	}
715 
716 	err = rhashtable_init(&acl->ruleset_ht,
717 			      &mlxsw_sp_acl_ruleset_ht_params);
718 	if (err)
719 		goto err_rhashtable_init;
720 
721 	fid = mlxsw_sp_fid_dummy_get(mlxsw_sp);
722 	if (IS_ERR(fid)) {
723 		err = PTR_ERR(fid);
724 		goto err_fid_get;
725 	}
726 	acl->dummy_fid = fid;
727 
728 	INIT_LIST_HEAD(&acl->rules);
729 	err = acl_ops->init(mlxsw_sp, acl->priv);
730 	if (err)
731 		goto err_acl_ops_init;
732 
733 	acl->ops = acl_ops;
734 
735 	/* Create the delayed work for the rule activity_update */
736 	INIT_DELAYED_WORK(&acl->rule_activity_update.dw,
737 			  mlxsw_sp_acl_rul_activity_update_work);
738 	acl->rule_activity_update.interval = MLXSW_SP_ACL_RULE_ACTIVITY_UPDATE_PERIOD_MS;
739 	mlxsw_core_schedule_dw(&acl->rule_activity_update.dw, 0);
740 	return 0;
741 
742 err_acl_ops_init:
743 	mlxsw_sp_fid_put(fid);
744 err_fid_get:
745 	rhashtable_destroy(&acl->ruleset_ht);
746 err_rhashtable_init:
747 	mlxsw_afa_destroy(acl->afa);
748 err_afa_create:
749 	mlxsw_afk_destroy(acl->afk);
750 err_afk_create:
751 	kfree(acl);
752 	return err;
753 }
754 
755 void mlxsw_sp_acl_fini(struct mlxsw_sp *mlxsw_sp)
756 {
757 	struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
758 	const struct mlxsw_sp_acl_ops *acl_ops = acl->ops;
759 
760 	cancel_delayed_work_sync(&mlxsw_sp->acl->rule_activity_update.dw);
761 	acl_ops->fini(mlxsw_sp, acl->priv);
762 	WARN_ON(!list_empty(&acl->rules));
763 	mlxsw_sp_fid_put(acl->dummy_fid);
764 	rhashtable_destroy(&acl->ruleset_ht);
765 	mlxsw_afa_destroy(acl->afa);
766 	mlxsw_afk_destroy(acl->afk);
767 	kfree(acl);
768 }
769