mlxsw: spectrum: Change init order
[linux-2.6-block.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_acl.c
CommitLineData
22a67766
JP
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>
a150201a 42#include <net/tc_act/tc_vlan.h>
22a67766
JP
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
52struct mlxsw_sp_acl {
446a1541 53 struct mlxsw_sp *mlxsw_sp;
22a67766
JP
54 struct mlxsw_afk *afk;
55 struct mlxsw_afa *afa;
a1107487 56 struct mlxsw_sp_fid *dummy_fid;
22a67766
JP
57 const struct mlxsw_sp_acl_ops *ops;
58 struct rhashtable ruleset_ht;
096e914f 59 struct list_head rules;
446a1541
AS
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;
22a67766
JP
65 unsigned long priv[0];
66 /* priv has to be always the last item */
67};
68
69struct mlxsw_afk *mlxsw_sp_acl_afk(struct mlxsw_sp_acl *acl)
70{
71 return acl->afk;
72}
73
74struct mlxsw_sp_acl_ruleset_ht_key {
75 struct net_device *dev; /* dev this ruleset is bound to */
76 bool ingress;
45b62742 77 u32 chain_index;
22a67766
JP
78 const struct mlxsw_sp_acl_profile_ops *ops;
79};
80
81struct mlxsw_sp_acl_ruleset {
82 struct rhash_head ht_node; /* Member of acl HT */
83 struct mlxsw_sp_acl_ruleset_ht_key ht_key;
84 struct rhashtable rule_ht;
85 unsigned int ref_count;
86 unsigned long priv[0];
87 /* priv has to be always the last item */
88};
89
90struct mlxsw_sp_acl_rule {
91 struct rhash_head ht_node; /* Member of rule HT */
096e914f 92 struct list_head list;
22a67766
JP
93 unsigned long cookie; /* HT key */
94 struct mlxsw_sp_acl_ruleset *ruleset;
95 struct mlxsw_sp_acl_rule_info *rulei;
446a1541 96 u64 last_used;
7c1b8eb1
AS
97 u64 last_packets;
98 u64 last_bytes;
22a67766
JP
99 unsigned long priv[0];
100 /* priv has to be always the last item */
101};
102
103static const struct rhashtable_params mlxsw_sp_acl_ruleset_ht_params = {
104 .key_len = sizeof(struct mlxsw_sp_acl_ruleset_ht_key),
105 .key_offset = offsetof(struct mlxsw_sp_acl_ruleset, ht_key),
106 .head_offset = offsetof(struct mlxsw_sp_acl_ruleset, ht_node),
107 .automatic_shrinking = true,
108};
109
110static const struct rhashtable_params mlxsw_sp_acl_rule_ht_params = {
111 .key_len = sizeof(unsigned long),
112 .key_offset = offsetof(struct mlxsw_sp_acl_rule, cookie),
113 .head_offset = offsetof(struct mlxsw_sp_acl_rule, ht_node),
114 .automatic_shrinking = true,
115};
116
a1107487
IS
117struct mlxsw_sp_fid *mlxsw_sp_acl_dummy_fid(struct mlxsw_sp *mlxsw_sp)
118{
119 return mlxsw_sp->acl->dummy_fid;
120}
121
22a67766
JP
122static struct mlxsw_sp_acl_ruleset *
123mlxsw_sp_acl_ruleset_create(struct mlxsw_sp *mlxsw_sp,
124 const struct mlxsw_sp_acl_profile_ops *ops)
125{
126 struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
127 struct mlxsw_sp_acl_ruleset *ruleset;
128 size_t alloc_size;
129 int err;
130
131 alloc_size = sizeof(*ruleset) + ops->ruleset_priv_size;
132 ruleset = kzalloc(alloc_size, GFP_KERNEL);
133 if (!ruleset)
134 return ERR_PTR(-ENOMEM);
135 ruleset->ref_count = 1;
136 ruleset->ht_key.ops = ops;
137
138 err = rhashtable_init(&ruleset->rule_ht, &mlxsw_sp_acl_rule_ht_params);
139 if (err)
140 goto err_rhashtable_init;
141
142 err = ops->ruleset_add(mlxsw_sp, acl->priv, ruleset->priv);
143 if (err)
144 goto err_ops_ruleset_add;
145
146 return ruleset;
147
148err_ops_ruleset_add:
149 rhashtable_destroy(&ruleset->rule_ht);
150err_rhashtable_init:
151 kfree(ruleset);
152 return ERR_PTR(err);
153}
154
155static void mlxsw_sp_acl_ruleset_destroy(struct mlxsw_sp *mlxsw_sp,
156 struct mlxsw_sp_acl_ruleset *ruleset)
157{
158 const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
159
160 ops->ruleset_del(mlxsw_sp, ruleset->priv);
161 rhashtable_destroy(&ruleset->rule_ht);
162 kfree(ruleset);
163}
164
165static int mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp,
166 struct mlxsw_sp_acl_ruleset *ruleset,
45b62742
JP
167 struct net_device *dev, bool ingress,
168 u32 chain_index)
22a67766
JP
169{
170 const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
171 struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
172 int err;
173
174 ruleset->ht_key.dev = dev;
175 ruleset->ht_key.ingress = ingress;
45b62742 176 ruleset->ht_key.chain_index = chain_index;
22a67766
JP
177 err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node,
178 mlxsw_sp_acl_ruleset_ht_params);
179 if (err)
180 return err;
45b62742
JP
181 if (!ruleset->ht_key.chain_index) {
182 /* We only need ruleset with chain index 0, the implicit one,
183 * to be directly bound to device. The rest of the rulesets
184 * are bound by "Goto action set".
185 */
186 err = ops->ruleset_bind(mlxsw_sp, ruleset->priv, dev, ingress);
187 if (err)
188 goto err_ops_ruleset_bind;
189 }
22a67766
JP
190 return 0;
191
192err_ops_ruleset_bind:
193 rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node,
194 mlxsw_sp_acl_ruleset_ht_params);
195 return err;
196}
197
198static void mlxsw_sp_acl_ruleset_unbind(struct mlxsw_sp *mlxsw_sp,
199 struct mlxsw_sp_acl_ruleset *ruleset)
200{
201 const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
202 struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
203
45b62742
JP
204 if (!ruleset->ht_key.chain_index)
205 ops->ruleset_unbind(mlxsw_sp, ruleset->priv);
22a67766
JP
206 rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node,
207 mlxsw_sp_acl_ruleset_ht_params);
208}
209
210static void mlxsw_sp_acl_ruleset_ref_inc(struct mlxsw_sp_acl_ruleset *ruleset)
211{
212 ruleset->ref_count++;
213}
214
215static void mlxsw_sp_acl_ruleset_ref_dec(struct mlxsw_sp *mlxsw_sp,
216 struct mlxsw_sp_acl_ruleset *ruleset)
217{
218 if (--ruleset->ref_count)
219 return;
220 mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, ruleset);
221 mlxsw_sp_acl_ruleset_destroy(mlxsw_sp, ruleset);
222}
223
dbec8ee9
JP
224static struct mlxsw_sp_acl_ruleset *
225__mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp_acl *acl, struct net_device *dev,
226 bool ingress, u32 chain_index,
227 const struct mlxsw_sp_acl_profile_ops *ops)
228{
229 struct mlxsw_sp_acl_ruleset_ht_key ht_key;
230
231 memset(&ht_key, 0, sizeof(ht_key));
232 ht_key.dev = dev;
233 ht_key.ingress = ingress;
234 ht_key.chain_index = chain_index;
235 ht_key.ops = ops;
236 return rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key,
237 mlxsw_sp_acl_ruleset_ht_params);
238}
239
240struct mlxsw_sp_acl_ruleset *
241mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp *mlxsw_sp, struct net_device *dev,
242 bool ingress, u32 chain_index,
243 enum mlxsw_sp_acl_profile profile)
244{
245 const struct mlxsw_sp_acl_profile_ops *ops;
246 struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
247 struct mlxsw_sp_acl_ruleset *ruleset;
248
249 ops = acl->ops->profile_ops(mlxsw_sp, profile);
250 if (!ops)
251 return ERR_PTR(-EINVAL);
252 ruleset = __mlxsw_sp_acl_ruleset_lookup(acl, dev, ingress,
253 chain_index, ops);
254 if (!ruleset)
255 return ERR_PTR(-ENOENT);
256 return ruleset;
257}
258
22a67766 259struct mlxsw_sp_acl_ruleset *
45b62742
JP
260mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, struct net_device *dev,
261 bool ingress, u32 chain_index,
22a67766
JP
262 enum mlxsw_sp_acl_profile profile)
263{
264 const struct mlxsw_sp_acl_profile_ops *ops;
265 struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
22a67766
JP
266 struct mlxsw_sp_acl_ruleset *ruleset;
267 int err;
268
269 ops = acl->ops->profile_ops(mlxsw_sp, profile);
270 if (!ops)
271 return ERR_PTR(-EINVAL);
272
dbec8ee9
JP
273 ruleset = __mlxsw_sp_acl_ruleset_lookup(acl, dev, ingress,
274 chain_index, ops);
22a67766
JP
275 if (ruleset) {
276 mlxsw_sp_acl_ruleset_ref_inc(ruleset);
277 return ruleset;
278 }
279 ruleset = mlxsw_sp_acl_ruleset_create(mlxsw_sp, ops);
280 if (IS_ERR(ruleset))
281 return ruleset;
45b62742
JP
282 err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, ruleset, dev,
283 ingress, chain_index);
22a67766
JP
284 if (err)
285 goto err_ruleset_bind;
286 return ruleset;
287
288err_ruleset_bind:
289 mlxsw_sp_acl_ruleset_destroy(mlxsw_sp, ruleset);
290 return ERR_PTR(err);
291}
292
293void mlxsw_sp_acl_ruleset_put(struct mlxsw_sp *mlxsw_sp,
294 struct mlxsw_sp_acl_ruleset *ruleset)
295{
296 mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset);
297}
298
0ade3b64
JP
299u16 mlxsw_sp_acl_ruleset_group_id(struct mlxsw_sp_acl_ruleset *ruleset)
300{
301 const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
302
303 return ops->ruleset_group_id(ruleset->priv);
304}
305
48170729
AS
306static int
307mlxsw_sp_acl_rulei_counter_alloc(struct mlxsw_sp *mlxsw_sp,
308 struct mlxsw_sp_acl_rule_info *rulei)
309{
310 int err;
311
312 err = mlxsw_sp_flow_counter_alloc(mlxsw_sp, &rulei->counter_index);
313 if (err)
314 return err;
315 rulei->counter_valid = true;
316 return 0;
317}
318
319static void
320mlxsw_sp_acl_rulei_counter_free(struct mlxsw_sp *mlxsw_sp,
321 struct mlxsw_sp_acl_rule_info *rulei)
322{
323 rulei->counter_valid = false;
324 mlxsw_sp_flow_counter_free(mlxsw_sp, rulei->counter_index);
325}
326
22a67766
JP
327struct mlxsw_sp_acl_rule_info *
328mlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl)
329{
330 struct mlxsw_sp_acl_rule_info *rulei;
331 int err;
332
333 rulei = kzalloc(sizeof(*rulei), GFP_KERNEL);
334 if (!rulei)
335 return NULL;
336 rulei->act_block = mlxsw_afa_block_create(acl->afa);
337 if (IS_ERR(rulei->act_block)) {
338 err = PTR_ERR(rulei->act_block);
339 goto err_afa_block_create;
340 }
341 return rulei;
342
343err_afa_block_create:
344 kfree(rulei);
345 return ERR_PTR(err);
346}
347
348void mlxsw_sp_acl_rulei_destroy(struct mlxsw_sp_acl_rule_info *rulei)
349{
350 mlxsw_afa_block_destroy(rulei->act_block);
351 kfree(rulei);
352}
353
354int mlxsw_sp_acl_rulei_commit(struct mlxsw_sp_acl_rule_info *rulei)
355{
356 return mlxsw_afa_block_commit(rulei->act_block);
357}
358
359void mlxsw_sp_acl_rulei_priority(struct mlxsw_sp_acl_rule_info *rulei,
360 unsigned int priority)
361{
362 rulei->priority = priority;
363}
364
365void mlxsw_sp_acl_rulei_keymask_u32(struct mlxsw_sp_acl_rule_info *rulei,
366 enum mlxsw_afk_element element,
367 u32 key_value, u32 mask_value)
368{
369 mlxsw_afk_values_add_u32(&rulei->values, element,
370 key_value, mask_value);
371}
372
373void mlxsw_sp_acl_rulei_keymask_buf(struct mlxsw_sp_acl_rule_info *rulei,
374 enum mlxsw_afk_element element,
375 const char *key_value,
376 const char *mask_value, unsigned int len)
377{
378 mlxsw_afk_values_add_buf(&rulei->values, element,
379 key_value, mask_value, len);
380}
381
382void mlxsw_sp_acl_rulei_act_continue(struct mlxsw_sp_acl_rule_info *rulei)
383{
384 mlxsw_afa_block_continue(rulei->act_block);
385}
386
387void mlxsw_sp_acl_rulei_act_jump(struct mlxsw_sp_acl_rule_info *rulei,
388 u16 group_id)
389{
390 mlxsw_afa_block_jump(rulei->act_block, group_id);
391}
392
393int mlxsw_sp_acl_rulei_act_drop(struct mlxsw_sp_acl_rule_info *rulei)
394{
395 return mlxsw_afa_block_append_drop(rulei->act_block);
396}
397
df7eea96
JP
398int mlxsw_sp_acl_rulei_act_trap(struct mlxsw_sp_acl_rule_info *rulei)
399{
400 return mlxsw_afa_block_append_trap(rulei->act_block);
401}
402
22a67766
JP
403int mlxsw_sp_acl_rulei_act_fwd(struct mlxsw_sp *mlxsw_sp,
404 struct mlxsw_sp_acl_rule_info *rulei,
405 struct net_device *out_dev)
406{
407 struct mlxsw_sp_port *mlxsw_sp_port;
408 u8 local_port;
409 bool in_port;
410
411 if (out_dev) {
412 if (!mlxsw_sp_port_dev_check(out_dev))
413 return -EINVAL;
414 mlxsw_sp_port = netdev_priv(out_dev);
415 if (mlxsw_sp_port->mlxsw_sp != mlxsw_sp)
416 return -EINVAL;
417 local_port = mlxsw_sp_port->local_port;
418 in_port = false;
419 } else {
4bb51bd6 420 /* If out_dev is NULL, the caller wants to
22a67766
JP
421 * set forward to ingress port.
422 */
423 local_port = 0;
424 in_port = true;
425 }
426 return mlxsw_afa_block_append_fwd(rulei->act_block,
427 local_port, in_port);
428}
429
a150201a
PM
430int mlxsw_sp_acl_rulei_act_vlan(struct mlxsw_sp *mlxsw_sp,
431 struct mlxsw_sp_acl_rule_info *rulei,
432 u32 action, u16 vid, u16 proto, u8 prio)
433{
434 u8 ethertype;
435
436 if (action == TCA_VLAN_ACT_MODIFY) {
437 switch (proto) {
438 case ETH_P_8021Q:
439 ethertype = 0;
440 break;
441 case ETH_P_8021AD:
442 ethertype = 1;
443 break;
444 default:
445 dev_err(mlxsw_sp->bus_info->dev, "Unsupported VLAN protocol %#04x\n",
446 proto);
447 return -EINVAL;
448 }
449
450 return mlxsw_afa_block_append_vlan_modify(rulei->act_block,
451 vid, prio, ethertype);
452 } else {
453 dev_err(mlxsw_sp->bus_info->dev, "Unsupported VLAN action\n");
454 return -EINVAL;
455 }
456}
457
48170729
AS
458int mlxsw_sp_acl_rulei_act_count(struct mlxsw_sp *mlxsw_sp,
459 struct mlxsw_sp_acl_rule_info *rulei)
460{
461 return mlxsw_afa_block_append_counter(rulei->act_block,
462 rulei->counter_index);
463}
464
ac44dd43
JP
465int mlxsw_sp_acl_rulei_act_fid_set(struct mlxsw_sp *mlxsw_sp,
466 struct mlxsw_sp_acl_rule_info *rulei,
467 u16 fid)
468{
469 return mlxsw_afa_block_append_fid_set(rulei->act_block, fid);
470}
471
22a67766
JP
472struct mlxsw_sp_acl_rule *
473mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp,
474 struct mlxsw_sp_acl_ruleset *ruleset,
475 unsigned long cookie)
476{
477 const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
478 struct mlxsw_sp_acl_rule *rule;
479 int err;
480
481 mlxsw_sp_acl_ruleset_ref_inc(ruleset);
482 rule = kzalloc(sizeof(*rule) + ops->rule_priv_size, GFP_KERNEL);
483 if (!rule) {
484 err = -ENOMEM;
485 goto err_alloc;
486 }
487 rule->cookie = cookie;
488 rule->ruleset = ruleset;
489
490 rule->rulei = mlxsw_sp_acl_rulei_create(mlxsw_sp->acl);
491 if (IS_ERR(rule->rulei)) {
492 err = PTR_ERR(rule->rulei);
493 goto err_rulei_create;
494 }
48170729
AS
495
496 err = mlxsw_sp_acl_rulei_counter_alloc(mlxsw_sp, rule->rulei);
497 if (err)
498 goto err_counter_alloc;
22a67766
JP
499 return rule;
500
48170729
AS
501err_counter_alloc:
502 mlxsw_sp_acl_rulei_destroy(rule->rulei);
22a67766
JP
503err_rulei_create:
504 kfree(rule);
505err_alloc:
506 mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset);
507 return ERR_PTR(err);
508}
509
510void mlxsw_sp_acl_rule_destroy(struct mlxsw_sp *mlxsw_sp,
511 struct mlxsw_sp_acl_rule *rule)
512{
513 struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
514
48170729 515 mlxsw_sp_acl_rulei_counter_free(mlxsw_sp, rule->rulei);
22a67766
JP
516 mlxsw_sp_acl_rulei_destroy(rule->rulei);
517 kfree(rule);
518 mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset);
519}
520
521int mlxsw_sp_acl_rule_add(struct mlxsw_sp *mlxsw_sp,
522 struct mlxsw_sp_acl_rule *rule)
523{
524 struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
525 const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
526 int err;
527
528 err = ops->rule_add(mlxsw_sp, ruleset->priv, rule->priv, rule->rulei);
529 if (err)
530 return err;
531
532 err = rhashtable_insert_fast(&ruleset->rule_ht, &rule->ht_node,
533 mlxsw_sp_acl_rule_ht_params);
534 if (err)
535 goto err_rhashtable_insert;
536
096e914f 537 list_add_tail(&rule->list, &mlxsw_sp->acl->rules);
22a67766
JP
538 return 0;
539
540err_rhashtable_insert:
541 ops->rule_del(mlxsw_sp, rule->priv);
542 return err;
543}
544
545void mlxsw_sp_acl_rule_del(struct mlxsw_sp *mlxsw_sp,
546 struct mlxsw_sp_acl_rule *rule)
547{
548 struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
549 const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
550
096e914f 551 list_del(&rule->list);
22a67766
JP
552 rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
553 mlxsw_sp_acl_rule_ht_params);
554 ops->rule_del(mlxsw_sp, rule->priv);
555}
556
557struct mlxsw_sp_acl_rule *
558mlxsw_sp_acl_rule_lookup(struct mlxsw_sp *mlxsw_sp,
559 struct mlxsw_sp_acl_ruleset *ruleset,
560 unsigned long cookie)
561{
562 return rhashtable_lookup_fast(&ruleset->rule_ht, &cookie,
563 mlxsw_sp_acl_rule_ht_params);
564}
565
566struct mlxsw_sp_acl_rule_info *
567mlxsw_sp_acl_rule_rulei(struct mlxsw_sp_acl_rule *rule)
568{
569 return rule->rulei;
570}
571
446a1541
AS
572static int mlxsw_sp_acl_rule_activity_update(struct mlxsw_sp *mlxsw_sp,
573 struct mlxsw_sp_acl_rule *rule)
574{
575 struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
576 const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
577 bool active;
578 int err;
579
580 err = ops->rule_activity_get(mlxsw_sp, rule->priv, &active);
581 if (err)
582 return err;
583 if (active)
584 rule->last_used = jiffies;
585 return 0;
586}
587
588static int mlxsw_sp_acl_rules_activity_update(struct mlxsw_sp_acl *acl)
589{
590 struct mlxsw_sp_acl_rule *rule;
591 int err;
592
593 /* Protect internal structures from changes */
594 rtnl_lock();
595 list_for_each_entry(rule, &acl->rules, list) {
596 err = mlxsw_sp_acl_rule_activity_update(acl->mlxsw_sp,
597 rule);
598 if (err)
599 goto err_rule_update;
600 }
601 rtnl_unlock();
602 return 0;
603
604err_rule_update:
605 rtnl_unlock();
606 return err;
607}
608
609static void mlxsw_sp_acl_rule_activity_work_schedule(struct mlxsw_sp_acl *acl)
610{
611 unsigned long interval = acl->rule_activity_update.interval;
612
613 mlxsw_core_schedule_dw(&acl->rule_activity_update.dw,
614 msecs_to_jiffies(interval));
615}
616
617static void mlxsw_sp_acl_rul_activity_update_work(struct work_struct *work)
618{
619 struct mlxsw_sp_acl *acl = container_of(work, struct mlxsw_sp_acl,
620 rule_activity_update.dw.work);
621 int err;
622
623 err = mlxsw_sp_acl_rules_activity_update(acl);
624 if (err)
625 dev_err(acl->mlxsw_sp->bus_info->dev, "Could not update acl activity");
626
627 mlxsw_sp_acl_rule_activity_work_schedule(acl);
628}
629
7c1b8eb1
AS
630int mlxsw_sp_acl_rule_get_stats(struct mlxsw_sp *mlxsw_sp,
631 struct mlxsw_sp_acl_rule *rule,
632 u64 *packets, u64 *bytes, u64 *last_use)
633
634{
635 struct mlxsw_sp_acl_rule_info *rulei;
636 u64 current_packets;
637 u64 current_bytes;
638 int err;
639
640 rulei = mlxsw_sp_acl_rule_rulei(rule);
641 err = mlxsw_sp_flow_counter_get(mlxsw_sp, rulei->counter_index,
642 &current_packets, &current_bytes);
643 if (err)
644 return err;
645
646 *packets = current_packets - rule->last_packets;
647 *bytes = current_bytes - rule->last_bytes;
648 *last_use = rule->last_used;
649
650 rule->last_bytes = current_bytes;
651 rule->last_packets = current_packets;
652
653 return 0;
654}
655
22a67766
JP
656#define MLXSW_SP_KDVL_ACT_EXT_SIZE 1
657
658static int mlxsw_sp_act_kvdl_set_add(void *priv, u32 *p_kvdl_index,
659 char *enc_actions, bool is_first)
660{
661 struct mlxsw_sp *mlxsw_sp = priv;
662 char pefa_pl[MLXSW_REG_PEFA_LEN];
663 u32 kvdl_index;
22a67766
JP
664 int err;
665
666 /* The first action set of a TCAM entry is stored directly in TCAM,
667 * not KVD linear area.
668 */
669 if (is_first)
670 return 0;
671
13124443
AS
672 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KDVL_ACT_EXT_SIZE,
673 &kvdl_index);
674 if (err)
675 return err;
22a67766
JP
676 mlxsw_reg_pefa_pack(pefa_pl, kvdl_index, enc_actions);
677 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pefa), pefa_pl);
678 if (err)
679 goto err_pefa_write;
680 *p_kvdl_index = kvdl_index;
681 return 0;
682
683err_pefa_write:
684 mlxsw_sp_kvdl_free(mlxsw_sp, kvdl_index);
685 return err;
686}
687
688static void mlxsw_sp_act_kvdl_set_del(void *priv, u32 kvdl_index,
689 bool is_first)
690{
691 struct mlxsw_sp *mlxsw_sp = priv;
692
693 if (is_first)
694 return;
695 mlxsw_sp_kvdl_free(mlxsw_sp, kvdl_index);
696}
697
698static int mlxsw_sp_act_kvdl_fwd_entry_add(void *priv, u32 *p_kvdl_index,
699 u8 local_port)
700{
701 struct mlxsw_sp *mlxsw_sp = priv;
702 char ppbs_pl[MLXSW_REG_PPBS_LEN];
703 u32 kvdl_index;
22a67766
JP
704 int err;
705
13124443
AS
706 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, 1, &kvdl_index);
707 if (err)
708 return err;
22a67766
JP
709 mlxsw_reg_ppbs_pack(ppbs_pl, kvdl_index, local_port);
710 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppbs), ppbs_pl);
711 if (err)
712 goto err_ppbs_write;
713 *p_kvdl_index = kvdl_index;
714 return 0;
715
716err_ppbs_write:
717 mlxsw_sp_kvdl_free(mlxsw_sp, kvdl_index);
718 return err;
719}
720
721static void mlxsw_sp_act_kvdl_fwd_entry_del(void *priv, u32 kvdl_index)
722{
723 struct mlxsw_sp *mlxsw_sp = priv;
724
725 mlxsw_sp_kvdl_free(mlxsw_sp, kvdl_index);
726}
727
728static const struct mlxsw_afa_ops mlxsw_sp_act_afa_ops = {
729 .kvdl_set_add = mlxsw_sp_act_kvdl_set_add,
730 .kvdl_set_del = mlxsw_sp_act_kvdl_set_del,
731 .kvdl_fwd_entry_add = mlxsw_sp_act_kvdl_fwd_entry_add,
732 .kvdl_fwd_entry_del = mlxsw_sp_act_kvdl_fwd_entry_del,
733};
734
735int mlxsw_sp_acl_init(struct mlxsw_sp *mlxsw_sp)
736{
737 const struct mlxsw_sp_acl_ops *acl_ops = &mlxsw_sp_acl_tcam_ops;
a1107487 738 struct mlxsw_sp_fid *fid;
22a67766
JP
739 struct mlxsw_sp_acl *acl;
740 int err;
741
742 acl = kzalloc(sizeof(*acl) + acl_ops->priv_size, GFP_KERNEL);
743 if (!acl)
744 return -ENOMEM;
745 mlxsw_sp->acl = acl;
446a1541 746 acl->mlxsw_sp = mlxsw_sp;
22a67766
JP
747 acl->afk = mlxsw_afk_create(MLXSW_CORE_RES_GET(mlxsw_sp->core,
748 ACL_FLEX_KEYS),
749 mlxsw_sp_afk_blocks,
750 MLXSW_SP_AFK_BLOCKS_COUNT);
751 if (!acl->afk) {
752 err = -ENOMEM;
753 goto err_afk_create;
754 }
755
756 acl->afa = mlxsw_afa_create(MLXSW_CORE_RES_GET(mlxsw_sp->core,
757 ACL_ACTIONS_PER_SET),
758 &mlxsw_sp_act_afa_ops, mlxsw_sp);
759 if (IS_ERR(acl->afa)) {
760 err = PTR_ERR(acl->afa);
761 goto err_afa_create;
762 }
763
764 err = rhashtable_init(&acl->ruleset_ht,
765 &mlxsw_sp_acl_ruleset_ht_params);
766 if (err)
767 goto err_rhashtable_init;
768
a1107487
IS
769 fid = mlxsw_sp_fid_dummy_get(mlxsw_sp);
770 if (IS_ERR(fid)) {
771 err = PTR_ERR(fid);
772 goto err_fid_get;
773 }
774 acl->dummy_fid = fid;
775
096e914f 776 INIT_LIST_HEAD(&acl->rules);
22a67766
JP
777 err = acl_ops->init(mlxsw_sp, acl->priv);
778 if (err)
779 goto err_acl_ops_init;
780
781 acl->ops = acl_ops;
446a1541
AS
782
783 /* Create the delayed work for the rule activity_update */
784 INIT_DELAYED_WORK(&acl->rule_activity_update.dw,
785 mlxsw_sp_acl_rul_activity_update_work);
786 acl->rule_activity_update.interval = MLXSW_SP_ACL_RULE_ACTIVITY_UPDATE_PERIOD_MS;
787 mlxsw_core_schedule_dw(&acl->rule_activity_update.dw, 0);
22a67766
JP
788 return 0;
789
790err_acl_ops_init:
a1107487
IS
791 mlxsw_sp_fid_put(fid);
792err_fid_get:
22a67766
JP
793 rhashtable_destroy(&acl->ruleset_ht);
794err_rhashtable_init:
795 mlxsw_afa_destroy(acl->afa);
796err_afa_create:
797 mlxsw_afk_destroy(acl->afk);
798err_afk_create:
799 kfree(acl);
800 return err;
801}
802
803void mlxsw_sp_acl_fini(struct mlxsw_sp *mlxsw_sp)
804{
805 struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
806 const struct mlxsw_sp_acl_ops *acl_ops = acl->ops;
807
446a1541 808 cancel_delayed_work_sync(&mlxsw_sp->acl->rule_activity_update.dw);
22a67766 809 acl_ops->fini(mlxsw_sp, acl->priv);
096e914f 810 WARN_ON(!list_empty(&acl->rules));
a1107487 811 mlxsw_sp_fid_put(acl->dummy_fid);
22a67766
JP
812 rhashtable_destroy(&acl->ruleset_ht);
813 mlxsw_afa_destroy(acl->afa);
814 mlxsw_afk_destroy(acl->afk);
815 kfree(acl);
816}