brintos

brintos / linux-shallow public Read only

0
0
Text · 40.3 KiB · 8d67d9f Raw
1575 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/* Microchip VCAP API3 *4 * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.5 */6 7#include <net/tc_act/tc_gate.h>8#include <net/tcp.h>9 10#include "sparx5_tc.h"11#include "vcap_api.h"12#include "vcap_api_client.h"13#include "vcap_tc.h"14#include "sparx5_main.h"15#include "sparx5_vcap_impl.h"16 17#define SPX5_MAX_RULE_SIZE 13 /* allows X1, X2, X4, X6 and X12 rules */18 19/* Collect keysets and type ids for multiple rules per size */20struct sparx5_wildcard_rule {21	bool selected;22	u8 value;23	u8 mask;24	enum vcap_keyfield_set keyset;25};26 27struct sparx5_multiple_rules {28	struct sparx5_wildcard_rule rule[SPX5_MAX_RULE_SIZE];29};30 31struct sparx5_tc_flower_template {32	struct list_head list; /* for insertion in the list of templates */33	int cid; /* chain id */34	enum vcap_keyfield_set orig; /* keyset used before the template */35	enum vcap_keyfield_set keyset; /* new keyset used by template */36	u16 l3_proto; /* protocol specified in the template */37};38 39/* SparX-5 VCAP fragment types:40 * 0 = no fragment, 1 = initial fragment,41 * 2 = suspicious fragment, 3 = valid follow-up fragment42 */43enum {                   /* key / mask */44	FRAG_NOT   = 0x03, /* 0 / 3 */45	FRAG_SOME  = 0x11, /* 1 / 1 */46	FRAG_FIRST = 0x13, /* 1 / 3 */47	FRAG_LATER = 0x33, /* 3 / 3 */48	FRAG_INVAL = 0xff, /* invalid */49};50 51/* Flower fragment flag to VCAP fragment type mapping */52static const u8 sparx5_vcap_frag_map[4][4] = {		  /* is_frag */53	{ FRAG_INVAL, FRAG_INVAL, FRAG_INVAL, FRAG_FIRST }, /* 0/0 */54	{ FRAG_NOT,   FRAG_NOT,   FRAG_INVAL, FRAG_INVAL }, /* 0/1 */55	{ FRAG_INVAL, FRAG_INVAL, FRAG_INVAL, FRAG_INVAL }, /* 1/0 */56	{ FRAG_SOME,  FRAG_LATER, FRAG_INVAL, FRAG_FIRST }  /* 1/1 */57	/* 0/0	      0/1	  1/0	      1/1 <-- first_frag */58};59 60static int61sparx5_tc_flower_es0_tpid(struct vcap_tc_flower_parse_usage *st)62{63	int err = 0;64 65	switch (st->tpid) {66	case ETH_P_8021Q:67		err = vcap_rule_add_key_u32(st->vrule,68					    VCAP_KF_8021Q_TPID,69					    SPX5_TPID_SEL_8100, ~0);70		break;71	case ETH_P_8021AD:72		err = vcap_rule_add_key_u32(st->vrule,73					    VCAP_KF_8021Q_TPID,74					    SPX5_TPID_SEL_88A8, ~0);75		break;76	default:77		NL_SET_ERR_MSG_MOD(st->fco->common.extack,78				   "Invalid vlan proto");79		err = -EINVAL;80		break;81	}82	return err;83}84 85static int86sparx5_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st)87{88	struct flow_match_basic mt;89	int err = 0;90 91	flow_rule_match_basic(st->frule, &mt);92 93	if (mt.mask->n_proto) {94		st->l3_proto = be16_to_cpu(mt.key->n_proto);95		if (!sparx5_vcap_is_known_etype(st->admin, st->l3_proto)) {96			err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,97						    st->l3_proto, ~0);98			if (err)99				goto out;100		} else if (st->l3_proto == ETH_P_IP) {101			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,102						    VCAP_BIT_1);103			if (err)104				goto out;105		} else if (st->l3_proto == ETH_P_IPV6) {106			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,107						    VCAP_BIT_0);108			if (err)109				goto out;110			if (st->admin->vtype == VCAP_TYPE_IS0) {111				err = vcap_rule_add_key_bit(st->vrule,112							    VCAP_KF_IP_SNAP_IS,113							    VCAP_BIT_1);114				if (err)115					goto out;116			}117		}118	}119 120	if (mt.mask->ip_proto) {121		st->l4_proto = mt.key->ip_proto;122		if (st->l4_proto == IPPROTO_TCP) {123			err = vcap_rule_add_key_bit(st->vrule,124						    VCAP_KF_TCP_IS,125						    VCAP_BIT_1);126			if (err)127				goto out;128		} else if (st->l4_proto == IPPROTO_UDP) {129			err = vcap_rule_add_key_bit(st->vrule,130						    VCAP_KF_TCP_IS,131						    VCAP_BIT_0);132			if (err)133				goto out;134			if (st->admin->vtype == VCAP_TYPE_IS0) {135				err = vcap_rule_add_key_bit(st->vrule,136							    VCAP_KF_TCP_UDP_IS,137							    VCAP_BIT_1);138				if (err)139					goto out;140			}141		} else {142			err = vcap_rule_add_key_u32(st->vrule,143						    VCAP_KF_L3_IP_PROTO,144						    st->l4_proto, ~0);145			if (err)146				goto out;147		}148	}149 150	st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_BASIC);151 152	return err;153 154out:155	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error");156	return err;157}158 159static int160sparx5_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage *st)161{162	struct netlink_ext_ack *extack = st->fco->common.extack;163	struct flow_match_control mt;164	u32 value, mask;165	int err = 0;166 167	flow_rule_match_control(st->frule, &mt);168 169	if (mt.mask->flags & (FLOW_DIS_IS_FRAGMENT | FLOW_DIS_FIRST_FRAG)) {170		u8 is_frag_key = !!(mt.key->flags & FLOW_DIS_IS_FRAGMENT);171		u8 is_frag_mask = !!(mt.mask->flags & FLOW_DIS_IS_FRAGMENT);172		u8 is_frag_idx = (is_frag_key << 1) | is_frag_mask;173 174		u8 first_frag_key = !!(mt.key->flags & FLOW_DIS_FIRST_FRAG);175		u8 first_frag_mask = !!(mt.mask->flags & FLOW_DIS_FIRST_FRAG);176		u8 first_frag_idx = (first_frag_key << 1) | first_frag_mask;177 178		/* Lookup verdict based on the 2 + 2 input bits */179		u8 vdt = sparx5_vcap_frag_map[is_frag_idx][first_frag_idx];180 181		if (vdt == FRAG_INVAL) {182			NL_SET_ERR_MSG_MOD(extack,183					   "Match on invalid fragment flag combination");184			return -EINVAL;185		}186 187		/* Extract VCAP fragment key and mask from verdict */188		value = (vdt >> 4) & 0x3;189		mask = vdt & 0x3;190 191		err = vcap_rule_add_key_u32(st->vrule,192					    VCAP_KF_L3_FRAGMENT_TYPE,193					    value, mask);194		if (err) {195			NL_SET_ERR_MSG_MOD(extack, "ip_frag parse error");196			return err;197		}198	}199 200	if (!flow_rule_is_supp_control_flags(FLOW_DIS_IS_FRAGMENT |201					     FLOW_DIS_FIRST_FRAG,202					     mt.mask->flags, extack))203		return -EOPNOTSUPP;204 205	st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL);206 207	return err;208}209 210static int211sparx5_tc_flower_handler_cvlan_usage(struct vcap_tc_flower_parse_usage *st)212{213	if (st->admin->vtype != VCAP_TYPE_IS0) {214		NL_SET_ERR_MSG_MOD(st->fco->common.extack,215				   "cvlan not supported in this VCAP");216		return -EINVAL;217	}218 219	return vcap_tc_flower_handler_cvlan_usage(st);220}221 222static int223sparx5_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st)224{225	enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS;226	enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS;227	int err;228 229	if (st->admin->vtype == VCAP_TYPE_IS0) {230		vid_key = VCAP_KF_8021Q_VID0;231		pcp_key = VCAP_KF_8021Q_PCP0;232	}233 234	err = vcap_tc_flower_handler_vlan_usage(st, vid_key, pcp_key);235	if (err)236		return err;237 238	if (st->admin->vtype == VCAP_TYPE_ES0 && st->tpid)239		err = sparx5_tc_flower_es0_tpid(st);240 241	return err;242}243 244static int (*sparx5_tc_flower_usage_handlers[])(struct vcap_tc_flower_parse_usage *st) = {245	[FLOW_DISSECTOR_KEY_ETH_ADDRS] = vcap_tc_flower_handler_ethaddr_usage,246	[FLOW_DISSECTOR_KEY_IPV4_ADDRS] = vcap_tc_flower_handler_ipv4_usage,247	[FLOW_DISSECTOR_KEY_IPV6_ADDRS] = vcap_tc_flower_handler_ipv6_usage,248	[FLOW_DISSECTOR_KEY_CONTROL] = sparx5_tc_flower_handler_control_usage,249	[FLOW_DISSECTOR_KEY_PORTS] = vcap_tc_flower_handler_portnum_usage,250	[FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage,251	[FLOW_DISSECTOR_KEY_CVLAN] = sparx5_tc_flower_handler_cvlan_usage,252	[FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage,253	[FLOW_DISSECTOR_KEY_TCP] = vcap_tc_flower_handler_tcp_usage,254	[FLOW_DISSECTOR_KEY_ARP] = vcap_tc_flower_handler_arp_usage,255	[FLOW_DISSECTOR_KEY_IP] = vcap_tc_flower_handler_ip_usage,256};257 258static int sparx5_tc_use_dissectors(struct vcap_tc_flower_parse_usage *st,259				    struct vcap_admin *admin,260				    struct vcap_rule *vrule)261{262	int idx, err = 0;263 264	for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) {265		if (!flow_rule_match_key(st->frule, idx))266			continue;267		if (!sparx5_tc_flower_usage_handlers[idx])268			continue;269		err = sparx5_tc_flower_usage_handlers[idx](st);270		if (err)271			return err;272	}273 274	if (st->frule->match.dissector->used_keys ^ st->used_keys) {275		NL_SET_ERR_MSG_MOD(st->fco->common.extack,276				   "Unsupported match item");277		return -ENOENT;278	}279 280	return err;281}282 283static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,284					 struct net_device *ndev,285					 struct flow_cls_offload *fco,286					 bool ingress)287{288	struct flow_rule *rule = flow_cls_offload_flow_rule(fco);289	struct flow_action_entry *actent, *last_actent = NULL;290	struct flow_action *act = &rule->action;291	u64 action_mask = 0;292	int idx;293 294	if (!flow_action_has_entries(act)) {295		NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");296		return -EINVAL;297	}298 299	if (!flow_action_basic_hw_stats_check(act, fco->common.extack))300		return -EOPNOTSUPP;301 302	flow_action_for_each(idx, actent, act) {303		if (action_mask & BIT(actent->id)) {304			NL_SET_ERR_MSG_MOD(fco->common.extack,305					   "More actions of the same type");306			return -EINVAL;307		}308		action_mask |= BIT(actent->id);309		last_actent = actent; /* Save last action for later check */310	}311 312	/* Check if last action is a goto313	 * The last chain/lookup does not need to have a goto action314	 */315	if (last_actent->id == FLOW_ACTION_GOTO) {316		/* Check if the destination chain is in one of the VCAPs */317		if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,318					 last_actent->chain_index)) {319			NL_SET_ERR_MSG_MOD(fco->common.extack,320					   "Invalid goto chain");321			return -EINVAL;322		}323	} else if (!vcap_is_last_chain(vctrl, fco->common.chain_index,324				       ingress)) {325		NL_SET_ERR_MSG_MOD(fco->common.extack,326				   "Last action must be 'goto'");327		return -EINVAL;328	}329 330	/* Catch unsupported combinations of actions */331	if (action_mask & BIT(FLOW_ACTION_TRAP) &&332	    action_mask & BIT(FLOW_ACTION_ACCEPT)) {333		NL_SET_ERR_MSG_MOD(fco->common.extack,334				   "Cannot combine pass and trap action");335		return -EOPNOTSUPP;336	}337 338	if (action_mask & BIT(FLOW_ACTION_VLAN_PUSH) &&339	    action_mask & BIT(FLOW_ACTION_VLAN_POP)) {340		NL_SET_ERR_MSG_MOD(fco->common.extack,341				   "Cannot combine vlan push and pop action");342		return -EOPNOTSUPP;343	}344 345	if (action_mask & BIT(FLOW_ACTION_VLAN_PUSH) &&346	    action_mask & BIT(FLOW_ACTION_VLAN_MANGLE)) {347		NL_SET_ERR_MSG_MOD(fco->common.extack,348				   "Cannot combine vlan push and modify action");349		return -EOPNOTSUPP;350	}351 352	if (action_mask & BIT(FLOW_ACTION_VLAN_POP) &&353	    action_mask & BIT(FLOW_ACTION_VLAN_MANGLE)) {354		NL_SET_ERR_MSG_MOD(fco->common.extack,355				   "Cannot combine vlan pop and modify action");356		return -EOPNOTSUPP;357	}358 359	return 0;360}361 362/* Add a rule counter action */363static int sparx5_tc_add_rule_counter(struct vcap_admin *admin,364				      struct vcap_rule *vrule)365{366	int err;367 368	switch (admin->vtype) {369	case VCAP_TYPE_IS0:370		break;371	case VCAP_TYPE_ES0:372		err = vcap_rule_mod_action_u32(vrule, VCAP_AF_ESDX,373					       vrule->id);374		if (err)375			return err;376		vcap_rule_set_counter_id(vrule, vrule->id);377		break;378	case VCAP_TYPE_IS2:379	case VCAP_TYPE_ES2:380		err = vcap_rule_mod_action_u32(vrule, VCAP_AF_CNT_ID,381					       vrule->id);382		if (err)383			return err;384		vcap_rule_set_counter_id(vrule, vrule->id);385		break;386	default:387		pr_err("%s:%d: vcap type: %d not supported\n",388		       __func__, __LINE__, admin->vtype);389		break;390	}391	return 0;392}393 394/* Collect all port keysets and apply the first of them, possibly wildcarded */395static int sparx5_tc_select_protocol_keyset(struct net_device *ndev,396					    struct vcap_rule *vrule,397					    struct vcap_admin *admin,398					    u16 l3_proto,399					    struct sparx5_multiple_rules *multi)400{401	struct sparx5_port *port = netdev_priv(ndev);402	struct vcap_keyset_list portkeysetlist = {};403	enum vcap_keyfield_set portkeysets[10] = {};404	struct vcap_keyset_list matches = {};405	enum vcap_keyfield_set keysets[10];406	int idx, jdx, err = 0, count = 0;407	struct sparx5_wildcard_rule *mru;408	const struct vcap_set *kinfo;409	struct vcap_control *vctrl;410 411	vctrl = port->sparx5->vcap_ctrl;412 413	/* Find the keysets that the rule can use */414	matches.keysets = keysets;415	matches.max = ARRAY_SIZE(keysets);416	if (!vcap_rule_find_keysets(vrule, &matches))417		return -EINVAL;418 419	/* Find the keysets that the port configuration supports */420	portkeysetlist.max = ARRAY_SIZE(portkeysets);421	portkeysetlist.keysets = portkeysets;422	err = sparx5_vcap_get_port_keyset(ndev,423					  admin, vrule->vcap_chain_id,424					  l3_proto,425					  &portkeysetlist);426	if (err)427		return err;428 429	/* Find the intersection of the two sets of keyset */430	for (idx = 0; idx < portkeysetlist.cnt; ++idx) {431		kinfo = vcap_keyfieldset(vctrl, admin->vtype,432					 portkeysetlist.keysets[idx]);433		if (!kinfo)434			continue;435 436		/* Find a port keyset that matches the required keys437		 * If there are multiple keysets then compose a type id mask438		 */439		for (jdx = 0; jdx < matches.cnt; ++jdx) {440			if (portkeysetlist.keysets[idx] != matches.keysets[jdx])441				continue;442 443			mru = &multi->rule[kinfo->sw_per_item];444			if (!mru->selected) {445				mru->selected = true;446				mru->keyset = portkeysetlist.keysets[idx];447				mru->value = kinfo->type_id;448			}449			mru->value &= kinfo->type_id;450			mru->mask |= kinfo->type_id;451			++count;452		}453	}454	if (count == 0)455		return -EPROTO;456 457	if (l3_proto == ETH_P_ALL && count < portkeysetlist.cnt)458		return -ENOENT;459 460	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {461		mru = &multi->rule[idx];462		if (!mru->selected)463			continue;464 465		/* Align the mask to the combined value */466		mru->mask ^= mru->value;467	}468 469	/* Set the chosen keyset on the rule and set a wildcarded type if there470	 * are more than one keyset471	 */472	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {473		mru = &multi->rule[idx];474		if (!mru->selected)475			continue;476 477		vcap_set_rule_set_keyset(vrule, mru->keyset);478		if (count > 1)479			/* Some keysets do not have a type field */480			vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE,481					      mru->value,482					      ~mru->mask);483		mru->selected = false; /* mark as done */484		break; /* Stop here and add more rules later */485	}486	return err;487}488 489static int sparx5_tc_add_rule_copy(struct vcap_control *vctrl,490				   struct flow_cls_offload *fco,491				   struct vcap_rule *erule,492				   struct vcap_admin *admin,493				   struct sparx5_wildcard_rule *rule)494{495	enum vcap_key_field keylist[] = {496		VCAP_KF_IF_IGR_PORT_MASK,497		VCAP_KF_IF_IGR_PORT_MASK_SEL,498		VCAP_KF_IF_IGR_PORT_MASK_RNG,499		VCAP_KF_LOOKUP_FIRST_IS,500		VCAP_KF_TYPE,501	};502	struct vcap_rule *vrule;503	int err;504 505	/* Add an extra rule with a special user and the new keyset */506	erule->user = VCAP_USER_TC_EXTRA;507	vrule = vcap_copy_rule(erule);508	if (IS_ERR(vrule))509		return PTR_ERR(vrule);510 511	/* Link the new rule to the existing rule with the cookie */512	vrule->cookie = erule->cookie;513	vcap_filter_rule_keys(vrule, keylist, ARRAY_SIZE(keylist), true);514	err = vcap_set_rule_set_keyset(vrule, rule->keyset);515	if (err) {516		pr_err("%s:%d: could not set keyset %s in rule: %u\n",517		       __func__, __LINE__,518		       vcap_keyset_name(vctrl, rule->keyset),519		       vrule->id);520		goto out;521	}522 523	/* Some keysets do not have a type field, so ignore return value */524	vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE, rule->value, ~rule->mask);525 526	err = vcap_set_rule_set_actionset(vrule, erule->actionset);527	if (err)528		goto out;529 530	err = sparx5_tc_add_rule_counter(admin, vrule);531	if (err)532		goto out;533 534	err = vcap_val_rule(vrule, ETH_P_ALL);535	if (err) {536		pr_err("%s:%d: could not validate rule: %u\n",537		       __func__, __LINE__, vrule->id);538		vcap_set_tc_exterr(fco, vrule);539		goto out;540	}541	err = vcap_add_rule(vrule);542	if (err) {543		pr_err("%s:%d: could not add rule: %u\n",544		       __func__, __LINE__, vrule->id);545		goto out;546	}547out:548	vcap_free_rule(vrule);549	return err;550}551 552static int sparx5_tc_add_remaining_rules(struct vcap_control *vctrl,553					 struct flow_cls_offload *fco,554					 struct vcap_rule *erule,555					 struct vcap_admin *admin,556					 struct sparx5_multiple_rules *multi)557{558	int idx, err = 0;559 560	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {561		if (!multi->rule[idx].selected)562			continue;563 564		err = sparx5_tc_add_rule_copy(vctrl, fco, erule, admin,565					      &multi->rule[idx]);566		if (err)567			break;568	}569	return err;570}571 572/* Add the actionset that is the default for the VCAP type */573static int sparx5_tc_set_actionset(struct vcap_admin *admin,574				   struct vcap_rule *vrule)575{576	enum vcap_actionfield_set aset;577	int err = 0;578 579	switch (admin->vtype) {580	case VCAP_TYPE_IS0:581		aset = VCAP_AFS_CLASSIFICATION;582		break;583	case VCAP_TYPE_IS2:584		aset = VCAP_AFS_BASE_TYPE;585		break;586	case VCAP_TYPE_ES0:587		aset = VCAP_AFS_ES0;588		break;589	case VCAP_TYPE_ES2:590		aset = VCAP_AFS_BASE_TYPE;591		break;592	default:593		pr_err("%s:%d: %s\n", __func__, __LINE__, "Invalid VCAP type");594		return -EINVAL;595	}596	/* Do not overwrite any current actionset */597	if (vrule->actionset == VCAP_AFS_NO_VALUE)598		err = vcap_set_rule_set_actionset(vrule, aset);599	return err;600}601 602/* Add the VCAP key to match on for a rule target value */603static int sparx5_tc_add_rule_link_target(struct vcap_admin *admin,604					  struct vcap_rule *vrule,605					  int target_cid)606{607	int link_val = target_cid % VCAP_CID_LOOKUP_SIZE;608	int err;609 610	if (!link_val)611		return 0;612 613	switch (admin->vtype) {614	case VCAP_TYPE_IS0:615		/* Add NXT_IDX key for chaining rules between IS0 instances */616		err = vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX_SEL,617					    1, /* enable */618					    ~0);619		if (err)620			return err;621		return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX,622					     link_val, /* target */623					     ~0);624	case VCAP_TYPE_IS2:625		/* Add PAG key for chaining rules from IS0 */626		return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_PAG,627					     link_val, /* target */628					     ~0);629	case VCAP_TYPE_ES0:630	case VCAP_TYPE_ES2:631		/* Add ISDX key for chaining rules from IS0 */632		return vcap_rule_add_key_u32(vrule, VCAP_KF_ISDX_CLS, link_val,633					     ~0);634	default:635		break;636	}637	return 0;638}639 640/* Add the VCAP action that adds a target value to a rule */641static int sparx5_tc_add_rule_link(struct vcap_control *vctrl,642				   struct vcap_admin *admin,643				   struct vcap_rule *vrule,644				   int from_cid, int to_cid)645{646	struct vcap_admin *to_admin = vcap_find_admin(vctrl, to_cid);647	int diff, err = 0;648 649	if (!to_admin) {650		pr_err("%s:%d: unsupported chain direction: %d\n",651		       __func__, __LINE__, to_cid);652		return -EINVAL;653	}654 655	diff = vcap_chain_offset(vctrl, from_cid, to_cid);656	if (!diff)657		return 0;658 659	if (admin->vtype == VCAP_TYPE_IS0 &&660	    to_admin->vtype == VCAP_TYPE_IS0) {661		/* Between IS0 instances the G_IDX value is used */662		err = vcap_rule_add_action_u32(vrule, VCAP_AF_NXT_IDX, diff);663		if (err)664			goto out;665		err = vcap_rule_add_action_u32(vrule, VCAP_AF_NXT_IDX_CTRL,666					       1); /* Replace */667		if (err)668			goto out;669	} else if (admin->vtype == VCAP_TYPE_IS0 &&670		   to_admin->vtype == VCAP_TYPE_IS2) {671		/* Between IS0 and IS2 the PAG value is used */672		err = vcap_rule_add_action_u32(vrule, VCAP_AF_PAG_VAL, diff);673		if (err)674			goto out;675		err = vcap_rule_add_action_u32(vrule,676					       VCAP_AF_PAG_OVERRIDE_MASK,677					       0xff);678		if (err)679			goto out;680	} else if (admin->vtype == VCAP_TYPE_IS0 &&681		   (to_admin->vtype == VCAP_TYPE_ES0 ||682		    to_admin->vtype == VCAP_TYPE_ES2)) {683		/* Between IS0 and ES0/ES2 the ISDX value is used */684		err = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_VAL,685					       diff);686		if (err)687			goto out;688		err = vcap_rule_add_action_bit(vrule,689					       VCAP_AF_ISDX_ADD_REPLACE_SEL,690					       VCAP_BIT_1);691		if (err)692			goto out;693	} else {694		pr_err("%s:%d: unsupported chain destination: %d\n",695		       __func__, __LINE__, to_cid);696		err = -EOPNOTSUPP;697	}698out:699	return err;700}701 702static int sparx5_tc_flower_parse_act_gate(struct sparx5_psfp_sg *sg,703					   struct flow_action_entry *act,704					   struct netlink_ext_ack *extack)705{706	int i;707 708	if (act->gate.prio < -1 || act->gate.prio > SPX5_PSFP_SG_MAX_IPV) {709		NL_SET_ERR_MSG_MOD(extack, "Invalid gate priority");710		return -EINVAL;711	}712 713	if (act->gate.cycletime < SPX5_PSFP_SG_MIN_CYCLE_TIME_NS ||714	    act->gate.cycletime > SPX5_PSFP_SG_MAX_CYCLE_TIME_NS) {715		NL_SET_ERR_MSG_MOD(extack, "Invalid gate cycletime");716		return -EINVAL;717	}718 719	if (act->gate.cycletimeext > SPX5_PSFP_SG_MAX_CYCLE_TIME_NS) {720		NL_SET_ERR_MSG_MOD(extack, "Invalid gate cycletimeext");721		return -EINVAL;722	}723 724	if (act->gate.num_entries >= SPX5_PSFP_GCE_CNT) {725		NL_SET_ERR_MSG_MOD(extack, "Invalid number of gate entries");726		return -EINVAL;727	}728 729	sg->gate_state = true;730	sg->ipv = act->gate.prio;731	sg->num_entries = act->gate.num_entries;732	sg->cycletime = act->gate.cycletime;733	sg->cycletimeext = act->gate.cycletimeext;734 735	for (i = 0; i < sg->num_entries; i++) {736		sg->gce[i].gate_state = !!act->gate.entries[i].gate_state;737		sg->gce[i].interval = act->gate.entries[i].interval;738		sg->gce[i].ipv = act->gate.entries[i].ipv;739		sg->gce[i].maxoctets = act->gate.entries[i].maxoctets;740	}741 742	return 0;743}744 745static int sparx5_tc_flower_parse_act_police(struct sparx5_policer *pol,746					     struct flow_action_entry *act,747					     struct netlink_ext_ack *extack)748{749	pol->type = SPX5_POL_SERVICE;750	pol->rate = div_u64(act->police.rate_bytes_ps, 1000) * 8;751	pol->burst = act->police.burst;752	pol->idx = act->hw_index;753 754	/* rate is now in kbit */755	if (pol->rate > DIV_ROUND_UP(SPX5_SDLB_GROUP_RATE_MAX, 1000)) {756		NL_SET_ERR_MSG_MOD(extack, "Maximum rate exceeded");757		return -EINVAL;758	}759 760	if (act->police.exceed.act_id != FLOW_ACTION_DROP) {761		NL_SET_ERR_MSG_MOD(extack, "Offload not supported when exceed action is not drop");762		return -EOPNOTSUPP;763	}764 765	if (act->police.notexceed.act_id != FLOW_ACTION_PIPE &&766	    act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) {767		NL_SET_ERR_MSG_MOD(extack, "Offload not supported when conform action is not pipe or ok");768		return -EOPNOTSUPP;769	}770 771	return 0;772}773 774static int sparx5_tc_flower_psfp_setup(struct sparx5 *sparx5,775				       struct vcap_rule *vrule, int sg_idx,776				       int pol_idx, struct sparx5_psfp_sg *sg,777				       struct sparx5_psfp_fm *fm,778				       struct sparx5_psfp_sf *sf)779{780	u32 psfp_sfid = 0, psfp_fmid = 0, psfp_sgid = 0;781	int ret;782 783	/* Must always have a stream gate - max sdu (filter option) is evaluated784	 * after frames have passed the gate, so in case of only a policer, we785	 * allocate a stream gate that is always open.786	 */787	if (sg_idx < 0) {788		sg_idx = sparx5_pool_idx_to_id(SPX5_PSFP_SG_OPEN);789		sg->ipv = 0; /* Disabled */790		sg->cycletime = SPX5_PSFP_SG_CYCLE_TIME_DEFAULT;791		sg->num_entries = 1;792		sg->gate_state = 1; /* Open */793		sg->gate_enabled = 1;794		sg->gce[0].gate_state = 1;795		sg->gce[0].interval = SPX5_PSFP_SG_CYCLE_TIME_DEFAULT;796		sg->gce[0].ipv = 0;797		sg->gce[0].maxoctets = 0; /* Disabled */798	}799 800	ret = sparx5_psfp_sg_add(sparx5, sg_idx, sg, &psfp_sgid);801	if (ret < 0)802		return ret;803 804	if (pol_idx >= 0) {805		/* Add new flow-meter */806		ret = sparx5_psfp_fm_add(sparx5, pol_idx, fm, &psfp_fmid);807		if (ret < 0)808			return ret;809	}810 811	/* Map stream filter to stream gate */812	sf->sgid = psfp_sgid;813 814	/* Add new stream-filter and map it to a steam gate */815	ret = sparx5_psfp_sf_add(sparx5, sf, &psfp_sfid);816	if (ret < 0)817		return ret;818 819	/* Streams are classified by ISDX - map ISDX 1:1 to sfid for now. */820	sparx5_isdx_conf_set(sparx5, psfp_sfid, psfp_sfid, psfp_fmid);821 822	ret = vcap_rule_add_action_bit(vrule, VCAP_AF_ISDX_ADD_REPLACE_SEL,823				       VCAP_BIT_1);824	if (ret)825		return ret;826 827	ret = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_VAL, psfp_sfid);828	if (ret)829		return ret;830 831	return 0;832}833 834/* Handle the action trap for a VCAP rule */835static int sparx5_tc_action_trap(struct vcap_admin *admin,836				 struct vcap_rule *vrule,837				 struct flow_cls_offload *fco)838{839	int err = 0;840 841	switch (admin->vtype) {842	case VCAP_TYPE_IS2:843		err = vcap_rule_add_action_bit(vrule,844					       VCAP_AF_CPU_COPY_ENA,845					       VCAP_BIT_1);846		if (err)847			break;848		err = vcap_rule_add_action_u32(vrule,849					       VCAP_AF_CPU_QUEUE_NUM, 0);850		if (err)851			break;852		err = vcap_rule_add_action_u32(vrule,853					       VCAP_AF_MASK_MODE,854					       SPX5_PMM_REPLACE_ALL);855		break;856	case VCAP_TYPE_ES0:857		err = vcap_rule_add_action_u32(vrule,858					       VCAP_AF_FWD_SEL,859					       SPX5_FWSEL_REDIRECT_TO_LOOPBACK);860		break;861	case VCAP_TYPE_ES2:862		err = vcap_rule_add_action_bit(vrule,863					       VCAP_AF_CPU_COPY_ENA,864					       VCAP_BIT_1);865		if (err)866			break;867		err = vcap_rule_add_action_u32(vrule,868					       VCAP_AF_CPU_QUEUE_NUM, 0);869		break;870	default:871		NL_SET_ERR_MSG_MOD(fco->common.extack,872				   "Trap action not supported in this VCAP");873		err = -EOPNOTSUPP;874		break;875	}876	return err;877}878 879static int sparx5_tc_action_vlan_pop(struct vcap_admin *admin,880				     struct vcap_rule *vrule,881				     struct flow_cls_offload *fco,882				     u16 tpid)883{884	int err = 0;885 886	switch (admin->vtype) {887	case VCAP_TYPE_ES0:888		break;889	default:890		NL_SET_ERR_MSG_MOD(fco->common.extack,891				   "VLAN pop action not supported in this VCAP");892		return -EOPNOTSUPP;893	}894 895	switch (tpid) {896	case ETH_P_8021Q:897	case ETH_P_8021AD:898		err = vcap_rule_add_action_u32(vrule,899					       VCAP_AF_PUSH_OUTER_TAG,900					       SPX5_OTAG_UNTAG);901		break;902	default:903		NL_SET_ERR_MSG_MOD(fco->common.extack,904				   "Invalid vlan proto");905		err = -EINVAL;906	}907	return err;908}909 910static int sparx5_tc_action_vlan_modify(struct vcap_admin *admin,911					struct vcap_rule *vrule,912					struct flow_cls_offload *fco,913					struct flow_action_entry *act,914					u16 tpid)915{916	int err = 0;917 918	switch (admin->vtype) {919	case VCAP_TYPE_ES0:920		err = vcap_rule_add_action_u32(vrule,921					       VCAP_AF_PUSH_OUTER_TAG,922					       SPX5_OTAG_TAG_A);923		if (err)924			return err;925		break;926	default:927		NL_SET_ERR_MSG_MOD(fco->common.extack,928				   "VLAN modify action not supported in this VCAP");929		return -EOPNOTSUPP;930	}931 932	switch (tpid) {933	case ETH_P_8021Q:934		err = vcap_rule_add_action_u32(vrule,935					       VCAP_AF_TAG_A_TPID_SEL,936					       SPX5_TPID_A_8100);937		break;938	case ETH_P_8021AD:939		err = vcap_rule_add_action_u32(vrule,940					       VCAP_AF_TAG_A_TPID_SEL,941					       SPX5_TPID_A_88A8);942		break;943	default:944		NL_SET_ERR_MSG_MOD(fco->common.extack,945				   "Invalid vlan proto");946		err = -EINVAL;947	}948	if (err)949		return err;950 951	err = vcap_rule_add_action_u32(vrule,952				       VCAP_AF_TAG_A_VID_SEL,953				       SPX5_VID_A_VAL);954	if (err)955		return err;956 957	err = vcap_rule_add_action_u32(vrule,958				       VCAP_AF_VID_A_VAL,959				       act->vlan.vid);960	if (err)961		return err;962 963	err = vcap_rule_add_action_u32(vrule,964				       VCAP_AF_TAG_A_PCP_SEL,965				       SPX5_PCP_A_VAL);966	if (err)967		return err;968 969	err = vcap_rule_add_action_u32(vrule,970				       VCAP_AF_PCP_A_VAL,971				       act->vlan.prio);972	if (err)973		return err;974 975	return vcap_rule_add_action_u32(vrule,976					VCAP_AF_TAG_A_DEI_SEL,977					SPX5_DEI_A_CLASSIFIED);978}979 980static int sparx5_tc_action_vlan_push(struct vcap_admin *admin,981				      struct vcap_rule *vrule,982				      struct flow_cls_offload *fco,983				      struct flow_action_entry *act,984				      u16 tpid)985{986	u16 act_tpid = be16_to_cpu(act->vlan.proto);987	int err = 0;988 989	switch (admin->vtype) {990	case VCAP_TYPE_ES0:991		break;992	default:993		NL_SET_ERR_MSG_MOD(fco->common.extack,994				   "VLAN push action not supported in this VCAP");995		return -EOPNOTSUPP;996	}997 998	if (tpid == ETH_P_8021AD) {999		NL_SET_ERR_MSG_MOD(fco->common.extack,1000				   "Cannot push on double tagged frames");1001		return -EOPNOTSUPP;1002	}1003 1004	err = sparx5_tc_action_vlan_modify(admin, vrule, fco, act, act_tpid);1005	if (err)1006		return err;1007 1008	switch (act_tpid) {1009	case ETH_P_8021Q:1010		break;1011	case ETH_P_8021AD:1012		/* Push classified tag as inner tag */1013		err = vcap_rule_add_action_u32(vrule,1014					       VCAP_AF_PUSH_INNER_TAG,1015					       SPX5_ITAG_PUSH_B_TAG);1016		if (err)1017			break;1018		err = vcap_rule_add_action_u32(vrule,1019					       VCAP_AF_TAG_B_TPID_SEL,1020					       SPX5_TPID_B_CLASSIFIED);1021		break;1022	default:1023		NL_SET_ERR_MSG_MOD(fco->common.extack,1024				   "Invalid vlan proto");1025		err = -EINVAL;1026	}1027	return err;1028}1029 1030static void sparx5_tc_flower_set_port_mask(struct vcap_u72_action *ports,1031					   struct net_device *ndev)1032{1033	struct sparx5_port *port = netdev_priv(ndev);1034	int byidx = port->portno / BITS_PER_BYTE;1035	int biidx = port->portno % BITS_PER_BYTE;1036 1037	ports->value[byidx] |= BIT(biidx);1038}1039 1040static int sparx5_tc_action_mirred(struct vcap_admin *admin,1041				   struct vcap_rule *vrule,1042				   struct flow_cls_offload *fco,1043				   struct flow_action_entry *act)1044{1045	struct vcap_u72_action ports = {0};1046	int err;1047 1048	if (admin->vtype != VCAP_TYPE_IS0 && admin->vtype != VCAP_TYPE_IS2) {1049		NL_SET_ERR_MSG_MOD(fco->common.extack,1050				   "Mirror action not supported in this VCAP");1051		return -EOPNOTSUPP;1052	}1053 1054	err = vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,1055				       SPX5_PMM_OR_DSTMASK);1056	if (err)1057		return err;1058 1059	sparx5_tc_flower_set_port_mask(&ports, act->dev);1060 1061	return vcap_rule_add_action_u72(vrule, VCAP_AF_PORT_MASK, &ports);1062}1063 1064static int sparx5_tc_action_redirect(struct vcap_admin *admin,1065				     struct vcap_rule *vrule,1066				     struct flow_cls_offload *fco,1067				     struct flow_action_entry *act)1068{1069	struct vcap_u72_action ports = {0};1070	int err;1071 1072	if (admin->vtype != VCAP_TYPE_IS0 && admin->vtype != VCAP_TYPE_IS2) {1073		NL_SET_ERR_MSG_MOD(fco->common.extack,1074				   "Redirect action not supported in this VCAP");1075		return -EOPNOTSUPP;1076	}1077 1078	err = vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,1079				       SPX5_PMM_REPLACE_ALL);1080	if (err)1081		return err;1082 1083	sparx5_tc_flower_set_port_mask(&ports, act->dev);1084 1085	return vcap_rule_add_action_u72(vrule, VCAP_AF_PORT_MASK, &ports);1086}1087 1088/* Remove rule keys that may prevent templates from matching a keyset */1089static void sparx5_tc_flower_simplify_rule(struct vcap_admin *admin,1090					   struct vcap_rule *vrule,1091					   u16 l3_proto)1092{1093	switch (admin->vtype) {1094	case VCAP_TYPE_IS0:1095		vcap_rule_rem_key(vrule, VCAP_KF_ETYPE);1096		switch (l3_proto) {1097		case ETH_P_IP:1098			break;1099		case ETH_P_IPV6:1100			vcap_rule_rem_key(vrule, VCAP_KF_IP_SNAP_IS);1101			break;1102		default:1103			break;1104		}1105		break;1106	case VCAP_TYPE_ES2:1107		switch (l3_proto) {1108		case ETH_P_IP:1109			if (vrule->keyset == VCAP_KFS_IP4_OTHER)1110				vcap_rule_rem_key(vrule, VCAP_KF_TCP_IS);1111			break;1112		case ETH_P_IPV6:1113			if (vrule->keyset == VCAP_KFS_IP6_STD)1114				vcap_rule_rem_key(vrule, VCAP_KF_TCP_IS);1115			vcap_rule_rem_key(vrule, VCAP_KF_IP4_IS);1116			break;1117		default:1118			break;1119		}1120		break;1121	case VCAP_TYPE_IS2:1122		switch (l3_proto) {1123		case ETH_P_IP:1124		case ETH_P_IPV6:1125			vcap_rule_rem_key(vrule, VCAP_KF_IP4_IS);1126			break;1127		default:1128			break;1129		}1130		break;1131	default:1132		break;1133	}1134}1135 1136static bool sparx5_tc_flower_use_template(struct net_device *ndev,1137					  struct flow_cls_offload *fco,1138					  struct vcap_admin *admin,1139					  struct vcap_rule *vrule)1140{1141	struct sparx5_port *port = netdev_priv(ndev);1142	struct sparx5_tc_flower_template *ftp;1143 1144	list_for_each_entry(ftp, &port->tc_templates, list) {1145		if (ftp->cid != fco->common.chain_index)1146			continue;1147 1148		vcap_set_rule_set_keyset(vrule, ftp->keyset);1149		sparx5_tc_flower_simplify_rule(admin, vrule, ftp->l3_proto);1150		return true;1151	}1152	return false;1153}1154 1155static int sparx5_tc_flower_replace(struct net_device *ndev,1156				    struct flow_cls_offload *fco,1157				    struct vcap_admin *admin,1158				    bool ingress)1159{1160	struct sparx5_psfp_sf sf = { .max_sdu = SPX5_PSFP_SF_MAX_SDU };1161	struct netlink_ext_ack *extack = fco->common.extack;1162	int err, idx, tc_sg_idx = -1, tc_pol_idx = -1;1163	struct vcap_tc_flower_parse_usage state = {1164		.fco = fco,1165		.l3_proto = ETH_P_ALL,1166		.admin = admin,1167	};1168	struct sparx5_port *port = netdev_priv(ndev);1169	struct sparx5_multiple_rules multi = {};1170	struct sparx5 *sparx5 = port->sparx5;1171	struct sparx5_psfp_sg sg = { 0 };1172	struct sparx5_psfp_fm fm = { 0 };1173	struct flow_action_entry *act;1174	struct vcap_control *vctrl;1175	struct flow_rule *frule;1176	struct vcap_rule *vrule;1177 1178	vctrl = port->sparx5->vcap_ctrl;1179 1180	err = sparx5_tc_flower_action_check(vctrl, ndev, fco, ingress);1181	if (err)1182		return err;1183 1184	vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC,1185				fco->common.prio, 0);1186	if (IS_ERR(vrule))1187		return PTR_ERR(vrule);1188 1189	vrule->cookie = fco->cookie;1190 1191	state.vrule = vrule;1192	state.frule = flow_cls_offload_flow_rule(fco);1193	err = sparx5_tc_use_dissectors(&state, admin, vrule);1194	if (err)1195		goto out;1196 1197	err = sparx5_tc_add_rule_counter(admin, vrule);1198	if (err)1199		goto out;1200 1201	err = sparx5_tc_add_rule_link_target(admin, vrule,1202					     fco->common.chain_index);1203	if (err)1204		goto out;1205 1206	frule = flow_cls_offload_flow_rule(fco);1207	flow_action_for_each(idx, act, &frule->action) {1208		switch (act->id) {1209		case FLOW_ACTION_GATE: {1210			err = sparx5_tc_flower_parse_act_gate(&sg, act, extack);1211			if (err < 0)1212				goto out;1213 1214			tc_sg_idx = act->hw_index;1215 1216			break;1217		}1218		case FLOW_ACTION_POLICE: {1219			err = sparx5_tc_flower_parse_act_police(&fm.pol, act,1220								extack);1221			if (err < 0)1222				goto out;1223 1224			tc_pol_idx = fm.pol.idx;1225			sf.max_sdu = act->police.mtu;1226 1227			break;1228		}1229		case FLOW_ACTION_TRAP:1230			err = sparx5_tc_action_trap(admin, vrule, fco);1231			if (err)1232				goto out;1233			break;1234		case FLOW_ACTION_MIRRED:1235			err = sparx5_tc_action_mirred(admin, vrule, fco, act);1236			if (err)1237				goto out;1238			break;1239		case FLOW_ACTION_REDIRECT:1240			err = sparx5_tc_action_redirect(admin, vrule, fco, act);1241			if (err)1242				goto out;1243			break;1244		case FLOW_ACTION_ACCEPT:1245			err = sparx5_tc_set_actionset(admin, vrule);1246			if (err)1247				goto out;1248			break;1249		case FLOW_ACTION_GOTO:1250			err = sparx5_tc_set_actionset(admin, vrule);1251			if (err)1252				goto out;1253			sparx5_tc_add_rule_link(vctrl, admin, vrule,1254						fco->common.chain_index,1255						act->chain_index);1256			break;1257		case FLOW_ACTION_VLAN_POP:1258			err = sparx5_tc_action_vlan_pop(admin, vrule, fco,1259							state.tpid);1260			if (err)1261				goto out;1262			break;1263		case FLOW_ACTION_VLAN_PUSH:1264			err = sparx5_tc_action_vlan_push(admin, vrule, fco,1265							 act, state.tpid);1266			if (err)1267				goto out;1268			break;1269		case FLOW_ACTION_VLAN_MANGLE:1270			err = sparx5_tc_action_vlan_modify(admin, vrule, fco,1271							   act, state.tpid);1272			if (err)1273				goto out;1274			break;1275		default:1276			NL_SET_ERR_MSG_MOD(fco->common.extack,1277					   "Unsupported TC action");1278			err = -EOPNOTSUPP;1279			goto out;1280		}1281	}1282 1283	/* Setup PSFP */1284	if (tc_sg_idx >= 0 || tc_pol_idx >= 0) {1285		err = sparx5_tc_flower_psfp_setup(sparx5, vrule, tc_sg_idx,1286						  tc_pol_idx, &sg, &fm, &sf);1287		if (err)1288			goto out;1289	}1290 1291	if (!sparx5_tc_flower_use_template(ndev, fco, admin, vrule)) {1292		err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin,1293						       state.l3_proto, &multi);1294		if (err) {1295			NL_SET_ERR_MSG_MOD(fco->common.extack,1296					   "No matching port keyset for filter protocol and keys");1297			goto out;1298		}1299	}1300 1301	/* provide the l3 protocol to guide the keyset selection */1302	err = vcap_val_rule(vrule, state.l3_proto);1303	if (err) {1304		vcap_set_tc_exterr(fco, vrule);1305		goto out;1306	}1307	err = vcap_add_rule(vrule);1308	if (err)1309		NL_SET_ERR_MSG_MOD(fco->common.extack,1310				   "Could not add the filter");1311 1312	if (state.l3_proto == ETH_P_ALL)1313		err = sparx5_tc_add_remaining_rules(vctrl, fco, vrule, admin,1314						    &multi);1315 1316out:1317	vcap_free_rule(vrule);1318	return err;1319}1320 1321static void sparx5_tc_free_psfp_resources(struct sparx5 *sparx5,1322					  struct vcap_rule *vrule)1323{1324	struct vcap_client_actionfield *afield;1325	u32 isdx, sfid, sgid, fmid;1326 1327	/* Check if VCAP_AF_ISDX_VAL action is set for this rule - and if1328	 * it is used for stream and/or flow-meter classification.1329	 */1330	afield = vcap_find_actionfield(vrule, VCAP_AF_ISDX_VAL);1331	if (!afield)1332		return;1333 1334	isdx = afield->data.u32.value;1335	sfid = sparx5_psfp_isdx_get_sf(sparx5, isdx);1336 1337	if (!sfid)1338		return;1339 1340	fmid = sparx5_psfp_isdx_get_fm(sparx5, isdx);1341	sgid = sparx5_psfp_sf_get_sg(sparx5, sfid);1342 1343	if (fmid && sparx5_psfp_fm_del(sparx5, fmid) < 0)1344		pr_err("%s:%d Could not delete invalid fmid: %d", __func__,1345		       __LINE__, fmid);1346 1347	if (sgid && sparx5_psfp_sg_del(sparx5, sgid) < 0)1348		pr_err("%s:%d Could not delete invalid sgid: %d", __func__,1349		       __LINE__, sgid);1350 1351	if (sparx5_psfp_sf_del(sparx5, sfid) < 0)1352		pr_err("%s:%d Could not delete invalid sfid: %d", __func__,1353		       __LINE__, sfid);1354 1355	sparx5_isdx_conf_set(sparx5, isdx, 0, 0);1356}1357 1358static int sparx5_tc_free_rule_resources(struct net_device *ndev,1359					 struct vcap_control *vctrl,1360					 int rule_id)1361{1362	struct sparx5_port *port = netdev_priv(ndev);1363	struct sparx5 *sparx5 = port->sparx5;1364	struct vcap_rule *vrule;1365	int ret = 0;1366 1367	vrule = vcap_get_rule(vctrl, rule_id);1368	if (IS_ERR(vrule))1369		return -EINVAL;1370 1371	sparx5_tc_free_psfp_resources(sparx5, vrule);1372 1373	vcap_free_rule(vrule);1374	return ret;1375}1376 1377static int sparx5_tc_flower_destroy(struct net_device *ndev,1378				    struct flow_cls_offload *fco,1379				    struct vcap_admin *admin)1380{1381	struct sparx5_port *port = netdev_priv(ndev);1382	int err = -ENOENT, count = 0, rule_id;1383	struct vcap_control *vctrl;1384 1385	vctrl = port->sparx5->vcap_ctrl;1386	while (true) {1387		rule_id = vcap_lookup_rule_by_cookie(vctrl, fco->cookie);1388		if (rule_id <= 0)1389			break;1390		if (count == 0) {1391			/* Resources are attached to the first rule of1392			 * a set of rules. Only works if the rules are1393			 * in the correct order.1394			 */1395			err = sparx5_tc_free_rule_resources(ndev, vctrl,1396							    rule_id);1397			if (err)1398				pr_err("%s:%d: could not free resources %d\n",1399				       __func__, __LINE__, rule_id);1400		}1401		err = vcap_del_rule(vctrl, ndev, rule_id);1402		if (err) {1403			pr_err("%s:%d: could not delete rule %d\n",1404			       __func__, __LINE__, rule_id);1405			break;1406		}1407	}1408	return err;1409}1410 1411static int sparx5_tc_flower_stats(struct net_device *ndev,1412				  struct flow_cls_offload *fco,1413				  struct vcap_admin *admin)1414{1415	struct sparx5_port *port = netdev_priv(ndev);1416	struct vcap_counter ctr = {};1417	struct vcap_control *vctrl;1418	ulong lastused = 0;1419	int err;1420 1421	vctrl = port->sparx5->vcap_ctrl;1422	err = vcap_get_rule_count_by_cookie(vctrl, &ctr, fco->cookie);1423	if (err)1424		return err;1425	flow_stats_update(&fco->stats, 0x0, ctr.value, 0, lastused,1426			  FLOW_ACTION_HW_STATS_IMMEDIATE);1427	return err;1428}1429 1430static int sparx5_tc_flower_template_create(struct net_device *ndev,1431					    struct flow_cls_offload *fco,1432					    struct vcap_admin *admin)1433{1434	struct sparx5_port *port = netdev_priv(ndev);1435	struct vcap_tc_flower_parse_usage state = {1436		.fco = fco,1437		.l3_proto = ETH_P_ALL,1438		.admin = admin,1439	};1440	struct sparx5_tc_flower_template *ftp;1441	struct vcap_keyset_list kslist = {};1442	enum vcap_keyfield_set keysets[10];1443	struct vcap_control *vctrl;1444	struct vcap_rule *vrule;1445	int count, err;1446 1447	if (admin->vtype == VCAP_TYPE_ES0) {1448		pr_err("%s:%d: %s\n", __func__, __LINE__,1449		       "VCAP does not support templates");1450		return -EINVAL;1451	}1452 1453	count = vcap_admin_rule_count(admin, fco->common.chain_index);1454	if (count > 0) {1455		pr_err("%s:%d: %s\n", __func__, __LINE__,1456		       "Filters are already present");1457		return -EBUSY;1458	}1459 1460	ftp = kzalloc(sizeof(*ftp), GFP_KERNEL);1461	if (!ftp)1462		return -ENOMEM;1463 1464	ftp->cid = fco->common.chain_index;1465	ftp->orig = VCAP_KFS_NO_VALUE;1466	ftp->keyset = VCAP_KFS_NO_VALUE;1467 1468	vctrl = port->sparx5->vcap_ctrl;1469	vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index,1470				VCAP_USER_TC, fco->common.prio, 0);1471	if (IS_ERR(vrule)) {1472		err = PTR_ERR(vrule);1473		goto err_rule;1474	}1475 1476	state.vrule = vrule;1477	state.frule = flow_cls_offload_flow_rule(fco);1478	err = sparx5_tc_use_dissectors(&state, admin, vrule);1479	if (err) {1480		pr_err("%s:%d: key error: %d\n", __func__, __LINE__, err);1481		goto out;1482	}1483 1484	ftp->l3_proto = state.l3_proto;1485 1486	sparx5_tc_flower_simplify_rule(admin, vrule, state.l3_proto);1487 1488	/* Find the keysets that the rule can use */1489	kslist.keysets = keysets;1490	kslist.max = ARRAY_SIZE(keysets);1491	if (!vcap_rule_find_keysets(vrule, &kslist)) {1492		pr_err("%s:%d: %s\n", __func__, __LINE__,1493		       "Could not find a suitable keyset");1494		err = -ENOENT;1495		goto out;1496	}1497 1498	ftp->keyset = vcap_select_min_rule_keyset(vctrl, admin->vtype, &kslist);1499	kslist.cnt = 0;1500	sparx5_vcap_set_port_keyset(ndev, admin, fco->common.chain_index,1501				    state.l3_proto,1502				    ftp->keyset,1503				    &kslist);1504 1505	if (kslist.cnt > 0)1506		ftp->orig = kslist.keysets[0];1507 1508	/* Store new template */1509	list_add_tail(&ftp->list, &port->tc_templates);1510	vcap_free_rule(vrule);1511	return 0;1512 1513out:1514	vcap_free_rule(vrule);1515err_rule:1516	kfree(ftp);1517	return err;1518}1519 1520static int sparx5_tc_flower_template_destroy(struct net_device *ndev,1521					     struct flow_cls_offload *fco,1522					     struct vcap_admin *admin)1523{1524	struct sparx5_port *port = netdev_priv(ndev);1525	struct sparx5_tc_flower_template *ftp, *tmp;1526	int err = -ENOENT;1527 1528	/* Rules using the template are removed by the tc framework */1529	list_for_each_entry_safe(ftp, tmp, &port->tc_templates, list) {1530		if (ftp->cid != fco->common.chain_index)1531			continue;1532 1533		sparx5_vcap_set_port_keyset(ndev, admin,1534					    fco->common.chain_index,1535					    ftp->l3_proto, ftp->orig,1536					    NULL);1537		list_del(&ftp->list);1538		kfree(ftp);1539		break;1540	}1541	return err;1542}1543 1544int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco,1545		     bool ingress)1546{1547	struct sparx5_port *port = netdev_priv(ndev);1548	struct vcap_control *vctrl;1549	struct vcap_admin *admin;1550	int err = -EINVAL;1551 1552	/* Get vcap instance from the chain id */1553	vctrl = port->sparx5->vcap_ctrl;1554	admin = vcap_find_admin(vctrl, fco->common.chain_index);1555	if (!admin) {1556		NL_SET_ERR_MSG_MOD(fco->common.extack, "Invalid chain");1557		return err;1558	}1559 1560	switch (fco->command) {1561	case FLOW_CLS_REPLACE:1562		return sparx5_tc_flower_replace(ndev, fco, admin, ingress);1563	case FLOW_CLS_DESTROY:1564		return sparx5_tc_flower_destroy(ndev, fco, admin);1565	case FLOW_CLS_STATS:1566		return sparx5_tc_flower_stats(ndev, fco, admin);1567	case FLOW_CLS_TMPLT_CREATE:1568		return sparx5_tc_flower_template_create(ndev, fco, admin);1569	case FLOW_CLS_TMPLT_DESTROY:1570		return sparx5_tc_flower_template_destroy(ndev, fco, admin);1571	default:1572		return -EOPNOTSUPP;1573	}1574}1575