1 | // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
2 | /* Copyright (c) 2019 Mellanox Technologies */ |
3 | |
4 | #include <linux/mlx5/vport.h> |
5 | #include "mlx5_core.h" |
6 | #include "fs_core.h" |
7 | #include "fs_cmd.h" |
8 | #include "mlx5dr.h" |
9 | #include "fs_dr.h" |
10 | #include "dr_types.h" |
11 | |
12 | static bool dr_is_fw_term_table(struct mlx5_flow_table *ft) |
13 | { |
14 | if (ft->flags & MLX5_FLOW_TABLE_TERMINATION) |
15 | return true; |
16 | |
17 | return false; |
18 | } |
19 | |
20 | static int mlx5_cmd_dr_update_root_ft(struct mlx5_flow_root_namespace *ns, |
21 | struct mlx5_flow_table *ft, |
22 | u32 underlay_qpn, |
23 | bool disconnect) |
24 | { |
25 | return mlx5_fs_cmd_get_fw_cmds()->update_root_ft(ns, ft, underlay_qpn, |
26 | disconnect); |
27 | } |
28 | |
29 | static int set_miss_action(struct mlx5_flow_root_namespace *ns, |
30 | struct mlx5_flow_table *ft, |
31 | struct mlx5_flow_table *next_ft) |
32 | { |
33 | struct mlx5dr_action *old_miss_action; |
34 | struct mlx5dr_action *action = NULL; |
35 | struct mlx5dr_table *next_tbl; |
36 | int err; |
37 | |
38 | next_tbl = next_ft ? next_ft->fs_dr_table.dr_table : NULL; |
39 | if (next_tbl) { |
40 | action = mlx5dr_action_create_dest_table(table: next_tbl); |
41 | if (!action) |
42 | return -EINVAL; |
43 | } |
44 | old_miss_action = ft->fs_dr_table.miss_action; |
45 | err = mlx5dr_table_set_miss_action(tbl: ft->fs_dr_table.dr_table, action); |
46 | if (err && action) { |
47 | err = mlx5dr_action_destroy(action); |
48 | if (err) |
49 | mlx5_core_err(ns->dev, |
50 | "Failed to destroy action (%d)\n" , err); |
51 | action = NULL; |
52 | } |
53 | ft->fs_dr_table.miss_action = action; |
54 | if (old_miss_action) { |
55 | err = mlx5dr_action_destroy(action: old_miss_action); |
56 | if (err) |
57 | mlx5_core_err(ns->dev, "Failed to destroy action (%d)\n" , |
58 | err); |
59 | } |
60 | |
61 | return err; |
62 | } |
63 | |
64 | static int mlx5_cmd_dr_create_flow_table(struct mlx5_flow_root_namespace *ns, |
65 | struct mlx5_flow_table *ft, |
66 | struct mlx5_flow_table_attr *ft_attr, |
67 | struct mlx5_flow_table *next_ft) |
68 | { |
69 | struct mlx5dr_table *tbl; |
70 | u32 flags; |
71 | int err; |
72 | |
73 | if (dr_is_fw_term_table(ft)) |
74 | return mlx5_fs_cmd_get_fw_cmds()->create_flow_table(ns, ft, |
75 | ft_attr, |
76 | next_ft); |
77 | flags = ft->flags; |
78 | /* turn off encap/decap if not supported for sw-str by fw */ |
79 | if (!MLX5_CAP_FLOWTABLE(ns->dev, sw_owner_reformat_supported)) |
80 | flags = ft->flags & ~(MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT | |
81 | MLX5_FLOW_TABLE_TUNNEL_EN_DECAP); |
82 | |
83 | tbl = mlx5dr_table_create(domain: ns->fs_dr_domain.dr_domain, level: ft->level, flags, |
84 | uid: ft_attr->uid); |
85 | if (!tbl) { |
86 | mlx5_core_err(ns->dev, "Failed creating dr flow_table\n" ); |
87 | return -EINVAL; |
88 | } |
89 | |
90 | ft->fs_dr_table.dr_table = tbl; |
91 | ft->id = mlx5dr_table_get_id(table: tbl); |
92 | |
93 | if (next_ft) { |
94 | err = set_miss_action(ns, ft, next_ft); |
95 | if (err) { |
96 | mlx5dr_table_destroy(table: tbl); |
97 | ft->fs_dr_table.dr_table = NULL; |
98 | return err; |
99 | } |
100 | } |
101 | |
102 | ft->max_fte = INT_MAX; |
103 | |
104 | return 0; |
105 | } |
106 | |
107 | static int mlx5_cmd_dr_destroy_flow_table(struct mlx5_flow_root_namespace *ns, |
108 | struct mlx5_flow_table *ft) |
109 | { |
110 | struct mlx5dr_action *action = ft->fs_dr_table.miss_action; |
111 | int err; |
112 | |
113 | if (dr_is_fw_term_table(ft)) |
114 | return mlx5_fs_cmd_get_fw_cmds()->destroy_flow_table(ns, ft); |
115 | |
116 | err = mlx5dr_table_destroy(table: ft->fs_dr_table.dr_table); |
117 | if (err) { |
118 | mlx5_core_err(ns->dev, "Failed to destroy flow_table (%d)\n" , |
119 | err); |
120 | return err; |
121 | } |
122 | if (action) { |
123 | err = mlx5dr_action_destroy(action); |
124 | if (err) { |
125 | mlx5_core_err(ns->dev, "Failed to destroy action(%d)\n" , |
126 | err); |
127 | return err; |
128 | } |
129 | } |
130 | |
131 | return err; |
132 | } |
133 | |
134 | static int mlx5_cmd_dr_modify_flow_table(struct mlx5_flow_root_namespace *ns, |
135 | struct mlx5_flow_table *ft, |
136 | struct mlx5_flow_table *next_ft) |
137 | { |
138 | if (dr_is_fw_term_table(ft)) |
139 | return mlx5_fs_cmd_get_fw_cmds()->modify_flow_table(ns, ft, next_ft); |
140 | |
141 | return set_miss_action(ns, ft, next_ft); |
142 | } |
143 | |
144 | static int mlx5_cmd_dr_create_flow_group(struct mlx5_flow_root_namespace *ns, |
145 | struct mlx5_flow_table *ft, |
146 | u32 *in, |
147 | struct mlx5_flow_group *fg) |
148 | { |
149 | struct mlx5dr_matcher *matcher; |
150 | u32 priority = MLX5_GET(create_flow_group_in, in, |
151 | start_flow_index); |
152 | u8 match_criteria_enable = MLX5_GET(create_flow_group_in, |
153 | in, |
154 | match_criteria_enable); |
155 | struct mlx5dr_match_parameters mask; |
156 | |
157 | if (dr_is_fw_term_table(ft)) |
158 | return mlx5_fs_cmd_get_fw_cmds()->create_flow_group(ns, ft, in, |
159 | fg); |
160 | |
161 | mask.match_buf = MLX5_ADDR_OF(create_flow_group_in, |
162 | in, match_criteria); |
163 | mask.match_sz = sizeof(fg->mask.match_criteria); |
164 | |
165 | matcher = mlx5dr_matcher_create(table: ft->fs_dr_table.dr_table, |
166 | priority, |
167 | match_criteria_enable, |
168 | mask: &mask); |
169 | if (!matcher) { |
170 | mlx5_core_err(ns->dev, "Failed creating matcher\n" ); |
171 | return -EINVAL; |
172 | } |
173 | |
174 | fg->fs_dr_matcher.dr_matcher = matcher; |
175 | return 0; |
176 | } |
177 | |
178 | static int mlx5_cmd_dr_destroy_flow_group(struct mlx5_flow_root_namespace *ns, |
179 | struct mlx5_flow_table *ft, |
180 | struct mlx5_flow_group *fg) |
181 | { |
182 | if (dr_is_fw_term_table(ft)) |
183 | return mlx5_fs_cmd_get_fw_cmds()->destroy_flow_group(ns, ft, fg); |
184 | |
185 | return mlx5dr_matcher_destroy(matcher: fg->fs_dr_matcher.dr_matcher); |
186 | } |
187 | |
188 | static struct mlx5dr_action *create_vport_action(struct mlx5dr_domain *domain, |
189 | struct mlx5_flow_rule *dst) |
190 | { |
191 | struct mlx5_flow_destination *dest_attr = &dst->dest_attr; |
192 | |
193 | return mlx5dr_action_create_dest_vport(domain, vport: dest_attr->vport.num, |
194 | vhca_id_valid: dest_attr->vport.flags & |
195 | MLX5_FLOW_DEST_VPORT_VHCA_ID, |
196 | vhca_id: dest_attr->vport.vhca_id); |
197 | } |
198 | |
199 | static struct mlx5dr_action *create_uplink_action(struct mlx5dr_domain *domain, |
200 | struct mlx5_flow_rule *dst) |
201 | { |
202 | struct mlx5_flow_destination *dest_attr = &dst->dest_attr; |
203 | |
204 | return mlx5dr_action_create_dest_vport(domain, vport: MLX5_VPORT_UPLINK, vhca_id_valid: 1, |
205 | vhca_id: dest_attr->vport.vhca_id); |
206 | } |
207 | |
208 | static struct mlx5dr_action *create_ft_action(struct mlx5dr_domain *domain, |
209 | struct mlx5_flow_rule *dst) |
210 | { |
211 | struct mlx5_flow_table *dest_ft = dst->dest_attr.ft; |
212 | struct mlx5dr_action *tbl_action; |
213 | |
214 | if (mlx5dr_is_fw_table(ft: dest_ft)) |
215 | return mlx5dr_action_create_dest_flow_fw_table(domain, ft: dest_ft); |
216 | |
217 | tbl_action = mlx5dr_action_create_dest_table(table: dest_ft->fs_dr_table.dr_table); |
218 | if (tbl_action) |
219 | tbl_action->dest_tbl->is_wire_ft = |
220 | dest_ft->flags & MLX5_FLOW_TABLE_UPLINK_VPORT ? 1 : 0; |
221 | |
222 | return tbl_action; |
223 | } |
224 | |
225 | static struct mlx5dr_action *create_range_action(struct mlx5dr_domain *domain, |
226 | struct mlx5_flow_rule *dst) |
227 | { |
228 | return mlx5dr_action_create_dest_match_range(dmn: domain, |
229 | field: dst->dest_attr.range.field, |
230 | hit_ft: dst->dest_attr.range.hit_ft, |
231 | miss_ft: dst->dest_attr.range.miss_ft, |
232 | min: dst->dest_attr.range.min, |
233 | max: dst->dest_attr.range.max); |
234 | } |
235 | |
236 | static struct mlx5dr_action *create_action_push_vlan(struct mlx5dr_domain *domain, |
237 | struct mlx5_fs_vlan *vlan) |
238 | { |
239 | u16 n_ethtype = vlan->ethtype; |
240 | u8 prio = vlan->prio; |
241 | u16 vid = vlan->vid; |
242 | u32 vlan_hdr; |
243 | |
244 | vlan_hdr = (u32)n_ethtype << 16 | (u32)(prio) << 12 | (u32)vid; |
245 | return mlx5dr_action_create_push_vlan(domain, htonl(vlan_hdr)); |
246 | } |
247 | |
248 | static bool contain_vport_reformat_action(struct mlx5_flow_rule *dst) |
249 | { |
250 | return (dst->dest_attr.type == MLX5_FLOW_DESTINATION_TYPE_VPORT || |
251 | dst->dest_attr.type == MLX5_FLOW_DESTINATION_TYPE_UPLINK) && |
252 | dst->dest_attr.vport.flags & MLX5_FLOW_DEST_VPORT_REFORMAT_ID; |
253 | } |
254 | |
255 | /* We want to support a rule with 32 destinations, which means we need to |
256 | * account for 32 destinations plus usually a counter plus one more action |
257 | * for a multi-destination flow table. |
258 | */ |
259 | #define MLX5_FLOW_CONTEXT_ACTION_MAX 34 |
260 | static int mlx5_cmd_dr_create_fte(struct mlx5_flow_root_namespace *ns, |
261 | struct mlx5_flow_table *ft, |
262 | struct mlx5_flow_group *group, |
263 | struct fs_fte *fte) |
264 | { |
265 | struct mlx5dr_domain *domain = ns->fs_dr_domain.dr_domain; |
266 | struct mlx5dr_action_dest *term_actions; |
267 | struct mlx5dr_match_parameters params; |
268 | struct mlx5_core_dev *dev = ns->dev; |
269 | struct mlx5dr_action **fs_dr_actions; |
270 | struct mlx5dr_action *tmp_action; |
271 | struct mlx5dr_action **actions; |
272 | bool delay_encap_set = false; |
273 | struct mlx5dr_rule *rule; |
274 | struct mlx5_flow_rule *dst; |
275 | int fs_dr_num_actions = 0; |
276 | int num_term_actions = 0; |
277 | int num_actions = 0; |
278 | size_t match_sz; |
279 | int err = 0; |
280 | int i; |
281 | |
282 | if (dr_is_fw_term_table(ft)) |
283 | return mlx5_fs_cmd_get_fw_cmds()->create_fte(ns, ft, group, fte); |
284 | |
285 | actions = kcalloc(MLX5_FLOW_CONTEXT_ACTION_MAX, size: sizeof(*actions), |
286 | GFP_KERNEL); |
287 | if (!actions) { |
288 | err = -ENOMEM; |
289 | goto out_err; |
290 | } |
291 | |
292 | fs_dr_actions = kcalloc(MLX5_FLOW_CONTEXT_ACTION_MAX, |
293 | size: sizeof(*fs_dr_actions), GFP_KERNEL); |
294 | if (!fs_dr_actions) { |
295 | err = -ENOMEM; |
296 | goto free_actions_alloc; |
297 | } |
298 | |
299 | term_actions = kcalloc(MLX5_FLOW_CONTEXT_ACTION_MAX, |
300 | size: sizeof(*term_actions), GFP_KERNEL); |
301 | if (!term_actions) { |
302 | err = -ENOMEM; |
303 | goto free_fs_dr_actions_alloc; |
304 | } |
305 | |
306 | match_sz = sizeof(fte->val); |
307 | |
308 | /* Drop reformat action bit if destination vport set with reformat */ |
309 | if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { |
310 | list_for_each_entry(dst, &fte->node.children, node.list) { |
311 | if (!contain_vport_reformat_action(dst)) |
312 | continue; |
313 | |
314 | fte->action.action &= ~MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT; |
315 | break; |
316 | } |
317 | } |
318 | |
319 | /* The order of the actions are must to be keep, only the following |
320 | * order is supported by SW steering: |
321 | * TX: modify header -> push vlan -> encap |
322 | * RX: decap -> pop vlan -> modify header |
323 | */ |
324 | if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_DECAP) { |
325 | enum mlx5dr_action_reformat_type decap_type = |
326 | DR_ACTION_REFORMAT_TYP_TNL_L2_TO_L2; |
327 | |
328 | tmp_action = mlx5dr_action_create_packet_reformat(dmn: domain, |
329 | reformat_type: decap_type, |
330 | reformat_param_0: 0, reformat_param_1: 0, data_sz: 0, |
331 | NULL); |
332 | if (!tmp_action) { |
333 | err = -ENOMEM; |
334 | goto free_actions; |
335 | } |
336 | fs_dr_actions[fs_dr_num_actions++] = tmp_action; |
337 | actions[num_actions++] = tmp_action; |
338 | } |
339 | |
340 | if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT) { |
341 | bool is_decap; |
342 | |
343 | if (fte->action.pkt_reformat->owner == MLX5_FLOW_RESOURCE_OWNER_FW) { |
344 | err = -EINVAL; |
345 | mlx5dr_err(domain, "FW-owned reformat can't be used in SW rule\n" ); |
346 | goto free_actions; |
347 | } |
348 | |
349 | is_decap = fte->action.pkt_reformat->reformat_type == |
350 | MLX5_REFORMAT_TYPE_L3_TUNNEL_TO_L2; |
351 | |
352 | if (is_decap) |
353 | actions[num_actions++] = |
354 | fte->action.pkt_reformat->action.dr_action; |
355 | else |
356 | delay_encap_set = true; |
357 | } |
358 | |
359 | if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP) { |
360 | tmp_action = |
361 | mlx5dr_action_create_pop_vlan(); |
362 | if (!tmp_action) { |
363 | err = -ENOMEM; |
364 | goto free_actions; |
365 | } |
366 | fs_dr_actions[fs_dr_num_actions++] = tmp_action; |
367 | actions[num_actions++] = tmp_action; |
368 | } |
369 | |
370 | if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP_2) { |
371 | tmp_action = |
372 | mlx5dr_action_create_pop_vlan(); |
373 | if (!tmp_action) { |
374 | err = -ENOMEM; |
375 | goto free_actions; |
376 | } |
377 | fs_dr_actions[fs_dr_num_actions++] = tmp_action; |
378 | actions[num_actions++] = tmp_action; |
379 | } |
380 | |
381 | if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) |
382 | actions[num_actions++] = |
383 | fte->action.modify_hdr->action.dr_action; |
384 | |
385 | if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH) { |
386 | tmp_action = create_action_push_vlan(domain, vlan: &fte->action.vlan[0]); |
387 | if (!tmp_action) { |
388 | err = -ENOMEM; |
389 | goto free_actions; |
390 | } |
391 | fs_dr_actions[fs_dr_num_actions++] = tmp_action; |
392 | actions[num_actions++] = tmp_action; |
393 | } |
394 | |
395 | if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH_2) { |
396 | tmp_action = create_action_push_vlan(domain, vlan: &fte->action.vlan[1]); |
397 | if (!tmp_action) { |
398 | err = -ENOMEM; |
399 | goto free_actions; |
400 | } |
401 | fs_dr_actions[fs_dr_num_actions++] = tmp_action; |
402 | actions[num_actions++] = tmp_action; |
403 | } |
404 | |
405 | if (delay_encap_set) |
406 | actions[num_actions++] = |
407 | fte->action.pkt_reformat->action.dr_action; |
408 | |
409 | /* The order of the actions below is not important */ |
410 | |
411 | if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_DROP) { |
412 | tmp_action = mlx5dr_action_create_drop(); |
413 | if (!tmp_action) { |
414 | err = -ENOMEM; |
415 | goto free_actions; |
416 | } |
417 | fs_dr_actions[fs_dr_num_actions++] = tmp_action; |
418 | term_actions[num_term_actions++].dest = tmp_action; |
419 | } |
420 | |
421 | if (fte->flow_context.flow_tag) { |
422 | tmp_action = |
423 | mlx5dr_action_create_tag(tag_value: fte->flow_context.flow_tag); |
424 | if (!tmp_action) { |
425 | err = -ENOMEM; |
426 | goto free_actions; |
427 | } |
428 | fs_dr_actions[fs_dr_num_actions++] = tmp_action; |
429 | actions[num_actions++] = tmp_action; |
430 | } |
431 | |
432 | if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { |
433 | list_for_each_entry(dst, &fte->node.children, node.list) { |
434 | enum mlx5_flow_destination_type type = dst->dest_attr.type; |
435 | u32 id; |
436 | |
437 | if (fs_dr_num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX || |
438 | num_term_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) { |
439 | err = -EOPNOTSUPP; |
440 | goto free_actions; |
441 | } |
442 | |
443 | if (type == MLX5_FLOW_DESTINATION_TYPE_COUNTER) |
444 | continue; |
445 | |
446 | switch (type) { |
447 | case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE: |
448 | tmp_action = create_ft_action(domain, dst); |
449 | if (!tmp_action) { |
450 | err = -ENOMEM; |
451 | goto free_actions; |
452 | } |
453 | fs_dr_actions[fs_dr_num_actions++] = tmp_action; |
454 | term_actions[num_term_actions++].dest = tmp_action; |
455 | break; |
456 | case MLX5_FLOW_DESTINATION_TYPE_UPLINK: |
457 | case MLX5_FLOW_DESTINATION_TYPE_VPORT: |
458 | tmp_action = type == MLX5_FLOW_DESTINATION_TYPE_VPORT ? |
459 | create_vport_action(domain, dst) : |
460 | create_uplink_action(domain, dst); |
461 | if (!tmp_action) { |
462 | err = -ENOMEM; |
463 | goto free_actions; |
464 | } |
465 | fs_dr_actions[fs_dr_num_actions++] = tmp_action; |
466 | term_actions[num_term_actions].dest = tmp_action; |
467 | |
468 | if (dst->dest_attr.vport.flags & |
469 | MLX5_FLOW_DEST_VPORT_REFORMAT_ID) |
470 | term_actions[num_term_actions].reformat = |
471 | dst->dest_attr.vport.pkt_reformat->action.dr_action; |
472 | |
473 | num_term_actions++; |
474 | break; |
475 | case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE_NUM: |
476 | id = dst->dest_attr.ft_num; |
477 | tmp_action = mlx5dr_action_create_dest_table_num(dmn: domain, |
478 | table_num: id); |
479 | if (!tmp_action) { |
480 | err = -ENOMEM; |
481 | goto free_actions; |
482 | } |
483 | fs_dr_actions[fs_dr_num_actions++] = tmp_action; |
484 | term_actions[num_term_actions++].dest = tmp_action; |
485 | break; |
486 | case MLX5_FLOW_DESTINATION_TYPE_FLOW_SAMPLER: |
487 | id = dst->dest_attr.sampler_id; |
488 | tmp_action = mlx5dr_action_create_flow_sampler(dmn: domain, |
489 | sampler_id: id); |
490 | if (!tmp_action) { |
491 | err = -ENOMEM; |
492 | goto free_actions; |
493 | } |
494 | fs_dr_actions[fs_dr_num_actions++] = tmp_action; |
495 | term_actions[num_term_actions++].dest = tmp_action; |
496 | break; |
497 | case MLX5_FLOW_DESTINATION_TYPE_RANGE: |
498 | tmp_action = create_range_action(domain, dst); |
499 | if (!tmp_action) { |
500 | err = -ENOMEM; |
501 | goto free_actions; |
502 | } |
503 | fs_dr_actions[fs_dr_num_actions++] = tmp_action; |
504 | term_actions[num_term_actions++].dest = tmp_action; |
505 | break; |
506 | default: |
507 | err = -EOPNOTSUPP; |
508 | goto free_actions; |
509 | } |
510 | } |
511 | } |
512 | |
513 | if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { |
514 | list_for_each_entry(dst, &fte->node.children, node.list) { |
515 | u32 id; |
516 | |
517 | if (dst->dest_attr.type != |
518 | MLX5_FLOW_DESTINATION_TYPE_COUNTER) |
519 | continue; |
520 | |
521 | if (num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX || |
522 | fs_dr_num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) { |
523 | err = -EOPNOTSUPP; |
524 | goto free_actions; |
525 | } |
526 | |
527 | id = dst->dest_attr.counter_id; |
528 | tmp_action = |
529 | mlx5dr_action_create_flow_counter(counter_id: id); |
530 | if (!tmp_action) { |
531 | err = -ENOMEM; |
532 | goto free_actions; |
533 | } |
534 | |
535 | fs_dr_actions[fs_dr_num_actions++] = tmp_action; |
536 | actions[num_actions++] = tmp_action; |
537 | } |
538 | } |
539 | |
540 | if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_EXECUTE_ASO) { |
541 | if (fte->action.exe_aso.type != MLX5_EXE_ASO_FLOW_METER) { |
542 | err = -EOPNOTSUPP; |
543 | goto free_actions; |
544 | } |
545 | |
546 | tmp_action = |
547 | mlx5dr_action_create_aso(dmn: domain, |
548 | obj_id: fte->action.exe_aso.object_id, |
549 | return_reg_id: fte->action.exe_aso.return_reg_id, |
550 | aso_type: fte->action.exe_aso.type, |
551 | init_color: fte->action.exe_aso.flow_meter.init_color, |
552 | meter_id: fte->action.exe_aso.flow_meter.meter_idx); |
553 | if (!tmp_action) { |
554 | err = -ENOMEM; |
555 | goto free_actions; |
556 | } |
557 | fs_dr_actions[fs_dr_num_actions++] = tmp_action; |
558 | actions[num_actions++] = tmp_action; |
559 | } |
560 | |
561 | params.match_sz = match_sz; |
562 | params.match_buf = (u64 *)fte->val; |
563 | if (num_term_actions == 1) { |
564 | if (term_actions->reformat) { |
565 | if (num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) { |
566 | err = -EOPNOTSUPP; |
567 | goto free_actions; |
568 | } |
569 | actions[num_actions++] = term_actions->reformat; |
570 | } |
571 | |
572 | if (num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) { |
573 | err = -EOPNOTSUPP; |
574 | goto free_actions; |
575 | } |
576 | actions[num_actions++] = term_actions->dest; |
577 | } else if (num_term_actions > 1) { |
578 | bool ignore_flow_level = |
579 | !!(fte->action.flags & FLOW_ACT_IGNORE_FLOW_LEVEL); |
580 | u32 flow_source = fte->flow_context.flow_source; |
581 | |
582 | if (num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX || |
583 | fs_dr_num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) { |
584 | err = -EOPNOTSUPP; |
585 | goto free_actions; |
586 | } |
587 | tmp_action = mlx5dr_action_create_mult_dest_tbl(dmn: domain, |
588 | dests: term_actions, |
589 | num_of_dests: num_term_actions, |
590 | ignore_flow_level, |
591 | flow_source); |
592 | if (!tmp_action) { |
593 | err = -EOPNOTSUPP; |
594 | goto free_actions; |
595 | } |
596 | fs_dr_actions[fs_dr_num_actions++] = tmp_action; |
597 | actions[num_actions++] = tmp_action; |
598 | } |
599 | |
600 | rule = mlx5dr_rule_create(matcher: group->fs_dr_matcher.dr_matcher, |
601 | value: ¶ms, |
602 | num_actions, |
603 | actions, |
604 | flow_source: fte->flow_context.flow_source); |
605 | if (!rule) { |
606 | err = -EINVAL; |
607 | goto free_actions; |
608 | } |
609 | |
610 | kfree(objp: term_actions); |
611 | kfree(objp: actions); |
612 | |
613 | fte->fs_dr_rule.dr_rule = rule; |
614 | fte->fs_dr_rule.num_actions = fs_dr_num_actions; |
615 | fte->fs_dr_rule.dr_actions = fs_dr_actions; |
616 | |
617 | return 0; |
618 | |
619 | free_actions: |
620 | /* Free in reverse order to handle action dependencies */ |
621 | for (i = fs_dr_num_actions - 1; i >= 0; i--) |
622 | if (!IS_ERR_OR_NULL(ptr: fs_dr_actions[i])) |
623 | mlx5dr_action_destroy(action: fs_dr_actions[i]); |
624 | |
625 | kfree(objp: term_actions); |
626 | free_fs_dr_actions_alloc: |
627 | kfree(objp: fs_dr_actions); |
628 | free_actions_alloc: |
629 | kfree(objp: actions); |
630 | out_err: |
631 | mlx5_core_err(dev, "Failed to create dr rule err(%d)\n" , err); |
632 | return err; |
633 | } |
634 | |
635 | static int mlx5_cmd_dr_packet_reformat_alloc(struct mlx5_flow_root_namespace *ns, |
636 | struct mlx5_pkt_reformat_params *params, |
637 | enum mlx5_flow_namespace_type namespace, |
638 | struct mlx5_pkt_reformat *pkt_reformat) |
639 | { |
640 | struct mlx5dr_domain *dr_domain = ns->fs_dr_domain.dr_domain; |
641 | struct mlx5dr_action *action; |
642 | int dr_reformat; |
643 | |
644 | switch (params->type) { |
645 | case MLX5_REFORMAT_TYPE_L2_TO_VXLAN: |
646 | case MLX5_REFORMAT_TYPE_L2_TO_NVGRE: |
647 | case MLX5_REFORMAT_TYPE_L2_TO_L2_TUNNEL: |
648 | dr_reformat = DR_ACTION_REFORMAT_TYP_L2_TO_TNL_L2; |
649 | break; |
650 | case MLX5_REFORMAT_TYPE_L3_TUNNEL_TO_L2: |
651 | dr_reformat = DR_ACTION_REFORMAT_TYP_TNL_L3_TO_L2; |
652 | break; |
653 | case MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL: |
654 | dr_reformat = DR_ACTION_REFORMAT_TYP_L2_TO_TNL_L3; |
655 | break; |
656 | case MLX5_REFORMAT_TYPE_INSERT_HDR: |
657 | dr_reformat = DR_ACTION_REFORMAT_TYP_INSERT_HDR; |
658 | break; |
659 | case MLX5_REFORMAT_TYPE_REMOVE_HDR: |
660 | dr_reformat = DR_ACTION_REFORMAT_TYP_REMOVE_HDR; |
661 | break; |
662 | default: |
663 | mlx5_core_err(ns->dev, "Packet-reformat not supported(%d)\n" , |
664 | params->type); |
665 | return -EOPNOTSUPP; |
666 | } |
667 | |
668 | action = mlx5dr_action_create_packet_reformat(dmn: dr_domain, |
669 | reformat_type: dr_reformat, |
670 | reformat_param_0: params->param_0, |
671 | reformat_param_1: params->param_1, |
672 | data_sz: params->size, |
673 | data: params->data); |
674 | if (!action) { |
675 | mlx5_core_err(ns->dev, "Failed allocating packet-reformat action\n" ); |
676 | return -EINVAL; |
677 | } |
678 | |
679 | pkt_reformat->owner = MLX5_FLOW_RESOURCE_OWNER_SW; |
680 | pkt_reformat->action.dr_action = action; |
681 | |
682 | return 0; |
683 | } |
684 | |
685 | static void mlx5_cmd_dr_packet_reformat_dealloc(struct mlx5_flow_root_namespace *ns, |
686 | struct mlx5_pkt_reformat *pkt_reformat) |
687 | { |
688 | mlx5dr_action_destroy(action: pkt_reformat->action.dr_action); |
689 | } |
690 | |
691 | static int (struct mlx5_flow_root_namespace *ns, |
692 | u8 namespace, u8 num_actions, |
693 | void *modify_actions, |
694 | struct mlx5_modify_hdr *modify_hdr) |
695 | { |
696 | struct mlx5dr_domain *dr_domain = ns->fs_dr_domain.dr_domain; |
697 | struct mlx5dr_action *action; |
698 | size_t actions_sz; |
699 | |
700 | actions_sz = MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto) * |
701 | num_actions; |
702 | action = mlx5dr_action_create_modify_header(domain: dr_domain, flags: 0, |
703 | actions_sz, |
704 | actions: modify_actions); |
705 | if (!action) { |
706 | mlx5_core_err(ns->dev, "Failed allocating modify-header action\n" ); |
707 | return -EINVAL; |
708 | } |
709 | |
710 | modify_hdr->owner = MLX5_FLOW_RESOURCE_OWNER_SW; |
711 | modify_hdr->action.dr_action = action; |
712 | |
713 | return 0; |
714 | } |
715 | |
716 | static void (struct mlx5_flow_root_namespace *ns, |
717 | struct mlx5_modify_hdr *modify_hdr) |
718 | { |
719 | mlx5dr_action_destroy(action: modify_hdr->action.dr_action); |
720 | } |
721 | |
722 | static int |
723 | mlx5_cmd_dr_destroy_match_definer(struct mlx5_flow_root_namespace *ns, |
724 | int definer_id) |
725 | { |
726 | return -EOPNOTSUPP; |
727 | } |
728 | |
729 | static int mlx5_cmd_dr_create_match_definer(struct mlx5_flow_root_namespace *ns, |
730 | u16 format_id, u32 *match_mask) |
731 | { |
732 | return -EOPNOTSUPP; |
733 | } |
734 | |
735 | static int mlx5_cmd_dr_delete_fte(struct mlx5_flow_root_namespace *ns, |
736 | struct mlx5_flow_table *ft, |
737 | struct fs_fte *fte) |
738 | { |
739 | struct mlx5_fs_dr_rule *rule = &fte->fs_dr_rule; |
740 | int err; |
741 | int i; |
742 | |
743 | if (dr_is_fw_term_table(ft)) |
744 | return mlx5_fs_cmd_get_fw_cmds()->delete_fte(ns, ft, fte); |
745 | |
746 | err = mlx5dr_rule_destroy(rule: rule->dr_rule); |
747 | if (err) |
748 | return err; |
749 | |
750 | /* Free in reverse order to handle action dependencies */ |
751 | for (i = rule->num_actions - 1; i >= 0; i--) |
752 | if (!IS_ERR_OR_NULL(ptr: rule->dr_actions[i])) |
753 | mlx5dr_action_destroy(action: rule->dr_actions[i]); |
754 | |
755 | kfree(objp: rule->dr_actions); |
756 | return 0; |
757 | } |
758 | |
759 | static int mlx5_cmd_dr_update_fte(struct mlx5_flow_root_namespace *ns, |
760 | struct mlx5_flow_table *ft, |
761 | struct mlx5_flow_group *group, |
762 | int modify_mask, |
763 | struct fs_fte *fte) |
764 | { |
765 | struct fs_fte fte_tmp = {}; |
766 | int ret; |
767 | |
768 | if (dr_is_fw_term_table(ft)) |
769 | return mlx5_fs_cmd_get_fw_cmds()->update_fte(ns, ft, group, modify_mask, fte); |
770 | |
771 | /* Backup current dr rule details */ |
772 | fte_tmp.fs_dr_rule = fte->fs_dr_rule; |
773 | memset(&fte->fs_dr_rule, 0, sizeof(struct mlx5_fs_dr_rule)); |
774 | |
775 | /* First add the new updated rule, then delete the old rule */ |
776 | ret = mlx5_cmd_dr_create_fte(ns, ft, group, fte); |
777 | if (ret) |
778 | goto restore_fte; |
779 | |
780 | ret = mlx5_cmd_dr_delete_fte(ns, ft, fte: &fte_tmp); |
781 | WARN_ONCE(ret, "dr update fte duplicate rule deletion failed\n" ); |
782 | return ret; |
783 | |
784 | restore_fte: |
785 | fte->fs_dr_rule = fte_tmp.fs_dr_rule; |
786 | return ret; |
787 | } |
788 | |
789 | static int mlx5_cmd_dr_set_peer(struct mlx5_flow_root_namespace *ns, |
790 | struct mlx5_flow_root_namespace *peer_ns, |
791 | u16 peer_vhca_id) |
792 | { |
793 | struct mlx5dr_domain *peer_domain = NULL; |
794 | |
795 | if (peer_ns) |
796 | peer_domain = peer_ns->fs_dr_domain.dr_domain; |
797 | mlx5dr_domain_set_peer(dmn: ns->fs_dr_domain.dr_domain, |
798 | peer_dmn: peer_domain, peer_vhca_id); |
799 | return 0; |
800 | } |
801 | |
802 | static int mlx5_cmd_dr_create_ns(struct mlx5_flow_root_namespace *ns) |
803 | { |
804 | ns->fs_dr_domain.dr_domain = |
805 | mlx5dr_domain_create(mdev: ns->dev, |
806 | type: MLX5DR_DOMAIN_TYPE_FDB); |
807 | if (!ns->fs_dr_domain.dr_domain) { |
808 | mlx5_core_err(ns->dev, "Failed to create dr flow namespace\n" ); |
809 | return -EOPNOTSUPP; |
810 | } |
811 | return 0; |
812 | } |
813 | |
814 | static int mlx5_cmd_dr_destroy_ns(struct mlx5_flow_root_namespace *ns) |
815 | { |
816 | return mlx5dr_domain_destroy(domain: ns->fs_dr_domain.dr_domain); |
817 | } |
818 | |
819 | static u32 mlx5_cmd_dr_get_capabilities(struct mlx5_flow_root_namespace *ns, |
820 | enum fs_flow_table_type ft_type) |
821 | { |
822 | u32 steering_caps = 0; |
823 | |
824 | if (ft_type != FS_FT_FDB || |
825 | MLX5_CAP_GEN(ns->dev, steering_format_version) == MLX5_STEERING_FORMAT_CONNECTX_5) |
826 | return 0; |
827 | |
828 | steering_caps |= MLX5_FLOW_STEERING_CAP_VLAN_PUSH_ON_RX; |
829 | steering_caps |= MLX5_FLOW_STEERING_CAP_VLAN_POP_ON_TX; |
830 | |
831 | if (mlx5dr_supp_match_ranges(dev: ns->dev)) |
832 | steering_caps |= MLX5_FLOW_STEERING_CAP_MATCH_RANGES; |
833 | |
834 | return steering_caps; |
835 | } |
836 | |
837 | int mlx5_fs_dr_action_get_pkt_reformat_id(struct mlx5_pkt_reformat *pkt_reformat) |
838 | { |
839 | switch (pkt_reformat->reformat_type) { |
840 | case MLX5_REFORMAT_TYPE_L2_TO_VXLAN: |
841 | case MLX5_REFORMAT_TYPE_L2_TO_NVGRE: |
842 | case MLX5_REFORMAT_TYPE_L2_TO_L2_TUNNEL: |
843 | case MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL: |
844 | case MLX5_REFORMAT_TYPE_INSERT_HDR: |
845 | return mlx5dr_action_get_pkt_reformat_id(action: pkt_reformat->action.dr_action); |
846 | } |
847 | return -EOPNOTSUPP; |
848 | } |
849 | |
850 | bool mlx5_fs_dr_is_supported(struct mlx5_core_dev *dev) |
851 | { |
852 | return mlx5dr_is_supported(dev); |
853 | } |
854 | |
855 | static const struct mlx5_flow_cmds mlx5_flow_cmds_dr = { |
856 | .create_flow_table = mlx5_cmd_dr_create_flow_table, |
857 | .destroy_flow_table = mlx5_cmd_dr_destroy_flow_table, |
858 | .modify_flow_table = mlx5_cmd_dr_modify_flow_table, |
859 | .create_flow_group = mlx5_cmd_dr_create_flow_group, |
860 | .destroy_flow_group = mlx5_cmd_dr_destroy_flow_group, |
861 | .create_fte = mlx5_cmd_dr_create_fte, |
862 | .update_fte = mlx5_cmd_dr_update_fte, |
863 | .delete_fte = mlx5_cmd_dr_delete_fte, |
864 | .update_root_ft = mlx5_cmd_dr_update_root_ft, |
865 | .packet_reformat_alloc = mlx5_cmd_dr_packet_reformat_alloc, |
866 | .packet_reformat_dealloc = mlx5_cmd_dr_packet_reformat_dealloc, |
867 | .modify_header_alloc = mlx5_cmd_dr_modify_header_alloc, |
868 | .modify_header_dealloc = mlx5_cmd_dr_modify_header_dealloc, |
869 | .create_match_definer = mlx5_cmd_dr_create_match_definer, |
870 | .destroy_match_definer = mlx5_cmd_dr_destroy_match_definer, |
871 | .set_peer = mlx5_cmd_dr_set_peer, |
872 | .create_ns = mlx5_cmd_dr_create_ns, |
873 | .destroy_ns = mlx5_cmd_dr_destroy_ns, |
874 | .get_capabilities = mlx5_cmd_dr_get_capabilities, |
875 | }; |
876 | |
877 | const struct mlx5_flow_cmds *mlx5_fs_cmd_get_dr_cmds(void) |
878 | { |
879 | return &mlx5_flow_cmds_dr; |
880 | } |
881 | |