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