/* 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)
staticint 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.
*/ staticint 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.
*/ staticint 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);
}
staticbool 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)) returntrue;
returnfalse;
}
/* 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);
returnfalse;
}
staticstruct 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];
/* Mirroring towards foreign interfaces is handled in software */ if (egress_port < 0 || a->id != offloadable_act_id) { if (f->common.skip_sw) {
NL_SET_ERR_MSG_FMT(extack, "Can only %s to %s if filter also runs in software",
act_string, egress_port < 0 ? "CPU" : "ingress of ocelot port"); return -EOPNOTSUPP;
}
egress_port = ocelot->num_phys_ports;
}
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: case FLOW_ACTION_REDIRECT_INGRESS: 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_flower_parse_egress_port(ocelot, f,
a, false,
extack); if (egress_port < 0) return egress_port;
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: case FLOW_ACTION_MIRRED_INGRESS: 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_flower_parse_egress_port(ocelot, f,
a, true,
extack); if (egress_port < 0) return egress_port;
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);
} elseif (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;
}
}
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;
}
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;
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;
}
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;
}
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;
}
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;
}
staticint ocelot_flower_parse(struct ocelot *ocelot, int port, bool ingress, struct flow_cls_offload *f, struct ocelot_vcap_filter *filter)
{ int ret;
/* 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.
*/ staticint
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;
}
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 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;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.