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