#define CNT_SIZE (sizeof(struct mlx5_flow_counter))
#define CNTEXT_SIZE (sizeof(struct mlx5_flow_counter_ext))
#define AGE_SIZE (sizeof(struct mlx5_age_param))
-#define MLX5_AGING_TIME_DELAY 7
#define CNT_POOL_TYPE_EXT (1 << 0)
#define CNT_POOL_TYPE_AGE (1 << 1)
#define IS_EXT_POOL(pool) (((pool)->type) & CNT_POOL_TYPE_EXT)
*/
#define POOL_IDX_INVALID UINT16_MAX
-struct mlx5_flow_counter_pool;
-
-/*age status*/
+/* Age status. */
enum {
AGE_FREE, /* Initialized state. */
AGE_CANDIDATE, /* Counter assigned to flows. */
/* Counter age parameter. */
struct mlx5_age_param {
- rte_atomic16_t state; /**< Age state. */
+ uint16_t state; /**< Age state (atomically accessed). */
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 timeout:24; /**< Aging timeout in seconds. */
+ uint32_t sec_since_last_hit;
+ /**< Time in seconds since last hit (atomically accessed). */
void *context; /**< Flow counter age context. */
};
uint64_t bytes;
};
-struct mlx5_flow_counter_pool;
/* Generic counters information. */
struct mlx5_flow_counter {
TAILQ_ENTRY(mlx5_flow_counter) next;
rte_atomic64_t a64_dcs;
};
/* The devx object of the minimum counter ID. */
+ uint64_t time_of_last_age_check;
+ /* System time (from rte_rdtsc()) read in the last aging check. */
uint32_t index:28; /* Pool index in container. */
uint32_t type:2; /* Memory type behind the counter array. */
uint32_t skip_cnt:1; /* Pool contains skipped counter. */
struct mlx5_counter_stats_raw *raw_hw; /* The raw on HW working. */
};
-struct mlx5_counter_stats_raw;
-
/* Memory management structure for group of counter statistics raws. */
struct mlx5_counter_stats_mem_mng {
LIST_ENTRY(mlx5_counter_stats_mem_mng) next;
((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())
/* Aging information for per port. */
struct mlx5_age_info {
- uint8_t flags; /*Indicate if is new event or need be trigered*/
+ uint8_t flags; /* Indicate if is new event or need to be triggered. */
struct mlx5_counters aged_counters; /* Aged flow counter list. */
rte_spinlock_t aged_sl; /* Aged flow counter list lock. */
};
offset = batch ? 0 : dcs->id % MLX5_COUNTERS_PER_POOL;
/*
* Identify the counters released between query trigger and query
- * handle more effiecntly. The counter released in this gap period
+ * handle more efficiently. The counter released in this gap period
* should wait for a new round of query as the new arrived packets
* will not be taken into account.
*/
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);
+ const uint64_t curr_time = MLX5_CURR_TIME_SEC;
+ const uint32_t time_delta = curr_time - pool->time_of_last_age_check;
+ uint16_t expected = AGE_CANDIDATE;
uint32_t i;
+ pool->time_of_last_age_check = curr_time;
for (i = 0; i < MLX5_COUNTERS_PER_POOL; ++i) {
cnt = MLX5_POOL_GET_CNT(pool, i);
age_param = MLX5_CNT_TO_AGE(cnt);
- if (rte_atomic16_read(&age_param->state) != AGE_CANDIDATE)
+ if (__atomic_load_n(&age_param->state,
+ __ATOMIC_RELAXED) != AGE_CANDIDATE)
continue;
if (cur->data[i].hits != prev->data[i].hits) {
- age_param->expire = curr + age_param->timeout;
+ __atomic_store_n(&age_param->sec_since_last_hit, 0,
+ __ATOMIC_RELAXED);
continue;
}
- if ((uint16_t)(curr - age_param->expire) >= (UINT16_MAX / 2))
+ if (__atomic_add_fetch(&age_param->sec_since_last_hit,
+ time_delta,
+ __ATOMIC_RELAXED) <= age_param->timeout)
continue;
/**
* Hold the lock first, or if between the
priv = rte_eth_devices[age_param->port_id].data->dev_private;
age_info = GET_PORT_AGE_INFO(priv);
rte_spinlock_lock(&age_info->aged_sl);
- if (rte_atomic16_cmpset((volatile uint16_t *)&age_param->state,
- AGE_CANDIDATE, AGE_TMOUT)) {
+ if (__atomic_compare_exchange_n(&age_param->state, &expected,
+ AGE_TMOUT, false,
+ __ATOMIC_RELAXED,
+ __ATOMIC_RELAXED)) {
TAILQ_INSERT_TAIL(&age_info->aged_counters, cnt, next);
MLX5_AGE_SET(age_info, MLX5_AGE_EVENT_NEW);
}
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION, action,
"configuration cannot be null");
- if (age->timeout >= UINT16_MAX / 2 / 10)
- return rte_flow_error_set(error, ENOTSUP,
+ if (!(age->timeout))
+ return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION, action,
- "Max age time: 3275 seconds");
+ "invalid timeout value 0");
if (action_flags & MLX5_FLOW_ACTION_AGE)
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION, NULL,
- "Duplicate age ctions set");
+ "duplicate age actions set");
return 0;
}
TAILQ_INIT(&pool->counters[1]);
TAILQ_INSERT_HEAD(&cont->pool_list, pool, next);
pool->index = n_valid;
+ pool->time_of_last_age_check = MLX5_CURR_TIME_SEC;
cont->pools[n_valid] = pool;
if (!batch) {
int base = RTE_ALIGN_FLOOR(dcs->id, MLX5_COUNTERS_PER_POOL);
(MLX5_CNT_CONTAINER
(priv->sh, batch, (age ^ 0x1)), dcs->id);
/*
- * Pool eixsts, counter will be added to the other
+ * Pool exists, counter will be added to the other
* container, need to reallocate it later.
*/
if (pool) {
struct mlx5_age_info *age_info;
struct mlx5_age_param *age_param;
struct mlx5_priv *priv = dev->data->dev_private;
+ uint16_t expected = AGE_CANDIDATE;
age_info = GET_PORT_AGE_INFO(priv);
age_param = flow_dv_counter_idx_get_age(dev, counter);
- if (!rte_atomic16_cmpset((volatile uint16_t *)&age_param->state,
- AGE_CANDIDATE, AGE_FREE)) {
+ if (!__atomic_compare_exchange_n(&age_param->state, &expected,
+ AGE_FREE, false, __ATOMIC_RELAXED,
+ __ATOMIC_RELAXED)) {
/**
* We need the lock even it is age timeout,
* since counter may still in process.
rte_spinlock_lock(&age_info->aged_sl);
TAILQ_REMOVE(&age_info->aged_counters, cnt, next);
rte_spinlock_unlock(&age_info->aged_sl);
- rte_atomic16_set(&age_param->state, AGE_FREE);
+ __atomic_store_n(&age_param->state, AGE_FREE, __ATOMIC_RELAXED);
}
}
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.
- */
- age_param->timeout = age->timeout * 10 - MLX5_AGING_TIME_DELAY;
- /* Set expire time in unit of 0.1 sec. */
+ age_param->timeout = age->timeout;
age_param->port_id = dev->data->port_id;
- age_param->expire = age_param->timeout +
- rte_rdtsc() / (rte_get_tsc_hz() / 10);
- rte_atomic16_set(&age_param->state, AGE_CANDIDATE);
+ __atomic_store_n(&age_param->sec_since_last_hit, 0, __ATOMIC_RELAXED);
+ __atomic_store_n(&age_param->state, AGE_CANDIDATE, __ATOMIC_RELAXED);
return counter;
}
/**
"counters are not available");
}
+/**
+ * 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 = __atomic_load_n(&age_param->state,
+ __ATOMIC_RELAXED) ==
+ AGE_TMOUT ? 1 : 0;
+ resp->sec_since_last_hit_valid = !resp->aged;
+ if (resp->sec_since_last_hit_valid)
+ resp->sec_since_last_hit =
+ __atomic_load_n(&age_param->sec_since_last_hit,
+ __ATOMIC_RELAXED);
+ return 0;
+ }
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL,
+ "age data not available");
+}
+
/**
* Query a flow.
*
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,