net/mlx5: remove cache term from the list utility
[dpdk.git] / drivers / net / mlx5 / mlx5_utils.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2019 Mellanox Technologies, Ltd
3  */
4
5 #include <rte_malloc.h>
6
7 #include <mlx5_malloc.h>
8
9 #include "mlx5_utils.h"
10
11
12 /********************* MLX5 list ************************/
13
14 static struct mlx5_list_entry *
15 mlx5_list_default_create_cb(struct mlx5_list *list,
16                              struct mlx5_list_entry *entry __rte_unused,
17                              void *ctx __rte_unused)
18 {
19         return mlx5_malloc(MLX5_MEM_ZERO, list->entry_sz, 0, SOCKET_ID_ANY);
20 }
21
22 static void
23 mlx5_list_default_remove_cb(struct mlx5_list *list __rte_unused,
24                              struct mlx5_list_entry *entry)
25 {
26         mlx5_free(entry);
27 }
28
29 int
30 mlx5_list_create(struct mlx5_list *list, const char *name,
31                      uint32_t entry_size, void *ctx,
32                      mlx5_list_create_cb cb_create,
33                      mlx5_list_match_cb cb_match,
34                      mlx5_list_remove_cb cb_remove)
35 {
36         MLX5_ASSERT(list);
37         if (!cb_match || (!cb_create ^ !cb_remove))
38                 return -1;
39         if (name)
40                 snprintf(list->name, sizeof(list->name), "%s", name);
41         list->entry_sz = entry_size;
42         list->ctx = ctx;
43         list->cb_create = cb_create ? cb_create : mlx5_list_default_create_cb;
44         list->cb_match = cb_match;
45         list->cb_remove = cb_remove ? cb_remove : mlx5_list_default_remove_cb;
46         rte_rwlock_init(&list->lock);
47         DRV_LOG(DEBUG, "mlx5 list %s initialized.", list->name);
48         LIST_INIT(&list->head);
49         return 0;
50 }
51
52 static struct mlx5_list_entry *
53 __list_lookup(struct mlx5_list *list, void *ctx, bool reuse)
54 {
55         struct mlx5_list_entry *entry;
56
57         LIST_FOREACH(entry, &list->head, next) {
58                 if (list->cb_match(list, entry, ctx))
59                         continue;
60                 if (reuse) {
61                         __atomic_add_fetch(&entry->ref_cnt, 1,
62                                            __ATOMIC_RELAXED);
63                         DRV_LOG(DEBUG, "mlx5 list %s entry %p ref++: %u.",
64                                 list->name, (void *)entry, entry->ref_cnt);
65                 }
66                 break;
67         }
68         return entry;
69 }
70
71 static struct mlx5_list_entry *
72 list_lookup(struct mlx5_list *list, void *ctx, bool reuse)
73 {
74         struct mlx5_list_entry *entry;
75
76         rte_rwlock_read_lock(&list->lock);
77         entry = __list_lookup(list, ctx, reuse);
78         rte_rwlock_read_unlock(&list->lock);
79         return entry;
80 }
81
82 struct mlx5_list_entry *
83 mlx5_list_lookup(struct mlx5_list *list, void *ctx)
84 {
85         return list_lookup(list, ctx, false);
86 }
87
88 struct mlx5_list_entry *
89 mlx5_list_register(struct mlx5_list *list, void *ctx)
90 {
91         struct mlx5_list_entry *entry;
92         uint32_t prev_gen_cnt = 0;
93
94         MLX5_ASSERT(list);
95         prev_gen_cnt = __atomic_load_n(&list->gen_cnt, __ATOMIC_ACQUIRE);
96         /* Lookup with read lock, reuse if found. */
97         entry = list_lookup(list, ctx, true);
98         if (entry)
99                 return entry;
100         /* Not found, append with write lock - block read from other threads. */
101         rte_rwlock_write_lock(&list->lock);
102         /* If list changed by other threads before lock, search again. */
103         if (prev_gen_cnt != __atomic_load_n(&list->gen_cnt, __ATOMIC_ACQUIRE)) {
104                 /* Lookup and reuse w/o read lock. */
105                 entry = __list_lookup(list, ctx, true);
106                 if (entry)
107                         goto done;
108         }
109         entry = list->cb_create(list, entry, ctx);
110         if (!entry) {
111                 DRV_LOG(ERR, "Failed to init mlx5 list %s entry %p.",
112                         list->name, (void *)entry);
113                 goto done;
114         }
115         entry->ref_cnt = 1;
116         LIST_INSERT_HEAD(&list->head, entry, next);
117         __atomic_add_fetch(&list->gen_cnt, 1, __ATOMIC_RELEASE);
118         __atomic_add_fetch(&list->count, 1, __ATOMIC_ACQUIRE);
119         DRV_LOG(DEBUG, "mlx5 list %s entry %p new: %u.",
120                 list->name, (void *)entry, entry->ref_cnt);
121 done:
122         rte_rwlock_write_unlock(&list->lock);
123         return entry;
124 }
125
126 int
127 mlx5_list_unregister(struct mlx5_list *list,
128                       struct mlx5_list_entry *entry)
129 {
130         rte_rwlock_write_lock(&list->lock);
131         MLX5_ASSERT(entry && entry->next.le_prev);
132         DRV_LOG(DEBUG, "mlx5 list %s entry %p ref--: %u.",
133                 list->name, (void *)entry, entry->ref_cnt);
134         if (--entry->ref_cnt) {
135                 rte_rwlock_write_unlock(&list->lock);
136                 return 1;
137         }
138         __atomic_add_fetch(&list->gen_cnt, 1, __ATOMIC_ACQUIRE);
139         __atomic_sub_fetch(&list->count, 1, __ATOMIC_ACQUIRE);
140         LIST_REMOVE(entry, next);
141         list->cb_remove(list, entry);
142         rte_rwlock_write_unlock(&list->lock);
143         DRV_LOG(DEBUG, "mlx5 list %s entry %p removed.",
144                 list->name, (void *)entry);
145         return 0;
146 }
147
148 void
149 mlx5_list_destroy(struct mlx5_list *list)
150 {
151         struct mlx5_list_entry *entry;
152
153         MLX5_ASSERT(list);
154         /* no LIST_FOREACH_SAFE, using while instead */
155         while (!LIST_EMPTY(&list->head)) {
156                 entry = LIST_FIRST(&list->head);
157                 LIST_REMOVE(entry, next);
158                 list->cb_remove(list, entry);
159                 DRV_LOG(DEBUG, "mlx5 list %s entry %p destroyed.",
160                         list->name, (void *)entry);
161         }
162         memset(list, 0, sizeof(*list));
163 }
164
165 uint32_t
166 mlx5_list_get_entry_num(struct mlx5_list *list)
167 {
168         MLX5_ASSERT(list);
169         return __atomic_load_n(&list->count, __ATOMIC_RELAXED);
170 }
171
172 /********************* Indexed pool **********************/
173
174 static inline void
175 mlx5_ipool_lock(struct mlx5_indexed_pool *pool)
176 {
177         if (pool->cfg.need_lock)
178                 rte_spinlock_lock(&pool->rsz_lock);
179 }
180
181 static inline void
182 mlx5_ipool_unlock(struct mlx5_indexed_pool *pool)
183 {
184         if (pool->cfg.need_lock)
185                 rte_spinlock_unlock(&pool->rsz_lock);
186 }
187
188 static inline uint32_t
189 mlx5_trunk_idx_get(struct mlx5_indexed_pool *pool, uint32_t entry_idx)
190 {
191         struct mlx5_indexed_pool_config *cfg = &pool->cfg;
192         uint32_t trunk_idx = 0;
193         uint32_t i;
194
195         if (!cfg->grow_trunk)
196                 return entry_idx / cfg->trunk_size;
197         if (entry_idx >= pool->grow_tbl[cfg->grow_trunk - 1]) {
198                 trunk_idx = (entry_idx - pool->grow_tbl[cfg->grow_trunk - 1]) /
199                             (cfg->trunk_size << (cfg->grow_shift *
200                             cfg->grow_trunk)) + cfg->grow_trunk;
201         } else {
202                 for (i = 0; i < cfg->grow_trunk; i++) {
203                         if (entry_idx < pool->grow_tbl[i])
204                                 break;
205                 }
206                 trunk_idx = i;
207         }
208         return trunk_idx;
209 }
210
211 static inline uint32_t
212 mlx5_trunk_size_get(struct mlx5_indexed_pool *pool, uint32_t trunk_idx)
213 {
214         struct mlx5_indexed_pool_config *cfg = &pool->cfg;
215
216         return cfg->trunk_size << (cfg->grow_shift *
217                (trunk_idx > cfg->grow_trunk ? cfg->grow_trunk : trunk_idx));
218 }
219
220 static inline uint32_t
221 mlx5_trunk_idx_offset_get(struct mlx5_indexed_pool *pool, uint32_t trunk_idx)
222 {
223         struct mlx5_indexed_pool_config *cfg = &pool->cfg;
224         uint32_t offset = 0;
225
226         if (!trunk_idx)
227                 return 0;
228         if (!cfg->grow_trunk)
229                 return cfg->trunk_size * trunk_idx;
230         if (trunk_idx < cfg->grow_trunk)
231                 offset = pool->grow_tbl[trunk_idx - 1];
232         else
233                 offset = pool->grow_tbl[cfg->grow_trunk - 1] +
234                          (cfg->trunk_size << (cfg->grow_shift *
235                          cfg->grow_trunk)) * (trunk_idx - cfg->grow_trunk);
236         return offset;
237 }
238
239 struct mlx5_indexed_pool *
240 mlx5_ipool_create(struct mlx5_indexed_pool_config *cfg)
241 {
242         struct mlx5_indexed_pool *pool;
243         uint32_t i;
244
245         if (!cfg || (!cfg->malloc ^ !cfg->free) ||
246             (cfg->per_core_cache && cfg->release_mem_en) ||
247             (cfg->trunk_size && ((cfg->trunk_size & (cfg->trunk_size - 1)) ||
248             ((__builtin_ffs(cfg->trunk_size) + TRUNK_IDX_BITS) > 32))))
249                 return NULL;
250         pool = mlx5_malloc(MLX5_MEM_ZERO, sizeof(*pool) + cfg->grow_trunk *
251                            sizeof(pool->grow_tbl[0]), RTE_CACHE_LINE_SIZE,
252                            SOCKET_ID_ANY);
253         if (!pool)
254                 return NULL;
255         pool->cfg = *cfg;
256         if (!pool->cfg.trunk_size)
257                 pool->cfg.trunk_size = MLX5_IPOOL_DEFAULT_TRUNK_SIZE;
258         if (!cfg->malloc && !cfg->free) {
259                 pool->cfg.malloc = mlx5_malloc;
260                 pool->cfg.free = mlx5_free;
261         }
262         if (pool->cfg.need_lock)
263                 rte_spinlock_init(&pool->rsz_lock);
264         /*
265          * Initialize the dynamic grow trunk size lookup table to have a quick
266          * lookup for the trunk entry index offset.
267          */
268         for (i = 0; i < cfg->grow_trunk; i++) {
269                 pool->grow_tbl[i] = cfg->trunk_size << (cfg->grow_shift * i);
270                 if (i > 0)
271                         pool->grow_tbl[i] += pool->grow_tbl[i - 1];
272         }
273         if (!pool->cfg.max_idx)
274                 pool->cfg.max_idx =
275                         mlx5_trunk_idx_offset_get(pool, TRUNK_MAX_IDX + 1);
276         if (!cfg->per_core_cache)
277                 pool->free_list = TRUNK_INVALID;
278         rte_spinlock_init(&pool->lcore_lock);
279         return pool;
280 }
281
282 static int
283 mlx5_ipool_grow(struct mlx5_indexed_pool *pool)
284 {
285         struct mlx5_indexed_trunk *trunk;
286         struct mlx5_indexed_trunk **trunk_tmp;
287         struct mlx5_indexed_trunk **p;
288         size_t trunk_size = 0;
289         size_t data_size;
290         size_t bmp_size;
291         uint32_t idx, cur_max_idx, i;
292
293         cur_max_idx = mlx5_trunk_idx_offset_get(pool, pool->n_trunk_valid);
294         if (pool->n_trunk_valid == TRUNK_MAX_IDX ||
295             cur_max_idx >= pool->cfg.max_idx)
296                 return -ENOMEM;
297         if (pool->n_trunk_valid == pool->n_trunk) {
298                 /* No free trunk flags, expand trunk list. */
299                 int n_grow = pool->n_trunk_valid ? pool->n_trunk :
300                              RTE_CACHE_LINE_SIZE / sizeof(void *);
301
302                 p = pool->cfg.malloc(0, (pool->n_trunk_valid + n_grow) *
303                                      sizeof(struct mlx5_indexed_trunk *),
304                                      RTE_CACHE_LINE_SIZE, rte_socket_id());
305                 if (!p)
306                         return -ENOMEM;
307                 if (pool->trunks)
308                         memcpy(p, pool->trunks, pool->n_trunk_valid *
309                                sizeof(struct mlx5_indexed_trunk *));
310                 memset(RTE_PTR_ADD(p, pool->n_trunk_valid * sizeof(void *)), 0,
311                        n_grow * sizeof(void *));
312                 trunk_tmp = pool->trunks;
313                 pool->trunks = p;
314                 if (trunk_tmp)
315                         pool->cfg.free(trunk_tmp);
316                 pool->n_trunk += n_grow;
317         }
318         if (!pool->cfg.release_mem_en) {
319                 idx = pool->n_trunk_valid;
320         } else {
321                 /* Find the first available slot in trunk list */
322                 for (idx = 0; idx < pool->n_trunk; idx++)
323                         if (pool->trunks[idx] == NULL)
324                                 break;
325         }
326         trunk_size += sizeof(*trunk);
327         data_size = mlx5_trunk_size_get(pool, idx);
328         bmp_size = rte_bitmap_get_memory_footprint(data_size);
329         /* rte_bitmap requires memory cacheline aligned. */
330         trunk_size += RTE_CACHE_LINE_ROUNDUP(data_size * pool->cfg.size);
331         trunk_size += bmp_size;
332         trunk = pool->cfg.malloc(0, trunk_size,
333                                  RTE_CACHE_LINE_SIZE, rte_socket_id());
334         if (!trunk)
335                 return -ENOMEM;
336         pool->trunks[idx] = trunk;
337         trunk->idx = idx;
338         trunk->free = data_size;
339         trunk->prev = TRUNK_INVALID;
340         trunk->next = TRUNK_INVALID;
341         MLX5_ASSERT(pool->free_list == TRUNK_INVALID);
342         pool->free_list = idx;
343         /* Mark all entries as available. */
344         trunk->bmp = rte_bitmap_init_with_all_set(data_size, &trunk->data
345                      [RTE_CACHE_LINE_ROUNDUP(data_size * pool->cfg.size)],
346                      bmp_size);
347         /* Clear the overhead bits in the trunk if it happens. */
348         if (cur_max_idx + data_size > pool->cfg.max_idx) {
349                 for (i = pool->cfg.max_idx - cur_max_idx; i < data_size; i++)
350                         rte_bitmap_clear(trunk->bmp, i);
351         }
352         MLX5_ASSERT(trunk->bmp);
353         pool->n_trunk_valid++;
354 #ifdef POOL_DEBUG
355         pool->trunk_new++;
356         pool->trunk_avail++;
357 #endif
358         return 0;
359 }
360
361 static inline struct mlx5_indexed_cache *
362 mlx5_ipool_update_global_cache(struct mlx5_indexed_pool *pool, int cidx)
363 {
364         struct mlx5_indexed_cache *gc, *lc, *olc = NULL;
365
366         lc = pool->cache[cidx]->lc;
367         gc = __atomic_load_n(&pool->gc, __ATOMIC_RELAXED);
368         if (gc && lc != gc) {
369                 mlx5_ipool_lock(pool);
370                 if (lc && !(--lc->ref_cnt))
371                         olc = lc;
372                 lc = pool->gc;
373                 lc->ref_cnt++;
374                 pool->cache[cidx]->lc = lc;
375                 mlx5_ipool_unlock(pool);
376                 if (olc)
377                         pool->cfg.free(olc);
378         }
379         return lc;
380 }
381
382 static uint32_t
383 mlx5_ipool_allocate_from_global(struct mlx5_indexed_pool *pool, int cidx)
384 {
385         struct mlx5_indexed_trunk *trunk;
386         struct mlx5_indexed_cache *p, *lc, *olc = NULL;
387         size_t trunk_size = 0;
388         size_t data_size;
389         uint32_t cur_max_idx, trunk_idx, trunk_n;
390         uint32_t fetch_size, ts_idx, i;
391         int n_grow;
392
393 check_again:
394         p = NULL;
395         fetch_size = 0;
396         /*
397          * Fetch new index from global if possible. First round local
398          * cache will be NULL.
399          */
400         lc = pool->cache[cidx]->lc;
401         mlx5_ipool_lock(pool);
402         /* Try to update local cache first. */
403         if (likely(pool->gc)) {
404                 if (lc != pool->gc) {
405                         if (lc && !(--lc->ref_cnt))
406                                 olc = lc;
407                         lc = pool->gc;
408                         lc->ref_cnt++;
409                         pool->cache[cidx]->lc = lc;
410                 }
411                 if (lc->len) {
412                         /* Use the updated local cache to fetch index. */
413                         fetch_size = pool->cfg.per_core_cache >> 2;
414                         if (lc->len < fetch_size)
415                                 fetch_size = lc->len;
416                         lc->len -= fetch_size;
417                         memcpy(pool->cache[cidx]->idx, &lc->idx[lc->len],
418                                sizeof(uint32_t) * fetch_size);
419                 }
420         }
421         mlx5_ipool_unlock(pool);
422         if (unlikely(olc)) {
423                 pool->cfg.free(olc);
424                 olc = NULL;
425         }
426         if (fetch_size) {
427                 pool->cache[cidx]->len = fetch_size - 1;
428                 return pool->cache[cidx]->idx[pool->cache[cidx]->len];
429         }
430         trunk_idx = lc ? __atomic_load_n(&lc->n_trunk_valid,
431                          __ATOMIC_ACQUIRE) : 0;
432         trunk_n = lc ? lc->n_trunk : 0;
433         cur_max_idx = mlx5_trunk_idx_offset_get(pool, trunk_idx);
434         /* Check if index reach maximum. */
435         if (trunk_idx == TRUNK_MAX_IDX ||
436             cur_max_idx >= pool->cfg.max_idx)
437                 return 0;
438         /* No enough space in trunk array, resize the trunks array. */
439         if (trunk_idx == trunk_n) {
440                 n_grow = trunk_idx ? trunk_idx :
441                              RTE_CACHE_LINE_SIZE / sizeof(void *);
442                 cur_max_idx = mlx5_trunk_idx_offset_get(pool, trunk_n + n_grow);
443                 /* Resize the trunk array. */
444                 p = pool->cfg.malloc(0, ((trunk_idx + n_grow) *
445                         sizeof(struct mlx5_indexed_trunk *)) +
446                         (cur_max_idx * sizeof(uint32_t)) + sizeof(*p),
447                         RTE_CACHE_LINE_SIZE, rte_socket_id());
448                 if (!p)
449                         return 0;
450                 p->trunks = (struct mlx5_indexed_trunk **)&p->idx[cur_max_idx];
451                 if (lc)
452                         memcpy(p->trunks, lc->trunks, trunk_idx *
453                        sizeof(struct mlx5_indexed_trunk *));
454 #ifdef RTE_LIBRTE_MLX5_DEBUG
455                 memset(RTE_PTR_ADD(p->trunks, trunk_idx * sizeof(void *)), 0,
456                         n_grow * sizeof(void *));
457 #endif
458                 p->n_trunk_valid = trunk_idx;
459                 p->n_trunk = trunk_n + n_grow;
460                 p->len = 0;
461         }
462         /* Prepare the new trunk. */
463         trunk_size = sizeof(*trunk);
464         data_size = mlx5_trunk_size_get(pool, trunk_idx);
465         trunk_size += RTE_CACHE_LINE_ROUNDUP(data_size * pool->cfg.size);
466         trunk = pool->cfg.malloc(0, trunk_size,
467                                  RTE_CACHE_LINE_SIZE, rte_socket_id());
468         if (unlikely(!trunk)) {
469                 pool->cfg.free(p);
470                 return 0;
471         }
472         trunk->idx = trunk_idx;
473         trunk->free = data_size;
474         mlx5_ipool_lock(pool);
475         /*
476          * Double check if trunks has been updated or have available index.
477          * During the new trunk allocate, index may still be flushed to the
478          * global cache. So also need to check the pool->gc->len.
479          */
480         if (pool->gc && (lc != pool->gc ||
481             lc->n_trunk_valid != trunk_idx ||
482             pool->gc->len)) {
483                 mlx5_ipool_unlock(pool);
484                 if (p)
485                         pool->cfg.free(p);
486                 pool->cfg.free(trunk);
487                 goto check_again;
488         }
489         /* Resize the trunk array and update local cache first.  */
490         if (p) {
491                 if (lc && !(--lc->ref_cnt))
492                         olc = lc;
493                 lc = p;
494                 lc->ref_cnt = 1;
495                 pool->cache[cidx]->lc = lc;
496                 __atomic_store_n(&pool->gc, p, __ATOMIC_RELAXED);
497         }
498         /* Add trunk to trunks array. */
499         lc->trunks[trunk_idx] = trunk;
500         __atomic_fetch_add(&lc->n_trunk_valid, 1, __ATOMIC_RELAXED);
501         /* Enqueue half of the index to global. */
502         ts_idx = mlx5_trunk_idx_offset_get(pool, trunk_idx) + 1;
503         fetch_size = trunk->free >> 1;
504         for (i = 0; i < fetch_size; i++)
505                 lc->idx[i] = ts_idx + i;
506         lc->len = fetch_size;
507         mlx5_ipool_unlock(pool);
508         /* Copy left half - 1 to local cache index array. */
509         pool->cache[cidx]->len = trunk->free - fetch_size - 1;
510         ts_idx += fetch_size;
511         for (i = 0; i < pool->cache[cidx]->len; i++)
512                 pool->cache[cidx]->idx[i] = ts_idx + i;
513         if (olc)
514                 pool->cfg.free(olc);
515         return ts_idx + i;
516 }
517
518 static void *
519 _mlx5_ipool_get_cache(struct mlx5_indexed_pool *pool, int cidx, uint32_t idx)
520 {
521         struct mlx5_indexed_trunk *trunk;
522         struct mlx5_indexed_cache *lc;
523         uint32_t trunk_idx;
524         uint32_t entry_idx;
525
526         MLX5_ASSERT(idx);
527         if (unlikely(!pool->cache[cidx])) {
528                 pool->cache[cidx] = pool->cfg.malloc(MLX5_MEM_ZERO,
529                         sizeof(struct mlx5_ipool_per_lcore) +
530                         (pool->cfg.per_core_cache * sizeof(uint32_t)),
531                         RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY);
532                 if (!pool->cache[cidx]) {
533                         DRV_LOG(ERR, "Ipool cache%d allocate failed\n", cidx);
534                         return NULL;
535                 }
536         }
537         lc = mlx5_ipool_update_global_cache(pool, cidx);
538         idx -= 1;
539         trunk_idx = mlx5_trunk_idx_get(pool, idx);
540         trunk = lc->trunks[trunk_idx];
541         MLX5_ASSERT(trunk);
542         entry_idx = idx - mlx5_trunk_idx_offset_get(pool, trunk_idx);
543         return &trunk->data[entry_idx * pool->cfg.size];
544 }
545
546 static void *
547 mlx5_ipool_get_cache(struct mlx5_indexed_pool *pool, uint32_t idx)
548 {
549         void *entry;
550         int cidx;
551
552         cidx = rte_lcore_index(rte_lcore_id());
553         if (unlikely(cidx == -1)) {
554                 cidx = RTE_MAX_LCORE;
555                 rte_spinlock_lock(&pool->lcore_lock);
556         }
557         entry = _mlx5_ipool_get_cache(pool, cidx, idx);
558         if (unlikely(cidx == RTE_MAX_LCORE))
559                 rte_spinlock_unlock(&pool->lcore_lock);
560         return entry;
561 }
562
563
564 static void *
565 _mlx5_ipool_malloc_cache(struct mlx5_indexed_pool *pool, int cidx,
566                          uint32_t *idx)
567 {
568         if (unlikely(!pool->cache[cidx])) {
569                 pool->cache[cidx] = pool->cfg.malloc(MLX5_MEM_ZERO,
570                         sizeof(struct mlx5_ipool_per_lcore) +
571                         (pool->cfg.per_core_cache * sizeof(uint32_t)),
572                         RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY);
573                 if (!pool->cache[cidx]) {
574                         DRV_LOG(ERR, "Ipool cache%d allocate failed\n", cidx);
575                         return NULL;
576                 }
577         } else if (pool->cache[cidx]->len) {
578                 pool->cache[cidx]->len--;
579                 *idx = pool->cache[cidx]->idx[pool->cache[cidx]->len];
580                 return _mlx5_ipool_get_cache(pool, cidx, *idx);
581         }
582         /* Not enough idx in global cache. Keep fetching from global. */
583         *idx = mlx5_ipool_allocate_from_global(pool, cidx);
584         if (unlikely(!(*idx)))
585                 return NULL;
586         return _mlx5_ipool_get_cache(pool, cidx, *idx);
587 }
588
589 static void *
590 mlx5_ipool_malloc_cache(struct mlx5_indexed_pool *pool, uint32_t *idx)
591 {
592         void *entry;
593         int cidx;
594
595         cidx = rte_lcore_index(rte_lcore_id());
596         if (unlikely(cidx == -1)) {
597                 cidx = RTE_MAX_LCORE;
598                 rte_spinlock_lock(&pool->lcore_lock);
599         }
600         entry = _mlx5_ipool_malloc_cache(pool, cidx, idx);
601         if (unlikely(cidx == RTE_MAX_LCORE))
602                 rte_spinlock_unlock(&pool->lcore_lock);
603         return entry;
604 }
605
606 static void
607 _mlx5_ipool_free_cache(struct mlx5_indexed_pool *pool, int cidx, uint32_t idx)
608 {
609         struct mlx5_ipool_per_lcore *ilc;
610         struct mlx5_indexed_cache *gc, *olc = NULL;
611         uint32_t reclaim_num = 0;
612
613         MLX5_ASSERT(idx);
614         /*
615          * When index was allocated on core A but freed on core B. In this
616          * case check if local cache on core B was allocated before.
617          */
618         if (unlikely(!pool->cache[cidx])) {
619                 pool->cache[cidx] = pool->cfg.malloc(MLX5_MEM_ZERO,
620                         sizeof(struct mlx5_ipool_per_lcore) +
621                         (pool->cfg.per_core_cache * sizeof(uint32_t)),
622                         RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY);
623                 if (!pool->cache[cidx]) {
624                         DRV_LOG(ERR, "Ipool cache%d allocate failed\n", cidx);
625                         return;
626                 }
627         }
628         /* Try to enqueue to local index cache. */
629         if (pool->cache[cidx]->len < pool->cfg.per_core_cache) {
630                 pool->cache[cidx]->idx[pool->cache[cidx]->len] = idx;
631                 pool->cache[cidx]->len++;
632                 return;
633         }
634         ilc = pool->cache[cidx];
635         reclaim_num = pool->cfg.per_core_cache >> 2;
636         ilc->len -= reclaim_num;
637         /* Local index cache full, try with global index cache. */
638         mlx5_ipool_lock(pool);
639         gc = pool->gc;
640         if (ilc->lc != gc) {
641                 if (!(--ilc->lc->ref_cnt))
642                         olc = ilc->lc;
643                 gc->ref_cnt++;
644                 ilc->lc = gc;
645         }
646         memcpy(&gc->idx[gc->len], &ilc->idx[ilc->len],
647                reclaim_num * sizeof(uint32_t));
648         gc->len += reclaim_num;
649         mlx5_ipool_unlock(pool);
650         if (olc)
651                 pool->cfg.free(olc);
652         pool->cache[cidx]->idx[pool->cache[cidx]->len] = idx;
653         pool->cache[cidx]->len++;
654 }
655
656 static void
657 mlx5_ipool_free_cache(struct mlx5_indexed_pool *pool, uint32_t idx)
658 {
659         int cidx;
660
661         cidx = rte_lcore_index(rte_lcore_id());
662         if (unlikely(cidx == -1)) {
663                 cidx = RTE_MAX_LCORE;
664                 rte_spinlock_lock(&pool->lcore_lock);
665         }
666         _mlx5_ipool_free_cache(pool, cidx, idx);
667         if (unlikely(cidx == RTE_MAX_LCORE))
668                 rte_spinlock_unlock(&pool->lcore_lock);
669 }
670
671 void *
672 mlx5_ipool_malloc(struct mlx5_indexed_pool *pool, uint32_t *idx)
673 {
674         struct mlx5_indexed_trunk *trunk;
675         uint64_t slab = 0;
676         uint32_t iidx = 0;
677         void *p;
678
679         if (pool->cfg.per_core_cache)
680                 return mlx5_ipool_malloc_cache(pool, idx);
681         mlx5_ipool_lock(pool);
682         if (pool->free_list == TRUNK_INVALID) {
683                 /* If no available trunks, grow new. */
684                 if (mlx5_ipool_grow(pool)) {
685                         mlx5_ipool_unlock(pool);
686                         return NULL;
687                 }
688         }
689         MLX5_ASSERT(pool->free_list != TRUNK_INVALID);
690         trunk = pool->trunks[pool->free_list];
691         MLX5_ASSERT(trunk->free);
692         if (!rte_bitmap_scan(trunk->bmp, &iidx, &slab)) {
693                 mlx5_ipool_unlock(pool);
694                 return NULL;
695         }
696         MLX5_ASSERT(slab);
697         iidx += __builtin_ctzll(slab);
698         MLX5_ASSERT(iidx != UINT32_MAX);
699         MLX5_ASSERT(iidx < mlx5_trunk_size_get(pool, trunk->idx));
700         rte_bitmap_clear(trunk->bmp, iidx);
701         p = &trunk->data[iidx * pool->cfg.size];
702         /*
703          * The ipool index should grow continually from small to big,
704          * some features as metering only accept limited bits of index.
705          * Random index with MSB set may be rejected.
706          */
707         iidx += mlx5_trunk_idx_offset_get(pool, trunk->idx);
708         iidx += 1; /* non-zero index. */
709         trunk->free--;
710 #ifdef POOL_DEBUG
711         pool->n_entry++;
712 #endif
713         if (!trunk->free) {
714                 /* Full trunk will be removed from free list in imalloc. */
715                 MLX5_ASSERT(pool->free_list == trunk->idx);
716                 pool->free_list = trunk->next;
717                 if (trunk->next != TRUNK_INVALID)
718                         pool->trunks[trunk->next]->prev = TRUNK_INVALID;
719                 trunk->prev = TRUNK_INVALID;
720                 trunk->next = TRUNK_INVALID;
721 #ifdef POOL_DEBUG
722                 pool->trunk_empty++;
723                 pool->trunk_avail--;
724 #endif
725         }
726         *idx = iidx;
727         mlx5_ipool_unlock(pool);
728         return p;
729 }
730
731 void *
732 mlx5_ipool_zmalloc(struct mlx5_indexed_pool *pool, uint32_t *idx)
733 {
734         void *entry = mlx5_ipool_malloc(pool, idx);
735
736         if (entry && pool->cfg.size)
737                 memset(entry, 0, pool->cfg.size);
738         return entry;
739 }
740
741 void
742 mlx5_ipool_free(struct mlx5_indexed_pool *pool, uint32_t idx)
743 {
744         struct mlx5_indexed_trunk *trunk;
745         uint32_t trunk_idx;
746         uint32_t entry_idx;
747
748         if (!idx)
749                 return;
750         if (pool->cfg.per_core_cache) {
751                 mlx5_ipool_free_cache(pool, idx);
752                 return;
753         }
754         idx -= 1;
755         mlx5_ipool_lock(pool);
756         trunk_idx = mlx5_trunk_idx_get(pool, idx);
757         if ((!pool->cfg.release_mem_en && trunk_idx >= pool->n_trunk_valid) ||
758             (pool->cfg.release_mem_en && trunk_idx >= pool->n_trunk))
759                 goto out;
760         trunk = pool->trunks[trunk_idx];
761         if (!trunk)
762                 goto out;
763         entry_idx = idx - mlx5_trunk_idx_offset_get(pool, trunk->idx);
764         if (trunk_idx != trunk->idx ||
765             rte_bitmap_get(trunk->bmp, entry_idx))
766                 goto out;
767         rte_bitmap_set(trunk->bmp, entry_idx);
768         trunk->free++;
769         if (pool->cfg.release_mem_en && trunk->free == mlx5_trunk_size_get
770            (pool, trunk->idx)) {
771                 if (pool->free_list == trunk->idx)
772                         pool->free_list = trunk->next;
773                 if (trunk->next != TRUNK_INVALID)
774                         pool->trunks[trunk->next]->prev = trunk->prev;
775                 if (trunk->prev != TRUNK_INVALID)
776                         pool->trunks[trunk->prev]->next = trunk->next;
777                 pool->cfg.free(trunk);
778                 pool->trunks[trunk_idx] = NULL;
779                 pool->n_trunk_valid--;
780 #ifdef POOL_DEBUG
781                 pool->trunk_avail--;
782                 pool->trunk_free++;
783 #endif
784                 if (pool->n_trunk_valid == 0) {
785                         pool->cfg.free(pool->trunks);
786                         pool->trunks = NULL;
787                         pool->n_trunk = 0;
788                 }
789         } else if (trunk->free == 1) {
790                 /* Put into free trunk list head. */
791                 MLX5_ASSERT(pool->free_list != trunk->idx);
792                 trunk->next = pool->free_list;
793                 trunk->prev = TRUNK_INVALID;
794                 if (pool->free_list != TRUNK_INVALID)
795                         pool->trunks[pool->free_list]->prev = trunk->idx;
796                 pool->free_list = trunk->idx;
797 #ifdef POOL_DEBUG
798                 pool->trunk_empty--;
799                 pool->trunk_avail++;
800 #endif
801         }
802 #ifdef POOL_DEBUG
803         pool->n_entry--;
804 #endif
805 out:
806         mlx5_ipool_unlock(pool);
807 }
808
809 void *
810 mlx5_ipool_get(struct mlx5_indexed_pool *pool, uint32_t idx)
811 {
812         struct mlx5_indexed_trunk *trunk;
813         void *p = NULL;
814         uint32_t trunk_idx;
815         uint32_t entry_idx;
816
817         if (!idx)
818                 return NULL;
819         if (pool->cfg.per_core_cache)
820                 return mlx5_ipool_get_cache(pool, idx);
821         idx -= 1;
822         mlx5_ipool_lock(pool);
823         trunk_idx = mlx5_trunk_idx_get(pool, idx);
824         if ((!pool->cfg.release_mem_en && trunk_idx >= pool->n_trunk_valid) ||
825             (pool->cfg.release_mem_en && trunk_idx >= pool->n_trunk))
826                 goto out;
827         trunk = pool->trunks[trunk_idx];
828         if (!trunk)
829                 goto out;
830         entry_idx = idx - mlx5_trunk_idx_offset_get(pool, trunk->idx);
831         if (trunk_idx != trunk->idx ||
832             rte_bitmap_get(trunk->bmp, entry_idx))
833                 goto out;
834         p = &trunk->data[entry_idx * pool->cfg.size];
835 out:
836         mlx5_ipool_unlock(pool);
837         return p;
838 }
839
840 int
841 mlx5_ipool_destroy(struct mlx5_indexed_pool *pool)
842 {
843         struct mlx5_indexed_trunk **trunks = NULL;
844         struct mlx5_indexed_cache *gc = pool->gc;
845         uint32_t i, n_trunk_valid = 0;
846
847         MLX5_ASSERT(pool);
848         mlx5_ipool_lock(pool);
849         if (pool->cfg.per_core_cache) {
850                 for (i = 0; i <= RTE_MAX_LCORE; i++) {
851                         /*
852                          * Free only old global cache. Pool gc will be
853                          * freed at last.
854                          */
855                         if (pool->cache[i]) {
856                                 if (pool->cache[i]->lc &&
857                                     pool->cache[i]->lc != pool->gc &&
858                                     (!(--pool->cache[i]->lc->ref_cnt)))
859                                         pool->cfg.free(pool->cache[i]->lc);
860                                 pool->cfg.free(pool->cache[i]);
861                         }
862                 }
863                 if (gc) {
864                         trunks = gc->trunks;
865                         n_trunk_valid = gc->n_trunk_valid;
866                 }
867         } else {
868                 gc = NULL;
869                 trunks = pool->trunks;
870                 n_trunk_valid = pool->n_trunk_valid;
871         }
872         for (i = 0; i < n_trunk_valid; i++) {
873                 if (trunks[i])
874                         pool->cfg.free(trunks[i]);
875         }
876         if (!gc && trunks)
877                 pool->cfg.free(trunks);
878         if (gc)
879                 pool->cfg.free(gc);
880         mlx5_ipool_unlock(pool);
881         mlx5_free(pool);
882         return 0;
883 }
884
885 void
886 mlx5_ipool_flush_cache(struct mlx5_indexed_pool *pool)
887 {
888         uint32_t i, j;
889         struct mlx5_indexed_cache *gc;
890         struct rte_bitmap *ibmp;
891         uint32_t bmp_num, mem_size;
892
893         if (!pool->cfg.per_core_cache)
894                 return;
895         gc = pool->gc;
896         if (!gc)
897                 return;
898         /* Reset bmp. */
899         bmp_num = mlx5_trunk_idx_offset_get(pool, gc->n_trunk_valid);
900         mem_size = rte_bitmap_get_memory_footprint(bmp_num);
901         pool->bmp_mem = pool->cfg.malloc(MLX5_MEM_ZERO, mem_size,
902                                          RTE_CACHE_LINE_SIZE, rte_socket_id());
903         if (!pool->bmp_mem) {
904                 DRV_LOG(ERR, "Ipool bitmap mem allocate failed.\n");
905                 return;
906         }
907         ibmp = rte_bitmap_init_with_all_set(bmp_num, pool->bmp_mem, mem_size);
908         if (!ibmp) {
909                 pool->cfg.free(pool->bmp_mem);
910                 pool->bmp_mem = NULL;
911                 DRV_LOG(ERR, "Ipool bitmap create failed.\n");
912                 return;
913         }
914         pool->ibmp = ibmp;
915         /* Clear global cache. */
916         for (i = 0; i < gc->len; i++)
917                 rte_bitmap_clear(ibmp, gc->idx[i] - 1);
918         /* Clear core cache. */
919         for (i = 0; i < RTE_MAX_LCORE + 1; i++) {
920                 struct mlx5_ipool_per_lcore *ilc = pool->cache[i];
921
922                 if (!ilc)
923                         continue;
924                 for (j = 0; j < ilc->len; j++)
925                         rte_bitmap_clear(ibmp, ilc->idx[j] - 1);
926         }
927 }
928
929 static void *
930 mlx5_ipool_get_next_cache(struct mlx5_indexed_pool *pool, uint32_t *pos)
931 {
932         struct rte_bitmap *ibmp;
933         uint64_t slab = 0;
934         uint32_t iidx = *pos;
935
936         ibmp = pool->ibmp;
937         if (!ibmp || !rte_bitmap_scan(ibmp, &iidx, &slab)) {
938                 if (pool->bmp_mem) {
939                         pool->cfg.free(pool->bmp_mem);
940                         pool->bmp_mem = NULL;
941                         pool->ibmp = NULL;
942                 }
943                 return NULL;
944         }
945         iidx += __builtin_ctzll(slab);
946         rte_bitmap_clear(ibmp, iidx);
947         iidx++;
948         *pos = iidx;
949         return mlx5_ipool_get_cache(pool, iidx);
950 }
951
952 void *
953 mlx5_ipool_get_next(struct mlx5_indexed_pool *pool, uint32_t *pos)
954 {
955         uint32_t idx = *pos;
956         void *entry;
957
958         if (pool->cfg.per_core_cache)
959                 return mlx5_ipool_get_next_cache(pool, pos);
960         while (idx <= mlx5_trunk_idx_offset_get(pool, pool->n_trunk)) {
961                 entry = mlx5_ipool_get(pool, idx);
962                 if (entry) {
963                         *pos = idx;
964                         return entry;
965                 }
966                 idx++;
967         }
968         return NULL;
969 }
970
971 void
972 mlx5_ipool_dump(struct mlx5_indexed_pool *pool)
973 {
974         printf("Pool %s entry size %u, trunks %u, %d entry per trunk, "
975                "total: %d\n",
976                pool->cfg.type, pool->cfg.size, pool->n_trunk_valid,
977                pool->cfg.trunk_size, pool->n_trunk_valid);
978 #ifdef POOL_DEBUG
979         printf("Pool %s entry %u, trunk alloc %u, empty: %u, "
980                "available %u free %u\n",
981                pool->cfg.type, pool->n_entry, pool->trunk_new,
982                pool->trunk_empty, pool->trunk_avail, pool->trunk_free);
983 #endif
984 }
985
986 struct mlx5_l3t_tbl *
987 mlx5_l3t_create(enum mlx5_l3t_type type)
988 {
989         struct mlx5_l3t_tbl *tbl;
990         struct mlx5_indexed_pool_config l3t_ip_cfg = {
991                 .trunk_size = 16,
992                 .grow_trunk = 6,
993                 .grow_shift = 1,
994                 .need_lock = 0,
995                 .release_mem_en = 1,
996                 .malloc = mlx5_malloc,
997                 .free = mlx5_free,
998         };
999
1000         if (type >= MLX5_L3T_TYPE_MAX) {
1001                 rte_errno = EINVAL;
1002                 return NULL;
1003         }
1004         tbl = mlx5_malloc(MLX5_MEM_ZERO, sizeof(struct mlx5_l3t_tbl), 1,
1005                           SOCKET_ID_ANY);
1006         if (!tbl) {
1007                 rte_errno = ENOMEM;
1008                 return NULL;
1009         }
1010         tbl->type = type;
1011         switch (type) {
1012         case MLX5_L3T_TYPE_WORD:
1013                 l3t_ip_cfg.size = sizeof(struct mlx5_l3t_entry_word);
1014                 l3t_ip_cfg.type = "mlx5_l3t_e_tbl_w";
1015                 break;
1016         case MLX5_L3T_TYPE_DWORD:
1017                 l3t_ip_cfg.size = sizeof(struct mlx5_l3t_entry_dword);
1018                 l3t_ip_cfg.type = "mlx5_l3t_e_tbl_dw";
1019                 break;
1020         case MLX5_L3T_TYPE_QWORD:
1021                 l3t_ip_cfg.size = sizeof(struct mlx5_l3t_entry_qword);
1022                 l3t_ip_cfg.type = "mlx5_l3t_e_tbl_qw";
1023                 break;
1024         default:
1025                 l3t_ip_cfg.size = sizeof(struct mlx5_l3t_entry_ptr);
1026                 l3t_ip_cfg.type = "mlx5_l3t_e_tbl_tpr";
1027                 break;
1028         }
1029         rte_spinlock_init(&tbl->sl);
1030         tbl->eip = mlx5_ipool_create(&l3t_ip_cfg);
1031         if (!tbl->eip) {
1032                 rte_errno = ENOMEM;
1033                 mlx5_free(tbl);
1034                 tbl = NULL;
1035         }
1036         return tbl;
1037 }
1038
1039 void
1040 mlx5_l3t_destroy(struct mlx5_l3t_tbl *tbl)
1041 {
1042         struct mlx5_l3t_level_tbl *g_tbl, *m_tbl;
1043         uint32_t i, j;
1044
1045         if (!tbl)
1046                 return;
1047         g_tbl = tbl->tbl;
1048         if (g_tbl) {
1049                 for (i = 0; i < MLX5_L3T_GT_SIZE; i++) {
1050                         m_tbl = g_tbl->tbl[i];
1051                         if (!m_tbl)
1052                                 continue;
1053                         for (j = 0; j < MLX5_L3T_MT_SIZE; j++) {
1054                                 if (!m_tbl->tbl[j])
1055                                         continue;
1056                                 MLX5_ASSERT(!((struct mlx5_l3t_entry_word *)
1057                                             m_tbl->tbl[j])->ref_cnt);
1058                                 mlx5_ipool_free(tbl->eip,
1059                                                 ((struct mlx5_l3t_entry_word *)
1060                                                 m_tbl->tbl[j])->idx);
1061                                 m_tbl->tbl[j] = 0;
1062                                 if (!(--m_tbl->ref_cnt))
1063                                         break;
1064                         }
1065                         MLX5_ASSERT(!m_tbl->ref_cnt);
1066                         mlx5_free(g_tbl->tbl[i]);
1067                         g_tbl->tbl[i] = 0;
1068                         if (!(--g_tbl->ref_cnt))
1069                                 break;
1070                 }
1071                 MLX5_ASSERT(!g_tbl->ref_cnt);
1072                 mlx5_free(tbl->tbl);
1073                 tbl->tbl = 0;
1074         }
1075         mlx5_ipool_destroy(tbl->eip);
1076         mlx5_free(tbl);
1077 }
1078
1079 static int32_t
1080 __l3t_get_entry(struct mlx5_l3t_tbl *tbl, uint32_t idx,
1081                 union mlx5_l3t_data *data)
1082 {
1083         struct mlx5_l3t_level_tbl *g_tbl, *m_tbl;
1084         struct mlx5_l3t_entry_word *w_e_tbl;
1085         struct mlx5_l3t_entry_dword *dw_e_tbl;
1086         struct mlx5_l3t_entry_qword *qw_e_tbl;
1087         struct mlx5_l3t_entry_ptr *ptr_e_tbl;
1088         void *e_tbl;
1089         uint32_t entry_idx;
1090
1091         g_tbl = tbl->tbl;
1092         if (!g_tbl)
1093                 return -1;
1094         m_tbl = g_tbl->tbl[(idx >> MLX5_L3T_GT_OFFSET) & MLX5_L3T_GT_MASK];
1095         if (!m_tbl)
1096                 return -1;
1097         e_tbl = m_tbl->tbl[(idx >> MLX5_L3T_MT_OFFSET) & MLX5_L3T_MT_MASK];
1098         if (!e_tbl)
1099                 return -1;
1100         entry_idx = idx & MLX5_L3T_ET_MASK;
1101         switch (tbl->type) {
1102         case MLX5_L3T_TYPE_WORD:
1103                 w_e_tbl = (struct mlx5_l3t_entry_word *)e_tbl;
1104                 data->word = w_e_tbl->entry[entry_idx].data;
1105                 if (w_e_tbl->entry[entry_idx].data)
1106                         w_e_tbl->entry[entry_idx].ref_cnt++;
1107                 break;
1108         case MLX5_L3T_TYPE_DWORD:
1109                 dw_e_tbl = (struct mlx5_l3t_entry_dword *)e_tbl;
1110                 data->dword = dw_e_tbl->entry[entry_idx].data;
1111                 if (dw_e_tbl->entry[entry_idx].data)
1112                         dw_e_tbl->entry[entry_idx].ref_cnt++;
1113                 break;
1114         case MLX5_L3T_TYPE_QWORD:
1115                 qw_e_tbl = (struct mlx5_l3t_entry_qword *)e_tbl;
1116                 data->qword = qw_e_tbl->entry[entry_idx].data;
1117                 if (qw_e_tbl->entry[entry_idx].data)
1118                         qw_e_tbl->entry[entry_idx].ref_cnt++;
1119                 break;
1120         default:
1121                 ptr_e_tbl = (struct mlx5_l3t_entry_ptr *)e_tbl;
1122                 data->ptr = ptr_e_tbl->entry[entry_idx].data;
1123                 if (ptr_e_tbl->entry[entry_idx].data)
1124                         ptr_e_tbl->entry[entry_idx].ref_cnt++;
1125                 break;
1126         }
1127         return 0;
1128 }
1129
1130 int32_t
1131 mlx5_l3t_get_entry(struct mlx5_l3t_tbl *tbl, uint32_t idx,
1132                    union mlx5_l3t_data *data)
1133 {
1134         int ret;
1135
1136         rte_spinlock_lock(&tbl->sl);
1137         ret = __l3t_get_entry(tbl, idx, data);
1138         rte_spinlock_unlock(&tbl->sl);
1139         return ret;
1140 }
1141
1142 int32_t
1143 mlx5_l3t_clear_entry(struct mlx5_l3t_tbl *tbl, uint32_t idx)
1144 {
1145         struct mlx5_l3t_level_tbl *g_tbl, *m_tbl;
1146         struct mlx5_l3t_entry_word *w_e_tbl;
1147         struct mlx5_l3t_entry_dword *dw_e_tbl;
1148         struct mlx5_l3t_entry_qword *qw_e_tbl;
1149         struct mlx5_l3t_entry_ptr *ptr_e_tbl;
1150         void *e_tbl;
1151         uint32_t entry_idx;
1152         uint64_t ref_cnt;
1153         int32_t ret = -1;
1154
1155         rte_spinlock_lock(&tbl->sl);
1156         g_tbl = tbl->tbl;
1157         if (!g_tbl)
1158                 goto out;
1159         m_tbl = g_tbl->tbl[(idx >> MLX5_L3T_GT_OFFSET) & MLX5_L3T_GT_MASK];
1160         if (!m_tbl)
1161                 goto out;
1162         e_tbl = m_tbl->tbl[(idx >> MLX5_L3T_MT_OFFSET) & MLX5_L3T_MT_MASK];
1163         if (!e_tbl)
1164                 goto out;
1165         entry_idx = idx & MLX5_L3T_ET_MASK;
1166         switch (tbl->type) {
1167         case MLX5_L3T_TYPE_WORD:
1168                 w_e_tbl = (struct mlx5_l3t_entry_word *)e_tbl;
1169                 MLX5_ASSERT(w_e_tbl->entry[entry_idx].ref_cnt);
1170                 ret = --w_e_tbl->entry[entry_idx].ref_cnt;
1171                 if (ret)
1172                         goto out;
1173                 w_e_tbl->entry[entry_idx].data = 0;
1174                 ref_cnt = --w_e_tbl->ref_cnt;
1175                 break;
1176         case MLX5_L3T_TYPE_DWORD:
1177                 dw_e_tbl = (struct mlx5_l3t_entry_dword *)e_tbl;
1178                 MLX5_ASSERT(dw_e_tbl->entry[entry_idx].ref_cnt);
1179                 ret = --dw_e_tbl->entry[entry_idx].ref_cnt;
1180                 if (ret)
1181                         goto out;
1182                 dw_e_tbl->entry[entry_idx].data = 0;
1183                 ref_cnt = --dw_e_tbl->ref_cnt;
1184                 break;
1185         case MLX5_L3T_TYPE_QWORD:
1186                 qw_e_tbl = (struct mlx5_l3t_entry_qword *)e_tbl;
1187                 MLX5_ASSERT(qw_e_tbl->entry[entry_idx].ref_cnt);
1188                 ret = --qw_e_tbl->entry[entry_idx].ref_cnt;
1189                 if (ret)
1190                         goto out;
1191                 qw_e_tbl->entry[entry_idx].data = 0;
1192                 ref_cnt = --qw_e_tbl->ref_cnt;
1193                 break;
1194         default:
1195                 ptr_e_tbl = (struct mlx5_l3t_entry_ptr *)e_tbl;
1196                 MLX5_ASSERT(ptr_e_tbl->entry[entry_idx].ref_cnt);
1197                 ret = --ptr_e_tbl->entry[entry_idx].ref_cnt;
1198                 if (ret)
1199                         goto out;
1200                 ptr_e_tbl->entry[entry_idx].data = NULL;
1201                 ref_cnt = --ptr_e_tbl->ref_cnt;
1202                 break;
1203         }
1204         if (!ref_cnt) {
1205                 mlx5_ipool_free(tbl->eip,
1206                                 ((struct mlx5_l3t_entry_word *)e_tbl)->idx);
1207                 m_tbl->tbl[(idx >> MLX5_L3T_MT_OFFSET) & MLX5_L3T_MT_MASK] =
1208                                                                         NULL;
1209                 if (!(--m_tbl->ref_cnt)) {
1210                         mlx5_free(m_tbl);
1211                         g_tbl->tbl
1212                         [(idx >> MLX5_L3T_GT_OFFSET) & MLX5_L3T_GT_MASK] = NULL;
1213                         if (!(--g_tbl->ref_cnt)) {
1214                                 mlx5_free(g_tbl);
1215                                 tbl->tbl = 0;
1216                         }
1217                 }
1218         }
1219 out:
1220         rte_spinlock_unlock(&tbl->sl);
1221         return ret;
1222 }
1223
1224 static int32_t
1225 __l3t_set_entry(struct mlx5_l3t_tbl *tbl, uint32_t idx,
1226                 union mlx5_l3t_data *data)
1227 {
1228         struct mlx5_l3t_level_tbl *g_tbl, *m_tbl;
1229         struct mlx5_l3t_entry_word *w_e_tbl;
1230         struct mlx5_l3t_entry_dword *dw_e_tbl;
1231         struct mlx5_l3t_entry_qword *qw_e_tbl;
1232         struct mlx5_l3t_entry_ptr *ptr_e_tbl;
1233         void *e_tbl;
1234         uint32_t entry_idx, tbl_idx = 0;
1235
1236         /* Check the global table, create it if empty. */
1237         g_tbl = tbl->tbl;
1238         if (!g_tbl) {
1239                 g_tbl = mlx5_malloc(MLX5_MEM_ZERO,
1240                                     sizeof(struct mlx5_l3t_level_tbl) +
1241                                     sizeof(void *) * MLX5_L3T_GT_SIZE, 1,
1242                                     SOCKET_ID_ANY);
1243                 if (!g_tbl) {
1244                         rte_errno = ENOMEM;
1245                         return -1;
1246                 }
1247                 tbl->tbl = g_tbl;
1248         }
1249         /*
1250          * Check the middle table, create it if empty. Ref_cnt will be
1251          * increased if new sub table created.
1252          */
1253         m_tbl = g_tbl->tbl[(idx >> MLX5_L3T_GT_OFFSET) & MLX5_L3T_GT_MASK];
1254         if (!m_tbl) {
1255                 m_tbl = mlx5_malloc(MLX5_MEM_ZERO,
1256                                     sizeof(struct mlx5_l3t_level_tbl) +
1257                                     sizeof(void *) * MLX5_L3T_MT_SIZE, 1,
1258                                     SOCKET_ID_ANY);
1259                 if (!m_tbl) {
1260                         rte_errno = ENOMEM;
1261                         return -1;
1262                 }
1263                 g_tbl->tbl[(idx >> MLX5_L3T_GT_OFFSET) & MLX5_L3T_GT_MASK] =
1264                                                                         m_tbl;
1265                 g_tbl->ref_cnt++;
1266         }
1267         /*
1268          * Check the entry table, create it if empty. Ref_cnt will be
1269          * increased if new sub entry table created.
1270          */
1271         e_tbl = m_tbl->tbl[(idx >> MLX5_L3T_MT_OFFSET) & MLX5_L3T_MT_MASK];
1272         if (!e_tbl) {
1273                 e_tbl = mlx5_ipool_zmalloc(tbl->eip, &tbl_idx);
1274                 if (!e_tbl) {
1275                         rte_errno = ENOMEM;
1276                         return -1;
1277                 }
1278                 ((struct mlx5_l3t_entry_word *)e_tbl)->idx = tbl_idx;
1279                 m_tbl->tbl[(idx >> MLX5_L3T_MT_OFFSET) & MLX5_L3T_MT_MASK] =
1280                                                                         e_tbl;
1281                 m_tbl->ref_cnt++;
1282         }
1283         entry_idx = idx & MLX5_L3T_ET_MASK;
1284         switch (tbl->type) {
1285         case MLX5_L3T_TYPE_WORD:
1286                 w_e_tbl = (struct mlx5_l3t_entry_word *)e_tbl;
1287                 if (w_e_tbl->entry[entry_idx].data) {
1288                         data->word = w_e_tbl->entry[entry_idx].data;
1289                         w_e_tbl->entry[entry_idx].ref_cnt++;
1290                         rte_errno = EEXIST;
1291                         return -1;
1292                 }
1293                 w_e_tbl->entry[entry_idx].data = data->word;
1294                 w_e_tbl->entry[entry_idx].ref_cnt = 1;
1295                 w_e_tbl->ref_cnt++;
1296                 break;
1297         case MLX5_L3T_TYPE_DWORD:
1298                 dw_e_tbl = (struct mlx5_l3t_entry_dword *)e_tbl;
1299                 if (dw_e_tbl->entry[entry_idx].data) {
1300                         data->dword = dw_e_tbl->entry[entry_idx].data;
1301                         dw_e_tbl->entry[entry_idx].ref_cnt++;
1302                         rte_errno = EEXIST;
1303                         return -1;
1304                 }
1305                 dw_e_tbl->entry[entry_idx].data = data->dword;
1306                 dw_e_tbl->entry[entry_idx].ref_cnt = 1;
1307                 dw_e_tbl->ref_cnt++;
1308                 break;
1309         case MLX5_L3T_TYPE_QWORD:
1310                 qw_e_tbl = (struct mlx5_l3t_entry_qword *)e_tbl;
1311                 if (qw_e_tbl->entry[entry_idx].data) {
1312                         data->qword = qw_e_tbl->entry[entry_idx].data;
1313                         qw_e_tbl->entry[entry_idx].ref_cnt++;
1314                         rte_errno = EEXIST;
1315                         return -1;
1316                 }
1317                 qw_e_tbl->entry[entry_idx].data = data->qword;
1318                 qw_e_tbl->entry[entry_idx].ref_cnt = 1;
1319                 qw_e_tbl->ref_cnt++;
1320                 break;
1321         default:
1322                 ptr_e_tbl = (struct mlx5_l3t_entry_ptr *)e_tbl;
1323                 if (ptr_e_tbl->entry[entry_idx].data) {
1324                         data->ptr = ptr_e_tbl->entry[entry_idx].data;
1325                         ptr_e_tbl->entry[entry_idx].ref_cnt++;
1326                         rte_errno = EEXIST;
1327                         return -1;
1328                 }
1329                 ptr_e_tbl->entry[entry_idx].data = data->ptr;
1330                 ptr_e_tbl->entry[entry_idx].ref_cnt = 1;
1331                 ptr_e_tbl->ref_cnt++;
1332                 break;
1333         }
1334         return 0;
1335 }
1336
1337 int32_t
1338 mlx5_l3t_set_entry(struct mlx5_l3t_tbl *tbl, uint32_t idx,
1339                    union mlx5_l3t_data *data)
1340 {
1341         int ret;
1342
1343         rte_spinlock_lock(&tbl->sl);
1344         ret = __l3t_set_entry(tbl, idx, data);
1345         rte_spinlock_unlock(&tbl->sl);
1346         return ret;
1347 }
1348
1349 int32_t
1350 mlx5_l3t_prepare_entry(struct mlx5_l3t_tbl *tbl, uint32_t idx,
1351                        union mlx5_l3t_data *data,
1352                        mlx5_l3t_alloc_callback_fn cb, void *ctx)
1353 {
1354         int32_t ret;
1355
1356         rte_spinlock_lock(&tbl->sl);
1357         /* Check if entry data is ready. */
1358         ret = __l3t_get_entry(tbl, idx, data);
1359         if (!ret) {
1360                 switch (tbl->type) {
1361                 case MLX5_L3T_TYPE_WORD:
1362                         if (data->word)
1363                                 goto out;
1364                         break;
1365                 case MLX5_L3T_TYPE_DWORD:
1366                         if (data->dword)
1367                                 goto out;
1368                         break;
1369                 case MLX5_L3T_TYPE_QWORD:
1370                         if (data->qword)
1371                                 goto out;
1372                         break;
1373                 default:
1374                         if (data->ptr)
1375                                 goto out;
1376                         break;
1377                 }
1378         }
1379         /* Entry data is not ready, use user callback to create it. */
1380         ret = cb(ctx, data);
1381         if (ret)
1382                 goto out;
1383         /* Save the new allocated data to entry. */
1384         ret = __l3t_set_entry(tbl, idx, data);
1385 out:
1386         rte_spinlock_unlock(&tbl->sl);
1387         return ret;
1388 }