diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h
index bd91e16..98f2e5d 100644
--- a/drivers/net/mlx5/mlx5.h
+++ b/drivers/net/mlx5/mlx5.h
@@ -339,6 +339,7 @@ struct mlx5_age_param {
 	uint16_t port_id; /**< Port id of the counter. */
 	uint32_t timeout:15; /**< Age timeout in unit of 0.1sec. */
 	uint32_t expire:16; /**< Expire time(0.1sec) in the future. */
+	uint32_t last_hit_time; /**< Last hit time in seconds. */
 	void *context; /**< Flow counter age context. */
 };
 
@@ -461,6 +462,12 @@ struct mlx5_flow_default_miss_resource {
 	((age_info)->flags & (1 << (BIT)))
 #define GET_PORT_AGE_INFO(priv) \
 	(&((priv)->sh->port[(priv)->dev_port - 1].age_info))
+/* Current time in seconds. */
+#define MLX5_CURR_TIME_SEC	(rte_rdtsc() / rte_get_tsc_hz())
+#define MLX5_AGE_UNITS_PER_SEC	10 /* Aging is calculated in 0.1 sec units. */
+/* Current time in defined units. */
+#define MLX5_AGE_CURR_TIME \
+	(rte_rdtsc() / (rte_get_tsc_hz() / MLX5_AGE_UNITS_PER_SEC))
 
 /* Aging information for per port. */
 struct mlx5_age_info {
diff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c
index ffa7646..0043fe4 100644
--- a/drivers/net/mlx5/mlx5_flow.c
+++ b/drivers/net/mlx5/mlx5_flow.c
@@ -6039,7 +6039,7 @@ struct mlx5_meter_domains_infos *
 	struct mlx5_age_param *age_param;
 	struct mlx5_counter_stats_raw *cur = pool->raw_hw;
 	struct mlx5_counter_stats_raw *prev = pool->raw;
-	uint16_t curr = rte_rdtsc() / (rte_get_tsc_hz() / 10);
+	uint16_t curr = MLX5_AGE_CURR_TIME;
 	uint32_t i;
 
 	for (i = 0; i < MLX5_COUNTERS_PER_POOL; ++i) {
@@ -6049,6 +6049,7 @@ struct mlx5_meter_domains_infos *
 			continue;
 		if (cur->data[i].hits != prev->data[i].hits) {
 			age_param->expire = curr + age_param->timeout;
+			age_param->last_hit_time = MLX5_CURR_TIME_SEC;
 			continue;
 		}
 		if ((uint16_t)(curr - age_param->expire) >= (UINT16_MAX / 2))
diff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c
index 79fdf34..28389c6 100644
--- a/drivers/net/mlx5/mlx5_flow_dv.c
+++ b/drivers/net/mlx5/mlx5_flow_dv.c
@@ -8015,21 +8015,17 @@ struct field_modify_info modify_tcp[] = {
 	if (!counter || age == NULL)
 		return counter;
 	age_param  = flow_dv_counter_idx_get_age(dev, counter);
-	/*
-	 * The counter age accuracy may have a bit delay. Have 3/4
-	 * second bias on the timeount in order to let it age in time.
-	 */
 	age_param->context = age->context ? age->context :
 		(void *)(uintptr_t)(dev_flow->flow_idx);
 	/*
 	 * The counter age accuracy may have a bit delay. Have 3/4
-	 * second bias on the timeount in order to let it age in time.
+	 * second bias on the timeout in order to let it age in time.
 	 */
 	age_param->timeout = age->timeout * 10 - MLX5_AGING_TIME_DELAY;
-	/* Set expire time in unit of 0.1 sec. */
 	age_param->port_id = dev->data->port_id;
-	age_param->expire = age_param->timeout +
-			rte_rdtsc() / (rte_get_tsc_hz() / 10);
+	/* Set expire time in unit of 0.1 sec. */
+	age_param->expire = age_param->timeout + MLX5_AGE_CURR_TIME;
+	age_param->last_hit_time = MLX5_CURR_TIME_SEC;
 	rte_atomic16_set(&age_param->state, AGE_CANDIDATE);
 	return counter;
 }
@@ -9529,6 +9525,50 @@ struct field_modify_info modify_tcp[] = {
 }
 
 /**
+ * Query a flow rule AGE action for aging information.
+ *
+ * @param[in] dev
+ *   Pointer to Ethernet device.
+ * @param[in] flow
+ *   Pointer to the sub flow.
+ * @param[out] data
+ *   data retrieved by the query.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+flow_dv_query_age(struct rte_eth_dev *dev, struct rte_flow *flow,
+		  void *data, struct rte_flow_error *error)
+{
+	struct rte_flow_query_age *resp = data;
+
+	if (flow->counter) {
+		struct mlx5_age_param *age_param =
+				flow_dv_counter_idx_get_age(dev, flow->counter);
+
+		if (!age_param || !age_param->timeout)
+			return rte_flow_error_set
+					(error, EINVAL,
+					 RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+					 NULL, "cannot read age data");
+		resp->aged = rte_atomic16_read(&age_param->state) ==
+				AGE_TMOUT ? 1 : 0;
+		resp->last_hit_time_valid = !resp->aged;
+		if (resp->last_hit_time_valid)
+			resp->sec_since_last_hit =
+				MLX5_CURR_TIME_SEC - age_param->last_hit_time;
+		return 0;
+	}
+	return rte_flow_error_set(error, EINVAL,
+				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				  NULL,
+				  "age data not available");
+}
+
+/**
  * Query a flow.
  *
  * @see rte_flow_query()
@@ -9550,6 +9590,9 @@ struct field_modify_info modify_tcp[] = {
 		case RTE_FLOW_ACTION_TYPE_COUNT:
 			ret = flow_dv_query_count(dev, flow, data, error);
 			break;
+		case RTE_FLOW_ACTION_TYPE_AGE:
+			ret = flow_dv_query_age(dev, flow, data, error);
+			break;
 		default:
 			return rte_flow_error_set(error, ENOTSUP,
 						  RTE_FLOW_ERROR_TYPE_ACTION,
