diff mbox series

[v3,2/4] net/mlx5: support meter creation with policy

Message ID 20210413001954.2000292-3-lizh@nvidia.com (mailing list archive)
State Superseded
Delegated to: Raslan Darawsheh
Headers show
Series net/mlx5: support meter policy operations | expand

Checks

Context Check Description
ci/checkpatch success coding style OK

Commit Message

Li Zhang April 13, 2021, 12:19 a.m. UTC
Create a meter with the new pre-defined policy.

The following cases to be considered:
1.Add entry match with meter_id in global drop table.
2.For non-termination policy (policy id 0),
  add jump rule to suffix table for green and
  jump rule to drop table for red.
3.Allocate counter per meter in drop table.
4.Allocate meter resource per domain per color.
5.It can work with both ASO and legacy meter HW objects.

Signed-off-by: Li Zhang <lizh@nvidia.com>
Acked-by: Matan Azrad <matan@nvidia.com>
---
 drivers/net/mlx5/linux/mlx5_os.c   |  13 +-
 drivers/net/mlx5/mlx5.h            |  62 ++---
 drivers/net/mlx5/mlx5_flow.c       |  26 +-
 drivers/net/mlx5/mlx5_flow.h       |  32 +--
 drivers/net/mlx5/mlx5_flow_dv.c    | 367 ++++++++++++++++++++---------
 drivers/net/mlx5/mlx5_flow_meter.c | 337 ++++++++++++++++++++++----
 6 files changed, 607 insertions(+), 230 deletions(-)
diff mbox series

Patch

diff --git a/drivers/net/mlx5/linux/mlx5_os.c b/drivers/net/mlx5/linux/mlx5_os.c
index 276283d492..c1d4e2a0dc 100644
--- a/drivers/net/mlx5/linux/mlx5_os.c
+++ b/drivers/net/mlx5/linux/mlx5_os.c
@@ -1297,13 +1297,14 @@  mlx5_dev_spawn(struct rte_device *dpdk_dev,
 			if (log_obj_size >=
 			config->hca_attr.qos.log_meter_aso_granularity &&
 			log_obj_size <=
-			config->hca_attr.qos.log_meter_aso_max_alloc) {
+			config->hca_attr.qos.log_meter_aso_max_alloc)
 				sh->meter_aso_en = 1;
-				err = mlx5_aso_flow_mtrs_mng_init(priv->sh);
-				if (err) {
-					err = -err;
-					goto error;
-				}
+		}
+		if (priv->mtr_en) {
+			err = mlx5_aso_flow_mtrs_mng_init(priv->sh);
+			if (err) {
+				err = -err;
+				goto error;
 			}
 		}
 #endif
diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h
index cb7c75aa2e..a8745df3e4 100644
--- a/drivers/net/mlx5/mlx5.h
+++ b/drivers/net/mlx5/mlx5.h
@@ -702,44 +702,12 @@  struct mlx5_flow_meter_def_policy {
 	/* Jump action per color. */
 };
 
-/* Meter table structure. */
-struct mlx5_meter_domain_info {
-	struct mlx5_flow_tbl_resource *tbl;
-	/**< Meter table. */
-	struct mlx5_flow_tbl_resource *sfx_tbl;
-	/**< Meter suffix table. */
-	struct mlx5_flow_dv_matcher *drop_matcher;
-	/**< Matcher for Drop. */
-	struct mlx5_flow_dv_matcher *color_matcher;
-	/**< Matcher for Color. */
-	void *jump_actn;
-	/**< Meter match action. */
-	void *green_rule;
-	/**< Meter green rule. */
-	void *drop_rule;
-	/**< Meter drop rule. */
-};
-
-/* Meter table set for TX RX FDB. */
-struct mlx5_meter_domains_infos {
-	uint32_t ref_cnt;
-	/**< Table user count. */
-	struct mlx5_meter_domain_info egress;
-	/**< TX meter table. */
-	struct mlx5_meter_domain_info ingress;
-	/**< RX meter table. */
-	struct mlx5_meter_domain_info transfer;
-	/**< FDB meter table. */
-	void *drop_actn;
-	/**< Drop action as not matched. */
-	void *green_count;
-	/**< Counters for green rule. */
-	void *drop_count;
-	/**< Counters for green rule. */
-};
-
 /* Meter parameter structure. */
 struct mlx5_flow_meter_info {
+	uint32_t meter_id;
+	/**< Meter id. */
+	uint32_t policy_id;
+	/* Policy id, the first sub_policy idx. */
 	struct mlx5_flow_meter_profile *profile;
 	/**< Meter profile parameters. */
 	rte_spinlock_t sl; /**< Meter action spinlock. */
@@ -778,8 +746,10 @@  struct mlx5_flow_meter_info {
 	 * received by the application.
 	 */
 	uint32_t transfer:1;
-	struct mlx5_meter_domains_infos *mfts;
-	/**< Flow table created for this meter. */
+	uint32_t def_policy:1;
+	/* Meter points to default policy. */
+	void *drop_rule[MLX5_MTR_DOMAIN_MAX];
+	/* Meter drop rule in drop table. */
 	uint32_t drop_cnt;
 	/**< Color counter for drop. */
 	uint32_t ref_cnt;
@@ -790,6 +760,11 @@  struct mlx5_flow_meter_info {
 	/**< Flow meter action. */
 };
 
+/* PPS(packets per second) map to BPS(Bytes per second).
+ * HW treat packet as 128bytes in PPS mode
+ */
+#define MLX5_MTRS_PPS_MAP_BPS_SHIFT 7
+
 /* RFC2697 parameter structure. */
 struct mlx5_flow_meter_srtcm_rfc2697_prm {
 	rte_be32_t cbs_cir;
@@ -878,12 +853,17 @@  struct mlx5_flow_mtr_mng {
 	/* Policy index lookup table. */
 	struct mlx5_flow_tbl_resource *drop_tbl[MLX5_MTR_DOMAIN_MAX];
 	/* Meter drop table. */
-	struct mlx5_flow_dv_matcher *drop_matcher[MLX5_MTR_DOMAIN_MAX];
+	struct mlx5_flow_dv_matcher *
+			drop_matcher[MLX5_MTR_DOMAIN_MAX][MLX5_REG_BITS];
 	/* Matcher meter in drop table. */
 	struct mlx5_flow_dv_matcher *def_matcher[MLX5_MTR_DOMAIN_MAX];
 	/* Default matcher in drop table. */
 	void *def_rule[MLX5_MTR_DOMAIN_MAX];
 	/* Default rule in drop table. */
+	uint8_t max_mtr_bits;
+	/* Indicate how many bits are used by meter id at the most. */
+	uint8_t max_mtr_flow_bits;
+	/* Indicate how many bits are used by meter flow id at the most. */
 };
 
 /* Table key of the hash organization. */
@@ -1322,10 +1302,6 @@  struct mlx5_priv {
 	uint32_t rss_shared_actions; /* RSS shared actions. */
 	struct mlx5_devx_obj *q_counters; /* DevX queue counter object. */
 	uint32_t counter_set_id; /* Queue counter ID to set in DevX objects. */
-	uint8_t max_mtr_bits;
-	/* Indicate how many bits are used by meter id at the most. */
-	uint8_t max_mtr_flow_bits;
-	/* Indicate how many bits are used by meter flow id at the most. */
 };
 
 #define PORT_ID(priv) ((priv)->dev_data->port_id)
diff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c
index 81e0e3b7a9..77590bbbed 100644
--- a/drivers/net/mlx5/mlx5_flow.c
+++ b/drivers/net/mlx5/mlx5_flow.c
@@ -4496,14 +4496,14 @@  flow_meter_split_prep(struct rte_eth_dev *dev,
 	flow_id = tag_id - 1;
 	flow_id_bits = MLX5_REG_BITS - __builtin_clz(flow_id);
 	flow_id_bits = flow_id_bits ? flow_id_bits : 1;
-	if ((flow_id_bits + priv->max_mtr_bits) > mtr_reg_bits) {
+	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->max_mtr_flow_bits)
-		priv->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++) {
@@ -6778,15 +6778,18 @@  mlx5_flow_create_def_policy(struct rte_eth_dev *dev)
  *   Pointer to Ethernet device.
  *
  * @return
- *   Pointer to table set on success, NULL otherwise.
+ *   0 on success, -1 otherwise.
  */
-struct mlx5_meter_domains_infos *
-mlx5_flow_create_mtr_tbls(struct rte_eth_dev *dev)
+int
+mlx5_flow_create_mtr_tbls(struct rte_eth_dev *dev,
+			struct mlx5_flow_meter_info *fm,
+			uint32_t mtr_idx,
+			uint8_t domain_bitmap)
 {
 	const struct mlx5_flow_driver_ops *fops;
 
 	fops = flow_get_drv_ops(MLX5_FLOW_TYPE_DV);
-	return fops->create_mtr_tbls(dev);
+	return fops->create_mtr_tbls(dev, fm, mtr_idx, domain_bitmap);
 }
 
 /**
@@ -6796,18 +6799,15 @@  mlx5_flow_create_mtr_tbls(struct rte_eth_dev *dev)
  *   Pointer to Ethernet device.
  * @param[in] tbl
  *   Pointer to the meter table set.
- *
- * @return
- *   0 on success.
  */
-int
+void
 mlx5_flow_destroy_mtr_tbls(struct rte_eth_dev *dev,
-			   struct mlx5_meter_domains_infos *tbls)
+			   struct mlx5_flow_meter_info *fm)
 {
 	const struct mlx5_flow_driver_ops *fops;
 
 	fops = flow_get_drv_ops(MLX5_FLOW_TYPE_DV);
-	return fops->destroy_mtr_tbls(dev, tbls);
+	fops->destroy_mtr_tbls(dev, fm);
 }
 
 /**
diff --git a/drivers/net/mlx5/mlx5_flow.h b/drivers/net/mlx5/mlx5_flow.h
index 51d040c4d2..89e43f2de6 100644
--- a/drivers/net/mlx5/mlx5_flow.h
+++ b/drivers/net/mlx5/mlx5_flow.h
@@ -222,16 +222,17 @@  enum mlx5_feature_name {
 #define MLX5_FLOW_ACTION_TUNNEL_SET (1ull << 37)
 #define MLX5_FLOW_ACTION_TUNNEL_MATCH (1ull << 38)
 #define MLX5_FLOW_ACTION_MODIFY_FIELD (1ull << 39)
+#define MLX5_FLOW_ACTION_METER_WITH_TERMINATED_POLICY (1ull << 40)
 
 #define MLX5_FLOW_FATE_ACTIONS \
 	(MLX5_FLOW_ACTION_DROP | MLX5_FLOW_ACTION_QUEUE | \
 	 MLX5_FLOW_ACTION_RSS | MLX5_FLOW_ACTION_JUMP | \
-	 MLX5_FLOW_ACTION_DEFAULT_MISS)
+	 MLX5_FLOW_ACTION_DEFAULT_MISS | \
+	 MLX5_FLOW_ACTION_METER_WITH_TERMINATED_POLICY)
 
 #define MLX5_FLOW_FATE_ESWITCH_ACTIONS \
 	(MLX5_FLOW_ACTION_DROP | MLX5_FLOW_ACTION_PORT_ID | \
-	 MLX5_FLOW_ACTION_JUMP)
-
+	 MLX5_FLOW_ACTION_JUMP | MLX5_FLOW_ACTION_METER_WITH_TERMINATED_POLICY)
 
 #define MLX5_FLOW_MODIFY_HDR_ACTIONS (MLX5_FLOW_ACTION_SET_IPV4_SRC | \
 				      MLX5_FLOW_ACTION_SET_IPV4_DST | \
@@ -832,9 +833,8 @@  struct mlx5_legacy_flow_meter {
 	/* Must be the first in struct. */
 	TAILQ_ENTRY(mlx5_legacy_flow_meter) next;
 	/**< Pointer to the next flow meter structure. */
-	uint32_t meter_id;
-	/**< Meter id. */
-	uint32_t idx; /* Index to meter object. */
+	uint32_t idx;
+	/* Index to meter object. */
 };
 
 #define MLX5_MAX_TUNNELS 256
@@ -1088,10 +1088,12 @@  typedef int (*mlx5_flow_query_t)(struct rte_eth_dev *dev,
 				 const struct rte_flow_action *actions,
 				 void *data,
 				 struct rte_flow_error *error);
-typedef struct mlx5_meter_domains_infos *(*mlx5_flow_create_mtr_tbls_t)
-					    (struct rte_eth_dev *dev);
-typedef int (*mlx5_flow_destroy_mtr_tbls_t)(struct rte_eth_dev *dev,
-					struct mlx5_meter_domains_infos *tbls);
+typedef int (*mlx5_flow_create_mtr_tbls_t)(struct rte_eth_dev *dev,
+					struct mlx5_flow_meter_info *fm,
+					uint32_t mtr_idx,
+					uint8_t domain_bitmap);
+typedef void (*mlx5_flow_destroy_mtr_tbls_t)(struct rte_eth_dev *dev,
+				struct mlx5_flow_meter_info *fm);
 typedef void (*mlx5_flow_destroy_mtr_drop_tbls_t)(struct rte_eth_dev *dev);
 typedef uint32_t (*mlx5_flow_mtr_alloc_t)
 					    (struct rte_eth_dev *dev);
@@ -1409,10 +1411,12 @@  int mlx5_flow_validate_item_ecpri(const struct rte_flow_item *item,
 				  uint16_t ether_type,
 				  const struct rte_flow_item_ecpri *acc_mask,
 				  struct rte_flow_error *error);
-struct mlx5_meter_domains_infos *mlx5_flow_create_mtr_tbls
-					(struct rte_eth_dev *dev);
-int mlx5_flow_destroy_mtr_tbls(struct rte_eth_dev *dev,
-			       struct mlx5_meter_domains_infos *tbl);
+int mlx5_flow_create_mtr_tbls(struct rte_eth_dev *dev,
+				struct mlx5_flow_meter_info *fm,
+				uint32_t mtr_idx,
+				uint8_t domain_bitmap);
+void mlx5_flow_destroy_mtr_tbls(struct rte_eth_dev *dev,
+			       struct mlx5_flow_meter_info *fm);
 void mlx5_flow_destroy_mtr_drop_tbls(struct rte_eth_dev *dev);
 int mlx5_flow_dv_discover_counter_offset_support(struct rte_eth_dev *dev);
 int mlx5_shared_action_flush(struct rte_eth_dev *dev);
diff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c
index 759731333d..f789f2454e 100644
--- a/drivers/net/mlx5/mlx5_flow_dv.c
+++ b/drivers/net/mlx5/mlx5_flow_dv.c
@@ -4859,11 +4859,14 @@  mlx5_flow_validate_action_meter(struct rte_eth_dev *dev,
 				uint64_t action_flags,
 				const struct rte_flow_action *action,
 				const struct rte_flow_attr *attr,
+				bool *def_policy,
 				struct rte_flow_error *error)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
 	const struct rte_flow_action_meter *am = action->conf;
 	struct mlx5_flow_meter_info *fm;
+	struct mlx5_flow_meter_policy *mtr_policy;
+	struct mlx5_flow_mtr_mng *mtrmng = priv->sh->mtrmng;
 
 	if (!am)
 		return rte_flow_error_set(error, EINVAL,
@@ -4894,10 +4897,40 @@  mlx5_flow_validate_action_meter(struct rte_eth_dev *dev,
 	      (!fm->ingress && !attr->ingress && attr->egress) ||
 	      (!fm->egress && !attr->egress && attr->ingress)))
 		return rte_flow_error_set(error, EINVAL,
+			RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+			"Flow attributes domain are either invalid "
+			"or have a domain conflict with current "
+			"meter attributes");
+	if (fm->def_policy) {
+		if (!((attr->transfer &&
+			mtrmng->def_policy[MLX5_MTR_DOMAIN_TRANSFER]) ||
+			(attr->egress &&
+			mtrmng->def_policy[MLX5_MTR_DOMAIN_EGRESS]) ||
+			(attr->ingress &&
+			mtrmng->def_policy[MLX5_MTR_DOMAIN_INGRESS])))
+			return rte_flow_error_set(error, EINVAL,
+					  RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+					  "Flow attributes domain "
+					  "have a conflict with current "
+					  "meter domain attributes");
+		*def_policy = true;
+	} else {
+		mtr_policy = mlx5_flow_meter_policy_find(dev,
+						fm->policy_id, NULL);
+		if (!mtr_policy)
+			return rte_flow_error_set(error, EINVAL,
+					  RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+					  "Invalid policy id for meter ");
+		if (!((attr->transfer && mtr_policy->transfer) ||
+			(attr->egress && mtr_policy->egress) ||
+			(attr->ingress && mtr_policy->ingress)))
+			return rte_flow_error_set(error, EINVAL,
 					  RTE_FLOW_ERROR_TYPE_ACTION, NULL,
-					  "Flow attributes are either invalid "
-					  "or have a conflict with current "
-					  "meter attributes");
+					  "Flow attributes domain "
+					  "have a conflict with current "
+					  "meter domain attributes");
+		*def_policy = false;
+	}
 	return 0;
 }
 
@@ -6287,6 +6320,7 @@  flow_dv_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attr,
 		.fdb_def_rule = !!priv->fdb_def_rule,
 	};
 	const struct rte_eth_hairpin_conf *conf;
+	bool def_policy = false;
 
 	if (items == NULL)
 		return -1;
@@ -6628,6 +6662,12 @@  flow_dv_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attr,
 			return rte_flow_error_set(error, ENOTSUP,
 						  RTE_FLOW_ERROR_TYPE_ACTION,
 						  actions, "too many actions");
+		if (action_flags &
+			MLX5_FLOW_ACTION_METER_WITH_TERMINATED_POLICY)
+			return rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_ACTION,
+				NULL, "meter action with policy "
+				"must be the last action");
 		switch (type) {
 		case RTE_FLOW_ACTION_TYPE_VOID:
 			break;
@@ -7030,10 +7070,14 @@  flow_dv_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attr,
 			ret = mlx5_flow_validate_action_meter(dev,
 							      action_flags,
 							      actions, attr,
+							      &def_policy,
 							      error);
 			if (ret < 0)
 				return ret;
 			action_flags |= MLX5_FLOW_ACTION_METER;
+			if (!def_policy)
+				action_flags |=
+				MLX5_FLOW_ACTION_METER_WITH_TERMINATED_POLICY;
 			++actions_n;
 			/* Meter action will add one more TAG action. */
 			rw_act_num += MLX5_ACT_NUM_SET_TAG;
@@ -7290,6 +7334,36 @@  flow_dv_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attr,
 						 "multiple VLAN actions");
 		}
 	}
+	if (action_flags & MLX5_FLOW_ACTION_METER_WITH_TERMINATED_POLICY) {
+		if ((action_flags & (MLX5_FLOW_FATE_ACTIONS &
+			~MLX5_FLOW_ACTION_METER_WITH_TERMINATED_POLICY)) &&
+			attr->ingress)
+			return rte_flow_error_set
+				(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_ACTION,
+				NULL, "fate action not supported for "
+				"meter with policy");
+		if (attr->egress) {
+			if (action_flags & MLX5_FLOW_MODIFY_HDR_ACTIONS)
+				return rte_flow_error_set
+					(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_ACTION,
+					NULL, "modify header action in egress "
+					"cannot be done before meter action");
+			if (action_flags & MLX5_FLOW_ACTION_ENCAP)
+				return rte_flow_error_set
+					(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_ACTION,
+					NULL, "encap action in egress "
+					"cannot be done before meter action");
+			if (action_flags & MLX5_FLOW_ACTION_OF_PUSH_VLAN)
+				return rte_flow_error_set
+					(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_ACTION,
+					NULL, "push vlan action in egress "
+					"cannot be done before meter action");
+		}
+	}
 	/*
 	 * Hairpin flow will add one more TAG action in TX implicit mode.
 	 * In TX explicit mode, there will be no hairpin flow ID.
@@ -14113,38 +14187,24 @@  flow_dv_query(struct rte_eth_dev *dev,
  *
  * @param[in] dev
  *   Pointer to Ethernet device.
- * @param[in] tbl
- *   Pointer to the meter table set.
- *
- * @return
- *   Always 0.
+ * @param[in] fm
+ *   Meter information table.
  */
-static int
-flow_dv_destroy_mtr_tbl(struct rte_eth_dev *dev,
-			struct mlx5_meter_domains_infos *tbl)
+static void
+flow_dv_destroy_mtr_tbls(struct rte_eth_dev *dev,
+			struct mlx5_flow_meter_info *fm)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
-	struct mlx5_meter_domains_infos *mtd =
-				(struct mlx5_meter_domains_infos *)tbl;
+	int i;
 
-	if (!mtd || !priv->config.dv_flow_en)
-		return 0;
-	if (mtd->egress.tbl)
-		flow_dv_tbl_resource_release(MLX5_SH(dev), mtd->egress.tbl);
-	if (mtd->egress.sfx_tbl)
-		flow_dv_tbl_resource_release(MLX5_SH(dev), mtd->egress.sfx_tbl);
-	if (mtd->ingress.tbl)
-		flow_dv_tbl_resource_release(MLX5_SH(dev), mtd->ingress.tbl);
-	if (mtd->ingress.sfx_tbl)
-		flow_dv_tbl_resource_release(MLX5_SH(dev),
-					     mtd->ingress.sfx_tbl);
-	if (mtd->transfer.tbl)
-		flow_dv_tbl_resource_release(MLX5_SH(dev), mtd->transfer.tbl);
-	if (mtd->transfer.sfx_tbl)
-		flow_dv_tbl_resource_release(MLX5_SH(dev),
-					     mtd->transfer.sfx_tbl);
-	mlx5_free(mtd);
-	return 0;
+	if (!fm || !priv->config.dv_flow_en)
+		return;
+	for (i = 0; i < MLX5_MTR_DOMAIN_MAX; i++) {
+		if (fm->drop_rule[i]) {
+			claim_zero(mlx5_flow_os_destroy_flow(fm->drop_rule[i]));
+			fm->drop_rule[i] = NULL;
+		}
+	}
 }
 
 static void
@@ -14153,7 +14213,7 @@  flow_dv_destroy_mtr_drop_tbls(struct rte_eth_dev *dev)
 	struct mlx5_priv *priv = dev->data->dev_private;
 	struct mlx5_flow_mtr_mng *mtrmng = priv->sh->mtrmng;
 	struct mlx5_flow_tbl_data_entry *tbl;
-	int i;
+	int i, j;
 
 	for (i = 0; i < MLX5_MTR_DOMAIN_MAX; i++) {
 		if (mtrmng->def_rule[i]) {
@@ -14168,12 +14228,16 @@  flow_dv_destroy_mtr_drop_tbls(struct rte_eth_dev *dev)
 				      &mtrmng->def_matcher[i]->entry);
 			mtrmng->def_matcher[i] = NULL;
 		}
-		if (mtrmng->drop_matcher[i]) {
-			tbl = container_of(mtrmng->drop_matcher[i]->tbl,
-				struct mlx5_flow_tbl_data_entry, tbl);
-			mlx5_cache_unregister(&tbl->matchers,
-				      &mtrmng->drop_matcher[i]->entry);
-			mtrmng->drop_matcher[i] = NULL;
+		for (j = 0; j < MLX5_REG_BITS; j++) {
+			if (mtrmng->drop_matcher[i][j]) {
+				tbl =
+				container_of(mtrmng->drop_matcher[i][j]->tbl,
+					     struct mlx5_flow_tbl_data_entry,
+					     tbl);
+				mlx5_cache_unregister(&tbl->matchers,
+					&mtrmng->drop_matcher[i][j]->entry);
+				mtrmng->drop_matcher[i][j] = NULL;
+			}
 		}
 		if (mtrmng->drop_tbl[i]) {
 			flow_dv_tbl_resource_release(MLX5_SH(dev),
@@ -14626,96 +14690,171 @@  flow_dv_create_def_policy(struct rte_eth_dev *dev)
 }
 
 /**
- * Create specify domain meter table and suffix table.
+ * Create the needed meter tables.
+ * Lock free, (mutex should be acquired by caller).
  *
  * @param[in] dev
  *   Pointer to Ethernet device.
- * @param[in,out] mtb
- *   Pointer to DV meter table set.
- * @param[in] egress
- *   Table attribute.
- * @param[in] transfer
- *   Table attribute.
- *
+ * @param[in] fm
+ *   Meter information table.
+ * @param[in] mtr_idx
+ *   Meter index.
+ * @param[in] policy_id
+ *   Policy index.
+ * @param[in] domain_bitmap
+ *   Domain bitmap.
  * @return
  *   0 on success, -1 otherwise.
  */
 static int
-flow_dv_prepare_mtr_tables(struct rte_eth_dev *dev,
-			   struct mlx5_meter_domains_infos *mtb,
-			   uint8_t egress, uint8_t transfer)
+flow_dv_create_mtr_tbls(struct rte_eth_dev *dev,
+			struct mlx5_flow_meter_info *fm,
+			uint32_t mtr_idx,
+			uint8_t domain_bitmap)
 {
+	struct mlx5_priv *priv = dev->data->dev_private;
+	struct mlx5_flow_mtr_mng *mtrmng = priv->sh->mtrmng;
 	struct rte_flow_error error;
-	struct mlx5_meter_domain_info *dtb;
+	struct mlx5_flow_tbl_data_entry *tbl_data;
+	uint8_t egress, transfer;
+	void *actions[METER_ACTIONS];
+	int domain, ret, i;
+	struct mlx5_flow_counter *cnt;
+	struct mlx5_flow_dv_match_params value = {
+		.size = sizeof(value.buf) -
+		MLX5_ST_SZ_BYTES(fte_match_set_misc4),
+	};
+	struct mlx5_flow_dv_match_params matcher_para = {
+		.size = sizeof(matcher_para.buf) -
+		MLX5_ST_SZ_BYTES(fte_match_set_misc4),
+	};
+	int mtr_id_reg_c = mlx5_flow_get_reg_id(dev, MLX5_MTR_ID,
+						     0, &error);
+	uint32_t mtr_id_mask = (UINT32_C(1) << mtrmng->max_mtr_bits) - 1;
+	uint8_t mtr_id_offset = priv->mtr_reg_share ? MLX5_MTR_COLOR_BITS : 0;
+	struct mlx5_cache_entry *entry;
+	struct mlx5_flow_dv_matcher matcher = {
+		.mask = {
+			.size = sizeof(matcher.mask.buf) -
+			MLX5_ST_SZ_BYTES(fte_match_set_misc4),
+		},
+	};
+	struct mlx5_flow_dv_matcher *drop_matcher;
+	struct mlx5_flow_cb_ctx ctx = {
+		.error = &error,
+		.data = &matcher,
+	};
 
-	if (transfer)
-		dtb = &mtb->transfer;
-	else if (egress)
-		dtb = &mtb->egress;
-	else
-		dtb = &mtb->ingress;
-	/* Create the meter suffix table with SUFFIX level. */
-	dtb->sfx_tbl = flow_dv_tbl_resource_get(dev,
+	if (!priv->mtr_en || mtr_id_reg_c < 0) {
+		rte_errno = ENOTSUP;
+		return -1;
+	}
+	for (domain = 0; domain < MLX5_MTR_DOMAIN_MAX; domain++) {
+		if (!(domain_bitmap & (1 << domain)) ||
+			(mtrmng->def_rule[domain] && !fm->drop_cnt))
+			continue;
+		egress = (domain == MLX5_MTR_DOMAIN_EGRESS) ? 1 : 0;
+		transfer = (domain == MLX5_MTR_DOMAIN_TRANSFER) ? 1 : 0;
+		/* Create the drop table with METER DROP level. */
+		if (!mtrmng->drop_tbl[domain]) {
+			mtrmng->drop_tbl[domain] = flow_dv_tbl_resource_get(dev,
 					MLX5_FLOW_TABLE_LEVEL_METER,
 					egress, transfer, false, NULL, 0,
-					0, MLX5_MTR_TABLE_ID_SUFFIX, &error);
-	if (!dtb->sfx_tbl) {
-		DRV_LOG(ERR, "Failed to create meter suffix table.");
-		return -1;
+					0, MLX5_MTR_TABLE_ID_DROP, &error);
+			if (!mtrmng->drop_tbl[domain]) {
+				DRV_LOG(ERR, "Failed to create meter drop table.");
+				goto policy_error;
+			}
+		}
+		/* Create default matcher in drop table. */
+		matcher.tbl = mtrmng->drop_tbl[domain],
+		tbl_data = container_of(mtrmng->drop_tbl[domain],
+				struct mlx5_flow_tbl_data_entry, tbl);
+		if (!mtrmng->def_matcher[domain]) {
+			flow_dv_match_meta_reg(matcher.mask.buf, value.buf,
+				       (enum modify_reg)mtr_id_reg_c,
+				       0, 0);
+			matcher.priority = MLX5_MTRS_DEFAULT_RULE_PRIORITY;
+			matcher.crc = rte_raw_cksum
+					((const void *)matcher.mask.buf,
+					matcher.mask.size);
+			entry = mlx5_cache_register(&tbl_data->matchers, &ctx);
+			if (!entry) {
+				DRV_LOG(ERR, "Failed to register meter "
+				"drop default matcher.");
+				goto policy_error;
+			}
+			mtrmng->def_matcher[domain] = container_of(entry,
+			struct mlx5_flow_dv_matcher, entry);
+		}
+		/* Create default rule in drop table. */
+		if (!mtrmng->def_rule[domain]) {
+			i = 0;
+			actions[i++] = priv->sh->dr_drop_action;
+			flow_dv_match_meta_reg(matcher_para.buf, value.buf,
+				(enum modify_reg)mtr_id_reg_c, 0, 0);
+			ret = mlx5_flow_os_create_flow
+				(mtrmng->def_matcher[domain]->matcher_object,
+				(void *)&value, i, actions,
+				&mtrmng->def_rule[domain]);
+			if (ret) {
+				DRV_LOG(ERR, "Failed to create meter "
+				"default drop rule for drop table.");
+				goto policy_error;
+			}
+		}
+		if (!fm->drop_cnt)
+			continue;
+		MLX5_ASSERT(mtrmng->max_mtr_bits);
+		if (!mtrmng->drop_matcher[domain][mtrmng->max_mtr_bits - 1]) {
+			/* Create matchers for Drop. */
+			flow_dv_match_meta_reg(matcher.mask.buf, value.buf,
+					(enum modify_reg)mtr_id_reg_c, 0,
+					(mtr_id_mask << mtr_id_offset));
+			matcher.priority = MLX5_REG_BITS - mtrmng->max_mtr_bits;
+			matcher.crc = rte_raw_cksum
+					((const void *)matcher.mask.buf,
+					matcher.mask.size);
+			entry = mlx5_cache_register(&tbl_data->matchers, &ctx);
+			if (!entry) {
+				DRV_LOG(ERR,
+				"Failed to register meter drop matcher.");
+				goto policy_error;
+			}
+			mtrmng->drop_matcher[domain][mtrmng->max_mtr_bits - 1] =
+				container_of(entry, struct mlx5_flow_dv_matcher,
+					     entry);
+		}
+		drop_matcher =
+			mtrmng->drop_matcher[domain][mtrmng->max_mtr_bits - 1];
+		/* Create drop rule, matching meter_id only. */
+		flow_dv_match_meta_reg(matcher_para.buf, value.buf,
+				(enum modify_reg)mtr_id_reg_c,
+				(mtr_idx << mtr_id_offset), UINT32_MAX);
+		i = 0;
+		cnt = flow_dv_counter_get_by_idx(dev,
+					fm->drop_cnt, NULL);
+		actions[i++] = cnt->action;
+		actions[i++] = priv->sh->dr_drop_action;
+		ret = mlx5_flow_os_create_flow(drop_matcher->matcher_object,
+					       (void *)&value, i, actions,
+					       &fm->drop_rule[domain]);
+		if (ret) {
+			DRV_LOG(ERR, "Failed to create meter "
+				"drop rule for drop table.");
+				goto policy_error;
+		}
 	}
 	return 0;
-}
-
-/**
- * Create the needed meter and suffix tables.
- * Lock free, (mutex should be acquired by caller).
- *
- * @param[in] dev
- *   Pointer to Ethernet device.
- *
- * @return
- *   Pointer to table set on success, NULL otherwise and rte_errno is set.
- */
-static struct mlx5_meter_domains_infos *
-flow_dv_create_mtr_tbl(struct rte_eth_dev *dev)
-{
-	struct mlx5_priv *priv = dev->data->dev_private;
-	struct mlx5_meter_domains_infos *mtb;
-	int ret;
-
-	if (!priv->mtr_en) {
-		rte_errno = ENOTSUP;
-		return NULL;
-	}
-	mtb = mlx5_malloc(MLX5_MEM_ZERO, sizeof(*mtb), 0, SOCKET_ID_ANY);
-	if (!mtb) {
-		DRV_LOG(ERR, "Failed to allocate memory for meter.");
-		return NULL;
-	}
-	/* Egress meter table. */
-	ret = flow_dv_prepare_mtr_tables(dev, mtb, 1, 0);
-	if (ret) {
-		DRV_LOG(ERR, "Failed to prepare egress meter table.");
-		goto error_exit;
-	}
-	/* Ingress meter table. */
-	ret = flow_dv_prepare_mtr_tables(dev, mtb, 0, 0);
-	if (ret) {
-		DRV_LOG(ERR, "Failed to prepare ingress meter table.");
-		goto error_exit;
-	}
-	/* FDB meter table. */
-	if (priv->config.dv_esw_en) {
-		ret = flow_dv_prepare_mtr_tables(dev, mtb, 0, 1);
-		if (ret) {
-			DRV_LOG(ERR, "Failed to prepare fdb meter table.");
-			goto error_exit;
+policy_error:
+	for (i = 0; i < MLX5_MTR_DOMAIN_MAX; i++) {
+		if (fm->drop_rule[i]) {
+			claim_zero(mlx5_flow_os_destroy_flow
+				(fm->drop_rule[i]));
+			fm->drop_rule[i] = NULL;
 		}
 	}
-	return mtb;
-error_exit:
-	flow_dv_destroy_mtr_tbl(dev, mtb);
-	return NULL;
+	return -1;
 }
 
 /**
@@ -15296,8 +15435,8 @@  const struct mlx5_flow_driver_ops mlx5_flow_dv_drv_ops = {
 	.remove = flow_dv_remove,
 	.destroy = flow_dv_destroy,
 	.query = flow_dv_query,
-	.create_mtr_tbls = flow_dv_create_mtr_tbl,
-	.destroy_mtr_tbls = flow_dv_destroy_mtr_tbl,
+	.create_mtr_tbls = flow_dv_create_mtr_tbls,
+	.destroy_mtr_tbls = flow_dv_destroy_mtr_tbls,
 	.destroy_mtr_drop_tbls = flow_dv_destroy_mtr_drop_tbls,
 	.create_meter = flow_dv_mtr_alloc,
 	.free_meter = flow_dv_aso_mtr_release_to_pool,
diff --git a/drivers/net/mlx5/mlx5_flow_meter.c b/drivers/net/mlx5/mlx5_flow_meter.c
index 8b004efd7d..fe608a33ec 100644
--- a/drivers/net/mlx5/mlx5_flow_meter.c
+++ b/drivers/net/mlx5/mlx5_flow_meter.c
@@ -665,9 +665,12 @@  mlx5_flow_meter_policy_create(struct rte_eth_dev *dev,
 	for (i = 0; i < MLX5_MTR_DOMAIN_MAX; i++) {
 		if (!(domain_bitmap & (1 << i)))
 			continue;
-		mtr_policy->ingress = (i == MLX5_MTR_DOMAIN_INGRESS) ? 1 : 0;
-		mtr_policy->egress = (i == MLX5_MTR_DOMAIN_EGRESS) ? 1 : 0;
-		mtr_policy->transfer = (i == MLX5_MTR_DOMAIN_TRANSFER) ? 1 : 0;
+		if (i == MLX5_MTR_DOMAIN_INGRESS)
+			mtr_policy->ingress = 1;
+		if (i == MLX5_MTR_DOMAIN_EGRESS)
+			mtr_policy->egress = 1;
+		if (i == MLX5_MTR_DOMAIN_TRANSFER)
+			mtr_policy->transfer = 1;
 		sub_policy = mlx5_ipool_zmalloc
 				(priv->sh->ipool[MLX5_IPOOL_MTR_POLICY],
 				&sub_policy_idx);
@@ -779,6 +782,56 @@  mlx5_flow_meter_policy_delete(struct rte_eth_dev *dev,
 	return 0;
 }
 
+/**
+ * Check meter validation.
+ *
+ * @param[in] priv
+ *   Pointer to mlx5 private data structure.
+ * @param[in] meter_id
+ *   Meter id.
+ * @param[in] params
+ *   Pointer to rte meter parameters.
+ * @param[out] error
+ *   Pointer to rte meter error structure.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+mlx5_flow_meter_validate(struct mlx5_priv *priv, uint32_t meter_id,
+			 struct rte_mtr_params *params,
+			 struct rte_mtr_error *error)
+{
+	/* Meter must use global drop action. */
+	if (!priv->sh->dr_drop_action)
+		return -rte_mtr_error_set(error, ENOTSUP,
+					  RTE_MTR_ERROR_TYPE_MTR_PARAMS,
+					  NULL,
+					  "No drop action ready for meter.");
+	/* Meter params must not be NULL. */
+	if (params == NULL)
+		return -rte_mtr_error_set(error, EINVAL,
+					  RTE_MTR_ERROR_TYPE_MTR_PARAMS,
+					  NULL, "Meter object params null.");
+	/* Previous meter color is not supported. */
+	if (params->use_prev_mtr_color)
+		return -rte_mtr_error_set(error, ENOTSUP,
+					  RTE_MTR_ERROR_TYPE_MTR_PARAMS,
+					  NULL,
+					  "Previous meter color "
+					  "not supported.");
+	if (params->meter_policy_id == MLX5_INVALID_POLICY_ID)
+		return -rte_mtr_error_set(error, ENOENT,
+				RTE_MTR_ERROR_TYPE_METER_POLICY_ID,
+				NULL, "Meter policy id not valid.");
+	/* Validate meter id. */
+	if (mlx5_flow_meter_find(priv, meter_id, NULL))
+		return -rte_mtr_error_set(error, EEXIST,
+			RTE_MTR_ERROR_TYPE_MTR_ID, NULL,
+			"Meter object already exists.");
+	return 0;
+}
+
 /**
  * Modify the flow meter action.
  *
@@ -884,13 +937,195 @@  mlx5_flow_meter_action_modify(struct mlx5_priv *priv,
 #endif
 }
 
-static void
-mlx5_flow_meter_stats_enable_update(struct mlx5_flow_meter_info *fm,
+static int
+mlx5_flow_meter_stats_enable_update(struct rte_eth_dev *dev,
+				struct mlx5_flow_meter_info *fm,
 				uint64_t stats_mask)
 {
 	fm->bytes_dropped =
 		(stats_mask & RTE_MTR_STATS_N_BYTES_DROPPED) ? 1 : 0;
 	fm->pkts_dropped = (stats_mask & RTE_MTR_STATS_N_PKTS_DROPPED) ? 1 : 0;
+	if (fm->bytes_dropped || fm->pkts_dropped) {
+		if (!fm->drop_cnt) {
+			/* Alloc policer counters. */
+			fm->drop_cnt = mlx5_counter_alloc(dev);
+			if (!fm->drop_cnt)
+				return -1;
+		}
+	} else {
+		if (fm->drop_cnt) {
+			mlx5_counter_free(dev, fm->drop_cnt);
+			fm->drop_cnt = 0;
+		}
+	}
+	return 0;
+}
+
+/**
+ * Create meter rules.
+ *
+ * @param[in] dev
+ *   Pointer to Ethernet device.
+ * @param[in] meter_id
+ *   Meter id.
+ * @param[in] params
+ *   Pointer to rte meter parameters.
+ * @param[in] shared
+ *   Meter shared with other flow or not.
+ * @param[out] error
+ *   Pointer to rte meter error structure.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+mlx5_flow_meter_create(struct rte_eth_dev *dev, uint32_t meter_id,
+		       struct rte_mtr_params *params, int shared,
+		       struct rte_mtr_error *error)
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+	struct mlx5_legacy_flow_meters *fms = &priv->flow_meters;
+	struct mlx5_flow_meter_profile *fmp;
+	struct mlx5_flow_meter_info *fm;
+	struct mlx5_legacy_flow_meter *legacy_fm;
+	struct mlx5_flow_meter_policy *mtr_policy = NULL;
+	struct mlx5_indexed_pool_config flow_ipool_cfg = {
+		.size = 0,
+		.trunk_size = 64,
+		.need_lock = 1,
+		.type = "mlx5_flow_mtr_flow_id_pool",
+	};
+	struct mlx5_aso_mtr *aso_mtr;
+	uint32_t mtr_idx, policy_idx;
+	union mlx5_l3t_data data;
+	int ret;
+	uint8_t domain_bitmap;
+	uint8_t mtr_id_bits;
+	uint8_t mtr_reg_bits = priv->mtr_reg_share ?
+				MLX5_MTR_IDLE_BITS_IN_COLOR_REG : MLX5_REG_BITS;
+
+	if (!priv->mtr_en)
+		return -rte_mtr_error_set(error, ENOTSUP,
+					RTE_MTR_ERROR_TYPE_UNSPECIFIED, NULL,
+					"Meter is not supported");
+	/* Validate the parameters. */
+	ret = mlx5_flow_meter_validate(priv, meter_id, params, error);
+	if (ret)
+		return ret;
+	/* Meter profile must exist. */
+	fmp = mlx5_flow_meter_profile_find(priv, params->meter_profile_id);
+	if (fmp == NULL)
+		return -rte_mtr_error_set(error, ENOENT,
+			RTE_MTR_ERROR_TYPE_METER_PROFILE_ID,
+			NULL, "Meter profile id not valid.");
+	/* Meter policy must exist. */
+	if (params->meter_policy_id == priv->sh->mtrmng->def_policy_id) {
+		__atomic_add_fetch
+			(&priv->sh->mtrmng->def_policy_ref_cnt,
+			1, __ATOMIC_RELAXED);
+		domain_bitmap = MLX5_MTR_ALL_DOMAIN_BIT;
+		if (!priv->config.dv_esw_en)
+			domain_bitmap &= ~MLX5_MTR_DOMAIN_TRANSFER_BIT;
+	} else {
+		mtr_policy = mlx5_flow_meter_policy_find(dev,
+				params->meter_policy_id, &policy_idx);
+		if (!priv->sh->meter_aso_en)
+			return -rte_mtr_error_set(error, ENOTSUP,
+				RTE_MTR_ERROR_TYPE_UNSPECIFIED, NULL,
+				"Part of the policies cannot be "
+				"supported without ASO ");
+		if (!mtr_policy)
+			return -rte_mtr_error_set(error, ENOENT,
+				RTE_MTR_ERROR_TYPE_METER_POLICY_ID,
+				NULL, "Meter policy id not valid.");
+		domain_bitmap = (mtr_policy->ingress ?
+					MLX5_MTR_DOMAIN_INGRESS_BIT : 0) |
+				(mtr_policy->egress ?
+					MLX5_MTR_DOMAIN_EGRESS_BIT : 0) |
+				(mtr_policy->transfer ?
+					MLX5_MTR_DOMAIN_TRANSFER_BIT : 0);
+	}
+	/* Allocate the flow meter memory. */
+	if (priv->sh->meter_aso_en) {
+		mtr_idx = mlx5_flow_mtr_alloc(dev);
+		if (!mtr_idx)
+			return -rte_mtr_error_set(error, ENOMEM,
+				RTE_MTR_ERROR_TYPE_UNSPECIFIED, NULL,
+				"Memory alloc failed for meter.");
+		aso_mtr = mlx5_aso_meter_by_idx(priv, mtr_idx);
+		fm = &aso_mtr->fm;
+	} else {
+		legacy_fm = mlx5_ipool_zmalloc
+				(priv->sh->ipool[MLX5_IPOOL_MTR], &mtr_idx);
+		if (legacy_fm == NULL)
+			return -rte_mtr_error_set(error, ENOMEM,
+				RTE_MTR_ERROR_TYPE_UNSPECIFIED, NULL,
+				"Memory alloc failed for meter.");
+		legacy_fm->idx = mtr_idx;
+		fm = &legacy_fm->fm;
+	}
+	mtr_id_bits = MLX5_REG_BITS - __builtin_clz(mtr_idx);
+	if ((mtr_id_bits + priv->sh->mtrmng->max_mtr_flow_bits) >
+	    mtr_reg_bits) {
+		DRV_LOG(ERR, "Meter number exceeds max limit.");
+		goto error;
+	}
+	if (mtr_id_bits > priv->sh->mtrmng->max_mtr_bits)
+		priv->sh->mtrmng->max_mtr_bits = mtr_id_bits;
+	/* Fill the flow meter parameters. */
+	fm->meter_id = meter_id;
+	fm->policy_id = params->meter_policy_id;
+	fm->profile = fmp;
+	if (mlx5_flow_meter_stats_enable_update(dev, fm, params->stats_mask))
+		goto error;
+	if (mlx5_flow_create_mtr_tbls(dev, fm, mtr_idx, domain_bitmap))
+		goto error;
+	/* Add to the flow meter list. */
+	if (!priv->sh->meter_aso_en)
+		TAILQ_INSERT_TAIL(fms, legacy_fm, next);
+	/* Add to the flow meter list. */
+	fm->active_state = 1; /* Config meter starts as active. */
+	fm->is_enable = 1;
+	fm->shared = !!shared;
+	__atomic_add_fetch(&fm->profile->ref_cnt, 1, __ATOMIC_RELAXED);
+	if (params->meter_policy_id == priv->sh->mtrmng->def_policy_id) {
+		fm->def_policy = 1;
+		fm->flow_ipool = mlx5_ipool_create(&flow_ipool_cfg);
+		if (!fm->flow_ipool)
+			goto error;
+	}
+	rte_spinlock_init(&fm->sl);
+	/* If ASO meter supported, update ASO flow meter by wqe. */
+	if (priv->sh->meter_aso_en) {
+		aso_mtr = container_of(fm, struct mlx5_aso_mtr, fm);
+		ret = mlx5_aso_meter_update_by_wqe(priv->sh, aso_mtr);
+		if (ret)
+			goto error;
+		if (!priv->mtr_idx_tbl) {
+			priv->mtr_idx_tbl =
+				mlx5_l3t_create(MLX5_L3T_TYPE_DWORD);
+			if (!priv->mtr_idx_tbl)
+				goto error;
+		}
+		data.dword = mtr_idx;
+		if (mlx5_l3t_set_entry(priv->mtr_idx_tbl, meter_id, &data))
+			goto error;
+	}
+	if (mtr_policy)
+		__atomic_add_fetch(&mtr_policy->ref_cnt, 1, __ATOMIC_RELAXED);
+	return 0;
+error:
+	mlx5_flow_destroy_mtr_tbls(dev, fm);
+	/* Free policer counters. */
+	if (fm->drop_cnt)
+		mlx5_counter_free(dev, fm->drop_cnt);
+	if (priv->sh->meter_aso_en)
+		mlx5_flow_mtr_free(dev, mtr_idx);
+	else
+		mlx5_ipool_free(priv->sh->ipool[MLX5_IPOOL_MTR], mtr_idx);
+	return -rte_mtr_error_set(error, ENOTSUP,
+		RTE_MTR_ERROR_TYPE_UNSPECIFIED,
+		NULL, "Failed to create devx meter.");
 }
 
 static int
@@ -902,6 +1137,7 @@  mlx5_flow_meter_params_flush(struct rte_eth_dev *dev,
 	struct mlx5_legacy_flow_meters *fms = &priv->flow_meters;
 	struct mlx5_flow_meter_profile *fmp;
 	struct mlx5_legacy_flow_meter *legacy_fm = NULL;
+	struct mlx5_flow_meter_policy *mtr_policy;
 
 	/* Meter object must not have any owner. */
 	MLX5_ASSERT(!fm->ref_cnt);
@@ -911,23 +1147,42 @@  mlx5_flow_meter_params_flush(struct rte_eth_dev *dev,
 		return -1;
 	/* Update dependencies. */
 	__atomic_sub_fetch(&fmp->ref_cnt, 1, __ATOMIC_RELAXED);
+	fm->profile = NULL;
 	/* Remove from list. */
 	if (!priv->sh->meter_aso_en) {
-		legacy_fm = container_of(fm, struct mlx5_legacy_flow_meter, fm);
+		legacy_fm = container_of(fm,
+			struct mlx5_legacy_flow_meter, fm);
 		TAILQ_REMOVE(fms, legacy_fm, next);
 	}
 	/* Free drop counters. */
 	if (fm->drop_cnt)
 		mlx5_counter_free(dev, fm->drop_cnt);
 	/* Free meter flow table. */
-	if (fm->flow_ipool)
+	if (fm->flow_ipool) {
 		mlx5_ipool_destroy(fm->flow_ipool);
-	mlx5_flow_destroy_mtr_tbls(dev, fm->mfts);
-	if (priv->sh->meter_aso_en)
+		fm->flow_ipool = 0;
+	}
+	mlx5_flow_destroy_mtr_tbls(dev, fm);
+	if (fm->def_policy)
+		__atomic_sub_fetch(&priv->sh->mtrmng->def_policy_ref_cnt,
+				1, __ATOMIC_RELAXED);
+	if (priv->sh->meter_aso_en) {
+		if (!fm->def_policy) {
+			mtr_policy = mlx5_flow_meter_policy_find(dev,
+						fm->policy_id, NULL);
+			if (mtr_policy)
+				__atomic_sub_fetch(&mtr_policy->ref_cnt,
+						1, __ATOMIC_RELAXED);
+			fm->policy_id = 0;
+		}
+		fm->def_policy = 0;
+		if (mlx5_l3t_clear_entry(priv->mtr_idx_tbl, fm->meter_id))
+			return -1;
 		mlx5_flow_mtr_free(dev, mtr_idx);
-	else
+	} else {
 		mlx5_ipool_free(priv->sh->ipool[MLX5_IPOOL_MTR],
 					legacy_fm->idx);
+	}
 	return 0;
 }
 
@@ -954,30 +1209,28 @@  mlx5_flow_meter_destroy(struct rte_eth_dev *dev, uint32_t meter_id,
 
 	if (!priv->mtr_en)
 		return -rte_mtr_error_set(error, ENOTSUP,
-					  RTE_MTR_ERROR_TYPE_UNSPECIFIED, NULL,
+					  RTE_MTR_ERROR_TYPE_UNSPECIFIED,
+					  NULL,
 					  "Meter is not supported");
 	/* Meter object must exist. */
 	fm = mlx5_flow_meter_find(priv, meter_id, &mtr_idx);
 	if (fm == NULL)
 		return -rte_mtr_error_set(error, ENOENT,
 					  RTE_MTR_ERROR_TYPE_MTR_ID,
-					  NULL, "Meter object id not valid.");
+					  NULL,
+					  "Meter object id not valid.");
 	/* Meter object must not have any owner. */
 	if (fm->ref_cnt > 0)
 		return -rte_mtr_error_set(error, EBUSY,
 					  RTE_MTR_ERROR_TYPE_UNSPECIFIED,
-					  NULL, "Meter object is being used.");
-	if (priv->sh->meter_aso_en) {
-		if (mlx5_l3t_clear_entry(priv->mtr_idx_tbl, meter_id))
-			return -rte_mtr_error_set(error, EBUSY,
-				RTE_MTR_ERROR_TYPE_UNSPECIFIED, NULL,
-				"Fail to delete ASO Meter in index table.");
-	}
+					  NULL,
+					  "Meter object is being used.");
 	/* Destroy the meter profile. */
 	if (mlx5_flow_meter_params_flush(dev, fm, mtr_idx))
 		return -rte_mtr_error_set(error, EINVAL,
 					RTE_MTR_ERROR_TYPE_METER_PROFILE_ID,
-					NULL, "MTR object meter profile invalid.");
+					NULL,
+					"MTR object meter profile invalid.");
 	return 0;
 }
 
@@ -1211,7 +1464,11 @@  mlx5_flow_meter_stats_update(struct rte_eth_dev *dev,
 		return -rte_mtr_error_set(error, ENOENT,
 					  RTE_MTR_ERROR_TYPE_MTR_ID,
 					  NULL, "Meter object id not valid.");
-	mlx5_flow_meter_stats_enable_update(fm, stats_mask);
+	if (mlx5_flow_meter_stats_enable_update(dev, fm, stats_mask))
+		return -rte_mtr_error_set(error, ENOENT,
+					  RTE_MTR_ERROR_TYPE_MTR_ID,
+					  NULL, "Fail to allocate "
+					  "counter for meter.");
 	return 0;
 }
 
@@ -1289,6 +1546,7 @@  static const struct rte_mtr_ops mlx5_flow_mtr_ops = {
 	.meter_policy_validate = mlx5_flow_meter_policy_validate,
 	.meter_policy_create = mlx5_flow_meter_policy_create,
 	.meter_policy_delete = mlx5_flow_meter_policy_delete,
+	.create = mlx5_flow_meter_create,
 	.destroy = mlx5_flow_meter_destroy,
 	.meter_enable = mlx5_flow_meter_enable,
 	.meter_disable = mlx5_flow_meter_disable,
@@ -1327,7 +1585,7 @@  mlx5_flow_meter_ops_get(struct rte_eth_dev *dev __rte_unused, void *arg)
  *   Pointer to Meter index.
  *
  * @return
- *   Pointer to the profile found on success, NULL otherwise.
+ *   Pointer to the meter info found on success, NULL otherwise.
  */
 struct mlx5_flow_meter_info *
 mlx5_flow_meter_find(struct mlx5_priv *priv, uint32_t meter_id,
@@ -1342,30 +1600,27 @@  mlx5_flow_meter_find(struct mlx5_priv *priv, uint32_t meter_id,
 
 	if (priv->sh->meter_aso_en) {
 		rte_spinlock_lock(&pools_mng->mtrsl);
-		if (priv->mtr_idx_tbl) {
-			if (mlx5_l3t_get_entry(priv->mtr_idx_tbl,
-				meter_id, &data) ||
-				!data.dword) {
-				rte_spinlock_unlock(&pools_mng->mtrsl);
-				return NULL;
-			}
-			if (mtr_idx)
-				*mtr_idx = data.dword;
-			aso_mtr = mlx5_aso_meter_by_idx(priv, data.dword);
-			/* Remove reference taken by the mlx5_l3t_get_entry. */
-			mlx5_l3t_clear_entry(priv->mtr_idx_tbl, meter_id);
-		} else {
-			if (mtr_idx)
-				*mtr_idx = meter_id;
-			aso_mtr = mlx5_aso_meter_by_idx(priv, meter_id);
+		if (!pools_mng->n_valid || !priv->mtr_idx_tbl) {
+			rte_spinlock_unlock(&pools_mng->mtrsl);
+			return NULL;
 		}
+		if (mlx5_l3t_get_entry(priv->mtr_idx_tbl, meter_id, &data) ||
+			!data.dword) {
+			rte_spinlock_unlock(&pools_mng->mtrsl);
+			return NULL;
+		}
+		if (mtr_idx)
+			*mtr_idx = data.dword;
+		aso_mtr = mlx5_aso_meter_by_idx(priv, data.dword);
+		/* Remove reference taken by the mlx5_l3t_get_entry. */
+		mlx5_l3t_clear_entry(priv->mtr_idx_tbl, meter_id);
 		rte_spinlock_unlock(&pools_mng->mtrsl);
 		if (!aso_mtr || aso_mtr->state == ASO_METER_FREE)
 			return NULL;
 		return &aso_mtr->fm;
 	}
 	TAILQ_FOREACH(legacy_fm, fms, next)
-		if (meter_id == legacy_fm->meter_id) {
+		if (meter_id == legacy_fm->fm.meter_id) {
 			if (mtr_idx)
 				*mtr_idx = legacy_fm->idx;
 			return &legacy_fm->fm;
@@ -1382,7 +1637,7 @@  mlx5_flow_meter_find(struct mlx5_priv *priv, uint32_t meter_id,
  *   Meter index.
  *
  * @return
- *   Pointer to the profile found on success, NULL otherwise.
+ *   Pointer to the meter info found on success, NULL otherwise.
  */
 struct mlx5_flow_meter_info *
 flow_dv_meter_find_by_idx(struct mlx5_priv *priv, uint32_t idx)
@@ -1391,6 +1646,8 @@  flow_dv_meter_find_by_idx(struct mlx5_priv *priv, uint32_t idx)
 
 	if (priv->sh->meter_aso_en) {
 		aso_mtr = mlx5_aso_meter_by_idx(priv, idx);
+		if (!aso_mtr)
+			return NULL;
 		return &aso_mtr->fm;
 	} else {
 		return mlx5_ipool_get(priv->sh->ipool[MLX5_IPOOL_MTR], idx);