DPDK patches and discussions
 help / color / mirror / Atom feed
From: Gregory Etelson <getelson@nvidia.com>
To: <dev@dpdk.org>
Cc: getelson@nvidia.com,   <mkashani@nvidia.com>,
	rasland@nvidia.com, "Dariusz Sosnowski" <dsosnowski@nvidia.com>,
	"Viacheslav Ovsiienko" <viacheslavo@nvidia.com>,
	"Bing Zhao" <bingz@nvidia.com>, "Ori Kam" <orika@nvidia.com>,
	"Suanming Mou" <suanmingm@nvidia.com>,
	"Matan Azrad" <matan@nvidia.com>
Subject: [PATCH v2 4/4] net/mlx5: support non-template SAMPLE flow action
Date: Thu, 26 Jun 2025 10:59:44 +0300	[thread overview]
Message-ID: <20250626075945.537256-5-getelson@nvidia.com> (raw)
In-Reply-To: <20250626075945.537256-1-getelson@nvidia.com>

MLX5 HWS flow engine does not support the SAMPLE flow action.

The patch adds the SAMPLE action support to the non-template API.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Acked-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
---
 drivers/net/mlx5/mlx5.c            |   1 +
 drivers/net/mlx5/mlx5_flow.h       |  23 ++
 drivers/net/mlx5/mlx5_flow_hw.c    |  48 +++-
 drivers/net/mlx5/mlx5_nta_sample.c | 411 ++++++++++++++++++++++++++++-
 drivers/net/mlx5/mlx5_nta_sample.h |   3 +
 5 files changed, 464 insertions(+), 22 deletions(-)

diff --git a/drivers/net/mlx5/mlx5.c b/drivers/net/mlx5/mlx5.c
index b4bd43aae2..09cefd5b36 100644
--- a/drivers/net/mlx5/mlx5.c
+++ b/drivers/net/mlx5/mlx5.c
@@ -2368,6 +2368,7 @@ mlx5_dev_close(struct rte_eth_dev *dev)
 	mlx5_flex_item_port_cleanup(dev);
 	mlx5_indirect_list_handles_release(dev);
 #ifdef HAVE_MLX5_HWS_SUPPORT
+	mlx5_nta_sample_context_free(dev);
 	flow_hw_destroy_vport_action(dev);
 	/* dr context will be closed after mlx5_os_free_shared_dr. */
 	flow_hw_resource_release(dev);
diff --git a/drivers/net/mlx5/mlx5_flow.h b/drivers/net/mlx5/mlx5_flow.h
index 561bb05dfa..e890e732c3 100644
--- a/drivers/net/mlx5/mlx5_flow.h
+++ b/drivers/net/mlx5/mlx5_flow.h
@@ -49,6 +49,7 @@ enum mlx5_rte_flow_action_type {
 	MLX5_RTE_FLOW_ACTION_TYPE_JUMP,
 	MLX5_RTE_FLOW_ACTION_TYPE_RSS,
 	MLX5_RTE_FLOW_ACTION_TYPE_METER_MARK,
+	MLX5_RTE_FLOW_ACTION_TYPE_MIRROR,
 };
 
 /* Private (internal) Field IDs for MODIFY_FIELD action. */
@@ -1341,6 +1342,11 @@ enum {
 
 SLIST_HEAD(mlx5_nta_rss_flow_head, rte_flow_hw);
 
+struct mlx5_sample_release_ctx {
+	struct mlx5_list_entry *mirror_entry;
+	uint32_t sample_group;
+};
+
 /** HWS non template flow data. */
 struct rte_flow_nt2hws {
 	/** BWC rule pointer. */
@@ -1351,6 +1357,8 @@ struct rte_flow_nt2hws {
 	struct rte_flow_hw_aux *flow_aux;
 	/** Modify header pointer. */
 	struct mlx5_flow_dv_modify_hdr_resource *modify_hdr;
+	/** SAMPLE resources */
+	struct mlx5_sample_release_ctx *sample_release_ctx;
 	/** Chain NTA flows. */
 	SLIST_ENTRY(rte_flow_hw) next;
 	/** Encap/decap index. */
@@ -3743,6 +3751,21 @@ mlx5_hw_create_mirror(struct rte_eth_dev *dev,
 		      const struct rte_flow_action *actions,
 		      struct rte_flow_error *error);
 
+int
+mlx5_flow_hw_group_set_miss_actions(struct rte_eth_dev *dev,
+				    uint32_t group_id,
+				    const struct rte_flow_group_attr *attr,
+				    const struct rte_flow_action actions[],
+				    struct rte_flow_error *error);
+
+uint64_t
+mlx5_flow_hw_action_flags_get(const struct rte_flow_action actions[],
+			      const struct rte_flow_action **qrss,
+			      const struct rte_flow_action **mark,
+			      int *encap_idx,
+			      int *act_cnt,
+			      struct rte_flow_error *error);
+
 #include "mlx5_nta_sample.h"
 
 #endif
diff --git a/drivers/net/mlx5/mlx5_flow_hw.c b/drivers/net/mlx5/mlx5_flow_hw.c
index a7162d5859..1a20f26d80 100644
--- a/drivers/net/mlx5/mlx5_flow_hw.c
+++ b/drivers/net/mlx5/mlx5_flow_hw.c
@@ -62,9 +62,6 @@ static struct rte_flow_fp_ops mlx5_flow_hw_fp_ops;
 #define MLX5_HW_VLAN_PUSH_VID_IDX 1
 #define MLX5_HW_VLAN_PUSH_PCP_IDX 2
 
-#define MLX5_MIRROR_MAX_CLONES_NUM 3
-#define MLX5_MIRROR_MAX_SAMPLE_ACTIONS_LEN 4
-
 #define MLX5_HW_PORT_IS_PROXY(priv) \
 	(!!((priv)->sh->esw_mode && (priv)->master))
 
@@ -577,8 +574,8 @@ flow_hw_hashfields_set(struct mlx5_flow_rss_desc *rss_desc,
 	*hash_fields |= fields;
 }
 
-static uint64_t
-flow_hw_action_flags_get(const struct rte_flow_action actions[],
+uint64_t
+mlx5_flow_hw_action_flags_get(const struct rte_flow_action actions[],
 			 const struct rte_flow_action **qrss,
 			 const struct rte_flow_action **mark,
 			 int *encap_idx,
@@ -695,6 +692,9 @@ flow_hw_action_flags_get(const struct rte_flow_action actions[],
 		case RTE_FLOW_ACTION_TYPE_JUMP_TO_TABLE_INDEX:
 			action_flags |= MLX5_FLOW_ACTION_JUMP_TO_TABLE_INDEX;
 			break;
+		case RTE_FLOW_ACTION_TYPE_SAMPLE:
+			action_flags |= MLX5_FLOW_ACTION_SAMPLE;
+			break;
 		case RTE_FLOW_ACTION_TYPE_VOID:
 		case RTE_FLOW_ACTION_TYPE_END:
 			break;
@@ -1987,6 +1987,7 @@ hws_table_tmpl_translate_indirect_mirror(struct rte_eth_dev *dev,
 			 action_src, action_dst,
 			 flow_hw_translate_indirect_mirror);
 	}
+
 	return ret;
 }
 
@@ -2903,6 +2904,12 @@ __flow_hw_translate_actions_template(struct rte_eth_dev *dev,
 				goto err;
 			}
 			break;
+		case MLX5_RTE_FLOW_ACTION_TYPE_MIRROR:
+			if (__flow_hw_act_data_general_append(priv, acts,
+							      actions->type,
+							      src_pos, dr_pos))
+				goto err;
+			break;
 		case RTE_FLOW_ACTION_TYPE_END:
 			actions_end = true;
 			break;
@@ -3743,6 +3750,12 @@ flow_hw_actions_construct(struct rte_eth_dev *dev,
 				((const struct rte_flow_action_jump_to_table_index *)
 				action->conf)->index;
 			break;
+		case MLX5_RTE_FLOW_ACTION_TYPE_MIRROR: {
+			const struct mlx5_mirror *mirror = action->conf;
+
+			rule_acts[act_data->action_dst].action = mirror->mirror_action;
+			break;
+		}
 		default:
 			break;
 		}
@@ -3995,6 +4008,7 @@ flow_hw_async_flow_create_generic(struct rte_eth_dev *dev,
 		aux->matcher_selector = selector;
 		flow->flags |= MLX5_FLOW_HW_FLOW_FLAG_MATCHER_SELECTOR;
 	}
+
 	if (likely(!ret)) {
 		flow_hw_q_inc_flow_ops(priv, queue);
 		return (struct rte_flow *)flow;
@@ -5694,8 +5708,8 @@ flow_hw_group_unset_miss_group(struct rte_eth_dev *dev,
  *   0 on success, a negative errno value otherwise and rte_errno is set.
  */
 
-static int
-flow_hw_group_set_miss_actions(struct rte_eth_dev *dev,
+int
+mlx5_flow_hw_group_set_miss_actions(struct rte_eth_dev *dev,
 			       uint32_t group_id,
 			       const struct rte_flow_group_attr *attr,
 			       const struct rte_flow_action actions[],
@@ -7623,6 +7637,10 @@ flow_hw_parse_flow_actions_to_dr_actions(struct rte_eth_dev *dev,
 			at->dr_off[i] = curr_off;
 			action_types[curr_off++] = MLX5DR_ACTION_TYP_JUMP_TO_MATCHER;
 			break;
+		case MLX5_RTE_FLOW_ACTION_TYPE_MIRROR:
+			at->dr_off[i] = curr_off;
+			action_types[curr_off++] = MLX5DR_ACTION_TYP_DEST_ARRAY;
+				break;
 		default:
 			type = mlx5_hw_dr_action_types[at->actions[i].type];
 			at->dr_off[i] = curr_off;
@@ -14107,6 +14125,10 @@ flow_hw_destroy(struct rte_eth_dev *dev, struct rte_flow_hw *flow)
 	}
 	if (flow->nt2hws->matcher)
 		flow_hw_unregister_matcher(dev, flow->nt2hws->matcher);
+	if (flow->nt2hws->sample_release_ctx != NULL) {
+		mlx5_nta_sample_mirror_entry_release(dev, flow->nt2hws->sample_release_ctx);
+		flow->nt2hws->sample_release_ctx = NULL;
+	}
 }
 
 #ifdef HAVE_MLX5_HWS_SUPPORT
@@ -14180,7 +14202,7 @@ static uintptr_t flow_hw_list_create(struct rte_eth_dev *dev,
 	const struct rte_flow_action *qrss = NULL;
 	const struct rte_flow_action *mark = NULL;
 	uint64_t item_flags = 0;
-	uint64_t action_flags = flow_hw_action_flags_get(actions, &qrss, &mark,
+	uint64_t action_flags = mlx5_flow_hw_action_flags_get(actions, &qrss, &mark,
 							 &encap_idx, &actions_n, error);
 	struct mlx5_flow_hw_split_resource resource = {
 		.suffix = {
@@ -14219,7 +14241,13 @@ static uintptr_t flow_hw_list_create(struct rte_eth_dev *dev,
 		if (ret)
 			goto free;
 	}
-
+	if (action_flags & MLX5_FLOW_ACTION_SAMPLE) {
+		flow = mlx5_nta_sample_flow_list_create(dev, type, attr, items, actions,
+							item_flags, action_flags, error);
+		if (flow != NULL)
+			return (uintptr_t)flow;
+		goto free;
+	}
 	if (action_flags & MLX5_FLOW_ACTION_RSS) {
 		const struct rte_flow_action_rss
 			*rss_conf = flow_nta_locate_rss(dev, actions, error);
@@ -15376,7 +15404,7 @@ const struct mlx5_flow_driver_ops mlx5_flow_hw_drv_ops = {
 	.template_table_create = flow_hw_template_table_create,
 	.template_table_destroy = flow_hw_table_destroy,
 	.table_resize = flow_hw_table_resize,
-	.group_set_miss_actions = flow_hw_group_set_miss_actions,
+	.group_set_miss_actions = mlx5_flow_hw_group_set_miss_actions,
 	.async_flow_create = flow_hw_async_flow_create,
 	.async_flow_create_by_index = flow_hw_async_flow_create_by_index,
 	.async_flow_update = flow_hw_async_flow_update,
diff --git a/drivers/net/mlx5/mlx5_nta_sample.c b/drivers/net/mlx5/mlx5_nta_sample.c
index aae864eb00..b8351be0d9 100644
--- a/drivers/net/mlx5/mlx5_nta_sample.c
+++ b/drivers/net/mlx5/mlx5_nta_sample.c
@@ -9,6 +9,8 @@
 #include "mlx5_flow.h"
 #include "mlx5_rx.h"
 
+SLIST_HEAD(mlx5_flow_head, rte_flow_hw);
+
 struct mlx5_nta_sample_ctx {
 	uint32_t groups_num;
 	struct mlx5_indexed_pool *group_ids;
@@ -16,6 +18,18 @@ struct mlx5_nta_sample_ctx {
 	struct mlx5_list *mirror_groups; /* cache groups for sample and suffix actions */
 };
 
+static void
+release_chained_flows(struct rte_eth_dev *dev, struct mlx5_flow_head *flow_head,
+		      enum mlx5_flow_type type)
+{
+	struct rte_flow_hw *flow = SLIST_FIRST(flow_head);
+
+	if (flow) {
+		flow->nt2hws->chaned_flow = 0;
+		flow_hw_list_destroy(dev, type, (uintptr_t)flow);
+	}
+}
+
 static void
 release_cached_group(struct rte_eth_dev *dev, uint32_t group)
 {
@@ -83,6 +97,32 @@ struct mlx5_nta_sample_cached_mirror_ctx {
 	struct mlx5_list_entry *suffix;
 };
 
+static struct mlx5_mirror *
+mirror_entry_to_mirror_action(struct mlx5_list_entry *entry)
+{
+	return container_of(entry, struct mlx5_nta_sample_cached_mirror, entry)->mirror;
+}
+
+static uint32_t
+mirror_entry_to_mirror_sample_group(struct mlx5_list_entry *entry)
+{
+	struct mlx5_list_entry *sample = container_of(entry,
+						      struct mlx5_nta_sample_cached_mirror,
+						      entry)->sample;
+
+	return container_of(sample, struct mlx5_nta_sample_cached_group, entry)->group;
+}
+
+static uint32_t
+mirror_entry_to_mirror_suffix_group(struct mlx5_list_entry *entry)
+{
+	struct mlx5_list_entry *suffix = container_of(entry,
+						      struct mlx5_nta_sample_cached_mirror,
+						      entry)->suffix;
+
+	return container_of(suffix, struct mlx5_nta_sample_cached_group, entry)->group;
+}
+
 static struct mlx5_list_entry *
 mlx5_nta_sample_cached_mirror_create(void *cache_ctx, void *cb_ctx)
 {
@@ -175,9 +215,15 @@ static void
 mlx5_nta_sample_cached_mirror_remove(void *cache_ctx, struct mlx5_list_entry *entry)
 {
 	struct rte_eth_dev *dev = cache_ctx;
+	struct mlx5_priv *priv = dev->data->dev_private;
+	struct mlx5_nta_sample_ctx *ctx = priv->nta_sample_ctx;
+
 	struct mlx5_nta_sample_cached_mirror *obj =
 		container_of(entry, struct mlx5_nta_sample_cached_mirror, entry);
 	mlx5_hw_mirror_destroy(dev, obj->mirror);
+
+	mlx5_list_unregister(ctx->mirror_groups, obj->sample);
+	mlx5_list_unregister(ctx->mirror_groups, obj->suffix);
 	mlx5_free(obj);
 }
 
@@ -358,6 +404,18 @@ register_mirror_actions(struct rte_flow_action *actions, struct mlx5_list *cache
 	return mlx5_list_register(cache, &ctx);
 }
 
+void
+mlx5_nta_sample_mirror_entry_release(struct rte_eth_dev *dev,
+				     struct mlx5_sample_release_ctx *release)
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+	struct mlx5_nta_sample_ctx *ctx = priv->nta_sample_ctx;
+
+	mlx5_list_unregister(ctx->mirror_actions, release->mirror_entry);
+	release_cached_group(dev, release->sample_group);
+	mlx5_free(release);
+}
+
 static struct mlx5_list_entry *
 mlx5_create_nta_mirror(struct rte_eth_dev *dev,
 		       const struct rte_flow_attr *attr,
@@ -405,6 +463,33 @@ mlx5_create_nta_mirror(struct rte_eth_dev *dev,
 	return NULL;
 }
 
+static uint32_t
+generate_random_mask(uint32_t ratio)
+{
+	uint32_t i;
+	double goal = 1.0 / ratio;
+
+	/* Check if the ratio value is power of 2 */
+	if (rte_popcount32(ratio) == 1) {
+		for (i = 2; i < UINT32_WIDTH; i++) {
+			if (RTE_BIT32(i) == ratio)
+				return RTE_BIT32(i) - 1;
+		}
+	}
+
+	/*
+	 * Find the last power of 2 with ratio larger then the goal.
+	 */
+	for (i = 2; i < UINT32_WIDTH; i++) {
+		double res = 1.0 / RTE_BIT32(i);
+
+		if (res < goal)
+			return RTE_BIT32(i - 1) - 1;
+	}
+
+	return UINT32_MAX;
+}
+
 static void
 mlx5_nta_parse_sample_actions(const struct rte_flow_action *action,
 			      const struct rte_flow_action **sample_action,
@@ -430,24 +515,309 @@ mlx5_nta_parse_sample_actions(const struct rte_flow_action *action,
 	} while ((action++)->type != RTE_FLOW_ACTION_TYPE_END);
 }
 
+static bool
+validate_prefix_actions(const struct rte_flow_action *actions)
+{
+	uint32_t i = 0;
+
+	while (actions[i].type != RTE_FLOW_ACTION_TYPE_END)
+		i++;
+	return i < MLX5_HW_MAX_ACTS - 1;
+}
+
+static void
+action_append(struct rte_flow_action *actions, const struct rte_flow_action *last)
+{
+	uint32_t i = 0;
+
+	while (actions[i].type != RTE_FLOW_ACTION_TYPE_END)
+		i++;
+	actions[i] = *last;
+}
+
+static int
+create_mirror_aux_flows(struct rte_eth_dev *dev,
+			enum mlx5_flow_type type,
+			const struct rte_flow_attr *attr,
+			struct rte_flow_action *suffix_actions,
+			struct rte_flow_action *sample_actions,
+			struct mlx5_list_entry *mirror_entry,
+			struct mlx5_flow_head *flow_head,
+			struct rte_flow_error *error)
+{
+	const struct rte_flow_attr suffix_attr = {
+		.ingress = attr->ingress,
+		.egress = attr->egress,
+		.transfer = attr->transfer,
+		.group = mirror_entry_to_mirror_suffix_group(mirror_entry),
+	};
+	const struct rte_flow_attr sample_attr = {
+		.ingress = attr->ingress,
+		.egress = attr->egress,
+		.transfer = attr->transfer,
+		.group = mirror_entry_to_mirror_sample_group(mirror_entry),
+	};
+	const struct rte_flow_item secondary_pattern[1] = {
+		[0] = { .type = RTE_FLOW_ITEM_TYPE_END }
+	};
+	int ret, encap_idx, actions_num;
+	uint64_t suffix_action_flags, sample_action_flags;
+	const struct rte_flow_action *qrss_action = NULL, *mark_action = NULL;
+	struct rte_flow_hw *suffix_flow = NULL, *sample_flow = NULL;
+
+	suffix_action_flags = mlx5_flow_hw_action_flags_get(suffix_actions,
+						       &qrss_action, &mark_action,
+						       &encap_idx, &actions_num, error);
+	if (qrss_action != NULL && qrss_action->type == RTE_FLOW_ACTION_TYPE_RSS)
+		return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+			"RSS action is not supported in suffix sample action");
+	sample_action_flags = mlx5_flow_hw_action_flags_get(sample_actions,
+						       &qrss_action, &mark_action,
+						       &encap_idx, &actions_num, error);
+	if (qrss_action != NULL && qrss_action->type == RTE_FLOW_ACTION_TYPE_RSS)
+		return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+			"RSS action is not supported in sample action");
+	ret = flow_hw_create_flow(dev, type, &suffix_attr,
+				  secondary_pattern, suffix_actions,
+				  MLX5_FLOW_LAYER_OUTER_L2, suffix_action_flags,
+				  true, &suffix_flow, error);
+	if (ret != 0)
+		return ret;
+	ret = flow_hw_create_flow(dev, type, &sample_attr,
+				  secondary_pattern, sample_actions,
+				  MLX5_FLOW_LAYER_OUTER_L2, sample_action_flags,
+				  true, &sample_flow, error);
+	if (ret != 0) {
+		flow_hw_destroy(dev, suffix_flow);
+		return ret;
+	}
+	suffix_flow->nt2hws->chaned_flow = 1;
+	SLIST_INSERT_HEAD(flow_head, suffix_flow, nt2hws->next);
+	sample_flow->nt2hws->chaned_flow = 1;
+	SLIST_INSERT_HEAD(flow_head, sample_flow, nt2hws->next);
+	return 0;
+}
+
+static struct rte_flow_hw *
+create_sample_flow(struct rte_eth_dev *dev,
+		   enum mlx5_flow_type type,
+		   const struct rte_flow_attr *attr,
+		   uint32_t ratio,
+		   uint32_t sample_group,
+		   struct mlx5_list_entry *mirror_entry,
+		   struct rte_flow_error *error)
+{
+	struct rte_flow_hw *sample_flow = NULL;
+	uint32_t random_mask = generate_random_mask(ratio);
+	const struct rte_flow_attr sample_attr = {
+		.ingress = attr->ingress,
+		.egress = attr->egress,
+		.transfer = attr->transfer,
+		.group = sample_group,
+	};
+	const struct rte_flow_item sample_pattern[2] = {
+		[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_RANDOM,
+			.mask = &(struct rte_flow_item_random) {
+				.value = random_mask
+			},
+			.spec = &(struct rte_flow_item_random) {
+				.value = 1
+			},
+		},
+		[1] = { .type = RTE_FLOW_ITEM_TYPE_END }
+	};
+	const struct rte_flow_action sample_actions[2] = {
+		[0] = {
+			.type = (enum rte_flow_action_type)MLX5_RTE_FLOW_ACTION_TYPE_MIRROR,
+			.conf = mirror_entry_to_mirror_action(mirror_entry)
+		},
+		[1] = { .type = RTE_FLOW_ACTION_TYPE_END }
+	};
+
+	if (random_mask > UINT16_MAX)
+		return NULL;
+	flow_hw_create_flow(dev, type, &sample_attr, sample_pattern, sample_actions,
+			    0, 0, true, &sample_flow, error);
+	return sample_flow;
+}
+
+static int
+create_random_miss_actions(struct rte_eth_dev *dev,
+			   const struct rte_flow_attr *attr,
+			   uint32_t sample_group,
+			   struct mlx5_list_entry *mirror_entry,
+			   struct rte_flow_error *error)
+{
+	const struct rte_flow_group_attr sample_group_attr = {
+		.ingress = attr->ingress,
+		.egress = attr->egress,
+		.transfer = attr->transfer,
+	};
+	const struct rte_flow_action sample_miss_actions[2] = {
+		[0] = {
+			.type = RTE_FLOW_ACTION_TYPE_JUMP,
+			.conf = &(struct rte_flow_action_jump) {
+				.group = mirror_entry_to_mirror_suffix_group(mirror_entry)
+			}
+		},
+		[1] = { .type = RTE_FLOW_ACTION_TYPE_END }
+	};
+
+	return mlx5_flow_hw_group_set_miss_actions(dev, sample_group, &sample_group_attr,
+						   sample_miss_actions, error);
+}
+
+static void
+save_sample_release_cxt(struct mlx5_sample_release_ctx *release,
+			struct mlx5_flow_head *flow_head,
+			struct mlx5_list_entry *mirror_entry,
+			uint32_t sample_group)
+{
+	struct rte_flow_hw *var;
+
+	release->mirror_entry = mirror_entry;
+	release->sample_group = sample_group;
+
+	SLIST_FOREACH(var, flow_head, nt2hws->next) {
+		if (SLIST_NEXT(var, nt2hws->next) == NULL)
+			var->nt2hws->sample_release_ctx = release;
+	}
+}
+
+static struct rte_flow_hw *
+mlx5_nta_create_sample_flow(struct rte_eth_dev *dev,
+			     enum mlx5_flow_type type,
+			     const struct rte_flow_attr *attr,
+			     uint32_t sample_ratio,
+			     uint64_t item_flags, uint64_t action_flags,
+			     const struct rte_flow_item *pattern,
+			     struct rte_flow_action *prefix_actions,
+			     struct rte_flow_action *suffix_actions,
+			     struct rte_flow_action *sample_actions,
+			     struct mlx5_list_entry *mirror_entry,
+			     struct mlx5_flow_head *flow_head,
+			     struct rte_flow_error *error)
+{
+	int ret;
+	uint32_t sample_group = alloc_cached_group(dev);
+	struct rte_flow_hw *base_flow = NULL, *sample_flow = NULL;
+	struct mlx5_sample_release_ctx *release = NULL;
+
+	if (sample_group == 0)
+		return NULL;
+	release = mlx5_malloc(MLX5_MEM_ANY, sizeof(*release), 0, SOCKET_ID_ANY);
+	if (release == NULL) {
+		rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_ACTION,
+				   NULL, "Failed to allocate release context");
+		goto error;
+	}
+	ret = create_mirror_aux_flows(dev, type, attr,
+				      suffix_actions, sample_actions,
+				      mirror_entry, flow_head, error);
+	if (ret != 0)
+		goto error;
+	ret = create_random_miss_actions(dev, attr, sample_group,
+					       mirror_entry, error);
+	if (ret != 0)
+		goto error;
+	sample_flow = create_sample_flow(dev, type, attr, sample_ratio, sample_group,
+					 mirror_entry, error);
+	if (sample_flow == NULL)
+		goto error;
+	sample_flow->nt2hws->chaned_flow = 1;
+	SLIST_INSERT_HEAD(flow_head, sample_flow, nt2hws->next);
+	action_append(prefix_actions,
+		&(struct rte_flow_action) {
+			.type = RTE_FLOW_ACTION_TYPE_JUMP,
+			.conf = &(struct rte_flow_action_jump) { .group = sample_group }
+		});
+	ret = flow_hw_create_flow(dev, type, attr, pattern, prefix_actions,
+				  item_flags, action_flags, true, &base_flow, error);
+	if (ret != 0)
+		goto error;
+	SLIST_INSERT_HEAD(flow_head, base_flow, nt2hws->next);
+	save_sample_release_cxt(release, flow_head, mirror_entry, sample_group);
+	return base_flow;
+
+error:
+	if (!SLIST_EMPTY(flow_head))
+		release_chained_flows(dev, flow_head, type);
+	if (release)
+		mlx5_free(release);
+	if (sample_flow)
+		release_cached_group(dev, sample_group);
+	return NULL;
+}
+
+static struct rte_flow_hw *
+mlx5_nta_create_mirror_flow(struct rte_eth_dev *dev,
+			     enum mlx5_flow_type type,
+			     const struct rte_flow_attr *attr,
+			     uint64_t item_flags, uint64_t action_flags,
+			     const struct rte_flow_item *pattern,
+			     struct rte_flow_action *prefix_actions,
+			     struct rte_flow_action *suffix_actions,
+			     struct rte_flow_action *sample_actions,
+			     struct mlx5_list_entry *mirror_entry,
+			     struct mlx5_flow_head *flow_head,
+			     struct rte_flow_error *error)
+{
+	int ret;
+	struct rte_flow_hw *base_flow = NULL;
+	struct mlx5_sample_release_ctx *release = mlx5_malloc(MLX5_MEM_ANY, sizeof(*release),
+							      0, SOCKET_ID_ANY);
+
+	if (release == NULL) {
+		rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_ACTION,
+				   NULL, "Failed to allocate release context");
+		return NULL;
+	}
+	ret = create_mirror_aux_flows(dev, type, attr,
+				      suffix_actions, sample_actions,
+				      mirror_entry, flow_head, error);
+	if (ret != 0)
+		goto error;
+	action_append(prefix_actions,
+		&(struct rte_flow_action) {
+			.type = (enum rte_flow_action_type)MLX5_RTE_FLOW_ACTION_TYPE_MIRROR,
+			.conf = mirror_entry_to_mirror_action(mirror_entry)
+		});
+	ret = flow_hw_create_flow(dev, type, attr, pattern, prefix_actions,
+				  item_flags, action_flags,
+				  true, &base_flow, error);
+	if (ret != 0)
+		goto error;
+	SLIST_INSERT_HEAD(flow_head, base_flow, nt2hws->next);
+	save_sample_release_cxt(release, flow_head, mirror_entry, 0);
+	return base_flow;
+
+error:
+	if (!SLIST_EMPTY(flow_head))
+		release_chained_flows(dev, flow_head, type);
+	if (release)
+		mlx5_free(release);
+	return NULL;
+}
+
 struct rte_flow_hw *
 mlx5_nta_sample_flow_list_create(struct rte_eth_dev *dev,
-				 enum mlx5_flow_type type __rte_unused,
+				 enum mlx5_flow_type type,
 				 const struct rte_flow_attr *attr,
-				 const struct rte_flow_item pattern[] __rte_unused,
+				 const struct rte_flow_item pattern[],
 				 const struct rte_flow_action actions[],
-				 uint64_t item_flags __rte_unused,
-				 uint64_t action_flags __rte_unused,
+				 uint64_t item_flags, uint64_t action_flags,
 				 struct rte_flow_error *error)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
-	struct mlx5_mirror *mirror __rte_unused;
-	struct mlx5_list_entry *entry;
+	struct mlx5_list_entry *mirror_entry;
+	struct rte_flow_hw *flow = NULL;
 	const struct rte_flow_action *sample;
 	struct rte_flow_action *sample_actions;
 	const struct rte_flow_action_sample *sample_conf;
 	struct rte_flow_action prefix_actions[MLX5_HW_MAX_ACTS] = { 0 };
 	struct rte_flow_action suffix_actions[MLX5_HW_MAX_ACTS] = { 0 };
+	struct mlx5_flow_head flow_head = SLIST_HEAD_INITIALIZER(NULL);
 
 	if (priv->nta_sample_ctx == NULL) {
 		int rc = mlx5_init_nta_sample_context(dev);
@@ -458,13 +828,30 @@ mlx5_nta_sample_flow_list_create(struct rte_eth_dev *dev,
 		}
 	}
 	mlx5_nta_parse_sample_actions(actions, &sample, prefix_actions, suffix_actions);
+	if (!validate_prefix_actions(prefix_actions)) {
+		rte_flow_error_set(error, -EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
+				   NULL, "Too many actions");
+		return NULL;
+	}
 	sample_conf = (const struct rte_flow_action_sample *)sample->conf;
 	sample_actions = (struct rte_flow_action *)(uintptr_t)sample_conf->actions;
-	entry = mlx5_create_nta_mirror(dev, attr, sample_actions,
+	mirror_entry = mlx5_create_nta_mirror(dev, attr, sample_actions,
 					suffix_actions, error);
-	if (entry == NULL)
-		goto error;
-	mirror = container_of(entry, struct mlx5_nta_sample_cached_mirror, entry)->mirror;
-error:
-	return NULL;
+	if (mirror_entry == NULL)
+		return NULL;
+	if (sample_conf->ratio == 1) {
+		flow = mlx5_nta_create_mirror_flow(dev, type, attr, item_flags, action_flags,
+						   pattern, prefix_actions, suffix_actions,
+						   sample_actions, mirror_entry,
+						   &flow_head, error);
+	} else {
+		flow = mlx5_nta_create_sample_flow(dev, type, attr, sample_conf->ratio,
+						   item_flags, action_flags, pattern,
+						   prefix_actions, suffix_actions,
+						   sample_actions, mirror_entry,
+						   &flow_head, error);
+	}
+	if (flow == NULL)
+		mlx5_list_unregister(priv->nta_sample_ctx->mirror_actions, mirror_entry);
+	return flow;
 }
diff --git a/drivers/net/mlx5/mlx5_nta_sample.h b/drivers/net/mlx5/mlx5_nta_sample.h
index 07b7589a75..44d5b3b01a 100644
--- a/drivers/net/mlx5/mlx5_nta_sample.h
+++ b/drivers/net/mlx5/mlx5_nta_sample.h
@@ -19,4 +19,7 @@ mlx5_nta_sample_flow_list_create(struct rte_eth_dev *dev,
 void
 mlx5_nta_sample_context_free(struct rte_eth_dev *dev);
 
+void
+mlx5_nta_sample_mirror_entry_release(struct rte_eth_dev *dev,
+				     struct mlx5_sample_release_ctx *release);
 #endif /* RTE_PMD_MLX5_NTA_SAMPLE_H_ */
-- 
2.48.1


  parent reply	other threads:[~2025-06-26  8:01 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-06-17 13:39 [PATCH 1/5] net/mlx5: fix the table flags of mirror action Gregory Etelson
2025-06-17 13:39 ` [PATCH 2/5] net/mlx5: add mlx5_hw_create_mirror function Gregory Etelson
2025-06-17 13:39 ` [PATCH 3/5] net/mlx5: create utility functions for non-template sample action Gregory Etelson
2025-06-17 13:39 ` [PATCH 4/5] net/mlx5: add MLX5 mirror flow action Gregory Etelson
2025-06-17 13:39 ` [PATCH 5/5] net/mlx5: support non-template SAMPLE " Gregory Etelson
2025-06-26  7:59 ` [PATCH v2 0/4] net/mlx5: support non template " Gregory Etelson
2025-06-26  7:59   ` [PATCH v2 1/4] net/mlx5: fix the table flags of mirror action Gregory Etelson
2025-06-26  7:59   ` [PATCH v2 2/4] net/mlx5: add a stand alone function for mirror creation Gregory Etelson
2025-06-26  7:59   ` [PATCH v2 3/4] net/mlx5: add functions for non template sample action Gregory Etelson
2025-06-26  7:59   ` Gregory Etelson [this message]
2025-06-26 15:28   ` [PATCH v2 0/4] net/mlx5: support non template SAMPLE flow action Thomas Monjalon

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250626075945.537256-5-getelson@nvidia.com \
    --to=getelson@nvidia.com \
    --cc=bingz@nvidia.com \
    --cc=dev@dpdk.org \
    --cc=dsosnowski@nvidia.com \
    --cc=matan@nvidia.com \
    --cc=mkashani@nvidia.com \
    --cc=orika@nvidia.com \
    --cc=rasland@nvidia.com \
    --cc=suanmingm@nvidia.com \
    --cc=viacheslavo@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).