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 { ...@@ -29,6 +29,72 @@ enum SPX5_FORWARDING_SEL {
SPX5_FWSEL_DISCARD, 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, int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type,
void *type_data); void *type_data);
......
...@@ -28,6 +28,31 @@ struct sparx5_multiple_rules { ...@@ -28,6 +28,31 @@ struct sparx5_multiple_rules {
struct sparx5_wildcard_rule rule[SPX5_MAX_RULE_SIZE]; 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 static int
sparx5_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st) 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) ...@@ -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 vid_key = VCAP_KF_8021Q_VID_CLS;
enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS; enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS;
int err;
if (st->admin->vtype == VCAP_TYPE_IS0) { if (st->admin->vtype == VCAP_TYPE_IS0) {
vid_key = VCAP_KF_8021Q_VID0; vid_key = VCAP_KF_8021Q_VID0;
pcp_key = VCAP_KF_8021Q_PCP0; 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) = { 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 ...@@ -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, [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_admin *admin,
struct vcap_rule *vrule, struct vcap_rule *vrule)
u16 *l3_proto)
{ {
struct vcap_tc_flower_parse_usage state = {
.fco = fco,
.vrule = vrule,
.l3_proto = ETH_P_ALL,
.admin = admin,
};
int idx, err = 0; int idx, err = 0;
state.frule = flow_cls_offload_flow_rule(fco);
for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) { 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; continue;
if (!sparx5_tc_flower_usage_handlers[idx]) if (!sparx5_tc_flower_usage_handlers[idx])
continue; continue;
err = sparx5_tc_flower_usage_handlers[idx](&state); err = sparx5_tc_flower_usage_handlers[idx](st);
if (err) if (err)
return err; return err;
} }
if (state.frule->match.dissector->used_keys ^ state.used_keys) { if (st->frule->match.dissector->used_keys ^ st->used_keys) {
NL_SET_ERR_MSG_MOD(fco->common.extack, NL_SET_ERR_MSG_MOD(st->fco->common.extack,
"Unsupported match item"); "Unsupported match item");
return -ENOENT; return -ENOENT;
} }
if (l3_proto)
*l3_proto = state.l3_proto;
return err; return err;
} }
...@@ -281,6 +304,27 @@ static int sparx5_tc_flower_action_check(struct vcap_control *vctrl, ...@@ -281,6 +304,27 @@ static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
return -EOPNOTSUPP; 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; return 0;
} }
...@@ -801,6 +845,157 @@ static int sparx5_tc_action_trap(struct vcap_admin *admin, ...@@ -801,6 +845,157 @@ static int sparx5_tc_action_trap(struct vcap_admin *admin,
return err; 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, static int sparx5_tc_flower_replace(struct net_device *ndev,
struct flow_cls_offload *fco, struct flow_cls_offload *fco,
struct vcap_admin *admin, struct vcap_admin *admin,
...@@ -809,6 +1004,11 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, ...@@ -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 sparx5_psfp_sf sf = { .max_sdu = SPX5_PSFP_SF_MAX_SDU };
struct netlink_ext_ack *extack = fco->common.extack; struct netlink_ext_ack *extack = fco->common.extack;
int err, idx, tc_sg_idx = -1, tc_pol_idx = -1; 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_port *port = netdev_priv(ndev);
struct sparx5_multiple_rules multi = {}; struct sparx5_multiple_rules multi = {};
struct sparx5 *sparx5 = port->sparx5; struct sparx5 *sparx5 = port->sparx5;
...@@ -818,7 +1018,6 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, ...@@ -818,7 +1018,6 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
struct vcap_control *vctrl; struct vcap_control *vctrl;
struct flow_rule *frule; struct flow_rule *frule;
struct vcap_rule *vrule; struct vcap_rule *vrule;
u16 l3_proto;
vctrl = port->sparx5->vcap_ctrl; vctrl = port->sparx5->vcap_ctrl;
...@@ -833,8 +1032,9 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, ...@@ -833,8 +1032,9 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
vrule->cookie = fco->cookie; vrule->cookie = fco->cookie;
l3_proto = ETH_P_ALL; state.vrule = vrule;
err = sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto); state.frule = flow_cls_offload_flow_rule(fco);
err = sparx5_tc_use_dissectors(&state, admin, vrule);
if (err) if (err)
goto out; goto out;
...@@ -888,6 +1088,24 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, ...@@ -888,6 +1088,24 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
fco->common.chain_index, fco->common.chain_index,
act->chain_index); act->chain_index);
break; 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: default:
NL_SET_ERR_MSG_MOD(fco->common.extack, NL_SET_ERR_MSG_MOD(fco->common.extack,
"Unsupported TC action"); "Unsupported TC action");
...@@ -904,8 +1122,8 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, ...@@ -904,8 +1122,8 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
goto out; goto out;
} }
err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin, l3_proto, err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin,
&multi); state.l3_proto, &multi);
if (err) { if (err) {
NL_SET_ERR_MSG_MOD(fco->common.extack, NL_SET_ERR_MSG_MOD(fco->common.extack,
"No matching port keyset for filter protocol and keys"); "No matching port keyset for filter protocol and keys");
...@@ -913,7 +1131,7 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, ...@@ -913,7 +1131,7 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
} }
/* provide the l3 protocol to guide the keyset selection */ /* 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) { if (err) {
vcap_set_tc_exterr(fco, vrule); vcap_set_tc_exterr(fco, vrule);
goto out; goto out;
...@@ -923,7 +1141,7 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, ...@@ -923,7 +1141,7 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
NL_SET_ERR_MSG_MOD(fco->common.extack, NL_SET_ERR_MSG_MOD(fco->common.extack,
"Could not add the filter"); "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, err = sparx5_tc_add_remaining_rules(vctrl, fco, vrule, admin,
&multi); &multi);
......
...@@ -852,6 +852,9 @@ static void sparx5_vcap_es0_add_default_fields(struct net_device *ndev, ...@@ -852,6 +852,9 @@ static void sparx5_vcap_es0_add_default_fields(struct net_device *ndev,
struct sparx5_port *port = netdev_priv(ndev); struct sparx5_port *port = netdev_priv(ndev);
vcap_rule_add_key_u32(rule, VCAP_KF_IF_EGR_PORT_NO, port->portno, ~0); 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, static void sparx5_vcap_es2_add_default_fields(struct net_device *ndev,
......
...@@ -176,6 +176,18 @@ enum vcap_es2_port_sel_arp { ...@@ -176,6 +176,18 @@ enum vcap_es2_port_sel_arp {
VCAP_ES2_PS_ARP_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 */ /* Get the port keyset for the vcap lookup */
int sparx5_vcap_get_port_keyset(struct net_device *ndev, int sparx5_vcap_get_port_keyset(struct net_device *ndev,
struct vcap_admin *admin, struct vcap_admin *admin,
......
...@@ -235,6 +235,9 @@ int vcap_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st, ...@@ -235,6 +235,9 @@ int vcap_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st,
goto out; goto out;
} }
if (mt.mask->vlan_tpid)
st->tpid = be16_to_cpu(mt.key->vlan_tpid);
st->used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN); st->used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN);
return 0; return 0;
......
...@@ -13,6 +13,7 @@ struct vcap_tc_flower_parse_usage { ...@@ -13,6 +13,7 @@ struct vcap_tc_flower_parse_usage {
struct vcap_admin *admin; struct vcap_admin *admin;
u16 l3_proto; u16 l3_proto;
u8 l4_proto; u8 l4_proto;
u16 tpid;
unsigned int used_keys; 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