1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Microchip VCAP API 3 * 4 * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. 5 */ 6 7 #include <net/tc_act/tc_gate.h> 8 #include <net/tcp.h> 9 10 #include "sparx5_tc.h" 11 #include "vcap_api.h" 12 #include "vcap_api_client.h" 13 #include "vcap_tc.h" 14 #include "sparx5_main.h" 15 #include "sparx5_vcap_impl.h" 16 17 #define SPX5_MAX_RULE_SIZE 13 /* allows X1, X2, X4, X6 and X12 rules */ 18 19 /* Collect keysets and type ids for multiple rules per size */ 20 struct sparx5_wildcard_rule { 21 bool selected; 22 u8 value; 23 u8 mask; 24 enum vcap_keyfield_set keyset; 25 }; 26 27 struct sparx5_multiple_rules { 28 struct sparx5_wildcard_rule rule[SPX5_MAX_RULE_SIZE]; 29 }; 30 31 static int 32 sparx5_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st) 33 { 34 struct flow_match_basic mt; 35 int err = 0; 36 37 flow_rule_match_basic(st->frule, &mt); 38 39 if (mt.mask->n_proto) { 40 st->l3_proto = be16_to_cpu(mt.key->n_proto); 41 if (!sparx5_vcap_is_known_etype(st->admin, st->l3_proto)) { 42 err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE, 43 st->l3_proto, ~0); 44 if (err) 45 goto out; 46 } else if (st->l3_proto == ETH_P_IP) { 47 err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS, 48 VCAP_BIT_1); 49 if (err) 50 goto out; 51 } else if (st->l3_proto == ETH_P_IPV6) { 52 err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS, 53 VCAP_BIT_0); 54 if (err) 55 goto out; 56 if (st->admin->vtype == VCAP_TYPE_IS0) { 57 err = vcap_rule_add_key_bit(st->vrule, 58 VCAP_KF_IP_SNAP_IS, 59 VCAP_BIT_1); 60 if (err) 61 goto out; 62 } 63 } 64 } 65 66 if (mt.mask->ip_proto) { 67 st->l4_proto = mt.key->ip_proto; 68 if (st->l4_proto == IPPROTO_TCP) { 69 err = vcap_rule_add_key_bit(st->vrule, 70 VCAP_KF_TCP_IS, 71 VCAP_BIT_1); 72 if (err) 73 goto out; 74 } else if (st->l4_proto == IPPROTO_UDP) { 75 err = vcap_rule_add_key_bit(st->vrule, 76 VCAP_KF_TCP_IS, 77 VCAP_BIT_0); 78 if (err) 79 goto out; 80 if (st->admin->vtype == VCAP_TYPE_IS0) { 81 err = vcap_rule_add_key_bit(st->vrule, 82 VCAP_KF_TCP_UDP_IS, 83 VCAP_BIT_1); 84 if (err) 85 goto out; 86 } 87 } else { 88 err = vcap_rule_add_key_u32(st->vrule, 89 VCAP_KF_L3_IP_PROTO, 90 st->l4_proto, ~0); 91 if (err) 92 goto out; 93 } 94 } 95 96 st->used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC); 97 98 return err; 99 100 out: 101 NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error"); 102 return err; 103 } 104 105 static int 106 sparx5_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage *st) 107 { 108 struct flow_match_control mt; 109 u32 value, mask; 110 int err = 0; 111 112 flow_rule_match_control(st->frule, &mt); 113 114 if (mt.mask->flags) { 115 if (mt.mask->flags & FLOW_DIS_FIRST_FRAG) { 116 if (mt.key->flags & FLOW_DIS_FIRST_FRAG) { 117 value = 1; /* initial fragment */ 118 mask = 0x3; 119 } else { 120 if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) { 121 value = 3; /* follow up fragment */ 122 mask = 0x3; 123 } else { 124 value = 0; /* no fragment */ 125 mask = 0x3; 126 } 127 } 128 } else { 129 if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) { 130 value = 3; /* follow up fragment */ 131 mask = 0x3; 132 } else { 133 value = 0; /* no fragment */ 134 mask = 0x3; 135 } 136 } 137 138 err = vcap_rule_add_key_u32(st->vrule, 139 VCAP_KF_L3_FRAGMENT_TYPE, 140 value, mask); 141 if (err) 142 goto out; 143 } 144 145 st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL); 146 147 return err; 148 149 out: 150 NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error"); 151 return err; 152 } 153 154 static int 155 sparx5_tc_flower_handler_cvlan_usage(struct vcap_tc_flower_parse_usage *st) 156 { 157 if (st->admin->vtype != VCAP_TYPE_IS0) { 158 NL_SET_ERR_MSG_MOD(st->fco->common.extack, 159 "cvlan not supported in this VCAP"); 160 return -EINVAL; 161 } 162 163 return vcap_tc_flower_handler_cvlan_usage(st); 164 } 165 166 static int 167 sparx5_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st) 168 { 169 enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS; 170 enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS; 171 172 if (st->admin->vtype == VCAP_TYPE_IS0) { 173 vid_key = VCAP_KF_8021Q_VID0; 174 pcp_key = VCAP_KF_8021Q_PCP0; 175 } 176 177 return vcap_tc_flower_handler_vlan_usage(st, vid_key, pcp_key); 178 } 179 180 static int (*sparx5_tc_flower_usage_handlers[])(struct vcap_tc_flower_parse_usage *st) = { 181 [FLOW_DISSECTOR_KEY_ETH_ADDRS] = vcap_tc_flower_handler_ethaddr_usage, 182 [FLOW_DISSECTOR_KEY_IPV4_ADDRS] = vcap_tc_flower_handler_ipv4_usage, 183 [FLOW_DISSECTOR_KEY_IPV6_ADDRS] = vcap_tc_flower_handler_ipv6_usage, 184 [FLOW_DISSECTOR_KEY_CONTROL] = sparx5_tc_flower_handler_control_usage, 185 [FLOW_DISSECTOR_KEY_PORTS] = vcap_tc_flower_handler_portnum_usage, 186 [FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage, 187 [FLOW_DISSECTOR_KEY_CVLAN] = sparx5_tc_flower_handler_cvlan_usage, 188 [FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage, 189 [FLOW_DISSECTOR_KEY_TCP] = vcap_tc_flower_handler_tcp_usage, 190 [FLOW_DISSECTOR_KEY_ARP] = vcap_tc_flower_handler_arp_usage, 191 [FLOW_DISSECTOR_KEY_IP] = vcap_tc_flower_handler_ip_usage, 192 }; 193 194 static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco, 195 struct vcap_admin *admin, 196 struct vcap_rule *vrule, 197 u16 *l3_proto) 198 { 199 struct vcap_tc_flower_parse_usage state = { 200 .fco = fco, 201 .vrule = vrule, 202 .l3_proto = ETH_P_ALL, 203 .admin = admin, 204 }; 205 int idx, err = 0; 206 207 state.frule = flow_cls_offload_flow_rule(fco); 208 for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) { 209 if (!flow_rule_match_key(state.frule, idx)) 210 continue; 211 if (!sparx5_tc_flower_usage_handlers[idx]) 212 continue; 213 err = sparx5_tc_flower_usage_handlers[idx](&state); 214 if (err) 215 return err; 216 } 217 218 if (state.frule->match.dissector->used_keys ^ state.used_keys) { 219 NL_SET_ERR_MSG_MOD(fco->common.extack, 220 "Unsupported match item"); 221 return -ENOENT; 222 } 223 224 if (l3_proto) 225 *l3_proto = state.l3_proto; 226 return err; 227 } 228 229 static int sparx5_tc_flower_action_check(struct vcap_control *vctrl, 230 struct net_device *ndev, 231 struct flow_cls_offload *fco, 232 bool ingress) 233 { 234 struct flow_rule *rule = flow_cls_offload_flow_rule(fco); 235 struct flow_action_entry *actent, *last_actent = NULL; 236 struct flow_action *act = &rule->action; 237 u64 action_mask = 0; 238 int idx; 239 240 if (!flow_action_has_entries(act)) { 241 NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions"); 242 return -EINVAL; 243 } 244 245 if (!flow_action_basic_hw_stats_check(act, fco->common.extack)) 246 return -EOPNOTSUPP; 247 248 flow_action_for_each(idx, actent, act) { 249 if (action_mask & BIT(actent->id)) { 250 NL_SET_ERR_MSG_MOD(fco->common.extack, 251 "More actions of the same type"); 252 return -EINVAL; 253 } 254 action_mask |= BIT(actent->id); 255 last_actent = actent; /* Save last action for later check */ 256 } 257 258 /* Check if last action is a goto 259 * The last chain/lookup does not need to have a goto action 260 */ 261 if (last_actent->id == FLOW_ACTION_GOTO) { 262 /* Check if the destination chain is in one of the VCAPs */ 263 if (!vcap_is_next_lookup(vctrl, fco->common.chain_index, 264 last_actent->chain_index)) { 265 NL_SET_ERR_MSG_MOD(fco->common.extack, 266 "Invalid goto chain"); 267 return -EINVAL; 268 } 269 } else if (!vcap_is_last_chain(vctrl, fco->common.chain_index, 270 ingress)) { 271 NL_SET_ERR_MSG_MOD(fco->common.extack, 272 "Last action must be 'goto'"); 273 return -EINVAL; 274 } 275 276 /* Catch unsupported combinations of actions */ 277 if (action_mask & BIT(FLOW_ACTION_TRAP) && 278 action_mask & BIT(FLOW_ACTION_ACCEPT)) { 279 NL_SET_ERR_MSG_MOD(fco->common.extack, 280 "Cannot combine pass and trap action"); 281 return -EOPNOTSUPP; 282 } 283 284 return 0; 285 } 286 287 /* Add a rule counter action */ 288 static int sparx5_tc_add_rule_counter(struct vcap_admin *admin, 289 struct vcap_rule *vrule) 290 { 291 int err; 292 293 if (admin->vtype == VCAP_TYPE_IS2 || admin->vtype == VCAP_TYPE_ES2) { 294 err = vcap_rule_mod_action_u32(vrule, VCAP_AF_CNT_ID, 295 vrule->id); 296 if (err) 297 return err; 298 vcap_rule_set_counter_id(vrule, vrule->id); 299 } 300 301 return 0; 302 } 303 304 /* Collect all port keysets and apply the first of them, possibly wildcarded */ 305 static int sparx5_tc_select_protocol_keyset(struct net_device *ndev, 306 struct vcap_rule *vrule, 307 struct vcap_admin *admin, 308 u16 l3_proto, 309 struct sparx5_multiple_rules *multi) 310 { 311 struct sparx5_port *port = netdev_priv(ndev); 312 struct vcap_keyset_list portkeysetlist = {}; 313 enum vcap_keyfield_set portkeysets[10] = {}; 314 struct vcap_keyset_list matches = {}; 315 enum vcap_keyfield_set keysets[10]; 316 int idx, jdx, err = 0, count = 0; 317 struct sparx5_wildcard_rule *mru; 318 const struct vcap_set *kinfo; 319 struct vcap_control *vctrl; 320 321 vctrl = port->sparx5->vcap_ctrl; 322 323 /* Find the keysets that the rule can use */ 324 matches.keysets = keysets; 325 matches.max = ARRAY_SIZE(keysets); 326 if (vcap_rule_find_keysets(vrule, &matches) == 0) 327 return -EINVAL; 328 329 /* Find the keysets that the port configuration supports */ 330 portkeysetlist.max = ARRAY_SIZE(portkeysets); 331 portkeysetlist.keysets = portkeysets; 332 err = sparx5_vcap_get_port_keyset(ndev, 333 admin, vrule->vcap_chain_id, 334 l3_proto, 335 &portkeysetlist); 336 if (err) 337 return err; 338 339 /* Find the intersection of the two sets of keyset */ 340 for (idx = 0; idx < portkeysetlist.cnt; ++idx) { 341 kinfo = vcap_keyfieldset(vctrl, admin->vtype, 342 portkeysetlist.keysets[idx]); 343 if (!kinfo) 344 continue; 345 346 /* Find a port keyset that matches the required keys 347 * If there are multiple keysets then compose a type id mask 348 */ 349 for (jdx = 0; jdx < matches.cnt; ++jdx) { 350 if (portkeysetlist.keysets[idx] != matches.keysets[jdx]) 351 continue; 352 353 mru = &multi->rule[kinfo->sw_per_item]; 354 if (!mru->selected) { 355 mru->selected = true; 356 mru->keyset = portkeysetlist.keysets[idx]; 357 mru->value = kinfo->type_id; 358 } 359 mru->value &= kinfo->type_id; 360 mru->mask |= kinfo->type_id; 361 ++count; 362 } 363 } 364 if (count == 0) 365 return -EPROTO; 366 367 if (l3_proto == ETH_P_ALL && count < portkeysetlist.cnt) 368 return -ENOENT; 369 370 for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) { 371 mru = &multi->rule[idx]; 372 if (!mru->selected) 373 continue; 374 375 /* Align the mask to the combined value */ 376 mru->mask ^= mru->value; 377 } 378 379 /* Set the chosen keyset on the rule and set a wildcarded type if there 380 * are more than one keyset 381 */ 382 for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) { 383 mru = &multi->rule[idx]; 384 if (!mru->selected) 385 continue; 386 387 vcap_set_rule_set_keyset(vrule, mru->keyset); 388 if (count > 1) 389 /* Some keysets do not have a type field */ 390 vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE, 391 mru->value, 392 ~mru->mask); 393 mru->selected = false; /* mark as done */ 394 break; /* Stop here and add more rules later */ 395 } 396 return err; 397 } 398 399 static int sparx5_tc_add_rule_copy(struct vcap_control *vctrl, 400 struct flow_cls_offload *fco, 401 struct vcap_rule *erule, 402 struct vcap_admin *admin, 403 struct sparx5_wildcard_rule *rule) 404 { 405 enum vcap_key_field keylist[] = { 406 VCAP_KF_IF_IGR_PORT_MASK, 407 VCAP_KF_IF_IGR_PORT_MASK_SEL, 408 VCAP_KF_IF_IGR_PORT_MASK_RNG, 409 VCAP_KF_LOOKUP_FIRST_IS, 410 VCAP_KF_TYPE, 411 }; 412 struct vcap_rule *vrule; 413 int err; 414 415 /* Add an extra rule with a special user and the new keyset */ 416 erule->user = VCAP_USER_TC_EXTRA; 417 vrule = vcap_copy_rule(erule); 418 if (IS_ERR(vrule)) 419 return PTR_ERR(vrule); 420 421 /* Link the new rule to the existing rule with the cookie */ 422 vrule->cookie = erule->cookie; 423 vcap_filter_rule_keys(vrule, keylist, ARRAY_SIZE(keylist), true); 424 err = vcap_set_rule_set_keyset(vrule, rule->keyset); 425 if (err) { 426 pr_err("%s:%d: could not set keyset %s in rule: %u\n", 427 __func__, __LINE__, 428 vcap_keyset_name(vctrl, rule->keyset), 429 vrule->id); 430 goto out; 431 } 432 433 /* Some keysets do not have a type field, so ignore return value */ 434 vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE, rule->value, ~rule->mask); 435 436 err = vcap_set_rule_set_actionset(vrule, erule->actionset); 437 if (err) 438 goto out; 439 440 err = sparx5_tc_add_rule_counter(admin, vrule); 441 if (err) 442 goto out; 443 444 err = vcap_val_rule(vrule, ETH_P_ALL); 445 if (err) { 446 pr_err("%s:%d: could not validate rule: %u\n", 447 __func__, __LINE__, vrule->id); 448 vcap_set_tc_exterr(fco, vrule); 449 goto out; 450 } 451 err = vcap_add_rule(vrule); 452 if (err) { 453 pr_err("%s:%d: could not add rule: %u\n", 454 __func__, __LINE__, vrule->id); 455 goto out; 456 } 457 out: 458 vcap_free_rule(vrule); 459 return err; 460 } 461 462 static int sparx5_tc_add_remaining_rules(struct vcap_control *vctrl, 463 struct flow_cls_offload *fco, 464 struct vcap_rule *erule, 465 struct vcap_admin *admin, 466 struct sparx5_multiple_rules *multi) 467 { 468 int idx, err = 0; 469 470 for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) { 471 if (!multi->rule[idx].selected) 472 continue; 473 474 err = sparx5_tc_add_rule_copy(vctrl, fco, erule, admin, 475 &multi->rule[idx]); 476 if (err) 477 break; 478 } 479 return err; 480 } 481 482 /* Add the actionset that is the default for the VCAP type */ 483 static int sparx5_tc_set_actionset(struct vcap_admin *admin, 484 struct vcap_rule *vrule) 485 { 486 enum vcap_actionfield_set aset; 487 int err = 0; 488 489 switch (admin->vtype) { 490 case VCAP_TYPE_IS0: 491 aset = VCAP_AFS_CLASSIFICATION; 492 break; 493 case VCAP_TYPE_IS2: 494 aset = VCAP_AFS_BASE_TYPE; 495 break; 496 case VCAP_TYPE_ES2: 497 aset = VCAP_AFS_BASE_TYPE; 498 break; 499 default: 500 return -EINVAL; 501 } 502 /* Do not overwrite any current actionset */ 503 if (vrule->actionset == VCAP_AFS_NO_VALUE) 504 err = vcap_set_rule_set_actionset(vrule, aset); 505 return err; 506 } 507 508 /* Add the VCAP key to match on for a rule target value */ 509 static int sparx5_tc_add_rule_link_target(struct vcap_admin *admin, 510 struct vcap_rule *vrule, 511 int target_cid) 512 { 513 int link_val = target_cid % VCAP_CID_LOOKUP_SIZE; 514 int err; 515 516 if (!link_val) 517 return 0; 518 519 switch (admin->vtype) { 520 case VCAP_TYPE_IS0: 521 /* Add NXT_IDX key for chaining rules between IS0 instances */ 522 err = vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX_SEL, 523 1, /* enable */ 524 ~0); 525 if (err) 526 return err; 527 return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX, 528 link_val, /* target */ 529 ~0); 530 case VCAP_TYPE_IS2: 531 /* Add PAG key for chaining rules from IS0 */ 532 return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_PAG, 533 link_val, /* target */ 534 ~0); 535 case VCAP_TYPE_ES2: 536 /* Add ISDX key for chaining rules from IS0 */ 537 return vcap_rule_add_key_u32(vrule, VCAP_KF_ISDX_CLS, link_val, 538 ~0); 539 default: 540 break; 541 } 542 return 0; 543 } 544 545 /* Add the VCAP action that adds a target value to a rule */ 546 static int sparx5_tc_add_rule_link(struct vcap_control *vctrl, 547 struct vcap_admin *admin, 548 struct vcap_rule *vrule, 549 int from_cid, int to_cid) 550 { 551 struct vcap_admin *to_admin = vcap_find_admin(vctrl, to_cid); 552 int diff, err = 0; 553 554 diff = vcap_chain_offset(vctrl, from_cid, to_cid); 555 if (!(to_admin && diff > 0)) { 556 pr_err("%s:%d: unsupported chain direction: %d\n", 557 __func__, __LINE__, to_cid); 558 return -EINVAL; 559 } 560 if (admin->vtype == VCAP_TYPE_IS0 && 561 to_admin->vtype == VCAP_TYPE_IS0) { 562 /* Between IS0 instances the G_IDX value is used */ 563 err = vcap_rule_add_action_u32(vrule, VCAP_AF_NXT_IDX, diff); 564 if (err) 565 goto out; 566 err = vcap_rule_add_action_u32(vrule, VCAP_AF_NXT_IDX_CTRL, 567 1); /* Replace */ 568 if (err) 569 goto out; 570 } else if (admin->vtype == VCAP_TYPE_IS0 && 571 to_admin->vtype == VCAP_TYPE_IS2) { 572 /* Between IS0 and IS2 the PAG value is used */ 573 err = vcap_rule_add_action_u32(vrule, VCAP_AF_PAG_VAL, diff); 574 if (err) 575 goto out; 576 err = vcap_rule_add_action_u32(vrule, 577 VCAP_AF_PAG_OVERRIDE_MASK, 578 0xff); 579 if (err) 580 goto out; 581 } else if (admin->vtype == VCAP_TYPE_IS0 && 582 to_admin->vtype == VCAP_TYPE_ES2) { 583 /* Between IS0 and ES2 the ISDX value is used */ 584 err = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_VAL, 585 diff); 586 if (err) 587 goto out; 588 err = vcap_rule_add_action_bit(vrule, 589 VCAP_AF_ISDX_ADD_REPLACE_SEL, 590 VCAP_BIT_1); 591 if (err) 592 goto out; 593 } else { 594 pr_err("%s:%d: unsupported chain destination: %d\n", 595 __func__, __LINE__, to_cid); 596 err = -EOPNOTSUPP; 597 } 598 out: 599 return err; 600 } 601 602 static int sparx5_tc_flower_parse_act_gate(struct sparx5_psfp_sg *sg, 603 struct flow_action_entry *act, 604 struct netlink_ext_ack *extack) 605 { 606 int i; 607 608 if (act->gate.prio < -1 || act->gate.prio > SPX5_PSFP_SG_MAX_IPV) { 609 NL_SET_ERR_MSG_MOD(extack, "Invalid gate priority"); 610 return -EINVAL; 611 } 612 613 if (act->gate.cycletime < SPX5_PSFP_SG_MIN_CYCLE_TIME_NS || 614 act->gate.cycletime > SPX5_PSFP_SG_MAX_CYCLE_TIME_NS) { 615 NL_SET_ERR_MSG_MOD(extack, "Invalid gate cycletime"); 616 return -EINVAL; 617 } 618 619 if (act->gate.cycletimeext > SPX5_PSFP_SG_MAX_CYCLE_TIME_NS) { 620 NL_SET_ERR_MSG_MOD(extack, "Invalid gate cycletimeext"); 621 return -EINVAL; 622 } 623 624 if (act->gate.num_entries >= SPX5_PSFP_GCE_CNT) { 625 NL_SET_ERR_MSG_MOD(extack, "Invalid number of gate entries"); 626 return -EINVAL; 627 } 628 629 sg->gate_state = true; 630 sg->ipv = act->gate.prio; 631 sg->num_entries = act->gate.num_entries; 632 sg->cycletime = act->gate.cycletime; 633 sg->cycletimeext = act->gate.cycletimeext; 634 635 for (i = 0; i < sg->num_entries; i++) { 636 sg->gce[i].gate_state = !!act->gate.entries[i].gate_state; 637 sg->gce[i].interval = act->gate.entries[i].interval; 638 sg->gce[i].ipv = act->gate.entries[i].ipv; 639 sg->gce[i].maxoctets = act->gate.entries[i].maxoctets; 640 } 641 642 return 0; 643 } 644 645 static int sparx5_tc_flower_parse_act_police(struct sparx5_policer *pol, 646 struct flow_action_entry *act, 647 struct netlink_ext_ack *extack) 648 { 649 pol->type = SPX5_POL_SERVICE; 650 pol->rate = div_u64(act->police.rate_bytes_ps, 1000) * 8; 651 pol->burst = act->police.burst; 652 pol->idx = act->hw_index; 653 654 /* rate is now in kbit */ 655 if (pol->rate > DIV_ROUND_UP(SPX5_SDLB_GROUP_RATE_MAX, 1000)) { 656 NL_SET_ERR_MSG_MOD(extack, "Maximum rate exceeded"); 657 return -EINVAL; 658 } 659 660 if (act->police.exceed.act_id != FLOW_ACTION_DROP) { 661 NL_SET_ERR_MSG_MOD(extack, "Offload not supported when exceed action is not drop"); 662 return -EOPNOTSUPP; 663 } 664 665 if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && 666 act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { 667 NL_SET_ERR_MSG_MOD(extack, "Offload not supported when conform action is not pipe or ok"); 668 return -EOPNOTSUPP; 669 } 670 671 return 0; 672 } 673 674 static int sparx5_tc_flower_psfp_setup(struct sparx5 *sparx5, 675 struct vcap_rule *vrule, int sg_idx, 676 int pol_idx, struct sparx5_psfp_sg *sg, 677 struct sparx5_psfp_fm *fm, 678 struct sparx5_psfp_sf *sf) 679 { 680 u32 psfp_sfid = 0, psfp_fmid = 0, psfp_sgid = 0; 681 int ret; 682 683 /* Must always have a stream gate - max sdu (filter option) is evaluated 684 * after frames have passed the gate, so in case of only a policer, we 685 * allocate a stream gate that is always open. 686 */ 687 if (sg_idx < 0) { 688 sg_idx = sparx5_pool_idx_to_id(SPX5_PSFP_SG_OPEN); 689 sg->ipv = 0; /* Disabled */ 690 sg->cycletime = SPX5_PSFP_SG_CYCLE_TIME_DEFAULT; 691 sg->num_entries = 1; 692 sg->gate_state = 1; /* Open */ 693 sg->gate_enabled = 1; 694 sg->gce[0].gate_state = 1; 695 sg->gce[0].interval = SPX5_PSFP_SG_CYCLE_TIME_DEFAULT; 696 sg->gce[0].ipv = 0; 697 sg->gce[0].maxoctets = 0; /* Disabled */ 698 } 699 700 ret = sparx5_psfp_sg_add(sparx5, sg_idx, sg, &psfp_sgid); 701 if (ret < 0) 702 return ret; 703 704 if (pol_idx >= 0) { 705 /* Add new flow-meter */ 706 ret = sparx5_psfp_fm_add(sparx5, pol_idx, fm, &psfp_fmid); 707 if (ret < 0) 708 return ret; 709 } 710 711 /* Map stream filter to stream gate */ 712 sf->sgid = psfp_sgid; 713 714 /* Add new stream-filter and map it to a steam gate */ 715 ret = sparx5_psfp_sf_add(sparx5, sf, &psfp_sfid); 716 if (ret < 0) 717 return ret; 718 719 /* Streams are classified by ISDX - map ISDX 1:1 to sfid for now. */ 720 sparx5_isdx_conf_set(sparx5, psfp_sfid, psfp_sfid, psfp_fmid); 721 722 ret = vcap_rule_add_action_bit(vrule, VCAP_AF_ISDX_ADD_REPLACE_SEL, 723 VCAP_BIT_1); 724 if (ret) 725 return ret; 726 727 ret = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_VAL, psfp_sfid); 728 if (ret) 729 return ret; 730 731 return 0; 732 } 733 734 static int sparx5_tc_flower_replace(struct net_device *ndev, 735 struct flow_cls_offload *fco, 736 struct vcap_admin *admin, 737 bool ingress) 738 { 739 struct sparx5_psfp_sf sf = { .max_sdu = SPX5_PSFP_SF_MAX_SDU }; 740 struct netlink_ext_ack *extack = fco->common.extack; 741 int err, idx, tc_sg_idx = -1, tc_pol_idx = -1; 742 struct sparx5_port *port = netdev_priv(ndev); 743 struct sparx5_multiple_rules multi = {}; 744 struct sparx5 *sparx5 = port->sparx5; 745 struct sparx5_psfp_sg sg = { 0 }; 746 struct sparx5_psfp_fm fm = { 0 }; 747 struct flow_action_entry *act; 748 struct vcap_control *vctrl; 749 struct flow_rule *frule; 750 struct vcap_rule *vrule; 751 u16 l3_proto; 752 753 vctrl = port->sparx5->vcap_ctrl; 754 755 err = sparx5_tc_flower_action_check(vctrl, ndev, fco, ingress); 756 if (err) 757 return err; 758 759 vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC, 760 fco->common.prio, 0); 761 if (IS_ERR(vrule)) 762 return PTR_ERR(vrule); 763 764 vrule->cookie = fco->cookie; 765 766 l3_proto = ETH_P_ALL; 767 err = sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto); 768 if (err) 769 goto out; 770 771 err = sparx5_tc_add_rule_counter(admin, vrule); 772 if (err) 773 goto out; 774 775 err = sparx5_tc_add_rule_link_target(admin, vrule, 776 fco->common.chain_index); 777 if (err) 778 goto out; 779 780 frule = flow_cls_offload_flow_rule(fco); 781 flow_action_for_each(idx, act, &frule->action) { 782 switch (act->id) { 783 case FLOW_ACTION_GATE: { 784 err = sparx5_tc_flower_parse_act_gate(&sg, act, extack); 785 if (err < 0) 786 goto out; 787 788 tc_sg_idx = act->hw_index; 789 790 break; 791 } 792 case FLOW_ACTION_POLICE: { 793 err = sparx5_tc_flower_parse_act_police(&fm.pol, act, 794 extack); 795 if (err < 0) 796 goto out; 797 798 tc_pol_idx = fm.pol.idx; 799 sf.max_sdu = act->police.mtu; 800 801 break; 802 } 803 case FLOW_ACTION_TRAP: 804 if (admin->vtype != VCAP_TYPE_IS2 && 805 admin->vtype != VCAP_TYPE_ES2) { 806 NL_SET_ERR_MSG_MOD(fco->common.extack, 807 "Trap action not supported in this VCAP"); 808 err = -EOPNOTSUPP; 809 goto out; 810 } 811 err = vcap_rule_add_action_bit(vrule, 812 VCAP_AF_CPU_COPY_ENA, 813 VCAP_BIT_1); 814 if (err) 815 goto out; 816 err = vcap_rule_add_action_u32(vrule, 817 VCAP_AF_CPU_QUEUE_NUM, 0); 818 if (err) 819 goto out; 820 if (admin->vtype != VCAP_TYPE_IS2) 821 break; 822 err = vcap_rule_add_action_u32(vrule, 823 VCAP_AF_MASK_MODE, 824 SPX5_PMM_REPLACE_ALL); 825 if (err) 826 goto out; 827 break; 828 case FLOW_ACTION_ACCEPT: 829 err = sparx5_tc_set_actionset(admin, vrule); 830 if (err) 831 goto out; 832 break; 833 case FLOW_ACTION_GOTO: 834 err = sparx5_tc_set_actionset(admin, vrule); 835 if (err) 836 goto out; 837 sparx5_tc_add_rule_link(vctrl, admin, vrule, 838 fco->common.chain_index, 839 act->chain_index); 840 break; 841 default: 842 NL_SET_ERR_MSG_MOD(fco->common.extack, 843 "Unsupported TC action"); 844 err = -EOPNOTSUPP; 845 goto out; 846 } 847 } 848 849 /* Setup PSFP */ 850 if (tc_sg_idx >= 0 || tc_pol_idx >= 0) { 851 err = sparx5_tc_flower_psfp_setup(sparx5, vrule, tc_sg_idx, 852 tc_pol_idx, &sg, &fm, &sf); 853 if (err) 854 goto out; 855 } 856 857 err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin, l3_proto, 858 &multi); 859 if (err) { 860 NL_SET_ERR_MSG_MOD(fco->common.extack, 861 "No matching port keyset for filter protocol and keys"); 862 goto out; 863 } 864 865 /* provide the l3 protocol to guide the keyset selection */ 866 err = vcap_val_rule(vrule, l3_proto); 867 if (err) { 868 vcap_set_tc_exterr(fco, vrule); 869 goto out; 870 } 871 err = vcap_add_rule(vrule); 872 if (err) 873 NL_SET_ERR_MSG_MOD(fco->common.extack, 874 "Could not add the filter"); 875 876 if (l3_proto == ETH_P_ALL) 877 err = sparx5_tc_add_remaining_rules(vctrl, fco, vrule, admin, 878 &multi); 879 880 out: 881 vcap_free_rule(vrule); 882 return err; 883 } 884 885 static void sparx5_tc_free_psfp_resources(struct sparx5 *sparx5, 886 struct vcap_rule *vrule) 887 { 888 struct vcap_client_actionfield *afield; 889 u32 isdx, sfid, sgid, fmid; 890 891 /* Check if VCAP_AF_ISDX_VAL action is set for this rule - and if 892 * it is used for stream and/or flow-meter classification. 893 */ 894 afield = vcap_find_actionfield(vrule, VCAP_AF_ISDX_VAL); 895 if (!afield) 896 return; 897 898 isdx = afield->data.u32.value; 899 sfid = sparx5_psfp_isdx_get_sf(sparx5, isdx); 900 901 if (!sfid) 902 return; 903 904 fmid = sparx5_psfp_isdx_get_fm(sparx5, isdx); 905 sgid = sparx5_psfp_sf_get_sg(sparx5, sfid); 906 907 if (fmid && sparx5_psfp_fm_del(sparx5, fmid) < 0) 908 pr_err("%s:%d Could not delete invalid fmid: %d", __func__, 909 __LINE__, fmid); 910 911 if (sgid && sparx5_psfp_sg_del(sparx5, sgid) < 0) 912 pr_err("%s:%d Could not delete invalid sgid: %d", __func__, 913 __LINE__, sgid); 914 915 if (sparx5_psfp_sf_del(sparx5, sfid) < 0) 916 pr_err("%s:%d Could not delete invalid sfid: %d", __func__, 917 __LINE__, sfid); 918 919 sparx5_isdx_conf_set(sparx5, isdx, 0, 0); 920 } 921 922 static int sparx5_tc_free_rule_resources(struct net_device *ndev, 923 struct vcap_control *vctrl, 924 int rule_id) 925 { 926 struct sparx5_port *port = netdev_priv(ndev); 927 struct sparx5 *sparx5 = port->sparx5; 928 struct vcap_rule *vrule; 929 int ret = 0; 930 931 vrule = vcap_get_rule(vctrl, rule_id); 932 if (!vrule || IS_ERR(vrule)) 933 return -EINVAL; 934 935 sparx5_tc_free_psfp_resources(sparx5, vrule); 936 937 vcap_free_rule(vrule); 938 return ret; 939 } 940 941 static int sparx5_tc_flower_destroy(struct net_device *ndev, 942 struct flow_cls_offload *fco, 943 struct vcap_admin *admin) 944 { 945 struct sparx5_port *port = netdev_priv(ndev); 946 int err = -ENOENT, count = 0, rule_id; 947 struct vcap_control *vctrl; 948 949 vctrl = port->sparx5->vcap_ctrl; 950 while (true) { 951 rule_id = vcap_lookup_rule_by_cookie(vctrl, fco->cookie); 952 if (rule_id <= 0) 953 break; 954 if (count == 0) { 955 /* Resources are attached to the first rule of 956 * a set of rules. Only works if the rules are 957 * in the correct order. 958 */ 959 err = sparx5_tc_free_rule_resources(ndev, vctrl, 960 rule_id); 961 if (err) 962 pr_err("%s:%d: could not free resources %d\n", 963 __func__, __LINE__, rule_id); 964 } 965 err = vcap_del_rule(vctrl, ndev, rule_id); 966 if (err) { 967 pr_err("%s:%d: could not delete rule %d\n", 968 __func__, __LINE__, rule_id); 969 break; 970 } 971 } 972 return err; 973 } 974 975 static int sparx5_tc_flower_stats(struct net_device *ndev, 976 struct flow_cls_offload *fco, 977 struct vcap_admin *admin) 978 { 979 struct sparx5_port *port = netdev_priv(ndev); 980 struct vcap_counter ctr = {}; 981 struct vcap_control *vctrl; 982 ulong lastused = 0; 983 int err; 984 985 vctrl = port->sparx5->vcap_ctrl; 986 err = vcap_get_rule_count_by_cookie(vctrl, &ctr, fco->cookie); 987 if (err) 988 return err; 989 flow_stats_update(&fco->stats, 0x0, ctr.value, 0, lastused, 990 FLOW_ACTION_HW_STATS_IMMEDIATE); 991 return err; 992 } 993 994 int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco, 995 bool ingress) 996 { 997 struct sparx5_port *port = netdev_priv(ndev); 998 struct vcap_control *vctrl; 999 struct vcap_admin *admin; 1000 int err = -EINVAL; 1001 1002 /* Get vcap instance from the chain id */ 1003 vctrl = port->sparx5->vcap_ctrl; 1004 admin = vcap_find_admin(vctrl, fco->common.chain_index); 1005 if (!admin) { 1006 NL_SET_ERR_MSG_MOD(fco->common.extack, "Invalid chain"); 1007 return err; 1008 } 1009 1010 switch (fco->command) { 1011 case FLOW_CLS_REPLACE: 1012 return sparx5_tc_flower_replace(ndev, fco, admin, ingress); 1013 case FLOW_CLS_DESTROY: 1014 return sparx5_tc_flower_destroy(ndev, fco, admin); 1015 case FLOW_CLS_STATS: 1016 return sparx5_tc_flower_stats(ndev, fco, admin); 1017 default: 1018 return -EOPNOTSUPP; 1019 } 1020 } 1021