Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright 2020 Mellanox Technologies, Ltd
3 : : */
4 : :
5 : : #include <stdint.h>
6 : : #include <rte_malloc.h>
7 : : #include <mlx5_malloc.h>
8 : : #include <rte_ring.h>
9 : : #include <mlx5_devx_cmds.h>
10 : : #include <rte_cycles.h>
11 : : #include <rte_eal_paging.h>
12 : : #include <rte_thread.h>
13 : :
14 : : #if defined(HAVE_IBV_FLOW_DV_SUPPORT) || !defined(HAVE_INFINIBAND_VERBS_H)
15 : :
16 : : #include "mlx5_utils.h"
17 : : #include "mlx5_hws_cnt.h"
18 : :
19 : : #define HWS_CNT_CACHE_SZ_DEFAULT 511
20 : : #define HWS_CNT_CACHE_PRELOAD_DEFAULT 254
21 : : #define HWS_CNT_CACHE_FETCH_DEFAULT 254
22 : : #define HWS_CNT_CACHE_THRESHOLD_DEFAULT 254
23 : : #define HWS_CNT_ALLOC_FACTOR_DEFAULT 20
24 : :
25 : : static void
26 [ # # ]: 0 : __hws_cnt_id_load(struct mlx5_hws_cnt_pool *cpool)
27 : : {
28 : : uint32_t cnt_num = mlx5_hws_cnt_pool_get_size(cpool);
29 : : uint32_t iidx;
30 : :
31 : : /*
32 : : * Counter ID order is important for tracking the max number of in used
33 : : * counter for querying, which means counter internal index order must
34 : : * be from zero to the number user configured, i.e: 0 - 8000000.
35 : : * Need to load counter ID in this order into the cache firstly,
36 : : * and then the global free list.
37 : : * In the end, user fetch the counter from minimal to the maximum.
38 : : */
39 [ # # ]: 0 : for (iidx = 0; iidx < cnt_num; iidx++) {
40 : 0 : cnt_id_t cnt_id = mlx5_hws_cnt_id_gen(cpool, iidx);
41 : :
42 [ # # # # : 0 : rte_ring_enqueue_elem(cpool->free_list, &cnt_id,
# ]
43 : : sizeof(cnt_id));
44 : : }
45 : 0 : }
46 : :
47 : : static void
48 : 0 : __mlx5_hws_cnt_svc(struct mlx5_dev_ctx_shared *sh,
49 : : struct mlx5_hws_cnt_pool *cpool)
50 : : {
51 : 0 : struct rte_ring *reset_list = cpool->wait_reset_list;
52 : 0 : struct rte_ring *reuse_list = cpool->reuse_list;
53 : : uint32_t reset_cnt_num;
54 : : struct rte_ring_zc_data zcdr = {0};
55 : : struct rte_ring_zc_data zcdu = {0};
56 : : uint32_t ret __rte_unused;
57 : :
58 : : reset_cnt_num = rte_ring_count(reset_list);
59 : : do {
60 : 0 : cpool->query_gen++;
61 : 0 : mlx5_aso_cnt_query(sh, cpool);
62 : : zcdr.n1 = 0;
63 : : zcdu.n1 = 0;
64 : : ret = rte_ring_enqueue_zc_burst_elem_start(reuse_list,
65 : : sizeof(cnt_id_t),
66 : : reset_cnt_num, &zcdu,
67 : : NULL);
68 : : MLX5_ASSERT(ret == reset_cnt_num);
69 : : ret = rte_ring_dequeue_zc_burst_elem_start(reset_list,
70 : : sizeof(cnt_id_t),
71 : : reset_cnt_num, &zcdr,
72 : : NULL);
73 : : MLX5_ASSERT(ret == reset_cnt_num);
74 : : __hws_cnt_r2rcpy(&zcdu, &zcdr, reset_cnt_num);
75 : : rte_ring_dequeue_zc_elem_finish(reset_list, reset_cnt_num);
76 : : rte_ring_enqueue_zc_elem_finish(reuse_list, reset_cnt_num);
77 : : reset_cnt_num = rte_ring_count(reset_list);
78 [ # # ]: 0 : } while (reset_cnt_num > 0);
79 : 0 : }
80 : :
81 : : /**
82 : : * Release AGE parameter.
83 : : *
84 : : * @param priv
85 : : * Pointer to the port private data structure.
86 : : * @param own_cnt_index
87 : : * Counter ID to created only for this AGE to release.
88 : : * Zero means there is no such counter.
89 : : * @param age_ipool
90 : : * Pointer to AGE parameter indexed pool.
91 : : * @param idx
92 : : * Index of AGE parameter in the indexed pool.
93 : : */
94 : : static void
95 : 0 : mlx5_hws_age_param_free(struct mlx5_priv *priv, cnt_id_t own_cnt_index,
96 : : struct mlx5_indexed_pool *age_ipool, uint32_t idx)
97 : : {
98 [ # # ]: 0 : if (own_cnt_index) {
99 [ # # ]: 0 : struct mlx5_hws_cnt_pool *cpool = priv->hws_cpool;
100 : :
101 : : MLX5_ASSERT(mlx5_hws_cnt_is_shared(cpool, own_cnt_index));
102 : : mlx5_hws_cnt_shared_put(cpool, &own_cnt_index);
103 : : }
104 : 0 : mlx5_ipool_free(age_ipool, idx);
105 : 0 : }
106 : :
107 : : /**
108 : : * Check and callback event for new aged flow in the HWS counter pool.
109 : : *
110 : : * @param[in] priv
111 : : * Pointer to port private object.
112 : : * @param[in] cpool
113 : : * Pointer to current counter pool.
114 : : */
115 : : static void
116 : 0 : mlx5_hws_aging_check(struct mlx5_priv *priv, struct mlx5_hws_cnt_pool *cpool)
117 : : {
118 : 0 : struct mlx5_age_info *age_info = GET_PORT_AGE_INFO(priv);
119 : 0 : struct flow_counter_stats *stats = cpool->raw_mng->raw;
120 : : struct mlx5_hws_age_param *param;
121 : : struct rte_ring *r;
122 : 0 : const uint64_t curr_time = MLX5_CURR_TIME_SEC;
123 [ # # ]: 0 : const uint32_t time_delta = curr_time - cpool->time_of_last_age_check;
124 : : uint32_t nb_alloc_cnts = mlx5_hws_cnt_pool_get_size(cpool);
125 : : uint16_t expected1 = HWS_AGE_CANDIDATE;
126 : : uint16_t expected2 = HWS_AGE_CANDIDATE_INSIDE_RING;
127 : : uint32_t i;
128 : :
129 : 0 : cpool->time_of_last_age_check = curr_time;
130 [ # # ]: 0 : for (i = 0; i < nb_alloc_cnts; ++i) {
131 : 0 : uint32_t age_idx = cpool->pool[i].age_idx;
132 : : uint64_t hits;
133 : :
134 [ # # # # ]: 0 : if (!cpool->pool[i].in_used || age_idx == 0)
135 : 0 : continue;
136 : 0 : param = mlx5_ipool_get(age_info->ages_ipool, age_idx);
137 [ # # ]: 0 : if (unlikely(param == NULL)) {
138 : : /*
139 : : * When AGE which used indirect counter it is user
140 : : * responsibility not using this indirect counter
141 : : * without this AGE.
142 : : * If this counter is used after the AGE was freed, the
143 : : * AGE index is invalid and using it here will cause a
144 : : * segmentation fault.
145 : : */
146 : 0 : DRV_LOG(WARNING,
147 : : "Counter %u is lost his AGE, it is unused.", i);
148 : 0 : continue;
149 : : }
150 [ # # ]: 0 : if (param->timeout == 0)
151 : 0 : continue;
152 [ # # # ]: 0 : switch (__atomic_load_n(¶m->state, __ATOMIC_RELAXED)) {
153 : 0 : case HWS_AGE_AGED_OUT_NOT_REPORTED:
154 : : case HWS_AGE_AGED_OUT_REPORTED:
155 : : /* Already aged-out, no action is needed. */
156 : 0 : continue;
157 : : case HWS_AGE_CANDIDATE:
158 : : case HWS_AGE_CANDIDATE_INSIDE_RING:
159 : : /* This AGE candidate to be aged-out, go to checking. */
160 : : break;
161 : 0 : case HWS_AGE_FREE:
162 : : /*
163 : : * AGE parameter with state "FREE" couldn't be pointed
164 : : * by any counter since counter is destroyed first.
165 : : * Fall-through.
166 : : */
167 : : default:
168 : : MLX5_ASSERT(0);
169 : 0 : continue;
170 : : }
171 [ # # ]: 0 : hits = rte_be_to_cpu_64(stats[i].hits);
172 [ # # ]: 0 : if (param->nb_cnts == 1) {
173 [ # # ]: 0 : if (hits != param->accumulator_last_hits) {
174 : 0 : __atomic_store_n(¶m->sec_since_last_hit, 0,
175 : : __ATOMIC_RELAXED);
176 : 0 : param->accumulator_last_hits = hits;
177 : 0 : continue;
178 : : }
179 : : } else {
180 : 0 : param->accumulator_hits += hits;
181 : 0 : param->accumulator_cnt++;
182 [ # # ]: 0 : if (param->accumulator_cnt < param->nb_cnts)
183 : 0 : continue;
184 : 0 : param->accumulator_cnt = 0;
185 [ # # ]: 0 : if (param->accumulator_last_hits !=
186 : : param->accumulator_hits) {
187 : 0 : __atomic_store_n(¶m->sec_since_last_hit,
188 : : 0, __ATOMIC_RELAXED);
189 : 0 : param->accumulator_last_hits =
190 : 0 : param->accumulator_hits;
191 : 0 : param->accumulator_hits = 0;
192 : 0 : continue;
193 : : }
194 : 0 : param->accumulator_hits = 0;
195 : : }
196 : 0 : if (__atomic_fetch_add(¶m->sec_since_last_hit, time_delta,
197 : 0 : __ATOMIC_RELAXED) + time_delta <=
198 [ # # ]: 0 : __atomic_load_n(¶m->timeout, __ATOMIC_RELAXED))
199 : 0 : continue;
200 : : /* Prepare the relevant ring for this AGE parameter */
201 [ # # ]: 0 : if (priv->hws_strict_queue)
202 : 0 : r = age_info->hw_q_age->aged_lists[param->queue_id];
203 : : else
204 : 0 : r = age_info->hw_age.aged_list;
205 : : /* Changing the state atomically and insert it into the ring. */
206 [ # # ]: 0 : if (__atomic_compare_exchange_n(¶m->state, &expected1,
207 : : HWS_AGE_AGED_OUT_NOT_REPORTED,
208 : : false, __ATOMIC_RELAXED,
209 : : __ATOMIC_RELAXED)) {
210 : : int ret = rte_ring_enqueue_burst_elem(r, &age_idx,
211 : : sizeof(uint32_t),
212 : : 1, NULL);
213 : :
214 : : /*
215 : : * The ring doesn't have enough room for this entry,
216 : : * it replace back the state for the next second.
217 : : *
218 : : * FIXME: if until next sec it get traffic, we are going
219 : : * to lose this "aged out", will be fixed later
220 : : * when optimise it to fill ring in bulks.
221 : : */
222 : : expected2 = HWS_AGE_AGED_OUT_NOT_REPORTED;
223 [ # # # # ]: 0 : if (ret == 0 &&
224 : 0 : !__atomic_compare_exchange_n(¶m->state,
225 : : &expected2, expected1,
226 : : false,
227 : : __ATOMIC_RELAXED,
228 [ # # ]: 0 : __ATOMIC_RELAXED) &&
229 : : expected2 == HWS_AGE_FREE)
230 : 0 : mlx5_hws_age_param_free(priv,
231 : : param->own_cnt_index,
232 : : age_info->ages_ipool,
233 : : age_idx);
234 : : /* The event is irrelevant in strict queue mode. */
235 [ # # ]: 0 : if (!priv->hws_strict_queue)
236 : 0 : MLX5_AGE_SET(age_info, MLX5_AGE_EVENT_NEW);
237 : : } else {
238 : 0 : __atomic_compare_exchange_n(¶m->state, &expected2,
239 : : HWS_AGE_AGED_OUT_NOT_REPORTED,
240 : : false, __ATOMIC_RELAXED,
241 : : __ATOMIC_RELAXED);
242 : : }
243 : : }
244 : : /* The event is irrelevant in strict queue mode. */
245 [ # # ]: 0 : if (!priv->hws_strict_queue)
246 : 0 : mlx5_age_event_prepare(priv->sh);
247 : 0 : }
248 : :
249 : : static void
250 : 0 : mlx5_hws_cnt_raw_data_free(struct mlx5_dev_ctx_shared *sh,
251 : : struct mlx5_hws_cnt_raw_data_mng *mng)
252 : : {
253 [ # # ]: 0 : if (mng == NULL)
254 : : return;
255 : 0 : sh->cdev->mr_scache.dereg_mr_cb(&mng->mr);
256 : 0 : mlx5_free(mng->raw);
257 : 0 : mlx5_free(mng);
258 : : }
259 : :
260 : : __rte_unused
261 : : static struct mlx5_hws_cnt_raw_data_mng *
262 : 0 : mlx5_hws_cnt_raw_data_alloc(struct mlx5_dev_ctx_shared *sh, uint32_t n)
263 : : {
264 : : struct mlx5_hws_cnt_raw_data_mng *mng = NULL;
265 : : int ret;
266 : 0 : size_t sz = n * sizeof(struct flow_counter_stats);
267 : 0 : size_t pgsz = rte_mem_page_size();
268 : :
269 : : MLX5_ASSERT(pgsz > 0);
270 : 0 : mng = mlx5_malloc(MLX5_MEM_ANY | MLX5_MEM_ZERO, sizeof(*mng), 0,
271 : : SOCKET_ID_ANY);
272 [ # # ]: 0 : if (mng == NULL)
273 : 0 : goto error;
274 : 0 : mng->raw = mlx5_malloc(MLX5_MEM_ANY | MLX5_MEM_ZERO, sz, pgsz,
275 : : SOCKET_ID_ANY);
276 [ # # ]: 0 : if (mng->raw == NULL)
277 : 0 : goto error;
278 : 0 : ret = sh->cdev->mr_scache.reg_mr_cb(sh->cdev->pd, mng->raw, sz,
279 : : &mng->mr);
280 [ # # ]: 0 : if (ret) {
281 : 0 : rte_errno = errno;
282 : 0 : goto error;
283 : : }
284 : : return mng;
285 : 0 : error:
286 : 0 : mlx5_hws_cnt_raw_data_free(sh, mng);
287 : 0 : return NULL;
288 : : }
289 : :
290 : : static uint32_t
291 : 0 : mlx5_hws_cnt_svc(void *opaque)
292 : : {
293 : : struct mlx5_dev_ctx_shared *sh =
294 : : (struct mlx5_dev_ctx_shared *)opaque;
295 : 0 : uint64_t interval =
296 : 0 : (uint64_t)sh->cnt_svc->query_interval * (US_PER_S / MS_PER_S);
297 : : struct mlx5_hws_cnt_pool *hws_cpool;
298 : : uint64_t start_cycle, query_cycle = 0;
299 : : uint64_t query_us;
300 : : uint64_t sleep_us;
301 : :
302 [ # # ]: 0 : while (sh->cnt_svc->svc_running != 0) {
303 [ # # ]: 0 : if (rte_spinlock_trylock(&sh->cpool_lock) == 0)
304 : 0 : continue;
305 : : start_cycle = rte_rdtsc();
306 : : /* 200ms for 16M counters. */
307 [ # # ]: 0 : LIST_FOREACH(hws_cpool, &sh->hws_cpool_list, next) {
308 : 0 : struct mlx5_priv *opriv = hws_cpool->priv;
309 : :
310 : 0 : __mlx5_hws_cnt_svc(sh, hws_cpool);
311 [ # # ]: 0 : if (opriv->hws_age_req)
312 : 0 : mlx5_hws_aging_check(opriv, hws_cpool);
313 : : }
314 : 0 : query_cycle = rte_rdtsc() - start_cycle;
315 : : rte_spinlock_unlock(&sh->cpool_lock);
316 : 0 : query_us = query_cycle / (rte_get_timer_hz() / US_PER_S);
317 : 0 : sleep_us = interval - query_us;
318 [ # # ]: 0 : if (interval > query_us)
319 : 0 : rte_delay_us_sleep(sleep_us);
320 : : }
321 : 0 : return 0;
322 : : }
323 : :
324 : : static void
325 : 0 : mlx5_hws_cnt_pool_deinit(struct mlx5_hws_cnt_pool * const cntp)
326 : : {
327 : : uint32_t qidx = 0;
328 [ # # ]: 0 : if (cntp == NULL)
329 : : return;
330 : 0 : rte_ring_free(cntp->free_list);
331 : 0 : rte_ring_free(cntp->wait_reset_list);
332 : 0 : rte_ring_free(cntp->reuse_list);
333 [ # # ]: 0 : if (cntp->cache) {
334 [ # # ]: 0 : for (qidx = 0; qidx < cntp->cache->q_num; qidx++)
335 : 0 : rte_ring_free(cntp->cache->qcache[qidx]);
336 : : }
337 : 0 : mlx5_free(cntp->cache);
338 : 0 : mlx5_free(cntp->raw_mng);
339 : 0 : mlx5_free(cntp->pool);
340 : 0 : mlx5_free(cntp);
341 : : }
342 : :
343 : : static bool
344 : : mlx5_hws_cnt_should_enable_cache(const struct mlx5_hws_cnt_pool_cfg *pcfg,
345 : : const struct mlx5_hws_cache_param *ccfg)
346 : : {
347 : : /*
348 : : * Enable cache if and only if there are enough counters requested
349 : : * to populate all of the caches.
350 : : */
351 : 0 : return pcfg->request_num >= ccfg->q_num * ccfg->size;
352 : : }
353 : :
354 : : static struct mlx5_hws_cnt_pool_caches *
355 : 0 : mlx5_hws_cnt_cache_init(const struct mlx5_hws_cnt_pool_cfg *pcfg,
356 : : const struct mlx5_hws_cache_param *ccfg)
357 : : {
358 : : struct mlx5_hws_cnt_pool_caches *cache;
359 : : char mz_name[RTE_MEMZONE_NAMESIZE];
360 : : uint32_t qidx;
361 : :
362 : : /* If counter pool is big enough, setup the counter pool cache. */
363 : 0 : cache = mlx5_malloc(MLX5_MEM_ANY | MLX5_MEM_ZERO,
364 : 0 : sizeof(*cache) +
365 : : sizeof(((struct mlx5_hws_cnt_pool_caches *)0)->qcache[0])
366 : 0 : * ccfg->q_num, 0, SOCKET_ID_ANY);
367 [ # # ]: 0 : if (cache == NULL)
368 : : return NULL;
369 : : /* Store the necessary cache parameters. */
370 : 0 : cache->fetch_sz = ccfg->fetch_sz;
371 : 0 : cache->preload_sz = ccfg->preload_sz;
372 : 0 : cache->threshold = ccfg->threshold;
373 : 0 : cache->q_num = ccfg->q_num;
374 [ # # ]: 0 : for (qidx = 0; qidx < ccfg->q_num; qidx++) {
375 : 0 : snprintf(mz_name, sizeof(mz_name), "%s_qc/%x", pcfg->name, qidx);
376 : 0 : cache->qcache[qidx] = rte_ring_create(mz_name, ccfg->size,
377 : : SOCKET_ID_ANY,
378 : : RING_F_SP_ENQ | RING_F_SC_DEQ |
379 : : RING_F_EXACT_SZ);
380 [ # # ]: 0 : if (cache->qcache[qidx] == NULL)
381 : 0 : goto error;
382 : : }
383 : : return cache;
384 : :
385 : : error:
386 [ # # ]: 0 : while (qidx--)
387 : 0 : rte_ring_free(cache->qcache[qidx]);
388 : 0 : mlx5_free(cache);
389 : 0 : return NULL;
390 : : }
391 : :
392 : : static struct mlx5_hws_cnt_pool *
393 : 0 : mlx5_hws_cnt_pool_init(struct mlx5_dev_ctx_shared *sh,
394 : : const struct mlx5_hws_cnt_pool_cfg *pcfg,
395 : : const struct mlx5_hws_cache_param *ccfg)
396 : : {
397 : : char mz_name[RTE_MEMZONE_NAMESIZE];
398 : : struct mlx5_hws_cnt_pool *cntp;
399 : : uint64_t cnt_num = 0;
400 : :
401 : : MLX5_ASSERT(pcfg);
402 : : MLX5_ASSERT(ccfg);
403 : 0 : cntp = mlx5_malloc(MLX5_MEM_ANY | MLX5_MEM_ZERO, sizeof(*cntp), 0,
404 : : SOCKET_ID_ANY);
405 [ # # ]: 0 : if (cntp == NULL)
406 : : return NULL;
407 : :
408 : 0 : cntp->cfg = *pcfg;
409 [ # # ]: 0 : if (cntp->cfg.host_cpool)
410 : : return cntp;
411 [ # # ]: 0 : if (pcfg->request_num > sh->hws_max_nb_counters) {
412 : 0 : DRV_LOG(ERR, "Counter number %u "
413 : : "is greater than the maximum supported (%u).",
414 : : pcfg->request_num, sh->hws_max_nb_counters);
415 : 0 : goto error;
416 : : }
417 : 0 : cnt_num = pcfg->request_num * (100 + pcfg->alloc_factor) / 100;
418 : : if (cnt_num > UINT32_MAX) {
419 : : DRV_LOG(ERR, "counter number %"PRIu64" is out of 32bit range",
420 : : cnt_num);
421 : : goto error;
422 : : }
423 : : /*
424 : : * When counter request number is supported, but the factor takes it
425 : : * out of size, the factor is reduced.
426 : : */
427 : 0 : cnt_num = RTE_MIN((uint32_t)cnt_num, sh->hws_max_nb_counters);
428 : 0 : cntp->pool = mlx5_malloc(MLX5_MEM_ANY | MLX5_MEM_ZERO,
429 : : sizeof(struct mlx5_hws_cnt) * cnt_num,
430 : : 0, SOCKET_ID_ANY);
431 [ # # ]: 0 : if (cntp->pool == NULL)
432 : 0 : goto error;
433 : 0 : snprintf(mz_name, sizeof(mz_name), "%s_F_RING", pcfg->name);
434 : 0 : cntp->free_list = rte_ring_create_elem(mz_name, sizeof(cnt_id_t),
435 : : (uint32_t)cnt_num, SOCKET_ID_ANY,
436 : : RING_F_MP_HTS_ENQ | RING_F_MC_HTS_DEQ |
437 : : RING_F_EXACT_SZ);
438 [ # # ]: 0 : if (cntp->free_list == NULL) {
439 : 0 : DRV_LOG(ERR, "failed to create free list ring");
440 : 0 : goto error;
441 : : }
442 : 0 : snprintf(mz_name, sizeof(mz_name), "%s_R_RING", pcfg->name);
443 : 0 : cntp->wait_reset_list = rte_ring_create_elem(mz_name, sizeof(cnt_id_t),
444 : : (uint32_t)cnt_num, SOCKET_ID_ANY,
445 : : RING_F_MP_HTS_ENQ | RING_F_SC_DEQ | RING_F_EXACT_SZ);
446 [ # # ]: 0 : if (cntp->wait_reset_list == NULL) {
447 : 0 : DRV_LOG(ERR, "failed to create free list ring");
448 : 0 : goto error;
449 : : }
450 : 0 : snprintf(mz_name, sizeof(mz_name), "%s_U_RING", pcfg->name);
451 : 0 : cntp->reuse_list = rte_ring_create_elem(mz_name, sizeof(cnt_id_t),
452 : : (uint32_t)cnt_num, SOCKET_ID_ANY,
453 : : RING_F_MP_HTS_ENQ | RING_F_MC_HTS_DEQ | RING_F_EXACT_SZ);
454 [ # # ]: 0 : if (cntp->reuse_list == NULL) {
455 : 0 : DRV_LOG(ERR, "failed to create reuse list ring");
456 : 0 : goto error;
457 : : }
458 : : /* Allocate counter cache only if needed. */
459 [ # # ]: 0 : if (mlx5_hws_cnt_should_enable_cache(pcfg, ccfg)) {
460 : 0 : cntp->cache = mlx5_hws_cnt_cache_init(pcfg, ccfg);
461 [ # # ]: 0 : if (cntp->cache == NULL)
462 : 0 : goto error;
463 : : }
464 : : /* Initialize the time for aging-out calculation. */
465 : 0 : cntp->time_of_last_age_check = MLX5_CURR_TIME_SEC;
466 : 0 : return cntp;
467 : 0 : error:
468 : 0 : mlx5_hws_cnt_pool_deinit(cntp);
469 : 0 : return NULL;
470 : : }
471 : :
472 : : int
473 : 0 : mlx5_hws_cnt_service_thread_create(struct mlx5_dev_ctx_shared *sh)
474 : : {
475 : : char name[RTE_THREAD_INTERNAL_NAME_SIZE];
476 : : rte_thread_attr_t attr;
477 : : int ret;
478 : 0 : uint32_t service_core = sh->cnt_svc->service_core;
479 : :
480 : 0 : ret = rte_thread_attr_init(&attr);
481 [ # # ]: 0 : if (ret != 0)
482 : 0 : goto error;
483 [ # # ]: 0 : CPU_SET(service_core, &attr.cpuset);
484 : 0 : sh->cnt_svc->svc_running = 1;
485 : 0 : ret = rte_thread_create(&sh->cnt_svc->service_thread,
486 : : &attr, mlx5_hws_cnt_svc, sh);
487 [ # # ]: 0 : if (ret != 0)
488 : 0 : goto error;
489 : : snprintf(name, sizeof(name), "mlx5-cn%d", service_core);
490 : 0 : rte_thread_set_prefixed_name(sh->cnt_svc->service_thread, name);
491 : :
492 : 0 : return 0;
493 : 0 : error:
494 : 0 : DRV_LOG(ERR, "Failed to create HW steering's counter service thread.");
495 : 0 : return ret;
496 : : }
497 : :
498 : : void
499 : 0 : mlx5_hws_cnt_service_thread_destroy(struct mlx5_dev_ctx_shared *sh)
500 : : {
501 [ # # ]: 0 : if (sh->cnt_svc->service_thread.opaque_id == 0)
502 : : return;
503 : 0 : sh->cnt_svc->svc_running = 0;
504 : 0 : rte_thread_join(sh->cnt_svc->service_thread, NULL);
505 : 0 : sh->cnt_svc->service_thread.opaque_id = 0;
506 : : }
507 : :
508 : : static int
509 : 0 : mlx5_hws_cnt_pool_dcs_alloc(struct mlx5_dev_ctx_shared *sh,
510 : : struct mlx5_hws_cnt_pool *cpool)
511 : : {
512 : 0 : struct mlx5_hca_attr *hca_attr = &sh->cdev->config.hca_attr;
513 [ # # ]: 0 : uint32_t max_log_bulk_sz = sh->hws_max_log_bulk_sz;
514 : : uint32_t log_bulk_sz;
515 : : uint32_t idx, alloc_candidate, alloced = 0;
516 : : unsigned int cnt_num = mlx5_hws_cnt_pool_get_size(cpool);
517 : 0 : struct mlx5_devx_counter_attr attr = {0};
518 : : struct mlx5_devx_obj *dcs;
519 : :
520 : : MLX5_ASSERT(cpool->cfg.host_cpool == NULL);
521 [ # # ]: 0 : if (hca_attr->flow_counter_bulk_log_max_alloc == 0) {
522 : 0 : DRV_LOG(ERR, "Fw doesn't support bulk log max alloc");
523 : 0 : return -1;
524 : : }
525 [ # # ]: 0 : cnt_num = RTE_ALIGN_CEIL(cnt_num, 4); /* minimal 4 counter in bulk. */
526 : 0 : log_bulk_sz = RTE_MIN(max_log_bulk_sz, rte_log2_u32(cnt_num));
527 : 0 : attr.pd = sh->cdev->pdn;
528 : 0 : attr.pd_valid = 1;
529 : 0 : attr.bulk_log_max_alloc = 1;
530 : 0 : attr.flow_counter_bulk_log_size = log_bulk_sz;
531 : : idx = 0;
532 : 0 : dcs = mlx5_devx_cmd_flow_counter_alloc_general(sh->cdev->ctx, &attr);
533 [ # # ]: 0 : if (dcs == NULL)
534 : 0 : goto error;
535 : 0 : cpool->dcs_mng.dcs[idx].obj = dcs;
536 : 0 : cpool->dcs_mng.dcs[idx].batch_sz = (1 << log_bulk_sz);
537 : 0 : cpool->dcs_mng.batch_total++;
538 : : idx++;
539 : 0 : cpool->dcs_mng.dcs[0].iidx = 0;
540 : : alloced = cpool->dcs_mng.dcs[0].batch_sz;
541 [ # # ]: 0 : if (cnt_num > cpool->dcs_mng.dcs[0].batch_sz) {
542 [ # # ]: 0 : while (idx < MLX5_HWS_CNT_DCS_NUM) {
543 : 0 : attr.flow_counter_bulk_log_size = --max_log_bulk_sz;
544 : 0 : alloc_candidate = RTE_BIT32(max_log_bulk_sz);
545 [ # # ]: 0 : if (alloced + alloc_candidate > sh->hws_max_nb_counters)
546 : 0 : continue;
547 : 0 : dcs = mlx5_devx_cmd_flow_counter_alloc_general
548 : 0 : (sh->cdev->ctx, &attr);
549 [ # # ]: 0 : if (dcs == NULL)
550 : 0 : goto error;
551 : 0 : cpool->dcs_mng.dcs[idx].obj = dcs;
552 : 0 : cpool->dcs_mng.dcs[idx].batch_sz = alloc_candidate;
553 : 0 : cpool->dcs_mng.dcs[idx].iidx = alloced;
554 : : alloced += cpool->dcs_mng.dcs[idx].batch_sz;
555 : 0 : cpool->dcs_mng.batch_total++;
556 [ # # ]: 0 : if (alloced >= cnt_num)
557 : : break;
558 : 0 : idx++;
559 : : }
560 : : }
561 : : return 0;
562 : 0 : error:
563 : 0 : DRV_LOG(DEBUG,
564 : : "Cannot alloc device counter, allocated[%" PRIu32 "] request[%" PRIu32 "]",
565 : : alloced, cnt_num);
566 [ # # ]: 0 : for (idx = 0; idx < cpool->dcs_mng.batch_total; idx++) {
567 : 0 : mlx5_devx_cmd_destroy(cpool->dcs_mng.dcs[idx].obj);
568 : 0 : cpool->dcs_mng.dcs[idx].obj = NULL;
569 : 0 : cpool->dcs_mng.dcs[idx].batch_sz = 0;
570 : 0 : cpool->dcs_mng.dcs[idx].iidx = 0;
571 : : }
572 : 0 : cpool->dcs_mng.batch_total = 0;
573 : 0 : return -1;
574 : : }
575 : :
576 : : static void
577 : 0 : mlx5_hws_cnt_pool_dcs_free(struct mlx5_dev_ctx_shared *sh,
578 : : struct mlx5_hws_cnt_pool *cpool)
579 : : {
580 : : uint32_t idx;
581 : :
582 [ # # ]: 0 : if (cpool == NULL)
583 : : return;
584 [ # # ]: 0 : for (idx = 0; idx < MLX5_HWS_CNT_DCS_NUM; idx++)
585 : 0 : mlx5_devx_cmd_destroy(cpool->dcs_mng.dcs[idx].obj);
586 [ # # ]: 0 : if (cpool->raw_mng) {
587 : 0 : mlx5_hws_cnt_raw_data_free(sh, cpool->raw_mng);
588 : 0 : cpool->raw_mng = NULL;
589 : : }
590 : : }
591 : :
592 : : static void
593 : 0 : mlx5_hws_cnt_pool_action_destroy(struct mlx5_hws_cnt_pool *cpool)
594 : : {
595 : : uint32_t idx;
596 : :
597 [ # # ]: 0 : for (idx = 0; idx < cpool->dcs_mng.batch_total; idx++) {
598 : : struct mlx5_hws_cnt_dcs *dcs = &cpool->dcs_mng.dcs[idx];
599 : :
600 [ # # ]: 0 : if (dcs->dr_action != NULL) {
601 : 0 : mlx5dr_action_destroy(dcs->dr_action);
602 : 0 : dcs->dr_action = NULL;
603 : : }
604 : : }
605 : 0 : }
606 : :
607 : : static int
608 [ # # ]: 0 : mlx5_hws_cnt_pool_action_create(struct mlx5_priv *priv,
609 : : struct mlx5_hws_cnt_pool *cpool)
610 : : {
611 : : struct mlx5_hws_cnt_pool *hpool = mlx5_hws_cnt_host_pool(cpool);
612 : : uint32_t idx;
613 : : int ret = 0;
614 : : uint32_t flags;
615 : :
616 : : flags = MLX5DR_ACTION_FLAG_HWS_RX | MLX5DR_ACTION_FLAG_HWS_TX;
617 [ # # # # ]: 0 : if (priv->sh->config.dv_esw_en && priv->master)
618 : : flags |= MLX5DR_ACTION_FLAG_HWS_FDB;
619 [ # # ]: 0 : for (idx = 0; idx < hpool->dcs_mng.batch_total; idx++) {
620 : : struct mlx5_hws_cnt_dcs *hdcs = &hpool->dcs_mng.dcs[idx];
621 : : struct mlx5_hws_cnt_dcs *dcs = &cpool->dcs_mng.dcs[idx];
622 : :
623 : 0 : dcs->dr_action = mlx5dr_action_create_counter(priv->dr_ctx,
624 : 0 : (struct mlx5dr_devx_obj *)hdcs->obj,
625 : : flags);
626 [ # # ]: 0 : if (dcs->dr_action == NULL) {
627 : 0 : mlx5_hws_cnt_pool_action_destroy(cpool);
628 : : ret = -ENOSYS;
629 : 0 : break;
630 : : }
631 : : }
632 : 0 : return ret;
633 : : }
634 : :
635 : : struct mlx5_hws_cnt_pool *
636 : 0 : mlx5_hws_cnt_pool_create(struct rte_eth_dev *dev,
637 : : const struct rte_flow_port_attr *pattr, uint16_t nb_queue)
638 : : {
639 : : struct mlx5_hws_cnt_pool *cpool = NULL;
640 : 0 : struct mlx5_priv *priv = dev->data->dev_private;
641 : 0 : struct mlx5_hws_cache_param cparam = {0};
642 : 0 : struct mlx5_hws_cnt_pool_cfg pcfg = {0};
643 : : char *mp_name;
644 : : int ret = 0;
645 : : size_t sz;
646 : :
647 : 0 : mp_name = mlx5_malloc(MLX5_MEM_ZERO, RTE_MEMZONE_NAMESIZE, 0, SOCKET_ID_ANY);
648 [ # # ]: 0 : if (mp_name == NULL)
649 : 0 : goto error;
650 [ # # ]: 0 : snprintf(mp_name, RTE_MEMZONE_NAMESIZE, "MLX5_HWS_CNT_P_%x", dev->data->port_id);
651 : 0 : pcfg.name = mp_name;
652 : 0 : pcfg.request_num = pattr->nb_counters;
653 : 0 : pcfg.alloc_factor = HWS_CNT_ALLOC_FACTOR_DEFAULT;
654 [ # # ]: 0 : if (pattr->flags & RTE_FLOW_PORT_FLAG_SHARE_INDIRECT) {
655 : 0 : struct mlx5_priv *host_priv =
656 : 0 : priv->shared_host->data->dev_private;
657 : 0 : struct mlx5_hws_cnt_pool *chost = host_priv->hws_cpool;
658 : :
659 : 0 : pcfg.host_cpool = chost;
660 : 0 : cpool = mlx5_hws_cnt_pool_init(priv->sh, &pcfg, &cparam);
661 [ # # ]: 0 : if (cpool == NULL)
662 : 0 : goto error;
663 : 0 : ret = mlx5_hws_cnt_pool_action_create(priv, cpool);
664 [ # # ]: 0 : if (ret != 0)
665 : 0 : goto error;
666 : : return cpool;
667 : : }
668 : : /* init cnt service if not. */
669 [ # # ]: 0 : if (priv->sh->cnt_svc == NULL) {
670 : 0 : ret = mlx5_hws_cnt_svc_init(priv->sh);
671 [ # # ]: 0 : if (ret != 0)
672 : : return NULL;
673 : : }
674 : 0 : cparam.fetch_sz = HWS_CNT_CACHE_FETCH_DEFAULT;
675 : 0 : cparam.preload_sz = HWS_CNT_CACHE_PRELOAD_DEFAULT;
676 : 0 : cparam.q_num = nb_queue;
677 : 0 : cparam.threshold = HWS_CNT_CACHE_THRESHOLD_DEFAULT;
678 : 0 : cparam.size = HWS_CNT_CACHE_SZ_DEFAULT;
679 : 0 : cpool = mlx5_hws_cnt_pool_init(priv->sh, &pcfg, &cparam);
680 [ # # ]: 0 : if (cpool == NULL)
681 : 0 : goto error;
682 : 0 : ret = mlx5_hws_cnt_pool_dcs_alloc(priv->sh, cpool);
683 [ # # ]: 0 : if (ret != 0)
684 : 0 : goto error;
685 : 0 : sz = RTE_ALIGN_CEIL(mlx5_hws_cnt_pool_get_size(cpool), 4);
686 : 0 : cpool->raw_mng = mlx5_hws_cnt_raw_data_alloc(priv->sh, sz);
687 [ # # ]: 0 : if (cpool->raw_mng == NULL)
688 : 0 : goto error;
689 : 0 : __hws_cnt_id_load(cpool);
690 : : /*
691 : : * Bump query gen right after pool create so the
692 : : * pre-loaded counters can be used directly
693 : : * because they already have init value no need
694 : : * to wait for query.
695 : : */
696 : 0 : cpool->query_gen = 1;
697 : 0 : ret = mlx5_hws_cnt_pool_action_create(priv, cpool);
698 [ # # ]: 0 : if (ret != 0)
699 : 0 : goto error;
700 : 0 : priv->sh->cnt_svc->refcnt++;
701 : 0 : cpool->priv = priv;
702 : 0 : rte_spinlock_lock(&priv->sh->cpool_lock);
703 [ # # ]: 0 : LIST_INSERT_HEAD(&priv->sh->hws_cpool_list, cpool, next);
704 : 0 : rte_spinlock_unlock(&priv->sh->cpool_lock);
705 : 0 : return cpool;
706 : 0 : error:
707 : 0 : mlx5_hws_cnt_pool_destroy(priv->sh, cpool);
708 : 0 : return NULL;
709 : : }
710 : :
711 : : void
712 : 0 : mlx5_hws_cnt_pool_destroy(struct mlx5_dev_ctx_shared *sh,
713 : : struct mlx5_hws_cnt_pool *cpool)
714 : : {
715 [ # # ]: 0 : if (cpool == NULL)
716 : : return;
717 : : /*
718 : : * 16M counter consumes 200ms to finish the query.
719 : : * Maybe blocked for at most 200ms here.
720 : : */
721 : 0 : rte_spinlock_lock(&sh->cpool_lock);
722 [ # # ]: 0 : LIST_REMOVE(cpool, next);
723 : : rte_spinlock_unlock(&sh->cpool_lock);
724 [ # # ]: 0 : if (cpool->cfg.host_cpool == NULL) {
725 [ # # ]: 0 : if (--sh->cnt_svc->refcnt == 0)
726 : 0 : mlx5_hws_cnt_svc_deinit(sh);
727 : : }
728 : 0 : mlx5_hws_cnt_pool_action_destroy(cpool);
729 [ # # ]: 0 : if (cpool->cfg.host_cpool == NULL) {
730 : 0 : mlx5_hws_cnt_pool_dcs_free(sh, cpool);
731 : 0 : mlx5_hws_cnt_raw_data_free(sh, cpool->raw_mng);
732 : : }
733 : 0 : mlx5_free((void *)cpool->cfg.name);
734 : 0 : mlx5_hws_cnt_pool_deinit(cpool);
735 : : }
736 : :
737 : : int
738 : 0 : mlx5_hws_cnt_svc_init(struct mlx5_dev_ctx_shared *sh)
739 : : {
740 : : int ret;
741 : :
742 : 0 : sh->cnt_svc = mlx5_malloc(MLX5_MEM_ANY | MLX5_MEM_ZERO,
743 : : sizeof(*sh->cnt_svc), 0, SOCKET_ID_ANY);
744 [ # # ]: 0 : if (sh->cnt_svc == NULL)
745 : : return -1;
746 : 0 : sh->cnt_svc->query_interval = sh->config.cnt_svc.cycle_time;
747 : 0 : sh->cnt_svc->service_core = sh->config.cnt_svc.service_core;
748 : 0 : ret = mlx5_aso_cnt_queue_init(sh);
749 [ # # ]: 0 : if (ret != 0) {
750 : 0 : mlx5_free(sh->cnt_svc);
751 : 0 : sh->cnt_svc = NULL;
752 : 0 : return -1;
753 : : }
754 : 0 : ret = mlx5_hws_cnt_service_thread_create(sh);
755 [ # # ]: 0 : if (ret != 0) {
756 : 0 : mlx5_aso_cnt_queue_uninit(sh);
757 : 0 : mlx5_free(sh->cnt_svc);
758 : 0 : sh->cnt_svc = NULL;
759 : : }
760 : : return 0;
761 : : }
762 : :
763 : : void
764 : 0 : mlx5_hws_cnt_svc_deinit(struct mlx5_dev_ctx_shared *sh)
765 : : {
766 [ # # ]: 0 : if (sh->cnt_svc == NULL)
767 : : return;
768 : 0 : mlx5_hws_cnt_service_thread_destroy(sh);
769 : 0 : mlx5_aso_cnt_queue_uninit(sh);
770 : 0 : mlx5_free(sh->cnt_svc);
771 : 0 : sh->cnt_svc = NULL;
772 : : }
773 : :
774 : : /**
775 : : * Destroy AGE action.
776 : : *
777 : : * @param priv
778 : : * Pointer to the port private data structure.
779 : : * @param idx
780 : : * Index of AGE parameter.
781 : : * @param error
782 : : * Pointer to error structure.
783 : : *
784 : : * @return
785 : : * 0 on success, a negative errno value otherwise and rte_errno is set.
786 : : */
787 : : int
788 : 0 : mlx5_hws_age_action_destroy(struct mlx5_priv *priv, uint32_t idx,
789 : : struct rte_flow_error *error)
790 : : {
791 : 0 : struct mlx5_age_info *age_info = GET_PORT_AGE_INFO(priv);
792 : 0 : struct mlx5_indexed_pool *ipool = age_info->ages_ipool;
793 : 0 : struct mlx5_hws_age_param *param = mlx5_ipool_get(ipool, idx);
794 : :
795 [ # # ]: 0 : if (param == NULL)
796 : 0 : return rte_flow_error_set(error, EINVAL,
797 : : RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
798 : : "invalid AGE parameter index");
799 [ # # # ]: 0 : switch (__atomic_exchange_n(¶m->state, HWS_AGE_FREE,
800 : : __ATOMIC_RELAXED)) {
801 : 0 : case HWS_AGE_CANDIDATE:
802 : : case HWS_AGE_AGED_OUT_REPORTED:
803 : 0 : mlx5_hws_age_param_free(priv, param->own_cnt_index, ipool, idx);
804 : 0 : break;
805 : : case HWS_AGE_AGED_OUT_NOT_REPORTED:
806 : : case HWS_AGE_CANDIDATE_INSIDE_RING:
807 : : /*
808 : : * In both cases AGE is inside the ring. Change the state here
809 : : * and destroy it later when it is taken out of ring.
810 : : */
811 : : break;
812 : 0 : case HWS_AGE_FREE:
813 : : /*
814 : : * If index is valid and state is FREE, it says this AGE has
815 : : * been freed for the user but not for the PMD since it is
816 : : * inside the ring.
817 : : */
818 : 0 : return rte_flow_error_set(error, EINVAL,
819 : : RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
820 : : "this AGE has already been released");
821 : : default:
822 : : MLX5_ASSERT(0);
823 : : break;
824 : : }
825 : : return 0;
826 : : }
827 : :
828 : : /**
829 : : * Create AGE action parameter.
830 : : *
831 : : * @param[in] priv
832 : : * Pointer to the port private data structure.
833 : : * @param[in] queue_id
834 : : * Which HWS queue to be used.
835 : : * @param[in] shared
836 : : * Whether it indirect AGE action.
837 : : * @param[in] flow_idx
838 : : * Flow index from indexed pool.
839 : : * For indirect AGE action it doesn't affect.
840 : : * @param[in] age
841 : : * Pointer to the aging action configuration.
842 : : * @param[out] error
843 : : * Pointer to error structure.
844 : : *
845 : : * @return
846 : : * Index to AGE action parameter on success, 0 otherwise.
847 : : */
848 : : uint32_t
849 : 0 : mlx5_hws_age_action_create(struct mlx5_priv *priv, uint32_t queue_id,
850 : : bool shared, const struct rte_flow_action_age *age,
851 : : uint32_t flow_idx, struct rte_flow_error *error)
852 : : {
853 : 0 : struct mlx5_age_info *age_info = GET_PORT_AGE_INFO(priv);
854 : 0 : struct mlx5_indexed_pool *ipool = age_info->ages_ipool;
855 : : struct mlx5_hws_age_param *param;
856 : : uint32_t age_idx;
857 : :
858 : 0 : param = mlx5_ipool_malloc(ipool, &age_idx);
859 [ # # ]: 0 : if (param == NULL) {
860 : 0 : rte_flow_error_set(error, ENOMEM,
861 : : RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
862 : : "cannot allocate AGE parameter");
863 : 0 : return 0;
864 : : }
865 : : MLX5_ASSERT(__atomic_load_n(¶m->state,
866 : : __ATOMIC_RELAXED) == HWS_AGE_FREE);
867 [ # # ]: 0 : if (shared) {
868 : 0 : param->nb_cnts = 0;
869 : 0 : param->accumulator_hits = 0;
870 : 0 : param->accumulator_cnt = 0;
871 : 0 : flow_idx = age_idx;
872 : : } else {
873 : 0 : param->nb_cnts = 1;
874 : : }
875 [ # # ]: 0 : param->context = age->context ? age->context :
876 : 0 : (void *)(uintptr_t)flow_idx;
877 : 0 : param->timeout = age->timeout;
878 : 0 : param->queue_id = queue_id;
879 : 0 : param->accumulator_last_hits = 0;
880 : 0 : param->own_cnt_index = 0;
881 : 0 : param->sec_since_last_hit = 0;
882 : 0 : param->state = HWS_AGE_CANDIDATE;
883 : 0 : return age_idx;
884 : : }
885 : :
886 : : /**
887 : : * Update indirect AGE action parameter.
888 : : *
889 : : * @param[in] priv
890 : : * Pointer to the port private data structure.
891 : : * @param[in] idx
892 : : * Index of AGE parameter.
893 : : * @param[in] update
894 : : * Update value.
895 : : * @param[out] error
896 : : * Pointer to error structure.
897 : : *
898 : : * @return
899 : : * 0 on success, a negative errno value otherwise and rte_errno is set.
900 : : */
901 : : int
902 : 0 : mlx5_hws_age_action_update(struct mlx5_priv *priv, uint32_t idx,
903 : : const void *update, struct rte_flow_error *error)
904 : : {
905 : : const struct rte_flow_update_age *update_ade = update;
906 : 0 : struct mlx5_age_info *age_info = GET_PORT_AGE_INFO(priv);
907 : 0 : struct mlx5_indexed_pool *ipool = age_info->ages_ipool;
908 : 0 : struct mlx5_hws_age_param *param = mlx5_ipool_get(ipool, idx);
909 : : bool sec_since_last_hit_reset = false;
910 : : bool state_update = false;
911 : :
912 [ # # ]: 0 : if (param == NULL)
913 : 0 : return rte_flow_error_set(error, EINVAL,
914 : : RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
915 : : "invalid AGE parameter index");
916 [ # # ]: 0 : if (update_ade->timeout_valid) {
917 : 0 : uint32_t old_timeout = __atomic_exchange_n(¶m->timeout,
918 : 0 : update_ade->timeout,
919 : : __ATOMIC_RELAXED);
920 : :
921 [ # # ]: 0 : if (old_timeout == 0)
922 : : sec_since_last_hit_reset = true;
923 [ # # ]: 0 : else if (old_timeout < update_ade->timeout ||
924 [ # # ]: 0 : update_ade->timeout == 0)
925 : : /*
926 : : * When timeout is increased, aged-out flows might be
927 : : * active again and state should be updated accordingly.
928 : : * When new timeout is 0, we update the state for not
929 : : * reporting aged-out stopped.
930 : : */
931 : : state_update = true;
932 : : }
933 [ # # ]: 0 : if (update_ade->touch) {
934 : : sec_since_last_hit_reset = true;
935 : : state_update = true;
936 : : }
937 [ # # ]: 0 : if (sec_since_last_hit_reset)
938 : 0 : __atomic_store_n(¶m->sec_since_last_hit, 0,
939 : : __ATOMIC_RELAXED);
940 [ # # ]: 0 : if (state_update) {
941 : : uint16_t expected = HWS_AGE_AGED_OUT_NOT_REPORTED;
942 : :
943 : : /*
944 : : * Change states of aged-out flows to active:
945 : : * - AGED_OUT_NOT_REPORTED -> CANDIDATE_INSIDE_RING
946 : : * - AGED_OUT_REPORTED -> CANDIDATE
947 : : */
948 [ # # ]: 0 : if (!__atomic_compare_exchange_n(¶m->state, &expected,
949 : : HWS_AGE_CANDIDATE_INSIDE_RING,
950 : : false, __ATOMIC_RELAXED,
951 [ # # ]: 0 : __ATOMIC_RELAXED) &&
952 : : expected == HWS_AGE_AGED_OUT_REPORTED)
953 : 0 : __atomic_store_n(¶m->state, HWS_AGE_CANDIDATE,
954 : : __ATOMIC_RELAXED);
955 : : }
956 : : return 0;
957 : : }
958 : :
959 : : /**
960 : : * Get the AGE context if the aged-out index is still valid.
961 : : *
962 : : * @param priv
963 : : * Pointer to the port private data structure.
964 : : * @param idx
965 : : * Index of AGE parameter.
966 : : *
967 : : * @return
968 : : * AGE context if the index is still aged-out, NULL otherwise.
969 : : */
970 : : void *
971 : 0 : mlx5_hws_age_context_get(struct mlx5_priv *priv, uint32_t idx)
972 : : {
973 : 0 : struct mlx5_age_info *age_info = GET_PORT_AGE_INFO(priv);
974 : 0 : struct mlx5_indexed_pool *ipool = age_info->ages_ipool;
975 : 0 : struct mlx5_hws_age_param *param = mlx5_ipool_get(ipool, idx);
976 : : uint16_t expected = HWS_AGE_AGED_OUT_NOT_REPORTED;
977 : :
978 : : MLX5_ASSERT(param != NULL);
979 [ # # ]: 0 : if (__atomic_compare_exchange_n(¶m->state, &expected,
980 : : HWS_AGE_AGED_OUT_REPORTED, false,
981 : : __ATOMIC_RELAXED, __ATOMIC_RELAXED))
982 : 0 : return param->context;
983 [ # # # ]: 0 : switch (expected) {
984 : 0 : case HWS_AGE_FREE:
985 : : /*
986 : : * This AGE couldn't have been destroyed since it was inside
987 : : * the ring. Its state has updated, and now it is actually
988 : : * destroyed.
989 : : */
990 : 0 : mlx5_hws_age_param_free(priv, param->own_cnt_index, ipool, idx);
991 : 0 : break;
992 : 0 : case HWS_AGE_CANDIDATE_INSIDE_RING:
993 : 0 : __atomic_store_n(¶m->state, HWS_AGE_CANDIDATE,
994 : : __ATOMIC_RELAXED);
995 : 0 : break;
996 : : case HWS_AGE_CANDIDATE:
997 : : /*
998 : : * Only BG thread pushes to ring and it never pushes this state.
999 : : * When AGE inside the ring becomes candidate, it has a special
1000 : : * state called HWS_AGE_CANDIDATE_INSIDE_RING.
1001 : : * Fall-through.
1002 : : */
1003 : : case HWS_AGE_AGED_OUT_REPORTED:
1004 : : /*
1005 : : * Only this thread (doing query) may write this state, and it
1006 : : * happens only after the query thread takes it out of the ring.
1007 : : * Fall-through.
1008 : : */
1009 : : case HWS_AGE_AGED_OUT_NOT_REPORTED:
1010 : : /*
1011 : : * In this case the compare return true and function return
1012 : : * the context immediately.
1013 : : * Fall-through.
1014 : : */
1015 : : default:
1016 : : MLX5_ASSERT(0);
1017 : : break;
1018 : : }
1019 : : return NULL;
1020 : : }
1021 : :
1022 : : #ifdef RTE_ARCH_64
1023 : : #define MLX5_HWS_AGED_OUT_RING_SIZE_MAX UINT32_MAX
1024 : : #else
1025 : : #define MLX5_HWS_AGED_OUT_RING_SIZE_MAX RTE_BIT32(8)
1026 : : #endif
1027 : :
1028 : : /**
1029 : : * Get the size of aged out ring list for each queue.
1030 : : *
1031 : : * The size is one percent of nb_counters divided by nb_queues.
1032 : : * The ring size must be power of 2, so it align up to power of 2.
1033 : : * In 32 bit systems, the size is limited by 256.
1034 : : *
1035 : : * This function is called when RTE_FLOW_PORT_FLAG_STRICT_QUEUE is on.
1036 : : *
1037 : : * @param nb_counters
1038 : : * Final number of allocated counter in the pool.
1039 : : * @param nb_queues
1040 : : * Number of HWS queues in this port.
1041 : : *
1042 : : * @return
1043 : : * Size of aged out ring per queue.
1044 : : */
1045 : : static __rte_always_inline uint32_t
1046 : : mlx5_hws_aged_out_q_ring_size_get(uint32_t nb_counters, uint32_t nb_queues)
1047 : : {
1048 : 0 : uint32_t size = rte_align32pow2((nb_counters / 100) / nb_queues);
1049 : : uint32_t max_size = MLX5_HWS_AGED_OUT_RING_SIZE_MAX;
1050 : :
1051 : : return RTE_MIN(size, max_size);
1052 : : }
1053 : :
1054 : : /**
1055 : : * Get the size of the aged out ring list.
1056 : : *
1057 : : * The size is one percent of nb_counters.
1058 : : * The ring size must be power of 2, so it align up to power of 2.
1059 : : * In 32 bit systems, the size is limited by 256.
1060 : : *
1061 : : * This function is called when RTE_FLOW_PORT_FLAG_STRICT_QUEUE is off.
1062 : : *
1063 : : * @param nb_counters
1064 : : * Final number of allocated counter in the pool.
1065 : : *
1066 : : * @return
1067 : : * Size of the aged out ring list.
1068 : : */
1069 : : static __rte_always_inline uint32_t
1070 : : mlx5_hws_aged_out_ring_size_get(uint32_t nb_counters)
1071 : : {
1072 : 0 : uint32_t size = rte_align32pow2(nb_counters / 100);
1073 : : uint32_t max_size = MLX5_HWS_AGED_OUT_RING_SIZE_MAX;
1074 : :
1075 : : return RTE_MIN(size, max_size);
1076 : : }
1077 : :
1078 : : /**
1079 : : * Initialize the shared aging list information per port.
1080 : : *
1081 : : * @param dev
1082 : : * Pointer to the rte_eth_dev structure.
1083 : : * @param nb_queues
1084 : : * Number of HWS queues.
1085 : : * @param strict_queue
1086 : : * Indicator whether is strict_queue mode.
1087 : : * @param ring_size
1088 : : * Size of aged-out ring for creation.
1089 : : *
1090 : : * @return
1091 : : * 0 on success, a negative errno value otherwise and rte_errno is set.
1092 : : */
1093 : : static int
1094 : 0 : mlx5_hws_age_info_init(struct rte_eth_dev *dev, uint16_t nb_queues,
1095 : : bool strict_queue, uint32_t ring_size)
1096 : : {
1097 : 0 : struct mlx5_priv *priv = dev->data->dev_private;
1098 : 0 : struct mlx5_age_info *age_info = GET_PORT_AGE_INFO(priv);
1099 : : uint32_t flags = RING_F_SP_ENQ | RING_F_SC_DEQ | RING_F_EXACT_SZ;
1100 : : char mz_name[RTE_MEMZONE_NAMESIZE];
1101 : : struct rte_ring *r = NULL;
1102 : : uint32_t qidx;
1103 : :
1104 : 0 : age_info->flags = 0;
1105 [ # # ]: 0 : if (strict_queue) {
1106 : 0 : size_t size = sizeof(*age_info->hw_q_age) +
1107 : : sizeof(struct rte_ring *) * nb_queues;
1108 : :
1109 : 0 : age_info->hw_q_age = mlx5_malloc(MLX5_MEM_ANY | MLX5_MEM_ZERO,
1110 : : size, 0, SOCKET_ID_ANY);
1111 [ # # ]: 0 : if (age_info->hw_q_age == NULL)
1112 : : return -ENOMEM;
1113 [ # # ]: 0 : for (qidx = 0; qidx < nb_queues; ++qidx) {
1114 : 0 : snprintf(mz_name, sizeof(mz_name),
1115 : : "port_%u_queue_%u_aged_out_ring",
1116 : 0 : dev->data->port_id, qidx);
1117 : 0 : r = rte_ring_create(mz_name, ring_size, SOCKET_ID_ANY,
1118 : : flags);
1119 [ # # ]: 0 : if (r == NULL) {
1120 : 0 : DRV_LOG(ERR, "\"%s\" creation failed: %s",
1121 : : mz_name, rte_strerror(rte_errno));
1122 : 0 : goto error;
1123 : : }
1124 : 0 : age_info->hw_q_age->aged_lists[qidx] = r;
1125 : 0 : DRV_LOG(DEBUG,
1126 : : "\"%s\" is successfully created (size=%u).",
1127 : : mz_name, ring_size);
1128 : : }
1129 : 0 : age_info->hw_q_age->nb_rings = nb_queues;
1130 : : } else {
1131 : 0 : snprintf(mz_name, sizeof(mz_name), "port_%u_aged_out_ring",
1132 : 0 : dev->data->port_id);
1133 : 0 : r = rte_ring_create(mz_name, ring_size, SOCKET_ID_ANY, flags);
1134 [ # # ]: 0 : if (r == NULL) {
1135 : 0 : DRV_LOG(ERR, "\"%s\" creation failed: %s", mz_name,
1136 : : rte_strerror(rte_errno));
1137 : 0 : return -rte_errno;
1138 : : }
1139 : 0 : age_info->hw_age.aged_list = r;
1140 : 0 : DRV_LOG(DEBUG, "\"%s\" is successfully created (size=%u).",
1141 : : mz_name, ring_size);
1142 : : /* In non "strict_queue" mode, initialize the event. */
1143 : 0 : MLX5_AGE_SET(age_info, MLX5_AGE_TRIGGER);
1144 : : }
1145 : : return 0;
1146 : : error:
1147 : : MLX5_ASSERT(strict_queue);
1148 [ # # ]: 0 : while (qidx--)
1149 : 0 : rte_ring_free(age_info->hw_q_age->aged_lists[qidx]);
1150 : 0 : mlx5_free(age_info->hw_q_age);
1151 : 0 : return -1;
1152 : : }
1153 : :
1154 : : /**
1155 : : * Cleanup aged-out ring before destroying.
1156 : : *
1157 : : * @param priv
1158 : : * Pointer to port private object.
1159 : : * @param r
1160 : : * Pointer to aged-out ring object.
1161 : : */
1162 : : static void
1163 : 0 : mlx5_hws_aged_out_ring_cleanup(struct mlx5_priv *priv, struct rte_ring *r)
1164 : : {
1165 : 0 : int ring_size = rte_ring_count(r);
1166 : :
1167 [ # # ]: 0 : while (ring_size > 0) {
1168 [ # # # # : 0 : uint32_t age_idx = 0;
# ]
1169 : :
1170 : : if (rte_ring_dequeue_elem(r, &age_idx, sizeof(uint32_t)) < 0)
1171 : : break;
1172 : : /* get the AGE context if the aged-out index is still valid. */
1173 : 0 : mlx5_hws_age_context_get(priv, age_idx);
1174 : 0 : ring_size--;
1175 : : }
1176 : 0 : rte_ring_free(r);
1177 : 0 : }
1178 : :
1179 : : /**
1180 : : * Destroy the shared aging list information per port.
1181 : : *
1182 : : * @param priv
1183 : : * Pointer to port private object.
1184 : : */
1185 : : static void
1186 : 0 : mlx5_hws_age_info_destroy(struct mlx5_priv *priv)
1187 : : {
1188 : 0 : struct mlx5_age_info *age_info = GET_PORT_AGE_INFO(priv);
1189 : 0 : uint16_t nb_queues = age_info->hw_q_age->nb_rings;
1190 : : struct rte_ring *r;
1191 : :
1192 [ # # ]: 0 : if (priv->hws_strict_queue) {
1193 : : uint32_t qidx;
1194 : :
1195 [ # # ]: 0 : for (qidx = 0; qidx < nb_queues; ++qidx) {
1196 : 0 : r = age_info->hw_q_age->aged_lists[qidx];
1197 : 0 : mlx5_hws_aged_out_ring_cleanup(priv, r);
1198 : : }
1199 : 0 : mlx5_free(age_info->hw_q_age);
1200 : : } else {
1201 : : r = age_info->hw_age.aged_list;
1202 : 0 : mlx5_hws_aged_out_ring_cleanup(priv, r);
1203 : : }
1204 : 0 : }
1205 : :
1206 : : /**
1207 : : * Initialize the aging mechanism per port.
1208 : : *
1209 : : * @param dev
1210 : : * Pointer to the rte_eth_dev structure.
1211 : : * @param attr
1212 : : * Port configuration attributes.
1213 : : * @param nb_queues
1214 : : * Number of HWS queues.
1215 : : *
1216 : : * @return
1217 : : * 0 on success, a negative errno value otherwise and rte_errno is set.
1218 : : */
1219 : : int
1220 : 0 : mlx5_hws_age_pool_init(struct rte_eth_dev *dev,
1221 : : const struct rte_flow_port_attr *attr,
1222 : : uint16_t nb_queues)
1223 : : {
1224 : 0 : struct mlx5_priv *priv = dev->data->dev_private;
1225 : 0 : struct mlx5_age_info *age_info = GET_PORT_AGE_INFO(priv);
1226 : 0 : struct mlx5_indexed_pool_config cfg = {
1227 : : .size =
1228 : : RTE_CACHE_LINE_ROUNDUP(sizeof(struct mlx5_hws_age_param)),
1229 : : .trunk_size = 1 << 12,
1230 : : .per_core_cache = 1 << 13,
1231 : : .need_lock = 1,
1232 : 0 : .release_mem_en = !!priv->sh->config.reclaim_mode,
1233 : : .malloc = mlx5_malloc,
1234 : : .free = mlx5_free,
1235 : : .type = "mlx5_hws_age_pool",
1236 : : };
1237 : : bool strict_queue = false;
1238 : : uint32_t nb_alloc_cnts;
1239 : : uint32_t rsize;
1240 : : uint32_t nb_ages_updated;
1241 : : int ret;
1242 : :
1243 : 0 : strict_queue = !!(attr->flags & RTE_FLOW_PORT_FLAG_STRICT_QUEUE);
1244 : : MLX5_ASSERT(priv->hws_cpool);
1245 [ # # ]: 0 : if (attr->flags & RTE_FLOW_PORT_FLAG_SHARE_INDIRECT) {
1246 : 0 : DRV_LOG(ERR, "Aging sn not supported "
1247 : : "in cross vHCA sharing mode");
1248 : 0 : rte_errno = ENOTSUP;
1249 : 0 : return -ENOTSUP;
1250 : : }
1251 [ # # ]: 0 : nb_alloc_cnts = mlx5_hws_cnt_pool_get_size(priv->hws_cpool);
1252 [ # # ]: 0 : if (strict_queue) {
1253 : 0 : rsize = mlx5_hws_aged_out_q_ring_size_get(nb_alloc_cnts,
1254 : : nb_queues);
1255 : 0 : nb_ages_updated = rsize * nb_queues + attr->nb_aging_objects;
1256 : : } else {
1257 : : rsize = mlx5_hws_aged_out_ring_size_get(nb_alloc_cnts);
1258 : 0 : nb_ages_updated = rsize + attr->nb_aging_objects;
1259 : : }
1260 : 0 : ret = mlx5_hws_age_info_init(dev, nb_queues, strict_queue, rsize);
1261 [ # # ]: 0 : if (ret < 0)
1262 : : return ret;
1263 : 0 : cfg.max_idx = rte_align32pow2(nb_ages_updated);
1264 [ # # ]: 0 : if (cfg.max_idx <= cfg.trunk_size) {
1265 : 0 : cfg.per_core_cache = 0;
1266 : 0 : cfg.trunk_size = cfg.max_idx;
1267 [ # # ]: 0 : } else if (cfg.max_idx <= MLX5_HW_IPOOL_SIZE_THRESHOLD) {
1268 : 0 : cfg.per_core_cache = MLX5_HW_IPOOL_CACHE_MIN;
1269 : : }
1270 : 0 : age_info->ages_ipool = mlx5_ipool_create(&cfg);
1271 [ # # ]: 0 : if (age_info->ages_ipool == NULL) {
1272 : 0 : mlx5_hws_age_info_destroy(priv);
1273 : 0 : rte_errno = ENOMEM;
1274 : 0 : return -rte_errno;
1275 : : }
1276 : 0 : priv->hws_age_req = 1;
1277 : 0 : return 0;
1278 : : }
1279 : :
1280 : : /**
1281 : : * Cleanup all aging resources per port.
1282 : : *
1283 : : * @param priv
1284 : : * Pointer to port private object.
1285 : : */
1286 : : void
1287 : 0 : mlx5_hws_age_pool_destroy(struct mlx5_priv *priv)
1288 : : {
1289 : 0 : struct mlx5_age_info *age_info = GET_PORT_AGE_INFO(priv);
1290 : :
1291 : 0 : rte_spinlock_lock(&priv->sh->cpool_lock);
1292 : : MLX5_ASSERT(priv->hws_age_req);
1293 : 0 : mlx5_hws_age_info_destroy(priv);
1294 : 0 : mlx5_ipool_destroy(age_info->ages_ipool);
1295 : 0 : age_info->ages_ipool = NULL;
1296 : 0 : priv->hws_age_req = 0;
1297 : 0 : rte_spinlock_unlock(&priv->sh->cpool_lock);
1298 : 0 : }
1299 : :
1300 : : #endif
|