prestera_acl.c (20995B)
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2/* Copyright (c) 2020-2021 Marvell International Ltd. All rights reserved */ 3 4#include <linux/rhashtable.h> 5 6#include "prestera_acl.h" 7#include "prestera_flow.h" 8#include "prestera_hw.h" 9#include "prestera.h" 10 11#define ACL_KEYMASK_SIZE \ 12 (sizeof(__be32) * __PRESTERA_ACL_RULE_MATCH_TYPE_MAX) 13 14struct prestera_acl { 15 struct prestera_switch *sw; 16 struct list_head vtcam_list; 17 struct list_head rules; 18 struct rhashtable ruleset_ht; 19 struct rhashtable acl_rule_entry_ht; 20 struct idr uid; 21}; 22 23struct prestera_acl_ruleset_ht_key { 24 struct prestera_flow_block *block; 25 u32 chain_index; 26}; 27 28struct prestera_acl_rule_entry { 29 struct rhash_head ht_node; 30 struct prestera_acl_rule_entry_key key; 31 u32 hw_id; 32 u32 vtcam_id; 33 struct { 34 struct { 35 u8 valid:1; 36 } accept, drop, trap; 37 struct { 38 u8 valid:1; 39 struct prestera_acl_action_police i; 40 } police; 41 struct { 42 struct prestera_acl_action_jump i; 43 u8 valid:1; 44 } jump; 45 struct { 46 u32 id; 47 struct prestera_counter_block *block; 48 } counter; 49 }; 50}; 51 52struct prestera_acl_ruleset { 53 struct rhash_head ht_node; /* Member of acl HT */ 54 struct prestera_acl_ruleset_ht_key ht_key; 55 struct rhashtable rule_ht; 56 struct prestera_acl *acl; 57 unsigned long rule_count; 58 refcount_t refcount; 59 void *keymask; 60 u32 vtcam_id; 61 u32 index; 62 u16 pcl_id; 63 bool offload; 64}; 65 66struct prestera_acl_vtcam { 67 struct list_head list; 68 __be32 keymask[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX]; 69 refcount_t refcount; 70 u32 id; 71 bool is_keymask_set; 72 u8 lookup; 73}; 74 75static const struct rhashtable_params prestera_acl_ruleset_ht_params = { 76 .key_len = sizeof(struct prestera_acl_ruleset_ht_key), 77 .key_offset = offsetof(struct prestera_acl_ruleset, ht_key), 78 .head_offset = offsetof(struct prestera_acl_ruleset, ht_node), 79 .automatic_shrinking = true, 80}; 81 82static const struct rhashtable_params prestera_acl_rule_ht_params = { 83 .key_len = sizeof(unsigned long), 84 .key_offset = offsetof(struct prestera_acl_rule, cookie), 85 .head_offset = offsetof(struct prestera_acl_rule, ht_node), 86 .automatic_shrinking = true, 87}; 88 89static const struct rhashtable_params __prestera_acl_rule_entry_ht_params = { 90 .key_offset = offsetof(struct prestera_acl_rule_entry, key), 91 .head_offset = offsetof(struct prestera_acl_rule_entry, ht_node), 92 .key_len = sizeof(struct prestera_acl_rule_entry_key), 93 .automatic_shrinking = true, 94}; 95 96int prestera_acl_chain_to_client(u32 chain_index, u32 *client) 97{ 98 static const u32 client_map[] = { 99 PRESTERA_HW_COUNTER_CLIENT_LOOKUP_0, 100 PRESTERA_HW_COUNTER_CLIENT_LOOKUP_1, 101 PRESTERA_HW_COUNTER_CLIENT_LOOKUP_2 102 }; 103 104 if (chain_index >= ARRAY_SIZE(client_map)) 105 return -EINVAL; 106 107 *client = client_map[chain_index]; 108 return 0; 109} 110 111static bool prestera_acl_chain_is_supported(u32 chain_index) 112{ 113 return (chain_index & ~PRESTERA_ACL_CHAIN_MASK) == 0; 114} 115 116static struct prestera_acl_ruleset * 117prestera_acl_ruleset_create(struct prestera_acl *acl, 118 struct prestera_flow_block *block, 119 u32 chain_index) 120{ 121 struct prestera_acl_ruleset *ruleset; 122 u32 uid = 0; 123 int err; 124 125 if (!prestera_acl_chain_is_supported(chain_index)) 126 return ERR_PTR(-EINVAL); 127 128 ruleset = kzalloc(sizeof(*ruleset), GFP_KERNEL); 129 if (!ruleset) 130 return ERR_PTR(-ENOMEM); 131 132 ruleset->acl = acl; 133 ruleset->ht_key.block = block; 134 ruleset->ht_key.chain_index = chain_index; 135 refcount_set(&ruleset->refcount, 1); 136 137 err = rhashtable_init(&ruleset->rule_ht, &prestera_acl_rule_ht_params); 138 if (err) 139 goto err_rhashtable_init; 140 141 err = idr_alloc_u32(&acl->uid, NULL, &uid, U8_MAX, GFP_KERNEL); 142 if (err) 143 goto err_ruleset_create; 144 145 /* make pcl-id based on uid */ 146 ruleset->pcl_id = PRESTERA_ACL_PCL_ID_MAKE((u8)uid, chain_index); 147 ruleset->index = uid; 148 149 err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node, 150 prestera_acl_ruleset_ht_params); 151 if (err) 152 goto err_ruleset_ht_insert; 153 154 return ruleset; 155 156err_ruleset_ht_insert: 157 idr_remove(&acl->uid, uid); 158err_ruleset_create: 159 rhashtable_destroy(&ruleset->rule_ht); 160err_rhashtable_init: 161 kfree(ruleset); 162 return ERR_PTR(err); 163} 164 165void prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset *ruleset, 166 void *keymask) 167{ 168 ruleset->keymask = kmemdup(keymask, ACL_KEYMASK_SIZE, GFP_KERNEL); 169} 170 171int prestera_acl_ruleset_offload(struct prestera_acl_ruleset *ruleset) 172{ 173 struct prestera_acl_iface iface; 174 u32 vtcam_id; 175 int err; 176 177 if (ruleset->offload) 178 return -EEXIST; 179 180 err = prestera_acl_vtcam_id_get(ruleset->acl, 181 ruleset->ht_key.chain_index, 182 ruleset->keymask, &vtcam_id); 183 if (err) 184 goto err_vtcam_create; 185 186 if (ruleset->ht_key.chain_index) { 187 /* for chain > 0, bind iface index to pcl-id to be able 188 * to jump from any other ruleset to this one using the index. 189 */ 190 iface.index = ruleset->index; 191 iface.type = PRESTERA_ACL_IFACE_TYPE_INDEX; 192 err = prestera_hw_vtcam_iface_bind(ruleset->acl->sw, &iface, 193 vtcam_id, ruleset->pcl_id); 194 if (err) 195 goto err_ruleset_bind; 196 } 197 198 ruleset->vtcam_id = vtcam_id; 199 ruleset->offload = true; 200 return 0; 201 202err_ruleset_bind: 203 prestera_acl_vtcam_id_put(ruleset->acl, ruleset->vtcam_id); 204err_vtcam_create: 205 return err; 206} 207 208static void prestera_acl_ruleset_destroy(struct prestera_acl_ruleset *ruleset) 209{ 210 struct prestera_acl *acl = ruleset->acl; 211 u8 uid = ruleset->pcl_id & PRESTERA_ACL_KEYMASK_PCL_ID_USER; 212 int err; 213 214 rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node, 215 prestera_acl_ruleset_ht_params); 216 217 if (ruleset->offload) { 218 if (ruleset->ht_key.chain_index) { 219 struct prestera_acl_iface iface = { 220 .type = PRESTERA_ACL_IFACE_TYPE_INDEX, 221 .index = ruleset->index 222 }; 223 err = prestera_hw_vtcam_iface_unbind(acl->sw, &iface, 224 ruleset->vtcam_id); 225 WARN_ON(err); 226 } 227 WARN_ON(prestera_acl_vtcam_id_put(acl, ruleset->vtcam_id)); 228 } 229 230 idr_remove(&acl->uid, uid); 231 rhashtable_destroy(&ruleset->rule_ht); 232 kfree(ruleset->keymask); 233 kfree(ruleset); 234} 235 236static struct prestera_acl_ruleset * 237__prestera_acl_ruleset_lookup(struct prestera_acl *acl, 238 struct prestera_flow_block *block, 239 u32 chain_index) 240{ 241 struct prestera_acl_ruleset_ht_key ht_key; 242 243 memset(&ht_key, 0, sizeof(ht_key)); 244 ht_key.block = block; 245 ht_key.chain_index = chain_index; 246 return rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key, 247 prestera_acl_ruleset_ht_params); 248} 249 250struct prestera_acl_ruleset * 251prestera_acl_ruleset_lookup(struct prestera_acl *acl, 252 struct prestera_flow_block *block, 253 u32 chain_index) 254{ 255 struct prestera_acl_ruleset *ruleset; 256 257 ruleset = __prestera_acl_ruleset_lookup(acl, block, chain_index); 258 if (!ruleset) 259 return ERR_PTR(-ENOENT); 260 261 refcount_inc(&ruleset->refcount); 262 return ruleset; 263} 264 265struct prestera_acl_ruleset * 266prestera_acl_ruleset_get(struct prestera_acl *acl, 267 struct prestera_flow_block *block, 268 u32 chain_index) 269{ 270 struct prestera_acl_ruleset *ruleset; 271 272 ruleset = __prestera_acl_ruleset_lookup(acl, block, chain_index); 273 if (ruleset) { 274 refcount_inc(&ruleset->refcount); 275 return ruleset; 276 } 277 278 return prestera_acl_ruleset_create(acl, block, chain_index); 279} 280 281void prestera_acl_ruleset_put(struct prestera_acl_ruleset *ruleset) 282{ 283 if (!refcount_dec_and_test(&ruleset->refcount)) 284 return; 285 286 prestera_acl_ruleset_destroy(ruleset); 287} 288 289int prestera_acl_ruleset_bind(struct prestera_acl_ruleset *ruleset, 290 struct prestera_port *port) 291{ 292 struct prestera_acl_iface iface = { 293 .type = PRESTERA_ACL_IFACE_TYPE_PORT, 294 .port = port 295 }; 296 297 return prestera_hw_vtcam_iface_bind(port->sw, &iface, ruleset->vtcam_id, 298 ruleset->pcl_id); 299} 300 301int prestera_acl_ruleset_unbind(struct prestera_acl_ruleset *ruleset, 302 struct prestera_port *port) 303{ 304 struct prestera_acl_iface iface = { 305 .type = PRESTERA_ACL_IFACE_TYPE_PORT, 306 .port = port 307 }; 308 309 return prestera_hw_vtcam_iface_unbind(port->sw, &iface, 310 ruleset->vtcam_id); 311} 312 313static int prestera_acl_ruleset_block_bind(struct prestera_acl_ruleset *ruleset, 314 struct prestera_flow_block *block) 315{ 316 struct prestera_flow_block_binding *binding; 317 int err; 318 319 block->ruleset_zero = ruleset; 320 list_for_each_entry(binding, &block->binding_list, list) { 321 err = prestera_acl_ruleset_bind(ruleset, binding->port); 322 if (err) 323 goto rollback; 324 } 325 return 0; 326 327rollback: 328 list_for_each_entry_continue_reverse(binding, &block->binding_list, 329 list) 330 err = prestera_acl_ruleset_unbind(ruleset, binding->port); 331 block->ruleset_zero = NULL; 332 333 return err; 334} 335 336static void 337prestera_acl_ruleset_block_unbind(struct prestera_acl_ruleset *ruleset, 338 struct prestera_flow_block *block) 339{ 340 struct prestera_flow_block_binding *binding; 341 342 list_for_each_entry(binding, &block->binding_list, list) 343 prestera_acl_ruleset_unbind(ruleset, binding->port); 344 block->ruleset_zero = NULL; 345} 346 347void 348prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule, u16 pcl_id) 349{ 350 struct prestera_acl_match *r_match = &rule->re_key.match; 351 __be16 pcl_id_mask = htons(PRESTERA_ACL_KEYMASK_PCL_ID); 352 __be16 pcl_id_key = htons(pcl_id); 353 354 rule_match_set(r_match->key, PCL_ID, pcl_id_key); 355 rule_match_set(r_match->mask, PCL_ID, pcl_id_mask); 356} 357 358struct prestera_acl_rule * 359prestera_acl_rule_lookup(struct prestera_acl_ruleset *ruleset, 360 unsigned long cookie) 361{ 362 return rhashtable_lookup_fast(&ruleset->rule_ht, &cookie, 363 prestera_acl_rule_ht_params); 364} 365 366u32 prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset *ruleset) 367{ 368 return ruleset->index; 369} 370 371bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset) 372{ 373 return ruleset->offload; 374} 375 376struct prestera_acl_rule * 377prestera_acl_rule_create(struct prestera_acl_ruleset *ruleset, 378 unsigned long cookie, u32 chain_index) 379{ 380 struct prestera_acl_rule *rule; 381 382 rule = kzalloc(sizeof(*rule), GFP_KERNEL); 383 if (!rule) 384 return ERR_PTR(-ENOMEM); 385 386 rule->ruleset = ruleset; 387 rule->cookie = cookie; 388 rule->chain_index = chain_index; 389 390 refcount_inc(&ruleset->refcount); 391 392 return rule; 393} 394 395void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule, 396 u32 priority) 397{ 398 rule->priority = priority; 399} 400 401void prestera_acl_rule_destroy(struct prestera_acl_rule *rule) 402{ 403 if (rule->jump_ruleset) 404 /* release ruleset kept by jump action */ 405 prestera_acl_ruleset_put(rule->jump_ruleset); 406 407 prestera_acl_ruleset_put(rule->ruleset); 408 kfree(rule); 409} 410 411int prestera_acl_rule_add(struct prestera_switch *sw, 412 struct prestera_acl_rule *rule) 413{ 414 int err; 415 struct prestera_acl_ruleset *ruleset = rule->ruleset; 416 struct prestera_flow_block *block = ruleset->ht_key.block; 417 418 /* try to add rule to hash table first */ 419 err = rhashtable_insert_fast(&ruleset->rule_ht, &rule->ht_node, 420 prestera_acl_rule_ht_params); 421 if (err) 422 goto err_ht_insert; 423 424 prestera_acl_rule_keymask_pcl_id_set(rule, ruleset->pcl_id); 425 rule->re_arg.vtcam_id = ruleset->vtcam_id; 426 rule->re_key.prio = rule->priority; 427 428 rule->re = prestera_acl_rule_entry_find(sw->acl, &rule->re_key); 429 err = WARN_ON(rule->re) ? -EEXIST : 0; 430 if (err) 431 goto err_rule_add; 432 433 rule->re = prestera_acl_rule_entry_create(sw->acl, &rule->re_key, 434 &rule->re_arg); 435 err = !rule->re ? -EINVAL : 0; 436 if (err) 437 goto err_rule_add; 438 439 /* bind the block (all ports) to chain index 0, rest of 440 * the chains are bound to goto action 441 */ 442 if (!ruleset->ht_key.chain_index && !ruleset->rule_count) { 443 err = prestera_acl_ruleset_block_bind(ruleset, block); 444 if (err) 445 goto err_acl_block_bind; 446 } 447 448 list_add_tail(&rule->list, &sw->acl->rules); 449 ruleset->rule_count++; 450 return 0; 451 452err_acl_block_bind: 453 prestera_acl_rule_entry_destroy(sw->acl, rule->re); 454err_rule_add: 455 rule->re = NULL; 456 rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node, 457 prestera_acl_rule_ht_params); 458err_ht_insert: 459 return err; 460} 461 462void prestera_acl_rule_del(struct prestera_switch *sw, 463 struct prestera_acl_rule *rule) 464{ 465 struct prestera_acl_ruleset *ruleset = rule->ruleset; 466 struct prestera_flow_block *block = ruleset->ht_key.block; 467 468 rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node, 469 prestera_acl_rule_ht_params); 470 ruleset->rule_count--; 471 list_del(&rule->list); 472 473 prestera_acl_rule_entry_destroy(sw->acl, rule->re); 474 475 /* unbind block (all ports) */ 476 if (!ruleset->ht_key.chain_index && !ruleset->rule_count) 477 prestera_acl_ruleset_block_unbind(ruleset, block); 478} 479 480int prestera_acl_rule_get_stats(struct prestera_acl *acl, 481 struct prestera_acl_rule *rule, 482 u64 *packets, u64 *bytes, u64 *last_use) 483{ 484 u64 current_packets; 485 u64 current_bytes; 486 int err; 487 488 err = prestera_counter_stats_get(acl->sw->counter, 489 rule->re->counter.block, 490 rule->re->counter.id, 491 ¤t_packets, ¤t_bytes); 492 if (err) 493 return err; 494 495 *packets = current_packets; 496 *bytes = current_bytes; 497 *last_use = jiffies; 498 499 return 0; 500} 501 502struct prestera_acl_rule_entry * 503prestera_acl_rule_entry_find(struct prestera_acl *acl, 504 struct prestera_acl_rule_entry_key *key) 505{ 506 return rhashtable_lookup_fast(&acl->acl_rule_entry_ht, key, 507 __prestera_acl_rule_entry_ht_params); 508} 509 510static int __prestera_acl_rule_entry2hw_del(struct prestera_switch *sw, 511 struct prestera_acl_rule_entry *e) 512{ 513 return prestera_hw_vtcam_rule_del(sw, e->vtcam_id, e->hw_id); 514} 515 516static int __prestera_acl_rule_entry2hw_add(struct prestera_switch *sw, 517 struct prestera_acl_rule_entry *e) 518{ 519 struct prestera_acl_hw_action_info act_hw[PRESTERA_ACL_RULE_ACTION_MAX]; 520 int act_num; 521 522 memset(&act_hw, 0, sizeof(act_hw)); 523 act_num = 0; 524 525 /* accept */ 526 if (e->accept.valid) { 527 act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_ACCEPT; 528 act_num++; 529 } 530 /* drop */ 531 if (e->drop.valid) { 532 act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_DROP; 533 act_num++; 534 } 535 /* trap */ 536 if (e->trap.valid) { 537 act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_TRAP; 538 act_num++; 539 } 540 /* police */ 541 if (e->police.valid) { 542 act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_POLICE; 543 act_hw[act_num].police = e->police.i; 544 act_num++; 545 } 546 /* jump */ 547 if (e->jump.valid) { 548 act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_JUMP; 549 act_hw[act_num].jump = e->jump.i; 550 act_num++; 551 } 552 /* counter */ 553 if (e->counter.block) { 554 act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_COUNT; 555 act_hw[act_num].count.id = e->counter.id; 556 act_num++; 557 } 558 559 return prestera_hw_vtcam_rule_add(sw, e->vtcam_id, e->key.prio, 560 e->key.match.key, e->key.match.mask, 561 act_hw, act_num, &e->hw_id); 562} 563 564static void 565__prestera_acl_rule_entry_act_destruct(struct prestera_switch *sw, 566 struct prestera_acl_rule_entry *e) 567{ 568 /* counter */ 569 prestera_counter_put(sw->counter, e->counter.block, e->counter.id); 570 /* police */ 571 if (e->police.valid) 572 prestera_hw_policer_release(sw, e->police.i.id); 573} 574 575void prestera_acl_rule_entry_destroy(struct prestera_acl *acl, 576 struct prestera_acl_rule_entry *e) 577{ 578 int ret; 579 580 rhashtable_remove_fast(&acl->acl_rule_entry_ht, &e->ht_node, 581 __prestera_acl_rule_entry_ht_params); 582 583 ret = __prestera_acl_rule_entry2hw_del(acl->sw, e); 584 WARN_ON(ret && ret != -ENODEV); 585 586 __prestera_acl_rule_entry_act_destruct(acl->sw, e); 587 kfree(e); 588} 589 590static int 591__prestera_acl_rule_entry_act_construct(struct prestera_switch *sw, 592 struct prestera_acl_rule_entry *e, 593 struct prestera_acl_rule_entry_arg *arg) 594{ 595 int err; 596 597 /* accept */ 598 e->accept.valid = arg->accept.valid; 599 /* drop */ 600 e->drop.valid = arg->drop.valid; 601 /* trap */ 602 e->trap.valid = arg->trap.valid; 603 /* jump */ 604 e->jump.valid = arg->jump.valid; 605 e->jump.i = arg->jump.i; 606 /* police */ 607 if (arg->police.valid) { 608 u8 type = arg->police.ingress ? PRESTERA_POLICER_TYPE_INGRESS : 609 PRESTERA_POLICER_TYPE_EGRESS; 610 611 err = prestera_hw_policer_create(sw, type, &e->police.i.id); 612 if (err) 613 goto err_out; 614 615 err = prestera_hw_policer_sr_tcm_set(sw, e->police.i.id, 616 arg->police.rate, 617 arg->police.burst); 618 if (err) { 619 prestera_hw_policer_release(sw, e->police.i.id); 620 goto err_out; 621 } 622 e->police.valid = arg->police.valid; 623 } 624 /* counter */ 625 if (arg->count.valid) { 626 err = prestera_counter_get(sw->counter, arg->count.client, 627 &e->counter.block, 628 &e->counter.id); 629 if (err) 630 goto err_out; 631 } 632 633 return 0; 634 635err_out: 636 __prestera_acl_rule_entry_act_destruct(sw, e); 637 return -EINVAL; 638} 639 640struct prestera_acl_rule_entry * 641prestera_acl_rule_entry_create(struct prestera_acl *acl, 642 struct prestera_acl_rule_entry_key *key, 643 struct prestera_acl_rule_entry_arg *arg) 644{ 645 struct prestera_acl_rule_entry *e; 646 int err; 647 648 e = kzalloc(sizeof(*e), GFP_KERNEL); 649 if (!e) 650 goto err_kzalloc; 651 652 memcpy(&e->key, key, sizeof(*key)); 653 e->vtcam_id = arg->vtcam_id; 654 err = __prestera_acl_rule_entry_act_construct(acl->sw, e, arg); 655 if (err) 656 goto err_act_construct; 657 658 err = __prestera_acl_rule_entry2hw_add(acl->sw, e); 659 if (err) 660 goto err_hw_add; 661 662 err = rhashtable_insert_fast(&acl->acl_rule_entry_ht, &e->ht_node, 663 __prestera_acl_rule_entry_ht_params); 664 if (err) 665 goto err_ht_insert; 666 667 return e; 668 669err_ht_insert: 670 WARN_ON(__prestera_acl_rule_entry2hw_del(acl->sw, e)); 671err_hw_add: 672 __prestera_acl_rule_entry_act_destruct(acl->sw, e); 673err_act_construct: 674 kfree(e); 675err_kzalloc: 676 return NULL; 677} 678 679static int __prestera_acl_vtcam_id_try_fit(struct prestera_acl *acl, u8 lookup, 680 void *keymask, u32 *vtcam_id) 681{ 682 struct prestera_acl_vtcam *vtcam; 683 int i; 684 685 list_for_each_entry(vtcam, &acl->vtcam_list, list) { 686 if (lookup != vtcam->lookup) 687 continue; 688 689 if (!keymask && !vtcam->is_keymask_set) 690 goto vtcam_found; 691 692 if (!(keymask && vtcam->is_keymask_set)) 693 continue; 694 695 /* try to fit with vtcam keymask */ 696 for (i = 0; i < __PRESTERA_ACL_RULE_MATCH_TYPE_MAX; i++) { 697 __be32 __keymask = ((__be32 *)keymask)[i]; 698 699 if (!__keymask) 700 /* vtcam keymask in not interested */ 701 continue; 702 703 if (__keymask & ~vtcam->keymask[i]) 704 /* keymask does not fit the vtcam keymask */ 705 break; 706 } 707 708 if (i == __PRESTERA_ACL_RULE_MATCH_TYPE_MAX) 709 /* keymask fits vtcam keymask, return it */ 710 goto vtcam_found; 711 } 712 713 /* nothing is found */ 714 return -ENOENT; 715 716vtcam_found: 717 refcount_inc(&vtcam->refcount); 718 *vtcam_id = vtcam->id; 719 return 0; 720} 721 722int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup, 723 void *keymask, u32 *vtcam_id) 724{ 725 struct prestera_acl_vtcam *vtcam; 726 u32 new_vtcam_id; 727 int err; 728 729 /* find the vtcam that suits keymask. We do not expect to have 730 * a big number of vtcams, so, the list type for vtcam list is 731 * fine for now 732 */ 733 list_for_each_entry(vtcam, &acl->vtcam_list, list) { 734 if (lookup != vtcam->lookup) 735 continue; 736 737 if (!keymask && !vtcam->is_keymask_set) { 738 refcount_inc(&vtcam->refcount); 739 goto vtcam_found; 740 } 741 742 if (keymask && vtcam->is_keymask_set && 743 !memcmp(keymask, vtcam->keymask, sizeof(vtcam->keymask))) { 744 refcount_inc(&vtcam->refcount); 745 goto vtcam_found; 746 } 747 } 748 749 /* vtcam not found, try to create new one */ 750 vtcam = kzalloc(sizeof(*vtcam), GFP_KERNEL); 751 if (!vtcam) 752 return -ENOMEM; 753 754 err = prestera_hw_vtcam_create(acl->sw, lookup, keymask, &new_vtcam_id, 755 PRESTERA_HW_VTCAM_DIR_INGRESS); 756 if (err) { 757 kfree(vtcam); 758 759 /* cannot create new, try to fit into existing vtcam */ 760 if (__prestera_acl_vtcam_id_try_fit(acl, lookup, 761 keymask, &new_vtcam_id)) 762 return err; 763 764 *vtcam_id = new_vtcam_id; 765 return 0; 766 } 767 768 vtcam->id = new_vtcam_id; 769 vtcam->lookup = lookup; 770 if (keymask) { 771 memcpy(vtcam->keymask, keymask, sizeof(vtcam->keymask)); 772 vtcam->is_keymask_set = true; 773 } 774 refcount_set(&vtcam->refcount, 1); 775 list_add_rcu(&vtcam->list, &acl->vtcam_list); 776 777vtcam_found: 778 *vtcam_id = vtcam->id; 779 return 0; 780} 781 782int prestera_acl_vtcam_id_put(struct prestera_acl *acl, u32 vtcam_id) 783{ 784 struct prestera_acl_vtcam *vtcam; 785 int err; 786 787 list_for_each_entry(vtcam, &acl->vtcam_list, list) { 788 if (vtcam_id != vtcam->id) 789 continue; 790 791 if (!refcount_dec_and_test(&vtcam->refcount)) 792 return 0; 793 794 err = prestera_hw_vtcam_destroy(acl->sw, vtcam->id); 795 if (err && err != -ENODEV) { 796 refcount_set(&vtcam->refcount, 1); 797 return err; 798 } 799 800 list_del(&vtcam->list); 801 kfree(vtcam); 802 return 0; 803 } 804 805 return -ENOENT; 806} 807 808int prestera_acl_init(struct prestera_switch *sw) 809{ 810 struct prestera_acl *acl; 811 int err; 812 813 acl = kzalloc(sizeof(*acl), GFP_KERNEL); 814 if (!acl) 815 return -ENOMEM; 816 817 acl->sw = sw; 818 INIT_LIST_HEAD(&acl->rules); 819 INIT_LIST_HEAD(&acl->vtcam_list); 820 idr_init(&acl->uid); 821 822 err = rhashtable_init(&acl->acl_rule_entry_ht, 823 &__prestera_acl_rule_entry_ht_params); 824 if (err) 825 goto err_acl_rule_entry_ht_init; 826 827 err = rhashtable_init(&acl->ruleset_ht, 828 &prestera_acl_ruleset_ht_params); 829 if (err) 830 goto err_ruleset_ht_init; 831 832 sw->acl = acl; 833 834 return 0; 835 836err_ruleset_ht_init: 837 rhashtable_destroy(&acl->acl_rule_entry_ht); 838err_acl_rule_entry_ht_init: 839 kfree(acl); 840 return err; 841} 842 843void prestera_acl_fini(struct prestera_switch *sw) 844{ 845 struct prestera_acl *acl = sw->acl; 846 847 WARN_ON(!idr_is_empty(&acl->uid)); 848 idr_destroy(&acl->uid); 849 850 WARN_ON(!list_empty(&acl->vtcam_list)); 851 WARN_ON(!list_empty(&acl->rules)); 852 853 rhashtable_destroy(&acl->ruleset_ht); 854 rhashtable_destroy(&acl->acl_rule_entry_ht); 855 856 kfree(acl); 857}