1953 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* 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#include <linux/mutex.h>12#include <linux/refcount.h>13#include <linux/idr.h>14#include <net/devlink.h>15#include <trace/events/mlxsw.h>16 17#include "reg.h"18#include "core.h"19#include "resources.h"20#include "spectrum.h"21#include "spectrum_acl_tcam.h"22#include "core_acl_flex_keys.h"23 24size_t mlxsw_sp_acl_tcam_priv_size(struct mlxsw_sp *mlxsw_sp)25{26 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;27 28 return ops->priv_size;29}30 31#define MLXSW_SP_ACL_TCAM_VREGION_REHASH_INTRVL_DFLT 5000 /* ms */32#define MLXSW_SP_ACL_TCAM_VREGION_REHASH_INTRVL_MIN 3000 /* ms */33#define MLXSW_SP_ACL_TCAM_VREGION_REHASH_CREDITS 100 /* number of entries */34 35int mlxsw_sp_acl_tcam_priority_get(struct mlxsw_sp *mlxsw_sp,36 struct mlxsw_sp_acl_rule_info *rulei,37 u32 *priority, bool fillup_priority)38{39 u64 max_priority;40 41 if (!fillup_priority) {42 *priority = 0;43 return 0;44 }45 46 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, KVD_SIZE))47 return -EIO;48 49 /* Priority range is 1..cap_kvd_size-1. */50 max_priority = MLXSW_CORE_RES_GET(mlxsw_sp->core, KVD_SIZE) - 1;51 if (rulei->priority >= max_priority)52 return -EINVAL;53 54 /* Unlike in TC, in HW, higher number means higher priority. */55 *priority = max_priority - rulei->priority;56 return 0;57}58 59static int mlxsw_sp_acl_tcam_region_id_get(struct mlxsw_sp_acl_tcam *tcam,60 u16 *p_id)61{62 int id;63 64 id = ida_alloc_max(&tcam->used_regions, tcam->max_regions - 1,65 GFP_KERNEL);66 if (id < 0)67 return id;68 69 *p_id = id;70 71 return 0;72}73 74static void mlxsw_sp_acl_tcam_region_id_put(struct mlxsw_sp_acl_tcam *tcam,75 u16 id)76{77 ida_free(&tcam->used_regions, id);78}79 80static int mlxsw_sp_acl_tcam_group_id_get(struct mlxsw_sp_acl_tcam *tcam,81 u16 *p_id)82{83 int id;84 85 id = ida_alloc_max(&tcam->used_groups, tcam->max_groups - 1,86 GFP_KERNEL);87 if (id < 0)88 return id;89 90 *p_id = id;91 92 return 0;93}94 95static void mlxsw_sp_acl_tcam_group_id_put(struct mlxsw_sp_acl_tcam *tcam,96 u16 id)97{98 ida_free(&tcam->used_groups, id);99}100 101struct mlxsw_sp_acl_tcam_pattern {102 const enum mlxsw_afk_element *elements;103 unsigned int elements_count;104};105 106struct mlxsw_sp_acl_tcam_group {107 struct mlxsw_sp_acl_tcam *tcam;108 u16 id;109 struct mutex lock; /* guards region list updates */110 struct list_head region_list;111 unsigned int region_count;112};113 114struct mlxsw_sp_acl_tcam_vgroup {115 struct mlxsw_sp_acl_tcam_group group;116 struct list_head vregion_list;117 struct rhashtable vchunk_ht;118 const struct mlxsw_sp_acl_tcam_pattern *patterns;119 unsigned int patterns_count;120 bool tmplt_elusage_set;121 struct mlxsw_afk_element_usage tmplt_elusage;122 bool vregion_rehash_enabled;123 unsigned int *p_min_prio;124 unsigned int *p_max_prio;125};126 127struct mlxsw_sp_acl_tcam_rehash_ctx {128 void *hints_priv;129 bool this_is_rollback;130 struct mlxsw_sp_acl_tcam_vchunk *current_vchunk; /* vchunk being131 * currently migrated.132 */133 struct mlxsw_sp_acl_tcam_ventry *start_ventry; /* ventry to start134 * migration from in135 * a vchunk being136 * currently migrated.137 */138 struct mlxsw_sp_acl_tcam_ventry *stop_ventry; /* ventry to stop139 * migration at140 * a vchunk being141 * currently migrated.142 */143};144 145struct mlxsw_sp_acl_tcam_vregion {146 struct mutex lock; /* Protects consistency of region, region2 pointers147 * and vchunk_list.148 */149 struct mlxsw_sp_acl_tcam_region *region;150 struct mlxsw_sp_acl_tcam_region *region2; /* Used during migration */151 struct list_head list; /* Member of a TCAM group */152 struct list_head tlist; /* Member of a TCAM */153 struct list_head vchunk_list; /* List of vchunks under this vregion */154 struct mlxsw_afk_key_info *key_info;155 struct mlxsw_sp_acl_tcam *tcam;156 struct mlxsw_sp_acl_tcam_vgroup *vgroup;157 struct {158 struct delayed_work dw;159 struct mlxsw_sp_acl_tcam_rehash_ctx ctx;160 } rehash;161 struct mlxsw_sp *mlxsw_sp;162 refcount_t ref_count;163};164 165struct mlxsw_sp_acl_tcam_vchunk;166 167struct mlxsw_sp_acl_tcam_chunk {168 struct mlxsw_sp_acl_tcam_vchunk *vchunk;169 struct mlxsw_sp_acl_tcam_region *region;170 unsigned long priv[];171 /* priv has to be always the last item */172};173 174struct mlxsw_sp_acl_tcam_vchunk {175 struct mlxsw_sp_acl_tcam_chunk *chunk;176 struct mlxsw_sp_acl_tcam_chunk *chunk2; /* Used during migration */177 struct list_head list; /* Member of a TCAM vregion */178 struct rhash_head ht_node; /* Member of a chunk HT */179 struct list_head ventry_list;180 unsigned int priority; /* Priority within the vregion and group */181 struct mlxsw_sp_acl_tcam_vgroup *vgroup;182 struct mlxsw_sp_acl_tcam_vregion *vregion;183 refcount_t ref_count;184};185 186struct mlxsw_sp_acl_tcam_entry {187 struct mlxsw_sp_acl_tcam_ventry *ventry;188 struct mlxsw_sp_acl_tcam_chunk *chunk;189 unsigned long priv[];190 /* priv has to be always the last item */191};192 193struct mlxsw_sp_acl_tcam_ventry {194 struct mlxsw_sp_acl_tcam_entry *entry;195 struct list_head list; /* Member of a TCAM vchunk */196 struct mlxsw_sp_acl_tcam_vchunk *vchunk;197 struct mlxsw_sp_acl_rule_info *rulei;198};199 200static const struct rhashtable_params mlxsw_sp_acl_tcam_vchunk_ht_params = {201 .key_len = sizeof(unsigned int),202 .key_offset = offsetof(struct mlxsw_sp_acl_tcam_vchunk, priority),203 .head_offset = offsetof(struct mlxsw_sp_acl_tcam_vchunk, ht_node),204 .automatic_shrinking = true,205};206 207static int mlxsw_sp_acl_tcam_group_update(struct mlxsw_sp *mlxsw_sp,208 struct mlxsw_sp_acl_tcam_group *group)209{210 struct mlxsw_sp_acl_tcam_region *region;211 char pagt_pl[MLXSW_REG_PAGT_LEN];212 int acl_index = 0;213 214 mlxsw_reg_pagt_pack(pagt_pl, group->id);215 list_for_each_entry(region, &group->region_list, list) {216 bool multi = false;217 218 /* Check if the next entry in the list has the same vregion. */219 if (region->list.next != &group->region_list &&220 list_next_entry(region, list)->vregion == region->vregion)221 multi = true;222 mlxsw_reg_pagt_acl_id_pack(pagt_pl, acl_index++,223 region->id, multi);224 }225 mlxsw_reg_pagt_size_set(pagt_pl, acl_index);226 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pagt), pagt_pl);227}228 229static int230mlxsw_sp_acl_tcam_group_add(struct mlxsw_sp_acl_tcam *tcam,231 struct mlxsw_sp_acl_tcam_group *group)232{233 int err;234 235 group->tcam = tcam;236 INIT_LIST_HEAD(&group->region_list);237 238 err = mlxsw_sp_acl_tcam_group_id_get(tcam, &group->id);239 if (err)240 return err;241 242 mutex_init(&group->lock);243 244 return 0;245}246 247static void mlxsw_sp_acl_tcam_group_del(struct mlxsw_sp_acl_tcam_group *group)248{249 struct mlxsw_sp_acl_tcam *tcam = group->tcam;250 251 mutex_destroy(&group->lock);252 mlxsw_sp_acl_tcam_group_id_put(tcam, group->id);253 WARN_ON(!list_empty(&group->region_list));254}255 256static int257mlxsw_sp_acl_tcam_vgroup_add(struct mlxsw_sp *mlxsw_sp,258 struct mlxsw_sp_acl_tcam *tcam,259 struct mlxsw_sp_acl_tcam_vgroup *vgroup,260 const struct mlxsw_sp_acl_tcam_pattern *patterns,261 unsigned int patterns_count,262 struct mlxsw_afk_element_usage *tmplt_elusage,263 bool vregion_rehash_enabled,264 unsigned int *p_min_prio,265 unsigned int *p_max_prio)266{267 int err;268 269 vgroup->patterns = patterns;270 vgroup->patterns_count = patterns_count;271 vgroup->vregion_rehash_enabled = vregion_rehash_enabled;272 vgroup->p_min_prio = p_min_prio;273 vgroup->p_max_prio = p_max_prio;274 275 if (tmplt_elusage) {276 vgroup->tmplt_elusage_set = true;277 memcpy(&vgroup->tmplt_elusage, tmplt_elusage,278 sizeof(vgroup->tmplt_elusage));279 }280 INIT_LIST_HEAD(&vgroup->vregion_list);281 282 err = mlxsw_sp_acl_tcam_group_add(tcam, &vgroup->group);283 if (err)284 return err;285 286 err = rhashtable_init(&vgroup->vchunk_ht,287 &mlxsw_sp_acl_tcam_vchunk_ht_params);288 if (err)289 goto err_rhashtable_init;290 291 return 0;292 293err_rhashtable_init:294 mlxsw_sp_acl_tcam_group_del(&vgroup->group);295 return err;296}297 298static void299mlxsw_sp_acl_tcam_vgroup_del(struct mlxsw_sp_acl_tcam_vgroup *vgroup)300{301 rhashtable_destroy(&vgroup->vchunk_ht);302 mlxsw_sp_acl_tcam_group_del(&vgroup->group);303 WARN_ON(!list_empty(&vgroup->vregion_list));304}305 306static int307mlxsw_sp_acl_tcam_group_bind(struct mlxsw_sp *mlxsw_sp,308 struct mlxsw_sp_acl_tcam_group *group,309 struct mlxsw_sp_port *mlxsw_sp_port,310 bool ingress)311{312 char ppbt_pl[MLXSW_REG_PPBT_LEN];313 314 mlxsw_reg_ppbt_pack(ppbt_pl, ingress ? MLXSW_REG_PXBT_E_IACL :315 MLXSW_REG_PXBT_E_EACL,316 MLXSW_REG_PXBT_OP_BIND, mlxsw_sp_port->local_port,317 group->id);318 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppbt), ppbt_pl);319}320 321static void322mlxsw_sp_acl_tcam_group_unbind(struct mlxsw_sp *mlxsw_sp,323 struct mlxsw_sp_acl_tcam_group *group,324 struct mlxsw_sp_port *mlxsw_sp_port,325 bool ingress)326{327 char ppbt_pl[MLXSW_REG_PPBT_LEN];328 329 mlxsw_reg_ppbt_pack(ppbt_pl, ingress ? MLXSW_REG_PXBT_E_IACL :330 MLXSW_REG_PXBT_E_EACL,331 MLXSW_REG_PXBT_OP_UNBIND, mlxsw_sp_port->local_port,332 group->id);333 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppbt), ppbt_pl);334}335 336static u16337mlxsw_sp_acl_tcam_group_id(struct mlxsw_sp_acl_tcam_group *group)338{339 return group->id;340}341 342static unsigned int343mlxsw_sp_acl_tcam_vregion_prio(struct mlxsw_sp_acl_tcam_vregion *vregion)344{345 struct mlxsw_sp_acl_tcam_vchunk *vchunk;346 347 if (list_empty(&vregion->vchunk_list))348 return 0;349 /* As a priority of a vregion, return priority of the first vchunk */350 vchunk = list_first_entry(&vregion->vchunk_list,351 typeof(*vchunk), list);352 return vchunk->priority;353}354 355static unsigned int356mlxsw_sp_acl_tcam_vregion_max_prio(struct mlxsw_sp_acl_tcam_vregion *vregion)357{358 struct mlxsw_sp_acl_tcam_vchunk *vchunk;359 360 if (list_empty(&vregion->vchunk_list))361 return 0;362 vchunk = list_last_entry(&vregion->vchunk_list,363 typeof(*vchunk), list);364 return vchunk->priority;365}366 367static void368mlxsw_sp_acl_tcam_vgroup_prio_update(struct mlxsw_sp_acl_tcam_vgroup *vgroup)369{370 struct mlxsw_sp_acl_tcam_vregion *vregion;371 372 if (list_empty(&vgroup->vregion_list))373 return;374 vregion = list_first_entry(&vgroup->vregion_list,375 typeof(*vregion), list);376 *vgroup->p_min_prio = mlxsw_sp_acl_tcam_vregion_prio(vregion);377 vregion = list_last_entry(&vgroup->vregion_list,378 typeof(*vregion), list);379 *vgroup->p_max_prio = mlxsw_sp_acl_tcam_vregion_max_prio(vregion);380}381 382static int383mlxsw_sp_acl_tcam_group_region_attach(struct mlxsw_sp *mlxsw_sp,384 struct mlxsw_sp_acl_tcam_group *group,385 struct mlxsw_sp_acl_tcam_region *region,386 unsigned int priority,387 struct mlxsw_sp_acl_tcam_region *next_region)388{389 struct mlxsw_sp_acl_tcam_region *region2;390 struct list_head *pos;391 int err;392 393 mutex_lock(&group->lock);394 if (group->region_count == group->tcam->max_group_size) {395 err = -ENOBUFS;396 goto err_region_count_check;397 }398 399 if (next_region) {400 /* If the next region is defined, place the new one401 * before it. The next one is a sibling.402 */403 pos = &next_region->list;404 } else {405 /* Position the region inside the list according to priority */406 list_for_each(pos, &group->region_list) {407 region2 = list_entry(pos, typeof(*region2), list);408 if (mlxsw_sp_acl_tcam_vregion_prio(region2->vregion) >409 priority)410 break;411 }412 }413 list_add_tail(®ion->list, pos);414 region->group = group;415 416 err = mlxsw_sp_acl_tcam_group_update(mlxsw_sp, group);417 if (err)418 goto err_group_update;419 420 group->region_count++;421 mutex_unlock(&group->lock);422 return 0;423 424err_group_update:425 list_del(®ion->list);426err_region_count_check:427 mutex_unlock(&group->lock);428 return err;429}430 431static void432mlxsw_sp_acl_tcam_group_region_detach(struct mlxsw_sp *mlxsw_sp,433 struct mlxsw_sp_acl_tcam_region *region)434{435 struct mlxsw_sp_acl_tcam_group *group = region->group;436 437 mutex_lock(&group->lock);438 list_del(®ion->list);439 group->region_count--;440 mlxsw_sp_acl_tcam_group_update(mlxsw_sp, group);441 mutex_unlock(&group->lock);442}443 444static int445mlxsw_sp_acl_tcam_vgroup_vregion_attach(struct mlxsw_sp *mlxsw_sp,446 struct mlxsw_sp_acl_tcam_vgroup *vgroup,447 struct mlxsw_sp_acl_tcam_vregion *vregion,448 unsigned int priority)449{450 struct mlxsw_sp_acl_tcam_vregion *vregion2;451 struct list_head *pos;452 int err;453 454 /* Position the vregion inside the list according to priority */455 list_for_each(pos, &vgroup->vregion_list) {456 vregion2 = list_entry(pos, typeof(*vregion2), list);457 if (mlxsw_sp_acl_tcam_vregion_prio(vregion2) > priority)458 break;459 }460 list_add_tail(&vregion->list, pos);461 462 err = mlxsw_sp_acl_tcam_group_region_attach(mlxsw_sp, &vgroup->group,463 vregion->region,464 priority, NULL);465 if (err)466 goto err_region_attach;467 468 return 0;469 470err_region_attach:471 list_del(&vregion->list);472 return err;473}474 475static void476mlxsw_sp_acl_tcam_vgroup_vregion_detach(struct mlxsw_sp *mlxsw_sp,477 struct mlxsw_sp_acl_tcam_vregion *vregion)478{479 list_del(&vregion->list);480 if (vregion->region2)481 mlxsw_sp_acl_tcam_group_region_detach(mlxsw_sp,482 vregion->region2);483 mlxsw_sp_acl_tcam_group_region_detach(mlxsw_sp, vregion->region);484}485 486static struct mlxsw_sp_acl_tcam_vregion *487mlxsw_sp_acl_tcam_vgroup_vregion_find(struct mlxsw_sp_acl_tcam_vgroup *vgroup,488 unsigned int priority,489 struct mlxsw_afk_element_usage *elusage,490 bool *p_need_split)491{492 struct mlxsw_sp_acl_tcam_vregion *vregion, *vregion2;493 struct list_head *pos;494 bool issubset;495 496 list_for_each(pos, &vgroup->vregion_list) {497 vregion = list_entry(pos, typeof(*vregion), list);498 499 /* First, check if the requested priority does not rather belong500 * under some of the next vregions.501 */502 if (pos->next != &vgroup->vregion_list) { /* not last */503 vregion2 = list_entry(pos->next, typeof(*vregion2),504 list);505 if (priority >=506 mlxsw_sp_acl_tcam_vregion_prio(vregion2))507 continue;508 }509 510 issubset = mlxsw_afk_key_info_subset(vregion->key_info,511 elusage);512 513 /* If requested element usage would not fit and the priority514 * is lower than the currently inspected vregion we cannot515 * use this region, so return NULL to indicate new vregion has516 * to be created.517 */518 if (!issubset &&519 priority < mlxsw_sp_acl_tcam_vregion_prio(vregion))520 return NULL;521 522 /* If requested element usage would not fit and the priority523 * is higher than the currently inspected vregion we cannot524 * use this vregion. There is still some hope that the next525 * vregion would be the fit. So let it be processed and526 * eventually break at the check right above this.527 */528 if (!issubset &&529 priority > mlxsw_sp_acl_tcam_vregion_max_prio(vregion))530 continue;531 532 /* Indicate if the vregion needs to be split in order to add533 * the requested priority. Split is needed when requested534 * element usage won't fit into the found vregion.535 */536 *p_need_split = !issubset;537 return vregion;538 }539 return NULL; /* New vregion has to be created. */540}541 542static void543mlxsw_sp_acl_tcam_vgroup_use_patterns(struct mlxsw_sp_acl_tcam_vgroup *vgroup,544 struct mlxsw_afk_element_usage *elusage,545 struct mlxsw_afk_element_usage *out)546{547 const struct mlxsw_sp_acl_tcam_pattern *pattern;548 int i;549 550 /* In case the template is set, we don't have to look up the pattern551 * and just use the template.552 */553 if (vgroup->tmplt_elusage_set) {554 memcpy(out, &vgroup->tmplt_elusage, sizeof(*out));555 WARN_ON(!mlxsw_afk_element_usage_subset(elusage, out));556 return;557 }558 559 for (i = 0; i < vgroup->patterns_count; i++) {560 pattern = &vgroup->patterns[i];561 mlxsw_afk_element_usage_fill(out, pattern->elements,562 pattern->elements_count);563 if (mlxsw_afk_element_usage_subset(elusage, out))564 return;565 }566 memcpy(out, elusage, sizeof(*out));567}568 569static int570mlxsw_sp_acl_tcam_region_alloc(struct mlxsw_sp *mlxsw_sp,571 struct mlxsw_sp_acl_tcam_region *region)572{573 struct mlxsw_afk_key_info *key_info = region->key_info;574 char ptar_pl[MLXSW_REG_PTAR_LEN];575 unsigned int encodings_count;576 int i;577 int err;578 579 mlxsw_reg_ptar_pack(ptar_pl, MLXSW_REG_PTAR_OP_ALLOC,580 region->key_type,581 MLXSW_SP_ACL_TCAM_REGION_BASE_COUNT,582 region->id, region->tcam_region_info);583 encodings_count = mlxsw_afk_key_info_blocks_count_get(key_info);584 for (i = 0; i < encodings_count; i++) {585 u16 encoding;586 587 encoding = mlxsw_afk_key_info_block_encoding_get(key_info, i);588 mlxsw_reg_ptar_key_id_pack(ptar_pl, i, encoding);589 }590 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptar), ptar_pl);591 if (err)592 return err;593 mlxsw_reg_ptar_unpack(ptar_pl, region->tcam_region_info);594 return 0;595}596 597static void598mlxsw_sp_acl_tcam_region_free(struct mlxsw_sp *mlxsw_sp,599 struct mlxsw_sp_acl_tcam_region *region)600{601 char ptar_pl[MLXSW_REG_PTAR_LEN];602 603 mlxsw_reg_ptar_pack(ptar_pl, MLXSW_REG_PTAR_OP_FREE,604 region->key_type, 0, region->id,605 region->tcam_region_info);606 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptar), ptar_pl);607}608 609static int610mlxsw_sp_acl_tcam_region_enable(struct mlxsw_sp *mlxsw_sp,611 struct mlxsw_sp_acl_tcam_region *region)612{613 char pacl_pl[MLXSW_REG_PACL_LEN];614 615 mlxsw_reg_pacl_pack(pacl_pl, region->id, true,616 region->tcam_region_info);617 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pacl), pacl_pl);618}619 620static void621mlxsw_sp_acl_tcam_region_disable(struct mlxsw_sp *mlxsw_sp,622 struct mlxsw_sp_acl_tcam_region *region)623{624 char pacl_pl[MLXSW_REG_PACL_LEN];625 626 mlxsw_reg_pacl_pack(pacl_pl, region->id, false,627 region->tcam_region_info);628 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pacl), pacl_pl);629}630 631static struct mlxsw_sp_acl_tcam_region *632mlxsw_sp_acl_tcam_region_create(struct mlxsw_sp *mlxsw_sp,633 struct mlxsw_sp_acl_tcam *tcam,634 struct mlxsw_sp_acl_tcam_vregion *vregion,635 void *hints_priv)636{637 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;638 struct mlxsw_sp_acl_tcam_region *region;639 int err;640 641 region = kzalloc(sizeof(*region) + ops->region_priv_size, GFP_KERNEL);642 if (!region)643 return ERR_PTR(-ENOMEM);644 region->mlxsw_sp = mlxsw_sp;645 region->vregion = vregion;646 region->key_info = vregion->key_info;647 648 err = mlxsw_sp_acl_tcam_region_id_get(tcam, ®ion->id);649 if (err)650 goto err_region_id_get;651 652 err = ops->region_associate(mlxsw_sp, region);653 if (err)654 goto err_tcam_region_associate;655 656 region->key_type = ops->key_type;657 err = mlxsw_sp_acl_tcam_region_alloc(mlxsw_sp, region);658 if (err)659 goto err_tcam_region_alloc;660 661 err = mlxsw_sp_acl_tcam_region_enable(mlxsw_sp, region);662 if (err)663 goto err_tcam_region_enable;664 665 err = ops->region_init(mlxsw_sp, region->priv, tcam->priv,666 region, hints_priv);667 if (err)668 goto err_tcam_region_init;669 670 return region;671 672err_tcam_region_init:673 mlxsw_sp_acl_tcam_region_disable(mlxsw_sp, region);674err_tcam_region_enable:675 mlxsw_sp_acl_tcam_region_free(mlxsw_sp, region);676err_tcam_region_alloc:677err_tcam_region_associate:678 mlxsw_sp_acl_tcam_region_id_put(tcam, region->id);679err_region_id_get:680 kfree(region);681 return ERR_PTR(err);682}683 684static void685mlxsw_sp_acl_tcam_region_destroy(struct mlxsw_sp *mlxsw_sp,686 struct mlxsw_sp_acl_tcam_region *region)687{688 struct mlxsw_sp_acl_tcam *tcam = mlxsw_sp_acl_to_tcam(mlxsw_sp->acl);689 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;690 691 ops->region_fini(mlxsw_sp, region->priv);692 mlxsw_sp_acl_tcam_region_disable(mlxsw_sp, region);693 mlxsw_sp_acl_tcam_region_free(mlxsw_sp, region);694 mlxsw_sp_acl_tcam_region_id_put(tcam, region->id);695 kfree(region);696}697 698static void699mlxsw_sp_acl_tcam_vregion_rehash_work_schedule(struct mlxsw_sp_acl_tcam_vregion *vregion)700{701 unsigned long interval = vregion->tcam->vregion_rehash_intrvl;702 703 if (!interval)704 return;705 mlxsw_core_schedule_dw(&vregion->rehash.dw,706 msecs_to_jiffies(interval));707}708 709static void710mlxsw_sp_acl_tcam_vregion_rehash(struct mlxsw_sp *mlxsw_sp,711 struct mlxsw_sp_acl_tcam_vregion *vregion,712 int *credits);713 714static void mlxsw_sp_acl_tcam_vregion_rehash_work(struct work_struct *work)715{716 struct mlxsw_sp_acl_tcam_vregion *vregion =717 container_of(work, struct mlxsw_sp_acl_tcam_vregion,718 rehash.dw.work);719 int credits = MLXSW_SP_ACL_TCAM_VREGION_REHASH_CREDITS;720 721 mutex_lock(&vregion->lock);722 mlxsw_sp_acl_tcam_vregion_rehash(vregion->mlxsw_sp, vregion, &credits);723 mutex_unlock(&vregion->lock);724 if (credits < 0)725 /* Rehash gone out of credits so it was interrupted.726 * Schedule the work as soon as possible to continue.727 */728 mlxsw_core_schedule_dw(&vregion->rehash.dw, 0);729 else730 mlxsw_sp_acl_tcam_vregion_rehash_work_schedule(vregion);731}732 733static void734mlxsw_sp_acl_tcam_rehash_ctx_vchunk_reset(struct mlxsw_sp_acl_tcam_rehash_ctx *ctx)735{736 /* The entry markers are relative to the current chunk and therefore737 * needs to be reset together with the chunk marker.738 */739 ctx->current_vchunk = NULL;740 ctx->start_ventry = NULL;741 ctx->stop_ventry = NULL;742}743 744static void745mlxsw_sp_acl_tcam_rehash_ctx_vchunk_changed(struct mlxsw_sp_acl_tcam_vchunk *vchunk)746{747 struct mlxsw_sp_acl_tcam_vregion *vregion = vchunk->vregion;748 749 /* If a rule was added or deleted from vchunk which is currently750 * under rehash migration, we have to reset the ventry pointers751 * to make sure all rules are properly migrated.752 */753 if (vregion->rehash.ctx.current_vchunk == vchunk) {754 vregion->rehash.ctx.start_ventry = NULL;755 vregion->rehash.ctx.stop_ventry = NULL;756 }757}758 759static void760mlxsw_sp_acl_tcam_rehash_ctx_vregion_changed(struct mlxsw_sp_acl_tcam_vregion *vregion)761{762 /* If a chunk was added or deleted from vregion we have to reset763 * the current chunk pointer to make sure all chunks764 * are properly migrated.765 */766 mlxsw_sp_acl_tcam_rehash_ctx_vchunk_reset(&vregion->rehash.ctx);767}768 769static struct mlxsw_sp_acl_tcam_vregion *770mlxsw_sp_acl_tcam_vregion_create(struct mlxsw_sp *mlxsw_sp,771 struct mlxsw_sp_acl_tcam_vgroup *vgroup,772 unsigned int priority,773 struct mlxsw_afk_element_usage *elusage)774{775 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;776 struct mlxsw_afk *afk = mlxsw_sp_acl_afk(mlxsw_sp->acl);777 struct mlxsw_sp_acl_tcam *tcam = vgroup->group.tcam;778 struct mlxsw_sp_acl_tcam_vregion *vregion;779 int err;780 781 vregion = kzalloc(sizeof(*vregion), GFP_KERNEL);782 if (!vregion)783 return ERR_PTR(-ENOMEM);784 INIT_LIST_HEAD(&vregion->vchunk_list);785 mutex_init(&vregion->lock);786 vregion->tcam = tcam;787 vregion->mlxsw_sp = mlxsw_sp;788 vregion->vgroup = vgroup;789 refcount_set(&vregion->ref_count, 1);790 791 vregion->key_info = mlxsw_afk_key_info_get(afk, elusage);792 if (IS_ERR(vregion->key_info)) {793 err = PTR_ERR(vregion->key_info);794 goto err_key_info_get;795 }796 797 vregion->region = mlxsw_sp_acl_tcam_region_create(mlxsw_sp, tcam,798 vregion, NULL);799 if (IS_ERR(vregion->region)) {800 err = PTR_ERR(vregion->region);801 goto err_region_create;802 }803 804 err = mlxsw_sp_acl_tcam_vgroup_vregion_attach(mlxsw_sp, vgroup, vregion,805 priority);806 if (err)807 goto err_vgroup_vregion_attach;808 809 if (vgroup->vregion_rehash_enabled && ops->region_rehash_hints_get) {810 /* Create the delayed work for vregion periodic rehash */811 INIT_DELAYED_WORK(&vregion->rehash.dw,812 mlxsw_sp_acl_tcam_vregion_rehash_work);813 mlxsw_sp_acl_tcam_vregion_rehash_work_schedule(vregion);814 mutex_lock(&tcam->lock);815 list_add_tail(&vregion->tlist, &tcam->vregion_list);816 mutex_unlock(&tcam->lock);817 }818 819 return vregion;820 821err_vgroup_vregion_attach:822 mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, vregion->region);823err_region_create:824 mlxsw_afk_key_info_put(vregion->key_info);825err_key_info_get:826 kfree(vregion);827 return ERR_PTR(err);828}829 830static void831mlxsw_sp_acl_tcam_vregion_destroy(struct mlxsw_sp *mlxsw_sp,832 struct mlxsw_sp_acl_tcam_vregion *vregion)833{834 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;835 struct mlxsw_sp_acl_tcam_vgroup *vgroup = vregion->vgroup;836 struct mlxsw_sp_acl_tcam *tcam = vregion->tcam;837 838 if (vgroup->vregion_rehash_enabled && ops->region_rehash_hints_get) {839 struct mlxsw_sp_acl_tcam_rehash_ctx *ctx = &vregion->rehash.ctx;840 841 mutex_lock(&tcam->lock);842 list_del(&vregion->tlist);843 mutex_unlock(&tcam->lock);844 if (cancel_delayed_work_sync(&vregion->rehash.dw) &&845 ctx->hints_priv)846 ops->region_rehash_hints_put(ctx->hints_priv);847 }848 mlxsw_sp_acl_tcam_vgroup_vregion_detach(mlxsw_sp, vregion);849 if (vregion->region2)850 mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, vregion->region2);851 mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, vregion->region);852 mlxsw_afk_key_info_put(vregion->key_info);853 mutex_destroy(&vregion->lock);854 kfree(vregion);855}856 857static struct mlxsw_sp_acl_tcam_vregion *858mlxsw_sp_acl_tcam_vregion_get(struct mlxsw_sp *mlxsw_sp,859 struct mlxsw_sp_acl_tcam_vgroup *vgroup,860 unsigned int priority,861 struct mlxsw_afk_element_usage *elusage)862{863 struct mlxsw_afk_element_usage vregion_elusage;864 struct mlxsw_sp_acl_tcam_vregion *vregion;865 bool need_split;866 867 vregion = mlxsw_sp_acl_tcam_vgroup_vregion_find(vgroup, priority,868 elusage, &need_split);869 if (vregion) {870 if (need_split) {871 /* According to priority, new vchunk should belong to872 * an existing vregion. However, this vchunk needs873 * elements that vregion does not contain. We need874 * to split the existing vregion into two and create875 * a new vregion for the new vchunk in between.876 * This is not supported now.877 */878 return ERR_PTR(-EOPNOTSUPP);879 }880 refcount_inc(&vregion->ref_count);881 return vregion;882 }883 884 mlxsw_sp_acl_tcam_vgroup_use_patterns(vgroup, elusage,885 &vregion_elusage);886 887 return mlxsw_sp_acl_tcam_vregion_create(mlxsw_sp, vgroup, priority,888 &vregion_elusage);889}890 891static void892mlxsw_sp_acl_tcam_vregion_put(struct mlxsw_sp *mlxsw_sp,893 struct mlxsw_sp_acl_tcam_vregion *vregion)894{895 if (!refcount_dec_and_test(&vregion->ref_count))896 return;897 mlxsw_sp_acl_tcam_vregion_destroy(mlxsw_sp, vregion);898}899 900static struct mlxsw_sp_acl_tcam_chunk *901mlxsw_sp_acl_tcam_chunk_create(struct mlxsw_sp *mlxsw_sp,902 struct mlxsw_sp_acl_tcam_vchunk *vchunk,903 struct mlxsw_sp_acl_tcam_region *region)904{905 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;906 struct mlxsw_sp_acl_tcam_chunk *chunk;907 908 chunk = kzalloc(sizeof(*chunk) + ops->chunk_priv_size, GFP_KERNEL);909 if (!chunk)910 return ERR_PTR(-ENOMEM);911 chunk->vchunk = vchunk;912 chunk->region = region;913 914 ops->chunk_init(region->priv, chunk->priv, vchunk->priority);915 return chunk;916}917 918static void919mlxsw_sp_acl_tcam_chunk_destroy(struct mlxsw_sp *mlxsw_sp,920 struct mlxsw_sp_acl_tcam_chunk *chunk)921{922 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;923 924 ops->chunk_fini(chunk->priv);925 kfree(chunk);926}927 928static struct mlxsw_sp_acl_tcam_vchunk *929mlxsw_sp_acl_tcam_vchunk_create(struct mlxsw_sp *mlxsw_sp,930 struct mlxsw_sp_acl_tcam_vgroup *vgroup,931 unsigned int priority,932 struct mlxsw_afk_element_usage *elusage)933{934 struct mlxsw_sp_acl_tcam_vchunk *vchunk, *vchunk2;935 struct mlxsw_sp_acl_tcam_vregion *vregion;936 struct list_head *pos;937 int err;938 939 if (priority == MLXSW_SP_ACL_TCAM_CATCHALL_PRIO)940 return ERR_PTR(-EINVAL);941 942 vchunk = kzalloc(sizeof(*vchunk), GFP_KERNEL);943 if (!vchunk)944 return ERR_PTR(-ENOMEM);945 INIT_LIST_HEAD(&vchunk->ventry_list);946 vchunk->priority = priority;947 vchunk->vgroup = vgroup;948 refcount_set(&vchunk->ref_count, 1);949 950 vregion = mlxsw_sp_acl_tcam_vregion_get(mlxsw_sp, vgroup,951 priority, elusage);952 if (IS_ERR(vregion)) {953 err = PTR_ERR(vregion);954 goto err_vregion_get;955 }956 957 vchunk->vregion = vregion;958 959 err = rhashtable_insert_fast(&vgroup->vchunk_ht, &vchunk->ht_node,960 mlxsw_sp_acl_tcam_vchunk_ht_params);961 if (err)962 goto err_rhashtable_insert;963 964 mutex_lock(&vregion->lock);965 vchunk->chunk = mlxsw_sp_acl_tcam_chunk_create(mlxsw_sp, vchunk,966 vchunk->vregion->region);967 if (IS_ERR(vchunk->chunk)) {968 mutex_unlock(&vregion->lock);969 err = PTR_ERR(vchunk->chunk);970 goto err_chunk_create;971 }972 973 mlxsw_sp_acl_tcam_rehash_ctx_vregion_changed(vregion);974 975 /* Position the vchunk inside the list according to priority */976 list_for_each(pos, &vregion->vchunk_list) {977 vchunk2 = list_entry(pos, typeof(*vchunk2), list);978 if (vchunk2->priority > priority)979 break;980 }981 list_add_tail(&vchunk->list, pos);982 mutex_unlock(&vregion->lock);983 mlxsw_sp_acl_tcam_vgroup_prio_update(vgroup);984 985 return vchunk;986 987err_chunk_create:988 rhashtable_remove_fast(&vgroup->vchunk_ht, &vchunk->ht_node,989 mlxsw_sp_acl_tcam_vchunk_ht_params);990err_rhashtable_insert:991 mlxsw_sp_acl_tcam_vregion_put(mlxsw_sp, vregion);992err_vregion_get:993 kfree(vchunk);994 return ERR_PTR(err);995}996 997static void998mlxsw_sp_acl_tcam_vchunk_destroy(struct mlxsw_sp *mlxsw_sp,999 struct mlxsw_sp_acl_tcam_vchunk *vchunk)1000{1001 struct mlxsw_sp_acl_tcam_vregion *vregion = vchunk->vregion;1002 struct mlxsw_sp_acl_tcam_vgroup *vgroup = vchunk->vgroup;1003 1004 mutex_lock(&vregion->lock);1005 mlxsw_sp_acl_tcam_rehash_ctx_vregion_changed(vregion);1006 list_del(&vchunk->list);1007 if (vchunk->chunk2)1008 mlxsw_sp_acl_tcam_chunk_destroy(mlxsw_sp, vchunk->chunk2);1009 mlxsw_sp_acl_tcam_chunk_destroy(mlxsw_sp, vchunk->chunk);1010 mutex_unlock(&vregion->lock);1011 rhashtable_remove_fast(&vgroup->vchunk_ht, &vchunk->ht_node,1012 mlxsw_sp_acl_tcam_vchunk_ht_params);1013 mlxsw_sp_acl_tcam_vregion_put(mlxsw_sp, vchunk->vregion);1014 kfree(vchunk);1015 mlxsw_sp_acl_tcam_vgroup_prio_update(vgroup);1016}1017 1018static struct mlxsw_sp_acl_tcam_vchunk *1019mlxsw_sp_acl_tcam_vchunk_get(struct mlxsw_sp *mlxsw_sp,1020 struct mlxsw_sp_acl_tcam_vgroup *vgroup,1021 unsigned int priority,1022 struct mlxsw_afk_element_usage *elusage)1023{1024 struct mlxsw_sp_acl_tcam_vchunk *vchunk;1025 1026 vchunk = rhashtable_lookup_fast(&vgroup->vchunk_ht, &priority,1027 mlxsw_sp_acl_tcam_vchunk_ht_params);1028 if (vchunk) {1029 if (WARN_ON(!mlxsw_afk_key_info_subset(vchunk->vregion->key_info,1030 elusage)))1031 return ERR_PTR(-EINVAL);1032 refcount_inc(&vchunk->ref_count);1033 return vchunk;1034 }1035 return mlxsw_sp_acl_tcam_vchunk_create(mlxsw_sp, vgroup,1036 priority, elusage);1037}1038 1039static void1040mlxsw_sp_acl_tcam_vchunk_put(struct mlxsw_sp *mlxsw_sp,1041 struct mlxsw_sp_acl_tcam_vchunk *vchunk)1042{1043 if (!refcount_dec_and_test(&vchunk->ref_count))1044 return;1045 mlxsw_sp_acl_tcam_vchunk_destroy(mlxsw_sp, vchunk);1046}1047 1048static struct mlxsw_sp_acl_tcam_entry *1049mlxsw_sp_acl_tcam_entry_create(struct mlxsw_sp *mlxsw_sp,1050 struct mlxsw_sp_acl_tcam_ventry *ventry,1051 struct mlxsw_sp_acl_tcam_chunk *chunk)1052{1053 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;1054 struct mlxsw_sp_acl_tcam_entry *entry;1055 int err;1056 1057 entry = kzalloc(sizeof(*entry) + ops->entry_priv_size, GFP_KERNEL);1058 if (!entry)1059 return ERR_PTR(-ENOMEM);1060 entry->ventry = ventry;1061 entry->chunk = chunk;1062 1063 err = ops->entry_add(mlxsw_sp, chunk->region->priv, chunk->priv,1064 entry->priv, ventry->rulei);1065 if (err)1066 goto err_entry_add;1067 1068 return entry;1069 1070err_entry_add:1071 kfree(entry);1072 return ERR_PTR(err);1073}1074 1075static void mlxsw_sp_acl_tcam_entry_destroy(struct mlxsw_sp *mlxsw_sp,1076 struct mlxsw_sp_acl_tcam_entry *entry)1077{1078 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;1079 1080 ops->entry_del(mlxsw_sp, entry->chunk->region->priv,1081 entry->chunk->priv, entry->priv);1082 kfree(entry);1083}1084 1085static int1086mlxsw_sp_acl_tcam_entry_action_replace(struct mlxsw_sp *mlxsw_sp,1087 struct mlxsw_sp_acl_tcam_region *region,1088 struct mlxsw_sp_acl_tcam_entry *entry,1089 struct mlxsw_sp_acl_rule_info *rulei)1090{1091 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;1092 1093 return ops->entry_action_replace(mlxsw_sp, region->priv,1094 entry->priv, rulei);1095}1096 1097static int1098mlxsw_sp_acl_tcam_entry_activity_get(struct mlxsw_sp *mlxsw_sp,1099 struct mlxsw_sp_acl_tcam_entry *entry,1100 bool *activity)1101{1102 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;1103 1104 return ops->entry_activity_get(mlxsw_sp, entry->chunk->region->priv,1105 entry->priv, activity);1106}1107 1108static int mlxsw_sp_acl_tcam_ventry_add(struct mlxsw_sp *mlxsw_sp,1109 struct mlxsw_sp_acl_tcam_vgroup *vgroup,1110 struct mlxsw_sp_acl_tcam_ventry *ventry,1111 struct mlxsw_sp_acl_rule_info *rulei)1112{1113 struct mlxsw_sp_acl_tcam_vregion *vregion;1114 struct mlxsw_sp_acl_tcam_vchunk *vchunk;1115 int err;1116 1117 vchunk = mlxsw_sp_acl_tcam_vchunk_get(mlxsw_sp, vgroup, rulei->priority,1118 &rulei->values.elusage);1119 if (IS_ERR(vchunk))1120 return PTR_ERR(vchunk);1121 1122 ventry->vchunk = vchunk;1123 ventry->rulei = rulei;1124 vregion = vchunk->vregion;1125 1126 mutex_lock(&vregion->lock);1127 ventry->entry = mlxsw_sp_acl_tcam_entry_create(mlxsw_sp, ventry,1128 vchunk->chunk);1129 if (IS_ERR(ventry->entry)) {1130 mutex_unlock(&vregion->lock);1131 err = PTR_ERR(ventry->entry);1132 goto err_entry_create;1133 }1134 1135 list_add_tail(&ventry->list, &vchunk->ventry_list);1136 mlxsw_sp_acl_tcam_rehash_ctx_vchunk_changed(vchunk);1137 mutex_unlock(&vregion->lock);1138 1139 return 0;1140 1141err_entry_create:1142 mlxsw_sp_acl_tcam_vchunk_put(mlxsw_sp, vchunk);1143 return err;1144}1145 1146static void mlxsw_sp_acl_tcam_ventry_del(struct mlxsw_sp *mlxsw_sp,1147 struct mlxsw_sp_acl_tcam_ventry *ventry)1148{1149 struct mlxsw_sp_acl_tcam_vchunk *vchunk = ventry->vchunk;1150 struct mlxsw_sp_acl_tcam_vregion *vregion = vchunk->vregion;1151 1152 mutex_lock(&vregion->lock);1153 mlxsw_sp_acl_tcam_rehash_ctx_vchunk_changed(vchunk);1154 list_del(&ventry->list);1155 mlxsw_sp_acl_tcam_entry_destroy(mlxsw_sp, ventry->entry);1156 mutex_unlock(&vregion->lock);1157 mlxsw_sp_acl_tcam_vchunk_put(mlxsw_sp, vchunk);1158}1159 1160static int1161mlxsw_sp_acl_tcam_ventry_action_replace(struct mlxsw_sp *mlxsw_sp,1162 struct mlxsw_sp_acl_tcam_ventry *ventry,1163 struct mlxsw_sp_acl_rule_info *rulei)1164{1165 struct mlxsw_sp_acl_tcam_vchunk *vchunk = ventry->vchunk;1166 1167 return mlxsw_sp_acl_tcam_entry_action_replace(mlxsw_sp,1168 vchunk->vregion->region,1169 ventry->entry, rulei);1170}1171 1172static int1173mlxsw_sp_acl_tcam_ventry_activity_get(struct mlxsw_sp *mlxsw_sp,1174 struct mlxsw_sp_acl_tcam_ventry *ventry,1175 bool *activity)1176{1177 struct mlxsw_sp_acl_tcam_vregion *vregion = ventry->vchunk->vregion;1178 int err;1179 1180 mutex_lock(&vregion->lock);1181 err = mlxsw_sp_acl_tcam_entry_activity_get(mlxsw_sp, ventry->entry,1182 activity);1183 mutex_unlock(&vregion->lock);1184 return err;1185}1186 1187static int1188mlxsw_sp_acl_tcam_ventry_migrate(struct mlxsw_sp *mlxsw_sp,1189 struct mlxsw_sp_acl_tcam_ventry *ventry,1190 struct mlxsw_sp_acl_tcam_chunk *chunk,1191 int *credits)1192{1193 struct mlxsw_sp_acl_tcam_entry *new_entry;1194 1195 /* First check if the entry is not already where we want it to be. */1196 if (ventry->entry->chunk == chunk)1197 return 0;1198 1199 if (--(*credits) < 0)1200 return 0;1201 1202 new_entry = mlxsw_sp_acl_tcam_entry_create(mlxsw_sp, ventry, chunk);1203 if (IS_ERR(new_entry))1204 return PTR_ERR(new_entry);1205 mlxsw_sp_acl_tcam_entry_destroy(mlxsw_sp, ventry->entry);1206 ventry->entry = new_entry;1207 return 0;1208}1209 1210static int1211mlxsw_sp_acl_tcam_vchunk_migrate_start(struct mlxsw_sp *mlxsw_sp,1212 struct mlxsw_sp_acl_tcam_vchunk *vchunk,1213 struct mlxsw_sp_acl_tcam_region *region,1214 struct mlxsw_sp_acl_tcam_rehash_ctx *ctx)1215{1216 struct mlxsw_sp_acl_tcam_chunk *new_chunk;1217 1218 WARN_ON(vchunk->chunk2);1219 1220 new_chunk = mlxsw_sp_acl_tcam_chunk_create(mlxsw_sp, vchunk, region);1221 if (IS_ERR(new_chunk))1222 return PTR_ERR(new_chunk);1223 vchunk->chunk2 = vchunk->chunk;1224 vchunk->chunk = new_chunk;1225 ctx->current_vchunk = vchunk;1226 ctx->start_ventry = NULL;1227 ctx->stop_ventry = NULL;1228 return 0;1229}1230 1231static void1232mlxsw_sp_acl_tcam_vchunk_migrate_end(struct mlxsw_sp *mlxsw_sp,1233 struct mlxsw_sp_acl_tcam_vchunk *vchunk,1234 struct mlxsw_sp_acl_tcam_rehash_ctx *ctx)1235{1236 mlxsw_sp_acl_tcam_chunk_destroy(mlxsw_sp, vchunk->chunk2);1237 vchunk->chunk2 = NULL;1238 mlxsw_sp_acl_tcam_rehash_ctx_vchunk_reset(ctx);1239}1240 1241static int1242mlxsw_sp_acl_tcam_vchunk_migrate_one(struct mlxsw_sp *mlxsw_sp,1243 struct mlxsw_sp_acl_tcam_vchunk *vchunk,1244 struct mlxsw_sp_acl_tcam_region *region,1245 struct mlxsw_sp_acl_tcam_rehash_ctx *ctx,1246 int *credits)1247{1248 struct mlxsw_sp_acl_tcam_ventry *ventry;1249 int err;1250 1251 if (vchunk->chunk->region != region) {1252 err = mlxsw_sp_acl_tcam_vchunk_migrate_start(mlxsw_sp, vchunk,1253 region, ctx);1254 if (err)1255 return err;1256 } else if (!vchunk->chunk2) {1257 /* The chunk is already as it should be, nothing to do. */1258 return 0;1259 }1260 1261 if (list_empty(&vchunk->ventry_list))1262 goto out;1263 1264 /* If the migration got interrupted, we have the ventry to start from1265 * stored in context.1266 */1267 if (ctx->start_ventry)1268 ventry = ctx->start_ventry;1269 else1270 ventry = list_first_entry(&vchunk->ventry_list,1271 typeof(*ventry), list);1272 1273 WARN_ON(ventry->vchunk != vchunk);1274 1275 list_for_each_entry_from(ventry, &vchunk->ventry_list, list) {1276 /* During rollback, once we reach the ventry that failed1277 * to migrate, we are done.1278 */1279 if (ventry == ctx->stop_ventry)1280 break;1281 1282 err = mlxsw_sp_acl_tcam_ventry_migrate(mlxsw_sp, ventry,1283 vchunk->chunk, credits);1284 if (err) {1285 if (ctx->this_is_rollback) {1286 /* Save the ventry which we ended with and try1287 * to continue later on.1288 */1289 ctx->start_ventry = ventry;1290 return err;1291 }1292 /* Swap the chunk and chunk2 pointers so the follow-up1293 * rollback call will see the original chunk pointer1294 * in vchunk->chunk.1295 */1296 swap(vchunk->chunk, vchunk->chunk2);1297 /* The rollback has to be done from beginning of the1298 * chunk, that is why we have to null the start_ventry.1299 * However, we know where to stop the rollback,1300 * at the current ventry.1301 */1302 ctx->start_ventry = NULL;1303 ctx->stop_ventry = ventry;1304 return err;1305 } else if (*credits < 0) {1306 /* We are out of credits, the rest of the ventries1307 * will be migrated later. Save the ventry1308 * which we ended with.1309 */1310 ctx->start_ventry = ventry;1311 return 0;1312 }1313 }1314 1315out:1316 mlxsw_sp_acl_tcam_vchunk_migrate_end(mlxsw_sp, vchunk, ctx);1317 return 0;1318}1319 1320static int1321mlxsw_sp_acl_tcam_vchunk_migrate_all(struct mlxsw_sp *mlxsw_sp,1322 struct mlxsw_sp_acl_tcam_vregion *vregion,1323 struct mlxsw_sp_acl_tcam_rehash_ctx *ctx,1324 int *credits)1325{1326 struct mlxsw_sp_acl_tcam_vchunk *vchunk;1327 int err;1328 1329 if (list_empty(&vregion->vchunk_list))1330 return 0;1331 1332 /* If the migration got interrupted, we have the vchunk1333 * we are working on stored in context.1334 */1335 if (ctx->current_vchunk)1336 vchunk = ctx->current_vchunk;1337 else1338 vchunk = list_first_entry(&vregion->vchunk_list,1339 typeof(*vchunk), list);1340 1341 list_for_each_entry_from(vchunk, &vregion->vchunk_list, list) {1342 err = mlxsw_sp_acl_tcam_vchunk_migrate_one(mlxsw_sp, vchunk,1343 vregion->region,1344 ctx, credits);1345 if (err || *credits < 0)1346 return err;1347 }1348 return 0;1349}1350 1351static int1352mlxsw_sp_acl_tcam_vregion_migrate(struct mlxsw_sp *mlxsw_sp,1353 struct mlxsw_sp_acl_tcam_vregion *vregion,1354 struct mlxsw_sp_acl_tcam_rehash_ctx *ctx,1355 int *credits)1356{1357 int err, err2;1358 1359 trace_mlxsw_sp_acl_tcam_vregion_migrate(mlxsw_sp, vregion);1360 err = mlxsw_sp_acl_tcam_vchunk_migrate_all(mlxsw_sp, vregion,1361 ctx, credits);1362 if (err) {1363 if (ctx->this_is_rollback)1364 return err;1365 /* In case migration was not successful, we need to swap1366 * so the original region pointer is assigned again1367 * to vregion->region.1368 */1369 swap(vregion->region, vregion->region2);1370 mlxsw_sp_acl_tcam_rehash_ctx_vchunk_reset(ctx);1371 ctx->this_is_rollback = true;1372 err2 = mlxsw_sp_acl_tcam_vchunk_migrate_all(mlxsw_sp, vregion,1373 ctx, credits);1374 if (err2) {1375 trace_mlxsw_sp_acl_tcam_vregion_rehash_rollback_failed(mlxsw_sp,1376 vregion);1377 dev_err(mlxsw_sp->bus_info->dev, "Failed to rollback during vregion migration fail\n");1378 /* Let the rollback to be continued later on. */1379 }1380 }1381 trace_mlxsw_sp_acl_tcam_vregion_migrate_end(mlxsw_sp, vregion);1382 return err;1383}1384 1385static bool1386mlxsw_sp_acl_tcam_vregion_rehash_in_progress(const struct mlxsw_sp_acl_tcam_rehash_ctx *ctx)1387{1388 return ctx->hints_priv;1389}1390 1391static int1392mlxsw_sp_acl_tcam_vregion_rehash_start(struct mlxsw_sp *mlxsw_sp,1393 struct mlxsw_sp_acl_tcam_vregion *vregion,1394 struct mlxsw_sp_acl_tcam_rehash_ctx *ctx)1395{1396 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;1397 unsigned int priority = mlxsw_sp_acl_tcam_vregion_prio(vregion);1398 struct mlxsw_sp_acl_tcam_region *new_region;1399 void *hints_priv;1400 int err;1401 1402 trace_mlxsw_sp_acl_tcam_vregion_rehash(mlxsw_sp, vregion);1403 1404 hints_priv = ops->region_rehash_hints_get(vregion->region->priv);1405 if (IS_ERR(hints_priv))1406 return PTR_ERR(hints_priv);1407 1408 new_region = mlxsw_sp_acl_tcam_region_create(mlxsw_sp, vregion->tcam,1409 vregion, hints_priv);1410 if (IS_ERR(new_region)) {1411 err = PTR_ERR(new_region);1412 goto err_region_create;1413 }1414 1415 /* vregion->region contains the pointer to the new region1416 * we are going to migrate to.1417 */1418 vregion->region2 = vregion->region;1419 vregion->region = new_region;1420 err = mlxsw_sp_acl_tcam_group_region_attach(mlxsw_sp,1421 vregion->region2->group,1422 new_region, priority,1423 vregion->region2);1424 if (err)1425 goto err_group_region_attach;1426 1427 ctx->hints_priv = hints_priv;1428 ctx->this_is_rollback = false;1429 mlxsw_sp_acl_tcam_rehash_ctx_vchunk_reset(ctx);1430 1431 return 0;1432 1433err_group_region_attach:1434 vregion->region = vregion->region2;1435 vregion->region2 = NULL;1436 mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, new_region);1437err_region_create:1438 ops->region_rehash_hints_put(hints_priv);1439 return err;1440}1441 1442static void1443mlxsw_sp_acl_tcam_vregion_rehash_end(struct mlxsw_sp *mlxsw_sp,1444 struct mlxsw_sp_acl_tcam_vregion *vregion,1445 struct mlxsw_sp_acl_tcam_rehash_ctx *ctx)1446{1447 struct mlxsw_sp_acl_tcam_region *unused_region = vregion->region2;1448 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;1449 1450 vregion->region2 = NULL;1451 mlxsw_sp_acl_tcam_group_region_detach(mlxsw_sp, unused_region);1452 mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, unused_region);1453 ops->region_rehash_hints_put(ctx->hints_priv);1454 ctx->hints_priv = NULL;1455}1456 1457static void1458mlxsw_sp_acl_tcam_vregion_rehash(struct mlxsw_sp *mlxsw_sp,1459 struct mlxsw_sp_acl_tcam_vregion *vregion,1460 int *credits)1461{1462 struct mlxsw_sp_acl_tcam_rehash_ctx *ctx = &vregion->rehash.ctx;1463 int err;1464 1465 /* Check if the previous rehash work was interrupted1466 * which means we have to continue it now.1467 * If not, start a new rehash.1468 */1469 if (!mlxsw_sp_acl_tcam_vregion_rehash_in_progress(ctx)) {1470 err = mlxsw_sp_acl_tcam_vregion_rehash_start(mlxsw_sp,1471 vregion, ctx);1472 if (err) {1473 if (err != -EAGAIN)1474 dev_err(mlxsw_sp->bus_info->dev, "Failed get rehash hints\n");1475 return;1476 }1477 }1478 1479 err = mlxsw_sp_acl_tcam_vregion_migrate(mlxsw_sp, vregion,1480 ctx, credits);1481 if (err) {1482 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to migrate vregion\n");1483 return;1484 }1485 1486 if (*credits >= 0)1487 mlxsw_sp_acl_tcam_vregion_rehash_end(mlxsw_sp, vregion, ctx);1488}1489 1490static int1491mlxsw_sp_acl_tcam_region_rehash_intrvl_get(struct devlink *devlink, u32 id,1492 struct devlink_param_gset_ctx *ctx)1493{1494 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);1495 struct mlxsw_sp_acl_tcam *tcam;1496 struct mlxsw_sp *mlxsw_sp;1497 1498 mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);1499 tcam = mlxsw_sp_acl_to_tcam(mlxsw_sp->acl);1500 ctx->val.vu32 = tcam->vregion_rehash_intrvl;1501 1502 return 0;1503}1504 1505static int1506mlxsw_sp_acl_tcam_region_rehash_intrvl_set(struct devlink *devlink, u32 id,1507 struct devlink_param_gset_ctx *ctx,1508 struct netlink_ext_ack *extack)1509{1510 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);1511 struct mlxsw_sp_acl_tcam_vregion *vregion;1512 struct mlxsw_sp_acl_tcam *tcam;1513 struct mlxsw_sp *mlxsw_sp;1514 u32 val = ctx->val.vu32;1515 1516 if (val < MLXSW_SP_ACL_TCAM_VREGION_REHASH_INTRVL_MIN && val)1517 return -EINVAL;1518 1519 mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);1520 tcam = mlxsw_sp_acl_to_tcam(mlxsw_sp->acl);1521 tcam->vregion_rehash_intrvl = val;1522 mutex_lock(&tcam->lock);1523 list_for_each_entry(vregion, &tcam->vregion_list, tlist) {1524 if (val)1525 mlxsw_core_schedule_dw(&vregion->rehash.dw, 0);1526 else1527 cancel_delayed_work_sync(&vregion->rehash.dw);1528 }1529 mutex_unlock(&tcam->lock);1530 return 0;1531}1532 1533static const struct devlink_param mlxsw_sp_acl_tcam_rehash_params[] = {1534 DEVLINK_PARAM_DRIVER(MLXSW_DEVLINK_PARAM_ID_ACL_REGION_REHASH_INTERVAL,1535 "acl_region_rehash_interval",1536 DEVLINK_PARAM_TYPE_U32,1537 BIT(DEVLINK_PARAM_CMODE_RUNTIME),1538 mlxsw_sp_acl_tcam_region_rehash_intrvl_get,1539 mlxsw_sp_acl_tcam_region_rehash_intrvl_set,1540 NULL),1541};1542 1543static int mlxsw_sp_acl_tcam_rehash_params_register(struct mlxsw_sp *mlxsw_sp)1544{1545 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);1546 1547 if (!mlxsw_sp->acl_tcam_ops->region_rehash_hints_get)1548 return 0;1549 1550 return devl_params_register(devlink, mlxsw_sp_acl_tcam_rehash_params,1551 ARRAY_SIZE(mlxsw_sp_acl_tcam_rehash_params));1552}1553 1554static void1555mlxsw_sp_acl_tcam_rehash_params_unregister(struct mlxsw_sp *mlxsw_sp)1556{1557 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);1558 1559 if (!mlxsw_sp->acl_tcam_ops->region_rehash_hints_get)1560 return;1561 1562 devl_params_unregister(devlink, mlxsw_sp_acl_tcam_rehash_params,1563 ARRAY_SIZE(mlxsw_sp_acl_tcam_rehash_params));1564}1565 1566int mlxsw_sp_acl_tcam_init(struct mlxsw_sp *mlxsw_sp,1567 struct mlxsw_sp_acl_tcam *tcam)1568{1569 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;1570 u64 max_tcam_regions;1571 u64 max_regions;1572 u64 max_groups;1573 int err;1574 1575 mutex_init(&tcam->lock);1576 tcam->vregion_rehash_intrvl =1577 MLXSW_SP_ACL_TCAM_VREGION_REHASH_INTRVL_DFLT;1578 INIT_LIST_HEAD(&tcam->vregion_list);1579 1580 err = mlxsw_sp_acl_tcam_rehash_params_register(mlxsw_sp);1581 if (err)1582 goto err_rehash_params_register;1583 1584 max_tcam_regions = MLXSW_CORE_RES_GET(mlxsw_sp->core,1585 ACL_MAX_TCAM_REGIONS);1586 max_regions = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_REGIONS);1587 1588 /* Use 1:1 mapping between ACL region and TCAM region */1589 if (max_tcam_regions < max_regions)1590 max_regions = max_tcam_regions;1591 1592 ida_init(&tcam->used_regions);1593 tcam->max_regions = max_regions;1594 1595 max_groups = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_GROUPS);1596 ida_init(&tcam->used_groups);1597 tcam->max_groups = max_groups;1598 tcam->max_group_size = MLXSW_CORE_RES_GET(mlxsw_sp->core,1599 ACL_MAX_GROUP_SIZE);1600 tcam->max_group_size = min_t(unsigned int, tcam->max_group_size,1601 MLXSW_REG_PAGT_ACL_MAX_NUM);1602 1603 err = ops->init(mlxsw_sp, tcam->priv, tcam);1604 if (err)1605 goto err_tcam_init;1606 1607 return 0;1608 1609err_tcam_init:1610 ida_destroy(&tcam->used_groups);1611 ida_destroy(&tcam->used_regions);1612 mlxsw_sp_acl_tcam_rehash_params_unregister(mlxsw_sp);1613err_rehash_params_register:1614 mutex_destroy(&tcam->lock);1615 return err;1616}1617 1618void mlxsw_sp_acl_tcam_fini(struct mlxsw_sp *mlxsw_sp,1619 struct mlxsw_sp_acl_tcam *tcam)1620{1621 const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;1622 1623 ops->fini(mlxsw_sp, tcam->priv);1624 ida_destroy(&tcam->used_groups);1625 ida_destroy(&tcam->used_regions);1626 mlxsw_sp_acl_tcam_rehash_params_unregister(mlxsw_sp);1627 mutex_destroy(&tcam->lock);1628}1629 1630static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv4[] = {1631 MLXSW_AFK_ELEMENT_SRC_SYS_PORT,1632 MLXSW_AFK_ELEMENT_DMAC_32_47,1633 MLXSW_AFK_ELEMENT_DMAC_0_31,1634 MLXSW_AFK_ELEMENT_SMAC_32_47,1635 MLXSW_AFK_ELEMENT_SMAC_0_31,1636 MLXSW_AFK_ELEMENT_ETHERTYPE,1637 MLXSW_AFK_ELEMENT_IP_PROTO,1638 MLXSW_AFK_ELEMENT_SRC_IP_0_31,1639 MLXSW_AFK_ELEMENT_DST_IP_0_31,1640 MLXSW_AFK_ELEMENT_DST_L4_PORT,1641 MLXSW_AFK_ELEMENT_SRC_L4_PORT,1642 MLXSW_AFK_ELEMENT_VID,1643 MLXSW_AFK_ELEMENT_PCP,1644 MLXSW_AFK_ELEMENT_TCP_FLAGS,1645 MLXSW_AFK_ELEMENT_IP_TTL_,1646 MLXSW_AFK_ELEMENT_IP_ECN,1647 MLXSW_AFK_ELEMENT_IP_DSCP,1648};1649 1650static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv6[] = {1651 MLXSW_AFK_ELEMENT_ETHERTYPE,1652 MLXSW_AFK_ELEMENT_IP_PROTO,1653 MLXSW_AFK_ELEMENT_SRC_IP_96_127,1654 MLXSW_AFK_ELEMENT_SRC_IP_64_95,1655 MLXSW_AFK_ELEMENT_SRC_IP_32_63,1656 MLXSW_AFK_ELEMENT_SRC_IP_0_31,1657 MLXSW_AFK_ELEMENT_DST_IP_96_127,1658 MLXSW_AFK_ELEMENT_DST_IP_64_95,1659 MLXSW_AFK_ELEMENT_DST_IP_32_63,1660 MLXSW_AFK_ELEMENT_DST_IP_0_31,1661 MLXSW_AFK_ELEMENT_DST_L4_PORT,1662 MLXSW_AFK_ELEMENT_SRC_L4_PORT,1663};1664 1665static const struct mlxsw_sp_acl_tcam_pattern mlxsw_sp_acl_tcam_patterns[] = {1666 {1667 .elements = mlxsw_sp_acl_tcam_pattern_ipv4,1668 .elements_count = ARRAY_SIZE(mlxsw_sp_acl_tcam_pattern_ipv4),1669 },1670 {1671 .elements = mlxsw_sp_acl_tcam_pattern_ipv6,1672 .elements_count = ARRAY_SIZE(mlxsw_sp_acl_tcam_pattern_ipv6),1673 },1674};1675 1676#define MLXSW_SP_ACL_TCAM_PATTERNS_COUNT \1677 ARRAY_SIZE(mlxsw_sp_acl_tcam_patterns)1678 1679struct mlxsw_sp_acl_tcam_flower_ruleset {1680 struct mlxsw_sp_acl_tcam_vgroup vgroup;1681};1682 1683struct mlxsw_sp_acl_tcam_flower_rule {1684 struct mlxsw_sp_acl_tcam_ventry ventry;1685};1686 1687static int1688mlxsw_sp_acl_tcam_flower_ruleset_add(struct mlxsw_sp *mlxsw_sp,1689 struct mlxsw_sp_acl_tcam *tcam,1690 void *ruleset_priv,1691 struct mlxsw_afk_element_usage *tmplt_elusage,1692 unsigned int *p_min_prio,1693 unsigned int *p_max_prio)1694{1695 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;1696 1697 return mlxsw_sp_acl_tcam_vgroup_add(mlxsw_sp, tcam, &ruleset->vgroup,1698 mlxsw_sp_acl_tcam_patterns,1699 MLXSW_SP_ACL_TCAM_PATTERNS_COUNT,1700 tmplt_elusage, true,1701 p_min_prio, p_max_prio);1702}1703 1704static void1705mlxsw_sp_acl_tcam_flower_ruleset_del(struct mlxsw_sp *mlxsw_sp,1706 void *ruleset_priv)1707{1708 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;1709 1710 mlxsw_sp_acl_tcam_vgroup_del(&ruleset->vgroup);1711}1712 1713static int1714mlxsw_sp_acl_tcam_flower_ruleset_bind(struct mlxsw_sp *mlxsw_sp,1715 void *ruleset_priv,1716 struct mlxsw_sp_port *mlxsw_sp_port,1717 bool ingress)1718{1719 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;1720 1721 return mlxsw_sp_acl_tcam_group_bind(mlxsw_sp, &ruleset->vgroup.group,1722 mlxsw_sp_port, ingress);1723}1724 1725static void1726mlxsw_sp_acl_tcam_flower_ruleset_unbind(struct mlxsw_sp *mlxsw_sp,1727 void *ruleset_priv,1728 struct mlxsw_sp_port *mlxsw_sp_port,1729 bool ingress)1730{1731 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;1732 1733 mlxsw_sp_acl_tcam_group_unbind(mlxsw_sp, &ruleset->vgroup.group,1734 mlxsw_sp_port, ingress);1735}1736 1737static u161738mlxsw_sp_acl_tcam_flower_ruleset_group_id(void *ruleset_priv)1739{1740 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;1741 1742 return mlxsw_sp_acl_tcam_group_id(&ruleset->vgroup.group);1743}1744 1745static int1746mlxsw_sp_acl_tcam_flower_rule_add(struct mlxsw_sp *mlxsw_sp,1747 void *ruleset_priv, void *rule_priv,1748 struct mlxsw_sp_acl_rule_info *rulei)1749{1750 struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;1751 struct mlxsw_sp_acl_tcam_flower_rule *rule = rule_priv;1752 1753 return mlxsw_sp_acl_tcam_ventry_add(mlxsw_sp, &ruleset->vgroup,1754 &rule->ventry, rulei);1755}1756 1757static void1758mlxsw_sp_acl_tcam_flower_rule_del(struct mlxsw_sp *mlxsw_sp, void *rule_priv)1759{1760 struct mlxsw_sp_acl_tcam_flower_rule *rule = rule_priv;1761 1762 mlxsw_sp_acl_tcam_ventry_del(mlxsw_sp, &rule->ventry);1763}1764 1765static int1766mlxsw_sp_acl_tcam_flower_rule_action_replace(struct mlxsw_sp *mlxsw_sp,1767 void *rule_priv,1768 struct mlxsw_sp_acl_rule_info *rulei)1769{1770 return -EOPNOTSUPP;1771}1772 1773static int1774mlxsw_sp_acl_tcam_flower_rule_activity_get(struct mlxsw_sp *mlxsw_sp,1775 void *rule_priv, bool *activity)1776{1777 struct mlxsw_sp_acl_tcam_flower_rule *rule = rule_priv;1778 1779 return mlxsw_sp_acl_tcam_ventry_activity_get(mlxsw_sp, &rule->ventry,1780 activity);1781}1782 1783static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_flower_ops = {1784 .ruleset_priv_size = sizeof(struct mlxsw_sp_acl_tcam_flower_ruleset),1785 .ruleset_add = mlxsw_sp_acl_tcam_flower_ruleset_add,1786 .ruleset_del = mlxsw_sp_acl_tcam_flower_ruleset_del,1787 .ruleset_bind = mlxsw_sp_acl_tcam_flower_ruleset_bind,1788 .ruleset_unbind = mlxsw_sp_acl_tcam_flower_ruleset_unbind,1789 .ruleset_group_id = mlxsw_sp_acl_tcam_flower_ruleset_group_id,1790 .rule_priv_size = sizeof(struct mlxsw_sp_acl_tcam_flower_rule),1791 .rule_add = mlxsw_sp_acl_tcam_flower_rule_add,1792 .rule_del = mlxsw_sp_acl_tcam_flower_rule_del,1793 .rule_action_replace = mlxsw_sp_acl_tcam_flower_rule_action_replace,1794 .rule_activity_get = mlxsw_sp_acl_tcam_flower_rule_activity_get,1795};1796 1797struct mlxsw_sp_acl_tcam_mr_ruleset {1798 struct mlxsw_sp_acl_tcam_vchunk *vchunk;1799 struct mlxsw_sp_acl_tcam_vgroup vgroup;1800};1801 1802struct mlxsw_sp_acl_tcam_mr_rule {1803 struct mlxsw_sp_acl_tcam_ventry ventry;1804};1805 1806static int1807mlxsw_sp_acl_tcam_mr_ruleset_add(struct mlxsw_sp *mlxsw_sp,1808 struct mlxsw_sp_acl_tcam *tcam,1809 void *ruleset_priv,1810 struct mlxsw_afk_element_usage *tmplt_elusage,1811 unsigned int *p_min_prio,1812 unsigned int *p_max_prio)1813{1814 struct mlxsw_sp_acl_tcam_mr_ruleset *ruleset = ruleset_priv;1815 int err;1816 1817 err = mlxsw_sp_acl_tcam_vgroup_add(mlxsw_sp, tcam, &ruleset->vgroup,1818 mlxsw_sp_acl_tcam_patterns,1819 MLXSW_SP_ACL_TCAM_PATTERNS_COUNT,1820 tmplt_elusage, false,1821 p_min_prio, p_max_prio);1822 if (err)1823 return err;1824 1825 /* For most of the TCAM clients it would make sense to take a tcam chunk1826 * only when the first rule is written. This is not the case for1827 * multicast router as it is required to bind the multicast router to a1828 * specific ACL Group ID which must exist in HW before multicast router1829 * is initialized.1830 */1831 ruleset->vchunk = mlxsw_sp_acl_tcam_vchunk_get(mlxsw_sp,1832 &ruleset->vgroup, 1,1833 tmplt_elusage);1834 if (IS_ERR(ruleset->vchunk)) {1835 err = PTR_ERR(ruleset->vchunk);1836 goto err_chunk_get;1837 }1838 1839 return 0;1840 1841err_chunk_get:1842 mlxsw_sp_acl_tcam_vgroup_del(&ruleset->vgroup);1843 return err;1844}1845 1846static void1847mlxsw_sp_acl_tcam_mr_ruleset_del(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv)1848{1849 struct mlxsw_sp_acl_tcam_mr_ruleset *ruleset = ruleset_priv;1850 1851 mlxsw_sp_acl_tcam_vchunk_put(mlxsw_sp, ruleset->vchunk);1852 mlxsw_sp_acl_tcam_vgroup_del(&ruleset->vgroup);1853}1854 1855static int1856mlxsw_sp_acl_tcam_mr_ruleset_bind(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv,1857 struct mlxsw_sp_port *mlxsw_sp_port,1858 bool ingress)1859{1860 /* Binding is done when initializing multicast router */1861 return 0;1862}1863 1864static void1865mlxsw_sp_acl_tcam_mr_ruleset_unbind(struct mlxsw_sp *mlxsw_sp,1866 void *ruleset_priv,1867 struct mlxsw_sp_port *mlxsw_sp_port,1868 bool ingress)1869{1870}1871 1872static u161873mlxsw_sp_acl_tcam_mr_ruleset_group_id(void *ruleset_priv)1874{1875 struct mlxsw_sp_acl_tcam_mr_ruleset *ruleset = ruleset_priv;1876 1877 return mlxsw_sp_acl_tcam_group_id(&ruleset->vgroup.group);1878}1879 1880static int1881mlxsw_sp_acl_tcam_mr_rule_add(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv,1882 void *rule_priv,1883 struct mlxsw_sp_acl_rule_info *rulei)1884{1885 struct mlxsw_sp_acl_tcam_mr_ruleset *ruleset = ruleset_priv;1886 struct mlxsw_sp_acl_tcam_mr_rule *rule = rule_priv;1887 1888 return mlxsw_sp_acl_tcam_ventry_add(mlxsw_sp, &ruleset->vgroup,1889 &rule->ventry, rulei);1890}1891 1892static void1893mlxsw_sp_acl_tcam_mr_rule_del(struct mlxsw_sp *mlxsw_sp, void *rule_priv)1894{1895 struct mlxsw_sp_acl_tcam_mr_rule *rule = rule_priv;1896 1897 mlxsw_sp_acl_tcam_ventry_del(mlxsw_sp, &rule->ventry);1898}1899 1900static int1901mlxsw_sp_acl_tcam_mr_rule_action_replace(struct mlxsw_sp *mlxsw_sp,1902 void *rule_priv,1903 struct mlxsw_sp_acl_rule_info *rulei)1904{1905 struct mlxsw_sp_acl_tcam_mr_rule *rule = rule_priv;1906 1907 return mlxsw_sp_acl_tcam_ventry_action_replace(mlxsw_sp, &rule->ventry,1908 rulei);1909}1910 1911static int1912mlxsw_sp_acl_tcam_mr_rule_activity_get(struct mlxsw_sp *mlxsw_sp,1913 void *rule_priv, bool *activity)1914{1915 *activity = false;1916 1917 return 0;1918}1919 1920static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_mr_ops = {1921 .ruleset_priv_size = sizeof(struct mlxsw_sp_acl_tcam_mr_ruleset),1922 .ruleset_add = mlxsw_sp_acl_tcam_mr_ruleset_add,1923 .ruleset_del = mlxsw_sp_acl_tcam_mr_ruleset_del,1924 .ruleset_bind = mlxsw_sp_acl_tcam_mr_ruleset_bind,1925 .ruleset_unbind = mlxsw_sp_acl_tcam_mr_ruleset_unbind,1926 .ruleset_group_id = mlxsw_sp_acl_tcam_mr_ruleset_group_id,1927 .rule_priv_size = sizeof(struct mlxsw_sp_acl_tcam_mr_rule),1928 .rule_add = mlxsw_sp_acl_tcam_mr_rule_add,1929 .rule_del = mlxsw_sp_acl_tcam_mr_rule_del,1930 .rule_action_replace = mlxsw_sp_acl_tcam_mr_rule_action_replace,1931 .rule_activity_get = mlxsw_sp_acl_tcam_mr_rule_activity_get,1932};1933 1934static const struct mlxsw_sp_acl_profile_ops *1935mlxsw_sp_acl_tcam_profile_ops_arr[] = {1936 [MLXSW_SP_ACL_PROFILE_FLOWER] = &mlxsw_sp_acl_tcam_flower_ops,1937 [MLXSW_SP_ACL_PROFILE_MR] = &mlxsw_sp_acl_tcam_mr_ops,1938};1939 1940const struct mlxsw_sp_acl_profile_ops *1941mlxsw_sp_acl_tcam_profile_ops(struct mlxsw_sp *mlxsw_sp,1942 enum mlxsw_sp_acl_profile profile)1943{1944 const struct mlxsw_sp_acl_profile_ops *ops;1945 1946 if (WARN_ON(profile >= ARRAY_SIZE(mlxsw_sp_acl_tcam_profile_ops_arr)))1947 return NULL;1948 ops = mlxsw_sp_acl_tcam_profile_ops_arr[profile];1949 if (WARN_ON(!ops))1950 return NULL;1951 return ops;1952}1953