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