1013 lines · c
1// SPDX-License-Identifier: (GPL-2.0 OR MIT)2/* Microsemi Ocelot Switch driver3 * Copyright (c) 2019 Microsemi Corporation4 */5 6#include <net/pkt_cls.h>7#include <net/tc_act/tc_gact.h>8#include <soc/mscc/ocelot_vcap.h>9#include "ocelot_police.h"10#include "ocelot_vcap.h"11 12/* Arbitrarily chosen constants for encoding the VCAP block and lookup number13 * into the chain number. This is UAPI.14 */15#define VCAP_BLOCK 1000016#define VCAP_LOOKUP 100017#define VCAP_IS1_NUM_LOOKUPS 318#define VCAP_IS2_NUM_LOOKUPS 219#define VCAP_IS2_NUM_PAG 25620#define VCAP_IS1_CHAIN(lookup) \21 (1 * VCAP_BLOCK + (lookup) * VCAP_LOOKUP)22#define VCAP_IS2_CHAIN(lookup, pag) \23 (2 * VCAP_BLOCK + (lookup) * VCAP_LOOKUP + (pag))24/* PSFP chain and block ID */25#define PSFP_BLOCK_ID OCELOT_NUM_VCAP_BLOCKS26#define OCELOT_PSFP_CHAIN (3 * VCAP_BLOCK)27 28static int ocelot_chain_to_block(int chain, bool ingress)29{30 int lookup, pag;31 32 if (!ingress) {33 if (chain == 0)34 return VCAP_ES0;35 return -EOPNOTSUPP;36 }37 38 /* Backwards compatibility with older, single-chain tc-flower39 * offload support in Ocelot40 */41 if (chain == 0)42 return VCAP_IS2;43 44 for (lookup = 0; lookup < VCAP_IS1_NUM_LOOKUPS; lookup++)45 if (chain == VCAP_IS1_CHAIN(lookup))46 return VCAP_IS1;47 48 for (lookup = 0; lookup < VCAP_IS2_NUM_LOOKUPS; lookup++)49 for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)50 if (chain == VCAP_IS2_CHAIN(lookup, pag))51 return VCAP_IS2;52 53 if (chain == OCELOT_PSFP_CHAIN)54 return PSFP_BLOCK_ID;55 56 return -EOPNOTSUPP;57}58 59/* Caller must ensure this is a valid IS1 or IS2 chain first,60 * by calling ocelot_chain_to_block.61 */62static int ocelot_chain_to_lookup(int chain)63{64 /* Backwards compatibility with older, single-chain tc-flower65 * offload support in Ocelot66 */67 if (chain == 0)68 return 0;69 70 return (chain / VCAP_LOOKUP) % 10;71}72 73/* Caller must ensure this is a valid IS2 chain first,74 * by calling ocelot_chain_to_block.75 */76static int ocelot_chain_to_pag(int chain)77{78 int lookup;79 80 /* Backwards compatibility with older, single-chain tc-flower81 * offload support in Ocelot82 */83 if (chain == 0)84 return 0;85 86 lookup = ocelot_chain_to_lookup(chain);87 88 /* calculate PAG value as chain index relative to the first PAG */89 return chain - VCAP_IS2_CHAIN(lookup, 0);90}91 92static bool ocelot_is_goto_target_valid(int goto_target, int chain,93 bool ingress)94{95 int pag;96 97 /* Can't offload GOTO in VCAP ES0 */98 if (!ingress)99 return (goto_target < 0);100 101 /* Non-optional GOTOs */102 if (chain == 0)103 /* VCAP IS1 can be skipped, either partially or completely */104 return (goto_target == VCAP_IS1_CHAIN(0) ||105 goto_target == VCAP_IS1_CHAIN(1) ||106 goto_target == VCAP_IS1_CHAIN(2) ||107 goto_target == VCAP_IS2_CHAIN(0, 0) ||108 goto_target == VCAP_IS2_CHAIN(1, 0) ||109 goto_target == OCELOT_PSFP_CHAIN);110 111 if (chain == VCAP_IS1_CHAIN(0))112 return (goto_target == VCAP_IS1_CHAIN(1));113 114 if (chain == VCAP_IS1_CHAIN(1))115 return (goto_target == VCAP_IS1_CHAIN(2));116 117 /* Lookup 2 of VCAP IS1 can really support non-optional GOTOs,118 * using a Policy Association Group (PAG) value, which is an 8-bit119 * value encoding a VCAP IS2 target chain.120 */121 if (chain == VCAP_IS1_CHAIN(2)) {122 for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)123 if (goto_target == VCAP_IS2_CHAIN(0, pag))124 return true;125 126 return false;127 }128 129 /* Non-optional GOTO from VCAP IS2 lookup 0 to lookup 1.130 * We cannot change the PAG at this point.131 */132 for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)133 if (chain == VCAP_IS2_CHAIN(0, pag))134 return (goto_target == VCAP_IS2_CHAIN(1, pag));135 136 /* VCAP IS2 lookup 1 can goto to PSFP block if hardware support */137 for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)138 if (chain == VCAP_IS2_CHAIN(1, pag))139 return (goto_target == OCELOT_PSFP_CHAIN);140 141 return false;142}143 144static struct ocelot_vcap_filter *145ocelot_find_vcap_filter_that_points_at(struct ocelot *ocelot, int chain)146{147 struct ocelot_vcap_filter *filter;148 struct ocelot_vcap_block *block;149 int block_id;150 151 block_id = ocelot_chain_to_block(chain, true);152 if (block_id < 0)153 return NULL;154 155 if (block_id == VCAP_IS2) {156 block = &ocelot->block[VCAP_IS1];157 158 list_for_each_entry(filter, &block->rules, list)159 if (filter->type == OCELOT_VCAP_FILTER_PAG &&160 filter->goto_target == chain)161 return filter;162 }163 164 list_for_each_entry(filter, &ocelot->dummy_rules, list)165 if (filter->goto_target == chain)166 return filter;167 168 return NULL;169}170 171static int172ocelot_flower_parse_ingress_vlan_modify(struct ocelot *ocelot, int port,173 struct ocelot_vcap_filter *filter,174 const struct flow_action_entry *a,175 struct netlink_ext_ack *extack)176{177 struct ocelot_port *ocelot_port = ocelot->ports[port];178 179 if (filter->goto_target != -1) {180 NL_SET_ERR_MSG_MOD(extack,181 "Last action must be GOTO");182 return -EOPNOTSUPP;183 }184 185 if (!ocelot_port->vlan_aware) {186 NL_SET_ERR_MSG_MOD(extack,187 "Can only modify VLAN under VLAN aware bridge");188 return -EOPNOTSUPP;189 }190 191 filter->action.vid_replace_ena = true;192 filter->action.pcp_dei_ena = true;193 filter->action.vid = a->vlan.vid;194 filter->action.pcp = a->vlan.prio;195 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;196 197 return 0;198}199 200static int201ocelot_flower_parse_egress_vlan_modify(struct ocelot_vcap_filter *filter,202 const struct flow_action_entry *a,203 struct netlink_ext_ack *extack)204{205 enum ocelot_tag_tpid_sel tpid;206 207 switch (ntohs(a->vlan.proto)) {208 case ETH_P_8021Q:209 tpid = OCELOT_TAG_TPID_SEL_8021Q;210 break;211 case ETH_P_8021AD:212 tpid = OCELOT_TAG_TPID_SEL_8021AD;213 break;214 default:215 NL_SET_ERR_MSG_MOD(extack,216 "Cannot modify custom TPID");217 return -EOPNOTSUPP;218 }219 220 filter->action.tag_a_tpid_sel = tpid;221 filter->action.push_outer_tag = OCELOT_ES0_TAG;222 filter->action.tag_a_vid_sel = OCELOT_ES0_VID_PLUS_CLASSIFIED_VID;223 filter->action.vid_a_val = a->vlan.vid;224 filter->action.pcp_a_val = a->vlan.prio;225 filter->action.tag_a_pcp_sel = OCELOT_ES0_PCP;226 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;227 228 return 0;229}230 231static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,232 bool ingress, struct flow_cls_offload *f,233 struct ocelot_vcap_filter *filter)234{235 const struct flow_action *action = &f->rule->action;236 struct netlink_ext_ack *extack = f->common.extack;237 bool allow_missing_goto_target = false;238 const struct flow_action_entry *a;239 enum ocelot_tag_tpid_sel tpid;240 int i, chain, egress_port;241 u32 pol_ix, pol_max;242 u64 rate;243 int err;244 245 if (!flow_action_basic_hw_stats_check(&f->rule->action,246 f->common.extack))247 return -EOPNOTSUPP;248 249 chain = f->common.chain_index;250 filter->block_id = ocelot_chain_to_block(chain, ingress);251 if (filter->block_id < 0) {252 NL_SET_ERR_MSG_MOD(extack, "Cannot offload to this chain");253 return -EOPNOTSUPP;254 }255 if (filter->block_id == VCAP_IS1 || filter->block_id == VCAP_IS2)256 filter->lookup = ocelot_chain_to_lookup(chain);257 if (filter->block_id == VCAP_IS2)258 filter->pag = ocelot_chain_to_pag(chain);259 260 filter->goto_target = -1;261 filter->type = OCELOT_VCAP_FILTER_DUMMY;262 263 flow_action_for_each(i, a, action) {264 switch (a->id) {265 case FLOW_ACTION_DROP:266 if (filter->block_id != VCAP_IS2) {267 NL_SET_ERR_MSG_MOD(extack,268 "Drop action can only be offloaded to VCAP IS2");269 return -EOPNOTSUPP;270 }271 if (filter->goto_target != -1) {272 NL_SET_ERR_MSG_MOD(extack,273 "Last action must be GOTO");274 return -EOPNOTSUPP;275 }276 filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;277 filter->action.port_mask = 0;278 filter->action.police_ena = true;279 filter->action.pol_ix = OCELOT_POLICER_DISCARD;280 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;281 break;282 case FLOW_ACTION_ACCEPT:283 if (filter->block_id != VCAP_ES0 &&284 filter->block_id != VCAP_IS1 &&285 filter->block_id != VCAP_IS2) {286 NL_SET_ERR_MSG_MOD(extack,287 "Accept action can only be offloaded to VCAP chains");288 return -EOPNOTSUPP;289 }290 if (filter->block_id != VCAP_ES0 &&291 filter->goto_target != -1) {292 NL_SET_ERR_MSG_MOD(extack,293 "Last action must be GOTO");294 return -EOPNOTSUPP;295 }296 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;297 break;298 case FLOW_ACTION_TRAP:299 if (filter->block_id != VCAP_IS2 ||300 filter->lookup != 0) {301 NL_SET_ERR_MSG_MOD(extack,302 "Trap action can only be offloaded to VCAP IS2 lookup 0");303 return -EOPNOTSUPP;304 }305 if (filter->goto_target != -1) {306 NL_SET_ERR_MSG_MOD(extack,307 "Last action must be GOTO");308 return -EOPNOTSUPP;309 }310 filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;311 filter->action.port_mask = 0;312 filter->action.cpu_copy_ena = true;313 filter->action.cpu_qu_num = 0;314 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;315 filter->is_trap = true;316 break;317 case FLOW_ACTION_POLICE:318 if (filter->block_id == PSFP_BLOCK_ID) {319 filter->type = OCELOT_PSFP_FILTER_OFFLOAD;320 break;321 }322 if (filter->block_id != VCAP_IS2 ||323 filter->lookup != 0) {324 NL_SET_ERR_MSG_MOD(extack,325 "Police action can only be offloaded to VCAP IS2 lookup 0 or PSFP");326 return -EOPNOTSUPP;327 }328 if (filter->goto_target != -1) {329 NL_SET_ERR_MSG_MOD(extack,330 "Last action must be GOTO");331 return -EOPNOTSUPP;332 }333 334 err = ocelot_policer_validate(action, a, extack);335 if (err)336 return err;337 338 filter->action.police_ena = true;339 340 pol_ix = a->hw_index + ocelot->vcap_pol.base;341 pol_max = ocelot->vcap_pol.max;342 343 if (ocelot->vcap_pol.max2 && pol_ix > pol_max) {344 pol_ix += ocelot->vcap_pol.base2 - pol_max - 1;345 pol_max = ocelot->vcap_pol.max2;346 }347 348 if (pol_ix >= pol_max)349 return -EINVAL;350 351 filter->action.pol_ix = pol_ix;352 353 rate = a->police.rate_bytes_ps;354 filter->action.pol.rate = div_u64(rate, 1000) * 8;355 filter->action.pol.burst = a->police.burst;356 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;357 break;358 case FLOW_ACTION_REDIRECT:359 if (filter->block_id != VCAP_IS2) {360 NL_SET_ERR_MSG_MOD(extack,361 "Redirect action can only be offloaded to VCAP IS2");362 return -EOPNOTSUPP;363 }364 if (filter->goto_target != -1) {365 NL_SET_ERR_MSG_MOD(extack,366 "Last action must be GOTO");367 return -EOPNOTSUPP;368 }369 egress_port = ocelot->ops->netdev_to_port(a->dev);370 if (egress_port < 0) {371 NL_SET_ERR_MSG_MOD(extack,372 "Destination not an ocelot port");373 return -EOPNOTSUPP;374 }375 filter->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;376 filter->action.port_mask = BIT(egress_port);377 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;378 break;379 case FLOW_ACTION_MIRRED:380 if (filter->block_id != VCAP_IS2) {381 NL_SET_ERR_MSG_MOD(extack,382 "Mirror action can only be offloaded to VCAP IS2");383 return -EOPNOTSUPP;384 }385 if (filter->goto_target != -1) {386 NL_SET_ERR_MSG_MOD(extack,387 "Last action must be GOTO");388 return -EOPNOTSUPP;389 }390 egress_port = ocelot->ops->netdev_to_port(a->dev);391 if (egress_port < 0) {392 NL_SET_ERR_MSG_MOD(extack,393 "Destination not an ocelot port");394 return -EOPNOTSUPP;395 }396 filter->egress_port.value = egress_port;397 filter->action.mirror_ena = true;398 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;399 break;400 case FLOW_ACTION_VLAN_POP:401 if (filter->block_id != VCAP_IS1) {402 NL_SET_ERR_MSG_MOD(extack,403 "VLAN pop action can only be offloaded to VCAP IS1");404 return -EOPNOTSUPP;405 }406 if (filter->goto_target != -1) {407 NL_SET_ERR_MSG_MOD(extack,408 "Last action must be GOTO");409 return -EOPNOTSUPP;410 }411 filter->action.vlan_pop_cnt_ena = true;412 filter->action.vlan_pop_cnt++;413 if (filter->action.vlan_pop_cnt > 2) {414 NL_SET_ERR_MSG_MOD(extack,415 "Cannot pop more than 2 VLAN headers");416 return -EOPNOTSUPP;417 }418 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;419 break;420 case FLOW_ACTION_VLAN_MANGLE:421 if (filter->block_id == VCAP_IS1) {422 err = ocelot_flower_parse_ingress_vlan_modify(ocelot, port,423 filter, a,424 extack);425 } else if (filter->block_id == VCAP_ES0) {426 err = ocelot_flower_parse_egress_vlan_modify(filter, a,427 extack);428 } else {429 NL_SET_ERR_MSG_MOD(extack,430 "VLAN modify action can only be offloaded to VCAP IS1 or ES0");431 err = -EOPNOTSUPP;432 }433 if (err)434 return err;435 break;436 case FLOW_ACTION_PRIORITY:437 if (filter->block_id != VCAP_IS1) {438 NL_SET_ERR_MSG_MOD(extack,439 "Priority action can only be offloaded to VCAP IS1");440 return -EOPNOTSUPP;441 }442 if (filter->goto_target != -1) {443 NL_SET_ERR_MSG_MOD(extack,444 "Last action must be GOTO");445 return -EOPNOTSUPP;446 }447 filter->action.qos_ena = true;448 filter->action.qos_val = a->priority;449 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;450 break;451 case FLOW_ACTION_GOTO:452 filter->goto_target = a->chain_index;453 454 if (filter->block_id == VCAP_IS1 && filter->lookup == 2) {455 int pag = ocelot_chain_to_pag(filter->goto_target);456 457 filter->action.pag_override_mask = 0xff;458 filter->action.pag_val = pag;459 filter->type = OCELOT_VCAP_FILTER_PAG;460 }461 break;462 case FLOW_ACTION_VLAN_PUSH:463 if (filter->block_id != VCAP_ES0) {464 NL_SET_ERR_MSG_MOD(extack,465 "VLAN push action can only be offloaded to VCAP ES0");466 return -EOPNOTSUPP;467 }468 switch (ntohs(a->vlan.proto)) {469 case ETH_P_8021Q:470 tpid = OCELOT_TAG_TPID_SEL_8021Q;471 break;472 case ETH_P_8021AD:473 tpid = OCELOT_TAG_TPID_SEL_8021AD;474 break;475 default:476 NL_SET_ERR_MSG_MOD(extack,477 "Cannot push custom TPID");478 return -EOPNOTSUPP;479 }480 filter->action.tag_a_tpid_sel = tpid;481 filter->action.push_outer_tag = OCELOT_ES0_TAG;482 filter->action.tag_a_vid_sel = OCELOT_ES0_VID;483 filter->action.vid_a_val = a->vlan.vid;484 filter->action.pcp_a_val = a->vlan.prio;485 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;486 break;487 case FLOW_ACTION_GATE:488 if (filter->block_id != PSFP_BLOCK_ID) {489 NL_SET_ERR_MSG_MOD(extack,490 "Gate action can only be offloaded to PSFP chain");491 return -EOPNOTSUPP;492 }493 filter->type = OCELOT_PSFP_FILTER_OFFLOAD;494 break;495 default:496 NL_SET_ERR_MSG_MOD(extack, "Cannot offload action");497 return -EOPNOTSUPP;498 }499 }500 501 if (filter->goto_target == -1) {502 if ((filter->block_id == VCAP_IS2 && filter->lookup == 1) ||503 chain == 0 || filter->block_id == PSFP_BLOCK_ID) {504 allow_missing_goto_target = true;505 } else {506 NL_SET_ERR_MSG_MOD(extack, "Missing GOTO action");507 return -EOPNOTSUPP;508 }509 }510 511 if (!ocelot_is_goto_target_valid(filter->goto_target, chain, ingress) &&512 !allow_missing_goto_target) {513 NL_SET_ERR_MSG_MOD(extack, "Cannot offload this GOTO target");514 return -EOPNOTSUPP;515 }516 517 return 0;518}519 520static int ocelot_flower_parse_indev(struct ocelot *ocelot, int port,521 struct flow_cls_offload *f,522 struct ocelot_vcap_filter *filter)523{524 struct flow_rule *rule = flow_cls_offload_flow_rule(f);525 const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];526 int key_length = vcap->keys[VCAP_ES0_IGR_PORT].length;527 struct netlink_ext_ack *extack = f->common.extack;528 struct net_device *dev, *indev;529 struct flow_match_meta match;530 int ingress_port;531 532 flow_rule_match_meta(rule, &match);533 534 if (!match.mask->ingress_ifindex)535 return 0;536 537 if (match.mask->ingress_ifindex != 0xFFFFFFFF) {538 NL_SET_ERR_MSG_MOD(extack, "Unsupported ingress ifindex mask");539 return -EOPNOTSUPP;540 }541 542 dev = ocelot->ops->port_to_netdev(ocelot, port);543 if (!dev)544 return -EINVAL;545 546 indev = __dev_get_by_index(dev_net(dev), match.key->ingress_ifindex);547 if (!indev) {548 NL_SET_ERR_MSG_MOD(extack,549 "Can't find the ingress port to match on");550 return -ENOENT;551 }552 553 ingress_port = ocelot->ops->netdev_to_port(indev);554 if (ingress_port < 0) {555 NL_SET_ERR_MSG_MOD(extack,556 "Can only offload an ocelot ingress port");557 return -EOPNOTSUPP;558 }559 if (ingress_port == port) {560 NL_SET_ERR_MSG_MOD(extack,561 "Ingress port is equal to the egress port");562 return -EINVAL;563 }564 565 filter->ingress_port.value = ingress_port;566 filter->ingress_port.mask = GENMASK(key_length - 1, 0);567 568 return 0;569}570 571static int572ocelot_flower_parse_key(struct ocelot *ocelot, int port, bool ingress,573 struct flow_cls_offload *f,574 struct ocelot_vcap_filter *filter)575{576 struct flow_rule *rule = flow_cls_offload_flow_rule(f);577 struct flow_dissector *dissector = rule->match.dissector;578 struct netlink_ext_ack *extack = f->common.extack;579 u16 proto = ntohs(f->common.protocol);580 bool match_protocol = true;581 int ret;582 583 if (dissector->used_keys &584 ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |585 BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |586 BIT_ULL(FLOW_DISSECTOR_KEY_META) |587 BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) |588 BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) |589 BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |590 BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |591 BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS))) {592 return -EOPNOTSUPP;593 }594 595 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META)) {596 struct flow_match_meta match;597 598 flow_rule_match_meta(rule, &match);599 if (match.mask->l2_miss) {600 NL_SET_ERR_MSG_MOD(extack, "Can't match on \"l2_miss\"");601 return -EOPNOTSUPP;602 }603 }604 605 /* For VCAP ES0 (egress rewriter) we can match on the ingress port */606 if (!ingress) {607 ret = ocelot_flower_parse_indev(ocelot, port, f, filter);608 if (ret)609 return ret;610 }611 612 if (flow_rule_match_has_control_flags(rule, extack))613 return -EOPNOTSUPP;614 615 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {616 struct flow_match_vlan match;617 618 flow_rule_match_vlan(rule, &match);619 filter->key_type = OCELOT_VCAP_KEY_ANY;620 filter->vlan.vid.value = match.key->vlan_id;621 filter->vlan.vid.mask = match.mask->vlan_id;622 filter->vlan.pcp.value[0] = match.key->vlan_priority;623 filter->vlan.pcp.mask[0] = match.mask->vlan_priority;624 match_protocol = false;625 }626 627 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {628 struct flow_match_eth_addrs match;629 630 if (filter->block_id == VCAP_ES0) {631 NL_SET_ERR_MSG_MOD(extack,632 "VCAP ES0 cannot match on MAC address");633 return -EOPNOTSUPP;634 }635 636 /* The hw support mac matches only for MAC_ETYPE key,637 * therefore if other matches(port, tcp flags, etc) are added638 * then just bail out639 */640 if ((dissector->used_keys &641 (BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |642 BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |643 BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL))) !=644 (BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |645 BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |646 BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL)))647 return -EOPNOTSUPP;648 649 flow_rule_match_eth_addrs(rule, &match);650 651 if (filter->block_id == VCAP_IS1 &&652 !is_zero_ether_addr(match.mask->dst)) {653 NL_SET_ERR_MSG_MOD(extack,654 "Key type S1_NORMAL cannot match on destination MAC");655 return -EOPNOTSUPP;656 }657 658 filter->key_type = OCELOT_VCAP_KEY_ETYPE;659 ether_addr_copy(filter->key.etype.dmac.value,660 match.key->dst);661 ether_addr_copy(filter->key.etype.smac.value,662 match.key->src);663 ether_addr_copy(filter->key.etype.dmac.mask,664 match.mask->dst);665 ether_addr_copy(filter->key.etype.smac.mask,666 match.mask->src);667 goto finished_key_parsing;668 }669 670 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {671 struct flow_match_basic match;672 673 flow_rule_match_basic(rule, &match);674 if (ntohs(match.key->n_proto) == ETH_P_IP) {675 if (filter->block_id == VCAP_ES0) {676 NL_SET_ERR_MSG_MOD(extack,677 "VCAP ES0 cannot match on IP protocol");678 return -EOPNOTSUPP;679 }680 681 filter->key_type = OCELOT_VCAP_KEY_IPV4;682 filter->key.ipv4.proto.value[0] =683 match.key->ip_proto;684 filter->key.ipv4.proto.mask[0] =685 match.mask->ip_proto;686 match_protocol = false;687 }688 if (ntohs(match.key->n_proto) == ETH_P_IPV6) {689 if (filter->block_id == VCAP_ES0) {690 NL_SET_ERR_MSG_MOD(extack,691 "VCAP ES0 cannot match on IP protocol");692 return -EOPNOTSUPP;693 }694 695 filter->key_type = OCELOT_VCAP_KEY_IPV6;696 filter->key.ipv6.proto.value[0] =697 match.key->ip_proto;698 filter->key.ipv6.proto.mask[0] =699 match.mask->ip_proto;700 match_protocol = false;701 }702 }703 704 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS) &&705 proto == ETH_P_IP) {706 struct flow_match_ipv4_addrs match;707 u8 *tmp;708 709 if (filter->block_id == VCAP_ES0) {710 NL_SET_ERR_MSG_MOD(extack,711 "VCAP ES0 cannot match on IP address");712 return -EOPNOTSUPP;713 }714 715 flow_rule_match_ipv4_addrs(rule, &match);716 717 if (filter->block_id == VCAP_IS1 && *(u32 *)&match.mask->dst) {718 NL_SET_ERR_MSG_MOD(extack,719 "Key type S1_NORMAL cannot match on destination IP");720 return -EOPNOTSUPP;721 }722 723 tmp = &filter->key.ipv4.sip.value.addr[0];724 memcpy(tmp, &match.key->src, 4);725 726 tmp = &filter->key.ipv4.sip.mask.addr[0];727 memcpy(tmp, &match.mask->src, 4);728 729 tmp = &filter->key.ipv4.dip.value.addr[0];730 memcpy(tmp, &match.key->dst, 4);731 732 tmp = &filter->key.ipv4.dip.mask.addr[0];733 memcpy(tmp, &match.mask->dst, 4);734 match_protocol = false;735 }736 737 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS) &&738 proto == ETH_P_IPV6) {739 return -EOPNOTSUPP;740 }741 742 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {743 struct flow_match_ports match;744 745 if (filter->block_id == VCAP_ES0) {746 NL_SET_ERR_MSG_MOD(extack,747 "VCAP ES0 cannot match on L4 ports");748 return -EOPNOTSUPP;749 }750 751 flow_rule_match_ports(rule, &match);752 filter->key.ipv4.sport.value = ntohs(match.key->src);753 filter->key.ipv4.sport.mask = ntohs(match.mask->src);754 filter->key.ipv4.dport.value = ntohs(match.key->dst);755 filter->key.ipv4.dport.mask = ntohs(match.mask->dst);756 match_protocol = false;757 }758 759finished_key_parsing:760 if (match_protocol && proto != ETH_P_ALL) {761 if (filter->block_id == VCAP_ES0) {762 NL_SET_ERR_MSG_MOD(extack,763 "VCAP ES0 cannot match on L2 proto");764 return -EOPNOTSUPP;765 }766 767 /* TODO: support SNAP, LLC etc */768 if (proto < ETH_P_802_3_MIN)769 return -EOPNOTSUPP;770 filter->key_type = OCELOT_VCAP_KEY_ETYPE;771 *(__be16 *)filter->key.etype.etype.value = htons(proto);772 *(__be16 *)filter->key.etype.etype.mask = htons(0xffff);773 }774 /* else, a filter of type OCELOT_VCAP_KEY_ANY is implicitly added */775 776 return 0;777}778 779static int ocelot_flower_parse(struct ocelot *ocelot, int port, bool ingress,780 struct flow_cls_offload *f,781 struct ocelot_vcap_filter *filter)782{783 int ret;784 785 filter->prio = f->common.prio;786 filter->id.cookie = f->cookie;787 filter->id.tc_offload = true;788 789 ret = ocelot_flower_parse_action(ocelot, port, ingress, f, filter);790 if (ret)791 return ret;792 793 /* PSFP filter need to parse key by stream identification function. */794 if (filter->type == OCELOT_PSFP_FILTER_OFFLOAD)795 return 0;796 797 return ocelot_flower_parse_key(ocelot, port, ingress, f, filter);798}799 800static struct ocelot_vcap_filter801*ocelot_vcap_filter_create(struct ocelot *ocelot, int port, bool ingress,802 struct flow_cls_offload *f)803{804 struct ocelot_vcap_filter *filter;805 806 filter = kzalloc(sizeof(*filter), GFP_KERNEL);807 if (!filter)808 return NULL;809 810 if (ingress) {811 filter->ingress_port_mask = BIT(port);812 } else {813 const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];814 int key_length = vcap->keys[VCAP_ES0_EGR_PORT].length;815 816 filter->egress_port.value = port;817 filter->egress_port.mask = GENMASK(key_length - 1, 0);818 }819 820 return filter;821}822 823static int ocelot_vcap_dummy_filter_add(struct ocelot *ocelot,824 struct ocelot_vcap_filter *filter)825{826 list_add(&filter->list, &ocelot->dummy_rules);827 828 return 0;829}830 831static int ocelot_vcap_dummy_filter_del(struct ocelot *ocelot,832 struct ocelot_vcap_filter *filter)833{834 list_del(&filter->list);835 kfree(filter);836 837 return 0;838}839 840/* If we have an egress VLAN modification rule, we need to actually write the841 * delta between the input VLAN (from the key) and the output VLAN (from the842 * action), but the action was parsed first. So we need to patch the delta into843 * the action here.844 */845static int846ocelot_flower_patch_es0_vlan_modify(struct ocelot_vcap_filter *filter,847 struct netlink_ext_ack *extack)848{849 if (filter->block_id != VCAP_ES0 ||850 filter->action.tag_a_vid_sel != OCELOT_ES0_VID_PLUS_CLASSIFIED_VID)851 return 0;852 853 if (filter->vlan.vid.mask != VLAN_VID_MASK) {854 NL_SET_ERR_MSG_MOD(extack,855 "VCAP ES0 VLAN rewriting needs a full VLAN in the key");856 return -EOPNOTSUPP;857 }858 859 filter->action.vid_a_val -= filter->vlan.vid.value;860 filter->action.vid_a_val &= VLAN_VID_MASK;861 862 return 0;863}864 865int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,866 struct flow_cls_offload *f, bool ingress)867{868 struct netlink_ext_ack *extack = f->common.extack;869 struct ocelot_vcap_filter *filter;870 int chain = f->common.chain_index;871 int block_id, ret;872 873 if (chain && !ocelot_find_vcap_filter_that_points_at(ocelot, chain)) {874 NL_SET_ERR_MSG_MOD(extack, "No default GOTO action points to this chain");875 return -EOPNOTSUPP;876 }877 878 block_id = ocelot_chain_to_block(chain, ingress);879 if (block_id < 0) {880 NL_SET_ERR_MSG_MOD(extack, "Cannot offload to this chain");881 return -EOPNOTSUPP;882 }883 884 filter = ocelot_vcap_block_find_filter_by_id(&ocelot->block[block_id],885 f->cookie, true);886 if (filter) {887 /* Filter already exists on other ports */888 if (!ingress) {889 NL_SET_ERR_MSG_MOD(extack, "VCAP ES0 does not support shared filters");890 return -EOPNOTSUPP;891 }892 893 filter->ingress_port_mask |= BIT(port);894 895 return ocelot_vcap_filter_replace(ocelot, filter);896 }897 898 /* Filter didn't exist, create it now */899 filter = ocelot_vcap_filter_create(ocelot, port, ingress, f);900 if (!filter)901 return -ENOMEM;902 903 ret = ocelot_flower_parse(ocelot, port, ingress, f, filter);904 if (ret) {905 kfree(filter);906 return ret;907 }908 909 ret = ocelot_flower_patch_es0_vlan_modify(filter, extack);910 if (ret) {911 kfree(filter);912 return ret;913 }914 915 /* The non-optional GOTOs for the TCAM skeleton don't need916 * to be actually offloaded.917 */918 if (filter->type == OCELOT_VCAP_FILTER_DUMMY)919 return ocelot_vcap_dummy_filter_add(ocelot, filter);920 921 if (filter->type == OCELOT_PSFP_FILTER_OFFLOAD) {922 kfree(filter);923 if (ocelot->ops->psfp_filter_add)924 return ocelot->ops->psfp_filter_add(ocelot, port, f);925 926 NL_SET_ERR_MSG_MOD(extack, "PSFP chain is not supported in HW");927 return -EOPNOTSUPP;928 }929 930 return ocelot_vcap_filter_add(ocelot, filter, f->common.extack);931}932EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);933 934int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,935 struct flow_cls_offload *f, bool ingress)936{937 struct ocelot_vcap_filter *filter;938 struct ocelot_vcap_block *block;939 int block_id;940 941 block_id = ocelot_chain_to_block(f->common.chain_index, ingress);942 if (block_id < 0)943 return 0;944 945 if (block_id == PSFP_BLOCK_ID) {946 if (ocelot->ops->psfp_filter_del)947 return ocelot->ops->psfp_filter_del(ocelot, f);948 949 return -EOPNOTSUPP;950 }951 952 block = &ocelot->block[block_id];953 954 filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie, true);955 if (!filter)956 return 0;957 958 if (filter->type == OCELOT_VCAP_FILTER_DUMMY)959 return ocelot_vcap_dummy_filter_del(ocelot, filter);960 961 if (ingress) {962 filter->ingress_port_mask &= ~BIT(port);963 if (filter->ingress_port_mask)964 return ocelot_vcap_filter_replace(ocelot, filter);965 }966 967 return ocelot_vcap_filter_del(ocelot, filter);968}969EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy);970 971int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,972 struct flow_cls_offload *f, bool ingress)973{974 struct ocelot_vcap_filter *filter;975 struct ocelot_vcap_block *block;976 struct flow_stats stats = {0};977 int block_id, ret;978 979 block_id = ocelot_chain_to_block(f->common.chain_index, ingress);980 if (block_id < 0)981 return 0;982 983 if (block_id == PSFP_BLOCK_ID) {984 if (ocelot->ops->psfp_stats_get) {985 ret = ocelot->ops->psfp_stats_get(ocelot, f, &stats);986 if (ret)987 return ret;988 989 goto stats_update;990 }991 992 return -EOPNOTSUPP;993 }994 995 block = &ocelot->block[block_id];996 997 filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie, true);998 if (!filter || filter->type == OCELOT_VCAP_FILTER_DUMMY)999 return 0;1000 1001 ret = ocelot_vcap_filter_stats_update(ocelot, filter);1002 if (ret)1003 return ret;1004 1005 stats.pkts = filter->stats.pkts;1006 1007stats_update:1008 flow_stats_update(&f->stats, 0x0, stats.pkts, stats.drops, 0x0,1009 FLOW_ACTION_HW_STATS_IMMEDIATE);1010 return 0;1011}1012EXPORT_SYMBOL_GPL(ocelot_cls_flower_stats);1013