diff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c
index e07c58d..5df9da8 100644
--- a/drivers/net/mlx5/mlx5_flow.c
+++ b/drivers/net/mlx5/mlx5_flow.c
@@ -2309,6 +2309,27 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev, int32_t priority,
 			flow_qrss_free_id(dev, dev_flow->qrss_id);
 }
 
+/**
+ * Release meter prefix suffix flow match id.
+ *
+ * @param dev
+ *   Pointer to Ethernet device.
+ * @param flow
+ *   Flow to release id's from.
+ */
+static void
+flow_meter_split_id_release(struct rte_eth_dev *dev,
+			    struct rte_flow *flow)
+{
+	struct mlx5_flow *dev_flow;
+	struct mlx5_priv *priv = dev->data->dev_private;
+
+	LIST_FOREACH(dev_flow, &flow->dev_flows, next)
+		if (dev_flow->qrss_id)
+			mlx5_flow_id_release(priv->sh->flow_id_pool,
+					     dev_flow->mtr_flow_id);
+}
+
 static int
 flow_null_validate(struct rte_eth_dev *dev __rte_unused,
 		   const struct rte_flow_attr *attr __rte_unused,
@@ -2599,6 +2620,7 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev, int32_t priority,
 	enum mlx5_flow_drv_type type = flow->drv_type;
 
 	flow_mreg_split_qrss_release(dev, flow);
+	flow_meter_split_id_release(dev, flow);
 	assert(type > MLX5_FLOW_TYPE_MIN && type < MLX5_FLOW_TYPE_MAX);
 	fops = flow_get_drv_ops(type);
 	fops->destroy(dev, flow);
@@ -2626,6 +2648,26 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev, int32_t priority,
 }
 
 /**
+ * Get port id item from the item list.
+ *
+ * @param[in] item
+ *   Pointer to the list of items.
+ *
+ * @return
+ *   Pointer to the port id item if exist, else return NULL.
+ */
+static const struct rte_flow_item *
+find_port_id_item(const struct rte_flow_item *item)
+{
+	assert(item);
+	for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
+		if (item->type == RTE_FLOW_ITEM_TYPE_PORT_ID)
+			return item;
+	}
+	return NULL;
+}
+
+/**
  * Get RSS action from the action list.
  *
  * @param[in] actions
@@ -2704,6 +2746,38 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev, int32_t priority,
 }
 
 /**
+ * Check meter action from the action list.
+ *
+ * @param[in] actions
+ *   Pointer to the list of actions.
+ * @param[out] mtr
+ *   Pointer to the meter exist flag.
+ *
+ * @return
+ *   Total number of actions.
+ */
+static int
+flow_check_meter_action(const struct rte_flow_action actions[], uint32_t *mtr)
+{
+	int actions_n = 0;
+
+	assert(mtr);
+	*mtr = 0;
+	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
+		switch (actions->type) {
+		case RTE_FLOW_ACTION_TYPE_METER:
+			*mtr = 1;
+			break;
+		default:
+			break;
+		}
+		actions_n++;
+	}
+	/* Count RTE_FLOW_ACTION_TYPE_END. */
+	return actions_n + 1;
+}
+
+/**
  * Check if the flow should be splited due to hairpin.
  * The reason for the split is that in current HW we can't
  * support encap on Rx, so if a flow have encap we move it
@@ -3334,6 +3408,111 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev, int32_t priority,
 }
 
 /**
+ * Split the meter flow.
+ *
+ * As meter flow will split to three sub flow, other than meter
+ * action, the other actions make sense to only meter accepts
+ * the packet. If it need to be dropped, no other additional
+ * actions should be take.
+ *
+ * One kind of special action which decapsulates the L3 tunnel
+ * header will be in the prefix sub flow, as not to take the
+ * L3 tunnel header into account.
+ *
+ * @param dev
+ *   Pointer to Ethernet device.
+ * @param[in] actions
+ *   Associated actions (list terminated by the END action).
+ * @param[out] actions_sfx
+ *   Suffix flow actions.
+ * @param[out] actions_pre
+ *   Prefix flow actions.
+ * @param[out] pattern_sfx
+ *   The pattern items for the suffix flow.
+ * @param[out] tag_sfx
+ *   Pointer to suffix flow tag.
+ *
+ * @return
+ *   0 on success.
+ */
+static int
+flow_meter_split_prep(struct rte_eth_dev *dev,
+		 const struct rte_flow_action actions[],
+		 struct rte_flow_action actions_sfx[],
+		 struct rte_flow_action actions_pre[])
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+	struct rte_flow_action *tag_action;
+	struct mlx5_rte_flow_action_set_tag *set_tag;
+	struct rte_flow_error error;
+	const struct rte_flow_action_raw_encap *raw_encap;
+	const struct rte_flow_action_raw_decap *raw_decap;
+	uint32_t tag_id;
+
+	/* Add the extra tag action first. */
+	tag_action = actions_pre;
+	tag_action->type = MLX5_RTE_FLOW_ACTION_TYPE_TAG;
+	actions_pre++;
+	/* Prepare the actions for prefix and suffix flow. */
+	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
+		switch (actions->type) {
+		case RTE_FLOW_ACTION_TYPE_METER:
+		case RTE_FLOW_ACTION_TYPE_VXLAN_DECAP:
+		case RTE_FLOW_ACTION_TYPE_NVGRE_DECAP:
+			memcpy(actions_pre, actions,
+			       sizeof(struct rte_flow_action));
+			actions_pre++;
+			break;
+		case RTE_FLOW_ACTION_TYPE_RAW_ENCAP:
+			raw_encap = actions->conf;
+			if (raw_encap->size >
+			    (sizeof(struct rte_flow_item_eth) +
+			     sizeof(struct rte_flow_item_ipv4))) {
+				memcpy(actions_sfx, actions,
+				       sizeof(struct rte_flow_action));
+				actions_sfx++;
+			} else {
+				rte_memcpy(actions_pre, actions,
+					   sizeof(struct rte_flow_action));
+				actions_pre++;
+			}
+			break;
+		case RTE_FLOW_ACTION_TYPE_RAW_DECAP:
+			raw_decap = actions->conf;
+			/* Size 0 decap means 50 bytes as vxlan decap. */
+			if (raw_decap->size && (raw_decap->size <
+			    (sizeof(struct rte_flow_item_eth) +
+			     sizeof(struct rte_flow_item_ipv4)))) {
+				memcpy(actions_sfx, actions,
+				       sizeof(struct rte_flow_action));
+				actions_sfx++;
+			} else {
+				rte_memcpy(actions_pre, actions,
+					   sizeof(struct rte_flow_action));
+				actions_pre++;
+			}
+			break;
+		default:
+			memcpy(actions_sfx, actions,
+				sizeof(struct rte_flow_action));
+			actions_sfx++;
+			break;
+		}
+	}
+	/* Add end action to the actions. */
+	actions_sfx->type = RTE_FLOW_ACTION_TYPE_END;
+	actions_pre->type = RTE_FLOW_ACTION_TYPE_END;
+	actions_pre++;
+	/* Set the tag. */
+	set_tag = (void *)actions_pre;
+	set_tag->id = mlx5_flow_get_reg_id(dev, MLX5_MTR_SFX, 0, &error);
+	mlx5_flow_id_get(priv->sh->flow_id_pool, &tag_id);
+	set_tag->data = rte_cpu_to_be_32(tag_id);
+	tag_action->conf = set_tag;
+	return tag_id;
+}
+
+/**
  * Split action list having QUEUE/RSS for metadata register copy.
  *
  * Once Q/RSS action is detected in user's action list, the flow action
@@ -3679,6 +3858,124 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev, int32_t priority,
 }
 
 /**
+ * The splitting for meter feature.
+ *
+ * - The meter flow will be split to two flows as prefix and
+ *   suffix flow. The packets make sense only it pass the prefix
+ *   meter action.
+ *
+ * - Reg_C_5 is used for the packet to match betweend prefix and
+ *   suffix flow.
+ *
+ * @param dev
+ *   Pointer to Ethernet device.
+ * @param[in] flow
+ *   Parent flow structure pointer.
+ * @param[in] attr
+ *   Flow rule attributes.
+ * @param[in] items
+ *   Pattern specification (list terminated by the END pattern item).
+ * @param[in] actions
+ *   Associated actions (list terminated by the END action).
+ * @param[in] external
+ *   This flow rule is created by request external to PMD.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ * @return
+ *   0 on success, negative value otherwise
+ */
+static int
+flow_create_split_meter(struct rte_eth_dev *dev,
+			   struct rte_flow *flow,
+			   const struct rte_flow_attr *attr,
+			   const struct rte_flow_item items[],
+			   const struct rte_flow_action actions[],
+			   bool external, struct rte_flow_error *error)
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+	struct rte_flow_action *sfx_actions = NULL;
+	struct rte_flow_action *pre_actions = NULL;
+	struct rte_flow_item *sfx_items = NULL;
+	const  struct rte_flow_item *sfx_port_id_item;
+	struct mlx5_flow *dev_flow = NULL;
+	struct rte_flow_attr sfx_attr = *attr;
+	uint32_t mtr = 0;
+	uint32_t mtr_tag_id = 0;
+	size_t act_size;
+	size_t item_size;
+	int actions_n = 0;
+	int ret;
+
+	if (priv->mtr_en)
+		actions_n = flow_check_meter_action(actions, &mtr);
+	if (mtr) {
+		struct mlx5_rte_flow_item_tag *tag_spec;
+		/* The five prefix actions: meter, decap, encap, tag, end. */
+		act_size = sizeof(struct rte_flow_action) * (actions_n + 5) +
+			   sizeof(struct rte_flow_action_set_tag);
+		/* tag, end. */
+#define METER_SUFFIX_ITEM 3
+		item_size = sizeof(struct rte_flow_item) * METER_SUFFIX_ITEM +
+			    sizeof(struct mlx5_rte_flow_item_tag);
+		sfx_actions = rte_zmalloc(__func__, (act_size + item_size), 0);
+		if (!sfx_actions)
+			return rte_flow_error_set(error, ENOMEM,
+						  RTE_FLOW_ERROR_TYPE_ACTION,
+						  NULL, "no memory to split "
+						  "meter flow");
+		pre_actions = sfx_actions + actions_n;
+		mtr_tag_id = flow_meter_split_prep(dev, actions, sfx_actions,
+						     pre_actions);
+		if (!mtr_tag_id) {
+			ret = -rte_errno;
+			goto exit;
+		}
+		/* Add the prefix subflow. */
+		ret = flow_create_split_inner(dev, flow, &dev_flow, attr, items,
+						  pre_actions, external, error);
+		if (ret) {
+			ret = -rte_errno;
+			goto exit;
+		}
+		dev_flow->mtr_flow_id = mtr_tag_id;
+		/* Prepare the suffix flow match pattern. */
+		sfx_items = (struct rte_flow_item *)((char *)sfx_actions +
+			     act_size);
+		tag_spec = (struct mlx5_rte_flow_item_tag *)(sfx_items +
+			    METER_SUFFIX_ITEM);
+		tag_spec->data = rte_cpu_to_be_32(dev_flow->mtr_flow_id);
+		tag_spec->id = mlx5_flow_get_reg_id(dev, MLX5_MTR_SFX, 0,
+						    error);
+		sfx_items->type = MLX5_RTE_FLOW_ITEM_TYPE_TAG;
+		sfx_items->spec = tag_spec;
+		sfx_items->last = NULL;
+		sfx_items->mask = NULL;
+		sfx_items++;
+		sfx_port_id_item = find_port_id_item(items);
+		if (sfx_port_id_item) {
+			memcpy(sfx_items, sfx_port_id_item,
+			       sizeof(*sfx_items));
+			sfx_items++;
+		}
+		sfx_items->type = RTE_FLOW_ITEM_TYPE_END;
+		sfx_items -= METER_SUFFIX_ITEM;
+		/* Setting the sfx group atrr. */
+		sfx_attr.group = sfx_attr.transfer ?
+				(MLX5_FLOW_TABLE_LEVEL_SUFFIX - 1) :
+				 MLX5_FLOW_TABLE_LEVEL_SUFFIX;
+	}
+	/* Add the prefix subflow. */
+	ret = flow_create_split_metadata(dev, flow, &sfx_attr,
+					 sfx_items ? sfx_items : items,
+					 sfx_actions ? sfx_actions : actions,
+					 external, error);
+exit:
+	if (sfx_actions)
+		rte_free(sfx_actions);
+	return ret;
+}
+
+/**
  * Split the flow to subflow set. The splitters might be linked
  * in the chain, like this:
  * flow_create_split_outer() calls:
@@ -3723,7 +4020,7 @@ uint32_t mlx5_flow_adjust_priority(struct rte_eth_dev *dev, int32_t priority,
 {
 	int ret;
 
-	ret = flow_create_split_metadata(dev, flow, attr, items,
+	ret = flow_create_split_meter(dev, flow, attr, items,
 					 actions, external, error);
 	assert(ret <= 0);
 	return ret;
diff --git a/drivers/net/mlx5/mlx5_flow.h b/drivers/net/mlx5/mlx5_flow.h
index ff89ae4..b0a5678 100644
--- a/drivers/net/mlx5/mlx5_flow.h
+++ b/drivers/net/mlx5/mlx5_flow.h
@@ -521,6 +521,7 @@ struct mlx5_flow {
 		struct mlx5_flow_verbs verbs;
 	};
 	uint32_t qrss_id; /**< Uniqie Q/RSS suffix subflow tag. */
+	uint32_t mtr_flow_id; /**< Unique meter match flow id. */
 	bool external; /**< true if the flow is created external to PMD. */
 };
 
