1c9da1ac1SSteen Hegelund // SPDX-License-Identifier: GPL-2.0+
2c9da1ac1SSteen Hegelund /* Microchip VCAP API
3c9da1ac1SSteen Hegelund  *
4c9da1ac1SSteen Hegelund  * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
5c9da1ac1SSteen Hegelund  */
6c9da1ac1SSteen Hegelund 
76ebf182bSDaniel Machon #include <net/tc_act/tc_gate.h>
8c9da1ac1SSteen Hegelund #include <net/tcp.h>
9c9da1ac1SSteen Hegelund 
10c9da1ac1SSteen Hegelund #include "sparx5_tc.h"
11c9da1ac1SSteen Hegelund #include "vcap_api.h"
12c9da1ac1SSteen Hegelund #include "vcap_api_client.h"
1347400aaeSHoratiu Vultur #include "vcap_tc.h"
14c9da1ac1SSteen Hegelund #include "sparx5_main.h"
15c9da1ac1SSteen Hegelund #include "sparx5_vcap_impl.h"
16c9da1ac1SSteen Hegelund 
170ca60948SSteen Hegelund #define SPX5_MAX_RULE_SIZE 13 /* allows X1, X2, X4, X6 and X12 rules */
180ca60948SSteen Hegelund 
190ca60948SSteen Hegelund /* Collect keysets and type ids for multiple rules per size */
200ca60948SSteen Hegelund struct sparx5_wildcard_rule {
210ca60948SSteen Hegelund 	bool selected;
220ca60948SSteen Hegelund 	u8 value;
230ca60948SSteen Hegelund 	u8 mask;
240ca60948SSteen Hegelund 	enum vcap_keyfield_set keyset;
250ca60948SSteen Hegelund };
260ca60948SSteen Hegelund 
270ca60948SSteen Hegelund struct sparx5_multiple_rules {
280ca60948SSteen Hegelund 	struct sparx5_wildcard_rule rule[SPX5_MAX_RULE_SIZE];
290ca60948SSteen Hegelund };
300ca60948SSteen Hegelund 
31e1d597ecSSteen Hegelund struct sparx5_tc_flower_template {
32e1d597ecSSteen Hegelund 	struct list_head list; /* for insertion in the list of templates */
33e1d597ecSSteen Hegelund 	int cid; /* chain id */
34e1d597ecSSteen Hegelund 	enum vcap_keyfield_set orig; /* keyset used before the template */
35e1d597ecSSteen Hegelund 	enum vcap_keyfield_set keyset; /* new keyset used by template */
36e1d597ecSSteen Hegelund 	u16 l3_proto; /* protocol specified in the template */
37e1d597ecSSteen Hegelund };
38e1d597ecSSteen Hegelund 
39d6c2964dSSteen Hegelund static int
40ebf44dedSSteen Hegelund sparx5_tc_flower_es0_tpid(struct vcap_tc_flower_parse_usage *st)
41ebf44dedSSteen Hegelund {
42ebf44dedSSteen Hegelund 	int err = 0;
43ebf44dedSSteen Hegelund 
44ebf44dedSSteen Hegelund 	switch (st->tpid) {
45ebf44dedSSteen Hegelund 	case ETH_P_8021Q:
46ebf44dedSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule,
47ebf44dedSSteen Hegelund 					    VCAP_KF_8021Q_TPID,
48ebf44dedSSteen Hegelund 					    SPX5_TPID_SEL_8100, ~0);
49ebf44dedSSteen Hegelund 		break;
50ebf44dedSSteen Hegelund 	case ETH_P_8021AD:
51ebf44dedSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule,
52ebf44dedSSteen Hegelund 					    VCAP_KF_8021Q_TPID,
53ebf44dedSSteen Hegelund 					    SPX5_TPID_SEL_88A8, ~0);
54ebf44dedSSteen Hegelund 		break;
55ebf44dedSSteen Hegelund 	default:
56ebf44dedSSteen Hegelund 		NL_SET_ERR_MSG_MOD(st->fco->common.extack,
57ebf44dedSSteen Hegelund 				   "Invalid vlan proto");
58ebf44dedSSteen Hegelund 		err = -EINVAL;
59ebf44dedSSteen Hegelund 		break;
60ebf44dedSSteen Hegelund 	}
61ebf44dedSSteen Hegelund 	return err;
62ebf44dedSSteen Hegelund }
63ebf44dedSSteen Hegelund 
64ebf44dedSSteen Hegelund static int
6547400aaeSHoratiu Vultur sparx5_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st)
66d6c2964dSSteen Hegelund {
6747400aaeSHoratiu Vultur 	struct flow_match_basic mt;
68d6c2964dSSteen Hegelund 	int err = 0;
69d6c2964dSSteen Hegelund 
7047400aaeSHoratiu Vultur 	flow_rule_match_basic(st->frule, &mt);
71d6c2964dSSteen Hegelund 
7247400aaeSHoratiu Vultur 	if (mt.mask->n_proto) {
7347400aaeSHoratiu Vultur 		st->l3_proto = be16_to_cpu(mt.key->n_proto);
7447400aaeSHoratiu Vultur 		if (!sparx5_vcap_is_known_etype(st->admin, st->l3_proto)) {
7547400aaeSHoratiu Vultur 			err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,
7647400aaeSHoratiu Vultur 						    st->l3_proto, ~0);
7747400aaeSHoratiu Vultur 			if (err)
7847400aaeSHoratiu Vultur 				goto out;
7947400aaeSHoratiu Vultur 		} else if (st->l3_proto == ETH_P_IP) {
8047400aaeSHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
8147400aaeSHoratiu Vultur 						    VCAP_BIT_1);
8247400aaeSHoratiu Vultur 			if (err)
8347400aaeSHoratiu Vultur 				goto out;
8447400aaeSHoratiu Vultur 		} else if (st->l3_proto == ETH_P_IPV6) {
8547400aaeSHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
8647400aaeSHoratiu Vultur 						    VCAP_BIT_0);
8747400aaeSHoratiu Vultur 			if (err)
8847400aaeSHoratiu Vultur 				goto out;
8947400aaeSHoratiu Vultur 			if (st->admin->vtype == VCAP_TYPE_IS0) {
9047400aaeSHoratiu Vultur 				err = vcap_rule_add_key_bit(st->vrule,
9147400aaeSHoratiu Vultur 							    VCAP_KF_IP_SNAP_IS,
9247400aaeSHoratiu Vultur 							    VCAP_BIT_1);
9347400aaeSHoratiu Vultur 				if (err)
9447400aaeSHoratiu Vultur 					goto out;
9547400aaeSHoratiu Vultur 			}
9647400aaeSHoratiu Vultur 		}
9747400aaeSHoratiu Vultur 	}
9847400aaeSHoratiu Vultur 
9947400aaeSHoratiu Vultur 	if (mt.mask->ip_proto) {
10047400aaeSHoratiu Vultur 		st->l4_proto = mt.key->ip_proto;
10147400aaeSHoratiu Vultur 		if (st->l4_proto == IPPROTO_TCP) {
10247400aaeSHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule,
10347400aaeSHoratiu Vultur 						    VCAP_KF_TCP_IS,
10447400aaeSHoratiu Vultur 						    VCAP_BIT_1);
10547400aaeSHoratiu Vultur 			if (err)
10647400aaeSHoratiu Vultur 				goto out;
10747400aaeSHoratiu Vultur 		} else if (st->l4_proto == IPPROTO_UDP) {
10847400aaeSHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule,
10947400aaeSHoratiu Vultur 						    VCAP_KF_TCP_IS,
11047400aaeSHoratiu Vultur 						    VCAP_BIT_0);
11147400aaeSHoratiu Vultur 			if (err)
11247400aaeSHoratiu Vultur 				goto out;
11347400aaeSHoratiu Vultur 			if (st->admin->vtype == VCAP_TYPE_IS0) {
11447400aaeSHoratiu Vultur 				err = vcap_rule_add_key_bit(st->vrule,
11547400aaeSHoratiu Vultur 							    VCAP_KF_TCP_UDP_IS,
11647400aaeSHoratiu Vultur 							    VCAP_BIT_1);
11747400aaeSHoratiu Vultur 				if (err)
11847400aaeSHoratiu Vultur 					goto out;
11947400aaeSHoratiu Vultur 			}
12047400aaeSHoratiu Vultur 		} else {
121d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
12247400aaeSHoratiu Vultur 						    VCAP_KF_L3_IP_PROTO,
12347400aaeSHoratiu Vultur 						    st->l4_proto, ~0);
124d6c2964dSSteen Hegelund 			if (err)
125d6c2964dSSteen Hegelund 				goto out;
126d6c2964dSSteen Hegelund 		}
127d6c2964dSSteen Hegelund 	}
128d6c2964dSSteen Hegelund 
129*2b3082c6SRatheesh Kannoth 	st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_BASIC);
130d6c2964dSSteen Hegelund 
131d6c2964dSSteen Hegelund 	return err;
132d6c2964dSSteen Hegelund 
133d6c2964dSSteen Hegelund out:
13447400aaeSHoratiu Vultur 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error");
135d6c2964dSSteen Hegelund 	return err;
136d6c2964dSSteen Hegelund }
137d6c2964dSSteen Hegelund 
138d6c2964dSSteen Hegelund static int
13947400aaeSHoratiu Vultur sparx5_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage *st)
140d6c2964dSSteen Hegelund {
141d6c2964dSSteen Hegelund 	struct flow_match_control mt;
142d6c2964dSSteen Hegelund 	u32 value, mask;
143d6c2964dSSteen Hegelund 	int err = 0;
144d6c2964dSSteen Hegelund 
145d6c2964dSSteen Hegelund 	flow_rule_match_control(st->frule, &mt);
146d6c2964dSSteen Hegelund 
147d6c2964dSSteen Hegelund 	if (mt.mask->flags) {
148d6c2964dSSteen Hegelund 		if (mt.mask->flags & FLOW_DIS_FIRST_FRAG) {
149d6c2964dSSteen Hegelund 			if (mt.key->flags & FLOW_DIS_FIRST_FRAG) {
150d6c2964dSSteen Hegelund 				value = 1; /* initial fragment */
151d6c2964dSSteen Hegelund 				mask = 0x3;
152d6c2964dSSteen Hegelund 			} else {
153d6c2964dSSteen Hegelund 				if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) {
154d6c2964dSSteen Hegelund 					value = 3; /* follow up fragment */
155d6c2964dSSteen Hegelund 					mask = 0x3;
156d6c2964dSSteen Hegelund 				} else {
157d6c2964dSSteen Hegelund 					value = 0; /* no fragment */
158d6c2964dSSteen Hegelund 					mask = 0x3;
159d6c2964dSSteen Hegelund 				}
160d6c2964dSSteen Hegelund 			}
161d6c2964dSSteen Hegelund 		} else {
162d6c2964dSSteen Hegelund 			if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) {
163d6c2964dSSteen Hegelund 				value = 3; /* follow up fragment */
164d6c2964dSSteen Hegelund 				mask = 0x3;
165d6c2964dSSteen Hegelund 			} else {
166d6c2964dSSteen Hegelund 				value = 0; /* no fragment */
167d6c2964dSSteen Hegelund 				mask = 0x3;
168d6c2964dSSteen Hegelund 			}
169d6c2964dSSteen Hegelund 		}
170d6c2964dSSteen Hegelund 
171d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule,
172d6c2964dSSteen Hegelund 					    VCAP_KF_L3_FRAGMENT_TYPE,
173d6c2964dSSteen Hegelund 					    value, mask);
174d6c2964dSSteen Hegelund 		if (err)
175d6c2964dSSteen Hegelund 			goto out;
176d6c2964dSSteen Hegelund 	}
177d6c2964dSSteen Hegelund 
178*2b3082c6SRatheesh Kannoth 	st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL);
179d6c2964dSSteen Hegelund 
180d6c2964dSSteen Hegelund 	return err;
181d6c2964dSSteen Hegelund 
182d6c2964dSSteen Hegelund out:
183d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error");
184d6c2964dSSteen Hegelund 	return err;
185d6c2964dSSteen Hegelund }
186d6c2964dSSteen Hegelund 
187d6c2964dSSteen Hegelund static int
18847400aaeSHoratiu Vultur sparx5_tc_flower_handler_cvlan_usage(struct vcap_tc_flower_parse_usage *st)
189d6c2964dSSteen Hegelund {
190a5300724SSteen Hegelund 	if (st->admin->vtype != VCAP_TYPE_IS0) {
191a5300724SSteen Hegelund 		NL_SET_ERR_MSG_MOD(st->fco->common.extack,
192a5300724SSteen Hegelund 				   "cvlan not supported in this VCAP");
19352df82ccSSteen Hegelund 		return -EINVAL;
194a5300724SSteen Hegelund 	}
19552df82ccSSteen Hegelund 
19647400aaeSHoratiu Vultur 	return vcap_tc_flower_handler_cvlan_usage(st);
19752df82ccSSteen Hegelund }
19852df82ccSSteen Hegelund 
19952df82ccSSteen Hegelund static int
20047400aaeSHoratiu Vultur sparx5_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st)
201d6c2964dSSteen Hegelund {
202d6c2964dSSteen Hegelund 	enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS;
203d6c2964dSSteen Hegelund 	enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS;
204ebf44dedSSteen Hegelund 	int err;
205d6c2964dSSteen Hegelund 
20652df82ccSSteen Hegelund 	if (st->admin->vtype == VCAP_TYPE_IS0) {
20752df82ccSSteen Hegelund 		vid_key = VCAP_KF_8021Q_VID0;
20852df82ccSSteen Hegelund 		pcp_key = VCAP_KF_8021Q_PCP0;
20952df82ccSSteen Hegelund 	}
21052df82ccSSteen Hegelund 
211ebf44dedSSteen Hegelund 	err = vcap_tc_flower_handler_vlan_usage(st, vid_key, pcp_key);
212ebf44dedSSteen Hegelund 	if (err)
213ebf44dedSSteen Hegelund 		return err;
214ebf44dedSSteen Hegelund 
215ebf44dedSSteen Hegelund 	if (st->admin->vtype == VCAP_TYPE_ES0 && st->tpid)
216ebf44dedSSteen Hegelund 		err = sparx5_tc_flower_es0_tpid(st);
217ebf44dedSSteen Hegelund 
218ebf44dedSSteen Hegelund 	return err;
219d6c2964dSSteen Hegelund }
220d6c2964dSSteen Hegelund 
22147400aaeSHoratiu Vultur static int (*sparx5_tc_flower_usage_handlers[])(struct vcap_tc_flower_parse_usage *st) = {
22247400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_ETH_ADDRS] = vcap_tc_flower_handler_ethaddr_usage,
22347400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_IPV4_ADDRS] = vcap_tc_flower_handler_ipv4_usage,
22447400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_IPV6_ADDRS] = vcap_tc_flower_handler_ipv6_usage,
225d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_CONTROL] = sparx5_tc_flower_handler_control_usage,
22647400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_PORTS] = vcap_tc_flower_handler_portnum_usage,
227d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage,
22852df82ccSSteen Hegelund 	[FLOW_DISSECTOR_KEY_CVLAN] = sparx5_tc_flower_handler_cvlan_usage,
229d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage,
23047400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_TCP] = vcap_tc_flower_handler_tcp_usage,
23147400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_ARP] = vcap_tc_flower_handler_arp_usage,
23247400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_IP] = vcap_tc_flower_handler_ip_usage,
233c9da1ac1SSteen Hegelund };
234c9da1ac1SSteen Hegelund 
235ebf44dedSSteen Hegelund static int sparx5_tc_use_dissectors(struct vcap_tc_flower_parse_usage *st,
236c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin,
237ebf44dedSSteen Hegelund 				    struct vcap_rule *vrule)
238c9da1ac1SSteen Hegelund {
239c9da1ac1SSteen Hegelund 	int idx, err = 0;
240c9da1ac1SSteen Hegelund 
241c9da1ac1SSteen Hegelund 	for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) {
242ebf44dedSSteen Hegelund 		if (!flow_rule_match_key(st->frule, idx))
243c9da1ac1SSteen Hegelund 			continue;
244c9da1ac1SSteen Hegelund 		if (!sparx5_tc_flower_usage_handlers[idx])
245c9da1ac1SSteen Hegelund 			continue;
246ebf44dedSSteen Hegelund 		err = sparx5_tc_flower_usage_handlers[idx](st);
247c9da1ac1SSteen Hegelund 		if (err)
248c9da1ac1SSteen Hegelund 			return err;
249c9da1ac1SSteen Hegelund 	}
250abc4010dSSteen Hegelund 
251ebf44dedSSteen Hegelund 	if (st->frule->match.dissector->used_keys ^ st->used_keys) {
252ebf44dedSSteen Hegelund 		NL_SET_ERR_MSG_MOD(st->fco->common.extack,
253abc4010dSSteen Hegelund 				   "Unsupported match item");
254abc4010dSSteen Hegelund 		return -ENOENT;
255abc4010dSSteen Hegelund 	}
256abc4010dSSteen Hegelund 
257c9da1ac1SSteen Hegelund 	return err;
258c9da1ac1SSteen Hegelund }
259c9da1ac1SSteen Hegelund 
260392d0ab0SSteen Hegelund static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
261784c3067SSteen Hegelund 					 struct net_device *ndev,
262e7e3f514SSteen Hegelund 					 struct flow_cls_offload *fco,
263e7e3f514SSteen Hegelund 					 bool ingress)
264392d0ab0SSteen Hegelund {
265392d0ab0SSteen Hegelund 	struct flow_rule *rule = flow_cls_offload_flow_rule(fco);
266392d0ab0SSteen Hegelund 	struct flow_action_entry *actent, *last_actent = NULL;
267392d0ab0SSteen Hegelund 	struct flow_action *act = &rule->action;
268392d0ab0SSteen Hegelund 	u64 action_mask = 0;
269392d0ab0SSteen Hegelund 	int idx;
270392d0ab0SSteen Hegelund 
271392d0ab0SSteen Hegelund 	if (!flow_action_has_entries(act)) {
272392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
273392d0ab0SSteen Hegelund 		return -EINVAL;
274392d0ab0SSteen Hegelund 	}
275392d0ab0SSteen Hegelund 
276392d0ab0SSteen Hegelund 	if (!flow_action_basic_hw_stats_check(act, fco->common.extack))
277392d0ab0SSteen Hegelund 		return -EOPNOTSUPP;
278392d0ab0SSteen Hegelund 
279392d0ab0SSteen Hegelund 	flow_action_for_each(idx, actent, act) {
280392d0ab0SSteen Hegelund 		if (action_mask & BIT(actent->id)) {
281392d0ab0SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
282392d0ab0SSteen Hegelund 					   "More actions of the same type");
283392d0ab0SSteen Hegelund 			return -EINVAL;
284392d0ab0SSteen Hegelund 		}
285392d0ab0SSteen Hegelund 		action_mask |= BIT(actent->id);
286392d0ab0SSteen Hegelund 		last_actent = actent; /* Save last action for later check */
287392d0ab0SSteen Hegelund 	}
288392d0ab0SSteen Hegelund 
289784c3067SSteen Hegelund 	/* Check if last action is a goto
290784c3067SSteen Hegelund 	 * The last chain/lookup does not need to have a goto action
291784c3067SSteen Hegelund 	 */
292784c3067SSteen Hegelund 	if (last_actent->id == FLOW_ACTION_GOTO) {
293784c3067SSteen Hegelund 		/* Check if the destination chain is in one of the VCAPs */
294392d0ab0SSteen Hegelund 		if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
295392d0ab0SSteen Hegelund 					 last_actent->chain_index)) {
296392d0ab0SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
297392d0ab0SSteen Hegelund 					   "Invalid goto chain");
298392d0ab0SSteen Hegelund 			return -EINVAL;
299392d0ab0SSteen Hegelund 		}
300e7e3f514SSteen Hegelund 	} else if (!vcap_is_last_chain(vctrl, fco->common.chain_index,
301e7e3f514SSteen Hegelund 				       ingress)) {
302784c3067SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
303784c3067SSteen Hegelund 				   "Last action must be 'goto'");
304784c3067SSteen Hegelund 		return -EINVAL;
305784c3067SSteen Hegelund 	}
306392d0ab0SSteen Hegelund 
307392d0ab0SSteen Hegelund 	/* Catch unsupported combinations of actions */
308392d0ab0SSteen Hegelund 	if (action_mask & BIT(FLOW_ACTION_TRAP) &&
309392d0ab0SSteen Hegelund 	    action_mask & BIT(FLOW_ACTION_ACCEPT)) {
310392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
311392d0ab0SSteen Hegelund 				   "Cannot combine pass and trap action");
312392d0ab0SSteen Hegelund 		return -EOPNOTSUPP;
313392d0ab0SSteen Hegelund 	}
314392d0ab0SSteen Hegelund 
315ebf44dedSSteen Hegelund 	if (action_mask & BIT(FLOW_ACTION_VLAN_PUSH) &&
316ebf44dedSSteen Hegelund 	    action_mask & BIT(FLOW_ACTION_VLAN_POP)) {
317ebf44dedSSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
318ebf44dedSSteen Hegelund 				   "Cannot combine vlan push and pop action");
319ebf44dedSSteen Hegelund 		return -EOPNOTSUPP;
320ebf44dedSSteen Hegelund 	}
321ebf44dedSSteen Hegelund 
322ebf44dedSSteen Hegelund 	if (action_mask & BIT(FLOW_ACTION_VLAN_PUSH) &&
323ebf44dedSSteen Hegelund 	    action_mask & BIT(FLOW_ACTION_VLAN_MANGLE)) {
324ebf44dedSSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
325ebf44dedSSteen Hegelund 				   "Cannot combine vlan push and modify action");
326ebf44dedSSteen Hegelund 		return -EOPNOTSUPP;
327ebf44dedSSteen Hegelund 	}
328ebf44dedSSteen Hegelund 
329ebf44dedSSteen Hegelund 	if (action_mask & BIT(FLOW_ACTION_VLAN_POP) &&
330ebf44dedSSteen Hegelund 	    action_mask & BIT(FLOW_ACTION_VLAN_MANGLE)) {
331ebf44dedSSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
332ebf44dedSSteen Hegelund 				   "Cannot combine vlan pop and modify action");
333ebf44dedSSteen Hegelund 		return -EOPNOTSUPP;
334ebf44dedSSteen Hegelund 	}
335ebf44dedSSteen Hegelund 
336392d0ab0SSteen Hegelund 	return 0;
337392d0ab0SSteen Hegelund }
338392d0ab0SSteen Hegelund 
339542e6e2cSSteen Hegelund /* Add a rule counter action */
34040e7fe18SSteen Hegelund static int sparx5_tc_add_rule_counter(struct vcap_admin *admin,
34140e7fe18SSteen Hegelund 				      struct vcap_rule *vrule)
34240e7fe18SSteen Hegelund {
34340e7fe18SSteen Hegelund 	int err;
34440e7fe18SSteen Hegelund 
3453cbe7537SSteen Hegelund 	switch (admin->vtype) {
3463cbe7537SSteen Hegelund 	case VCAP_TYPE_IS0:
3473cbe7537SSteen Hegelund 		break;
3483cbe7537SSteen Hegelund 	case VCAP_TYPE_ES0:
3493cbe7537SSteen Hegelund 		err = vcap_rule_mod_action_u32(vrule, VCAP_AF_ESDX,
3503cbe7537SSteen Hegelund 					       vrule->id);
3513cbe7537SSteen Hegelund 		if (err)
3523cbe7537SSteen Hegelund 			return err;
3533cbe7537SSteen Hegelund 		vcap_rule_set_counter_id(vrule, vrule->id);
3543cbe7537SSteen Hegelund 		break;
3553cbe7537SSteen Hegelund 	case VCAP_TYPE_IS2:
3563cbe7537SSteen Hegelund 	case VCAP_TYPE_ES2:
357542e6e2cSSteen Hegelund 		err = vcap_rule_mod_action_u32(vrule, VCAP_AF_CNT_ID,
358542e6e2cSSteen Hegelund 					       vrule->id);
35940e7fe18SSteen Hegelund 		if (err)
36040e7fe18SSteen Hegelund 			return err;
36140e7fe18SSteen Hegelund 		vcap_rule_set_counter_id(vrule, vrule->id);
3623cbe7537SSteen Hegelund 		break;
3633cbe7537SSteen Hegelund 	default:
3643cbe7537SSteen Hegelund 		pr_err("%s:%d: vcap type: %d not supported\n",
3653cbe7537SSteen Hegelund 		       __func__, __LINE__, admin->vtype);
3663cbe7537SSteen Hegelund 		break;
367542e6e2cSSteen Hegelund 	}
368542e6e2cSSteen Hegelund 	return 0;
36940e7fe18SSteen Hegelund }
37040e7fe18SSteen Hegelund 
3710ca60948SSteen Hegelund /* Collect all port keysets and apply the first of them, possibly wildcarded */
3720ca60948SSteen Hegelund static int sparx5_tc_select_protocol_keyset(struct net_device *ndev,
3730ca60948SSteen Hegelund 					    struct vcap_rule *vrule,
3740ca60948SSteen Hegelund 					    struct vcap_admin *admin,
3750ca60948SSteen Hegelund 					    u16 l3_proto,
3760ca60948SSteen Hegelund 					    struct sparx5_multiple_rules *multi)
3770ca60948SSteen Hegelund {
3780ca60948SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
3790ca60948SSteen Hegelund 	struct vcap_keyset_list portkeysetlist = {};
3800ca60948SSteen Hegelund 	enum vcap_keyfield_set portkeysets[10] = {};
3810ca60948SSteen Hegelund 	struct vcap_keyset_list matches = {};
3820ca60948SSteen Hegelund 	enum vcap_keyfield_set keysets[10];
3830ca60948SSteen Hegelund 	int idx, jdx, err = 0, count = 0;
3840ca60948SSteen Hegelund 	struct sparx5_wildcard_rule *mru;
3850ca60948SSteen Hegelund 	const struct vcap_set *kinfo;
3860ca60948SSteen Hegelund 	struct vcap_control *vctrl;
3870ca60948SSteen Hegelund 
3880ca60948SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
3890ca60948SSteen Hegelund 
3900ca60948SSteen Hegelund 	/* Find the keysets that the rule can use */
3910ca60948SSteen Hegelund 	matches.keysets = keysets;
3920ca60948SSteen Hegelund 	matches.max = ARRAY_SIZE(keysets);
393e1d597ecSSteen Hegelund 	if (!vcap_rule_find_keysets(vrule, &matches))
3940ca60948SSteen Hegelund 		return -EINVAL;
3950ca60948SSteen Hegelund 
3960ca60948SSteen Hegelund 	/* Find the keysets that the port configuration supports */
3970ca60948SSteen Hegelund 	portkeysetlist.max = ARRAY_SIZE(portkeysets);
3980ca60948SSteen Hegelund 	portkeysetlist.keysets = portkeysets;
3990ca60948SSteen Hegelund 	err = sparx5_vcap_get_port_keyset(ndev,
4000ca60948SSteen Hegelund 					  admin, vrule->vcap_chain_id,
4010ca60948SSteen Hegelund 					  l3_proto,
4020ca60948SSteen Hegelund 					  &portkeysetlist);
4030ca60948SSteen Hegelund 	if (err)
4040ca60948SSteen Hegelund 		return err;
4050ca60948SSteen Hegelund 
4060ca60948SSteen Hegelund 	/* Find the intersection of the two sets of keyset */
4070ca60948SSteen Hegelund 	for (idx = 0; idx < portkeysetlist.cnt; ++idx) {
4080ca60948SSteen Hegelund 		kinfo = vcap_keyfieldset(vctrl, admin->vtype,
4090ca60948SSteen Hegelund 					 portkeysetlist.keysets[idx]);
4100ca60948SSteen Hegelund 		if (!kinfo)
4110ca60948SSteen Hegelund 			continue;
4120ca60948SSteen Hegelund 
4130ca60948SSteen Hegelund 		/* Find a port keyset that matches the required keys
4140ca60948SSteen Hegelund 		 * If there are multiple keysets then compose a type id mask
4150ca60948SSteen Hegelund 		 */
4160ca60948SSteen Hegelund 		for (jdx = 0; jdx < matches.cnt; ++jdx) {
4170ca60948SSteen Hegelund 			if (portkeysetlist.keysets[idx] != matches.keysets[jdx])
4180ca60948SSteen Hegelund 				continue;
4190ca60948SSteen Hegelund 
4200ca60948SSteen Hegelund 			mru = &multi->rule[kinfo->sw_per_item];
4210ca60948SSteen Hegelund 			if (!mru->selected) {
4220ca60948SSteen Hegelund 				mru->selected = true;
4230ca60948SSteen Hegelund 				mru->keyset = portkeysetlist.keysets[idx];
4240ca60948SSteen Hegelund 				mru->value = kinfo->type_id;
4250ca60948SSteen Hegelund 			}
4260ca60948SSteen Hegelund 			mru->value &= kinfo->type_id;
4270ca60948SSteen Hegelund 			mru->mask |= kinfo->type_id;
4280ca60948SSteen Hegelund 			++count;
4290ca60948SSteen Hegelund 		}
4300ca60948SSteen Hegelund 	}
4310ca60948SSteen Hegelund 	if (count == 0)
4320ca60948SSteen Hegelund 		return -EPROTO;
4330ca60948SSteen Hegelund 
4340ca60948SSteen Hegelund 	if (l3_proto == ETH_P_ALL && count < portkeysetlist.cnt)
4350ca60948SSteen Hegelund 		return -ENOENT;
4360ca60948SSteen Hegelund 
4370ca60948SSteen Hegelund 	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
4380ca60948SSteen Hegelund 		mru = &multi->rule[idx];
4390ca60948SSteen Hegelund 		if (!mru->selected)
4400ca60948SSteen Hegelund 			continue;
4410ca60948SSteen Hegelund 
4420ca60948SSteen Hegelund 		/* Align the mask to the combined value */
4430ca60948SSteen Hegelund 		mru->mask ^= mru->value;
4440ca60948SSteen Hegelund 	}
4450ca60948SSteen Hegelund 
4460ca60948SSteen Hegelund 	/* Set the chosen keyset on the rule and set a wildcarded type if there
4470ca60948SSteen Hegelund 	 * are more than one keyset
4480ca60948SSteen Hegelund 	 */
4490ca60948SSteen Hegelund 	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
4500ca60948SSteen Hegelund 		mru = &multi->rule[idx];
4510ca60948SSteen Hegelund 		if (!mru->selected)
4520ca60948SSteen Hegelund 			continue;
4530ca60948SSteen Hegelund 
4540ca60948SSteen Hegelund 		vcap_set_rule_set_keyset(vrule, mru->keyset);
4550ca60948SSteen Hegelund 		if (count > 1)
4560ca60948SSteen Hegelund 			/* Some keysets do not have a type field */
4570ca60948SSteen Hegelund 			vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE,
4580ca60948SSteen Hegelund 					      mru->value,
4590ca60948SSteen Hegelund 					      ~mru->mask);
4600ca60948SSteen Hegelund 		mru->selected = false; /* mark as done */
4610ca60948SSteen Hegelund 		break; /* Stop here and add more rules later */
4620ca60948SSteen Hegelund 	}
4630ca60948SSteen Hegelund 	return err;
4640ca60948SSteen Hegelund }
4650ca60948SSteen Hegelund 
4660ca60948SSteen Hegelund static int sparx5_tc_add_rule_copy(struct vcap_control *vctrl,
4670ca60948SSteen Hegelund 				   struct flow_cls_offload *fco,
4680ca60948SSteen Hegelund 				   struct vcap_rule *erule,
4690ca60948SSteen Hegelund 				   struct vcap_admin *admin,
4700ca60948SSteen Hegelund 				   struct sparx5_wildcard_rule *rule)
4710ca60948SSteen Hegelund {
4720ca60948SSteen Hegelund 	enum vcap_key_field keylist[] = {
4730ca60948SSteen Hegelund 		VCAP_KF_IF_IGR_PORT_MASK,
4740ca60948SSteen Hegelund 		VCAP_KF_IF_IGR_PORT_MASK_SEL,
4750ca60948SSteen Hegelund 		VCAP_KF_IF_IGR_PORT_MASK_RNG,
4760ca60948SSteen Hegelund 		VCAP_KF_LOOKUP_FIRST_IS,
4770ca60948SSteen Hegelund 		VCAP_KF_TYPE,
4780ca60948SSteen Hegelund 	};
4790ca60948SSteen Hegelund 	struct vcap_rule *vrule;
4800ca60948SSteen Hegelund 	int err;
4810ca60948SSteen Hegelund 
4820ca60948SSteen Hegelund 	/* Add an extra rule with a special user and the new keyset */
4830ca60948SSteen Hegelund 	erule->user = VCAP_USER_TC_EXTRA;
4840ca60948SSteen Hegelund 	vrule = vcap_copy_rule(erule);
4850ca60948SSteen Hegelund 	if (IS_ERR(vrule))
4860ca60948SSteen Hegelund 		return PTR_ERR(vrule);
4870ca60948SSteen Hegelund 
4880ca60948SSteen Hegelund 	/* Link the new rule to the existing rule with the cookie */
4890ca60948SSteen Hegelund 	vrule->cookie = erule->cookie;
4900ca60948SSteen Hegelund 	vcap_filter_rule_keys(vrule, keylist, ARRAY_SIZE(keylist), true);
4910ca60948SSteen Hegelund 	err = vcap_set_rule_set_keyset(vrule, rule->keyset);
4920ca60948SSteen Hegelund 	if (err) {
4930ca60948SSteen Hegelund 		pr_err("%s:%d: could not set keyset %s in rule: %u\n",
4940ca60948SSteen Hegelund 		       __func__, __LINE__,
4950ca60948SSteen Hegelund 		       vcap_keyset_name(vctrl, rule->keyset),
4960ca60948SSteen Hegelund 		       vrule->id);
4970ca60948SSteen Hegelund 		goto out;
4980ca60948SSteen Hegelund 	}
4990ca60948SSteen Hegelund 
5000ca60948SSteen Hegelund 	/* Some keysets do not have a type field, so ignore return value */
5010ca60948SSteen Hegelund 	vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE, rule->value, ~rule->mask);
5020ca60948SSteen Hegelund 
5030ca60948SSteen Hegelund 	err = vcap_set_rule_set_actionset(vrule, erule->actionset);
5040ca60948SSteen Hegelund 	if (err)
5050ca60948SSteen Hegelund 		goto out;
5060ca60948SSteen Hegelund 
5070ca60948SSteen Hegelund 	err = sparx5_tc_add_rule_counter(admin, vrule);
5080ca60948SSteen Hegelund 	if (err)
5090ca60948SSteen Hegelund 		goto out;
5100ca60948SSteen Hegelund 
5110ca60948SSteen Hegelund 	err = vcap_val_rule(vrule, ETH_P_ALL);
5120ca60948SSteen Hegelund 	if (err) {
5130ca60948SSteen Hegelund 		pr_err("%s:%d: could not validate rule: %u\n",
5140ca60948SSteen Hegelund 		       __func__, __LINE__, vrule->id);
5150ca60948SSteen Hegelund 		vcap_set_tc_exterr(fco, vrule);
5160ca60948SSteen Hegelund 		goto out;
5170ca60948SSteen Hegelund 	}
5180ca60948SSteen Hegelund 	err = vcap_add_rule(vrule);
5190ca60948SSteen Hegelund 	if (err) {
5200ca60948SSteen Hegelund 		pr_err("%s:%d: could not add rule: %u\n",
5210ca60948SSteen Hegelund 		       __func__, __LINE__, vrule->id);
5220ca60948SSteen Hegelund 		goto out;
5230ca60948SSteen Hegelund 	}
5240ca60948SSteen Hegelund out:
5250ca60948SSteen Hegelund 	vcap_free_rule(vrule);
5260ca60948SSteen Hegelund 	return err;
5270ca60948SSteen Hegelund }
5280ca60948SSteen Hegelund 
5290ca60948SSteen Hegelund static int sparx5_tc_add_remaining_rules(struct vcap_control *vctrl,
5300ca60948SSteen Hegelund 					 struct flow_cls_offload *fco,
5310ca60948SSteen Hegelund 					 struct vcap_rule *erule,
5320ca60948SSteen Hegelund 					 struct vcap_admin *admin,
5330ca60948SSteen Hegelund 					 struct sparx5_multiple_rules *multi)
5340ca60948SSteen Hegelund {
5350ca60948SSteen Hegelund 	int idx, err = 0;
5360ca60948SSteen Hegelund 
5370ca60948SSteen Hegelund 	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
5380ca60948SSteen Hegelund 		if (!multi->rule[idx].selected)
5390ca60948SSteen Hegelund 			continue;
5400ca60948SSteen Hegelund 
5410ca60948SSteen Hegelund 		err = sparx5_tc_add_rule_copy(vctrl, fco, erule, admin,
5420ca60948SSteen Hegelund 					      &multi->rule[idx]);
5430ca60948SSteen Hegelund 		if (err)
5440ca60948SSteen Hegelund 			break;
5450ca60948SSteen Hegelund 	}
5460ca60948SSteen Hegelund 	return err;
5470ca60948SSteen Hegelund }
5480ca60948SSteen Hegelund 
549542e6e2cSSteen Hegelund /* Add the actionset that is the default for the VCAP type */
550542e6e2cSSteen Hegelund static int sparx5_tc_set_actionset(struct vcap_admin *admin,
551542e6e2cSSteen Hegelund 				   struct vcap_rule *vrule)
552542e6e2cSSteen Hegelund {
553542e6e2cSSteen Hegelund 	enum vcap_actionfield_set aset;
554542e6e2cSSteen Hegelund 	int err = 0;
555542e6e2cSSteen Hegelund 
556542e6e2cSSteen Hegelund 	switch (admin->vtype) {
557542e6e2cSSteen Hegelund 	case VCAP_TYPE_IS0:
558542e6e2cSSteen Hegelund 		aset = VCAP_AFS_CLASSIFICATION;
559542e6e2cSSteen Hegelund 		break;
560542e6e2cSSteen Hegelund 	case VCAP_TYPE_IS2:
561542e6e2cSSteen Hegelund 		aset = VCAP_AFS_BASE_TYPE;
562542e6e2cSSteen Hegelund 		break;
56352b28a93SSteen Hegelund 	case VCAP_TYPE_ES0:
56452b28a93SSteen Hegelund 		aset = VCAP_AFS_ES0;
56552b28a93SSteen Hegelund 		break;
5667b911a53SSteen Hegelund 	case VCAP_TYPE_ES2:
5677b911a53SSteen Hegelund 		aset = VCAP_AFS_BASE_TYPE;
5687b911a53SSteen Hegelund 		break;
569542e6e2cSSteen Hegelund 	default:
57052b28a93SSteen Hegelund 		pr_err("%s:%d: %s\n", __func__, __LINE__, "Invalid VCAP type");
571542e6e2cSSteen Hegelund 		return -EINVAL;
572542e6e2cSSteen Hegelund 	}
573542e6e2cSSteen Hegelund 	/* Do not overwrite any current actionset */
574542e6e2cSSteen Hegelund 	if (vrule->actionset == VCAP_AFS_NO_VALUE)
575542e6e2cSSteen Hegelund 		err = vcap_set_rule_set_actionset(vrule, aset);
576542e6e2cSSteen Hegelund 	return err;
577542e6e2cSSteen Hegelund }
578542e6e2cSSteen Hegelund 
57988bd9ea7SSteen Hegelund /* Add the VCAP key to match on for a rule target value */
58088bd9ea7SSteen Hegelund static int sparx5_tc_add_rule_link_target(struct vcap_admin *admin,
58188bd9ea7SSteen Hegelund 					  struct vcap_rule *vrule,
58288bd9ea7SSteen Hegelund 					  int target_cid)
58388bd9ea7SSteen Hegelund {
58488bd9ea7SSteen Hegelund 	int link_val = target_cid % VCAP_CID_LOOKUP_SIZE;
58588bd9ea7SSteen Hegelund 	int err;
58688bd9ea7SSteen Hegelund 
58788bd9ea7SSteen Hegelund 	if (!link_val)
58888bd9ea7SSteen Hegelund 		return 0;
58988bd9ea7SSteen Hegelund 
59088bd9ea7SSteen Hegelund 	switch (admin->vtype) {
59188bd9ea7SSteen Hegelund 	case VCAP_TYPE_IS0:
59288bd9ea7SSteen Hegelund 		/* Add NXT_IDX key for chaining rules between IS0 instances */
59388bd9ea7SSteen Hegelund 		err = vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX_SEL,
59488bd9ea7SSteen Hegelund 					    1, /* enable */
59588bd9ea7SSteen Hegelund 					    ~0);
59688bd9ea7SSteen Hegelund 		if (err)
59788bd9ea7SSteen Hegelund 			return err;
59888bd9ea7SSteen Hegelund 		return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX,
59988bd9ea7SSteen Hegelund 					     link_val, /* target */
60088bd9ea7SSteen Hegelund 					     ~0);
60188bd9ea7SSteen Hegelund 	case VCAP_TYPE_IS2:
60288bd9ea7SSteen Hegelund 		/* Add PAG key for chaining rules from IS0 */
60388bd9ea7SSteen Hegelund 		return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_PAG,
60488bd9ea7SSteen Hegelund 					     link_val, /* target */
60588bd9ea7SSteen Hegelund 					     ~0);
60652b28a93SSteen Hegelund 	case VCAP_TYPE_ES0:
6077b911a53SSteen Hegelund 	case VCAP_TYPE_ES2:
6087b911a53SSteen Hegelund 		/* Add ISDX key for chaining rules from IS0 */
6097b911a53SSteen Hegelund 		return vcap_rule_add_key_u32(vrule, VCAP_KF_ISDX_CLS, link_val,
6107b911a53SSteen Hegelund 					     ~0);
61188bd9ea7SSteen Hegelund 	default:
61288bd9ea7SSteen Hegelund 		break;
61388bd9ea7SSteen Hegelund 	}
61488bd9ea7SSteen Hegelund 	return 0;
61588bd9ea7SSteen Hegelund }
61688bd9ea7SSteen Hegelund 
61788bd9ea7SSteen Hegelund /* Add the VCAP action that adds a target value to a rule */
61888bd9ea7SSteen Hegelund static int sparx5_tc_add_rule_link(struct vcap_control *vctrl,
61988bd9ea7SSteen Hegelund 				   struct vcap_admin *admin,
62088bd9ea7SSteen Hegelund 				   struct vcap_rule *vrule,
62188bd9ea7SSteen Hegelund 				   int from_cid, int to_cid)
62288bd9ea7SSteen Hegelund {
62388bd9ea7SSteen Hegelund 	struct vcap_admin *to_admin = vcap_find_admin(vctrl, to_cid);
62488bd9ea7SSteen Hegelund 	int diff, err = 0;
62588bd9ea7SSteen Hegelund 
626b5b0c364SSteen Hegelund 	if (!to_admin) {
62788bd9ea7SSteen Hegelund 		pr_err("%s:%d: unsupported chain direction: %d\n",
62888bd9ea7SSteen Hegelund 		       __func__, __LINE__, to_cid);
62988bd9ea7SSteen Hegelund 		return -EINVAL;
63088bd9ea7SSteen Hegelund 	}
631b5b0c364SSteen Hegelund 
632b5b0c364SSteen Hegelund 	diff = vcap_chain_offset(vctrl, from_cid, to_cid);
633b5b0c364SSteen Hegelund 	if (!diff)
634b5b0c364SSteen Hegelund 		return 0;
635b5b0c364SSteen Hegelund 
63688bd9ea7SSteen Hegelund 	if (admin->vtype == VCAP_TYPE_IS0 &&
63788bd9ea7SSteen Hegelund 	    to_admin->vtype == VCAP_TYPE_IS0) {
63888bd9ea7SSteen Hegelund 		/* Between IS0 instances the G_IDX value is used */
63988bd9ea7SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule, VCAP_AF_NXT_IDX, diff);
64088bd9ea7SSteen Hegelund 		if (err)
64188bd9ea7SSteen Hegelund 			goto out;
64288bd9ea7SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule, VCAP_AF_NXT_IDX_CTRL,
64388bd9ea7SSteen Hegelund 					       1); /* Replace */
64488bd9ea7SSteen Hegelund 		if (err)
64588bd9ea7SSteen Hegelund 			goto out;
64688bd9ea7SSteen Hegelund 	} else if (admin->vtype == VCAP_TYPE_IS0 &&
64788bd9ea7SSteen Hegelund 		   to_admin->vtype == VCAP_TYPE_IS2) {
64888bd9ea7SSteen Hegelund 		/* Between IS0 and IS2 the PAG value is used */
64988bd9ea7SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule, VCAP_AF_PAG_VAL, diff);
65088bd9ea7SSteen Hegelund 		if (err)
65188bd9ea7SSteen Hegelund 			goto out;
65288bd9ea7SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
65388bd9ea7SSteen Hegelund 					       VCAP_AF_PAG_OVERRIDE_MASK,
65488bd9ea7SSteen Hegelund 					       0xff);
65588bd9ea7SSteen Hegelund 		if (err)
65688bd9ea7SSteen Hegelund 			goto out;
6577b911a53SSteen Hegelund 	} else if (admin->vtype == VCAP_TYPE_IS0 &&
65852b28a93SSteen Hegelund 		   (to_admin->vtype == VCAP_TYPE_ES0 ||
65952b28a93SSteen Hegelund 		    to_admin->vtype == VCAP_TYPE_ES2)) {
66052b28a93SSteen Hegelund 		/* Between IS0 and ES0/ES2 the ISDX value is used */
6617b911a53SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_VAL,
6627b911a53SSteen Hegelund 					       diff);
6637b911a53SSteen Hegelund 		if (err)
6647b911a53SSteen Hegelund 			goto out;
6657b911a53SSteen Hegelund 		err = vcap_rule_add_action_bit(vrule,
6667b911a53SSteen Hegelund 					       VCAP_AF_ISDX_ADD_REPLACE_SEL,
6677b911a53SSteen Hegelund 					       VCAP_BIT_1);
6687b911a53SSteen Hegelund 		if (err)
6697b911a53SSteen Hegelund 			goto out;
67088bd9ea7SSteen Hegelund 	} else {
67188bd9ea7SSteen Hegelund 		pr_err("%s:%d: unsupported chain destination: %d\n",
67288bd9ea7SSteen Hegelund 		       __func__, __LINE__, to_cid);
67388bd9ea7SSteen Hegelund 		err = -EOPNOTSUPP;
67488bd9ea7SSteen Hegelund 	}
67588bd9ea7SSteen Hegelund out:
67688bd9ea7SSteen Hegelund 	return err;
67788bd9ea7SSteen Hegelund }
67888bd9ea7SSteen Hegelund 
6796ebf182bSDaniel Machon static int sparx5_tc_flower_parse_act_gate(struct sparx5_psfp_sg *sg,
6806ebf182bSDaniel Machon 					   struct flow_action_entry *act,
6816ebf182bSDaniel Machon 					   struct netlink_ext_ack *extack)
6826ebf182bSDaniel Machon {
6836ebf182bSDaniel Machon 	int i;
6846ebf182bSDaniel Machon 
6856ebf182bSDaniel Machon 	if (act->gate.prio < -1 || act->gate.prio > SPX5_PSFP_SG_MAX_IPV) {
6866ebf182bSDaniel Machon 		NL_SET_ERR_MSG_MOD(extack, "Invalid gate priority");
6876ebf182bSDaniel Machon 		return -EINVAL;
6886ebf182bSDaniel Machon 	}
6896ebf182bSDaniel Machon 
6906ebf182bSDaniel Machon 	if (act->gate.cycletime < SPX5_PSFP_SG_MIN_CYCLE_TIME_NS ||
6916ebf182bSDaniel Machon 	    act->gate.cycletime > SPX5_PSFP_SG_MAX_CYCLE_TIME_NS) {
6926ebf182bSDaniel Machon 		NL_SET_ERR_MSG_MOD(extack, "Invalid gate cycletime");
6936ebf182bSDaniel Machon 		return -EINVAL;
6946ebf182bSDaniel Machon 	}
6956ebf182bSDaniel Machon 
6966ebf182bSDaniel Machon 	if (act->gate.cycletimeext > SPX5_PSFP_SG_MAX_CYCLE_TIME_NS) {
6976ebf182bSDaniel Machon 		NL_SET_ERR_MSG_MOD(extack, "Invalid gate cycletimeext");
6986ebf182bSDaniel Machon 		return -EINVAL;
6996ebf182bSDaniel Machon 	}
7006ebf182bSDaniel Machon 
7016ebf182bSDaniel Machon 	if (act->gate.num_entries >= SPX5_PSFP_GCE_CNT) {
7026ebf182bSDaniel Machon 		NL_SET_ERR_MSG_MOD(extack, "Invalid number of gate entries");
7036ebf182bSDaniel Machon 		return -EINVAL;
7046ebf182bSDaniel Machon 	}
7056ebf182bSDaniel Machon 
7066ebf182bSDaniel Machon 	sg->gate_state = true;
7076ebf182bSDaniel Machon 	sg->ipv = act->gate.prio;
7086ebf182bSDaniel Machon 	sg->num_entries = act->gate.num_entries;
7096ebf182bSDaniel Machon 	sg->cycletime = act->gate.cycletime;
7106ebf182bSDaniel Machon 	sg->cycletimeext = act->gate.cycletimeext;
7116ebf182bSDaniel Machon 
7126ebf182bSDaniel Machon 	for (i = 0; i < sg->num_entries; i++) {
7136ebf182bSDaniel Machon 		sg->gce[i].gate_state = !!act->gate.entries[i].gate_state;
7146ebf182bSDaniel Machon 		sg->gce[i].interval = act->gate.entries[i].interval;
7156ebf182bSDaniel Machon 		sg->gce[i].ipv = act->gate.entries[i].ipv;
7166ebf182bSDaniel Machon 		sg->gce[i].maxoctets = act->gate.entries[i].maxoctets;
7176ebf182bSDaniel Machon 	}
7186ebf182bSDaniel Machon 
7196ebf182bSDaniel Machon 	return 0;
7206ebf182bSDaniel Machon }
7216ebf182bSDaniel Machon 
7226ebf182bSDaniel Machon static int sparx5_tc_flower_parse_act_police(struct sparx5_policer *pol,
7236ebf182bSDaniel Machon 					     struct flow_action_entry *act,
7246ebf182bSDaniel Machon 					     struct netlink_ext_ack *extack)
7256ebf182bSDaniel Machon {
7266ebf182bSDaniel Machon 	pol->type = SPX5_POL_SERVICE;
7276ebf182bSDaniel Machon 	pol->rate = div_u64(act->police.rate_bytes_ps, 1000) * 8;
7286ebf182bSDaniel Machon 	pol->burst = act->police.burst;
7296ebf182bSDaniel Machon 	pol->idx = act->hw_index;
7306ebf182bSDaniel Machon 
7316ebf182bSDaniel Machon 	/* rate is now in kbit */
7326ebf182bSDaniel Machon 	if (pol->rate > DIV_ROUND_UP(SPX5_SDLB_GROUP_RATE_MAX, 1000)) {
7336ebf182bSDaniel Machon 		NL_SET_ERR_MSG_MOD(extack, "Maximum rate exceeded");
7346ebf182bSDaniel Machon 		return -EINVAL;
7356ebf182bSDaniel Machon 	}
7366ebf182bSDaniel Machon 
7376ebf182bSDaniel Machon 	if (act->police.exceed.act_id != FLOW_ACTION_DROP) {
7386ebf182bSDaniel Machon 		NL_SET_ERR_MSG_MOD(extack, "Offload not supported when exceed action is not drop");
7396ebf182bSDaniel Machon 		return -EOPNOTSUPP;
7406ebf182bSDaniel Machon 	}
7416ebf182bSDaniel Machon 
7426ebf182bSDaniel Machon 	if (act->police.notexceed.act_id != FLOW_ACTION_PIPE &&
7436ebf182bSDaniel Machon 	    act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) {
7446ebf182bSDaniel Machon 		NL_SET_ERR_MSG_MOD(extack, "Offload not supported when conform action is not pipe or ok");
7456ebf182bSDaniel Machon 		return -EOPNOTSUPP;
7466ebf182bSDaniel Machon 	}
7476ebf182bSDaniel Machon 
7486ebf182bSDaniel Machon 	return 0;
7496ebf182bSDaniel Machon }
7506ebf182bSDaniel Machon 
7516ebf182bSDaniel Machon static int sparx5_tc_flower_psfp_setup(struct sparx5 *sparx5,
7526ebf182bSDaniel Machon 				       struct vcap_rule *vrule, int sg_idx,
7536ebf182bSDaniel Machon 				       int pol_idx, struct sparx5_psfp_sg *sg,
7546ebf182bSDaniel Machon 				       struct sparx5_psfp_fm *fm,
7556ebf182bSDaniel Machon 				       struct sparx5_psfp_sf *sf)
7566ebf182bSDaniel Machon {
7576ebf182bSDaniel Machon 	u32 psfp_sfid = 0, psfp_fmid = 0, psfp_sgid = 0;
7586ebf182bSDaniel Machon 	int ret;
7596ebf182bSDaniel Machon 
7606ebf182bSDaniel Machon 	/* Must always have a stream gate - max sdu (filter option) is evaluated
7616ebf182bSDaniel Machon 	 * after frames have passed the gate, so in case of only a policer, we
7626ebf182bSDaniel Machon 	 * allocate a stream gate that is always open.
7636ebf182bSDaniel Machon 	 */
7646ebf182bSDaniel Machon 	if (sg_idx < 0) {
7656ebf182bSDaniel Machon 		sg_idx = sparx5_pool_idx_to_id(SPX5_PSFP_SG_OPEN);
7666ebf182bSDaniel Machon 		sg->ipv = 0; /* Disabled */
7676ebf182bSDaniel Machon 		sg->cycletime = SPX5_PSFP_SG_CYCLE_TIME_DEFAULT;
7686ebf182bSDaniel Machon 		sg->num_entries = 1;
7696ebf182bSDaniel Machon 		sg->gate_state = 1; /* Open */
7706ebf182bSDaniel Machon 		sg->gate_enabled = 1;
7716ebf182bSDaniel Machon 		sg->gce[0].gate_state = 1;
7726ebf182bSDaniel Machon 		sg->gce[0].interval = SPX5_PSFP_SG_CYCLE_TIME_DEFAULT;
7736ebf182bSDaniel Machon 		sg->gce[0].ipv = 0;
7746ebf182bSDaniel Machon 		sg->gce[0].maxoctets = 0; /* Disabled */
7756ebf182bSDaniel Machon 	}
7766ebf182bSDaniel Machon 
7776ebf182bSDaniel Machon 	ret = sparx5_psfp_sg_add(sparx5, sg_idx, sg, &psfp_sgid);
7786ebf182bSDaniel Machon 	if (ret < 0)
7796ebf182bSDaniel Machon 		return ret;
7806ebf182bSDaniel Machon 
7816ebf182bSDaniel Machon 	if (pol_idx >= 0) {
7826ebf182bSDaniel Machon 		/* Add new flow-meter */
7836ebf182bSDaniel Machon 		ret = sparx5_psfp_fm_add(sparx5, pol_idx, fm, &psfp_fmid);
7846ebf182bSDaniel Machon 		if (ret < 0)
7856ebf182bSDaniel Machon 			return ret;
7866ebf182bSDaniel Machon 	}
7876ebf182bSDaniel Machon 
7886ebf182bSDaniel Machon 	/* Map stream filter to stream gate */
7896ebf182bSDaniel Machon 	sf->sgid = psfp_sgid;
7906ebf182bSDaniel Machon 
7916ebf182bSDaniel Machon 	/* Add new stream-filter and map it to a steam gate */
7926ebf182bSDaniel Machon 	ret = sparx5_psfp_sf_add(sparx5, sf, &psfp_sfid);
7936ebf182bSDaniel Machon 	if (ret < 0)
7946ebf182bSDaniel Machon 		return ret;
7956ebf182bSDaniel Machon 
7966ebf182bSDaniel Machon 	/* Streams are classified by ISDX - map ISDX 1:1 to sfid for now. */
7976ebf182bSDaniel Machon 	sparx5_isdx_conf_set(sparx5, psfp_sfid, psfp_sfid, psfp_fmid);
7986ebf182bSDaniel Machon 
7996ebf182bSDaniel Machon 	ret = vcap_rule_add_action_bit(vrule, VCAP_AF_ISDX_ADD_REPLACE_SEL,
8006ebf182bSDaniel Machon 				       VCAP_BIT_1);
8016ebf182bSDaniel Machon 	if (ret)
8026ebf182bSDaniel Machon 		return ret;
8036ebf182bSDaniel Machon 
8046ebf182bSDaniel Machon 	ret = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_VAL, psfp_sfid);
8056ebf182bSDaniel Machon 	if (ret)
8066ebf182bSDaniel Machon 		return ret;
8076ebf182bSDaniel Machon 
8086ebf182bSDaniel Machon 	return 0;
8096ebf182bSDaniel Machon }
8106ebf182bSDaniel Machon 
81152b28a93SSteen Hegelund /* Handle the action trap for a VCAP rule */
81252b28a93SSteen Hegelund static int sparx5_tc_action_trap(struct vcap_admin *admin,
81352b28a93SSteen Hegelund 				 struct vcap_rule *vrule,
81452b28a93SSteen Hegelund 				 struct flow_cls_offload *fco)
81552b28a93SSteen Hegelund {
81652b28a93SSteen Hegelund 	int err = 0;
81752b28a93SSteen Hegelund 
81852b28a93SSteen Hegelund 	switch (admin->vtype) {
81952b28a93SSteen Hegelund 	case VCAP_TYPE_IS2:
82052b28a93SSteen Hegelund 		err = vcap_rule_add_action_bit(vrule,
82152b28a93SSteen Hegelund 					       VCAP_AF_CPU_COPY_ENA,
82252b28a93SSteen Hegelund 					       VCAP_BIT_1);
82352b28a93SSteen Hegelund 		if (err)
82452b28a93SSteen Hegelund 			break;
82552b28a93SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
82652b28a93SSteen Hegelund 					       VCAP_AF_CPU_QUEUE_NUM, 0);
82752b28a93SSteen Hegelund 		if (err)
82852b28a93SSteen Hegelund 			break;
82952b28a93SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
83052b28a93SSteen Hegelund 					       VCAP_AF_MASK_MODE,
83152b28a93SSteen Hegelund 					       SPX5_PMM_REPLACE_ALL);
83252b28a93SSteen Hegelund 		break;
83352b28a93SSteen Hegelund 	case VCAP_TYPE_ES0:
83452b28a93SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
83552b28a93SSteen Hegelund 					       VCAP_AF_FWD_SEL,
83652b28a93SSteen Hegelund 					       SPX5_FWSEL_REDIRECT_TO_LOOPBACK);
83752b28a93SSteen Hegelund 		break;
83852b28a93SSteen Hegelund 	case VCAP_TYPE_ES2:
83952b28a93SSteen Hegelund 		err = vcap_rule_add_action_bit(vrule,
84052b28a93SSteen Hegelund 					       VCAP_AF_CPU_COPY_ENA,
84152b28a93SSteen Hegelund 					       VCAP_BIT_1);
84252b28a93SSteen Hegelund 		if (err)
84352b28a93SSteen Hegelund 			break;
84452b28a93SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
84552b28a93SSteen Hegelund 					       VCAP_AF_CPU_QUEUE_NUM, 0);
84652b28a93SSteen Hegelund 		break;
84752b28a93SSteen Hegelund 	default:
84852b28a93SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
84952b28a93SSteen Hegelund 				   "Trap action not supported in this VCAP");
85052b28a93SSteen Hegelund 		err = -EOPNOTSUPP;
85152b28a93SSteen Hegelund 		break;
85252b28a93SSteen Hegelund 	}
85352b28a93SSteen Hegelund 	return err;
85452b28a93SSteen Hegelund }
85552b28a93SSteen Hegelund 
856ebf44dedSSteen Hegelund static int sparx5_tc_action_vlan_pop(struct vcap_admin *admin,
857ebf44dedSSteen Hegelund 				     struct vcap_rule *vrule,
858ebf44dedSSteen Hegelund 				     struct flow_cls_offload *fco,
859ebf44dedSSteen Hegelund 				     u16 tpid)
860ebf44dedSSteen Hegelund {
861ebf44dedSSteen Hegelund 	int err = 0;
862ebf44dedSSteen Hegelund 
863ebf44dedSSteen Hegelund 	switch (admin->vtype) {
864ebf44dedSSteen Hegelund 	case VCAP_TYPE_ES0:
865ebf44dedSSteen Hegelund 		break;
866ebf44dedSSteen Hegelund 	default:
867ebf44dedSSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
868ebf44dedSSteen Hegelund 				   "VLAN pop action not supported in this VCAP");
869ebf44dedSSteen Hegelund 		return -EOPNOTSUPP;
870ebf44dedSSteen Hegelund 	}
871ebf44dedSSteen Hegelund 
872ebf44dedSSteen Hegelund 	switch (tpid) {
873ebf44dedSSteen Hegelund 	case ETH_P_8021Q:
874ebf44dedSSteen Hegelund 	case ETH_P_8021AD:
875ebf44dedSSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
876ebf44dedSSteen Hegelund 					       VCAP_AF_PUSH_OUTER_TAG,
877ebf44dedSSteen Hegelund 					       SPX5_OTAG_UNTAG);
878ebf44dedSSteen Hegelund 		break;
879ebf44dedSSteen Hegelund 	default:
880ebf44dedSSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
881ebf44dedSSteen Hegelund 				   "Invalid vlan proto");
882ebf44dedSSteen Hegelund 		err = -EINVAL;
883ebf44dedSSteen Hegelund 	}
884ebf44dedSSteen Hegelund 	return err;
885ebf44dedSSteen Hegelund }
886ebf44dedSSteen Hegelund 
887ebf44dedSSteen Hegelund static int sparx5_tc_action_vlan_modify(struct vcap_admin *admin,
888ebf44dedSSteen Hegelund 					struct vcap_rule *vrule,
889ebf44dedSSteen Hegelund 					struct flow_cls_offload *fco,
890ebf44dedSSteen Hegelund 					struct flow_action_entry *act,
891ebf44dedSSteen Hegelund 					u16 tpid)
892ebf44dedSSteen Hegelund {
893ebf44dedSSteen Hegelund 	int err = 0;
894ebf44dedSSteen Hegelund 
895ebf44dedSSteen Hegelund 	switch (admin->vtype) {
896ebf44dedSSteen Hegelund 	case VCAP_TYPE_ES0:
897ebf44dedSSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
898ebf44dedSSteen Hegelund 					       VCAP_AF_PUSH_OUTER_TAG,
899ebf44dedSSteen Hegelund 					       SPX5_OTAG_TAG_A);
900ebf44dedSSteen Hegelund 		if (err)
901ebf44dedSSteen Hegelund 			return err;
902ebf44dedSSteen Hegelund 		break;
903ebf44dedSSteen Hegelund 	default:
904ebf44dedSSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
905ebf44dedSSteen Hegelund 				   "VLAN modify action not supported in this VCAP");
906ebf44dedSSteen Hegelund 		return -EOPNOTSUPP;
907ebf44dedSSteen Hegelund 	}
908ebf44dedSSteen Hegelund 
909ebf44dedSSteen Hegelund 	switch (tpid) {
910ebf44dedSSteen Hegelund 	case ETH_P_8021Q:
911ebf44dedSSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
912ebf44dedSSteen Hegelund 					       VCAP_AF_TAG_A_TPID_SEL,
913ebf44dedSSteen Hegelund 					       SPX5_TPID_A_8100);
914ebf44dedSSteen Hegelund 		break;
915ebf44dedSSteen Hegelund 	case ETH_P_8021AD:
916ebf44dedSSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
917ebf44dedSSteen Hegelund 					       VCAP_AF_TAG_A_TPID_SEL,
918ebf44dedSSteen Hegelund 					       SPX5_TPID_A_88A8);
919ebf44dedSSteen Hegelund 		break;
920ebf44dedSSteen Hegelund 	default:
921ebf44dedSSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
922ebf44dedSSteen Hegelund 				   "Invalid vlan proto");
923ebf44dedSSteen Hegelund 		err = -EINVAL;
924ebf44dedSSteen Hegelund 	}
925ebf44dedSSteen Hegelund 	if (err)
926ebf44dedSSteen Hegelund 		return err;
927ebf44dedSSteen Hegelund 
928ebf44dedSSteen Hegelund 	err = vcap_rule_add_action_u32(vrule,
929ebf44dedSSteen Hegelund 				       VCAP_AF_TAG_A_VID_SEL,
930ebf44dedSSteen Hegelund 				       SPX5_VID_A_VAL);
931ebf44dedSSteen Hegelund 	if (err)
932ebf44dedSSteen Hegelund 		return err;
933ebf44dedSSteen Hegelund 
934ebf44dedSSteen Hegelund 	err = vcap_rule_add_action_u32(vrule,
935ebf44dedSSteen Hegelund 				       VCAP_AF_VID_A_VAL,
936ebf44dedSSteen Hegelund 				       act->vlan.vid);
937ebf44dedSSteen Hegelund 	if (err)
938ebf44dedSSteen Hegelund 		return err;
939ebf44dedSSteen Hegelund 
940ebf44dedSSteen Hegelund 	err = vcap_rule_add_action_u32(vrule,
941ebf44dedSSteen Hegelund 				       VCAP_AF_TAG_A_PCP_SEL,
942ebf44dedSSteen Hegelund 				       SPX5_PCP_A_VAL);
943ebf44dedSSteen Hegelund 	if (err)
944ebf44dedSSteen Hegelund 		return err;
945ebf44dedSSteen Hegelund 
946ebf44dedSSteen Hegelund 	err = vcap_rule_add_action_u32(vrule,
947ebf44dedSSteen Hegelund 				       VCAP_AF_PCP_A_VAL,
948ebf44dedSSteen Hegelund 				       act->vlan.prio);
949ebf44dedSSteen Hegelund 	if (err)
950ebf44dedSSteen Hegelund 		return err;
951ebf44dedSSteen Hegelund 
952ebf44dedSSteen Hegelund 	return vcap_rule_add_action_u32(vrule,
953ebf44dedSSteen Hegelund 					VCAP_AF_TAG_A_DEI_SEL,
954ebf44dedSSteen Hegelund 					SPX5_DEI_A_CLASSIFIED);
955ebf44dedSSteen Hegelund }
956ebf44dedSSteen Hegelund 
957ebf44dedSSteen Hegelund static int sparx5_tc_action_vlan_push(struct vcap_admin *admin,
958ebf44dedSSteen Hegelund 				      struct vcap_rule *vrule,
959ebf44dedSSteen Hegelund 				      struct flow_cls_offload *fco,
960ebf44dedSSteen Hegelund 				      struct flow_action_entry *act,
961ebf44dedSSteen Hegelund 				      u16 tpid)
962ebf44dedSSteen Hegelund {
963ebf44dedSSteen Hegelund 	u16 act_tpid = be16_to_cpu(act->vlan.proto);
964ebf44dedSSteen Hegelund 	int err = 0;
965ebf44dedSSteen Hegelund 
966ebf44dedSSteen Hegelund 	switch (admin->vtype) {
967ebf44dedSSteen Hegelund 	case VCAP_TYPE_ES0:
968ebf44dedSSteen Hegelund 		break;
969ebf44dedSSteen Hegelund 	default:
970ebf44dedSSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
971ebf44dedSSteen Hegelund 				   "VLAN push action not supported in this VCAP");
972ebf44dedSSteen Hegelund 		return -EOPNOTSUPP;
973ebf44dedSSteen Hegelund 	}
974ebf44dedSSteen Hegelund 
975ebf44dedSSteen Hegelund 	if (tpid == ETH_P_8021AD) {
976ebf44dedSSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
977ebf44dedSSteen Hegelund 				   "Cannot push on double tagged frames");
978ebf44dedSSteen Hegelund 		return -EOPNOTSUPP;
979ebf44dedSSteen Hegelund 	}
980ebf44dedSSteen Hegelund 
981ebf44dedSSteen Hegelund 	err = sparx5_tc_action_vlan_modify(admin, vrule, fco, act, act_tpid);
982ebf44dedSSteen Hegelund 	if (err)
983ebf44dedSSteen Hegelund 		return err;
984ebf44dedSSteen Hegelund 
985ebf44dedSSteen Hegelund 	switch (act_tpid) {
986ebf44dedSSteen Hegelund 	case ETH_P_8021Q:
987ebf44dedSSteen Hegelund 		break;
988ebf44dedSSteen Hegelund 	case ETH_P_8021AD:
989ebf44dedSSteen Hegelund 		/* Push classified tag as inner tag */
990ebf44dedSSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
991ebf44dedSSteen Hegelund 					       VCAP_AF_PUSH_INNER_TAG,
992ebf44dedSSteen Hegelund 					       SPX5_ITAG_PUSH_B_TAG);
993ebf44dedSSteen Hegelund 		if (err)
994ebf44dedSSteen Hegelund 			break;
995ebf44dedSSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
996ebf44dedSSteen Hegelund 					       VCAP_AF_TAG_B_TPID_SEL,
997ebf44dedSSteen Hegelund 					       SPX5_TPID_B_CLASSIFIED);
998ebf44dedSSteen Hegelund 		break;
999ebf44dedSSteen Hegelund 	default:
1000ebf44dedSSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
1001ebf44dedSSteen Hegelund 				   "Invalid vlan proto");
1002ebf44dedSSteen Hegelund 		err = -EINVAL;
1003ebf44dedSSteen Hegelund 	}
1004ebf44dedSSteen Hegelund 	return err;
1005ebf44dedSSteen Hegelund }
1006ebf44dedSSteen Hegelund 
1007e1d597ecSSteen Hegelund /* Remove rule keys that may prevent templates from matching a keyset */
1008e1d597ecSSteen Hegelund static void sparx5_tc_flower_simplify_rule(struct vcap_admin *admin,
1009e1d597ecSSteen Hegelund 					   struct vcap_rule *vrule,
1010e1d597ecSSteen Hegelund 					   u16 l3_proto)
1011e1d597ecSSteen Hegelund {
1012e1d597ecSSteen Hegelund 	switch (admin->vtype) {
1013e1d597ecSSteen Hegelund 	case VCAP_TYPE_IS0:
1014e1d597ecSSteen Hegelund 		vcap_rule_rem_key(vrule, VCAP_KF_ETYPE);
1015e1d597ecSSteen Hegelund 		switch (l3_proto) {
1016e1d597ecSSteen Hegelund 		case ETH_P_IP:
1017e1d597ecSSteen Hegelund 			break;
1018e1d597ecSSteen Hegelund 		case ETH_P_IPV6:
1019e1d597ecSSteen Hegelund 			vcap_rule_rem_key(vrule, VCAP_KF_IP_SNAP_IS);
1020e1d597ecSSteen Hegelund 			break;
1021e1d597ecSSteen Hegelund 		default:
1022e1d597ecSSteen Hegelund 			break;
1023e1d597ecSSteen Hegelund 		}
1024e1d597ecSSteen Hegelund 		break;
1025e1d597ecSSteen Hegelund 	case VCAP_TYPE_ES2:
1026e1d597ecSSteen Hegelund 		switch (l3_proto) {
1027e1d597ecSSteen Hegelund 		case ETH_P_IP:
1028e1d597ecSSteen Hegelund 			if (vrule->keyset == VCAP_KFS_IP4_OTHER)
1029e1d597ecSSteen Hegelund 				vcap_rule_rem_key(vrule, VCAP_KF_TCP_IS);
1030e1d597ecSSteen Hegelund 			break;
1031e1d597ecSSteen Hegelund 		case ETH_P_IPV6:
1032e1d597ecSSteen Hegelund 			if (vrule->keyset == VCAP_KFS_IP6_STD)
1033e1d597ecSSteen Hegelund 				vcap_rule_rem_key(vrule, VCAP_KF_TCP_IS);
1034e1d597ecSSteen Hegelund 			vcap_rule_rem_key(vrule, VCAP_KF_IP4_IS);
1035e1d597ecSSteen Hegelund 			break;
1036e1d597ecSSteen Hegelund 		default:
1037e1d597ecSSteen Hegelund 			break;
1038e1d597ecSSteen Hegelund 		}
1039e1d597ecSSteen Hegelund 		break;
1040e1d597ecSSteen Hegelund 	case VCAP_TYPE_IS2:
1041e1d597ecSSteen Hegelund 		switch (l3_proto) {
1042e1d597ecSSteen Hegelund 		case ETH_P_IP:
1043e1d597ecSSteen Hegelund 		case ETH_P_IPV6:
1044e1d597ecSSteen Hegelund 			vcap_rule_rem_key(vrule, VCAP_KF_IP4_IS);
1045e1d597ecSSteen Hegelund 			break;
1046e1d597ecSSteen Hegelund 		default:
1047e1d597ecSSteen Hegelund 			break;
1048e1d597ecSSteen Hegelund 		}
1049e1d597ecSSteen Hegelund 		break;
1050e1d597ecSSteen Hegelund 	default:
1051e1d597ecSSteen Hegelund 		break;
1052e1d597ecSSteen Hegelund 	}
1053e1d597ecSSteen Hegelund }
1054e1d597ecSSteen Hegelund 
1055e1d597ecSSteen Hegelund static bool sparx5_tc_flower_use_template(struct net_device *ndev,
1056e1d597ecSSteen Hegelund 					  struct flow_cls_offload *fco,
1057e1d597ecSSteen Hegelund 					  struct vcap_admin *admin,
1058e1d597ecSSteen Hegelund 					  struct vcap_rule *vrule)
1059e1d597ecSSteen Hegelund {
1060e1d597ecSSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
1061e1d597ecSSteen Hegelund 	struct sparx5_tc_flower_template *ftp;
1062e1d597ecSSteen Hegelund 
1063e1d597ecSSteen Hegelund 	list_for_each_entry(ftp, &port->tc_templates, list) {
1064e1d597ecSSteen Hegelund 		if (ftp->cid != fco->common.chain_index)
1065e1d597ecSSteen Hegelund 			continue;
1066e1d597ecSSteen Hegelund 
1067e1d597ecSSteen Hegelund 		vcap_set_rule_set_keyset(vrule, ftp->keyset);
1068e1d597ecSSteen Hegelund 		sparx5_tc_flower_simplify_rule(admin, vrule, ftp->l3_proto);
1069e1d597ecSSteen Hegelund 		return true;
1070e1d597ecSSteen Hegelund 	}
1071e1d597ecSSteen Hegelund 	return false;
1072e1d597ecSSteen Hegelund }
1073e1d597ecSSteen Hegelund 
1074c9da1ac1SSteen Hegelund static int sparx5_tc_flower_replace(struct net_device *ndev,
1075c9da1ac1SSteen Hegelund 				    struct flow_cls_offload *fco,
1076e7e3f514SSteen Hegelund 				    struct vcap_admin *admin,
1077e7e3f514SSteen Hegelund 				    bool ingress)
1078c9da1ac1SSteen Hegelund {
10796ebf182bSDaniel Machon 	struct sparx5_psfp_sf sf = { .max_sdu = SPX5_PSFP_SF_MAX_SDU };
10806ebf182bSDaniel Machon 	struct netlink_ext_ack *extack = fco->common.extack;
10816ebf182bSDaniel Machon 	int err, idx, tc_sg_idx = -1, tc_pol_idx = -1;
1082ebf44dedSSteen Hegelund 	struct vcap_tc_flower_parse_usage state = {
1083ebf44dedSSteen Hegelund 		.fco = fco,
1084ebf44dedSSteen Hegelund 		.l3_proto = ETH_P_ALL,
1085ebf44dedSSteen Hegelund 		.admin = admin,
1086ebf44dedSSteen Hegelund 	};
1087c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
10880ca60948SSteen Hegelund 	struct sparx5_multiple_rules multi = {};
10896ebf182bSDaniel Machon 	struct sparx5 *sparx5 = port->sparx5;
10906ebf182bSDaniel Machon 	struct sparx5_psfp_sg sg = { 0 };
10916ebf182bSDaniel Machon 	struct sparx5_psfp_fm fm = { 0 };
1092c9da1ac1SSteen Hegelund 	struct flow_action_entry *act;
1093c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
1094c9da1ac1SSteen Hegelund 	struct flow_rule *frule;
1095c9da1ac1SSteen Hegelund 	struct vcap_rule *vrule;
1096c9da1ac1SSteen Hegelund 
1097c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
1098392d0ab0SSteen Hegelund 
1099e7e3f514SSteen Hegelund 	err = sparx5_tc_flower_action_check(vctrl, ndev, fco, ingress);
1100392d0ab0SSteen Hegelund 	if (err)
1101392d0ab0SSteen Hegelund 		return err;
1102392d0ab0SSteen Hegelund 
1103c9da1ac1SSteen Hegelund 	vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC,
1104c9da1ac1SSteen Hegelund 				fco->common.prio, 0);
1105c9da1ac1SSteen Hegelund 	if (IS_ERR(vrule))
1106c9da1ac1SSteen Hegelund 		return PTR_ERR(vrule);
1107c9da1ac1SSteen Hegelund 
1108c9da1ac1SSteen Hegelund 	vrule->cookie = fco->cookie;
1109bcddc196SSteen Hegelund 
1110ebf44dedSSteen Hegelund 	state.vrule = vrule;
1111ebf44dedSSteen Hegelund 	state.frule = flow_cls_offload_flow_rule(fco);
1112ebf44dedSSteen Hegelund 	err = sparx5_tc_use_dissectors(&state, admin, vrule);
1113bcddc196SSteen Hegelund 	if (err)
1114bcddc196SSteen Hegelund 		goto out;
111540e7fe18SSteen Hegelund 
111640e7fe18SSteen Hegelund 	err = sparx5_tc_add_rule_counter(admin, vrule);
111740e7fe18SSteen Hegelund 	if (err)
111840e7fe18SSteen Hegelund 		goto out;
111940e7fe18SSteen Hegelund 
112088bd9ea7SSteen Hegelund 	err = sparx5_tc_add_rule_link_target(admin, vrule,
112188bd9ea7SSteen Hegelund 					     fco->common.chain_index);
112288bd9ea7SSteen Hegelund 	if (err)
112388bd9ea7SSteen Hegelund 		goto out;
112488bd9ea7SSteen Hegelund 
1125392d0ab0SSteen Hegelund 	frule = flow_cls_offload_flow_rule(fco);
1126c9da1ac1SSteen Hegelund 	flow_action_for_each(idx, act, &frule->action) {
1127c9da1ac1SSteen Hegelund 		switch (act->id) {
11286ebf182bSDaniel Machon 		case FLOW_ACTION_GATE: {
11296ebf182bSDaniel Machon 			err = sparx5_tc_flower_parse_act_gate(&sg, act, extack);
11306ebf182bSDaniel Machon 			if (err < 0)
11316ebf182bSDaniel Machon 				goto out;
11326ebf182bSDaniel Machon 
11336ebf182bSDaniel Machon 			tc_sg_idx = act->hw_index;
11346ebf182bSDaniel Machon 
11356ebf182bSDaniel Machon 			break;
11366ebf182bSDaniel Machon 		}
11376ebf182bSDaniel Machon 		case FLOW_ACTION_POLICE: {
11386ebf182bSDaniel Machon 			err = sparx5_tc_flower_parse_act_police(&fm.pol, act,
11396ebf182bSDaniel Machon 								extack);
11406ebf182bSDaniel Machon 			if (err < 0)
11416ebf182bSDaniel Machon 				goto out;
11426ebf182bSDaniel Machon 
11436ebf182bSDaniel Machon 			tc_pol_idx = fm.pol.idx;
11446ebf182bSDaniel Machon 			sf.max_sdu = act->police.mtu;
11456ebf182bSDaniel Machon 
11466ebf182bSDaniel Machon 			break;
11476ebf182bSDaniel Machon 		}
1148c9da1ac1SSteen Hegelund 		case FLOW_ACTION_TRAP:
114952b28a93SSteen Hegelund 			err = sparx5_tc_action_trap(admin, vrule, fco);
1150c9da1ac1SSteen Hegelund 			if (err)
1151c9da1ac1SSteen Hegelund 				goto out;
1152c9da1ac1SSteen Hegelund 			break;
1153c9da1ac1SSteen Hegelund 		case FLOW_ACTION_ACCEPT:
1154542e6e2cSSteen Hegelund 			err = sparx5_tc_set_actionset(admin, vrule);
1155c9da1ac1SSteen Hegelund 			if (err)
1156c9da1ac1SSteen Hegelund 				goto out;
1157c9da1ac1SSteen Hegelund 			break;
1158392d0ab0SSteen Hegelund 		case FLOW_ACTION_GOTO:
1159542e6e2cSSteen Hegelund 			err = sparx5_tc_set_actionset(admin, vrule);
1160542e6e2cSSteen Hegelund 			if (err)
1161542e6e2cSSteen Hegelund 				goto out;
116288bd9ea7SSteen Hegelund 			sparx5_tc_add_rule_link(vctrl, admin, vrule,
116388bd9ea7SSteen Hegelund 						fco->common.chain_index,
116488bd9ea7SSteen Hegelund 						act->chain_index);
1165392d0ab0SSteen Hegelund 			break;
1166ebf44dedSSteen Hegelund 		case FLOW_ACTION_VLAN_POP:
1167ebf44dedSSteen Hegelund 			err = sparx5_tc_action_vlan_pop(admin, vrule, fco,
1168ebf44dedSSteen Hegelund 							state.tpid);
1169ebf44dedSSteen Hegelund 			if (err)
1170ebf44dedSSteen Hegelund 				goto out;
1171ebf44dedSSteen Hegelund 			break;
1172ebf44dedSSteen Hegelund 		case FLOW_ACTION_VLAN_PUSH:
1173ebf44dedSSteen Hegelund 			err = sparx5_tc_action_vlan_push(admin, vrule, fco,
1174ebf44dedSSteen Hegelund 							 act, state.tpid);
1175ebf44dedSSteen Hegelund 			if (err)
1176ebf44dedSSteen Hegelund 				goto out;
1177ebf44dedSSteen Hegelund 			break;
1178ebf44dedSSteen Hegelund 		case FLOW_ACTION_VLAN_MANGLE:
1179ebf44dedSSteen Hegelund 			err = sparx5_tc_action_vlan_modify(admin, vrule, fco,
1180ebf44dedSSteen Hegelund 							   act, state.tpid);
1181ebf44dedSSteen Hegelund 			if (err)
1182ebf44dedSSteen Hegelund 				goto out;
1183ebf44dedSSteen Hegelund 			break;
1184c9da1ac1SSteen Hegelund 		default:
1185c9da1ac1SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
1186c9da1ac1SSteen Hegelund 					   "Unsupported TC action");
1187c9da1ac1SSteen Hegelund 			err = -EOPNOTSUPP;
1188c9da1ac1SSteen Hegelund 			goto out;
1189c9da1ac1SSteen Hegelund 		}
1190c9da1ac1SSteen Hegelund 	}
11910ca60948SSteen Hegelund 
11926ebf182bSDaniel Machon 	/* Setup PSFP */
11936ebf182bSDaniel Machon 	if (tc_sg_idx >= 0 || tc_pol_idx >= 0) {
11946ebf182bSDaniel Machon 		err = sparx5_tc_flower_psfp_setup(sparx5, vrule, tc_sg_idx,
11956ebf182bSDaniel Machon 						  tc_pol_idx, &sg, &fm, &sf);
11966ebf182bSDaniel Machon 		if (err)
11976ebf182bSDaniel Machon 			goto out;
11986ebf182bSDaniel Machon 	}
11996ebf182bSDaniel Machon 
1200e1d597ecSSteen Hegelund 	if (!sparx5_tc_flower_use_template(ndev, fco, admin, vrule)) {
1201ebf44dedSSteen Hegelund 		err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin,
1202ebf44dedSSteen Hegelund 						       state.l3_proto, &multi);
12030ca60948SSteen Hegelund 		if (err) {
12040ca60948SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
12050ca60948SSteen Hegelund 					   "No matching port keyset for filter protocol and keys");
12060ca60948SSteen Hegelund 			goto out;
12070ca60948SSteen Hegelund 		}
1208e1d597ecSSteen Hegelund 	}
12090ca60948SSteen Hegelund 
1210abc4010dSSteen Hegelund 	/* provide the l3 protocol to guide the keyset selection */
1211ebf44dedSSteen Hegelund 	err = vcap_val_rule(vrule, state.l3_proto);
1212c9da1ac1SSteen Hegelund 	if (err) {
1213c9da1ac1SSteen Hegelund 		vcap_set_tc_exterr(fco, vrule);
1214c9da1ac1SSteen Hegelund 		goto out;
1215c9da1ac1SSteen Hegelund 	}
1216c9da1ac1SSteen Hegelund 	err = vcap_add_rule(vrule);
1217c9da1ac1SSteen Hegelund 	if (err)
1218c9da1ac1SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
1219c9da1ac1SSteen Hegelund 				   "Could not add the filter");
12200ca60948SSteen Hegelund 
1221ebf44dedSSteen Hegelund 	if (state.l3_proto == ETH_P_ALL)
12220ca60948SSteen Hegelund 		err = sparx5_tc_add_remaining_rules(vctrl, fco, vrule, admin,
12230ca60948SSteen Hegelund 						    &multi);
12240ca60948SSteen Hegelund 
1225c9da1ac1SSteen Hegelund out:
1226c9da1ac1SSteen Hegelund 	vcap_free_rule(vrule);
1227c9da1ac1SSteen Hegelund 	return err;
1228c9da1ac1SSteen Hegelund }
1229c9da1ac1SSteen Hegelund 
12306ebf182bSDaniel Machon static void sparx5_tc_free_psfp_resources(struct sparx5 *sparx5,
12316ebf182bSDaniel Machon 					  struct vcap_rule *vrule)
12326ebf182bSDaniel Machon {
12336ebf182bSDaniel Machon 	struct vcap_client_actionfield *afield;
12346ebf182bSDaniel Machon 	u32 isdx, sfid, sgid, fmid;
12356ebf182bSDaniel Machon 
12366ebf182bSDaniel Machon 	/* Check if VCAP_AF_ISDX_VAL action is set for this rule - and if
12376ebf182bSDaniel Machon 	 * it is used for stream and/or flow-meter classification.
12386ebf182bSDaniel Machon 	 */
12396ebf182bSDaniel Machon 	afield = vcap_find_actionfield(vrule, VCAP_AF_ISDX_VAL);
12406ebf182bSDaniel Machon 	if (!afield)
12416ebf182bSDaniel Machon 		return;
12426ebf182bSDaniel Machon 
12436ebf182bSDaniel Machon 	isdx = afield->data.u32.value;
12446ebf182bSDaniel Machon 	sfid = sparx5_psfp_isdx_get_sf(sparx5, isdx);
12456ebf182bSDaniel Machon 
12466ebf182bSDaniel Machon 	if (!sfid)
12476ebf182bSDaniel Machon 		return;
12486ebf182bSDaniel Machon 
12496ebf182bSDaniel Machon 	fmid = sparx5_psfp_isdx_get_fm(sparx5, isdx);
12506ebf182bSDaniel Machon 	sgid = sparx5_psfp_sf_get_sg(sparx5, sfid);
12516ebf182bSDaniel Machon 
12526ebf182bSDaniel Machon 	if (fmid && sparx5_psfp_fm_del(sparx5, fmid) < 0)
12536ebf182bSDaniel Machon 		pr_err("%s:%d Could not delete invalid fmid: %d", __func__,
12546ebf182bSDaniel Machon 		       __LINE__, fmid);
12556ebf182bSDaniel Machon 
12566ebf182bSDaniel Machon 	if (sgid && sparx5_psfp_sg_del(sparx5, sgid) < 0)
12576ebf182bSDaniel Machon 		pr_err("%s:%d Could not delete invalid sgid: %d", __func__,
12586ebf182bSDaniel Machon 		       __LINE__, sgid);
12596ebf182bSDaniel Machon 
12606ebf182bSDaniel Machon 	if (sparx5_psfp_sf_del(sparx5, sfid) < 0)
12616ebf182bSDaniel Machon 		pr_err("%s:%d Could not delete invalid sfid: %d", __func__,
12626ebf182bSDaniel Machon 		       __LINE__, sfid);
12636ebf182bSDaniel Machon 
12646ebf182bSDaniel Machon 	sparx5_isdx_conf_set(sparx5, isdx, 0, 0);
12656ebf182bSDaniel Machon }
12666ebf182bSDaniel Machon 
12676ebf182bSDaniel Machon static int sparx5_tc_free_rule_resources(struct net_device *ndev,
12686ebf182bSDaniel Machon 					 struct vcap_control *vctrl,
12696ebf182bSDaniel Machon 					 int rule_id)
12706ebf182bSDaniel Machon {
12716ebf182bSDaniel Machon 	struct sparx5_port *port = netdev_priv(ndev);
12726ebf182bSDaniel Machon 	struct sparx5 *sparx5 = port->sparx5;
12736ebf182bSDaniel Machon 	struct vcap_rule *vrule;
12746ebf182bSDaniel Machon 	int ret = 0;
12756ebf182bSDaniel Machon 
12766ebf182bSDaniel Machon 	vrule = vcap_get_rule(vctrl, rule_id);
12776ebf182bSDaniel Machon 	if (!vrule || IS_ERR(vrule))
12786ebf182bSDaniel Machon 		return -EINVAL;
12796ebf182bSDaniel Machon 
12806ebf182bSDaniel Machon 	sparx5_tc_free_psfp_resources(sparx5, vrule);
12816ebf182bSDaniel Machon 
12826ebf182bSDaniel Machon 	vcap_free_rule(vrule);
12836ebf182bSDaniel Machon 	return ret;
12846ebf182bSDaniel Machon }
12856ebf182bSDaniel Machon 
1286c9da1ac1SSteen Hegelund static int sparx5_tc_flower_destroy(struct net_device *ndev,
1287c9da1ac1SSteen Hegelund 				    struct flow_cls_offload *fco,
1288c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin)
1289c9da1ac1SSteen Hegelund {
1290c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
12916ebf182bSDaniel Machon 	int err = -ENOENT, count = 0, rule_id;
1292c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
1293c9da1ac1SSteen Hegelund 
1294c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
1295c9da1ac1SSteen Hegelund 	while (true) {
1296c9da1ac1SSteen Hegelund 		rule_id = vcap_lookup_rule_by_cookie(vctrl, fco->cookie);
1297c9da1ac1SSteen Hegelund 		if (rule_id <= 0)
1298c9da1ac1SSteen Hegelund 			break;
12996ebf182bSDaniel Machon 		if (count == 0) {
13006ebf182bSDaniel Machon 			/* Resources are attached to the first rule of
13016ebf182bSDaniel Machon 			 * a set of rules. Only works if the rules are
13026ebf182bSDaniel Machon 			 * in the correct order.
13036ebf182bSDaniel Machon 			 */
13046ebf182bSDaniel Machon 			err = sparx5_tc_free_rule_resources(ndev, vctrl,
13056ebf182bSDaniel Machon 							    rule_id);
13066ebf182bSDaniel Machon 			if (err)
13076ebf182bSDaniel Machon 				pr_err("%s:%d: could not free resources %d\n",
13086ebf182bSDaniel Machon 				       __func__, __LINE__, rule_id);
13096ebf182bSDaniel Machon 		}
1310c9da1ac1SSteen Hegelund 		err = vcap_del_rule(vctrl, ndev, rule_id);
1311c9da1ac1SSteen Hegelund 		if (err) {
1312c9da1ac1SSteen Hegelund 			pr_err("%s:%d: could not delete rule %d\n",
1313c9da1ac1SSteen Hegelund 			       __func__, __LINE__, rule_id);
1314c9da1ac1SSteen Hegelund 			break;
1315c9da1ac1SSteen Hegelund 		}
1316c9da1ac1SSteen Hegelund 	}
1317c9da1ac1SSteen Hegelund 	return err;
1318c9da1ac1SSteen Hegelund }
1319c9da1ac1SSteen Hegelund 
132040e7fe18SSteen Hegelund static int sparx5_tc_flower_stats(struct net_device *ndev,
132140e7fe18SSteen Hegelund 				  struct flow_cls_offload *fco,
132240e7fe18SSteen Hegelund 				  struct vcap_admin *admin)
132340e7fe18SSteen Hegelund {
132440e7fe18SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
132527d293ccSSteen Hegelund 	struct vcap_counter ctr = {};
132640e7fe18SSteen Hegelund 	struct vcap_control *vctrl;
132740e7fe18SSteen Hegelund 	ulong lastused = 0;
132840e7fe18SSteen Hegelund 	int err;
132940e7fe18SSteen Hegelund 
133040e7fe18SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
133127d293ccSSteen Hegelund 	err = vcap_get_rule_count_by_cookie(vctrl, &ctr, fco->cookie);
133240e7fe18SSteen Hegelund 	if (err)
133340e7fe18SSteen Hegelund 		return err;
133427d293ccSSteen Hegelund 	flow_stats_update(&fco->stats, 0x0, ctr.value, 0, lastused,
133540e7fe18SSteen Hegelund 			  FLOW_ACTION_HW_STATS_IMMEDIATE);
133640e7fe18SSteen Hegelund 	return err;
133740e7fe18SSteen Hegelund }
133840e7fe18SSteen Hegelund 
1339e1d597ecSSteen Hegelund static int sparx5_tc_flower_template_create(struct net_device *ndev,
1340e1d597ecSSteen Hegelund 					    struct flow_cls_offload *fco,
1341e1d597ecSSteen Hegelund 					    struct vcap_admin *admin)
1342e1d597ecSSteen Hegelund {
1343e1d597ecSSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
1344e1d597ecSSteen Hegelund 	struct vcap_tc_flower_parse_usage state = {
1345e1d597ecSSteen Hegelund 		.fco = fco,
1346e1d597ecSSteen Hegelund 		.l3_proto = ETH_P_ALL,
1347e1d597ecSSteen Hegelund 		.admin = admin,
1348e1d597ecSSteen Hegelund 	};
1349e1d597ecSSteen Hegelund 	struct sparx5_tc_flower_template *ftp;
1350e1d597ecSSteen Hegelund 	struct vcap_keyset_list kslist = {};
1351e1d597ecSSteen Hegelund 	enum vcap_keyfield_set keysets[10];
1352e1d597ecSSteen Hegelund 	struct vcap_control *vctrl;
1353e1d597ecSSteen Hegelund 	struct vcap_rule *vrule;
1354e1d597ecSSteen Hegelund 	int count, err;
1355e1d597ecSSteen Hegelund 
1356e1d597ecSSteen Hegelund 	if (admin->vtype == VCAP_TYPE_ES0) {
1357e1d597ecSSteen Hegelund 		pr_err("%s:%d: %s\n", __func__, __LINE__,
1358e1d597ecSSteen Hegelund 		       "VCAP does not support templates");
1359e1d597ecSSteen Hegelund 		return -EINVAL;
1360e1d597ecSSteen Hegelund 	}
1361e1d597ecSSteen Hegelund 
1362e1d597ecSSteen Hegelund 	count = vcap_admin_rule_count(admin, fco->common.chain_index);
1363e1d597ecSSteen Hegelund 	if (count > 0) {
1364e1d597ecSSteen Hegelund 		pr_err("%s:%d: %s\n", __func__, __LINE__,
1365e1d597ecSSteen Hegelund 		       "Filters are already present");
1366e1d597ecSSteen Hegelund 		return -EBUSY;
1367e1d597ecSSteen Hegelund 	}
1368e1d597ecSSteen Hegelund 
1369e1d597ecSSteen Hegelund 	ftp = kzalloc(sizeof(*ftp), GFP_KERNEL);
1370e1d597ecSSteen Hegelund 	if (!ftp)
1371e1d597ecSSteen Hegelund 		return -ENOMEM;
1372e1d597ecSSteen Hegelund 
1373e1d597ecSSteen Hegelund 	ftp->cid = fco->common.chain_index;
1374e1d597ecSSteen Hegelund 	ftp->orig = VCAP_KFS_NO_VALUE;
1375e1d597ecSSteen Hegelund 	ftp->keyset = VCAP_KFS_NO_VALUE;
1376e1d597ecSSteen Hegelund 
1377e1d597ecSSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
1378e1d597ecSSteen Hegelund 	vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index,
1379e1d597ecSSteen Hegelund 				VCAP_USER_TC, fco->common.prio, 0);
1380e1d597ecSSteen Hegelund 	if (IS_ERR(vrule)) {
1381e1d597ecSSteen Hegelund 		err = PTR_ERR(vrule);
1382e1d597ecSSteen Hegelund 		goto err_rule;
1383e1d597ecSSteen Hegelund 	}
1384e1d597ecSSteen Hegelund 
1385e1d597ecSSteen Hegelund 	state.vrule = vrule;
1386e1d597ecSSteen Hegelund 	state.frule = flow_cls_offload_flow_rule(fco);
1387e1d597ecSSteen Hegelund 	err = sparx5_tc_use_dissectors(&state, admin, vrule);
1388e1d597ecSSteen Hegelund 	if (err) {
1389e1d597ecSSteen Hegelund 		pr_err("%s:%d: key error: %d\n", __func__, __LINE__, err);
1390e1d597ecSSteen Hegelund 		goto out;
1391e1d597ecSSteen Hegelund 	}
1392e1d597ecSSteen Hegelund 
1393e1d597ecSSteen Hegelund 	ftp->l3_proto = state.l3_proto;
1394e1d597ecSSteen Hegelund 
1395e1d597ecSSteen Hegelund 	sparx5_tc_flower_simplify_rule(admin, vrule, state.l3_proto);
1396e1d597ecSSteen Hegelund 
1397e1d597ecSSteen Hegelund 	/* Find the keysets that the rule can use */
1398e1d597ecSSteen Hegelund 	kslist.keysets = keysets;
1399e1d597ecSSteen Hegelund 	kslist.max = ARRAY_SIZE(keysets);
1400e1d597ecSSteen Hegelund 	if (!vcap_rule_find_keysets(vrule, &kslist)) {
1401e1d597ecSSteen Hegelund 		pr_err("%s:%d: %s\n", __func__, __LINE__,
1402e1d597ecSSteen Hegelund 		       "Could not find a suitable keyset");
1403e1d597ecSSteen Hegelund 		err = -ENOENT;
1404e1d597ecSSteen Hegelund 		goto out;
1405e1d597ecSSteen Hegelund 	}
1406e1d597ecSSteen Hegelund 
1407e1d597ecSSteen Hegelund 	ftp->keyset = vcap_select_min_rule_keyset(vctrl, admin->vtype, &kslist);
1408e1d597ecSSteen Hegelund 	kslist.cnt = 0;
1409e1d597ecSSteen Hegelund 	sparx5_vcap_set_port_keyset(ndev, admin, fco->common.chain_index,
1410e1d597ecSSteen Hegelund 				    state.l3_proto,
1411e1d597ecSSteen Hegelund 				    ftp->keyset,
1412e1d597ecSSteen Hegelund 				    &kslist);
1413e1d597ecSSteen Hegelund 
1414e1d597ecSSteen Hegelund 	if (kslist.cnt > 0)
1415e1d597ecSSteen Hegelund 		ftp->orig = kslist.keysets[0];
1416e1d597ecSSteen Hegelund 
1417e1d597ecSSteen Hegelund 	/* Store new template */
1418e1d597ecSSteen Hegelund 	list_add_tail(&ftp->list, &port->tc_templates);
1419e1d597ecSSteen Hegelund 	vcap_free_rule(vrule);
1420e1d597ecSSteen Hegelund 	return 0;
1421e1d597ecSSteen Hegelund 
1422e1d597ecSSteen Hegelund out:
1423e1d597ecSSteen Hegelund 	vcap_free_rule(vrule);
1424e1d597ecSSteen Hegelund err_rule:
1425e1d597ecSSteen Hegelund 	kfree(ftp);
1426e1d597ecSSteen Hegelund 	return err;
1427e1d597ecSSteen Hegelund }
1428e1d597ecSSteen Hegelund 
1429e1d597ecSSteen Hegelund static int sparx5_tc_flower_template_destroy(struct net_device *ndev,
1430e1d597ecSSteen Hegelund 					     struct flow_cls_offload *fco,
1431e1d597ecSSteen Hegelund 					     struct vcap_admin *admin)
1432e1d597ecSSteen Hegelund {
1433e1d597ecSSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
1434e1d597ecSSteen Hegelund 	struct sparx5_tc_flower_template *ftp, *tmp;
1435e1d597ecSSteen Hegelund 	int err = -ENOENT;
1436e1d597ecSSteen Hegelund 
1437e1d597ecSSteen Hegelund 	/* Rules using the template are removed by the tc framework */
1438e1d597ecSSteen Hegelund 	list_for_each_entry_safe(ftp, tmp, &port->tc_templates, list) {
1439e1d597ecSSteen Hegelund 		if (ftp->cid != fco->common.chain_index)
1440e1d597ecSSteen Hegelund 			continue;
1441e1d597ecSSteen Hegelund 
1442e1d597ecSSteen Hegelund 		sparx5_vcap_set_port_keyset(ndev, admin,
1443e1d597ecSSteen Hegelund 					    fco->common.chain_index,
1444e1d597ecSSteen Hegelund 					    ftp->l3_proto, ftp->orig,
1445e1d597ecSSteen Hegelund 					    NULL);
1446e1d597ecSSteen Hegelund 		list_del(&ftp->list);
1447e1d597ecSSteen Hegelund 		kfree(ftp);
1448e1d597ecSSteen Hegelund 		break;
1449e1d597ecSSteen Hegelund 	}
1450e1d597ecSSteen Hegelund 	return err;
1451e1d597ecSSteen Hegelund }
1452e1d597ecSSteen Hegelund 
1453c9da1ac1SSteen Hegelund int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco,
1454c9da1ac1SSteen Hegelund 		     bool ingress)
1455c9da1ac1SSteen Hegelund {
1456c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
1457c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
1458c9da1ac1SSteen Hegelund 	struct vcap_admin *admin;
1459c9da1ac1SSteen Hegelund 	int err = -EINVAL;
1460c9da1ac1SSteen Hegelund 
1461c9da1ac1SSteen Hegelund 	/* Get vcap instance from the chain id */
1462c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
1463c9da1ac1SSteen Hegelund 	admin = vcap_find_admin(vctrl, fco->common.chain_index);
1464c9da1ac1SSteen Hegelund 	if (!admin) {
1465c9da1ac1SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack, "Invalid chain");
1466c9da1ac1SSteen Hegelund 		return err;
1467c9da1ac1SSteen Hegelund 	}
1468c9da1ac1SSteen Hegelund 
1469c9da1ac1SSteen Hegelund 	switch (fco->command) {
1470c9da1ac1SSteen Hegelund 	case FLOW_CLS_REPLACE:
1471e7e3f514SSteen Hegelund 		return sparx5_tc_flower_replace(ndev, fco, admin, ingress);
1472c9da1ac1SSteen Hegelund 	case FLOW_CLS_DESTROY:
1473c9da1ac1SSteen Hegelund 		return sparx5_tc_flower_destroy(ndev, fco, admin);
147440e7fe18SSteen Hegelund 	case FLOW_CLS_STATS:
147540e7fe18SSteen Hegelund 		return sparx5_tc_flower_stats(ndev, fco, admin);
1476e1d597ecSSteen Hegelund 	case FLOW_CLS_TMPLT_CREATE:
1477e1d597ecSSteen Hegelund 		return sparx5_tc_flower_template_create(ndev, fco, admin);
1478e1d597ecSSteen Hegelund 	case FLOW_CLS_TMPLT_DESTROY:
1479e1d597ecSSteen Hegelund 		return sparx5_tc_flower_template_destroy(ndev, fco, admin);
1480c9da1ac1SSteen Hegelund 	default:
1481c9da1ac1SSteen Hegelund 		return -EOPNOTSUPP;
1482c9da1ac1SSteen Hegelund 	}
1483c9da1ac1SSteen Hegelund }
1484