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