File: mlx5_nta_split.c

package info (click to toggle)
dpdk 25.11-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 127,892 kB
  • sloc: ansic: 2,358,479; python: 16,426; sh: 4,474; makefile: 1,713; awk: 70
file content (570 lines) | stat: -rw-r--r-- 17,570 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright (c) 2024 NVIDIA Corporation & Affiliates
 */

#include <rte_common.h>
#include <rte_flow.h>

#include "mlx5_malloc.h"
#include "mlx5.h"
#include "mlx5_defs.h"
#include "mlx5_flow.h"
#include "mlx5_rx.h"

#ifdef HAVE_MLX5_HWS_SUPPORT

#define BITS_PER_BYTE	8

/*
 * Generate new actions lists for prefix and suffix flows.
 *
 * @param[in] dev
 *   Pointer to rte_eth_dev structure.
 * @param[in] prefix_act
 *   Pointer to actions for prefix flow rule.
 * @param[in] suffix_act
 *   Pointer to actions for suffix flow rule.
 * @param[in] actions
 *   Pointer to the original actions list.
 * @param[in] qrss
 *   Pointer to the action of QUEUE / RSS.
 * @param[in] actions_n
 *   Number of the actions in the original list.
 * @param[out] error
 *   Pointer to error structure.
 *
 * @return
 *   Positive prefix flow ID on success, zero on failure.
 */
static uint32_t
mlx5_flow_nta_split_qrss_actions_prep(struct rte_eth_dev *dev,
				      struct rte_flow_action *prefix_act,
				      struct rte_flow_action *suffix_act,
				      const struct rte_flow_action *actions,
				      const struct rte_flow_action *qrss,
				      int actions_n,
				      struct rte_flow_error *error)
{
	struct mlx5_priv *priv = dev->data->dev_private;
	struct rte_flow_action_modify_field *set_tag;
	struct rte_flow_action_jump *jump;
	const int qrss_idx = qrss - actions;
	uint32_t flow_id = 0;

	/* Allocate the new subflow ID and used to be matched later. */
	mlx5_ipool_malloc(priv->sh->ipool[MLX5_IPOOL_RSS_EXPANTION_FLOW_ID], &flow_id);
	if (!flow_id) {
		rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_ACTION, NULL,
				   "can't allocate id for split Q/RSS subflow");
		return 0;
	}
	/*
	 * Given actions will be split
	 * - Replace QUEUE/RSS action with SET_TAG to set flow ID.
	 * - Add jump to mreg CP_TBL.
	 * As a result, there will be one more action.
	 */
	memcpy(prefix_act, actions, sizeof(struct rte_flow_action) * actions_n);
	/* Count MLX5_RTE_FLOW_ACTION_TYPE_TAG. */
	actions_n++;
	set_tag = (void *)(prefix_act + actions_n);
	/* Internal SET_TAG action to set flow ID. */
	set_tag->operation = RTE_FLOW_MODIFY_SET;
	set_tag->width = sizeof(flow_id) * BITS_PER_BYTE;
	set_tag->src.field = RTE_FLOW_FIELD_VALUE;
	memcpy(&set_tag->src.value, &flow_id, sizeof(flow_id));
	set_tag->dst.field = RTE_FLOW_FIELD_TAG;
	set_tag->dst.tag_index = RTE_PMD_MLX5_LINEAR_HASH_TAG_INDEX;
	/* Construct new actions array and replace QUEUE/RSS action. */
	prefix_act[qrss_idx] = (struct rte_flow_action) {
		.type = RTE_FLOW_ACTION_TYPE_MODIFY_FIELD,
		.conf = set_tag,
	};
	/* JUMP action to jump to mreg copy table (CP_TBL). */
	jump = (void *)(set_tag + 1);
	*jump = (struct rte_flow_action_jump) {
		.group = MLX5_FLOW_MREG_CP_TABLE_GROUP,
	};
	prefix_act[actions_n - 2] = (struct rte_flow_action) {
		.type = RTE_FLOW_ACTION_TYPE_JUMP,
		.conf = jump,
	};
	prefix_act[actions_n - 1] = (struct rte_flow_action) {
		.type = RTE_FLOW_ACTION_TYPE_END,
	};
	/* Copy the suffix Q/RSS action, can also be indirect RSS. */
	suffix_act[0] = (struct rte_flow_action) {
		.type = qrss->type,
		.conf = qrss->conf,
	};
	suffix_act[1] = (struct rte_flow_action) {
		.type = RTE_FLOW_ACTION_TYPE_END,
	};
	return flow_id;
}

/*
 * Generate new attribute and items for suffix flows.
 *
 * @param[in] dev
 *   Pointer to rte_eth_dev structure.
 * @param[in] split_attr
 *   Pointer to attribute for prefix flow rule.
 * @param[in] split_items
 *   Pointer to actions for suffix flow rule.
 * @param[in] qrss_id
 *   Prefix flow ID to match.
 */
static void
mlx5_flow_nta_split_qrss_items_prep(struct rte_eth_dev *dev,
				    struct rte_flow_attr *split_attr,
				    struct rte_flow_item *split_items,
				    uint32_t qrss_id)
{
	struct mlx5_rte_flow_item_tag *q_tag_spec;

	/* MLX5_FLOW_MREG_CP_TABLE_GROUP -> MLX5_FLOW_MREG_ACT_TABLE_GROUP(Q/RSS base) */
	split_attr->ingress = 1;
	split_attr->group = MLX5_FLOW_MREG_ACT_TABLE_GROUP;
	/* Only internal tag will be used, together with the item flags for RSS. */
	q_tag_spec = (void *)((char *)split_items + 2 * sizeof(struct rte_flow_item));
	split_items[0].type = (enum rte_flow_item_type)MLX5_RTE_FLOW_ITEM_TYPE_TAG;
	split_items[0].spec = q_tag_spec;
	split_items[1].type = RTE_FLOW_ITEM_TYPE_END;
	q_tag_spec->data = qrss_id;
	q_tag_spec->id = (enum modify_reg)
			 flow_hw_get_reg_id_by_domain(dev, RTE_FLOW_ITEM_TYPE_TAG,
						      MLX5DR_TABLE_TYPE_NIC_RX,
						      RTE_PMD_MLX5_LINEAR_HASH_TAG_INDEX);
	MLX5_ASSERT(q_tag_spec->id != REG_NON);
}

/*
 * Checking the split information and split the actions, items, attributes into
 * prefix and suffix to connect the flows after passing the copy tables.
 *
 * @param[in] dev
 *   Pointer to rte_eth_dev structure.
 * @param[in] attr
 *   Pointer to the flow attributes.
 * @param[in] actions
 *   Pointer to the original actions list.
 * @param[in] qrss
 *   Pointer to the action of QUEUE / RSS.
 * @param[in] action_flags
 *   Holds the actions detected.
 * @param[in] actions_n
 *   Number of original actions.
 * @param[in] external
 *   This flow rule is created by request external to PMD.
 * @param[out] res
 *   Pointer to the resource to store the split result.
 * @param[out] error
 *   Pointer to error structure.
 *
 * @return
 *   - Positive 1 on succeed.
 *   - 0 on no split.
 *   - negative errno value on error.
 */
int
mlx5_flow_nta_split_metadata(struct rte_eth_dev *dev,
			     const struct rte_flow_attr *attr,
			     const struct rte_flow_action actions[],
			     const struct rte_flow_action *qrss,
			     uint64_t action_flags,
			     int actions_n,
			     bool external,
			     struct mlx5_flow_hw_split_resource *res,
			     struct rte_flow_error *error)
{
	struct mlx5_priv *priv = dev->data->dev_private;
	struct mlx5_sh_config *config = &priv->sh->config;
	const struct rte_flow_action_queue *queue;
	const struct rte_flow_action_rss *rss;
	struct rte_flow_action *prfx_actions;
	struct rte_flow_action *sfx_actions;
	struct rte_flow_attr *sfx_attr;
	struct rte_flow_item *sfx_items;
	size_t pefx_act_size, sfx_act_size;
	size_t attr_size, item_size;
	size_t total_size;
	uint32_t qrss_id;

	/*
	 * The metadata copy flow should be created:
	 *   1. only on NIC Rx domain with Q / RSS
	 *   2. only when extended metadata mode is enabled
	 *   3. only on HWS, should always be "config->dv_flow_en == 2", this
	 *      checking can be skipped
	 * Note:
	 *   1. Even if metadata is not enabled in the data-path, it can still
	 *      be used to match on the Rx side.
	 *   2. The HWS Tx default copy rule or SQ rules already have the metadata
	 *      copy on the root table. The user's rule will always be inserted
	 *      and executed after the root table steering.
	 */
	if (config->dv_xmeta_en == MLX5_XMETA_MODE_LEGACY || attr->transfer ||
	    attr->egress || !external || !qrss)
		return 0;
	if (action_flags & MLX5_FLOW_ACTION_QUEUE) {
		queue = (const struct rte_flow_action_queue *)actions->conf;
		if (mlx5_rxq_is_hairpin(dev, queue->index))
			return 0;
	} else if (action_flags & MLX5_FLOW_ACTION_RSS) {
		rss = (const struct rte_flow_action_rss *)actions->conf;
		if (mlx5_rxq_is_hairpin(dev, rss->queue_num))
			return 0;
	}
	/* The prefix and suffix flows' actions. */
	pefx_act_size = sizeof(struct rte_flow_action) * (actions_n + 1) +
			sizeof(struct rte_flow_action_modify_field) +
			sizeof(struct rte_flow_action_jump);
	sfx_act_size = sizeof(struct rte_flow_action) * 2;
	/* The suffix attribute. */
	attr_size = sizeof(struct rte_flow_attr);
	/* The suffix items - mlx5_tag + end. */
	item_size = sizeof(struct rte_flow_item) * 2 +
		    sizeof(struct mlx5_rte_flow_item_tag);
	total_size = pefx_act_size + sfx_act_size + attr_size + item_size;
	prfx_actions = mlx5_malloc(MLX5_MEM_ZERO, total_size, 0, SOCKET_ID_ANY);
	if (!prfx_actions)
		return rte_flow_error_set(error, ENOMEM,
					  RTE_FLOW_ERROR_TYPE_ACTION,
					  NULL, "no memory to split "
					  "metadata flow");
	sfx_actions = (void *)((char *)prfx_actions + pefx_act_size);
	qrss_id = mlx5_flow_nta_split_qrss_actions_prep(dev, prfx_actions,
							sfx_actions, actions,
							qrss, actions_n, error);
	if (!qrss_id) {
		mlx5_free(prfx_actions);
		return -rte_errno;
	}
	sfx_attr = (void *)((char *)sfx_actions + sfx_act_size);
	sfx_items = (void *)((char *)sfx_attr + attr_size);
	mlx5_flow_nta_split_qrss_items_prep(dev, sfx_attr, sfx_items, qrss_id);
	res->prefix.actions = prfx_actions;
	res->suffix.actions = sfx_actions;
	res->suffix.items = sfx_items;
	res->suffix.attr = sfx_attr;
	res->buf_start = prfx_actions;
	res->flow_idx = qrss_id;
	return 1;
}

/*
 * Release the buffer and flow ID.
 *
 * @param[in] dev
 *   Pointer to rte_eth_dev structure.
 * @param[in] res
 *   Pointer to the resource to release.
 */
void
mlx5_flow_nta_split_resource_free(struct rte_eth_dev *dev,
				  struct mlx5_flow_hw_split_resource *res)
{
	struct mlx5_priv *priv = dev->data->dev_private;

	mlx5_ipool_free(priv->sh->ipool[MLX5_IPOOL_RSS_EXPANTION_FLOW_ID], res->flow_idx);
	mlx5_free(res->buf_start);
}

/*
 * Callback functions for the metadata copy and mark / flag set flow.
 * The create and remove cannot reuse the DV since the flow opaque and structure
 * are different, and the action used to copy the metadata is also different.
 */
struct mlx5_list_entry *
flow_nta_mreg_create_cb(void *tool_ctx, void *cb_ctx)
{
	struct rte_eth_dev *dev = tool_ctx;
	struct mlx5_priv *priv = dev->data->dev_private;
	struct mlx5_flow_cb_ctx *ctx = cb_ctx;
	struct mlx5_flow_mreg_copy_resource *mcp_res;
	struct rte_flow_error *error = ctx->error;
	uint32_t idx = 0;
	uint32_t mark_id = *(uint32_t *)(ctx->data);
	struct rte_flow_attr attr = {
		.group = MLX5_FLOW_MREG_CP_TABLE_GROUP,
		.ingress = 1,
	};
	struct mlx5_rte_flow_item_tag tag_spec = {
		.id = REG_C_0,
		.data = mark_id,
	};
	struct mlx5_rte_flow_item_tag tag_mask = {
		.data = priv->sh->dv_mark_mask,
	};
	struct rte_flow_action_mark ftag = {
		.id = mark_id,
	};
	struct rte_flow_action_modify_field rx_meta = {
		.operation = RTE_FLOW_MODIFY_SET,
		.dst = {
			.field = (enum rte_flow_field_id)MLX5_RTE_FLOW_FIELD_META_REG,
			.tag_index = REG_B,
		},
		.src = {
			.field = (enum rte_flow_field_id)MLX5_RTE_FLOW_FIELD_META_REG,
			.tag_index = REG_C_1,
		},
		.width = 32,
	};
	struct rte_flow_action_jump jump = {
		.group = MLX5_FLOW_MREG_ACT_TABLE_GROUP,
	};
	struct rte_flow_item items[2];
	struct rte_flow_action actions[4];

	/* Provide the full width of FLAG specific value. */
	if (mark_id == (priv->sh->dv_regc0_mask & MLX5_FLOW_MARK_DEFAULT))
		tag_spec.data = MLX5_FLOW_MARK_DEFAULT;
	/* Build a new flow. */
	if (mark_id != MLX5_DEFAULT_COPY_ID) {
		items[0] = (struct rte_flow_item) {
			.type = (enum rte_flow_item_type)MLX5_RTE_FLOW_ITEM_TYPE_TAG,
			.spec = &tag_spec,
			.mask = &tag_mask,
		};
		actions[0] = (struct rte_flow_action) {
			.type = RTE_FLOW_ACTION_TYPE_MARK,
			.conf = &ftag,
		};
	} else {
		/* Default rule, wildcard match with lowest priority. */
		attr.priority = MLX5_FLOW_LOWEST_PRIO_INDICATOR;
		items[0] = (struct rte_flow_item) {
			.type = RTE_FLOW_ITEM_TYPE_ETH,
		};
		actions[0] = (struct rte_flow_action) {
			.type = RTE_FLOW_ACTION_TYPE_VOID,
		};
	}
	/* (match REG 'tag') or all. */
	items[1].type = RTE_FLOW_ITEM_TYPE_END;
	/* (Mark) or void + copy to Rx meta + jump to the MREG_ACT_TABLE_GROUP. */
	actions[1].type = RTE_FLOW_ACTION_TYPE_MODIFY_FIELD;
	actions[1].conf = &rx_meta;
	actions[2].type = RTE_FLOW_ACTION_TYPE_JUMP;
	actions[2].conf = &jump;
	actions[3].type = RTE_FLOW_ACTION_TYPE_END;
	/* Build a new entry. */
	mcp_res = mlx5_ipool_zmalloc(priv->sh->ipool[MLX5_IPOOL_MCP], &idx);
	if (!mcp_res) {
		rte_errno = ENOMEM;
		return NULL;
	}
	mcp_res->idx = idx;
	mcp_res->mark_id = mark_id;
	/*
	 * The copy flows are not included in any list. There
	 * ones are referenced from other flows and cannot
	 * be applied, removed, deleted in arbitrary order
	 * by list traversing.
	 */
	mcp_res->hw_flow = mlx5_flow_list_create(dev, MLX5_FLOW_TYPE_MCP, &attr,
						 items, actions, false, error);
	if (!mcp_res->hw_flow) {
		mlx5_ipool_free(priv->sh->ipool[MLX5_IPOOL_MCP], idx);
		return NULL;
	}
	return &mcp_res->hlist_ent;
}

void
flow_nta_mreg_remove_cb(void *tool_ctx, struct mlx5_list_entry *entry)
{
	struct mlx5_flow_mreg_copy_resource *mcp_res =
			       container_of(entry, typeof(*mcp_res), hlist_ent);
	struct rte_eth_dev *dev = tool_ctx;
	struct mlx5_priv *priv = dev->data->dev_private;

	MLX5_ASSERT(mcp_res->hw_flow);
	mlx5_flow_list_destroy(dev, MLX5_FLOW_TYPE_MCP, mcp_res->hw_flow);
	mlx5_ipool_free(priv->sh->ipool[MLX5_IPOOL_MCP], mcp_res->idx);
}

/*
 * Add a flow of copying flow metadata registers in RX_CP_TBL.
 * @see flow_mreg_add_copy_action
 *
 * @param[in] dev
 *   Pointer to Ethernet device.
 * @param[in] mark_id
 *   ID of MARK action, zero means default flow for META.
 * @param[out] error
 *   Perform verbose error reporting if not NULL.
 *
 * @return
 *   Associated resource on success, NULL otherwise and rte_errno is set.
 */
static struct mlx5_flow_mreg_copy_resource *
mlx5_flow_nta_add_copy_action(struct rte_eth_dev *dev,
			      uint32_t mark_id,
			      struct rte_flow_error *error)
{
	struct mlx5_priv *priv = dev->data->dev_private;
	struct mlx5_list_entry *entry;
	uint32_t specialize = 0;
	struct mlx5_flow_cb_ctx ctx = {
		.dev = dev,
		.error = error,
		.data = &mark_id,
		.data2 = &specialize,
	};

	/* Check if already registered. */
	MLX5_ASSERT(priv->sh->mreg_cp_tbl);
	entry = mlx5_hlist_register(priv->sh->mreg_cp_tbl, mark_id, &ctx);
	if (!entry)
		return NULL;
	return container_of(entry, struct mlx5_flow_mreg_copy_resource, hlist_ent);
}

/*
 * Release flow in RX_CP_TBL.
 *
 * @param[in] dev
 *   Pointer to Ethernet device.
 * @param[in] idx
 *   Index in the pool to store the copy flow.
 */
void
mlx5_flow_nta_del_copy_action(struct rte_eth_dev *dev, uint32_t idx)
{
	struct mlx5_flow_mreg_copy_resource *mcp_res;
	struct mlx5_priv *priv = dev->data->dev_private;

	if (!idx)
		return;
	mcp_res = mlx5_ipool_get(priv->sh->ipool[MLX5_IPOOL_MCP], idx);
	if (!mcp_res || !priv->sh->mreg_cp_tbl)
		return;
	MLX5_ASSERT(mcp_res->hw_flow);
	mlx5_hlist_unregister(priv->sh->mreg_cp_tbl, &mcp_res->hlist_ent);
}

/*
 * Remove the default copy action from RX_CP_TBL.
 * @see flow_mreg_del_default_copy_action
 *
 * This functions is called in the mlx5_dev_start(). No thread safe
 * is guaranteed.
 *
 * @param[in] dev
 *   Pointer to Ethernet device.
 */
void
mlx5_flow_nta_del_default_copy_action(struct rte_eth_dev *dev)
{
	struct mlx5_list_entry *entry;
	struct mlx5_priv *priv = dev->data->dev_private;
	struct mlx5_flow_cb_ctx ctx;
	uint32_t mark_id;

	/* Check if default flow is registered. */
	if (!priv->sh->mreg_cp_tbl)
		return;
	mark_id = MLX5_DEFAULT_COPY_ID;
	ctx.data = &mark_id;
	entry = mlx5_hlist_lookup(priv->sh->mreg_cp_tbl, mark_id, &ctx);
	if (!entry)
		return;
	mlx5_hlist_unregister(priv->sh->mreg_cp_tbl, entry);
}

/*
 * Add the default copy action in RX_CP_TBL.
 *
 * This functions is called in the mlx5_dev_start(). No thread safe
 * is guaranteed.
 * @see flow_mreg_add_default_copy_action
 *
 * @param[in] dev
 *   Pointer to Ethernet device.
 * @param[out] error
 *   Perform verbose error reporting if not NULL.
 *
 * @return
 *   0 for success, negative value otherwise and rte_errno is set.
 */
int
mlx5_flow_nta_add_default_copy_action(struct rte_eth_dev *dev,
				      struct rte_flow_error *error)
{
	struct mlx5_priv *priv = dev->data->dev_private;
	struct mlx5_sh_config *config = &priv->sh->config;
	struct mlx5_flow_mreg_copy_resource *mcp_res;
	struct mlx5_flow_cb_ctx ctx;
	uint32_t mark_id;

	if (config->dv_xmeta_en == MLX5_XMETA_MODE_LEGACY ||
	    !priv->sh->dv_regc0_mask)
		return 0;
	/*
	 * Add default mreg copy flow may be called multiple time, but
	 * only be called once in stop. Avoid register it twice.
	 */
	mark_id = MLX5_DEFAULT_COPY_ID;
	ctx.data = &mark_id;
	if (mlx5_hlist_lookup(priv->sh->mreg_cp_tbl, mark_id, &ctx))
		return 0;
	mcp_res = mlx5_flow_nta_add_copy_action(dev, mark_id, error);
	if (!mcp_res)
		return -rte_errno;
	return 0;
}

/*
 * Add a flow of copying flow metadata registers in RX_CP_TBL.
 * @see flow_mreg_update_copy_table
 *
 * @param[in] dev
 *   Pointer to Ethernet device.
 * @param[out] idx
 *   Pointer to store the index of flow in the pool.
 * @param[in] mark
 *   Pointer to mark or flag action.
 * @param[in] action_flags
 *   Holds the actions detected.
 * @param[out] error
 *   Perform verbose error reporting if not NULL.
 *
 * @return
 *   0 on success, negative value otherwise and rte_errno is set.
 */
int
mlx5_flow_nta_update_copy_table(struct rte_eth_dev *dev,
				uint32_t *idx,
				const struct rte_flow_action *mark,
				uint64_t action_flags,
				struct rte_flow_error *error)
{
	struct mlx5_priv *priv = dev->data->dev_private;
	struct mlx5_sh_config *config = &priv->sh->config;
	struct mlx5_flow_mreg_copy_resource *mcp_res;
	const struct rte_flow_action_mark *mark_conf;
	uint32_t mark_id;

	if (config->dv_xmeta_en == MLX5_XMETA_MODE_LEGACY ||
	    !priv->sh->dv_regc0_mask)
		return 0;
	/* Find MARK action. */
	if (action_flags & (MLX5_FLOW_ACTION_FLAG | MLX5_FLOW_ACTION_MARK)) {
		if (mark) {
			mark_conf = (const struct rte_flow_action_mark *)mark->conf;
			mark_id = mark_conf->id;
		} else {
			mark_id = MLX5_FLOW_MARK_DEFAULT;
		}
		mcp_res = mlx5_flow_nta_add_copy_action(dev, mark_id, error);
		if (!mcp_res)
			return -rte_errno;
		*idx = mcp_res->idx;
	}
	return 0;
}

#endif