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 
7c9da1ac1SSteen Hegelund #include <net/tcp.h>
8c9da1ac1SSteen Hegelund 
9c9da1ac1SSteen Hegelund #include "sparx5_tc.h"
10c9da1ac1SSteen Hegelund #include "vcap_api.h"
11c9da1ac1SSteen Hegelund #include "vcap_api_client.h"
12c9da1ac1SSteen Hegelund #include "sparx5_main.h"
13c9da1ac1SSteen Hegelund #include "sparx5_vcap_impl.h"
14c9da1ac1SSteen Hegelund 
150ca60948SSteen Hegelund #define SPX5_MAX_RULE_SIZE 13 /* allows X1, X2, X4, X6 and X12 rules */
160ca60948SSteen Hegelund 
170ca60948SSteen Hegelund /* Collect keysets and type ids for multiple rules per size */
180ca60948SSteen Hegelund struct sparx5_wildcard_rule {
190ca60948SSteen Hegelund 	bool selected;
200ca60948SSteen Hegelund 	u8 value;
210ca60948SSteen Hegelund 	u8 mask;
220ca60948SSteen Hegelund 	enum vcap_keyfield_set keyset;
230ca60948SSteen Hegelund };
240ca60948SSteen Hegelund 
250ca60948SSteen Hegelund struct sparx5_multiple_rules {
260ca60948SSteen Hegelund 	struct sparx5_wildcard_rule rule[SPX5_MAX_RULE_SIZE];
270ca60948SSteen Hegelund };
280ca60948SSteen Hegelund 
29c9da1ac1SSteen Hegelund struct sparx5_tc_flower_parse_usage {
30c9da1ac1SSteen Hegelund 	struct flow_cls_offload *fco;
31c9da1ac1SSteen Hegelund 	struct flow_rule *frule;
32c9da1ac1SSteen Hegelund 	struct vcap_rule *vrule;
33d6c2964dSSteen Hegelund 	u16 l3_proto;
34d6c2964dSSteen Hegelund 	u8 l4_proto;
35c9da1ac1SSteen Hegelund 	unsigned int used_keys;
36c9da1ac1SSteen Hegelund };
37c9da1ac1SSteen Hegelund 
3840e7fe18SSteen Hegelund struct sparx5_tc_rule_pkt_cnt {
3940e7fe18SSteen Hegelund 	u64 cookie;
4040e7fe18SSteen Hegelund 	u32 pkts;
4140e7fe18SSteen Hegelund };
4240e7fe18SSteen Hegelund 
43d6c2964dSSteen Hegelund /* These protocols have dedicated keysets in IS2 and a TC dissector
44d6c2964dSSteen Hegelund  * ETH_P_ARP does not have a TC dissector
45d6c2964dSSteen Hegelund  */
46d6c2964dSSteen Hegelund static u16 sparx5_tc_known_etypes[] = {
47d6c2964dSSteen Hegelund 	ETH_P_ALL,
483a344f99SSteen Hegelund 	ETH_P_ARP,
49d6c2964dSSteen Hegelund 	ETH_P_IP,
50d6c2964dSSteen Hegelund 	ETH_P_IPV6,
51d6c2964dSSteen Hegelund };
52d6c2964dSSteen Hegelund 
533a344f99SSteen Hegelund enum sparx5_is2_arp_opcode {
543a344f99SSteen Hegelund 	SPX5_IS2_ARP_REQUEST,
553a344f99SSteen Hegelund 	SPX5_IS2_ARP_REPLY,
563a344f99SSteen Hegelund 	SPX5_IS2_RARP_REQUEST,
573a344f99SSteen Hegelund 	SPX5_IS2_RARP_REPLY,
583a344f99SSteen Hegelund };
593a344f99SSteen Hegelund 
603a344f99SSteen Hegelund enum tc_arp_opcode {
613a344f99SSteen Hegelund 	TC_ARP_OP_RESERVED,
623a344f99SSteen Hegelund 	TC_ARP_OP_REQUEST,
633a344f99SSteen Hegelund 	TC_ARP_OP_REPLY,
643a344f99SSteen Hegelund };
653a344f99SSteen Hegelund 
66d6c2964dSSteen Hegelund static bool sparx5_tc_is_known_etype(u16 etype)
67d6c2964dSSteen Hegelund {
68d6c2964dSSteen Hegelund 	int idx;
69d6c2964dSSteen Hegelund 
70d6c2964dSSteen Hegelund 	/* For now this only knows about IS2 traffic classification */
71d6c2964dSSteen Hegelund 	for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_known_etypes); ++idx)
72d6c2964dSSteen Hegelund 		if (sparx5_tc_known_etypes[idx] == etype)
73d6c2964dSSteen Hegelund 			return true;
74d6c2964dSSteen Hegelund 
75d6c2964dSSteen Hegelund 	return false;
76d6c2964dSSteen Hegelund }
77d6c2964dSSteen Hegelund 
78c9da1ac1SSteen Hegelund static int sparx5_tc_flower_handler_ethaddr_usage(struct sparx5_tc_flower_parse_usage *st)
79c9da1ac1SSteen Hegelund {
80c9da1ac1SSteen Hegelund 	enum vcap_key_field smac_key = VCAP_KF_L2_SMAC;
81c9da1ac1SSteen Hegelund 	enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC;
82c9da1ac1SSteen Hegelund 	struct flow_match_eth_addrs match;
83c9da1ac1SSteen Hegelund 	struct vcap_u48_key smac, dmac;
84c9da1ac1SSteen Hegelund 	int err = 0;
85c9da1ac1SSteen Hegelund 
86c9da1ac1SSteen Hegelund 	flow_rule_match_eth_addrs(st->frule, &match);
87c9da1ac1SSteen Hegelund 
88c9da1ac1SSteen Hegelund 	if (!is_zero_ether_addr(match.mask->src)) {
89c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN);
90c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN);
91c9da1ac1SSteen Hegelund 		err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac);
92c9da1ac1SSteen Hegelund 		if (err)
93c9da1ac1SSteen Hegelund 			goto out;
94c9da1ac1SSteen Hegelund 	}
95c9da1ac1SSteen Hegelund 
96c9da1ac1SSteen Hegelund 	if (!is_zero_ether_addr(match.mask->dst)) {
97c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN);
98c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN);
99c9da1ac1SSteen Hegelund 		err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac);
100c9da1ac1SSteen Hegelund 		if (err)
101c9da1ac1SSteen Hegelund 			goto out;
102c9da1ac1SSteen Hegelund 	}
103c9da1ac1SSteen Hegelund 
104c9da1ac1SSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS);
105c9da1ac1SSteen Hegelund 
106c9da1ac1SSteen Hegelund 	return err;
107c9da1ac1SSteen Hegelund 
108c9da1ac1SSteen Hegelund out:
109c9da1ac1SSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "eth_addr parse error");
110c9da1ac1SSteen Hegelund 	return err;
111c9da1ac1SSteen Hegelund }
112c9da1ac1SSteen Hegelund 
113d6c2964dSSteen Hegelund static int
114d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ipv4_usage(struct sparx5_tc_flower_parse_usage *st)
115d6c2964dSSteen Hegelund {
116d6c2964dSSteen Hegelund 	int err = 0;
117d6c2964dSSteen Hegelund 
118d6c2964dSSteen Hegelund 	if (st->l3_proto == ETH_P_IP) {
119d6c2964dSSteen Hegelund 		struct flow_match_ipv4_addrs mt;
120d6c2964dSSteen Hegelund 
121d6c2964dSSteen Hegelund 		flow_rule_match_ipv4_addrs(st->frule, &mt);
122d6c2964dSSteen Hegelund 		if (mt.mask->src) {
123d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
124d6c2964dSSteen Hegelund 						    VCAP_KF_L3_IP4_SIP,
125d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.key->src),
126d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.mask->src));
127d6c2964dSSteen Hegelund 			if (err)
128d6c2964dSSteen Hegelund 				goto out;
129d6c2964dSSteen Hegelund 		}
130d6c2964dSSteen Hegelund 		if (mt.mask->dst) {
131d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
132d6c2964dSSteen Hegelund 						    VCAP_KF_L3_IP4_DIP,
133d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.key->dst),
134d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.mask->dst));
135d6c2964dSSteen Hegelund 			if (err)
136d6c2964dSSteen Hegelund 				goto out;
137d6c2964dSSteen Hegelund 		}
138d6c2964dSSteen Hegelund 	}
139d6c2964dSSteen Hegelund 
140d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS);
141d6c2964dSSteen Hegelund 
142d6c2964dSSteen Hegelund 	return err;
143d6c2964dSSteen Hegelund 
144d6c2964dSSteen Hegelund out:
145d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv4_addr parse error");
146d6c2964dSSteen Hegelund 	return err;
147d6c2964dSSteen Hegelund }
148d6c2964dSSteen Hegelund 
149d6c2964dSSteen Hegelund static int
150d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ipv6_usage(struct sparx5_tc_flower_parse_usage *st)
151d6c2964dSSteen Hegelund {
152d6c2964dSSteen Hegelund 	int err = 0;
153d6c2964dSSteen Hegelund 
154d6c2964dSSteen Hegelund 	if (st->l3_proto == ETH_P_IPV6) {
155d6c2964dSSteen Hegelund 		struct flow_match_ipv6_addrs mt;
156d6c2964dSSteen Hegelund 		struct vcap_u128_key sip;
157d6c2964dSSteen Hegelund 		struct vcap_u128_key dip;
158d6c2964dSSteen Hegelund 
159d6c2964dSSteen Hegelund 		flow_rule_match_ipv6_addrs(st->frule, &mt);
160d6c2964dSSteen Hegelund 		/* Check if address masks are non-zero */
161d6c2964dSSteen Hegelund 		if (!ipv6_addr_any(&mt.mask->src)) {
162d6c2964dSSteen Hegelund 			vcap_netbytes_copy(sip.value, mt.key->src.s6_addr, 16);
163d6c2964dSSteen Hegelund 			vcap_netbytes_copy(sip.mask, mt.mask->src.s6_addr, 16);
164d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u128(st->vrule,
165d6c2964dSSteen Hegelund 						     VCAP_KF_L3_IP6_SIP, &sip);
166d6c2964dSSteen Hegelund 			if (err)
167d6c2964dSSteen Hegelund 				goto out;
168d6c2964dSSteen Hegelund 		}
169d6c2964dSSteen Hegelund 		if (!ipv6_addr_any(&mt.mask->dst)) {
170d6c2964dSSteen Hegelund 			vcap_netbytes_copy(dip.value, mt.key->dst.s6_addr, 16);
171d6c2964dSSteen Hegelund 			vcap_netbytes_copy(dip.mask, mt.mask->dst.s6_addr, 16);
172d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u128(st->vrule,
173d6c2964dSSteen Hegelund 						     VCAP_KF_L3_IP6_DIP, &dip);
174d6c2964dSSteen Hegelund 			if (err)
175d6c2964dSSteen Hegelund 				goto out;
176d6c2964dSSteen Hegelund 		}
177d6c2964dSSteen Hegelund 	}
178d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS);
179d6c2964dSSteen Hegelund 	return err;
180d6c2964dSSteen Hegelund out:
181d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv6_addr parse error");
182d6c2964dSSteen Hegelund 	return err;
183d6c2964dSSteen Hegelund }
184d6c2964dSSteen Hegelund 
185d6c2964dSSteen Hegelund static int
186d6c2964dSSteen Hegelund sparx5_tc_flower_handler_control_usage(struct sparx5_tc_flower_parse_usage *st)
187d6c2964dSSteen Hegelund {
188d6c2964dSSteen Hegelund 	struct flow_match_control mt;
189d6c2964dSSteen Hegelund 	u32 value, mask;
190d6c2964dSSteen Hegelund 	int err = 0;
191d6c2964dSSteen Hegelund 
192d6c2964dSSteen Hegelund 	flow_rule_match_control(st->frule, &mt);
193d6c2964dSSteen Hegelund 
194d6c2964dSSteen Hegelund 	if (mt.mask->flags) {
195d6c2964dSSteen Hegelund 		if (mt.mask->flags & FLOW_DIS_FIRST_FRAG) {
196d6c2964dSSteen Hegelund 			if (mt.key->flags & FLOW_DIS_FIRST_FRAG) {
197d6c2964dSSteen Hegelund 				value = 1; /* initial fragment */
198d6c2964dSSteen Hegelund 				mask = 0x3;
199d6c2964dSSteen Hegelund 			} else {
200d6c2964dSSteen Hegelund 				if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) {
201d6c2964dSSteen Hegelund 					value = 3; /* follow up fragment */
202d6c2964dSSteen Hegelund 					mask = 0x3;
203d6c2964dSSteen Hegelund 				} else {
204d6c2964dSSteen Hegelund 					value = 0; /* no fragment */
205d6c2964dSSteen Hegelund 					mask = 0x3;
206d6c2964dSSteen Hegelund 				}
207d6c2964dSSteen Hegelund 			}
208d6c2964dSSteen Hegelund 		} else {
209d6c2964dSSteen Hegelund 			if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) {
210d6c2964dSSteen Hegelund 				value = 3; /* follow up fragment */
211d6c2964dSSteen Hegelund 				mask = 0x3;
212d6c2964dSSteen Hegelund 			} else {
213d6c2964dSSteen Hegelund 				value = 0; /* no fragment */
214d6c2964dSSteen Hegelund 				mask = 0x3;
215d6c2964dSSteen Hegelund 			}
216d6c2964dSSteen Hegelund 		}
217d6c2964dSSteen Hegelund 
218d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule,
219d6c2964dSSteen Hegelund 					    VCAP_KF_L3_FRAGMENT_TYPE,
220d6c2964dSSteen Hegelund 					    value, mask);
221d6c2964dSSteen Hegelund 		if (err)
222d6c2964dSSteen Hegelund 			goto out;
223d6c2964dSSteen Hegelund 	}
224d6c2964dSSteen Hegelund 
225d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL);
226d6c2964dSSteen Hegelund 
227d6c2964dSSteen Hegelund 	return err;
228d6c2964dSSteen Hegelund 
229d6c2964dSSteen Hegelund out:
230d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error");
231d6c2964dSSteen Hegelund 	return err;
232d6c2964dSSteen Hegelund }
233d6c2964dSSteen Hegelund 
234d6c2964dSSteen Hegelund static int
235d6c2964dSSteen Hegelund sparx5_tc_flower_handler_portnum_usage(struct sparx5_tc_flower_parse_usage *st)
236d6c2964dSSteen Hegelund {
237d6c2964dSSteen Hegelund 	struct flow_match_ports mt;
238d6c2964dSSteen Hegelund 	u16 value, mask;
239d6c2964dSSteen Hegelund 	int err = 0;
240d6c2964dSSteen Hegelund 
241d6c2964dSSteen Hegelund 	flow_rule_match_ports(st->frule, &mt);
242d6c2964dSSteen Hegelund 
243d6c2964dSSteen Hegelund 	if (mt.mask->src) {
244d6c2964dSSteen Hegelund 		value = be16_to_cpu(mt.key->src);
245d6c2964dSSteen Hegelund 		mask = be16_to_cpu(mt.mask->src);
246d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_SPORT, value,
247d6c2964dSSteen Hegelund 					    mask);
248d6c2964dSSteen Hegelund 		if (err)
249d6c2964dSSteen Hegelund 			goto out;
250d6c2964dSSteen Hegelund 	}
251d6c2964dSSteen Hegelund 
252d6c2964dSSteen Hegelund 	if (mt.mask->dst) {
253d6c2964dSSteen Hegelund 		value = be16_to_cpu(mt.key->dst);
254d6c2964dSSteen Hegelund 		mask = be16_to_cpu(mt.mask->dst);
255d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_DPORT, value,
256d6c2964dSSteen Hegelund 					    mask);
257d6c2964dSSteen Hegelund 		if (err)
258d6c2964dSSteen Hegelund 			goto out;
259d6c2964dSSteen Hegelund 	}
260d6c2964dSSteen Hegelund 
261d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_PORTS);
262d6c2964dSSteen Hegelund 
263d6c2964dSSteen Hegelund 	return err;
264d6c2964dSSteen Hegelund 
265d6c2964dSSteen Hegelund out:
266d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "port parse error");
267d6c2964dSSteen Hegelund 	return err;
268d6c2964dSSteen Hegelund }
269d6c2964dSSteen Hegelund 
270d6c2964dSSteen Hegelund static int
271d6c2964dSSteen Hegelund sparx5_tc_flower_handler_basic_usage(struct sparx5_tc_flower_parse_usage *st)
272d6c2964dSSteen Hegelund {
273d6c2964dSSteen Hegelund 	struct flow_match_basic mt;
274d6c2964dSSteen Hegelund 	int err = 0;
275d6c2964dSSteen Hegelund 
276d6c2964dSSteen Hegelund 	flow_rule_match_basic(st->frule, &mt);
277d6c2964dSSteen Hegelund 
278d6c2964dSSteen Hegelund 	if (mt.mask->n_proto) {
279d6c2964dSSteen Hegelund 		st->l3_proto = be16_to_cpu(mt.key->n_proto);
280d6c2964dSSteen Hegelund 		if (!sparx5_tc_is_known_etype(st->l3_proto)) {
281d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,
282d6c2964dSSteen Hegelund 						    st->l3_proto, ~0);
283d6c2964dSSteen Hegelund 			if (err)
284d6c2964dSSteen Hegelund 				goto out;
285d6c2964dSSteen Hegelund 		} else if (st->l3_proto == ETH_P_IP) {
286d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
287d6c2964dSSteen Hegelund 						    VCAP_BIT_1);
288d6c2964dSSteen Hegelund 			if (err)
289d6c2964dSSteen Hegelund 				goto out;
290d6c2964dSSteen Hegelund 		} else if (st->l3_proto == ETH_P_IPV6) {
291d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
292d6c2964dSSteen Hegelund 						    VCAP_BIT_0);
293d6c2964dSSteen Hegelund 			if (err)
294d6c2964dSSteen Hegelund 				goto out;
295d6c2964dSSteen Hegelund 		}
296d6c2964dSSteen Hegelund 	}
297d6c2964dSSteen Hegelund 
298d6c2964dSSteen Hegelund 	if (mt.mask->ip_proto) {
299d6c2964dSSteen Hegelund 		st->l4_proto = mt.key->ip_proto;
300d6c2964dSSteen Hegelund 		if (st->l4_proto == IPPROTO_TCP) {
301d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule,
302d6c2964dSSteen Hegelund 						    VCAP_KF_TCP_IS,
303d6c2964dSSteen Hegelund 						    VCAP_BIT_1);
304d6c2964dSSteen Hegelund 			if (err)
305d6c2964dSSteen Hegelund 				goto out;
306d6c2964dSSteen Hegelund 		} else if (st->l4_proto == IPPROTO_UDP) {
307d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule,
308d6c2964dSSteen Hegelund 						    VCAP_KF_TCP_IS,
309d6c2964dSSteen Hegelund 						    VCAP_BIT_0);
310d6c2964dSSteen Hegelund 			if (err)
311d6c2964dSSteen Hegelund 				goto out;
312d6c2964dSSteen Hegelund 		} else {
313d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
314d6c2964dSSteen Hegelund 						    VCAP_KF_L3_IP_PROTO,
315d6c2964dSSteen Hegelund 						    st->l4_proto, ~0);
316d6c2964dSSteen Hegelund 			if (err)
317d6c2964dSSteen Hegelund 				goto out;
318d6c2964dSSteen Hegelund 		}
319d6c2964dSSteen Hegelund 	}
320d6c2964dSSteen Hegelund 
321d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC);
322d6c2964dSSteen Hegelund 
323d6c2964dSSteen Hegelund 	return err;
324d6c2964dSSteen Hegelund 
325d6c2964dSSteen Hegelund out:
326d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error");
327d6c2964dSSteen Hegelund 	return err;
328d6c2964dSSteen Hegelund }
329d6c2964dSSteen Hegelund 
330d6c2964dSSteen Hegelund static int
331d6c2964dSSteen Hegelund sparx5_tc_flower_handler_vlan_usage(struct sparx5_tc_flower_parse_usage *st)
332d6c2964dSSteen Hegelund {
333d6c2964dSSteen Hegelund 	enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS;
334d6c2964dSSteen Hegelund 	enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS;
335d6c2964dSSteen Hegelund 	struct flow_match_vlan mt;
336d6c2964dSSteen Hegelund 	int err;
337d6c2964dSSteen Hegelund 
338d6c2964dSSteen Hegelund 	flow_rule_match_vlan(st->frule, &mt);
339d6c2964dSSteen Hegelund 
340d6c2964dSSteen Hegelund 	if (mt.mask->vlan_id) {
341d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, vid_key,
342d6c2964dSSteen Hegelund 					    mt.key->vlan_id,
343d6c2964dSSteen Hegelund 					    mt.mask->vlan_id);
344d6c2964dSSteen Hegelund 		if (err)
345d6c2964dSSteen Hegelund 			goto out;
346d6c2964dSSteen Hegelund 	}
347d6c2964dSSteen Hegelund 
348d6c2964dSSteen Hegelund 	if (mt.mask->vlan_priority) {
349d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, pcp_key,
350d6c2964dSSteen Hegelund 					    mt.key->vlan_priority,
351d6c2964dSSteen Hegelund 					    mt.mask->vlan_priority);
352d6c2964dSSteen Hegelund 		if (err)
353d6c2964dSSteen Hegelund 			goto out;
354d6c2964dSSteen Hegelund 	}
355d6c2964dSSteen Hegelund 
356d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN);
357d6c2964dSSteen Hegelund 
3584e9a6139SDan Carpenter 	return 0;
359d6c2964dSSteen Hegelund out:
360d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "vlan parse error");
361d6c2964dSSteen Hegelund 	return err;
362d6c2964dSSteen Hegelund }
363d6c2964dSSteen Hegelund 
364d6c2964dSSteen Hegelund static int
365d6c2964dSSteen Hegelund sparx5_tc_flower_handler_tcp_usage(struct sparx5_tc_flower_parse_usage *st)
366d6c2964dSSteen Hegelund {
367d6c2964dSSteen Hegelund 	struct flow_match_tcp mt;
368d6c2964dSSteen Hegelund 	u16 tcp_flags_mask;
369d6c2964dSSteen Hegelund 	u16 tcp_flags_key;
370d6c2964dSSteen Hegelund 	enum vcap_bit val;
371d6c2964dSSteen Hegelund 	int err = 0;
372d6c2964dSSteen Hegelund 
373d6c2964dSSteen Hegelund 	flow_rule_match_tcp(st->frule, &mt);
374d6c2964dSSteen Hegelund 	tcp_flags_key = be16_to_cpu(mt.key->flags);
375d6c2964dSSteen Hegelund 	tcp_flags_mask = be16_to_cpu(mt.mask->flags);
376d6c2964dSSteen Hegelund 
377d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_FIN) {
378d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
379d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_FIN)
380d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
381d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_FIN, val);
382d6c2964dSSteen Hegelund 		if (err)
383d6c2964dSSteen Hegelund 			goto out;
384d6c2964dSSteen Hegelund 	}
385d6c2964dSSteen Hegelund 
386d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_SYN) {
387d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
388d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_SYN)
389d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
390d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_SYN, val);
391d6c2964dSSteen Hegelund 		if (err)
392d6c2964dSSteen Hegelund 			goto out;
393d6c2964dSSteen Hegelund 	}
394d6c2964dSSteen Hegelund 
395d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_RST) {
396d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
397d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_RST)
398d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
399d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_RST, val);
400d6c2964dSSteen Hegelund 		if (err)
401d6c2964dSSteen Hegelund 			goto out;
402d6c2964dSSteen Hegelund 	}
403d6c2964dSSteen Hegelund 
404d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_PSH) {
405d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
406d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_PSH)
407d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
408d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_PSH, val);
409d6c2964dSSteen Hegelund 		if (err)
410d6c2964dSSteen Hegelund 			goto out;
411d6c2964dSSteen Hegelund 	}
412d6c2964dSSteen Hegelund 
413d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_ACK) {
414d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
415d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_ACK)
416d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
417d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_ACK, val);
418d6c2964dSSteen Hegelund 		if (err)
419d6c2964dSSteen Hegelund 			goto out;
420d6c2964dSSteen Hegelund 	}
421d6c2964dSSteen Hegelund 
422d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_URG) {
423d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
424d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_URG)
425d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
426d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_URG, val);
427d6c2964dSSteen Hegelund 		if (err)
428d6c2964dSSteen Hegelund 			goto out;
429d6c2964dSSteen Hegelund 	}
430d6c2964dSSteen Hegelund 
431d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_TCP);
432d6c2964dSSteen Hegelund 
433d6c2964dSSteen Hegelund 	return err;
434d6c2964dSSteen Hegelund 
435d6c2964dSSteen Hegelund out:
436d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "tcp_flags parse error");
437d6c2964dSSteen Hegelund 	return err;
438d6c2964dSSteen Hegelund }
439d6c2964dSSteen Hegelund 
440d6c2964dSSteen Hegelund static int
4413a344f99SSteen Hegelund sparx5_tc_flower_handler_arp_usage(struct sparx5_tc_flower_parse_usage *st)
4423a344f99SSteen Hegelund {
4433a344f99SSteen Hegelund 	struct flow_match_arp mt;
4443a344f99SSteen Hegelund 	u16 value, mask;
4453a344f99SSteen Hegelund 	u32 ipval, ipmsk;
4463a344f99SSteen Hegelund 	int err;
4473a344f99SSteen Hegelund 
4483a344f99SSteen Hegelund 	flow_rule_match_arp(st->frule, &mt);
4493a344f99SSteen Hegelund 
4503a344f99SSteen Hegelund 	if (mt.mask->op) {
4513a344f99SSteen Hegelund 		mask = 0x3;
4523a344f99SSteen Hegelund 		if (st->l3_proto == ETH_P_ARP) {
4533a344f99SSteen Hegelund 			value = mt.key->op == TC_ARP_OP_REQUEST ?
4543a344f99SSteen Hegelund 					SPX5_IS2_ARP_REQUEST :
4553a344f99SSteen Hegelund 					SPX5_IS2_ARP_REPLY;
4563a344f99SSteen Hegelund 		} else { /* RARP */
4573a344f99SSteen Hegelund 			value = mt.key->op == TC_ARP_OP_REQUEST ?
4583a344f99SSteen Hegelund 					SPX5_IS2_RARP_REQUEST :
4593a344f99SSteen Hegelund 					SPX5_IS2_RARP_REPLY;
4603a344f99SSteen Hegelund 		}
4613a344f99SSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ARP_OPCODE,
4623a344f99SSteen Hegelund 					    value, mask);
4633a344f99SSteen Hegelund 		if (err)
4643a344f99SSteen Hegelund 			goto out;
4653a344f99SSteen Hegelund 	}
4663a344f99SSteen Hegelund 
4673a344f99SSteen Hegelund 	/* The IS2 ARP keyset does not support ARP hardware addresses */
4683a344f99SSteen Hegelund 	if (!is_zero_ether_addr(mt.mask->sha) ||
4694e9a6139SDan Carpenter 	    !is_zero_ether_addr(mt.mask->tha)) {
4704e9a6139SDan Carpenter 		err = -EINVAL;
4713a344f99SSteen Hegelund 		goto out;
4724e9a6139SDan Carpenter 	}
4733a344f99SSteen Hegelund 
4743a344f99SSteen Hegelund 	if (mt.mask->sip) {
4753a344f99SSteen Hegelund 		ipval = be32_to_cpu((__force __be32)mt.key->sip);
4763a344f99SSteen Hegelund 		ipmsk = be32_to_cpu((__force __be32)mt.mask->sip);
4773a344f99SSteen Hegelund 
4783a344f99SSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_SIP,
4793a344f99SSteen Hegelund 					    ipval, ipmsk);
4803a344f99SSteen Hegelund 		if (err)
4813a344f99SSteen Hegelund 			goto out;
4823a344f99SSteen Hegelund 	}
4833a344f99SSteen Hegelund 
4843a344f99SSteen Hegelund 	if (mt.mask->tip) {
4853a344f99SSteen Hegelund 		ipval = be32_to_cpu((__force __be32)mt.key->tip);
4863a344f99SSteen Hegelund 		ipmsk = be32_to_cpu((__force __be32)mt.mask->tip);
4873a344f99SSteen Hegelund 
4883a344f99SSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_DIP,
4893a344f99SSteen Hegelund 					    ipval, ipmsk);
4903a344f99SSteen Hegelund 		if (err)
4913a344f99SSteen Hegelund 			goto out;
4923a344f99SSteen Hegelund 	}
4933a344f99SSteen Hegelund 
4943a344f99SSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ARP);
4953a344f99SSteen Hegelund 
4964e9a6139SDan Carpenter 	return 0;
4973a344f99SSteen Hegelund 
4983a344f99SSteen Hegelund out:
4993a344f99SSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "arp parse error");
5003a344f99SSteen Hegelund 	return err;
5013a344f99SSteen Hegelund }
5023a344f99SSteen Hegelund 
5033a344f99SSteen Hegelund static int
504d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ip_usage(struct sparx5_tc_flower_parse_usage *st)
505d6c2964dSSteen Hegelund {
506d6c2964dSSteen Hegelund 	struct flow_match_ip mt;
507d6c2964dSSteen Hegelund 	int err = 0;
508d6c2964dSSteen Hegelund 
509d6c2964dSSteen Hegelund 	flow_rule_match_ip(st->frule, &mt);
510d6c2964dSSteen Hegelund 
511d6c2964dSSteen Hegelund 	if (mt.mask->tos) {
512d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_TOS,
513d6c2964dSSteen Hegelund 					    mt.key->tos,
514d6c2964dSSteen Hegelund 					    mt.mask->tos);
515d6c2964dSSteen Hegelund 		if (err)
516d6c2964dSSteen Hegelund 			goto out;
517d6c2964dSSteen Hegelund 	}
518d6c2964dSSteen Hegelund 
519d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IP);
520d6c2964dSSteen Hegelund 
521d6c2964dSSteen Hegelund 	return err;
522d6c2964dSSteen Hegelund 
523d6c2964dSSteen Hegelund out:
524d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_tos parse error");
525d6c2964dSSteen Hegelund 	return err;
526d6c2964dSSteen Hegelund }
527d6c2964dSSteen Hegelund 
528c9da1ac1SSteen Hegelund static int (*sparx5_tc_flower_usage_handlers[])(struct sparx5_tc_flower_parse_usage *st) = {
529c9da1ac1SSteen Hegelund 	[FLOW_DISSECTOR_KEY_ETH_ADDRS] = sparx5_tc_flower_handler_ethaddr_usage,
530d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_IPV4_ADDRS] = sparx5_tc_flower_handler_ipv4_usage,
531d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_IPV6_ADDRS] = sparx5_tc_flower_handler_ipv6_usage,
532d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_CONTROL] = sparx5_tc_flower_handler_control_usage,
533d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_PORTS] = sparx5_tc_flower_handler_portnum_usage,
534d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage,
535d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage,
536d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_TCP] = sparx5_tc_flower_handler_tcp_usage,
5373a344f99SSteen Hegelund 	[FLOW_DISSECTOR_KEY_ARP] = sparx5_tc_flower_handler_arp_usage,
538d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_IP] = sparx5_tc_flower_handler_ip_usage,
539c9da1ac1SSteen Hegelund };
540c9da1ac1SSteen Hegelund 
541c9da1ac1SSteen Hegelund static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco,
542c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin,
543abc4010dSSteen Hegelund 				    struct vcap_rule *vrule,
544abc4010dSSteen Hegelund 				    u16 *l3_proto)
545c9da1ac1SSteen Hegelund {
546c9da1ac1SSteen Hegelund 	struct sparx5_tc_flower_parse_usage state = {
547c9da1ac1SSteen Hegelund 		.fco = fco,
548c9da1ac1SSteen Hegelund 		.vrule = vrule,
549abc4010dSSteen Hegelund 		.l3_proto = ETH_P_ALL,
550c9da1ac1SSteen Hegelund 	};
551c9da1ac1SSteen Hegelund 	int idx, err = 0;
552c9da1ac1SSteen Hegelund 
553c9da1ac1SSteen Hegelund 	state.frule = flow_cls_offload_flow_rule(fco);
554c9da1ac1SSteen Hegelund 	for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) {
555c9da1ac1SSteen Hegelund 		if (!flow_rule_match_key(state.frule, idx))
556c9da1ac1SSteen Hegelund 			continue;
557c9da1ac1SSteen Hegelund 		if (!sparx5_tc_flower_usage_handlers[idx])
558c9da1ac1SSteen Hegelund 			continue;
559c9da1ac1SSteen Hegelund 		err = sparx5_tc_flower_usage_handlers[idx](&state);
560c9da1ac1SSteen Hegelund 		if (err)
561c9da1ac1SSteen Hegelund 			return err;
562c9da1ac1SSteen Hegelund 	}
563abc4010dSSteen Hegelund 
564abc4010dSSteen Hegelund 	if (state.frule->match.dissector->used_keys ^ state.used_keys) {
565abc4010dSSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
566abc4010dSSteen Hegelund 				   "Unsupported match item");
567abc4010dSSteen Hegelund 		return -ENOENT;
568abc4010dSSteen Hegelund 	}
569abc4010dSSteen Hegelund 
570abc4010dSSteen Hegelund 	if (l3_proto)
571abc4010dSSteen Hegelund 		*l3_proto = state.l3_proto;
572c9da1ac1SSteen Hegelund 	return err;
573c9da1ac1SSteen Hegelund }
574c9da1ac1SSteen Hegelund 
575392d0ab0SSteen Hegelund static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
576*784c3067SSteen Hegelund 					 struct net_device *ndev,
577*784c3067SSteen Hegelund 					 struct flow_cls_offload *fco)
578392d0ab0SSteen Hegelund {
579392d0ab0SSteen Hegelund 	struct flow_rule *rule = flow_cls_offload_flow_rule(fco);
580392d0ab0SSteen Hegelund 	struct flow_action_entry *actent, *last_actent = NULL;
581392d0ab0SSteen Hegelund 	struct flow_action *act = &rule->action;
582392d0ab0SSteen Hegelund 	u64 action_mask = 0;
583392d0ab0SSteen Hegelund 	int idx;
584392d0ab0SSteen Hegelund 
585392d0ab0SSteen Hegelund 	if (!flow_action_has_entries(act)) {
586392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
587392d0ab0SSteen Hegelund 		return -EINVAL;
588392d0ab0SSteen Hegelund 	}
589392d0ab0SSteen Hegelund 
590392d0ab0SSteen Hegelund 	if (!flow_action_basic_hw_stats_check(act, fco->common.extack))
591392d0ab0SSteen Hegelund 		return -EOPNOTSUPP;
592392d0ab0SSteen Hegelund 
593392d0ab0SSteen Hegelund 	flow_action_for_each(idx, actent, act) {
594392d0ab0SSteen Hegelund 		if (action_mask & BIT(actent->id)) {
595392d0ab0SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
596392d0ab0SSteen Hegelund 					   "More actions of the same type");
597392d0ab0SSteen Hegelund 			return -EINVAL;
598392d0ab0SSteen Hegelund 		}
599392d0ab0SSteen Hegelund 		action_mask |= BIT(actent->id);
600392d0ab0SSteen Hegelund 		last_actent = actent; /* Save last action for later check */
601392d0ab0SSteen Hegelund 	}
602392d0ab0SSteen Hegelund 
603*784c3067SSteen Hegelund 	/* Check if last action is a goto
604*784c3067SSteen Hegelund 	 * The last chain/lookup does not need to have a goto action
605*784c3067SSteen Hegelund 	 */
606*784c3067SSteen Hegelund 	if (last_actent->id == FLOW_ACTION_GOTO) {
607*784c3067SSteen Hegelund 		/* Check if the destination chain is in one of the VCAPs */
608392d0ab0SSteen Hegelund 		if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
609392d0ab0SSteen Hegelund 					 last_actent->chain_index)) {
610392d0ab0SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
611392d0ab0SSteen Hegelund 					   "Invalid goto chain");
612392d0ab0SSteen Hegelund 			return -EINVAL;
613392d0ab0SSteen Hegelund 		}
614*784c3067SSteen Hegelund 	} else if (!vcap_is_last_chain(vctrl, fco->common.chain_index)) {
615*784c3067SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
616*784c3067SSteen Hegelund 				   "Last action must be 'goto'");
617*784c3067SSteen Hegelund 		return -EINVAL;
618*784c3067SSteen Hegelund 	}
619392d0ab0SSteen Hegelund 
620392d0ab0SSteen Hegelund 	/* Catch unsupported combinations of actions */
621392d0ab0SSteen Hegelund 	if (action_mask & BIT(FLOW_ACTION_TRAP) &&
622392d0ab0SSteen Hegelund 	    action_mask & BIT(FLOW_ACTION_ACCEPT)) {
623392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
624392d0ab0SSteen Hegelund 				   "Cannot combine pass and trap action");
625392d0ab0SSteen Hegelund 		return -EOPNOTSUPP;
626392d0ab0SSteen Hegelund 	}
627392d0ab0SSteen Hegelund 
628392d0ab0SSteen Hegelund 	return 0;
629392d0ab0SSteen Hegelund }
630392d0ab0SSteen Hegelund 
63140e7fe18SSteen Hegelund /* Add a rule counter action - only IS2 is considered for now */
63240e7fe18SSteen Hegelund static int sparx5_tc_add_rule_counter(struct vcap_admin *admin,
63340e7fe18SSteen Hegelund 				      struct vcap_rule *vrule)
63440e7fe18SSteen Hegelund {
63540e7fe18SSteen Hegelund 	int err;
63640e7fe18SSteen Hegelund 
6370ca60948SSteen Hegelund 	err = vcap_rule_mod_action_u32(vrule, VCAP_AF_CNT_ID, vrule->id);
63840e7fe18SSteen Hegelund 	if (err)
63940e7fe18SSteen Hegelund 		return err;
64040e7fe18SSteen Hegelund 
64140e7fe18SSteen Hegelund 	vcap_rule_set_counter_id(vrule, vrule->id);
64240e7fe18SSteen Hegelund 	return err;
64340e7fe18SSteen Hegelund }
64440e7fe18SSteen Hegelund 
6450ca60948SSteen Hegelund /* Collect all port keysets and apply the first of them, possibly wildcarded */
6460ca60948SSteen Hegelund static int sparx5_tc_select_protocol_keyset(struct net_device *ndev,
6470ca60948SSteen Hegelund 					    struct vcap_rule *vrule,
6480ca60948SSteen Hegelund 					    struct vcap_admin *admin,
6490ca60948SSteen Hegelund 					    u16 l3_proto,
6500ca60948SSteen Hegelund 					    struct sparx5_multiple_rules *multi)
6510ca60948SSteen Hegelund {
6520ca60948SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
6530ca60948SSteen Hegelund 	struct vcap_keyset_list portkeysetlist = {};
6540ca60948SSteen Hegelund 	enum vcap_keyfield_set portkeysets[10] = {};
6550ca60948SSteen Hegelund 	struct vcap_keyset_list matches = {};
6560ca60948SSteen Hegelund 	enum vcap_keyfield_set keysets[10];
6570ca60948SSteen Hegelund 	int idx, jdx, err = 0, count = 0;
6580ca60948SSteen Hegelund 	struct sparx5_wildcard_rule *mru;
6590ca60948SSteen Hegelund 	const struct vcap_set *kinfo;
6600ca60948SSteen Hegelund 	struct vcap_control *vctrl;
6610ca60948SSteen Hegelund 
6620ca60948SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
6630ca60948SSteen Hegelund 
6640ca60948SSteen Hegelund 	/* Find the keysets that the rule can use */
6650ca60948SSteen Hegelund 	matches.keysets = keysets;
6660ca60948SSteen Hegelund 	matches.max = ARRAY_SIZE(keysets);
6670ca60948SSteen Hegelund 	if (vcap_rule_find_keysets(vrule, &matches) == 0)
6680ca60948SSteen Hegelund 		return -EINVAL;
6690ca60948SSteen Hegelund 
6700ca60948SSteen Hegelund 	/* Find the keysets that the port configuration supports */
6710ca60948SSteen Hegelund 	portkeysetlist.max = ARRAY_SIZE(portkeysets);
6720ca60948SSteen Hegelund 	portkeysetlist.keysets = portkeysets;
6730ca60948SSteen Hegelund 	err = sparx5_vcap_get_port_keyset(ndev,
6740ca60948SSteen Hegelund 					  admin, vrule->vcap_chain_id,
6750ca60948SSteen Hegelund 					  l3_proto,
6760ca60948SSteen Hegelund 					  &portkeysetlist);
6770ca60948SSteen Hegelund 	if (err)
6780ca60948SSteen Hegelund 		return err;
6790ca60948SSteen Hegelund 
6800ca60948SSteen Hegelund 	/* Find the intersection of the two sets of keyset */
6810ca60948SSteen Hegelund 	for (idx = 0; idx < portkeysetlist.cnt; ++idx) {
6820ca60948SSteen Hegelund 		kinfo = vcap_keyfieldset(vctrl, admin->vtype,
6830ca60948SSteen Hegelund 					 portkeysetlist.keysets[idx]);
6840ca60948SSteen Hegelund 		if (!kinfo)
6850ca60948SSteen Hegelund 			continue;
6860ca60948SSteen Hegelund 
6870ca60948SSteen Hegelund 		/* Find a port keyset that matches the required keys
6880ca60948SSteen Hegelund 		 * If there are multiple keysets then compose a type id mask
6890ca60948SSteen Hegelund 		 */
6900ca60948SSteen Hegelund 		for (jdx = 0; jdx < matches.cnt; ++jdx) {
6910ca60948SSteen Hegelund 			if (portkeysetlist.keysets[idx] != matches.keysets[jdx])
6920ca60948SSteen Hegelund 				continue;
6930ca60948SSteen Hegelund 
6940ca60948SSteen Hegelund 			mru = &multi->rule[kinfo->sw_per_item];
6950ca60948SSteen Hegelund 			if (!mru->selected) {
6960ca60948SSteen Hegelund 				mru->selected = true;
6970ca60948SSteen Hegelund 				mru->keyset = portkeysetlist.keysets[idx];
6980ca60948SSteen Hegelund 				mru->value = kinfo->type_id;
6990ca60948SSteen Hegelund 			}
7000ca60948SSteen Hegelund 			mru->value &= kinfo->type_id;
7010ca60948SSteen Hegelund 			mru->mask |= kinfo->type_id;
7020ca60948SSteen Hegelund 			++count;
7030ca60948SSteen Hegelund 		}
7040ca60948SSteen Hegelund 	}
7050ca60948SSteen Hegelund 	if (count == 0)
7060ca60948SSteen Hegelund 		return -EPROTO;
7070ca60948SSteen Hegelund 
7080ca60948SSteen Hegelund 	if (l3_proto == ETH_P_ALL && count < portkeysetlist.cnt)
7090ca60948SSteen Hegelund 		return -ENOENT;
7100ca60948SSteen Hegelund 
7110ca60948SSteen Hegelund 	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
7120ca60948SSteen Hegelund 		mru = &multi->rule[idx];
7130ca60948SSteen Hegelund 		if (!mru->selected)
7140ca60948SSteen Hegelund 			continue;
7150ca60948SSteen Hegelund 
7160ca60948SSteen Hegelund 		/* Align the mask to the combined value */
7170ca60948SSteen Hegelund 		mru->mask ^= mru->value;
7180ca60948SSteen Hegelund 	}
7190ca60948SSteen Hegelund 
7200ca60948SSteen Hegelund 	/* Set the chosen keyset on the rule and set a wildcarded type if there
7210ca60948SSteen Hegelund 	 * are more than one keyset
7220ca60948SSteen Hegelund 	 */
7230ca60948SSteen Hegelund 	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
7240ca60948SSteen Hegelund 		mru = &multi->rule[idx];
7250ca60948SSteen Hegelund 		if (!mru->selected)
7260ca60948SSteen Hegelund 			continue;
7270ca60948SSteen Hegelund 
7280ca60948SSteen Hegelund 		vcap_set_rule_set_keyset(vrule, mru->keyset);
7290ca60948SSteen Hegelund 		if (count > 1)
7300ca60948SSteen Hegelund 			/* Some keysets do not have a type field */
7310ca60948SSteen Hegelund 			vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE,
7320ca60948SSteen Hegelund 					      mru->value,
7330ca60948SSteen Hegelund 					      ~mru->mask);
7340ca60948SSteen Hegelund 		mru->selected = false; /* mark as done */
7350ca60948SSteen Hegelund 		break; /* Stop here and add more rules later */
7360ca60948SSteen Hegelund 	}
7370ca60948SSteen Hegelund 	return err;
7380ca60948SSteen Hegelund }
7390ca60948SSteen Hegelund 
7400ca60948SSteen Hegelund static int sparx5_tc_add_rule_copy(struct vcap_control *vctrl,
7410ca60948SSteen Hegelund 				   struct flow_cls_offload *fco,
7420ca60948SSteen Hegelund 				   struct vcap_rule *erule,
7430ca60948SSteen Hegelund 				   struct vcap_admin *admin,
7440ca60948SSteen Hegelund 				   struct sparx5_wildcard_rule *rule)
7450ca60948SSteen Hegelund {
7460ca60948SSteen Hegelund 	enum vcap_key_field keylist[] = {
7470ca60948SSteen Hegelund 		VCAP_KF_IF_IGR_PORT_MASK,
7480ca60948SSteen Hegelund 		VCAP_KF_IF_IGR_PORT_MASK_SEL,
7490ca60948SSteen Hegelund 		VCAP_KF_IF_IGR_PORT_MASK_RNG,
7500ca60948SSteen Hegelund 		VCAP_KF_LOOKUP_FIRST_IS,
7510ca60948SSteen Hegelund 		VCAP_KF_TYPE,
7520ca60948SSteen Hegelund 	};
7530ca60948SSteen Hegelund 	struct vcap_rule *vrule;
7540ca60948SSteen Hegelund 	int err;
7550ca60948SSteen Hegelund 
7560ca60948SSteen Hegelund 	/* Add an extra rule with a special user and the new keyset */
7570ca60948SSteen Hegelund 	erule->user = VCAP_USER_TC_EXTRA;
7580ca60948SSteen Hegelund 	vrule = vcap_copy_rule(erule);
7590ca60948SSteen Hegelund 	if (IS_ERR(vrule))
7600ca60948SSteen Hegelund 		return PTR_ERR(vrule);
7610ca60948SSteen Hegelund 
7620ca60948SSteen Hegelund 	/* Link the new rule to the existing rule with the cookie */
7630ca60948SSteen Hegelund 	vrule->cookie = erule->cookie;
7640ca60948SSteen Hegelund 	vcap_filter_rule_keys(vrule, keylist, ARRAY_SIZE(keylist), true);
7650ca60948SSteen Hegelund 	err = vcap_set_rule_set_keyset(vrule, rule->keyset);
7660ca60948SSteen Hegelund 	if (err) {
7670ca60948SSteen Hegelund 		pr_err("%s:%d: could not set keyset %s in rule: %u\n",
7680ca60948SSteen Hegelund 		       __func__, __LINE__,
7690ca60948SSteen Hegelund 		       vcap_keyset_name(vctrl, rule->keyset),
7700ca60948SSteen Hegelund 		       vrule->id);
7710ca60948SSteen Hegelund 		goto out;
7720ca60948SSteen Hegelund 	}
7730ca60948SSteen Hegelund 
7740ca60948SSteen Hegelund 	/* Some keysets do not have a type field, so ignore return value */
7750ca60948SSteen Hegelund 	vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE, rule->value, ~rule->mask);
7760ca60948SSteen Hegelund 
7770ca60948SSteen Hegelund 	err = vcap_set_rule_set_actionset(vrule, erule->actionset);
7780ca60948SSteen Hegelund 	if (err)
7790ca60948SSteen Hegelund 		goto out;
7800ca60948SSteen Hegelund 
7810ca60948SSteen Hegelund 	err = sparx5_tc_add_rule_counter(admin, vrule);
7820ca60948SSteen Hegelund 	if (err)
7830ca60948SSteen Hegelund 		goto out;
7840ca60948SSteen Hegelund 
7850ca60948SSteen Hegelund 	err = vcap_val_rule(vrule, ETH_P_ALL);
7860ca60948SSteen Hegelund 	if (err) {
7870ca60948SSteen Hegelund 		pr_err("%s:%d: could not validate rule: %u\n",
7880ca60948SSteen Hegelund 		       __func__, __LINE__, vrule->id);
7890ca60948SSteen Hegelund 		vcap_set_tc_exterr(fco, vrule);
7900ca60948SSteen Hegelund 		goto out;
7910ca60948SSteen Hegelund 	}
7920ca60948SSteen Hegelund 	err = vcap_add_rule(vrule);
7930ca60948SSteen Hegelund 	if (err) {
7940ca60948SSteen Hegelund 		pr_err("%s:%d: could not add rule: %u\n",
7950ca60948SSteen Hegelund 		       __func__, __LINE__, vrule->id);
7960ca60948SSteen Hegelund 		goto out;
7970ca60948SSteen Hegelund 	}
7980ca60948SSteen Hegelund out:
7990ca60948SSteen Hegelund 	vcap_free_rule(vrule);
8000ca60948SSteen Hegelund 	return err;
8010ca60948SSteen Hegelund }
8020ca60948SSteen Hegelund 
8030ca60948SSteen Hegelund static int sparx5_tc_add_remaining_rules(struct vcap_control *vctrl,
8040ca60948SSteen Hegelund 					 struct flow_cls_offload *fco,
8050ca60948SSteen Hegelund 					 struct vcap_rule *erule,
8060ca60948SSteen Hegelund 					 struct vcap_admin *admin,
8070ca60948SSteen Hegelund 					 struct sparx5_multiple_rules *multi)
8080ca60948SSteen Hegelund {
8090ca60948SSteen Hegelund 	int idx, err = 0;
8100ca60948SSteen Hegelund 
8110ca60948SSteen Hegelund 	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
8120ca60948SSteen Hegelund 		if (!multi->rule[idx].selected)
8130ca60948SSteen Hegelund 			continue;
8140ca60948SSteen Hegelund 
8150ca60948SSteen Hegelund 		err = sparx5_tc_add_rule_copy(vctrl, fco, erule, admin,
8160ca60948SSteen Hegelund 					      &multi->rule[idx]);
8170ca60948SSteen Hegelund 		if (err)
8180ca60948SSteen Hegelund 			break;
8190ca60948SSteen Hegelund 	}
8200ca60948SSteen Hegelund 	return err;
8210ca60948SSteen Hegelund }
8220ca60948SSteen Hegelund 
823c9da1ac1SSteen Hegelund static int sparx5_tc_flower_replace(struct net_device *ndev,
824c9da1ac1SSteen Hegelund 				    struct flow_cls_offload *fco,
825c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin)
826c9da1ac1SSteen Hegelund {
827c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
8280ca60948SSteen Hegelund 	struct sparx5_multiple_rules multi = {};
829c9da1ac1SSteen Hegelund 	struct flow_action_entry *act;
830c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
831c9da1ac1SSteen Hegelund 	struct flow_rule *frule;
832c9da1ac1SSteen Hegelund 	struct vcap_rule *vrule;
833abc4010dSSteen Hegelund 	u16 l3_proto;
834c9da1ac1SSteen Hegelund 	int err, idx;
835c9da1ac1SSteen Hegelund 
836c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
837392d0ab0SSteen Hegelund 
838*784c3067SSteen Hegelund 	err = sparx5_tc_flower_action_check(vctrl, ndev, fco);
839392d0ab0SSteen Hegelund 	if (err)
840392d0ab0SSteen Hegelund 		return err;
841392d0ab0SSteen Hegelund 
842c9da1ac1SSteen Hegelund 	vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC,
843c9da1ac1SSteen Hegelund 				fco->common.prio, 0);
844c9da1ac1SSteen Hegelund 	if (IS_ERR(vrule))
845c9da1ac1SSteen Hegelund 		return PTR_ERR(vrule);
846c9da1ac1SSteen Hegelund 
847c9da1ac1SSteen Hegelund 	vrule->cookie = fco->cookie;
848bcddc196SSteen Hegelund 
849bcddc196SSteen Hegelund 	l3_proto = ETH_P_ALL;
850bcddc196SSteen Hegelund 	err = sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto);
851bcddc196SSteen Hegelund 	if (err)
852bcddc196SSteen Hegelund 		goto out;
85340e7fe18SSteen Hegelund 
85440e7fe18SSteen Hegelund 	err = sparx5_tc_add_rule_counter(admin, vrule);
85540e7fe18SSteen Hegelund 	if (err)
85640e7fe18SSteen Hegelund 		goto out;
85740e7fe18SSteen Hegelund 
858392d0ab0SSteen Hegelund 	frule = flow_cls_offload_flow_rule(fco);
859c9da1ac1SSteen Hegelund 	flow_action_for_each(idx, act, &frule->action) {
860c9da1ac1SSteen Hegelund 		switch (act->id) {
861c9da1ac1SSteen Hegelund 		case FLOW_ACTION_TRAP:
862c9da1ac1SSteen Hegelund 			err = vcap_rule_add_action_bit(vrule,
863c9da1ac1SSteen Hegelund 						       VCAP_AF_CPU_COPY_ENA,
864c9da1ac1SSteen Hegelund 						       VCAP_BIT_1);
865c9da1ac1SSteen Hegelund 			if (err)
866c9da1ac1SSteen Hegelund 				goto out;
867c9da1ac1SSteen Hegelund 			err = vcap_rule_add_action_u32(vrule,
868c9da1ac1SSteen Hegelund 						       VCAP_AF_CPU_QUEUE_NUM, 0);
869c9da1ac1SSteen Hegelund 			if (err)
870c9da1ac1SSteen Hegelund 				goto out;
871c9da1ac1SSteen Hegelund 			err = vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,
872c9da1ac1SSteen Hegelund 						       SPX5_PMM_REPLACE_ALL);
873c9da1ac1SSteen Hegelund 			if (err)
874c9da1ac1SSteen Hegelund 				goto out;
875c9da1ac1SSteen Hegelund 			/* For now the actionset is hardcoded */
876c9da1ac1SSteen Hegelund 			err = vcap_set_rule_set_actionset(vrule,
877c9da1ac1SSteen Hegelund 							  VCAP_AFS_BASE_TYPE);
878c9da1ac1SSteen Hegelund 			if (err)
879c9da1ac1SSteen Hegelund 				goto out;
880c9da1ac1SSteen Hegelund 			break;
881c9da1ac1SSteen Hegelund 		case FLOW_ACTION_ACCEPT:
882c9da1ac1SSteen Hegelund 			/* For now the actionset is hardcoded */
883c9da1ac1SSteen Hegelund 			err = vcap_set_rule_set_actionset(vrule,
884c9da1ac1SSteen Hegelund 							  VCAP_AFS_BASE_TYPE);
885c9da1ac1SSteen Hegelund 			if (err)
886c9da1ac1SSteen Hegelund 				goto out;
887c9da1ac1SSteen Hegelund 			break;
888392d0ab0SSteen Hegelund 		case FLOW_ACTION_GOTO:
889392d0ab0SSteen Hegelund 			/* Links between VCAPs will be added later */
890392d0ab0SSteen Hegelund 			break;
891c9da1ac1SSteen Hegelund 		default:
892c9da1ac1SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
893c9da1ac1SSteen Hegelund 					   "Unsupported TC action");
894c9da1ac1SSteen Hegelund 			err = -EOPNOTSUPP;
895c9da1ac1SSteen Hegelund 			goto out;
896c9da1ac1SSteen Hegelund 		}
897c9da1ac1SSteen Hegelund 	}
8980ca60948SSteen Hegelund 
8990ca60948SSteen Hegelund 	err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin, l3_proto,
9000ca60948SSteen Hegelund 					       &multi);
9010ca60948SSteen Hegelund 	if (err) {
9020ca60948SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
9030ca60948SSteen Hegelund 				   "No matching port keyset for filter protocol and keys");
9040ca60948SSteen Hegelund 		goto out;
9050ca60948SSteen Hegelund 	}
9060ca60948SSteen Hegelund 
907abc4010dSSteen Hegelund 	/* provide the l3 protocol to guide the keyset selection */
908abc4010dSSteen Hegelund 	err = vcap_val_rule(vrule, l3_proto);
909c9da1ac1SSteen Hegelund 	if (err) {
910c9da1ac1SSteen Hegelund 		vcap_set_tc_exterr(fco, vrule);
911c9da1ac1SSteen Hegelund 		goto out;
912c9da1ac1SSteen Hegelund 	}
913c9da1ac1SSteen Hegelund 	err = vcap_add_rule(vrule);
914c9da1ac1SSteen Hegelund 	if (err)
915c9da1ac1SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
916c9da1ac1SSteen Hegelund 				   "Could not add the filter");
9170ca60948SSteen Hegelund 
9180ca60948SSteen Hegelund 	if (l3_proto == ETH_P_ALL)
9190ca60948SSteen Hegelund 		err = sparx5_tc_add_remaining_rules(vctrl, fco, vrule, admin,
9200ca60948SSteen Hegelund 						    &multi);
9210ca60948SSteen Hegelund 
922c9da1ac1SSteen Hegelund out:
923c9da1ac1SSteen Hegelund 	vcap_free_rule(vrule);
924c9da1ac1SSteen Hegelund 	return err;
925c9da1ac1SSteen Hegelund }
926c9da1ac1SSteen Hegelund 
927c9da1ac1SSteen Hegelund static int sparx5_tc_flower_destroy(struct net_device *ndev,
928c9da1ac1SSteen Hegelund 				    struct flow_cls_offload *fco,
929c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin)
930c9da1ac1SSteen Hegelund {
931c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
932c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
933c9da1ac1SSteen Hegelund 	int err = -ENOENT, rule_id;
934c9da1ac1SSteen Hegelund 
935c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
936c9da1ac1SSteen Hegelund 	while (true) {
937c9da1ac1SSteen Hegelund 		rule_id = vcap_lookup_rule_by_cookie(vctrl, fco->cookie);
938c9da1ac1SSteen Hegelund 		if (rule_id <= 0)
939c9da1ac1SSteen Hegelund 			break;
940c9da1ac1SSteen Hegelund 		err = vcap_del_rule(vctrl, ndev, rule_id);
941c9da1ac1SSteen Hegelund 		if (err) {
942c9da1ac1SSteen Hegelund 			pr_err("%s:%d: could not delete rule %d\n",
943c9da1ac1SSteen Hegelund 			       __func__, __LINE__, rule_id);
944c9da1ac1SSteen Hegelund 			break;
945c9da1ac1SSteen Hegelund 		}
946c9da1ac1SSteen Hegelund 	}
947c9da1ac1SSteen Hegelund 	return err;
948c9da1ac1SSteen Hegelund }
949c9da1ac1SSteen Hegelund 
95040e7fe18SSteen Hegelund /* Collect packet counts from all rules with the same cookie */
95140e7fe18SSteen Hegelund static int sparx5_tc_rule_counter_cb(void *arg, struct vcap_rule *rule)
95240e7fe18SSteen Hegelund {
95340e7fe18SSteen Hegelund 	struct sparx5_tc_rule_pkt_cnt *rinfo = arg;
95440e7fe18SSteen Hegelund 	struct vcap_counter counter;
95540e7fe18SSteen Hegelund 	int err = 0;
95640e7fe18SSteen Hegelund 
95740e7fe18SSteen Hegelund 	if (rule->cookie == rinfo->cookie) {
95840e7fe18SSteen Hegelund 		err = vcap_rule_get_counter(rule, &counter);
95940e7fe18SSteen Hegelund 		if (err)
96040e7fe18SSteen Hegelund 			return err;
96140e7fe18SSteen Hegelund 		rinfo->pkts += counter.value;
96240e7fe18SSteen Hegelund 		/* Reset the rule counter */
96340e7fe18SSteen Hegelund 		counter.value = 0;
96440e7fe18SSteen Hegelund 		vcap_rule_set_counter(rule, &counter);
96540e7fe18SSteen Hegelund 	}
96640e7fe18SSteen Hegelund 	return err;
96740e7fe18SSteen Hegelund }
96840e7fe18SSteen Hegelund 
96940e7fe18SSteen Hegelund static int sparx5_tc_flower_stats(struct net_device *ndev,
97040e7fe18SSteen Hegelund 				  struct flow_cls_offload *fco,
97140e7fe18SSteen Hegelund 				  struct vcap_admin *admin)
97240e7fe18SSteen Hegelund {
97340e7fe18SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
97440e7fe18SSteen Hegelund 	struct sparx5_tc_rule_pkt_cnt rinfo = {};
97540e7fe18SSteen Hegelund 	struct vcap_control *vctrl;
97640e7fe18SSteen Hegelund 	ulong lastused = 0;
97740e7fe18SSteen Hegelund 	u64 drops = 0;
97840e7fe18SSteen Hegelund 	u32 pkts = 0;
97940e7fe18SSteen Hegelund 	int err;
98040e7fe18SSteen Hegelund 
98140e7fe18SSteen Hegelund 	rinfo.cookie = fco->cookie;
98240e7fe18SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
98340e7fe18SSteen Hegelund 	err = vcap_rule_iter(vctrl, sparx5_tc_rule_counter_cb, &rinfo);
98440e7fe18SSteen Hegelund 	if (err)
98540e7fe18SSteen Hegelund 		return err;
98640e7fe18SSteen Hegelund 	pkts = rinfo.pkts;
98740e7fe18SSteen Hegelund 	flow_stats_update(&fco->stats, 0x0, pkts, drops, lastused,
98840e7fe18SSteen Hegelund 			  FLOW_ACTION_HW_STATS_IMMEDIATE);
98940e7fe18SSteen Hegelund 	return err;
99040e7fe18SSteen Hegelund }
99140e7fe18SSteen Hegelund 
992c9da1ac1SSteen Hegelund int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco,
993c9da1ac1SSteen Hegelund 		     bool ingress)
994c9da1ac1SSteen Hegelund {
995c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
996c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
997c9da1ac1SSteen Hegelund 	struct vcap_admin *admin;
998c9da1ac1SSteen Hegelund 	int err = -EINVAL;
999c9da1ac1SSteen Hegelund 
1000c9da1ac1SSteen Hegelund 	/* Get vcap instance from the chain id */
1001c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
1002c9da1ac1SSteen Hegelund 	admin = vcap_find_admin(vctrl, fco->common.chain_index);
1003c9da1ac1SSteen Hegelund 	if (!admin) {
1004c9da1ac1SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack, "Invalid chain");
1005c9da1ac1SSteen Hegelund 		return err;
1006c9da1ac1SSteen Hegelund 	}
1007c9da1ac1SSteen Hegelund 
1008c9da1ac1SSteen Hegelund 	switch (fco->command) {
1009c9da1ac1SSteen Hegelund 	case FLOW_CLS_REPLACE:
1010c9da1ac1SSteen Hegelund 		return sparx5_tc_flower_replace(ndev, fco, admin);
1011c9da1ac1SSteen Hegelund 	case FLOW_CLS_DESTROY:
1012c9da1ac1SSteen Hegelund 		return sparx5_tc_flower_destroy(ndev, fco, admin);
101340e7fe18SSteen Hegelund 	case FLOW_CLS_STATS:
101440e7fe18SSteen Hegelund 		return sparx5_tc_flower_stats(ndev, fco, admin);
1015c9da1ac1SSteen Hegelund 	default:
1016c9da1ac1SSteen Hegelund 		return -EOPNOTSUPP;
1017c9da1ac1SSteen Hegelund 	}
1018c9da1ac1SSteen Hegelund }
1019