1// SPDX-License-Identifier: GPL-2.0+
2
3#include "lan966x_main.h"
4#include "vcap_api.h"
5#include "vcap_api_client.h"
6#include "vcap_tc.h"
7
8#define LAN966X_FORCE_UNTAGED	3
9
10static bool lan966x_tc_is_known_etype(struct vcap_tc_flower_parse_usage *st,
11				      u16 etype)
12{
13	switch (st->admin->vtype) {
14	case VCAP_TYPE_IS1:
15		switch (etype) {
16		case ETH_P_ALL:
17		case ETH_P_ARP:
18		case ETH_P_IP:
19		case ETH_P_IPV6:
20			return true;
21		}
22		break;
23	case VCAP_TYPE_IS2:
24		switch (etype) {
25		case ETH_P_ALL:
26		case ETH_P_ARP:
27		case ETH_P_IP:
28		case ETH_P_IPV6:
29		case ETH_P_SNAP:
30		case ETH_P_802_2:
31			return true;
32		}
33		break;
34	case VCAP_TYPE_ES0:
35		return true;
36	default:
37		NL_SET_ERR_MSG_MOD(st->fco->common.extack,
38				   "VCAP type not supported");
39		return false;
40	}
41
42	return false;
43}
44
45static int
46lan966x_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage *st)
47{
48	struct flow_match_control match;
49	int err = 0;
50
51	flow_rule_match_control(st->frule, &match);
52	if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) {
53		if (match.key->flags & FLOW_DIS_IS_FRAGMENT)
54			err = vcap_rule_add_key_bit(st->vrule,
55						    VCAP_KF_L3_FRAGMENT,
56						    VCAP_BIT_1);
57		else
58			err = vcap_rule_add_key_bit(st->vrule,
59						    VCAP_KF_L3_FRAGMENT,
60						    VCAP_BIT_0);
61		if (err)
62			goto out;
63	}
64
65	if (match.mask->flags & FLOW_DIS_FIRST_FRAG) {
66		if (match.key->flags & FLOW_DIS_FIRST_FRAG)
67			err = vcap_rule_add_key_bit(st->vrule,
68						    VCAP_KF_L3_FRAG_OFS_GT0,
69						    VCAP_BIT_0);
70		else
71			err = vcap_rule_add_key_bit(st->vrule,
72						    VCAP_KF_L3_FRAG_OFS_GT0,
73						    VCAP_BIT_1);
74		if (err)
75			goto out;
76	}
77
78	st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL);
79
80	return err;
81
82out:
83	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error");
84	return err;
85}
86
87static int
88lan966x_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st)
89{
90	struct flow_match_basic match;
91	int err = 0;
92
93	flow_rule_match_basic(st->frule, &match);
94	if (match.mask->n_proto) {
95		st->l3_proto = be16_to_cpu(match.key->n_proto);
96		if (!lan966x_tc_is_known_etype(st, st->l3_proto)) {
97			err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,
98						    st->l3_proto, ~0);
99			if (err)
100				goto out;
101		} else if (st->l3_proto == ETH_P_IP) {
102			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
103						    VCAP_BIT_1);
104			if (err)
105				goto out;
106		} else if (st->l3_proto == ETH_P_IPV6 &&
107			   st->admin->vtype == VCAP_TYPE_IS1) {
108			/* Don't set any keys in this case */
109		} else if (st->l3_proto == ETH_P_SNAP &&
110			   st->admin->vtype == VCAP_TYPE_IS1) {
111			err = vcap_rule_add_key_bit(st->vrule,
112						    VCAP_KF_ETYPE_LEN_IS,
113						    VCAP_BIT_0);
114			if (err)
115				goto out;
116
117			err = vcap_rule_add_key_bit(st->vrule,
118						    VCAP_KF_IP_SNAP_IS,
119						    VCAP_BIT_1);
120			if (err)
121				goto out;
122		} else if (st->admin->vtype == VCAP_TYPE_IS1) {
123			err = vcap_rule_add_key_bit(st->vrule,
124						    VCAP_KF_ETYPE_LEN_IS,
125						    VCAP_BIT_1);
126			if (err)
127				goto out;
128
129			err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,
130						    st->l3_proto, ~0);
131			if (err)
132				goto out;
133		}
134	}
135	if (match.mask->ip_proto) {
136		st->l4_proto = match.key->ip_proto;
137
138		if (st->l4_proto == IPPROTO_TCP) {
139			if (st->admin->vtype == VCAP_TYPE_IS1) {
140				err = vcap_rule_add_key_bit(st->vrule,
141							    VCAP_KF_TCP_UDP_IS,
142							    VCAP_BIT_1);
143				if (err)
144					goto out;
145			}
146
147			err = vcap_rule_add_key_bit(st->vrule,
148						    VCAP_KF_TCP_IS,
149						    VCAP_BIT_1);
150			if (err)
151				goto out;
152		} else if (st->l4_proto == IPPROTO_UDP) {
153			if (st->admin->vtype == VCAP_TYPE_IS1) {
154				err = vcap_rule_add_key_bit(st->vrule,
155							    VCAP_KF_TCP_UDP_IS,
156							    VCAP_BIT_1);
157				if (err)
158					goto out;
159			}
160
161			err = vcap_rule_add_key_bit(st->vrule,
162						    VCAP_KF_TCP_IS,
163						    VCAP_BIT_0);
164			if (err)
165				goto out;
166		} else {
167			err = vcap_rule_add_key_u32(st->vrule,
168						    VCAP_KF_L3_IP_PROTO,
169						    st->l4_proto, ~0);
170			if (err)
171				goto out;
172		}
173	}
174
175	st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_BASIC);
176	return err;
177out:
178	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error");
179	return err;
180}
181
182static int
183lan966x_tc_flower_handler_cvlan_usage(struct vcap_tc_flower_parse_usage *st)
184{
185	if (st->admin->vtype != VCAP_TYPE_IS1) {
186		NL_SET_ERR_MSG_MOD(st->fco->common.extack,
187				   "cvlan not supported in this VCAP");
188		return -EINVAL;
189	}
190
191	return vcap_tc_flower_handler_cvlan_usage(st);
192}
193
194static int
195lan966x_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st)
196{
197	enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS;
198	enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS;
199
200	if (st->admin->vtype == VCAP_TYPE_IS1) {
201		vid_key = VCAP_KF_8021Q_VID0;
202		pcp_key = VCAP_KF_8021Q_PCP0;
203	}
204
205	return vcap_tc_flower_handler_vlan_usage(st, vid_key, pcp_key);
206}
207
208static int
209(*lan966x_tc_flower_handlers_usage[])(struct vcap_tc_flower_parse_usage *st) = {
210	[FLOW_DISSECTOR_KEY_ETH_ADDRS] = vcap_tc_flower_handler_ethaddr_usage,
211	[FLOW_DISSECTOR_KEY_IPV4_ADDRS] = vcap_tc_flower_handler_ipv4_usage,
212	[FLOW_DISSECTOR_KEY_IPV6_ADDRS] = vcap_tc_flower_handler_ipv6_usage,
213	[FLOW_DISSECTOR_KEY_CONTROL] = lan966x_tc_flower_handler_control_usage,
214	[FLOW_DISSECTOR_KEY_PORTS] = vcap_tc_flower_handler_portnum_usage,
215	[FLOW_DISSECTOR_KEY_BASIC] = lan966x_tc_flower_handler_basic_usage,
216	[FLOW_DISSECTOR_KEY_CVLAN] = lan966x_tc_flower_handler_cvlan_usage,
217	[FLOW_DISSECTOR_KEY_VLAN] = lan966x_tc_flower_handler_vlan_usage,
218	[FLOW_DISSECTOR_KEY_TCP] = vcap_tc_flower_handler_tcp_usage,
219	[FLOW_DISSECTOR_KEY_ARP] = vcap_tc_flower_handler_arp_usage,
220	[FLOW_DISSECTOR_KEY_IP] = vcap_tc_flower_handler_ip_usage,
221};
222
223static int lan966x_tc_flower_use_dissectors(struct flow_cls_offload *f,
224					    struct vcap_admin *admin,
225					    struct vcap_rule *vrule,
226					    u16 *l3_proto)
227{
228	struct vcap_tc_flower_parse_usage state = {
229		.fco = f,
230		.vrule = vrule,
231		.l3_proto = ETH_P_ALL,
232		.admin = admin,
233	};
234	int err = 0;
235
236	state.frule = flow_cls_offload_flow_rule(f);
237	for (int i = 0; i < ARRAY_SIZE(lan966x_tc_flower_handlers_usage); ++i) {
238		if (!flow_rule_match_key(state.frule, i) ||
239		    !lan966x_tc_flower_handlers_usage[i])
240			continue;
241
242		err = lan966x_tc_flower_handlers_usage[i](&state);
243		if (err)
244			return err;
245	}
246
247	if (l3_proto)
248		*l3_proto = state.l3_proto;
249
250	return err;
251}
252
253static int lan966x_tc_flower_action_check(struct vcap_control *vctrl,
254					  struct net_device *dev,
255					  struct flow_cls_offload *fco,
256					  bool ingress)
257{
258	struct flow_rule *rule = flow_cls_offload_flow_rule(fco);
259	struct flow_action_entry *actent, *last_actent = NULL;
260	struct flow_action *act = &rule->action;
261	u64 action_mask = 0;
262	int idx;
263
264	if (!flow_action_has_entries(act)) {
265		NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
266		return -EINVAL;
267	}
268
269	if (!flow_action_basic_hw_stats_check(act, fco->common.extack))
270		return -EOPNOTSUPP;
271
272	flow_action_for_each(idx, actent, act) {
273		if (action_mask & BIT(actent->id)) {
274			NL_SET_ERR_MSG_MOD(fco->common.extack,
275					   "More actions of the same type");
276			return -EINVAL;
277		}
278		action_mask |= BIT(actent->id);
279		last_actent = actent; /* Save last action for later check */
280	}
281
282	/* Check that last action is a goto
283	 * The last chain/lookup does not need to have goto action
284	 */
285	if (last_actent->id == FLOW_ACTION_GOTO) {
286		/* Check if the destination chain is in one of the VCAPs */
287		if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
288					 last_actent->chain_index)) {
289			NL_SET_ERR_MSG_MOD(fco->common.extack,
290					   "Invalid goto chain");
291			return -EINVAL;
292		}
293	} else if (!vcap_is_last_chain(vctrl, fco->common.chain_index,
294				       ingress)) {
295		NL_SET_ERR_MSG_MOD(fco->common.extack,
296				   "Last action must be 'goto'");
297		return -EINVAL;
298	}
299
300	/* Catch unsupported combinations of actions */
301	if (action_mask & BIT(FLOW_ACTION_TRAP) &&
302	    action_mask & BIT(FLOW_ACTION_ACCEPT)) {
303		NL_SET_ERR_MSG_MOD(fco->common.extack,
304				   "Cannot combine pass and trap action");
305		return -EOPNOTSUPP;
306	}
307
308	return 0;
309}
310
311/* Add the actionset that is the default for the VCAP type */
312static int lan966x_tc_set_actionset(struct vcap_admin *admin,
313				    struct vcap_rule *vrule)
314{
315	enum vcap_actionfield_set aset;
316	int err = 0;
317
318	switch (admin->vtype) {
319	case VCAP_TYPE_IS1:
320		aset = VCAP_AFS_S1;
321		break;
322	case VCAP_TYPE_IS2:
323		aset = VCAP_AFS_BASE_TYPE;
324		break;
325	case VCAP_TYPE_ES0:
326		aset = VCAP_AFS_VID;
327		break;
328	default:
329		return -EINVAL;
330	}
331
332	/* Do not overwrite any current actionset */
333	if (vrule->actionset == VCAP_AFS_NO_VALUE)
334		err = vcap_set_rule_set_actionset(vrule, aset);
335
336	return err;
337}
338
339static int lan966x_tc_add_rule_link_target(struct vcap_admin *admin,
340					   struct vcap_rule *vrule,
341					   int target_cid)
342{
343	int link_val = target_cid % VCAP_CID_LOOKUP_SIZE;
344	int err;
345
346	if (!link_val)
347		return 0;
348
349	switch (admin->vtype) {
350	case VCAP_TYPE_IS1:
351		/* Choose IS1 specific NXT_IDX key (for chaining rules from IS1) */
352		err = vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX_SEL,
353					    1, ~0);
354		if (err)
355			return err;
356
357		return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX,
358					     link_val, ~0);
359	case VCAP_TYPE_IS2:
360		/* Add IS2 specific PAG key (for chaining rules from IS1) */
361		return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_PAG,
362					     link_val, ~0);
363	case VCAP_TYPE_ES0:
364		/* Add ES0 specific ISDX key (for chaining rules from IS1) */
365		return vcap_rule_add_key_u32(vrule, VCAP_KF_ISDX_CLS,
366					     link_val, ~0);
367	default:
368		break;
369	}
370	return 0;
371}
372
373static int lan966x_tc_add_rule_link(struct vcap_control *vctrl,
374				    struct vcap_admin *admin,
375				    struct vcap_rule *vrule,
376				    struct flow_cls_offload *f,
377				    int to_cid)
378{
379	struct vcap_admin *to_admin = vcap_find_admin(vctrl, to_cid);
380	int diff, err = 0;
381
382	if (!to_admin) {
383		NL_SET_ERR_MSG_MOD(f->common.extack,
384				   "Unknown destination chain");
385		return -EINVAL;
386	}
387
388	diff = vcap_chain_offset(vctrl, f->common.chain_index, to_cid);
389	if (!diff)
390		return 0;
391
392	/* Between IS1 and IS2 the PAG value is used */
393	if (admin->vtype == VCAP_TYPE_IS1 && to_admin->vtype == VCAP_TYPE_IS2) {
394		/* This works for IS1->IS2 */
395		err = vcap_rule_add_action_u32(vrule, VCAP_AF_PAG_VAL, diff);
396		if (err)
397			return err;
398
399		err = vcap_rule_add_action_u32(vrule, VCAP_AF_PAG_OVERRIDE_MASK,
400					       0xff);
401		if (err)
402			return err;
403	} else if (admin->vtype == VCAP_TYPE_IS1 &&
404		   to_admin->vtype == VCAP_TYPE_ES0) {
405		/* This works for IS1->ES0 */
406		err = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_ADD_VAL,
407					       diff);
408		if (err)
409			return err;
410
411		err = vcap_rule_add_action_bit(vrule, VCAP_AF_ISDX_REPLACE_ENA,
412					       VCAP_BIT_1);
413		if (err)
414			return err;
415	} else {
416		NL_SET_ERR_MSG_MOD(f->common.extack,
417				   "Unsupported chain destination");
418		return -EOPNOTSUPP;
419	}
420
421	return err;
422}
423
424static int lan966x_tc_add_rule_counter(struct vcap_admin *admin,
425				       struct vcap_rule *vrule)
426{
427	int err = 0;
428
429	switch (admin->vtype) {
430	case VCAP_TYPE_ES0:
431		err = vcap_rule_mod_action_u32(vrule, VCAP_AF_ESDX,
432					       vrule->id);
433		break;
434	default:
435		break;
436	}
437
438	return err;
439}
440
441static int lan966x_tc_flower_add(struct lan966x_port *port,
442				 struct flow_cls_offload *f,
443				 struct vcap_admin *admin,
444				 bool ingress)
445{
446	struct flow_action_entry *act;
447	u16 l3_proto = ETH_P_ALL;
448	struct flow_rule *frule;
449	struct vcap_rule *vrule;
450	int err, idx;
451
452	err = lan966x_tc_flower_action_check(port->lan966x->vcap_ctrl,
453					     port->dev, f, ingress);
454	if (err)
455		return err;
456
457	vrule = vcap_alloc_rule(port->lan966x->vcap_ctrl, port->dev,
458				f->common.chain_index, VCAP_USER_TC,
459				f->common.prio, 0);
460	if (IS_ERR(vrule))
461		return PTR_ERR(vrule);
462
463	vrule->cookie = f->cookie;
464	err = lan966x_tc_flower_use_dissectors(f, admin, vrule, &l3_proto);
465	if (err)
466		goto out;
467
468	err = lan966x_tc_add_rule_link_target(admin, vrule,
469					      f->common.chain_index);
470	if (err)
471		goto out;
472
473	frule = flow_cls_offload_flow_rule(f);
474
475	flow_action_for_each(idx, act, &frule->action) {
476		switch (act->id) {
477		case FLOW_ACTION_TRAP:
478			if (admin->vtype != VCAP_TYPE_IS2) {
479				NL_SET_ERR_MSG_MOD(f->common.extack,
480						   "Trap action not supported in this VCAP");
481				err = -EOPNOTSUPP;
482				goto out;
483			}
484
485			err = vcap_rule_add_action_bit(vrule,
486						       VCAP_AF_CPU_COPY_ENA,
487						       VCAP_BIT_1);
488			err |= vcap_rule_add_action_u32(vrule,
489							VCAP_AF_CPU_QUEUE_NUM,
490							0);
491			err |= vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,
492							LAN966X_PMM_REPLACE);
493			if (err)
494				goto out;
495
496			break;
497		case FLOW_ACTION_GOTO:
498			err = lan966x_tc_set_actionset(admin, vrule);
499			if (err)
500				goto out;
501
502			err = lan966x_tc_add_rule_link(port->lan966x->vcap_ctrl,
503						       admin, vrule,
504						       f, act->chain_index);
505			if (err)
506				goto out;
507
508			break;
509		case FLOW_ACTION_VLAN_POP:
510			if (admin->vtype != VCAP_TYPE_ES0) {
511				NL_SET_ERR_MSG_MOD(f->common.extack,
512						   "Cannot use vlan pop on non es0");
513				err = -EOPNOTSUPP;
514				goto out;
515			}
516
517			/* Force untag */
518			err = vcap_rule_add_action_u32(vrule, VCAP_AF_PUSH_OUTER_TAG,
519						       LAN966X_FORCE_UNTAGED);
520			if (err)
521				goto out;
522
523			break;
524		default:
525			NL_SET_ERR_MSG_MOD(f->common.extack,
526					   "Unsupported TC action");
527			err = -EOPNOTSUPP;
528			goto out;
529		}
530	}
531
532	err = lan966x_tc_add_rule_counter(admin, vrule);
533	if (err) {
534		vcap_set_tc_exterr(f, vrule);
535		goto out;
536	}
537
538	err = vcap_val_rule(vrule, l3_proto);
539	if (err) {
540		vcap_set_tc_exterr(f, vrule);
541		goto out;
542	}
543
544	err = vcap_add_rule(vrule);
545	if (err)
546		NL_SET_ERR_MSG_MOD(f->common.extack,
547				   "Could not add the filter");
548out:
549	vcap_free_rule(vrule);
550	return err;
551}
552
553static int lan966x_tc_flower_del(struct lan966x_port *port,
554				 struct flow_cls_offload *f,
555				 struct vcap_admin *admin)
556{
557	struct vcap_control *vctrl;
558	int err = -ENOENT, rule_id;
559
560	vctrl = port->lan966x->vcap_ctrl;
561	while (true) {
562		rule_id = vcap_lookup_rule_by_cookie(vctrl, f->cookie);
563		if (rule_id <= 0)
564			break;
565
566		err = vcap_del_rule(vctrl, port->dev, rule_id);
567		if (err) {
568			NL_SET_ERR_MSG_MOD(f->common.extack,
569					   "Cannot delete rule");
570			break;
571		}
572	}
573
574	return err;
575}
576
577static int lan966x_tc_flower_stats(struct lan966x_port *port,
578				   struct flow_cls_offload *f,
579				   struct vcap_admin *admin)
580{
581	struct vcap_counter count = {};
582	int err;
583
584	err = vcap_get_rule_count_by_cookie(port->lan966x->vcap_ctrl,
585					    &count, f->cookie);
586	if (err)
587		return err;
588
589	flow_stats_update(&f->stats, 0x0, count.value, 0, 0,
590			  FLOW_ACTION_HW_STATS_IMMEDIATE);
591
592	return err;
593}
594
595int lan966x_tc_flower(struct lan966x_port *port,
596		      struct flow_cls_offload *f,
597		      bool ingress)
598{
599	struct vcap_admin *admin;
600
601	admin = vcap_find_admin(port->lan966x->vcap_ctrl,
602				f->common.chain_index);
603	if (!admin) {
604		NL_SET_ERR_MSG_MOD(f->common.extack, "Invalid chain");
605		return -EINVAL;
606	}
607
608	switch (f->command) {
609	case FLOW_CLS_REPLACE:
610		return lan966x_tc_flower_add(port, f, admin, ingress);
611	case FLOW_CLS_DESTROY:
612		return lan966x_tc_flower_del(port, f, admin);
613	case FLOW_CLS_STATS:
614		return lan966x_tc_flower_stats(port, f, admin);
615	default:
616		return -EOPNOTSUPP;
617	}
618
619	return 0;
620}
621