[v2,4/4] net/mlx5: connect meter policy to created flows

Message ID 20210402155613.1533218-5-lizh@nvidia.com (mailing list archive)
State Superseded, archived
Delegated to: Raslan Darawsheh
Headers
Series net/mlx5: support meter policy operations |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/Intel-compilation fail apply issues

Commit Message

Li Zhang April 2, 2021, 3:56 p.m. UTC
  From: Shun Hao <shunh@nvidia.com>

Currently ASO meter must be followed by policy table, so this adds
the support that connecting meter and policy table.

There are several cases to be considered:
1. For non-termination policy, connect meter to the default policy
table.
2. For non-RSS termination policy case, simply get the policy
table id and connect meter to it.
3. For RSS termination policy case, need to split the flow due
to RSS info in policy, and translate each sub-flow using that RSS,
then create the sub policy table to be connected.
4. In termination policy case, if there's no actions to modify the
packet before meter, no need to use set_tag to save meter id in
register. Only add a new flow in drop table using the same match
criteria as suf-flow, to save cache miss.

Signed-off-by: Shun Hao <shunh@nvidia.com>
---
 drivers/net/mlx5/mlx5_flow.c    | 425 ++++++++++++++++++++++++++++----
 drivers/net/mlx5/mlx5_flow.h    |   9 +
 drivers/net/mlx5/mlx5_flow_dv.c |  24 +-
 3 files changed, 406 insertions(+), 52 deletions(-)
  

Patch

diff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c
index b41bf6456d..56d0e480cb 100644
--- a/drivers/net/mlx5/mlx5_flow.c
+++ b/drivers/net/mlx5/mlx5_flow.c
@@ -3359,18 +3359,52 @@  flow_drv_destroy(struct rte_eth_dev *dev, struct rte_flow *flow)
 	fops->destroy(dev, flow);
 }
 
+/**
+ * Flow driver find RSS policy tbl API. This abstracts calling driver
+ * specific functions. Parent flow (rte_flow) should have driver
+ * type (drv_type). It will find the RSS policy table that has the rss_desc.
+ *
+ * @param[in] dev
+ *   Pointer to Ethernet device.
+ * @param[in, out] flow
+ *   Pointer to flow structure.
+ * @param[in] policy_id
+ *   The policy id of a meter.
+ * @param[in] rss_desc
+ *   Pointer to rss_desc
+ */
+static struct mlx5_flow_meter_sub_policy *
+flow_drv_meter_sub_policy_prepare(struct rte_eth_dev *dev,
+		struct rte_flow *flow,
+		uint32_t policy_id,
+		struct mlx5_flow_rss_desc *rss_desc[MLX5_MTR_RTE_COLORS])
+{
+	const struct mlx5_flow_driver_ops *fops;
+	enum mlx5_flow_drv_type type = flow->drv_type;
+
+	MLX5_ASSERT(type > MLX5_FLOW_TYPE_MIN && type < MLX5_FLOW_TYPE_MAX);
+	fops = flow_get_drv_ops(type);
+	return fops->meter_sub_policy_prepare(dev, policy_id, rss_desc);
+}
+
 /**
  * Get RSS action from the action list.
  *
+ * @param[in] dev
+ *   Pointer to Ethernet device.
  * @param[in] actions
  *   Pointer to the list of actions.
+ * @param[in] flow
+ *   Parent flow structure pointer.
  *
  * @return
  *   Pointer to the RSS action if exist, else return NULL.
  */
 static const struct rte_flow_action_rss*
-flow_get_rss_action(const struct rte_flow_action actions[])
+flow_get_rss_action(struct rte_eth_dev *dev,
+		    const struct rte_flow_action actions[])
 {
+	struct mlx5_priv *priv = dev->data->dev_private;
 	const struct rte_flow_action_rss *rss = NULL;
 
 	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
@@ -3388,6 +3422,23 @@  flow_get_rss_action(const struct rte_flow_action actions[])
 					rss = act->conf;
 			break;
 		}
+		case RTE_FLOW_ACTION_TYPE_METER:
+		{
+			uint32_t mtr_idx;
+			struct mlx5_flow_meter_info *fm;
+			struct mlx5_flow_meter_policy *policy;
+			const struct rte_flow_action_meter *mtr = actions->conf;
+
+			fm = mlx5_flow_meter_find(priv, mtr->mtr_id, &mtr_idx);
+			if (fm) {
+				policy = mlx5_flow_meter_policy_find(dev,
+						fm->policy_id, NULL);
+				if (policy && policy->is_rss)
+					rss =
+				policy->act_cnt[RTE_COLOR_GREEN].rss->conf;
+			}
+			break;
+		}
 		default:
 			break;
 		}
@@ -3686,6 +3737,55 @@  flow_parse_metadata_split_actions_info(const struct rte_flow_action actions[],
 	return actions_n + 1;
 }
 
+/**
+ * Check if the action will change packet.
+ *
+ * @param[in] type
+ *   action type.
+ *
+ * @return
+ *   true if action will change packet, false otherwise.
+ */
+static bool flow_check_modify_action_type(enum rte_flow_action_type type)
+{
+	switch (type) {
+	case RTE_FLOW_ACTION_TYPE_SET_MAC_SRC:
+	case RTE_FLOW_ACTION_TYPE_SET_MAC_DST:
+	case RTE_FLOW_ACTION_TYPE_SET_IPV4_SRC:
+	case RTE_FLOW_ACTION_TYPE_SET_IPV4_DST:
+	case RTE_FLOW_ACTION_TYPE_SET_IPV6_SRC:
+	case RTE_FLOW_ACTION_TYPE_SET_IPV6_DST:
+	case RTE_FLOW_ACTION_TYPE_SET_TP_SRC:
+	case RTE_FLOW_ACTION_TYPE_SET_TP_DST:
+	case RTE_FLOW_ACTION_TYPE_DEC_TTL:
+	case RTE_FLOW_ACTION_TYPE_SET_TTL:
+	case RTE_FLOW_ACTION_TYPE_INC_TCP_SEQ:
+	case RTE_FLOW_ACTION_TYPE_DEC_TCP_SEQ:
+	case RTE_FLOW_ACTION_TYPE_INC_TCP_ACK:
+	case RTE_FLOW_ACTION_TYPE_DEC_TCP_ACK:
+	case RTE_FLOW_ACTION_TYPE_SET_IPV4_DSCP:
+	case RTE_FLOW_ACTION_TYPE_SET_IPV6_DSCP:
+	case RTE_FLOW_ACTION_TYPE_FLAG:
+	case RTE_FLOW_ACTION_TYPE_MARK:
+	case RTE_FLOW_ACTION_TYPE_SET_META:
+	case RTE_FLOW_ACTION_TYPE_SET_TAG:
+	case RTE_FLOW_ACTION_TYPE_OF_POP_VLAN:
+	case RTE_FLOW_ACTION_TYPE_OF_PUSH_VLAN:
+	case RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_VID:
+	case RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_PCP:
+	case RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP:
+	case RTE_FLOW_ACTION_TYPE_VXLAN_DECAP:
+	case RTE_FLOW_ACTION_TYPE_NVGRE_ENCAP:
+	case RTE_FLOW_ACTION_TYPE_NVGRE_DECAP:
+	case RTE_FLOW_ACTION_TYPE_RAW_ENCAP:
+	case RTE_FLOW_ACTION_TYPE_RAW_DECAP:
+	case RTE_FLOW_ACTION_TYPE_MODIFY_FIELD:
+		return true;
+	default:
+		return false;
+	}
+}
+
 /**
  * Check meter action from the action list.
  *
@@ -3693,6 +3793,8 @@  flow_parse_metadata_split_actions_info(const struct rte_flow_action actions[],
  *   Pointer to the list of actions.
  * @param[out] has_mtr
  *   Pointer to the meter exist flag.
+ * @param[out] has_modify
+ *   Pointer to the flag showing there's packet change action.
  * @param[out] meter_id
  *   Pointer to the meter id.
  *
@@ -3701,8 +3803,7 @@  flow_parse_metadata_split_actions_info(const struct rte_flow_action actions[],
  */
 static int
 flow_check_meter_action(const struct rte_flow_action actions[],
-			bool *has_mtr,
-			uint32_t *meter_id)
+			bool *has_mtr, bool *has_modify, uint32_t *meter_id)
 {
 	const struct rte_flow_action_meter *mtr = NULL;
 	int actions_n = 0;
@@ -3719,6 +3820,9 @@  flow_check_meter_action(const struct rte_flow_action actions[],
 		default:
 			break;
 		}
+		if (!*has_mtr)
+			*has_modify |=
+				flow_check_modify_action_type(actions->type);
 		actions_n++;
 	}
 	/* Count RTE_FLOW_ACTION_TYPE_END. */
@@ -4361,6 +4465,112 @@  flow_create_split_inner(struct rte_eth_dev *dev,
 	return flow_drv_translate(dev, dev_flow, attr, items, actions, error);
 }
 
+/**
+ * Get the table id of meter policy table.
+ *
+ * @param[in] dev
+ *   Pointer to Ethernet device.
+ * @param[in] flow
+ *   Parent flow structure pointer.
+ * @param[in] policy_id;
+ *   Meter Policy id.
+ * @param[in] attr
+ *   Flow rule attributes.
+ * @param[in] items
+ *   Pattern specification (list terminated by the END pattern item).
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
+ * @return
+ *   The table id, 0 otherwise and rte_errno is set.
+ */
+static uint32_t
+get_sub_policy_tbl_id(struct rte_eth_dev *dev,
+		      struct rte_flow *flow,
+		      uint32_t policy_id,
+		      const struct rte_flow_attr *attr,
+		      const struct rte_flow_item items[],
+		      struct rte_flow_error *error)
+{
+	uint32_t policy_tbl_id = 0;
+	struct mlx5_flow_meter_policy *policy;
+
+	policy = mlx5_flow_meter_policy_find(dev, policy_id, NULL);
+	if (!policy) {
+		rte_flow_error_set(error, EINVAL,
+				   RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+				   "Failed to find Meter Policy.");
+		goto exit;
+	}
+	if (policy->is_rss) {
+		struct mlx5_flow_workspace *wks =
+				mlx5_flow_get_thread_workspace();
+		struct mlx5_flow_rss_desc rss_desc_v[MLX5_MTR_RTE_COLORS];
+		struct mlx5_flow_rss_desc *rss_desc[MLX5_MTR_RTE_COLORS] = {0};
+		struct mlx5_flow_meter_sub_policy *sub_policy = NULL;
+		uint32_t i;
+
+		MLX5_ASSERT(wks);
+		/** This is a tmp dev_flow,
+		 *  no need to register any matcher for it in translate.
+		 */
+		wks->skip_matcher_reg = 1;
+		for (i = 0; i < MLX5_MTR_RTE_COLORS; i++) {
+			struct mlx5_flow dev_flow = {0};
+			struct mlx5_flow_handle dev_handle = {{0}};
+			const void *rss_act = policy->act_cnt[i].rss->conf;
+			struct rte_flow_action rss_actions[2] = {
+				[0] = {
+					.type = RTE_FLOW_ACTION_TYPE_RSS,
+					.conf = rss_act
+				},
+				[1] = {
+					.type = RTE_FLOW_ACTION_TYPE_END,
+					.conf = NULL
+				}
+			};
+
+			dev_flow.handle = &dev_handle;
+			dev_flow.ingress = attr->ingress;
+			dev_flow.flow = flow;
+			dev_flow.external = 0;
+#ifdef HAVE_IBV_FLOW_DV_SUPPORT
+			dev_flow.dv.transfer = attr->transfer;
+#endif
+			/* Translate RSS action to get rss hash fields. */
+			if (flow_drv_translate(dev, &dev_flow, attr,
+						items, rss_actions, error))
+				goto exit;
+			rss_desc_v[i] = wks->rss_desc;
+			rss_desc_v[i].key_len = MLX5_RSS_HASH_KEY_LEN;
+			rss_desc_v[i].hash_fields = dev_flow.hash_fields;
+			rss_desc_v[i].queue_num = rss_desc_v[i].hash_fields ?
+						  rss_desc_v[i].queue_num : 1;
+			rss_desc[i] = &rss_desc_v[i];
+		}
+		sub_policy = flow_drv_meter_sub_policy_prepare(dev,
+						flow, policy_id, rss_desc);
+		if (!sub_policy) {
+			rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+				"Failed to get meter RSS policy table.");
+			goto exit;
+		}
+		policy_tbl_id = sub_policy->idx;
+	} else {
+		enum mlx5_meter_domain mtr_domain =
+			attr->transfer ? MLX5_MTR_DOMAIN_TRANSFER :
+				attr->egress ? MLX5_MTR_DOMAIN_EGRESS :
+					MLX5_MTR_DOMAIN_INGRESS;
+		policy_tbl_id =
+			policy->sub_policys[mtr_domain][0]->idx;
+	}
+exit:
+	return policy_tbl_id;
+}
+
+#define MLX5_MTR_PRE_TAG_NUM 2
+
 /**
  * Split the meter flow.
  *
@@ -4391,13 +4601,15 @@  flow_create_split_inner(struct rte_eth_dev *dev,
  *   Suffix flow actions.
  * @param[out] actions_pre
  *   Prefix flow actions.
+ * @param[out] mtr_flow_id
+ *   Pointer to meter flow id.
  * @param[out] error
  *   Perform verbose error reporting if not NULL.
  *
  * @return
- *   The flow id, 0 otherwise and rte_errno is set.
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
  */
-static uint32_t
+static int
 flow_meter_split_prep(struct rte_eth_dev *dev,
 		      struct rte_flow *flow,
 		      struct mlx5_flow_meter_info *fm,
@@ -4407,6 +4619,7 @@  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[],
+		      uint32_t *mtr_flow_id,
 		      struct rte_flow_error *error)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
@@ -4420,7 +4633,7 @@  flow_meter_split_prep(struct rte_eth_dev *dev,
 	uint32_t tag_id = 0;
 	bool copy_vlan = false;
 	struct rte_flow_action *hw_mtr_action;
-	struct rte_flow_action_jump *jump_data;
+	struct mlx5_rte_flow_item_mtr_jump *jump_data;
 	struct rte_flow_action *action_pre_head = NULL;
 	bool mtr_first = priv->sh->meter_aso_en &&
 			 (attr->egress ||
@@ -4429,8 +4642,6 @@  flow_meter_split_prep(struct rte_eth_dev *dev,
 	uint8_t mtr_reg_bits = priv->mtr_reg_share ?
 				MLX5_MTR_IDLE_BITS_IN_COLOR_REG : MLX5_REG_BITS;
 	uint8_t flow_id_bits = 0;
-	int shift;
-	uint32_t flow_id_val = 0;
 
 	/* For ASO meter, meter must be before tag in TX direction. */
 	if (mtr_first) {
@@ -4474,7 +4685,9 @@  flow_meter_split_prep(struct rte_eth_dev *dev,
 			break;
 		}
 		if (!action_cur)
-			action_cur = actions_sfx++;
+			action_cur =
+				(fm->policy_id != MLX5_MTR_DEFAULT_POLICY_ID) ?
+					actions_pre++ : actions_sfx++;
 		memcpy(action_cur, actions, sizeof(struct rte_flow_action));
 	}
 	/* Add end action to the actions. */
@@ -4483,37 +4696,56 @@  flow_meter_split_prep(struct rte_eth_dev *dev,
 		/** For ASO meter, need to add an extra jump action explicitly,
 		 *  to jump from meter to policer table.
 		 */
+		uint32_t sub_policy_tbl_id = 0;
+
+		if (fm->policy_id != MLX5_MTR_DEFAULT_POLICY_ID) {
+			sub_policy_tbl_id =
+				get_sub_policy_tbl_id(dev, flow, fm->policy_id,
+						      attr, items, error);
+			if (!sub_policy_tbl_id)
+				return -rte_errno;
+		}
 		hw_mtr_action = actions_pre;
-		hw_mtr_action->type = RTE_FLOW_ACTION_TYPE_JUMP;
+		hw_mtr_action->type = (enum rte_flow_action_type)
+				      MLX5_RTE_FLOW_ACTION_TYPE_MTR_JUMP;
 		actions_pre++;
 		actions_pre->type = RTE_FLOW_ACTION_TYPE_END;
 		actions_pre++;
-		jump_data = (struct rte_flow_action_jump *)actions_pre;
-		jump_data->group = attr->transfer ?
+		jump_data = (struct mlx5_rte_flow_item_mtr_jump *)actions_pre;
+		jump_data->table_group.group = attr->transfer ?
 				(MLX5_FLOW_TABLE_LEVEL_POLICY - 1) :
 				 MLX5_FLOW_TABLE_LEVEL_POLICY;
+		jump_data->table_id = sub_policy_tbl_id;
 		hw_mtr_action->conf = jump_data;
 		actions_pre = (struct rte_flow_action *)(jump_data + 1);
 	} else {
 		actions_pre->type = RTE_FLOW_ACTION_TYPE_END;
 		actions_pre++;
 	}
-	/* Generate meter flow_id only if support multiple flows per meter. */
-	mlx5_ipool_malloc(fm->flow_ipool, &tag_id);
-	if (!tag_id)
-		return rte_flow_error_set(error, ENOMEM,
-				RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
-				"Failed to allocate meter flow id.");
-	flow_id_bits = MLX5_REG_BITS - __builtin_clz(tag_id - 1);
-	flow_id_bits = flow_id_bits ? flow_id_bits : 1;
-	if ((flow_id_bits + priv->sh->mtrmng->max_mtr_bits) > mtr_reg_bits) {
-		mlx5_ipool_free(fm->flow_ipool, tag_id);
-		return rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
-				"Meter flow id exceeds max limit.");
+	MLX5_ASSERT(tag_action);
+	if (!mtr_flow_id) {
+		tag_action->type = RTE_FLOW_ACTION_TYPE_VOID;
+		goto exit;
+	}
+	/* Only Non-termination Policy Meter creates mtr flow id. */
+	if (fm->policy_id == MLX5_MTR_DEFAULT_POLICY_ID) {
+		mlx5_ipool_malloc(fm->flow_ipool, &tag_id);
+		if (!tag_id)
+			return rte_flow_error_set(error, ENOMEM,
+					RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+					"Failed to allocate meter flow id.");
+		flow_id_bits = MLX5_REG_BITS - __builtin_clz(tag_id - 1);
+		flow_id_bits = flow_id_bits ? flow_id_bits : 1;
+		if ((flow_id_bits + priv->sh->mtrmng->max_mtr_bits) >
+		    mtr_reg_bits) {
+			mlx5_ipool_free(fm->flow_ipool, tag_id);
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+					"Meter flow id exceeds max limit.");
+		}
+		if (flow_id_bits > priv->sh->mtrmng->max_mtr_flow_bits)
+			priv->sh->mtrmng->max_mtr_flow_bits = flow_id_bits;
 	}
-	if (flow_id_bits > priv->sh->mtrmng->max_mtr_flow_bits)
-		priv->sh->mtrmng->max_mtr_flow_bits = flow_id_bits;
 	/* Prepare the suffix subflow items. */
 	tag_item = sfx_items++;
 	for (; items->type != RTE_FLOW_ITEM_TYPE_END; items++) {
@@ -4543,7 +4775,6 @@  flow_meter_split_prep(struct rte_eth_dev *dev,
 	sfx_items->type = RTE_FLOW_ITEM_TYPE_END;
 	sfx_items++;
 	/* Build tag actions and items for meter_id/meter flow_id. */
-	assert(tag_action);
 	set_tag = (struct mlx5_rte_flow_action_set_tag *)actions_pre;
 	tag_item_spec = (struct mlx5_rte_flow_item_tag *)sfx_items;
 	tag_item_mask = tag_item_spec + 1;
@@ -4554,14 +4785,19 @@  flow_meter_split_prep(struct rte_eth_dev *dev,
 		.length = mtr_reg_bits,
 		.data = flow->meter,
 	};
-	/*
-	 * The color Reg bits used by flow_id are growing from
-	 * msb to lsb, so must do bit reverse for flow_id val in RegC.
-	 */
-	for (shift = 0; shift < flow_id_bits; shift++)
-		flow_id_val = (flow_id_val << 1) |
-			      (((tag_id - 1) >> shift) & 0x1);
-	set_tag->data |= flow_id_val << (mtr_reg_bits - flow_id_bits);
+	if (tag_id) {
+		int shift;
+		uint32_t flow_id_val = 0;
+
+		/*
+		 * The color Reg bits used by flow_id are growing from
+		 * msb to lsb, so must do bit reverse for flow_id val in RegC.
+		 */
+		for (shift = 0; shift < flow_id_bits; shift++)
+			flow_id_val = (flow_id_val << 1) |
+					(((tag_id - 1) >> shift) & 0x1);
+		set_tag->data |= flow_id_val << (mtr_reg_bits - flow_id_bits);
+	}
 	tag_item_spec->id = set_tag->id;
 	tag_item_spec->data = set_tag->data << mtr_id_offset;
 	tag_item_mask->data = UINT32_MAX << mtr_id_offset;
@@ -4573,7 +4809,10 @@  flow_meter_split_prep(struct rte_eth_dev *dev,
 	tag_item->spec = tag_item_spec;
 	tag_item->last = NULL;
 	tag_item->mask = tag_item_mask;
-	return tag_id;
+exit:
+	if (mtr_flow_id)
+		*mtr_flow_id = tag_id;
+	return 0;
 }
 
 /**
@@ -5237,6 +5476,57 @@  flow_create_split_metadata(struct rte_eth_dev *dev,
 	return ret;
 }
 
+/**
+ * Create the Termination Policy Meter drop 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] flow_split_info
+ *   Pointer to flow split info structure.
+ * @param[in] fm
+ *   Pointer to flow meter structure.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ * @return
+ *   0 on success, negative value otherwise
+ */
+static uint32_t
+flow_meter_create_drop_flow_with_org_pattern(struct rte_eth_dev *dev,
+			struct rte_flow *flow,
+			const struct rte_flow_attr *attr,
+			const struct rte_flow_item items[],
+			struct mlx5_flow_split_info *flow_split_info,
+			struct mlx5_flow_meter_info *fm,
+			struct rte_flow_error *error)
+{
+	struct mlx5_flow *dev_flow = NULL;
+	struct rte_flow_attr drop_attr = *attr;
+	struct rte_flow_action drop_actions[3] = {{0}};
+	struct mlx5_flow_split_info drop_split_info = *flow_split_info;
+
+	MLX5_ASSERT(fm->drop_cnt);
+	drop_actions[0].type =
+		(enum rte_flow_action_type)MLX5_RTE_FLOW_ACTION_TYPE_MTR_COUNT;
+	drop_actions[0].conf =
+		(void *)(uintptr_t)fm->drop_cnt;
+	drop_actions[1].type =
+		RTE_FLOW_ACTION_TYPE_DROP;
+	drop_actions[2].type =
+		RTE_FLOW_ACTION_TYPE_END;
+	drop_split_info.external = false;
+	drop_split_info.skip_scale |= 1 << MLX5_SCALE_FLOW_GROUP_BIT;
+	drop_attr.group = MLX5_FLOW_TABLE_LEVEL_METER_DROP;
+	return flow_create_split_inner(dev, flow, &dev_flow,
+				&drop_attr, items, drop_actions,
+				&drop_split_info, error);
+}
+
 /**
  * The splitting for meter feature.
  *
@@ -5281,10 +5571,13 @@  flow_create_split_meter(struct rte_eth_dev *dev,
 	struct mlx5_flow *dev_flow = NULL;
 	struct rte_flow_attr sfx_attr = *attr;
 	struct mlx5_flow_meter_info *fm = NULL;
+	uint8_t skip_scale_restore;
 	bool has_mtr = false;
-	uint32_t meter_id;
+	bool has_modify = false;
+	bool set_mtr_reg = true;
+	uint32_t meter_id = 0;
 	uint32_t mtr_idx = 0;
-	uint32_t mtr_tag_id = 0;
+	uint32_t mtr_flow_id = 0;
 	size_t act_size;
 	size_t item_size;
 	int actions_n = 0;
@@ -5292,7 +5585,7 @@  flow_create_split_meter(struct rte_eth_dev *dev,
 
 	if (priv->mtr_en)
 		actions_n = flow_check_meter_action(actions, &has_mtr,
-						    &meter_id);
+						    &has_modify, &meter_id);
 	if (has_mtr) {
 		if (flow->meter) {
 			fm = flow_dv_meter_find_by_idx(priv, flow->meter);
@@ -5312,7 +5605,18 @@  flow_create_split_meter(struct rte_eth_dev *dev,
 				return -rte_errno;
 			flow->meter = mtr_idx;
 		}
+		MLX5_ASSERT(wks);
 		wks->fm = fm;
+		/*
+		 * If it's Termination Policy Meter, and
+		 * 1. There's no action in flow to change
+		 *    packet (modify/encap/decap etc.), OR
+		 * 2. No drop count needed for this meter.
+		 * no need to use regC to save meter id anymore.
+		 */
+		if (fm->policy_id != MLX5_MTR_DEFAULT_POLICY_ID &&
+		    (!has_modify || !fm->drop_cnt))
+			set_mtr_reg = false;
 		/* Prefix actions: meter, decap, encap, tag, jump, end. */
 		act_size = sizeof(struct rte_flow_action) * (actions_n + 6) +
 			   sizeof(struct mlx5_rte_flow_action_set_tag);
@@ -5329,27 +5633,48 @@  flow_create_split_meter(struct rte_eth_dev *dev,
 						  "meter flow");
 		sfx_items = (struct rte_flow_item *)((char *)sfx_actions +
 			     act_size);
-		pre_actions = sfx_actions + actions_n;
-		mtr_tag_id = flow_meter_split_prep(dev, flow, fm, &sfx_attr,
-						   items, sfx_items, actions,
-						   sfx_actions, pre_actions,
-						   error);
-		if (!mtr_tag_id) {
+		/* There's no suffix flow for Termination Policy Meter. */
+		if (fm->policy_id != MLX5_MTR_DEFAULT_POLICY_ID)
+			pre_actions = sfx_actions + 1;
+		else
+			pre_actions = sfx_actions + actions_n;
+		ret = flow_meter_split_prep(dev, flow, fm, &sfx_attr,
+					    items, sfx_items, actions,
+					    sfx_actions, pre_actions,
+					    (set_mtr_reg ? &mtr_flow_id : NULL),
+					    error);
+		if (ret) {
 			ret = -rte_errno;
 			goto exit;
 		}
 		/* Add the prefix subflow. */
 		flow_split_info->prefix_mark = 0;
+		skip_scale_restore = flow_split_info->skip_scale;
+		flow_split_info->skip_scale |=
+			1 << MLX5_SCALE_JUMP_FLOW_GROUP_BIT;
 		ret = flow_create_split_inner(dev, flow, &dev_flow,
 					      attr, items, pre_actions,
 					      flow_split_info, error);
 		if (ret) {
-			mlx5_ipool_free(fm->flow_ipool, mtr_tag_id);
+			if (mtr_flow_id)
+				mlx5_ipool_free(fm->flow_ipool, mtr_flow_id);
 			ret = -rte_errno;
 			goto exit;
 		}
-		dev_flow->handle->split_flow_id = mtr_tag_id;
-		dev_flow->handle->is_meter_flow_id = 1;
+		flow_split_info->skip_scale = skip_scale_restore;
+		if (mtr_flow_id) {
+			dev_flow->handle->split_flow_id = mtr_flow_id;
+			dev_flow->handle->is_meter_flow_id = 1;
+		}
+		if (fm->policy_id != MLX5_MTR_DEFAULT_POLICY_ID) {
+			if (!set_mtr_reg && fm->drop_cnt)
+				ret =
+			flow_meter_create_drop_flow_with_org_pattern(dev, flow,
+							&sfx_attr, items,
+							flow_split_info,
+							fm, error);
+			goto exit;
+		}
 		/* Setting the sfx group atrr. */
 		sfx_attr.group = sfx_attr.transfer ?
 				(MLX5_FLOW_TABLE_LEVEL_SUFFIX - 1) :
@@ -5743,7 +6068,7 @@  flow_list_create(struct rte_eth_dev *dev, uint32_t *list,
 	memset(rss_desc, 0, offsetof(struct mlx5_flow_rss_desc, queue));
 	/* RSS Action only works on NIC RX domain */
 	if (attr->ingress && !attr->transfer)
-		rss = flow_get_rss_action(p_actions_rx);
+		rss = flow_get_rss_action(dev, p_actions_rx);
 	if (rss) {
 		if (flow_rss_workspace_adjust(wks, rss_desc, rss->queue_num))
 			return 0;
diff --git a/drivers/net/mlx5/mlx5_flow.h b/drivers/net/mlx5/mlx5_flow.h
index 3024bd9b60..cdbaf95085 100644
--- a/drivers/net/mlx5/mlx5_flow.h
+++ b/drivers/net/mlx5/mlx5_flow.h
@@ -37,6 +37,8 @@  enum mlx5_rte_flow_action_type {
 	MLX5_RTE_FLOW_ACTION_TYPE_DEFAULT_MISS,
 	MLX5_RTE_FLOW_ACTION_TYPE_TUNNEL_SET,
 	MLX5_RTE_FLOW_ACTION_TYPE_AGE,
+	MLX5_RTE_FLOW_ACTION_TYPE_MTR_JUMP,
+	MLX5_RTE_FLOW_ACTION_TYPE_MTR_COUNT,
 };
 
 #define MLX5_SHARED_ACTION_TYPE_OFFSET 30
@@ -70,6 +72,11 @@  struct mlx5_rte_flow_item_tx_queue {
 	uint32_t queue;
 };
 
+struct mlx5_rte_flow_item_mtr_jump {
+	struct rte_flow_action_jump table_group;
+	uint32_t table_id;
+};
+
 /* Feature name to allocate metadata register. */
 enum mlx5_feature_name {
 	MLX5_HAIRPIN_RX,
@@ -1029,6 +1036,8 @@  struct mlx5_flow_workspace {
 	uint32_t rssq_num; /* Allocated queue num in rss_desc. */
 	uint32_t flow_idx; /* Intermediate device flow index. */
 	struct mlx5_flow_meter_info *fm; /* Pointer to the meter in flow. */
+	uint32_t skip_matcher_reg:1;
+	/* Indicates if need to skip matcher register in translate. */
 };
 
 struct mlx5_flow_split_info {
diff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c
index b646b330ac..9f5945379e 100644
--- a/drivers/net/mlx5/mlx5_flow_dv.c
+++ b/drivers/net/mlx5/mlx5_flow_dv.c
@@ -7409,6 +7409,7 @@  flow_dv_prepare(struct rte_eth_dev *dev,
 	struct mlx5_flow_workspace *wks = mlx5_flow_get_thread_workspace();
 
 	MLX5_ASSERT(wks);
+	wks->skip_matcher_reg = 0;
 	/* In case of corrupting the memory. */
 	if (wks->flow_idx >= MLX5_NUM_MAX_DEV_FLOWS) {
 		rte_flow_error_set(error, ENOSPC,
@@ -11252,6 +11253,8 @@  flow_dv_translate(struct rte_eth_dev *dev,
 		int action_type = actions->type;
 		const struct rte_flow_action *found_action = NULL;
 		uint32_t jump_group = 0;
+		uint32_t jump_table_id = 0;
+		struct mlx5_flow_counter *cnt;
 
 		if (!mlx5_flow_os_action_supported(action_type))
 			return rte_flow_error_set(error, ENOTSUP,
@@ -11432,6 +11435,15 @@  flow_dv_translate(struct rte_eth_dev *dev,
 				age = action->conf;
 			action_flags |= MLX5_FLOW_ACTION_COUNT;
 			break;
+		case MLX5_RTE_FLOW_ACTION_TYPE_MTR_COUNT:
+			cnt = flow_dv_counter_get_by_idx(dev,
+				(uint32_t)(uintptr_t)action->conf, NULL);
+			if (!cnt)
+				return rte_flow_error_set(error, rte_errno,
+					RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+					NULL, "Failed to get meter counter.");
+			dev_flow->dv.actions[actions_n++] = cnt->action;
+			break;
 		case RTE_FLOW_ACTION_TYPE_OF_POP_VLAN:
 			dev_flow->dv.actions[actions_n++] =
 						priv->sh->pop_vlan_action;
@@ -11536,6 +11548,11 @@  flow_dv_translate(struct rte_eth_dev *dev,
 			/* If decap is followed by encap, handle it at encap. */
 			action_flags |= MLX5_FLOW_ACTION_DECAP;
 			break;
+		case MLX5_RTE_FLOW_ACTION_TYPE_MTR_JUMP:
+			jump_table_id =
+				((const struct mlx5_rte_flow_item_mtr_jump *)
+				 action->conf)->table_id;
+			/* Fall through */
 		case RTE_FLOW_ACTION_TYPE_JUMP:
 			jump_group = ((const struct rte_flow_action_jump *)
 							action->conf)->group;
@@ -11555,7 +11572,7 @@  flow_dv_translate(struct rte_eth_dev *dev,
 						       attr->transfer,
 						       !!dev_flow->external,
 						       tunnel, jump_group, 0,
-						       0, error);
+						       jump_table_id, error);
 			if (!tbl)
 				return rte_flow_error_set
 						(error, errno,
@@ -12091,6 +12108,8 @@  flow_dv_translate(struct rte_eth_dev *dev,
 	}
 	dev_flow->dv.actions_n = actions_n;
 	dev_flow->act_flags = action_flags;
+	if (wks->skip_matcher_reg)
+		return 0;
 	/* Register matcher. */
 	matcher.crc = rte_raw_cksum((const void *)matcher.mask.buf,
 				    matcher.mask.size);
@@ -12242,7 +12261,8 @@  flow_dv_apply(struct rte_eth_dev *dev, struct rte_flow *flow,
 		dv_h = &dh->dvh;
 		n = dv->actions_n;
 		if (dh->fate_action == MLX5_FLOW_FATE_DROP) {
-			if (dv->transfer) {
+			if (dv->transfer || dev_flow->dv.group ==
+			    MLX5_FLOW_TABLE_LEVEL_METER_DROP) {
 				dv->actions[n++] = priv->sh->esw_drop_action;
 			} else {
 				MLX5_ASSERT(priv->drop_queue.hrxq);