xref: /dpdk/drivers/net/mlx5/mlx5_nta_split.c (revision 821a6a5cc4951337a7eac64b6cce6a25c01be442)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2024 NVIDIA Corporation & Affiliates
3  */
4 
5 #include <rte_common.h>
6 #include <rte_flow.h>
7 
8 #include "mlx5_malloc.h"
9 #include "mlx5.h"
10 #include "mlx5_defs.h"
11 #include "mlx5_flow.h"
12 #include "mlx5_rx.h"
13 
14 #ifdef HAVE_MLX5_HWS_SUPPORT
15 
16 /*
17  * Generate new actions lists for prefix and suffix flows.
18  *
19  * @param[in] dev
20  *   Pointer to rte_eth_dev structure.
21  * @param[in] prefix_act
22  *   Pointer to actions for prefix flow rule.
23  * @param[in] suffix_act
24  *   Pointer to actions for suffix flow rule.
25  * @param[in] actions
26  *   Pointer to the original actions list.
27  * @param[in] qrss
28  *   Pointer to the action of QUEUE / RSS.
29  * @param[in] actions_n
30  *   Number of the actions in the original list.
31  * @param[out] error
32  *   Pointer to error structure.
33  *
34  * @return
35  *   Positive prefix flow ID on success, zero on failure.
36  */
37 static uint32_t
38 mlx5_flow_nta_split_qrss_actions_prep(struct rte_eth_dev *dev,
39 				      struct rte_flow_action *prefix_act,
40 				      struct rte_flow_action *suffix_act,
41 				      const struct rte_flow_action *actions,
42 				      const struct rte_flow_action *qrss,
43 				      int actions_n,
44 				      struct rte_flow_error *error)
45 {
46 	struct mlx5_priv *priv = dev->data->dev_private;
47 	struct mlx5_rte_flow_action_set_tag *set_tag;
48 	struct rte_flow_action_jump *jump;
49 	const int qrss_idx = qrss - actions;
50 	uint32_t flow_id = 0;
51 	int ret = 0;
52 
53 	/* Allocate the new subflow ID and used to be matched later. */
54 	mlx5_ipool_malloc(priv->sh->ipool[MLX5_IPOOL_RSS_EXPANTION_FLOW_ID], &flow_id);
55 	if (!flow_id) {
56 		rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_ACTION, NULL,
57 				   "can't allocate id for split Q/RSS subflow");
58 		return 0;
59 	}
60 	/*
61 	 * Given actions will be split
62 	 * - Replace QUEUE/RSS action with SET_TAG to set flow ID.
63 	 * - Add jump to mreg CP_TBL.
64 	 * As a result, there will be one more action.
65 	 */
66 	memcpy(prefix_act, actions, sizeof(struct rte_flow_action) * actions_n);
67 	/* Count MLX5_RTE_FLOW_ACTION_TYPE_TAG. */
68 	actions_n++;
69 	set_tag = (void *)(prefix_act + actions_n);
70 	/* Reuse ASO reg, should always succeed. Consider to use REG_C_6. */
71 	ret = flow_hw_get_reg_id_by_domain(dev, RTE_FLOW_ITEM_TYPE_METER_COLOR,
72 					   MLX5DR_TABLE_TYPE_NIC_RX, 0);
73 	MLX5_ASSERT(ret != (int)REG_NON);
74 	set_tag->id = (enum modify_reg)ret;
75 	/* Internal SET_TAG action to set flow ID. */
76 	set_tag->data = flow_id;
77 	/* Construct new actions array and replace QUEUE/RSS action. */
78 	prefix_act[qrss_idx] = (struct rte_flow_action) {
79 		.type = (enum rte_flow_action_type)MLX5_RTE_FLOW_ACTION_TYPE_TAG,
80 		.conf = set_tag,
81 	};
82 	/* JUMP action to jump to mreg copy table (CP_TBL). */
83 	jump = (void *)(set_tag + 1);
84 	*jump = (struct rte_flow_action_jump) {
85 		.group = MLX5_FLOW_MREG_CP_TABLE_GROUP,
86 	};
87 	prefix_act[actions_n - 2] = (struct rte_flow_action) {
88 		.type = RTE_FLOW_ACTION_TYPE_JUMP,
89 		.conf = jump,
90 	};
91 	prefix_act[actions_n - 1] = (struct rte_flow_action) {
92 		.type = RTE_FLOW_ACTION_TYPE_END,
93 	};
94 	/* Copy the suffix Q/RSS action, can also be indirect RSS. */
95 	suffix_act[0] = (struct rte_flow_action) {
96 		.type = qrss->type,
97 		.conf = qrss->conf,
98 	};
99 	suffix_act[1] = (struct rte_flow_action) {
100 		.type = RTE_FLOW_ACTION_TYPE_END,
101 	};
102 	return flow_id;
103 }
104 
105 /*
106  * Generate new attribute and items for suffix flows.
107  *
108  * @param[in] dev
109  *   Pointer to rte_eth_dev structure.
110  * @param[in] split_attr
111  *   Pointer to attribute for prefix flow rule.
112  * @param[in] split_items
113  *   Pointer to actions for suffix flow rule.
114  * @param[in] qrss_id
115  *   Prefix flow ID to match.
116  */
117 static void
118 mlx5_flow_nta_split_qrss_items_prep(struct rte_eth_dev *dev,
119 				    struct rte_flow_attr *split_attr,
120 				    struct rte_flow_item *split_items,
121 				    uint32_t qrss_id)
122 {
123 	struct mlx5_rte_flow_item_tag *q_tag_spec;
124 
125 	/* MLX5_FLOW_MREG_CP_TABLE_GROUP -> MLX5_FLOW_MREG_ACT_TABLE_GROUP(Q/RSS base) */
126 	split_attr->ingress = 1;
127 	split_attr->group = MLX5_FLOW_MREG_ACT_TABLE_GROUP;
128 	/* Only internal tag will be used, together with the item flags for RSS. */
129 	q_tag_spec = (void *)((char *)split_items + 2 * sizeof(struct rte_flow_item));
130 	split_items[0].type = (enum rte_flow_item_type)MLX5_RTE_FLOW_ITEM_TYPE_TAG;
131 	split_items[0].spec = q_tag_spec;
132 	split_items[1].type = RTE_FLOW_ITEM_TYPE_END;
133 	q_tag_spec->data = qrss_id;
134 	q_tag_spec->id = (enum modify_reg)
135 			 flow_hw_get_reg_id_by_domain(dev, RTE_FLOW_ITEM_TYPE_METER_COLOR,
136 						      MLX5DR_TABLE_TYPE_NIC_RX, 0);
137 	MLX5_ASSERT(q_tag_spec->id != REG_NON);
138 }
139 
140 /*
141  * Checking the split information and split the actions, items, attributes into
142  * prefix and suffix to connect the flows after passing the copy tables.
143  *
144  * @param[in] dev
145  *   Pointer to rte_eth_dev structure.
146  * @param[in] attr
147  *   Pointer to the flow attributes.
148  * @param[in] actions
149  *   Pointer to the original actions list.
150  * @param[in] qrss
151  *   Pointer to the action of QUEUE / RSS.
152  * @param[in] action_flags
153  *   Holds the actions detected.
154  * @param[in] actions_n
155  *   Number of original actions.
156  * @param[in] external
157  *   This flow rule is created by request external to PMD.
158  * @param[out] res
159  *   Pointer to the resource to store the split result.
160  * @param[out] error
161  *   Pointer to error structure.
162  *
163  * @return
164  *   - Positive 1 on succeed.
165  *   - 0 on no split.
166  *   - negative errno value on error.
167  */
168 int
169 mlx5_flow_nta_split_metadata(struct rte_eth_dev *dev,
170 			     const struct rte_flow_attr *attr,
171 			     const struct rte_flow_action actions[],
172 			     const struct rte_flow_action *qrss,
173 			     uint64_t action_flags,
174 			     int actions_n,
175 			     bool external,
176 			     struct mlx5_flow_hw_split_resource *res,
177 			     struct rte_flow_error *error)
178 {
179 	struct mlx5_priv *priv = dev->data->dev_private;
180 	struct mlx5_sh_config *config = &priv->sh->config;
181 	const struct rte_flow_action_queue *queue;
182 	const struct rte_flow_action_rss *rss;
183 	struct rte_flow_action *prfx_actions;
184 	struct rte_flow_action *sfx_actions;
185 	struct rte_flow_attr *sfx_attr;
186 	struct rte_flow_item *sfx_items;
187 	size_t pefx_act_size, sfx_act_size;
188 	size_t attr_size, item_size;
189 	size_t total_size;
190 	uint32_t qrss_id;
191 
192 	/*
193 	 * The metadata copy flow should be created:
194 	 *   1. only on NIC Rx domain with Q / RSS
195 	 *   2. only when extended metadata mode is enabled
196 	 *   3. only on HWS, should always be "config->dv_flow_en == 2", this
197 	 *      checking can be skipped
198 	 * Note:
199 	 *   1. Even if metadata is not enabled in the data-path, it can still
200 	 *      be used to match on the Rx side.
201 	 *   2. The HWS Tx default copy rule or SQ rules already have the metadata
202 	 *      copy on the root table. The user's rule will always be inserted
203 	 *      and executed after the root table steering.
204 	 */
205 	if (config->dv_xmeta_en == MLX5_XMETA_MODE_LEGACY || attr->transfer ||
206 	    attr->egress || !external || !qrss)
207 		return 0;
208 	if (action_flags & MLX5_FLOW_ACTION_QUEUE) {
209 		queue = (const struct rte_flow_action_queue *)actions->conf;
210 		if (mlx5_rxq_is_hairpin(dev, queue->index))
211 			return 0;
212 	} else if (action_flags & MLX5_FLOW_ACTION_RSS) {
213 		rss = (const struct rte_flow_action_rss *)actions->conf;
214 		if (mlx5_rxq_is_hairpin(dev, rss->queue[0]))
215 			return 0;
216 	}
217 	/* The prefix and suffix flows' actions. */
218 	pefx_act_size = sizeof(struct rte_flow_action) * (actions_n + 1) +
219 			sizeof(struct rte_flow_action_set_tag) +
220 			sizeof(struct rte_flow_action_jump);
221 	sfx_act_size = sizeof(struct rte_flow_action) * 2;
222 	/* The suffix attribute. */
223 	attr_size = sizeof(struct rte_flow_attr);
224 	/* The suffix items - mlx5_tag + end. */
225 	item_size = sizeof(struct rte_flow_item) * 2 +
226 		    sizeof(struct mlx5_rte_flow_item_tag);
227 	total_size = pefx_act_size + sfx_act_size + attr_size + item_size;
228 	prfx_actions = mlx5_malloc(MLX5_MEM_ZERO, total_size, 0, SOCKET_ID_ANY);
229 	if (!prfx_actions)
230 		return rte_flow_error_set(error, ENOMEM,
231 					  RTE_FLOW_ERROR_TYPE_ACTION,
232 					  NULL, "no memory to split "
233 					  "metadata flow");
234 	sfx_actions = (void *)((char *)prfx_actions + pefx_act_size);
235 	qrss_id = mlx5_flow_nta_split_qrss_actions_prep(dev, prfx_actions,
236 							sfx_actions, actions,
237 							qrss, actions_n, error);
238 	if (!qrss_id) {
239 		mlx5_free(prfx_actions);
240 		return -rte_errno;
241 	}
242 	sfx_attr = (void *)((char *)sfx_actions + sfx_act_size);
243 	sfx_items = (void *)((char *)sfx_attr + attr_size);
244 	mlx5_flow_nta_split_qrss_items_prep(dev, sfx_attr, sfx_items, qrss_id);
245 	res->prefix.actions = prfx_actions;
246 	res->suffix.actions = sfx_actions;
247 	res->suffix.items = sfx_items;
248 	res->suffix.attr = sfx_attr;
249 	res->buf_start = prfx_actions;
250 	res->flow_idx = qrss_id;
251 	return 1;
252 }
253 
254 /*
255  * Release the buffer and flow ID.
256  *
257  * @param[in] dev
258  *   Pointer to rte_eth_dev structure.
259  * @param[in] res
260  *   Pointer to the resource to release.
261  */
262 void
263 mlx5_flow_nta_split_resource_free(struct rte_eth_dev *dev,
264 				  struct mlx5_flow_hw_split_resource *res)
265 {
266 	struct mlx5_priv *priv = dev->data->dev_private;
267 
268 	mlx5_ipool_free(priv->sh->ipool[MLX5_IPOOL_RSS_EXPANTION_FLOW_ID], res->flow_idx);
269 	mlx5_free(res->buf_start);
270 }
271 
272 /*
273  * Callback functions for the metadata copy and mark / flag set flow.
274  * The create and remove cannot reuse the DV since the flow opaque and structure
275  * are different, and the action used to copy the metadata is also different.
276  */
277 struct mlx5_list_entry *
278 flow_nta_mreg_create_cb(void *tool_ctx, void *cb_ctx)
279 {
280 	struct rte_eth_dev *dev = tool_ctx;
281 	struct mlx5_priv *priv = dev->data->dev_private;
282 	struct mlx5_flow_cb_ctx *ctx = cb_ctx;
283 	struct mlx5_flow_mreg_copy_resource *mcp_res;
284 	struct rte_flow_error *error = ctx->error;
285 	uint32_t idx = 0;
286 	uint32_t mark_id = *(uint32_t *)(ctx->data);
287 	struct rte_flow_attr attr = {
288 		.group = MLX5_FLOW_MREG_CP_TABLE_GROUP,
289 		.ingress = 1,
290 	};
291 	struct mlx5_rte_flow_item_tag tag_spec = {
292 		.id = REG_C_0,
293 		.data = mark_id,
294 	};
295 	struct mlx5_rte_flow_item_tag tag_mask = {
296 		.data = priv->sh->dv_mark_mask,
297 	};
298 	struct rte_flow_action_mark ftag = {
299 		.id = mark_id,
300 	};
301 	struct rte_flow_action_modify_field rx_meta = {
302 		.operation = RTE_FLOW_MODIFY_SET,
303 		.dst = {
304 			.field = (enum rte_flow_field_id)MLX5_RTE_FLOW_FIELD_META_REG,
305 			.tag_index = REG_B,
306 		},
307 		.src = {
308 			.field = (enum rte_flow_field_id)MLX5_RTE_FLOW_FIELD_META_REG,
309 			.tag_index = REG_C_1,
310 		},
311 		.width = 32,
312 	};
313 	struct rte_flow_action_jump jump = {
314 		.group = MLX5_FLOW_MREG_ACT_TABLE_GROUP,
315 	};
316 	struct rte_flow_item items[2];
317 	struct rte_flow_action actions[4];
318 
319 	/* Provide the full width of FLAG specific value. */
320 	if (mark_id == (priv->sh->dv_regc0_mask & MLX5_FLOW_MARK_DEFAULT))
321 		tag_spec.data = MLX5_FLOW_MARK_DEFAULT;
322 	/* Build a new flow. */
323 	if (mark_id != MLX5_DEFAULT_COPY_ID) {
324 		items[0] = (struct rte_flow_item) {
325 			.type = (enum rte_flow_item_type)MLX5_RTE_FLOW_ITEM_TYPE_TAG,
326 			.spec = &tag_spec,
327 			.mask = &tag_mask,
328 		};
329 		actions[0] = (struct rte_flow_action) {
330 			.type = RTE_FLOW_ACTION_TYPE_MARK,
331 			.conf = &ftag,
332 		};
333 	} else {
334 		/* Default rule, wildcard match with lowest priority. */
335 		attr.priority = MLX5_FLOW_LOWEST_PRIO_INDICATOR;
336 		items[0] = (struct rte_flow_item) {
337 			.type = RTE_FLOW_ITEM_TYPE_ETH,
338 		};
339 		actions[0] = (struct rte_flow_action) {
340 			.type = RTE_FLOW_ACTION_TYPE_VOID,
341 		};
342 	}
343 	/* (match REG 'tag') or all. */
344 	items[1].type = RTE_FLOW_ITEM_TYPE_END;
345 	/* (Mark) or void + copy to Rx meta + jump to the MREG_ACT_TABLE_GROUP. */
346 	actions[1].type = RTE_FLOW_ACTION_TYPE_MODIFY_FIELD,
347 	actions[1].conf = &rx_meta,
348 	actions[2].type = RTE_FLOW_ACTION_TYPE_JUMP;
349 	actions[2].conf = &jump;
350 	actions[3].type = RTE_FLOW_ACTION_TYPE_END;
351 	/* Build a new entry. */
352 	mcp_res = mlx5_ipool_zmalloc(priv->sh->ipool[MLX5_IPOOL_MCP], &idx);
353 	if (!mcp_res) {
354 		rte_errno = ENOMEM;
355 		return NULL;
356 	}
357 	mcp_res->idx = idx;
358 	mcp_res->mark_id = mark_id;
359 	/*
360 	 * The copy flows are not included in any list. There
361 	 * ones are referenced from other flows and cannot
362 	 * be applied, removed, deleted in arbitrary order
363 	 * by list traversing.
364 	 */
365 	mcp_res->hw_flow = mlx5_flow_list_create(dev, MLX5_FLOW_TYPE_MCP, &attr,
366 						 items, actions, false, error);
367 	if (!mcp_res->hw_flow) {
368 		mlx5_ipool_free(priv->sh->ipool[MLX5_IPOOL_MCP], idx);
369 		return NULL;
370 	}
371 	return &mcp_res->hlist_ent;
372 }
373 
374 void
375 flow_nta_mreg_remove_cb(void *tool_ctx, struct mlx5_list_entry *entry)
376 {
377 	struct mlx5_flow_mreg_copy_resource *mcp_res =
378 			       container_of(entry, typeof(*mcp_res), hlist_ent);
379 	struct rte_eth_dev *dev = tool_ctx;
380 	struct mlx5_priv *priv = dev->data->dev_private;
381 
382 	MLX5_ASSERT(mcp_res->hw_flow);
383 	mlx5_flow_list_destroy(dev, MLX5_FLOW_TYPE_MCP, mcp_res->hw_flow);
384 	mlx5_ipool_free(priv->sh->ipool[MLX5_IPOOL_MCP], mcp_res->idx);
385 }
386 
387 /*
388  * Add a flow of copying flow metadata registers in RX_CP_TBL.
389  * @see flow_mreg_add_copy_action
390  *
391  * @param[in] dev
392  *   Pointer to Ethernet device.
393  * @param[in] mark_id
394  *   ID of MARK action, zero means default flow for META.
395  * @param[out] error
396  *   Perform verbose error reporting if not NULL.
397  *
398  * @return
399  *   Associated resource on success, NULL otherwise and rte_errno is set.
400  */
401 static struct mlx5_flow_mreg_copy_resource *
402 mlx5_flow_nta_add_copy_action(struct rte_eth_dev *dev,
403 			      uint32_t mark_id,
404 			      struct rte_flow_error *error)
405 {
406 	struct mlx5_priv *priv = dev->data->dev_private;
407 	struct mlx5_list_entry *entry;
408 	uint32_t specialize = 0;
409 	struct mlx5_flow_cb_ctx ctx = {
410 		.dev = dev,
411 		.error = error,
412 		.data = &mark_id,
413 		.data2 = &specialize,
414 	};
415 
416 	/* Check if already registered. */
417 	MLX5_ASSERT(priv->sh->mreg_cp_tbl);
418 	entry = mlx5_hlist_register(priv->sh->mreg_cp_tbl, mark_id, &ctx);
419 	if (!entry)
420 		return NULL;
421 	return container_of(entry, struct mlx5_flow_mreg_copy_resource, hlist_ent);
422 }
423 
424 /*
425  * Release flow in RX_CP_TBL.
426  *
427  * @param[in] dev
428  *   Pointer to Ethernet device.
429  * @param[in] idx
430  *   Index in the pool to store the copy flow.
431  */
432 void
433 mlx5_flow_nta_del_copy_action(struct rte_eth_dev *dev, uint32_t idx)
434 {
435 	struct mlx5_flow_mreg_copy_resource *mcp_res;
436 	struct mlx5_priv *priv = dev->data->dev_private;
437 
438 	if (!idx)
439 		return;
440 	mcp_res = mlx5_ipool_get(priv->sh->ipool[MLX5_IPOOL_MCP], idx);
441 	if (!mcp_res || !priv->sh->mreg_cp_tbl)
442 		return;
443 	MLX5_ASSERT(mcp_res->hw_flow);
444 	mlx5_hlist_unregister(priv->sh->mreg_cp_tbl, &mcp_res->hlist_ent);
445 }
446 
447 /*
448  * Remove the default copy action from RX_CP_TBL.
449  * @see flow_mreg_del_default_copy_action
450  *
451  * This functions is called in the mlx5_dev_start(). No thread safe
452  * is guaranteed.
453  *
454  * @param[in] dev
455  *   Pointer to Ethernet device.
456  */
457 void
458 mlx5_flow_nta_del_default_copy_action(struct rte_eth_dev *dev)
459 {
460 	struct mlx5_list_entry *entry;
461 	struct mlx5_priv *priv = dev->data->dev_private;
462 	struct mlx5_flow_cb_ctx ctx;
463 	uint32_t mark_id;
464 
465 	/* Check if default flow is registered. */
466 	if (!priv->sh->mreg_cp_tbl)
467 		return;
468 	mark_id = MLX5_DEFAULT_COPY_ID;
469 	ctx.data = &mark_id;
470 	entry = mlx5_hlist_lookup(priv->sh->mreg_cp_tbl, mark_id, &ctx);
471 	if (!entry)
472 		return;
473 	mlx5_hlist_unregister(priv->sh->mreg_cp_tbl, entry);
474 }
475 
476 /*
477  * Add the default copy action in RX_CP_TBL.
478  *
479  * This functions is called in the mlx5_dev_start(). No thread safe
480  * is guaranteed.
481  * @see flow_mreg_add_default_copy_action
482  *
483  * @param[in] dev
484  *   Pointer to Ethernet device.
485  * @param[out] error
486  *   Perform verbose error reporting if not NULL.
487  *
488  * @return
489  *   0 for success, negative value otherwise and rte_errno is set.
490  */
491 int
492 mlx5_flow_nta_add_default_copy_action(struct rte_eth_dev *dev,
493 				      struct rte_flow_error *error)
494 {
495 	struct mlx5_priv *priv = dev->data->dev_private;
496 	struct mlx5_sh_config *config = &priv->sh->config;
497 	struct mlx5_flow_mreg_copy_resource *mcp_res;
498 	struct mlx5_flow_cb_ctx ctx;
499 	uint32_t mark_id;
500 
501 	if (config->dv_xmeta_en == MLX5_XMETA_MODE_LEGACY ||
502 	    !priv->sh->dv_regc0_mask)
503 		return 0;
504 	/*
505 	 * Add default mreg copy flow may be called multiple time, but
506 	 * only be called once in stop. Avoid register it twice.
507 	 */
508 	mark_id = MLX5_DEFAULT_COPY_ID;
509 	ctx.data = &mark_id;
510 	if (mlx5_hlist_lookup(priv->sh->mreg_cp_tbl, mark_id, &ctx))
511 		return 0;
512 	mcp_res = mlx5_flow_nta_add_copy_action(dev, mark_id, error);
513 	if (!mcp_res)
514 		return -rte_errno;
515 	return 0;
516 }
517 
518 /*
519  * Add a flow of copying flow metadata registers in RX_CP_TBL.
520  * @see flow_mreg_update_copy_table
521  *
522  * @param[in] dev
523  *   Pointer to Ethernet device.
524  * @param[out] idx
525  *   Pointer to store the index of flow in the pool.
526  * @param[in] mark
527  *   Pointer to mark or flag action.
528  * @param[in] action_flags
529  *   Holds the actions detected.
530  * @param[out] error
531  *   Perform verbose error reporting if not NULL.
532  *
533  * @return
534  *   0 on success, negative value otherwise and rte_errno is set.
535  */
536 int
537 mlx5_flow_nta_update_copy_table(struct rte_eth_dev *dev,
538 				uint32_t *idx,
539 				const struct rte_flow_action *mark,
540 				uint64_t action_flags,
541 				struct rte_flow_error *error)
542 {
543 	struct mlx5_priv *priv = dev->data->dev_private;
544 	struct mlx5_sh_config *config = &priv->sh->config;
545 	struct mlx5_flow_mreg_copy_resource *mcp_res;
546 	const struct rte_flow_action_mark *mark_conf;
547 	uint32_t mark_id;
548 
549 	if (config->dv_xmeta_en == MLX5_XMETA_MODE_LEGACY ||
550 	    !priv->sh->dv_regc0_mask)
551 		return 0;
552 	/* Find MARK action. */
553 	if (action_flags & (MLX5_FLOW_ACTION_FLAG | MLX5_FLOW_ACTION_MARK)) {
554 		if (mark) {
555 			mark_conf = (const struct rte_flow_action_mark *)mark->conf;
556 			mark_id = mark_conf->id;
557 		} else {
558 			mark_id = MLX5_FLOW_MARK_DEFAULT;
559 		}
560 		mcp_res = mlx5_flow_nta_add_copy_action(dev, mark_id, error);
561 		if (!mcp_res)
562 			return -rte_errno;
563 		*idx = mcp_res->idx;
564 	}
565 	return 0;
566 }
567 
568 #endif
569