1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
43#include "reg.h"
44#include "core.h"
45#include "resources.h"
46#include "spectrum.h"
47#include "core_acl_flex_keys.h"
48#include "core_acl_flex_actions.h"
49#include "spectrum_acl_flex_keys.h"
50
51struct mlxsw_sp_acl {
52 struct mlxsw_afk *afk;
53 struct mlxsw_afa *afa;
54 const struct mlxsw_sp_acl_ops *ops;
55 struct rhashtable ruleset_ht;
56 unsigned long priv[0];
57
58};
59
60struct mlxsw_afk *mlxsw_sp_acl_afk(struct mlxsw_sp_acl *acl)
61{
62 return acl->afk;
63}
64
65struct mlxsw_sp_acl_ruleset_ht_key {
66 struct net_device *dev;
67 bool ingress;
68 const struct mlxsw_sp_acl_profile_ops *ops;
69};
70
71struct mlxsw_sp_acl_ruleset {
72 struct rhash_head ht_node;
73 struct mlxsw_sp_acl_ruleset_ht_key ht_key;
74 struct rhashtable rule_ht;
75 unsigned int ref_count;
76 unsigned long priv[0];
77
78};
79
80struct mlxsw_sp_acl_rule {
81 struct rhash_head ht_node;
82 unsigned long cookie;
83 struct mlxsw_sp_acl_ruleset *ruleset;
84 struct mlxsw_sp_acl_rule_info *rulei;
85 unsigned long priv[0];
86
87};
88
89static const struct rhashtable_params mlxsw_sp_acl_ruleset_ht_params = {
90 .key_len = sizeof(struct mlxsw_sp_acl_ruleset_ht_key),
91 .key_offset = offsetof(struct mlxsw_sp_acl_ruleset, ht_key),
92 .head_offset = offsetof(struct mlxsw_sp_acl_ruleset, ht_node),
93 .automatic_shrinking = true,
94};
95
96static const struct rhashtable_params mlxsw_sp_acl_rule_ht_params = {
97 .key_len = sizeof(unsigned long),
98 .key_offset = offsetof(struct mlxsw_sp_acl_rule, cookie),
99 .head_offset = offsetof(struct mlxsw_sp_acl_rule, ht_node),
100 .automatic_shrinking = true,
101};
102
103static struct mlxsw_sp_acl_ruleset *
104mlxsw_sp_acl_ruleset_create(struct mlxsw_sp *mlxsw_sp,
105 const struct mlxsw_sp_acl_profile_ops *ops)
106{
107 struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
108 struct mlxsw_sp_acl_ruleset *ruleset;
109 size_t alloc_size;
110 int err;
111
112 alloc_size = sizeof(*ruleset) + ops->ruleset_priv_size;
113 ruleset = kzalloc(alloc_size, GFP_KERNEL);
114 if (!ruleset)
115 return ERR_PTR(-ENOMEM);
116 ruleset->ref_count = 1;
117 ruleset->ht_key.ops = ops;
118
119 err = rhashtable_init(&ruleset->rule_ht, &mlxsw_sp_acl_rule_ht_params);
120 if (err)
121 goto err_rhashtable_init;
122
123 err = ops->ruleset_add(mlxsw_sp, acl->priv, ruleset->priv);
124 if (err)
125 goto err_ops_ruleset_add;
126
127 return ruleset;
128
129err_ops_ruleset_add:
130 rhashtable_destroy(&ruleset->rule_ht);
131err_rhashtable_init:
132 kfree(ruleset);
133 return ERR_PTR(err);
134}
135
136static void mlxsw_sp_acl_ruleset_destroy(struct mlxsw_sp *mlxsw_sp,
137 struct mlxsw_sp_acl_ruleset *ruleset)
138{
139 const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
140
141 ops->ruleset_del(mlxsw_sp, ruleset->priv);
142 rhashtable_destroy(&ruleset->rule_ht);
143 kfree(ruleset);
144}
145
146static int mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp,
147 struct mlxsw_sp_acl_ruleset *ruleset,
148 struct net_device *dev, bool ingress)
149{
150 const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
151 struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
152 int err;
153
154 ruleset->ht_key.dev = dev;
155 ruleset->ht_key.ingress = ingress;
156 err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node,
157 mlxsw_sp_acl_ruleset_ht_params);
158 if (err)
159 return err;
160 err = ops->ruleset_bind(mlxsw_sp, ruleset->priv, dev, ingress);
161 if (err)
162 goto err_ops_ruleset_bind;
163 return 0;
164
165err_ops_ruleset_bind:
166 rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node,
167 mlxsw_sp_acl_ruleset_ht_params);
168 return err;
169}
170
171static void mlxsw_sp_acl_ruleset_unbind(struct mlxsw_sp *mlxsw_sp,
172 struct mlxsw_sp_acl_ruleset *ruleset)
173{
174 const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
175 struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
176
177 ops->ruleset_unbind(mlxsw_sp, ruleset->priv);
178 rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node,
179 mlxsw_sp_acl_ruleset_ht_params);
180}
181
182static void mlxsw_sp_acl_ruleset_ref_inc(struct mlxsw_sp_acl_ruleset *ruleset)
183{
184 ruleset->ref_count++;
185}
186
187static void mlxsw_sp_acl_ruleset_ref_dec(struct mlxsw_sp *mlxsw_sp,
188 struct mlxsw_sp_acl_ruleset *ruleset)
189{
190 if (--ruleset->ref_count)
191 return;
192 mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, ruleset);
193 mlxsw_sp_acl_ruleset_destroy(mlxsw_sp, ruleset);
194}
195
196struct mlxsw_sp_acl_ruleset *
197mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp,
198 struct net_device *dev, bool ingress,
199 enum mlxsw_sp_acl_profile profile)
200{
201 const struct mlxsw_sp_acl_profile_ops *ops;
202 struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
203 struct mlxsw_sp_acl_ruleset_ht_key ht_key;
204 struct mlxsw_sp_acl_ruleset *ruleset;
205 int err;
206
207 ops = acl->ops->profile_ops(mlxsw_sp, profile);
208 if (!ops)
209 return ERR_PTR(-EINVAL);
210
211 memset(&ht_key, 0, sizeof(ht_key));
212 ht_key.dev = dev;
213 ht_key.ingress = ingress;
214 ht_key.ops = ops;
215 ruleset = rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key,
216 mlxsw_sp_acl_ruleset_ht_params);
217 if (ruleset) {
218 mlxsw_sp_acl_ruleset_ref_inc(ruleset);
219 return ruleset;
220 }
221 ruleset = mlxsw_sp_acl_ruleset_create(mlxsw_sp, ops);
222 if (IS_ERR(ruleset))
223 return ruleset;
224 err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, ruleset, dev, ingress);
225 if (err)
226 goto err_ruleset_bind;
227 return ruleset;
228
229err_ruleset_bind:
230 mlxsw_sp_acl_ruleset_destroy(mlxsw_sp, ruleset);
231 return ERR_PTR(err);
232}
233
234void mlxsw_sp_acl_ruleset_put(struct mlxsw_sp *mlxsw_sp,
235 struct mlxsw_sp_acl_ruleset *ruleset)
236{
237 mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset);
238}
239
240struct mlxsw_sp_acl_rule_info *
241mlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl)
242{
243 struct mlxsw_sp_acl_rule_info *rulei;
244 int err;
245
246 rulei = kzalloc(sizeof(*rulei), GFP_KERNEL);
247 if (!rulei)
248 return NULL;
249 rulei->act_block = mlxsw_afa_block_create(acl->afa);
250 if (IS_ERR(rulei->act_block)) {
251 err = PTR_ERR(rulei->act_block);
252 goto err_afa_block_create;
253 }
254 return rulei;
255
256err_afa_block_create:
257 kfree(rulei);
258 return ERR_PTR(err);
259}
260
261void mlxsw_sp_acl_rulei_destroy(struct mlxsw_sp_acl_rule_info *rulei)
262{
263 mlxsw_afa_block_destroy(rulei->act_block);
264 kfree(rulei);
265}
266
267int mlxsw_sp_acl_rulei_commit(struct mlxsw_sp_acl_rule_info *rulei)
268{
269 return mlxsw_afa_block_commit(rulei->act_block);
270}
271
272void mlxsw_sp_acl_rulei_priority(struct mlxsw_sp_acl_rule_info *rulei,
273 unsigned int priority)
274{
275 rulei->priority = priority;
276}
277
278void mlxsw_sp_acl_rulei_keymask_u32(struct mlxsw_sp_acl_rule_info *rulei,
279 enum mlxsw_afk_element element,
280 u32 key_value, u32 mask_value)
281{
282 mlxsw_afk_values_add_u32(&rulei->values, element,
283 key_value, mask_value);
284}
285
286void mlxsw_sp_acl_rulei_keymask_buf(struct mlxsw_sp_acl_rule_info *rulei,
287 enum mlxsw_afk_element element,
288 const char *key_value,
289 const char *mask_value, unsigned int len)
290{
291 mlxsw_afk_values_add_buf(&rulei->values, element,
292 key_value, mask_value, len);
293}
294
295void mlxsw_sp_acl_rulei_act_continue(struct mlxsw_sp_acl_rule_info *rulei)
296{
297 mlxsw_afa_block_continue(rulei->act_block);
298}
299
300void mlxsw_sp_acl_rulei_act_jump(struct mlxsw_sp_acl_rule_info *rulei,
301 u16 group_id)
302{
303 mlxsw_afa_block_jump(rulei->act_block, group_id);
304}
305
306int mlxsw_sp_acl_rulei_act_drop(struct mlxsw_sp_acl_rule_info *rulei)
307{
308 return mlxsw_afa_block_append_drop(rulei->act_block);
309}
310
311int mlxsw_sp_acl_rulei_act_fwd(struct mlxsw_sp *mlxsw_sp,
312 struct mlxsw_sp_acl_rule_info *rulei,
313 struct net_device *out_dev)
314{
315 struct mlxsw_sp_port *mlxsw_sp_port;
316 u8 local_port;
317 bool in_port;
318
319 if (out_dev) {
320 if (!mlxsw_sp_port_dev_check(out_dev))
321 return -EINVAL;
322 mlxsw_sp_port = netdev_priv(out_dev);
323 if (mlxsw_sp_port->mlxsw_sp != mlxsw_sp)
324 return -EINVAL;
325 local_port = mlxsw_sp_port->local_port;
326 in_port = false;
327 } else {
328
329
330
331 local_port = 0;
332 in_port = true;
333 }
334 return mlxsw_afa_block_append_fwd(rulei->act_block,
335 local_port, in_port);
336}
337
338struct mlxsw_sp_acl_rule *
339mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp,
340 struct mlxsw_sp_acl_ruleset *ruleset,
341 unsigned long cookie)
342{
343 const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
344 struct mlxsw_sp_acl_rule *rule;
345 int err;
346
347 mlxsw_sp_acl_ruleset_ref_inc(ruleset);
348 rule = kzalloc(sizeof(*rule) + ops->rule_priv_size, GFP_KERNEL);
349 if (!rule) {
350 err = -ENOMEM;
351 goto err_alloc;
352 }
353 rule->cookie = cookie;
354 rule->ruleset = ruleset;
355
356 rule->rulei = mlxsw_sp_acl_rulei_create(mlxsw_sp->acl);
357 if (IS_ERR(rule->rulei)) {
358 err = PTR_ERR(rule->rulei);
359 goto err_rulei_create;
360 }
361 return rule;
362
363err_rulei_create:
364 kfree(rule);
365err_alloc:
366 mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset);
367 return ERR_PTR(err);
368}
369
370void mlxsw_sp_acl_rule_destroy(struct mlxsw_sp *mlxsw_sp,
371 struct mlxsw_sp_acl_rule *rule)
372{
373 struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
374
375 mlxsw_sp_acl_rulei_destroy(rule->rulei);
376 kfree(rule);
377 mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset);
378}
379
380int mlxsw_sp_acl_rule_add(struct mlxsw_sp *mlxsw_sp,
381 struct mlxsw_sp_acl_rule *rule)
382{
383 struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
384 const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
385 int err;
386
387 err = ops->rule_add(mlxsw_sp, ruleset->priv, rule->priv, rule->rulei);
388 if (err)
389 return err;
390
391 err = rhashtable_insert_fast(&ruleset->rule_ht, &rule->ht_node,
392 mlxsw_sp_acl_rule_ht_params);
393 if (err)
394 goto err_rhashtable_insert;
395
396 return 0;
397
398err_rhashtable_insert:
399 ops->rule_del(mlxsw_sp, rule->priv);
400 return err;
401}
402
403void mlxsw_sp_acl_rule_del(struct mlxsw_sp *mlxsw_sp,
404 struct mlxsw_sp_acl_rule *rule)
405{
406 struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
407 const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
408
409 rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
410 mlxsw_sp_acl_rule_ht_params);
411 ops->rule_del(mlxsw_sp, rule->priv);
412}
413
414struct mlxsw_sp_acl_rule *
415mlxsw_sp_acl_rule_lookup(struct mlxsw_sp *mlxsw_sp,
416 struct mlxsw_sp_acl_ruleset *ruleset,
417 unsigned long cookie)
418{
419 return rhashtable_lookup_fast(&ruleset->rule_ht, &cookie,
420 mlxsw_sp_acl_rule_ht_params);
421}
422
423struct mlxsw_sp_acl_rule_info *
424mlxsw_sp_acl_rule_rulei(struct mlxsw_sp_acl_rule *rule)
425{
426 return rule->rulei;
427}
428
429#define MLXSW_SP_KDVL_ACT_EXT_SIZE 1
430
431static int mlxsw_sp_act_kvdl_set_add(void *priv, u32 *p_kvdl_index,
432 char *enc_actions, bool is_first)
433{
434 struct mlxsw_sp *mlxsw_sp = priv;
435 char pefa_pl[MLXSW_REG_PEFA_LEN];
436 u32 kvdl_index;
437 int ret;
438 int err;
439
440
441
442
443 if (is_first)
444 return 0;
445
446 ret = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KDVL_ACT_EXT_SIZE);
447 if (ret < 0)
448 return ret;
449 kvdl_index = ret;
450 mlxsw_reg_pefa_pack(pefa_pl, kvdl_index, enc_actions);
451 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pefa), pefa_pl);
452 if (err)
453 goto err_pefa_write;
454 *p_kvdl_index = kvdl_index;
455 return 0;
456
457err_pefa_write:
458 mlxsw_sp_kvdl_free(mlxsw_sp, kvdl_index);
459 return err;
460}
461
462static void mlxsw_sp_act_kvdl_set_del(void *priv, u32 kvdl_index,
463 bool is_first)
464{
465 struct mlxsw_sp *mlxsw_sp = priv;
466
467 if (is_first)
468 return;
469 mlxsw_sp_kvdl_free(mlxsw_sp, kvdl_index);
470}
471
472static int mlxsw_sp_act_kvdl_fwd_entry_add(void *priv, u32 *p_kvdl_index,
473 u8 local_port)
474{
475 struct mlxsw_sp *mlxsw_sp = priv;
476 char ppbs_pl[MLXSW_REG_PPBS_LEN];
477 u32 kvdl_index;
478 int ret;
479 int err;
480
481 ret = mlxsw_sp_kvdl_alloc(mlxsw_sp, 1);
482 if (ret < 0)
483 return ret;
484 kvdl_index = ret;
485 mlxsw_reg_ppbs_pack(ppbs_pl, kvdl_index, local_port);
486 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppbs), ppbs_pl);
487 if (err)
488 goto err_ppbs_write;
489 *p_kvdl_index = kvdl_index;
490 return 0;
491
492err_ppbs_write:
493 mlxsw_sp_kvdl_free(mlxsw_sp, kvdl_index);
494 return err;
495}
496
497static void mlxsw_sp_act_kvdl_fwd_entry_del(void *priv, u32 kvdl_index)
498{
499 struct mlxsw_sp *mlxsw_sp = priv;
500
501 mlxsw_sp_kvdl_free(mlxsw_sp, kvdl_index);
502}
503
504static const struct mlxsw_afa_ops mlxsw_sp_act_afa_ops = {
505 .kvdl_set_add = mlxsw_sp_act_kvdl_set_add,
506 .kvdl_set_del = mlxsw_sp_act_kvdl_set_del,
507 .kvdl_fwd_entry_add = mlxsw_sp_act_kvdl_fwd_entry_add,
508 .kvdl_fwd_entry_del = mlxsw_sp_act_kvdl_fwd_entry_del,
509};
510
511int mlxsw_sp_acl_init(struct mlxsw_sp *mlxsw_sp)
512{
513 const struct mlxsw_sp_acl_ops *acl_ops = &mlxsw_sp_acl_tcam_ops;
514 struct mlxsw_sp_acl *acl;
515 int err;
516
517 acl = kzalloc(sizeof(*acl) + acl_ops->priv_size, GFP_KERNEL);
518 if (!acl)
519 return -ENOMEM;
520 mlxsw_sp->acl = acl;
521
522 acl->afk = mlxsw_afk_create(MLXSW_CORE_RES_GET(mlxsw_sp->core,
523 ACL_FLEX_KEYS),
524 mlxsw_sp_afk_blocks,
525 MLXSW_SP_AFK_BLOCKS_COUNT);
526 if (!acl->afk) {
527 err = -ENOMEM;
528 goto err_afk_create;
529 }
530
531 acl->afa = mlxsw_afa_create(MLXSW_CORE_RES_GET(mlxsw_sp->core,
532 ACL_ACTIONS_PER_SET),
533 &mlxsw_sp_act_afa_ops, mlxsw_sp);
534 if (IS_ERR(acl->afa)) {
535 err = PTR_ERR(acl->afa);
536 goto err_afa_create;
537 }
538
539 err = rhashtable_init(&acl->ruleset_ht,
540 &mlxsw_sp_acl_ruleset_ht_params);
541 if (err)
542 goto err_rhashtable_init;
543
544 err = acl_ops->init(mlxsw_sp, acl->priv);
545 if (err)
546 goto err_acl_ops_init;
547
548 acl->ops = acl_ops;
549 return 0;
550
551err_acl_ops_init:
552 rhashtable_destroy(&acl->ruleset_ht);
553err_rhashtable_init:
554 mlxsw_afa_destroy(acl->afa);
555err_afa_create:
556 mlxsw_afk_destroy(acl->afk);
557err_afk_create:
558 kfree(acl);
559 return err;
560}
561
562void mlxsw_sp_acl_fini(struct mlxsw_sp *mlxsw_sp)
563{
564 struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
565 const struct mlxsw_sp_acl_ops *acl_ops = acl->ops;
566
567 acl_ops->fini(mlxsw_sp, acl->priv);
568 rhashtable_destroy(&acl->ruleset_ht);
569 mlxsw_afa_destroy(acl->afa);
570 mlxsw_afk_destroy(acl->afk);
571 kfree(acl);
572}
573