627 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3#include "lan966x_main.h"4#include "vcap_api.h"5#include "vcap_api_client.h"6#include "vcap_tc.h"7 8#define LAN966X_FORCE_UNTAGED 39 10static bool lan966x_tc_is_known_etype(struct vcap_tc_flower_parse_usage *st,11 u16 etype)12{13 switch (st->admin->vtype) {14 case VCAP_TYPE_IS1:15 switch (etype) {16 case ETH_P_ALL:17 case ETH_P_ARP:18 case ETH_P_IP:19 case ETH_P_IPV6:20 return true;21 }22 break;23 case VCAP_TYPE_IS2:24 switch (etype) {25 case ETH_P_ALL:26 case ETH_P_ARP:27 case ETH_P_IP:28 case ETH_P_IPV6:29 case ETH_P_SNAP:30 case ETH_P_802_2:31 return true;32 }33 break;34 case VCAP_TYPE_ES0:35 return true;36 default:37 NL_SET_ERR_MSG_MOD(st->fco->common.extack,38 "VCAP type not supported");39 return false;40 }41 42 return false;43}44 45static int46lan966x_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage *st)47{48 struct netlink_ext_ack *extack = st->fco->common.extack;49 struct flow_match_control match;50 int err = 0;51 52 flow_rule_match_control(st->frule, &match);53 if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) {54 if (match.key->flags & FLOW_DIS_IS_FRAGMENT)55 err = vcap_rule_add_key_bit(st->vrule,56 VCAP_KF_L3_FRAGMENT,57 VCAP_BIT_1);58 else59 err = vcap_rule_add_key_bit(st->vrule,60 VCAP_KF_L3_FRAGMENT,61 VCAP_BIT_0);62 if (err)63 goto bad_frag_out;64 }65 66 if (match.mask->flags & FLOW_DIS_FIRST_FRAG) {67 if (match.key->flags & FLOW_DIS_FIRST_FRAG)68 err = vcap_rule_add_key_bit(st->vrule,69 VCAP_KF_L3_FRAG_OFS_GT0,70 VCAP_BIT_0);71 else72 err = vcap_rule_add_key_bit(st->vrule,73 VCAP_KF_L3_FRAG_OFS_GT0,74 VCAP_BIT_1);75 if (err)76 goto bad_frag_out;77 }78 79 if (!flow_rule_is_supp_control_flags(FLOW_DIS_IS_FRAGMENT |80 FLOW_DIS_FIRST_FRAG,81 match.mask->flags, extack))82 return -EOPNOTSUPP;83 84 st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL);85 86 return err;87 88bad_frag_out:89 NL_SET_ERR_MSG_MOD(extack, "ip_frag parse error");90 return err;91}92 93static int94lan966x_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st)95{96 struct flow_match_basic match;97 int err = 0;98 99 flow_rule_match_basic(st->frule, &match);100 if (match.mask->n_proto) {101 st->l3_proto = be16_to_cpu(match.key->n_proto);102 if (!lan966x_tc_is_known_etype(st, st->l3_proto)) {103 err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,104 st->l3_proto, ~0);105 if (err)106 goto out;107 } else if (st->l3_proto == ETH_P_IP) {108 err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,109 VCAP_BIT_1);110 if (err)111 goto out;112 } else if (st->l3_proto == ETH_P_IPV6 &&113 st->admin->vtype == VCAP_TYPE_IS1) {114 /* Don't set any keys in this case */115 } else if (st->l3_proto == ETH_P_SNAP &&116 st->admin->vtype == VCAP_TYPE_IS1) {117 err = vcap_rule_add_key_bit(st->vrule,118 VCAP_KF_ETYPE_LEN_IS,119 VCAP_BIT_0);120 if (err)121 goto out;122 123 err = vcap_rule_add_key_bit(st->vrule,124 VCAP_KF_IP_SNAP_IS,125 VCAP_BIT_1);126 if (err)127 goto out;128 } else if (st->admin->vtype == VCAP_TYPE_IS1) {129 err = vcap_rule_add_key_bit(st->vrule,130 VCAP_KF_ETYPE_LEN_IS,131 VCAP_BIT_1);132 if (err)133 goto out;134 135 err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,136 st->l3_proto, ~0);137 if (err)138 goto out;139 }140 }141 if (match.mask->ip_proto) {142 st->l4_proto = match.key->ip_proto;143 144 if (st->l4_proto == IPPROTO_TCP) {145 if (st->admin->vtype == VCAP_TYPE_IS1) {146 err = vcap_rule_add_key_bit(st->vrule,147 VCAP_KF_TCP_UDP_IS,148 VCAP_BIT_1);149 if (err)150 goto out;151 }152 153 err = vcap_rule_add_key_bit(st->vrule,154 VCAP_KF_TCP_IS,155 VCAP_BIT_1);156 if (err)157 goto out;158 } else if (st->l4_proto == IPPROTO_UDP) {159 if (st->admin->vtype == VCAP_TYPE_IS1) {160 err = vcap_rule_add_key_bit(st->vrule,161 VCAP_KF_TCP_UDP_IS,162 VCAP_BIT_1);163 if (err)164 goto out;165 }166 167 err = vcap_rule_add_key_bit(st->vrule,168 VCAP_KF_TCP_IS,169 VCAP_BIT_0);170 if (err)171 goto out;172 } else {173 err = vcap_rule_add_key_u32(st->vrule,174 VCAP_KF_L3_IP_PROTO,175 st->l4_proto, ~0);176 if (err)177 goto out;178 }179 }180 181 st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_BASIC);182 return err;183out:184 NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error");185 return err;186}187 188static int189lan966x_tc_flower_handler_cvlan_usage(struct vcap_tc_flower_parse_usage *st)190{191 if (st->admin->vtype != VCAP_TYPE_IS1) {192 NL_SET_ERR_MSG_MOD(st->fco->common.extack,193 "cvlan not supported in this VCAP");194 return -EINVAL;195 }196 197 return vcap_tc_flower_handler_cvlan_usage(st);198}199 200static int201lan966x_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st)202{203 enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS;204 enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS;205 206 if (st->admin->vtype == VCAP_TYPE_IS1) {207 vid_key = VCAP_KF_8021Q_VID0;208 pcp_key = VCAP_KF_8021Q_PCP0;209 }210 211 return vcap_tc_flower_handler_vlan_usage(st, vid_key, pcp_key);212}213 214static int215(*lan966x_tc_flower_handlers_usage[])(struct vcap_tc_flower_parse_usage *st) = {216 [FLOW_DISSECTOR_KEY_ETH_ADDRS] = vcap_tc_flower_handler_ethaddr_usage,217 [FLOW_DISSECTOR_KEY_IPV4_ADDRS] = vcap_tc_flower_handler_ipv4_usage,218 [FLOW_DISSECTOR_KEY_IPV6_ADDRS] = vcap_tc_flower_handler_ipv6_usage,219 [FLOW_DISSECTOR_KEY_CONTROL] = lan966x_tc_flower_handler_control_usage,220 [FLOW_DISSECTOR_KEY_PORTS] = vcap_tc_flower_handler_portnum_usage,221 [FLOW_DISSECTOR_KEY_BASIC] = lan966x_tc_flower_handler_basic_usage,222 [FLOW_DISSECTOR_KEY_CVLAN] = lan966x_tc_flower_handler_cvlan_usage,223 [FLOW_DISSECTOR_KEY_VLAN] = lan966x_tc_flower_handler_vlan_usage,224 [FLOW_DISSECTOR_KEY_TCP] = vcap_tc_flower_handler_tcp_usage,225 [FLOW_DISSECTOR_KEY_ARP] = vcap_tc_flower_handler_arp_usage,226 [FLOW_DISSECTOR_KEY_IP] = vcap_tc_flower_handler_ip_usage,227};228 229static int lan966x_tc_flower_use_dissectors(struct flow_cls_offload *f,230 struct vcap_admin *admin,231 struct vcap_rule *vrule,232 u16 *l3_proto)233{234 struct vcap_tc_flower_parse_usage state = {235 .fco = f,236 .vrule = vrule,237 .l3_proto = ETH_P_ALL,238 .admin = admin,239 };240 int err = 0;241 242 state.frule = flow_cls_offload_flow_rule(f);243 for (int i = 0; i < ARRAY_SIZE(lan966x_tc_flower_handlers_usage); ++i) {244 if (!flow_rule_match_key(state.frule, i) ||245 !lan966x_tc_flower_handlers_usage[i])246 continue;247 248 err = lan966x_tc_flower_handlers_usage[i](&state);249 if (err)250 return err;251 }252 253 if (l3_proto)254 *l3_proto = state.l3_proto;255 256 return err;257}258 259static int lan966x_tc_flower_action_check(struct vcap_control *vctrl,260 struct net_device *dev,261 struct flow_cls_offload *fco,262 bool ingress)263{264 struct flow_rule *rule = flow_cls_offload_flow_rule(fco);265 struct flow_action_entry *actent, *last_actent = NULL;266 struct flow_action *act = &rule->action;267 u64 action_mask = 0;268 int idx;269 270 if (!flow_action_has_entries(act)) {271 NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");272 return -EINVAL;273 }274 275 if (!flow_action_basic_hw_stats_check(act, fco->common.extack))276 return -EOPNOTSUPP;277 278 flow_action_for_each(idx, actent, act) {279 if (action_mask & BIT(actent->id)) {280 NL_SET_ERR_MSG_MOD(fco->common.extack,281 "More actions of the same type");282 return -EINVAL;283 }284 action_mask |= BIT(actent->id);285 last_actent = actent; /* Save last action for later check */286 }287 288 /* Check that last action is a goto289 * The last chain/lookup does not need to have goto action290 */291 if (last_actent->id == FLOW_ACTION_GOTO) {292 /* Check if the destination chain is in one of the VCAPs */293 if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,294 last_actent->chain_index)) {295 NL_SET_ERR_MSG_MOD(fco->common.extack,296 "Invalid goto chain");297 return -EINVAL;298 }299 } else if (!vcap_is_last_chain(vctrl, fco->common.chain_index,300 ingress)) {301 NL_SET_ERR_MSG_MOD(fco->common.extack,302 "Last action must be 'goto'");303 return -EINVAL;304 }305 306 /* Catch unsupported combinations of actions */307 if (action_mask & BIT(FLOW_ACTION_TRAP) &&308 action_mask & BIT(FLOW_ACTION_ACCEPT)) {309 NL_SET_ERR_MSG_MOD(fco->common.extack,310 "Cannot combine pass and trap action");311 return -EOPNOTSUPP;312 }313 314 return 0;315}316 317/* Add the actionset that is the default for the VCAP type */318static int lan966x_tc_set_actionset(struct vcap_admin *admin,319 struct vcap_rule *vrule)320{321 enum vcap_actionfield_set aset;322 int err = 0;323 324 switch (admin->vtype) {325 case VCAP_TYPE_IS1:326 aset = VCAP_AFS_S1;327 break;328 case VCAP_TYPE_IS2:329 aset = VCAP_AFS_BASE_TYPE;330 break;331 case VCAP_TYPE_ES0:332 aset = VCAP_AFS_VID;333 break;334 default:335 return -EINVAL;336 }337 338 /* Do not overwrite any current actionset */339 if (vrule->actionset == VCAP_AFS_NO_VALUE)340 err = vcap_set_rule_set_actionset(vrule, aset);341 342 return err;343}344 345static int lan966x_tc_add_rule_link_target(struct vcap_admin *admin,346 struct vcap_rule *vrule,347 int target_cid)348{349 int link_val = target_cid % VCAP_CID_LOOKUP_SIZE;350 int err;351 352 if (!link_val)353 return 0;354 355 switch (admin->vtype) {356 case VCAP_TYPE_IS1:357 /* Choose IS1 specific NXT_IDX key (for chaining rules from IS1) */358 err = vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX_SEL,359 1, ~0);360 if (err)361 return err;362 363 return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX,364 link_val, ~0);365 case VCAP_TYPE_IS2:366 /* Add IS2 specific PAG key (for chaining rules from IS1) */367 return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_PAG,368 link_val, ~0);369 case VCAP_TYPE_ES0:370 /* Add ES0 specific ISDX key (for chaining rules from IS1) */371 return vcap_rule_add_key_u32(vrule, VCAP_KF_ISDX_CLS,372 link_val, ~0);373 default:374 break;375 }376 return 0;377}378 379static int lan966x_tc_add_rule_link(struct vcap_control *vctrl,380 struct vcap_admin *admin,381 struct vcap_rule *vrule,382 struct flow_cls_offload *f,383 int to_cid)384{385 struct vcap_admin *to_admin = vcap_find_admin(vctrl, to_cid);386 int diff, err = 0;387 388 if (!to_admin) {389 NL_SET_ERR_MSG_MOD(f->common.extack,390 "Unknown destination chain");391 return -EINVAL;392 }393 394 diff = vcap_chain_offset(vctrl, f->common.chain_index, to_cid);395 if (!diff)396 return 0;397 398 /* Between IS1 and IS2 the PAG value is used */399 if (admin->vtype == VCAP_TYPE_IS1 && to_admin->vtype == VCAP_TYPE_IS2) {400 /* This works for IS1->IS2 */401 err = vcap_rule_add_action_u32(vrule, VCAP_AF_PAG_VAL, diff);402 if (err)403 return err;404 405 err = vcap_rule_add_action_u32(vrule, VCAP_AF_PAG_OVERRIDE_MASK,406 0xff);407 if (err)408 return err;409 } else if (admin->vtype == VCAP_TYPE_IS1 &&410 to_admin->vtype == VCAP_TYPE_ES0) {411 /* This works for IS1->ES0 */412 err = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_ADD_VAL,413 diff);414 if (err)415 return err;416 417 err = vcap_rule_add_action_bit(vrule, VCAP_AF_ISDX_REPLACE_ENA,418 VCAP_BIT_1);419 if (err)420 return err;421 } else {422 NL_SET_ERR_MSG_MOD(f->common.extack,423 "Unsupported chain destination");424 return -EOPNOTSUPP;425 }426 427 return err;428}429 430static int lan966x_tc_add_rule_counter(struct vcap_admin *admin,431 struct vcap_rule *vrule)432{433 int err = 0;434 435 switch (admin->vtype) {436 case VCAP_TYPE_ES0:437 err = vcap_rule_mod_action_u32(vrule, VCAP_AF_ESDX,438 vrule->id);439 break;440 default:441 break;442 }443 444 return err;445}446 447static int lan966x_tc_flower_add(struct lan966x_port *port,448 struct flow_cls_offload *f,449 struct vcap_admin *admin,450 bool ingress)451{452 struct flow_action_entry *act;453 u16 l3_proto = ETH_P_ALL;454 struct flow_rule *frule;455 struct vcap_rule *vrule;456 int err, idx;457 458 err = lan966x_tc_flower_action_check(port->lan966x->vcap_ctrl,459 port->dev, f, ingress);460 if (err)461 return err;462 463 vrule = vcap_alloc_rule(port->lan966x->vcap_ctrl, port->dev,464 f->common.chain_index, VCAP_USER_TC,465 f->common.prio, 0);466 if (IS_ERR(vrule))467 return PTR_ERR(vrule);468 469 vrule->cookie = f->cookie;470 err = lan966x_tc_flower_use_dissectors(f, admin, vrule, &l3_proto);471 if (err)472 goto out;473 474 err = lan966x_tc_add_rule_link_target(admin, vrule,475 f->common.chain_index);476 if (err)477 goto out;478 479 frule = flow_cls_offload_flow_rule(f);480 481 flow_action_for_each(idx, act, &frule->action) {482 switch (act->id) {483 case FLOW_ACTION_TRAP:484 if (admin->vtype != VCAP_TYPE_IS2) {485 NL_SET_ERR_MSG_MOD(f->common.extack,486 "Trap action not supported in this VCAP");487 err = -EOPNOTSUPP;488 goto out;489 }490 491 err = vcap_rule_add_action_bit(vrule,492 VCAP_AF_CPU_COPY_ENA,493 VCAP_BIT_1);494 err |= vcap_rule_add_action_u32(vrule,495 VCAP_AF_CPU_QUEUE_NUM,496 0);497 err |= vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,498 LAN966X_PMM_REPLACE);499 if (err)500 goto out;501 502 break;503 case FLOW_ACTION_GOTO:504 err = lan966x_tc_set_actionset(admin, vrule);505 if (err)506 goto out;507 508 err = lan966x_tc_add_rule_link(port->lan966x->vcap_ctrl,509 admin, vrule,510 f, act->chain_index);511 if (err)512 goto out;513 514 break;515 case FLOW_ACTION_VLAN_POP:516 if (admin->vtype != VCAP_TYPE_ES0) {517 NL_SET_ERR_MSG_MOD(f->common.extack,518 "Cannot use vlan pop on non es0");519 err = -EOPNOTSUPP;520 goto out;521 }522 523 /* Force untag */524 err = vcap_rule_add_action_u32(vrule, VCAP_AF_PUSH_OUTER_TAG,525 LAN966X_FORCE_UNTAGED);526 if (err)527 goto out;528 529 break;530 default:531 NL_SET_ERR_MSG_MOD(f->common.extack,532 "Unsupported TC action");533 err = -EOPNOTSUPP;534 goto out;535 }536 }537 538 err = lan966x_tc_add_rule_counter(admin, vrule);539 if (err) {540 vcap_set_tc_exterr(f, vrule);541 goto out;542 }543 544 err = vcap_val_rule(vrule, l3_proto);545 if (err) {546 vcap_set_tc_exterr(f, vrule);547 goto out;548 }549 550 err = vcap_add_rule(vrule);551 if (err)552 NL_SET_ERR_MSG_MOD(f->common.extack,553 "Could not add the filter");554out:555 vcap_free_rule(vrule);556 return err;557}558 559static int lan966x_tc_flower_del(struct lan966x_port *port,560 struct flow_cls_offload *f,561 struct vcap_admin *admin)562{563 struct vcap_control *vctrl;564 int err = -ENOENT, rule_id;565 566 vctrl = port->lan966x->vcap_ctrl;567 while (true) {568 rule_id = vcap_lookup_rule_by_cookie(vctrl, f->cookie);569 if (rule_id <= 0)570 break;571 572 err = vcap_del_rule(vctrl, port->dev, rule_id);573 if (err) {574 NL_SET_ERR_MSG_MOD(f->common.extack,575 "Cannot delete rule");576 break;577 }578 }579 580 return err;581}582 583static int lan966x_tc_flower_stats(struct lan966x_port *port,584 struct flow_cls_offload *f,585 struct vcap_admin *admin)586{587 struct vcap_counter count = {};588 int err;589 590 err = vcap_get_rule_count_by_cookie(port->lan966x->vcap_ctrl,591 &count, f->cookie);592 if (err)593 return err;594 595 flow_stats_update(&f->stats, 0x0, count.value, 0, 0,596 FLOW_ACTION_HW_STATS_IMMEDIATE);597 598 return err;599}600 601int lan966x_tc_flower(struct lan966x_port *port,602 struct flow_cls_offload *f,603 bool ingress)604{605 struct vcap_admin *admin;606 607 admin = vcap_find_admin(port->lan966x->vcap_ctrl,608 f->common.chain_index);609 if (!admin) {610 NL_SET_ERR_MSG_MOD(f->common.extack, "Invalid chain");611 return -EINVAL;612 }613 614 switch (f->command) {615 case FLOW_CLS_REPLACE:616 return lan966x_tc_flower_add(port, f, admin, ingress);617 case FLOW_CLS_DESTROY:618 return lan966x_tc_flower_del(port, f, admin);619 case FLOW_CLS_STATS:620 return lan966x_tc_flower_stats(port, f, admin);621 default:622 return -EOPNOTSUPP;623 }624 625 return 0;626}627