net/mlx5: support concurrent access for hash list
[dpdk.git] / drivers / net / mlx5 / mlx5_utils.c
index 7a6b0c6..d041b07 100644 (file)
@@ -9,14 +9,40 @@
 
 #include "mlx5_utils.h"
 
+/********************* Hash List **********************/
+
+static struct mlx5_hlist_entry *
+mlx5_hlist_default_create_cb(struct mlx5_hlist *h, uint64_t key __rte_unused,
+                            void *ctx __rte_unused)
+{
+       return mlx5_malloc(MLX5_MEM_ZERO, h->entry_sz, 0, SOCKET_ID_ANY);
+}
+
+static void
+mlx5_hlist_default_remove_cb(struct mlx5_hlist *h __rte_unused,
+                            struct mlx5_hlist_entry *entry)
+{
+       mlx5_free(entry);
+}
+
+static int
+mlx5_hlist_default_match_cb(struct mlx5_hlist *h __rte_unused,
+                           struct mlx5_hlist_entry *entry,
+                           uint64_t key, void *ctx __rte_unused)
+{
+       return entry->key != key;
+}
+
 struct mlx5_hlist *
-mlx5_hlist_create(const char *name, uint32_t size)
+mlx5_hlist_create(const char *name, uint32_t size, uint32_t entry_size,
+                 uint32_t flags, mlx5_hlist_create_cb cb_create,
+                 mlx5_hlist_match_cb cb_match, mlx5_hlist_remove_cb cb_remove)
 {
        struct mlx5_hlist *h;
        uint32_t act_size;
        uint32_t alloc_size;
 
-       if (!size)
+       if (!size || (!cb_create ^ !cb_remove))
                return NULL;
        /* Align to the next power of 2, 32bits integer is enough now. */
        if (!rte_is_power_of_2(size)) {
@@ -40,45 +66,108 @@ mlx5_hlist_create(const char *name, uint32_t size)
                snprintf(h->name, MLX5_HLIST_NAMESIZE, "%s", name);
        h->table_sz = act_size;
        h->mask = act_size - 1;
+       h->entry_sz = entry_size;
+       h->direct_key = !!(flags & MLX5_HLIST_DIRECT_KEY);
+       h->write_most = !!(flags & MLX5_HLIST_WRITE_MOST);
+       h->cb_create = cb_create ? cb_create : mlx5_hlist_default_create_cb;
+       h->cb_match = cb_match ? cb_match : mlx5_hlist_default_match_cb;
+       h->cb_remove = cb_remove ? cb_remove : mlx5_hlist_default_remove_cb;
+       rte_rwlock_init(&h->lock);
        DRV_LOG(DEBUG, "Hash list with %s size 0x%" PRIX32 " is created.",
                h->name, act_size);
        return h;
 }
 
-struct mlx5_hlist_entry *
-mlx5_hlist_lookup(struct mlx5_hlist *h, uint64_t key)
+static struct mlx5_hlist_entry *
+__hlist_lookup(struct mlx5_hlist *h, uint64_t key, void *ctx, bool reuse)
 {
        uint32_t idx;
        struct mlx5_hlist_head *first;
        struct mlx5_hlist_entry *node;
 
        MLX5_ASSERT(h);
-       idx = rte_hash_crc_8byte(key, 0) & h->mask;
+       if (h->direct_key)
+               idx = (uint32_t)(key & h->mask);
+       else
+               idx = rte_hash_crc_8byte(key, 0) & h->mask;
        first = &h->heads[idx];
        LIST_FOREACH(node, first, next) {
-               if (node->key == key)
-                       return node;
+               if (!h->cb_match(h, node, key, ctx)) {
+                       if (reuse) {
+                               __atomic_add_fetch(&node->ref_cnt, 1,
+                                                  __ATOMIC_RELAXED);
+                               DRV_LOG(DEBUG, "Hash list %s entry %p "
+                                       "reuse: %u.",
+                                       h->name, (void *)node, node->ref_cnt);
+                       }
+                       break;
+               }
        }
-       return NULL;
+       return node;
 }
 
-int
-mlx5_hlist_insert(struct mlx5_hlist *h, struct mlx5_hlist_entry *entry)
+static struct mlx5_hlist_entry *
+hlist_lookup(struct mlx5_hlist *h, uint64_t key, void *ctx, bool reuse)
+{
+       struct mlx5_hlist_entry *node;
+
+       MLX5_ASSERT(h);
+       rte_rwlock_read_lock(&h->lock);
+       node = __hlist_lookup(h, key, ctx, reuse);
+       rte_rwlock_read_unlock(&h->lock);
+       return node;
+}
+
+struct mlx5_hlist_entry *
+mlx5_hlist_lookup(struct mlx5_hlist *h, uint64_t key, void *ctx)
+{
+       return hlist_lookup(h, key, ctx, false);
+}
+
+struct mlx5_hlist_entry*
+mlx5_hlist_register(struct mlx5_hlist *h, uint64_t key, void *ctx)
 {
        uint32_t idx;
        struct mlx5_hlist_head *first;
-       struct mlx5_hlist_entry *node;
+       struct mlx5_hlist_entry *entry;
+       uint32_t prev_gen_cnt = 0;
 
        MLX5_ASSERT(h && entry);
-       idx = rte_hash_crc_8byte(entry->key, 0) & h->mask;
+       /* Use write lock directly for write-most list. */
+       if (!h->write_most) {
+               prev_gen_cnt = __atomic_load_n(&h->gen_cnt, __ATOMIC_ACQUIRE);
+               entry = hlist_lookup(h, key, ctx, true);
+               if (entry)
+                       return entry;
+       }
+       rte_rwlock_write_lock(&h->lock);
+       /* Check if the list changed by other threads. */
+       if (h->write_most ||
+           prev_gen_cnt != __atomic_load_n(&h->gen_cnt, __ATOMIC_ACQUIRE)) {
+               entry = __hlist_lookup(h, key, ctx, true);
+               if (entry)
+                       goto done;
+       }
+       if (h->direct_key)
+               idx = (uint32_t)(key & h->mask);
+       else
+               idx = rte_hash_crc_8byte(key, 0) & h->mask;
        first = &h->heads[idx];
-       /* No need to reuse the lookup function. */
-       LIST_FOREACH(node, first, next) {
-               if (node->key == entry->key)
-                       return -EEXIST;
+       entry = h->cb_create(h, key, ctx);
+       if (!entry) {
+               rte_errno = ENOMEM;
+               DRV_LOG(ERR, "Can't allocate hash list %s entry.", h->name);
+               goto done;
        }
+       entry->key = key;
+       entry->ref_cnt = 1;
        LIST_INSERT_HEAD(first, entry, next);
-       return 0;
+       __atomic_add_fetch(&h->gen_cnt, 1, __ATOMIC_ACQ_REL);
+       DRV_LOG(DEBUG, "Hash list %s entry %p new: %u.",
+               h->name, (void *)entry, entry->ref_cnt);
+done:
+       rte_rwlock_write_unlock(&h->lock);
+       return entry;
 }
 
 struct mlx5_hlist_entry *
@@ -119,26 +208,36 @@ mlx5_hlist_insert_ex(struct mlx5_hlist *h, struct mlx5_hlist_entry *entry,
        return 0;
 }
 
-void
-mlx5_hlist_remove(struct mlx5_hlist *h __rte_unused,
-                 struct mlx5_hlist_entry *entry)
+int
+mlx5_hlist_unregister(struct mlx5_hlist *h, struct mlx5_hlist_entry *entry)
 {
-       MLX5_ASSERT(entry && entry->next.le_prev);
+       rte_rwlock_write_lock(&h->lock);
+       MLX5_ASSERT(entry && entry->ref_cnt && entry->next.le_prev);
+       DRV_LOG(DEBUG, "Hash list %s entry %p deref: %u.",
+               h->name, (void *)entry, entry->ref_cnt);
+       if (--entry->ref_cnt) {
+               rte_rwlock_write_unlock(&h->lock);
+               return 1;
+       }
        LIST_REMOVE(entry, next);
        /* Set to NULL to get rid of removing action for more than once. */
        entry->next.le_prev = NULL;
+       h->cb_remove(h, entry);
+       rte_rwlock_write_unlock(&h->lock);
+       DRV_LOG(DEBUG, "Hash list %s entry %p removed.",
+               h->name, (void *)entry);
+       return 0;
 }
 
 void
-mlx5_hlist_destroy(struct mlx5_hlist *h,
-                  mlx5_hlist_destroy_callback_fn cb, void *ctx)
+mlx5_hlist_destroy(struct mlx5_hlist *h)
 {
        uint32_t idx;
        struct mlx5_hlist_entry *entry;
 
        MLX5_ASSERT(h);
        for (idx = 0; idx < h->table_sz; ++idx) {
-               /* no LIST_FOREACH_SAFE, using while instead */
+               /* No LIST_FOREACH_SAFE, using while instead. */
                while (!LIST_EMPTY(&h->heads[idx])) {
                        entry = LIST_FIRST(&h->heads[idx]);
                        LIST_REMOVE(entry, next);
@@ -150,15 +249,14 @@ mlx5_hlist_destroy(struct mlx5_hlist *h,
                         * the beginning). Or else the default free function
                         * will be used.
                         */
-                       if (cb)
-                               cb(entry, ctx);
-                       else
-                               mlx5_free(entry);
+                       h->cb_remove(h, entry);
                }
        }
        mlx5_free(h);
 }
 
+/********************* Indexed pool **********************/
+
 static inline void
 mlx5_ipool_lock(struct mlx5_indexed_pool *pool)
 {