linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
   2/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
   3
   4#include <linux/kernel.h>
   5#include <linux/slab.h>
   6#include <linux/errno.h>
   7#include <linux/list.h>
   8#include <linux/string.h>
   9#include <linux/rhashtable.h>
  10#include <linux/netdevice.h>
  11#include <net/net_namespace.h>
  12#include <net/tc_act/tc_vlan.h>
  13
  14#include "reg.h"
  15#include "core.h"
  16#include "resources.h"
  17#include "spectrum.h"
  18#include "core_acl_flex_keys.h"
  19#include "core_acl_flex_actions.h"
  20#include "spectrum_acl_tcam.h"
  21
  22struct mlxsw_sp_acl {
  23        struct mlxsw_sp *mlxsw_sp;
  24        struct mlxsw_afk *afk;
  25        struct mlxsw_sp_fid *dummy_fid;
  26        struct rhashtable ruleset_ht;
  27        struct list_head rules;
  28        struct {
  29                struct delayed_work dw;
  30                unsigned long interval; /* ms */
  31#define MLXSW_SP_ACL_RULE_ACTIVITY_UPDATE_PERIOD_MS 1000
  32        } rule_activity_update;
  33        struct mlxsw_sp_acl_tcam tcam;
  34};
  35
  36struct mlxsw_afk *mlxsw_sp_acl_afk(struct mlxsw_sp_acl *acl)
  37{
  38        return acl->afk;
  39}
  40
  41struct mlxsw_sp_acl_block_binding {
  42        struct list_head list;
  43        struct net_device *dev;
  44        struct mlxsw_sp_port *mlxsw_sp_port;
  45        bool ingress;
  46};
  47
  48struct mlxsw_sp_acl_ruleset_ht_key {
  49        struct mlxsw_sp_acl_block *block;
  50        u32 chain_index;
  51        const struct mlxsw_sp_acl_profile_ops *ops;
  52};
  53
  54struct mlxsw_sp_acl_ruleset {
  55        struct rhash_head ht_node; /* Member of acl HT */
  56        struct mlxsw_sp_acl_ruleset_ht_key ht_key;
  57        struct rhashtable rule_ht;
  58        unsigned int ref_count;
  59        unsigned long priv[0];
  60        /* priv has to be always the last item */
  61};
  62
  63struct mlxsw_sp_acl_rule {
  64        struct rhash_head ht_node; /* Member of rule HT */
  65        struct list_head list;
  66        unsigned long cookie; /* HT key */
  67        struct mlxsw_sp_acl_ruleset *ruleset;
  68        struct mlxsw_sp_acl_rule_info *rulei;
  69        u64 last_used;
  70        u64 last_packets;
  71        u64 last_bytes;
  72        unsigned long priv[0];
  73        /* priv has to be always the last item */
  74};
  75
  76static const struct rhashtable_params mlxsw_sp_acl_ruleset_ht_params = {
  77        .key_len = sizeof(struct mlxsw_sp_acl_ruleset_ht_key),
  78        .key_offset = offsetof(struct mlxsw_sp_acl_ruleset, ht_key),
  79        .head_offset = offsetof(struct mlxsw_sp_acl_ruleset, ht_node),
  80        .automatic_shrinking = true,
  81};
  82
  83static const struct rhashtable_params mlxsw_sp_acl_rule_ht_params = {
  84        .key_len = sizeof(unsigned long),
  85        .key_offset = offsetof(struct mlxsw_sp_acl_rule, cookie),
  86        .head_offset = offsetof(struct mlxsw_sp_acl_rule, ht_node),
  87        .automatic_shrinking = true,
  88};
  89
  90struct mlxsw_sp_fid *mlxsw_sp_acl_dummy_fid(struct mlxsw_sp *mlxsw_sp)
  91{
  92        return mlxsw_sp->acl->dummy_fid;
  93}
  94
  95struct mlxsw_sp *mlxsw_sp_acl_block_mlxsw_sp(struct mlxsw_sp_acl_block *block)
  96{
  97        return block->mlxsw_sp;
  98}
  99
 100unsigned int mlxsw_sp_acl_block_rule_count(struct mlxsw_sp_acl_block *block)
 101{
 102        return block ? block->rule_count : 0;
 103}
 104
 105void mlxsw_sp_acl_block_disable_inc(struct mlxsw_sp_acl_block *block)
 106{
 107        if (block)
 108                block->disable_count++;
 109}
 110
 111void mlxsw_sp_acl_block_disable_dec(struct mlxsw_sp_acl_block *block)
 112{
 113        if (block)
 114                block->disable_count--;
 115}
 116
 117bool mlxsw_sp_acl_block_disabled(struct mlxsw_sp_acl_block *block)
 118{
 119        return block->disable_count;
 120}
 121
 122bool mlxsw_sp_acl_block_is_egress_bound(struct mlxsw_sp_acl_block *block)
 123{
 124        struct mlxsw_sp_acl_block_binding *binding;
 125
 126        list_for_each_entry(binding, &block->binding_list, list) {
 127                if (!binding->ingress)
 128                        return true;
 129        }
 130        return false;
 131}
 132
 133static bool
 134mlxsw_sp_acl_ruleset_is_singular(const struct mlxsw_sp_acl_ruleset *ruleset)
 135{
 136        /* We hold a reference on ruleset ourselves */
 137        return ruleset->ref_count == 2;
 138}
 139
 140static int
 141mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp,
 142                          struct mlxsw_sp_acl_block *block,
 143                          struct mlxsw_sp_acl_block_binding *binding)
 144{
 145        struct mlxsw_sp_acl_ruleset *ruleset = block->ruleset_zero;
 146        const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
 147
 148        return ops->ruleset_bind(mlxsw_sp, ruleset->priv,
 149                                 binding->mlxsw_sp_port, binding->ingress);
 150}
 151
 152static void
 153mlxsw_sp_acl_ruleset_unbind(struct mlxsw_sp *mlxsw_sp,
 154                            struct mlxsw_sp_acl_block *block,
 155                            struct mlxsw_sp_acl_block_binding *binding)
 156{
 157        struct mlxsw_sp_acl_ruleset *ruleset = block->ruleset_zero;
 158        const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
 159
 160        ops->ruleset_unbind(mlxsw_sp, ruleset->priv,
 161                            binding->mlxsw_sp_port, binding->ingress);
 162}
 163
 164static bool mlxsw_sp_acl_ruleset_block_bound(struct mlxsw_sp_acl_block *block)
 165{
 166        return block->ruleset_zero;
 167}
 168
 169static int
 170mlxsw_sp_acl_ruleset_block_bind(struct mlxsw_sp *mlxsw_sp,
 171                                struct mlxsw_sp_acl_ruleset *ruleset,
 172                                struct mlxsw_sp_acl_block *block)
 173{
 174        struct mlxsw_sp_acl_block_binding *binding;
 175        int err;
 176
 177        block->ruleset_zero = ruleset;
 178        list_for_each_entry(binding, &block->binding_list, list) {
 179                err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, block, binding);
 180                if (err)
 181                        goto rollback;
 182        }
 183        return 0;
 184
 185rollback:
 186        list_for_each_entry_continue_reverse(binding, &block->binding_list,
 187                                             list)
 188                mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block, binding);
 189        block->ruleset_zero = NULL;
 190
 191        return err;
 192}
 193
 194static void
 195mlxsw_sp_acl_ruleset_block_unbind(struct mlxsw_sp *mlxsw_sp,
 196                                  struct mlxsw_sp_acl_ruleset *ruleset,
 197                                  struct mlxsw_sp_acl_block *block)
 198{
 199        struct mlxsw_sp_acl_block_binding *binding;
 200
 201        list_for_each_entry(binding, &block->binding_list, list)
 202                mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block, binding);
 203        block->ruleset_zero = NULL;
 204}
 205
 206struct mlxsw_sp_acl_block *mlxsw_sp_acl_block_create(struct mlxsw_sp *mlxsw_sp,
 207                                                     struct net *net)
 208{
 209        struct mlxsw_sp_acl_block *block;
 210
 211        block = kzalloc(sizeof(*block), GFP_KERNEL);
 212        if (!block)
 213                return NULL;
 214        INIT_LIST_HEAD(&block->binding_list);
 215        block->mlxsw_sp = mlxsw_sp;
 216        block->net = net;
 217        return block;
 218}
 219
 220void mlxsw_sp_acl_block_destroy(struct mlxsw_sp_acl_block *block)
 221{
 222        WARN_ON(!list_empty(&block->binding_list));
 223        kfree(block);
 224}
 225
 226static struct mlxsw_sp_acl_block_binding *
 227mlxsw_sp_acl_block_lookup(struct mlxsw_sp_acl_block *block,
 228                          struct mlxsw_sp_port *mlxsw_sp_port, bool ingress)
 229{
 230        struct mlxsw_sp_acl_block_binding *binding;
 231
 232        list_for_each_entry(binding, &block->binding_list, list)
 233                if (binding->mlxsw_sp_port == mlxsw_sp_port &&
 234                    binding->ingress == ingress)
 235                        return binding;
 236        return NULL;
 237}
 238
 239int mlxsw_sp_acl_block_bind(struct mlxsw_sp *mlxsw_sp,
 240                            struct mlxsw_sp_acl_block *block,
 241                            struct mlxsw_sp_port *mlxsw_sp_port,
 242                            bool ingress,
 243                            struct netlink_ext_ack *extack)
 244{
 245        struct mlxsw_sp_acl_block_binding *binding;
 246        int err;
 247
 248        if (WARN_ON(mlxsw_sp_acl_block_lookup(block, mlxsw_sp_port, ingress)))
 249                return -EEXIST;
 250
 251        if (!ingress && block->egress_blocker_rule_count) {
 252                NL_SET_ERR_MSG_MOD(extack, "Block cannot be bound to egress because it contains unsupported rules");
 253                return -EOPNOTSUPP;
 254        }
 255
 256        binding = kzalloc(sizeof(*binding), GFP_KERNEL);
 257        if (!binding)
 258                return -ENOMEM;
 259        binding->mlxsw_sp_port = mlxsw_sp_port;
 260        binding->ingress = ingress;
 261
 262        if (mlxsw_sp_acl_ruleset_block_bound(block)) {
 263                err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, block, binding);
 264                if (err)
 265                        goto err_ruleset_bind;
 266        }
 267
 268        list_add(&binding->list, &block->binding_list);
 269        return 0;
 270
 271err_ruleset_bind:
 272        kfree(binding);
 273        return err;
 274}
 275
 276int mlxsw_sp_acl_block_unbind(struct mlxsw_sp *mlxsw_sp,
 277                              struct mlxsw_sp_acl_block *block,
 278                              struct mlxsw_sp_port *mlxsw_sp_port,
 279                              bool ingress)
 280{
 281        struct mlxsw_sp_acl_block_binding *binding;
 282
 283        binding = mlxsw_sp_acl_block_lookup(block, mlxsw_sp_port, ingress);
 284        if (!binding)
 285                return -ENOENT;
 286
 287        list_del(&binding->list);
 288
 289        if (mlxsw_sp_acl_ruleset_block_bound(block))
 290                mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block, binding);
 291
 292        kfree(binding);
 293        return 0;
 294}
 295
 296static struct mlxsw_sp_acl_ruleset *
 297mlxsw_sp_acl_ruleset_create(struct mlxsw_sp *mlxsw_sp,
 298                            struct mlxsw_sp_acl_block *block, u32 chain_index,
 299                            const struct mlxsw_sp_acl_profile_ops *ops,
 300                            struct mlxsw_afk_element_usage *tmplt_elusage)
 301{
 302        struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
 303        struct mlxsw_sp_acl_ruleset *ruleset;
 304        size_t alloc_size;
 305        int err;
 306
 307        alloc_size = sizeof(*ruleset) + ops->ruleset_priv_size;
 308        ruleset = kzalloc(alloc_size, GFP_KERNEL);
 309        if (!ruleset)
 310                return ERR_PTR(-ENOMEM);
 311        ruleset->ref_count = 1;
 312        ruleset->ht_key.block = block;
 313        ruleset->ht_key.chain_index = chain_index;
 314        ruleset->ht_key.ops = ops;
 315
 316        err = rhashtable_init(&ruleset->rule_ht, &mlxsw_sp_acl_rule_ht_params);
 317        if (err)
 318                goto err_rhashtable_init;
 319
 320        err = ops->ruleset_add(mlxsw_sp, &acl->tcam, ruleset->priv,
 321                               tmplt_elusage);
 322        if (err)
 323                goto err_ops_ruleset_add;
 324
 325        err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node,
 326                                     mlxsw_sp_acl_ruleset_ht_params);
 327        if (err)
 328                goto err_ht_insert;
 329
 330        return ruleset;
 331
 332err_ht_insert:
 333        ops->ruleset_del(mlxsw_sp, ruleset->priv);
 334err_ops_ruleset_add:
 335        rhashtable_destroy(&ruleset->rule_ht);
 336err_rhashtable_init:
 337        kfree(ruleset);
 338        return ERR_PTR(err);
 339}
 340
 341static void mlxsw_sp_acl_ruleset_destroy(struct mlxsw_sp *mlxsw_sp,
 342                                         struct mlxsw_sp_acl_ruleset *ruleset)
 343{
 344        const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
 345        struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
 346
 347        rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node,
 348                               mlxsw_sp_acl_ruleset_ht_params);
 349        ops->ruleset_del(mlxsw_sp, ruleset->priv);
 350        rhashtable_destroy(&ruleset->rule_ht);
 351        kfree(ruleset);
 352}
 353
 354static void mlxsw_sp_acl_ruleset_ref_inc(struct mlxsw_sp_acl_ruleset *ruleset)
 355{
 356        ruleset->ref_count++;
 357}
 358
 359static void mlxsw_sp_acl_ruleset_ref_dec(struct mlxsw_sp *mlxsw_sp,
 360                                         struct mlxsw_sp_acl_ruleset *ruleset)
 361{
 362        if (--ruleset->ref_count)
 363                return;
 364        mlxsw_sp_acl_ruleset_destroy(mlxsw_sp, ruleset);
 365}
 366
 367static struct mlxsw_sp_acl_ruleset *
 368__mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp_acl *acl,
 369                              struct mlxsw_sp_acl_block *block, u32 chain_index,
 370                              const struct mlxsw_sp_acl_profile_ops *ops)
 371{
 372        struct mlxsw_sp_acl_ruleset_ht_key ht_key;
 373
 374        memset(&ht_key, 0, sizeof(ht_key));
 375        ht_key.block = block;
 376        ht_key.chain_index = chain_index;
 377        ht_key.ops = ops;
 378        return rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key,
 379                                      mlxsw_sp_acl_ruleset_ht_params);
 380}
 381
 382struct mlxsw_sp_acl_ruleset *
 383mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp *mlxsw_sp,
 384                            struct mlxsw_sp_acl_block *block, u32 chain_index,
 385                            enum mlxsw_sp_acl_profile profile)
 386{
 387        const struct mlxsw_sp_acl_profile_ops *ops;
 388        struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
 389        struct mlxsw_sp_acl_ruleset *ruleset;
 390
 391        ops = mlxsw_sp_acl_tcam_profile_ops(mlxsw_sp, profile);
 392        if (!ops)
 393                return ERR_PTR(-EINVAL);
 394        ruleset = __mlxsw_sp_acl_ruleset_lookup(acl, block, chain_index, ops);
 395        if (!ruleset)
 396                return ERR_PTR(-ENOENT);
 397        return ruleset;
 398}
 399
 400struct mlxsw_sp_acl_ruleset *
 401mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp,
 402                         struct mlxsw_sp_acl_block *block, u32 chain_index,
 403                         enum mlxsw_sp_acl_profile profile,
 404                         struct mlxsw_afk_element_usage *tmplt_elusage)
 405{
 406        const struct mlxsw_sp_acl_profile_ops *ops;
 407        struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
 408        struct mlxsw_sp_acl_ruleset *ruleset;
 409
 410        ops = mlxsw_sp_acl_tcam_profile_ops(mlxsw_sp, profile);
 411        if (!ops)
 412                return ERR_PTR(-EINVAL);
 413
 414        ruleset = __mlxsw_sp_acl_ruleset_lookup(acl, block, chain_index, ops);
 415        if (ruleset) {
 416                mlxsw_sp_acl_ruleset_ref_inc(ruleset);
 417                return ruleset;
 418        }
 419        return mlxsw_sp_acl_ruleset_create(mlxsw_sp, block, chain_index, ops,
 420                                           tmplt_elusage);
 421}
 422
 423void mlxsw_sp_acl_ruleset_put(struct mlxsw_sp *mlxsw_sp,
 424                              struct mlxsw_sp_acl_ruleset *ruleset)
 425{
 426        mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset);
 427}
 428
 429u16 mlxsw_sp_acl_ruleset_group_id(struct mlxsw_sp_acl_ruleset *ruleset)
 430{
 431        const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
 432
 433        return ops->ruleset_group_id(ruleset->priv);
 434}
 435
 436struct mlxsw_sp_acl_rule_info *
 437mlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl,
 438                          struct mlxsw_afa_block *afa_block)
 439{
 440        struct mlxsw_sp_acl_rule_info *rulei;
 441        int err;
 442
 443        rulei = kzalloc(sizeof(*rulei), GFP_KERNEL);
 444        if (!rulei)
 445                return NULL;
 446
 447        if (afa_block) {
 448                rulei->act_block = afa_block;
 449                return rulei;
 450        }
 451
 452        rulei->act_block = mlxsw_afa_block_create(acl->mlxsw_sp->afa);
 453        if (IS_ERR(rulei->act_block)) {
 454                err = PTR_ERR(rulei->act_block);
 455                goto err_afa_block_create;
 456        }
 457        rulei->action_created = 1;
 458        return rulei;
 459
 460err_afa_block_create:
 461        kfree(rulei);
 462        return ERR_PTR(err);
 463}
 464
 465void mlxsw_sp_acl_rulei_destroy(struct mlxsw_sp_acl_rule_info *rulei)
 466{
 467        if (rulei->action_created)
 468                mlxsw_afa_block_destroy(rulei->act_block);
 469        kfree(rulei);
 470}
 471
 472int mlxsw_sp_acl_rulei_commit(struct mlxsw_sp_acl_rule_info *rulei)
 473{
 474        return mlxsw_afa_block_commit(rulei->act_block);
 475}
 476
 477void mlxsw_sp_acl_rulei_priority(struct mlxsw_sp_acl_rule_info *rulei,
 478                                 unsigned int priority)
 479{
 480        rulei->priority = priority;
 481}
 482
 483void mlxsw_sp_acl_rulei_keymask_u32(struct mlxsw_sp_acl_rule_info *rulei,
 484                                    enum mlxsw_afk_element element,
 485                                    u32 key_value, u32 mask_value)
 486{
 487        mlxsw_afk_values_add_u32(&rulei->values, element,
 488                                 key_value, mask_value);
 489}
 490
 491void mlxsw_sp_acl_rulei_keymask_buf(struct mlxsw_sp_acl_rule_info *rulei,
 492                                    enum mlxsw_afk_element element,
 493                                    const char *key_value,
 494                                    const char *mask_value, unsigned int len)
 495{
 496        mlxsw_afk_values_add_buf(&rulei->values, element,
 497                                 key_value, mask_value, len);
 498}
 499
 500int mlxsw_sp_acl_rulei_act_continue(struct mlxsw_sp_acl_rule_info *rulei)
 501{
 502        return mlxsw_afa_block_continue(rulei->act_block);
 503}
 504
 505int mlxsw_sp_acl_rulei_act_jump(struct mlxsw_sp_acl_rule_info *rulei,
 506                                u16 group_id)
 507{
 508        return mlxsw_afa_block_jump(rulei->act_block, group_id);
 509}
 510
 511int mlxsw_sp_acl_rulei_act_terminate(struct mlxsw_sp_acl_rule_info *rulei)
 512{
 513        return mlxsw_afa_block_terminate(rulei->act_block);
 514}
 515
 516int mlxsw_sp_acl_rulei_act_drop(struct mlxsw_sp_acl_rule_info *rulei)
 517{
 518        return mlxsw_afa_block_append_drop(rulei->act_block);
 519}
 520
 521int mlxsw_sp_acl_rulei_act_trap(struct mlxsw_sp_acl_rule_info *rulei)
 522{
 523        return mlxsw_afa_block_append_trap(rulei->act_block,
 524                                           MLXSW_TRAP_ID_ACL0);
 525}
 526
 527int mlxsw_sp_acl_rulei_act_fwd(struct mlxsw_sp *mlxsw_sp,
 528                               struct mlxsw_sp_acl_rule_info *rulei,
 529                               struct net_device *out_dev,
 530                               struct netlink_ext_ack *extack)
 531{
 532        struct mlxsw_sp_port *mlxsw_sp_port;
 533        u8 local_port;
 534        bool in_port;
 535
 536        if (out_dev) {
 537                if (!mlxsw_sp_port_dev_check(out_dev)) {
 538                        NL_SET_ERR_MSG_MOD(extack, "Invalid output device");
 539                        return -EINVAL;
 540                }
 541                mlxsw_sp_port = netdev_priv(out_dev);
 542                if (mlxsw_sp_port->mlxsw_sp != mlxsw_sp) {
 543                        NL_SET_ERR_MSG_MOD(extack, "Invalid output device");
 544                        return -EINVAL;
 545                }
 546                local_port = mlxsw_sp_port->local_port;
 547                in_port = false;
 548        } else {
 549                /* If out_dev is NULL, the caller wants to
 550                 * set forward to ingress port.
 551                 */
 552                local_port = 0;
 553                in_port = true;
 554        }
 555        return mlxsw_afa_block_append_fwd(rulei->act_block,
 556                                          local_port, in_port, extack);
 557}
 558
 559int mlxsw_sp_acl_rulei_act_mirror(struct mlxsw_sp *mlxsw_sp,
 560                                  struct mlxsw_sp_acl_rule_info *rulei,
 561                                  struct mlxsw_sp_acl_block *block,
 562                                  struct net_device *out_dev,
 563                                  struct netlink_ext_ack *extack)
 564{
 565        struct mlxsw_sp_acl_block_binding *binding;
 566        struct mlxsw_sp_port *in_port;
 567
 568        if (!list_is_singular(&block->binding_list)) {
 569                NL_SET_ERR_MSG_MOD(extack, "Only a single mirror source is allowed");
 570                return -EOPNOTSUPP;
 571        }
 572        binding = list_first_entry(&block->binding_list,
 573                                   struct mlxsw_sp_acl_block_binding, list);
 574        in_port = binding->mlxsw_sp_port;
 575
 576        return mlxsw_afa_block_append_mirror(rulei->act_block,
 577                                             in_port->local_port,
 578                                             out_dev,
 579                                             binding->ingress,
 580                                             extack);
 581}
 582
 583int mlxsw_sp_acl_rulei_act_vlan(struct mlxsw_sp *mlxsw_sp,
 584                                struct mlxsw_sp_acl_rule_info *rulei,
 585                                u32 action, u16 vid, u16 proto, u8 prio,
 586                                struct netlink_ext_ack *extack)
 587{
 588        u8 ethertype;
 589
 590        if (action == FLOW_ACTION_VLAN_MANGLE) {
 591                switch (proto) {
 592                case ETH_P_8021Q:
 593                        ethertype = 0;
 594                        break;
 595                case ETH_P_8021AD:
 596                        ethertype = 1;
 597                        break;
 598                default:
 599                        NL_SET_ERR_MSG_MOD(extack, "Unsupported VLAN protocol");
 600                        dev_err(mlxsw_sp->bus_info->dev, "Unsupported VLAN protocol %#04x\n",
 601                                proto);
 602                        return -EINVAL;
 603                }
 604
 605                return mlxsw_afa_block_append_vlan_modify(rulei->act_block,
 606                                                          vid, prio, ethertype,
 607                                                          extack);
 608        } else {
 609                NL_SET_ERR_MSG_MOD(extack, "Unsupported VLAN action");
 610                dev_err(mlxsw_sp->bus_info->dev, "Unsupported VLAN action\n");
 611                return -EINVAL;
 612        }
 613}
 614
 615int mlxsw_sp_acl_rulei_act_count(struct mlxsw_sp *mlxsw_sp,
 616                                 struct mlxsw_sp_acl_rule_info *rulei,
 617                                 struct netlink_ext_ack *extack)
 618{
 619        return mlxsw_afa_block_append_counter(rulei->act_block,
 620                                              &rulei->counter_index, extack);
 621}
 622
 623int mlxsw_sp_acl_rulei_act_fid_set(struct mlxsw_sp *mlxsw_sp,
 624                                   struct mlxsw_sp_acl_rule_info *rulei,
 625                                   u16 fid, struct netlink_ext_ack *extack)
 626{
 627        return mlxsw_afa_block_append_fid_set(rulei->act_block, fid, extack);
 628}
 629
 630struct mlxsw_sp_acl_rule *
 631mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp,
 632                         struct mlxsw_sp_acl_ruleset *ruleset,
 633                         unsigned long cookie,
 634                         struct mlxsw_afa_block *afa_block,
 635                         struct netlink_ext_ack *extack)
 636{
 637        const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
 638        struct mlxsw_sp_acl_rule *rule;
 639        int err;
 640
 641        mlxsw_sp_acl_ruleset_ref_inc(ruleset);
 642        rule = kzalloc(sizeof(*rule) + ops->rule_priv_size,
 643                       GFP_KERNEL);
 644        if (!rule) {
 645                err = -ENOMEM;
 646                goto err_alloc;
 647        }
 648        rule->cookie = cookie;
 649        rule->ruleset = ruleset;
 650
 651        rule->rulei = mlxsw_sp_acl_rulei_create(mlxsw_sp->acl, afa_block);
 652        if (IS_ERR(rule->rulei)) {
 653                err = PTR_ERR(rule->rulei);
 654                goto err_rulei_create;
 655        }
 656
 657        return rule;
 658
 659err_rulei_create:
 660        kfree(rule);
 661err_alloc:
 662        mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset);
 663        return ERR_PTR(err);
 664}
 665
 666void mlxsw_sp_acl_rule_destroy(struct mlxsw_sp *mlxsw_sp,
 667                               struct mlxsw_sp_acl_rule *rule)
 668{
 669        struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
 670
 671        mlxsw_sp_acl_rulei_destroy(rule->rulei);
 672        kfree(rule);
 673        mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset);
 674}
 675
 676int mlxsw_sp_acl_rule_add(struct mlxsw_sp *mlxsw_sp,
 677                          struct mlxsw_sp_acl_rule *rule)
 678{
 679        struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
 680        const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
 681        struct mlxsw_sp_acl_block *block = ruleset->ht_key.block;
 682        int err;
 683
 684        err = ops->rule_add(mlxsw_sp, ruleset->priv, rule->priv, rule->rulei);
 685        if (err)
 686                return err;
 687
 688        err = rhashtable_insert_fast(&ruleset->rule_ht, &rule->ht_node,
 689                                     mlxsw_sp_acl_rule_ht_params);
 690        if (err)
 691                goto err_rhashtable_insert;
 692
 693        if (!ruleset->ht_key.chain_index &&
 694            mlxsw_sp_acl_ruleset_is_singular(ruleset)) {
 695                /* We only need ruleset with chain index 0, the implicit
 696                 * one, to be directly bound to device. The rest of the
 697                 * rulesets are bound by "Goto action set".
 698                 */
 699                err = mlxsw_sp_acl_ruleset_block_bind(mlxsw_sp, ruleset, block);
 700                if (err)
 701                        goto err_ruleset_block_bind;
 702        }
 703
 704        list_add_tail(&rule->list, &mlxsw_sp->acl->rules);
 705        block->rule_count++;
 706        block->egress_blocker_rule_count += rule->rulei->egress_bind_blocker;
 707        return 0;
 708
 709err_ruleset_block_bind:
 710        rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
 711                               mlxsw_sp_acl_rule_ht_params);
 712err_rhashtable_insert:
 713        ops->rule_del(mlxsw_sp, rule->priv);
 714        return err;
 715}
 716
 717void mlxsw_sp_acl_rule_del(struct mlxsw_sp *mlxsw_sp,
 718                           struct mlxsw_sp_acl_rule *rule)
 719{
 720        struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
 721        const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
 722        struct mlxsw_sp_acl_block *block = ruleset->ht_key.block;
 723
 724        block->egress_blocker_rule_count -= rule->rulei->egress_bind_blocker;
 725        ruleset->ht_key.block->rule_count--;
 726        list_del(&rule->list);
 727        if (!ruleset->ht_key.chain_index &&
 728            mlxsw_sp_acl_ruleset_is_singular(ruleset))
 729                mlxsw_sp_acl_ruleset_block_unbind(mlxsw_sp, ruleset,
 730                                                  ruleset->ht_key.block);
 731        rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
 732                               mlxsw_sp_acl_rule_ht_params);
 733        ops->rule_del(mlxsw_sp, rule->priv);
 734}
 735
 736int mlxsw_sp_acl_rule_action_replace(struct mlxsw_sp *mlxsw_sp,
 737                                     struct mlxsw_sp_acl_rule *rule,
 738                                     struct mlxsw_afa_block *afa_block)
 739{
 740        struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
 741        const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
 742        struct mlxsw_sp_acl_rule_info *rulei;
 743
 744        rulei = mlxsw_sp_acl_rule_rulei(rule);
 745        rulei->act_block = afa_block;
 746
 747        return ops->rule_action_replace(mlxsw_sp, rule->priv, rule->rulei);
 748}
 749
 750struct mlxsw_sp_acl_rule *
 751mlxsw_sp_acl_rule_lookup(struct mlxsw_sp *mlxsw_sp,
 752                         struct mlxsw_sp_acl_ruleset *ruleset,
 753                         unsigned long cookie)
 754{
 755        return rhashtable_lookup_fast(&ruleset->rule_ht, &cookie,
 756                                       mlxsw_sp_acl_rule_ht_params);
 757}
 758
 759struct mlxsw_sp_acl_rule_info *
 760mlxsw_sp_acl_rule_rulei(struct mlxsw_sp_acl_rule *rule)
 761{
 762        return rule->rulei;
 763}
 764
 765static int mlxsw_sp_acl_rule_activity_update(struct mlxsw_sp *mlxsw_sp,
 766                                             struct mlxsw_sp_acl_rule *rule)
 767{
 768        struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
 769        const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
 770        bool active;
 771        int err;
 772
 773        err = ops->rule_activity_get(mlxsw_sp, rule->priv, &active);
 774        if (err)
 775                return err;
 776        if (active)
 777                rule->last_used = jiffies;
 778        return 0;
 779}
 780
 781static int mlxsw_sp_acl_rules_activity_update(struct mlxsw_sp_acl *acl)
 782{
 783        struct mlxsw_sp_acl_rule *rule;
 784        int err;
 785
 786        /* Protect internal structures from changes */
 787        rtnl_lock();
 788        list_for_each_entry(rule, &acl->rules, list) {
 789                err = mlxsw_sp_acl_rule_activity_update(acl->mlxsw_sp,
 790                                                        rule);
 791                if (err)
 792                        goto err_rule_update;
 793        }
 794        rtnl_unlock();
 795        return 0;
 796
 797err_rule_update:
 798        rtnl_unlock();
 799        return err;
 800}
 801
 802static void mlxsw_sp_acl_rule_activity_work_schedule(struct mlxsw_sp_acl *acl)
 803{
 804        unsigned long interval = acl->rule_activity_update.interval;
 805
 806        mlxsw_core_schedule_dw(&acl->rule_activity_update.dw,
 807                               msecs_to_jiffies(interval));
 808}
 809
 810static void mlxsw_sp_acl_rule_activity_update_work(struct work_struct *work)
 811{
 812        struct mlxsw_sp_acl *acl = container_of(work, struct mlxsw_sp_acl,
 813                                                rule_activity_update.dw.work);
 814        int err;
 815
 816        err = mlxsw_sp_acl_rules_activity_update(acl);
 817        if (err)
 818                dev_err(acl->mlxsw_sp->bus_info->dev, "Could not update acl activity");
 819
 820        mlxsw_sp_acl_rule_activity_work_schedule(acl);
 821}
 822
 823int mlxsw_sp_acl_rule_get_stats(struct mlxsw_sp *mlxsw_sp,
 824                                struct mlxsw_sp_acl_rule *rule,
 825                                u64 *packets, u64 *bytes, u64 *last_use)
 826
 827{
 828        struct mlxsw_sp_acl_rule_info *rulei;
 829        u64 current_packets;
 830        u64 current_bytes;
 831        int err;
 832
 833        rulei = mlxsw_sp_acl_rule_rulei(rule);
 834        err = mlxsw_sp_flow_counter_get(mlxsw_sp, rulei->counter_index,
 835                                        &current_packets, &current_bytes);
 836        if (err)
 837                return err;
 838
 839        *packets = current_packets - rule->last_packets;
 840        *bytes = current_bytes - rule->last_bytes;
 841        *last_use = rule->last_used;
 842
 843        rule->last_bytes = current_bytes;
 844        rule->last_packets = current_packets;
 845
 846        return 0;
 847}
 848
 849int mlxsw_sp_acl_init(struct mlxsw_sp *mlxsw_sp)
 850{
 851        struct mlxsw_sp_fid *fid;
 852        struct mlxsw_sp_acl *acl;
 853        size_t alloc_size;
 854        int err;
 855
 856        alloc_size = sizeof(*acl) + mlxsw_sp_acl_tcam_priv_size(mlxsw_sp);
 857        acl = kzalloc(alloc_size, GFP_KERNEL);
 858        if (!acl)
 859                return -ENOMEM;
 860        mlxsw_sp->acl = acl;
 861        acl->mlxsw_sp = mlxsw_sp;
 862        acl->afk = mlxsw_afk_create(MLXSW_CORE_RES_GET(mlxsw_sp->core,
 863                                                       ACL_FLEX_KEYS),
 864                                    mlxsw_sp->afk_ops);
 865        if (!acl->afk) {
 866                err = -ENOMEM;
 867                goto err_afk_create;
 868        }
 869
 870        err = rhashtable_init(&acl->ruleset_ht,
 871                              &mlxsw_sp_acl_ruleset_ht_params);
 872        if (err)
 873                goto err_rhashtable_init;
 874
 875        fid = mlxsw_sp_fid_dummy_get(mlxsw_sp);
 876        if (IS_ERR(fid)) {
 877                err = PTR_ERR(fid);
 878                goto err_fid_get;
 879        }
 880        acl->dummy_fid = fid;
 881
 882        INIT_LIST_HEAD(&acl->rules);
 883        err = mlxsw_sp_acl_tcam_init(mlxsw_sp, &acl->tcam);
 884        if (err)
 885                goto err_acl_ops_init;
 886
 887        /* Create the delayed work for the rule activity_update */
 888        INIT_DELAYED_WORK(&acl->rule_activity_update.dw,
 889                          mlxsw_sp_acl_rule_activity_update_work);
 890        acl->rule_activity_update.interval = MLXSW_SP_ACL_RULE_ACTIVITY_UPDATE_PERIOD_MS;
 891        mlxsw_core_schedule_dw(&acl->rule_activity_update.dw, 0);
 892        return 0;
 893
 894err_acl_ops_init:
 895        mlxsw_sp_fid_put(fid);
 896err_fid_get:
 897        rhashtable_destroy(&acl->ruleset_ht);
 898err_rhashtable_init:
 899        mlxsw_afk_destroy(acl->afk);
 900err_afk_create:
 901        kfree(acl);
 902        return err;
 903}
 904
 905void mlxsw_sp_acl_fini(struct mlxsw_sp *mlxsw_sp)
 906{
 907        struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
 908
 909        cancel_delayed_work_sync(&mlxsw_sp->acl->rule_activity_update.dw);
 910        mlxsw_sp_acl_tcam_fini(mlxsw_sp, &acl->tcam);
 911        WARN_ON(!list_empty(&acl->rules));
 912        mlxsw_sp_fid_put(acl->dummy_fid);
 913        rhashtable_destroy(&acl->ruleset_ht);
 914        mlxsw_afk_destroy(acl->afk);
 915        kfree(acl);
 916}
 917
 918u32 mlxsw_sp_acl_region_rehash_intrvl_get(struct mlxsw_sp *mlxsw_sp)
 919{
 920        struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
 921
 922        return mlxsw_sp_acl_tcam_vregion_rehash_intrvl_get(mlxsw_sp,
 923                                                           &acl->tcam);
 924}
 925
 926int mlxsw_sp_acl_region_rehash_intrvl_set(struct mlxsw_sp *mlxsw_sp, u32 val)
 927{
 928        struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
 929
 930        return mlxsw_sp_acl_tcam_vregion_rehash_intrvl_set(mlxsw_sp,
 931                                                           &acl->tcam, val);
 932}
 933