From d5a7d04c795ace8e9d22182a3318f981b4f7833f Mon Sep 17 00:00:00 2001 From: Dekel Peled Date: Mon, 19 Oct 2020 16:52:50 +0300 Subject: [PATCH] net/mlx5: support query of age action Recent patch [1] adds to ethdev the API for query of age action. This patch implements in MLX5 PMD the query of age action using this API. [1] https://mails.dpdk.org/archives/dev/2020-October/184864.html Signed-off-by: Dekel Peled Acked-by: Matan Azrad --- doc/guides/rel_notes/release_20_11.rst | 3 + drivers/net/mlx5/mlx5.h | 21 +++---- drivers/net/mlx5/mlx5_flow.c | 23 ++++--- drivers/net/mlx5/mlx5_flow_dv.c | 84 +++++++++++++++++++------- 4 files changed, 92 insertions(+), 39 deletions(-) diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst index b2e5fcf7f7..062b90a8f9 100644 --- a/doc/guides/rel_notes/release_20_11.rst +++ b/doc/guides/rel_notes/release_20_11.rst @@ -174,6 +174,9 @@ New Features Updated Mellanox mlx5 driver with new features and improvements, including: * Added support for matching on fragmented/non-fragmented IPv4/IPv6 packets. + * Updated the supported timeout for Age action to the maximal value supported + by rte_flow API. + * Added support of Age action query. * **Updated Solarflare network PMD.** diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h index 1408cf94d6..afa2f31f5b 100644 --- a/drivers/net/mlx5/mlx5.h +++ b/drivers/net/mlx5/mlx5.h @@ -276,7 +276,6 @@ struct mlx5_drop { #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) @@ -315,9 +314,7 @@ struct mlx5_drop { */ #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. */ @@ -337,10 +334,11 @@ enum { /* 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. */ }; @@ -349,7 +347,6 @@ struct flow_counter_stats { uint64_t bytes; }; -struct mlx5_flow_counter_pool; /* Generic counters information. */ struct mlx5_flow_counter { TAILQ_ENTRY(mlx5_flow_counter) next; @@ -391,6 +388,8 @@ struct mlx5_flow_counter_pool { 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. */ @@ -400,8 +399,6 @@ struct mlx5_flow_counter_pool { 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; @@ -463,10 +460,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()) /* 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. */ }; diff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c index 8faa83e7c1..39ad712ab9 100644 --- a/drivers/net/mlx5/mlx5_flow.c +++ b/drivers/net/mlx5/mlx5_flow.c @@ -6694,7 +6694,7 @@ next_container: 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. */ @@ -6748,19 +6748,26 @@ mlx5_flow_aging_check(struct mlx5_dev_ctx_shared *sh, 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 @@ -6771,8 +6778,10 @@ mlx5_flow_aging_check(struct mlx5_dev_ctx_shared *sh, 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); } diff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c index b7ad84775d..d3a3f23b80 100644 --- a/drivers/net/mlx5/mlx5_flow_dv.c +++ b/drivers/net/mlx5/mlx5_flow_dv.c @@ -4179,14 +4179,14 @@ flow_dv_validate_action_age(uint64_t action_flags, 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; } @@ -4926,6 +4926,7 @@ flow_dv_pool_create(struct rte_eth_dev *dev, struct mlx5_devx_obj *dcs, 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); @@ -5048,7 +5049,7 @@ retry: (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) { @@ -5329,11 +5330,13 @@ flow_dv_counter_remove_from_age(struct rte_eth_dev *dev, 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. @@ -5341,7 +5344,7 @@ flow_dv_counter_remove_from_age(struct rte_eth_dev *dev, 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); } } @@ -8531,22 +8534,12 @@ flow_dv_translate_create_counter(struct rte_eth_dev *dev, 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; } /** @@ -10954,6 +10947,52 @@ flow_dv_query_count(struct rte_eth_dev *dev, struct rte_flow *flow, "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. * @@ -10976,6 +11015,9 @@ flow_dv_query(struct rte_eth_dev *dev, 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, -- 2.20.1