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