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

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
Loading
Loading
Loading
Loading
+66 −0
Original line number Diff line number Diff line
@@ -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);

+242 −24
Original line number Diff line number Diff line
@@ -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);

+3 −0
Original line number Diff line number Diff line
@@ -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,
+12 −0
Original line number Diff line number Diff line
@@ -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,
+3 −0
Original line number Diff line number Diff line
@@ -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;
Loading