| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012 |
- // SPDX-License-Identifier: (GPL-2.0 OR MIT)
- /* Microsemi Ocelot Switch driver
- * Copyright (c) 2019 Microsemi Corporation
- */
- #include <net/pkt_cls.h>
- #include <net/tc_act/tc_gact.h>
- #include <soc/mscc/ocelot_vcap.h>
- #include "ocelot_police.h"
- #include "ocelot_vcap.h"
- /* Arbitrarily chosen constants for encoding the VCAP block and lookup number
- * into the chain number. This is UAPI.
- */
- #define VCAP_BLOCK 10000
- #define VCAP_LOOKUP 1000
- #define VCAP_IS1_NUM_LOOKUPS 3
- #define VCAP_IS2_NUM_LOOKUPS 2
- #define VCAP_IS2_NUM_PAG 256
- #define VCAP_IS1_CHAIN(lookup) \
- (1 * VCAP_BLOCK + (lookup) * VCAP_LOOKUP)
- #define VCAP_IS2_CHAIN(lookup, pag) \
- (2 * VCAP_BLOCK + (lookup) * VCAP_LOOKUP + (pag))
- /* PSFP chain and block ID */
- #define PSFP_BLOCK_ID OCELOT_NUM_VCAP_BLOCKS
- #define OCELOT_PSFP_CHAIN (3 * VCAP_BLOCK)
- static int ocelot_chain_to_block(int chain, bool ingress)
- {
- int lookup, pag;
- if (!ingress) {
- if (chain == 0)
- return VCAP_ES0;
- return -EOPNOTSUPP;
- }
- /* Backwards compatibility with older, single-chain tc-flower
- * offload support in Ocelot
- */
- if (chain == 0)
- return VCAP_IS2;
- for (lookup = 0; lookup < VCAP_IS1_NUM_LOOKUPS; lookup++)
- if (chain == VCAP_IS1_CHAIN(lookup))
- return VCAP_IS1;
- for (lookup = 0; lookup < VCAP_IS2_NUM_LOOKUPS; lookup++)
- for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)
- if (chain == VCAP_IS2_CHAIN(lookup, pag))
- return VCAP_IS2;
- if (chain == OCELOT_PSFP_CHAIN)
- return PSFP_BLOCK_ID;
- return -EOPNOTSUPP;
- }
- /* Caller must ensure this is a valid IS1 or IS2 chain first,
- * by calling ocelot_chain_to_block.
- */
- static int ocelot_chain_to_lookup(int chain)
- {
- /* Backwards compatibility with older, single-chain tc-flower
- * offload support in Ocelot
- */
- if (chain == 0)
- return 0;
- return (chain / VCAP_LOOKUP) % 10;
- }
- /* Caller must ensure this is a valid IS2 chain first,
- * by calling ocelot_chain_to_block.
- */
- static int ocelot_chain_to_pag(int chain)
- {
- int lookup;
- /* Backwards compatibility with older, single-chain tc-flower
- * offload support in Ocelot
- */
- if (chain == 0)
- return 0;
- lookup = ocelot_chain_to_lookup(chain);
- /* calculate PAG value as chain index relative to the first PAG */
- return chain - VCAP_IS2_CHAIN(lookup, 0);
- }
- static bool ocelot_is_goto_target_valid(int goto_target, int chain,
- bool ingress)
- {
- int pag;
- /* Can't offload GOTO in VCAP ES0 */
- if (!ingress)
- return (goto_target < 0);
- /* Non-optional GOTOs */
- if (chain == 0)
- /* VCAP IS1 can be skipped, either partially or completely */
- return (goto_target == VCAP_IS1_CHAIN(0) ||
- goto_target == VCAP_IS1_CHAIN(1) ||
- goto_target == VCAP_IS1_CHAIN(2) ||
- goto_target == VCAP_IS2_CHAIN(0, 0) ||
- goto_target == VCAP_IS2_CHAIN(1, 0) ||
- goto_target == OCELOT_PSFP_CHAIN);
- if (chain == VCAP_IS1_CHAIN(0))
- return (goto_target == VCAP_IS1_CHAIN(1));
- if (chain == VCAP_IS1_CHAIN(1))
- return (goto_target == VCAP_IS1_CHAIN(2));
- /* Lookup 2 of VCAP IS1 can really support non-optional GOTOs,
- * using a Policy Association Group (PAG) value, which is an 8-bit
- * value encoding a VCAP IS2 target chain.
- */
- if (chain == VCAP_IS1_CHAIN(2)) {
- for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)
- if (goto_target == VCAP_IS2_CHAIN(0, pag))
- return true;
- return false;
- }
- /* Non-optional GOTO from VCAP IS2 lookup 0 to lookup 1.
- * We cannot change the PAG at this point.
- */
- for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)
- if (chain == VCAP_IS2_CHAIN(0, pag))
- return (goto_target == VCAP_IS2_CHAIN(1, pag));
- /* VCAP IS2 lookup 1 can goto to PSFP block if hardware support */
- for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)
- if (chain == VCAP_IS2_CHAIN(1, pag))
- return (goto_target == OCELOT_PSFP_CHAIN);
- return false;
- }
- static struct ocelot_vcap_filter *
- ocelot_find_vcap_filter_that_points_at(struct ocelot *ocelot, int chain)
- {
- struct ocelot_vcap_filter *filter;
- struct ocelot_vcap_block *block;
- int block_id;
- block_id = ocelot_chain_to_block(chain, true);
- if (block_id < 0)
- return NULL;
- if (block_id == VCAP_IS2) {
- block = &ocelot->block[VCAP_IS1];
- list_for_each_entry(filter, &block->rules, list)
- if (filter->type == OCELOT_VCAP_FILTER_PAG &&
- filter->goto_target == chain)
- return filter;
- }
- list_for_each_entry(filter, &ocelot->dummy_rules, list)
- if (filter->goto_target == chain)
- return filter;
- return NULL;
- }
- static int
- ocelot_flower_parse_ingress_vlan_modify(struct ocelot *ocelot, int port,
- struct ocelot_vcap_filter *filter,
- const struct flow_action_entry *a,
- struct netlink_ext_ack *extack)
- {
- struct ocelot_port *ocelot_port = ocelot->ports[port];
- if (filter->goto_target != -1) {
- NL_SET_ERR_MSG_MOD(extack,
- "Last action must be GOTO");
- return -EOPNOTSUPP;
- }
- if (!ocelot_port->vlan_aware) {
- NL_SET_ERR_MSG_MOD(extack,
- "Can only modify VLAN under VLAN aware bridge");
- return -EOPNOTSUPP;
- }
- filter->action.vid_replace_ena = true;
- filter->action.pcp_dei_ena = true;
- filter->action.vid = a->vlan.vid;
- filter->action.pcp = a->vlan.prio;
- filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
- return 0;
- }
- static int
- ocelot_flower_parse_egress_vlan_modify(struct ocelot_vcap_filter *filter,
- const struct flow_action_entry *a,
- struct netlink_ext_ack *extack)
- {
- enum ocelot_tag_tpid_sel tpid;
- switch (ntohs(a->vlan.proto)) {
- case ETH_P_8021Q:
- tpid = OCELOT_TAG_TPID_SEL_8021Q;
- break;
- case ETH_P_8021AD:
- tpid = OCELOT_TAG_TPID_SEL_8021AD;
- break;
- default:
- NL_SET_ERR_MSG_MOD(extack,
- "Cannot modify custom TPID");
- return -EOPNOTSUPP;
- }
- filter->action.tag_a_tpid_sel = tpid;
- filter->action.push_outer_tag = OCELOT_ES0_TAG;
- filter->action.tag_a_vid_sel = OCELOT_ES0_VID_PLUS_CLASSIFIED_VID;
- filter->action.vid_a_val = a->vlan.vid;
- filter->action.pcp_a_val = a->vlan.prio;
- filter->action.tag_a_pcp_sel = OCELOT_ES0_PCP;
- filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
- return 0;
- }
- static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,
- bool ingress, struct flow_cls_offload *f,
- struct ocelot_vcap_filter *filter)
- {
- const struct flow_action *action = &f->rule->action;
- struct netlink_ext_ack *extack = f->common.extack;
- bool allow_missing_goto_target = false;
- const struct flow_action_entry *a;
- enum ocelot_tag_tpid_sel tpid;
- int i, chain, egress_port;
- u32 pol_ix, pol_max;
- u64 rate;
- int err;
- if (!flow_action_basic_hw_stats_check(&f->rule->action,
- f->common.extack))
- return -EOPNOTSUPP;
- chain = f->common.chain_index;
- filter->block_id = ocelot_chain_to_block(chain, ingress);
- if (filter->block_id < 0) {
- NL_SET_ERR_MSG_MOD(extack, "Cannot offload to this chain");
- return -EOPNOTSUPP;
- }
- if (filter->block_id == VCAP_IS1 || filter->block_id == VCAP_IS2)
- filter->lookup = ocelot_chain_to_lookup(chain);
- if (filter->block_id == VCAP_IS2)
- filter->pag = ocelot_chain_to_pag(chain);
- filter->goto_target = -1;
- filter->type = OCELOT_VCAP_FILTER_DUMMY;
- flow_action_for_each(i, a, action) {
- switch (a->id) {
- case FLOW_ACTION_DROP:
- if (filter->block_id != VCAP_IS2) {
- NL_SET_ERR_MSG_MOD(extack,
- "Drop action can only be offloaded to VCAP IS2");
- return -EOPNOTSUPP;
- }
- if (filter->goto_target != -1) {
- NL_SET_ERR_MSG_MOD(extack,
- "Last action must be GOTO");
- return -EOPNOTSUPP;
- }
- filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
- filter->action.port_mask = 0;
- filter->action.police_ena = true;
- filter->action.pol_ix = OCELOT_POLICER_DISCARD;
- filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
- break;
- case FLOW_ACTION_ACCEPT:
- if (filter->block_id != VCAP_ES0 &&
- filter->block_id != VCAP_IS1 &&
- filter->block_id != VCAP_IS2) {
- NL_SET_ERR_MSG_MOD(extack,
- "Accept action can only be offloaded to VCAP chains");
- return -EOPNOTSUPP;
- }
- if (filter->block_id != VCAP_ES0 &&
- filter->goto_target != -1) {
- NL_SET_ERR_MSG_MOD(extack,
- "Last action must be GOTO");
- return -EOPNOTSUPP;
- }
- filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
- break;
- case FLOW_ACTION_TRAP:
- if (filter->block_id != VCAP_IS2 ||
- filter->lookup != 0) {
- NL_SET_ERR_MSG_MOD(extack,
- "Trap action can only be offloaded to VCAP IS2 lookup 0");
- return -EOPNOTSUPP;
- }
- if (filter->goto_target != -1) {
- NL_SET_ERR_MSG_MOD(extack,
- "Last action must be GOTO");
- return -EOPNOTSUPP;
- }
- filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
- filter->action.port_mask = 0;
- filter->action.cpu_copy_ena = true;
- filter->action.cpu_qu_num = 0;
- filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
- filter->is_trap = true;
- break;
- case FLOW_ACTION_POLICE:
- if (filter->block_id == PSFP_BLOCK_ID) {
- filter->type = OCELOT_PSFP_FILTER_OFFLOAD;
- break;
- }
- if (filter->block_id != VCAP_IS2 ||
- filter->lookup != 0) {
- NL_SET_ERR_MSG_MOD(extack,
- "Police action can only be offloaded to VCAP IS2 lookup 0 or PSFP");
- return -EOPNOTSUPP;
- }
- if (filter->goto_target != -1) {
- NL_SET_ERR_MSG_MOD(extack,
- "Last action must be GOTO");
- return -EOPNOTSUPP;
- }
- err = ocelot_policer_validate(action, a, extack);
- if (err)
- return err;
- filter->action.police_ena = true;
- pol_ix = a->hw_index + ocelot->vcap_pol.base;
- pol_max = ocelot->vcap_pol.max;
- if (ocelot->vcap_pol.max2 && pol_ix > pol_max) {
- pol_ix += ocelot->vcap_pol.base2 - pol_max - 1;
- pol_max = ocelot->vcap_pol.max2;
- }
- if (pol_ix >= pol_max)
- return -EINVAL;
- filter->action.pol_ix = pol_ix;
- rate = a->police.rate_bytes_ps;
- filter->action.pol.rate = div_u64(rate, 1000) * 8;
- filter->action.pol.burst = a->police.burst;
- filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
- break;
- case FLOW_ACTION_REDIRECT:
- if (filter->block_id != VCAP_IS2) {
- NL_SET_ERR_MSG_MOD(extack,
- "Redirect action can only be offloaded to VCAP IS2");
- return -EOPNOTSUPP;
- }
- if (filter->goto_target != -1) {
- NL_SET_ERR_MSG_MOD(extack,
- "Last action must be GOTO");
- return -EOPNOTSUPP;
- }
- egress_port = ocelot->ops->netdev_to_port(a->dev);
- if (egress_port < 0) {
- NL_SET_ERR_MSG_MOD(extack,
- "Destination not an ocelot port");
- return -EOPNOTSUPP;
- }
- filter->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;
- filter->action.port_mask = BIT(egress_port);
- filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
- break;
- case FLOW_ACTION_MIRRED:
- if (filter->block_id != VCAP_IS2) {
- NL_SET_ERR_MSG_MOD(extack,
- "Mirror action can only be offloaded to VCAP IS2");
- return -EOPNOTSUPP;
- }
- if (filter->goto_target != -1) {
- NL_SET_ERR_MSG_MOD(extack,
- "Last action must be GOTO");
- return -EOPNOTSUPP;
- }
- egress_port = ocelot->ops->netdev_to_port(a->dev);
- if (egress_port < 0) {
- NL_SET_ERR_MSG_MOD(extack,
- "Destination not an ocelot port");
- return -EOPNOTSUPP;
- }
- filter->egress_port.value = egress_port;
- filter->action.mirror_ena = true;
- filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
- break;
- case FLOW_ACTION_VLAN_POP:
- if (filter->block_id != VCAP_IS1) {
- NL_SET_ERR_MSG_MOD(extack,
- "VLAN pop action can only be offloaded to VCAP IS1");
- return -EOPNOTSUPP;
- }
- if (filter->goto_target != -1) {
- NL_SET_ERR_MSG_MOD(extack,
- "Last action must be GOTO");
- return -EOPNOTSUPP;
- }
- filter->action.vlan_pop_cnt_ena = true;
- filter->action.vlan_pop_cnt++;
- if (filter->action.vlan_pop_cnt > 2) {
- NL_SET_ERR_MSG_MOD(extack,
- "Cannot pop more than 2 VLAN headers");
- return -EOPNOTSUPP;
- }
- filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
- break;
- case FLOW_ACTION_VLAN_MANGLE:
- if (filter->block_id == VCAP_IS1) {
- err = ocelot_flower_parse_ingress_vlan_modify(ocelot, port,
- filter, a,
- extack);
- } else if (filter->block_id == VCAP_ES0) {
- err = ocelot_flower_parse_egress_vlan_modify(filter, a,
- extack);
- } else {
- NL_SET_ERR_MSG_MOD(extack,
- "VLAN modify action can only be offloaded to VCAP IS1 or ES0");
- err = -EOPNOTSUPP;
- }
- if (err)
- return err;
- break;
- case FLOW_ACTION_PRIORITY:
- if (filter->block_id != VCAP_IS1) {
- NL_SET_ERR_MSG_MOD(extack,
- "Priority action can only be offloaded to VCAP IS1");
- return -EOPNOTSUPP;
- }
- if (filter->goto_target != -1) {
- NL_SET_ERR_MSG_MOD(extack,
- "Last action must be GOTO");
- return -EOPNOTSUPP;
- }
- filter->action.qos_ena = true;
- filter->action.qos_val = a->priority;
- filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
- break;
- case FLOW_ACTION_GOTO:
- filter->goto_target = a->chain_index;
- if (filter->block_id == VCAP_IS1 && filter->lookup == 2) {
- int pag = ocelot_chain_to_pag(filter->goto_target);
- filter->action.pag_override_mask = 0xff;
- filter->action.pag_val = pag;
- filter->type = OCELOT_VCAP_FILTER_PAG;
- }
- break;
- case FLOW_ACTION_VLAN_PUSH:
- if (filter->block_id != VCAP_ES0) {
- NL_SET_ERR_MSG_MOD(extack,
- "VLAN push action can only be offloaded to VCAP ES0");
- return -EOPNOTSUPP;
- }
- switch (ntohs(a->vlan.proto)) {
- case ETH_P_8021Q:
- tpid = OCELOT_TAG_TPID_SEL_8021Q;
- break;
- case ETH_P_8021AD:
- tpid = OCELOT_TAG_TPID_SEL_8021AD;
- break;
- default:
- NL_SET_ERR_MSG_MOD(extack,
- "Cannot push custom TPID");
- return -EOPNOTSUPP;
- }
- filter->action.tag_a_tpid_sel = tpid;
- filter->action.push_outer_tag = OCELOT_ES0_TAG;
- filter->action.tag_a_vid_sel = OCELOT_ES0_VID;
- filter->action.vid_a_val = a->vlan.vid;
- filter->action.pcp_a_val = a->vlan.prio;
- filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
- break;
- case FLOW_ACTION_GATE:
- if (filter->block_id != PSFP_BLOCK_ID) {
- NL_SET_ERR_MSG_MOD(extack,
- "Gate action can only be offloaded to PSFP chain");
- return -EOPNOTSUPP;
- }
- filter->type = OCELOT_PSFP_FILTER_OFFLOAD;
- break;
- default:
- NL_SET_ERR_MSG_MOD(extack, "Cannot offload action");
- return -EOPNOTSUPP;
- }
- }
- if (filter->goto_target == -1) {
- if ((filter->block_id == VCAP_IS2 && filter->lookup == 1) ||
- chain == 0 || filter->block_id == PSFP_BLOCK_ID) {
- allow_missing_goto_target = true;
- } else {
- NL_SET_ERR_MSG_MOD(extack, "Missing GOTO action");
- return -EOPNOTSUPP;
- }
- }
- if (!ocelot_is_goto_target_valid(filter->goto_target, chain, ingress) &&
- !allow_missing_goto_target) {
- NL_SET_ERR_MSG_MOD(extack, "Cannot offload this GOTO target");
- return -EOPNOTSUPP;
- }
- return 0;
- }
- static int ocelot_flower_parse_indev(struct ocelot *ocelot, int port,
- struct flow_cls_offload *f,
- struct ocelot_vcap_filter *filter)
- {
- struct flow_rule *rule = flow_cls_offload_flow_rule(f);
- const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
- int key_length = vcap->keys[VCAP_ES0_IGR_PORT].length;
- struct netlink_ext_ack *extack = f->common.extack;
- struct net_device *dev, *indev;
- struct flow_match_meta match;
- int ingress_port;
- flow_rule_match_meta(rule, &match);
- if (!match.mask->ingress_ifindex)
- return 0;
- if (match.mask->ingress_ifindex != 0xFFFFFFFF) {
- NL_SET_ERR_MSG_MOD(extack, "Unsupported ingress ifindex mask");
- return -EOPNOTSUPP;
- }
- dev = ocelot->ops->port_to_netdev(ocelot, port);
- if (!dev)
- return -EINVAL;
- indev = __dev_get_by_index(dev_net(dev), match.key->ingress_ifindex);
- if (!indev) {
- NL_SET_ERR_MSG_MOD(extack,
- "Can't find the ingress port to match on");
- return -ENOENT;
- }
- ingress_port = ocelot->ops->netdev_to_port(indev);
- if (ingress_port < 0) {
- NL_SET_ERR_MSG_MOD(extack,
- "Can only offload an ocelot ingress port");
- return -EOPNOTSUPP;
- }
- if (ingress_port == port) {
- NL_SET_ERR_MSG_MOD(extack,
- "Ingress port is equal to the egress port");
- return -EINVAL;
- }
- filter->ingress_port.value = ingress_port;
- filter->ingress_port.mask = GENMASK(key_length - 1, 0);
- return 0;
- }
- static int
- ocelot_flower_parse_key(struct ocelot *ocelot, int port, bool ingress,
- struct flow_cls_offload *f,
- struct ocelot_vcap_filter *filter)
- {
- struct flow_rule *rule = flow_cls_offload_flow_rule(f);
- struct flow_dissector *dissector = rule->match.dissector;
- struct netlink_ext_ack *extack = f->common.extack;
- u16 proto = ntohs(f->common.protocol);
- bool match_protocol = true;
- int ret;
- if (dissector->used_keys &
- ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
- BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
- BIT_ULL(FLOW_DISSECTOR_KEY_META) |
- BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) |
- BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS))) {
- return -EOPNOTSUPP;
- }
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META)) {
- struct flow_match_meta match;
- flow_rule_match_meta(rule, &match);
- if (match.mask->l2_miss) {
- NL_SET_ERR_MSG_MOD(extack, "Can't match on \"l2_miss\"");
- return -EOPNOTSUPP;
- }
- }
- /* For VCAP ES0 (egress rewriter) we can match on the ingress port */
- if (!ingress) {
- ret = ocelot_flower_parse_indev(ocelot, port, f, filter);
- if (ret)
- return ret;
- }
- if (flow_rule_match_has_control_flags(rule, extack))
- return -EOPNOTSUPP;
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
- struct flow_match_vlan match;
- flow_rule_match_vlan(rule, &match);
- filter->key_type = OCELOT_VCAP_KEY_ANY;
- filter->vlan.vid.value = match.key->vlan_id;
- filter->vlan.vid.mask = match.mask->vlan_id;
- filter->vlan.pcp.value[0] = match.key->vlan_priority;
- filter->vlan.pcp.mask[0] = match.mask->vlan_priority;
- match_protocol = false;
- }
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
- struct flow_match_eth_addrs match;
- if (filter->block_id == VCAP_ES0) {
- NL_SET_ERR_MSG_MOD(extack,
- "VCAP ES0 cannot match on MAC address");
- return -EOPNOTSUPP;
- }
- /* The hw support mac matches only for MAC_ETYPE key,
- * therefore if other matches(port, tcp flags, etc) are added
- * then just bail out
- */
- if ((dissector->used_keys &
- (BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
- BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL))) !=
- (BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
- BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL)))
- return -EOPNOTSUPP;
- flow_rule_match_eth_addrs(rule, &match);
- if (filter->block_id == VCAP_IS1 &&
- !is_zero_ether_addr(match.mask->dst)) {
- NL_SET_ERR_MSG_MOD(extack,
- "Key type S1_NORMAL cannot match on destination MAC");
- return -EOPNOTSUPP;
- }
- filter->key_type = OCELOT_VCAP_KEY_ETYPE;
- ether_addr_copy(filter->key.etype.dmac.value,
- match.key->dst);
- ether_addr_copy(filter->key.etype.smac.value,
- match.key->src);
- ether_addr_copy(filter->key.etype.dmac.mask,
- match.mask->dst);
- ether_addr_copy(filter->key.etype.smac.mask,
- match.mask->src);
- goto finished_key_parsing;
- }
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
- struct flow_match_basic match;
- flow_rule_match_basic(rule, &match);
- if (ntohs(match.key->n_proto) == ETH_P_IP) {
- if (filter->block_id == VCAP_ES0) {
- NL_SET_ERR_MSG_MOD(extack,
- "VCAP ES0 cannot match on IP protocol");
- return -EOPNOTSUPP;
- }
- filter->key_type = OCELOT_VCAP_KEY_IPV4;
- filter->key.ipv4.proto.value[0] =
- match.key->ip_proto;
- filter->key.ipv4.proto.mask[0] =
- match.mask->ip_proto;
- match_protocol = false;
- }
- if (ntohs(match.key->n_proto) == ETH_P_IPV6) {
- if (filter->block_id == VCAP_ES0) {
- NL_SET_ERR_MSG_MOD(extack,
- "VCAP ES0 cannot match on IP protocol");
- return -EOPNOTSUPP;
- }
- filter->key_type = OCELOT_VCAP_KEY_IPV6;
- filter->key.ipv6.proto.value[0] =
- match.key->ip_proto;
- filter->key.ipv6.proto.mask[0] =
- match.mask->ip_proto;
- match_protocol = false;
- }
- }
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS) &&
- proto == ETH_P_IP) {
- struct flow_match_ipv4_addrs match;
- u8 *tmp;
- if (filter->block_id == VCAP_ES0) {
- NL_SET_ERR_MSG_MOD(extack,
- "VCAP ES0 cannot match on IP address");
- return -EOPNOTSUPP;
- }
- flow_rule_match_ipv4_addrs(rule, &match);
- if (filter->block_id == VCAP_IS1 && *(u32 *)&match.mask->dst) {
- NL_SET_ERR_MSG_MOD(extack,
- "Key type S1_NORMAL cannot match on destination IP");
- return -EOPNOTSUPP;
- }
- tmp = &filter->key.ipv4.sip.value.addr[0];
- memcpy(tmp, &match.key->src, 4);
- tmp = &filter->key.ipv4.sip.mask.addr[0];
- memcpy(tmp, &match.mask->src, 4);
- tmp = &filter->key.ipv4.dip.value.addr[0];
- memcpy(tmp, &match.key->dst, 4);
- tmp = &filter->key.ipv4.dip.mask.addr[0];
- memcpy(tmp, &match.mask->dst, 4);
- match_protocol = false;
- }
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS) &&
- proto == ETH_P_IPV6) {
- return -EOPNOTSUPP;
- }
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
- struct flow_match_ports match;
- if (filter->block_id == VCAP_ES0) {
- NL_SET_ERR_MSG_MOD(extack,
- "VCAP ES0 cannot match on L4 ports");
- return -EOPNOTSUPP;
- }
- flow_rule_match_ports(rule, &match);
- filter->key.ipv4.sport.value = ntohs(match.key->src);
- filter->key.ipv4.sport.mask = ntohs(match.mask->src);
- filter->key.ipv4.dport.value = ntohs(match.key->dst);
- filter->key.ipv4.dport.mask = ntohs(match.mask->dst);
- match_protocol = false;
- }
- finished_key_parsing:
- if (match_protocol && proto != ETH_P_ALL) {
- if (filter->block_id == VCAP_ES0) {
- NL_SET_ERR_MSG_MOD(extack,
- "VCAP ES0 cannot match on L2 proto");
- return -EOPNOTSUPP;
- }
- /* TODO: support SNAP, LLC etc */
- if (proto < ETH_P_802_3_MIN)
- return -EOPNOTSUPP;
- filter->key_type = OCELOT_VCAP_KEY_ETYPE;
- *(__be16 *)filter->key.etype.etype.value = htons(proto);
- *(__be16 *)filter->key.etype.etype.mask = htons(0xffff);
- }
- /* else, a filter of type OCELOT_VCAP_KEY_ANY is implicitly added */
- return 0;
- }
- static int ocelot_flower_parse(struct ocelot *ocelot, int port, bool ingress,
- struct flow_cls_offload *f,
- struct ocelot_vcap_filter *filter)
- {
- int ret;
- filter->prio = f->common.prio;
- filter->id.cookie = f->cookie;
- filter->id.tc_offload = true;
- ret = ocelot_flower_parse_action(ocelot, port, ingress, f, filter);
- if (ret)
- return ret;
- /* PSFP filter need to parse key by stream identification function. */
- if (filter->type == OCELOT_PSFP_FILTER_OFFLOAD)
- return 0;
- return ocelot_flower_parse_key(ocelot, port, ingress, f, filter);
- }
- static struct ocelot_vcap_filter
- *ocelot_vcap_filter_create(struct ocelot *ocelot, int port, bool ingress,
- struct flow_cls_offload *f)
- {
- struct ocelot_vcap_filter *filter;
- filter = kzalloc(sizeof(*filter), GFP_KERNEL);
- if (!filter)
- return NULL;
- if (ingress) {
- filter->ingress_port_mask = BIT(port);
- } else {
- const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
- int key_length = vcap->keys[VCAP_ES0_EGR_PORT].length;
- filter->egress_port.value = port;
- filter->egress_port.mask = GENMASK(key_length - 1, 0);
- }
- return filter;
- }
- static int ocelot_vcap_dummy_filter_add(struct ocelot *ocelot,
- struct ocelot_vcap_filter *filter)
- {
- list_add(&filter->list, &ocelot->dummy_rules);
- return 0;
- }
- static int ocelot_vcap_dummy_filter_del(struct ocelot *ocelot,
- struct ocelot_vcap_filter *filter)
- {
- list_del(&filter->list);
- kfree(filter);
- return 0;
- }
- /* If we have an egress VLAN modification rule, we need to actually write the
- * delta between the input VLAN (from the key) and the output VLAN (from the
- * action), but the action was parsed first. So we need to patch the delta into
- * the action here.
- */
- static int
- ocelot_flower_patch_es0_vlan_modify(struct ocelot_vcap_filter *filter,
- struct netlink_ext_ack *extack)
- {
- if (filter->block_id != VCAP_ES0 ||
- filter->action.tag_a_vid_sel != OCELOT_ES0_VID_PLUS_CLASSIFIED_VID)
- return 0;
- if (filter->vlan.vid.mask != VLAN_VID_MASK) {
- NL_SET_ERR_MSG_MOD(extack,
- "VCAP ES0 VLAN rewriting needs a full VLAN in the key");
- return -EOPNOTSUPP;
- }
- filter->action.vid_a_val -= filter->vlan.vid.value;
- filter->action.vid_a_val &= VLAN_VID_MASK;
- return 0;
- }
- int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
- struct flow_cls_offload *f, bool ingress)
- {
- struct netlink_ext_ack *extack = f->common.extack;
- struct ocelot_vcap_filter *filter;
- int chain = f->common.chain_index;
- int block_id, ret;
- if (chain && !ocelot_find_vcap_filter_that_points_at(ocelot, chain)) {
- NL_SET_ERR_MSG_MOD(extack, "No default GOTO action points to this chain");
- return -EOPNOTSUPP;
- }
- block_id = ocelot_chain_to_block(chain, ingress);
- if (block_id < 0) {
- NL_SET_ERR_MSG_MOD(extack, "Cannot offload to this chain");
- return -EOPNOTSUPP;
- }
- filter = ocelot_vcap_block_find_filter_by_id(&ocelot->block[block_id],
- f->cookie, true);
- if (filter) {
- /* Filter already exists on other ports */
- if (!ingress) {
- NL_SET_ERR_MSG_MOD(extack, "VCAP ES0 does not support shared filters");
- return -EOPNOTSUPP;
- }
- filter->ingress_port_mask |= BIT(port);
- return ocelot_vcap_filter_replace(ocelot, filter);
- }
- /* Filter didn't exist, create it now */
- filter = ocelot_vcap_filter_create(ocelot, port, ingress, f);
- if (!filter)
- return -ENOMEM;
- ret = ocelot_flower_parse(ocelot, port, ingress, f, filter);
- if (ret) {
- kfree(filter);
- return ret;
- }
- ret = ocelot_flower_patch_es0_vlan_modify(filter, extack);
- if (ret) {
- kfree(filter);
- return ret;
- }
- /* The non-optional GOTOs for the TCAM skeleton don't need
- * to be actually offloaded.
- */
- if (filter->type == OCELOT_VCAP_FILTER_DUMMY)
- return ocelot_vcap_dummy_filter_add(ocelot, filter);
- if (filter->type == OCELOT_PSFP_FILTER_OFFLOAD) {
- kfree(filter);
- if (ocelot->ops->psfp_filter_add)
- return ocelot->ops->psfp_filter_add(ocelot, port, f);
- NL_SET_ERR_MSG_MOD(extack, "PSFP chain is not supported in HW");
- return -EOPNOTSUPP;
- }
- return ocelot_vcap_filter_add(ocelot, filter, f->common.extack);
- }
- EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);
- int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
- struct flow_cls_offload *f, bool ingress)
- {
- struct ocelot_vcap_filter *filter;
- struct ocelot_vcap_block *block;
- int block_id;
- block_id = ocelot_chain_to_block(f->common.chain_index, ingress);
- if (block_id < 0)
- return 0;
- if (block_id == PSFP_BLOCK_ID) {
- if (ocelot->ops->psfp_filter_del)
- return ocelot->ops->psfp_filter_del(ocelot, f);
- return -EOPNOTSUPP;
- }
- block = &ocelot->block[block_id];
- filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie, true);
- if (!filter)
- return 0;
- if (filter->type == OCELOT_VCAP_FILTER_DUMMY)
- return ocelot_vcap_dummy_filter_del(ocelot, filter);
- if (ingress) {
- filter->ingress_port_mask &= ~BIT(port);
- if (filter->ingress_port_mask)
- return ocelot_vcap_filter_replace(ocelot, filter);
- }
- return ocelot_vcap_filter_del(ocelot, filter);
- }
- EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy);
- int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,
- struct flow_cls_offload *f, bool ingress)
- {
- struct ocelot_vcap_filter *filter;
- struct ocelot_vcap_block *block;
- struct flow_stats stats = {0};
- int block_id, ret;
- block_id = ocelot_chain_to_block(f->common.chain_index, ingress);
- if (block_id < 0)
- return 0;
- if (block_id == PSFP_BLOCK_ID) {
- if (ocelot->ops->psfp_stats_get) {
- ret = ocelot->ops->psfp_stats_get(ocelot, f, &stats);
- if (ret)
- return ret;
- goto stats_update;
- }
- return -EOPNOTSUPP;
- }
- block = &ocelot->block[block_id];
- filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie, true);
- if (!filter || filter->type == OCELOT_VCAP_FILTER_DUMMY)
- return 0;
- ret = ocelot_vcap_filter_stats_update(ocelot, filter);
- if (ret)
- return ret;
- stats.pkts = filter->stats.pkts;
- stats_update:
- flow_stats_update(&f->stats, 0x0, stats.pkts, stats.drops, 0x0,
- FLOW_ACTION_HW_STATS_IMMEDIATE);
- return 0;
- }
- EXPORT_SYMBOL_GPL(ocelot_cls_flower_stats);
|