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 | |
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 |
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(rule: st->frule, out: &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(rule: st->vrule, |
55 | key: VCAP_KF_L3_FRAGMENT, |
56 | val: VCAP_BIT_1); |
57 | else |
58 | err = vcap_rule_add_key_bit(rule: st->vrule, |
59 | key: VCAP_KF_L3_FRAGMENT, |
60 | val: 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(rule: st->vrule, |
68 | key: VCAP_KF_L3_FRAG_OFS_GT0, |
69 | val: VCAP_BIT_0); |
70 | else |
71 | err = vcap_rule_add_key_bit(rule: st->vrule, |
72 | key: VCAP_KF_L3_FRAG_OFS_GT0, |
73 | val: 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 |
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(rule: st->frule, out: &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, etype: st->l3_proto)) { |
97 | err = vcap_rule_add_key_u32(rule: st->vrule, key: VCAP_KF_ETYPE, |
98 | value: st->l3_proto, mask: ~0); |
99 | if (err) |
100 | goto out; |
101 | } else if (st->l3_proto == ETH_P_IP) { |
102 | err = vcap_rule_add_key_bit(rule: st->vrule, key: VCAP_KF_IP4_IS, |
103 | val: 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(rule: st->vrule, |
112 | key: VCAP_KF_ETYPE_LEN_IS, |
113 | val: VCAP_BIT_0); |
114 | if (err) |
115 | goto out; |
116 | |
117 | err = vcap_rule_add_key_bit(rule: st->vrule, |
118 | key: VCAP_KF_IP_SNAP_IS, |
119 | val: 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(rule: st->vrule, |
124 | key: VCAP_KF_ETYPE_LEN_IS, |
125 | val: VCAP_BIT_1); |
126 | if (err) |
127 | goto out; |
128 | |
129 | err = vcap_rule_add_key_u32(rule: st->vrule, key: VCAP_KF_ETYPE, |
130 | value: st->l3_proto, mask: ~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(rule: st->vrule, |
141 | key: VCAP_KF_TCP_UDP_IS, |
142 | val: VCAP_BIT_1); |
143 | if (err) |
144 | goto out; |
145 | } |
146 | |
147 | err = vcap_rule_add_key_bit(rule: st->vrule, |
148 | key: VCAP_KF_TCP_IS, |
149 | val: 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(rule: st->vrule, |
155 | key: VCAP_KF_TCP_UDP_IS, |
156 | val: VCAP_BIT_1); |
157 | if (err) |
158 | goto out; |
159 | } |
160 | |
161 | err = vcap_rule_add_key_bit(rule: st->vrule, |
162 | key: VCAP_KF_TCP_IS, |
163 | val: VCAP_BIT_0); |
164 | if (err) |
165 | goto out; |
166 | } else { |
167 | err = vcap_rule_add_key_u32(rule: st->vrule, |
168 | key: VCAP_KF_L3_IP_PROTO, |
169 | value: st->l4_proto, mask: ~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 |
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 |
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 | |
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(flow_cmd: f); |
237 | for (int i = 0; i < ARRAY_SIZE(lan966x_tc_flower_handlers_usage); ++i) { |
238 | if (!flow_rule_match_key(rule: state.frule, key: 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 | |
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(flow_cmd: 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(action: 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(action: act, extack: 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, cur_cid: fco->common.chain_index, |
288 | next_cid: 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, cid: 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 */ |
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(rule: vrule, actionset: aset); |
335 | |
336 | return err; |
337 | } |
338 | |
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(rule: vrule, key: VCAP_KF_LOOKUP_GEN_IDX_SEL, |
353 | value: 1, mask: ~0); |
354 | if (err) |
355 | return err; |
356 | |
357 | return vcap_rule_add_key_u32(rule: vrule, key: VCAP_KF_LOOKUP_GEN_IDX, |
358 | value: link_val, mask: ~0); |
359 | case VCAP_TYPE_IS2: |
360 | /* Add IS2 specific PAG key (for chaining rules from IS1) */ |
361 | return vcap_rule_add_key_u32(rule: vrule, key: VCAP_KF_LOOKUP_PAG, |
362 | value: link_val, mask: ~0); |
363 | case VCAP_TYPE_ES0: |
364 | /* Add ES0 specific ISDX key (for chaining rules from IS1) */ |
365 | return vcap_rule_add_key_u32(rule: vrule, key: VCAP_KF_ISDX_CLS, |
366 | value: link_val, mask: ~0); |
367 | default: |
368 | break; |
369 | } |
370 | return 0; |
371 | } |
372 | |
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, cid: 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, from_cid: 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(rule: vrule, action: VCAP_AF_PAG_VAL, value: diff); |
396 | if (err) |
397 | return err; |
398 | |
399 | err = vcap_rule_add_action_u32(rule: vrule, action: VCAP_AF_PAG_OVERRIDE_MASK, |
400 | value: 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(rule: vrule, action: VCAP_AF_ISDX_ADD_VAL, |
407 | value: diff); |
408 | if (err) |
409 | return err; |
410 | |
411 | err = vcap_rule_add_action_bit(rule: vrule, action: VCAP_AF_ISDX_REPLACE_ENA, |
412 | val: 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 | |
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(rule: vrule, action: VCAP_AF_ESDX, |
432 | value: vrule->id); |
433 | break; |
434 | default: |
435 | break; |
436 | } |
437 | |
438 | return err; |
439 | } |
440 | |
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(vctrl: port->lan966x->vcap_ctrl, |
453 | dev: port->dev, fco: f, ingress); |
454 | if (err) |
455 | return err; |
456 | |
457 | vrule = vcap_alloc_rule(vctrl: port->lan966x->vcap_ctrl, ndev: port->dev, |
458 | vcap_chain_id: f->common.chain_index, user: VCAP_USER_TC, |
459 | priority: f->common.prio, id: 0); |
460 | if (IS_ERR(ptr: vrule)) |
461 | return PTR_ERR(ptr: vrule); |
462 | |
463 | vrule->cookie = f->cookie; |
464 | err = lan966x_tc_flower_use_dissectors(f, admin, vrule, l3_proto: &l3_proto); |
465 | if (err) |
466 | goto out; |
467 | |
468 | err = lan966x_tc_add_rule_link_target(admin, vrule, |
469 | target_cid: f->common.chain_index); |
470 | if (err) |
471 | goto out; |
472 | |
473 | frule = flow_cls_offload_flow_rule(flow_cmd: 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(rule: vrule, |
486 | action: VCAP_AF_CPU_COPY_ENA, |
487 | val: VCAP_BIT_1); |
488 | err |= vcap_rule_add_action_u32(rule: vrule, |
489 | action: VCAP_AF_CPU_QUEUE_NUM, |
490 | value: 0); |
491 | err |= vcap_rule_add_action_u32(rule: vrule, action: VCAP_AF_MASK_MODE, |
492 | value: 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(vctrl: port->lan966x->vcap_ctrl, |
503 | admin, vrule, |
504 | f, to_cid: 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(rule: vrule, action: 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(fco: f, vrule); |
535 | goto out; |
536 | } |
537 | |
538 | err = vcap_val_rule(rule: vrule, l3_proto); |
539 | if (err) { |
540 | vcap_set_tc_exterr(fco: f, vrule); |
541 | goto out; |
542 | } |
543 | |
544 | err = vcap_add_rule(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(rule: vrule); |
550 | return err; |
551 | } |
552 | |
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, cookie: f->cookie); |
563 | if (rule_id <= 0) |
564 | break; |
565 | |
566 | err = vcap_del_rule(vctrl, ndev: port->dev, id: 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 | |
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(vctrl: port->lan966x->vcap_ctrl, |
585 | ctr: &count, cookie: f->cookie); |
586 | if (err) |
587 | return err; |
588 | |
589 | flow_stats_update(flow_stats: &f->stats, bytes: 0x0, pkts: count.value, drops: 0, lastused: 0, |
590 | used_hw_stats: FLOW_ACTION_HW_STATS_IMMEDIATE); |
591 | |
592 | return err; |
593 | } |
594 | |
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(vctrl: port->lan966x->vcap_ctrl, |
602 | cid: 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 | |