root/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mlxsw_sp_acl_afk
  2. mlxsw_sp_acl_dummy_fid
  3. mlxsw_sp_acl_block_mlxsw_sp
  4. mlxsw_sp_acl_block_rule_count
  5. mlxsw_sp_acl_block_disable_inc
  6. mlxsw_sp_acl_block_disable_dec
  7. mlxsw_sp_acl_block_disabled
  8. mlxsw_sp_acl_block_is_egress_bound
  9. mlxsw_sp_acl_ruleset_is_singular
  10. mlxsw_sp_acl_ruleset_bind
  11. mlxsw_sp_acl_ruleset_unbind
  12. mlxsw_sp_acl_ruleset_block_bound
  13. mlxsw_sp_acl_ruleset_block_bind
  14. mlxsw_sp_acl_ruleset_block_unbind
  15. mlxsw_sp_acl_block_create
  16. mlxsw_sp_acl_block_destroy
  17. mlxsw_sp_acl_block_lookup
  18. mlxsw_sp_acl_block_bind
  19. mlxsw_sp_acl_block_unbind
  20. mlxsw_sp_acl_ruleset_create
  21. mlxsw_sp_acl_ruleset_destroy
  22. mlxsw_sp_acl_ruleset_ref_inc
  23. mlxsw_sp_acl_ruleset_ref_dec
  24. __mlxsw_sp_acl_ruleset_lookup
  25. mlxsw_sp_acl_ruleset_lookup
  26. mlxsw_sp_acl_ruleset_get
  27. mlxsw_sp_acl_ruleset_put
  28. mlxsw_sp_acl_ruleset_group_id
  29. mlxsw_sp_acl_rulei_create
  30. mlxsw_sp_acl_rulei_destroy
  31. mlxsw_sp_acl_rulei_commit
  32. mlxsw_sp_acl_rulei_priority
  33. mlxsw_sp_acl_rulei_keymask_u32
  34. mlxsw_sp_acl_rulei_keymask_buf
  35. mlxsw_sp_acl_rulei_act_continue
  36. mlxsw_sp_acl_rulei_act_jump
  37. mlxsw_sp_acl_rulei_act_terminate
  38. mlxsw_sp_acl_rulei_act_drop
  39. mlxsw_sp_acl_rulei_act_trap
  40. mlxsw_sp_acl_rulei_act_fwd
  41. mlxsw_sp_acl_rulei_act_mirror
  42. mlxsw_sp_acl_rulei_act_vlan
  43. mlxsw_sp_acl_rulei_act_count
  44. mlxsw_sp_acl_rulei_act_fid_set
  45. mlxsw_sp_acl_rule_create
  46. mlxsw_sp_acl_rule_destroy
  47. mlxsw_sp_acl_rule_add
  48. mlxsw_sp_acl_rule_del
  49. mlxsw_sp_acl_rule_action_replace
  50. mlxsw_sp_acl_rule_lookup
  51. mlxsw_sp_acl_rule_rulei
  52. mlxsw_sp_acl_rule_activity_update
  53. mlxsw_sp_acl_rules_activity_update
  54. mlxsw_sp_acl_rule_activity_work_schedule
  55. mlxsw_sp_acl_rule_activity_update_work
  56. mlxsw_sp_acl_rule_get_stats
  57. mlxsw_sp_acl_init
  58. mlxsw_sp_acl_fini
  59. mlxsw_sp_acl_region_rehash_intrvl_get
  60. mlxsw_sp_acl_region_rehash_intrvl_set

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

/* [<][>][^][v][top][bottom][index][help] */