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;
33542e6e2cSSteen Hegelund 	struct vcap_admin *admin;
34d6c2964dSSteen Hegelund 	u16 l3_proto;
35d6c2964dSSteen Hegelund 	u8 l4_proto;
36c9da1ac1SSteen Hegelund 	unsigned int used_keys;
37c9da1ac1SSteen Hegelund };
38c9da1ac1SSteen Hegelund 
393a344f99SSteen Hegelund enum sparx5_is2_arp_opcode {
403a344f99SSteen Hegelund 	SPX5_IS2_ARP_REQUEST,
413a344f99SSteen Hegelund 	SPX5_IS2_ARP_REPLY,
423a344f99SSteen Hegelund 	SPX5_IS2_RARP_REQUEST,
433a344f99SSteen Hegelund 	SPX5_IS2_RARP_REPLY,
443a344f99SSteen Hegelund };
453a344f99SSteen Hegelund 
463a344f99SSteen Hegelund enum tc_arp_opcode {
473a344f99SSteen Hegelund 	TC_ARP_OP_RESERVED,
483a344f99SSteen Hegelund 	TC_ARP_OP_REQUEST,
493a344f99SSteen Hegelund 	TC_ARP_OP_REPLY,
503a344f99SSteen Hegelund };
513a344f99SSteen Hegelund 
52c9da1ac1SSteen Hegelund static int sparx5_tc_flower_handler_ethaddr_usage(struct sparx5_tc_flower_parse_usage *st)
53c9da1ac1SSteen Hegelund {
54c9da1ac1SSteen Hegelund 	enum vcap_key_field smac_key = VCAP_KF_L2_SMAC;
55c9da1ac1SSteen Hegelund 	enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC;
56c9da1ac1SSteen Hegelund 	struct flow_match_eth_addrs match;
57c9da1ac1SSteen Hegelund 	struct vcap_u48_key smac, dmac;
58c9da1ac1SSteen Hegelund 	int err = 0;
59c9da1ac1SSteen Hegelund 
60c9da1ac1SSteen Hegelund 	flow_rule_match_eth_addrs(st->frule, &match);
61c9da1ac1SSteen Hegelund 
62c9da1ac1SSteen Hegelund 	if (!is_zero_ether_addr(match.mask->src)) {
63c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN);
64c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN);
65c9da1ac1SSteen Hegelund 		err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac);
66c9da1ac1SSteen Hegelund 		if (err)
67c9da1ac1SSteen Hegelund 			goto out;
68c9da1ac1SSteen Hegelund 	}
69c9da1ac1SSteen Hegelund 
70c9da1ac1SSteen Hegelund 	if (!is_zero_ether_addr(match.mask->dst)) {
71c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN);
72c9da1ac1SSteen Hegelund 		vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN);
73c9da1ac1SSteen Hegelund 		err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac);
74c9da1ac1SSteen Hegelund 		if (err)
75c9da1ac1SSteen Hegelund 			goto out;
76c9da1ac1SSteen Hegelund 	}
77c9da1ac1SSteen Hegelund 
78c9da1ac1SSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS);
79c9da1ac1SSteen Hegelund 
80c9da1ac1SSteen Hegelund 	return err;
81c9da1ac1SSteen Hegelund 
82c9da1ac1SSteen Hegelund out:
83c9da1ac1SSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "eth_addr parse error");
84c9da1ac1SSteen Hegelund 	return err;
85c9da1ac1SSteen Hegelund }
86c9da1ac1SSteen Hegelund 
87d6c2964dSSteen Hegelund static int
88d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ipv4_usage(struct sparx5_tc_flower_parse_usage *st)
89d6c2964dSSteen Hegelund {
90d6c2964dSSteen Hegelund 	int err = 0;
91d6c2964dSSteen Hegelund 
92d6c2964dSSteen Hegelund 	if (st->l3_proto == ETH_P_IP) {
93d6c2964dSSteen Hegelund 		struct flow_match_ipv4_addrs mt;
94d6c2964dSSteen Hegelund 
95d6c2964dSSteen Hegelund 		flow_rule_match_ipv4_addrs(st->frule, &mt);
96d6c2964dSSteen Hegelund 		if (mt.mask->src) {
97d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
98d6c2964dSSteen Hegelund 						    VCAP_KF_L3_IP4_SIP,
99d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.key->src),
100d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.mask->src));
101d6c2964dSSteen Hegelund 			if (err)
102d6c2964dSSteen Hegelund 				goto out;
103d6c2964dSSteen Hegelund 		}
104d6c2964dSSteen Hegelund 		if (mt.mask->dst) {
105d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
106d6c2964dSSteen Hegelund 						    VCAP_KF_L3_IP4_DIP,
107d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.key->dst),
108d6c2964dSSteen Hegelund 						    be32_to_cpu(mt.mask->dst));
109d6c2964dSSteen Hegelund 			if (err)
110d6c2964dSSteen Hegelund 				goto out;
111d6c2964dSSteen Hegelund 		}
112d6c2964dSSteen Hegelund 	}
113d6c2964dSSteen Hegelund 
114d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS);
115d6c2964dSSteen Hegelund 
116d6c2964dSSteen Hegelund 	return err;
117d6c2964dSSteen Hegelund 
118d6c2964dSSteen Hegelund out:
119d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv4_addr parse error");
120d6c2964dSSteen Hegelund 	return err;
121d6c2964dSSteen Hegelund }
122d6c2964dSSteen Hegelund 
123d6c2964dSSteen Hegelund static int
124d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ipv6_usage(struct sparx5_tc_flower_parse_usage *st)
125d6c2964dSSteen Hegelund {
126d6c2964dSSteen Hegelund 	int err = 0;
127d6c2964dSSteen Hegelund 
128d6c2964dSSteen Hegelund 	if (st->l3_proto == ETH_P_IPV6) {
129d6c2964dSSteen Hegelund 		struct flow_match_ipv6_addrs mt;
130d6c2964dSSteen Hegelund 		struct vcap_u128_key sip;
131d6c2964dSSteen Hegelund 		struct vcap_u128_key dip;
132d6c2964dSSteen Hegelund 
133d6c2964dSSteen Hegelund 		flow_rule_match_ipv6_addrs(st->frule, &mt);
134d6c2964dSSteen Hegelund 		/* Check if address masks are non-zero */
135d6c2964dSSteen Hegelund 		if (!ipv6_addr_any(&mt.mask->src)) {
136d6c2964dSSteen Hegelund 			vcap_netbytes_copy(sip.value, mt.key->src.s6_addr, 16);
137d6c2964dSSteen Hegelund 			vcap_netbytes_copy(sip.mask, mt.mask->src.s6_addr, 16);
138d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u128(st->vrule,
139d6c2964dSSteen Hegelund 						     VCAP_KF_L3_IP6_SIP, &sip);
140d6c2964dSSteen Hegelund 			if (err)
141d6c2964dSSteen Hegelund 				goto out;
142d6c2964dSSteen Hegelund 		}
143d6c2964dSSteen Hegelund 		if (!ipv6_addr_any(&mt.mask->dst)) {
144d6c2964dSSteen Hegelund 			vcap_netbytes_copy(dip.value, mt.key->dst.s6_addr, 16);
145d6c2964dSSteen Hegelund 			vcap_netbytes_copy(dip.mask, mt.mask->dst.s6_addr, 16);
146d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u128(st->vrule,
147d6c2964dSSteen Hegelund 						     VCAP_KF_L3_IP6_DIP, &dip);
148d6c2964dSSteen Hegelund 			if (err)
149d6c2964dSSteen Hegelund 				goto out;
150d6c2964dSSteen Hegelund 		}
151d6c2964dSSteen Hegelund 	}
152d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS);
153d6c2964dSSteen Hegelund 	return err;
154d6c2964dSSteen Hegelund out:
155d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv6_addr parse error");
156d6c2964dSSteen Hegelund 	return err;
157d6c2964dSSteen Hegelund }
158d6c2964dSSteen Hegelund 
159d6c2964dSSteen Hegelund static int
160d6c2964dSSteen Hegelund sparx5_tc_flower_handler_control_usage(struct sparx5_tc_flower_parse_usage *st)
161d6c2964dSSteen Hegelund {
162d6c2964dSSteen Hegelund 	struct flow_match_control mt;
163d6c2964dSSteen Hegelund 	u32 value, mask;
164d6c2964dSSteen Hegelund 	int err = 0;
165d6c2964dSSteen Hegelund 
166d6c2964dSSteen Hegelund 	flow_rule_match_control(st->frule, &mt);
167d6c2964dSSteen Hegelund 
168d6c2964dSSteen Hegelund 	if (mt.mask->flags) {
169d6c2964dSSteen Hegelund 		if (mt.mask->flags & FLOW_DIS_FIRST_FRAG) {
170d6c2964dSSteen Hegelund 			if (mt.key->flags & FLOW_DIS_FIRST_FRAG) {
171d6c2964dSSteen Hegelund 				value = 1; /* initial fragment */
172d6c2964dSSteen Hegelund 				mask = 0x3;
173d6c2964dSSteen Hegelund 			} else {
174d6c2964dSSteen Hegelund 				if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) {
175d6c2964dSSteen Hegelund 					value = 3; /* follow up fragment */
176d6c2964dSSteen Hegelund 					mask = 0x3;
177d6c2964dSSteen Hegelund 				} else {
178d6c2964dSSteen Hegelund 					value = 0; /* no fragment */
179d6c2964dSSteen Hegelund 					mask = 0x3;
180d6c2964dSSteen Hegelund 				}
181d6c2964dSSteen Hegelund 			}
182d6c2964dSSteen Hegelund 		} else {
183d6c2964dSSteen Hegelund 			if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) {
184d6c2964dSSteen Hegelund 				value = 3; /* follow up fragment */
185d6c2964dSSteen Hegelund 				mask = 0x3;
186d6c2964dSSteen Hegelund 			} else {
187d6c2964dSSteen Hegelund 				value = 0; /* no fragment */
188d6c2964dSSteen Hegelund 				mask = 0x3;
189d6c2964dSSteen Hegelund 			}
190d6c2964dSSteen Hegelund 		}
191d6c2964dSSteen Hegelund 
192d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule,
193d6c2964dSSteen Hegelund 					    VCAP_KF_L3_FRAGMENT_TYPE,
194d6c2964dSSteen Hegelund 					    value, mask);
195d6c2964dSSteen Hegelund 		if (err)
196d6c2964dSSteen Hegelund 			goto out;
197d6c2964dSSteen Hegelund 	}
198d6c2964dSSteen Hegelund 
199d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL);
200d6c2964dSSteen Hegelund 
201d6c2964dSSteen Hegelund 	return err;
202d6c2964dSSteen Hegelund 
203d6c2964dSSteen Hegelund out:
204d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error");
205d6c2964dSSteen Hegelund 	return err;
206d6c2964dSSteen Hegelund }
207d6c2964dSSteen Hegelund 
208d6c2964dSSteen Hegelund static int
209d6c2964dSSteen Hegelund sparx5_tc_flower_handler_portnum_usage(struct sparx5_tc_flower_parse_usage *st)
210d6c2964dSSteen Hegelund {
211d6c2964dSSteen Hegelund 	struct flow_match_ports mt;
212d6c2964dSSteen Hegelund 	u16 value, mask;
213d6c2964dSSteen Hegelund 	int err = 0;
214d6c2964dSSteen Hegelund 
215d6c2964dSSteen Hegelund 	flow_rule_match_ports(st->frule, &mt);
216d6c2964dSSteen Hegelund 
217d6c2964dSSteen Hegelund 	if (mt.mask->src) {
218d6c2964dSSteen Hegelund 		value = be16_to_cpu(mt.key->src);
219d6c2964dSSteen Hegelund 		mask = be16_to_cpu(mt.mask->src);
220d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_SPORT, value,
221d6c2964dSSteen Hegelund 					    mask);
222d6c2964dSSteen Hegelund 		if (err)
223d6c2964dSSteen Hegelund 			goto out;
224d6c2964dSSteen Hegelund 	}
225d6c2964dSSteen Hegelund 
226d6c2964dSSteen Hegelund 	if (mt.mask->dst) {
227d6c2964dSSteen Hegelund 		value = be16_to_cpu(mt.key->dst);
228d6c2964dSSteen Hegelund 		mask = be16_to_cpu(mt.mask->dst);
229d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_DPORT, value,
230d6c2964dSSteen Hegelund 					    mask);
231d6c2964dSSteen Hegelund 		if (err)
232d6c2964dSSteen Hegelund 			goto out;
233d6c2964dSSteen Hegelund 	}
234d6c2964dSSteen Hegelund 
235d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_PORTS);
236d6c2964dSSteen Hegelund 
237d6c2964dSSteen Hegelund 	return err;
238d6c2964dSSteen Hegelund 
239d6c2964dSSteen Hegelund out:
240d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "port parse error");
241d6c2964dSSteen Hegelund 	return err;
242d6c2964dSSteen Hegelund }
243d6c2964dSSteen Hegelund 
244d6c2964dSSteen Hegelund static int
245d6c2964dSSteen Hegelund sparx5_tc_flower_handler_basic_usage(struct sparx5_tc_flower_parse_usage *st)
246d6c2964dSSteen Hegelund {
247d6c2964dSSteen Hegelund 	struct flow_match_basic mt;
248d6c2964dSSteen Hegelund 	int err = 0;
249d6c2964dSSteen Hegelund 
250d6c2964dSSteen Hegelund 	flow_rule_match_basic(st->frule, &mt);
251d6c2964dSSteen Hegelund 
252d6c2964dSSteen Hegelund 	if (mt.mask->n_proto) {
253d6c2964dSSteen Hegelund 		st->l3_proto = be16_to_cpu(mt.key->n_proto);
25463e35645SSteen Hegelund 		if (!sparx5_vcap_is_known_etype(st->admin, st->l3_proto)) {
255d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,
256d6c2964dSSteen Hegelund 						    st->l3_proto, ~0);
257d6c2964dSSteen Hegelund 			if (err)
258d6c2964dSSteen Hegelund 				goto out;
259d6c2964dSSteen Hegelund 		} else if (st->l3_proto == ETH_P_IP) {
260d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
261d6c2964dSSteen Hegelund 						    VCAP_BIT_1);
262d6c2964dSSteen Hegelund 			if (err)
263d6c2964dSSteen Hegelund 				goto out;
264d6c2964dSSteen Hegelund 		} else if (st->l3_proto == ETH_P_IPV6) {
265d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
266d6c2964dSSteen Hegelund 						    VCAP_BIT_0);
267d6c2964dSSteen Hegelund 			if (err)
268d6c2964dSSteen Hegelund 				goto out;
2694114ef2cSSteen Hegelund 			if (st->admin->vtype == VCAP_TYPE_IS0) {
2704114ef2cSSteen Hegelund 				err = vcap_rule_add_key_bit(st->vrule,
2714114ef2cSSteen Hegelund 							    VCAP_KF_IP_SNAP_IS,
2724114ef2cSSteen Hegelund 							    VCAP_BIT_1);
2734114ef2cSSteen Hegelund 				if (err)
2744114ef2cSSteen Hegelund 					goto out;
2754114ef2cSSteen Hegelund 			}
2764114ef2cSSteen Hegelund 
277d6c2964dSSteen Hegelund 		}
278d6c2964dSSteen Hegelund 	}
279d6c2964dSSteen Hegelund 
280d6c2964dSSteen Hegelund 	if (mt.mask->ip_proto) {
281d6c2964dSSteen Hegelund 		st->l4_proto = mt.key->ip_proto;
282d6c2964dSSteen Hegelund 		if (st->l4_proto == IPPROTO_TCP) {
283d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule,
284d6c2964dSSteen Hegelund 						    VCAP_KF_TCP_IS,
285d6c2964dSSteen Hegelund 						    VCAP_BIT_1);
286d6c2964dSSteen Hegelund 			if (err)
287d6c2964dSSteen Hegelund 				goto out;
288d6c2964dSSteen Hegelund 		} else if (st->l4_proto == IPPROTO_UDP) {
289d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_bit(st->vrule,
290d6c2964dSSteen Hegelund 						    VCAP_KF_TCP_IS,
291d6c2964dSSteen Hegelund 						    VCAP_BIT_0);
292d6c2964dSSteen Hegelund 			if (err)
293d6c2964dSSteen Hegelund 				goto out;
294542e6e2cSSteen Hegelund 			if (st->admin->vtype == VCAP_TYPE_IS0) {
295542e6e2cSSteen Hegelund 				err = vcap_rule_add_key_bit(st->vrule,
296542e6e2cSSteen Hegelund 							    VCAP_KF_TCP_UDP_IS,
297542e6e2cSSteen Hegelund 							    VCAP_BIT_1);
298542e6e2cSSteen Hegelund 				if (err)
299542e6e2cSSteen Hegelund 					goto out;
300542e6e2cSSteen Hegelund 			}
301d6c2964dSSteen Hegelund 		} else {
302d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
303d6c2964dSSteen Hegelund 						    VCAP_KF_L3_IP_PROTO,
304d6c2964dSSteen Hegelund 						    st->l4_proto, ~0);
305d6c2964dSSteen Hegelund 			if (err)
306d6c2964dSSteen Hegelund 				goto out;
307d6c2964dSSteen Hegelund 		}
308d6c2964dSSteen Hegelund 	}
309d6c2964dSSteen Hegelund 
310d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC);
311d6c2964dSSteen Hegelund 
312d6c2964dSSteen Hegelund 	return err;
313d6c2964dSSteen Hegelund 
314d6c2964dSSteen Hegelund out:
315d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error");
316d6c2964dSSteen Hegelund 	return err;
317d6c2964dSSteen Hegelund }
318d6c2964dSSteen Hegelund 
319d6c2964dSSteen Hegelund static int
32052df82ccSSteen Hegelund sparx5_tc_flower_handler_cvlan_usage(struct sparx5_tc_flower_parse_usage *st)
32152df82ccSSteen Hegelund {
32252df82ccSSteen Hegelund 	enum vcap_key_field vid_key = VCAP_KF_8021Q_VID0;
32352df82ccSSteen Hegelund 	enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP0;
32452df82ccSSteen Hegelund 	struct flow_match_vlan mt;
32552df82ccSSteen Hegelund 	u16 tpid;
32652df82ccSSteen Hegelund 	int err;
32752df82ccSSteen Hegelund 
328*a5300724SSteen Hegelund 	if (st->admin->vtype != VCAP_TYPE_IS0) {
329*a5300724SSteen Hegelund 		NL_SET_ERR_MSG_MOD(st->fco->common.extack,
330*a5300724SSteen Hegelund 				   "cvlan not supported in this VCAP");
33152df82ccSSteen Hegelund 		return -EINVAL;
332*a5300724SSteen Hegelund 	}
33352df82ccSSteen Hegelund 
33452df82ccSSteen Hegelund 	flow_rule_match_cvlan(st->frule, &mt);
33552df82ccSSteen Hegelund 
33652df82ccSSteen Hegelund 	tpid = be16_to_cpu(mt.key->vlan_tpid);
33752df82ccSSteen Hegelund 
33852df82ccSSteen Hegelund 	if (tpid == ETH_P_8021Q) {
33952df82ccSSteen Hegelund 		vid_key = VCAP_KF_8021Q_VID1;
34052df82ccSSteen Hegelund 		pcp_key = VCAP_KF_8021Q_PCP1;
34152df82ccSSteen Hegelund 	}
34252df82ccSSteen Hegelund 
34352df82ccSSteen Hegelund 	if (mt.mask->vlan_id) {
34452df82ccSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, vid_key,
34552df82ccSSteen Hegelund 					    mt.key->vlan_id,
34652df82ccSSteen Hegelund 					    mt.mask->vlan_id);
34752df82ccSSteen Hegelund 		if (err)
34852df82ccSSteen Hegelund 			goto out;
34952df82ccSSteen Hegelund 	}
35052df82ccSSteen Hegelund 
35152df82ccSSteen Hegelund 	if (mt.mask->vlan_priority) {
35252df82ccSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, pcp_key,
35352df82ccSSteen Hegelund 					    mt.key->vlan_priority,
35452df82ccSSteen Hegelund 					    mt.mask->vlan_priority);
35552df82ccSSteen Hegelund 		if (err)
35652df82ccSSteen Hegelund 			goto out;
35752df82ccSSteen Hegelund 	}
35852df82ccSSteen Hegelund 
35952df82ccSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CVLAN);
36052df82ccSSteen Hegelund 
36152df82ccSSteen Hegelund 	return 0;
36252df82ccSSteen Hegelund out:
36352df82ccSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "cvlan parse error");
36452df82ccSSteen Hegelund 	return err;
36552df82ccSSteen Hegelund }
36652df82ccSSteen Hegelund 
36752df82ccSSteen Hegelund static int
368d6c2964dSSteen Hegelund sparx5_tc_flower_handler_vlan_usage(struct sparx5_tc_flower_parse_usage *st)
369d6c2964dSSteen Hegelund {
370d6c2964dSSteen Hegelund 	enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS;
371d6c2964dSSteen Hegelund 	enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS;
372d6c2964dSSteen Hegelund 	struct flow_match_vlan mt;
373d6c2964dSSteen Hegelund 	int err;
374d6c2964dSSteen Hegelund 
375d6c2964dSSteen Hegelund 	flow_rule_match_vlan(st->frule, &mt);
376d6c2964dSSteen Hegelund 
37752df82ccSSteen Hegelund 	if (st->admin->vtype == VCAP_TYPE_IS0) {
37852df82ccSSteen Hegelund 		vid_key = VCAP_KF_8021Q_VID0;
37952df82ccSSteen Hegelund 		pcp_key = VCAP_KF_8021Q_PCP0;
38052df82ccSSteen Hegelund 	}
38152df82ccSSteen Hegelund 
382d6c2964dSSteen Hegelund 	if (mt.mask->vlan_id) {
383d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, vid_key,
384d6c2964dSSteen Hegelund 					    mt.key->vlan_id,
385d6c2964dSSteen Hegelund 					    mt.mask->vlan_id);
386d6c2964dSSteen Hegelund 		if (err)
387d6c2964dSSteen Hegelund 			goto out;
388d6c2964dSSteen Hegelund 	}
389d6c2964dSSteen Hegelund 
390d6c2964dSSteen Hegelund 	if (mt.mask->vlan_priority) {
391d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, pcp_key,
392d6c2964dSSteen Hegelund 					    mt.key->vlan_priority,
393d6c2964dSSteen Hegelund 					    mt.mask->vlan_priority);
394d6c2964dSSteen Hegelund 		if (err)
395d6c2964dSSteen Hegelund 			goto out;
396d6c2964dSSteen Hegelund 	}
397d6c2964dSSteen Hegelund 
398d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN);
399d6c2964dSSteen Hegelund 
4004e9a6139SDan Carpenter 	return 0;
401d6c2964dSSteen Hegelund out:
402d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "vlan parse error");
403d6c2964dSSteen Hegelund 	return err;
404d6c2964dSSteen Hegelund }
405d6c2964dSSteen Hegelund 
406d6c2964dSSteen Hegelund static int
407d6c2964dSSteen Hegelund sparx5_tc_flower_handler_tcp_usage(struct sparx5_tc_flower_parse_usage *st)
408d6c2964dSSteen Hegelund {
409d6c2964dSSteen Hegelund 	struct flow_match_tcp mt;
410d6c2964dSSteen Hegelund 	u16 tcp_flags_mask;
411d6c2964dSSteen Hegelund 	u16 tcp_flags_key;
412d6c2964dSSteen Hegelund 	enum vcap_bit val;
413d6c2964dSSteen Hegelund 	int err = 0;
414d6c2964dSSteen Hegelund 
415d6c2964dSSteen Hegelund 	flow_rule_match_tcp(st->frule, &mt);
416d6c2964dSSteen Hegelund 	tcp_flags_key = be16_to_cpu(mt.key->flags);
417d6c2964dSSteen Hegelund 	tcp_flags_mask = be16_to_cpu(mt.mask->flags);
418d6c2964dSSteen Hegelund 
419d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_FIN) {
420d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
421d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_FIN)
422d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
423d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_FIN, val);
424d6c2964dSSteen Hegelund 		if (err)
425d6c2964dSSteen Hegelund 			goto out;
426d6c2964dSSteen Hegelund 	}
427d6c2964dSSteen Hegelund 
428d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_SYN) {
429d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
430d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_SYN)
431d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
432d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_SYN, val);
433d6c2964dSSteen Hegelund 		if (err)
434d6c2964dSSteen Hegelund 			goto out;
435d6c2964dSSteen Hegelund 	}
436d6c2964dSSteen Hegelund 
437d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_RST) {
438d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
439d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_RST)
440d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
441d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_RST, val);
442d6c2964dSSteen Hegelund 		if (err)
443d6c2964dSSteen Hegelund 			goto out;
444d6c2964dSSteen Hegelund 	}
445d6c2964dSSteen Hegelund 
446d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_PSH) {
447d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
448d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_PSH)
449d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
450d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_PSH, val);
451d6c2964dSSteen Hegelund 		if (err)
452d6c2964dSSteen Hegelund 			goto out;
453d6c2964dSSteen Hegelund 	}
454d6c2964dSSteen Hegelund 
455d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_ACK) {
456d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
457d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_ACK)
458d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
459d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_ACK, val);
460d6c2964dSSteen Hegelund 		if (err)
461d6c2964dSSteen Hegelund 			goto out;
462d6c2964dSSteen Hegelund 	}
463d6c2964dSSteen Hegelund 
464d6c2964dSSteen Hegelund 	if (tcp_flags_mask & TCPHDR_URG) {
465d6c2964dSSteen Hegelund 		val = VCAP_BIT_0;
466d6c2964dSSteen Hegelund 		if (tcp_flags_key & TCPHDR_URG)
467d6c2964dSSteen Hegelund 			val = VCAP_BIT_1;
468d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_URG, val);
469d6c2964dSSteen Hegelund 		if (err)
470d6c2964dSSteen Hegelund 			goto out;
471d6c2964dSSteen Hegelund 	}
472d6c2964dSSteen Hegelund 
473d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_TCP);
474d6c2964dSSteen Hegelund 
475d6c2964dSSteen Hegelund 	return err;
476d6c2964dSSteen Hegelund 
477d6c2964dSSteen Hegelund out:
478d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "tcp_flags parse error");
479d6c2964dSSteen Hegelund 	return err;
480d6c2964dSSteen Hegelund }
481d6c2964dSSteen Hegelund 
482d6c2964dSSteen Hegelund static int
4833a344f99SSteen Hegelund sparx5_tc_flower_handler_arp_usage(struct sparx5_tc_flower_parse_usage *st)
4843a344f99SSteen Hegelund {
4853a344f99SSteen Hegelund 	struct flow_match_arp mt;
4863a344f99SSteen Hegelund 	u16 value, mask;
4873a344f99SSteen Hegelund 	u32 ipval, ipmsk;
4883a344f99SSteen Hegelund 	int err;
4893a344f99SSteen Hegelund 
4903a344f99SSteen Hegelund 	flow_rule_match_arp(st->frule, &mt);
4913a344f99SSteen Hegelund 
4923a344f99SSteen Hegelund 	if (mt.mask->op) {
4933a344f99SSteen Hegelund 		mask = 0x3;
4943a344f99SSteen Hegelund 		if (st->l3_proto == ETH_P_ARP) {
4953a344f99SSteen Hegelund 			value = mt.key->op == TC_ARP_OP_REQUEST ?
4963a344f99SSteen Hegelund 					SPX5_IS2_ARP_REQUEST :
4973a344f99SSteen Hegelund 					SPX5_IS2_ARP_REPLY;
4983a344f99SSteen Hegelund 		} else { /* RARP */
4993a344f99SSteen Hegelund 			value = mt.key->op == TC_ARP_OP_REQUEST ?
5003a344f99SSteen Hegelund 					SPX5_IS2_RARP_REQUEST :
5013a344f99SSteen Hegelund 					SPX5_IS2_RARP_REPLY;
5023a344f99SSteen Hegelund 		}
5033a344f99SSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ARP_OPCODE,
5043a344f99SSteen Hegelund 					    value, mask);
5053a344f99SSteen Hegelund 		if (err)
5063a344f99SSteen Hegelund 			goto out;
5073a344f99SSteen Hegelund 	}
5083a344f99SSteen Hegelund 
5093a344f99SSteen Hegelund 	/* The IS2 ARP keyset does not support ARP hardware addresses */
5103a344f99SSteen Hegelund 	if (!is_zero_ether_addr(mt.mask->sha) ||
5114e9a6139SDan Carpenter 	    !is_zero_ether_addr(mt.mask->tha)) {
5124e9a6139SDan Carpenter 		err = -EINVAL;
5133a344f99SSteen Hegelund 		goto out;
5144e9a6139SDan Carpenter 	}
5153a344f99SSteen Hegelund 
5163a344f99SSteen Hegelund 	if (mt.mask->sip) {
5173a344f99SSteen Hegelund 		ipval = be32_to_cpu((__force __be32)mt.key->sip);
5183a344f99SSteen Hegelund 		ipmsk = be32_to_cpu((__force __be32)mt.mask->sip);
5193a344f99SSteen Hegelund 
5203a344f99SSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_SIP,
5213a344f99SSteen Hegelund 					    ipval, ipmsk);
5223a344f99SSteen Hegelund 		if (err)
5233a344f99SSteen Hegelund 			goto out;
5243a344f99SSteen Hegelund 	}
5253a344f99SSteen Hegelund 
5263a344f99SSteen Hegelund 	if (mt.mask->tip) {
5273a344f99SSteen Hegelund 		ipval = be32_to_cpu((__force __be32)mt.key->tip);
5283a344f99SSteen Hegelund 		ipmsk = be32_to_cpu((__force __be32)mt.mask->tip);
5293a344f99SSteen Hegelund 
5303a344f99SSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_DIP,
5313a344f99SSteen Hegelund 					    ipval, ipmsk);
5323a344f99SSteen Hegelund 		if (err)
5333a344f99SSteen Hegelund 			goto out;
5343a344f99SSteen Hegelund 	}
5353a344f99SSteen Hegelund 
5363a344f99SSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ARP);
5373a344f99SSteen Hegelund 
5384e9a6139SDan Carpenter 	return 0;
5393a344f99SSteen Hegelund 
5403a344f99SSteen Hegelund out:
5413a344f99SSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "arp parse error");
5423a344f99SSteen Hegelund 	return err;
5433a344f99SSteen Hegelund }
5443a344f99SSteen Hegelund 
5453a344f99SSteen Hegelund static int
546d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ip_usage(struct sparx5_tc_flower_parse_usage *st)
547d6c2964dSSteen Hegelund {
548d6c2964dSSteen Hegelund 	struct flow_match_ip mt;
549d6c2964dSSteen Hegelund 	int err = 0;
550d6c2964dSSteen Hegelund 
551d6c2964dSSteen Hegelund 	flow_rule_match_ip(st->frule, &mt);
552d6c2964dSSteen Hegelund 
553d6c2964dSSteen Hegelund 	if (mt.mask->tos) {
554d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_TOS,
555d6c2964dSSteen Hegelund 					    mt.key->tos,
556d6c2964dSSteen Hegelund 					    mt.mask->tos);
557d6c2964dSSteen Hegelund 		if (err)
558d6c2964dSSteen Hegelund 			goto out;
559d6c2964dSSteen Hegelund 	}
560d6c2964dSSteen Hegelund 
561d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IP);
562d6c2964dSSteen Hegelund 
563d6c2964dSSteen Hegelund 	return err;
564d6c2964dSSteen Hegelund 
565d6c2964dSSteen Hegelund out:
566d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_tos parse error");
567d6c2964dSSteen Hegelund 	return err;
568d6c2964dSSteen Hegelund }
569d6c2964dSSteen Hegelund 
570c9da1ac1SSteen Hegelund static int (*sparx5_tc_flower_usage_handlers[])(struct sparx5_tc_flower_parse_usage *st) = {
571c9da1ac1SSteen Hegelund 	[FLOW_DISSECTOR_KEY_ETH_ADDRS] = sparx5_tc_flower_handler_ethaddr_usage,
572d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_IPV4_ADDRS] = sparx5_tc_flower_handler_ipv4_usage,
573d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_IPV6_ADDRS] = sparx5_tc_flower_handler_ipv6_usage,
574d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_CONTROL] = sparx5_tc_flower_handler_control_usage,
575d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_PORTS] = sparx5_tc_flower_handler_portnum_usage,
576d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage,
57752df82ccSSteen Hegelund 	[FLOW_DISSECTOR_KEY_CVLAN] = sparx5_tc_flower_handler_cvlan_usage,
578d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage,
579d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_TCP] = sparx5_tc_flower_handler_tcp_usage,
5803a344f99SSteen Hegelund 	[FLOW_DISSECTOR_KEY_ARP] = sparx5_tc_flower_handler_arp_usage,
581d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_IP] = sparx5_tc_flower_handler_ip_usage,
582c9da1ac1SSteen Hegelund };
583c9da1ac1SSteen Hegelund 
584c9da1ac1SSteen Hegelund static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco,
585c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin,
586abc4010dSSteen Hegelund 				    struct vcap_rule *vrule,
587abc4010dSSteen Hegelund 				    u16 *l3_proto)
588c9da1ac1SSteen Hegelund {
589c9da1ac1SSteen Hegelund 	struct sparx5_tc_flower_parse_usage state = {
590c9da1ac1SSteen Hegelund 		.fco = fco,
591c9da1ac1SSteen Hegelund 		.vrule = vrule,
592abc4010dSSteen Hegelund 		.l3_proto = ETH_P_ALL,
593542e6e2cSSteen Hegelund 		.admin = admin,
594c9da1ac1SSteen Hegelund 	};
595c9da1ac1SSteen Hegelund 	int idx, err = 0;
596c9da1ac1SSteen Hegelund 
597c9da1ac1SSteen Hegelund 	state.frule = flow_cls_offload_flow_rule(fco);
598c9da1ac1SSteen Hegelund 	for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) {
599c9da1ac1SSteen Hegelund 		if (!flow_rule_match_key(state.frule, idx))
600c9da1ac1SSteen Hegelund 			continue;
601c9da1ac1SSteen Hegelund 		if (!sparx5_tc_flower_usage_handlers[idx])
602c9da1ac1SSteen Hegelund 			continue;
603c9da1ac1SSteen Hegelund 		err = sparx5_tc_flower_usage_handlers[idx](&state);
604c9da1ac1SSteen Hegelund 		if (err)
605c9da1ac1SSteen Hegelund 			return err;
606c9da1ac1SSteen Hegelund 	}
607abc4010dSSteen Hegelund 
608abc4010dSSteen Hegelund 	if (state.frule->match.dissector->used_keys ^ state.used_keys) {
609abc4010dSSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
610abc4010dSSteen Hegelund 				   "Unsupported match item");
611abc4010dSSteen Hegelund 		return -ENOENT;
612abc4010dSSteen Hegelund 	}
613abc4010dSSteen Hegelund 
614abc4010dSSteen Hegelund 	if (l3_proto)
615abc4010dSSteen Hegelund 		*l3_proto = state.l3_proto;
616c9da1ac1SSteen Hegelund 	return err;
617c9da1ac1SSteen Hegelund }
618c9da1ac1SSteen Hegelund 
619392d0ab0SSteen Hegelund static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
620784c3067SSteen Hegelund 					 struct net_device *ndev,
621784c3067SSteen Hegelund 					 struct flow_cls_offload *fco)
622392d0ab0SSteen Hegelund {
623392d0ab0SSteen Hegelund 	struct flow_rule *rule = flow_cls_offload_flow_rule(fco);
624392d0ab0SSteen Hegelund 	struct flow_action_entry *actent, *last_actent = NULL;
625392d0ab0SSteen Hegelund 	struct flow_action *act = &rule->action;
626392d0ab0SSteen Hegelund 	u64 action_mask = 0;
627392d0ab0SSteen Hegelund 	int idx;
628392d0ab0SSteen Hegelund 
629392d0ab0SSteen Hegelund 	if (!flow_action_has_entries(act)) {
630392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
631392d0ab0SSteen Hegelund 		return -EINVAL;
632392d0ab0SSteen Hegelund 	}
633392d0ab0SSteen Hegelund 
634392d0ab0SSteen Hegelund 	if (!flow_action_basic_hw_stats_check(act, fco->common.extack))
635392d0ab0SSteen Hegelund 		return -EOPNOTSUPP;
636392d0ab0SSteen Hegelund 
637392d0ab0SSteen Hegelund 	flow_action_for_each(idx, actent, act) {
638392d0ab0SSteen Hegelund 		if (action_mask & BIT(actent->id)) {
639392d0ab0SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
640392d0ab0SSteen Hegelund 					   "More actions of the same type");
641392d0ab0SSteen Hegelund 			return -EINVAL;
642392d0ab0SSteen Hegelund 		}
643392d0ab0SSteen Hegelund 		action_mask |= BIT(actent->id);
644392d0ab0SSteen Hegelund 		last_actent = actent; /* Save last action for later check */
645392d0ab0SSteen Hegelund 	}
646392d0ab0SSteen Hegelund 
647784c3067SSteen Hegelund 	/* Check if last action is a goto
648784c3067SSteen Hegelund 	 * The last chain/lookup does not need to have a goto action
649784c3067SSteen Hegelund 	 */
650784c3067SSteen Hegelund 	if (last_actent->id == FLOW_ACTION_GOTO) {
651784c3067SSteen Hegelund 		/* Check if the destination chain is in one of the VCAPs */
652392d0ab0SSteen Hegelund 		if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
653392d0ab0SSteen Hegelund 					 last_actent->chain_index)) {
654392d0ab0SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
655392d0ab0SSteen Hegelund 					   "Invalid goto chain");
656392d0ab0SSteen Hegelund 			return -EINVAL;
657392d0ab0SSteen Hegelund 		}
658784c3067SSteen Hegelund 	} else if (!vcap_is_last_chain(vctrl, fco->common.chain_index)) {
659784c3067SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
660784c3067SSteen Hegelund 				   "Last action must be 'goto'");
661784c3067SSteen Hegelund 		return -EINVAL;
662784c3067SSteen Hegelund 	}
663392d0ab0SSteen Hegelund 
664392d0ab0SSteen Hegelund 	/* Catch unsupported combinations of actions */
665392d0ab0SSteen Hegelund 	if (action_mask & BIT(FLOW_ACTION_TRAP) &&
666392d0ab0SSteen Hegelund 	    action_mask & BIT(FLOW_ACTION_ACCEPT)) {
667392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
668392d0ab0SSteen Hegelund 				   "Cannot combine pass and trap action");
669392d0ab0SSteen Hegelund 		return -EOPNOTSUPP;
670392d0ab0SSteen Hegelund 	}
671392d0ab0SSteen Hegelund 
672392d0ab0SSteen Hegelund 	return 0;
673392d0ab0SSteen Hegelund }
674392d0ab0SSteen Hegelund 
675542e6e2cSSteen Hegelund /* Add a rule counter action */
67640e7fe18SSteen Hegelund static int sparx5_tc_add_rule_counter(struct vcap_admin *admin,
67740e7fe18SSteen Hegelund 				      struct vcap_rule *vrule)
67840e7fe18SSteen Hegelund {
67940e7fe18SSteen Hegelund 	int err;
68040e7fe18SSteen Hegelund 
681542e6e2cSSteen Hegelund 	if (admin->vtype == VCAP_TYPE_IS2) {
682542e6e2cSSteen Hegelund 		err = vcap_rule_mod_action_u32(vrule, VCAP_AF_CNT_ID,
683542e6e2cSSteen Hegelund 					       vrule->id);
68440e7fe18SSteen Hegelund 		if (err)
68540e7fe18SSteen Hegelund 			return err;
68640e7fe18SSteen Hegelund 		vcap_rule_set_counter_id(vrule, vrule->id);
687542e6e2cSSteen Hegelund 	}
688542e6e2cSSteen Hegelund 
689542e6e2cSSteen Hegelund 	return 0;
69040e7fe18SSteen Hegelund }
69140e7fe18SSteen Hegelund 
6920ca60948SSteen Hegelund /* Collect all port keysets and apply the first of them, possibly wildcarded */
6930ca60948SSteen Hegelund static int sparx5_tc_select_protocol_keyset(struct net_device *ndev,
6940ca60948SSteen Hegelund 					    struct vcap_rule *vrule,
6950ca60948SSteen Hegelund 					    struct vcap_admin *admin,
6960ca60948SSteen Hegelund 					    u16 l3_proto,
6970ca60948SSteen Hegelund 					    struct sparx5_multiple_rules *multi)
6980ca60948SSteen Hegelund {
6990ca60948SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
7000ca60948SSteen Hegelund 	struct vcap_keyset_list portkeysetlist = {};
7010ca60948SSteen Hegelund 	enum vcap_keyfield_set portkeysets[10] = {};
7020ca60948SSteen Hegelund 	struct vcap_keyset_list matches = {};
7030ca60948SSteen Hegelund 	enum vcap_keyfield_set keysets[10];
7040ca60948SSteen Hegelund 	int idx, jdx, err = 0, count = 0;
7050ca60948SSteen Hegelund 	struct sparx5_wildcard_rule *mru;
7060ca60948SSteen Hegelund 	const struct vcap_set *kinfo;
7070ca60948SSteen Hegelund 	struct vcap_control *vctrl;
7080ca60948SSteen Hegelund 
7090ca60948SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
7100ca60948SSteen Hegelund 
7110ca60948SSteen Hegelund 	/* Find the keysets that the rule can use */
7120ca60948SSteen Hegelund 	matches.keysets = keysets;
7130ca60948SSteen Hegelund 	matches.max = ARRAY_SIZE(keysets);
7140ca60948SSteen Hegelund 	if (vcap_rule_find_keysets(vrule, &matches) == 0)
7150ca60948SSteen Hegelund 		return -EINVAL;
7160ca60948SSteen Hegelund 
7170ca60948SSteen Hegelund 	/* Find the keysets that the port configuration supports */
7180ca60948SSteen Hegelund 	portkeysetlist.max = ARRAY_SIZE(portkeysets);
7190ca60948SSteen Hegelund 	portkeysetlist.keysets = portkeysets;
7200ca60948SSteen Hegelund 	err = sparx5_vcap_get_port_keyset(ndev,
7210ca60948SSteen Hegelund 					  admin, vrule->vcap_chain_id,
7220ca60948SSteen Hegelund 					  l3_proto,
7230ca60948SSteen Hegelund 					  &portkeysetlist);
7240ca60948SSteen Hegelund 	if (err)
7250ca60948SSteen Hegelund 		return err;
7260ca60948SSteen Hegelund 
7270ca60948SSteen Hegelund 	/* Find the intersection of the two sets of keyset */
7280ca60948SSteen Hegelund 	for (idx = 0; idx < portkeysetlist.cnt; ++idx) {
7290ca60948SSteen Hegelund 		kinfo = vcap_keyfieldset(vctrl, admin->vtype,
7300ca60948SSteen Hegelund 					 portkeysetlist.keysets[idx]);
7310ca60948SSteen Hegelund 		if (!kinfo)
7320ca60948SSteen Hegelund 			continue;
7330ca60948SSteen Hegelund 
7340ca60948SSteen Hegelund 		/* Find a port keyset that matches the required keys
7350ca60948SSteen Hegelund 		 * If there are multiple keysets then compose a type id mask
7360ca60948SSteen Hegelund 		 */
7370ca60948SSteen Hegelund 		for (jdx = 0; jdx < matches.cnt; ++jdx) {
7380ca60948SSteen Hegelund 			if (portkeysetlist.keysets[idx] != matches.keysets[jdx])
7390ca60948SSteen Hegelund 				continue;
7400ca60948SSteen Hegelund 
7410ca60948SSteen Hegelund 			mru = &multi->rule[kinfo->sw_per_item];
7420ca60948SSteen Hegelund 			if (!mru->selected) {
7430ca60948SSteen Hegelund 				mru->selected = true;
7440ca60948SSteen Hegelund 				mru->keyset = portkeysetlist.keysets[idx];
7450ca60948SSteen Hegelund 				mru->value = kinfo->type_id;
7460ca60948SSteen Hegelund 			}
7470ca60948SSteen Hegelund 			mru->value &= kinfo->type_id;
7480ca60948SSteen Hegelund 			mru->mask |= kinfo->type_id;
7490ca60948SSteen Hegelund 			++count;
7500ca60948SSteen Hegelund 		}
7510ca60948SSteen Hegelund 	}
7520ca60948SSteen Hegelund 	if (count == 0)
7530ca60948SSteen Hegelund 		return -EPROTO;
7540ca60948SSteen Hegelund 
7550ca60948SSteen Hegelund 	if (l3_proto == ETH_P_ALL && count < portkeysetlist.cnt)
7560ca60948SSteen Hegelund 		return -ENOENT;
7570ca60948SSteen Hegelund 
7580ca60948SSteen Hegelund 	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
7590ca60948SSteen Hegelund 		mru = &multi->rule[idx];
7600ca60948SSteen Hegelund 		if (!mru->selected)
7610ca60948SSteen Hegelund 			continue;
7620ca60948SSteen Hegelund 
7630ca60948SSteen Hegelund 		/* Align the mask to the combined value */
7640ca60948SSteen Hegelund 		mru->mask ^= mru->value;
7650ca60948SSteen Hegelund 	}
7660ca60948SSteen Hegelund 
7670ca60948SSteen Hegelund 	/* Set the chosen keyset on the rule and set a wildcarded type if there
7680ca60948SSteen Hegelund 	 * are more than one keyset
7690ca60948SSteen Hegelund 	 */
7700ca60948SSteen Hegelund 	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
7710ca60948SSteen Hegelund 		mru = &multi->rule[idx];
7720ca60948SSteen Hegelund 		if (!mru->selected)
7730ca60948SSteen Hegelund 			continue;
7740ca60948SSteen Hegelund 
7750ca60948SSteen Hegelund 		vcap_set_rule_set_keyset(vrule, mru->keyset);
7760ca60948SSteen Hegelund 		if (count > 1)
7770ca60948SSteen Hegelund 			/* Some keysets do not have a type field */
7780ca60948SSteen Hegelund 			vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE,
7790ca60948SSteen Hegelund 					      mru->value,
7800ca60948SSteen Hegelund 					      ~mru->mask);
7810ca60948SSteen Hegelund 		mru->selected = false; /* mark as done */
7820ca60948SSteen Hegelund 		break; /* Stop here and add more rules later */
7830ca60948SSteen Hegelund 	}
7840ca60948SSteen Hegelund 	return err;
7850ca60948SSteen Hegelund }
7860ca60948SSteen Hegelund 
7870ca60948SSteen Hegelund static int sparx5_tc_add_rule_copy(struct vcap_control *vctrl,
7880ca60948SSteen Hegelund 				   struct flow_cls_offload *fco,
7890ca60948SSteen Hegelund 				   struct vcap_rule *erule,
7900ca60948SSteen Hegelund 				   struct vcap_admin *admin,
7910ca60948SSteen Hegelund 				   struct sparx5_wildcard_rule *rule)
7920ca60948SSteen Hegelund {
7930ca60948SSteen Hegelund 	enum vcap_key_field keylist[] = {
7940ca60948SSteen Hegelund 		VCAP_KF_IF_IGR_PORT_MASK,
7950ca60948SSteen Hegelund 		VCAP_KF_IF_IGR_PORT_MASK_SEL,
7960ca60948SSteen Hegelund 		VCAP_KF_IF_IGR_PORT_MASK_RNG,
7970ca60948SSteen Hegelund 		VCAP_KF_LOOKUP_FIRST_IS,
7980ca60948SSteen Hegelund 		VCAP_KF_TYPE,
7990ca60948SSteen Hegelund 	};
8000ca60948SSteen Hegelund 	struct vcap_rule *vrule;
8010ca60948SSteen Hegelund 	int err;
8020ca60948SSteen Hegelund 
8030ca60948SSteen Hegelund 	/* Add an extra rule with a special user and the new keyset */
8040ca60948SSteen Hegelund 	erule->user = VCAP_USER_TC_EXTRA;
8050ca60948SSteen Hegelund 	vrule = vcap_copy_rule(erule);
8060ca60948SSteen Hegelund 	if (IS_ERR(vrule))
8070ca60948SSteen Hegelund 		return PTR_ERR(vrule);
8080ca60948SSteen Hegelund 
8090ca60948SSteen Hegelund 	/* Link the new rule to the existing rule with the cookie */
8100ca60948SSteen Hegelund 	vrule->cookie = erule->cookie;
8110ca60948SSteen Hegelund 	vcap_filter_rule_keys(vrule, keylist, ARRAY_SIZE(keylist), true);
8120ca60948SSteen Hegelund 	err = vcap_set_rule_set_keyset(vrule, rule->keyset);
8130ca60948SSteen Hegelund 	if (err) {
8140ca60948SSteen Hegelund 		pr_err("%s:%d: could not set keyset %s in rule: %u\n",
8150ca60948SSteen Hegelund 		       __func__, __LINE__,
8160ca60948SSteen Hegelund 		       vcap_keyset_name(vctrl, rule->keyset),
8170ca60948SSteen Hegelund 		       vrule->id);
8180ca60948SSteen Hegelund 		goto out;
8190ca60948SSteen Hegelund 	}
8200ca60948SSteen Hegelund 
8210ca60948SSteen Hegelund 	/* Some keysets do not have a type field, so ignore return value */
8220ca60948SSteen Hegelund 	vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE, rule->value, ~rule->mask);
8230ca60948SSteen Hegelund 
8240ca60948SSteen Hegelund 	err = vcap_set_rule_set_actionset(vrule, erule->actionset);
8250ca60948SSteen Hegelund 	if (err)
8260ca60948SSteen Hegelund 		goto out;
8270ca60948SSteen Hegelund 
8280ca60948SSteen Hegelund 	err = sparx5_tc_add_rule_counter(admin, vrule);
8290ca60948SSteen Hegelund 	if (err)
8300ca60948SSteen Hegelund 		goto out;
8310ca60948SSteen Hegelund 
8320ca60948SSteen Hegelund 	err = vcap_val_rule(vrule, ETH_P_ALL);
8330ca60948SSteen Hegelund 	if (err) {
8340ca60948SSteen Hegelund 		pr_err("%s:%d: could not validate rule: %u\n",
8350ca60948SSteen Hegelund 		       __func__, __LINE__, vrule->id);
8360ca60948SSteen Hegelund 		vcap_set_tc_exterr(fco, vrule);
8370ca60948SSteen Hegelund 		goto out;
8380ca60948SSteen Hegelund 	}
8390ca60948SSteen Hegelund 	err = vcap_add_rule(vrule);
8400ca60948SSteen Hegelund 	if (err) {
8410ca60948SSteen Hegelund 		pr_err("%s:%d: could not add rule: %u\n",
8420ca60948SSteen Hegelund 		       __func__, __LINE__, vrule->id);
8430ca60948SSteen Hegelund 		goto out;
8440ca60948SSteen Hegelund 	}
8450ca60948SSteen Hegelund out:
8460ca60948SSteen Hegelund 	vcap_free_rule(vrule);
8470ca60948SSteen Hegelund 	return err;
8480ca60948SSteen Hegelund }
8490ca60948SSteen Hegelund 
8500ca60948SSteen Hegelund static int sparx5_tc_add_remaining_rules(struct vcap_control *vctrl,
8510ca60948SSteen Hegelund 					 struct flow_cls_offload *fco,
8520ca60948SSteen Hegelund 					 struct vcap_rule *erule,
8530ca60948SSteen Hegelund 					 struct vcap_admin *admin,
8540ca60948SSteen Hegelund 					 struct sparx5_multiple_rules *multi)
8550ca60948SSteen Hegelund {
8560ca60948SSteen Hegelund 	int idx, err = 0;
8570ca60948SSteen Hegelund 
8580ca60948SSteen Hegelund 	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
8590ca60948SSteen Hegelund 		if (!multi->rule[idx].selected)
8600ca60948SSteen Hegelund 			continue;
8610ca60948SSteen Hegelund 
8620ca60948SSteen Hegelund 		err = sparx5_tc_add_rule_copy(vctrl, fco, erule, admin,
8630ca60948SSteen Hegelund 					      &multi->rule[idx]);
8640ca60948SSteen Hegelund 		if (err)
8650ca60948SSteen Hegelund 			break;
8660ca60948SSteen Hegelund 	}
8670ca60948SSteen Hegelund 	return err;
8680ca60948SSteen Hegelund }
8690ca60948SSteen Hegelund 
870542e6e2cSSteen Hegelund /* Add the actionset that is the default for the VCAP type */
871542e6e2cSSteen Hegelund static int sparx5_tc_set_actionset(struct vcap_admin *admin,
872542e6e2cSSteen Hegelund 				   struct vcap_rule *vrule)
873542e6e2cSSteen Hegelund {
874542e6e2cSSteen Hegelund 	enum vcap_actionfield_set aset;
875542e6e2cSSteen Hegelund 	int err = 0;
876542e6e2cSSteen Hegelund 
877542e6e2cSSteen Hegelund 	switch (admin->vtype) {
878542e6e2cSSteen Hegelund 	case VCAP_TYPE_IS0:
879542e6e2cSSteen Hegelund 		aset = VCAP_AFS_CLASSIFICATION;
880542e6e2cSSteen Hegelund 		break;
881542e6e2cSSteen Hegelund 	case VCAP_TYPE_IS2:
882542e6e2cSSteen Hegelund 		aset = VCAP_AFS_BASE_TYPE;
883542e6e2cSSteen Hegelund 		break;
884542e6e2cSSteen Hegelund 	default:
885542e6e2cSSteen Hegelund 		return -EINVAL;
886542e6e2cSSteen Hegelund 	}
887542e6e2cSSteen Hegelund 	/* Do not overwrite any current actionset */
888542e6e2cSSteen Hegelund 	if (vrule->actionset == VCAP_AFS_NO_VALUE)
889542e6e2cSSteen Hegelund 		err = vcap_set_rule_set_actionset(vrule, aset);
890542e6e2cSSteen Hegelund 	return err;
891542e6e2cSSteen Hegelund }
892542e6e2cSSteen Hegelund 
89388bd9ea7SSteen Hegelund /* Add the VCAP key to match on for a rule target value */
89488bd9ea7SSteen Hegelund static int sparx5_tc_add_rule_link_target(struct vcap_admin *admin,
89588bd9ea7SSteen Hegelund 					  struct vcap_rule *vrule,
89688bd9ea7SSteen Hegelund 					  int target_cid)
89788bd9ea7SSteen Hegelund {
89888bd9ea7SSteen Hegelund 	int link_val = target_cid % VCAP_CID_LOOKUP_SIZE;
89988bd9ea7SSteen Hegelund 	int err;
90088bd9ea7SSteen Hegelund 
90188bd9ea7SSteen Hegelund 	if (!link_val)
90288bd9ea7SSteen Hegelund 		return 0;
90388bd9ea7SSteen Hegelund 
90488bd9ea7SSteen Hegelund 	switch (admin->vtype) {
90588bd9ea7SSteen Hegelund 	case VCAP_TYPE_IS0:
90688bd9ea7SSteen Hegelund 		/* Add NXT_IDX key for chaining rules between IS0 instances */
90788bd9ea7SSteen Hegelund 		err = vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX_SEL,
90888bd9ea7SSteen Hegelund 					    1, /* enable */
90988bd9ea7SSteen Hegelund 					    ~0);
91088bd9ea7SSteen Hegelund 		if (err)
91188bd9ea7SSteen Hegelund 			return err;
91288bd9ea7SSteen Hegelund 		return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX,
91388bd9ea7SSteen Hegelund 					     link_val, /* target */
91488bd9ea7SSteen Hegelund 					     ~0);
91588bd9ea7SSteen Hegelund 	case VCAP_TYPE_IS2:
91688bd9ea7SSteen Hegelund 		/* Add PAG key for chaining rules from IS0 */
91788bd9ea7SSteen Hegelund 		return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_PAG,
91888bd9ea7SSteen Hegelund 					     link_val, /* target */
91988bd9ea7SSteen Hegelund 					     ~0);
92088bd9ea7SSteen Hegelund 	default:
92188bd9ea7SSteen Hegelund 		break;
92288bd9ea7SSteen Hegelund 	}
92388bd9ea7SSteen Hegelund 	return 0;
92488bd9ea7SSteen Hegelund }
92588bd9ea7SSteen Hegelund 
92688bd9ea7SSteen Hegelund /* Add the VCAP action that adds a target value to a rule */
92788bd9ea7SSteen Hegelund static int sparx5_tc_add_rule_link(struct vcap_control *vctrl,
92888bd9ea7SSteen Hegelund 				   struct vcap_admin *admin,
92988bd9ea7SSteen Hegelund 				   struct vcap_rule *vrule,
93088bd9ea7SSteen Hegelund 				   int from_cid, int to_cid)
93188bd9ea7SSteen Hegelund {
93288bd9ea7SSteen Hegelund 	struct vcap_admin *to_admin = vcap_find_admin(vctrl, to_cid);
93388bd9ea7SSteen Hegelund 	int diff, err = 0;
93488bd9ea7SSteen Hegelund 
93588bd9ea7SSteen Hegelund 	diff = vcap_chain_offset(vctrl, from_cid, to_cid);
93688bd9ea7SSteen Hegelund 	if (!(to_admin && diff > 0)) {
93788bd9ea7SSteen Hegelund 		pr_err("%s:%d: unsupported chain direction: %d\n",
93888bd9ea7SSteen Hegelund 		       __func__, __LINE__, to_cid);
93988bd9ea7SSteen Hegelund 		return -EINVAL;
94088bd9ea7SSteen Hegelund 	}
94188bd9ea7SSteen Hegelund 	if (admin->vtype == VCAP_TYPE_IS0 &&
94288bd9ea7SSteen Hegelund 	    to_admin->vtype == VCAP_TYPE_IS0) {
94388bd9ea7SSteen Hegelund 		/* Between IS0 instances the G_IDX value is used */
94488bd9ea7SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule, VCAP_AF_NXT_IDX, diff);
94588bd9ea7SSteen Hegelund 		if (err)
94688bd9ea7SSteen Hegelund 			goto out;
94788bd9ea7SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule, VCAP_AF_NXT_IDX_CTRL,
94888bd9ea7SSteen Hegelund 					       1); /* Replace */
94988bd9ea7SSteen Hegelund 		if (err)
95088bd9ea7SSteen Hegelund 			goto out;
95188bd9ea7SSteen Hegelund 	} else if (admin->vtype == VCAP_TYPE_IS0 &&
95288bd9ea7SSteen Hegelund 		   to_admin->vtype == VCAP_TYPE_IS2) {
95388bd9ea7SSteen Hegelund 		/* Between IS0 and IS2 the PAG value is used */
95488bd9ea7SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule, VCAP_AF_PAG_VAL, diff);
95588bd9ea7SSteen Hegelund 		if (err)
95688bd9ea7SSteen Hegelund 			goto out;
95788bd9ea7SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
95888bd9ea7SSteen Hegelund 					       VCAP_AF_PAG_OVERRIDE_MASK,
95988bd9ea7SSteen Hegelund 					       0xff);
96088bd9ea7SSteen Hegelund 		if (err)
96188bd9ea7SSteen Hegelund 			goto out;
96288bd9ea7SSteen Hegelund 	} else {
96388bd9ea7SSteen Hegelund 		pr_err("%s:%d: unsupported chain destination: %d\n",
96488bd9ea7SSteen Hegelund 		       __func__, __LINE__, to_cid);
96588bd9ea7SSteen Hegelund 		err = -EOPNOTSUPP;
96688bd9ea7SSteen Hegelund 	}
96788bd9ea7SSteen Hegelund out:
96888bd9ea7SSteen Hegelund 	return err;
96988bd9ea7SSteen Hegelund }
97088bd9ea7SSteen Hegelund 
971c9da1ac1SSteen Hegelund static int sparx5_tc_flower_replace(struct net_device *ndev,
972c9da1ac1SSteen Hegelund 				    struct flow_cls_offload *fco,
973c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin)
974c9da1ac1SSteen Hegelund {
975c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
9760ca60948SSteen Hegelund 	struct sparx5_multiple_rules multi = {};
977c9da1ac1SSteen Hegelund 	struct flow_action_entry *act;
978c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
979c9da1ac1SSteen Hegelund 	struct flow_rule *frule;
980c9da1ac1SSteen Hegelund 	struct vcap_rule *vrule;
981abc4010dSSteen Hegelund 	u16 l3_proto;
982c9da1ac1SSteen Hegelund 	int err, idx;
983c9da1ac1SSteen Hegelund 
984c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
985392d0ab0SSteen Hegelund 
986784c3067SSteen Hegelund 	err = sparx5_tc_flower_action_check(vctrl, ndev, fco);
987392d0ab0SSteen Hegelund 	if (err)
988392d0ab0SSteen Hegelund 		return err;
989392d0ab0SSteen Hegelund 
990c9da1ac1SSteen Hegelund 	vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC,
991c9da1ac1SSteen Hegelund 				fco->common.prio, 0);
992c9da1ac1SSteen Hegelund 	if (IS_ERR(vrule))
993c9da1ac1SSteen Hegelund 		return PTR_ERR(vrule);
994c9da1ac1SSteen Hegelund 
995c9da1ac1SSteen Hegelund 	vrule->cookie = fco->cookie;
996bcddc196SSteen Hegelund 
997bcddc196SSteen Hegelund 	l3_proto = ETH_P_ALL;
998bcddc196SSteen Hegelund 	err = sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto);
999bcddc196SSteen Hegelund 	if (err)
1000bcddc196SSteen Hegelund 		goto out;
100140e7fe18SSteen Hegelund 
100240e7fe18SSteen Hegelund 	err = sparx5_tc_add_rule_counter(admin, vrule);
100340e7fe18SSteen Hegelund 	if (err)
100440e7fe18SSteen Hegelund 		goto out;
100540e7fe18SSteen Hegelund 
100688bd9ea7SSteen Hegelund 	err = sparx5_tc_add_rule_link_target(admin, vrule,
100788bd9ea7SSteen Hegelund 					     fco->common.chain_index);
100888bd9ea7SSteen Hegelund 	if (err)
100988bd9ea7SSteen Hegelund 		goto out;
101088bd9ea7SSteen Hegelund 
1011392d0ab0SSteen Hegelund 	frule = flow_cls_offload_flow_rule(fco);
1012c9da1ac1SSteen Hegelund 	flow_action_for_each(idx, act, &frule->action) {
1013c9da1ac1SSteen Hegelund 		switch (act->id) {
1014c9da1ac1SSteen Hegelund 		case FLOW_ACTION_TRAP:
101588bd9ea7SSteen Hegelund 			if (admin->vtype != VCAP_TYPE_IS2) {
101688bd9ea7SSteen Hegelund 				NL_SET_ERR_MSG_MOD(fco->common.extack,
101788bd9ea7SSteen Hegelund 						   "Trap action not supported in this VCAP");
101888bd9ea7SSteen Hegelund 				err = -EOPNOTSUPP;
101988bd9ea7SSteen Hegelund 				goto out;
102088bd9ea7SSteen Hegelund 			}
1021c9da1ac1SSteen Hegelund 			err = vcap_rule_add_action_bit(vrule,
1022c9da1ac1SSteen Hegelund 						       VCAP_AF_CPU_COPY_ENA,
1023c9da1ac1SSteen Hegelund 						       VCAP_BIT_1);
1024c9da1ac1SSteen Hegelund 			if (err)
1025c9da1ac1SSteen Hegelund 				goto out;
1026c9da1ac1SSteen Hegelund 			err = vcap_rule_add_action_u32(vrule,
1027c9da1ac1SSteen Hegelund 						       VCAP_AF_CPU_QUEUE_NUM, 0);
1028c9da1ac1SSteen Hegelund 			if (err)
1029c9da1ac1SSteen Hegelund 				goto out;
1030c9da1ac1SSteen Hegelund 			err = vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,
1031c9da1ac1SSteen Hegelund 						       SPX5_PMM_REPLACE_ALL);
1032c9da1ac1SSteen Hegelund 			if (err)
1033c9da1ac1SSteen Hegelund 				goto out;
1034c9da1ac1SSteen Hegelund 			break;
1035c9da1ac1SSteen Hegelund 		case FLOW_ACTION_ACCEPT:
1036542e6e2cSSteen Hegelund 			err = sparx5_tc_set_actionset(admin, vrule);
1037c9da1ac1SSteen Hegelund 			if (err)
1038c9da1ac1SSteen Hegelund 				goto out;
1039c9da1ac1SSteen Hegelund 			break;
1040392d0ab0SSteen Hegelund 		case FLOW_ACTION_GOTO:
1041542e6e2cSSteen Hegelund 			err = sparx5_tc_set_actionset(admin, vrule);
1042542e6e2cSSteen Hegelund 			if (err)
1043542e6e2cSSteen Hegelund 				goto out;
104488bd9ea7SSteen Hegelund 			sparx5_tc_add_rule_link(vctrl, admin, vrule,
104588bd9ea7SSteen Hegelund 						fco->common.chain_index,
104688bd9ea7SSteen Hegelund 						act->chain_index);
1047392d0ab0SSteen Hegelund 			break;
1048c9da1ac1SSteen Hegelund 		default:
1049c9da1ac1SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
1050c9da1ac1SSteen Hegelund 					   "Unsupported TC action");
1051c9da1ac1SSteen Hegelund 			err = -EOPNOTSUPP;
1052c9da1ac1SSteen Hegelund 			goto out;
1053c9da1ac1SSteen Hegelund 		}
1054c9da1ac1SSteen Hegelund 	}
10550ca60948SSteen Hegelund 
10560ca60948SSteen Hegelund 	err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin, l3_proto,
10570ca60948SSteen Hegelund 					       &multi);
10580ca60948SSteen Hegelund 	if (err) {
10590ca60948SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
10600ca60948SSteen Hegelund 				   "No matching port keyset for filter protocol and keys");
10610ca60948SSteen Hegelund 		goto out;
10620ca60948SSteen Hegelund 	}
10630ca60948SSteen Hegelund 
1064abc4010dSSteen Hegelund 	/* provide the l3 protocol to guide the keyset selection */
1065abc4010dSSteen Hegelund 	err = vcap_val_rule(vrule, l3_proto);
1066c9da1ac1SSteen Hegelund 	if (err) {
1067c9da1ac1SSteen Hegelund 		vcap_set_tc_exterr(fco, vrule);
1068c9da1ac1SSteen Hegelund 		goto out;
1069c9da1ac1SSteen Hegelund 	}
1070c9da1ac1SSteen Hegelund 	err = vcap_add_rule(vrule);
1071c9da1ac1SSteen Hegelund 	if (err)
1072c9da1ac1SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
1073c9da1ac1SSteen Hegelund 				   "Could not add the filter");
10740ca60948SSteen Hegelund 
10750ca60948SSteen Hegelund 	if (l3_proto == ETH_P_ALL)
10760ca60948SSteen Hegelund 		err = sparx5_tc_add_remaining_rules(vctrl, fco, vrule, admin,
10770ca60948SSteen Hegelund 						    &multi);
10780ca60948SSteen Hegelund 
1079c9da1ac1SSteen Hegelund out:
1080c9da1ac1SSteen Hegelund 	vcap_free_rule(vrule);
1081c9da1ac1SSteen Hegelund 	return err;
1082c9da1ac1SSteen Hegelund }
1083c9da1ac1SSteen Hegelund 
1084c9da1ac1SSteen Hegelund static int sparx5_tc_flower_destroy(struct net_device *ndev,
1085c9da1ac1SSteen Hegelund 				    struct flow_cls_offload *fco,
1086c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin)
1087c9da1ac1SSteen Hegelund {
1088c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
1089c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
1090c9da1ac1SSteen Hegelund 	int err = -ENOENT, rule_id;
1091c9da1ac1SSteen Hegelund 
1092c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
1093c9da1ac1SSteen Hegelund 	while (true) {
1094c9da1ac1SSteen Hegelund 		rule_id = vcap_lookup_rule_by_cookie(vctrl, fco->cookie);
1095c9da1ac1SSteen Hegelund 		if (rule_id <= 0)
1096c9da1ac1SSteen Hegelund 			break;
1097c9da1ac1SSteen Hegelund 		err = vcap_del_rule(vctrl, ndev, rule_id);
1098c9da1ac1SSteen Hegelund 		if (err) {
1099c9da1ac1SSteen Hegelund 			pr_err("%s:%d: could not delete rule %d\n",
1100c9da1ac1SSteen Hegelund 			       __func__, __LINE__, rule_id);
1101c9da1ac1SSteen Hegelund 			break;
1102c9da1ac1SSteen Hegelund 		}
1103c9da1ac1SSteen Hegelund 	}
1104c9da1ac1SSteen Hegelund 	return err;
1105c9da1ac1SSteen Hegelund }
1106c9da1ac1SSteen Hegelund 
110740e7fe18SSteen Hegelund static int sparx5_tc_flower_stats(struct net_device *ndev,
110840e7fe18SSteen Hegelund 				  struct flow_cls_offload *fco,
110940e7fe18SSteen Hegelund 				  struct vcap_admin *admin)
111040e7fe18SSteen Hegelund {
111140e7fe18SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
111227d293ccSSteen Hegelund 	struct vcap_counter ctr = {};
111340e7fe18SSteen Hegelund 	struct vcap_control *vctrl;
111440e7fe18SSteen Hegelund 	ulong lastused = 0;
111540e7fe18SSteen Hegelund 	int err;
111640e7fe18SSteen Hegelund 
111740e7fe18SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
111827d293ccSSteen Hegelund 	err = vcap_get_rule_count_by_cookie(vctrl, &ctr, fco->cookie);
111940e7fe18SSteen Hegelund 	if (err)
112040e7fe18SSteen Hegelund 		return err;
112127d293ccSSteen Hegelund 	flow_stats_update(&fco->stats, 0x0, ctr.value, 0, lastused,
112240e7fe18SSteen Hegelund 			  FLOW_ACTION_HW_STATS_IMMEDIATE);
112340e7fe18SSteen Hegelund 	return err;
112440e7fe18SSteen Hegelund }
112540e7fe18SSteen Hegelund 
1126c9da1ac1SSteen Hegelund int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco,
1127c9da1ac1SSteen Hegelund 		     bool ingress)
1128c9da1ac1SSteen Hegelund {
1129c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
1130c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
1131c9da1ac1SSteen Hegelund 	struct vcap_admin *admin;
1132c9da1ac1SSteen Hegelund 	int err = -EINVAL;
1133c9da1ac1SSteen Hegelund 
1134c9da1ac1SSteen Hegelund 	/* Get vcap instance from the chain id */
1135c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
1136c9da1ac1SSteen Hegelund 	admin = vcap_find_admin(vctrl, fco->common.chain_index);
1137c9da1ac1SSteen Hegelund 	if (!admin) {
1138c9da1ac1SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack, "Invalid chain");
1139c9da1ac1SSteen Hegelund 		return err;
1140c9da1ac1SSteen Hegelund 	}
1141c9da1ac1SSteen Hegelund 
1142c9da1ac1SSteen Hegelund 	switch (fco->command) {
1143c9da1ac1SSteen Hegelund 	case FLOW_CLS_REPLACE:
1144c9da1ac1SSteen Hegelund 		return sparx5_tc_flower_replace(ndev, fco, admin);
1145c9da1ac1SSteen Hegelund 	case FLOW_CLS_DESTROY:
1146c9da1ac1SSteen Hegelund 		return sparx5_tc_flower_destroy(ndev, fco, admin);
114740e7fe18SSteen Hegelund 	case FLOW_CLS_STATS:
114840e7fe18SSteen Hegelund 		return sparx5_tc_flower_stats(ndev, fco, admin);
1149c9da1ac1SSteen Hegelund 	default:
1150c9da1ac1SSteen Hegelund 		return -EOPNOTSUPP;
1151c9da1ac1SSteen Hegelund 	}
1152c9da1ac1SSteen Hegelund }
1153