Commit ebf44ded authored by Steen Hegelund's avatar Steen Hegelund Committed by Paolo Abeni

net: microchip: sparx5: Add TC vlan action support for the ES0 VCAP

This provides these 3 actions for rule in the ES0 VCAP:

- action vlan pop
- action vlan modify id X priority Y
- action vlan push id X priority Y protocol Z
Signed-off-by: default avatarSteen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 52b28a93
......@@ -29,6 +29,72 @@ enum SPX5_FORWARDING_SEL {
SPX5_FWSEL_DISCARD,
};
/* Controls tag A (outer tagging) */
enum SPX5_OUTER_TAG_SEL {
SPX5_OTAG_PORT,
SPX5_OTAG_TAG_A,
SPX5_OTAG_FORCED_PORT,
SPX5_OTAG_UNTAG,
};
/* Selects TPID for ES0 tag A */
enum SPX5_TPID_A_SEL {
SPX5_TPID_A_8100,
SPX5_TPID_A_88A8,
SPX5_TPID_A_CUST1,
SPX5_TPID_A_CUST2,
SPX5_TPID_A_CUST3,
SPX5_TPID_A_CLASSIFIED,
};
/* Selects VID for ES0 tag A */
enum SPX5_VID_A_SEL {
SPX5_VID_A_CLASSIFIED,
SPX5_VID_A_VAL,
SPX5_VID_A_IFH,
SPX5_VID_A_RESERVED,
};
/* Select PCP source for ES0 tag A */
enum SPX5_PCP_A_SEL {
SPX5_PCP_A_CLASSIFIED,
SPX5_PCP_A_VAL,
SPX5_PCP_A_RESERVED,
SPX5_PCP_A_POPPED,
SPX5_PCP_A_MAPPED_0,
SPX5_PCP_A_MAPPED_1,
SPX5_PCP_A_MAPPED_2,
SPX5_PCP_A_MAPPED_3,
};
/* Select DEI source for ES0 tag A */
enum SPX5_DEI_A_SEL {
SPX5_DEI_A_CLASSIFIED,
SPX5_DEI_A_VAL,
SPX5_DEI_A_REW,
SPX5_DEI_A_POPPED,
SPX5_DEI_A_MAPPED_0,
SPX5_DEI_A_MAPPED_1,
SPX5_DEI_A_MAPPED_2,
SPX5_DEI_A_MAPPED_3,
};
/* Controls tag B (inner tagging) */
enum SPX5_INNER_TAG_SEL {
SPX5_ITAG_NO_PUSH,
SPX5_ITAG_PUSH_B_TAG,
};
/* Selects TPID for ES0 tag B. */
enum SPX5_TPID_B_SEL {
SPX5_TPID_B_8100,
SPX5_TPID_B_88A8,
SPX5_TPID_B_CUST1,
SPX5_TPID_B_CUST2,
SPX5_TPID_B_CUST3,
SPX5_TPID_B_CLASSIFIED,
};
int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type,
void *type_data);
......
......@@ -28,6 +28,31 @@ struct sparx5_multiple_rules {
struct sparx5_wildcard_rule rule[SPX5_MAX_RULE_SIZE];
};
static int
sparx5_tc_flower_es0_tpid(struct vcap_tc_flower_parse_usage *st)
{
int err = 0;
switch (st->tpid) {
case ETH_P_8021Q:
err = vcap_rule_add_key_u32(st->vrule,
VCAP_KF_8021Q_TPID,
SPX5_TPID_SEL_8100, ~0);
break;
case ETH_P_8021AD:
err = vcap_rule_add_key_u32(st->vrule,
VCAP_KF_8021Q_TPID,
SPX5_TPID_SEL_88A8, ~0);
break;
default:
NL_SET_ERR_MSG_MOD(st->fco->common.extack,
"Invalid vlan proto");
err = -EINVAL;
break;
}
return err;
}
static int
sparx5_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st)
{
......@@ -168,13 +193,21 @@ sparx5_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st)
{
enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS;
enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS;
int err;
if (st->admin->vtype == VCAP_TYPE_IS0) {
vid_key = VCAP_KF_8021Q_VID0;
pcp_key = VCAP_KF_8021Q_PCP0;
}
return vcap_tc_flower_handler_vlan_usage(st, vid_key, pcp_key);
err = vcap_tc_flower_handler_vlan_usage(st, vid_key, pcp_key);
if (err)
return err;
if (st->admin->vtype == VCAP_TYPE_ES0 && st->tpid)
err = sparx5_tc_flower_es0_tpid(st);
return err;
}
static int (*sparx5_tc_flower_usage_handlers[])(struct vcap_tc_flower_parse_usage *st) = {
......@@ -191,38 +224,28 @@ static int (*sparx5_tc_flower_usage_handlers[])(struct vcap_tc_flower_parse_usag
[FLOW_DISSECTOR_KEY_IP] = vcap_tc_flower_handler_ip_usage,
};
static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco,
static int sparx5_tc_use_dissectors(struct vcap_tc_flower_parse_usage *st,
struct vcap_admin *admin,
struct vcap_rule *vrule,
u16 *l3_proto)
struct vcap_rule *vrule)
{
struct vcap_tc_flower_parse_usage state = {
.fco = fco,
.vrule = vrule,
.l3_proto = ETH_P_ALL,
.admin = admin,
};
int idx, err = 0;
state.frule = flow_cls_offload_flow_rule(fco);
for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) {
if (!flow_rule_match_key(state.frule, idx))
if (!flow_rule_match_key(st->frule, idx))
continue;
if (!sparx5_tc_flower_usage_handlers[idx])
continue;
err = sparx5_tc_flower_usage_handlers[idx](&state);
err = sparx5_tc_flower_usage_handlers[idx](st);
if (err)
return err;
}
if (state.frule->match.dissector->used_keys ^ state.used_keys) {
NL_SET_ERR_MSG_MOD(fco->common.extack,
if (st->frule->match.dissector->used_keys ^ st->used_keys) {
NL_SET_ERR_MSG_MOD(st->fco->common.extack,
"Unsupported match item");
return -ENOENT;
}
if (l3_proto)
*l3_proto = state.l3_proto;
return err;
}
......@@ -281,6 +304,27 @@ static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
return -EOPNOTSUPP;
}
if (action_mask & BIT(FLOW_ACTION_VLAN_PUSH) &&
action_mask & BIT(FLOW_ACTION_VLAN_POP)) {
NL_SET_ERR_MSG_MOD(fco->common.extack,
"Cannot combine vlan push and pop action");
return -EOPNOTSUPP;
}
if (action_mask & BIT(FLOW_ACTION_VLAN_PUSH) &&
action_mask & BIT(FLOW_ACTION_VLAN_MANGLE)) {
NL_SET_ERR_MSG_MOD(fco->common.extack,
"Cannot combine vlan push and modify action");
return -EOPNOTSUPP;
}
if (action_mask & BIT(FLOW_ACTION_VLAN_POP) &&
action_mask & BIT(FLOW_ACTION_VLAN_MANGLE)) {
NL_SET_ERR_MSG_MOD(fco->common.extack,
"Cannot combine vlan pop and modify action");
return -EOPNOTSUPP;
}
return 0;
}
......@@ -801,6 +845,157 @@ static int sparx5_tc_action_trap(struct vcap_admin *admin,
return err;
}
static int sparx5_tc_action_vlan_pop(struct vcap_admin *admin,
struct vcap_rule *vrule,
struct flow_cls_offload *fco,
u16 tpid)
{
int err = 0;
switch (admin->vtype) {
case VCAP_TYPE_ES0:
break;
default:
NL_SET_ERR_MSG_MOD(fco->common.extack,
"VLAN pop action not supported in this VCAP");
return -EOPNOTSUPP;
}
switch (tpid) {
case ETH_P_8021Q:
case ETH_P_8021AD:
err = vcap_rule_add_action_u32(vrule,
VCAP_AF_PUSH_OUTER_TAG,
SPX5_OTAG_UNTAG);
break;
default:
NL_SET_ERR_MSG_MOD(fco->common.extack,
"Invalid vlan proto");
err = -EINVAL;
}
return err;
}
static int sparx5_tc_action_vlan_modify(struct vcap_admin *admin,
struct vcap_rule *vrule,
struct flow_cls_offload *fco,
struct flow_action_entry *act,
u16 tpid)
{
int err = 0;
switch (admin->vtype) {
case VCAP_TYPE_ES0:
err = vcap_rule_add_action_u32(vrule,
VCAP_AF_PUSH_OUTER_TAG,
SPX5_OTAG_TAG_A);
if (err)
return err;
break;
default:
NL_SET_ERR_MSG_MOD(fco->common.extack,
"VLAN modify action not supported in this VCAP");
return -EOPNOTSUPP;
}
switch (tpid) {
case ETH_P_8021Q:
err = vcap_rule_add_action_u32(vrule,
VCAP_AF_TAG_A_TPID_SEL,
SPX5_TPID_A_8100);
break;
case ETH_P_8021AD:
err = vcap_rule_add_action_u32(vrule,
VCAP_AF_TAG_A_TPID_SEL,
SPX5_TPID_A_88A8);
break;
default:
NL_SET_ERR_MSG_MOD(fco->common.extack,
"Invalid vlan proto");
err = -EINVAL;
}
if (err)
return err;
err = vcap_rule_add_action_u32(vrule,
VCAP_AF_TAG_A_VID_SEL,
SPX5_VID_A_VAL);
if (err)
return err;
err = vcap_rule_add_action_u32(vrule,
VCAP_AF_VID_A_VAL,
act->vlan.vid);
if (err)
return err;
err = vcap_rule_add_action_u32(vrule,
VCAP_AF_TAG_A_PCP_SEL,
SPX5_PCP_A_VAL);
if (err)
return err;
err = vcap_rule_add_action_u32(vrule,
VCAP_AF_PCP_A_VAL,
act->vlan.prio);
if (err)
return err;
return vcap_rule_add_action_u32(vrule,
VCAP_AF_TAG_A_DEI_SEL,
SPX5_DEI_A_CLASSIFIED);
}
static int sparx5_tc_action_vlan_push(struct vcap_admin *admin,
struct vcap_rule *vrule,
struct flow_cls_offload *fco,
struct flow_action_entry *act,
u16 tpid)
{
u16 act_tpid = be16_to_cpu(act->vlan.proto);
int err = 0;
switch (admin->vtype) {
case VCAP_TYPE_ES0:
break;
default:
NL_SET_ERR_MSG_MOD(fco->common.extack,
"VLAN push action not supported in this VCAP");
return -EOPNOTSUPP;
}
if (tpid == ETH_P_8021AD) {
NL_SET_ERR_MSG_MOD(fco->common.extack,
"Cannot push on double tagged frames");
return -EOPNOTSUPP;
}
err = sparx5_tc_action_vlan_modify(admin, vrule, fco, act, act_tpid);
if (err)
return err;
switch (act_tpid) {
case ETH_P_8021Q:
break;
case ETH_P_8021AD:
/* Push classified tag as inner tag */
err = vcap_rule_add_action_u32(vrule,
VCAP_AF_PUSH_INNER_TAG,
SPX5_ITAG_PUSH_B_TAG);
if (err)
break;
err = vcap_rule_add_action_u32(vrule,
VCAP_AF_TAG_B_TPID_SEL,
SPX5_TPID_B_CLASSIFIED);
break;
default:
NL_SET_ERR_MSG_MOD(fco->common.extack,
"Invalid vlan proto");
err = -EINVAL;
}
return err;
}
static int sparx5_tc_flower_replace(struct net_device *ndev,
struct flow_cls_offload *fco,
struct vcap_admin *admin,
......@@ -809,6 +1004,11 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
struct sparx5_psfp_sf sf = { .max_sdu = SPX5_PSFP_SF_MAX_SDU };
struct netlink_ext_ack *extack = fco->common.extack;
int err, idx, tc_sg_idx = -1, tc_pol_idx = -1;
struct vcap_tc_flower_parse_usage state = {
.fco = fco,
.l3_proto = ETH_P_ALL,
.admin = admin,
};
struct sparx5_port *port = netdev_priv(ndev);
struct sparx5_multiple_rules multi = {};
struct sparx5 *sparx5 = port->sparx5;
......@@ -818,7 +1018,6 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
struct vcap_control *vctrl;
struct flow_rule *frule;
struct vcap_rule *vrule;
u16 l3_proto;
vctrl = port->sparx5->vcap_ctrl;
......@@ -833,8 +1032,9 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
vrule->cookie = fco->cookie;
l3_proto = ETH_P_ALL;
err = sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto);
state.vrule = vrule;
state.frule = flow_cls_offload_flow_rule(fco);
err = sparx5_tc_use_dissectors(&state, admin, vrule);
if (err)
goto out;
......@@ -888,6 +1088,24 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
fco->common.chain_index,
act->chain_index);
break;
case FLOW_ACTION_VLAN_POP:
err = sparx5_tc_action_vlan_pop(admin, vrule, fco,
state.tpid);
if (err)
goto out;
break;
case FLOW_ACTION_VLAN_PUSH:
err = sparx5_tc_action_vlan_push(admin, vrule, fco,
act, state.tpid);
if (err)
goto out;
break;
case FLOW_ACTION_VLAN_MANGLE:
err = sparx5_tc_action_vlan_modify(admin, vrule, fco,
act, state.tpid);
if (err)
goto out;
break;
default:
NL_SET_ERR_MSG_MOD(fco->common.extack,
"Unsupported TC action");
......@@ -904,8 +1122,8 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
goto out;
}
err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin, l3_proto,
&multi);
err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin,
state.l3_proto, &multi);
if (err) {
NL_SET_ERR_MSG_MOD(fco->common.extack,
"No matching port keyset for filter protocol and keys");
......@@ -913,7 +1131,7 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
}
/* provide the l3 protocol to guide the keyset selection */
err = vcap_val_rule(vrule, l3_proto);
err = vcap_val_rule(vrule, state.l3_proto);
if (err) {
vcap_set_tc_exterr(fco, vrule);
goto out;
......@@ -923,7 +1141,7 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
NL_SET_ERR_MSG_MOD(fco->common.extack,
"Could not add the filter");
if (l3_proto == ETH_P_ALL)
if (state.l3_proto == ETH_P_ALL)
err = sparx5_tc_add_remaining_rules(vctrl, fco, vrule, admin,
&multi);
......
......@@ -852,6 +852,9 @@ static void sparx5_vcap_es0_add_default_fields(struct net_device *ndev,
struct sparx5_port *port = netdev_priv(ndev);
vcap_rule_add_key_u32(rule, VCAP_KF_IF_EGR_PORT_NO, port->portno, ~0);
/* Match untagged frames if there was no VLAN key */
vcap_rule_add_key_u32(rule, VCAP_KF_8021Q_TPID, SPX5_TPID_SEL_UNTAGGED,
~0);
}
static void sparx5_vcap_es2_add_default_fields(struct net_device *ndev,
......
......@@ -176,6 +176,18 @@ enum vcap_es2_port_sel_arp {
VCAP_ES2_PS_ARP_ARP,
};
/* Selects TPID for ES0 matching */
enum SPX5_TPID_SEL {
SPX5_TPID_SEL_UNTAGGED,
SPX5_TPID_SEL_8100,
SPX5_TPID_SEL_UNUSED_0,
SPX5_TPID_SEL_UNUSED_1,
SPX5_TPID_SEL_88A8,
SPX5_TPID_SEL_TPIDCFG_1,
SPX5_TPID_SEL_TPIDCFG_2,
SPX5_TPID_SEL_TPIDCFG_3,
};
/* Get the port keyset for the vcap lookup */
int sparx5_vcap_get_port_keyset(struct net_device *ndev,
struct vcap_admin *admin,
......
......@@ -235,6 +235,9 @@ int vcap_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st,
goto out;
}
if (mt.mask->vlan_tpid)
st->tpid = be16_to_cpu(mt.key->vlan_tpid);
st->used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN);
return 0;
......
......@@ -13,6 +13,7 @@ struct vcap_tc_flower_parse_usage {
struct vcap_admin *admin;
u16 l3_proto;
u8 l4_proto;
u16 tpid;
unsigned int used_keys;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment