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