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 
38d6c2964dSSteen Hegelund /* These protocols have dedicated keysets in IS2 and a TC dissector
39d6c2964dSSteen Hegelund  * ETH_P_ARP does not have a TC dissector
40d6c2964dSSteen Hegelund  */
41d6c2964dSSteen Hegelund static u16 sparx5_tc_known_etypes[] = {
42d6c2964dSSteen Hegelund 	ETH_P_ALL,
433a344f99SSteen Hegelund 	ETH_P_ARP,
44d6c2964dSSteen Hegelund 	ETH_P_IP,
45d6c2964dSSteen Hegelund 	ETH_P_IPV6,
46d6c2964dSSteen Hegelund };
47d6c2964dSSteen Hegelund 
483a344f99SSteen Hegelund enum sparx5_is2_arp_opcode {
493a344f99SSteen Hegelund 	SPX5_IS2_ARP_REQUEST,
503a344f99SSteen Hegelund 	SPX5_IS2_ARP_REPLY,
513a344f99SSteen Hegelund 	SPX5_IS2_RARP_REQUEST,
523a344f99SSteen Hegelund 	SPX5_IS2_RARP_REPLY,
533a344f99SSteen Hegelund };
543a344f99SSteen Hegelund 
553a344f99SSteen Hegelund enum tc_arp_opcode {
563a344f99SSteen Hegelund 	TC_ARP_OP_RESERVED,
573a344f99SSteen Hegelund 	TC_ARP_OP_REQUEST,
583a344f99SSteen Hegelund 	TC_ARP_OP_REPLY,
593a344f99SSteen Hegelund };
603a344f99SSteen Hegelund 
61d6c2964dSSteen Hegelund static bool sparx5_tc_is_known_etype(u16 etype)
62d6c2964dSSteen Hegelund {
63d6c2964dSSteen Hegelund 	int idx;
64d6c2964dSSteen Hegelund 
65d6c2964dSSteen Hegelund 	/* For now this only knows about IS2 traffic classification */
66d6c2964dSSteen Hegelund 	for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_known_etypes); ++idx)
67d6c2964dSSteen Hegelund 		if (sparx5_tc_known_etypes[idx] == etype)
68d6c2964dSSteen Hegelund 			return true;
69d6c2964dSSteen Hegelund 
70d6c2964dSSteen Hegelund 	return false;
71d6c2964dSSteen Hegelund }
72d6c2964dSSteen Hegelund 
73c9da1ac1SSteen Hegelund static int sparx5_tc_flower_handler_ethaddr_usage(struct sparx5_tc_flower_parse_usage *st)
74c9da1ac1SSteen Hegelund {
75c9da1ac1SSteen Hegelund 	enum vcap_key_field smac_key = VCAP_KF_L2_SMAC;
76c9da1ac1SSteen Hegelund 	enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC;
77c9da1ac1SSteen Hegelund 	struct flow_match_eth_addrs match;
78c9da1ac1SSteen Hegelund 	struct vcap_u48_key smac, dmac;
79c9da1ac1SSteen Hegelund 	int err = 0;
80c9da1ac1SSteen Hegelund 
81c9da1ac1SSteen Hegelund 	flow_rule_match_eth_addrs(st->frule, &match);
82c9da1ac1SSteen Hegelund 
83c9da1ac1SSteen Hegelund 	if (!is_zero_ether_addr(match.mask->src)) {
84c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN);
85c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN);
86c9da1ac1SSteen Hegelund 		err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac);
87c9da1ac1SSteen Hegelund 		if (err)
88c9da1ac1SSteen Hegelund 			goto out;
89c9da1ac1SSteen Hegelund 	}
90c9da1ac1SSteen Hegelund 
91c9da1ac1SSteen Hegelund 	if (!is_zero_ether_addr(match.mask->dst)) {
92c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN);
93c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN);
94c9da1ac1SSteen Hegelund 		err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac);
95c9da1ac1SSteen Hegelund 		if (err)
96c9da1ac1SSteen Hegelund 			goto out;
97c9da1ac1SSteen Hegelund 	}
98c9da1ac1SSteen Hegelund 
99c9da1ac1SSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS);
100c9da1ac1SSteen Hegelund 
101c9da1ac1SSteen Hegelund 	return err;
102c9da1ac1SSteen Hegelund 
103c9da1ac1SSteen Hegelund out:
104c9da1ac1SSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "eth_addr parse error");
105c9da1ac1SSteen Hegelund 	return err;
106c9da1ac1SSteen Hegelund }
107c9da1ac1SSteen Hegelund 
108d6c2964dSSteen Hegelund static int
109d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ipv4_usage(struct sparx5_tc_flower_parse_usage *st)
110d6c2964dSSteen Hegelund {
111d6c2964dSSteen Hegelund 	int err = 0;
112d6c2964dSSteen Hegelund 
113d6c2964dSSteen Hegelund 	if (st->l3_proto == ETH_P_IP) {
114d6c2964dSSteen Hegelund 		struct flow_match_ipv4_addrs mt;
115d6c2964dSSteen Hegelund 
116d6c2964dSSteen Hegelund 		flow_rule_match_ipv4_addrs(st->frule, &mt);
117d6c2964dSSteen Hegelund 		if (mt.mask->src) {
118d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
119d6c2964dSSteen Hegelund 						    VCAP_KF_L3_IP4_SIP,
120d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.key->src),
121d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.mask->src));
122d6c2964dSSteen Hegelund 			if (err)
123d6c2964dSSteen Hegelund 				goto out;
124d6c2964dSSteen Hegelund 		}
125d6c2964dSSteen Hegelund 		if (mt.mask->dst) {
126d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
127d6c2964dSSteen Hegelund 						    VCAP_KF_L3_IP4_DIP,
128d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.key->dst),
129d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.mask->dst));
130d6c2964dSSteen Hegelund 			if (err)
131d6c2964dSSteen Hegelund 				goto out;
132d6c2964dSSteen Hegelund 		}
133d6c2964dSSteen Hegelund 	}
134d6c2964dSSteen Hegelund 
135d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS);
136d6c2964dSSteen Hegelund 
137d6c2964dSSteen Hegelund 	return err;
138d6c2964dSSteen Hegelund 
139d6c2964dSSteen Hegelund out:
140d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv4_addr parse error");
141d6c2964dSSteen Hegelund 	return err;
142d6c2964dSSteen Hegelund }
143d6c2964dSSteen Hegelund 
144d6c2964dSSteen Hegelund static int
145d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ipv6_usage(struct sparx5_tc_flower_parse_usage *st)
146d6c2964dSSteen Hegelund {
147d6c2964dSSteen Hegelund 	int err = 0;
148d6c2964dSSteen Hegelund 
149d6c2964dSSteen Hegelund 	if (st->l3_proto == ETH_P_IPV6) {
150d6c2964dSSteen Hegelund 		struct flow_match_ipv6_addrs mt;
151d6c2964dSSteen Hegelund 		struct vcap_u128_key sip;
152d6c2964dSSteen Hegelund 		struct vcap_u128_key dip;
153d6c2964dSSteen Hegelund 
154d6c2964dSSteen Hegelund 		flow_rule_match_ipv6_addrs(st->frule, &mt);
155d6c2964dSSteen Hegelund 		/* Check if address masks are non-zero */
156d6c2964dSSteen Hegelund 		if (!ipv6_addr_any(&mt.mask->src)) {
157d6c2964dSSteen Hegelund 			vcap_netbytes_copy(sip.value, mt.key->src.s6_addr, 16);
158d6c2964dSSteen Hegelund 			vcap_netbytes_copy(sip.mask, mt.mask->src.s6_addr, 16);
159d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u128(st->vrule,
160d6c2964dSSteen Hegelund 						     VCAP_KF_L3_IP6_SIP, &sip);
161d6c2964dSSteen Hegelund 			if (err)
162d6c2964dSSteen Hegelund 				goto out;
163d6c2964dSSteen Hegelund 		}
164d6c2964dSSteen Hegelund 		if (!ipv6_addr_any(&mt.mask->dst)) {
165d6c2964dSSteen Hegelund 			vcap_netbytes_copy(dip.value, mt.key->dst.s6_addr, 16);
166d6c2964dSSteen Hegelund 			vcap_netbytes_copy(dip.mask, mt.mask->dst.s6_addr, 16);
167d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u128(st->vrule,
168d6c2964dSSteen Hegelund 						     VCAP_KF_L3_IP6_DIP, &dip);
169d6c2964dSSteen Hegelund 			if (err)
170d6c2964dSSteen Hegelund 				goto out;
171d6c2964dSSteen Hegelund 		}
172d6c2964dSSteen Hegelund 	}
173d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS);
174d6c2964dSSteen Hegelund 	return err;
175d6c2964dSSteen Hegelund out:
176d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv6_addr parse error");
177d6c2964dSSteen Hegelund 	return err;
178d6c2964dSSteen Hegelund }
179d6c2964dSSteen Hegelund 
180d6c2964dSSteen Hegelund static int
181d6c2964dSSteen Hegelund sparx5_tc_flower_handler_control_usage(struct sparx5_tc_flower_parse_usage *st)
182d6c2964dSSteen Hegelund {
183d6c2964dSSteen Hegelund 	struct flow_match_control mt;
184d6c2964dSSteen Hegelund 	u32 value, mask;
185d6c2964dSSteen Hegelund 	int err = 0;
186d6c2964dSSteen Hegelund 
187d6c2964dSSteen Hegelund 	flow_rule_match_control(st->frule, &mt);
188d6c2964dSSteen Hegelund 
189d6c2964dSSteen Hegelund 	if (mt.mask->flags) {
190d6c2964dSSteen Hegelund 		if (mt.mask->flags & FLOW_DIS_FIRST_FRAG) {
191d6c2964dSSteen Hegelund 			if (mt.key->flags & FLOW_DIS_FIRST_FRAG) {
192d6c2964dSSteen Hegelund 				value = 1; /* initial fragment */
193d6c2964dSSteen Hegelund 				mask = 0x3;
194d6c2964dSSteen Hegelund 			} else {
195d6c2964dSSteen Hegelund 				if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) {
196d6c2964dSSteen Hegelund 					value = 3; /* follow up fragment */
197d6c2964dSSteen Hegelund 					mask = 0x3;
198d6c2964dSSteen Hegelund 				} else {
199d6c2964dSSteen Hegelund 					value = 0; /* no fragment */
200d6c2964dSSteen Hegelund 					mask = 0x3;
201d6c2964dSSteen Hegelund 				}
202d6c2964dSSteen Hegelund 			}
203d6c2964dSSteen Hegelund 		} else {
204d6c2964dSSteen Hegelund 			if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) {
205d6c2964dSSteen Hegelund 				value = 3; /* follow up fragment */
206d6c2964dSSteen Hegelund 				mask = 0x3;
207d6c2964dSSteen Hegelund 			} else {
208d6c2964dSSteen Hegelund 				value = 0; /* no fragment */
209d6c2964dSSteen Hegelund 				mask = 0x3;
210d6c2964dSSteen Hegelund 			}
211d6c2964dSSteen Hegelund 		}
212d6c2964dSSteen Hegelund 
213d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule,
214d6c2964dSSteen Hegelund 					    VCAP_KF_L3_FRAGMENT_TYPE,
215d6c2964dSSteen Hegelund 					    value, mask);
216d6c2964dSSteen Hegelund 		if (err)
217d6c2964dSSteen Hegelund 			goto out;
218d6c2964dSSteen Hegelund 	}
219d6c2964dSSteen Hegelund 
220d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL);
221d6c2964dSSteen Hegelund 
222d6c2964dSSteen Hegelund 	return err;
223d6c2964dSSteen Hegelund 
224d6c2964dSSteen Hegelund out:
225d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error");
226d6c2964dSSteen Hegelund 	return err;
227d6c2964dSSteen Hegelund }
228d6c2964dSSteen Hegelund 
229d6c2964dSSteen Hegelund static int
230d6c2964dSSteen Hegelund sparx5_tc_flower_handler_portnum_usage(struct sparx5_tc_flower_parse_usage *st)
231d6c2964dSSteen Hegelund {
232d6c2964dSSteen Hegelund 	struct flow_match_ports mt;
233d6c2964dSSteen Hegelund 	u16 value, mask;
234d6c2964dSSteen Hegelund 	int err = 0;
235d6c2964dSSteen Hegelund 
236d6c2964dSSteen Hegelund 	flow_rule_match_ports(st->frule, &mt);
237d6c2964dSSteen Hegelund 
238d6c2964dSSteen Hegelund 	if (mt.mask->src) {
239d6c2964dSSteen Hegelund 		value = be16_to_cpu(mt.key->src);
240d6c2964dSSteen Hegelund 		mask = be16_to_cpu(mt.mask->src);
241d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_SPORT, value,
242d6c2964dSSteen Hegelund 					    mask);
243d6c2964dSSteen Hegelund 		if (err)
244d6c2964dSSteen Hegelund 			goto out;
245d6c2964dSSteen Hegelund 	}
246d6c2964dSSteen Hegelund 
247d6c2964dSSteen Hegelund 	if (mt.mask->dst) {
248d6c2964dSSteen Hegelund 		value = be16_to_cpu(mt.key->dst);
249d6c2964dSSteen Hegelund 		mask = be16_to_cpu(mt.mask->dst);
250d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_DPORT, value,
251d6c2964dSSteen Hegelund 					    mask);
252d6c2964dSSteen Hegelund 		if (err)
253d6c2964dSSteen Hegelund 			goto out;
254d6c2964dSSteen Hegelund 	}
255d6c2964dSSteen Hegelund 
256d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_PORTS);
257d6c2964dSSteen Hegelund 
258d6c2964dSSteen Hegelund 	return err;
259d6c2964dSSteen Hegelund 
260d6c2964dSSteen Hegelund out:
261d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "port parse error");
262d6c2964dSSteen Hegelund 	return err;
263d6c2964dSSteen Hegelund }
264d6c2964dSSteen Hegelund 
265d6c2964dSSteen Hegelund static int
266d6c2964dSSteen Hegelund sparx5_tc_flower_handler_basic_usage(struct sparx5_tc_flower_parse_usage *st)
267d6c2964dSSteen Hegelund {
268d6c2964dSSteen Hegelund 	struct flow_match_basic mt;
269d6c2964dSSteen Hegelund 	int err = 0;
270d6c2964dSSteen Hegelund 
271d6c2964dSSteen Hegelund 	flow_rule_match_basic(st->frule, &mt);
272d6c2964dSSteen Hegelund 
273d6c2964dSSteen Hegelund 	if (mt.mask->n_proto) {
274d6c2964dSSteen Hegelund 		st->l3_proto = be16_to_cpu(mt.key->n_proto);
275d6c2964dSSteen Hegelund 		if (!sparx5_tc_is_known_etype(st->l3_proto)) {
276d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,
277d6c2964dSSteen Hegelund 						    st->l3_proto, ~0);
278d6c2964dSSteen Hegelund 			if (err)
279d6c2964dSSteen Hegelund 				goto out;
280d6c2964dSSteen Hegelund 		} else if (st->l3_proto == ETH_P_IP) {
281d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
282d6c2964dSSteen Hegelund 						    VCAP_BIT_1);
283d6c2964dSSteen Hegelund 			if (err)
284d6c2964dSSteen Hegelund 				goto out;
285d6c2964dSSteen Hegelund 		} else if (st->l3_proto == ETH_P_IPV6) {
286d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
287d6c2964dSSteen Hegelund 						    VCAP_BIT_0);
288d6c2964dSSteen Hegelund 			if (err)
289d6c2964dSSteen Hegelund 				goto out;
290d6c2964dSSteen Hegelund 		}
291d6c2964dSSteen Hegelund 	}
292d6c2964dSSteen Hegelund 
293d6c2964dSSteen Hegelund 	if (mt.mask->ip_proto) {
294d6c2964dSSteen Hegelund 		st->l4_proto = mt.key->ip_proto;
295d6c2964dSSteen Hegelund 		if (st->l4_proto == IPPROTO_TCP) {
296d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule,
297d6c2964dSSteen Hegelund 						    VCAP_KF_TCP_IS,
298d6c2964dSSteen Hegelund 						    VCAP_BIT_1);
299d6c2964dSSteen Hegelund 			if (err)
300d6c2964dSSteen Hegelund 				goto out;
301d6c2964dSSteen Hegelund 		} else if (st->l4_proto == IPPROTO_UDP) {
302d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule,
303d6c2964dSSteen Hegelund 						    VCAP_KF_TCP_IS,
304d6c2964dSSteen Hegelund 						    VCAP_BIT_0);
305d6c2964dSSteen Hegelund 			if (err)
306d6c2964dSSteen Hegelund 				goto out;
307d6c2964dSSteen Hegelund 		} else {
308d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
309d6c2964dSSteen Hegelund 						    VCAP_KF_L3_IP_PROTO,
310d6c2964dSSteen Hegelund 						    st->l4_proto, ~0);
311d6c2964dSSteen Hegelund 			if (err)
312d6c2964dSSteen Hegelund 				goto out;
313d6c2964dSSteen Hegelund 		}
314d6c2964dSSteen Hegelund 	}
315d6c2964dSSteen Hegelund 
316d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC);
317d6c2964dSSteen Hegelund 
318d6c2964dSSteen Hegelund 	return err;
319d6c2964dSSteen Hegelund 
320d6c2964dSSteen Hegelund out:
321d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error");
322d6c2964dSSteen Hegelund 	return err;
323d6c2964dSSteen Hegelund }
324d6c2964dSSteen Hegelund 
325d6c2964dSSteen Hegelund static int
326d6c2964dSSteen Hegelund sparx5_tc_flower_handler_vlan_usage(struct sparx5_tc_flower_parse_usage *st)
327d6c2964dSSteen Hegelund {
328d6c2964dSSteen Hegelund 	enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS;
329d6c2964dSSteen Hegelund 	enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS;
330d6c2964dSSteen Hegelund 	struct flow_match_vlan mt;
331d6c2964dSSteen Hegelund 	int err;
332d6c2964dSSteen Hegelund 
333d6c2964dSSteen Hegelund 	flow_rule_match_vlan(st->frule, &mt);
334d6c2964dSSteen Hegelund 
335d6c2964dSSteen Hegelund 	if (mt.mask->vlan_id) {
336d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, vid_key,
337d6c2964dSSteen Hegelund 					    mt.key->vlan_id,
338d6c2964dSSteen Hegelund 					    mt.mask->vlan_id);
339d6c2964dSSteen Hegelund 		if (err)
340d6c2964dSSteen Hegelund 			goto out;
341d6c2964dSSteen Hegelund 	}
342d6c2964dSSteen Hegelund 
343d6c2964dSSteen Hegelund 	if (mt.mask->vlan_priority) {
344d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, pcp_key,
345d6c2964dSSteen Hegelund 					    mt.key->vlan_priority,
346d6c2964dSSteen Hegelund 					    mt.mask->vlan_priority);
347d6c2964dSSteen Hegelund 		if (err)
348d6c2964dSSteen Hegelund 			goto out;
349d6c2964dSSteen Hegelund 	}
350d6c2964dSSteen Hegelund 
351d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN);
352d6c2964dSSteen Hegelund 
3534e9a6139SDan Carpenter 	return 0;
354d6c2964dSSteen Hegelund out:
355d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "vlan parse error");
356d6c2964dSSteen Hegelund 	return err;
357d6c2964dSSteen Hegelund }
358d6c2964dSSteen Hegelund 
359d6c2964dSSteen Hegelund static int
360d6c2964dSSteen Hegelund sparx5_tc_flower_handler_tcp_usage(struct sparx5_tc_flower_parse_usage *st)
361d6c2964dSSteen Hegelund {
362d6c2964dSSteen Hegelund 	struct flow_match_tcp mt;
363d6c2964dSSteen Hegelund 	u16 tcp_flags_mask;
364d6c2964dSSteen Hegelund 	u16 tcp_flags_key;
365d6c2964dSSteen Hegelund 	enum vcap_bit val;
366d6c2964dSSteen Hegelund 	int err = 0;
367d6c2964dSSteen Hegelund 
368d6c2964dSSteen Hegelund 	flow_rule_match_tcp(st->frule, &mt);
369d6c2964dSSteen Hegelund 	tcp_flags_key = be16_to_cpu(mt.key->flags);
370d6c2964dSSteen Hegelund 	tcp_flags_mask = be16_to_cpu(mt.mask->flags);
371d6c2964dSSteen Hegelund 
372d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_FIN) {
373d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
374d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_FIN)
375d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
376d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_FIN, val);
377d6c2964dSSteen Hegelund 		if (err)
378d6c2964dSSteen Hegelund 			goto out;
379d6c2964dSSteen Hegelund 	}
380d6c2964dSSteen Hegelund 
381d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_SYN) {
382d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
383d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_SYN)
384d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
385d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_SYN, val);
386d6c2964dSSteen Hegelund 		if (err)
387d6c2964dSSteen Hegelund 			goto out;
388d6c2964dSSteen Hegelund 	}
389d6c2964dSSteen Hegelund 
390d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_RST) {
391d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
392d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_RST)
393d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
394d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_RST, val);
395d6c2964dSSteen Hegelund 		if (err)
396d6c2964dSSteen Hegelund 			goto out;
397d6c2964dSSteen Hegelund 	}
398d6c2964dSSteen Hegelund 
399d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_PSH) {
400d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
401d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_PSH)
402d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
403d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_PSH, val);
404d6c2964dSSteen Hegelund 		if (err)
405d6c2964dSSteen Hegelund 			goto out;
406d6c2964dSSteen Hegelund 	}
407d6c2964dSSteen Hegelund 
408d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_ACK) {
409d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
410d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_ACK)
411d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
412d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_ACK, val);
413d6c2964dSSteen Hegelund 		if (err)
414d6c2964dSSteen Hegelund 			goto out;
415d6c2964dSSteen Hegelund 	}
416d6c2964dSSteen Hegelund 
417d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_URG) {
418d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
419d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_URG)
420d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
421d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_URG, val);
422d6c2964dSSteen Hegelund 		if (err)
423d6c2964dSSteen Hegelund 			goto out;
424d6c2964dSSteen Hegelund 	}
425d6c2964dSSteen Hegelund 
426d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_TCP);
427d6c2964dSSteen Hegelund 
428d6c2964dSSteen Hegelund 	return err;
429d6c2964dSSteen Hegelund 
430d6c2964dSSteen Hegelund out:
431d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "tcp_flags parse error");
432d6c2964dSSteen Hegelund 	return err;
433d6c2964dSSteen Hegelund }
434d6c2964dSSteen Hegelund 
435d6c2964dSSteen Hegelund static int
4363a344f99SSteen Hegelund sparx5_tc_flower_handler_arp_usage(struct sparx5_tc_flower_parse_usage *st)
4373a344f99SSteen Hegelund {
4383a344f99SSteen Hegelund 	struct flow_match_arp mt;
4393a344f99SSteen Hegelund 	u16 value, mask;
4403a344f99SSteen Hegelund 	u32 ipval, ipmsk;
4413a344f99SSteen Hegelund 	int err;
4423a344f99SSteen Hegelund 
4433a344f99SSteen Hegelund 	flow_rule_match_arp(st->frule, &mt);
4443a344f99SSteen Hegelund 
4453a344f99SSteen Hegelund 	if (mt.mask->op) {
4463a344f99SSteen Hegelund 		mask = 0x3;
4473a344f99SSteen Hegelund 		if (st->l3_proto == ETH_P_ARP) {
4483a344f99SSteen Hegelund 			value = mt.key->op == TC_ARP_OP_REQUEST ?
4493a344f99SSteen Hegelund 					SPX5_IS2_ARP_REQUEST :
4503a344f99SSteen Hegelund 					SPX5_IS2_ARP_REPLY;
4513a344f99SSteen Hegelund 		} else { /* RARP */
4523a344f99SSteen Hegelund 			value = mt.key->op == TC_ARP_OP_REQUEST ?
4533a344f99SSteen Hegelund 					SPX5_IS2_RARP_REQUEST :
4543a344f99SSteen Hegelund 					SPX5_IS2_RARP_REPLY;
4553a344f99SSteen Hegelund 		}
4563a344f99SSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ARP_OPCODE,
4573a344f99SSteen Hegelund 					    value, mask);
4583a344f99SSteen Hegelund 		if (err)
4593a344f99SSteen Hegelund 			goto out;
4603a344f99SSteen Hegelund 	}
4613a344f99SSteen Hegelund 
4623a344f99SSteen Hegelund 	/* The IS2 ARP keyset does not support ARP hardware addresses */
4633a344f99SSteen Hegelund 	if (!is_zero_ether_addr(mt.mask->sha) ||
4644e9a6139SDan Carpenter 	    !is_zero_ether_addr(mt.mask->tha)) {
4654e9a6139SDan Carpenter 		err = -EINVAL;
4663a344f99SSteen Hegelund 		goto out;
4674e9a6139SDan Carpenter 	}
4683a344f99SSteen Hegelund 
4693a344f99SSteen Hegelund 	if (mt.mask->sip) {
4703a344f99SSteen Hegelund 		ipval = be32_to_cpu((__force __be32)mt.key->sip);
4713a344f99SSteen Hegelund 		ipmsk = be32_to_cpu((__force __be32)mt.mask->sip);
4723a344f99SSteen Hegelund 
4733a344f99SSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_SIP,
4743a344f99SSteen Hegelund 					    ipval, ipmsk);
4753a344f99SSteen Hegelund 		if (err)
4763a344f99SSteen Hegelund 			goto out;
4773a344f99SSteen Hegelund 	}
4783a344f99SSteen Hegelund 
4793a344f99SSteen Hegelund 	if (mt.mask->tip) {
4803a344f99SSteen Hegelund 		ipval = be32_to_cpu((__force __be32)mt.key->tip);
4813a344f99SSteen Hegelund 		ipmsk = be32_to_cpu((__force __be32)mt.mask->tip);
4823a344f99SSteen Hegelund 
4833a344f99SSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_DIP,
4843a344f99SSteen Hegelund 					    ipval, ipmsk);
4853a344f99SSteen Hegelund 		if (err)
4863a344f99SSteen Hegelund 			goto out;
4873a344f99SSteen Hegelund 	}
4883a344f99SSteen Hegelund 
4893a344f99SSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ARP);
4903a344f99SSteen Hegelund 
4914e9a6139SDan Carpenter 	return 0;
4923a344f99SSteen Hegelund 
4933a344f99SSteen Hegelund out:
4943a344f99SSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "arp parse error");
4953a344f99SSteen Hegelund 	return err;
4963a344f99SSteen Hegelund }
4973a344f99SSteen Hegelund 
4983a344f99SSteen Hegelund static int
499d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ip_usage(struct sparx5_tc_flower_parse_usage *st)
500d6c2964dSSteen Hegelund {
501d6c2964dSSteen Hegelund 	struct flow_match_ip mt;
502d6c2964dSSteen Hegelund 	int err = 0;
503d6c2964dSSteen Hegelund 
504d6c2964dSSteen Hegelund 	flow_rule_match_ip(st->frule, &mt);
505d6c2964dSSteen Hegelund 
506d6c2964dSSteen Hegelund 	if (mt.mask->tos) {
507d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_TOS,
508d6c2964dSSteen Hegelund 					    mt.key->tos,
509d6c2964dSSteen Hegelund 					    mt.mask->tos);
510d6c2964dSSteen Hegelund 		if (err)
511d6c2964dSSteen Hegelund 			goto out;
512d6c2964dSSteen Hegelund 	}
513d6c2964dSSteen Hegelund 
514d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IP);
515d6c2964dSSteen Hegelund 
516d6c2964dSSteen Hegelund 	return err;
517d6c2964dSSteen Hegelund 
518d6c2964dSSteen Hegelund out:
519d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_tos parse error");
520d6c2964dSSteen Hegelund 	return err;
521d6c2964dSSteen Hegelund }
522d6c2964dSSteen Hegelund 
523c9da1ac1SSteen Hegelund static int (*sparx5_tc_flower_usage_handlers[])(struct sparx5_tc_flower_parse_usage *st) = {
524c9da1ac1SSteen Hegelund 	[FLOW_DISSECTOR_KEY_ETH_ADDRS] = sparx5_tc_flower_handler_ethaddr_usage,
525d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_IPV4_ADDRS] = sparx5_tc_flower_handler_ipv4_usage,
526d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_IPV6_ADDRS] = sparx5_tc_flower_handler_ipv6_usage,
527d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_CONTROL] = sparx5_tc_flower_handler_control_usage,
528d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_PORTS] = sparx5_tc_flower_handler_portnum_usage,
529d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage,
530d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage,
531d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_TCP] = sparx5_tc_flower_handler_tcp_usage,
5323a344f99SSteen Hegelund 	[FLOW_DISSECTOR_KEY_ARP] = sparx5_tc_flower_handler_arp_usage,
533d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_IP] = sparx5_tc_flower_handler_ip_usage,
534c9da1ac1SSteen Hegelund };
535c9da1ac1SSteen Hegelund 
536c9da1ac1SSteen Hegelund static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco,
537c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin,
538abc4010dSSteen Hegelund 				    struct vcap_rule *vrule,
539abc4010dSSteen Hegelund 				    u16 *l3_proto)
540c9da1ac1SSteen Hegelund {
541c9da1ac1SSteen Hegelund 	struct sparx5_tc_flower_parse_usage state = {
542c9da1ac1SSteen Hegelund 		.fco = fco,
543c9da1ac1SSteen Hegelund 		.vrule = vrule,
544abc4010dSSteen Hegelund 		.l3_proto = ETH_P_ALL,
545c9da1ac1SSteen Hegelund 	};
546c9da1ac1SSteen Hegelund 	int idx, err = 0;
547c9da1ac1SSteen Hegelund 
548c9da1ac1SSteen Hegelund 	state.frule = flow_cls_offload_flow_rule(fco);
549c9da1ac1SSteen Hegelund 	for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) {
550c9da1ac1SSteen Hegelund 		if (!flow_rule_match_key(state.frule, idx))
551c9da1ac1SSteen Hegelund 			continue;
552c9da1ac1SSteen Hegelund 		if (!sparx5_tc_flower_usage_handlers[idx])
553c9da1ac1SSteen Hegelund 			continue;
554c9da1ac1SSteen Hegelund 		err = sparx5_tc_flower_usage_handlers[idx](&state);
555c9da1ac1SSteen Hegelund 		if (err)
556c9da1ac1SSteen Hegelund 			return err;
557c9da1ac1SSteen Hegelund 	}
558abc4010dSSteen Hegelund 
559abc4010dSSteen Hegelund 	if (state.frule->match.dissector->used_keys ^ state.used_keys) {
560abc4010dSSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
561abc4010dSSteen Hegelund 				   "Unsupported match item");
562abc4010dSSteen Hegelund 		return -ENOENT;
563abc4010dSSteen Hegelund 	}
564abc4010dSSteen Hegelund 
565abc4010dSSteen Hegelund 	if (l3_proto)
566abc4010dSSteen Hegelund 		*l3_proto = state.l3_proto;
567c9da1ac1SSteen Hegelund 	return err;
568c9da1ac1SSteen Hegelund }
569c9da1ac1SSteen Hegelund 
570392d0ab0SSteen Hegelund static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
571784c3067SSteen Hegelund 					 struct net_device *ndev,
572784c3067SSteen Hegelund 					 struct flow_cls_offload *fco)
573392d0ab0SSteen Hegelund {
574392d0ab0SSteen Hegelund 	struct flow_rule *rule = flow_cls_offload_flow_rule(fco);
575392d0ab0SSteen Hegelund 	struct flow_action_entry *actent, *last_actent = NULL;
576392d0ab0SSteen Hegelund 	struct flow_action *act = &rule->action;
577392d0ab0SSteen Hegelund 	u64 action_mask = 0;
578392d0ab0SSteen Hegelund 	int idx;
579392d0ab0SSteen Hegelund 
580392d0ab0SSteen Hegelund 	if (!flow_action_has_entries(act)) {
581392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
582392d0ab0SSteen Hegelund 		return -EINVAL;
583392d0ab0SSteen Hegelund 	}
584392d0ab0SSteen Hegelund 
585392d0ab0SSteen Hegelund 	if (!flow_action_basic_hw_stats_check(act, fco->common.extack))
586392d0ab0SSteen Hegelund 		return -EOPNOTSUPP;
587392d0ab0SSteen Hegelund 
588392d0ab0SSteen Hegelund 	flow_action_for_each(idx, actent, act) {
589392d0ab0SSteen Hegelund 		if (action_mask & BIT(actent->id)) {
590392d0ab0SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
591392d0ab0SSteen Hegelund 					   "More actions of the same type");
592392d0ab0SSteen Hegelund 			return -EINVAL;
593392d0ab0SSteen Hegelund 		}
594392d0ab0SSteen Hegelund 		action_mask |= BIT(actent->id);
595392d0ab0SSteen Hegelund 		last_actent = actent; /* Save last action for later check */
596392d0ab0SSteen Hegelund 	}
597392d0ab0SSteen Hegelund 
598784c3067SSteen Hegelund 	/* Check if last action is a goto
599784c3067SSteen Hegelund 	 * The last chain/lookup does not need to have a goto action
600784c3067SSteen Hegelund 	 */
601784c3067SSteen Hegelund 	if (last_actent->id == FLOW_ACTION_GOTO) {
602784c3067SSteen Hegelund 		/* Check if the destination chain is in one of the VCAPs */
603392d0ab0SSteen Hegelund 		if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
604392d0ab0SSteen Hegelund 					 last_actent->chain_index)) {
605392d0ab0SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
606392d0ab0SSteen Hegelund 					   "Invalid goto chain");
607392d0ab0SSteen Hegelund 			return -EINVAL;
608392d0ab0SSteen Hegelund 		}
609784c3067SSteen Hegelund 	} else if (!vcap_is_last_chain(vctrl, fco->common.chain_index)) {
610784c3067SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
611784c3067SSteen Hegelund 				   "Last action must be 'goto'");
612784c3067SSteen Hegelund 		return -EINVAL;
613784c3067SSteen Hegelund 	}
614392d0ab0SSteen Hegelund 
615392d0ab0SSteen Hegelund 	/* Catch unsupported combinations of actions */
616392d0ab0SSteen Hegelund 	if (action_mask & BIT(FLOW_ACTION_TRAP) &&
617392d0ab0SSteen Hegelund 	    action_mask & BIT(FLOW_ACTION_ACCEPT)) {
618392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
619392d0ab0SSteen Hegelund 				   "Cannot combine pass and trap action");
620392d0ab0SSteen Hegelund 		return -EOPNOTSUPP;
621392d0ab0SSteen Hegelund 	}
622392d0ab0SSteen Hegelund 
623392d0ab0SSteen Hegelund 	return 0;
624392d0ab0SSteen Hegelund }
625392d0ab0SSteen Hegelund 
62640e7fe18SSteen Hegelund /* Add a rule counter action - only IS2 is considered for now */
62740e7fe18SSteen Hegelund static int sparx5_tc_add_rule_counter(struct vcap_admin *admin,
62840e7fe18SSteen Hegelund 				      struct vcap_rule *vrule)
62940e7fe18SSteen Hegelund {
63040e7fe18SSteen Hegelund 	int err;
63140e7fe18SSteen Hegelund 
6320ca60948SSteen Hegelund 	err = vcap_rule_mod_action_u32(vrule, VCAP_AF_CNT_ID, vrule->id);
63340e7fe18SSteen Hegelund 	if (err)
63440e7fe18SSteen Hegelund 		return err;
63540e7fe18SSteen Hegelund 
63640e7fe18SSteen Hegelund 	vcap_rule_set_counter_id(vrule, vrule->id);
63740e7fe18SSteen Hegelund 	return err;
63840e7fe18SSteen Hegelund }
63940e7fe18SSteen Hegelund 
6400ca60948SSteen Hegelund /* Collect all port keysets and apply the first of them, possibly wildcarded */
6410ca60948SSteen Hegelund static int sparx5_tc_select_protocol_keyset(struct net_device *ndev,
6420ca60948SSteen Hegelund 					    struct vcap_rule *vrule,
6430ca60948SSteen Hegelund 					    struct vcap_admin *admin,
6440ca60948SSteen Hegelund 					    u16 l3_proto,
6450ca60948SSteen Hegelund 					    struct sparx5_multiple_rules *multi)
6460ca60948SSteen Hegelund {
6470ca60948SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
6480ca60948SSteen Hegelund 	struct vcap_keyset_list portkeysetlist = {};
6490ca60948SSteen Hegelund 	enum vcap_keyfield_set portkeysets[10] = {};
6500ca60948SSteen Hegelund 	struct vcap_keyset_list matches = {};
6510ca60948SSteen Hegelund 	enum vcap_keyfield_set keysets[10];
6520ca60948SSteen Hegelund 	int idx, jdx, err = 0, count = 0;
6530ca60948SSteen Hegelund 	struct sparx5_wildcard_rule *mru;
6540ca60948SSteen Hegelund 	const struct vcap_set *kinfo;
6550ca60948SSteen Hegelund 	struct vcap_control *vctrl;
6560ca60948SSteen Hegelund 
6570ca60948SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
6580ca60948SSteen Hegelund 
6590ca60948SSteen Hegelund 	/* Find the keysets that the rule can use */
6600ca60948SSteen Hegelund 	matches.keysets = keysets;
6610ca60948SSteen Hegelund 	matches.max = ARRAY_SIZE(keysets);
6620ca60948SSteen Hegelund 	if (vcap_rule_find_keysets(vrule, &matches) == 0)
6630ca60948SSteen Hegelund 		return -EINVAL;
6640ca60948SSteen Hegelund 
6650ca60948SSteen Hegelund 	/* Find the keysets that the port configuration supports */
6660ca60948SSteen Hegelund 	portkeysetlist.max = ARRAY_SIZE(portkeysets);
6670ca60948SSteen Hegelund 	portkeysetlist.keysets = portkeysets;
6680ca60948SSteen Hegelund 	err = sparx5_vcap_get_port_keyset(ndev,
6690ca60948SSteen Hegelund 					  admin, vrule->vcap_chain_id,
6700ca60948SSteen Hegelund 					  l3_proto,
6710ca60948SSteen Hegelund 					  &portkeysetlist);
6720ca60948SSteen Hegelund 	if (err)
6730ca60948SSteen Hegelund 		return err;
6740ca60948SSteen Hegelund 
6750ca60948SSteen Hegelund 	/* Find the intersection of the two sets of keyset */
6760ca60948SSteen Hegelund 	for (idx = 0; idx < portkeysetlist.cnt; ++idx) {
6770ca60948SSteen Hegelund 		kinfo = vcap_keyfieldset(vctrl, admin->vtype,
6780ca60948SSteen Hegelund 					 portkeysetlist.keysets[idx]);
6790ca60948SSteen Hegelund 		if (!kinfo)
6800ca60948SSteen Hegelund 			continue;
6810ca60948SSteen Hegelund 
6820ca60948SSteen Hegelund 		/* Find a port keyset that matches the required keys
6830ca60948SSteen Hegelund 		 * If there are multiple keysets then compose a type id mask
6840ca60948SSteen Hegelund 		 */
6850ca60948SSteen Hegelund 		for (jdx = 0; jdx < matches.cnt; ++jdx) {
6860ca60948SSteen Hegelund 			if (portkeysetlist.keysets[idx] != matches.keysets[jdx])
6870ca60948SSteen Hegelund 				continue;
6880ca60948SSteen Hegelund 
6890ca60948SSteen Hegelund 			mru = &multi->rule[kinfo->sw_per_item];
6900ca60948SSteen Hegelund 			if (!mru->selected) {
6910ca60948SSteen Hegelund 				mru->selected = true;
6920ca60948SSteen Hegelund 				mru->keyset = portkeysetlist.keysets[idx];
6930ca60948SSteen Hegelund 				mru->value = kinfo->type_id;
6940ca60948SSteen Hegelund 			}
6950ca60948SSteen Hegelund 			mru->value &= kinfo->type_id;
6960ca60948SSteen Hegelund 			mru->mask |= kinfo->type_id;
6970ca60948SSteen Hegelund 			++count;
6980ca60948SSteen Hegelund 		}
6990ca60948SSteen Hegelund 	}
7000ca60948SSteen Hegelund 	if (count == 0)
7010ca60948SSteen Hegelund 		return -EPROTO;
7020ca60948SSteen Hegelund 
7030ca60948SSteen Hegelund 	if (l3_proto == ETH_P_ALL && count < portkeysetlist.cnt)
7040ca60948SSteen Hegelund 		return -ENOENT;
7050ca60948SSteen Hegelund 
7060ca60948SSteen Hegelund 	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
7070ca60948SSteen Hegelund 		mru = &multi->rule[idx];
7080ca60948SSteen Hegelund 		if (!mru->selected)
7090ca60948SSteen Hegelund 			continue;
7100ca60948SSteen Hegelund 
7110ca60948SSteen Hegelund 		/* Align the mask to the combined value */
7120ca60948SSteen Hegelund 		mru->mask ^= mru->value;
7130ca60948SSteen Hegelund 	}
7140ca60948SSteen Hegelund 
7150ca60948SSteen Hegelund 	/* Set the chosen keyset on the rule and set a wildcarded type if there
7160ca60948SSteen Hegelund 	 * are more than one keyset
7170ca60948SSteen Hegelund 	 */
7180ca60948SSteen Hegelund 	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
7190ca60948SSteen Hegelund 		mru = &multi->rule[idx];
7200ca60948SSteen Hegelund 		if (!mru->selected)
7210ca60948SSteen Hegelund 			continue;
7220ca60948SSteen Hegelund 
7230ca60948SSteen Hegelund 		vcap_set_rule_set_keyset(vrule, mru->keyset);
7240ca60948SSteen Hegelund 		if (count > 1)
7250ca60948SSteen Hegelund 			/* Some keysets do not have a type field */
7260ca60948SSteen Hegelund 			vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE,
7270ca60948SSteen Hegelund 					      mru->value,
7280ca60948SSteen Hegelund 					      ~mru->mask);
7290ca60948SSteen Hegelund 		mru->selected = false; /* mark as done */
7300ca60948SSteen Hegelund 		break; /* Stop here and add more rules later */
7310ca60948SSteen Hegelund 	}
7320ca60948SSteen Hegelund 	return err;
7330ca60948SSteen Hegelund }
7340ca60948SSteen Hegelund 
7350ca60948SSteen Hegelund static int sparx5_tc_add_rule_copy(struct vcap_control *vctrl,
7360ca60948SSteen Hegelund 				   struct flow_cls_offload *fco,
7370ca60948SSteen Hegelund 				   struct vcap_rule *erule,
7380ca60948SSteen Hegelund 				   struct vcap_admin *admin,
7390ca60948SSteen Hegelund 				   struct sparx5_wildcard_rule *rule)
7400ca60948SSteen Hegelund {
7410ca60948SSteen Hegelund 	enum vcap_key_field keylist[] = {
7420ca60948SSteen Hegelund 		VCAP_KF_IF_IGR_PORT_MASK,
7430ca60948SSteen Hegelund 		VCAP_KF_IF_IGR_PORT_MASK_SEL,
7440ca60948SSteen Hegelund 		VCAP_KF_IF_IGR_PORT_MASK_RNG,
7450ca60948SSteen Hegelund 		VCAP_KF_LOOKUP_FIRST_IS,
7460ca60948SSteen Hegelund 		VCAP_KF_TYPE,
7470ca60948SSteen Hegelund 	};
7480ca60948SSteen Hegelund 	struct vcap_rule *vrule;
7490ca60948SSteen Hegelund 	int err;
7500ca60948SSteen Hegelund 
7510ca60948SSteen Hegelund 	/* Add an extra rule with a special user and the new keyset */
7520ca60948SSteen Hegelund 	erule->user = VCAP_USER_TC_EXTRA;
7530ca60948SSteen Hegelund 	vrule = vcap_copy_rule(erule);
7540ca60948SSteen Hegelund 	if (IS_ERR(vrule))
7550ca60948SSteen Hegelund 		return PTR_ERR(vrule);
7560ca60948SSteen Hegelund 
7570ca60948SSteen Hegelund 	/* Link the new rule to the existing rule with the cookie */
7580ca60948SSteen Hegelund 	vrule->cookie = erule->cookie;
7590ca60948SSteen Hegelund 	vcap_filter_rule_keys(vrule, keylist, ARRAY_SIZE(keylist), true);
7600ca60948SSteen Hegelund 	err = vcap_set_rule_set_keyset(vrule, rule->keyset);
7610ca60948SSteen Hegelund 	if (err) {
7620ca60948SSteen Hegelund 		pr_err("%s:%d: could not set keyset %s in rule: %u\n",
7630ca60948SSteen Hegelund 		       __func__, __LINE__,
7640ca60948SSteen Hegelund 		       vcap_keyset_name(vctrl, rule->keyset),
7650ca60948SSteen Hegelund 		       vrule->id);
7660ca60948SSteen Hegelund 		goto out;
7670ca60948SSteen Hegelund 	}
7680ca60948SSteen Hegelund 
7690ca60948SSteen Hegelund 	/* Some keysets do not have a type field, so ignore return value */
7700ca60948SSteen Hegelund 	vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE, rule->value, ~rule->mask);
7710ca60948SSteen Hegelund 
7720ca60948SSteen Hegelund 	err = vcap_set_rule_set_actionset(vrule, erule->actionset);
7730ca60948SSteen Hegelund 	if (err)
7740ca60948SSteen Hegelund 		goto out;
7750ca60948SSteen Hegelund 
7760ca60948SSteen Hegelund 	err = sparx5_tc_add_rule_counter(admin, vrule);
7770ca60948SSteen Hegelund 	if (err)
7780ca60948SSteen Hegelund 		goto out;
7790ca60948SSteen Hegelund 
7800ca60948SSteen Hegelund 	err = vcap_val_rule(vrule, ETH_P_ALL);
7810ca60948SSteen Hegelund 	if (err) {
7820ca60948SSteen Hegelund 		pr_err("%s:%d: could not validate rule: %u\n",
7830ca60948SSteen Hegelund 		       __func__, __LINE__, vrule->id);
7840ca60948SSteen Hegelund 		vcap_set_tc_exterr(fco, vrule);
7850ca60948SSteen Hegelund 		goto out;
7860ca60948SSteen Hegelund 	}
7870ca60948SSteen Hegelund 	err = vcap_add_rule(vrule);
7880ca60948SSteen Hegelund 	if (err) {
7890ca60948SSteen Hegelund 		pr_err("%s:%d: could not add rule: %u\n",
7900ca60948SSteen Hegelund 		       __func__, __LINE__, vrule->id);
7910ca60948SSteen Hegelund 		goto out;
7920ca60948SSteen Hegelund 	}
7930ca60948SSteen Hegelund out:
7940ca60948SSteen Hegelund 	vcap_free_rule(vrule);
7950ca60948SSteen Hegelund 	return err;
7960ca60948SSteen Hegelund }
7970ca60948SSteen Hegelund 
7980ca60948SSteen Hegelund static int sparx5_tc_add_remaining_rules(struct vcap_control *vctrl,
7990ca60948SSteen Hegelund 					 struct flow_cls_offload *fco,
8000ca60948SSteen Hegelund 					 struct vcap_rule *erule,
8010ca60948SSteen Hegelund 					 struct vcap_admin *admin,
8020ca60948SSteen Hegelund 					 struct sparx5_multiple_rules *multi)
8030ca60948SSteen Hegelund {
8040ca60948SSteen Hegelund 	int idx, err = 0;
8050ca60948SSteen Hegelund 
8060ca60948SSteen Hegelund 	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
8070ca60948SSteen Hegelund 		if (!multi->rule[idx].selected)
8080ca60948SSteen Hegelund 			continue;
8090ca60948SSteen Hegelund 
8100ca60948SSteen Hegelund 		err = sparx5_tc_add_rule_copy(vctrl, fco, erule, admin,
8110ca60948SSteen Hegelund 					      &multi->rule[idx]);
8120ca60948SSteen Hegelund 		if (err)
8130ca60948SSteen Hegelund 			break;
8140ca60948SSteen Hegelund 	}
8150ca60948SSteen Hegelund 	return err;
8160ca60948SSteen Hegelund }
8170ca60948SSteen Hegelund 
818c9da1ac1SSteen Hegelund static int sparx5_tc_flower_replace(struct net_device *ndev,
819c9da1ac1SSteen Hegelund 				    struct flow_cls_offload *fco,
820c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin)
821c9da1ac1SSteen Hegelund {
822c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
8230ca60948SSteen Hegelund 	struct sparx5_multiple_rules multi = {};
824c9da1ac1SSteen Hegelund 	struct flow_action_entry *act;
825c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
826c9da1ac1SSteen Hegelund 	struct flow_rule *frule;
827c9da1ac1SSteen Hegelund 	struct vcap_rule *vrule;
828abc4010dSSteen Hegelund 	u16 l3_proto;
829c9da1ac1SSteen Hegelund 	int err, idx;
830c9da1ac1SSteen Hegelund 
831c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
832392d0ab0SSteen Hegelund 
833784c3067SSteen Hegelund 	err = sparx5_tc_flower_action_check(vctrl, ndev, fco);
834392d0ab0SSteen Hegelund 	if (err)
835392d0ab0SSteen Hegelund 		return err;
836392d0ab0SSteen Hegelund 
837c9da1ac1SSteen Hegelund 	vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC,
838c9da1ac1SSteen Hegelund 				fco->common.prio, 0);
839c9da1ac1SSteen Hegelund 	if (IS_ERR(vrule))
840c9da1ac1SSteen Hegelund 		return PTR_ERR(vrule);
841c9da1ac1SSteen Hegelund 
842c9da1ac1SSteen Hegelund 	vrule->cookie = fco->cookie;
843bcddc196SSteen Hegelund 
844bcddc196SSteen Hegelund 	l3_proto = ETH_P_ALL;
845bcddc196SSteen Hegelund 	err = sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto);
846bcddc196SSteen Hegelund 	if (err)
847bcddc196SSteen Hegelund 		goto out;
84840e7fe18SSteen Hegelund 
84940e7fe18SSteen Hegelund 	err = sparx5_tc_add_rule_counter(admin, vrule);
85040e7fe18SSteen Hegelund 	if (err)
85140e7fe18SSteen Hegelund 		goto out;
85240e7fe18SSteen Hegelund 
853392d0ab0SSteen Hegelund 	frule = flow_cls_offload_flow_rule(fco);
854c9da1ac1SSteen Hegelund 	flow_action_for_each(idx, act, &frule->action) {
855c9da1ac1SSteen Hegelund 		switch (act->id) {
856c9da1ac1SSteen Hegelund 		case FLOW_ACTION_TRAP:
857c9da1ac1SSteen Hegelund 			err = vcap_rule_add_action_bit(vrule,
858c9da1ac1SSteen Hegelund 						       VCAP_AF_CPU_COPY_ENA,
859c9da1ac1SSteen Hegelund 						       VCAP_BIT_1);
860c9da1ac1SSteen Hegelund 			if (err)
861c9da1ac1SSteen Hegelund 				goto out;
862c9da1ac1SSteen Hegelund 			err = vcap_rule_add_action_u32(vrule,
863c9da1ac1SSteen Hegelund 						       VCAP_AF_CPU_QUEUE_NUM, 0);
864c9da1ac1SSteen Hegelund 			if (err)
865c9da1ac1SSteen Hegelund 				goto out;
866c9da1ac1SSteen Hegelund 			err = vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,
867c9da1ac1SSteen Hegelund 						       SPX5_PMM_REPLACE_ALL);
868c9da1ac1SSteen Hegelund 			if (err)
869c9da1ac1SSteen Hegelund 				goto out;
870c9da1ac1SSteen Hegelund 			/* For now the actionset is hardcoded */
871c9da1ac1SSteen Hegelund 			err = vcap_set_rule_set_actionset(vrule,
872c9da1ac1SSteen Hegelund 							  VCAP_AFS_BASE_TYPE);
873c9da1ac1SSteen Hegelund 			if (err)
874c9da1ac1SSteen Hegelund 				goto out;
875c9da1ac1SSteen Hegelund 			break;
876c9da1ac1SSteen Hegelund 		case FLOW_ACTION_ACCEPT:
877c9da1ac1SSteen Hegelund 			/* For now the actionset is hardcoded */
878c9da1ac1SSteen Hegelund 			err = vcap_set_rule_set_actionset(vrule,
879c9da1ac1SSteen Hegelund 							  VCAP_AFS_BASE_TYPE);
880c9da1ac1SSteen Hegelund 			if (err)
881c9da1ac1SSteen Hegelund 				goto out;
882c9da1ac1SSteen Hegelund 			break;
883392d0ab0SSteen Hegelund 		case FLOW_ACTION_GOTO:
884392d0ab0SSteen Hegelund 			/* Links between VCAPs will be added later */
885392d0ab0SSteen Hegelund 			break;
886c9da1ac1SSteen Hegelund 		default:
887c9da1ac1SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
888c9da1ac1SSteen Hegelund 					   "Unsupported TC action");
889c9da1ac1SSteen Hegelund 			err = -EOPNOTSUPP;
890c9da1ac1SSteen Hegelund 			goto out;
891c9da1ac1SSteen Hegelund 		}
892c9da1ac1SSteen Hegelund 	}
8930ca60948SSteen Hegelund 
8940ca60948SSteen Hegelund 	err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin, l3_proto,
8950ca60948SSteen Hegelund 					       &multi);
8960ca60948SSteen Hegelund 	if (err) {
8970ca60948SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
8980ca60948SSteen Hegelund 				   "No matching port keyset for filter protocol and keys");
8990ca60948SSteen Hegelund 		goto out;
9000ca60948SSteen Hegelund 	}
9010ca60948SSteen Hegelund 
902abc4010dSSteen Hegelund 	/* provide the l3 protocol to guide the keyset selection */
903abc4010dSSteen Hegelund 	err = vcap_val_rule(vrule, l3_proto);
904c9da1ac1SSteen Hegelund 	if (err) {
905c9da1ac1SSteen Hegelund 		vcap_set_tc_exterr(fco, vrule);
906c9da1ac1SSteen Hegelund 		goto out;
907c9da1ac1SSteen Hegelund 	}
908c9da1ac1SSteen Hegelund 	err = vcap_add_rule(vrule);
909c9da1ac1SSteen Hegelund 	if (err)
910c9da1ac1SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
911c9da1ac1SSteen Hegelund 				   "Could not add the filter");
9120ca60948SSteen Hegelund 
9130ca60948SSteen Hegelund 	if (l3_proto == ETH_P_ALL)
9140ca60948SSteen Hegelund 		err = sparx5_tc_add_remaining_rules(vctrl, fco, vrule, admin,
9150ca60948SSteen Hegelund 						    &multi);
9160ca60948SSteen Hegelund 
917c9da1ac1SSteen Hegelund out:
918c9da1ac1SSteen Hegelund 	vcap_free_rule(vrule);
919c9da1ac1SSteen Hegelund 	return err;
920c9da1ac1SSteen Hegelund }
921c9da1ac1SSteen Hegelund 
922c9da1ac1SSteen Hegelund static int sparx5_tc_flower_destroy(struct net_device *ndev,
923c9da1ac1SSteen Hegelund 				    struct flow_cls_offload *fco,
924c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin)
925c9da1ac1SSteen Hegelund {
926c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
927c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
928c9da1ac1SSteen Hegelund 	int err = -ENOENT, rule_id;
929c9da1ac1SSteen Hegelund 
930c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
931c9da1ac1SSteen Hegelund 	while (true) {
932c9da1ac1SSteen Hegelund 		rule_id = vcap_lookup_rule_by_cookie(vctrl, fco->cookie);
933c9da1ac1SSteen Hegelund 		if (rule_id <= 0)
934c9da1ac1SSteen Hegelund 			break;
935c9da1ac1SSteen Hegelund 		err = vcap_del_rule(vctrl, ndev, rule_id);
936c9da1ac1SSteen Hegelund 		if (err) {
937c9da1ac1SSteen Hegelund 			pr_err("%s:%d: could not delete rule %d\n",
938c9da1ac1SSteen Hegelund 			       __func__, __LINE__, rule_id);
939c9da1ac1SSteen Hegelund 			break;
940c9da1ac1SSteen Hegelund 		}
941c9da1ac1SSteen Hegelund 	}
942c9da1ac1SSteen Hegelund 	return err;
943c9da1ac1SSteen Hegelund }
944c9da1ac1SSteen Hegelund 
94540e7fe18SSteen Hegelund static int sparx5_tc_flower_stats(struct net_device *ndev,
94640e7fe18SSteen Hegelund 				  struct flow_cls_offload *fco,
94740e7fe18SSteen Hegelund 				  struct vcap_admin *admin)
94840e7fe18SSteen Hegelund {
94940e7fe18SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
950*27d293ccSSteen Hegelund 	struct vcap_counter ctr = {};
95140e7fe18SSteen Hegelund 	struct vcap_control *vctrl;
95240e7fe18SSteen Hegelund 	ulong lastused = 0;
95340e7fe18SSteen Hegelund 	int err;
95440e7fe18SSteen Hegelund 
95540e7fe18SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
956*27d293ccSSteen Hegelund 	err = vcap_get_rule_count_by_cookie(vctrl, &ctr, fco->cookie);
95740e7fe18SSteen Hegelund 	if (err)
95840e7fe18SSteen Hegelund 		return err;
959*27d293ccSSteen Hegelund 	flow_stats_update(&fco->stats, 0x0, ctr.value, 0, lastused,
96040e7fe18SSteen Hegelund 			  FLOW_ACTION_HW_STATS_IMMEDIATE);
96140e7fe18SSteen Hegelund 	return err;
96240e7fe18SSteen Hegelund }
96340e7fe18SSteen Hegelund 
964c9da1ac1SSteen Hegelund int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco,
965c9da1ac1SSteen Hegelund 		     bool ingress)
966c9da1ac1SSteen Hegelund {
967c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
968c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
969c9da1ac1SSteen Hegelund 	struct vcap_admin *admin;
970c9da1ac1SSteen Hegelund 	int err = -EINVAL;
971c9da1ac1SSteen Hegelund 
972c9da1ac1SSteen Hegelund 	/* Get vcap instance from the chain id */
973c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
974c9da1ac1SSteen Hegelund 	admin = vcap_find_admin(vctrl, fco->common.chain_index);
975c9da1ac1SSteen Hegelund 	if (!admin) {
976c9da1ac1SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack, "Invalid chain");
977c9da1ac1SSteen Hegelund 		return err;
978c9da1ac1SSteen Hegelund 	}
979c9da1ac1SSteen Hegelund 
980c9da1ac1SSteen Hegelund 	switch (fco->command) {
981c9da1ac1SSteen Hegelund 	case FLOW_CLS_REPLACE:
982c9da1ac1SSteen Hegelund 		return sparx5_tc_flower_replace(ndev, fco, admin);
983c9da1ac1SSteen Hegelund 	case FLOW_CLS_DESTROY:
984c9da1ac1SSteen Hegelund 		return sparx5_tc_flower_destroy(ndev, fco, admin);
98540e7fe18SSteen Hegelund 	case FLOW_CLS_STATS:
98640e7fe18SSteen Hegelund 		return sparx5_tc_flower_stats(ndev, fco, admin);
987c9da1ac1SSteen Hegelund 	default:
988c9da1ac1SSteen Hegelund 		return -EOPNOTSUPP;
989c9da1ac1SSteen Hegelund 	}
990c9da1ac1SSteen Hegelund }
991