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 }; 26 27 struct prestera_acl_rule_entry { 28 struct rhash_head ht_node; 29 struct prestera_acl_rule_entry_key key; 30 u32 hw_id; 31 u32 vtcam_id; 32 struct { 33 struct { 34 u8 valid:1; 35 } accept, drop, trap; 36 struct { 37 u32 id; 38 struct prestera_counter_block *block; 39 } counter; 40 }; 41 }; 42 43 struct prestera_acl_ruleset { 44 struct rhash_head ht_node; /* Member of acl HT */ 45 struct prestera_acl_ruleset_ht_key ht_key; 46 struct rhashtable rule_ht; 47 struct prestera_acl *acl; 48 unsigned long rule_count; 49 refcount_t refcount; 50 void *keymask; 51 u32 vtcam_id; 52 u16 pcl_id; 53 bool offload; 54 }; 55 56 struct prestera_acl_vtcam { 57 struct list_head list; 58 __be32 keymask[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX]; 59 refcount_t refcount; 60 u32 id; 61 bool is_keymask_set; 62 u8 lookup; 63 }; 64 65 static const struct rhashtable_params prestera_acl_ruleset_ht_params = { 66 .key_len = sizeof(struct prestera_acl_ruleset_ht_key), 67 .key_offset = offsetof(struct prestera_acl_ruleset, ht_key), 68 .head_offset = offsetof(struct prestera_acl_ruleset, ht_node), 69 .automatic_shrinking = true, 70 }; 71 72 static const struct rhashtable_params prestera_acl_rule_ht_params = { 73 .key_len = sizeof(unsigned long), 74 .key_offset = offsetof(struct prestera_acl_rule, cookie), 75 .head_offset = offsetof(struct prestera_acl_rule, ht_node), 76 .automatic_shrinking = true, 77 }; 78 79 static const struct rhashtable_params __prestera_acl_rule_entry_ht_params = { 80 .key_offset = offsetof(struct prestera_acl_rule_entry, key), 81 .head_offset = offsetof(struct prestera_acl_rule_entry, ht_node), 82 .key_len = sizeof(struct prestera_acl_rule_entry_key), 83 .automatic_shrinking = true, 84 }; 85 86 static struct prestera_acl_ruleset * 87 prestera_acl_ruleset_create(struct prestera_acl *acl, 88 struct prestera_flow_block *block) 89 { 90 struct prestera_acl_ruleset *ruleset; 91 int err; 92 u32 uid; 93 94 ruleset = kzalloc(sizeof(*ruleset), GFP_KERNEL); 95 if (!ruleset) 96 return ERR_PTR(-ENOMEM); 97 98 ruleset->acl = acl; 99 ruleset->ht_key.block = block; 100 refcount_set(&ruleset->refcount, 1); 101 102 err = rhashtable_init(&ruleset->rule_ht, &prestera_acl_rule_ht_params); 103 if (err) 104 goto err_rhashtable_init; 105 106 err = idr_alloc_u32(&acl->uid, NULL, &uid, U8_MAX, GFP_KERNEL); 107 if (err) 108 goto err_ruleset_create; 109 110 /* make pcl-id based on uid */ 111 ruleset->pcl_id = (u8)uid; 112 err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node, 113 prestera_acl_ruleset_ht_params); 114 if (err) 115 goto err_ruleset_ht_insert; 116 117 return ruleset; 118 119 err_ruleset_ht_insert: 120 idr_remove(&acl->uid, uid); 121 err_ruleset_create: 122 rhashtable_destroy(&ruleset->rule_ht); 123 err_rhashtable_init: 124 kfree(ruleset); 125 return ERR_PTR(err); 126 } 127 128 int prestera_acl_ruleset_offload(struct prestera_acl_ruleset *ruleset) 129 { 130 u32 vtcam_id; 131 int err; 132 133 if (ruleset->offload) 134 return -EEXIST; 135 136 err = prestera_acl_vtcam_id_get(ruleset->acl, 0, 137 ruleset->keymask, &vtcam_id); 138 if (err) 139 return err; 140 141 ruleset->vtcam_id = vtcam_id; 142 ruleset->offload = true; 143 return 0; 144 } 145 146 static void prestera_acl_ruleset_destroy(struct prestera_acl_ruleset *ruleset) 147 { 148 struct prestera_acl *acl = ruleset->acl; 149 u8 uid = ruleset->pcl_id & PRESTERA_ACL_KEYMASK_PCL_ID_USER; 150 151 rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node, 152 prestera_acl_ruleset_ht_params); 153 154 if (ruleset->offload) 155 WARN_ON(prestera_acl_vtcam_id_put(acl, ruleset->vtcam_id)); 156 157 idr_remove(&acl->uid, uid); 158 159 rhashtable_destroy(&ruleset->rule_ht); 160 kfree(ruleset->keymask); 161 kfree(ruleset); 162 } 163 164 static struct prestera_acl_ruleset * 165 __prestera_acl_ruleset_lookup(struct prestera_acl *acl, 166 struct prestera_flow_block *block) 167 { 168 struct prestera_acl_ruleset_ht_key ht_key; 169 170 memset(&ht_key, 0, sizeof(ht_key)); 171 ht_key.block = block; 172 return rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key, 173 prestera_acl_ruleset_ht_params); 174 } 175 176 struct prestera_acl_ruleset * 177 prestera_acl_ruleset_lookup(struct prestera_acl *acl, 178 struct prestera_flow_block *block) 179 { 180 struct prestera_acl_ruleset *ruleset; 181 182 ruleset = __prestera_acl_ruleset_lookup(acl, block); 183 if (!ruleset) 184 return ERR_PTR(-ENOENT); 185 186 refcount_inc(&ruleset->refcount); 187 return ruleset; 188 } 189 190 struct prestera_acl_ruleset * 191 prestera_acl_ruleset_get(struct prestera_acl *acl, 192 struct prestera_flow_block *block) 193 { 194 struct prestera_acl_ruleset *ruleset; 195 196 ruleset = __prestera_acl_ruleset_lookup(acl, block); 197 if (ruleset) { 198 refcount_inc(&ruleset->refcount); 199 return ruleset; 200 } 201 202 return prestera_acl_ruleset_create(acl, block); 203 } 204 205 void prestera_acl_ruleset_put(struct prestera_acl_ruleset *ruleset) 206 { 207 if (!refcount_dec_and_test(&ruleset->refcount)) 208 return; 209 210 prestera_acl_ruleset_destroy(ruleset); 211 } 212 213 int prestera_acl_ruleset_bind(struct prestera_acl_ruleset *ruleset, 214 struct prestera_port *port) 215 { 216 struct prestera_acl_iface iface = { 217 .type = PRESTERA_ACL_IFACE_TYPE_PORT, 218 .port = port 219 }; 220 221 return prestera_hw_vtcam_iface_bind(port->sw, &iface, ruleset->vtcam_id, 222 ruleset->pcl_id); 223 } 224 225 int prestera_acl_ruleset_unbind(struct prestera_acl_ruleset *ruleset, 226 struct prestera_port *port) 227 { 228 struct prestera_acl_iface iface = { 229 .type = PRESTERA_ACL_IFACE_TYPE_PORT, 230 .port = port 231 }; 232 233 return prestera_hw_vtcam_iface_unbind(port->sw, &iface, 234 ruleset->vtcam_id); 235 } 236 237 static int prestera_acl_ruleset_block_bind(struct prestera_acl_ruleset *ruleset, 238 struct prestera_flow_block *block) 239 { 240 struct prestera_flow_block_binding *binding; 241 int err; 242 243 block->ruleset_zero = ruleset; 244 list_for_each_entry(binding, &block->binding_list, list) { 245 err = prestera_acl_ruleset_bind(ruleset, binding->port); 246 if (err) 247 goto rollback; 248 } 249 return 0; 250 251 rollback: 252 list_for_each_entry_continue_reverse(binding, &block->binding_list, 253 list) 254 err = prestera_acl_ruleset_unbind(ruleset, binding->port); 255 block->ruleset_zero = NULL; 256 257 return err; 258 } 259 260 static void 261 prestera_acl_ruleset_block_unbind(struct prestera_acl_ruleset *ruleset, 262 struct prestera_flow_block *block) 263 { 264 struct prestera_flow_block_binding *binding; 265 266 list_for_each_entry(binding, &block->binding_list, list) 267 prestera_acl_ruleset_unbind(ruleset, binding->port); 268 block->ruleset_zero = NULL; 269 } 270 271 void 272 prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule, u16 pcl_id) 273 { 274 struct prestera_acl_match *r_match = &rule->re_key.match; 275 __be16 pcl_id_mask = htons(PRESTERA_ACL_KEYMASK_PCL_ID); 276 __be16 pcl_id_key = htons(pcl_id); 277 278 rule_match_set(r_match->key, PCL_ID, pcl_id_key); 279 rule_match_set(r_match->mask, PCL_ID, pcl_id_mask); 280 } 281 282 struct prestera_acl_rule * 283 prestera_acl_rule_lookup(struct prestera_acl_ruleset *ruleset, 284 unsigned long cookie) 285 { 286 return rhashtable_lookup_fast(&ruleset->rule_ht, &cookie, 287 prestera_acl_rule_ht_params); 288 } 289 290 bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset) 291 { 292 return ruleset->offload; 293 } 294 295 struct prestera_acl_rule * 296 prestera_acl_rule_create(struct prestera_acl_ruleset *ruleset, 297 unsigned long cookie) 298 { 299 struct prestera_acl_rule *rule; 300 301 rule = kzalloc(sizeof(*rule), GFP_KERNEL); 302 if (!rule) 303 return ERR_PTR(-ENOMEM); 304 305 rule->ruleset = ruleset; 306 rule->cookie = cookie; 307 308 refcount_inc(&ruleset->refcount); 309 310 return rule; 311 } 312 313 void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule, 314 u32 priority) 315 { 316 rule->priority = priority; 317 } 318 319 void prestera_acl_rule_destroy(struct prestera_acl_rule *rule) 320 { 321 prestera_acl_ruleset_put(rule->ruleset); 322 kfree(rule); 323 } 324 325 int prestera_acl_rule_add(struct prestera_switch *sw, 326 struct prestera_acl_rule *rule) 327 { 328 int err; 329 struct prestera_acl_ruleset *ruleset = rule->ruleset; 330 struct prestera_flow_block *block = ruleset->ht_key.block; 331 332 /* try to add rule to hash table first */ 333 err = rhashtable_insert_fast(&ruleset->rule_ht, &rule->ht_node, 334 prestera_acl_rule_ht_params); 335 if (err) 336 goto err_ht_insert; 337 338 prestera_acl_rule_keymask_pcl_id_set(rule, ruleset->pcl_id); 339 rule->re_arg.vtcam_id = ruleset->vtcam_id; 340 rule->re_key.prio = rule->priority; 341 342 /* setup counter */ 343 rule->re_arg.count.valid = true; 344 rule->re_arg.count.client = PRESTERA_HW_COUNTER_CLIENT_LOOKUP_0; 345 346 rule->re = prestera_acl_rule_entry_find(sw->acl, &rule->re_key); 347 err = WARN_ON(rule->re) ? -EEXIST : 0; 348 if (err) 349 goto err_rule_add; 350 351 rule->re = prestera_acl_rule_entry_create(sw->acl, &rule->re_key, 352 &rule->re_arg); 353 err = !rule->re ? -EINVAL : 0; 354 if (err) 355 goto err_rule_add; 356 357 /* bind the block (all ports) to chain index 0 */ 358 if (!ruleset->rule_count) { 359 err = prestera_acl_ruleset_block_bind(ruleset, block); 360 if (err) 361 goto err_acl_block_bind; 362 } 363 364 list_add_tail(&rule->list, &sw->acl->rules); 365 ruleset->rule_count++; 366 return 0; 367 368 err_acl_block_bind: 369 prestera_acl_rule_entry_destroy(sw->acl, rule->re); 370 err_rule_add: 371 rule->re = NULL; 372 rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node, 373 prestera_acl_rule_ht_params); 374 err_ht_insert: 375 return err; 376 } 377 378 void prestera_acl_rule_del(struct prestera_switch *sw, 379 struct prestera_acl_rule *rule) 380 { 381 struct prestera_acl_ruleset *ruleset = rule->ruleset; 382 struct prestera_flow_block *block = ruleset->ht_key.block; 383 384 rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node, 385 prestera_acl_rule_ht_params); 386 ruleset->rule_count--; 387 list_del(&rule->list); 388 389 prestera_acl_rule_entry_destroy(sw->acl, rule->re); 390 391 /* unbind block (all ports) */ 392 if (!ruleset->rule_count) 393 prestera_acl_ruleset_block_unbind(ruleset, block); 394 } 395 396 int prestera_acl_rule_get_stats(struct prestera_acl *acl, 397 struct prestera_acl_rule *rule, 398 u64 *packets, u64 *bytes, u64 *last_use) 399 { 400 u64 current_packets; 401 u64 current_bytes; 402 int err; 403 404 err = prestera_counter_stats_get(acl->sw->counter, 405 rule->re->counter.block, 406 rule->re->counter.id, 407 ¤t_packets, ¤t_bytes); 408 if (err) 409 return err; 410 411 *packets = current_packets; 412 *bytes = current_bytes; 413 *last_use = jiffies; 414 415 return 0; 416 } 417 418 struct prestera_acl_rule_entry * 419 prestera_acl_rule_entry_find(struct prestera_acl *acl, 420 struct prestera_acl_rule_entry_key *key) 421 { 422 struct prestera_acl_rule_entry *e; 423 424 e = rhashtable_lookup_fast(&acl->acl_rule_entry_ht, key, 425 __prestera_acl_rule_entry_ht_params); 426 return IS_ERR(e) ? NULL : e; 427 } 428 429 static int __prestera_acl_rule_entry2hw_del(struct prestera_switch *sw, 430 struct prestera_acl_rule_entry *e) 431 { 432 return prestera_hw_vtcam_rule_del(sw, e->vtcam_id, e->hw_id); 433 } 434 435 static int __prestera_acl_rule_entry2hw_add(struct prestera_switch *sw, 436 struct prestera_acl_rule_entry *e) 437 { 438 struct prestera_acl_hw_action_info act_hw[PRESTERA_ACL_RULE_ACTION_MAX]; 439 int act_num; 440 441 memset(&act_hw, 0, sizeof(act_hw)); 442 act_num = 0; 443 444 /* accept */ 445 if (e->accept.valid) { 446 act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_ACCEPT; 447 act_num++; 448 } 449 /* drop */ 450 if (e->drop.valid) { 451 act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_DROP; 452 act_num++; 453 } 454 /* trap */ 455 if (e->trap.valid) { 456 act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_TRAP; 457 act_num++; 458 } 459 /* counter */ 460 if (e->counter.block) { 461 act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_COUNT; 462 act_hw[act_num].count.id = e->counter.id; 463 act_num++; 464 } 465 466 return prestera_hw_vtcam_rule_add(sw, e->vtcam_id, e->key.prio, 467 e->key.match.key, e->key.match.mask, 468 act_hw, act_num, &e->hw_id); 469 } 470 471 static void 472 __prestera_acl_rule_entry_act_destruct(struct prestera_switch *sw, 473 struct prestera_acl_rule_entry *e) 474 { 475 /* counter */ 476 prestera_counter_put(sw->counter, e->counter.block, e->counter.id); 477 } 478 479 void prestera_acl_rule_entry_destroy(struct prestera_acl *acl, 480 struct prestera_acl_rule_entry *e) 481 { 482 int ret; 483 484 rhashtable_remove_fast(&acl->acl_rule_entry_ht, &e->ht_node, 485 __prestera_acl_rule_entry_ht_params); 486 487 ret = __prestera_acl_rule_entry2hw_del(acl->sw, e); 488 WARN_ON(ret && ret != -ENODEV); 489 490 __prestera_acl_rule_entry_act_destruct(acl->sw, e); 491 kfree(e); 492 } 493 494 static int 495 __prestera_acl_rule_entry_act_construct(struct prestera_switch *sw, 496 struct prestera_acl_rule_entry *e, 497 struct prestera_acl_rule_entry_arg *arg) 498 { 499 /* accept */ 500 e->accept.valid = arg->accept.valid; 501 /* drop */ 502 e->drop.valid = arg->drop.valid; 503 /* trap */ 504 e->trap.valid = arg->trap.valid; 505 /* counter */ 506 if (arg->count.valid) { 507 int err; 508 509 err = prestera_counter_get(sw->counter, arg->count.client, 510 &e->counter.block, 511 &e->counter.id); 512 if (err) 513 goto err_out; 514 } 515 516 return 0; 517 518 err_out: 519 __prestera_acl_rule_entry_act_destruct(sw, e); 520 return -EINVAL; 521 } 522 523 struct prestera_acl_rule_entry * 524 prestera_acl_rule_entry_create(struct prestera_acl *acl, 525 struct prestera_acl_rule_entry_key *key, 526 struct prestera_acl_rule_entry_arg *arg) 527 { 528 struct prestera_acl_rule_entry *e; 529 int err; 530 531 e = kzalloc(sizeof(*e), GFP_KERNEL); 532 if (!e) 533 goto err_kzalloc; 534 535 memcpy(&e->key, key, sizeof(*key)); 536 e->vtcam_id = arg->vtcam_id; 537 err = __prestera_acl_rule_entry_act_construct(acl->sw, e, arg); 538 if (err) 539 goto err_act_construct; 540 541 err = __prestera_acl_rule_entry2hw_add(acl->sw, e); 542 if (err) 543 goto err_hw_add; 544 545 err = rhashtable_insert_fast(&acl->acl_rule_entry_ht, &e->ht_node, 546 __prestera_acl_rule_entry_ht_params); 547 if (err) 548 goto err_ht_insert; 549 550 return e; 551 552 err_ht_insert: 553 WARN_ON(__prestera_acl_rule_entry2hw_del(acl->sw, e)); 554 err_hw_add: 555 __prestera_acl_rule_entry_act_destruct(acl->sw, e); 556 err_act_construct: 557 kfree(e); 558 err_kzalloc: 559 return NULL; 560 } 561 562 int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup, 563 void *keymask, u32 *vtcam_id) 564 { 565 struct prestera_acl_vtcam *vtcam; 566 u32 new_vtcam_id; 567 int err; 568 569 /* find the vtcam that suits keymask. We do not expect to have 570 * a big number of vtcams, so, the list type for vtcam list is 571 * fine for now 572 */ 573 list_for_each_entry(vtcam, &acl->vtcam_list, list) { 574 if (lookup != vtcam->lookup) 575 continue; 576 577 if (!keymask && !vtcam->is_keymask_set) { 578 refcount_inc(&vtcam->refcount); 579 goto vtcam_found; 580 } 581 582 if (keymask && vtcam->is_keymask_set && 583 !memcmp(keymask, vtcam->keymask, sizeof(vtcam->keymask))) { 584 refcount_inc(&vtcam->refcount); 585 goto vtcam_found; 586 } 587 } 588 589 /* vtcam not found, try to create new one */ 590 vtcam = kzalloc(sizeof(*vtcam), GFP_KERNEL); 591 if (!vtcam) 592 return -ENOMEM; 593 594 err = prestera_hw_vtcam_create(acl->sw, lookup, keymask, &new_vtcam_id, 595 PRESTERA_HW_VTCAM_DIR_INGRESS); 596 if (err) { 597 kfree(vtcam); 598 return err; 599 } 600 601 vtcam->id = new_vtcam_id; 602 vtcam->lookup = lookup; 603 if (keymask) { 604 memcpy(vtcam->keymask, keymask, sizeof(vtcam->keymask)); 605 vtcam->is_keymask_set = true; 606 } 607 refcount_set(&vtcam->refcount, 1); 608 list_add_rcu(&vtcam->list, &acl->vtcam_list); 609 610 vtcam_found: 611 *vtcam_id = vtcam->id; 612 return 0; 613 } 614 615 int prestera_acl_vtcam_id_put(struct prestera_acl *acl, u32 vtcam_id) 616 { 617 struct prestera_acl_vtcam *vtcam; 618 int err; 619 620 list_for_each_entry(vtcam, &acl->vtcam_list, list) { 621 if (vtcam_id != vtcam->id) 622 continue; 623 624 if (!refcount_dec_and_test(&vtcam->refcount)) 625 return 0; 626 627 err = prestera_hw_vtcam_destroy(acl->sw, vtcam->id); 628 if (err && err != -ENODEV) { 629 refcount_set(&vtcam->refcount, 1); 630 return err; 631 } 632 633 list_del(&vtcam->list); 634 kfree(vtcam); 635 return 0; 636 } 637 638 return -ENOENT; 639 } 640 641 int prestera_acl_init(struct prestera_switch *sw) 642 { 643 struct prestera_acl *acl; 644 int err; 645 646 acl = kzalloc(sizeof(*acl), GFP_KERNEL); 647 if (!acl) 648 return -ENOMEM; 649 650 acl->sw = sw; 651 INIT_LIST_HEAD(&acl->rules); 652 INIT_LIST_HEAD(&acl->vtcam_list); 653 idr_init(&acl->uid); 654 655 err = rhashtable_init(&acl->acl_rule_entry_ht, 656 &__prestera_acl_rule_entry_ht_params); 657 if (err) 658 goto err_acl_rule_entry_ht_init; 659 660 err = rhashtable_init(&acl->ruleset_ht, 661 &prestera_acl_ruleset_ht_params); 662 if (err) 663 goto err_ruleset_ht_init; 664 665 sw->acl = acl; 666 667 return 0; 668 669 err_ruleset_ht_init: 670 rhashtable_destroy(&acl->acl_rule_entry_ht); 671 err_acl_rule_entry_ht_init: 672 kfree(acl); 673 return err; 674 } 675 676 void prestera_acl_fini(struct prestera_switch *sw) 677 { 678 struct prestera_acl *acl = sw->acl; 679 680 WARN_ON(!idr_is_empty(&acl->uid)); 681 idr_destroy(&acl->uid); 682 683 WARN_ON(!list_empty(&acl->vtcam_list)); 684 WARN_ON(!list_empty(&acl->rules)); 685 686 rhashtable_destroy(&acl->ruleset_ht); 687 rhashtable_destroy(&acl->acl_rule_entry_ht); 688 689 kfree(acl); 690 } 691