File Index
Files and directories
Sign in
FILE

fs_dr.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/fs_dr.c C
  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
 12static 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
 21static 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
 56static 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
 99static 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
126static 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
136static 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
170static 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
180static 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
191static 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
200static 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
217static 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
228static 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
240static 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
252static 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 = kcalloc(MLX5_FLOW_CONTEXT_ACTION_MAX, sizeof(*actions),
279			  GFP_KERNEL);
280	if (!actions) {
281		err = -ENOMEM;
282		goto out_err;
283	}
284
285	fs_dr_actions = kcalloc(MLX5_FLOW_CONTEXT_ACTION_MAX,
286				sizeof(*fs_dr_actions), GFP_KERNEL);
287	if (!fs_dr_actions) {
288		err = -ENOMEM;
289		goto free_actions_alloc;
290	}
291
292	term_actions = kcalloc(MLX5_FLOW_CONTEXT_ACTION_MAX,
293			       sizeof(*term_actions), GFP_KERNEL);
294	if (!term_actions) {
295		err = -ENOMEM;
296		goto free_fs_dr_actions_alloc;
297	}
298
299	match_sz = sizeof(fte->val);
300
301	/* Drop reformat action bit if destination vport set with reformat */
302	if (fte->act_dests.action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
303		list_for_each_entry(dst, &fte->node.children, node.list) {
304			if (!contain_vport_reformat_action(dst))
305				continue;
306
307			fte->act_dests.action.action &= ~MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT;
308			break;
309		}
310	}
311
312	/* The order of the actions are must to be keep, only the following
313	 * order is supported by SW steering:
314	 * TX: modify header -> push vlan -> encap
315	 * RX: decap -> pop vlan -> modify header
316	 */
317	if (fte->act_dests.action.action & MLX5_FLOW_CONTEXT_ACTION_DECAP) {
318		enum mlx5dr_action_reformat_type decap_type =
319			DR_ACTION_REFORMAT_TYP_TNL_L2_TO_L2;
320
321		tmp_action = mlx5dr_action_create_packet_reformat(domain,
322								  decap_type,
323								  0, 0, 0,
324								  NULL);
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->act_dests.action.action & MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT) {
334		bool is_decap;
335
336		pkt_reformat = fte->act_dests.action.pkt_reformat;
337		if (pkt_reformat->owner == MLX5_FLOW_RESOURCE_OWNER_FW) {
338			err = -EINVAL;
339			mlx5dr_err(domain, "FW-owned reformat can't be used in SW rule\n");
340			goto free_actions;
341		}
342
343		is_decap = pkt_reformat->reformat_type ==
344			   MLX5_REFORMAT_TYPE_L3_TUNNEL_TO_L2;
345
346		if (is_decap)
347			actions[num_actions++] =
348				pkt_reformat->fs_dr_action.dr_action;
349		else
350			delay_encap_set = true;
351	}
352
353	if (fte->act_dests.action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP) {
354		tmp_action =
355			mlx5dr_action_create_pop_vlan();
356		if (!tmp_action) {
357			err = -ENOMEM;
358			goto free_actions;
359		}
360		fs_dr_actions[fs_dr_num_actions++] = tmp_action;
361		actions[num_actions++] = tmp_action;
362	}
363
364	if (fte->act_dests.action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP_2) {
365		tmp_action =
366			mlx5dr_action_create_pop_vlan();
367		if (!tmp_action) {
368			err = -ENOMEM;
369			goto free_actions;
370		}
371		fs_dr_actions[fs_dr_num_actions++] = tmp_action;
372		actions[num_actions++] = tmp_action;
373	}
374
375	if (fte->act_dests.action.action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
376		struct mlx5_modify_hdr *modify_hdr = fte->act_dests.action.modify_hdr;
377
378		actions[num_actions++] = modify_hdr->fs_dr_action.dr_action;
379	}
380
381	if (fte->act_dests.action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH) {
382		tmp_action = create_action_push_vlan(domain, &fte->act_dests.action.vlan[0]);
383		if (!tmp_action) {
384			err = -ENOMEM;
385			goto free_actions;
386		}
387		fs_dr_actions[fs_dr_num_actions++] = tmp_action;
388		actions[num_actions++] = tmp_action;
389	}
390
391	if (fte->act_dests.action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH_2) {
392		tmp_action = create_action_push_vlan(domain, &fte->act_dests.action.vlan[1]);
393		if (!tmp_action) {
394			err = -ENOMEM;
395			goto free_actions;
396		}
397		fs_dr_actions[fs_dr_num_actions++] = tmp_action;
398		actions[num_actions++] = tmp_action;
399	}
400
401	if (delay_encap_set)
402		actions[num_actions++] = pkt_reformat->fs_dr_action.dr_action;
403
404	/* The order of the actions below is not important */
405
406	if (fte->act_dests.action.action & MLX5_FLOW_CONTEXT_ACTION_DROP) {
407		tmp_action = mlx5dr_action_create_drop();
408		if (!tmp_action) {
409			err = -ENOMEM;
410			goto free_actions;
411		}
412		fs_dr_actions[fs_dr_num_actions++] = tmp_action;
413		term_actions[num_term_actions++].dest = tmp_action;
414	}
415
416	if (fte->act_dests.flow_context.flow_tag) {
417		tmp_action =
418			mlx5dr_action_create_tag(fte->act_dests.flow_context.flow_tag);
419		if (!tmp_action) {
420			err = -ENOMEM;
421			goto free_actions;
422		}
423		fs_dr_actions[fs_dr_num_actions++] = tmp_action;
424		actions[num_actions++] = tmp_action;
425	}
426
427	if (fte->act_dests.action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
428		list_for_each_entry(dst, &fte->node.children, node.list) {
429			enum mlx5_flow_destination_type type = dst->dest_attr.type;
430			u32 id;
431
432			if (fs_dr_num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX ||
433			    num_term_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) {
434				err = -EOPNOTSUPP;
435				goto free_actions;
436			}
437
438			if (type == MLX5_FLOW_DESTINATION_TYPE_COUNTER)
439				continue;
440
441			switch (type) {
442			case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE:
443				tmp_action = create_ft_action(domain, dst);
444				if (!tmp_action) {
445					err = -ENOMEM;
446					goto free_actions;
447				}
448				fs_dr_actions[fs_dr_num_actions++] = tmp_action;
449				term_actions[num_term_actions++].dest = tmp_action;
450				break;
451			case MLX5_FLOW_DESTINATION_TYPE_UPLINK:
452			case MLX5_FLOW_DESTINATION_TYPE_VPORT:
453				tmp_action = type == MLX5_FLOW_DESTINATION_TYPE_VPORT ?
454					     create_vport_action(domain, dst) :
455					     create_uplink_action(domain, dst);
456				if (!tmp_action) {
457					err = -ENOMEM;
458					goto free_actions;
459				}
460				fs_dr_actions[fs_dr_num_actions++] = tmp_action;
461				term_actions[num_term_actions].dest = tmp_action;
462
463				if (dst->dest_attr.vport.flags &
464				    MLX5_FLOW_DEST_VPORT_REFORMAT_ID) {
465					pkt_reformat = dst->dest_attr.vport.pkt_reformat;
466					term_actions[num_term_actions].reformat =
467						pkt_reformat->fs_dr_action.dr_action;
468				}
469
470				num_term_actions++;
471				break;
472			case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE_NUM:
473				id = dst->dest_attr.ft_num;
474				tmp_action = mlx5dr_action_create_dest_table_num(domain,
475										 id);
476				if (!tmp_action) {
477					err = -ENOMEM;
478					goto free_actions;
479				}
480				fs_dr_actions[fs_dr_num_actions++] = tmp_action;
481				term_actions[num_term_actions++].dest = tmp_action;
482				break;
483			case MLX5_FLOW_DESTINATION_TYPE_FLOW_SAMPLER:
484				id = dst->dest_attr.sampler_id;
485				tmp_action = mlx5dr_action_create_flow_sampler(domain,
486									       id);
487				if (!tmp_action) {
488					err = -ENOMEM;
489					goto free_actions;
490				}
491				fs_dr_actions[fs_dr_num_actions++] = tmp_action;
492				term_actions[num_term_actions++].dest = tmp_action;
493				break;
494			case MLX5_FLOW_DESTINATION_TYPE_RANGE:
495				tmp_action = create_range_action(domain, dst);
496				if (!tmp_action) {
497					err = -ENOMEM;
498					goto free_actions;
499				}
500				fs_dr_actions[fs_dr_num_actions++] = tmp_action;
501				term_actions[num_term_actions++].dest = tmp_action;
502				break;
503			default:
504				err = -EOPNOTSUPP;
505				goto free_actions;
506			}
507		}
508	}
509
510	if (fte->act_dests.action.action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
511		list_for_each_entry(dst, &fte->node.children, node.list) {
512			u32 id;
513
514			if (dst->dest_attr.type !=
515			    MLX5_FLOW_DESTINATION_TYPE_COUNTER)
516				continue;
517
518			if (num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX ||
519			    fs_dr_num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) {
520				err = -EOPNOTSUPP;
521				goto free_actions;
522			}
523
524			id = mlx5_fc_id(dst->dest_attr.counter);
525			tmp_action =
526				mlx5dr_action_create_flow_counter(id);
527			if (!tmp_action) {
528				err = -ENOMEM;
529				goto free_actions;
530			}
531
532			fs_dr_actions[fs_dr_num_actions++] = tmp_action;
533			actions[num_actions++] = tmp_action;
534		}
535	}
536
537	if (fte->act_dests.action.action & MLX5_FLOW_CONTEXT_ACTION_EXECUTE_ASO) {
538		struct mlx5_flow_act *action = &fte->act_dests.action;
539
540		if (fte->act_dests.action.exe_aso.type != MLX5_EXE_ASO_FLOW_METER) {
541			err = -EOPNOTSUPP;
542			goto free_actions;
543		}
544
545		tmp_action =
546			mlx5dr_action_create_aso(domain,
547						 action->exe_aso.object_id,
548						 action->exe_aso.return_reg_id,
549						 action->exe_aso.type,
550						 action->exe_aso.flow_meter.init_color,
551						 action->exe_aso.flow_meter.meter_idx);
552		if (!tmp_action) {
553			err = -ENOMEM;
554			goto free_actions;
555		}
556		fs_dr_actions[fs_dr_num_actions++] = tmp_action;
557		actions[num_actions++] = tmp_action;
558	}
559
560	params.match_sz = match_sz;
561	params.match_buf = (u64 *)fte->val;
562	if (num_term_actions == 1) {
563		if (term_actions->reformat) {
564			if (num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) {
565				err = -EOPNOTSUPP;
566				goto free_actions;
567			}
568			actions[num_actions++] = term_actions->reformat;
569		}
570
571		if (num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) {
572			err = -EOPNOTSUPP;
573			goto free_actions;
574		}
575		actions[num_actions++] = term_actions->dest;
576	} else if (num_term_actions > 1) {
577		bool ignore_flow_level =
578			!!(fte->act_dests.action.flags & FLOW_ACT_IGNORE_FLOW_LEVEL);
579		u32 flow_source = fte->act_dests.flow_context.flow_source;
580
581		if (num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX ||
582		    fs_dr_num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) {
583			err = -EOPNOTSUPP;
584			goto free_actions;
585		}
586		tmp_action = mlx5dr_action_create_mult_dest_tbl(domain,
587								term_actions,
588								num_term_actions,
589								ignore_flow_level,
590								flow_source);
591		if (!tmp_action) {
592			err = -EOPNOTSUPP;
593			goto free_actions;
594		}
595		fs_dr_actions[fs_dr_num_actions++] = tmp_action;
596		actions[num_actions++] = tmp_action;
597	}
598
599	rule = mlx5dr_rule_create(group->fs_dr_matcher.dr_matcher,
600				  &params,
601				  num_actions,
602				  actions,
603				  fte->act_dests.flow_context.flow_source);
604	if (!rule) {
605		err = -EINVAL;
606		goto free_actions;
607	}
608
609	kfree(term_actions);
610	kfree(actions);
611
612	fte->fs_dr_rule.dr_rule = rule;
613	fte->fs_dr_rule.num_actions = fs_dr_num_actions;
614	fte->fs_dr_rule.dr_actions = fs_dr_actions;
615
616	return 0;
617
618free_actions:
619	/* Free in reverse order to handle action dependencies */
620	for (i = fs_dr_num_actions - 1; i >= 0; i--)
621		if (!IS_ERR_OR_NULL(fs_dr_actions[i]))
622			mlx5dr_action_destroy(fs_dr_actions[i]);
623
624	kfree(term_actions);
625free_fs_dr_actions_alloc:
626	kfree(fs_dr_actions);
627free_actions_alloc:
628	kfree(actions);
629out_err:
630	mlx5_core_err(dev, "Failed to create dr rule err(%d)\n", err);
631	return err;
632}
633
634static int mlx5_cmd_dr_packet_reformat_alloc(struct mlx5_flow_root_namespace *ns,
635					     struct mlx5_pkt_reformat_params *params,
636					     enum mlx5_flow_namespace_type namespace,
637					     struct mlx5_pkt_reformat *pkt_reformat)
638{
639	struct mlx5dr_domain *dr_domain = ns->fs_dr_domain.dr_domain;
640	struct mlx5dr_action *action;
641	int dr_reformat;
642
643	switch (params->type) {
644	case MLX5_REFORMAT_TYPE_L2_TO_VXLAN:
645	case MLX5_REFORMAT_TYPE_L2_TO_NVGRE:
646	case MLX5_REFORMAT_TYPE_L2_TO_L2_TUNNEL:
647		dr_reformat = DR_ACTION_REFORMAT_TYP_L2_TO_TNL_L2;
648		break;
649	case MLX5_REFORMAT_TYPE_L3_TUNNEL_TO_L2:
650		dr_reformat = DR_ACTION_REFORMAT_TYP_TNL_L3_TO_L2;
651		break;
652	case MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL:
653		dr_reformat = DR_ACTION_REFORMAT_TYP_L2_TO_TNL_L3;
654		break;
655	case MLX5_REFORMAT_TYPE_INSERT_HDR:
656		dr_reformat = DR_ACTION_REFORMAT_TYP_INSERT_HDR;
657		break;
658	case MLX5_REFORMAT_TYPE_REMOVE_HDR:
659		dr_reformat = DR_ACTION_REFORMAT_TYP_REMOVE_HDR;
660		break;
661	default:
662		mlx5_core_err(ns->dev, "Packet-reformat not supported(%d)\n",
663			      params->type);
664		return -EOPNOTSUPP;
665	}
666
667	action = mlx5dr_action_create_packet_reformat(dr_domain,
668						      dr_reformat,
669						      params->param_0,
670						      params->param_1,
671						      params->size,
672						      params->data);
673	if (!action) {
674		mlx5_core_err(ns->dev, "Failed allocating packet-reformat action\n");
675		return -EINVAL;
676	}
677
678	pkt_reformat->owner = MLX5_FLOW_RESOURCE_OWNER_SW;
679	pkt_reformat->fs_dr_action.dr_action = action;
680
681	return 0;
682}
683
684static void mlx5_cmd_dr_packet_reformat_dealloc(struct mlx5_flow_root_namespace *ns,
685						struct mlx5_pkt_reformat *pkt_reformat)
686{
687	mlx5dr_action_destroy(pkt_reformat->fs_dr_action.dr_action);
688}
689
690static int mlx5_cmd_dr_modify_header_alloc(struct mlx5_flow_root_namespace *ns,
691					   u8 namespace, u8 num_actions,
692					   void *modify_actions,
693					   struct mlx5_modify_hdr *modify_hdr)
694{
695	struct mlx5dr_domain *dr_domain = ns->fs_dr_domain.dr_domain;
696	struct mlx5dr_action *action;
697	size_t actions_sz;
698
699	actions_sz = MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto) *
700		num_actions;
701	action = mlx5dr_action_create_modify_header(dr_domain, 0,
702						    actions_sz,
703						    modify_actions);
704	if (!action) {
705		mlx5_core_err(ns->dev, "Failed allocating modify-header action\n");
706		return -EINVAL;
707	}
708
709	modify_hdr->owner = MLX5_FLOW_RESOURCE_OWNER_SW;
710	modify_hdr->fs_dr_action.dr_action = action;
711
712	return 0;
713}
714
715static void mlx5_cmd_dr_modify_header_dealloc(struct mlx5_flow_root_namespace *ns,
716					      struct mlx5_modify_hdr *modify_hdr)
717{
718	mlx5dr_action_destroy(modify_hdr->fs_dr_action.dr_action);
719}
720
721static int
722mlx5_cmd_dr_destroy_match_definer(struct mlx5_flow_root_namespace *ns,
723				  int definer_id)
724{
725	return -EOPNOTSUPP;
726}
727
728static int mlx5_cmd_dr_create_match_definer(struct mlx5_flow_root_namespace *ns,
729					    u16 format_id, u32 *match_mask)
730{
731	return -EOPNOTSUPP;
732}
733
734static int mlx5_cmd_dr_delete_fte(struct mlx5_flow_root_namespace *ns,
735				  struct mlx5_flow_table *ft,
736				  struct fs_fte *fte)
737{
738	struct mlx5_fs_dr_rule *rule = &fte->fs_dr_rule;
739	int err;
740	int i;
741
742	if (mlx5_fs_cmd_is_fw_term_table(ft))
743		return mlx5_fs_cmd_get_fw_cmds()->delete_fte(ns, ft, fte);
744
745	err = mlx5dr_rule_destroy(rule->dr_rule);
746	if (err)
747		return err;
748
749	/* Free in reverse order to handle action dependencies */
750	for (i = rule->num_actions - 1; i >= 0; i--)
751		if (!IS_ERR_OR_NULL(rule->dr_actions[i]))
752			mlx5dr_action_destroy(rule->dr_actions[i]);
753
754	kfree(rule->dr_actions);
755	return 0;
756}
757
758static int mlx5_cmd_dr_update_fte(struct mlx5_flow_root_namespace *ns,
759				  struct mlx5_flow_table *ft,
760				  struct mlx5_flow_group *group,
761				  int modify_mask,
762				  struct fs_fte *fte)
763{
764	struct fs_fte fte_tmp = {};
765	int ret;
766
767	if (mlx5_fs_cmd_is_fw_term_table(ft))
768		return mlx5_fs_cmd_get_fw_cmds()->update_fte(ns, ft, group, modify_mask, fte);
769
770	/* Backup current dr rule details */
771	fte_tmp.fs_dr_rule = fte->fs_dr_rule;
772	memset(&fte->fs_dr_rule, 0, sizeof(struct mlx5_fs_dr_rule));
773
774	/* First add the new updated rule, then delete the old rule */
775	ret = mlx5_cmd_dr_create_fte(ns, ft, group, fte);
776	if (ret)
777		goto restore_fte;
778
779	ret = mlx5_cmd_dr_delete_fte(ns, ft, &fte_tmp);
780	WARN_ONCE(ret, "dr update fte duplicate rule deletion failed\n");
781	return ret;
782
783restore_fte:
784	fte->fs_dr_rule = fte_tmp.fs_dr_rule;
785	return ret;
786}
787
788static int mlx5_cmd_dr_set_peer(struct mlx5_flow_root_namespace *ns,
789				struct mlx5_flow_root_namespace *peer_ns,
790				u16 peer_vhca_id)
791{
792	struct mlx5dr_domain *peer_domain = NULL;
793
794	if (peer_ns)
795		peer_domain = peer_ns->fs_dr_domain.dr_domain;
796	mlx5dr_domain_set_peer(ns->fs_dr_domain.dr_domain,
797			       peer_domain, peer_vhca_id);
798	return 0;
799}
800
801static int mlx5_cmd_dr_create_ns(struct mlx5_flow_root_namespace *ns)
802{
803	ns->fs_dr_domain.dr_domain =
804		mlx5dr_domain_create(ns->dev,
805				     MLX5DR_DOMAIN_TYPE_FDB);
806	if (!ns->fs_dr_domain.dr_domain) {
807		mlx5_core_err(ns->dev, "Failed to create dr flow namespace\n");
808		return -EOPNOTSUPP;
809	}
810	return 0;
811}
812
813static int mlx5_cmd_dr_destroy_ns(struct mlx5_flow_root_namespace *ns)
814{
815	return mlx5dr_domain_destroy(ns->fs_dr_domain.dr_domain);
816}
817
818static u32 mlx5_cmd_dr_get_capabilities(struct mlx5_flow_root_namespace *ns,
819					enum fs_flow_table_type ft_type)
820{
821	u32 steering_caps = MLX5_FLOW_STEERING_CAP_DUPLICATE_MATCH;
822
823	if (ft_type != FS_FT_FDB ||
824	    MLX5_CAP_GEN(ns->dev, steering_format_version) == MLX5_STEERING_FORMAT_CONNECTX_5)
825		return steering_caps;
826
827	steering_caps |= MLX5_FLOW_STEERING_CAP_VLAN_PUSH_ON_RX;
828	steering_caps |= MLX5_FLOW_STEERING_CAP_VLAN_POP_ON_TX;
829
830	if (mlx5dr_supp_match_ranges(ns->dev))
831		steering_caps |= MLX5_FLOW_STEERING_CAP_MATCH_RANGES;
832
833	return steering_caps;
834}
835
836int
837mlx5_fs_dr_action_get_pkt_reformat_id(struct mlx5_pkt_reformat *pkt_reformat,
838				      u32 *reformat_id)
839{
840	struct mlx5dr_action *dr_action;
841
842	switch (pkt_reformat->reformat_type) {
843	case MLX5_REFORMAT_TYPE_L2_TO_VXLAN:
844	case MLX5_REFORMAT_TYPE_L2_TO_NVGRE:
845	case MLX5_REFORMAT_TYPE_L2_TO_L2_TUNNEL:
846	case MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL:
847	case MLX5_REFORMAT_TYPE_INSERT_HDR:
848		dr_action = pkt_reformat->fs_dr_action.dr_action;
849		*reformat_id = mlx5dr_action_get_pkt_reformat_id(dr_action);
850		return 0;
851	}
852	return -EOPNOTSUPP;
853}
854
855bool mlx5_fs_dr_is_supported(struct mlx5_core_dev *dev)
856{
857	return mlx5dr_is_supported(dev);
858}
859
860static const struct mlx5_flow_cmds mlx5_flow_cmds_dr = {
861	.create_flow_table = mlx5_cmd_dr_create_flow_table,
862	.destroy_flow_table = mlx5_cmd_dr_destroy_flow_table,
863	.modify_flow_table = mlx5_cmd_dr_modify_flow_table,
864	.create_flow_group = mlx5_cmd_dr_create_flow_group,
865	.destroy_flow_group = mlx5_cmd_dr_destroy_flow_group,
866	.create_fte = mlx5_cmd_dr_create_fte,
867	.update_fte = mlx5_cmd_dr_update_fte,
868	.delete_fte = mlx5_cmd_dr_delete_fte,
869	.update_root_ft = mlx5_cmd_dr_update_root_ft,
870	.packet_reformat_alloc = mlx5_cmd_dr_packet_reformat_alloc,
871	.packet_reformat_dealloc = mlx5_cmd_dr_packet_reformat_dealloc,
872	.modify_header_alloc = mlx5_cmd_dr_modify_header_alloc,
873	.modify_header_dealloc = mlx5_cmd_dr_modify_header_dealloc,
874	.create_match_definer = mlx5_cmd_dr_create_match_definer,
875	.destroy_match_definer = mlx5_cmd_dr_destroy_match_definer,
876	.set_peer = mlx5_cmd_dr_set_peer,
877	.create_ns = mlx5_cmd_dr_create_ns,
878	.destroy_ns = mlx5_cmd_dr_destroy_ns,
879	.get_capabilities = mlx5_cmd_dr_get_capabilities,
880};
881
882const struct mlx5_flow_cmds *mlx5_fs_cmd_get_dr_cmds(void)
883{
884		return &mlx5_flow_cmds_dr;
885}
~/ Read-only file preview.