1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2019 Mellanox Technologies */
3 
4 #include "mlx5_core.h"
5 #include "fs_core.h"
6 #include "fs_cmd.h"
7 #include "mlx5dr.h"
8 #include "fs_dr.h"
9 
10 static bool mlx5_dr_is_fw_table(u32 flags)
11 {
12 	if (flags & MLX5_FLOW_TABLE_TERMINATION)
13 		return true;
14 
15 	return false;
16 }
17 
18 static int mlx5_cmd_dr_update_root_ft(struct mlx5_flow_root_namespace *ns,
19 				      struct mlx5_flow_table *ft,
20 				      u32 underlay_qpn,
21 				      bool disconnect)
22 {
23 	return mlx5_fs_cmd_get_fw_cmds()->update_root_ft(ns, ft, underlay_qpn,
24 							 disconnect);
25 }
26 
27 static int set_miss_action(struct mlx5_flow_root_namespace *ns,
28 			   struct mlx5_flow_table *ft,
29 			   struct mlx5_flow_table *next_ft)
30 {
31 	struct mlx5dr_action *old_miss_action;
32 	struct mlx5dr_action *action = NULL;
33 	struct mlx5dr_table *next_tbl;
34 	int err;
35 
36 	next_tbl = next_ft ? next_ft->fs_dr_table.dr_table : NULL;
37 	if (next_tbl) {
38 		action = mlx5dr_action_create_dest_table(next_tbl);
39 		if (!action)
40 			return -EINVAL;
41 	}
42 	old_miss_action = ft->fs_dr_table.miss_action;
43 	err = mlx5dr_table_set_miss_action(ft->fs_dr_table.dr_table, action);
44 	if (err && action) {
45 		err = mlx5dr_action_destroy(action);
46 		if (err) {
47 			action = NULL;
48 			mlx5_core_err(ns->dev, "Failed to destroy action (%d)\n",
49 				      err);
50 		}
51 	}
52 	ft->fs_dr_table.miss_action = action;
53 	if (old_miss_action) {
54 		err = mlx5dr_action_destroy(old_miss_action);
55 		if (err)
56 			mlx5_core_err(ns->dev, "Failed to destroy action (%d)\n",
57 				      err);
58 	}
59 
60 	return err;
61 }
62 
63 static int mlx5_cmd_dr_create_flow_table(struct mlx5_flow_root_namespace *ns,
64 					 struct mlx5_flow_table *ft,
65 					 unsigned int log_size,
66 					 struct mlx5_flow_table *next_ft)
67 {
68 	struct mlx5dr_table *tbl;
69 	u32 flags;
70 	int err;
71 
72 	if (mlx5_dr_is_fw_table(ft->flags))
73 		return mlx5_fs_cmd_get_fw_cmds()->create_flow_table(ns, ft,
74 								    log_size,
75 								    next_ft);
76 	flags = ft->flags;
77 	/* turn off encap/decap if not supported for sw-str by fw */
78 	if (!MLX5_CAP_FLOWTABLE(ns->dev, sw_owner_reformat_supported))
79 		flags = ft->flags & ~(MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT |
80 				      MLX5_FLOW_TABLE_TUNNEL_EN_DECAP);
81 
82 	tbl = mlx5dr_table_create(ns->fs_dr_domain.dr_domain, ft->level, flags);
83 	if (!tbl) {
84 		mlx5_core_err(ns->dev, "Failed creating dr flow_table\n");
85 		return -EINVAL;
86 	}
87 
88 	ft->fs_dr_table.dr_table = tbl;
89 	ft->id = mlx5dr_table_get_id(tbl);
90 
91 	if (next_ft) {
92 		err = set_miss_action(ns, ft, next_ft);
93 		if (err) {
94 			mlx5dr_table_destroy(tbl);
95 			ft->fs_dr_table.dr_table = NULL;
96 			return err;
97 		}
98 	}
99 
100 	return 0;
101 }
102 
103 static int mlx5_cmd_dr_destroy_flow_table(struct mlx5_flow_root_namespace *ns,
104 					  struct mlx5_flow_table *ft)
105 {
106 	struct mlx5dr_action *action = ft->fs_dr_table.miss_action;
107 	int err;
108 
109 	if (mlx5_dr_is_fw_table(ft->flags))
110 		return mlx5_fs_cmd_get_fw_cmds()->destroy_flow_table(ns, ft);
111 
112 	err = mlx5dr_table_destroy(ft->fs_dr_table.dr_table);
113 	if (err) {
114 		mlx5_core_err(ns->dev, "Failed to destroy flow_table (%d)\n",
115 			      err);
116 		return err;
117 	}
118 	if (action) {
119 		err = mlx5dr_action_destroy(action);
120 		if (err) {
121 			mlx5_core_err(ns->dev, "Failed to destroy action(%d)\n",
122 				      err);
123 			return err;
124 		}
125 	}
126 
127 	return err;
128 }
129 
130 static int mlx5_cmd_dr_modify_flow_table(struct mlx5_flow_root_namespace *ns,
131 					 struct mlx5_flow_table *ft,
132 					 struct mlx5_flow_table *next_ft)
133 {
134 	return set_miss_action(ns, ft, next_ft);
135 }
136 
137 static int mlx5_cmd_dr_create_flow_group(struct mlx5_flow_root_namespace *ns,
138 					 struct mlx5_flow_table *ft,
139 					 u32 *in,
140 					 struct mlx5_flow_group *fg)
141 {
142 	struct mlx5dr_matcher *matcher;
143 	u32 priority = MLX5_GET(create_flow_group_in, in,
144 				start_flow_index);
145 	u8 match_criteria_enable = MLX5_GET(create_flow_group_in,
146 					    in,
147 					    match_criteria_enable);
148 	struct mlx5dr_match_parameters mask;
149 
150 	if (mlx5_dr_is_fw_table(ft->flags))
151 		return mlx5_fs_cmd_get_fw_cmds()->create_flow_group(ns, ft, in,
152 								    fg);
153 
154 	mask.match_buf = MLX5_ADDR_OF(create_flow_group_in,
155 				      in, match_criteria);
156 	mask.match_sz = sizeof(fg->mask.match_criteria);
157 
158 	matcher = mlx5dr_matcher_create(ft->fs_dr_table.dr_table,
159 					priority,
160 					match_criteria_enable,
161 					&mask);
162 	if (!matcher) {
163 		mlx5_core_err(ns->dev, "Failed creating matcher\n");
164 		return -EINVAL;
165 	}
166 
167 	fg->fs_dr_matcher.dr_matcher = matcher;
168 	return 0;
169 }
170 
171 static int mlx5_cmd_dr_destroy_flow_group(struct mlx5_flow_root_namespace *ns,
172 					  struct mlx5_flow_table *ft,
173 					  struct mlx5_flow_group *fg)
174 {
175 	if (mlx5_dr_is_fw_table(ft->flags))
176 		return mlx5_fs_cmd_get_fw_cmds()->destroy_flow_group(ns, ft, fg);
177 
178 	return mlx5dr_matcher_destroy(fg->fs_dr_matcher.dr_matcher);
179 }
180 
181 static struct mlx5dr_action *create_vport_action(struct mlx5dr_domain *domain,
182 						 struct mlx5_flow_rule *dst)
183 {
184 	struct mlx5_flow_destination *dest_attr = &dst->dest_attr;
185 
186 	return mlx5dr_action_create_dest_vport(domain, dest_attr->vport.num,
187 					       dest_attr->vport.flags &
188 					       MLX5_FLOW_DEST_VPORT_VHCA_ID,
189 					       dest_attr->vport.vhca_id);
190 }
191 
192 static struct mlx5dr_action *create_ft_action(struct mlx5dr_domain *domain,
193 					      struct mlx5_flow_rule *dst)
194 {
195 	struct mlx5_flow_table *dest_ft = dst->dest_attr.ft;
196 
197 	if (mlx5_dr_is_fw_table(dest_ft->flags))
198 		return mlx5dr_action_create_dest_flow_fw_table(domain, dest_ft);
199 	return mlx5dr_action_create_dest_table(dest_ft->fs_dr_table.dr_table);
200 }
201 
202 static struct mlx5dr_action *create_action_push_vlan(struct mlx5dr_domain *domain,
203 						     struct mlx5_fs_vlan *vlan)
204 {
205 	u16 n_ethtype = vlan->ethtype;
206 	u8  prio = vlan->prio;
207 	u16 vid = vlan->vid;
208 	u32 vlan_hdr;
209 
210 	vlan_hdr = (u32)n_ethtype << 16 | (u32)(prio) << 12 |  (u32)vid;
211 	return mlx5dr_action_create_push_vlan(domain, htonl(vlan_hdr));
212 }
213 
214 static bool contain_vport_reformat_action(struct mlx5_flow_rule *dst)
215 {
216 	return dst->dest_attr.type == MLX5_FLOW_DESTINATION_TYPE_VPORT &&
217 		dst->dest_attr.vport.flags & MLX5_FLOW_DEST_VPORT_REFORMAT_ID;
218 }
219 
220 #define MLX5_FLOW_CONTEXT_ACTION_MAX  20
221 static int mlx5_cmd_dr_create_fte(struct mlx5_flow_root_namespace *ns,
222 				  struct mlx5_flow_table *ft,
223 				  struct mlx5_flow_group *group,
224 				  struct fs_fte *fte)
225 {
226 	struct mlx5dr_domain *domain = ns->fs_dr_domain.dr_domain;
227 	struct mlx5dr_action_dest *term_actions;
228 	struct mlx5dr_match_parameters params;
229 	struct mlx5_core_dev *dev = ns->dev;
230 	struct mlx5dr_action **fs_dr_actions;
231 	struct mlx5dr_action *tmp_action;
232 	struct mlx5dr_action **actions;
233 	bool delay_encap_set = false;
234 	struct mlx5dr_rule *rule;
235 	struct mlx5_flow_rule *dst;
236 	int fs_dr_num_actions = 0;
237 	int num_term_actions = 0;
238 	int num_actions = 0;
239 	size_t match_sz;
240 	int err = 0;
241 	int i;
242 
243 	if (mlx5_dr_is_fw_table(ft->flags))
244 		return mlx5_fs_cmd_get_fw_cmds()->create_fte(ns, ft, group, fte);
245 
246 	actions = kcalloc(MLX5_FLOW_CONTEXT_ACTION_MAX, sizeof(*actions),
247 			  GFP_KERNEL);
248 	if (!actions) {
249 		err = -ENOMEM;
250 		goto out_err;
251 	}
252 
253 	fs_dr_actions = kcalloc(MLX5_FLOW_CONTEXT_ACTION_MAX,
254 				sizeof(*fs_dr_actions), GFP_KERNEL);
255 	if (!fs_dr_actions) {
256 		err = -ENOMEM;
257 		goto free_actions_alloc;
258 	}
259 
260 	term_actions = kcalloc(MLX5_FLOW_CONTEXT_ACTION_MAX,
261 			       sizeof(*term_actions), GFP_KERNEL);
262 	if (!term_actions) {
263 		err = -ENOMEM;
264 		goto free_fs_dr_actions_alloc;
265 	}
266 
267 	match_sz = sizeof(fte->val);
268 
269 	/* Drop reformat action bit if destination vport set with reformat */
270 	if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
271 		list_for_each_entry(dst, &fte->node.children, node.list) {
272 			if (!contain_vport_reformat_action(dst))
273 				continue;
274 
275 			fte->action.action &= ~MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT;
276 			break;
277 		}
278 	}
279 
280 	/* The order of the actions are must to be keep, only the following
281 	 * order is supported by SW steering:
282 	 * TX: modify header -> push vlan -> encap
283 	 * RX: decap -> pop vlan -> modify header
284 	 */
285 	if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_DECAP) {
286 		enum mlx5dr_action_reformat_type decap_type =
287 			DR_ACTION_REFORMAT_TYP_TNL_L2_TO_L2;
288 
289 		tmp_action = mlx5dr_action_create_packet_reformat(domain,
290 								  decap_type, 0,
291 								  NULL);
292 		if (!tmp_action) {
293 			err = -ENOMEM;
294 			goto free_actions;
295 		}
296 		fs_dr_actions[fs_dr_num_actions++] = tmp_action;
297 		actions[num_actions++] = tmp_action;
298 	}
299 
300 	if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT) {
301 		bool is_decap = fte->action.pkt_reformat->reformat_type ==
302 			MLX5_REFORMAT_TYPE_L3_TUNNEL_TO_L2;
303 
304 		if (is_decap)
305 			actions[num_actions++] =
306 				fte->action.pkt_reformat->action.dr_action;
307 		else
308 			delay_encap_set = true;
309 	}
310 
311 	if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP) {
312 		tmp_action =
313 			mlx5dr_action_create_pop_vlan();
314 		if (!tmp_action) {
315 			err = -ENOMEM;
316 			goto free_actions;
317 		}
318 		fs_dr_actions[fs_dr_num_actions++] = tmp_action;
319 		actions[num_actions++] = tmp_action;
320 	}
321 
322 	if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP_2) {
323 		tmp_action =
324 			mlx5dr_action_create_pop_vlan();
325 		if (!tmp_action) {
326 			err = -ENOMEM;
327 			goto free_actions;
328 		}
329 		fs_dr_actions[fs_dr_num_actions++] = tmp_action;
330 		actions[num_actions++] = tmp_action;
331 	}
332 
333 	if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
334 		actions[num_actions++] =
335 			fte->action.modify_hdr->action.dr_action;
336 
337 	if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH) {
338 		tmp_action = create_action_push_vlan(domain, &fte->action.vlan[0]);
339 		if (!tmp_action) {
340 			err = -ENOMEM;
341 			goto free_actions;
342 		}
343 		fs_dr_actions[fs_dr_num_actions++] = tmp_action;
344 		actions[num_actions++] = tmp_action;
345 	}
346 
347 	if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH_2) {
348 		tmp_action = create_action_push_vlan(domain, &fte->action.vlan[1]);
349 		if (!tmp_action) {
350 			err = -ENOMEM;
351 			goto free_actions;
352 		}
353 		fs_dr_actions[fs_dr_num_actions++] = tmp_action;
354 		actions[num_actions++] = tmp_action;
355 	}
356 
357 	if (delay_encap_set)
358 		actions[num_actions++] =
359 			fte->action.pkt_reformat->action.dr_action;
360 
361 	/* The order of the actions below is not important */
362 
363 	if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_DROP) {
364 		tmp_action = mlx5dr_action_create_drop();
365 		if (!tmp_action) {
366 			err = -ENOMEM;
367 			goto free_actions;
368 		}
369 		fs_dr_actions[fs_dr_num_actions++] = tmp_action;
370 		term_actions[num_term_actions++].dest = tmp_action;
371 	}
372 
373 	if (fte->flow_context.flow_tag) {
374 		tmp_action =
375 			mlx5dr_action_create_tag(fte->flow_context.flow_tag);
376 		if (!tmp_action) {
377 			err = -ENOMEM;
378 			goto free_actions;
379 		}
380 		fs_dr_actions[fs_dr_num_actions++] = tmp_action;
381 		actions[num_actions++] = tmp_action;
382 	}
383 
384 	if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
385 		list_for_each_entry(dst, &fte->node.children, node.list) {
386 			enum mlx5_flow_destination_type type = dst->dest_attr.type;
387 			u32 ft_id;
388 
389 			if (num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX ||
390 			    num_term_actions >= MLX5_FLOW_CONTEXT_ACTION_MAX) {
391 				err = -ENOSPC;
392 				goto free_actions;
393 			}
394 
395 			if (type == MLX5_FLOW_DESTINATION_TYPE_COUNTER)
396 				continue;
397 
398 			switch (type) {
399 			case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE:
400 				tmp_action = create_ft_action(domain, dst);
401 				if (!tmp_action) {
402 					err = -ENOMEM;
403 					goto free_actions;
404 				}
405 				fs_dr_actions[fs_dr_num_actions++] = tmp_action;
406 				term_actions[num_term_actions++].dest = tmp_action;
407 				break;
408 			case MLX5_FLOW_DESTINATION_TYPE_VPORT:
409 				tmp_action = create_vport_action(domain, dst);
410 				if (!tmp_action) {
411 					err = -ENOMEM;
412 					goto free_actions;
413 				}
414 				fs_dr_actions[fs_dr_num_actions++] = tmp_action;
415 				term_actions[num_term_actions].dest = tmp_action;
416 
417 				if (dst->dest_attr.vport.flags &
418 				    MLX5_FLOW_DEST_VPORT_REFORMAT_ID)
419 					term_actions[num_term_actions].reformat =
420 						dst->dest_attr.vport.pkt_reformat->action.dr_action;
421 
422 				num_term_actions++;
423 				break;
424 			case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE_NUM:
425 				ft_id = dst->dest_attr.ft_num;
426 				tmp_action = mlx5dr_action_create_dest_table_num(domain,
427 										 ft_id);
428 				if (!tmp_action) {
429 					err = -ENOMEM;
430 					goto free_actions;
431 				}
432 				fs_dr_actions[fs_dr_num_actions++] = tmp_action;
433 				term_actions[num_term_actions++].dest = tmp_action;
434 				break;
435 			default:
436 				err = -EOPNOTSUPP;
437 				goto free_actions;
438 			}
439 		}
440 	}
441 
442 	if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
443 		list_for_each_entry(dst, &fte->node.children, node.list) {
444 			u32 id;
445 
446 			if (dst->dest_attr.type !=
447 			    MLX5_FLOW_DESTINATION_TYPE_COUNTER)
448 				continue;
449 
450 			if (num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) {
451 				err = -ENOSPC;
452 				goto free_actions;
453 			}
454 
455 			id = dst->dest_attr.counter_id;
456 			tmp_action =
457 				mlx5dr_action_create_flow_counter(id);
458 			if (!tmp_action) {
459 				err = -ENOMEM;
460 				goto free_actions;
461 			}
462 
463 			fs_dr_actions[fs_dr_num_actions++] = tmp_action;
464 			actions[num_actions++] = tmp_action;
465 		}
466 	}
467 
468 	params.match_sz = match_sz;
469 	params.match_buf = (u64 *)fte->val;
470 	if (num_term_actions == 1) {
471 		if (term_actions->reformat)
472 			actions[num_actions++] = term_actions->reformat;
473 
474 		actions[num_actions++] = term_actions->dest;
475 	} else if (num_term_actions > 1) {
476 		tmp_action = mlx5dr_action_create_mult_dest_tbl(domain,
477 								term_actions,
478 								num_term_actions);
479 		if (!tmp_action) {
480 			err = -EOPNOTSUPP;
481 			goto free_actions;
482 		}
483 		fs_dr_actions[fs_dr_num_actions++] = tmp_action;
484 		actions[num_actions++] = tmp_action;
485 	}
486 
487 	rule = mlx5dr_rule_create(group->fs_dr_matcher.dr_matcher,
488 				  &params,
489 				  num_actions,
490 				  actions,
491 				  fte->flow_context.flow_source);
492 	if (!rule) {
493 		err = -EINVAL;
494 		goto free_actions;
495 	}
496 
497 	kfree(term_actions);
498 	kfree(actions);
499 
500 	fte->fs_dr_rule.dr_rule = rule;
501 	fte->fs_dr_rule.num_actions = fs_dr_num_actions;
502 	fte->fs_dr_rule.dr_actions = fs_dr_actions;
503 
504 	return 0;
505 
506 free_actions:
507 	/* Free in reverse order to handle action dependencies */
508 	for (i = fs_dr_num_actions - 1; i >= 0; i--)
509 		if (!IS_ERR_OR_NULL(fs_dr_actions[i]))
510 			mlx5dr_action_destroy(fs_dr_actions[i]);
511 
512 	kfree(term_actions);
513 free_fs_dr_actions_alloc:
514 	kfree(fs_dr_actions);
515 free_actions_alloc:
516 	kfree(actions);
517 out_err:
518 	mlx5_core_err(dev, "Failed to create dr rule err(%d)\n", err);
519 	return err;
520 }
521 
522 static int mlx5_cmd_dr_packet_reformat_alloc(struct mlx5_flow_root_namespace *ns,
523 					     int reformat_type,
524 					     size_t size,
525 					     void *reformat_data,
526 					     enum mlx5_flow_namespace_type namespace,
527 					     struct mlx5_pkt_reformat *pkt_reformat)
528 {
529 	struct mlx5dr_domain *dr_domain = ns->fs_dr_domain.dr_domain;
530 	struct mlx5dr_action *action;
531 	int dr_reformat;
532 
533 	switch (reformat_type) {
534 	case MLX5_REFORMAT_TYPE_L2_TO_VXLAN:
535 	case MLX5_REFORMAT_TYPE_L2_TO_NVGRE:
536 	case MLX5_REFORMAT_TYPE_L2_TO_L2_TUNNEL:
537 		dr_reformat = DR_ACTION_REFORMAT_TYP_L2_TO_TNL_L2;
538 		break;
539 	case MLX5_REFORMAT_TYPE_L3_TUNNEL_TO_L2:
540 		dr_reformat = DR_ACTION_REFORMAT_TYP_TNL_L3_TO_L2;
541 		break;
542 	case MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL:
543 		dr_reformat = DR_ACTION_REFORMAT_TYP_L2_TO_TNL_L3;
544 		break;
545 	default:
546 		mlx5_core_err(ns->dev, "Packet-reformat not supported(%d)\n",
547 			      reformat_type);
548 		return -EOPNOTSUPP;
549 	}
550 
551 	action = mlx5dr_action_create_packet_reformat(dr_domain,
552 						      dr_reformat,
553 						      size,
554 						      reformat_data);
555 	if (!action) {
556 		mlx5_core_err(ns->dev, "Failed allocating packet-reformat action\n");
557 		return -EINVAL;
558 	}
559 
560 	pkt_reformat->action.dr_action = action;
561 
562 	return 0;
563 }
564 
565 static void mlx5_cmd_dr_packet_reformat_dealloc(struct mlx5_flow_root_namespace *ns,
566 						struct mlx5_pkt_reformat *pkt_reformat)
567 {
568 	mlx5dr_action_destroy(pkt_reformat->action.dr_action);
569 }
570 
571 static int mlx5_cmd_dr_modify_header_alloc(struct mlx5_flow_root_namespace *ns,
572 					   u8 namespace, u8 num_actions,
573 					   void *modify_actions,
574 					   struct mlx5_modify_hdr *modify_hdr)
575 {
576 	struct mlx5dr_domain *dr_domain = ns->fs_dr_domain.dr_domain;
577 	struct mlx5dr_action *action;
578 	size_t actions_sz;
579 
580 	actions_sz = MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto) *
581 		num_actions;
582 	action = mlx5dr_action_create_modify_header(dr_domain, 0,
583 						    actions_sz,
584 						    modify_actions);
585 	if (!action) {
586 		mlx5_core_err(ns->dev, "Failed allocating modify-header action\n");
587 		return -EINVAL;
588 	}
589 
590 	modify_hdr->action.dr_action = action;
591 
592 	return 0;
593 }
594 
595 static void mlx5_cmd_dr_modify_header_dealloc(struct mlx5_flow_root_namespace *ns,
596 					      struct mlx5_modify_hdr *modify_hdr)
597 {
598 	mlx5dr_action_destroy(modify_hdr->action.dr_action);
599 }
600 
601 static int mlx5_cmd_dr_update_fte(struct mlx5_flow_root_namespace *ns,
602 				  struct mlx5_flow_table *ft,
603 				  struct mlx5_flow_group *group,
604 				  int modify_mask,
605 				  struct fs_fte *fte)
606 {
607 	return -EOPNOTSUPP;
608 }
609 
610 static int mlx5_cmd_dr_delete_fte(struct mlx5_flow_root_namespace *ns,
611 				  struct mlx5_flow_table *ft,
612 				  struct fs_fte *fte)
613 {
614 	struct mlx5_fs_dr_rule *rule = &fte->fs_dr_rule;
615 	int err;
616 	int i;
617 
618 	if (mlx5_dr_is_fw_table(ft->flags))
619 		return mlx5_fs_cmd_get_fw_cmds()->delete_fte(ns, ft, fte);
620 
621 	err = mlx5dr_rule_destroy(rule->dr_rule);
622 	if (err)
623 		return err;
624 
625 	/* Free in reverse order to handle action dependencies */
626 	for (i = rule->num_actions - 1; i >= 0; i--)
627 		if (!IS_ERR_OR_NULL(rule->dr_actions[i]))
628 			mlx5dr_action_destroy(rule->dr_actions[i]);
629 
630 	kfree(rule->dr_actions);
631 	return 0;
632 }
633 
634 static int mlx5_cmd_dr_set_peer(struct mlx5_flow_root_namespace *ns,
635 				struct mlx5_flow_root_namespace *peer_ns)
636 {
637 	struct mlx5dr_domain *peer_domain = NULL;
638 
639 	if (peer_ns)
640 		peer_domain = peer_ns->fs_dr_domain.dr_domain;
641 	mlx5dr_domain_set_peer(ns->fs_dr_domain.dr_domain,
642 			       peer_domain);
643 	return 0;
644 }
645 
646 static int mlx5_cmd_dr_create_ns(struct mlx5_flow_root_namespace *ns)
647 {
648 	ns->fs_dr_domain.dr_domain =
649 		mlx5dr_domain_create(ns->dev,
650 				     MLX5DR_DOMAIN_TYPE_FDB);
651 	if (!ns->fs_dr_domain.dr_domain) {
652 		mlx5_core_err(ns->dev, "Failed to create dr flow namespace\n");
653 		return -EOPNOTSUPP;
654 	}
655 	return 0;
656 }
657 
658 static int mlx5_cmd_dr_destroy_ns(struct mlx5_flow_root_namespace *ns)
659 {
660 	return mlx5dr_domain_destroy(ns->fs_dr_domain.dr_domain);
661 }
662 
663 bool mlx5_fs_dr_is_supported(struct mlx5_core_dev *dev)
664 {
665 	return mlx5dr_is_supported(dev);
666 }
667 
668 static const struct mlx5_flow_cmds mlx5_flow_cmds_dr = {
669 	.create_flow_table = mlx5_cmd_dr_create_flow_table,
670 	.destroy_flow_table = mlx5_cmd_dr_destroy_flow_table,
671 	.modify_flow_table = mlx5_cmd_dr_modify_flow_table,
672 	.create_flow_group = mlx5_cmd_dr_create_flow_group,
673 	.destroy_flow_group = mlx5_cmd_dr_destroy_flow_group,
674 	.create_fte = mlx5_cmd_dr_create_fte,
675 	.update_fte = mlx5_cmd_dr_update_fte,
676 	.delete_fte = mlx5_cmd_dr_delete_fte,
677 	.update_root_ft = mlx5_cmd_dr_update_root_ft,
678 	.packet_reformat_alloc = mlx5_cmd_dr_packet_reformat_alloc,
679 	.packet_reformat_dealloc = mlx5_cmd_dr_packet_reformat_dealloc,
680 	.modify_header_alloc = mlx5_cmd_dr_modify_header_alloc,
681 	.modify_header_dealloc = mlx5_cmd_dr_modify_header_dealloc,
682 	.set_peer = mlx5_cmd_dr_set_peer,
683 	.create_ns = mlx5_cmd_dr_create_ns,
684 	.destroy_ns = mlx5_cmd_dr_destroy_ns,
685 };
686 
687 const struct mlx5_flow_cmds *mlx5_fs_cmd_get_dr_cmds(void)
688 {
689 		return &mlx5_flow_cmds_dr;
690 }
691