1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
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
mlxsw_sp_acl_tcam_priv_size(struct mlxsw_sp * mlxsw_sp)19 size_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
mlxsw_sp_acl_tcam_init(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam * tcam)26 int 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 /* Use 1:1 mapping between ACL region and TCAM region */
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
67 err_tcam_init:
68 kfree(tcam->used_groups);
69 err_alloc_used_groups:
70 kfree(tcam->used_regions);
71 return err;
72 }
73
mlxsw_sp_acl_tcam_fini(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam * tcam)74 void 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
mlxsw_sp_acl_tcam_priority_get(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_rule_info * rulei,u32 * priority,bool fillup_priority)84 int 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 /* Unlike in TC, in HW, higher number means higher priority. */
103 *priority = max_priority - rulei->priority;
104 return 0;
105 }
106
mlxsw_sp_acl_tcam_region_id_get(struct mlxsw_sp_acl_tcam * tcam,u16 * p_id)107 static 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
mlxsw_sp_acl_tcam_region_id_put(struct mlxsw_sp_acl_tcam * tcam,u16 id)121 static 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
mlxsw_sp_acl_tcam_group_id_get(struct mlxsw_sp_acl_tcam * tcam,u16 * p_id)127 static 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
mlxsw_sp_acl_tcam_group_id_put(struct mlxsw_sp_acl_tcam * tcam,u16 id)141 static 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
147 struct mlxsw_sp_acl_tcam_pattern {
148 const enum mlxsw_afk_element *elements;
149 unsigned int elements_count;
150 };
151
152 struct 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
165 struct mlxsw_sp_acl_tcam_chunk {
166 struct list_head list; /* Member of a TCAM region */
167 struct rhash_head ht_node; /* Member of a chunk HT */
168 unsigned int priority; /* Priority within the region and group */
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 /* priv has to be always the last item */
174 };
175
176 struct mlxsw_sp_acl_tcam_entry {
177 struct mlxsw_sp_acl_tcam_chunk *chunk;
178 unsigned long priv[0];
179 /* priv has to be always the last item */
180 };
181
182 static 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
mlxsw_sp_acl_tcam_group_update(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_group * group)189 static 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
203 static int
mlxsw_sp_acl_tcam_group_add(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam * tcam,struct mlxsw_sp_acl_tcam_group * group,const struct mlxsw_sp_acl_tcam_pattern * patterns,unsigned int patterns_count,struct mlxsw_afk_element_usage * tmplt_elusage)204 mlxsw_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
233 err_rhashtable_init:
234 mlxsw_sp_acl_tcam_group_id_put(tcam, group->id);
235 return err;
236 }
237
mlxsw_sp_acl_tcam_group_del(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_group * group)238 static 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
248 static int
mlxsw_sp_acl_tcam_group_bind(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_group * group,struct mlxsw_sp_port * mlxsw_sp_port,bool ingress)249 mlxsw_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
263 static void
mlxsw_sp_acl_tcam_group_unbind(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_group * group,struct mlxsw_sp_port * mlxsw_sp_port,bool ingress)264 mlxsw_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
278 static u16
mlxsw_sp_acl_tcam_group_id(struct mlxsw_sp_acl_tcam_group * group)279 mlxsw_sp_acl_tcam_group_id(struct mlxsw_sp_acl_tcam_group *group)
280 {
281 return group->id;
282 }
283
284 static unsigned int
mlxsw_sp_acl_tcam_region_prio(struct mlxsw_sp_acl_tcam_region * region)285 mlxsw_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 /* As a priority of a region, return priority of the first chunk */
292 chunk = list_first_entry(®ion->chunk_list, typeof(*chunk), list);
293 return chunk->priority;
294 }
295
296 static unsigned int
mlxsw_sp_acl_tcam_region_max_prio(struct mlxsw_sp_acl_tcam_region * region)297 mlxsw_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
307 static void
mlxsw_sp_acl_tcam_group_list_add(struct mlxsw_sp_acl_tcam_group * group,struct mlxsw_sp_acl_tcam_region * region)308 mlxsw_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 /* Position the region inside the list according to priority */
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
325 static void
mlxsw_sp_acl_tcam_group_list_del(struct mlxsw_sp_acl_tcam_group * group,struct mlxsw_sp_acl_tcam_region * region)326 mlxsw_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
333 static int
mlxsw_sp_acl_tcam_group_region_attach(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_group * group,struct mlxsw_sp_acl_tcam_region * region)334 mlxsw_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
352 err_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
358 static void
mlxsw_sp_acl_tcam_group_region_detach(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_region * region)359 mlxsw_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
368 static struct mlxsw_sp_acl_tcam_region *
mlxsw_sp_acl_tcam_group_region_find(struct mlxsw_sp_acl_tcam_group * group,unsigned int priority,struct mlxsw_afk_element_usage * elusage,bool * p_need_split)369 mlxsw_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 /* First, check if the requested priority does not rather belong
382 * under some of the next regions.
383 */
384 if (pos->next != &group->region_list) { /* not last */
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 /* If requested element usage would not fit and the priority
393 * is lower than the currently inspected region we cannot
394 * use this region, so return NULL to indicate new region has
395 * to be created.
396 */
397 if (!issubset &&
398 priority < mlxsw_sp_acl_tcam_region_prio(region))
399 return NULL;
400
401 /* If requested element usage would not fit and the priority
402 * is higher than the currently inspected region we cannot
403 * use this region. There is still some hope that the next
404 * region would be the fit. So let it be processed and
405 * eventually break at the check right above this.
406 */
407 if (!issubset &&
408 priority > mlxsw_sp_acl_tcam_region_max_prio(region))
409 continue;
410
411 /* Indicate if the region needs to be split in order to add
412 * the requested priority. Split is needed when requested
413 * element usage won't fit into the found region.
414 */
415 *p_need_split = !issubset;
416 return region;
417 }
418 return NULL; /* New region has to be created. */
419 }
420
421 static void
mlxsw_sp_acl_tcam_group_use_patterns(struct mlxsw_sp_acl_tcam_group * group,struct mlxsw_afk_element_usage * elusage,struct mlxsw_afk_element_usage * out)422 mlxsw_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 /* In case the template is set, we don't have to look up the pattern
430 * and just use the template.
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
448 static int
mlxsw_sp_acl_tcam_region_alloc(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_region * region)449 mlxsw_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
476 static void
mlxsw_sp_acl_tcam_region_free(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_region * region)477 mlxsw_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
488 static int
mlxsw_sp_acl_tcam_region_enable(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_region * region)489 mlxsw_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
499 static void
mlxsw_sp_acl_tcam_region_disable(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_region * region)500 mlxsw_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
510 static struct mlxsw_sp_acl_tcam_region *
mlxsw_sp_acl_tcam_region_create(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam * tcam,struct mlxsw_afk_element_usage * elusage)511 mlxsw_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
555 err_tcam_region_init:
556 mlxsw_sp_acl_tcam_region_disable(mlxsw_sp, region);
557 err_tcam_region_enable:
558 mlxsw_sp_acl_tcam_region_free(mlxsw_sp, region);
559 err_tcam_region_alloc:
560 err_tcam_region_associate:
561 mlxsw_sp_acl_tcam_region_id_put(tcam, region->id);
562 err_region_id_get:
563 mlxsw_afk_key_info_put(region->key_info);
564 err_key_info_get:
565 kfree(region);
566 return ERR_PTR(err);
567 }
568
569 static void
mlxsw_sp_acl_tcam_region_destroy(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_region * region)570 mlxsw_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
583 static int
mlxsw_sp_acl_tcam_chunk_assoc(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_group * group,unsigned int priority,struct mlxsw_afk_element_usage * elusage,struct mlxsw_sp_acl_tcam_chunk * chunk)584 mlxsw_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 /* According to priority, the chunk should belong to an
599 * existing region. However, this chunk needs elements
600 * that region does not contain. We need to split the existing
601 * region into two and create a new region for this chunk
602 * in between. This is not supported now.
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
630 err_group_region_attach:
631 mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, region);
632 return err;
633 }
634
635 static void
mlxsw_sp_acl_tcam_chunk_deassoc(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_chunk * chunk)636 mlxsw_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
648 static struct mlxsw_sp_acl_tcam_chunk *
mlxsw_sp_acl_tcam_chunk_create(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_group * group,unsigned int priority,struct mlxsw_afk_element_usage * elusage)649 mlxsw_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
682 err_rhashtable_insert:
683 ops->chunk_fini(chunk->priv);
684 mlxsw_sp_acl_tcam_chunk_deassoc(mlxsw_sp, chunk);
685 err_chunk_assoc:
686 kfree(chunk);
687 return ERR_PTR(err);
688 }
689
690 static void
mlxsw_sp_acl_tcam_chunk_destroy(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_chunk * chunk)691 mlxsw_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
704 static struct mlxsw_sp_acl_tcam_chunk *
mlxsw_sp_acl_tcam_chunk_get(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_group * group,unsigned int priority,struct mlxsw_afk_element_usage * elusage)705 mlxsw_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
mlxsw_sp_acl_tcam_chunk_put(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_chunk * chunk)725 static 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
mlxsw_sp_acl_tcam_entry_priv_size(struct mlxsw_sp * mlxsw_sp)733 static 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
mlxsw_sp_acl_tcam_entry_add(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_group * group,struct mlxsw_sp_acl_tcam_entry * entry,struct mlxsw_sp_acl_rule_info * rulei)740 static 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
765 err_entry_add:
766 mlxsw_sp_acl_tcam_chunk_put(mlxsw_sp, chunk);
767 return err;
768 }
769
mlxsw_sp_acl_tcam_entry_del(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_entry * entry)770 static 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
781 static int
mlxsw_sp_acl_tcam_entry_activity_get(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_entry * entry,bool * activity)782 mlxsw_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
794 static 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
814 static 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
829 static 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
843 struct mlxsw_sp_acl_tcam_flower_ruleset {
844 struct mlxsw_sp_acl_tcam_group group;
845 };
846
847 struct mlxsw_sp_acl_tcam_flower_rule {
848 struct mlxsw_sp_acl_tcam_entry entry;
849 };
850
851 static int
mlxsw_sp_acl_tcam_flower_ruleset_add(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam * tcam,void * ruleset_priv,struct mlxsw_afk_element_usage * tmplt_elusage)852 mlxsw_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
865 static void
mlxsw_sp_acl_tcam_flower_ruleset_del(struct mlxsw_sp * mlxsw_sp,void * ruleset_priv)866 mlxsw_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
874 static int
mlxsw_sp_acl_tcam_flower_ruleset_bind(struct mlxsw_sp * mlxsw_sp,void * ruleset_priv,struct mlxsw_sp_port * mlxsw_sp_port,bool ingress)875 mlxsw_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
886 static void
mlxsw_sp_acl_tcam_flower_ruleset_unbind(struct mlxsw_sp * mlxsw_sp,void * ruleset_priv,struct mlxsw_sp_port * mlxsw_sp_port,bool ingress)887 mlxsw_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
898 static u16
mlxsw_sp_acl_tcam_flower_ruleset_group_id(void * ruleset_priv)899 mlxsw_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
mlxsw_sp_acl_tcam_flower_rule_priv_size(struct mlxsw_sp * mlxsw_sp)906 static 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
912 static int
mlxsw_sp_acl_tcam_flower_rule_add(struct mlxsw_sp * mlxsw_sp,void * ruleset_priv,void * rule_priv,struct mlxsw_sp_acl_rule_info * rulei)913 mlxsw_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
924 static void
mlxsw_sp_acl_tcam_flower_rule_del(struct mlxsw_sp * mlxsw_sp,void * rule_priv)925 mlxsw_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
932 static int
mlxsw_sp_acl_tcam_flower_rule_activity_get(struct mlxsw_sp * mlxsw_sp,void * rule_priv,bool * activity)933 mlxsw_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
942 static 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
955 static const struct mlxsw_sp_acl_profile_ops *
956 mlxsw_sp_acl_tcam_profile_ops_arr[] = {
957 [MLXSW_SP_ACL_PROFILE_FLOWER] = &mlxsw_sp_acl_tcam_flower_ops,
958 };
959
960 const struct mlxsw_sp_acl_profile_ops *
mlxsw_sp_acl_tcam_profile_ops(struct mlxsw_sp * mlxsw_sp,enum mlxsw_sp_acl_profile profile)961 mlxsw_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