1
2
3
4#include <linux/kernel.h>
5#include <linux/slab.h>
6#include <linux/errno.h>
7#include <linux/bitops.h>
8#include <linux/list.h>
9#include <linux/rhashtable.h>
10#include <linux/netdevice.h>
11
12#include "reg.h"
13#include "core.h"
14#include "resources.h"
15#include "spectrum.h"
16#include "spectrum_acl_tcam.h"
17#include "core_acl_flex_keys.h"
18
19size_t mlxsw_sp_acl_tcam_priv_size(struct mlxsw_sp *mlxsw_sp)
20{
21 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
22
23 return ops->priv_size;
24}
25
26int mlxsw_sp_acl_tcam_init(struct mlxsw_sp *mlxsw_sp,
27 struct mlxsw_sp_acl_tcam *tcam)
28{
29 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
30 u64 max_tcam_regions;
31 u64 max_regions;
32 u64 max_groups;
33 size_t alloc_size;
34 int err;
35
36 max_tcam_regions = MLXSW_CORE_RES_GET(mlxsw_sp->core,
37 ACL_MAX_TCAM_REGIONS);
38 max_regions = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_REGIONS);
39
40
41 if (max_tcam_regions < max_regions)
42 max_regions = max_tcam_regions;
43
44 alloc_size = sizeof(tcam->used_regions[0]) * BITS_TO_LONGS(max_regions);
45 tcam->used_regions = kzalloc(alloc_size, GFP_KERNEL);
46 if (!tcam->used_regions)
47 return -ENOMEM;
48 tcam->max_regions = max_regions;
49
50 max_groups = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_GROUPS);
51 alloc_size = sizeof(tcam->used_groups[0]) * BITS_TO_LONGS(max_groups);
52 tcam->used_groups = kzalloc(alloc_size, GFP_KERNEL);
53 if (!tcam->used_groups) {
54 err = -ENOMEM;
55 goto err_alloc_used_groups;
56 }
57 tcam->max_groups = max_groups;
58 tcam->max_group_size = MLXSW_CORE_RES_GET(mlxsw_sp->core,
59 ACL_MAX_GROUP_SIZE);
60
61 err = ops->init(mlxsw_sp, tcam->priv, tcam);
62 if (err)
63 goto err_tcam_init;
64
65 return 0;
66
67err_tcam_init:
68 kfree(tcam->used_groups);
69err_alloc_used_groups:
70 kfree(tcam->used_regions);
71 return err;
72}
73
74void mlxsw_sp_acl_tcam_fini(struct mlxsw_sp *mlxsw_sp,
75 struct mlxsw_sp_acl_tcam *tcam)
76{
77 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
78
79 ops->fini(mlxsw_sp, tcam->priv);
80 kfree(tcam->used_groups);
81 kfree(tcam->used_regions);
82}
83
84int mlxsw_sp_acl_tcam_priority_get(struct mlxsw_sp *mlxsw_sp,
85 struct mlxsw_sp_acl_rule_info *rulei,
86 u32 *priority, bool fillup_priority)
87{
88 u64 max_priority;
89
90 if (!fillup_priority) {
91 *priority = 0;
92 return 0;
93 }
94
95 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, KVD_SIZE))
96 return -EIO;
97
98 max_priority = MLXSW_CORE_RES_GET(mlxsw_sp->core, KVD_SIZE);
99 if (rulei->priority > max_priority)
100 return -EINVAL;
101
102
103 *priority = max_priority - rulei->priority;
104 return 0;
105}
106
107static int mlxsw_sp_acl_tcam_region_id_get(struct mlxsw_sp_acl_tcam *tcam,
108 u16 *p_id)
109{
110 u16 id;
111
112 id = find_first_zero_bit(tcam->used_regions, tcam->max_regions);
113 if (id < tcam->max_regions) {
114 __set_bit(id, tcam->used_regions);
115 *p_id = id;
116 return 0;
117 }
118 return -ENOBUFS;
119}
120
121static void mlxsw_sp_acl_tcam_region_id_put(struct mlxsw_sp_acl_tcam *tcam,
122 u16 id)
123{
124 __clear_bit(id, tcam->used_regions);
125}
126
127static int mlxsw_sp_acl_tcam_group_id_get(struct mlxsw_sp_acl_tcam *tcam,
128 u16 *p_id)
129{
130 u16 id;
131
132 id = find_first_zero_bit(tcam->used_groups, tcam->max_groups);
133 if (id < tcam->max_groups) {
134 __set_bit(id, tcam->used_groups);
135 *p_id = id;
136 return 0;
137 }
138 return -ENOBUFS;
139}
140
141static void mlxsw_sp_acl_tcam_group_id_put(struct mlxsw_sp_acl_tcam *tcam,
142 u16 id)
143{
144 __clear_bit(id, tcam->used_groups);
145}
146
147struct mlxsw_sp_acl_tcam_pattern {
148 const enum mlxsw_afk_element *elements;
149 unsigned int elements_count;
150};
151
152struct mlxsw_sp_acl_tcam_group {
153 struct mlxsw_sp_acl_tcam *tcam;
154 u16 id;
155 struct list_head region_list;
156 unsigned int region_count;
157 struct rhashtable chunk_ht;
158 struct mlxsw_sp_acl_tcam_group_ops *ops;
159 const struct mlxsw_sp_acl_tcam_pattern *patterns;
160 unsigned int patterns_count;
161 bool tmplt_elusage_set;
162 struct mlxsw_afk_element_usage tmplt_elusage;
163};
164
165struct mlxsw_sp_acl_tcam_chunk {
166 struct list_head list;
167 struct rhash_head ht_node;
168 unsigned int priority;
169 struct mlxsw_sp_acl_tcam_group *group;
170 struct mlxsw_sp_acl_tcam_region *region;
171 unsigned int ref_count;
172 unsigned long priv[0];
173
174};
175
176struct mlxsw_sp_acl_tcam_entry {
177 struct mlxsw_sp_acl_tcam_chunk *chunk;
178 unsigned long priv[0];
179
180};
181
182static const struct rhashtable_params mlxsw_sp_acl_tcam_chunk_ht_params = {
183 .key_len = sizeof(unsigned int),
184 .key_offset = offsetof(struct mlxsw_sp_acl_tcam_chunk, priority),
185 .head_offset = offsetof(struct mlxsw_sp_acl_tcam_chunk, ht_node),
186 .automatic_shrinking = true,
187};
188
189static int mlxsw_sp_acl_tcam_group_update(struct mlxsw_sp *mlxsw_sp,
190 struct mlxsw_sp_acl_tcam_group *group)
191{
192 struct mlxsw_sp_acl_tcam_region *region;
193 char pagt_pl[MLXSW_REG_PAGT_LEN];
194 int acl_index = 0;
195
196 mlxsw_reg_pagt_pack(pagt_pl, group->id);
197 list_for_each_entry(region, &group->region_list, list)
198 mlxsw_reg_pagt_acl_id_pack(pagt_pl, acl_index++, region->id);
199 mlxsw_reg_pagt_size_set(pagt_pl, acl_index);
200 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pagt), pagt_pl);
201}
202
203static int
204mlxsw_sp_acl_tcam_group_add(struct mlxsw_sp *mlxsw_sp,
205 struct mlxsw_sp_acl_tcam *tcam,
206 struct mlxsw_sp_acl_tcam_group *group,
207 const struct mlxsw_sp_acl_tcam_pattern *patterns,
208 unsigned int patterns_count,
209 struct mlxsw_afk_element_usage *tmplt_elusage)
210{
211 int err;
212
213 group->tcam = tcam;
214 group->patterns = patterns;
215 group->patterns_count = patterns_count;
216 if (tmplt_elusage) {
217 group->tmplt_elusage_set = true;
218 memcpy(&group->tmplt_elusage, tmplt_elusage,
219 sizeof(group->tmplt_elusage));
220 }
221 INIT_LIST_HEAD(&group->region_list);
222 err = mlxsw_sp_acl_tcam_group_id_get(tcam, &group->id);
223 if (err)
224 return err;
225
226 err = rhashtable_init(&group->chunk_ht,
227 &mlxsw_sp_acl_tcam_chunk_ht_params);
228 if (err)
229 goto err_rhashtable_init;
230
231 return 0;
232
233err_rhashtable_init:
234 mlxsw_sp_acl_tcam_group_id_put(tcam, group->id);
235 return err;
236}
237
238static void mlxsw_sp_acl_tcam_group_del(struct mlxsw_sp *mlxsw_sp,
239 struct mlxsw_sp_acl_tcam_group *group)
240{
241 struct mlxsw_sp_acl_tcam *tcam = group->tcam;
242
243 rhashtable_destroy(&group->chunk_ht);
244 mlxsw_sp_acl_tcam_group_id_put(tcam, group->id);
245 WARN_ON(!list_empty(&group->region_list));
246}
247
248static int
249mlxsw_sp_acl_tcam_group_bind(struct mlxsw_sp *mlxsw_sp,
250 struct mlxsw_sp_acl_tcam_group *group,
251 struct mlxsw_sp_port *mlxsw_sp_port,
252 bool ingress)
253{
254 char ppbt_pl[MLXSW_REG_PPBT_LEN];
255
256 mlxsw_reg_ppbt_pack(ppbt_pl, ingress ? MLXSW_REG_PXBT_E_IACL :
257 MLXSW_REG_PXBT_E_EACL,
258 MLXSW_REG_PXBT_OP_BIND, mlxsw_sp_port->local_port,
259 group->id);
260 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppbt), ppbt_pl);
261}
262
263static void
264mlxsw_sp_acl_tcam_group_unbind(struct mlxsw_sp *mlxsw_sp,
265 struct mlxsw_sp_acl_tcam_group *group,
266 struct mlxsw_sp_port *mlxsw_sp_port,
267 bool ingress)
268{
269 char ppbt_pl[MLXSW_REG_PPBT_LEN];
270
271 mlxsw_reg_ppbt_pack(ppbt_pl, ingress ? MLXSW_REG_PXBT_E_IACL :
272 MLXSW_REG_PXBT_E_EACL,
273 MLXSW_REG_PXBT_OP_UNBIND, mlxsw_sp_port->local_port,
274 group->id);
275 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppbt), ppbt_pl);
276}
277
278static u16
279mlxsw_sp_acl_tcam_group_id(struct mlxsw_sp_acl_tcam_group *group)
280{
281 return group->id;
282}
283
284static unsigned int
285mlxsw_sp_acl_tcam_region_prio(struct mlxsw_sp_acl_tcam_region *region)
286{
287 struct mlxsw_sp_acl_tcam_chunk *chunk;
288
289 if (list_empty(®ion->chunk_list))
290 return 0;
291
292 chunk = list_first_entry(®ion->chunk_list, typeof(*chunk), list);
293 return chunk->priority;
294}
295
296static unsigned int
297mlxsw_sp_acl_tcam_region_max_prio(struct mlxsw_sp_acl_tcam_region *region)
298{
299 struct mlxsw_sp_acl_tcam_chunk *chunk;
300
301 if (list_empty(®ion->chunk_list))
302 return 0;
303 chunk = list_last_entry(®ion->chunk_list, typeof(*chunk), list);
304 return chunk->priority;
305}
306
307static void
308mlxsw_sp_acl_tcam_group_list_add(struct mlxsw_sp_acl_tcam_group *group,
309 struct mlxsw_sp_acl_tcam_region *region)
310{
311 struct mlxsw_sp_acl_tcam_region *region2;
312 struct list_head *pos;
313
314
315 list_for_each(pos, &group->region_list) {
316 region2 = list_entry(pos, typeof(*region2), list);
317 if (mlxsw_sp_acl_tcam_region_prio(region2) >
318 mlxsw_sp_acl_tcam_region_prio(region))
319 break;
320 }
321 list_add_tail(®ion->list, pos);
322 group->region_count++;
323}
324
325static void
326mlxsw_sp_acl_tcam_group_list_del(struct mlxsw_sp_acl_tcam_group *group,
327 struct mlxsw_sp_acl_tcam_region *region)
328{
329 group->region_count--;
330 list_del(®ion->list);
331}
332
333static int
334mlxsw_sp_acl_tcam_group_region_attach(struct mlxsw_sp *mlxsw_sp,
335 struct mlxsw_sp_acl_tcam_group *group,
336 struct mlxsw_sp_acl_tcam_region *region)
337{
338 int err;
339
340 if (group->region_count == group->tcam->max_group_size)
341 return -ENOBUFS;
342
343 mlxsw_sp_acl_tcam_group_list_add(group, region);
344
345 err = mlxsw_sp_acl_tcam_group_update(mlxsw_sp, group);
346 if (err)
347 goto err_group_update;
348 region->group = group;
349
350 return 0;
351
352err_group_update:
353 mlxsw_sp_acl_tcam_group_list_del(group, region);
354 mlxsw_sp_acl_tcam_group_update(mlxsw_sp, group);
355 return err;
356}
357
358static void
359mlxsw_sp_acl_tcam_group_region_detach(struct mlxsw_sp *mlxsw_sp,
360 struct mlxsw_sp_acl_tcam_region *region)
361{
362 struct mlxsw_sp_acl_tcam_group *group = region->group;
363
364 mlxsw_sp_acl_tcam_group_list_del(group, region);
365 mlxsw_sp_acl_tcam_group_update(mlxsw_sp, group);
366}
367
368static struct mlxsw_sp_acl_tcam_region *
369mlxsw_sp_acl_tcam_group_region_find(struct mlxsw_sp_acl_tcam_group *group,
370 unsigned int priority,
371 struct mlxsw_afk_element_usage *elusage,
372 bool *p_need_split)
373{
374 struct mlxsw_sp_acl_tcam_region *region, *region2;
375 struct list_head *pos;
376 bool issubset;
377
378 list_for_each(pos, &group->region_list) {
379 region = list_entry(pos, typeof(*region), list);
380
381
382
383
384 if (pos->next != &group->region_list) {
385 region2 = list_entry(pos->next, typeof(*region2), list);
386 if (priority >= mlxsw_sp_acl_tcam_region_prio(region2))
387 continue;
388 }
389
390 issubset = mlxsw_afk_key_info_subset(region->key_info, elusage);
391
392
393
394
395
396
397 if (!issubset &&
398 priority < mlxsw_sp_acl_tcam_region_prio(region))
399 return NULL;
400
401
402
403
404
405
406
407 if (!issubset &&
408 priority > mlxsw_sp_acl_tcam_region_max_prio(region))
409 continue;
410
411
412
413
414
415 *p_need_split = !issubset;
416 return region;
417 }
418 return NULL;
419}
420
421static void
422mlxsw_sp_acl_tcam_group_use_patterns(struct mlxsw_sp_acl_tcam_group *group,
423 struct mlxsw_afk_element_usage *elusage,
424 struct mlxsw_afk_element_usage *out)
425{
426 const struct mlxsw_sp_acl_tcam_pattern *pattern;
427 int i;
428
429
430
431
432 if (group->tmplt_elusage_set) {
433 memcpy(out, &group->tmplt_elusage, sizeof(*out));
434 WARN_ON(!mlxsw_afk_element_usage_subset(elusage, out));
435 return;
436 }
437
438 for (i = 0; i < group->patterns_count; i++) {
439 pattern = &group->patterns[i];
440 mlxsw_afk_element_usage_fill(out, pattern->elements,
441 pattern->elements_count);
442 if (mlxsw_afk_element_usage_subset(elusage, out))
443 return;
444 }
445 memcpy(out, elusage, sizeof(*out));
446}
447
448static int
449mlxsw_sp_acl_tcam_region_alloc(struct mlxsw_sp *mlxsw_sp,
450 struct mlxsw_sp_acl_tcam_region *region)
451{
452 struct mlxsw_afk_key_info *key_info = region->key_info;
453 char ptar_pl[MLXSW_REG_PTAR_LEN];
454 unsigned int encodings_count;
455 int i;
456 int err;
457
458 mlxsw_reg_ptar_pack(ptar_pl, MLXSW_REG_PTAR_OP_ALLOC,
459 region->key_type,
460 MLXSW_SP_ACL_TCAM_REGION_BASE_COUNT,
461 region->id, region->tcam_region_info);
462 encodings_count = mlxsw_afk_key_info_blocks_count_get(key_info);
463 for (i = 0; i < encodings_count; i++) {
464 u16 encoding;
465
466 encoding = mlxsw_afk_key_info_block_encoding_get(key_info, i);
467 mlxsw_reg_ptar_key_id_pack(ptar_pl, i, encoding);
468 }
469 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptar), ptar_pl);
470 if (err)
471 return err;
472 mlxsw_reg_ptar_unpack(ptar_pl, region->tcam_region_info);
473 return 0;
474}
475
476static void
477mlxsw_sp_acl_tcam_region_free(struct mlxsw_sp *mlxsw_sp,
478 struct mlxsw_sp_acl_tcam_region *region)
479{
480 char ptar_pl[MLXSW_REG_PTAR_LEN];
481
482 mlxsw_reg_ptar_pack(ptar_pl, MLXSW_REG_PTAR_OP_FREE,
483 region->key_type, 0, region->id,
484 region->tcam_region_info);
485 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptar), ptar_pl);
486}
487
488static int
489mlxsw_sp_acl_tcam_region_enable(struct mlxsw_sp *mlxsw_sp,
490 struct mlxsw_sp_acl_tcam_region *region)
491{
492 char pacl_pl[MLXSW_REG_PACL_LEN];
493
494 mlxsw_reg_pacl_pack(pacl_pl, region->id, true,
495 region->tcam_region_info);
496 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pacl), pacl_pl);
497}
498
499static void
500mlxsw_sp_acl_tcam_region_disable(struct mlxsw_sp *mlxsw_sp,
501 struct mlxsw_sp_acl_tcam_region *region)
502{
503 char pacl_pl[MLXSW_REG_PACL_LEN];
504
505 mlxsw_reg_pacl_pack(pacl_pl, region->id, false,
506 region->tcam_region_info);
507 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pacl), pacl_pl);
508}
509
510static struct mlxsw_sp_acl_tcam_region *
511mlxsw_sp_acl_tcam_region_create(struct mlxsw_sp *mlxsw_sp,
512 struct mlxsw_sp_acl_tcam *tcam,
513 struct mlxsw_afk_element_usage *elusage)
514{
515 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
516 struct mlxsw_afk *afk = mlxsw_sp_acl_afk(mlxsw_sp->acl);
517 struct mlxsw_sp_acl_tcam_region *region;
518 int err;
519
520 region = kzalloc(sizeof(*region) + ops->region_priv_size, GFP_KERNEL);
521 if (!region)
522 return ERR_PTR(-ENOMEM);
523 INIT_LIST_HEAD(®ion->chunk_list);
524 region->mlxsw_sp = mlxsw_sp;
525
526 region->key_info = mlxsw_afk_key_info_get(afk, elusage);
527 if (IS_ERR(region->key_info)) {
528 err = PTR_ERR(region->key_info);
529 goto err_key_info_get;
530 }
531
532 err = mlxsw_sp_acl_tcam_region_id_get(tcam, ®ion->id);
533 if (err)
534 goto err_region_id_get;
535
536 err = ops->region_associate(mlxsw_sp, region);
537 if (err)
538 goto err_tcam_region_associate;
539
540 region->key_type = ops->key_type;
541 err = mlxsw_sp_acl_tcam_region_alloc(mlxsw_sp, region);
542 if (err)
543 goto err_tcam_region_alloc;
544
545 err = mlxsw_sp_acl_tcam_region_enable(mlxsw_sp, region);
546 if (err)
547 goto err_tcam_region_enable;
548
549 err = ops->region_init(mlxsw_sp, region->priv, tcam->priv, region);
550 if (err)
551 goto err_tcam_region_init;
552
553 return region;
554
555err_tcam_region_init:
556 mlxsw_sp_acl_tcam_region_disable(mlxsw_sp, region);
557err_tcam_region_enable:
558 mlxsw_sp_acl_tcam_region_free(mlxsw_sp, region);
559err_tcam_region_alloc:
560err_tcam_region_associate:
561 mlxsw_sp_acl_tcam_region_id_put(tcam, region->id);
562err_region_id_get:
563 mlxsw_afk_key_info_put(region->key_info);
564err_key_info_get:
565 kfree(region);
566 return ERR_PTR(err);
567}
568
569static void
570mlxsw_sp_acl_tcam_region_destroy(struct mlxsw_sp *mlxsw_sp,
571 struct mlxsw_sp_acl_tcam_region *region)
572{
573 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
574
575 ops->region_fini(mlxsw_sp, region->priv);
576 mlxsw_sp_acl_tcam_region_disable(mlxsw_sp, region);
577 mlxsw_sp_acl_tcam_region_free(mlxsw_sp, region);
578 mlxsw_sp_acl_tcam_region_id_put(region->group->tcam, region->id);
579 mlxsw_afk_key_info_put(region->key_info);
580 kfree(region);
581}
582
583static int
584mlxsw_sp_acl_tcam_chunk_assoc(struct mlxsw_sp *mlxsw_sp,
585 struct mlxsw_sp_acl_tcam_group *group,
586 unsigned int priority,
587 struct mlxsw_afk_element_usage *elusage,
588 struct mlxsw_sp_acl_tcam_chunk *chunk)
589{
590 struct mlxsw_sp_acl_tcam_region *region;
591 bool region_created = false;
592 bool need_split;
593 int err;
594
595 region = mlxsw_sp_acl_tcam_group_region_find(group, priority, elusage,
596 &need_split);
597 if (region && need_split) {
598
599
600
601
602
603
604 return -EOPNOTSUPP;
605 }
606 if (!region) {
607 struct mlxsw_afk_element_usage region_elusage;
608
609 mlxsw_sp_acl_tcam_group_use_patterns(group, elusage,
610 ®ion_elusage);
611 region = mlxsw_sp_acl_tcam_region_create(mlxsw_sp, group->tcam,
612 ®ion_elusage);
613 if (IS_ERR(region))
614 return PTR_ERR(region);
615 region_created = true;
616 }
617
618 chunk->region = region;
619 list_add_tail(&chunk->list, ®ion->chunk_list);
620
621 if (!region_created)
622 return 0;
623
624 err = mlxsw_sp_acl_tcam_group_region_attach(mlxsw_sp, group, region);
625 if (err)
626 goto err_group_region_attach;
627
628 return 0;
629
630err_group_region_attach:
631 mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, region);
632 return err;
633}
634
635static void
636mlxsw_sp_acl_tcam_chunk_deassoc(struct mlxsw_sp *mlxsw_sp,
637 struct mlxsw_sp_acl_tcam_chunk *chunk)
638{
639 struct mlxsw_sp_acl_tcam_region *region = chunk->region;
640
641 list_del(&chunk->list);
642 if (list_empty(®ion->chunk_list)) {
643 mlxsw_sp_acl_tcam_group_region_detach(mlxsw_sp, region);
644 mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, region);
645 }
646}
647
648static struct mlxsw_sp_acl_tcam_chunk *
649mlxsw_sp_acl_tcam_chunk_create(struct mlxsw_sp *mlxsw_sp,
650 struct mlxsw_sp_acl_tcam_group *group,
651 unsigned int priority,
652 struct mlxsw_afk_element_usage *elusage)
653{
654 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
655 struct mlxsw_sp_acl_tcam_chunk *chunk;
656 int err;
657
658 if (priority == MLXSW_SP_ACL_TCAM_CATCHALL_PRIO)
659 return ERR_PTR(-EINVAL);
660
661 chunk = kzalloc(sizeof(*chunk) + ops->chunk_priv_size, GFP_KERNEL);
662 if (!chunk)
663 return ERR_PTR(-ENOMEM);
664 chunk->priority = priority;
665 chunk->group = group;
666 chunk->ref_count = 1;
667
668 err = mlxsw_sp_acl_tcam_chunk_assoc(mlxsw_sp, group, priority,
669 elusage, chunk);
670 if (err)
671 goto err_chunk_assoc;
672
673 ops->chunk_init(chunk->region->priv, chunk->priv, priority);
674
675 err = rhashtable_insert_fast(&group->chunk_ht, &chunk->ht_node,
676 mlxsw_sp_acl_tcam_chunk_ht_params);
677 if (err)
678 goto err_rhashtable_insert;
679
680 return chunk;
681
682err_rhashtable_insert:
683 ops->chunk_fini(chunk->priv);
684 mlxsw_sp_acl_tcam_chunk_deassoc(mlxsw_sp, chunk);
685err_chunk_assoc:
686 kfree(chunk);
687 return ERR_PTR(err);
688}
689
690static void
691mlxsw_sp_acl_tcam_chunk_destroy(struct mlxsw_sp *mlxsw_sp,
692 struct mlxsw_sp_acl_tcam_chunk *chunk)
693{
694 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
695 struct mlxsw_sp_acl_tcam_group *group = chunk->group;
696
697 rhashtable_remove_fast(&group->chunk_ht, &chunk->ht_node,
698 mlxsw_sp_acl_tcam_chunk_ht_params);
699 ops->chunk_fini(chunk->priv);
700 mlxsw_sp_acl_tcam_chunk_deassoc(mlxsw_sp, chunk);
701 kfree(chunk);
702}
703
704static struct mlxsw_sp_acl_tcam_chunk *
705mlxsw_sp_acl_tcam_chunk_get(struct mlxsw_sp *mlxsw_sp,
706 struct mlxsw_sp_acl_tcam_group *group,
707 unsigned int priority,
708 struct mlxsw_afk_element_usage *elusage)
709{
710 struct mlxsw_sp_acl_tcam_chunk *chunk;
711
712 chunk = rhashtable_lookup_fast(&group->chunk_ht, &priority,
713 mlxsw_sp_acl_tcam_chunk_ht_params);
714 if (chunk) {
715 if (WARN_ON(!mlxsw_afk_key_info_subset(chunk->region->key_info,
716 elusage)))
717 return ERR_PTR(-EINVAL);
718 chunk->ref_count++;
719 return chunk;
720 }
721 return mlxsw_sp_acl_tcam_chunk_create(mlxsw_sp, group,
722 priority, elusage);
723}
724
725static void mlxsw_sp_acl_tcam_chunk_put(struct mlxsw_sp *mlxsw_sp,
726 struct mlxsw_sp_acl_tcam_chunk *chunk)
727{
728 if (--chunk->ref_count)
729 return;
730 mlxsw_sp_acl_tcam_chunk_destroy(mlxsw_sp, chunk);
731}
732
733static size_t mlxsw_sp_acl_tcam_entry_priv_size(struct mlxsw_sp *mlxsw_sp)
734{
735 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
736
737 return ops->entry_priv_size;
738}
739
740static int mlxsw_sp_acl_tcam_entry_add(struct mlxsw_sp *mlxsw_sp,
741 struct mlxsw_sp_acl_tcam_group *group,
742 struct mlxsw_sp_acl_tcam_entry *entry,
743 struct mlxsw_sp_acl_rule_info *rulei)
744{
745 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
746 struct mlxsw_sp_acl_tcam_chunk *chunk;
747 struct mlxsw_sp_acl_tcam_region *region;
748 int err;
749
750 chunk = mlxsw_sp_acl_tcam_chunk_get(mlxsw_sp, group, rulei->priority,
751 &rulei->values.elusage);
752 if (IS_ERR(chunk))
753 return PTR_ERR(chunk);
754
755 region = chunk->region;
756
757 err = ops->entry_add(mlxsw_sp, region->priv, chunk->priv,
758 entry->priv, rulei);
759 if (err)
760 goto err_entry_add;
761 entry->chunk = chunk;
762
763 return 0;
764
765err_entry_add:
766 mlxsw_sp_acl_tcam_chunk_put(mlxsw_sp, chunk);
767 return err;
768}
769
770static void mlxsw_sp_acl_tcam_entry_del(struct mlxsw_sp *mlxsw_sp,
771 struct mlxsw_sp_acl_tcam_entry *entry)
772{
773 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
774 struct mlxsw_sp_acl_tcam_chunk *chunk = entry->chunk;
775 struct mlxsw_sp_acl_tcam_region *region = chunk->region;
776
777 ops->entry_del(mlxsw_sp, region->priv, chunk->priv, entry->priv);
778 mlxsw_sp_acl_tcam_chunk_put(mlxsw_sp, chunk);
779}
780
781static int
782mlxsw_sp_acl_tcam_entry_activity_get(struct mlxsw_sp *mlxsw_sp,
783 struct mlxsw_sp_acl_tcam_entry *entry,
784 bool *activity)
785{
786 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
787 struct mlxsw_sp_acl_tcam_chunk *chunk = entry->chunk;
788 struct mlxsw_sp_acl_tcam_region *region = chunk->region;
789
790 return ops->entry_activity_get(mlxsw_sp, region->priv,
791 entry->priv, activity);
792}
793
794static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv4[] = {
795 MLXSW_AFK_ELEMENT_SRC_SYS_PORT,
796 MLXSW_AFK_ELEMENT_DMAC_32_47,
797 MLXSW_AFK_ELEMENT_DMAC_0_31,
798 MLXSW_AFK_ELEMENT_SMAC_32_47,
799 MLXSW_AFK_ELEMENT_SMAC_0_31,
800 MLXSW_AFK_ELEMENT_ETHERTYPE,
801 MLXSW_AFK_ELEMENT_IP_PROTO,
802 MLXSW_AFK_ELEMENT_SRC_IP_0_31,
803 MLXSW_AFK_ELEMENT_DST_IP_0_31,
804 MLXSW_AFK_ELEMENT_DST_L4_PORT,
805 MLXSW_AFK_ELEMENT_SRC_L4_PORT,
806 MLXSW_AFK_ELEMENT_VID,
807 MLXSW_AFK_ELEMENT_PCP,
808 MLXSW_AFK_ELEMENT_TCP_FLAGS,
809 MLXSW_AFK_ELEMENT_IP_TTL_,
810 MLXSW_AFK_ELEMENT_IP_ECN,
811 MLXSW_AFK_ELEMENT_IP_DSCP,
812};
813
814static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv6[] = {
815 MLXSW_AFK_ELEMENT_ETHERTYPE,
816 MLXSW_AFK_ELEMENT_IP_PROTO,
817 MLXSW_AFK_ELEMENT_SRC_IP_96_127,
818 MLXSW_AFK_ELEMENT_SRC_IP_64_95,
819 MLXSW_AFK_ELEMENT_SRC_IP_32_63,
820 MLXSW_AFK_ELEMENT_SRC_IP_0_31,
821 MLXSW_AFK_ELEMENT_DST_IP_96_127,
822 MLXSW_AFK_ELEMENT_DST_IP_64_95,
823 MLXSW_AFK_ELEMENT_DST_IP_32_63,
824 MLXSW_AFK_ELEMENT_DST_IP_0_31,
825 MLXSW_AFK_ELEMENT_DST_L4_PORT,
826 MLXSW_AFK_ELEMENT_SRC_L4_PORT,
827};
828
829static const struct mlxsw_sp_acl_tcam_pattern mlxsw_sp_acl_tcam_patterns[] = {
830 {
831 .elements = mlxsw_sp_acl_tcam_pattern_ipv4,
832 .elements_count = ARRAY_SIZE(mlxsw_sp_acl_tcam_pattern_ipv4),
833 },
834 {
835 .elements = mlxsw_sp_acl_tcam_pattern_ipv6,
836 .elements_count = ARRAY_SIZE(mlxsw_sp_acl_tcam_pattern_ipv6),
837 },
838};
839
840#define MLXSW_SP_ACL_TCAM_PATTERNS_COUNT \
841 ARRAY_SIZE(mlxsw_sp_acl_tcam_patterns)
842
843struct mlxsw_sp_acl_tcam_flower_ruleset {
844 struct mlxsw_sp_acl_tcam_group group;
845};
846
847struct mlxsw_sp_acl_tcam_flower_rule {
848 struct mlxsw_sp_acl_tcam_entry entry;
849};
850
851static int
852mlxsw_sp_acl_tcam_flower_ruleset_add(struct mlxsw_sp *mlxsw_sp,
853 struct mlxsw_sp_acl_tcam *tcam,
854 void *ruleset_priv,
855 struct mlxsw_afk_element_usage *tmplt_elusage)
856{
857 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;
858
859 return mlxsw_sp_acl_tcam_group_add(mlxsw_sp, tcam, &ruleset->group,
860 mlxsw_sp_acl_tcam_patterns,
861 MLXSW_SP_ACL_TCAM_PATTERNS_COUNT,
862 tmplt_elusage);
863}
864
865static void
866mlxsw_sp_acl_tcam_flower_ruleset_del(struct mlxsw_sp *mlxsw_sp,
867 void *ruleset_priv)
868{
869 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;
870
871 mlxsw_sp_acl_tcam_group_del(mlxsw_sp, &ruleset->group);
872}
873
874static int
875mlxsw_sp_acl_tcam_flower_ruleset_bind(struct mlxsw_sp *mlxsw_sp,
876 void *ruleset_priv,
877 struct mlxsw_sp_port *mlxsw_sp_port,
878 bool ingress)
879{
880 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;
881
882 return mlxsw_sp_acl_tcam_group_bind(mlxsw_sp, &ruleset->group,
883 mlxsw_sp_port, ingress);
884}
885
886static void
887mlxsw_sp_acl_tcam_flower_ruleset_unbind(struct mlxsw_sp *mlxsw_sp,
888 void *ruleset_priv,
889 struct mlxsw_sp_port *mlxsw_sp_port,
890 bool ingress)
891{
892 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;
893
894 mlxsw_sp_acl_tcam_group_unbind(mlxsw_sp, &ruleset->group,
895 mlxsw_sp_port, ingress);
896}
897
898static u16
899mlxsw_sp_acl_tcam_flower_ruleset_group_id(void *ruleset_priv)
900{
901 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;
902
903 return mlxsw_sp_acl_tcam_group_id(&ruleset->group);
904}
905
906static size_t mlxsw_sp_acl_tcam_flower_rule_priv_size(struct mlxsw_sp *mlxsw_sp)
907{
908 return sizeof(struct mlxsw_sp_acl_tcam_flower_rule) +
909 mlxsw_sp_acl_tcam_entry_priv_size(mlxsw_sp);
910}
911
912static int
913mlxsw_sp_acl_tcam_flower_rule_add(struct mlxsw_sp *mlxsw_sp,
914 void *ruleset_priv, void *rule_priv,
915 struct mlxsw_sp_acl_rule_info *rulei)
916{
917 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;
918 struct mlxsw_sp_acl_tcam_flower_rule *rule = rule_priv;
919
920 return mlxsw_sp_acl_tcam_entry_add(mlxsw_sp, &ruleset->group,
921 &rule->entry, rulei);
922}
923
924static void
925mlxsw_sp_acl_tcam_flower_rule_del(struct mlxsw_sp *mlxsw_sp, void *rule_priv)
926{
927 struct mlxsw_sp_acl_tcam_flower_rule *rule = rule_priv;
928
929 mlxsw_sp_acl_tcam_entry_del(mlxsw_sp, &rule->entry);
930}
931
932static int
933mlxsw_sp_acl_tcam_flower_rule_activity_get(struct mlxsw_sp *mlxsw_sp,
934 void *rule_priv, bool *activity)
935{
936 struct mlxsw_sp_acl_tcam_flower_rule *rule = rule_priv;
937
938 return mlxsw_sp_acl_tcam_entry_activity_get(mlxsw_sp, &rule->entry,
939 activity);
940}
941
942static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_flower_ops = {
943 .ruleset_priv_size = sizeof(struct mlxsw_sp_acl_tcam_flower_ruleset),
944 .ruleset_add = mlxsw_sp_acl_tcam_flower_ruleset_add,
945 .ruleset_del = mlxsw_sp_acl_tcam_flower_ruleset_del,
946 .ruleset_bind = mlxsw_sp_acl_tcam_flower_ruleset_bind,
947 .ruleset_unbind = mlxsw_sp_acl_tcam_flower_ruleset_unbind,
948 .ruleset_group_id = mlxsw_sp_acl_tcam_flower_ruleset_group_id,
949 .rule_priv_size = mlxsw_sp_acl_tcam_flower_rule_priv_size,
950 .rule_add = mlxsw_sp_acl_tcam_flower_rule_add,
951 .rule_del = mlxsw_sp_acl_tcam_flower_rule_del,
952 .rule_activity_get = mlxsw_sp_acl_tcam_flower_rule_activity_get,
953};
954
955static const struct mlxsw_sp_acl_profile_ops *
956mlxsw_sp_acl_tcam_profile_ops_arr[] = {
957 [MLXSW_SP_ACL_PROFILE_FLOWER] = &mlxsw_sp_acl_tcam_flower_ops,
958};
959
960const struct mlxsw_sp_acl_profile_ops *
961mlxsw_sp_acl_tcam_profile_ops(struct mlxsw_sp *mlxsw_sp,
962 enum mlxsw_sp_acl_profile profile)
963{
964 const struct mlxsw_sp_acl_profile_ops *ops;
965
966 if (WARN_ON(profile >= ARRAY_SIZE(mlxsw_sp_acl_tcam_profile_ops_arr)))
967 return NULL;
968 ops = mlxsw_sp_acl_tcam_profile_ops_arr[profile];
969 if (WARN_ON(!ops))
970 return NULL;
971 return ops;
972}
973