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 
lan966x_tc_is_known_etype(struct vcap_tc_flower_parse_usage * st,u16 etype)10 static 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 
45 static int
lan966x_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage * st)46 lan966x_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 
82 out:
83 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error");
84 	return err;
85 }
86 
87 static int
lan966x_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage * st)88 lan966x_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;
177 out:
178 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error");
179 	return err;
180 }
181 
182 static int
lan966x_tc_flower_handler_cvlan_usage(struct vcap_tc_flower_parse_usage * st)183 lan966x_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 
194 static int
lan966x_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage * st)195 lan966x_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 
208 static 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 
lan966x_tc_flower_use_dissectors(struct flow_cls_offload * f,struct vcap_admin * admin,struct vcap_rule * vrule,u16 * l3_proto)223 static 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 
lan966x_tc_flower_action_check(struct vcap_control * vctrl,struct net_device * dev,struct flow_cls_offload * fco,bool ingress)253 static 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 */
lan966x_tc_set_actionset(struct vcap_admin * admin,struct vcap_rule * vrule)312 static 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 
lan966x_tc_add_rule_link_target(struct vcap_admin * admin,struct vcap_rule * vrule,int target_cid)339 static 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 
lan966x_tc_add_rule_link(struct vcap_control * vctrl,struct vcap_admin * admin,struct vcap_rule * vrule,struct flow_cls_offload * f,int to_cid)373 static 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 
lan966x_tc_add_rule_counter(struct vcap_admin * admin,struct vcap_rule * vrule)424 static 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 
lan966x_tc_flower_add(struct lan966x_port * port,struct flow_cls_offload * f,struct vcap_admin * admin,bool ingress)441 static 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");
548 out:
549 	vcap_free_rule(vrule);
550 	return err;
551 }
552 
lan966x_tc_flower_del(struct lan966x_port * port,struct flow_cls_offload * f,struct vcap_admin * admin)553 static 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 
lan966x_tc_flower_stats(struct lan966x_port * port,struct flow_cls_offload * f,struct vcap_admin * admin)577 static 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 
lan966x_tc_flower(struct lan966x_port * port,struct flow_cls_offload * f,bool ingress)595 int 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