13643abd6SHoratiu Vultur // SPDX-License-Identifier: GPL-2.0+
23643abd6SHoratiu Vultur 
33643abd6SHoratiu Vultur #include "lan966x_main.h"
43643abd6SHoratiu Vultur #include "vcap_api.h"
53643abd6SHoratiu Vultur #include "vcap_api_client.h"
647400aaeSHoratiu Vultur #include "vcap_tc.h"
73643abd6SHoratiu Vultur 
847400aaeSHoratiu Vultur static bool lan966x_tc_is_known_etype(u16 etype)
93643abd6SHoratiu Vultur {
1047400aaeSHoratiu Vultur 	switch (etype) {
1147400aaeSHoratiu Vultur 	case ETH_P_ALL:
1247400aaeSHoratiu Vultur 	case ETH_P_ARP:
1347400aaeSHoratiu Vultur 	case ETH_P_IP:
1447400aaeSHoratiu Vultur 	case ETH_P_IPV6:
1547400aaeSHoratiu Vultur 		return true;
1647400aaeSHoratiu Vultur 	}
1747400aaeSHoratiu Vultur 
1847400aaeSHoratiu Vultur 	return false;
1947400aaeSHoratiu Vultur }
2047400aaeSHoratiu Vultur 
2147400aaeSHoratiu Vultur static int
2247400aaeSHoratiu Vultur lan966x_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage *st)
2347400aaeSHoratiu Vultur {
2447400aaeSHoratiu Vultur 	struct flow_match_control match;
253643abd6SHoratiu Vultur 	int err = 0;
263643abd6SHoratiu Vultur 
2747400aaeSHoratiu Vultur 	flow_rule_match_control(st->frule, &match);
2847400aaeSHoratiu Vultur 	if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) {
2947400aaeSHoratiu Vultur 		if (match.key->flags & FLOW_DIS_IS_FRAGMENT)
3047400aaeSHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule,
3147400aaeSHoratiu Vultur 						    VCAP_KF_L3_FRAGMENT,
3247400aaeSHoratiu Vultur 						    VCAP_BIT_1);
3347400aaeSHoratiu Vultur 		else
3447400aaeSHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule,
3547400aaeSHoratiu Vultur 						    VCAP_KF_L3_FRAGMENT,
3647400aaeSHoratiu Vultur 						    VCAP_BIT_0);
373643abd6SHoratiu Vultur 		if (err)
383643abd6SHoratiu Vultur 			goto out;
393643abd6SHoratiu Vultur 	}
403643abd6SHoratiu Vultur 
4147400aaeSHoratiu Vultur 	if (match.mask->flags & FLOW_DIS_FIRST_FRAG) {
4247400aaeSHoratiu Vultur 		if (match.key->flags & FLOW_DIS_FIRST_FRAG)
4347400aaeSHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule,
4447400aaeSHoratiu Vultur 						    VCAP_KF_L3_FRAG_OFS_GT0,
4547400aaeSHoratiu Vultur 						    VCAP_BIT_0);
4647400aaeSHoratiu Vultur 		else
4747400aaeSHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule,
4847400aaeSHoratiu Vultur 						    VCAP_KF_L3_FRAG_OFS_GT0,
4947400aaeSHoratiu Vultur 						    VCAP_BIT_1);
503643abd6SHoratiu Vultur 		if (err)
513643abd6SHoratiu Vultur 			goto out;
523643abd6SHoratiu Vultur 	}
533643abd6SHoratiu Vultur 
5447400aaeSHoratiu Vultur 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL);
553643abd6SHoratiu Vultur 
563643abd6SHoratiu Vultur 	return err;
573643abd6SHoratiu Vultur 
583643abd6SHoratiu Vultur out:
5947400aaeSHoratiu Vultur 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error");
603643abd6SHoratiu Vultur 	return err;
613643abd6SHoratiu Vultur }
623643abd6SHoratiu Vultur 
633643abd6SHoratiu Vultur static int
6447400aaeSHoratiu Vultur lan966x_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st)
6547400aaeSHoratiu Vultur {
6647400aaeSHoratiu Vultur 	struct flow_match_basic match;
6747400aaeSHoratiu Vultur 	int err = 0;
6847400aaeSHoratiu Vultur 
6947400aaeSHoratiu Vultur 	flow_rule_match_basic(st->frule, &match);
7047400aaeSHoratiu Vultur 	if (match.mask->n_proto) {
7147400aaeSHoratiu Vultur 		st->l3_proto = be16_to_cpu(match.key->n_proto);
7247400aaeSHoratiu Vultur 		if (!lan966x_tc_is_known_etype(st->l3_proto)) {
7347400aaeSHoratiu Vultur 			err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,
7447400aaeSHoratiu Vultur 						    st->l3_proto, ~0);
7547400aaeSHoratiu Vultur 			if (err)
7647400aaeSHoratiu Vultur 				goto out;
7747400aaeSHoratiu Vultur 		} else if (st->l3_proto == ETH_P_IP) {
7847400aaeSHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
7947400aaeSHoratiu Vultur 						    VCAP_BIT_1);
8047400aaeSHoratiu Vultur 			if (err)
8147400aaeSHoratiu Vultur 				goto out;
82*135c2116SHoratiu Vultur 		} else if (st->l3_proto == ETH_P_IPV6 &&
83*135c2116SHoratiu Vultur 			   st->admin->vtype == VCAP_TYPE_IS1) {
84*135c2116SHoratiu Vultur 			/* Don't set any keys in this case */
85*135c2116SHoratiu Vultur 		} else if (st->l3_proto == ETH_P_SNAP &&
86*135c2116SHoratiu Vultur 			   st->admin->vtype == VCAP_TYPE_IS1) {
87*135c2116SHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule,
88*135c2116SHoratiu Vultur 						    VCAP_KF_ETYPE_LEN_IS,
89*135c2116SHoratiu Vultur 						    VCAP_BIT_0);
90*135c2116SHoratiu Vultur 			if (err)
91*135c2116SHoratiu Vultur 				goto out;
92*135c2116SHoratiu Vultur 
93*135c2116SHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule,
94*135c2116SHoratiu Vultur 						    VCAP_KF_IP_SNAP_IS,
95*135c2116SHoratiu Vultur 						    VCAP_BIT_1);
96*135c2116SHoratiu Vultur 			if (err)
97*135c2116SHoratiu Vultur 				goto out;
98*135c2116SHoratiu Vultur 		} else if (st->admin->vtype == VCAP_TYPE_IS1) {
99*135c2116SHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule,
100*135c2116SHoratiu Vultur 						    VCAP_KF_ETYPE_LEN_IS,
101*135c2116SHoratiu Vultur 						    VCAP_BIT_1);
102*135c2116SHoratiu Vultur 			if (err)
103*135c2116SHoratiu Vultur 				goto out;
104*135c2116SHoratiu Vultur 
105*135c2116SHoratiu Vultur 			err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,
106*135c2116SHoratiu Vultur 						    st->l3_proto, ~0);
107*135c2116SHoratiu Vultur 			if (err)
108*135c2116SHoratiu Vultur 				goto out;
10947400aaeSHoratiu Vultur 		}
11047400aaeSHoratiu Vultur 	}
11147400aaeSHoratiu Vultur 	if (match.mask->ip_proto) {
11247400aaeSHoratiu Vultur 		st->l4_proto = match.key->ip_proto;
11347400aaeSHoratiu Vultur 
11447400aaeSHoratiu Vultur 		if (st->l4_proto == IPPROTO_TCP) {
115*135c2116SHoratiu Vultur 			if (st->admin->vtype == VCAP_TYPE_IS1) {
116*135c2116SHoratiu Vultur 				err = vcap_rule_add_key_bit(st->vrule,
117*135c2116SHoratiu Vultur 							    VCAP_KF_TCP_UDP_IS,
118*135c2116SHoratiu Vultur 							    VCAP_BIT_1);
119*135c2116SHoratiu Vultur 				if (err)
120*135c2116SHoratiu Vultur 					goto out;
121*135c2116SHoratiu Vultur 			}
122*135c2116SHoratiu Vultur 
12347400aaeSHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule,
12447400aaeSHoratiu Vultur 						    VCAP_KF_TCP_IS,
12547400aaeSHoratiu Vultur 						    VCAP_BIT_1);
12647400aaeSHoratiu Vultur 			if (err)
12747400aaeSHoratiu Vultur 				goto out;
12847400aaeSHoratiu Vultur 		} else if (st->l4_proto == IPPROTO_UDP) {
129*135c2116SHoratiu Vultur 			if (st->admin->vtype == VCAP_TYPE_IS1) {
130*135c2116SHoratiu Vultur 				err = vcap_rule_add_key_bit(st->vrule,
131*135c2116SHoratiu Vultur 							    VCAP_KF_TCP_UDP_IS,
132*135c2116SHoratiu Vultur 							    VCAP_BIT_1);
133*135c2116SHoratiu Vultur 				if (err)
134*135c2116SHoratiu Vultur 					goto out;
135*135c2116SHoratiu Vultur 			}
136*135c2116SHoratiu Vultur 
13747400aaeSHoratiu Vultur 			err = vcap_rule_add_key_bit(st->vrule,
13847400aaeSHoratiu Vultur 						    VCAP_KF_TCP_IS,
13947400aaeSHoratiu Vultur 						    VCAP_BIT_0);
14047400aaeSHoratiu Vultur 			if (err)
14147400aaeSHoratiu Vultur 				goto out;
14247400aaeSHoratiu Vultur 		} else {
14347400aaeSHoratiu Vultur 			err = vcap_rule_add_key_u32(st->vrule,
14447400aaeSHoratiu Vultur 						    VCAP_KF_L3_IP_PROTO,
14547400aaeSHoratiu Vultur 						    st->l4_proto, ~0);
14647400aaeSHoratiu Vultur 			if (err)
14747400aaeSHoratiu Vultur 				goto out;
14847400aaeSHoratiu Vultur 		}
14947400aaeSHoratiu Vultur 	}
15047400aaeSHoratiu Vultur 
15147400aaeSHoratiu Vultur 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC);
15247400aaeSHoratiu Vultur 	return err;
15347400aaeSHoratiu Vultur out:
15447400aaeSHoratiu Vultur 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error");
15547400aaeSHoratiu Vultur 	return err;
15647400aaeSHoratiu Vultur }
15747400aaeSHoratiu Vultur 
15847400aaeSHoratiu Vultur static int
159*135c2116SHoratiu Vultur lan966x_tc_flower_handler_cvlan_usage(struct vcap_tc_flower_parse_usage *st)
160*135c2116SHoratiu Vultur {
161*135c2116SHoratiu Vultur 	if (st->admin->vtype != VCAP_TYPE_IS1) {
162*135c2116SHoratiu Vultur 		NL_SET_ERR_MSG_MOD(st->fco->common.extack,
163*135c2116SHoratiu Vultur 				   "cvlan not supported in this VCAP");
164*135c2116SHoratiu Vultur 		return -EINVAL;
165*135c2116SHoratiu Vultur 	}
166*135c2116SHoratiu Vultur 
167*135c2116SHoratiu Vultur 	return vcap_tc_flower_handler_cvlan_usage(st);
168*135c2116SHoratiu Vultur }
169*135c2116SHoratiu Vultur 
170*135c2116SHoratiu Vultur static int
17147400aaeSHoratiu Vultur lan966x_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st)
17247400aaeSHoratiu Vultur {
173*135c2116SHoratiu Vultur 	enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS;
174*135c2116SHoratiu Vultur 	enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS;
175*135c2116SHoratiu Vultur 
176*135c2116SHoratiu Vultur 	if (st->admin->vtype == VCAP_TYPE_IS1) {
177*135c2116SHoratiu Vultur 		vid_key = VCAP_KF_8021Q_VID0;
178*135c2116SHoratiu Vultur 		pcp_key = VCAP_KF_8021Q_PCP0;
179*135c2116SHoratiu Vultur 	}
180*135c2116SHoratiu Vultur 
181*135c2116SHoratiu Vultur 	return vcap_tc_flower_handler_vlan_usage(st, vid_key, pcp_key);
18247400aaeSHoratiu Vultur }
18347400aaeSHoratiu Vultur 
18447400aaeSHoratiu Vultur static int
18547400aaeSHoratiu Vultur (*lan966x_tc_flower_handlers_usage[])(struct vcap_tc_flower_parse_usage *st) = {
18647400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_ETH_ADDRS] = vcap_tc_flower_handler_ethaddr_usage,
18747400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_IPV4_ADDRS] = vcap_tc_flower_handler_ipv4_usage,
18847400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_IPV6_ADDRS] = vcap_tc_flower_handler_ipv6_usage,
18947400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_CONTROL] = lan966x_tc_flower_handler_control_usage,
19047400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_PORTS] = vcap_tc_flower_handler_portnum_usage,
19147400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_BASIC] = lan966x_tc_flower_handler_basic_usage,
192*135c2116SHoratiu Vultur 	[FLOW_DISSECTOR_KEY_CVLAN] = lan966x_tc_flower_handler_cvlan_usage,
19347400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_VLAN] = lan966x_tc_flower_handler_vlan_usage,
19447400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_TCP] = vcap_tc_flower_handler_tcp_usage,
19547400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_ARP] = vcap_tc_flower_handler_arp_usage,
19647400aaeSHoratiu Vultur 	[FLOW_DISSECTOR_KEY_IP] = vcap_tc_flower_handler_ip_usage,
1973643abd6SHoratiu Vultur };
1983643abd6SHoratiu Vultur 
1993643abd6SHoratiu Vultur static int lan966x_tc_flower_use_dissectors(struct flow_cls_offload *f,
2003643abd6SHoratiu Vultur 					    struct vcap_admin *admin,
2013643abd6SHoratiu Vultur 					    struct vcap_rule *vrule,
2023643abd6SHoratiu Vultur 					    u16 *l3_proto)
2033643abd6SHoratiu Vultur {
20447400aaeSHoratiu Vultur 	struct vcap_tc_flower_parse_usage state = {
20547400aaeSHoratiu Vultur 		.fco = f,
2063643abd6SHoratiu Vultur 		.vrule = vrule,
2073643abd6SHoratiu Vultur 		.l3_proto = ETH_P_ALL,
208*135c2116SHoratiu Vultur 		.admin = admin,
2093643abd6SHoratiu Vultur 	};
2103643abd6SHoratiu Vultur 	int err = 0;
2113643abd6SHoratiu Vultur 
2123643abd6SHoratiu Vultur 	state.frule = flow_cls_offload_flow_rule(f);
2133643abd6SHoratiu Vultur 	for (int i = 0; i < ARRAY_SIZE(lan966x_tc_flower_handlers_usage); ++i) {
2143643abd6SHoratiu Vultur 		if (!flow_rule_match_key(state.frule, i) ||
2153643abd6SHoratiu Vultur 		    !lan966x_tc_flower_handlers_usage[i])
2163643abd6SHoratiu Vultur 			continue;
2173643abd6SHoratiu Vultur 
2183643abd6SHoratiu Vultur 		err = lan966x_tc_flower_handlers_usage[i](&state);
2193643abd6SHoratiu Vultur 		if (err)
2203643abd6SHoratiu Vultur 			return err;
2213643abd6SHoratiu Vultur 	}
2223643abd6SHoratiu Vultur 
2233643abd6SHoratiu Vultur 	if (l3_proto)
2243643abd6SHoratiu Vultur 		*l3_proto = state.l3_proto;
2253643abd6SHoratiu Vultur 
2263643abd6SHoratiu Vultur 	return err;
2273643abd6SHoratiu Vultur }
2283643abd6SHoratiu Vultur 
2293643abd6SHoratiu Vultur static int lan966x_tc_flower_action_check(struct vcap_control *vctrl,
230784c3067SSteen Hegelund 					  struct net_device *dev,
231e7e3f514SSteen Hegelund 					  struct flow_cls_offload *fco,
232e7e3f514SSteen Hegelund 					  bool ingress)
2333643abd6SHoratiu Vultur {
2343643abd6SHoratiu Vultur 	struct flow_rule *rule = flow_cls_offload_flow_rule(fco);
2353643abd6SHoratiu Vultur 	struct flow_action_entry *actent, *last_actent = NULL;
2363643abd6SHoratiu Vultur 	struct flow_action *act = &rule->action;
2373643abd6SHoratiu Vultur 	u64 action_mask = 0;
2383643abd6SHoratiu Vultur 	int idx;
2393643abd6SHoratiu Vultur 
2403643abd6SHoratiu Vultur 	if (!flow_action_has_entries(act)) {
2413643abd6SHoratiu Vultur 		NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
2423643abd6SHoratiu Vultur 		return -EINVAL;
2433643abd6SHoratiu Vultur 	}
2443643abd6SHoratiu Vultur 
2453643abd6SHoratiu Vultur 	if (!flow_action_basic_hw_stats_check(act, fco->common.extack))
2463643abd6SHoratiu Vultur 		return -EOPNOTSUPP;
2473643abd6SHoratiu Vultur 
2483643abd6SHoratiu Vultur 	flow_action_for_each(idx, actent, act) {
2493643abd6SHoratiu Vultur 		if (action_mask & BIT(actent->id)) {
2503643abd6SHoratiu Vultur 			NL_SET_ERR_MSG_MOD(fco->common.extack,
2513643abd6SHoratiu Vultur 					   "More actions of the same type");
2523643abd6SHoratiu Vultur 			return -EINVAL;
2533643abd6SHoratiu Vultur 		}
2543643abd6SHoratiu Vultur 		action_mask |= BIT(actent->id);
2553643abd6SHoratiu Vultur 		last_actent = actent; /* Save last action for later check */
2563643abd6SHoratiu Vultur 	}
2573643abd6SHoratiu Vultur 
258784c3067SSteen Hegelund 	/* Check that last action is a goto
259784c3067SSteen Hegelund 	 * The last chain/lookup does not need to have 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 */
2633643abd6SHoratiu Vultur 		if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
2643643abd6SHoratiu Vultur 					 last_actent->chain_index)) {
2653643abd6SHoratiu Vultur 			NL_SET_ERR_MSG_MOD(fco->common.extack,
2663643abd6SHoratiu Vultur 					   "Invalid goto chain");
2673643abd6SHoratiu Vultur 			return -EINVAL;
2683643abd6SHoratiu Vultur 		}
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 	}
2753643abd6SHoratiu Vultur 
2763643abd6SHoratiu Vultur 	/* Catch unsupported combinations of actions */
2773643abd6SHoratiu Vultur 	if (action_mask & BIT(FLOW_ACTION_TRAP) &&
2783643abd6SHoratiu Vultur 	    action_mask & BIT(FLOW_ACTION_ACCEPT)) {
2793643abd6SHoratiu Vultur 		NL_SET_ERR_MSG_MOD(fco->common.extack,
2803643abd6SHoratiu Vultur 				   "Cannot combine pass and trap action");
2813643abd6SHoratiu Vultur 		return -EOPNOTSUPP;
2823643abd6SHoratiu Vultur 	}
2833643abd6SHoratiu Vultur 
2843643abd6SHoratiu Vultur 	return 0;
2853643abd6SHoratiu Vultur }
2863643abd6SHoratiu Vultur 
287*135c2116SHoratiu Vultur /* Add the actionset that is the default for the VCAP type */
288*135c2116SHoratiu Vultur static int lan966x_tc_set_actionset(struct vcap_admin *admin,
289*135c2116SHoratiu Vultur 				    struct vcap_rule *vrule)
290*135c2116SHoratiu Vultur {
291*135c2116SHoratiu Vultur 	enum vcap_actionfield_set aset;
292*135c2116SHoratiu Vultur 	int err = 0;
293*135c2116SHoratiu Vultur 
294*135c2116SHoratiu Vultur 	switch (admin->vtype) {
295*135c2116SHoratiu Vultur 	case VCAP_TYPE_IS1:
296*135c2116SHoratiu Vultur 		aset = VCAP_AFS_S1;
297*135c2116SHoratiu Vultur 		break;
298*135c2116SHoratiu Vultur 	case VCAP_TYPE_IS2:
299*135c2116SHoratiu Vultur 		aset = VCAP_AFS_BASE_TYPE;
300*135c2116SHoratiu Vultur 		break;
301*135c2116SHoratiu Vultur 	default:
302*135c2116SHoratiu Vultur 		return -EINVAL;
303*135c2116SHoratiu Vultur 	}
304*135c2116SHoratiu Vultur 
305*135c2116SHoratiu Vultur 	/* Do not overwrite any current actionset */
306*135c2116SHoratiu Vultur 	if (vrule->actionset == VCAP_AFS_NO_VALUE)
307*135c2116SHoratiu Vultur 		err = vcap_set_rule_set_actionset(vrule, aset);
308*135c2116SHoratiu Vultur 
309*135c2116SHoratiu Vultur 	return err;
310*135c2116SHoratiu Vultur }
311*135c2116SHoratiu Vultur 
3123643abd6SHoratiu Vultur static int lan966x_tc_flower_add(struct lan966x_port *port,
3133643abd6SHoratiu Vultur 				 struct flow_cls_offload *f,
314e7e3f514SSteen Hegelund 				 struct vcap_admin *admin,
315e7e3f514SSteen Hegelund 				 bool ingress)
3163643abd6SHoratiu Vultur {
3173643abd6SHoratiu Vultur 	struct flow_action_entry *act;
3183643abd6SHoratiu Vultur 	u16 l3_proto = ETH_P_ALL;
3193643abd6SHoratiu Vultur 	struct flow_rule *frule;
3203643abd6SHoratiu Vultur 	struct vcap_rule *vrule;
3213643abd6SHoratiu Vultur 	int err, idx;
3223643abd6SHoratiu Vultur 
323784c3067SSteen Hegelund 	err = lan966x_tc_flower_action_check(port->lan966x->vcap_ctrl,
324e7e3f514SSteen Hegelund 					     port->dev, f, ingress);
3253643abd6SHoratiu Vultur 	if (err)
3263643abd6SHoratiu Vultur 		return err;
3273643abd6SHoratiu Vultur 
3283643abd6SHoratiu Vultur 	vrule = vcap_alloc_rule(port->lan966x->vcap_ctrl, port->dev,
3293643abd6SHoratiu Vultur 				f->common.chain_index, VCAP_USER_TC,
3303643abd6SHoratiu Vultur 				f->common.prio, 0);
3313643abd6SHoratiu Vultur 	if (IS_ERR(vrule))
3323643abd6SHoratiu Vultur 		return PTR_ERR(vrule);
3333643abd6SHoratiu Vultur 
3343643abd6SHoratiu Vultur 	vrule->cookie = f->cookie;
3353643abd6SHoratiu Vultur 	err = lan966x_tc_flower_use_dissectors(f, admin, vrule, &l3_proto);
3363643abd6SHoratiu Vultur 	if (err)
3373643abd6SHoratiu Vultur 		goto out;
3383643abd6SHoratiu Vultur 
3393643abd6SHoratiu Vultur 	frule = flow_cls_offload_flow_rule(f);
3403643abd6SHoratiu Vultur 
3413643abd6SHoratiu Vultur 	flow_action_for_each(idx, act, &frule->action) {
3423643abd6SHoratiu Vultur 		switch (act->id) {
3433643abd6SHoratiu Vultur 		case FLOW_ACTION_TRAP:
344*135c2116SHoratiu Vultur 			if (admin->vtype != VCAP_TYPE_IS2) {
345*135c2116SHoratiu Vultur 				NL_SET_ERR_MSG_MOD(f->common.extack,
346*135c2116SHoratiu Vultur 						   "Trap action not supported in this VCAP");
347*135c2116SHoratiu Vultur 				err = -EOPNOTSUPP;
348*135c2116SHoratiu Vultur 				goto out;
349*135c2116SHoratiu Vultur 			}
350*135c2116SHoratiu Vultur 
3513643abd6SHoratiu Vultur 			err = vcap_rule_add_action_bit(vrule,
3523643abd6SHoratiu Vultur 						       VCAP_AF_CPU_COPY_ENA,
3533643abd6SHoratiu Vultur 						       VCAP_BIT_1);
3543643abd6SHoratiu Vultur 			err |= vcap_rule_add_action_u32(vrule,
3553643abd6SHoratiu Vultur 							VCAP_AF_CPU_QUEUE_NUM,
3563643abd6SHoratiu Vultur 							0);
3573643abd6SHoratiu Vultur 			err |= vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,
3583643abd6SHoratiu Vultur 							LAN966X_PMM_REPLACE);
3593643abd6SHoratiu Vultur 			if (err)
3603643abd6SHoratiu Vultur 				goto out;
3613643abd6SHoratiu Vultur 
3623643abd6SHoratiu Vultur 			break;
3633643abd6SHoratiu Vultur 		case FLOW_ACTION_GOTO:
364*135c2116SHoratiu Vultur 			err = lan966x_tc_set_actionset(admin, vrule);
365*135c2116SHoratiu Vultur 			if (err)
366*135c2116SHoratiu Vultur 				goto out;
367*135c2116SHoratiu Vultur 
3683643abd6SHoratiu Vultur 			break;
3693643abd6SHoratiu Vultur 		default:
3703643abd6SHoratiu Vultur 			NL_SET_ERR_MSG_MOD(f->common.extack,
3713643abd6SHoratiu Vultur 					   "Unsupported TC action");
3723643abd6SHoratiu Vultur 			err = -EOPNOTSUPP;
3733643abd6SHoratiu Vultur 			goto out;
3743643abd6SHoratiu Vultur 		}
3753643abd6SHoratiu Vultur 	}
3763643abd6SHoratiu Vultur 
3773643abd6SHoratiu Vultur 	err = vcap_val_rule(vrule, l3_proto);
3783643abd6SHoratiu Vultur 	if (err) {
3793643abd6SHoratiu Vultur 		vcap_set_tc_exterr(f, vrule);
3803643abd6SHoratiu Vultur 		goto out;
3813643abd6SHoratiu Vultur 	}
3823643abd6SHoratiu Vultur 
3833643abd6SHoratiu Vultur 	err = vcap_add_rule(vrule);
3843643abd6SHoratiu Vultur 	if (err)
3853643abd6SHoratiu Vultur 		NL_SET_ERR_MSG_MOD(f->common.extack,
3863643abd6SHoratiu Vultur 				   "Could not add the filter");
3873643abd6SHoratiu Vultur out:
3883643abd6SHoratiu Vultur 	vcap_free_rule(vrule);
3893643abd6SHoratiu Vultur 	return err;
3903643abd6SHoratiu Vultur }
3913643abd6SHoratiu Vultur 
3923643abd6SHoratiu Vultur static int lan966x_tc_flower_del(struct lan966x_port *port,
3933643abd6SHoratiu Vultur 				 struct flow_cls_offload *f,
3943643abd6SHoratiu Vultur 				 struct vcap_admin *admin)
3953643abd6SHoratiu Vultur {
3963643abd6SHoratiu Vultur 	struct vcap_control *vctrl;
3973643abd6SHoratiu Vultur 	int err = -ENOENT, rule_id;
3983643abd6SHoratiu Vultur 
3993643abd6SHoratiu Vultur 	vctrl = port->lan966x->vcap_ctrl;
4003643abd6SHoratiu Vultur 	while (true) {
4013643abd6SHoratiu Vultur 		rule_id = vcap_lookup_rule_by_cookie(vctrl, f->cookie);
4023643abd6SHoratiu Vultur 		if (rule_id <= 0)
4033643abd6SHoratiu Vultur 			break;
4043643abd6SHoratiu Vultur 
4053643abd6SHoratiu Vultur 		err = vcap_del_rule(vctrl, port->dev, rule_id);
4063643abd6SHoratiu Vultur 		if (err) {
4073643abd6SHoratiu Vultur 			NL_SET_ERR_MSG_MOD(f->common.extack,
4083643abd6SHoratiu Vultur 					   "Cannot delete rule");
4093643abd6SHoratiu Vultur 			break;
4103643abd6SHoratiu Vultur 		}
4113643abd6SHoratiu Vultur 	}
4123643abd6SHoratiu Vultur 
4133643abd6SHoratiu Vultur 	return err;
4143643abd6SHoratiu Vultur }
4153643abd6SHoratiu Vultur 
4169ed138ffSHoratiu Vultur static int lan966x_tc_flower_stats(struct lan966x_port *port,
4179ed138ffSHoratiu Vultur 				   struct flow_cls_offload *f,
4189ed138ffSHoratiu Vultur 				   struct vcap_admin *admin)
4199ed138ffSHoratiu Vultur {
4209ed138ffSHoratiu Vultur 	struct vcap_counter count = {};
4219ed138ffSHoratiu Vultur 	int err;
4229ed138ffSHoratiu Vultur 
4239ed138ffSHoratiu Vultur 	err = vcap_get_rule_count_by_cookie(port->lan966x->vcap_ctrl,
4249ed138ffSHoratiu Vultur 					    &count, f->cookie);
4259ed138ffSHoratiu Vultur 	if (err)
4269ed138ffSHoratiu Vultur 		return err;
4279ed138ffSHoratiu Vultur 
4289ed138ffSHoratiu Vultur 	flow_stats_update(&f->stats, 0x0, count.value, 0, 0,
4299ed138ffSHoratiu Vultur 			  FLOW_ACTION_HW_STATS_IMMEDIATE);
4309ed138ffSHoratiu Vultur 
4319ed138ffSHoratiu Vultur 	return err;
4329ed138ffSHoratiu Vultur }
4339ed138ffSHoratiu Vultur 
4343643abd6SHoratiu Vultur int lan966x_tc_flower(struct lan966x_port *port,
435e7e3f514SSteen Hegelund 		      struct flow_cls_offload *f,
436e7e3f514SSteen Hegelund 		      bool ingress)
4373643abd6SHoratiu Vultur {
4383643abd6SHoratiu Vultur 	struct vcap_admin *admin;
4393643abd6SHoratiu Vultur 
4403643abd6SHoratiu Vultur 	admin = vcap_find_admin(port->lan966x->vcap_ctrl,
4413643abd6SHoratiu Vultur 				f->common.chain_index);
4423643abd6SHoratiu Vultur 	if (!admin) {
4433643abd6SHoratiu Vultur 		NL_SET_ERR_MSG_MOD(f->common.extack, "Invalid chain");
4443643abd6SHoratiu Vultur 		return -EINVAL;
4453643abd6SHoratiu Vultur 	}
4463643abd6SHoratiu Vultur 
4473643abd6SHoratiu Vultur 	switch (f->command) {
4483643abd6SHoratiu Vultur 	case FLOW_CLS_REPLACE:
449e7e3f514SSteen Hegelund 		return lan966x_tc_flower_add(port, f, admin, ingress);
4503643abd6SHoratiu Vultur 	case FLOW_CLS_DESTROY:
4513643abd6SHoratiu Vultur 		return lan966x_tc_flower_del(port, f, admin);
4529ed138ffSHoratiu Vultur 	case FLOW_CLS_STATS:
4539ed138ffSHoratiu Vultur 		return lan966x_tc_flower_stats(port, f, admin);
4543643abd6SHoratiu Vultur 	default:
4553643abd6SHoratiu Vultur 		return -EOPNOTSUPP;
4563643abd6SHoratiu Vultur 	}
4573643abd6SHoratiu Vultur 
4583643abd6SHoratiu Vultur 	return 0;
4593643abd6SHoratiu Vultur }
460