get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/79761/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 79761,
    "url": "http://patches.dpdk.org/api/patches/79761/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/1601984948-313027-8-git-send-email-suanmingm@nvidia.com/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<1601984948-313027-8-git-send-email-suanmingm@nvidia.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/1601984948-313027-8-git-send-email-suanmingm@nvidia.com",
    "date": "2020-10-06T11:48:50",
    "name": "[07/25] net/mlx5: support concurrent access for hash list",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "9eb32a4746f9a3b649661cba5102ce8bd023ff0a",
    "submitter": {
        "id": 1887,
        "url": "http://patches.dpdk.org/api/people/1887/?format=api",
        "name": "Suanming Mou",
        "email": "suanmingm@nvidia.com"
    },
    "delegate": {
        "id": 3268,
        "url": "http://patches.dpdk.org/api/users/3268/?format=api",
        "username": "rasland",
        "first_name": "Raslan",
        "last_name": "Darawsheh",
        "email": "rasland@nvidia.com"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/1601984948-313027-8-git-send-email-suanmingm@nvidia.com/mbox/",
    "series": [
        {
            "id": 12718,
            "url": "http://patches.dpdk.org/api/series/12718/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=12718",
            "date": "2020-10-06T11:48:45",
            "name": "net/mlx5: support multiple-thread flow operations",
            "version": 1,
            "mbox": "http://patches.dpdk.org/series/12718/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/79761/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/79761/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from dpdk.org (dpdk.org [92.243.14.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id D9A3AA04BB;\n\tTue,  6 Oct 2020 13:51:47 +0200 (CEST)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id 814761B694;\n\tTue,  6 Oct 2020 13:49:34 +0200 (CEST)",
            "from mellanox.co.il (mail-il-dmz.mellanox.com [193.47.165.129])\n by dpdk.org (Postfix) with ESMTP id B9A7D1B671\n for <dev@dpdk.org>; Tue,  6 Oct 2020 13:49:29 +0200 (CEST)",
            "from Internal Mail-Server by MTLPINE1 (envelope-from\n suanmingm@nvidia.com) with SMTP; 6 Oct 2020 14:49:27 +0300",
            "from nvidia.com (mtbc-r640-04.mtbc.labs.mlnx [10.75.70.9])\n by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id 096BnC0R028553;\n Tue, 6 Oct 2020 14:49:25 +0300"
        ],
        "From": "Suanming Mou <suanmingm@nvidia.com>",
        "To": "viacheslavo@nvidia.com, matan@nvidia.com",
        "Cc": "rasland@nvidia.com, dev@dpdk.org, Xueming Li <xuemingl@nvidia.com>",
        "Date": "Tue,  6 Oct 2020 19:48:50 +0800",
        "Message-Id": "<1601984948-313027-8-git-send-email-suanmingm@nvidia.com>",
        "X-Mailer": "git-send-email 1.8.3.1",
        "In-Reply-To": "<1601984948-313027-1-git-send-email-suanmingm@nvidia.com>",
        "References": "<1601984948-313027-1-git-send-email-suanmingm@nvidia.com>",
        "Subject": "[dpdk-dev] [PATCH 07/25] net/mlx5: support concurrent access for\n\thash list",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "From: Xueming Li <xuemingl@nvidia.com>\n\nTo support multi-thread flow operations, this patch adds following to\nsupport concurrent access to hash list:\n1. list level read/write lock\n2. entry reference count\n3. entry create/match/remove callback\n4. remove insert/lookup/remove function which are not thread safe\n5. add register/unregister function to support entry reuse\n\nFor better performance, lookup function uses read lock to\nallow concurrent lookup from different thread, all other hash list\nmodification functions uses write lock which blocks concurrent\nmodification from other thread.\n\nThe exact objects change will be applied in the next patches.\n\nSigned-off-by: Xueming Li <xuemingl@nvidia.com>\n---\n drivers/net/mlx5/linux/mlx5_os.c |  27 ++++---\n drivers/net/mlx5/mlx5.c          |  13 ++--\n drivers/net/mlx5/mlx5_flow.c     |   7 +-\n drivers/net/mlx5/mlx5_flow_dv.c  |   6 +-\n drivers/net/mlx5/mlx5_utils.c    | 154 ++++++++++++++++++++++++++++++++-------\n drivers/net/mlx5/mlx5_utils.h    | 142 +++++++++++++++++++++++++++++-------\n 6 files changed, 272 insertions(+), 77 deletions(-)",
    "diff": "diff --git a/drivers/net/mlx5/linux/mlx5_os.c b/drivers/net/mlx5/linux/mlx5_os.c\nindex 94c1e38..13b5a3f 100644\n--- a/drivers/net/mlx5/linux/mlx5_os.c\n+++ b/drivers/net/mlx5/linux/mlx5_os.c\n@@ -237,14 +237,16 @@\n \t\treturn err;\n \t/* Create tags hash list table. */\n \tsnprintf(s, sizeof(s), \"%s_tags\", sh->ibdev_name);\n-\tsh->tag_table = mlx5_hlist_create(s, MLX5_TAGS_HLIST_ARRAY_SIZE);\n+\tsh->tag_table = mlx5_hlist_create(s, MLX5_TAGS_HLIST_ARRAY_SIZE, 0,\n+\t\t\t\t\t  false, NULL, NULL, NULL);\n \tif (!sh->tag_table) {\n \t\tDRV_LOG(ERR, \"tags with hash creation failed.\");\n \t\terr = ENOMEM;\n \t\tgoto error;\n \t}\n \tsnprintf(s, sizeof(s), \"%s_hdr_modify\", sh->ibdev_name);\n-\tsh->modify_cmds = mlx5_hlist_create(s, MLX5_FLOW_HDR_MODIFY_HTABLE_SZ);\n+\tsh->modify_cmds = mlx5_hlist_create(s, MLX5_FLOW_HDR_MODIFY_HTABLE_SZ,\n+\t\t\t\t\t    0, false, NULL, NULL, NULL);\n \tif (!sh->modify_cmds) {\n \t\tDRV_LOG(ERR, \"hdr modify hash creation failed\");\n \t\terr = ENOMEM;\n@@ -252,7 +254,8 @@\n \t}\n \tsnprintf(s, sizeof(s), \"%s_encaps_decaps\", sh->ibdev_name);\n \tsh->encaps_decaps = mlx5_hlist_create(s,\n-\t\t\t\t\t      MLX5_FLOW_ENCAP_DECAP_HTABLE_SZ);\n+\t\t\t\t\t      MLX5_FLOW_ENCAP_DECAP_HTABLE_SZ,\n+\t\t\t\t\t      0, false, NULL, NULL, NULL);\n \tif (!sh->encaps_decaps) {\n \t\tDRV_LOG(ERR, \"encap decap hash creation failed\");\n \t\terr = ENOMEM;\n@@ -332,16 +335,16 @@\n \t\tsh->pop_vlan_action = NULL;\n \t}\n \tif (sh->encaps_decaps) {\n-\t\tmlx5_hlist_destroy(sh->encaps_decaps, NULL, NULL);\n+\t\tmlx5_hlist_destroy(sh->encaps_decaps);\n \t\tsh->encaps_decaps = NULL;\n \t}\n \tif (sh->modify_cmds) {\n-\t\tmlx5_hlist_destroy(sh->modify_cmds, NULL, NULL);\n+\t\tmlx5_hlist_destroy(sh->modify_cmds);\n \t\tsh->modify_cmds = NULL;\n \t}\n \tif (sh->tag_table) {\n \t\t/* tags should be destroyed with flow before. */\n-\t\tmlx5_hlist_destroy(sh->tag_table, NULL, NULL);\n+\t\tmlx5_hlist_destroy(sh->tag_table);\n \t\tsh->tag_table = NULL;\n \t}\n \tmlx5_free_table_hash_list(priv);\n@@ -393,16 +396,16 @@\n \tpthread_mutex_destroy(&sh->dv_mutex);\n #endif /* HAVE_MLX5DV_DR */\n \tif (sh->encaps_decaps) {\n-\t\tmlx5_hlist_destroy(sh->encaps_decaps, NULL, NULL);\n+\t\tmlx5_hlist_destroy(sh->encaps_decaps);\n \t\tsh->encaps_decaps = NULL;\n \t}\n \tif (sh->modify_cmds) {\n-\t\tmlx5_hlist_destroy(sh->modify_cmds, NULL, NULL);\n+\t\tmlx5_hlist_destroy(sh->modify_cmds);\n \t\tsh->modify_cmds = NULL;\n \t}\n \tif (sh->tag_table) {\n \t\t/* tags should be destroyed with flow before. */\n-\t\tmlx5_hlist_destroy(sh->tag_table, NULL, NULL);\n+\t\tmlx5_hlist_destroy(sh->tag_table);\n \t\tsh->tag_table = NULL;\n \t}\n \tmlx5_free_table_hash_list(priv);\n@@ -1343,7 +1346,9 @@\n \t    mlx5_flow_ext_mreg_supported(eth_dev) &&\n \t    priv->sh->dv_regc0_mask) {\n \t\tpriv->mreg_cp_tbl = mlx5_hlist_create(MLX5_FLOW_MREG_HNAME,\n-\t\t\t\t\t\t      MLX5_FLOW_MREG_HTABLE_SZ);\n+\t\t\t\t\t\t      MLX5_FLOW_MREG_HTABLE_SZ,\n+\t\t\t\t\t\t      0, false,\n+\t\t\t\t\t\t      NULL, NULL, NULL);\n \t\tif (!priv->mreg_cp_tbl) {\n \t\t\terr = ENOMEM;\n \t\t\tgoto error;\n@@ -1353,7 +1358,7 @@\n error:\n \tif (priv) {\n \t\tif (priv->mreg_cp_tbl)\n-\t\t\tmlx5_hlist_destroy(priv->mreg_cp_tbl, NULL, NULL);\n+\t\t\tmlx5_hlist_destroy(priv->mreg_cp_tbl);\n \t\tif (priv->sh)\n \t\t\tmlx5_os_free_shared_dr(priv);\n \t\tif (priv->nl_socket_route >= 0)\ndiff --git a/drivers/net/mlx5/mlx5.c b/drivers/net/mlx5/mlx5.c\nindex b3d1638..ddf236a 100644\n--- a/drivers/net/mlx5/mlx5.c\n+++ b/drivers/net/mlx5/mlx5.c\n@@ -998,7 +998,7 @@ struct mlx5_dev_ctx_shared *\n \n \tif (!sh->flow_tbls)\n \t\treturn;\n-\tpos = mlx5_hlist_lookup(sh->flow_tbls, table_key.v64);\n+\tpos = mlx5_hlist_lookup(sh->flow_tbls, table_key.v64, NULL);\n \tif (pos) {\n \t\ttbl_data = container_of(pos, struct mlx5_flow_tbl_data_entry,\n \t\t\t\t\tentry);\n@@ -1007,7 +1007,7 @@ struct mlx5_dev_ctx_shared *\n \t\tmlx5_free(tbl_data);\n \t}\n \ttable_key.direction = 1;\n-\tpos = mlx5_hlist_lookup(sh->flow_tbls, table_key.v64);\n+\tpos = mlx5_hlist_lookup(sh->flow_tbls, table_key.v64, NULL);\n \tif (pos) {\n \t\ttbl_data = container_of(pos, struct mlx5_flow_tbl_data_entry,\n \t\t\t\t\tentry);\n@@ -1017,7 +1017,7 @@ struct mlx5_dev_ctx_shared *\n \t}\n \ttable_key.direction = 0;\n \ttable_key.domain = 1;\n-\tpos = mlx5_hlist_lookup(sh->flow_tbls, table_key.v64);\n+\tpos = mlx5_hlist_lookup(sh->flow_tbls, table_key.v64, NULL);\n \tif (pos) {\n \t\ttbl_data = container_of(pos, struct mlx5_flow_tbl_data_entry,\n \t\t\t\t\tentry);\n@@ -1025,7 +1025,7 @@ struct mlx5_dev_ctx_shared *\n \t\tmlx5_hlist_remove(sh->flow_tbls, pos);\n \t\tmlx5_free(tbl_data);\n \t}\n-\tmlx5_hlist_destroy(sh->flow_tbls, NULL, NULL);\n+\tmlx5_hlist_destroy(sh->flow_tbls);\n }\n \n /**\n@@ -1047,7 +1047,8 @@ struct mlx5_dev_ctx_shared *\n \n \tMLX5_ASSERT(sh);\n \tsnprintf(s, sizeof(s), \"%s_flow_table\", priv->sh->ibdev_name);\n-\tsh->flow_tbls = mlx5_hlist_create(s, MLX5_FLOW_TABLE_HLIST_ARRAY_SIZE);\n+\tsh->flow_tbls = mlx5_hlist_create(s, MLX5_FLOW_TABLE_HLIST_ARRAY_SIZE,\n+\t\t\t\t\t  0, false, NULL, NULL, NULL);\n \tif (!sh->flow_tbls) {\n \t\tDRV_LOG(ERR, \"flow tables with hash creation failed.\");\n \t\terr = ENOMEM;\n@@ -1275,7 +1276,7 @@ struct mlx5_dev_ctx_shared *\n \t}\n \tmlx5_proc_priv_uninit(dev);\n \tif (priv->mreg_cp_tbl)\n-\t\tmlx5_hlist_destroy(priv->mreg_cp_tbl, NULL, NULL);\n+\t\tmlx5_hlist_destroy(priv->mreg_cp_tbl);\n \tmlx5_mprq_free_mp(dev);\n \tmlx5_os_free_shared_dr(priv);\n \tif (priv->rss_conf.rss_key != NULL)\ndiff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c\nindex 2790c32..3a3b783 100644\n--- a/drivers/net/mlx5/mlx5_flow.c\n+++ b/drivers/net/mlx5/mlx5_flow.c\n@@ -3018,7 +3018,7 @@ struct mlx5_flow_tunnel_info {\n \tcp_mreg.src = ret;\n \t/* Check if already registered. */\n \tMLX5_ASSERT(priv->mreg_cp_tbl);\n-\tmcp_res = (void *)mlx5_hlist_lookup(priv->mreg_cp_tbl, mark_id);\n+\tmcp_res = (void *)mlx5_hlist_lookup(priv->mreg_cp_tbl, mark_id, NULL);\n \tif (mcp_res) {\n \t\t/* For non-default rule. */\n \t\tif (mark_id != MLX5_DEFAULT_COPY_ID)\n@@ -3095,8 +3095,7 @@ struct mlx5_flow_tunnel_info {\n \t\tgoto error;\n \tmcp_res->refcnt++;\n \tmcp_res->hlist_ent.key = mark_id;\n-\tret = mlx5_hlist_insert(priv->mreg_cp_tbl,\n-\t\t\t\t&mcp_res->hlist_ent);\n+\tret = !mlx5_hlist_insert(priv->mreg_cp_tbl, &mcp_res->hlist_ent);\n \tMLX5_ASSERT(!ret);\n \tif (ret)\n \t\tgoto error;\n@@ -3246,7 +3245,7 @@ struct mlx5_flow_tunnel_info {\n \tif (!priv->mreg_cp_tbl)\n \t\treturn;\n \tmcp_res = (void *)mlx5_hlist_lookup(priv->mreg_cp_tbl,\n-\t\t\t\t\t    MLX5_DEFAULT_COPY_ID);\n+\t\t\t\t\t    MLX5_DEFAULT_COPY_ID, NULL);\n \tif (!mcp_res)\n \t\treturn;\n \tMLX5_ASSERT(mcp_res->rix_flow);\ndiff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c\nindex ede7bf8..fafe188 100644\n--- a/drivers/net/mlx5/mlx5_flow_dv.c\n+++ b/drivers/net/mlx5/mlx5_flow_dv.c\n@@ -7632,7 +7632,7 @@ struct field_modify_info modify_tcp[] = {\n \t\t}\n \t};\n \tstruct mlx5_hlist_entry *pos = mlx5_hlist_lookup(sh->flow_tbls,\n-\t\t\t\t\t\t\t table_key.v64);\n+\t\t\t\t\t\t\t table_key.v64, NULL);\n \tstruct mlx5_flow_tbl_data_entry *tbl_data;\n \tuint32_t idx = 0;\n \tint ret;\n@@ -7678,7 +7678,7 @@ struct field_modify_info modify_tcp[] = {\n \t/* Jump action reference count is initialized here. */\n \trte_atomic32_init(&tbl_data->jump.refcnt);\n \tpos->key = table_key.v64;\n-\tret = mlx5_hlist_insert(sh->flow_tbls, pos);\n+\tret = !mlx5_hlist_insert(sh->flow_tbls, pos);\n \tif (ret < 0) {\n \t\trte_flow_error_set(error, -ret,\n \t\t\t\t   RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,\n@@ -7858,7 +7858,7 @@ struct field_modify_info modify_tcp[] = {\n \tint ret;\n \n \t/* Lookup a matching resource from cache. */\n-\tentry = mlx5_hlist_lookup(sh->tag_table, (uint64_t)tag_be24);\n+\tentry = mlx5_hlist_lookup(sh->tag_table, (uint64_t)tag_be24, NULL);\n \tif (entry) {\n \t\tcache_resource = container_of\n \t\t\t(entry, struct mlx5_flow_dv_tag_resource, entry);\ndiff --git a/drivers/net/mlx5/mlx5_utils.c b/drivers/net/mlx5/mlx5_utils.c\nindex 0a75fa6..4eb3db0 100644\n--- a/drivers/net/mlx5/mlx5_utils.c\n+++ b/drivers/net/mlx5/mlx5_utils.c\n@@ -9,14 +9,39 @@\n \n #include \"mlx5_utils.h\"\n \n+/********************* Hash List **********************/\n+\n+static struct mlx5_hlist_entry *\n+mlx5_hlist_default_create_cb(struct mlx5_hlist *h, uint64_t key __rte_unused,\n+\t\t\t     void *ctx __rte_unused)\n+{\n+\treturn mlx5_malloc(MLX5_MEM_ZERO, h->entry_sz, 0, SOCKET_ID_ANY);\n+}\n+\n+static void\n+mlx5_hlist_default_remove_cb(struct mlx5_hlist *h __rte_unused,\n+\t\t\t     struct mlx5_hlist_entry *entry)\n+{\n+\tmlx5_free(entry);\n+}\n+\n+static int\n+mlx5_hlist_default_match_cb(struct mlx5_hlist *h __rte_unused,\n+\t\t\t    struct mlx5_hlist_entry *entry, void *ctx)\n+{\n+\treturn entry->key != *(uint64_t *)ctx;\n+}\n+\n struct mlx5_hlist *\n-mlx5_hlist_create(const char *name, uint32_t size)\n+mlx5_hlist_create(const char *name, uint32_t size, uint32_t entry_size,\n+\t\t  bool write_most, mlx5_hlist_create_cb cb_create,\n+\t\t  mlx5_hlist_match_cb cb_match, mlx5_hlist_remove_cb cb_remove)\n {\n \tstruct mlx5_hlist *h;\n \tuint32_t act_size;\n \tuint32_t alloc_size;\n \n-\tif (!size)\n+\tif (!size || (!cb_create ^ !cb_remove))\n \t\treturn NULL;\n \t/* Align to the next power of 2, 32bits integer is enough now. */\n \tif (!rte_is_power_of_2(size)) {\n@@ -40,13 +65,19 @@ struct mlx5_hlist *\n \t\tsnprintf(h->name, MLX5_HLIST_NAMESIZE, \"%s\", name);\n \th->table_sz = act_size;\n \th->mask = act_size - 1;\n+\th->entry_sz = entry_size;\n+\th->write_most = write_most;\n+\th->cb_create = cb_create ? cb_create : mlx5_hlist_default_create_cb;\n+\th->cb_match = cb_match ? cb_match : mlx5_hlist_default_match_cb;\n+\th->cb_remove = cb_remove ? cb_remove : mlx5_hlist_default_remove_cb;\n+\trte_rwlock_init(&h->lock);\n \tDRV_LOG(DEBUG, \"Hash list with %s size 0x%\" PRIX32 \" is created.\",\n \t\th->name, act_size);\n \treturn h;\n }\n \n-struct mlx5_hlist_entry *\n-mlx5_hlist_lookup(struct mlx5_hlist *h, uint64_t key)\n+static struct mlx5_hlist_entry *\n+__hlist_lookup(struct mlx5_hlist *h, uint64_t key, void *ctx, bool reuse)\n {\n \tuint32_t idx;\n \tstruct mlx5_hlist_head *first;\n@@ -56,29 +87,81 @@ struct mlx5_hlist_entry *\n \tidx = rte_hash_crc_8byte(key, 0) & h->mask;\n \tfirst = &h->heads[idx];\n \tLIST_FOREACH(node, first, next) {\n-\t\tif (node->key == key)\n-\t\t\treturn node;\n+\t\tif (!__atomic_load_n(&node->ref_cnt, __ATOMIC_RELAXED))\n+\t\t\t/* Ignore entry in middle of removal */\n+\t\t\tcontinue;\n+\t\tif (!h->cb_match(h, node, ctx ? ctx : &key)) {\n+\t\t\tif (reuse) {\n+\t\t\t\t__atomic_add_fetch(&node->ref_cnt, 1,\n+\t\t\t\t\t\t   __ATOMIC_RELAXED);\n+\t\t\t\tDRV_LOG(DEBUG, \"hash list %s entry %p reuse: %u\",\n+\t\t\t\t\th->name, (void *)node, node->ref_cnt);\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n \t}\n-\treturn NULL;\n+\treturn node;\n }\n \n-int\n-mlx5_hlist_insert(struct mlx5_hlist *h, struct mlx5_hlist_entry *entry)\n+static struct mlx5_hlist_entry *\n+hlist_lookup(struct mlx5_hlist *h, uint64_t key, void *ctx, bool reuse)\n+{\n+\tstruct mlx5_hlist_entry *node;\n+\n+\tMLX5_ASSERT(h);\n+\trte_rwlock_read_lock(&h->lock);\n+\tnode = __hlist_lookup(h, key, ctx, reuse);\n+\trte_rwlock_read_unlock(&h->lock);\n+\treturn node;\n+}\n+\n+struct mlx5_hlist_entry *\n+mlx5_hlist_lookup(struct mlx5_hlist *h, uint64_t key, void *ctx)\n+{\n+\treturn __hlist_lookup(h, key, ctx, false);\n+}\n+\n+struct mlx5_hlist_entry*\n+mlx5_hlist_register(struct mlx5_hlist *h, uint64_t key, void *ctx)\n {\n \tuint32_t idx;\n \tstruct mlx5_hlist_head *first;\n-\tstruct mlx5_hlist_entry *node;\n+\tstruct mlx5_hlist_entry *entry;\n+\tuint32_t prev_gen_cnt = 0;\n \n \tMLX5_ASSERT(h && entry);\n-\tidx = rte_hash_crc_8byte(entry->key, 0) & h->mask;\n+\t/* Use write lock directly for write-most list */\n+\tif (!h->write_most) {\n+\t\tprev_gen_cnt = __atomic_load_n(&h->gen_cnt, __ATOMIC_ACQUIRE);\n+\t\tentry = hlist_lookup(h, key, ctx, true);\n+\t\tif (entry)\n+\t\t\treturn entry;\n+\t}\n+\trte_rwlock_write_lock(&h->lock);\n+\t/* Check if the list changed by other threads. */\n+\tif (h->write_most ||\n+\t    prev_gen_cnt != __atomic_load_n(&h->gen_cnt, __ATOMIC_ACQUIRE)) {\n+\t\tentry = __hlist_lookup(h, key, ctx, true);\n+\t\tif (entry)\n+\t\t\tgoto done;\n+\t}\n+\tidx = rte_hash_crc_8byte(key, 0) & h->mask;\n \tfirst = &h->heads[idx];\n-\t/* No need to reuse the lookup function. */\n-\tLIST_FOREACH(node, first, next) {\n-\t\tif (node->key == entry->key)\n-\t\t\treturn -EEXIST;\n+\tentry = h->cb_create(h, key, ctx);\n+\tif (!entry) {\n+\t\trte_errno = ENOMEM;\n+\t\tDRV_LOG(ERR, \"Failed to allocate hash list %s entry\", h->name);\n+\t\tgoto done;\n \t}\n+\tentry->key = key;\n+\tentry->ref_cnt = 1;\n \tLIST_INSERT_HEAD(first, entry, next);\n-\treturn 0;\n+\t__atomic_add_fetch(&h->gen_cnt, 1, __ATOMIC_ACQ_REL);\n+\tDRV_LOG(DEBUG, \"hash list %s entry %p new: %u\",\n+\t\th->name, (void *)entry, entry->ref_cnt);\n+done:\n+\trte_rwlock_write_unlock(&h->lock);\n+\treturn entry;\n }\n \n struct mlx5_hlist_entry *\n@@ -119,19 +202,41 @@ struct mlx5_hlist_entry *\n \treturn 0;\n }\n \n-void\n-mlx5_hlist_remove(struct mlx5_hlist *h __rte_unused,\n-\t\t  struct mlx5_hlist_entry *entry)\n+int\n+mlx5_hlist_unregister(struct mlx5_hlist *h, struct mlx5_hlist_entry *entry)\n {\n+\tuint32_t ref_cnt;\n+\n \tMLX5_ASSERT(entry && entry->next.le_prev);\n+\tMLX5_ASSERT(__atomic_fetch_n(&entry->ref_cnt, __ATOMIC_RELAXED));\n+\n+\tref_cnt = __atomic_sub_fetch(&entry->ref_cnt, 1, __ATOMIC_ACQ_REL);\n+\tDRV_LOG(DEBUG, \"hash list %s entry %p deref: %u\",\n+\t\th->name, (void *)entry, entry->ref_cnt);\n+\tif (ref_cnt)\n+\t\treturn 1;\n+\trte_rwlock_write_lock(&h->lock);\n+\t/*\n+\t * Check the ref_cnt again in the slowpath since there maybe new\n+\t * users come.\n+\t */\n+\tif (__atomic_load_n(&entry->ref_cnt, __ATOMIC_RELAXED)) {\n+\t\trte_rwlock_write_unlock(&h->lock);\n+\t\treturn 1;\n+\t}\n \tLIST_REMOVE(entry, next);\n \t/* Set to NULL to get rid of removing action for more than once. */\n \tentry->next.le_prev = NULL;\n+\th->cb_remove(h, entry);\n+\t__atomic_add_fetch(&h->gen_cnt, 1, __ATOMIC_ACQ_REL);\n+\trte_rwlock_write_unlock(&h->lock);\n+\tDRV_LOG(DEBUG, \"hash list %s entry %p removed\",\n+\t\th->name, (void *)entry);\n+\treturn 0;\n }\n \n void\n-mlx5_hlist_destroy(struct mlx5_hlist *h,\n-\t\t   mlx5_hlist_destroy_callback_fn cb, void *ctx)\n+mlx5_hlist_destroy(struct mlx5_hlist *h)\n {\n \tuint32_t idx;\n \tstruct mlx5_hlist_entry *entry;\n@@ -150,15 +255,14 @@ struct mlx5_hlist_entry *\n \t\t\t * the beginning). Or else the default free function\n \t\t\t * will be used.\n \t\t\t */\n-\t\t\tif (cb)\n-\t\t\t\tcb(entry, ctx);\n-\t\t\telse\n-\t\t\t\tmlx5_free(entry);\n+\t\t\th->cb_remove(h, entry);\n \t\t}\n \t}\n \tmlx5_free(h);\n }\n \n+/********************* Indexed pool **********************/\n+\n static inline void\n mlx5_ipool_lock(struct mlx5_indexed_pool *pool)\n {\ndiff --git a/drivers/net/mlx5/mlx5_utils.h b/drivers/net/mlx5/mlx5_utils.h\nindex f078bdc..8719dee 100644\n--- a/drivers/net/mlx5/mlx5_utils.h\n+++ b/drivers/net/mlx5/mlx5_utils.h\n@@ -13,6 +13,7 @@\n #include <errno.h>\n \n #include <rte_spinlock.h>\n+#include <rte_rwlock.h>\n #include <rte_memory.h>\n #include <rte_bitmap.h>\n \n@@ -20,6 +21,11 @@\n \n #include \"mlx5_defs.h\"\n \n+#define mlx5_hlist_remove(h, e) \\\n+\tmlx5_hlist_unregister(h, e)\n+\n+#define mlx5_hlist_insert(h, e) \\\n+\tmlx5_hlist_register(h, 0, e)\n \n /* Convert a bit number to the corresponding 64-bit mask */\n #define MLX5_BITSHIFT(v) (UINT64_C(1) << (v))\n@@ -245,6 +251,8 @@ struct mlx5_indexed_pool {\n /** Maximum size of string for naming the hlist table. */\n #define MLX5_HLIST_NAMESIZE\t\t\t32\n \n+struct mlx5_hlist;\n+\n /**\n  * Structure of the entry in the hash list, user should define its own struct\n  * that contains this in order to store the data. The 'key' is 64-bits right\n@@ -253,6 +261,7 @@ struct mlx5_indexed_pool {\n struct mlx5_hlist_entry {\n \tLIST_ENTRY(mlx5_hlist_entry) next; /* entry pointers in the list. */\n \tuint64_t key; /* user defined 'key', could be the hash signature. */\n+\tuint32_t ref_cnt; /* reference count. */\n };\n \n /** Structure for hash head. */\n@@ -275,13 +284,73 @@ struct mlx5_hlist_entry {\n typedef int (*mlx5_hlist_match_callback_fn)(struct mlx5_hlist_entry *entry,\n \t\t\t\t\t     void *ctx);\n \n-/** hash list table structure */\n+/**\n+ * Type of callback function for entry removal.\n+ *\n+ * @param list\n+ *   The hash list.\n+ * @param entry\n+ *   The entry in the list.\n+ */\n+typedef void (*mlx5_hlist_remove_cb)(struct mlx5_hlist *list,\n+\t\t\t\t     struct mlx5_hlist_entry *entry);\n+\n+/**\n+ * Type of function for user defined matching.\n+ *\n+ * @param list\n+ *   The hash list.\n+ * @param entry\n+ *   The entry in the list.\n+ * @param ctx\n+ *   The pointer to new entry context.\n+ *\n+ * @return\n+ *   0 if matching, non-zero number otherwise.\n+ */\n+typedef int (*mlx5_hlist_match_cb)(struct mlx5_hlist *list,\n+\t\t\t\t   struct mlx5_hlist_entry *entry, void *ctx);\n+\n+/**\n+ * Type of function for user defined hash list entry creation.\n+ *\n+ * @param list\n+ *   The hash list.\n+ * @param key\n+ *   The key of the new entry.\n+ * @param ctx\n+ *   The pointer to new entry context.\n+ *\n+ * @return\n+ *   Pointer to allocated entry on success, NULL otherwise.\n+ */\n+typedef struct mlx5_hlist_entry *(*mlx5_hlist_create_cb)\n+\t\t\t\t  (struct mlx5_hlist *list,\n+\t\t\t\t   uint64_t key, void *ctx);\n+\n+/**\n+ * Hash list table structure\n+ *\n+ * Entry in hash list could be reused if entry already exists, reference\n+ * count will increase and the existing entry returns.\n+ *\n+ * When destroy an entry from list, decrease reference count and only\n+ * destroy when no further reference.\n+ */\n struct mlx5_hlist {\n \tchar name[MLX5_HLIST_NAMESIZE]; /**< Name of the hash list. */\n \t/**< number of heads, need to be power of 2. */\n \tuint32_t table_sz;\n+\tuint32_t entry_sz; /**< Size of entry, used to allocate entry. */\n \t/**< mask to get the index of the list heads. */\n \tuint32_t mask;\n+\trte_rwlock_t lock;\n+\tuint32_t gen_cnt; /* list modification will update generation count. */\n+\tbool write_most; /* list mostly used for append new or destroy. */\n+\tvoid *ctx;\n+\tmlx5_hlist_create_cb cb_create; /**< entry create callback. */\n+\tmlx5_hlist_match_cb cb_match; /**< entry match callback. */\n+\tmlx5_hlist_remove_cb cb_remove; /**< entry remove callback. */\n \tstruct mlx5_hlist_head heads[];\t/**< list head arrays. */\n };\n \n@@ -297,40 +366,43 @@ struct mlx5_hlist {\n  *   Name of the hash list(optional).\n  * @param size\n  *   Heads array size of the hash list.\n- *\n+ * @param entry_size\n+ *   Entry size to allocate if cb_create not specified.\n+ * @param write_most\n+ *   most operations to list is modification.\n+ * @param cb_create\n+ *   Callback function for entry create.\n+ * @param cb_match\n+ *   Callback function for entry match.\n+ * @param cb_destroy\n+ *   Callback function for entry destroy.\n  * @return\n  *   Pointer of the hash list table created, NULL on failure.\n  */\n-struct mlx5_hlist *mlx5_hlist_create(const char *name, uint32_t size);\n+struct mlx5_hlist *mlx5_hlist_create(const char *name, uint32_t size,\n+\t\t\t\t     uint32_t entry_size, bool write_most,\n+\t\t\t\t     mlx5_hlist_create_cb cb_create,\n+\t\t\t\t     mlx5_hlist_match_cb cb_match,\n+\t\t\t\t     mlx5_hlist_remove_cb cb_destroy);\n \n /**\n  * Search an entry matching the key.\n  *\n+ * Result returned might be destroyed by other thread, must use\n+ * this function only in main thread.\n+ *\n  * @param h\n  *   Pointer to the hast list table.\n  * @param key\n  *   Key for the searching entry.\n+ * @param ctx\n+ *   Common context parameter used by entry callback function.\n  *\n  * @return\n  *   Pointer of the hlist entry if found, NULL otherwise.\n  */\n-struct mlx5_hlist_entry *mlx5_hlist_lookup(struct mlx5_hlist *h, uint64_t key);\n-\n-/**\n- * Insert an entry to the hash list table, the entry is only part of whole data\n- * element and a 64B key is used for matching. User should construct the key or\n- * give a calculated hash signature and guarantee there is no collision.\n- *\n- * @param h\n- *   Pointer to the hast list table.\n- * @param entry\n- *   Entry to be inserted into the hash list table.\n- *\n- * @return\n- *   - zero for success.\n- *   - -EEXIST if the entry is already inserted.\n- */\n-int mlx5_hlist_insert(struct mlx5_hlist *h, struct mlx5_hlist_entry *entry);\n+struct mlx5_hlist_entry *mlx5_hlist_lookup(struct mlx5_hlist *h, uint64_t key,\n+\t\t\t\t\t   void *ctx);\n \n /**\n  * Extended routine to search an entry matching the context with\n@@ -376,6 +448,24 @@ int mlx5_hlist_insert_ex(struct mlx5_hlist *h, struct mlx5_hlist_entry *entry,\n \t\t\t mlx5_hlist_match_callback_fn cb, void *ctx);\n \n /**\n+ * Insert an entry to the hash list table, the entry is only part of whole data\n+ * element and a 64B key is used for matching. User should construct the key or\n+ * give a calculated hash signature and guarantee there is no collision.\n+ *\n+ * @param h\n+ *   Pointer to the hast list table.\n+ * @param entry\n+ *   Entry to be inserted into the hash list table.\n+ * @param ctx\n+ *   Common context parameter used by callback function.\n+ *\n+ * @return\n+ *   registered entry on success, NULL otherwise\n+ */\n+struct mlx5_hlist_entry *mlx5_hlist_register(struct mlx5_hlist *h, uint64_t key,\n+\t\t\t\t\t     void *ctx);\n+\n+/**\n  * Remove an entry from the hash list table. User should guarantee the validity\n  * of the entry.\n  *\n@@ -383,9 +473,10 @@ int mlx5_hlist_insert_ex(struct mlx5_hlist *h, struct mlx5_hlist_entry *entry,\n  *   Pointer to the hast list table. (not used)\n  * @param entry\n  *   Entry to be removed from the hash list table.\n+ * @return\n+ *   0 on entry removed, 1 on entry still referenced.\n  */\n-void mlx5_hlist_remove(struct mlx5_hlist *h __rte_unused,\n-\t\t       struct mlx5_hlist_entry *entry);\n+int mlx5_hlist_unregister(struct mlx5_hlist *h, struct mlx5_hlist_entry *entry);\n \n /**\n  * Destroy the hash list table, all the entries already inserted into the lists\n@@ -394,13 +485,8 @@ void mlx5_hlist_remove(struct mlx5_hlist *h __rte_unused,\n  *\n  * @param h\n  *   Pointer to the hast list table.\n- * @param cb\n- *   Callback function for each inserted entry when destroying the hash list.\n- * @param ctx\n- *   Common context parameter used by callback function for each entry.\n  */\n-void mlx5_hlist_destroy(struct mlx5_hlist *h,\n-\t\t\tmlx5_hlist_destroy_callback_fn cb, void *ctx);\n+void mlx5_hlist_destroy(struct mlx5_hlist *h);\n \n /**\n  * This function allocates non-initialized memory entry from pool.\n",
    "prefixes": [
        "07/25"
    ]
}