diff --git a/drivers/net/mlx5/linux/mlx5_os.c b/drivers/net/mlx5/linux/mlx5_os.c
index ee0d45796a..bec288c840 100644
--- a/drivers/net/mlx5/linux/mlx5_os.c
+++ b/drivers/net/mlx5/linux/mlx5_os.c
@@ -818,7 +818,6 @@ mlx5_dev_spawn(struct rte_device *dpdk_dev,
 	char name[RTE_ETH_NAME_MAX_LEN];
 	int own_domain_id = 0;
 	uint16_t port_id;
-	uint32_t log_obj_size;
 #ifdef HAVE_MLX5DV_DR_DEVX_PORT
 	struct mlx5dv_devx_port devx_port = { .comp_mask = 0 };
 #endif
@@ -1254,7 +1253,9 @@ mlx5_dev_spawn(struct rte_device *dpdk_dev,
 				"required for coalescing is %d bytes",
 				config->hca_attr.lro_min_mss_size);
 		}
-#if defined(HAVE_MLX5DV_DR) && defined(HAVE_MLX5_DR_CREATE_ACTION_FLOW_METER)
+#if defined(HAVE_MLX5DV_DR) && \
+	(defined(HAVE_MLX5_DR_CREATE_ACTION_FLOW_METER) || \
+	 defined(HAVE_MLX5_DR_CREATE_ACTION_ASO))
 		if (config->hca_attr.qos.sup &&
 		    config->hca_attr.qos.flow_meter_old &&
 		    config->dv_flow_en) {
@@ -1291,7 +1292,7 @@ mlx5_dev_spawn(struct rte_device *dpdk_dev,
 		}
 		if (config->hca_attr.qos.sup &&
 			config->hca_attr.qos.flow_meter_aso_sup) {
-			log_obj_size =
+			uint32_t log_obj_size =
 				rte_log2_u32(MLX5_ASO_MTRS_PER_POOL >> 1);
 			if (log_obj_size >=
 			config->hca_attr.qos.log_meter_aso_granularity &&
diff --git a/drivers/net/mlx5/mlx5.c b/drivers/net/mlx5/mlx5.c
index ca3c12918c..dac71fed95 100644
--- a/drivers/net/mlx5/mlx5.c
+++ b/drivers/net/mlx5/mlx5.c
@@ -607,11 +607,23 @@ mlx5_aso_flow_mtrs_mng_close(struct mlx5_dev_ctx_shared *sh)
 	struct mlx5_aso_mtr_pool *mtr_pool;
 	struct mlx5_aso_mtr_pools_mng *mtrmng = sh->mtrmng;
 	uint32_t idx;
+#ifdef HAVE_MLX5_DR_CREATE_ACTION_ASO
+	struct mlx5_aso_mtr *aso_mtr;
+	int i;
+#endif /* HAVE_MLX5_DR_CREATE_ACTION_ASO */
 
 	mlx5_aso_queue_uninit(sh, ASO_OPC_MOD_POLICER);
 	idx = mtrmng->n_valid;
 	while (idx--) {
 		mtr_pool = mtrmng->pools[idx];
+#ifdef HAVE_MLX5_DR_CREATE_ACTION_ASO
+		for (i = 0; i < MLX5_ASO_MTRS_PER_POOL; i++) {
+			aso_mtr = &mtr_pool->mtrs[i];
+			if (aso_mtr->fm.meter_action)
+				claim_zero(mlx5_glue->destroy_flow_action
+						(aso_mtr->fm.meter_action));
+		}
+#endif /* HAVE_MLX5_DR_CREATE_ACTION_ASO */
 		claim_zero(mlx5_devx_cmd_destroy
 						(mtr_pool->devx_obj));
 		mtrmng->n_valid--;
diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h
index 30372e586b..99ef83dd4b 100644
--- a/drivers/net/mlx5/mlx5.h
+++ b/drivers/net/mlx5/mlx5.h
@@ -625,8 +625,6 @@ struct mlx5_meter_domains_infos {
 	/**< Counters for green rule. */
 	void *drop_count;
 	/**< Counters for green rule. */
-	void *meter_action;
-	/**< Flow meter action. */
 };
 
 /* Meter parameter structure. */
@@ -689,6 +687,8 @@ struct mlx5_flow_meter_info {
 	/**< Use count. */
 	struct mlx5_indexed_pool *flow_ipool;
 	/**< Index pool for flow id. */
+	void *meter_action;
+	/**< Flow meter action. */
 };
 
 /* RFC2697 parameter structure. */
@@ -1478,7 +1478,8 @@ int mlx5_flow_meter_attach(struct mlx5_priv *priv,
 			   struct mlx5_flow_meter_info *fm,
 			   const struct rte_flow_attr *attr,
 			   struct rte_flow_error *error);
-void mlx5_flow_meter_detach(struct mlx5_flow_meter_info *fm);
+void mlx5_flow_meter_detach(struct mlx5_priv *priv,
+			    struct mlx5_flow_meter_info *fm);
 
 /* mlx5_os.c */
 struct rte_pci_driver;
diff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c
index ac39ac13ab..e97f68d2f5 100644
--- a/drivers/net/mlx5/mlx5_flow.c
+++ b/drivers/net/mlx5/mlx5_flow.c
@@ -4363,6 +4363,8 @@ flow_create_split_inner(struct rte_eth_dev *dev,
  *   Parent flow structure pointer.
  * @param[in] fm
  *   Pointer to flow meter structure.
+ * @param[in] attr
+ *   Flow rule attributes.
  * @param[in] items
  *   Pattern specification (list terminated by the END pattern item).
  * @param[out] sfx_items
@@ -4383,6 +4385,7 @@ static uint32_t
 flow_meter_split_prep(struct rte_eth_dev *dev,
 		      struct rte_flow *flow,
 		      struct mlx5_flow_meter_info *fm,
+		      const struct rte_flow_attr *attr,
 		      const struct rte_flow_item items[],
 		      struct rte_flow_item sfx_items[],
 		      const struct rte_flow_action actions[],
@@ -4400,6 +4403,12 @@ flow_meter_split_prep(struct rte_eth_dev *dev,
 	struct mlx5_rte_flow_item_tag *tag_item_mask;
 	uint32_t tag_id = 0;
 	bool copy_vlan = false;
+	struct rte_flow_action *hw_mtr_action;
+	struct rte_flow_action_jump *jump_data;
+	struct rte_flow_action *action_pre_head = NULL;
+	bool mtr_first = priv->sh->meter_aso_en &&
+			(attr->egress ||
+			(attr->transfer && priv->representor_id != UINT16_MAX));
 	uint8_t mtr_id_offset = priv->mtr_reg_share ? MLX5_MTR_COLOR_BITS : 0;
 	uint8_t mtr_reg_bits = priv->mtr_reg_share ?
 				MLX5_MTR_IDLE_BITS_IN_COLOR_REG : MLX5_REG_BITS;
@@ -4408,29 +4417,39 @@ flow_meter_split_prep(struct rte_eth_dev *dev,
 	uint8_t flow_id_bits = 0;
 	int shift;
 
+	/* For ASO meter, meter must be before tag in TX direction. */
+	if (mtr_first) {
+		action_pre_head = actions_pre++;
+		/* Leave space for tag action. */
+		tag_action = actions_pre++;
+	}
 	/* Prepare the actions for prefix and suffix flow. */
 	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
-		struct rte_flow_action **action_cur = NULL;
+		struct rte_flow_action *action_cur = NULL;
 
 		switch (actions->type) {
 		case RTE_FLOW_ACTION_TYPE_METER:
-			/* Add the extra tag action first. */
-			tag_action = actions_pre++;
-			action_cur = &actions_pre;
+			if (mtr_first) {
+				action_cur = action_pre_head;
+			} else {
+				/* Leave space for tag action. */
+				tag_action = actions_pre++;
+				action_cur = actions_pre++;
+			}
 			break;
 		case RTE_FLOW_ACTION_TYPE_VXLAN_DECAP:
 		case RTE_FLOW_ACTION_TYPE_NVGRE_DECAP:
-			action_cur = &actions_pre;
+			action_cur = actions_pre++;
 			break;
 		case RTE_FLOW_ACTION_TYPE_RAW_ENCAP:
 			raw_encap = actions->conf;
 			if (raw_encap->size < MLX5_ENCAPSULATION_DECISION_SIZE)
-				action_cur = &actions_pre;
+				action_cur = actions_pre++;
 			break;
 		case RTE_FLOW_ACTION_TYPE_RAW_DECAP:
 			raw_decap = actions->conf;
 			if (raw_decap->size > MLX5_ENCAPSULATION_DECISION_SIZE)
-				action_cur = &actions_pre;
+				action_cur = actions_pre++;
 			break;
 		case RTE_FLOW_ACTION_TYPE_OF_PUSH_VLAN:
 		case RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_VID:
@@ -4440,14 +4459,32 @@ flow_meter_split_prep(struct rte_eth_dev *dev,
 			break;
 		}
 		if (!action_cur)
-			action_cur = &actions_sfx;
-		memcpy(*action_cur, actions, sizeof(struct rte_flow_action));
-		(*action_cur)++;
+			action_cur = actions_sfx++;
+		memcpy(action_cur, actions, sizeof(struct rte_flow_action));
 	}
 	/* Add end action to the actions. */
 	actions_sfx->type = RTE_FLOW_ACTION_TYPE_END;
-	actions_pre->type = RTE_FLOW_ACTION_TYPE_END;
-	actions_pre++;
+	if (priv->sh->meter_aso_en) {
+		/**
+		 * For ASO meter, need to add an extra jump action explicitly,
+		 * to jump from meter to policer table.
+		 */
+		hw_mtr_action = actions_pre;
+		hw_mtr_action->type = RTE_FLOW_ACTION_TYPE_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 ?
+				(MLX5_FLOW_TABLE_LEVEL_METER - 1) :
+				 MLX5_FLOW_TABLE_LEVEL_METER;
+		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,
@@ -5264,10 +5301,11 @@ flow_create_split_meter(struct rte_eth_dev *dev,
 			flow->meter = mtr_idx;
 		}
 		wks->fm = fm;
-		/* The prefix actions: meter, decap, encap, tag, end. */
-		act_size = sizeof(struct rte_flow_action) * (actions_n + 5) +
-			   sizeof(struct mlx5_rte_flow_action_set_tag);
-		/* The suffix items: tag, vlan, port id, end. */
+		/* 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) +
+			   sizeof(struct rte_flow_action_jump);
+		/* Suffix items: tag, vlan, port id, end. */
 #define METER_SUFFIX_ITEM 4
 		item_size = sizeof(struct rte_flow_item) * METER_SUFFIX_ITEM +
 			    sizeof(struct mlx5_rte_flow_item_tag) * 2;
@@ -5281,8 +5319,8 @@ flow_create_split_meter(struct rte_eth_dev *dev,
 		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, items,
-						   sfx_items, actions,
+		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) {
diff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c
index d9c9ab7ca7..8f2ea71357 100644
--- a/drivers/net/mlx5/mlx5_flow_dv.c
+++ b/drivers/net/mlx5/mlx5_flow_dv.c
@@ -4890,9 +4890,11 @@ mlx5_flow_validate_action_meter(struct rte_eth_dev *dev,
 		return rte_flow_error_set(error, EINVAL,
 					  RTE_FLOW_ERROR_TYPE_ACTION, NULL,
 					  "Meter not found");
-	if (fm->ref_cnt && (!(fm->transfer == attr->transfer ||
+	/* aso meter can always be shared by different domains */
+	if (fm->ref_cnt && !priv->sh->meter_aso_en &&
+	    !(fm->transfer == attr->transfer ||
 	      (!fm->ingress && !attr->ingress && attr->egress) ||
-	      (!fm->egress && !attr->egress && attr->ingress))))
+	      (!fm->egress && !attr->egress && attr->ingress)))
 		return rte_flow_error_set(error, EINVAL,
 					  RTE_FLOW_ERROR_TYPE_ACTION, NULL,
 					  "Flow attributes are either invalid "
@@ -6095,6 +6097,25 @@ flow_dv_mtr_alloc(struct rte_eth_dev *dev)
 					struct mlx5_aso_mtr_pool,
 					mtrs[mtr_free->offset]);
 	mtr_idx = MLX5_MAKE_MTR_IDX(pool->index, mtr_free->offset);
+	if (!mtr_free->fm.meter_action) {
+#ifdef HAVE_MLX5_DR_CREATE_ACTION_ASO
+		struct rte_flow_error error;
+		uint8_t reg_id;
+
+		reg_id = mlx5_flow_get_reg_id(dev, MLX5_MTR_COLOR, 0, &error);
+		mtr_free->fm.meter_action =
+			mlx5_glue->dv_create_flow_action_aso
+						(priv->sh->rx_domain,
+						 pool->devx_obj->obj,
+						 mtr_free->offset,
+						 (1 << MLX5_FLOW_COLOR_GREEN),
+						 reg_id - REG_C_0);
+#endif /* HAVE_MLX5_DR_CREATE_ACTION_ASO */
+		if (!mtr_free->fm.meter_action) {
+			flow_dv_aso_mtr_release_to_pool(dev, mtr_idx);
+			return 0;
+		}
+	}
 	return mtr_idx;
 }
 
@@ -11579,7 +11600,7 @@ flow_dv_translate(struct rte_eth_dev *dev,
 					NULL, "Failed to get meter in flow.");
 			/* Set the meter action. */
 			dev_flow->dv.actions[actions_n++] =
-				wks->fm->mfts->meter_action;
+				wks->fm->meter_action;
 			action_flags |= MLX5_FLOW_ACTION_METER;
 			break;
 		case RTE_FLOW_ACTION_TYPE_SET_IPV4_DSCP:
@@ -12741,7 +12762,7 @@ flow_dv_destroy(struct rte_eth_dev *dev, struct rte_flow *flow)
 	if (flow->meter) {
 		fm = flow_dv_meter_find_by_idx(priv, flow->meter);
 		if (fm)
-			mlx5_flow_meter_detach(fm);
+			mlx5_flow_meter_detach(priv, fm);
 		flow->meter = 0;
 	}
 	if (flow->age)
diff --git a/drivers/net/mlx5/mlx5_flow_meter.c b/drivers/net/mlx5/mlx5_flow_meter.c
index fcefc135b4..714b382d55 100644
--- a/drivers/net/mlx5/mlx5_flow_meter.c
+++ b/drivers/net/mlx5/mlx5_flow_meter.c
@@ -602,9 +602,9 @@ mlx5_flow_meter_action_modify(struct mlx5_priv *priv,
 				ebs_mantissa, val);
 		}
 		/* Apply modifications to meter only if it was created. */
-		if (fm->mfts->meter_action) {
+		if (fm->meter_action) {
 			ret = mlx5_glue->dv_modify_flow_action_meter
-					(fm->mfts->meter_action, &mod_attr,
+					(fm->meter_action, &mod_attr,
 					rte_cpu_to_be_64(modify_bits));
 			if (ret)
 				return ret;
@@ -620,6 +620,7 @@ mlx5_flow_meter_action_modify(struct mlx5_priv *priv,
 	(void)srtcm;
 	(void)modify_bits;
 	(void)active_state;
+	(void)is_enable;
 	return -ENOTSUP;
 #endif
 }
@@ -1405,63 +1406,91 @@ mlx5_flow_meter_attach(struct mlx5_priv *priv,
 {
 	int ret = 0;
 
-	rte_spinlock_lock(&fm->sl);
-	if (fm->mfts->meter_action) {
-		if (fm->shared &&
-		    attr->transfer == fm->transfer &&
-		    attr->ingress == fm->ingress &&
-		    attr->egress == fm->egress)
+	if (priv->sh->meter_aso_en) {
+		struct mlx5_aso_mtr *aso_mtr;
+
+		aso_mtr = container_of(fm, struct mlx5_aso_mtr, fm);
+		if (mlx5_aso_mtr_wait(priv->sh, aso_mtr)) {
+			return rte_flow_error_set(error, ENOENT,
+					RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+					NULL,
+					"Timeout in meter configuration");
+		}
+		rte_spinlock_lock(&fm->sl);
+		if (fm->shared || !fm->ref_cnt) {
 			fm->ref_cnt++;
-		else
+		} else {
+			rte_flow_error_set(error, EINVAL,
+				   RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+				   "Meter cannot be shared");
 			ret = -1;
+		}
+		rte_spinlock_unlock(&fm->sl);
 	} else {
-		fm->ingress = attr->ingress;
-		fm->egress = attr->egress;
-		fm->transfer = attr->transfer;
-		 fm->ref_cnt = 1;
-		/* This also creates the meter object. */
-		fm->mfts->meter_action = mlx5_flow_meter_action_create(priv,
-								       fm);
-		if (!fm->mfts->meter_action) {
-			fm->ref_cnt = 0;
-			fm->ingress = 0;
-			fm->egress = 0;
-			fm->transfer = 0;
-			ret = -1;
-			DRV_LOG(ERR, "Meter action create failed.");
+		rte_spinlock_lock(&fm->sl);
+		if (fm->meter_action) {
+			if (fm->shared &&
+			    attr->transfer == fm->transfer &&
+			    attr->ingress == fm->ingress &&
+			    attr->egress == fm->egress) {
+				fm->ref_cnt++;
+			} else {
+				rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+					fm->shared ?
+					"Meter attr not match." :
+					"Meter cannot be shared.");
+				ret = -1;
+			}
+		} else {
+			fm->ingress = attr->ingress;
+			fm->egress = attr->egress;
+			fm->transfer = attr->transfer;
+			fm->ref_cnt = 1;
+			/* This also creates the meter object. */
+			fm->meter_action = mlx5_flow_meter_action_create(priv,
+									 fm);
+			if (!fm->meter_action) {
+				fm->ref_cnt = 0;
+				fm->ingress = 0;
+				fm->egress = 0;
+				fm->transfer = 0;
+				rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+					"Meter action create failed.");
+				ret = -1;
+			}
 		}
+		rte_spinlock_unlock(&fm->sl);
 	}
-	rte_spinlock_unlock(&fm->sl);
-	if (ret)
-		rte_flow_error_set(error, EINVAL,
-				   RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
-				   fm->mfts->meter_action ?
-				   "Meter attr not match" :
-				   "Meter action create failed");
 	return ret ? -rte_errno : 0;
 }
 
 /**
  * Detach meter from flow.
  *
+ * @param [in] priv
+ *  Pointer to mlx5 private data.
  * @param [in] fm
  *  Pointer to flow meter.
  */
 void
-mlx5_flow_meter_detach(struct mlx5_flow_meter_info *fm)
+mlx5_flow_meter_detach(struct mlx5_priv *priv,
+		       struct mlx5_flow_meter_info *fm)
 {
 #ifdef HAVE_MLX5_DR_CREATE_ACTION_FLOW_METER
 	rte_spinlock_lock(&fm->sl);
 	MLX5_ASSERT(fm->ref_cnt);
-	if (--fm->ref_cnt == 0) {
-		mlx5_glue->destroy_flow_action(fm->mfts->meter_action);
-		fm->mfts->meter_action = NULL;
+	if (--fm->ref_cnt == 0 && !priv->sh->meter_aso_en) {
+		mlx5_glue->destroy_flow_action(fm->meter_action);
+		fm->meter_action = NULL;
 		fm->ingress = 0;
 		fm->egress = 0;
 		fm->transfer = 0;
 	}
 	rte_spinlock_unlock(&fm->sl);
 #else
+	(void)priv;
 	(void)fm;
 #endif
 }
