xref: /linux/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c (revision 621cde16e49b3ecf7d59a8106a20aaebfb4a59a9)
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 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 
88 bad_frag_out:
89 	NL_SET_ERR_MSG_MOD(extack, "ip_frag parse error");
90 	return err;
91 }
92 
93 static int
lan966x_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage * st)94 lan966x_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;
183 out:
184 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error");
185 	return err;
186 }
187 
188 static int
lan966x_tc_flower_handler_cvlan_usage(struct vcap_tc_flower_parse_usage * st)189 lan966x_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 
200 static int
lan966x_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage * st)201 lan966x_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 
214 static 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 
lan966x_tc_flower_use_dissectors(struct flow_cls_offload * f,struct vcap_admin * admin,struct vcap_rule * vrule,u16 * l3_proto)229 static 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 
lan966x_tc_flower_action_check(struct vcap_control * vctrl,struct net_device * dev,struct flow_cls_offload * fco,bool ingress)259 static 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 */
lan966x_tc_set_actionset(struct vcap_admin * admin,struct vcap_rule * vrule)318 static 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 
lan966x_tc_add_rule_link_target(struct vcap_admin * admin,struct vcap_rule * vrule,int target_cid)345 static 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 
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)379 static 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 
lan966x_tc_add_rule_counter(struct vcap_admin * admin,struct vcap_rule * vrule)430 static 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 
lan966x_tc_flower_add(struct lan966x_port * port,struct flow_cls_offload * f,struct vcap_admin * admin,bool ingress)447 static 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");
554 out:
555 	vcap_free_rule(vrule);
556 	return err;
557 }
558 
lan966x_tc_flower_del(struct lan966x_port * port,struct flow_cls_offload * f,struct vcap_admin * admin)559 static 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 
lan966x_tc_flower_stats(struct lan966x_port * port,struct flow_cls_offload * f,struct vcap_admin * admin)583 static 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 
lan966x_tc_flower(struct lan966x_port * port,struct flow_cls_offload * f,bool ingress)601 int 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 }
627