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(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
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(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;
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(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
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(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 */
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(rule: vrule, actionset: 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(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
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, 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
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(rule: vrule, action: VCAP_AF_ESDX,
432 value: 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(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");
548out:
549 vcap_free_rule(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, 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
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(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
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(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

source code of linux/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c