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 b38ff77210..e97a1d77a6 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_add(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_add = mlx5_flow_meter_policy_add,
 	.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);
@@ -1526,32 +1783,33 @@ int
 mlx5_flow_meter_flush(struct rte_eth_dev *dev, struct rte_mtr_error *error)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
-	struct mlx5_aso_mtr_pools_mng *pools_mng =
-				&priv->sh->mtrmng->pools_mng;
 	struct mlx5_legacy_flow_meters *fms = &priv->flow_meters;
 	struct mlx5_mtr_profiles *fmps = &priv->flow_meter_profiles;
 	struct mlx5_flow_meter_profile *fmp;
 	struct mlx5_legacy_flow_meter *legacy_fm;
 	struct mlx5_flow_meter_info *fm;
-	struct mlx5_aso_mtr_pool *mtr_pool;
 	struct mlx5_flow_meter_sub_policy *sub_policy;
 	void *tmp;
-	uint32_t i, offset, mtr_idx, policy_idx;
+	uint32_t i, mtr_idx, policy_idx;
 	void *entry;
+	struct mlx5_aso_mtr *aso_mtr;
 
 	if (!priv->mtr_en)
 		return 0;
 	if (priv->sh->meter_aso_en) {
-		i = pools_mng->n_valid;
-		while (i--) {
-			mtr_pool = pools_mng->pools[i];
-			for (offset = 0; offset < MLX5_ASO_MTRS_PER_POOL;
-				offset++) {
-				fm = &mtr_pool->mtrs[offset].fm;
-				mtr_idx = MLX5_MAKE_MTR_IDX(i, offset);
-				(void)mlx5_flow_meter_params_flush(dev,
+		if (priv->mtr_idx_tbl) {
+			MLX5_L3T_FOREACH(priv->mtr_idx_tbl, i, entry) {
+				mtr_idx = *(uint32_t *)entry;
+				if (mtr_idx) {
+					aso_mtr =
+					mlx5_aso_meter_by_idx(priv, mtr_idx);
+					fm = &aso_mtr->fm;
+					(void)mlx5_flow_meter_params_flush(dev,
 						fm, mtr_idx);
+				}
 			}
+			mlx5_l3t_destroy(priv->mtr_idx_tbl);
+			priv->mtr_idx_tbl = NULL;
 		}
 	} else {
 		TAILQ_FOREACH_SAFE(legacy_fm, fms, next, tmp) {
@@ -1562,7 +1820,7 @@ mlx5_flow_meter_flush(struct rte_eth_dev *dev, struct rte_mtr_error *error)
 				NULL, "MTR object meter profile invalid.");
 		}
 	}
-	if (priv->sh->mtrmng->policy_idx_tbl) {
+	if (priv->sh->mtrmng->policy_idx_tbl && priv->sh->refcnt == 1) {
 		MLX5_L3T_FOREACH(priv->sh->mtrmng->policy_idx_tbl,
 					i, entry) {
 			policy_idx = *(uint32_t *)entry;
@@ -1597,6 +1855,7 @@ mlx5_flow_meter_flush(struct rte_eth_dev *dev, struct rte_mtr_error *error)
 	}
 	/* Delete default policy table. */
 	mlx5_flow_destroy_def_policy(dev);
-	mlx5_flow_destroy_mtr_drop_tbls(dev);
+	if (priv->sh->refcnt == 1)
+		mlx5_flow_destroy_mtr_drop_tbls(dev);
 	return 0;
 }
