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