[v6,10/12] net/mlx5: update translate function for mirror

Message ID 1599634114-148013-11-git-send-email-jiaweiw@nvidia.com (mailing list archive)
State Superseded, archived
Delegated to: Ferruh Yigit
Headers
Series support the flow-based traffic sampling |

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

Jiawei Wang Sept. 9, 2020, 6:48 a.m. UTC
  Translate the attribute of sample action that include sample ratio
and sub actions list.
PMD will check the destination action number in current flow,
if found multiple destination action, then create the new group
rdma action for each destination, these group rdma actions will
be used for creating the multiple destination DR action and add
it into the flow.

Signed-off-by: Jiawei Wang <jiaweiw@nvidia.com>
---
 drivers/net/mlx5/mlx5.c         |  11 +
 drivers/net/mlx5/mlx5.h         |   2 +
 drivers/net/mlx5/mlx5_flow.h    |  24 +++
 drivers/net/mlx5/mlx5_flow_dv.c | 459 +++++++++++++++++++++++++++++++++++++---
 4 files changed, 467 insertions(+), 29 deletions(-)
  

Patch

diff --git a/drivers/net/mlx5/mlx5.c b/drivers/net/mlx5/mlx5.c
index 8d5f517..adb27de 100644
--- a/drivers/net/mlx5/mlx5.c
+++ b/drivers/net/mlx5/mlx5.c
@@ -252,6 +252,17 @@  static LIST_HEAD(, mlx5_dev_ctx_shared) mlx5_dev_ctx_list =
 		.free = mlx5_free,
 		.type = "mlx5_sample_ipool",
 	},
+	{
+		.size = sizeof(struct mlx5_flow_dv_multi_dest_resource),
+		.trunk_size = 64,
+		.grow_trunk = 3,
+		.grow_shift = 2,
+		.need_lock = 0,
+		.release_mem_en = 1,
+		.malloc = mlx5_malloc,
+		.free = mlx5_free,
+		.type = "mlx5_multi_dest_ipool",
+	},
 #endif
 	{
 		.size = sizeof(struct mlx5_flow_meter),
diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h
index 9115391..4bd0cdb 100644
--- a/drivers/net/mlx5/mlx5.h
+++ b/drivers/net/mlx5/mlx5.h
@@ -40,6 +40,7 @@  enum mlx5_ipool_index {
 	MLX5_IPOOL_PORT_ID, /* Pool for port id resource. */
 	MLX5_IPOOL_JUMP, /* Pool for jump resource. */
 	MLX5_IPOOL_SAMPLE, /* Pool for sample resource. */
+	MLX5_IPOOL_MULTI_DEST, /* Pool for multi-dest resource. */
 #endif
 	MLX5_IPOOL_MTR, /* Pool for meter resource. */
 	MLX5_IPOOL_MCP, /* Pool for metadata resource. */
@@ -643,6 +644,7 @@  struct mlx5_dev_ctx_shared {
 	uint32_t port_id_action_list; /* List of port ID actions. */
 	uint32_t push_vlan_action_list; /* List of push VLAN actions. */
 	uint32_t sample_action_list; /* List of sample actions. */
+	uint32_t multi_dest_list; /* List of multi_dest. */
 	struct mlx5_flow_counter_mng cmng; /* Counters management structure. */
 	struct mlx5_flow_default_miss_resource default_miss;
 	/* Default miss action resource structure. */
diff --git a/drivers/net/mlx5/mlx5_flow.h b/drivers/net/mlx5/mlx5_flow.h
index 50e230f..33dd49b 100644
--- a/drivers/net/mlx5/mlx5_flow.h
+++ b/drivers/net/mlx5/mlx5_flow.h
@@ -521,6 +521,8 @@  struct mlx5_flow_sub_actions_list {
 	void *dr_queue_action;
 	void *dr_tag_action;
 	void *dr_cnt_action;
+	void *dr_port_id_action;
+	void *dr_encap_action;
 };
 
 /* Sample sub-actions resource list. */
@@ -528,6 +530,8 @@  struct mlx5_flow_sub_actions_idx {
 	uint32_t rix_hrxq; /**< Hash Rx queue object index. */
 	uint32_t rix_tag; /**< Index to the tag action. */
 	uint32_t cnt;
+	uint32_t rix_port_id_action; /**< Index to port ID action resource. */
+	uint32_t rix_encap_decap; /**< Index to encap/decap resource. */
 };
 
 /* Sample action resource structure. */
@@ -547,6 +551,22 @@  struct mlx5_flow_dv_sample_resource {
 	/**< Action resources. */
 };
 
+#define MLX5_MAX_GROUP_NUM	2
+
+/* Multi-dest action resource structure. */
+struct mlx5_flow_dv_multi_dest_resource {
+	ILIST_ENTRY(uint32_t)next; /**< Pointer to next element. */
+	rte_atomic32_t refcnt; /**< Reference counter. */
+	uint8_t ft_type; /** Flow Table Type */
+	uint8_t num_of_group; /**< Number of group actions. */
+	void *action; /**< Pointer to the rdma core action. */
+	void *group_action[MLX5_MAX_GROUP_NUM];
+	struct mlx5_flow_sub_actions_idx sample_idx[MLX5_MAX_GROUP_NUM];
+	/**< Action index resources. */
+	struct mlx5_flow_sub_actions_list sample_act[MLX5_MAX_GROUP_NUM];
+	/**< Action resources. */
+};
+
 /* Verbs specification header. */
 struct ibv_spec_header {
 	enum ibv_flow_spec_type type;
@@ -581,6 +601,8 @@  struct mlx5_flow_handle_dv {
 	/**< Index to the tag action. */
 	uint32_t rix_sample;
 	/**< Index to sample action resource in cache. */
+	uint32_t rix_multi_dest;
+	/**< Index to multi-dest resource in cache. */
 } __rte_packed;
 
 /** Device flow handle structure: used both for creating & destroying. */
@@ -641,6 +663,8 @@  struct mlx5_flow_dv_workspace {
 	/**< Holds the value that the packet is compared to. */
 	struct mlx5_flow_dv_sample_resource *sample_res;
 	/**< Pointer to the sample action resource. */
+	struct mlx5_flow_dv_multi_dest_resource *multi_dest_res;
+	/**< Pointer to the multi_dest resource. */
 };
 
 /*
diff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c
index 19ad9fc..cf85780 100644
--- a/drivers/net/mlx5/mlx5_flow_dv.c
+++ b/drivers/net/mlx5/mlx5_flow_dv.c
@@ -80,6 +80,10 @@ 
 flow_dv_encap_decap_resource_release(struct rte_eth_dev *dev,
 				      uint32_t encap_decap_idx);
 
+static int
+flow_dv_port_id_action_resource_release(struct rte_eth_dev *dev,
+					uint32_t port_id);
+
 /**
  * Initialize flow attributes structure according to flow items' types.
  *
@@ -8304,9 +8308,9 @@  struct field_modify_info modify_tcp[] = {
  */
 static struct mlx5_hrxq *
 flow_dv_handle_rx_queue(struct rte_eth_dev *dev,
-			  struct mlx5_flow *dev_flow,
-			  struct mlx5_flow_rss_desc *rss_desc,
-			  uint32_t *hrxq_idx)
+			struct mlx5_flow *dev_flow,
+			struct mlx5_flow_rss_desc *rss_desc,
+			uint32_t *hrxq_idx)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
 	struct mlx5_flow_handle *dh = dev_flow->handle;
@@ -8314,19 +8318,19 @@  struct field_modify_info modify_tcp[] = {
 
 	MLX5_ASSERT(rss_desc->queue_num);
 	*hrxq_idx = mlx5_hrxq_get(dev, rss_desc->key,
-				 MLX5_RSS_HASH_KEY_LEN,
-				 dev_flow->hash_fields,
-				 rss_desc->queue,
-				 rss_desc->queue_num);
+				  MLX5_RSS_HASH_KEY_LEN,
+				  dev_flow->hash_fields,
+				  rss_desc->queue,
+				  rss_desc->queue_num);
 	if (!*hrxq_idx) {
 		*hrxq_idx = mlx5_hrxq_new
 				(dev, rss_desc->key,
-				MLX5_RSS_HASH_KEY_LEN,
-				dev_flow->hash_fields,
-				rss_desc->queue,
-				rss_desc->queue_num,
-				!!(dh->layers &
-				MLX5_FLOW_LAYER_TUNNEL));
+				 MLX5_RSS_HASH_KEY_LEN,
+				 dev_flow->hash_fields,
+				 rss_desc->queue,
+				 rss_desc->queue_num,
+				 !!(dh->layers &
+				 MLX5_FLOW_LAYER_TUNNEL));
 		if (!*hrxq_idx)
 			return NULL;
 	}
@@ -8480,6 +8484,146 @@  struct field_modify_info modify_tcp[] = {
 }
 
 /**
+ * Find existing multi_dest resource or create and register a new one.
+ *
+ * @param[in, out] dev
+ *   Pointer to rte_eth_dev structure.
+ * @param[in] attr
+ *   Attributes of flow that includes this item.
+ * @param[in] resource
+ *   Pointer to multi_dest resource.
+ * @param[in] sample_actions
+ *   Pointer to sample path actions list.
+ * @param[in] normal_actions
+ *   Pointer to normal path actions list.
+ * @parm[in, out] dev_flow
+ *   Pointer to the dev_flow.
+ * @param[out] error
+ *   pointer to error structure.
+ *
+ * @return
+ *   0 on success otherwise -errno and errno is set.
+ */
+static int
+flow_dv_multi_dest_resource_register(struct rte_eth_dev *dev,
+			 const struct rte_flow_attr *attr,
+			 struct mlx5_flow_dv_multi_dest_resource *resource,
+			 void **sample_actions,
+			 void **normal_actions,
+			 struct mlx5_flow *dev_flow,
+			 struct rte_flow_error *error)
+{
+	struct mlx5_flow_dv_multi_dest_resource *cache_resource;
+	struct mlx5_priv *priv = dev->data->dev_private;
+	struct mlx5_dev_ctx_shared *sh = priv->sh;
+	struct mlx5_flow_sub_actions_list *sample_act;
+	struct mlx5dv_dr_domain *domain;
+	uint32_t idx = 0;
+
+	/* Lookup a matching resource from cache. */
+	ILIST_FOREACH(sh->ipool[MLX5_IPOOL_MULTI_DEST],
+		      sh->multi_dest_list,
+		      idx, cache_resource, next) {
+		if (resource->num_of_group == cache_resource->num_of_group &&
+		    resource->ft_type == cache_resource->ft_type &&
+		    !memcmp((void *)cache_resource->sample_act,
+			    (void *)resource->sample_act,
+			   (resource->num_of_group *
+			   sizeof(struct mlx5_flow_sub_actions_list)))) {
+			DRV_LOG(DEBUG, "sample resource %p: refcnt %d++",
+				(void *)cache_resource,
+				rte_atomic32_read(&cache_resource->refcnt));
+			rte_atomic32_inc(&cache_resource->refcnt);
+			dev_flow->handle->dvh.rix_multi_dest = idx;
+			dev_flow->dv.multi_dest_res = cache_resource;
+			return 0;
+		}
+	}
+	/* Register new sample resource. */
+	cache_resource = mlx5_ipool_zmalloc(sh->ipool[MLX5_IPOOL_MULTI_DEST],
+				       &dev_flow->handle->dvh.rix_multi_dest);
+	if (!cache_resource)
+		return rte_flow_error_set(error, ENOMEM,
+					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+					  NULL,
+					  "cannot allocate resource memory");
+	*cache_resource = *resource;
+	/* create the groups actions */
+	for (idx = 0; idx < resource->num_of_group; idx++) {
+		sample_act = &resource->sample_act[idx];
+		uint32_t actions_num = sample_act->actions_num;
+		void **dr_actions = (idx == 0) ? normal_actions :
+						 sample_actions;
+		cache_resource->group_action[idx] =
+			mlx5_glue->dr_create_flow_action_group(actions_num,
+					dr_actions);
+		if (!cache_resource->group_action[idx]) {
+			rte_flow_error_set(error, ENOMEM,
+					   RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+					   NULL,
+					   "cannot create group "
+					   "action");
+			goto error;
+		}
+	}
+	if (attr->transfer)
+		domain = sh->fdb_domain;
+	else if (attr->ingress)
+		domain = sh->rx_domain;
+	else
+		domain = sh->tx_domain;
+	/* create a multi-dest actioin */
+	cache_resource->action =
+		mlx5_glue->dr_create_flow_action_multi_dest
+			(domain,
+			cache_resource->num_of_group,
+			&cache_resource->group_action[0]);
+	if (!cache_resource->action) {
+		rte_flow_error_set(error, ENOMEM,
+				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				  NULL,
+				  "cannot create multi-dest "
+				  "action");
+		goto error;
+	}
+	rte_atomic32_init(&cache_resource->refcnt);
+	rte_atomic32_inc(&cache_resource->refcnt);
+	ILIST_INSERT(sh->ipool[MLX5_IPOOL_MULTI_DEST],
+		     &sh->multi_dest_list,
+		     dev_flow->handle->dvh.rix_multi_dest, cache_resource,
+		     next);
+	dev_flow->dv.multi_dest_res = cache_resource;
+	DRV_LOG(DEBUG, "new sample resource %p: refcnt %d++",
+		(void *)cache_resource,
+		rte_atomic32_read(&cache_resource->refcnt));
+	return 0;
+error:
+	for (idx = 0; idx < resource->num_of_group; idx++) {
+		struct mlx5_flow_sub_actions_idx *act_res =
+					&cache_resource->sample_idx[idx];
+		if (cache_resource->group_action[idx])
+			claim_zero(mlx5_glue->destroy_flow_action
+					(cache_resource->group_action[idx]));
+		if (act_res->rix_hrxq &&
+		    !mlx5_hrxq_release(dev,
+				act_res->rix_hrxq))
+			act_res->rix_hrxq = 0;
+		if (act_res->rix_encap_decap &&
+			flow_dv_encap_decap_resource_release(dev,
+				act_res->rix_encap_decap))
+			act_res->rix_encap_decap = 0;
+		if (act_res->rix_port_id_action &&
+			!flow_dv_port_id_action_resource_release(dev,
+				act_res->rix_port_id_action))
+			act_res->rix_port_id_action = 0;
+	}
+	mlx5_ipool_free(sh->ipool[MLX5_IPOOL_MULTI_DEST],
+				dev_flow->handle->dvh.rix_multi_dest);
+	dev_flow->handle->dvh.rix_multi_dest = 0;
+	return -rte_errno;
+}
+
+/**
  * Convert Sample action to DV specification.
  *
  * @param[in] dev
@@ -8490,6 +8634,8 @@  struct field_modify_info modify_tcp[] = {
  *   Pointer to the mlx5_flow.
  * @param[in] attr
  *   Pointer to the flow attributes.
+ * @param[in, out] num_of_dest
+ *   Pointer to the num of destination.
  * @param[in, out] sample_actions
  *   Pointer to sample actions list.
  * @param[in, out] res
@@ -8505,6 +8651,7 @@  struct field_modify_info modify_tcp[] = {
 				const struct rte_flow_action *action,
 				struct mlx5_flow *dev_flow,
 				const struct rte_flow_attr *attr,
+				uint32_t *num_of_dest,
 				void **sample_actions,
 				struct mlx5_flow_dv_sample_resource *res,
 				struct rte_flow_error *error)
@@ -8554,6 +8701,7 @@  struct field_modify_info modify_tcp[] = {
 			sample_idx->rix_hrxq = hrxq_idx;
 			sample_actions[sample_act->actions_num++] =
 						hrxq->action;
+			(*num_of_dest)++;
 			action_flags |= MLX5_FLOW_ACTION_QUEUE;
 			if (action_flags & MLX5_FLOW_ACTION_MARK)
 				dev_flow->handle->rix_hrxq = hrxq_idx;
@@ -8566,6 +8714,7 @@  struct field_modify_info modify_tcp[] = {
 			uint32_t tag_be = mlx5_flow_mark_set
 				(((const struct rte_flow_action_mark *)
 				(sub_actions->conf))->id);
+
 			dev_flow->handle->mark = 1;
 			pre_rix = dev_flow->handle->dvh.rix_tag;
 			/* Save the mark resource before sample */
@@ -8608,6 +8757,56 @@  struct field_modify_info modify_tcp[] = {
 			action_flags |= MLX5_FLOW_ACTION_COUNT;
 			break;
 		}
+		case RTE_FLOW_ACTION_TYPE_PORT_ID:
+		{
+			struct mlx5_flow_dv_port_id_action_resource
+					port_id_resource;
+			uint32_t port_id = 0;
+
+			memset(&port_id_resource, 0, sizeof(port_id_resource));
+			/* Save the port id resource before sample */
+			pre_rix = dev_flow->handle->rix_port_id_action;
+			pre_r = dev_flow->dv.port_id_action;
+			if (flow_dv_translate_action_port_id(dev, sub_actions,
+							     &port_id, error))
+				return -rte_errno;
+			port_id_resource.port_id = port_id;
+			if (flow_dv_port_id_action_resource_register
+			    (dev, &port_id_resource, dev_flow, error))
+				return -rte_errno;
+			sample_act->dr_port_id_action =
+				dev_flow->dv.port_id_action->action;
+			sample_idx->rix_port_id_action =
+				dev_flow->handle->rix_port_id_action;
+			sample_actions[sample_act->actions_num++] =
+						sample_act->dr_port_id_action;
+			/* Recover the port id resource after sample */
+			dev_flow->dv.port_id_action = pre_r;
+			dev_flow->handle->rix_port_id_action = pre_rix;
+			(*num_of_dest)++;
+			action_flags |= MLX5_FLOW_ACTION_PORT_ID;
+			break;
+		}
+		case RTE_FLOW_ACTION_TYPE_RAW_ENCAP:
+			/* Save the encap resource before sample */
+			pre_rix = dev_flow->handle->dvh.rix_encap_decap;
+			pre_r = dev_flow->dv.encap_decap;
+			if (flow_dv_create_action_l2_encap(dev, sub_actions,
+							   dev_flow,
+							   attr->transfer,
+							   error))
+				return -rte_errno;
+			sample_act->dr_encap_action =
+				dev_flow->dv.encap_decap->action;
+			sample_idx->rix_encap_decap =
+				dev_flow->handle->dvh.rix_encap_decap;
+			sample_actions[sample_act->actions_num++] =
+						sample_act->dr_encap_action;
+			/* Recover the encap resource after sample */
+			dev_flow->dv.encap_decap = pre_r;
+			dev_flow->handle->dvh.rix_encap_decap = pre_rix;
+			action_flags |= MLX5_FLOW_ACTION_ENCAP;
+			break;
 		default:
 			return rte_flow_error_set(error, EINVAL,
 				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
@@ -8641,10 +8840,16 @@  struct field_modify_info modify_tcp[] = {
  *   Pointer to the mlx5_flow.
  * @param[in] attr
  *   Pointer to the flow attributes.
+ * @param[in] num_of_dest
+ *   The num of destination.
  * @param[in, out] res
  *   Pointer to sample resource.
+ * @param[in, out] mdest_res
+ *   Pointer to multi-dest resource.
  * @param[in] sample_actions
  *   Pointer to sample path actions list.
+ * @param[in] action_flags
+ *   Holds the actions detected until now.
  * @param[out] error
  *   Pointer to the error structure.
  *
@@ -8653,17 +8858,84 @@  struct field_modify_info modify_tcp[] = {
  */
 static int
 flow_dv_create_action_sample(struct rte_eth_dev *dev,
-				struct mlx5_flow *dev_flow,
-				const struct rte_flow_attr *attr,
-				struct mlx5_flow_dv_sample_resource *res,
-				void **sample_actions,
-				struct rte_flow_error *error)
+			     struct mlx5_flow *dev_flow,
+			     const struct rte_flow_attr *attr,
+			     uint32_t num_of_dest,
+			     struct mlx5_flow_dv_sample_resource *res,
+			     struct mlx5_flow_dv_multi_dest_resource *mdest_res,
+			     void **sample_actions,
+			     uint64_t action_flags,
+			     struct rte_flow_error *error)
 {
-	if (flow_dv_sample_resource_register(dev, attr, res, dev_flow,
-						sample_actions, error))
-		return rte_flow_error_set(error, EINVAL,
-					  RTE_FLOW_ERROR_TYPE_ACTION,
-					  NULL, "can't create sample action");
+	struct mlx5_priv *priv = dev->data->dev_private;
+	struct mlx5_flow_sub_actions_list *sample_act =
+					&mdest_res->sample_act[0];
+	void *normal_actions[MLX5_DV_MAX_NUMBER_OF_ACTIONS] = {0};
+	struct mlx5_flow_rss_desc *rss_desc = &((struct mlx5_flow_rss_desc *)
+					      priv->rss_desc)
+					      [!!priv->flow_nested_idx];
+	uint32_t normal_idx = 0;
+	struct mlx5_hrxq *hrxq;
+	uint32_t hrxq_idx;
+
+	if (num_of_dest > 1) {
+		if (sample_act->action_flags & MLX5_FLOW_ACTION_QUEUE) {
+			/* Handle QP action for mirroring */
+			hrxq = flow_dv_handle_rx_queue(dev, dev_flow,
+						       rss_desc, &hrxq_idx);
+			if (!hrxq)
+				return rte_flow_error_set
+				     (error, rte_errno,
+				      RTE_FLOW_ERROR_TYPE_ACTION,
+				      NULL,
+				      "cannot create rx queue");
+			normal_actions[normal_idx++] = hrxq->action;
+			mdest_res->sample_idx[0].rix_hrxq = hrxq_idx;
+			sample_act->dr_queue_action = hrxq->action;
+			if (action_flags & MLX5_FLOW_ACTION_MARK)
+				dev_flow->handle->rix_hrxq = hrxq_idx;
+			dev_flow->handle->fate_action = MLX5_FLOW_FATE_QUEUE;
+		}
+		if (sample_act->action_flags & MLX5_FLOW_ACTION_ENCAP) {
+			normal_actions[normal_idx++] =
+					dev_flow->dv.encap_decap->action;
+			mdest_res->sample_idx[0].rix_encap_decap =
+					dev_flow->handle->dvh.rix_encap_decap;
+			sample_act->dr_encap_action =
+					dev_flow->dv.encap_decap->action;
+		}
+		if (sample_act->action_flags & MLX5_FLOW_ACTION_PORT_ID) {
+			normal_actions[normal_idx++] =
+				dev_flow->dv.port_id_action->action;
+			mdest_res->sample_idx[0].rix_port_id_action =
+				dev_flow->handle->rix_port_id_action;
+			sample_act->dr_port_id_action =
+				dev_flow->dv.port_id_action->action;
+		}
+		sample_act->actions_num = normal_idx;
+		/* update sample action resource into multi-dest index 1 */
+		mdest_res->ft_type = res->ft_type;
+		memcpy(&mdest_res->sample_idx[1], &res->sample_idx,
+				sizeof(struct mlx5_flow_sub_actions_idx));
+		memcpy(&mdest_res->sample_act[1], &res->sample_act,
+				sizeof(struct mlx5_flow_sub_actions_list));
+		mdest_res->num_of_group = num_of_dest;
+		if (flow_dv_multi_dest_resource_register(dev, attr, mdest_res,
+							 sample_actions,
+							 normal_actions,
+							 dev_flow, error))
+			return rte_flow_error_set(error, EINVAL,
+						  RTE_FLOW_ERROR_TYPE_ACTION,
+						  NULL, "can't create sample "
+						  "action");
+	} else {
+		if (flow_dv_sample_resource_register(dev, attr, res, dev_flow,
+						     sample_actions, error))
+			return rte_flow_error_set(error, EINVAL,
+						  RTE_FLOW_ERROR_TYPE_ACTION,
+						  NULL,
+						  "can't create sample action");
+	}
 	return 0;
 }
 
@@ -8731,15 +9003,21 @@  struct field_modify_info modify_tcp[] = {
 	void *match_value = dev_flow->dv.value.buf;
 	uint8_t next_protocol = 0xff;
 	struct rte_vlan_hdr vlan = { 0 };
+	struct mlx5_flow_dv_multi_dest_resource mdest_res;
 	struct mlx5_flow_dv_sample_resource sample_res;
 	void *sample_actions[MLX5_DV_MAX_NUMBER_OF_ACTIONS] = {0};
+	struct mlx5_flow_sub_actions_list *sample_act;
 	uint32_t sample_act_pos = UINT32_MAX;
+	uint32_t num_of_dest = 0;
+	int tmp_actions_n = 0;
 	uint32_t table;
 	int ret = 0;
 
+	memset(&mdest_res, 0, sizeof(struct mlx5_flow_dv_multi_dest_resource));
 	memset(&sample_res, 0, sizeof(struct mlx5_flow_dv_sample_resource));
 	mhdr_res->ft_type = attr->egress ? MLX5DV_FLOW_TABLE_TYPE_NIC_TX :
 					   MLX5DV_FLOW_TABLE_TYPE_NIC_RX;
+	sample_act = &mdest_res.sample_act[0];
 	ret = mlx5_flow_group_to_table(attr, dev_flow->external, attr->group,
 				       !!priv->fdb_def_rule, &table, error);
 	if (ret)
@@ -8786,6 +9064,8 @@  struct field_modify_info modify_tcp[] = {
 					dev_flow->dv.port_id_action->action;
 			action_flags |= MLX5_FLOW_ACTION_PORT_ID;
 			dev_flow->handle->fate_action = MLX5_FLOW_FATE_PORT_ID;
+			sample_act->action_flags |= MLX5_FLOW_ACTION_PORT_ID;
+			num_of_dest++;
 			break;
 		case RTE_FLOW_ACTION_TYPE_FLAG:
 			action_flags |= MLX5_FLOW_ACTION_FLAG;
@@ -8871,6 +9151,8 @@  struct field_modify_info modify_tcp[] = {
 			rss_desc->queue[0] = queue->index;
 			action_flags |= MLX5_FLOW_ACTION_QUEUE;
 			dev_flow->handle->fate_action = MLX5_FLOW_FATE_QUEUE;
+			sample_act->action_flags |= MLX5_FLOW_ACTION_QUEUE;
+			num_of_dest++;
 			break;
 		case RTE_FLOW_ACTION_TYPE_RSS:
 			rss = actions->conf;
@@ -8958,6 +9240,9 @@  struct field_modify_info modify_tcp[] = {
 			dev_flow->dv.actions[actions_n++] =
 					dev_flow->dv.encap_decap->action;
 			action_flags |= MLX5_FLOW_ACTION_ENCAP;
+			if (action_flags & MLX5_FLOW_ACTION_SAMPLE)
+				sample_act->action_flags |=
+							MLX5_FLOW_ACTION_ENCAP;
 			break;
 		case RTE_FLOW_ACTION_TYPE_VXLAN_DECAP:
 		case RTE_FLOW_ACTION_TYPE_NVGRE_DECAP:
@@ -8987,6 +9272,9 @@  struct field_modify_info modify_tcp[] = {
 					dev_flow->dv.encap_decap->action;
 			}
 			action_flags |= MLX5_FLOW_ACTION_ENCAP;
+			if (action_flags & MLX5_FLOW_ACTION_SAMPLE)
+				sample_act->action_flags |=
+							MLX5_FLOW_ACTION_ENCAP;
 			break;
 		case RTE_FLOW_ACTION_TYPE_RAW_DECAP:
 			while ((++action)->type == RTE_FLOW_ACTION_TYPE_VOID)
@@ -9179,6 +9467,7 @@  struct field_modify_info modify_tcp[] = {
 			ret = flow_dv_translate_action_sample(dev,
 							      actions,
 							      dev_flow, attr,
+							      &num_of_dest,
 							      sample_actions,
 							      &sample_res,
 							      error);
@@ -9186,6 +9475,11 @@  struct field_modify_info modify_tcp[] = {
 				return ret;
 			actions_n++;
 			action_flags |= MLX5_FLOW_ACTION_SAMPLE;
+			/* put encap action into group if work with port id */
+			if ((action_flags & MLX5_FLOW_ACTION_ENCAP) &&
+			    (action_flags & MLX5_FLOW_ACTION_PORT_ID))
+				sample_act->action_flags |=
+							MLX5_FLOW_ACTION_ENCAP;
 			break;
 		case RTE_FLOW_ACTION_TYPE_END:
 			actions_end = true;
@@ -9209,15 +9503,19 @@  struct field_modify_info modify_tcp[] = {
 						NULL,
 						"cannot create counter"
 						" object.");
-				dev_flow->dv.actions[actions_n++] =
+				dev_flow->dv.actions[actions_n] =
 					  (flow_dv_counter_get_by_idx(dev,
 					  flow->counter, NULL))->action;
+				actions_n++;
 			}
 			if (action_flags & MLX5_FLOW_ACTION_SAMPLE) {
 				ret = flow_dv_create_action_sample(dev,
 							  dev_flow, attr,
+							  num_of_dest,
 							  &sample_res,
+							  &mdest_res,
 							  sample_actions,
+							  action_flags,
 							  error);
 				if (ret < 0)
 					return rte_flow_error_set
@@ -9225,8 +9523,13 @@  struct field_modify_info modify_tcp[] = {
 						RTE_FLOW_ERROR_TYPE_ACTION,
 						NULL,
 						"cannot create sample action");
-				dev_flow->dv.actions[sample_act_pos] =
+				if (num_of_dest > 1) {
+					dev_flow->dv.actions[sample_act_pos] =
+					dev_flow->dv.multi_dest_res->action;
+				} else {
+					dev_flow->dv.actions[sample_act_pos] =
 					dev_flow->dv.sample_res->verbs_action;
+				}
 			}
 			break;
 		default:
@@ -9236,6 +9539,31 @@  struct field_modify_info modify_tcp[] = {
 		    modify_action_position == UINT32_MAX)
 			modify_action_position = actions_n++;
 	}
+	/*
+	 * For multiple destination (sample action with ratio=1), the encap
+	 * action and port id action will be combined into group action.
+	 * So need remove the original these actions in the flow and only
+	 * use the sample action instead of.
+	 */
+	if (num_of_dest > 1 && sample_act->dr_port_id_action) {
+		int i;
+		void *temp_actions[MLX5_DV_MAX_NUMBER_OF_ACTIONS] = {0};
+
+		for (i = 0; i < actions_n; i++) {
+			if ((sample_act->dr_encap_action &&
+				sample_act->dr_encap_action ==
+				dev_flow->dv.actions[i]) ||
+				(sample_act->dr_port_id_action &&
+				sample_act->dr_port_id_action ==
+				dev_flow->dv.actions[i]))
+				continue;
+			temp_actions[tmp_actions_n++] = dev_flow->dv.actions[i];
+		}
+		memcpy((void *)dev_flow->dv.actions,
+				(void *)temp_actions,
+				tmp_actions_n * sizeof(void *));
+		actions_n = tmp_actions_n;
+	}
 	dev_flow->dv.actions_n = actions_n;
 	dev_flow->act_flags = action_flags;
 	for (; items->type != RTE_FLOW_ITEM_TYPE_END; items++) {
@@ -9540,7 +9868,7 @@  struct field_modify_info modify_tcp[] = {
 				dv->actions[n++] = drop_hrxq->action;
 			}
 		} else if (dh->fate_action == MLX5_FLOW_FATE_QUEUE &&
-			   !dv_h->rix_sample) {
+			   !dv_h->rix_sample && !dv_h->rix_multi_dest) {
 			struct mlx5_hrxq *hrxq;
 			uint32_t hrxq_idx;
 			struct mlx5_flow_rss_desc *rss_desc =
@@ -9827,11 +10155,11 @@  struct field_modify_info modify_tcp[] = {
  */
 static int
 flow_dv_port_id_action_resource_release(struct rte_eth_dev *dev,
-					struct mlx5_flow_handle *handle)
+					uint32_t port_id)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
 	struct mlx5_flow_dv_port_id_action_resource *cache_resource;
-	uint32_t idx = handle->rix_port_id_action;
+	uint32_t idx = port_id;
 
 	cache_resource = mlx5_ipool_get(priv->sh->ipool[MLX5_IPOOL_PORT_ID],
 					idx);
@@ -9921,7 +10249,8 @@  struct field_modify_info modify_tcp[] = {
 		flow_dv_jump_tbl_resource_release(dev, handle);
 		break;
 	case MLX5_FLOW_FATE_PORT_ID:
-		flow_dv_port_id_action_resource_release(dev, handle);
+		flow_dv_port_id_action_resource_release(dev,
+				handle->rix_port_id_action);
 		break;
 	case MLX5_FLOW_FATE_DEFAULT_MISS:
 		flow_dv_default_miss_resource_release(dev);
@@ -9999,6 +10328,76 @@  struct field_modify_info modify_tcp[] = {
 }
 
 /**
+ * Release an multi-dest resource.
+ *
+ * @param dev
+ *   Pointer to Ethernet device.
+ * @param handle
+ *   Pointer to mlx5_flow_handle.
+ *
+ * @return
+ *   1 while a reference on it exists, 0 when freed.
+ */
+static int
+flow_dv_multi_dest_resource_release(struct rte_eth_dev *dev,
+				     struct mlx5_flow_handle *handle)
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+	struct mlx5_flow_dv_multi_dest_resource *cache_resource;
+	struct mlx5_flow_sub_actions_idx *mdest_act_res;
+	uint32_t idx = handle->dvh.rix_multi_dest;
+	uint32_t i = 0;
+
+	cache_resource = mlx5_ipool_get(priv->sh->ipool[MLX5_IPOOL_MULTI_DEST],
+			 idx);
+	if (!cache_resource)
+		return 0;
+	MLX5_ASSERT(cache_resource->action);
+	DRV_LOG(DEBUG, "multi-dest resource %p: refcnt %d--",
+		(void *)cache_resource,
+		rte_atomic32_read(&cache_resource->refcnt));
+	if (rte_atomic32_dec_and_test(&cache_resource->refcnt)) {
+		if (cache_resource->action)
+			claim_zero(mlx5_glue->destroy_flow_action
+						(cache_resource->action));
+		for (; i < cache_resource->num_of_group; i++) {
+			mdest_act_res = &cache_resource->sample_idx[i];
+			if (cache_resource->group_action[i])
+				claim_zero(mlx5_glue->destroy_flow_action
+					(cache_resource->group_action[i]));
+			if (mdest_act_res->rix_hrxq) {
+				mlx5_hrxq_release(dev,
+					mdest_act_res->rix_hrxq);
+				mdest_act_res->rix_hrxq = 0;
+			}
+			if (mdest_act_res->rix_encap_decap) {
+				flow_dv_encap_decap_resource_release(dev,
+					mdest_act_res->rix_encap_decap);
+				mdest_act_res->rix_encap_decap = 0;
+			}
+			if (mdest_act_res->rix_port_id_action) {
+				flow_dv_port_id_action_resource_release(dev,
+					mdest_act_res->rix_port_id_action);
+				mdest_act_res->rix_port_id_action = 0;
+			}
+			if (mdest_act_res->rix_tag) {
+				flow_dv_tag_release(dev,
+					mdest_act_res->rix_tag);
+				mdest_act_res->rix_tag = 0;
+			}
+		}
+		ILIST_REMOVE(priv->sh->ipool[MLX5_IPOOL_MULTI_DEST],
+			     &priv->sh->multi_dest_list, idx,
+			     cache_resource, next);
+		mlx5_ipool_free(priv->sh->ipool[MLX5_IPOOL_MULTI_DEST], idx);
+		DRV_LOG(DEBUG, "multi-dest resource %p: removed",
+			(void *)cache_resource);
+		return 0;
+	}
+	return 1;
+}
+
+/**
  * Remove the flow from the NIC but keeps it in memory.
  * Lock free, (mutex should be acquired by caller).
  *
@@ -10079,6 +10478,8 @@  struct field_modify_info modify_tcp[] = {
 			flow_dv_matcher_release(dev, dev_handle);
 		if (dev_handle->dvh.rix_sample)
 			flow_dv_sample_resource_release(dev, dev_handle);
+		if (dev_handle->dvh.rix_multi_dest)
+			flow_dv_multi_dest_resource_release(dev, dev_handle);
 		if (dev_handle->dvh.rix_encap_decap)
 			flow_dv_encap_decap_resource_release(dev,
 				dev_handle->dvh.rix_encap_decap);