1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* Microchip VCAP API |
3 | * |
4 | * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. |
5 | */ |
6 | |
7 | #include <net/tc_act/tc_gate.h> |
8 | #include <net/tcp.h> |
9 | |
10 | #include "sparx5_tc.h" |
11 | #include "vcap_api.h" |
12 | #include "vcap_api_client.h" |
13 | #include "vcap_tc.h" |
14 | #include "sparx5_main.h" |
15 | #include "sparx5_vcap_impl.h" |
16 | |
17 | #define SPX5_MAX_RULE_SIZE 13 /* allows X1, X2, X4, X6 and X12 rules */ |
18 | |
19 | /* Collect keysets and type ids for multiple rules per size */ |
20 | struct sparx5_wildcard_rule { |
21 | bool selected; |
22 | u8 value; |
23 | u8 mask; |
24 | enum vcap_keyfield_set keyset; |
25 | }; |
26 | |
27 | struct sparx5_multiple_rules { |
28 | struct sparx5_wildcard_rule rule[SPX5_MAX_RULE_SIZE]; |
29 | }; |
30 | |
31 | struct sparx5_tc_flower_template { |
32 | struct list_head list; /* for insertion in the list of templates */ |
33 | int cid; /* chain id */ |
34 | enum vcap_keyfield_set orig; /* keyset used before the template */ |
35 | enum vcap_keyfield_set keyset; /* new keyset used by template */ |
36 | u16 l3_proto; /* protocol specified in the template */ |
37 | }; |
38 | |
39 | /* SparX-5 VCAP fragment types: |
40 | * 0 = no fragment, 1 = initial fragment, |
41 | * 2 = suspicious fragment, 3 = valid follow-up fragment |
42 | */ |
43 | enum { /* key / mask */ |
44 | FRAG_NOT = 0x03, /* 0 / 3 */ |
45 | FRAG_SOME = 0x11, /* 1 / 1 */ |
46 | FRAG_FIRST = 0x13, /* 1 / 3 */ |
47 | FRAG_LATER = 0x33, /* 3 / 3 */ |
48 | FRAG_INVAL = 0xff, /* invalid */ |
49 | }; |
50 | |
51 | /* Flower fragment flag to VCAP fragment type mapping */ |
52 | static const u8 sparx5_vcap_frag_map[4][4] = { /* is_frag */ |
53 | { FRAG_INVAL, FRAG_INVAL, FRAG_INVAL, FRAG_FIRST }, /* 0/0 */ |
54 | { FRAG_NOT, FRAG_NOT, FRAG_INVAL, FRAG_INVAL }, /* 0/1 */ |
55 | { FRAG_INVAL, FRAG_INVAL, FRAG_INVAL, FRAG_INVAL }, /* 1/0 */ |
56 | { FRAG_SOME, FRAG_LATER, FRAG_INVAL, FRAG_FIRST } /* 1/1 */ |
57 | /* 0/0 0/1 1/0 1/1 <-- first_frag */ |
58 | }; |
59 | |
60 | static int |
61 | sparx5_tc_flower_es0_tpid(struct vcap_tc_flower_parse_usage *st) |
62 | { |
63 | int err = 0; |
64 | |
65 | switch (st->tpid) { |
66 | case ETH_P_8021Q: |
67 | err = vcap_rule_add_key_u32(rule: st->vrule, |
68 | key: VCAP_KF_8021Q_TPID, |
69 | value: SPX5_TPID_SEL_8100, mask: ~0); |
70 | break; |
71 | case ETH_P_8021AD: |
72 | err = vcap_rule_add_key_u32(rule: st->vrule, |
73 | key: VCAP_KF_8021Q_TPID, |
74 | value: SPX5_TPID_SEL_88A8, mask: ~0); |
75 | break; |
76 | default: |
77 | NL_SET_ERR_MSG_MOD(st->fco->common.extack, |
78 | "Invalid vlan proto" ); |
79 | err = -EINVAL; |
80 | break; |
81 | } |
82 | return err; |
83 | } |
84 | |
85 | static int |
86 | sparx5_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st) |
87 | { |
88 | struct flow_match_basic mt; |
89 | int err = 0; |
90 | |
91 | flow_rule_match_basic(rule: st->frule, out: &mt); |
92 | |
93 | if (mt.mask->n_proto) { |
94 | st->l3_proto = be16_to_cpu(mt.key->n_proto); |
95 | if (!sparx5_vcap_is_known_etype(admin: st->admin, etype: st->l3_proto)) { |
96 | err = vcap_rule_add_key_u32(rule: st->vrule, key: VCAP_KF_ETYPE, |
97 | value: st->l3_proto, mask: ~0); |
98 | if (err) |
99 | goto out; |
100 | } else if (st->l3_proto == ETH_P_IP) { |
101 | err = vcap_rule_add_key_bit(rule: st->vrule, key: VCAP_KF_IP4_IS, |
102 | val: VCAP_BIT_1); |
103 | if (err) |
104 | goto out; |
105 | } else if (st->l3_proto == ETH_P_IPV6) { |
106 | err = vcap_rule_add_key_bit(rule: st->vrule, key: VCAP_KF_IP4_IS, |
107 | val: VCAP_BIT_0); |
108 | if (err) |
109 | goto out; |
110 | if (st->admin->vtype == VCAP_TYPE_IS0) { |
111 | err = vcap_rule_add_key_bit(rule: st->vrule, |
112 | key: VCAP_KF_IP_SNAP_IS, |
113 | val: VCAP_BIT_1); |
114 | if (err) |
115 | goto out; |
116 | } |
117 | } |
118 | } |
119 | |
120 | if (mt.mask->ip_proto) { |
121 | st->l4_proto = mt.key->ip_proto; |
122 | if (st->l4_proto == IPPROTO_TCP) { |
123 | err = vcap_rule_add_key_bit(rule: st->vrule, |
124 | key: VCAP_KF_TCP_IS, |
125 | val: VCAP_BIT_1); |
126 | if (err) |
127 | goto out; |
128 | } else if (st->l4_proto == IPPROTO_UDP) { |
129 | err = vcap_rule_add_key_bit(rule: st->vrule, |
130 | key: VCAP_KF_TCP_IS, |
131 | val: VCAP_BIT_0); |
132 | if (err) |
133 | goto out; |
134 | if (st->admin->vtype == VCAP_TYPE_IS0) { |
135 | err = vcap_rule_add_key_bit(rule: st->vrule, |
136 | key: VCAP_KF_TCP_UDP_IS, |
137 | val: VCAP_BIT_1); |
138 | if (err) |
139 | goto out; |
140 | } |
141 | } else { |
142 | err = vcap_rule_add_key_u32(rule: st->vrule, |
143 | key: VCAP_KF_L3_IP_PROTO, |
144 | value: st->l4_proto, mask: ~0); |
145 | if (err) |
146 | goto out; |
147 | } |
148 | } |
149 | |
150 | st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_BASIC); |
151 | |
152 | return err; |
153 | |
154 | out: |
155 | NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error" ); |
156 | return err; |
157 | } |
158 | |
159 | static int |
160 | sparx5_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage *st) |
161 | { |
162 | struct flow_match_control mt; |
163 | u32 value, mask; |
164 | int err = 0; |
165 | |
166 | flow_rule_match_control(rule: st->frule, out: &mt); |
167 | |
168 | if (mt.mask->flags) { |
169 | u8 is_frag_key = !!(mt.key->flags & FLOW_DIS_IS_FRAGMENT); |
170 | u8 is_frag_mask = !!(mt.mask->flags & FLOW_DIS_IS_FRAGMENT); |
171 | u8 is_frag_idx = (is_frag_key << 1) | is_frag_mask; |
172 | |
173 | u8 first_frag_key = !!(mt.key->flags & FLOW_DIS_FIRST_FRAG); |
174 | u8 first_frag_mask = !!(mt.mask->flags & FLOW_DIS_FIRST_FRAG); |
175 | u8 first_frag_idx = (first_frag_key << 1) | first_frag_mask; |
176 | |
177 | /* Lookup verdict based on the 2 + 2 input bits */ |
178 | u8 vdt = sparx5_vcap_frag_map[is_frag_idx][first_frag_idx]; |
179 | |
180 | if (vdt == FRAG_INVAL) { |
181 | NL_SET_ERR_MSG_MOD(st->fco->common.extack, |
182 | "Match on invalid fragment flag combination" ); |
183 | return -EINVAL; |
184 | } |
185 | |
186 | /* Extract VCAP fragment key and mask from verdict */ |
187 | value = (vdt >> 4) & 0x3; |
188 | mask = vdt & 0x3; |
189 | |
190 | err = vcap_rule_add_key_u32(rule: st->vrule, |
191 | key: VCAP_KF_L3_FRAGMENT_TYPE, |
192 | value, mask); |
193 | if (err) |
194 | goto out; |
195 | } |
196 | |
197 | st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL); |
198 | |
199 | return err; |
200 | |
201 | out: |
202 | NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error" ); |
203 | return err; |
204 | } |
205 | |
206 | static int |
207 | sparx5_tc_flower_handler_cvlan_usage(struct vcap_tc_flower_parse_usage *st) |
208 | { |
209 | if (st->admin->vtype != VCAP_TYPE_IS0) { |
210 | NL_SET_ERR_MSG_MOD(st->fco->common.extack, |
211 | "cvlan not supported in this VCAP" ); |
212 | return -EINVAL; |
213 | } |
214 | |
215 | return vcap_tc_flower_handler_cvlan_usage(st); |
216 | } |
217 | |
218 | static int |
219 | sparx5_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st) |
220 | { |
221 | enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS; |
222 | enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS; |
223 | int err; |
224 | |
225 | if (st->admin->vtype == VCAP_TYPE_IS0) { |
226 | vid_key = VCAP_KF_8021Q_VID0; |
227 | pcp_key = VCAP_KF_8021Q_PCP0; |
228 | } |
229 | |
230 | err = vcap_tc_flower_handler_vlan_usage(st, vid_key, pcp_key); |
231 | if (err) |
232 | return err; |
233 | |
234 | if (st->admin->vtype == VCAP_TYPE_ES0 && st->tpid) |
235 | err = sparx5_tc_flower_es0_tpid(st); |
236 | |
237 | return err; |
238 | } |
239 | |
240 | static int (*sparx5_tc_flower_usage_handlers[])(struct vcap_tc_flower_parse_usage *st) = { |
241 | [FLOW_DISSECTOR_KEY_ETH_ADDRS] = vcap_tc_flower_handler_ethaddr_usage, |
242 | [FLOW_DISSECTOR_KEY_IPV4_ADDRS] = vcap_tc_flower_handler_ipv4_usage, |
243 | [FLOW_DISSECTOR_KEY_IPV6_ADDRS] = vcap_tc_flower_handler_ipv6_usage, |
244 | [FLOW_DISSECTOR_KEY_CONTROL] = sparx5_tc_flower_handler_control_usage, |
245 | [FLOW_DISSECTOR_KEY_PORTS] = vcap_tc_flower_handler_portnum_usage, |
246 | [FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage, |
247 | [FLOW_DISSECTOR_KEY_CVLAN] = sparx5_tc_flower_handler_cvlan_usage, |
248 | [FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage, |
249 | [FLOW_DISSECTOR_KEY_TCP] = vcap_tc_flower_handler_tcp_usage, |
250 | [FLOW_DISSECTOR_KEY_ARP] = vcap_tc_flower_handler_arp_usage, |
251 | [FLOW_DISSECTOR_KEY_IP] = vcap_tc_flower_handler_ip_usage, |
252 | }; |
253 | |
254 | static int sparx5_tc_use_dissectors(struct vcap_tc_flower_parse_usage *st, |
255 | struct vcap_admin *admin, |
256 | struct vcap_rule *vrule) |
257 | { |
258 | int idx, err = 0; |
259 | |
260 | for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) { |
261 | if (!flow_rule_match_key(rule: st->frule, key: idx)) |
262 | continue; |
263 | if (!sparx5_tc_flower_usage_handlers[idx]) |
264 | continue; |
265 | err = sparx5_tc_flower_usage_handlers[idx](st); |
266 | if (err) |
267 | return err; |
268 | } |
269 | |
270 | if (st->frule->match.dissector->used_keys ^ st->used_keys) { |
271 | NL_SET_ERR_MSG_MOD(st->fco->common.extack, |
272 | "Unsupported match item" ); |
273 | return -ENOENT; |
274 | } |
275 | |
276 | return err; |
277 | } |
278 | |
279 | static int sparx5_tc_flower_action_check(struct vcap_control *vctrl, |
280 | struct net_device *ndev, |
281 | struct flow_cls_offload *fco, |
282 | bool ingress) |
283 | { |
284 | struct flow_rule *rule = flow_cls_offload_flow_rule(flow_cmd: fco); |
285 | struct flow_action_entry *actent, *last_actent = NULL; |
286 | struct flow_action *act = &rule->action; |
287 | u64 action_mask = 0; |
288 | int idx; |
289 | |
290 | if (!flow_action_has_entries(action: act)) { |
291 | NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions" ); |
292 | return -EINVAL; |
293 | } |
294 | |
295 | if (!flow_action_basic_hw_stats_check(action: act, extack: fco->common.extack)) |
296 | return -EOPNOTSUPP; |
297 | |
298 | flow_action_for_each(idx, actent, act) { |
299 | if (action_mask & BIT(actent->id)) { |
300 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
301 | "More actions of the same type" ); |
302 | return -EINVAL; |
303 | } |
304 | action_mask |= BIT(actent->id); |
305 | last_actent = actent; /* Save last action for later check */ |
306 | } |
307 | |
308 | /* Check if last action is a goto |
309 | * The last chain/lookup does not need to have a goto action |
310 | */ |
311 | if (last_actent->id == FLOW_ACTION_GOTO) { |
312 | /* Check if the destination chain is in one of the VCAPs */ |
313 | if (!vcap_is_next_lookup(vctrl, cur_cid: fco->common.chain_index, |
314 | next_cid: last_actent->chain_index)) { |
315 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
316 | "Invalid goto chain" ); |
317 | return -EINVAL; |
318 | } |
319 | } else if (!vcap_is_last_chain(vctrl, cid: fco->common.chain_index, |
320 | ingress)) { |
321 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
322 | "Last action must be 'goto'" ); |
323 | return -EINVAL; |
324 | } |
325 | |
326 | /* Catch unsupported combinations of actions */ |
327 | if (action_mask & BIT(FLOW_ACTION_TRAP) && |
328 | action_mask & BIT(FLOW_ACTION_ACCEPT)) { |
329 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
330 | "Cannot combine pass and trap action" ); |
331 | return -EOPNOTSUPP; |
332 | } |
333 | |
334 | if (action_mask & BIT(FLOW_ACTION_VLAN_PUSH) && |
335 | action_mask & BIT(FLOW_ACTION_VLAN_POP)) { |
336 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
337 | "Cannot combine vlan push and pop action" ); |
338 | return -EOPNOTSUPP; |
339 | } |
340 | |
341 | if (action_mask & BIT(FLOW_ACTION_VLAN_PUSH) && |
342 | action_mask & BIT(FLOW_ACTION_VLAN_MANGLE)) { |
343 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
344 | "Cannot combine vlan push and modify action" ); |
345 | return -EOPNOTSUPP; |
346 | } |
347 | |
348 | if (action_mask & BIT(FLOW_ACTION_VLAN_POP) && |
349 | action_mask & BIT(FLOW_ACTION_VLAN_MANGLE)) { |
350 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
351 | "Cannot combine vlan pop and modify action" ); |
352 | return -EOPNOTSUPP; |
353 | } |
354 | |
355 | return 0; |
356 | } |
357 | |
358 | /* Add a rule counter action */ |
359 | static int sparx5_tc_add_rule_counter(struct vcap_admin *admin, |
360 | struct vcap_rule *vrule) |
361 | { |
362 | int err; |
363 | |
364 | switch (admin->vtype) { |
365 | case VCAP_TYPE_IS0: |
366 | break; |
367 | case VCAP_TYPE_ES0: |
368 | err = vcap_rule_mod_action_u32(rule: vrule, action: VCAP_AF_ESDX, |
369 | value: vrule->id); |
370 | if (err) |
371 | return err; |
372 | vcap_rule_set_counter_id(rule: vrule, counter_id: vrule->id); |
373 | break; |
374 | case VCAP_TYPE_IS2: |
375 | case VCAP_TYPE_ES2: |
376 | err = vcap_rule_mod_action_u32(rule: vrule, action: VCAP_AF_CNT_ID, |
377 | value: vrule->id); |
378 | if (err) |
379 | return err; |
380 | vcap_rule_set_counter_id(rule: vrule, counter_id: vrule->id); |
381 | break; |
382 | default: |
383 | pr_err("%s:%d: vcap type: %d not supported\n" , |
384 | __func__, __LINE__, admin->vtype); |
385 | break; |
386 | } |
387 | return 0; |
388 | } |
389 | |
390 | /* Collect all port keysets and apply the first of them, possibly wildcarded */ |
391 | static int sparx5_tc_select_protocol_keyset(struct net_device *ndev, |
392 | struct vcap_rule *vrule, |
393 | struct vcap_admin *admin, |
394 | u16 l3_proto, |
395 | struct sparx5_multiple_rules *multi) |
396 | { |
397 | struct sparx5_port *port = netdev_priv(dev: ndev); |
398 | struct vcap_keyset_list portkeysetlist = {}; |
399 | enum vcap_keyfield_set portkeysets[10] = {}; |
400 | struct vcap_keyset_list matches = {}; |
401 | enum vcap_keyfield_set keysets[10]; |
402 | int idx, jdx, err = 0, count = 0; |
403 | struct sparx5_wildcard_rule *mru; |
404 | const struct vcap_set *kinfo; |
405 | struct vcap_control *vctrl; |
406 | |
407 | vctrl = port->sparx5->vcap_ctrl; |
408 | |
409 | /* Find the keysets that the rule can use */ |
410 | matches.keysets = keysets; |
411 | matches.max = ARRAY_SIZE(keysets); |
412 | if (!vcap_rule_find_keysets(rule: vrule, matches: &matches)) |
413 | return -EINVAL; |
414 | |
415 | /* Find the keysets that the port configuration supports */ |
416 | portkeysetlist.max = ARRAY_SIZE(portkeysets); |
417 | portkeysetlist.keysets = portkeysets; |
418 | err = sparx5_vcap_get_port_keyset(ndev, |
419 | admin, cid: vrule->vcap_chain_id, |
420 | l3_proto, |
421 | kslist: &portkeysetlist); |
422 | if (err) |
423 | return err; |
424 | |
425 | /* Find the intersection of the two sets of keyset */ |
426 | for (idx = 0; idx < portkeysetlist.cnt; ++idx) { |
427 | kinfo = vcap_keyfieldset(vctrl, vt: admin->vtype, |
428 | keyset: portkeysetlist.keysets[idx]); |
429 | if (!kinfo) |
430 | continue; |
431 | |
432 | /* Find a port keyset that matches the required keys |
433 | * If there are multiple keysets then compose a type id mask |
434 | */ |
435 | for (jdx = 0; jdx < matches.cnt; ++jdx) { |
436 | if (portkeysetlist.keysets[idx] != matches.keysets[jdx]) |
437 | continue; |
438 | |
439 | mru = &multi->rule[kinfo->sw_per_item]; |
440 | if (!mru->selected) { |
441 | mru->selected = true; |
442 | mru->keyset = portkeysetlist.keysets[idx]; |
443 | mru->value = kinfo->type_id; |
444 | } |
445 | mru->value &= kinfo->type_id; |
446 | mru->mask |= kinfo->type_id; |
447 | ++count; |
448 | } |
449 | } |
450 | if (count == 0) |
451 | return -EPROTO; |
452 | |
453 | if (l3_proto == ETH_P_ALL && count < portkeysetlist.cnt) |
454 | return -ENOENT; |
455 | |
456 | for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) { |
457 | mru = &multi->rule[idx]; |
458 | if (!mru->selected) |
459 | continue; |
460 | |
461 | /* Align the mask to the combined value */ |
462 | mru->mask ^= mru->value; |
463 | } |
464 | |
465 | /* Set the chosen keyset on the rule and set a wildcarded type if there |
466 | * are more than one keyset |
467 | */ |
468 | for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) { |
469 | mru = &multi->rule[idx]; |
470 | if (!mru->selected) |
471 | continue; |
472 | |
473 | vcap_set_rule_set_keyset(rule: vrule, keyset: mru->keyset); |
474 | if (count > 1) |
475 | /* Some keysets do not have a type field */ |
476 | vcap_rule_mod_key_u32(rule: vrule, key: VCAP_KF_TYPE, |
477 | value: mru->value, |
478 | mask: ~mru->mask); |
479 | mru->selected = false; /* mark as done */ |
480 | break; /* Stop here and add more rules later */ |
481 | } |
482 | return err; |
483 | } |
484 | |
485 | static int sparx5_tc_add_rule_copy(struct vcap_control *vctrl, |
486 | struct flow_cls_offload *fco, |
487 | struct vcap_rule *erule, |
488 | struct vcap_admin *admin, |
489 | struct sparx5_wildcard_rule *rule) |
490 | { |
491 | enum vcap_key_field keylist[] = { |
492 | VCAP_KF_IF_IGR_PORT_MASK, |
493 | VCAP_KF_IF_IGR_PORT_MASK_SEL, |
494 | VCAP_KF_IF_IGR_PORT_MASK_RNG, |
495 | VCAP_KF_LOOKUP_FIRST_IS, |
496 | VCAP_KF_TYPE, |
497 | }; |
498 | struct vcap_rule *vrule; |
499 | int err; |
500 | |
501 | /* Add an extra rule with a special user and the new keyset */ |
502 | erule->user = VCAP_USER_TC_EXTRA; |
503 | vrule = vcap_copy_rule(rule: erule); |
504 | if (IS_ERR(ptr: vrule)) |
505 | return PTR_ERR(ptr: vrule); |
506 | |
507 | /* Link the new rule to the existing rule with the cookie */ |
508 | vrule->cookie = erule->cookie; |
509 | vcap_filter_rule_keys(rule: vrule, keylist, ARRAY_SIZE(keylist), drop_unsupported: true); |
510 | err = vcap_set_rule_set_keyset(rule: vrule, keyset: rule->keyset); |
511 | if (err) { |
512 | pr_err("%s:%d: could not set keyset %s in rule: %u\n" , |
513 | __func__, __LINE__, |
514 | vcap_keyset_name(vctrl, rule->keyset), |
515 | vrule->id); |
516 | goto out; |
517 | } |
518 | |
519 | /* Some keysets do not have a type field, so ignore return value */ |
520 | vcap_rule_mod_key_u32(rule: vrule, key: VCAP_KF_TYPE, value: rule->value, mask: ~rule->mask); |
521 | |
522 | err = vcap_set_rule_set_actionset(rule: vrule, actionset: erule->actionset); |
523 | if (err) |
524 | goto out; |
525 | |
526 | err = sparx5_tc_add_rule_counter(admin, vrule); |
527 | if (err) |
528 | goto out; |
529 | |
530 | err = vcap_val_rule(rule: vrule, ETH_P_ALL); |
531 | if (err) { |
532 | pr_err("%s:%d: could not validate rule: %u\n" , |
533 | __func__, __LINE__, vrule->id); |
534 | vcap_set_tc_exterr(fco, vrule); |
535 | goto out; |
536 | } |
537 | err = vcap_add_rule(rule: vrule); |
538 | if (err) { |
539 | pr_err("%s:%d: could not add rule: %u\n" , |
540 | __func__, __LINE__, vrule->id); |
541 | goto out; |
542 | } |
543 | out: |
544 | vcap_free_rule(rule: vrule); |
545 | return err; |
546 | } |
547 | |
548 | static int sparx5_tc_add_remaining_rules(struct vcap_control *vctrl, |
549 | struct flow_cls_offload *fco, |
550 | struct vcap_rule *erule, |
551 | struct vcap_admin *admin, |
552 | struct sparx5_multiple_rules *multi) |
553 | { |
554 | int idx, err = 0; |
555 | |
556 | for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) { |
557 | if (!multi->rule[idx].selected) |
558 | continue; |
559 | |
560 | err = sparx5_tc_add_rule_copy(vctrl, fco, erule, admin, |
561 | rule: &multi->rule[idx]); |
562 | if (err) |
563 | break; |
564 | } |
565 | return err; |
566 | } |
567 | |
568 | /* Add the actionset that is the default for the VCAP type */ |
569 | static int sparx5_tc_set_actionset(struct vcap_admin *admin, |
570 | struct vcap_rule *vrule) |
571 | { |
572 | enum vcap_actionfield_set aset; |
573 | int err = 0; |
574 | |
575 | switch (admin->vtype) { |
576 | case VCAP_TYPE_IS0: |
577 | aset = VCAP_AFS_CLASSIFICATION; |
578 | break; |
579 | case VCAP_TYPE_IS2: |
580 | aset = VCAP_AFS_BASE_TYPE; |
581 | break; |
582 | case VCAP_TYPE_ES0: |
583 | aset = VCAP_AFS_ES0; |
584 | break; |
585 | case VCAP_TYPE_ES2: |
586 | aset = VCAP_AFS_BASE_TYPE; |
587 | break; |
588 | default: |
589 | pr_err("%s:%d: %s\n" , __func__, __LINE__, "Invalid VCAP type" ); |
590 | return -EINVAL; |
591 | } |
592 | /* Do not overwrite any current actionset */ |
593 | if (vrule->actionset == VCAP_AFS_NO_VALUE) |
594 | err = vcap_set_rule_set_actionset(rule: vrule, actionset: aset); |
595 | return err; |
596 | } |
597 | |
598 | /* Add the VCAP key to match on for a rule target value */ |
599 | static int sparx5_tc_add_rule_link_target(struct vcap_admin *admin, |
600 | struct vcap_rule *vrule, |
601 | int target_cid) |
602 | { |
603 | int link_val = target_cid % VCAP_CID_LOOKUP_SIZE; |
604 | int err; |
605 | |
606 | if (!link_val) |
607 | return 0; |
608 | |
609 | switch (admin->vtype) { |
610 | case VCAP_TYPE_IS0: |
611 | /* Add NXT_IDX key for chaining rules between IS0 instances */ |
612 | err = vcap_rule_add_key_u32(rule: vrule, key: VCAP_KF_LOOKUP_GEN_IDX_SEL, |
613 | value: 1, /* enable */ |
614 | mask: ~0); |
615 | if (err) |
616 | return err; |
617 | return vcap_rule_add_key_u32(rule: vrule, key: VCAP_KF_LOOKUP_GEN_IDX, |
618 | value: link_val, /* target */ |
619 | mask: ~0); |
620 | case VCAP_TYPE_IS2: |
621 | /* Add PAG key for chaining rules from IS0 */ |
622 | return vcap_rule_add_key_u32(rule: vrule, key: VCAP_KF_LOOKUP_PAG, |
623 | value: link_val, /* target */ |
624 | mask: ~0); |
625 | case VCAP_TYPE_ES0: |
626 | case VCAP_TYPE_ES2: |
627 | /* Add ISDX key for chaining rules from IS0 */ |
628 | return vcap_rule_add_key_u32(rule: vrule, key: VCAP_KF_ISDX_CLS, value: link_val, |
629 | mask: ~0); |
630 | default: |
631 | break; |
632 | } |
633 | return 0; |
634 | } |
635 | |
636 | /* Add the VCAP action that adds a target value to a rule */ |
637 | static int sparx5_tc_add_rule_link(struct vcap_control *vctrl, |
638 | struct vcap_admin *admin, |
639 | struct vcap_rule *vrule, |
640 | int from_cid, int to_cid) |
641 | { |
642 | struct vcap_admin *to_admin = vcap_find_admin(vctrl, cid: to_cid); |
643 | int diff, err = 0; |
644 | |
645 | if (!to_admin) { |
646 | pr_err("%s:%d: unsupported chain direction: %d\n" , |
647 | __func__, __LINE__, to_cid); |
648 | return -EINVAL; |
649 | } |
650 | |
651 | diff = vcap_chain_offset(vctrl, from_cid, to_cid); |
652 | if (!diff) |
653 | return 0; |
654 | |
655 | if (admin->vtype == VCAP_TYPE_IS0 && |
656 | to_admin->vtype == VCAP_TYPE_IS0) { |
657 | /* Between IS0 instances the G_IDX value is used */ |
658 | err = vcap_rule_add_action_u32(rule: vrule, action: VCAP_AF_NXT_IDX, value: diff); |
659 | if (err) |
660 | goto out; |
661 | err = vcap_rule_add_action_u32(rule: vrule, action: VCAP_AF_NXT_IDX_CTRL, |
662 | value: 1); /* Replace */ |
663 | if (err) |
664 | goto out; |
665 | } else if (admin->vtype == VCAP_TYPE_IS0 && |
666 | to_admin->vtype == VCAP_TYPE_IS2) { |
667 | /* Between IS0 and IS2 the PAG value is used */ |
668 | err = vcap_rule_add_action_u32(rule: vrule, action: VCAP_AF_PAG_VAL, value: diff); |
669 | if (err) |
670 | goto out; |
671 | err = vcap_rule_add_action_u32(rule: vrule, |
672 | action: VCAP_AF_PAG_OVERRIDE_MASK, |
673 | value: 0xff); |
674 | if (err) |
675 | goto out; |
676 | } else if (admin->vtype == VCAP_TYPE_IS0 && |
677 | (to_admin->vtype == VCAP_TYPE_ES0 || |
678 | to_admin->vtype == VCAP_TYPE_ES2)) { |
679 | /* Between IS0 and ES0/ES2 the ISDX value is used */ |
680 | err = vcap_rule_add_action_u32(rule: vrule, action: VCAP_AF_ISDX_VAL, |
681 | value: diff); |
682 | if (err) |
683 | goto out; |
684 | err = vcap_rule_add_action_bit(rule: vrule, |
685 | action: VCAP_AF_ISDX_ADD_REPLACE_SEL, |
686 | val: VCAP_BIT_1); |
687 | if (err) |
688 | goto out; |
689 | } else { |
690 | pr_err("%s:%d: unsupported chain destination: %d\n" , |
691 | __func__, __LINE__, to_cid); |
692 | err = -EOPNOTSUPP; |
693 | } |
694 | out: |
695 | return err; |
696 | } |
697 | |
698 | static int sparx5_tc_flower_parse_act_gate(struct sparx5_psfp_sg *sg, |
699 | struct flow_action_entry *act, |
700 | struct netlink_ext_ack *extack) |
701 | { |
702 | int i; |
703 | |
704 | if (act->gate.prio < -1 || act->gate.prio > SPX5_PSFP_SG_MAX_IPV) { |
705 | NL_SET_ERR_MSG_MOD(extack, "Invalid gate priority" ); |
706 | return -EINVAL; |
707 | } |
708 | |
709 | if (act->gate.cycletime < SPX5_PSFP_SG_MIN_CYCLE_TIME_NS || |
710 | act->gate.cycletime > SPX5_PSFP_SG_MAX_CYCLE_TIME_NS) { |
711 | NL_SET_ERR_MSG_MOD(extack, "Invalid gate cycletime" ); |
712 | return -EINVAL; |
713 | } |
714 | |
715 | if (act->gate.cycletimeext > SPX5_PSFP_SG_MAX_CYCLE_TIME_NS) { |
716 | NL_SET_ERR_MSG_MOD(extack, "Invalid gate cycletimeext" ); |
717 | return -EINVAL; |
718 | } |
719 | |
720 | if (act->gate.num_entries >= SPX5_PSFP_GCE_CNT) { |
721 | NL_SET_ERR_MSG_MOD(extack, "Invalid number of gate entries" ); |
722 | return -EINVAL; |
723 | } |
724 | |
725 | sg->gate_state = true; |
726 | sg->ipv = act->gate.prio; |
727 | sg->num_entries = act->gate.num_entries; |
728 | sg->cycletime = act->gate.cycletime; |
729 | sg->cycletimeext = act->gate.cycletimeext; |
730 | |
731 | for (i = 0; i < sg->num_entries; i++) { |
732 | sg->gce[i].gate_state = !!act->gate.entries[i].gate_state; |
733 | sg->gce[i].interval = act->gate.entries[i].interval; |
734 | sg->gce[i].ipv = act->gate.entries[i].ipv; |
735 | sg->gce[i].maxoctets = act->gate.entries[i].maxoctets; |
736 | } |
737 | |
738 | return 0; |
739 | } |
740 | |
741 | static int sparx5_tc_flower_parse_act_police(struct sparx5_policer *pol, |
742 | struct flow_action_entry *act, |
743 | struct netlink_ext_ack *extack) |
744 | { |
745 | pol->type = SPX5_POL_SERVICE; |
746 | pol->rate = div_u64(dividend: act->police.rate_bytes_ps, divisor: 1000) * 8; |
747 | pol->burst = act->police.burst; |
748 | pol->idx = act->hw_index; |
749 | |
750 | /* rate is now in kbit */ |
751 | if (pol->rate > DIV_ROUND_UP(SPX5_SDLB_GROUP_RATE_MAX, 1000)) { |
752 | NL_SET_ERR_MSG_MOD(extack, "Maximum rate exceeded" ); |
753 | return -EINVAL; |
754 | } |
755 | |
756 | if (act->police.exceed.act_id != FLOW_ACTION_DROP) { |
757 | NL_SET_ERR_MSG_MOD(extack, "Offload not supported when exceed action is not drop" ); |
758 | return -EOPNOTSUPP; |
759 | } |
760 | |
761 | if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && |
762 | act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { |
763 | NL_SET_ERR_MSG_MOD(extack, "Offload not supported when conform action is not pipe or ok" ); |
764 | return -EOPNOTSUPP; |
765 | } |
766 | |
767 | return 0; |
768 | } |
769 | |
770 | static int sparx5_tc_flower_psfp_setup(struct sparx5 *sparx5, |
771 | struct vcap_rule *vrule, int sg_idx, |
772 | int pol_idx, struct sparx5_psfp_sg *sg, |
773 | struct sparx5_psfp_fm *fm, |
774 | struct sparx5_psfp_sf *sf) |
775 | { |
776 | u32 psfp_sfid = 0, psfp_fmid = 0, psfp_sgid = 0; |
777 | int ret; |
778 | |
779 | /* Must always have a stream gate - max sdu (filter option) is evaluated |
780 | * after frames have passed the gate, so in case of only a policer, we |
781 | * allocate a stream gate that is always open. |
782 | */ |
783 | if (sg_idx < 0) { |
784 | sg_idx = sparx5_pool_idx_to_id(SPX5_PSFP_SG_OPEN); |
785 | sg->ipv = 0; /* Disabled */ |
786 | sg->cycletime = SPX5_PSFP_SG_CYCLE_TIME_DEFAULT; |
787 | sg->num_entries = 1; |
788 | sg->gate_state = 1; /* Open */ |
789 | sg->gate_enabled = 1; |
790 | sg->gce[0].gate_state = 1; |
791 | sg->gce[0].interval = SPX5_PSFP_SG_CYCLE_TIME_DEFAULT; |
792 | sg->gce[0].ipv = 0; |
793 | sg->gce[0].maxoctets = 0; /* Disabled */ |
794 | } |
795 | |
796 | ret = sparx5_psfp_sg_add(sparx5, uidx: sg_idx, sg, id: &psfp_sgid); |
797 | if (ret < 0) |
798 | return ret; |
799 | |
800 | if (pol_idx >= 0) { |
801 | /* Add new flow-meter */ |
802 | ret = sparx5_psfp_fm_add(sparx5, uidx: pol_idx, fm, id: &psfp_fmid); |
803 | if (ret < 0) |
804 | return ret; |
805 | } |
806 | |
807 | /* Map stream filter to stream gate */ |
808 | sf->sgid = psfp_sgid; |
809 | |
810 | /* Add new stream-filter and map it to a steam gate */ |
811 | ret = sparx5_psfp_sf_add(sparx5, sf, id: &psfp_sfid); |
812 | if (ret < 0) |
813 | return ret; |
814 | |
815 | /* Streams are classified by ISDX - map ISDX 1:1 to sfid for now. */ |
816 | sparx5_isdx_conf_set(sparx5, isdx: psfp_sfid, sfid: psfp_sfid, fmid: psfp_fmid); |
817 | |
818 | ret = vcap_rule_add_action_bit(rule: vrule, action: VCAP_AF_ISDX_ADD_REPLACE_SEL, |
819 | val: VCAP_BIT_1); |
820 | if (ret) |
821 | return ret; |
822 | |
823 | ret = vcap_rule_add_action_u32(rule: vrule, action: VCAP_AF_ISDX_VAL, value: psfp_sfid); |
824 | if (ret) |
825 | return ret; |
826 | |
827 | return 0; |
828 | } |
829 | |
830 | /* Handle the action trap for a VCAP rule */ |
831 | static int sparx5_tc_action_trap(struct vcap_admin *admin, |
832 | struct vcap_rule *vrule, |
833 | struct flow_cls_offload *fco) |
834 | { |
835 | int err = 0; |
836 | |
837 | switch (admin->vtype) { |
838 | case VCAP_TYPE_IS2: |
839 | err = vcap_rule_add_action_bit(rule: vrule, |
840 | action: VCAP_AF_CPU_COPY_ENA, |
841 | val: VCAP_BIT_1); |
842 | if (err) |
843 | break; |
844 | err = vcap_rule_add_action_u32(rule: vrule, |
845 | action: VCAP_AF_CPU_QUEUE_NUM, value: 0); |
846 | if (err) |
847 | break; |
848 | err = vcap_rule_add_action_u32(rule: vrule, |
849 | action: VCAP_AF_MASK_MODE, |
850 | value: SPX5_PMM_REPLACE_ALL); |
851 | break; |
852 | case VCAP_TYPE_ES0: |
853 | err = vcap_rule_add_action_u32(rule: vrule, |
854 | action: VCAP_AF_FWD_SEL, |
855 | value: SPX5_FWSEL_REDIRECT_TO_LOOPBACK); |
856 | break; |
857 | case VCAP_TYPE_ES2: |
858 | err = vcap_rule_add_action_bit(rule: vrule, |
859 | action: VCAP_AF_CPU_COPY_ENA, |
860 | val: VCAP_BIT_1); |
861 | if (err) |
862 | break; |
863 | err = vcap_rule_add_action_u32(rule: vrule, |
864 | action: VCAP_AF_CPU_QUEUE_NUM, value: 0); |
865 | break; |
866 | default: |
867 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
868 | "Trap action not supported in this VCAP" ); |
869 | err = -EOPNOTSUPP; |
870 | break; |
871 | } |
872 | return err; |
873 | } |
874 | |
875 | static int sparx5_tc_action_vlan_pop(struct vcap_admin *admin, |
876 | struct vcap_rule *vrule, |
877 | struct flow_cls_offload *fco, |
878 | u16 tpid) |
879 | { |
880 | int err = 0; |
881 | |
882 | switch (admin->vtype) { |
883 | case VCAP_TYPE_ES0: |
884 | break; |
885 | default: |
886 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
887 | "VLAN pop action not supported in this VCAP" ); |
888 | return -EOPNOTSUPP; |
889 | } |
890 | |
891 | switch (tpid) { |
892 | case ETH_P_8021Q: |
893 | case ETH_P_8021AD: |
894 | err = vcap_rule_add_action_u32(rule: vrule, |
895 | action: VCAP_AF_PUSH_OUTER_TAG, |
896 | value: SPX5_OTAG_UNTAG); |
897 | break; |
898 | default: |
899 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
900 | "Invalid vlan proto" ); |
901 | err = -EINVAL; |
902 | } |
903 | return err; |
904 | } |
905 | |
906 | static int sparx5_tc_action_vlan_modify(struct vcap_admin *admin, |
907 | struct vcap_rule *vrule, |
908 | struct flow_cls_offload *fco, |
909 | struct flow_action_entry *act, |
910 | u16 tpid) |
911 | { |
912 | int err = 0; |
913 | |
914 | switch (admin->vtype) { |
915 | case VCAP_TYPE_ES0: |
916 | err = vcap_rule_add_action_u32(rule: vrule, |
917 | action: VCAP_AF_PUSH_OUTER_TAG, |
918 | value: SPX5_OTAG_TAG_A); |
919 | if (err) |
920 | return err; |
921 | break; |
922 | default: |
923 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
924 | "VLAN modify action not supported in this VCAP" ); |
925 | return -EOPNOTSUPP; |
926 | } |
927 | |
928 | switch (tpid) { |
929 | case ETH_P_8021Q: |
930 | err = vcap_rule_add_action_u32(rule: vrule, |
931 | action: VCAP_AF_TAG_A_TPID_SEL, |
932 | value: SPX5_TPID_A_8100); |
933 | break; |
934 | case ETH_P_8021AD: |
935 | err = vcap_rule_add_action_u32(rule: vrule, |
936 | action: VCAP_AF_TAG_A_TPID_SEL, |
937 | value: SPX5_TPID_A_88A8); |
938 | break; |
939 | default: |
940 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
941 | "Invalid vlan proto" ); |
942 | err = -EINVAL; |
943 | } |
944 | if (err) |
945 | return err; |
946 | |
947 | err = vcap_rule_add_action_u32(rule: vrule, |
948 | action: VCAP_AF_TAG_A_VID_SEL, |
949 | value: SPX5_VID_A_VAL); |
950 | if (err) |
951 | return err; |
952 | |
953 | err = vcap_rule_add_action_u32(rule: vrule, |
954 | action: VCAP_AF_VID_A_VAL, |
955 | value: act->vlan.vid); |
956 | if (err) |
957 | return err; |
958 | |
959 | err = vcap_rule_add_action_u32(rule: vrule, |
960 | action: VCAP_AF_TAG_A_PCP_SEL, |
961 | value: SPX5_PCP_A_VAL); |
962 | if (err) |
963 | return err; |
964 | |
965 | err = vcap_rule_add_action_u32(rule: vrule, |
966 | action: VCAP_AF_PCP_A_VAL, |
967 | value: act->vlan.prio); |
968 | if (err) |
969 | return err; |
970 | |
971 | return vcap_rule_add_action_u32(rule: vrule, |
972 | action: VCAP_AF_TAG_A_DEI_SEL, |
973 | value: SPX5_DEI_A_CLASSIFIED); |
974 | } |
975 | |
976 | static int sparx5_tc_action_vlan_push(struct vcap_admin *admin, |
977 | struct vcap_rule *vrule, |
978 | struct flow_cls_offload *fco, |
979 | struct flow_action_entry *act, |
980 | u16 tpid) |
981 | { |
982 | u16 act_tpid = be16_to_cpu(act->vlan.proto); |
983 | int err = 0; |
984 | |
985 | switch (admin->vtype) { |
986 | case VCAP_TYPE_ES0: |
987 | break; |
988 | default: |
989 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
990 | "VLAN push action not supported in this VCAP" ); |
991 | return -EOPNOTSUPP; |
992 | } |
993 | |
994 | if (tpid == ETH_P_8021AD) { |
995 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
996 | "Cannot push on double tagged frames" ); |
997 | return -EOPNOTSUPP; |
998 | } |
999 | |
1000 | err = sparx5_tc_action_vlan_modify(admin, vrule, fco, act, tpid: act_tpid); |
1001 | if (err) |
1002 | return err; |
1003 | |
1004 | switch (act_tpid) { |
1005 | case ETH_P_8021Q: |
1006 | break; |
1007 | case ETH_P_8021AD: |
1008 | /* Push classified tag as inner tag */ |
1009 | err = vcap_rule_add_action_u32(rule: vrule, |
1010 | action: VCAP_AF_PUSH_INNER_TAG, |
1011 | value: SPX5_ITAG_PUSH_B_TAG); |
1012 | if (err) |
1013 | break; |
1014 | err = vcap_rule_add_action_u32(rule: vrule, |
1015 | action: VCAP_AF_TAG_B_TPID_SEL, |
1016 | value: SPX5_TPID_B_CLASSIFIED); |
1017 | break; |
1018 | default: |
1019 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
1020 | "Invalid vlan proto" ); |
1021 | err = -EINVAL; |
1022 | } |
1023 | return err; |
1024 | } |
1025 | |
1026 | /* Remove rule keys that may prevent templates from matching a keyset */ |
1027 | static void sparx5_tc_flower_simplify_rule(struct vcap_admin *admin, |
1028 | struct vcap_rule *vrule, |
1029 | u16 l3_proto) |
1030 | { |
1031 | switch (admin->vtype) { |
1032 | case VCAP_TYPE_IS0: |
1033 | vcap_rule_rem_key(rule: vrule, key: VCAP_KF_ETYPE); |
1034 | switch (l3_proto) { |
1035 | case ETH_P_IP: |
1036 | break; |
1037 | case ETH_P_IPV6: |
1038 | vcap_rule_rem_key(rule: vrule, key: VCAP_KF_IP_SNAP_IS); |
1039 | break; |
1040 | default: |
1041 | break; |
1042 | } |
1043 | break; |
1044 | case VCAP_TYPE_ES2: |
1045 | switch (l3_proto) { |
1046 | case ETH_P_IP: |
1047 | if (vrule->keyset == VCAP_KFS_IP4_OTHER) |
1048 | vcap_rule_rem_key(rule: vrule, key: VCAP_KF_TCP_IS); |
1049 | break; |
1050 | case ETH_P_IPV6: |
1051 | if (vrule->keyset == VCAP_KFS_IP6_STD) |
1052 | vcap_rule_rem_key(rule: vrule, key: VCAP_KF_TCP_IS); |
1053 | vcap_rule_rem_key(rule: vrule, key: VCAP_KF_IP4_IS); |
1054 | break; |
1055 | default: |
1056 | break; |
1057 | } |
1058 | break; |
1059 | case VCAP_TYPE_IS2: |
1060 | switch (l3_proto) { |
1061 | case ETH_P_IP: |
1062 | case ETH_P_IPV6: |
1063 | vcap_rule_rem_key(rule: vrule, key: VCAP_KF_IP4_IS); |
1064 | break; |
1065 | default: |
1066 | break; |
1067 | } |
1068 | break; |
1069 | default: |
1070 | break; |
1071 | } |
1072 | } |
1073 | |
1074 | static bool sparx5_tc_flower_use_template(struct net_device *ndev, |
1075 | struct flow_cls_offload *fco, |
1076 | struct vcap_admin *admin, |
1077 | struct vcap_rule *vrule) |
1078 | { |
1079 | struct sparx5_port *port = netdev_priv(dev: ndev); |
1080 | struct sparx5_tc_flower_template *ftp; |
1081 | |
1082 | list_for_each_entry(ftp, &port->tc_templates, list) { |
1083 | if (ftp->cid != fco->common.chain_index) |
1084 | continue; |
1085 | |
1086 | vcap_set_rule_set_keyset(rule: vrule, keyset: ftp->keyset); |
1087 | sparx5_tc_flower_simplify_rule(admin, vrule, l3_proto: ftp->l3_proto); |
1088 | return true; |
1089 | } |
1090 | return false; |
1091 | } |
1092 | |
1093 | static int sparx5_tc_flower_replace(struct net_device *ndev, |
1094 | struct flow_cls_offload *fco, |
1095 | struct vcap_admin *admin, |
1096 | bool ingress) |
1097 | { |
1098 | struct sparx5_psfp_sf sf = { .max_sdu = SPX5_PSFP_SF_MAX_SDU }; |
1099 | struct netlink_ext_ack *extack = fco->common.extack; |
1100 | int err, idx, tc_sg_idx = -1, tc_pol_idx = -1; |
1101 | struct vcap_tc_flower_parse_usage state = { |
1102 | .fco = fco, |
1103 | .l3_proto = ETH_P_ALL, |
1104 | .admin = admin, |
1105 | }; |
1106 | struct sparx5_port *port = netdev_priv(dev: ndev); |
1107 | struct sparx5_multiple_rules multi = {}; |
1108 | struct sparx5 *sparx5 = port->sparx5; |
1109 | struct sparx5_psfp_sg sg = { 0 }; |
1110 | struct sparx5_psfp_fm fm = { 0 }; |
1111 | struct flow_action_entry *act; |
1112 | struct vcap_control *vctrl; |
1113 | struct flow_rule *frule; |
1114 | struct vcap_rule *vrule; |
1115 | |
1116 | vctrl = port->sparx5->vcap_ctrl; |
1117 | |
1118 | err = sparx5_tc_flower_action_check(vctrl, ndev, fco, ingress); |
1119 | if (err) |
1120 | return err; |
1121 | |
1122 | vrule = vcap_alloc_rule(vctrl, ndev, vcap_chain_id: fco->common.chain_index, user: VCAP_USER_TC, |
1123 | priority: fco->common.prio, id: 0); |
1124 | if (IS_ERR(ptr: vrule)) |
1125 | return PTR_ERR(ptr: vrule); |
1126 | |
1127 | vrule->cookie = fco->cookie; |
1128 | |
1129 | state.vrule = vrule; |
1130 | state.frule = flow_cls_offload_flow_rule(flow_cmd: fco); |
1131 | err = sparx5_tc_use_dissectors(st: &state, admin, vrule); |
1132 | if (err) |
1133 | goto out; |
1134 | |
1135 | err = sparx5_tc_add_rule_counter(admin, vrule); |
1136 | if (err) |
1137 | goto out; |
1138 | |
1139 | err = sparx5_tc_add_rule_link_target(admin, vrule, |
1140 | target_cid: fco->common.chain_index); |
1141 | if (err) |
1142 | goto out; |
1143 | |
1144 | frule = flow_cls_offload_flow_rule(flow_cmd: fco); |
1145 | flow_action_for_each(idx, act, &frule->action) { |
1146 | switch (act->id) { |
1147 | case FLOW_ACTION_GATE: { |
1148 | err = sparx5_tc_flower_parse_act_gate(sg: &sg, act, extack); |
1149 | if (err < 0) |
1150 | goto out; |
1151 | |
1152 | tc_sg_idx = act->hw_index; |
1153 | |
1154 | break; |
1155 | } |
1156 | case FLOW_ACTION_POLICE: { |
1157 | err = sparx5_tc_flower_parse_act_police(pol: &fm.pol, act, |
1158 | extack); |
1159 | if (err < 0) |
1160 | goto out; |
1161 | |
1162 | tc_pol_idx = fm.pol.idx; |
1163 | sf.max_sdu = act->police.mtu; |
1164 | |
1165 | break; |
1166 | } |
1167 | case FLOW_ACTION_TRAP: |
1168 | err = sparx5_tc_action_trap(admin, vrule, fco); |
1169 | if (err) |
1170 | goto out; |
1171 | break; |
1172 | case FLOW_ACTION_ACCEPT: |
1173 | err = sparx5_tc_set_actionset(admin, vrule); |
1174 | if (err) |
1175 | goto out; |
1176 | break; |
1177 | case FLOW_ACTION_GOTO: |
1178 | err = sparx5_tc_set_actionset(admin, vrule); |
1179 | if (err) |
1180 | goto out; |
1181 | sparx5_tc_add_rule_link(vctrl, admin, vrule, |
1182 | from_cid: fco->common.chain_index, |
1183 | to_cid: act->chain_index); |
1184 | break; |
1185 | case FLOW_ACTION_VLAN_POP: |
1186 | err = sparx5_tc_action_vlan_pop(admin, vrule, fco, |
1187 | tpid: state.tpid); |
1188 | if (err) |
1189 | goto out; |
1190 | break; |
1191 | case FLOW_ACTION_VLAN_PUSH: |
1192 | err = sparx5_tc_action_vlan_push(admin, vrule, fco, |
1193 | act, tpid: state.tpid); |
1194 | if (err) |
1195 | goto out; |
1196 | break; |
1197 | case FLOW_ACTION_VLAN_MANGLE: |
1198 | err = sparx5_tc_action_vlan_modify(admin, vrule, fco, |
1199 | act, tpid: state.tpid); |
1200 | if (err) |
1201 | goto out; |
1202 | break; |
1203 | default: |
1204 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
1205 | "Unsupported TC action" ); |
1206 | err = -EOPNOTSUPP; |
1207 | goto out; |
1208 | } |
1209 | } |
1210 | |
1211 | /* Setup PSFP */ |
1212 | if (tc_sg_idx >= 0 || tc_pol_idx >= 0) { |
1213 | err = sparx5_tc_flower_psfp_setup(sparx5, vrule, sg_idx: tc_sg_idx, |
1214 | pol_idx: tc_pol_idx, sg: &sg, fm: &fm, sf: &sf); |
1215 | if (err) |
1216 | goto out; |
1217 | } |
1218 | |
1219 | if (!sparx5_tc_flower_use_template(ndev, fco, admin, vrule)) { |
1220 | err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin, |
1221 | l3_proto: state.l3_proto, multi: &multi); |
1222 | if (err) { |
1223 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
1224 | "No matching port keyset for filter protocol and keys" ); |
1225 | goto out; |
1226 | } |
1227 | } |
1228 | |
1229 | /* provide the l3 protocol to guide the keyset selection */ |
1230 | err = vcap_val_rule(rule: vrule, l3_proto: state.l3_proto); |
1231 | if (err) { |
1232 | vcap_set_tc_exterr(fco, vrule); |
1233 | goto out; |
1234 | } |
1235 | err = vcap_add_rule(rule: vrule); |
1236 | if (err) |
1237 | NL_SET_ERR_MSG_MOD(fco->common.extack, |
1238 | "Could not add the filter" ); |
1239 | |
1240 | if (state.l3_proto == ETH_P_ALL) |
1241 | err = sparx5_tc_add_remaining_rules(vctrl, fco, erule: vrule, admin, |
1242 | multi: &multi); |
1243 | |
1244 | out: |
1245 | vcap_free_rule(rule: vrule); |
1246 | return err; |
1247 | } |
1248 | |
1249 | static void sparx5_tc_free_psfp_resources(struct sparx5 *sparx5, |
1250 | struct vcap_rule *vrule) |
1251 | { |
1252 | struct vcap_client_actionfield *afield; |
1253 | u32 isdx, sfid, sgid, fmid; |
1254 | |
1255 | /* Check if VCAP_AF_ISDX_VAL action is set for this rule - and if |
1256 | * it is used for stream and/or flow-meter classification. |
1257 | */ |
1258 | afield = vcap_find_actionfield(rule: vrule, act: VCAP_AF_ISDX_VAL); |
1259 | if (!afield) |
1260 | return; |
1261 | |
1262 | isdx = afield->data.u32.value; |
1263 | sfid = sparx5_psfp_isdx_get_sf(sparx5, isdx); |
1264 | |
1265 | if (!sfid) |
1266 | return; |
1267 | |
1268 | fmid = sparx5_psfp_isdx_get_fm(sparx5, isdx); |
1269 | sgid = sparx5_psfp_sf_get_sg(sparx5, sfid); |
1270 | |
1271 | if (fmid && sparx5_psfp_fm_del(sparx5, id: fmid) < 0) |
1272 | pr_err("%s:%d Could not delete invalid fmid: %d" , __func__, |
1273 | __LINE__, fmid); |
1274 | |
1275 | if (sgid && sparx5_psfp_sg_del(sparx5, id: sgid) < 0) |
1276 | pr_err("%s:%d Could not delete invalid sgid: %d" , __func__, |
1277 | __LINE__, sgid); |
1278 | |
1279 | if (sparx5_psfp_sf_del(sparx5, id: sfid) < 0) |
1280 | pr_err("%s:%d Could not delete invalid sfid: %d" , __func__, |
1281 | __LINE__, sfid); |
1282 | |
1283 | sparx5_isdx_conf_set(sparx5, isdx, sfid: 0, fmid: 0); |
1284 | } |
1285 | |
1286 | static int sparx5_tc_free_rule_resources(struct net_device *ndev, |
1287 | struct vcap_control *vctrl, |
1288 | int rule_id) |
1289 | { |
1290 | struct sparx5_port *port = netdev_priv(dev: ndev); |
1291 | struct sparx5 *sparx5 = port->sparx5; |
1292 | struct vcap_rule *vrule; |
1293 | int ret = 0; |
1294 | |
1295 | vrule = vcap_get_rule(vctrl, id: rule_id); |
1296 | if (IS_ERR(ptr: vrule)) |
1297 | return -EINVAL; |
1298 | |
1299 | sparx5_tc_free_psfp_resources(sparx5, vrule); |
1300 | |
1301 | vcap_free_rule(rule: vrule); |
1302 | return ret; |
1303 | } |
1304 | |
1305 | static int sparx5_tc_flower_destroy(struct net_device *ndev, |
1306 | struct flow_cls_offload *fco, |
1307 | struct vcap_admin *admin) |
1308 | { |
1309 | struct sparx5_port *port = netdev_priv(dev: ndev); |
1310 | int err = -ENOENT, count = 0, rule_id; |
1311 | struct vcap_control *vctrl; |
1312 | |
1313 | vctrl = port->sparx5->vcap_ctrl; |
1314 | while (true) { |
1315 | rule_id = vcap_lookup_rule_by_cookie(vctrl, cookie: fco->cookie); |
1316 | if (rule_id <= 0) |
1317 | break; |
1318 | if (count == 0) { |
1319 | /* Resources are attached to the first rule of |
1320 | * a set of rules. Only works if the rules are |
1321 | * in the correct order. |
1322 | */ |
1323 | err = sparx5_tc_free_rule_resources(ndev, vctrl, |
1324 | rule_id); |
1325 | if (err) |
1326 | pr_err("%s:%d: could not free resources %d\n" , |
1327 | __func__, __LINE__, rule_id); |
1328 | } |
1329 | err = vcap_del_rule(vctrl, ndev, id: rule_id); |
1330 | if (err) { |
1331 | pr_err("%s:%d: could not delete rule %d\n" , |
1332 | __func__, __LINE__, rule_id); |
1333 | break; |
1334 | } |
1335 | } |
1336 | return err; |
1337 | } |
1338 | |
1339 | static int sparx5_tc_flower_stats(struct net_device *ndev, |
1340 | struct flow_cls_offload *fco, |
1341 | struct vcap_admin *admin) |
1342 | { |
1343 | struct sparx5_port *port = netdev_priv(dev: ndev); |
1344 | struct vcap_counter ctr = {}; |
1345 | struct vcap_control *vctrl; |
1346 | ulong lastused = 0; |
1347 | int err; |
1348 | |
1349 | vctrl = port->sparx5->vcap_ctrl; |
1350 | err = vcap_get_rule_count_by_cookie(vctrl, ctr: &ctr, cookie: fco->cookie); |
1351 | if (err) |
1352 | return err; |
1353 | flow_stats_update(flow_stats: &fco->stats, bytes: 0x0, pkts: ctr.value, drops: 0, lastused, |
1354 | used_hw_stats: FLOW_ACTION_HW_STATS_IMMEDIATE); |
1355 | return err; |
1356 | } |
1357 | |
1358 | static int sparx5_tc_flower_template_create(struct net_device *ndev, |
1359 | struct flow_cls_offload *fco, |
1360 | struct vcap_admin *admin) |
1361 | { |
1362 | struct sparx5_port *port = netdev_priv(dev: ndev); |
1363 | struct vcap_tc_flower_parse_usage state = { |
1364 | .fco = fco, |
1365 | .l3_proto = ETH_P_ALL, |
1366 | .admin = admin, |
1367 | }; |
1368 | struct sparx5_tc_flower_template *ftp; |
1369 | struct vcap_keyset_list kslist = {}; |
1370 | enum vcap_keyfield_set keysets[10]; |
1371 | struct vcap_control *vctrl; |
1372 | struct vcap_rule *vrule; |
1373 | int count, err; |
1374 | |
1375 | if (admin->vtype == VCAP_TYPE_ES0) { |
1376 | pr_err("%s:%d: %s\n" , __func__, __LINE__, |
1377 | "VCAP does not support templates" ); |
1378 | return -EINVAL; |
1379 | } |
1380 | |
1381 | count = vcap_admin_rule_count(admin, cid: fco->common.chain_index); |
1382 | if (count > 0) { |
1383 | pr_err("%s:%d: %s\n" , __func__, __LINE__, |
1384 | "Filters are already present" ); |
1385 | return -EBUSY; |
1386 | } |
1387 | |
1388 | ftp = kzalloc(size: sizeof(*ftp), GFP_KERNEL); |
1389 | if (!ftp) |
1390 | return -ENOMEM; |
1391 | |
1392 | ftp->cid = fco->common.chain_index; |
1393 | ftp->orig = VCAP_KFS_NO_VALUE; |
1394 | ftp->keyset = VCAP_KFS_NO_VALUE; |
1395 | |
1396 | vctrl = port->sparx5->vcap_ctrl; |
1397 | vrule = vcap_alloc_rule(vctrl, ndev, vcap_chain_id: fco->common.chain_index, |
1398 | user: VCAP_USER_TC, priority: fco->common.prio, id: 0); |
1399 | if (IS_ERR(ptr: vrule)) { |
1400 | err = PTR_ERR(ptr: vrule); |
1401 | goto err_rule; |
1402 | } |
1403 | |
1404 | state.vrule = vrule; |
1405 | state.frule = flow_cls_offload_flow_rule(flow_cmd: fco); |
1406 | err = sparx5_tc_use_dissectors(st: &state, admin, vrule); |
1407 | if (err) { |
1408 | pr_err("%s:%d: key error: %d\n" , __func__, __LINE__, err); |
1409 | goto out; |
1410 | } |
1411 | |
1412 | ftp->l3_proto = state.l3_proto; |
1413 | |
1414 | sparx5_tc_flower_simplify_rule(admin, vrule, l3_proto: state.l3_proto); |
1415 | |
1416 | /* Find the keysets that the rule can use */ |
1417 | kslist.keysets = keysets; |
1418 | kslist.max = ARRAY_SIZE(keysets); |
1419 | if (!vcap_rule_find_keysets(rule: vrule, matches: &kslist)) { |
1420 | pr_err("%s:%d: %s\n" , __func__, __LINE__, |
1421 | "Could not find a suitable keyset" ); |
1422 | err = -ENOENT; |
1423 | goto out; |
1424 | } |
1425 | |
1426 | ftp->keyset = vcap_select_min_rule_keyset(vctrl, vtype: admin->vtype, kslist: &kslist); |
1427 | kslist.cnt = 0; |
1428 | sparx5_vcap_set_port_keyset(ndev, admin, cid: fco->common.chain_index, |
1429 | l3_proto: state.l3_proto, |
1430 | keyset: ftp->keyset, |
1431 | orig: &kslist); |
1432 | |
1433 | if (kslist.cnt > 0) |
1434 | ftp->orig = kslist.keysets[0]; |
1435 | |
1436 | /* Store new template */ |
1437 | list_add_tail(new: &ftp->list, head: &port->tc_templates); |
1438 | vcap_free_rule(rule: vrule); |
1439 | return 0; |
1440 | |
1441 | out: |
1442 | vcap_free_rule(rule: vrule); |
1443 | err_rule: |
1444 | kfree(objp: ftp); |
1445 | return err; |
1446 | } |
1447 | |
1448 | static int sparx5_tc_flower_template_destroy(struct net_device *ndev, |
1449 | struct flow_cls_offload *fco, |
1450 | struct vcap_admin *admin) |
1451 | { |
1452 | struct sparx5_port *port = netdev_priv(dev: ndev); |
1453 | struct sparx5_tc_flower_template *ftp, *tmp; |
1454 | int err = -ENOENT; |
1455 | |
1456 | /* Rules using the template are removed by the tc framework */ |
1457 | list_for_each_entry_safe(ftp, tmp, &port->tc_templates, list) { |
1458 | if (ftp->cid != fco->common.chain_index) |
1459 | continue; |
1460 | |
1461 | sparx5_vcap_set_port_keyset(ndev, admin, |
1462 | cid: fco->common.chain_index, |
1463 | l3_proto: ftp->l3_proto, keyset: ftp->orig, |
1464 | NULL); |
1465 | list_del(entry: &ftp->list); |
1466 | kfree(objp: ftp); |
1467 | break; |
1468 | } |
1469 | return err; |
1470 | } |
1471 | |
1472 | int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco, |
1473 | bool ingress) |
1474 | { |
1475 | struct sparx5_port *port = netdev_priv(dev: ndev); |
1476 | struct vcap_control *vctrl; |
1477 | struct vcap_admin *admin; |
1478 | int err = -EINVAL; |
1479 | |
1480 | /* Get vcap instance from the chain id */ |
1481 | vctrl = port->sparx5->vcap_ctrl; |
1482 | admin = vcap_find_admin(vctrl, cid: fco->common.chain_index); |
1483 | if (!admin) { |
1484 | NL_SET_ERR_MSG_MOD(fco->common.extack, "Invalid chain" ); |
1485 | return err; |
1486 | } |
1487 | |
1488 | switch (fco->command) { |
1489 | case FLOW_CLS_REPLACE: |
1490 | return sparx5_tc_flower_replace(ndev, fco, admin, ingress); |
1491 | case FLOW_CLS_DESTROY: |
1492 | return sparx5_tc_flower_destroy(ndev, fco, admin); |
1493 | case FLOW_CLS_STATS: |
1494 | return sparx5_tc_flower_stats(ndev, fco, admin); |
1495 | case FLOW_CLS_TMPLT_CREATE: |
1496 | return sparx5_tc_flower_template_create(ndev, fco, admin); |
1497 | case FLOW_CLS_TMPLT_DESTROY: |
1498 | return sparx5_tc_flower_template_destroy(ndev, fco, admin); |
1499 | default: |
1500 | return -EOPNOTSUPP; |
1501 | } |
1502 | } |
1503 | |