1c9da1ac1SSteen Hegelund // SPDX-License-Identifier: GPL-2.0+
2c9da1ac1SSteen Hegelund /* Microchip VCAP API
3c9da1ac1SSteen Hegelund  *
4c9da1ac1SSteen Hegelund  * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
5c9da1ac1SSteen Hegelund  */
6c9da1ac1SSteen Hegelund 
76ebf182bSDaniel Machon #include <net/tc_act/tc_gate.h>
8c9da1ac1SSteen Hegelund #include <net/tcp.h>
9c9da1ac1SSteen Hegelund 
10c9da1ac1SSteen Hegelund #include "sparx5_tc.h"
11c9da1ac1SSteen Hegelund #include "vcap_api.h"
12c9da1ac1SSteen Hegelund #include "vcap_api_client.h"
1347400aaeSHoratiu Vultur #include "vcap_tc.h"
14c9da1ac1SSteen Hegelund #include "sparx5_main.h"
15c9da1ac1SSteen Hegelund #include "sparx5_vcap_impl.h"
16c9da1ac1SSteen Hegelund 
170ca60948SSteen Hegelund #define SPX5_MAX_RULE_SIZE 13 /* allows X1, X2, X4, X6 and X12 rules */
180ca60948SSteen Hegelund 
190ca60948SSteen Hegelund /* Collect keysets and type ids for multiple rules per size */
200ca60948SSteen Hegelund struct sparx5_wildcard_rule {
210ca60948SSteen Hegelund 	bool selected;
220ca60948SSteen Hegelund 	u8 value;
230ca60948SSteen Hegelund 	u8 mask;
240ca60948SSteen Hegelund 	enum vcap_keyfield_set keyset;
250ca60948SSteen Hegelund };
260ca60948SSteen Hegelund 
270ca60948SSteen Hegelund struct sparx5_multiple_rules {
280ca60948SSteen Hegelund 	struct sparx5_wildcard_rule rule[SPX5_MAX_RULE_SIZE];
290ca60948SSteen Hegelund };
300ca60948SSteen Hegelund 
31d6c2964dSSteen Hegelund static int
3247400aaeSHoratiu Vultur sparx5_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st)
33d6c2964dSSteen Hegelund {
3447400aaeSHoratiu Vultur 	struct flow_match_basic mt;
35d6c2964dSSteen Hegelund 	int err = 0;
36d6c2964dSSteen Hegelund 
3747400aaeSHoratiu Vultur 	flow_rule_match_basic(st->frule, &mt);
38d6c2964dSSteen Hegelund 
3947400aaeSHoratiu Vultur 	if (mt.mask->n_proto) {
4047400aaeSHoratiu Vultur 		st->l3_proto = be16_to_cpu(mt.key->n_proto);
4147400aaeSHoratiu Vultur 		if (!sparx5_vcap_is_known_etype(st->admin, st->l3_proto)) {
4247400aaeSHoratiu Vultur 			err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,
4347400aaeSHoratiu Vultur 						    st->l3_proto, ~0);
4447400aaeSHoratiu Vultur 			if (err)
4547400aaeSHoratiu Vultur 				goto out;
4647400aaeSHoratiu Vultur 		} else if (st->l3_proto == ETH_P_IP) {
4747400aaeSHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
4847400aaeSHoratiu Vultur 						    VCAP_BIT_1);
4947400aaeSHoratiu Vultur 			if (err)
5047400aaeSHoratiu Vultur 				goto out;
5147400aaeSHoratiu Vultur 		} else if (st->l3_proto == ETH_P_IPV6) {
5247400aaeSHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
5347400aaeSHoratiu Vultur 						    VCAP_BIT_0);
5447400aaeSHoratiu Vultur 			if (err)
5547400aaeSHoratiu Vultur 				goto out;
5647400aaeSHoratiu Vultur 			if (st->admin->vtype == VCAP_TYPE_IS0) {
5747400aaeSHoratiu Vultur 				err = vcap_rule_add_key_bit(st->vrule,
5847400aaeSHoratiu Vultur 							    VCAP_KF_IP_SNAP_IS,
5947400aaeSHoratiu Vultur 							    VCAP_BIT_1);
6047400aaeSHoratiu Vultur 				if (err)
6147400aaeSHoratiu Vultur 					goto out;
6247400aaeSHoratiu Vultur 			}
6347400aaeSHoratiu Vultur 		}
6447400aaeSHoratiu Vultur 	}
6547400aaeSHoratiu Vultur 
6647400aaeSHoratiu Vultur 	if (mt.mask->ip_proto) {
6747400aaeSHoratiu Vultur 		st->l4_proto = mt.key->ip_proto;
6847400aaeSHoratiu Vultur 		if (st->l4_proto == IPPROTO_TCP) {
6947400aaeSHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule,
7047400aaeSHoratiu Vultur 						    VCAP_KF_TCP_IS,
7147400aaeSHoratiu Vultur 						    VCAP_BIT_1);
7247400aaeSHoratiu Vultur 			if (err)
7347400aaeSHoratiu Vultur 				goto out;
7447400aaeSHoratiu Vultur 		} else if (st->l4_proto == IPPROTO_UDP) {
7547400aaeSHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule,
7647400aaeSHoratiu Vultur 						    VCAP_KF_TCP_IS,
7747400aaeSHoratiu Vultur 						    VCAP_BIT_0);
7847400aaeSHoratiu Vultur 			if (err)
7947400aaeSHoratiu Vultur 				goto out;
8047400aaeSHoratiu Vultur 			if (st->admin->vtype == VCAP_TYPE_IS0) {
8147400aaeSHoratiu Vultur 				err = vcap_rule_add_key_bit(st->vrule,
8247400aaeSHoratiu Vultur 							    VCAP_KF_TCP_UDP_IS,
8347400aaeSHoratiu Vultur 							    VCAP_BIT_1);
8447400aaeSHoratiu Vultur 				if (err)
8547400aaeSHoratiu Vultur 					goto out;
8647400aaeSHoratiu Vultur 			}
8747400aaeSHoratiu Vultur 		} else {
88d6c2964dSSteen Hegelund 			err = vcap_rule_add_key_u32(st->vrule,
8947400aaeSHoratiu Vultur 						    VCAP_KF_L3_IP_PROTO,
9047400aaeSHoratiu Vultur 						    st->l4_proto, ~0);
91d6c2964dSSteen Hegelund 			if (err)
92d6c2964dSSteen Hegelund 				goto out;
93d6c2964dSSteen Hegelund 		}
94d6c2964dSSteen Hegelund 	}
95d6c2964dSSteen Hegelund 
9647400aaeSHoratiu Vultur 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC);
97d6c2964dSSteen Hegelund 
98d6c2964dSSteen Hegelund 	return err;
99d6c2964dSSteen Hegelund 
100d6c2964dSSteen Hegelund out:
10147400aaeSHoratiu Vultur 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error");
102d6c2964dSSteen Hegelund 	return err;
103d6c2964dSSteen Hegelund }
104d6c2964dSSteen Hegelund 
105d6c2964dSSteen Hegelund static int
10647400aaeSHoratiu Vultur sparx5_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage *st)
107d6c2964dSSteen Hegelund {
108d6c2964dSSteen Hegelund 	struct flow_match_control mt;
109d6c2964dSSteen Hegelund 	u32 value, mask;
110d6c2964dSSteen Hegelund 	int err = 0;
111d6c2964dSSteen Hegelund 
112d6c2964dSSteen Hegelund 	flow_rule_match_control(st->frule, &mt);
113d6c2964dSSteen Hegelund 
114d6c2964dSSteen Hegelund 	if (mt.mask->flags) {
115d6c2964dSSteen Hegelund 		if (mt.mask->flags & FLOW_DIS_FIRST_FRAG) {
116d6c2964dSSteen Hegelund 			if (mt.key->flags & FLOW_DIS_FIRST_FRAG) {
117d6c2964dSSteen Hegelund 				value = 1; /* initial fragment */
118d6c2964dSSteen Hegelund 				mask = 0x3;
119d6c2964dSSteen Hegelund 			} else {
120d6c2964dSSteen Hegelund 				if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) {
121d6c2964dSSteen Hegelund 					value = 3; /* follow up fragment */
122d6c2964dSSteen Hegelund 					mask = 0x3;
123d6c2964dSSteen Hegelund 				} else {
124d6c2964dSSteen Hegelund 					value = 0; /* no fragment */
125d6c2964dSSteen Hegelund 					mask = 0x3;
126d6c2964dSSteen Hegelund 				}
127d6c2964dSSteen Hegelund 			}
128d6c2964dSSteen Hegelund 		} else {
129d6c2964dSSteen Hegelund 			if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) {
130d6c2964dSSteen Hegelund 				value = 3; /* follow up fragment */
131d6c2964dSSteen Hegelund 				mask = 0x3;
132d6c2964dSSteen Hegelund 			} else {
133d6c2964dSSteen Hegelund 				value = 0; /* no fragment */
134d6c2964dSSteen Hegelund 				mask = 0x3;
135d6c2964dSSteen Hegelund 			}
136d6c2964dSSteen Hegelund 		}
137d6c2964dSSteen Hegelund 
138d6c2964dSSteen Hegelund 		err = vcap_rule_add_key_u32(st->vrule,
139d6c2964dSSteen Hegelund 					    VCAP_KF_L3_FRAGMENT_TYPE,
140d6c2964dSSteen Hegelund 					    value, mask);
141d6c2964dSSteen Hegelund 		if (err)
142d6c2964dSSteen Hegelund 			goto out;
143d6c2964dSSteen Hegelund 	}
144d6c2964dSSteen Hegelund 
145d6c2964dSSteen Hegelund 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL);
146d6c2964dSSteen Hegelund 
147d6c2964dSSteen Hegelund 	return err;
148d6c2964dSSteen Hegelund 
149d6c2964dSSteen Hegelund out:
150d6c2964dSSteen Hegelund 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error");
151d6c2964dSSteen Hegelund 	return err;
152d6c2964dSSteen Hegelund }
153d6c2964dSSteen Hegelund 
154d6c2964dSSteen Hegelund static int
15547400aaeSHoratiu Vultur sparx5_tc_flower_handler_cvlan_usage(struct vcap_tc_flower_parse_usage *st)
156d6c2964dSSteen Hegelund {
157a5300724SSteen Hegelund 	if (st->admin->vtype != VCAP_TYPE_IS0) {
158a5300724SSteen Hegelund 		NL_SET_ERR_MSG_MOD(st->fco->common.extack,
159a5300724SSteen Hegelund 				   "cvlan not supported in this VCAP");
16052df82ccSSteen Hegelund 		return -EINVAL;
161a5300724SSteen Hegelund 	}
16252df82ccSSteen Hegelund 
16347400aaeSHoratiu Vultur 	return vcap_tc_flower_handler_cvlan_usage(st);
16452df82ccSSteen Hegelund }
16552df82ccSSteen Hegelund 
16652df82ccSSteen Hegelund static int
16747400aaeSHoratiu Vultur sparx5_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st)
168d6c2964dSSteen Hegelund {
169d6c2964dSSteen Hegelund 	enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS;
170d6c2964dSSteen Hegelund 	enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS;
171d6c2964dSSteen Hegelund 
17252df82ccSSteen Hegelund 	if (st->admin->vtype == VCAP_TYPE_IS0) {
17352df82ccSSteen Hegelund 		vid_key = VCAP_KF_8021Q_VID0;
17452df82ccSSteen Hegelund 		pcp_key = VCAP_KF_8021Q_PCP0;
17552df82ccSSteen Hegelund 	}
17652df82ccSSteen Hegelund 
17747400aaeSHoratiu Vultur 	return vcap_tc_flower_handler_vlan_usage(st, vid_key, pcp_key);
178d6c2964dSSteen Hegelund }
179d6c2964dSSteen Hegelund 
18047400aaeSHoratiu Vultur static int (*sparx5_tc_flower_usage_handlers[])(struct vcap_tc_flower_parse_usage *st) = {
18147400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_ETH_ADDRS] = vcap_tc_flower_handler_ethaddr_usage,
18247400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_IPV4_ADDRS] = vcap_tc_flower_handler_ipv4_usage,
18347400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_IPV6_ADDRS] = vcap_tc_flower_handler_ipv6_usage,
184d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_CONTROL] = sparx5_tc_flower_handler_control_usage,
18547400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_PORTS] = vcap_tc_flower_handler_portnum_usage,
186d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage,
18752df82ccSSteen Hegelund 	[FLOW_DISSECTOR_KEY_CVLAN] = sparx5_tc_flower_handler_cvlan_usage,
188d6c2964dSSteen Hegelund 	[FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage,
18947400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_TCP] = vcap_tc_flower_handler_tcp_usage,
19047400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_ARP] = vcap_tc_flower_handler_arp_usage,
19147400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_IP] = vcap_tc_flower_handler_ip_usage,
192c9da1ac1SSteen Hegelund };
193c9da1ac1SSteen Hegelund 
194c9da1ac1SSteen Hegelund static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco,
195c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin,
196abc4010dSSteen Hegelund 				    struct vcap_rule *vrule,
197abc4010dSSteen Hegelund 				    u16 *l3_proto)
198c9da1ac1SSteen Hegelund {
19947400aaeSHoratiu Vultur 	struct vcap_tc_flower_parse_usage state = {
200c9da1ac1SSteen Hegelund 		.fco = fco,
201c9da1ac1SSteen Hegelund 		.vrule = vrule,
202abc4010dSSteen Hegelund 		.l3_proto = ETH_P_ALL,
203542e6e2cSSteen Hegelund 		.admin = admin,
204c9da1ac1SSteen Hegelund 	};
205c9da1ac1SSteen Hegelund 	int idx, err = 0;
206c9da1ac1SSteen Hegelund 
207c9da1ac1SSteen Hegelund 	state.frule = flow_cls_offload_flow_rule(fco);
208c9da1ac1SSteen Hegelund 	for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) {
209c9da1ac1SSteen Hegelund 		if (!flow_rule_match_key(state.frule, idx))
210c9da1ac1SSteen Hegelund 			continue;
211c9da1ac1SSteen Hegelund 		if (!sparx5_tc_flower_usage_handlers[idx])
212c9da1ac1SSteen Hegelund 			continue;
213c9da1ac1SSteen Hegelund 		err = sparx5_tc_flower_usage_handlers[idx](&state);
214c9da1ac1SSteen Hegelund 		if (err)
215c9da1ac1SSteen Hegelund 			return err;
216c9da1ac1SSteen Hegelund 	}
217abc4010dSSteen Hegelund 
218abc4010dSSteen Hegelund 	if (state.frule->match.dissector->used_keys ^ state.used_keys) {
219abc4010dSSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
220abc4010dSSteen Hegelund 				   "Unsupported match item");
221abc4010dSSteen Hegelund 		return -ENOENT;
222abc4010dSSteen Hegelund 	}
223abc4010dSSteen Hegelund 
224abc4010dSSteen Hegelund 	if (l3_proto)
225abc4010dSSteen Hegelund 		*l3_proto = state.l3_proto;
226c9da1ac1SSteen Hegelund 	return err;
227c9da1ac1SSteen Hegelund }
228c9da1ac1SSteen Hegelund 
229392d0ab0SSteen Hegelund static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
230784c3067SSteen Hegelund 					 struct net_device *ndev,
231e7e3f514SSteen Hegelund 					 struct flow_cls_offload *fco,
232e7e3f514SSteen Hegelund 					 bool ingress)
233392d0ab0SSteen Hegelund {
234392d0ab0SSteen Hegelund 	struct flow_rule *rule = flow_cls_offload_flow_rule(fco);
235392d0ab0SSteen Hegelund 	struct flow_action_entry *actent, *last_actent = NULL;
236392d0ab0SSteen Hegelund 	struct flow_action *act = &rule->action;
237392d0ab0SSteen Hegelund 	u64 action_mask = 0;
238392d0ab0SSteen Hegelund 	int idx;
239392d0ab0SSteen Hegelund 
240392d0ab0SSteen Hegelund 	if (!flow_action_has_entries(act)) {
241392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
242392d0ab0SSteen Hegelund 		return -EINVAL;
243392d0ab0SSteen Hegelund 	}
244392d0ab0SSteen Hegelund 
245392d0ab0SSteen Hegelund 	if (!flow_action_basic_hw_stats_check(act, fco->common.extack))
246392d0ab0SSteen Hegelund 		return -EOPNOTSUPP;
247392d0ab0SSteen Hegelund 
248392d0ab0SSteen Hegelund 	flow_action_for_each(idx, actent, act) {
249392d0ab0SSteen Hegelund 		if (action_mask & BIT(actent->id)) {
250392d0ab0SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
251392d0ab0SSteen Hegelund 					   "More actions of the same type");
252392d0ab0SSteen Hegelund 			return -EINVAL;
253392d0ab0SSteen Hegelund 		}
254392d0ab0SSteen Hegelund 		action_mask |= BIT(actent->id);
255392d0ab0SSteen Hegelund 		last_actent = actent; /* Save last action for later check */
256392d0ab0SSteen Hegelund 	}
257392d0ab0SSteen Hegelund 
258784c3067SSteen Hegelund 	/* Check if last action is a goto
259784c3067SSteen Hegelund 	 * The last chain/lookup does not need to have a goto action
260784c3067SSteen Hegelund 	 */
261784c3067SSteen Hegelund 	if (last_actent->id == FLOW_ACTION_GOTO) {
262784c3067SSteen Hegelund 		/* Check if the destination chain is in one of the VCAPs */
263392d0ab0SSteen Hegelund 		if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
264392d0ab0SSteen Hegelund 					 last_actent->chain_index)) {
265392d0ab0SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
266392d0ab0SSteen Hegelund 					   "Invalid goto chain");
267392d0ab0SSteen Hegelund 			return -EINVAL;
268392d0ab0SSteen Hegelund 		}
269e7e3f514SSteen Hegelund 	} else if (!vcap_is_last_chain(vctrl, fco->common.chain_index,
270e7e3f514SSteen Hegelund 				       ingress)) {
271784c3067SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
272784c3067SSteen Hegelund 				   "Last action must be 'goto'");
273784c3067SSteen Hegelund 		return -EINVAL;
274784c3067SSteen Hegelund 	}
275392d0ab0SSteen Hegelund 
276392d0ab0SSteen Hegelund 	/* Catch unsupported combinations of actions */
277392d0ab0SSteen Hegelund 	if (action_mask & BIT(FLOW_ACTION_TRAP) &&
278392d0ab0SSteen Hegelund 	    action_mask & BIT(FLOW_ACTION_ACCEPT)) {
279392d0ab0SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
280392d0ab0SSteen Hegelund 				   "Cannot combine pass and trap action");
281392d0ab0SSteen Hegelund 		return -EOPNOTSUPP;
282392d0ab0SSteen Hegelund 	}
283392d0ab0SSteen Hegelund 
284392d0ab0SSteen Hegelund 	return 0;
285392d0ab0SSteen Hegelund }
286392d0ab0SSteen Hegelund 
287542e6e2cSSteen Hegelund /* Add a rule counter action */
28840e7fe18SSteen Hegelund static int sparx5_tc_add_rule_counter(struct vcap_admin *admin,
28940e7fe18SSteen Hegelund 				      struct vcap_rule *vrule)
29040e7fe18SSteen Hegelund {
29140e7fe18SSteen Hegelund 	int err;
29240e7fe18SSteen Hegelund 
2933cbe7537SSteen Hegelund 	switch (admin->vtype) {
2943cbe7537SSteen Hegelund 	case VCAP_TYPE_IS0:
2953cbe7537SSteen Hegelund 		break;
2963cbe7537SSteen Hegelund 	case VCAP_TYPE_ES0:
2973cbe7537SSteen Hegelund 		err = vcap_rule_mod_action_u32(vrule, VCAP_AF_ESDX,
2983cbe7537SSteen Hegelund 					       vrule->id);
2993cbe7537SSteen Hegelund 		if (err)
3003cbe7537SSteen Hegelund 			return err;
3013cbe7537SSteen Hegelund 		vcap_rule_set_counter_id(vrule, vrule->id);
3023cbe7537SSteen Hegelund 		break;
3033cbe7537SSteen Hegelund 	case VCAP_TYPE_IS2:
3043cbe7537SSteen Hegelund 	case VCAP_TYPE_ES2:
305542e6e2cSSteen Hegelund 		err = vcap_rule_mod_action_u32(vrule, VCAP_AF_CNT_ID,
306542e6e2cSSteen Hegelund 					       vrule->id);
30740e7fe18SSteen Hegelund 		if (err)
30840e7fe18SSteen Hegelund 			return err;
30940e7fe18SSteen Hegelund 		vcap_rule_set_counter_id(vrule, vrule->id);
3103cbe7537SSteen Hegelund 		break;
3113cbe7537SSteen Hegelund 	default:
3123cbe7537SSteen Hegelund 		pr_err("%s:%d: vcap type: %d not supported\n",
3133cbe7537SSteen Hegelund 		       __func__, __LINE__, admin->vtype);
3143cbe7537SSteen Hegelund 		break;
315542e6e2cSSteen Hegelund 	}
316542e6e2cSSteen Hegelund 	return 0;
31740e7fe18SSteen Hegelund }
31840e7fe18SSteen Hegelund 
3190ca60948SSteen Hegelund /* Collect all port keysets and apply the first of them, possibly wildcarded */
3200ca60948SSteen Hegelund static int sparx5_tc_select_protocol_keyset(struct net_device *ndev,
3210ca60948SSteen Hegelund 					    struct vcap_rule *vrule,
3220ca60948SSteen Hegelund 					    struct vcap_admin *admin,
3230ca60948SSteen Hegelund 					    u16 l3_proto,
3240ca60948SSteen Hegelund 					    struct sparx5_multiple_rules *multi)
3250ca60948SSteen Hegelund {
3260ca60948SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
3270ca60948SSteen Hegelund 	struct vcap_keyset_list portkeysetlist = {};
3280ca60948SSteen Hegelund 	enum vcap_keyfield_set portkeysets[10] = {};
3290ca60948SSteen Hegelund 	struct vcap_keyset_list matches = {};
3300ca60948SSteen Hegelund 	enum vcap_keyfield_set keysets[10];
3310ca60948SSteen Hegelund 	int idx, jdx, err = 0, count = 0;
3320ca60948SSteen Hegelund 	struct sparx5_wildcard_rule *mru;
3330ca60948SSteen Hegelund 	const struct vcap_set *kinfo;
3340ca60948SSteen Hegelund 	struct vcap_control *vctrl;
3350ca60948SSteen Hegelund 
3360ca60948SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
3370ca60948SSteen Hegelund 
3380ca60948SSteen Hegelund 	/* Find the keysets that the rule can use */
3390ca60948SSteen Hegelund 	matches.keysets = keysets;
3400ca60948SSteen Hegelund 	matches.max = ARRAY_SIZE(keysets);
3410ca60948SSteen Hegelund 	if (vcap_rule_find_keysets(vrule, &matches) == 0)
3420ca60948SSteen Hegelund 		return -EINVAL;
3430ca60948SSteen Hegelund 
3440ca60948SSteen Hegelund 	/* Find the keysets that the port configuration supports */
3450ca60948SSteen Hegelund 	portkeysetlist.max = ARRAY_SIZE(portkeysets);
3460ca60948SSteen Hegelund 	portkeysetlist.keysets = portkeysets;
3470ca60948SSteen Hegelund 	err = sparx5_vcap_get_port_keyset(ndev,
3480ca60948SSteen Hegelund 					  admin, vrule->vcap_chain_id,
3490ca60948SSteen Hegelund 					  l3_proto,
3500ca60948SSteen Hegelund 					  &portkeysetlist);
3510ca60948SSteen Hegelund 	if (err)
3520ca60948SSteen Hegelund 		return err;
3530ca60948SSteen Hegelund 
3540ca60948SSteen Hegelund 	/* Find the intersection of the two sets of keyset */
3550ca60948SSteen Hegelund 	for (idx = 0; idx < portkeysetlist.cnt; ++idx) {
3560ca60948SSteen Hegelund 		kinfo = vcap_keyfieldset(vctrl, admin->vtype,
3570ca60948SSteen Hegelund 					 portkeysetlist.keysets[idx]);
3580ca60948SSteen Hegelund 		if (!kinfo)
3590ca60948SSteen Hegelund 			continue;
3600ca60948SSteen Hegelund 
3610ca60948SSteen Hegelund 		/* Find a port keyset that matches the required keys
3620ca60948SSteen Hegelund 		 * If there are multiple keysets then compose a type id mask
3630ca60948SSteen Hegelund 		 */
3640ca60948SSteen Hegelund 		for (jdx = 0; jdx < matches.cnt; ++jdx) {
3650ca60948SSteen Hegelund 			if (portkeysetlist.keysets[idx] != matches.keysets[jdx])
3660ca60948SSteen Hegelund 				continue;
3670ca60948SSteen Hegelund 
3680ca60948SSteen Hegelund 			mru = &multi->rule[kinfo->sw_per_item];
3690ca60948SSteen Hegelund 			if (!mru->selected) {
3700ca60948SSteen Hegelund 				mru->selected = true;
3710ca60948SSteen Hegelund 				mru->keyset = portkeysetlist.keysets[idx];
3720ca60948SSteen Hegelund 				mru->value = kinfo->type_id;
3730ca60948SSteen Hegelund 			}
3740ca60948SSteen Hegelund 			mru->value &= kinfo->type_id;
3750ca60948SSteen Hegelund 			mru->mask |= kinfo->type_id;
3760ca60948SSteen Hegelund 			++count;
3770ca60948SSteen Hegelund 		}
3780ca60948SSteen Hegelund 	}
3790ca60948SSteen Hegelund 	if (count == 0)
3800ca60948SSteen Hegelund 		return -EPROTO;
3810ca60948SSteen Hegelund 
3820ca60948SSteen Hegelund 	if (l3_proto == ETH_P_ALL && count < portkeysetlist.cnt)
3830ca60948SSteen Hegelund 		return -ENOENT;
3840ca60948SSteen Hegelund 
3850ca60948SSteen Hegelund 	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
3860ca60948SSteen Hegelund 		mru = &multi->rule[idx];
3870ca60948SSteen Hegelund 		if (!mru->selected)
3880ca60948SSteen Hegelund 			continue;
3890ca60948SSteen Hegelund 
3900ca60948SSteen Hegelund 		/* Align the mask to the combined value */
3910ca60948SSteen Hegelund 		mru->mask ^= mru->value;
3920ca60948SSteen Hegelund 	}
3930ca60948SSteen Hegelund 
3940ca60948SSteen Hegelund 	/* Set the chosen keyset on the rule and set a wildcarded type if there
3950ca60948SSteen Hegelund 	 * are more than one keyset
3960ca60948SSteen Hegelund 	 */
3970ca60948SSteen Hegelund 	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
3980ca60948SSteen Hegelund 		mru = &multi->rule[idx];
3990ca60948SSteen Hegelund 		if (!mru->selected)
4000ca60948SSteen Hegelund 			continue;
4010ca60948SSteen Hegelund 
4020ca60948SSteen Hegelund 		vcap_set_rule_set_keyset(vrule, mru->keyset);
4030ca60948SSteen Hegelund 		if (count > 1)
4040ca60948SSteen Hegelund 			/* Some keysets do not have a type field */
4050ca60948SSteen Hegelund 			vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE,
4060ca60948SSteen Hegelund 					      mru->value,
4070ca60948SSteen Hegelund 					      ~mru->mask);
4080ca60948SSteen Hegelund 		mru->selected = false; /* mark as done */
4090ca60948SSteen Hegelund 		break; /* Stop here and add more rules later */
4100ca60948SSteen Hegelund 	}
4110ca60948SSteen Hegelund 	return err;
4120ca60948SSteen Hegelund }
4130ca60948SSteen Hegelund 
4140ca60948SSteen Hegelund static int sparx5_tc_add_rule_copy(struct vcap_control *vctrl,
4150ca60948SSteen Hegelund 				   struct flow_cls_offload *fco,
4160ca60948SSteen Hegelund 				   struct vcap_rule *erule,
4170ca60948SSteen Hegelund 				   struct vcap_admin *admin,
4180ca60948SSteen Hegelund 				   struct sparx5_wildcard_rule *rule)
4190ca60948SSteen Hegelund {
4200ca60948SSteen Hegelund 	enum vcap_key_field keylist[] = {
4210ca60948SSteen Hegelund 		VCAP_KF_IF_IGR_PORT_MASK,
4220ca60948SSteen Hegelund 		VCAP_KF_IF_IGR_PORT_MASK_SEL,
4230ca60948SSteen Hegelund 		VCAP_KF_IF_IGR_PORT_MASK_RNG,
4240ca60948SSteen Hegelund 		VCAP_KF_LOOKUP_FIRST_IS,
4250ca60948SSteen Hegelund 		VCAP_KF_TYPE,
4260ca60948SSteen Hegelund 	};
4270ca60948SSteen Hegelund 	struct vcap_rule *vrule;
4280ca60948SSteen Hegelund 	int err;
4290ca60948SSteen Hegelund 
4300ca60948SSteen Hegelund 	/* Add an extra rule with a special user and the new keyset */
4310ca60948SSteen Hegelund 	erule->user = VCAP_USER_TC_EXTRA;
4320ca60948SSteen Hegelund 	vrule = vcap_copy_rule(erule);
4330ca60948SSteen Hegelund 	if (IS_ERR(vrule))
4340ca60948SSteen Hegelund 		return PTR_ERR(vrule);
4350ca60948SSteen Hegelund 
4360ca60948SSteen Hegelund 	/* Link the new rule to the existing rule with the cookie */
4370ca60948SSteen Hegelund 	vrule->cookie = erule->cookie;
4380ca60948SSteen Hegelund 	vcap_filter_rule_keys(vrule, keylist, ARRAY_SIZE(keylist), true);
4390ca60948SSteen Hegelund 	err = vcap_set_rule_set_keyset(vrule, rule->keyset);
4400ca60948SSteen Hegelund 	if (err) {
4410ca60948SSteen Hegelund 		pr_err("%s:%d: could not set keyset %s in rule: %u\n",
4420ca60948SSteen Hegelund 		       __func__, __LINE__,
4430ca60948SSteen Hegelund 		       vcap_keyset_name(vctrl, rule->keyset),
4440ca60948SSteen Hegelund 		       vrule->id);
4450ca60948SSteen Hegelund 		goto out;
4460ca60948SSteen Hegelund 	}
4470ca60948SSteen Hegelund 
4480ca60948SSteen Hegelund 	/* Some keysets do not have a type field, so ignore return value */
4490ca60948SSteen Hegelund 	vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE, rule->value, ~rule->mask);
4500ca60948SSteen Hegelund 
4510ca60948SSteen Hegelund 	err = vcap_set_rule_set_actionset(vrule, erule->actionset);
4520ca60948SSteen Hegelund 	if (err)
4530ca60948SSteen Hegelund 		goto out;
4540ca60948SSteen Hegelund 
4550ca60948SSteen Hegelund 	err = sparx5_tc_add_rule_counter(admin, vrule);
4560ca60948SSteen Hegelund 	if (err)
4570ca60948SSteen Hegelund 		goto out;
4580ca60948SSteen Hegelund 
4590ca60948SSteen Hegelund 	err = vcap_val_rule(vrule, ETH_P_ALL);
4600ca60948SSteen Hegelund 	if (err) {
4610ca60948SSteen Hegelund 		pr_err("%s:%d: could not validate rule: %u\n",
4620ca60948SSteen Hegelund 		       __func__, __LINE__, vrule->id);
4630ca60948SSteen Hegelund 		vcap_set_tc_exterr(fco, vrule);
4640ca60948SSteen Hegelund 		goto out;
4650ca60948SSteen Hegelund 	}
4660ca60948SSteen Hegelund 	err = vcap_add_rule(vrule);
4670ca60948SSteen Hegelund 	if (err) {
4680ca60948SSteen Hegelund 		pr_err("%s:%d: could not add rule: %u\n",
4690ca60948SSteen Hegelund 		       __func__, __LINE__, vrule->id);
4700ca60948SSteen Hegelund 		goto out;
4710ca60948SSteen Hegelund 	}
4720ca60948SSteen Hegelund out:
4730ca60948SSteen Hegelund 	vcap_free_rule(vrule);
4740ca60948SSteen Hegelund 	return err;
4750ca60948SSteen Hegelund }
4760ca60948SSteen Hegelund 
4770ca60948SSteen Hegelund static int sparx5_tc_add_remaining_rules(struct vcap_control *vctrl,
4780ca60948SSteen Hegelund 					 struct flow_cls_offload *fco,
4790ca60948SSteen Hegelund 					 struct vcap_rule *erule,
4800ca60948SSteen Hegelund 					 struct vcap_admin *admin,
4810ca60948SSteen Hegelund 					 struct sparx5_multiple_rules *multi)
4820ca60948SSteen Hegelund {
4830ca60948SSteen Hegelund 	int idx, err = 0;
4840ca60948SSteen Hegelund 
4850ca60948SSteen Hegelund 	for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
4860ca60948SSteen Hegelund 		if (!multi->rule[idx].selected)
4870ca60948SSteen Hegelund 			continue;
4880ca60948SSteen Hegelund 
4890ca60948SSteen Hegelund 		err = sparx5_tc_add_rule_copy(vctrl, fco, erule, admin,
4900ca60948SSteen Hegelund 					      &multi->rule[idx]);
4910ca60948SSteen Hegelund 		if (err)
4920ca60948SSteen Hegelund 			break;
4930ca60948SSteen Hegelund 	}
4940ca60948SSteen Hegelund 	return err;
4950ca60948SSteen Hegelund }
4960ca60948SSteen Hegelund 
497542e6e2cSSteen Hegelund /* Add the actionset that is the default for the VCAP type */
498542e6e2cSSteen Hegelund static int sparx5_tc_set_actionset(struct vcap_admin *admin,
499542e6e2cSSteen Hegelund 				   struct vcap_rule *vrule)
500542e6e2cSSteen Hegelund {
501542e6e2cSSteen Hegelund 	enum vcap_actionfield_set aset;
502542e6e2cSSteen Hegelund 	int err = 0;
503542e6e2cSSteen Hegelund 
504542e6e2cSSteen Hegelund 	switch (admin->vtype) {
505542e6e2cSSteen Hegelund 	case VCAP_TYPE_IS0:
506542e6e2cSSteen Hegelund 		aset = VCAP_AFS_CLASSIFICATION;
507542e6e2cSSteen Hegelund 		break;
508542e6e2cSSteen Hegelund 	case VCAP_TYPE_IS2:
509542e6e2cSSteen Hegelund 		aset = VCAP_AFS_BASE_TYPE;
510542e6e2cSSteen Hegelund 		break;
511*52b28a93SSteen Hegelund 	case VCAP_TYPE_ES0:
512*52b28a93SSteen Hegelund 		aset = VCAP_AFS_ES0;
513*52b28a93SSteen Hegelund 		break;
5147b911a53SSteen Hegelund 	case VCAP_TYPE_ES2:
5157b911a53SSteen Hegelund 		aset = VCAP_AFS_BASE_TYPE;
5167b911a53SSteen Hegelund 		break;
517542e6e2cSSteen Hegelund 	default:
518*52b28a93SSteen Hegelund 		pr_err("%s:%d: %s\n", __func__, __LINE__, "Invalid VCAP type");
519542e6e2cSSteen Hegelund 		return -EINVAL;
520542e6e2cSSteen Hegelund 	}
521542e6e2cSSteen Hegelund 	/* Do not overwrite any current actionset */
522542e6e2cSSteen Hegelund 	if (vrule->actionset == VCAP_AFS_NO_VALUE)
523542e6e2cSSteen Hegelund 		err = vcap_set_rule_set_actionset(vrule, aset);
524542e6e2cSSteen Hegelund 	return err;
525542e6e2cSSteen Hegelund }
526542e6e2cSSteen Hegelund 
52788bd9ea7SSteen Hegelund /* Add the VCAP key to match on for a rule target value */
52888bd9ea7SSteen Hegelund static int sparx5_tc_add_rule_link_target(struct vcap_admin *admin,
52988bd9ea7SSteen Hegelund 					  struct vcap_rule *vrule,
53088bd9ea7SSteen Hegelund 					  int target_cid)
53188bd9ea7SSteen Hegelund {
53288bd9ea7SSteen Hegelund 	int link_val = target_cid % VCAP_CID_LOOKUP_SIZE;
53388bd9ea7SSteen Hegelund 	int err;
53488bd9ea7SSteen Hegelund 
53588bd9ea7SSteen Hegelund 	if (!link_val)
53688bd9ea7SSteen Hegelund 		return 0;
53788bd9ea7SSteen Hegelund 
53888bd9ea7SSteen Hegelund 	switch (admin->vtype) {
53988bd9ea7SSteen Hegelund 	case VCAP_TYPE_IS0:
54088bd9ea7SSteen Hegelund 		/* Add NXT_IDX key for chaining rules between IS0 instances */
54188bd9ea7SSteen Hegelund 		err = vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX_SEL,
54288bd9ea7SSteen Hegelund 					    1, /* enable */
54388bd9ea7SSteen Hegelund 					    ~0);
54488bd9ea7SSteen Hegelund 		if (err)
54588bd9ea7SSteen Hegelund 			return err;
54688bd9ea7SSteen Hegelund 		return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX,
54788bd9ea7SSteen Hegelund 					     link_val, /* target */
54888bd9ea7SSteen Hegelund 					     ~0);
54988bd9ea7SSteen Hegelund 	case VCAP_TYPE_IS2:
55088bd9ea7SSteen Hegelund 		/* Add PAG key for chaining rules from IS0 */
55188bd9ea7SSteen Hegelund 		return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_PAG,
55288bd9ea7SSteen Hegelund 					     link_val, /* target */
55388bd9ea7SSteen Hegelund 					     ~0);
554*52b28a93SSteen Hegelund 	case VCAP_TYPE_ES0:
5557b911a53SSteen Hegelund 	case VCAP_TYPE_ES2:
5567b911a53SSteen Hegelund 		/* Add ISDX key for chaining rules from IS0 */
5577b911a53SSteen Hegelund 		return vcap_rule_add_key_u32(vrule, VCAP_KF_ISDX_CLS, link_val,
5587b911a53SSteen Hegelund 					     ~0);
55988bd9ea7SSteen Hegelund 	default:
56088bd9ea7SSteen Hegelund 		break;
56188bd9ea7SSteen Hegelund 	}
56288bd9ea7SSteen Hegelund 	return 0;
56388bd9ea7SSteen Hegelund }
56488bd9ea7SSteen Hegelund 
56588bd9ea7SSteen Hegelund /* Add the VCAP action that adds a target value to a rule */
56688bd9ea7SSteen Hegelund static int sparx5_tc_add_rule_link(struct vcap_control *vctrl,
56788bd9ea7SSteen Hegelund 				   struct vcap_admin *admin,
56888bd9ea7SSteen Hegelund 				   struct vcap_rule *vrule,
56988bd9ea7SSteen Hegelund 				   int from_cid, int to_cid)
57088bd9ea7SSteen Hegelund {
57188bd9ea7SSteen Hegelund 	struct vcap_admin *to_admin = vcap_find_admin(vctrl, to_cid);
57288bd9ea7SSteen Hegelund 	int diff, err = 0;
57388bd9ea7SSteen Hegelund 
574b5b0c364SSteen Hegelund 	if (!to_admin) {
57588bd9ea7SSteen Hegelund 		pr_err("%s:%d: unsupported chain direction: %d\n",
57688bd9ea7SSteen Hegelund 		       __func__, __LINE__, to_cid);
57788bd9ea7SSteen Hegelund 		return -EINVAL;
57888bd9ea7SSteen Hegelund 	}
579b5b0c364SSteen Hegelund 
580b5b0c364SSteen Hegelund 	diff = vcap_chain_offset(vctrl, from_cid, to_cid);
581b5b0c364SSteen Hegelund 	if (!diff)
582b5b0c364SSteen Hegelund 		return 0;
583b5b0c364SSteen Hegelund 
58488bd9ea7SSteen Hegelund 	if (admin->vtype == VCAP_TYPE_IS0 &&
58588bd9ea7SSteen Hegelund 	    to_admin->vtype == VCAP_TYPE_IS0) {
58688bd9ea7SSteen Hegelund 		/* Between IS0 instances the G_IDX value is used */
58788bd9ea7SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule, VCAP_AF_NXT_IDX, diff);
58888bd9ea7SSteen Hegelund 		if (err)
58988bd9ea7SSteen Hegelund 			goto out;
59088bd9ea7SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule, VCAP_AF_NXT_IDX_CTRL,
59188bd9ea7SSteen Hegelund 					       1); /* Replace */
59288bd9ea7SSteen Hegelund 		if (err)
59388bd9ea7SSteen Hegelund 			goto out;
59488bd9ea7SSteen Hegelund 	} else if (admin->vtype == VCAP_TYPE_IS0 &&
59588bd9ea7SSteen Hegelund 		   to_admin->vtype == VCAP_TYPE_IS2) {
59688bd9ea7SSteen Hegelund 		/* Between IS0 and IS2 the PAG value is used */
59788bd9ea7SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule, VCAP_AF_PAG_VAL, diff);
59888bd9ea7SSteen Hegelund 		if (err)
59988bd9ea7SSteen Hegelund 			goto out;
60088bd9ea7SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
60188bd9ea7SSteen Hegelund 					       VCAP_AF_PAG_OVERRIDE_MASK,
60288bd9ea7SSteen Hegelund 					       0xff);
60388bd9ea7SSteen Hegelund 		if (err)
60488bd9ea7SSteen Hegelund 			goto out;
6057b911a53SSteen Hegelund 	} else if (admin->vtype == VCAP_TYPE_IS0 &&
606*52b28a93SSteen Hegelund 		   (to_admin->vtype == VCAP_TYPE_ES0 ||
607*52b28a93SSteen Hegelund 		    to_admin->vtype == VCAP_TYPE_ES2)) {
608*52b28a93SSteen Hegelund 		/* Between IS0 and ES0/ES2 the ISDX value is used */
6097b911a53SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_VAL,
6107b911a53SSteen Hegelund 					       diff);
6117b911a53SSteen Hegelund 		if (err)
6127b911a53SSteen Hegelund 			goto out;
6137b911a53SSteen Hegelund 		err = vcap_rule_add_action_bit(vrule,
6147b911a53SSteen Hegelund 					       VCAP_AF_ISDX_ADD_REPLACE_SEL,
6157b911a53SSteen Hegelund 					       VCAP_BIT_1);
6167b911a53SSteen Hegelund 		if (err)
6177b911a53SSteen Hegelund 			goto out;
61888bd9ea7SSteen Hegelund 	} else {
61988bd9ea7SSteen Hegelund 		pr_err("%s:%d: unsupported chain destination: %d\n",
62088bd9ea7SSteen Hegelund 		       __func__, __LINE__, to_cid);
62188bd9ea7SSteen Hegelund 		err = -EOPNOTSUPP;
62288bd9ea7SSteen Hegelund 	}
62388bd9ea7SSteen Hegelund out:
62488bd9ea7SSteen Hegelund 	return err;
62588bd9ea7SSteen Hegelund }
62688bd9ea7SSteen Hegelund 
6276ebf182bSDaniel Machon static int sparx5_tc_flower_parse_act_gate(struct sparx5_psfp_sg *sg,
6286ebf182bSDaniel Machon 					   struct flow_action_entry *act,
6296ebf182bSDaniel Machon 					   struct netlink_ext_ack *extack)
6306ebf182bSDaniel Machon {
6316ebf182bSDaniel Machon 	int i;
6326ebf182bSDaniel Machon 
6336ebf182bSDaniel Machon 	if (act->gate.prio < -1 || act->gate.prio > SPX5_PSFP_SG_MAX_IPV) {
6346ebf182bSDaniel Machon 		NL_SET_ERR_MSG_MOD(extack, "Invalid gate priority");
6356ebf182bSDaniel Machon 		return -EINVAL;
6366ebf182bSDaniel Machon 	}
6376ebf182bSDaniel Machon 
6386ebf182bSDaniel Machon 	if (act->gate.cycletime < SPX5_PSFP_SG_MIN_CYCLE_TIME_NS ||
6396ebf182bSDaniel Machon 	    act->gate.cycletime > SPX5_PSFP_SG_MAX_CYCLE_TIME_NS) {
6406ebf182bSDaniel Machon 		NL_SET_ERR_MSG_MOD(extack, "Invalid gate cycletime");
6416ebf182bSDaniel Machon 		return -EINVAL;
6426ebf182bSDaniel Machon 	}
6436ebf182bSDaniel Machon 
6446ebf182bSDaniel Machon 	if (act->gate.cycletimeext > SPX5_PSFP_SG_MAX_CYCLE_TIME_NS) {
6456ebf182bSDaniel Machon 		NL_SET_ERR_MSG_MOD(extack, "Invalid gate cycletimeext");
6466ebf182bSDaniel Machon 		return -EINVAL;
6476ebf182bSDaniel Machon 	}
6486ebf182bSDaniel Machon 
6496ebf182bSDaniel Machon 	if (act->gate.num_entries >= SPX5_PSFP_GCE_CNT) {
6506ebf182bSDaniel Machon 		NL_SET_ERR_MSG_MOD(extack, "Invalid number of gate entries");
6516ebf182bSDaniel Machon 		return -EINVAL;
6526ebf182bSDaniel Machon 	}
6536ebf182bSDaniel Machon 
6546ebf182bSDaniel Machon 	sg->gate_state = true;
6556ebf182bSDaniel Machon 	sg->ipv = act->gate.prio;
6566ebf182bSDaniel Machon 	sg->num_entries = act->gate.num_entries;
6576ebf182bSDaniel Machon 	sg->cycletime = act->gate.cycletime;
6586ebf182bSDaniel Machon 	sg->cycletimeext = act->gate.cycletimeext;
6596ebf182bSDaniel Machon 
6606ebf182bSDaniel Machon 	for (i = 0; i < sg->num_entries; i++) {
6616ebf182bSDaniel Machon 		sg->gce[i].gate_state = !!act->gate.entries[i].gate_state;
6626ebf182bSDaniel Machon 		sg->gce[i].interval = act->gate.entries[i].interval;
6636ebf182bSDaniel Machon 		sg->gce[i].ipv = act->gate.entries[i].ipv;
6646ebf182bSDaniel Machon 		sg->gce[i].maxoctets = act->gate.entries[i].maxoctets;
6656ebf182bSDaniel Machon 	}
6666ebf182bSDaniel Machon 
6676ebf182bSDaniel Machon 	return 0;
6686ebf182bSDaniel Machon }
6696ebf182bSDaniel Machon 
6706ebf182bSDaniel Machon static int sparx5_tc_flower_parse_act_police(struct sparx5_policer *pol,
6716ebf182bSDaniel Machon 					     struct flow_action_entry *act,
6726ebf182bSDaniel Machon 					     struct netlink_ext_ack *extack)
6736ebf182bSDaniel Machon {
6746ebf182bSDaniel Machon 	pol->type = SPX5_POL_SERVICE;
6756ebf182bSDaniel Machon 	pol->rate = div_u64(act->police.rate_bytes_ps, 1000) * 8;
6766ebf182bSDaniel Machon 	pol->burst = act->police.burst;
6776ebf182bSDaniel Machon 	pol->idx = act->hw_index;
6786ebf182bSDaniel Machon 
6796ebf182bSDaniel Machon 	/* rate is now in kbit */
6806ebf182bSDaniel Machon 	if (pol->rate > DIV_ROUND_UP(SPX5_SDLB_GROUP_RATE_MAX, 1000)) {
6816ebf182bSDaniel Machon 		NL_SET_ERR_MSG_MOD(extack, "Maximum rate exceeded");
6826ebf182bSDaniel Machon 		return -EINVAL;
6836ebf182bSDaniel Machon 	}
6846ebf182bSDaniel Machon 
6856ebf182bSDaniel Machon 	if (act->police.exceed.act_id != FLOW_ACTION_DROP) {
6866ebf182bSDaniel Machon 		NL_SET_ERR_MSG_MOD(extack, "Offload not supported when exceed action is not drop");
6876ebf182bSDaniel Machon 		return -EOPNOTSUPP;
6886ebf182bSDaniel Machon 	}
6896ebf182bSDaniel Machon 
6906ebf182bSDaniel Machon 	if (act->police.notexceed.act_id != FLOW_ACTION_PIPE &&
6916ebf182bSDaniel Machon 	    act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) {
6926ebf182bSDaniel Machon 		NL_SET_ERR_MSG_MOD(extack, "Offload not supported when conform action is not pipe or ok");
6936ebf182bSDaniel Machon 		return -EOPNOTSUPP;
6946ebf182bSDaniel Machon 	}
6956ebf182bSDaniel Machon 
6966ebf182bSDaniel Machon 	return 0;
6976ebf182bSDaniel Machon }
6986ebf182bSDaniel Machon 
6996ebf182bSDaniel Machon static int sparx5_tc_flower_psfp_setup(struct sparx5 *sparx5,
7006ebf182bSDaniel Machon 				       struct vcap_rule *vrule, int sg_idx,
7016ebf182bSDaniel Machon 				       int pol_idx, struct sparx5_psfp_sg *sg,
7026ebf182bSDaniel Machon 				       struct sparx5_psfp_fm *fm,
7036ebf182bSDaniel Machon 				       struct sparx5_psfp_sf *sf)
7046ebf182bSDaniel Machon {
7056ebf182bSDaniel Machon 	u32 psfp_sfid = 0, psfp_fmid = 0, psfp_sgid = 0;
7066ebf182bSDaniel Machon 	int ret;
7076ebf182bSDaniel Machon 
7086ebf182bSDaniel Machon 	/* Must always have a stream gate - max sdu (filter option) is evaluated
7096ebf182bSDaniel Machon 	 * after frames have passed the gate, so in case of only a policer, we
7106ebf182bSDaniel Machon 	 * allocate a stream gate that is always open.
7116ebf182bSDaniel Machon 	 */
7126ebf182bSDaniel Machon 	if (sg_idx < 0) {
7136ebf182bSDaniel Machon 		sg_idx = sparx5_pool_idx_to_id(SPX5_PSFP_SG_OPEN);
7146ebf182bSDaniel Machon 		sg->ipv = 0; /* Disabled */
7156ebf182bSDaniel Machon 		sg->cycletime = SPX5_PSFP_SG_CYCLE_TIME_DEFAULT;
7166ebf182bSDaniel Machon 		sg->num_entries = 1;
7176ebf182bSDaniel Machon 		sg->gate_state = 1; /* Open */
7186ebf182bSDaniel Machon 		sg->gate_enabled = 1;
7196ebf182bSDaniel Machon 		sg->gce[0].gate_state = 1;
7206ebf182bSDaniel Machon 		sg->gce[0].interval = SPX5_PSFP_SG_CYCLE_TIME_DEFAULT;
7216ebf182bSDaniel Machon 		sg->gce[0].ipv = 0;
7226ebf182bSDaniel Machon 		sg->gce[0].maxoctets = 0; /* Disabled */
7236ebf182bSDaniel Machon 	}
7246ebf182bSDaniel Machon 
7256ebf182bSDaniel Machon 	ret = sparx5_psfp_sg_add(sparx5, sg_idx, sg, &psfp_sgid);
7266ebf182bSDaniel Machon 	if (ret < 0)
7276ebf182bSDaniel Machon 		return ret;
7286ebf182bSDaniel Machon 
7296ebf182bSDaniel Machon 	if (pol_idx >= 0) {
7306ebf182bSDaniel Machon 		/* Add new flow-meter */
7316ebf182bSDaniel Machon 		ret = sparx5_psfp_fm_add(sparx5, pol_idx, fm, &psfp_fmid);
7326ebf182bSDaniel Machon 		if (ret < 0)
7336ebf182bSDaniel Machon 			return ret;
7346ebf182bSDaniel Machon 	}
7356ebf182bSDaniel Machon 
7366ebf182bSDaniel Machon 	/* Map stream filter to stream gate */
7376ebf182bSDaniel Machon 	sf->sgid = psfp_sgid;
7386ebf182bSDaniel Machon 
7396ebf182bSDaniel Machon 	/* Add new stream-filter and map it to a steam gate */
7406ebf182bSDaniel Machon 	ret = sparx5_psfp_sf_add(sparx5, sf, &psfp_sfid);
7416ebf182bSDaniel Machon 	if (ret < 0)
7426ebf182bSDaniel Machon 		return ret;
7436ebf182bSDaniel Machon 
7446ebf182bSDaniel Machon 	/* Streams are classified by ISDX - map ISDX 1:1 to sfid for now. */
7456ebf182bSDaniel Machon 	sparx5_isdx_conf_set(sparx5, psfp_sfid, psfp_sfid, psfp_fmid);
7466ebf182bSDaniel Machon 
7476ebf182bSDaniel Machon 	ret = vcap_rule_add_action_bit(vrule, VCAP_AF_ISDX_ADD_REPLACE_SEL,
7486ebf182bSDaniel Machon 				       VCAP_BIT_1);
7496ebf182bSDaniel Machon 	if (ret)
7506ebf182bSDaniel Machon 		return ret;
7516ebf182bSDaniel Machon 
7526ebf182bSDaniel Machon 	ret = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_VAL, psfp_sfid);
7536ebf182bSDaniel Machon 	if (ret)
7546ebf182bSDaniel Machon 		return ret;
7556ebf182bSDaniel Machon 
7566ebf182bSDaniel Machon 	return 0;
7576ebf182bSDaniel Machon }
7586ebf182bSDaniel Machon 
759*52b28a93SSteen Hegelund /* Handle the action trap for a VCAP rule */
760*52b28a93SSteen Hegelund static int sparx5_tc_action_trap(struct vcap_admin *admin,
761*52b28a93SSteen Hegelund 				 struct vcap_rule *vrule,
762*52b28a93SSteen Hegelund 				 struct flow_cls_offload *fco)
763*52b28a93SSteen Hegelund {
764*52b28a93SSteen Hegelund 	int err = 0;
765*52b28a93SSteen Hegelund 
766*52b28a93SSteen Hegelund 	switch (admin->vtype) {
767*52b28a93SSteen Hegelund 	case VCAP_TYPE_IS2:
768*52b28a93SSteen Hegelund 		err = vcap_rule_add_action_bit(vrule,
769*52b28a93SSteen Hegelund 					       VCAP_AF_CPU_COPY_ENA,
770*52b28a93SSteen Hegelund 					       VCAP_BIT_1);
771*52b28a93SSteen Hegelund 		if (err)
772*52b28a93SSteen Hegelund 			break;
773*52b28a93SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
774*52b28a93SSteen Hegelund 					       VCAP_AF_CPU_QUEUE_NUM, 0);
775*52b28a93SSteen Hegelund 		if (err)
776*52b28a93SSteen Hegelund 			break;
777*52b28a93SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
778*52b28a93SSteen Hegelund 					       VCAP_AF_MASK_MODE,
779*52b28a93SSteen Hegelund 					       SPX5_PMM_REPLACE_ALL);
780*52b28a93SSteen Hegelund 		break;
781*52b28a93SSteen Hegelund 	case VCAP_TYPE_ES0:
782*52b28a93SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
783*52b28a93SSteen Hegelund 					       VCAP_AF_FWD_SEL,
784*52b28a93SSteen Hegelund 					       SPX5_FWSEL_REDIRECT_TO_LOOPBACK);
785*52b28a93SSteen Hegelund 		break;
786*52b28a93SSteen Hegelund 	case VCAP_TYPE_ES2:
787*52b28a93SSteen Hegelund 		err = vcap_rule_add_action_bit(vrule,
788*52b28a93SSteen Hegelund 					       VCAP_AF_CPU_COPY_ENA,
789*52b28a93SSteen Hegelund 					       VCAP_BIT_1);
790*52b28a93SSteen Hegelund 		if (err)
791*52b28a93SSteen Hegelund 			break;
792*52b28a93SSteen Hegelund 		err = vcap_rule_add_action_u32(vrule,
793*52b28a93SSteen Hegelund 					       VCAP_AF_CPU_QUEUE_NUM, 0);
794*52b28a93SSteen Hegelund 		break;
795*52b28a93SSteen Hegelund 	default:
796*52b28a93SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
797*52b28a93SSteen Hegelund 				   "Trap action not supported in this VCAP");
798*52b28a93SSteen Hegelund 		err = -EOPNOTSUPP;
799*52b28a93SSteen Hegelund 		break;
800*52b28a93SSteen Hegelund 	}
801*52b28a93SSteen Hegelund 	return err;
802*52b28a93SSteen Hegelund }
803*52b28a93SSteen Hegelund 
804c9da1ac1SSteen Hegelund static int sparx5_tc_flower_replace(struct net_device *ndev,
805c9da1ac1SSteen Hegelund 				    struct flow_cls_offload *fco,
806e7e3f514SSteen Hegelund 				    struct vcap_admin *admin,
807e7e3f514SSteen Hegelund 				    bool ingress)
808c9da1ac1SSteen Hegelund {
8096ebf182bSDaniel Machon 	struct sparx5_psfp_sf sf = { .max_sdu = SPX5_PSFP_SF_MAX_SDU };
8106ebf182bSDaniel Machon 	struct netlink_ext_ack *extack = fco->common.extack;
8116ebf182bSDaniel Machon 	int err, idx, tc_sg_idx = -1, tc_pol_idx = -1;
812c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
8130ca60948SSteen Hegelund 	struct sparx5_multiple_rules multi = {};
8146ebf182bSDaniel Machon 	struct sparx5 *sparx5 = port->sparx5;
8156ebf182bSDaniel Machon 	struct sparx5_psfp_sg sg = { 0 };
8166ebf182bSDaniel Machon 	struct sparx5_psfp_fm fm = { 0 };
817c9da1ac1SSteen Hegelund 	struct flow_action_entry *act;
818c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
819c9da1ac1SSteen Hegelund 	struct flow_rule *frule;
820c9da1ac1SSteen Hegelund 	struct vcap_rule *vrule;
821abc4010dSSteen Hegelund 	u16 l3_proto;
822c9da1ac1SSteen Hegelund 
823c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
824392d0ab0SSteen Hegelund 
825e7e3f514SSteen Hegelund 	err = sparx5_tc_flower_action_check(vctrl, ndev, fco, ingress);
826392d0ab0SSteen Hegelund 	if (err)
827392d0ab0SSteen Hegelund 		return err;
828392d0ab0SSteen Hegelund 
829c9da1ac1SSteen Hegelund 	vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC,
830c9da1ac1SSteen Hegelund 				fco->common.prio, 0);
831c9da1ac1SSteen Hegelund 	if (IS_ERR(vrule))
832c9da1ac1SSteen Hegelund 		return PTR_ERR(vrule);
833c9da1ac1SSteen Hegelund 
834c9da1ac1SSteen Hegelund 	vrule->cookie = fco->cookie;
835bcddc196SSteen Hegelund 
836bcddc196SSteen Hegelund 	l3_proto = ETH_P_ALL;
837bcddc196SSteen Hegelund 	err = sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto);
838bcddc196SSteen Hegelund 	if (err)
839bcddc196SSteen Hegelund 		goto out;
84040e7fe18SSteen Hegelund 
84140e7fe18SSteen Hegelund 	err = sparx5_tc_add_rule_counter(admin, vrule);
84240e7fe18SSteen Hegelund 	if (err)
84340e7fe18SSteen Hegelund 		goto out;
84440e7fe18SSteen Hegelund 
84588bd9ea7SSteen Hegelund 	err = sparx5_tc_add_rule_link_target(admin, vrule,
84688bd9ea7SSteen Hegelund 					     fco->common.chain_index);
84788bd9ea7SSteen Hegelund 	if (err)
84888bd9ea7SSteen Hegelund 		goto out;
84988bd9ea7SSteen Hegelund 
850392d0ab0SSteen Hegelund 	frule = flow_cls_offload_flow_rule(fco);
851c9da1ac1SSteen Hegelund 	flow_action_for_each(idx, act, &frule->action) {
852c9da1ac1SSteen Hegelund 		switch (act->id) {
8536ebf182bSDaniel Machon 		case FLOW_ACTION_GATE: {
8546ebf182bSDaniel Machon 			err = sparx5_tc_flower_parse_act_gate(&sg, act, extack);
8556ebf182bSDaniel Machon 			if (err < 0)
8566ebf182bSDaniel Machon 				goto out;
8576ebf182bSDaniel Machon 
8586ebf182bSDaniel Machon 			tc_sg_idx = act->hw_index;
8596ebf182bSDaniel Machon 
8606ebf182bSDaniel Machon 			break;
8616ebf182bSDaniel Machon 		}
8626ebf182bSDaniel Machon 		case FLOW_ACTION_POLICE: {
8636ebf182bSDaniel Machon 			err = sparx5_tc_flower_parse_act_police(&fm.pol, act,
8646ebf182bSDaniel Machon 								extack);
8656ebf182bSDaniel Machon 			if (err < 0)
8666ebf182bSDaniel Machon 				goto out;
8676ebf182bSDaniel Machon 
8686ebf182bSDaniel Machon 			tc_pol_idx = fm.pol.idx;
8696ebf182bSDaniel Machon 			sf.max_sdu = act->police.mtu;
8706ebf182bSDaniel Machon 
8716ebf182bSDaniel Machon 			break;
8726ebf182bSDaniel Machon 		}
873c9da1ac1SSteen Hegelund 		case FLOW_ACTION_TRAP:
874*52b28a93SSteen Hegelund 			err = sparx5_tc_action_trap(admin, vrule, fco);
875c9da1ac1SSteen Hegelund 			if (err)
876c9da1ac1SSteen Hegelund 				goto out;
877c9da1ac1SSteen Hegelund 			break;
878c9da1ac1SSteen Hegelund 		case FLOW_ACTION_ACCEPT:
879542e6e2cSSteen Hegelund 			err = sparx5_tc_set_actionset(admin, vrule);
880c9da1ac1SSteen Hegelund 			if (err)
881c9da1ac1SSteen Hegelund 				goto out;
882c9da1ac1SSteen Hegelund 			break;
883392d0ab0SSteen Hegelund 		case FLOW_ACTION_GOTO:
884542e6e2cSSteen Hegelund 			err = sparx5_tc_set_actionset(admin, vrule);
885542e6e2cSSteen Hegelund 			if (err)
886542e6e2cSSteen Hegelund 				goto out;
88788bd9ea7SSteen Hegelund 			sparx5_tc_add_rule_link(vctrl, admin, vrule,
88888bd9ea7SSteen Hegelund 						fco->common.chain_index,
88988bd9ea7SSteen Hegelund 						act->chain_index);
890392d0ab0SSteen Hegelund 			break;
891c9da1ac1SSteen Hegelund 		default:
892c9da1ac1SSteen Hegelund 			NL_SET_ERR_MSG_MOD(fco->common.extack,
893c9da1ac1SSteen Hegelund 					   "Unsupported TC action");
894c9da1ac1SSteen Hegelund 			err = -EOPNOTSUPP;
895c9da1ac1SSteen Hegelund 			goto out;
896c9da1ac1SSteen Hegelund 		}
897c9da1ac1SSteen Hegelund 	}
8980ca60948SSteen Hegelund 
8996ebf182bSDaniel Machon 	/* Setup PSFP */
9006ebf182bSDaniel Machon 	if (tc_sg_idx >= 0 || tc_pol_idx >= 0) {
9016ebf182bSDaniel Machon 		err = sparx5_tc_flower_psfp_setup(sparx5, vrule, tc_sg_idx,
9026ebf182bSDaniel Machon 						  tc_pol_idx, &sg, &fm, &sf);
9036ebf182bSDaniel Machon 		if (err)
9046ebf182bSDaniel Machon 			goto out;
9056ebf182bSDaniel Machon 	}
9066ebf182bSDaniel Machon 
9070ca60948SSteen Hegelund 	err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin, l3_proto,
9080ca60948SSteen Hegelund 					       &multi);
9090ca60948SSteen Hegelund 	if (err) {
9100ca60948SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
9110ca60948SSteen Hegelund 				   "No matching port keyset for filter protocol and keys");
9120ca60948SSteen Hegelund 		goto out;
9130ca60948SSteen Hegelund 	}
9140ca60948SSteen Hegelund 
915abc4010dSSteen Hegelund 	/* provide the l3 protocol to guide the keyset selection */
916abc4010dSSteen Hegelund 	err = vcap_val_rule(vrule, l3_proto);
917c9da1ac1SSteen Hegelund 	if (err) {
918c9da1ac1SSteen Hegelund 		vcap_set_tc_exterr(fco, vrule);
919c9da1ac1SSteen Hegelund 		goto out;
920c9da1ac1SSteen Hegelund 	}
921c9da1ac1SSteen Hegelund 	err = vcap_add_rule(vrule);
922c9da1ac1SSteen Hegelund 	if (err)
923c9da1ac1SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack,
924c9da1ac1SSteen Hegelund 				   "Could not add the filter");
9250ca60948SSteen Hegelund 
9260ca60948SSteen Hegelund 	if (l3_proto == ETH_P_ALL)
9270ca60948SSteen Hegelund 		err = sparx5_tc_add_remaining_rules(vctrl, fco, vrule, admin,
9280ca60948SSteen Hegelund 						    &multi);
9290ca60948SSteen Hegelund 
930c9da1ac1SSteen Hegelund out:
931c9da1ac1SSteen Hegelund 	vcap_free_rule(vrule);
932c9da1ac1SSteen Hegelund 	return err;
933c9da1ac1SSteen Hegelund }
934c9da1ac1SSteen Hegelund 
9356ebf182bSDaniel Machon static void sparx5_tc_free_psfp_resources(struct sparx5 *sparx5,
9366ebf182bSDaniel Machon 					  struct vcap_rule *vrule)
9376ebf182bSDaniel Machon {
9386ebf182bSDaniel Machon 	struct vcap_client_actionfield *afield;
9396ebf182bSDaniel Machon 	u32 isdx, sfid, sgid, fmid;
9406ebf182bSDaniel Machon 
9416ebf182bSDaniel Machon 	/* Check if VCAP_AF_ISDX_VAL action is set for this rule - and if
9426ebf182bSDaniel Machon 	 * it is used for stream and/or flow-meter classification.
9436ebf182bSDaniel Machon 	 */
9446ebf182bSDaniel Machon 	afield = vcap_find_actionfield(vrule, VCAP_AF_ISDX_VAL);
9456ebf182bSDaniel Machon 	if (!afield)
9466ebf182bSDaniel Machon 		return;
9476ebf182bSDaniel Machon 
9486ebf182bSDaniel Machon 	isdx = afield->data.u32.value;
9496ebf182bSDaniel Machon 	sfid = sparx5_psfp_isdx_get_sf(sparx5, isdx);
9506ebf182bSDaniel Machon 
9516ebf182bSDaniel Machon 	if (!sfid)
9526ebf182bSDaniel Machon 		return;
9536ebf182bSDaniel Machon 
9546ebf182bSDaniel Machon 	fmid = sparx5_psfp_isdx_get_fm(sparx5, isdx);
9556ebf182bSDaniel Machon 	sgid = sparx5_psfp_sf_get_sg(sparx5, sfid);
9566ebf182bSDaniel Machon 
9576ebf182bSDaniel Machon 	if (fmid && sparx5_psfp_fm_del(sparx5, fmid) < 0)
9586ebf182bSDaniel Machon 		pr_err("%s:%d Could not delete invalid fmid: %d", __func__,
9596ebf182bSDaniel Machon 		       __LINE__, fmid);
9606ebf182bSDaniel Machon 
9616ebf182bSDaniel Machon 	if (sgid && sparx5_psfp_sg_del(sparx5, sgid) < 0)
9626ebf182bSDaniel Machon 		pr_err("%s:%d Could not delete invalid sgid: %d", __func__,
9636ebf182bSDaniel Machon 		       __LINE__, sgid);
9646ebf182bSDaniel Machon 
9656ebf182bSDaniel Machon 	if (sparx5_psfp_sf_del(sparx5, sfid) < 0)
9666ebf182bSDaniel Machon 		pr_err("%s:%d Could not delete invalid sfid: %d", __func__,
9676ebf182bSDaniel Machon 		       __LINE__, sfid);
9686ebf182bSDaniel Machon 
9696ebf182bSDaniel Machon 	sparx5_isdx_conf_set(sparx5, isdx, 0, 0);
9706ebf182bSDaniel Machon }
9716ebf182bSDaniel Machon 
9726ebf182bSDaniel Machon static int sparx5_tc_free_rule_resources(struct net_device *ndev,
9736ebf182bSDaniel Machon 					 struct vcap_control *vctrl,
9746ebf182bSDaniel Machon 					 int rule_id)
9756ebf182bSDaniel Machon {
9766ebf182bSDaniel Machon 	struct sparx5_port *port = netdev_priv(ndev);
9776ebf182bSDaniel Machon 	struct sparx5 *sparx5 = port->sparx5;
9786ebf182bSDaniel Machon 	struct vcap_rule *vrule;
9796ebf182bSDaniel Machon 	int ret = 0;
9806ebf182bSDaniel Machon 
9816ebf182bSDaniel Machon 	vrule = vcap_get_rule(vctrl, rule_id);
9826ebf182bSDaniel Machon 	if (!vrule || IS_ERR(vrule))
9836ebf182bSDaniel Machon 		return -EINVAL;
9846ebf182bSDaniel Machon 
9856ebf182bSDaniel Machon 	sparx5_tc_free_psfp_resources(sparx5, vrule);
9866ebf182bSDaniel Machon 
9876ebf182bSDaniel Machon 	vcap_free_rule(vrule);
9886ebf182bSDaniel Machon 	return ret;
9896ebf182bSDaniel Machon }
9906ebf182bSDaniel Machon 
991c9da1ac1SSteen Hegelund static int sparx5_tc_flower_destroy(struct net_device *ndev,
992c9da1ac1SSteen Hegelund 				    struct flow_cls_offload *fco,
993c9da1ac1SSteen Hegelund 				    struct vcap_admin *admin)
994c9da1ac1SSteen Hegelund {
995c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
9966ebf182bSDaniel Machon 	int err = -ENOENT, count = 0, rule_id;
997c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
998c9da1ac1SSteen Hegelund 
999c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
1000c9da1ac1SSteen Hegelund 	while (true) {
1001c9da1ac1SSteen Hegelund 		rule_id = vcap_lookup_rule_by_cookie(vctrl, fco->cookie);
1002c9da1ac1SSteen Hegelund 		if (rule_id <= 0)
1003c9da1ac1SSteen Hegelund 			break;
10046ebf182bSDaniel Machon 		if (count == 0) {
10056ebf182bSDaniel Machon 			/* Resources are attached to the first rule of
10066ebf182bSDaniel Machon 			 * a set of rules. Only works if the rules are
10076ebf182bSDaniel Machon 			 * in the correct order.
10086ebf182bSDaniel Machon 			 */
10096ebf182bSDaniel Machon 			err = sparx5_tc_free_rule_resources(ndev, vctrl,
10106ebf182bSDaniel Machon 							    rule_id);
10116ebf182bSDaniel Machon 			if (err)
10126ebf182bSDaniel Machon 				pr_err("%s:%d: could not free resources %d\n",
10136ebf182bSDaniel Machon 				       __func__, __LINE__, rule_id);
10146ebf182bSDaniel Machon 		}
1015c9da1ac1SSteen Hegelund 		err = vcap_del_rule(vctrl, ndev, rule_id);
1016c9da1ac1SSteen Hegelund 		if (err) {
1017c9da1ac1SSteen Hegelund 			pr_err("%s:%d: could not delete rule %d\n",
1018c9da1ac1SSteen Hegelund 			       __func__, __LINE__, rule_id);
1019c9da1ac1SSteen Hegelund 			break;
1020c9da1ac1SSteen Hegelund 		}
1021c9da1ac1SSteen Hegelund 	}
1022c9da1ac1SSteen Hegelund 	return err;
1023c9da1ac1SSteen Hegelund }
1024c9da1ac1SSteen Hegelund 
102540e7fe18SSteen Hegelund static int sparx5_tc_flower_stats(struct net_device *ndev,
102640e7fe18SSteen Hegelund 				  struct flow_cls_offload *fco,
102740e7fe18SSteen Hegelund 				  struct vcap_admin *admin)
102840e7fe18SSteen Hegelund {
102940e7fe18SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
103027d293ccSSteen Hegelund 	struct vcap_counter ctr = {};
103140e7fe18SSteen Hegelund 	struct vcap_control *vctrl;
103240e7fe18SSteen Hegelund 	ulong lastused = 0;
103340e7fe18SSteen Hegelund 	int err;
103440e7fe18SSteen Hegelund 
103540e7fe18SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
103627d293ccSSteen Hegelund 	err = vcap_get_rule_count_by_cookie(vctrl, &ctr, fco->cookie);
103740e7fe18SSteen Hegelund 	if (err)
103840e7fe18SSteen Hegelund 		return err;
103927d293ccSSteen Hegelund 	flow_stats_update(&fco->stats, 0x0, ctr.value, 0, lastused,
104040e7fe18SSteen Hegelund 			  FLOW_ACTION_HW_STATS_IMMEDIATE);
104140e7fe18SSteen Hegelund 	return err;
104240e7fe18SSteen Hegelund }
104340e7fe18SSteen Hegelund 
1044c9da1ac1SSteen Hegelund int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco,
1045c9da1ac1SSteen Hegelund 		     bool ingress)
1046c9da1ac1SSteen Hegelund {
1047c9da1ac1SSteen Hegelund 	struct sparx5_port *port = netdev_priv(ndev);
1048c9da1ac1SSteen Hegelund 	struct vcap_control *vctrl;
1049c9da1ac1SSteen Hegelund 	struct vcap_admin *admin;
1050c9da1ac1SSteen Hegelund 	int err = -EINVAL;
1051c9da1ac1SSteen Hegelund 
1052c9da1ac1SSteen Hegelund 	/* Get vcap instance from the chain id */
1053c9da1ac1SSteen Hegelund 	vctrl = port->sparx5->vcap_ctrl;
1054c9da1ac1SSteen Hegelund 	admin = vcap_find_admin(vctrl, fco->common.chain_index);
1055c9da1ac1SSteen Hegelund 	if (!admin) {
1056c9da1ac1SSteen Hegelund 		NL_SET_ERR_MSG_MOD(fco->common.extack, "Invalid chain");
1057c9da1ac1SSteen Hegelund 		return err;
1058c9da1ac1SSteen Hegelund 	}
1059c9da1ac1SSteen Hegelund 
1060c9da1ac1SSteen Hegelund 	switch (fco->command) {
1061c9da1ac1SSteen Hegelund 	case FLOW_CLS_REPLACE:
1062e7e3f514SSteen Hegelund 		return sparx5_tc_flower_replace(ndev, fco, admin, ingress);
1063c9da1ac1SSteen Hegelund 	case FLOW_CLS_DESTROY:
1064c9da1ac1SSteen Hegelund 		return sparx5_tc_flower_destroy(ndev, fco, admin);
106540e7fe18SSteen Hegelund 	case FLOW_CLS_STATS:
106640e7fe18SSteen Hegelund 		return sparx5_tc_flower_stats(ndev, fco, admin);
1067c9da1ac1SSteen Hegelund 	default:
1068c9da1ac1SSteen Hegelund 		return -EOPNOTSUPP;
1069c9da1ac1SSteen Hegelund 	}
1070c9da1ac1SSteen Hegelund }
1071