Linux Audio

Check our new training course

Real-Time Linux with PREEMPT_RT training

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