1 /* 2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.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/bitops.h> 39 #include <linux/list.h> 40 #include <linux/rhashtable.h> 41 #include <linux/netdevice.h> 42 #include <linux/parman.h> 43 44 #include "reg.h" 45 #include "core.h" 46 #include "resources.h" 47 #include "spectrum.h" 48 #include "core_acl_flex_keys.h" 49 50 struct mlxsw_sp_acl_tcam { 51 unsigned long *used_regions; /* bit array */ 52 unsigned int max_regions; 53 unsigned long *used_groups; /* bit array */ 54 unsigned int max_groups; 55 unsigned int max_group_size; 56 }; 57 58 static int mlxsw_sp_acl_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv) 59 { 60 struct mlxsw_sp_acl_tcam *tcam = priv; 61 u64 max_tcam_regions; 62 u64 max_regions; 63 u64 max_groups; 64 size_t alloc_size; 65 int err; 66 67 max_tcam_regions = MLXSW_CORE_RES_GET(mlxsw_sp->core, 68 ACL_MAX_TCAM_REGIONS); 69 max_regions = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_REGIONS); 70 71 /* Use 1:1 mapping between ACL region and TCAM region */ 72 if (max_tcam_regions < max_regions) 73 max_regions = max_tcam_regions; 74 75 alloc_size = sizeof(tcam->used_regions[0]) * BITS_TO_LONGS(max_regions); 76 tcam->used_regions = kzalloc(alloc_size, GFP_KERNEL); 77 if (!tcam->used_regions) 78 return -ENOMEM; 79 tcam->max_regions = max_regions; 80 81 max_groups = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_GROUPS); 82 alloc_size = sizeof(tcam->used_groups[0]) * BITS_TO_LONGS(max_groups); 83 tcam->used_groups = kzalloc(alloc_size, GFP_KERNEL); 84 if (!tcam->used_groups) { 85 err = -ENOMEM; 86 goto err_alloc_used_groups; 87 } 88 tcam->max_groups = max_groups; 89 tcam->max_group_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, 90 ACL_MAX_GROUP_SIZE); 91 return 0; 92 93 err_alloc_used_groups: 94 kfree(tcam->used_regions); 95 return err; 96 } 97 98 static void mlxsw_sp_acl_tcam_fini(struct mlxsw_sp *mlxsw_sp, void *priv) 99 { 100 struct mlxsw_sp_acl_tcam *tcam = priv; 101 102 kfree(tcam->used_groups); 103 kfree(tcam->used_regions); 104 } 105 106 static int mlxsw_sp_acl_tcam_region_id_get(struct mlxsw_sp_acl_tcam *tcam, 107 u16 *p_id) 108 { 109 u16 id; 110 111 id = find_first_zero_bit(tcam->used_regions, tcam->max_regions); 112 if (id < tcam->max_regions) { 113 __set_bit(id, tcam->used_regions); 114 *p_id = id; 115 return 0; 116 } 117 return -ENOBUFS; 118 } 119 120 static void mlxsw_sp_acl_tcam_region_id_put(struct mlxsw_sp_acl_tcam *tcam, 121 u16 id) 122 { 123 __clear_bit(id, tcam->used_regions); 124 } 125 126 static int mlxsw_sp_acl_tcam_group_id_get(struct mlxsw_sp_acl_tcam *tcam, 127 u16 *p_id) 128 { 129 u16 id; 130 131 id = find_first_zero_bit(tcam->used_groups, tcam->max_groups); 132 if (id < tcam->max_groups) { 133 __set_bit(id, tcam->used_groups); 134 *p_id = id; 135 return 0; 136 } 137 return -ENOBUFS; 138 } 139 140 static void mlxsw_sp_acl_tcam_group_id_put(struct mlxsw_sp_acl_tcam *tcam, 141 u16 id) 142 { 143 __clear_bit(id, tcam->used_groups); 144 } 145 146 struct mlxsw_sp_acl_tcam_pattern { 147 const enum mlxsw_afk_element *elements; 148 unsigned int elements_count; 149 }; 150 151 struct mlxsw_sp_acl_tcam_group { 152 struct mlxsw_sp_acl_tcam *tcam; 153 u16 id; 154 struct list_head region_list; 155 unsigned int region_count; 156 struct rhashtable chunk_ht; 157 struct mlxsw_sp_acl_tcam_group_ops *ops; 158 const struct mlxsw_sp_acl_tcam_pattern *patterns; 159 unsigned int patterns_count; 160 }; 161 162 struct mlxsw_sp_acl_tcam_region { 163 struct list_head list; /* Member of a TCAM group */ 164 struct list_head chunk_list; /* List of chunks under this region */ 165 struct parman *parman; 166 struct mlxsw_sp *mlxsw_sp; 167 struct mlxsw_sp_acl_tcam_group *group; 168 u16 id; /* ACL ID and region ID - they are same */ 169 char tcam_region_info[MLXSW_REG_PXXX_TCAM_REGION_INFO_LEN]; 170 struct mlxsw_afk_key_info *key_info; 171 struct { 172 struct parman_prio parman_prio; 173 struct parman_item parman_item; 174 struct mlxsw_sp_acl_rule_info *rulei; 175 } catchall; 176 }; 177 178 struct mlxsw_sp_acl_tcam_chunk { 179 struct list_head list; /* Member of a TCAM region */ 180 struct rhash_head ht_node; /* Member of a chunk HT */ 181 unsigned int priority; /* Priority within the region and group */ 182 struct parman_prio parman_prio; 183 struct mlxsw_sp_acl_tcam_group *group; 184 struct mlxsw_sp_acl_tcam_region *region; 185 unsigned int ref_count; 186 }; 187 188 struct mlxsw_sp_acl_tcam_entry { 189 struct parman_item parman_item; 190 struct mlxsw_sp_acl_tcam_chunk *chunk; 191 }; 192 193 static const struct rhashtable_params mlxsw_sp_acl_tcam_chunk_ht_params = { 194 .key_len = sizeof(unsigned int), 195 .key_offset = offsetof(struct mlxsw_sp_acl_tcam_chunk, priority), 196 .head_offset = offsetof(struct mlxsw_sp_acl_tcam_chunk, ht_node), 197 .automatic_shrinking = true, 198 }; 199 200 static int mlxsw_sp_acl_tcam_group_update(struct mlxsw_sp *mlxsw_sp, 201 struct mlxsw_sp_acl_tcam_group *group) 202 { 203 struct mlxsw_sp_acl_tcam_region *region; 204 char pagt_pl[MLXSW_REG_PAGT_LEN]; 205 int acl_index = 0; 206 207 mlxsw_reg_pagt_pack(pagt_pl, group->id); 208 list_for_each_entry(region, &group->region_list, list) 209 mlxsw_reg_pagt_acl_id_pack(pagt_pl, acl_index++, region->id); 210 mlxsw_reg_pagt_size_set(pagt_pl, acl_index); 211 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pagt), pagt_pl); 212 } 213 214 static int 215 mlxsw_sp_acl_tcam_group_add(struct mlxsw_sp *mlxsw_sp, 216 struct mlxsw_sp_acl_tcam *tcam, 217 struct mlxsw_sp_acl_tcam_group *group, 218 const struct mlxsw_sp_acl_tcam_pattern *patterns, 219 unsigned int patterns_count) 220 { 221 int err; 222 223 group->tcam = tcam; 224 group->patterns = patterns; 225 group->patterns_count = patterns_count; 226 INIT_LIST_HEAD(&group->region_list); 227 err = mlxsw_sp_acl_tcam_group_id_get(tcam, &group->id); 228 if (err) 229 return err; 230 231 err = rhashtable_init(&group->chunk_ht, 232 &mlxsw_sp_acl_tcam_chunk_ht_params); 233 if (err) 234 goto err_rhashtable_init; 235 236 return 0; 237 238 err_rhashtable_init: 239 mlxsw_sp_acl_tcam_group_id_put(tcam, group->id); 240 return err; 241 } 242 243 static void mlxsw_sp_acl_tcam_group_del(struct mlxsw_sp *mlxsw_sp, 244 struct mlxsw_sp_acl_tcam_group *group) 245 { 246 struct mlxsw_sp_acl_tcam *tcam = group->tcam; 247 248 rhashtable_destroy(&group->chunk_ht); 249 mlxsw_sp_acl_tcam_group_id_put(tcam, group->id); 250 WARN_ON(!list_empty(&group->region_list)); 251 } 252 253 static int 254 mlxsw_sp_acl_tcam_group_bind(struct mlxsw_sp *mlxsw_sp, 255 struct mlxsw_sp_acl_tcam_group *group, 256 struct mlxsw_sp_port *mlxsw_sp_port, 257 bool ingress) 258 { 259 char ppbt_pl[MLXSW_REG_PPBT_LEN]; 260 261 mlxsw_reg_ppbt_pack(ppbt_pl, ingress ? MLXSW_REG_PXBT_E_IACL : 262 MLXSW_REG_PXBT_E_EACL, 263 MLXSW_REG_PXBT_OP_BIND, mlxsw_sp_port->local_port, 264 group->id); 265 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppbt), ppbt_pl); 266 } 267 268 static void 269 mlxsw_sp_acl_tcam_group_unbind(struct mlxsw_sp *mlxsw_sp, 270 struct mlxsw_sp_acl_tcam_group *group, 271 struct mlxsw_sp_port *mlxsw_sp_port, 272 bool ingress) 273 { 274 char ppbt_pl[MLXSW_REG_PPBT_LEN]; 275 276 mlxsw_reg_ppbt_pack(ppbt_pl, ingress ? MLXSW_REG_PXBT_E_IACL : 277 MLXSW_REG_PXBT_E_EACL, 278 MLXSW_REG_PXBT_OP_UNBIND, mlxsw_sp_port->local_port, 279 group->id); 280 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppbt), ppbt_pl); 281 } 282 283 static u16 284 mlxsw_sp_acl_tcam_group_id(struct mlxsw_sp_acl_tcam_group *group) 285 { 286 return group->id; 287 } 288 289 static unsigned int 290 mlxsw_sp_acl_tcam_region_prio(struct mlxsw_sp_acl_tcam_region *region) 291 { 292 struct mlxsw_sp_acl_tcam_chunk *chunk; 293 294 if (list_empty(®ion->chunk_list)) 295 return 0; 296 /* As a priority of a region, return priority of the first chunk */ 297 chunk = list_first_entry(®ion->chunk_list, typeof(*chunk), list); 298 return chunk->priority; 299 } 300 301 static unsigned int 302 mlxsw_sp_acl_tcam_region_max_prio(struct mlxsw_sp_acl_tcam_region *region) 303 { 304 struct mlxsw_sp_acl_tcam_chunk *chunk; 305 306 if (list_empty(®ion->chunk_list)) 307 return 0; 308 chunk = list_last_entry(®ion->chunk_list, typeof(*chunk), list); 309 return chunk->priority; 310 } 311 312 static void 313 mlxsw_sp_acl_tcam_group_list_add(struct mlxsw_sp_acl_tcam_group *group, 314 struct mlxsw_sp_acl_tcam_region *region) 315 { 316 struct mlxsw_sp_acl_tcam_region *region2; 317 struct list_head *pos; 318 319 /* Position the region inside the list according to priority */ 320 list_for_each(pos, &group->region_list) { 321 region2 = list_entry(pos, typeof(*region2), list); 322 if (mlxsw_sp_acl_tcam_region_prio(region2) > 323 mlxsw_sp_acl_tcam_region_prio(region)) 324 break; 325 } 326 list_add_tail(®ion->list, pos); 327 group->region_count++; 328 } 329 330 static void 331 mlxsw_sp_acl_tcam_group_list_del(struct mlxsw_sp_acl_tcam_group *group, 332 struct mlxsw_sp_acl_tcam_region *region) 333 { 334 group->region_count--; 335 list_del(®ion->list); 336 } 337 338 static int 339 mlxsw_sp_acl_tcam_group_region_attach(struct mlxsw_sp *mlxsw_sp, 340 struct mlxsw_sp_acl_tcam_group *group, 341 struct mlxsw_sp_acl_tcam_region *region) 342 { 343 int err; 344 345 if (group->region_count == group->tcam->max_group_size) 346 return -ENOBUFS; 347 348 mlxsw_sp_acl_tcam_group_list_add(group, region); 349 350 err = mlxsw_sp_acl_tcam_group_update(mlxsw_sp, group); 351 if (err) 352 goto err_group_update; 353 region->group = group; 354 355 return 0; 356 357 err_group_update: 358 mlxsw_sp_acl_tcam_group_list_del(group, region); 359 mlxsw_sp_acl_tcam_group_update(mlxsw_sp, group); 360 return err; 361 } 362 363 static void 364 mlxsw_sp_acl_tcam_group_region_detach(struct mlxsw_sp *mlxsw_sp, 365 struct mlxsw_sp_acl_tcam_region *region) 366 { 367 struct mlxsw_sp_acl_tcam_group *group = region->group; 368 369 mlxsw_sp_acl_tcam_group_list_del(group, region); 370 mlxsw_sp_acl_tcam_group_update(mlxsw_sp, group); 371 } 372 373 static struct mlxsw_sp_acl_tcam_region * 374 mlxsw_sp_acl_tcam_group_region_find(struct mlxsw_sp_acl_tcam_group *group, 375 unsigned int priority, 376 struct mlxsw_afk_element_usage *elusage, 377 bool *p_need_split) 378 { 379 struct mlxsw_sp_acl_tcam_region *region, *region2; 380 struct list_head *pos; 381 bool issubset; 382 383 list_for_each(pos, &group->region_list) { 384 region = list_entry(pos, typeof(*region), list); 385 386 /* First, check if the requested priority does not rather belong 387 * under some of the next regions. 388 */ 389 if (pos->next != &group->region_list) { /* not last */ 390 region2 = list_entry(pos->next, typeof(*region2), list); 391 if (priority >= mlxsw_sp_acl_tcam_region_prio(region2)) 392 continue; 393 } 394 395 issubset = mlxsw_afk_key_info_subset(region->key_info, elusage); 396 397 /* If requested element usage would not fit and the priority 398 * is lower than the currently inspected region we cannot 399 * use this region, so return NULL to indicate new region has 400 * to be created. 401 */ 402 if (!issubset && 403 priority < mlxsw_sp_acl_tcam_region_prio(region)) 404 return NULL; 405 406 /* If requested element usage would not fit and the priority 407 * is higher than the currently inspected region we cannot 408 * use this region. There is still some hope that the next 409 * region would be the fit. So let it be processed and 410 * eventually break at the check right above this. 411 */ 412 if (!issubset && 413 priority > mlxsw_sp_acl_tcam_region_max_prio(region)) 414 continue; 415 416 /* Indicate if the region needs to be split in order to add 417 * the requested priority. Split is needed when requested 418 * element usage won't fit into the found region. 419 */ 420 *p_need_split = !issubset; 421 return region; 422 } 423 return NULL; /* New region has to be created. */ 424 } 425 426 static void 427 mlxsw_sp_acl_tcam_group_use_patterns(struct mlxsw_sp_acl_tcam_group *group, 428 struct mlxsw_afk_element_usage *elusage, 429 struct mlxsw_afk_element_usage *out) 430 { 431 const struct mlxsw_sp_acl_tcam_pattern *pattern; 432 int i; 433 434 for (i = 0; i < group->patterns_count; i++) { 435 pattern = &group->patterns[i]; 436 mlxsw_afk_element_usage_fill(out, pattern->elements, 437 pattern->elements_count); 438 if (mlxsw_afk_element_usage_subset(elusage, out)) 439 return; 440 } 441 memcpy(out, elusage, sizeof(*out)); 442 } 443 444 #define MLXSW_SP_ACL_TCAM_REGION_BASE_COUNT 16 445 #define MLXSW_SP_ACL_TCAM_REGION_RESIZE_STEP 16 446 447 static int 448 mlxsw_sp_acl_tcam_region_alloc(struct mlxsw_sp *mlxsw_sp, 449 struct mlxsw_sp_acl_tcam_region *region) 450 { 451 struct mlxsw_afk_key_info *key_info = region->key_info; 452 char ptar_pl[MLXSW_REG_PTAR_LEN]; 453 unsigned int encodings_count; 454 int i; 455 int err; 456 457 mlxsw_reg_ptar_pack(ptar_pl, MLXSW_REG_PTAR_OP_ALLOC, 458 MLXSW_SP_ACL_TCAM_REGION_BASE_COUNT, 459 region->id, region->tcam_region_info); 460 encodings_count = mlxsw_afk_key_info_blocks_count_get(key_info); 461 for (i = 0; i < encodings_count; i++) { 462 u16 encoding; 463 464 encoding = mlxsw_afk_key_info_block_encoding_get(key_info, i); 465 mlxsw_reg_ptar_key_id_pack(ptar_pl, i, encoding); 466 } 467 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptar), ptar_pl); 468 if (err) 469 return err; 470 mlxsw_reg_ptar_unpack(ptar_pl, region->tcam_region_info); 471 return 0; 472 } 473 474 static void 475 mlxsw_sp_acl_tcam_region_free(struct mlxsw_sp *mlxsw_sp, 476 struct mlxsw_sp_acl_tcam_region *region) 477 { 478 char ptar_pl[MLXSW_REG_PTAR_LEN]; 479 480 mlxsw_reg_ptar_pack(ptar_pl, MLXSW_REG_PTAR_OP_FREE, 0, region->id, 481 region->tcam_region_info); 482 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptar), ptar_pl); 483 } 484 485 static int 486 mlxsw_sp_acl_tcam_region_resize(struct mlxsw_sp *mlxsw_sp, 487 struct mlxsw_sp_acl_tcam_region *region, 488 u16 new_size) 489 { 490 char ptar_pl[MLXSW_REG_PTAR_LEN]; 491 492 mlxsw_reg_ptar_pack(ptar_pl, MLXSW_REG_PTAR_OP_RESIZE, 493 new_size, region->id, region->tcam_region_info); 494 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptar), ptar_pl); 495 } 496 497 static int 498 mlxsw_sp_acl_tcam_region_enable(struct mlxsw_sp *mlxsw_sp, 499 struct mlxsw_sp_acl_tcam_region *region) 500 { 501 char pacl_pl[MLXSW_REG_PACL_LEN]; 502 503 mlxsw_reg_pacl_pack(pacl_pl, region->id, true, 504 region->tcam_region_info); 505 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pacl), pacl_pl); 506 } 507 508 static void 509 mlxsw_sp_acl_tcam_region_disable(struct mlxsw_sp *mlxsw_sp, 510 struct mlxsw_sp_acl_tcam_region *region) 511 { 512 char pacl_pl[MLXSW_REG_PACL_LEN]; 513 514 mlxsw_reg_pacl_pack(pacl_pl, region->id, false, 515 region->tcam_region_info); 516 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pacl), pacl_pl); 517 } 518 519 static int 520 mlxsw_sp_acl_tcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp, 521 struct mlxsw_sp_acl_tcam_region *region, 522 unsigned int offset, 523 struct mlxsw_sp_acl_rule_info *rulei) 524 { 525 char ptce2_pl[MLXSW_REG_PTCE2_LEN]; 526 char *act_set; 527 char *mask; 528 char *key; 529 530 mlxsw_reg_ptce2_pack(ptce2_pl, true, MLXSW_REG_PTCE2_OP_WRITE_WRITE, 531 region->tcam_region_info, offset); 532 key = mlxsw_reg_ptce2_flex_key_blocks_data(ptce2_pl); 533 mask = mlxsw_reg_ptce2_mask_data(ptce2_pl); 534 mlxsw_afk_encode(region->key_info, &rulei->values, key, mask); 535 536 /* Only the first action set belongs here, the rest is in KVD */ 537 act_set = mlxsw_afa_block_first_set(rulei->act_block); 538 mlxsw_reg_ptce2_flex_action_set_memcpy_to(ptce2_pl, act_set); 539 540 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptce2), ptce2_pl); 541 } 542 543 static void 544 mlxsw_sp_acl_tcam_region_entry_remove(struct mlxsw_sp *mlxsw_sp, 545 struct mlxsw_sp_acl_tcam_region *region, 546 unsigned int offset) 547 { 548 char ptce2_pl[MLXSW_REG_PTCE2_LEN]; 549 550 mlxsw_reg_ptce2_pack(ptce2_pl, false, MLXSW_REG_PTCE2_OP_WRITE_WRITE, 551 region->tcam_region_info, offset); 552 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptce2), ptce2_pl); 553 } 554 555 static int 556 mlxsw_sp_acl_tcam_region_entry_activity_get(struct mlxsw_sp *mlxsw_sp, 557 struct mlxsw_sp_acl_tcam_region *region, 558 unsigned int offset, 559 bool *activity) 560 { 561 char ptce2_pl[MLXSW_REG_PTCE2_LEN]; 562 int err; 563 564 mlxsw_reg_ptce2_pack(ptce2_pl, true, MLXSW_REG_PTCE2_OP_QUERY_CLEAR_ON_READ, 565 region->tcam_region_info, offset); 566 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptce2), ptce2_pl); 567 if (err) 568 return err; 569 *activity = mlxsw_reg_ptce2_a_get(ptce2_pl); 570 return 0; 571 } 572 573 #define MLXSW_SP_ACL_TCAM_CATCHALL_PRIO (~0U) 574 575 static int 576 mlxsw_sp_acl_tcam_region_catchall_add(struct mlxsw_sp *mlxsw_sp, 577 struct mlxsw_sp_acl_tcam_region *region) 578 { 579 struct parman_prio *parman_prio = ®ion->catchall.parman_prio; 580 struct parman_item *parman_item = ®ion->catchall.parman_item; 581 struct mlxsw_sp_acl_rule_info *rulei; 582 int err; 583 584 parman_prio_init(region->parman, parman_prio, 585 MLXSW_SP_ACL_TCAM_CATCHALL_PRIO); 586 err = parman_item_add(region->parman, parman_prio, parman_item); 587 if (err) 588 goto err_parman_item_add; 589 590 rulei = mlxsw_sp_acl_rulei_create(mlxsw_sp->acl); 591 if (IS_ERR(rulei)) { 592 err = PTR_ERR(rulei); 593 goto err_rulei_create; 594 } 595 596 err = mlxsw_sp_acl_rulei_act_continue(rulei); 597 if (WARN_ON(err)) 598 goto err_rulei_act_continue; 599 600 err = mlxsw_sp_acl_rulei_commit(rulei); 601 if (err) 602 goto err_rulei_commit; 603 604 err = mlxsw_sp_acl_tcam_region_entry_insert(mlxsw_sp, region, 605 parman_item->index, rulei); 606 region->catchall.rulei = rulei; 607 if (err) 608 goto err_rule_insert; 609 610 return 0; 611 612 err_rule_insert: 613 err_rulei_commit: 614 err_rulei_act_continue: 615 mlxsw_sp_acl_rulei_destroy(rulei); 616 err_rulei_create: 617 parman_item_remove(region->parman, parman_prio, parman_item); 618 err_parman_item_add: 619 parman_prio_fini(parman_prio); 620 return err; 621 } 622 623 static void 624 mlxsw_sp_acl_tcam_region_catchall_del(struct mlxsw_sp *mlxsw_sp, 625 struct mlxsw_sp_acl_tcam_region *region) 626 { 627 struct parman_prio *parman_prio = ®ion->catchall.parman_prio; 628 struct parman_item *parman_item = ®ion->catchall.parman_item; 629 struct mlxsw_sp_acl_rule_info *rulei = region->catchall.rulei; 630 631 mlxsw_sp_acl_tcam_region_entry_remove(mlxsw_sp, region, 632 parman_item->index); 633 mlxsw_sp_acl_rulei_destroy(rulei); 634 parman_item_remove(region->parman, parman_prio, parman_item); 635 parman_prio_fini(parman_prio); 636 } 637 638 static void 639 mlxsw_sp_acl_tcam_region_move(struct mlxsw_sp *mlxsw_sp, 640 struct mlxsw_sp_acl_tcam_region *region, 641 u16 src_offset, u16 dst_offset, u16 size) 642 { 643 char prcr_pl[MLXSW_REG_PRCR_LEN]; 644 645 mlxsw_reg_prcr_pack(prcr_pl, MLXSW_REG_PRCR_OP_MOVE, 646 region->tcam_region_info, src_offset, 647 region->tcam_region_info, dst_offset, size); 648 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(prcr), prcr_pl); 649 } 650 651 static int mlxsw_sp_acl_tcam_region_parman_resize(void *priv, 652 unsigned long new_count) 653 { 654 struct mlxsw_sp_acl_tcam_region *region = priv; 655 struct mlxsw_sp *mlxsw_sp = region->mlxsw_sp; 656 u64 max_tcam_rules; 657 658 max_tcam_rules = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_TCAM_RULES); 659 if (new_count > max_tcam_rules) 660 return -EINVAL; 661 return mlxsw_sp_acl_tcam_region_resize(mlxsw_sp, region, new_count); 662 } 663 664 static void mlxsw_sp_acl_tcam_region_parman_move(void *priv, 665 unsigned long from_index, 666 unsigned long to_index, 667 unsigned long count) 668 { 669 struct mlxsw_sp_acl_tcam_region *region = priv; 670 struct mlxsw_sp *mlxsw_sp = region->mlxsw_sp; 671 672 mlxsw_sp_acl_tcam_region_move(mlxsw_sp, region, 673 from_index, to_index, count); 674 } 675 676 static const struct parman_ops mlxsw_sp_acl_tcam_region_parman_ops = { 677 .base_count = MLXSW_SP_ACL_TCAM_REGION_BASE_COUNT, 678 .resize_step = MLXSW_SP_ACL_TCAM_REGION_RESIZE_STEP, 679 .resize = mlxsw_sp_acl_tcam_region_parman_resize, 680 .move = mlxsw_sp_acl_tcam_region_parman_move, 681 .algo = PARMAN_ALGO_TYPE_LSORT, 682 }; 683 684 static struct mlxsw_sp_acl_tcam_region * 685 mlxsw_sp_acl_tcam_region_create(struct mlxsw_sp *mlxsw_sp, 686 struct mlxsw_sp_acl_tcam *tcam, 687 struct mlxsw_afk_element_usage *elusage) 688 { 689 struct mlxsw_afk *afk = mlxsw_sp_acl_afk(mlxsw_sp->acl); 690 struct mlxsw_sp_acl_tcam_region *region; 691 int err; 692 693 region = kzalloc(sizeof(*region), GFP_KERNEL); 694 if (!region) 695 return ERR_PTR(-ENOMEM); 696 INIT_LIST_HEAD(®ion->chunk_list); 697 region->mlxsw_sp = mlxsw_sp; 698 699 region->parman = parman_create(&mlxsw_sp_acl_tcam_region_parman_ops, 700 region); 701 if (!region->parman) { 702 err = -ENOMEM; 703 goto err_parman_create; 704 } 705 706 region->key_info = mlxsw_afk_key_info_get(afk, elusage); 707 if (IS_ERR(region->key_info)) { 708 err = PTR_ERR(region->key_info); 709 goto err_key_info_get; 710 } 711 712 err = mlxsw_sp_acl_tcam_region_id_get(tcam, ®ion->id); 713 if (err) 714 goto err_region_id_get; 715 716 err = mlxsw_sp_acl_tcam_region_alloc(mlxsw_sp, region); 717 if (err) 718 goto err_tcam_region_alloc; 719 720 err = mlxsw_sp_acl_tcam_region_enable(mlxsw_sp, region); 721 if (err) 722 goto err_tcam_region_enable; 723 724 err = mlxsw_sp_acl_tcam_region_catchall_add(mlxsw_sp, region); 725 if (err) 726 goto err_tcam_region_catchall_add; 727 728 return region; 729 730 err_tcam_region_catchall_add: 731 mlxsw_sp_acl_tcam_region_disable(mlxsw_sp, region); 732 err_tcam_region_enable: 733 mlxsw_sp_acl_tcam_region_free(mlxsw_sp, region); 734 err_tcam_region_alloc: 735 mlxsw_sp_acl_tcam_region_id_put(tcam, region->id); 736 err_region_id_get: 737 mlxsw_afk_key_info_put(region->key_info); 738 err_key_info_get: 739 parman_destroy(region->parman); 740 err_parman_create: 741 kfree(region); 742 return ERR_PTR(err); 743 } 744 745 static void 746 mlxsw_sp_acl_tcam_region_destroy(struct mlxsw_sp *mlxsw_sp, 747 struct mlxsw_sp_acl_tcam_region *region) 748 { 749 mlxsw_sp_acl_tcam_region_catchall_del(mlxsw_sp, region); 750 mlxsw_sp_acl_tcam_region_disable(mlxsw_sp, region); 751 mlxsw_sp_acl_tcam_region_free(mlxsw_sp, region); 752 mlxsw_sp_acl_tcam_region_id_put(region->group->tcam, region->id); 753 mlxsw_afk_key_info_put(region->key_info); 754 parman_destroy(region->parman); 755 kfree(region); 756 } 757 758 static int 759 mlxsw_sp_acl_tcam_chunk_assoc(struct mlxsw_sp *mlxsw_sp, 760 struct mlxsw_sp_acl_tcam_group *group, 761 unsigned int priority, 762 struct mlxsw_afk_element_usage *elusage, 763 struct mlxsw_sp_acl_tcam_chunk *chunk) 764 { 765 struct mlxsw_sp_acl_tcam_region *region; 766 bool region_created = false; 767 bool need_split; 768 int err; 769 770 region = mlxsw_sp_acl_tcam_group_region_find(group, priority, elusage, 771 &need_split); 772 if (region && need_split) { 773 /* According to priority, the chunk should belong to an 774 * existing region. However, this chunk needs elements 775 * that region does not contain. We need to split the existing 776 * region into two and create a new region for this chunk 777 * in between. This is not supported now. 778 */ 779 return -EOPNOTSUPP; 780 } 781 if (!region) { 782 struct mlxsw_afk_element_usage region_elusage; 783 784 mlxsw_sp_acl_tcam_group_use_patterns(group, elusage, 785 ®ion_elusage); 786 region = mlxsw_sp_acl_tcam_region_create(mlxsw_sp, group->tcam, 787 ®ion_elusage); 788 if (IS_ERR(region)) 789 return PTR_ERR(region); 790 region_created = true; 791 } 792 793 chunk->region = region; 794 list_add_tail(&chunk->list, ®ion->chunk_list); 795 796 if (!region_created) 797 return 0; 798 799 err = mlxsw_sp_acl_tcam_group_region_attach(mlxsw_sp, group, region); 800 if (err) 801 goto err_group_region_attach; 802 803 return 0; 804 805 err_group_region_attach: 806 mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, region); 807 return err; 808 } 809 810 static void 811 mlxsw_sp_acl_tcam_chunk_deassoc(struct mlxsw_sp *mlxsw_sp, 812 struct mlxsw_sp_acl_tcam_chunk *chunk) 813 { 814 struct mlxsw_sp_acl_tcam_region *region = chunk->region; 815 816 list_del(&chunk->list); 817 if (list_empty(®ion->chunk_list)) { 818 mlxsw_sp_acl_tcam_group_region_detach(mlxsw_sp, region); 819 mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, region); 820 } 821 } 822 823 static struct mlxsw_sp_acl_tcam_chunk * 824 mlxsw_sp_acl_tcam_chunk_create(struct mlxsw_sp *mlxsw_sp, 825 struct mlxsw_sp_acl_tcam_group *group, 826 unsigned int priority, 827 struct mlxsw_afk_element_usage *elusage) 828 { 829 struct mlxsw_sp_acl_tcam_chunk *chunk; 830 int err; 831 832 if (priority == MLXSW_SP_ACL_TCAM_CATCHALL_PRIO) 833 return ERR_PTR(-EINVAL); 834 835 chunk = kzalloc(sizeof(*chunk), GFP_KERNEL); 836 if (!chunk) 837 return ERR_PTR(-ENOMEM); 838 chunk->priority = priority; 839 chunk->group = group; 840 chunk->ref_count = 1; 841 842 err = mlxsw_sp_acl_tcam_chunk_assoc(mlxsw_sp, group, priority, 843 elusage, chunk); 844 if (err) 845 goto err_chunk_assoc; 846 847 parman_prio_init(chunk->region->parman, &chunk->parman_prio, priority); 848 849 err = rhashtable_insert_fast(&group->chunk_ht, &chunk->ht_node, 850 mlxsw_sp_acl_tcam_chunk_ht_params); 851 if (err) 852 goto err_rhashtable_insert; 853 854 return chunk; 855 856 err_rhashtable_insert: 857 parman_prio_fini(&chunk->parman_prio); 858 mlxsw_sp_acl_tcam_chunk_deassoc(mlxsw_sp, chunk); 859 err_chunk_assoc: 860 kfree(chunk); 861 return ERR_PTR(err); 862 } 863 864 static void 865 mlxsw_sp_acl_tcam_chunk_destroy(struct mlxsw_sp *mlxsw_sp, 866 struct mlxsw_sp_acl_tcam_chunk *chunk) 867 { 868 struct mlxsw_sp_acl_tcam_group *group = chunk->group; 869 870 rhashtable_remove_fast(&group->chunk_ht, &chunk->ht_node, 871 mlxsw_sp_acl_tcam_chunk_ht_params); 872 parman_prio_fini(&chunk->parman_prio); 873 mlxsw_sp_acl_tcam_chunk_deassoc(mlxsw_sp, chunk); 874 kfree(chunk); 875 } 876 877 static struct mlxsw_sp_acl_tcam_chunk * 878 mlxsw_sp_acl_tcam_chunk_get(struct mlxsw_sp *mlxsw_sp, 879 struct mlxsw_sp_acl_tcam_group *group, 880 unsigned int priority, 881 struct mlxsw_afk_element_usage *elusage) 882 { 883 struct mlxsw_sp_acl_tcam_chunk *chunk; 884 885 chunk = rhashtable_lookup_fast(&group->chunk_ht, &priority, 886 mlxsw_sp_acl_tcam_chunk_ht_params); 887 if (chunk) { 888 if (WARN_ON(!mlxsw_afk_key_info_subset(chunk->region->key_info, 889 elusage))) 890 return ERR_PTR(-EINVAL); 891 chunk->ref_count++; 892 return chunk; 893 } 894 return mlxsw_sp_acl_tcam_chunk_create(mlxsw_sp, group, 895 priority, elusage); 896 } 897 898 static void mlxsw_sp_acl_tcam_chunk_put(struct mlxsw_sp *mlxsw_sp, 899 struct mlxsw_sp_acl_tcam_chunk *chunk) 900 { 901 if (--chunk->ref_count) 902 return; 903 mlxsw_sp_acl_tcam_chunk_destroy(mlxsw_sp, chunk); 904 } 905 906 static int mlxsw_sp_acl_tcam_entry_add(struct mlxsw_sp *mlxsw_sp, 907 struct mlxsw_sp_acl_tcam_group *group, 908 struct mlxsw_sp_acl_tcam_entry *entry, 909 struct mlxsw_sp_acl_rule_info *rulei) 910 { 911 struct mlxsw_sp_acl_tcam_chunk *chunk; 912 struct mlxsw_sp_acl_tcam_region *region; 913 int err; 914 915 chunk = mlxsw_sp_acl_tcam_chunk_get(mlxsw_sp, group, rulei->priority, 916 &rulei->values.elusage); 917 if (IS_ERR(chunk)) 918 return PTR_ERR(chunk); 919 920 region = chunk->region; 921 err = parman_item_add(region->parman, &chunk->parman_prio, 922 &entry->parman_item); 923 if (err) 924 goto err_parman_item_add; 925 926 err = mlxsw_sp_acl_tcam_region_entry_insert(mlxsw_sp, region, 927 entry->parman_item.index, 928 rulei); 929 if (err) 930 goto err_rule_insert; 931 entry->chunk = chunk; 932 933 return 0; 934 935 err_rule_insert: 936 parman_item_remove(region->parman, &chunk->parman_prio, 937 &entry->parman_item); 938 err_parman_item_add: 939 mlxsw_sp_acl_tcam_chunk_put(mlxsw_sp, chunk); 940 return err; 941 } 942 943 static void mlxsw_sp_acl_tcam_entry_del(struct mlxsw_sp *mlxsw_sp, 944 struct mlxsw_sp_acl_tcam_entry *entry) 945 { 946 struct mlxsw_sp_acl_tcam_chunk *chunk = entry->chunk; 947 struct mlxsw_sp_acl_tcam_region *region = chunk->region; 948 949 mlxsw_sp_acl_tcam_region_entry_remove(mlxsw_sp, region, 950 entry->parman_item.index); 951 parman_item_remove(region->parman, &chunk->parman_prio, 952 &entry->parman_item); 953 mlxsw_sp_acl_tcam_chunk_put(mlxsw_sp, chunk); 954 } 955 956 static int 957 mlxsw_sp_acl_tcam_entry_activity_get(struct mlxsw_sp *mlxsw_sp, 958 struct mlxsw_sp_acl_tcam_entry *entry, 959 bool *activity) 960 { 961 struct mlxsw_sp_acl_tcam_chunk *chunk = entry->chunk; 962 struct mlxsw_sp_acl_tcam_region *region = chunk->region; 963 964 return mlxsw_sp_acl_tcam_region_entry_activity_get(mlxsw_sp, region, 965 entry->parman_item.index, 966 activity); 967 } 968 969 static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv4[] = { 970 MLXSW_AFK_ELEMENT_SRC_SYS_PORT, 971 MLXSW_AFK_ELEMENT_DMAC, 972 MLXSW_AFK_ELEMENT_SMAC, 973 MLXSW_AFK_ELEMENT_ETHERTYPE, 974 MLXSW_AFK_ELEMENT_IP_PROTO, 975 MLXSW_AFK_ELEMENT_SRC_IP4, 976 MLXSW_AFK_ELEMENT_DST_IP4, 977 MLXSW_AFK_ELEMENT_DST_L4_PORT, 978 MLXSW_AFK_ELEMENT_SRC_L4_PORT, 979 MLXSW_AFK_ELEMENT_VID, 980 MLXSW_AFK_ELEMENT_PCP, 981 MLXSW_AFK_ELEMENT_TCP_FLAGS, 982 MLXSW_AFK_ELEMENT_IP_TTL_, 983 MLXSW_AFK_ELEMENT_IP_ECN, 984 MLXSW_AFK_ELEMENT_IP_DSCP, 985 }; 986 987 static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv6[] = { 988 MLXSW_AFK_ELEMENT_ETHERTYPE, 989 MLXSW_AFK_ELEMENT_IP_PROTO, 990 MLXSW_AFK_ELEMENT_SRC_IP6_HI, 991 MLXSW_AFK_ELEMENT_SRC_IP6_LO, 992 MLXSW_AFK_ELEMENT_DST_IP6_HI, 993 MLXSW_AFK_ELEMENT_DST_IP6_LO, 994 MLXSW_AFK_ELEMENT_DST_L4_PORT, 995 MLXSW_AFK_ELEMENT_SRC_L4_PORT, 996 }; 997 998 static const struct mlxsw_sp_acl_tcam_pattern mlxsw_sp_acl_tcam_patterns[] = { 999 { 1000 .elements = mlxsw_sp_acl_tcam_pattern_ipv4, 1001 .elements_count = ARRAY_SIZE(mlxsw_sp_acl_tcam_pattern_ipv4), 1002 }, 1003 { 1004 .elements = mlxsw_sp_acl_tcam_pattern_ipv6, 1005 .elements_count = ARRAY_SIZE(mlxsw_sp_acl_tcam_pattern_ipv6), 1006 }, 1007 }; 1008 1009 #define MLXSW_SP_ACL_TCAM_PATTERNS_COUNT \ 1010 ARRAY_SIZE(mlxsw_sp_acl_tcam_patterns) 1011 1012 struct mlxsw_sp_acl_tcam_flower_ruleset { 1013 struct mlxsw_sp_acl_tcam_group group; 1014 }; 1015 1016 struct mlxsw_sp_acl_tcam_flower_rule { 1017 struct mlxsw_sp_acl_tcam_entry entry; 1018 }; 1019 1020 static int 1021 mlxsw_sp_acl_tcam_flower_ruleset_add(struct mlxsw_sp *mlxsw_sp, 1022 void *priv, void *ruleset_priv) 1023 { 1024 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv; 1025 struct mlxsw_sp_acl_tcam *tcam = priv; 1026 1027 return mlxsw_sp_acl_tcam_group_add(mlxsw_sp, tcam, &ruleset->group, 1028 mlxsw_sp_acl_tcam_patterns, 1029 MLXSW_SP_ACL_TCAM_PATTERNS_COUNT); 1030 } 1031 1032 static void 1033 mlxsw_sp_acl_tcam_flower_ruleset_del(struct mlxsw_sp *mlxsw_sp, 1034 void *ruleset_priv) 1035 { 1036 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv; 1037 1038 mlxsw_sp_acl_tcam_group_del(mlxsw_sp, &ruleset->group); 1039 } 1040 1041 static int 1042 mlxsw_sp_acl_tcam_flower_ruleset_bind(struct mlxsw_sp *mlxsw_sp, 1043 void *ruleset_priv, 1044 struct mlxsw_sp_port *mlxsw_sp_port, 1045 bool ingress) 1046 { 1047 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv; 1048 1049 return mlxsw_sp_acl_tcam_group_bind(mlxsw_sp, &ruleset->group, 1050 mlxsw_sp_port, ingress); 1051 } 1052 1053 static void 1054 mlxsw_sp_acl_tcam_flower_ruleset_unbind(struct mlxsw_sp *mlxsw_sp, 1055 void *ruleset_priv, 1056 struct mlxsw_sp_port *mlxsw_sp_port, 1057 bool ingress) 1058 { 1059 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv; 1060 1061 mlxsw_sp_acl_tcam_group_unbind(mlxsw_sp, &ruleset->group, 1062 mlxsw_sp_port, ingress); 1063 } 1064 1065 static u16 1066 mlxsw_sp_acl_tcam_flower_ruleset_group_id(void *ruleset_priv) 1067 { 1068 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv; 1069 1070 return mlxsw_sp_acl_tcam_group_id(&ruleset->group); 1071 } 1072 1073 static int 1074 mlxsw_sp_acl_tcam_flower_rule_add(struct mlxsw_sp *mlxsw_sp, 1075 void *ruleset_priv, void *rule_priv, 1076 struct mlxsw_sp_acl_rule_info *rulei) 1077 { 1078 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv; 1079 struct mlxsw_sp_acl_tcam_flower_rule *rule = rule_priv; 1080 1081 return mlxsw_sp_acl_tcam_entry_add(mlxsw_sp, &ruleset->group, 1082 &rule->entry, rulei); 1083 } 1084 1085 static void 1086 mlxsw_sp_acl_tcam_flower_rule_del(struct mlxsw_sp *mlxsw_sp, void *rule_priv) 1087 { 1088 struct mlxsw_sp_acl_tcam_flower_rule *rule = rule_priv; 1089 1090 mlxsw_sp_acl_tcam_entry_del(mlxsw_sp, &rule->entry); 1091 } 1092 1093 static int 1094 mlxsw_sp_acl_tcam_flower_rule_activity_get(struct mlxsw_sp *mlxsw_sp, 1095 void *rule_priv, bool *activity) 1096 { 1097 struct mlxsw_sp_acl_tcam_flower_rule *rule = rule_priv; 1098 1099 return mlxsw_sp_acl_tcam_entry_activity_get(mlxsw_sp, &rule->entry, 1100 activity); 1101 } 1102 1103 static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_flower_ops = { 1104 .ruleset_priv_size = sizeof(struct mlxsw_sp_acl_tcam_flower_ruleset), 1105 .ruleset_add = mlxsw_sp_acl_tcam_flower_ruleset_add, 1106 .ruleset_del = mlxsw_sp_acl_tcam_flower_ruleset_del, 1107 .ruleset_bind = mlxsw_sp_acl_tcam_flower_ruleset_bind, 1108 .ruleset_unbind = mlxsw_sp_acl_tcam_flower_ruleset_unbind, 1109 .ruleset_group_id = mlxsw_sp_acl_tcam_flower_ruleset_group_id, 1110 .rule_priv_size = sizeof(struct mlxsw_sp_acl_tcam_flower_rule), 1111 .rule_add = mlxsw_sp_acl_tcam_flower_rule_add, 1112 .rule_del = mlxsw_sp_acl_tcam_flower_rule_del, 1113 .rule_activity_get = mlxsw_sp_acl_tcam_flower_rule_activity_get, 1114 }; 1115 1116 static const struct mlxsw_sp_acl_profile_ops * 1117 mlxsw_sp_acl_tcam_profile_ops_arr[] = { 1118 [MLXSW_SP_ACL_PROFILE_FLOWER] = &mlxsw_sp_acl_tcam_flower_ops, 1119 }; 1120 1121 static const struct mlxsw_sp_acl_profile_ops * 1122 mlxsw_sp_acl_tcam_profile_ops(struct mlxsw_sp *mlxsw_sp, 1123 enum mlxsw_sp_acl_profile profile) 1124 { 1125 const struct mlxsw_sp_acl_profile_ops *ops; 1126 1127 if (WARN_ON(profile >= ARRAY_SIZE(mlxsw_sp_acl_tcam_profile_ops_arr))) 1128 return NULL; 1129 ops = mlxsw_sp_acl_tcam_profile_ops_arr[profile]; 1130 if (WARN_ON(!ops)) 1131 return NULL; 1132 return ops; 1133 } 1134 1135 const struct mlxsw_sp_acl_ops mlxsw_sp_acl_tcam_ops = { 1136 .priv_size = sizeof(struct mlxsw_sp_acl_tcam), 1137 .init = mlxsw_sp_acl_tcam_init, 1138 .fini = mlxsw_sp_acl_tcam_fini, 1139 .profile_ops = mlxsw_sp_acl_tcam_profile_ops, 1140 }; 1141