get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 422,
    "url": "https://patches.dpdk.org/api/patches/422/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/1411036471-3822-4-git-send-email-pablo.de.lara.guarch@intel.com/",
    "project": {
        "id": 1,
        "url": "https://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": "<1411036471-3822-4-git-send-email-pablo.de.lara.guarch@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/1411036471-3822-4-git-send-email-pablo.de.lara.guarch@intel.com",
    "date": "2014-09-18T10:34:31",
    "name": "[dpdk-dev,3/3] app/test: Added unit tests for Thread Safe Hash library",
    "commit_ref": null,
    "pull_url": null,
    "state": "rejected",
    "archived": true,
    "hash": "cf632d169ba1ee74bff928166ce466660aa19b13",
    "submitter": {
        "id": 9,
        "url": "https://patches.dpdk.org/api/people/9/?format=api",
        "name": "De Lara Guarch, Pablo",
        "email": "pablo.de.lara.guarch@intel.com"
    },
    "delegate": null,
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/1411036471-3822-4-git-send-email-pablo.de.lara.guarch@intel.com/mbox/",
    "series": [],
    "comments": "https://patches.dpdk.org/api/patches/422/comments/",
    "check": "pending",
    "checks": "https://patches.dpdk.org/api/patches/422/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@dpdk.org",
        "Delivered-To": "patchwork@dpdk.org",
        "Received": [
            "from [92.243.14.124] (localhost [IPv6:::1])\n\tby dpdk.org (Postfix) with ESMTP id EC92BB3B0;\n\tThu, 18 Sep 2014 12:29:11 +0200 (CEST)",
            "from mga11.intel.com (mga11.intel.com [192.55.52.93])\n\tby dpdk.org (Postfix) with ESMTP id 2704868C2\n\tfor <dev@dpdk.org>; Thu, 18 Sep 2014 12:29:03 +0200 (CEST)",
            "from fmsmga002.fm.intel.com ([10.253.24.26])\n\tby fmsmga102.fm.intel.com with ESMTP; 18 Sep 2014 03:34:36 -0700",
            "from irvmail001.ir.intel.com ([163.33.26.43])\n\tby fmsmga002.fm.intel.com with ESMTP; 18 Sep 2014 03:34:33 -0700",
            "from sivswdev02.ir.intel.com (sivswdev02.ir.intel.com\n\t[10.237.217.46])\n\tby irvmail001.ir.intel.com (8.14.3/8.13.6/MailSET/Hub) with ESMTP id\n\ts8IAYWsp012113 for <dev@dpdk.org>; Thu, 18 Sep 2014 11:34:32 +0100",
            "from sivswdev02.ir.intel.com (localhost [127.0.0.1])\n\tby sivswdev02.ir.intel.com with ESMTP id s8IAYW6F003883\n\tfor <dev@dpdk.org>; Thu, 18 Sep 2014 11:34:32 +0100",
            "(from pdelarax@localhost)\n\tby sivswdev02.ir.intel.com with  id s8IAYWrx003879\n\tfor dev@dpdk.org; Thu, 18 Sep 2014 11:34:32 +0100"
        ],
        "X-ExtLoop1": "1",
        "X-IronPort-AV": "E=Sophos;i=\"5.04,546,1406617200\"; d=\"scan'208\";a=\"601529813\"",
        "From": "Pablo de Lara <pablo.de.lara.guarch@intel.com>",
        "To": "dev@dpdk.org",
        "Date": "Thu, 18 Sep 2014 11:34:31 +0100",
        "Message-Id": "<1411036471-3822-4-git-send-email-pablo.de.lara.guarch@intel.com>",
        "X-Mailer": "git-send-email 1.7.4.1",
        "In-Reply-To": "<1411036471-3822-1-git-send-email-pablo.de.lara.guarch@intel.com>",
        "References": "<1411036471-3822-1-git-send-email-pablo.de.lara.guarch@intel.com>",
        "Subject": "[dpdk-dev] [PATCH 3/3] app/test: Added unit tests for Thread Safe\n\tHash library",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "patches and discussions about DPDK <dev.dpdk.org>",
        "List-Unsubscribe": "<http://dpdk.org/ml/options/dev>,\n\t<mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://dpdk.org/ml/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<http://dpdk.org/ml/listinfo/dev>,\n\t<mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "Added 3 new unit tests:\n\n- Functional unit test: Tests creation and handling of\n  a hash table with a single thread.\n\n- Performance unit tests: Benchmark hash operations\n  add/delete/lookup, returning number of CPU cycles/operation.\n\n- Multi thread unit tests: Checks there is no data corruption\n  due to multiple threads working on the same hash table.\n\nSigned-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>\n---\n app/test/Makefile                   |    4 +\n app/test/test_tshash_func.c         | 1117 +++++++++++++++++++++++++++++++++++\n app/test/test_tshash_multi_thread.c |  351 +++++++++++\n app/test/test_tshash_perf.c         |  631 ++++++++++++++++++++\n 4 files changed, 2103 insertions(+), 0 deletions(-)\n create mode 100644 app/test/test_tshash_func.c\n create mode 100644 app/test/test_tshash_multi_thread.c\n create mode 100644 app/test/test_tshash_perf.c",
    "diff": "diff --git a/app/test/Makefile b/app/test/Makefile\nindex 37a3772..71dd7c2 100644\n--- a/app/test/Makefile\n+++ b/app/test/Makefile\n@@ -83,6 +83,10 @@ SRCS-y += test_memcpy_perf.c\n SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash.c\n SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_perf.c\n \n+SRCS-$(CONFIG_RTE_LIBRTE_THREAD_SAFE_HASH) += test_tshash_func.c\n+SRCS-$(CONFIG_RTE_LIBRTE_THREAD_SAFE_HASH) += test_tshash_perf.c\n+SRCS-$(CONFIG_RTE_LIBRTE_THREAD_SAFE_HASH) += test_tshash_multi_thread.c\n+\n SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm.c\n SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6.c\n \ndiff --git a/app/test/test_tshash_func.c b/app/test/test_tshash_func.c\nnew file mode 100644\nindex 0000000..7ec2e12\n--- /dev/null\n+++ b/app/test/test_tshash_func.c\n@@ -0,0 +1,1117 @@\n+/*-\n+ *   BSD LICENSE\n+ *\n+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.\n+ *   All rights reserved.\n+ *\n+ *   Redistribution and use in source and binary forms, with or without\n+ *   modification, are permitted provided that the following conditions\n+ *   are met:\n+ *\n+ *     * Redistributions of source code must retain the above copyright\n+ *       notice, this list of conditions and the following disclaimer.\n+ *     * Redistributions in binary form must reproduce the above copyright\n+ *       notice, this list of conditions and the following disclaimer in\n+ *       the documentation and/or other materials provided with the\n+ *       distribution.\n+ *     * Neither the name of Intel Corporation nor the names of its\n+ *       contributors may be used to endorse or promote products derived\n+ *       from this software without specific prior written permission.\n+ *\n+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ *   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+#include <stdio.h>\n+#include <stdint.h>\n+#include <string.h>\n+#include <stdlib.h>\n+#include <stdarg.h>\n+#include <errno.h>\n+#include <sys/queue.h>\n+\n+#include <rte_common.h>\n+#include <rte_malloc.h>\n+#include <rte_cycles.h>\n+#include <rte_random.h>\n+#include <rte_memory.h>\n+#include <rte_memzone.h>\n+#include <rte_tailq.h>\n+#include <rte_eal.h>\n+#include <rte_ip.h>\n+#include <rte_string_fns.h>\n+#include <cmdline_parse.h>\n+\n+#include <rte_tshash.h>\n+#include <rte_hash.h>\n+#ifdef RTE_MACHINE_CPUFLAG_SSE4_2\n+#include <rte_hash_crc.h>\n+#endif\n+#include <rte_jhash.h>\n+\n+#include \"test.h\"\n+\n+#define MAX_KEYS 1024\n+#define NUM_KEYS 64\n+\n+#if defined RTE_LIBRTE_THREAD_SAFE_HASH_STATS\n+\n+#ifdef RTE_MACHINE_CPUFLAG_SSE4_2\n+static rte_hash_function hashtest_funcs[] = {rte_jhash, rte_hash_crc};\n+static const char * const hash_func_strings[] = {\"jhash\", \"crc\"};\n+#else\n+static rte_hash_function hashtest_funcs[] = {rte_jhash};\n+static const char * const hash_func_strings[] = {\"jhash\"};\n+#endif\n+\n+\n+/*\n+ * Check condition and return an error if true.\n+ */\n+#define RETURN_IF_ERROR(cond, str, ...) do {\t\t\t\t\\\n+\tif (cond) {\t\t\t\t\t\t\t\\\n+\t\tprintf(\"ERROR line %d: \" str \"\\n\", __LINE__, ##__VA_ARGS__); \\\n+\t\treturn -1;\t\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\\\n+} while (0)\n+\n+/*\n+ * Hash function that always returns the same value, to easily test what\n+ * happens when a bucket is full.\n+ */\n+static uint32_t pseudo_hash(__attribute__((unused)) const void *keys,\n+\t\t\t    __attribute__((unused)) uint32_t key_len,\n+\t\t\t    __attribute__((unused)) uint32_t init_val)\n+{\n+\treturn 3;\n+}\n+\n+static void *generate_key(uint8_t num_bytes)\n+{\n+\tchar *key = rte_zmalloc(NULL, num_bytes, 0);\n+\tunsigned i;\n+\n+\tfor (i = 0; i < num_bytes; i++)\n+\t\tkey[i] = rand() % 255;\n+\n+\treturn (void *)key;\n+}\n+\n+/* Parameters used for hash table in unit test functions. Name set later. */\n+static struct rte_tshash_parameters {\n+\tchar name[RTE_TSHASH_NAMESIZE];\n+\tunsigned max_entries;\n+\tunsigned key_len;\n+\tuint8_t socket_id;\n+\n+} ut_params;\n+\n+static uint8_t key_sizes[] = {16, 32, 48, 64, 96, 128};\n+static struct rte_tshash *hash_tables[RTE_DIM(key_sizes)];\n+static uint8_t burst_sizes[] = {13, 16, 32, 64};\n+\n+static struct rte_tshash_extra_args e_args = {\n+\t\t.malloc_mem = 0,\n+\t\t.max_load_factor = 0,\n+\t\t.rte_tshash_cmp_eq = NULL,\n+\t\t.hash_func = NULL\n+\n+};\n+\n+static int\n+create_hash_tables(void)\n+{\n+\tunsigned i;\n+\tfor (i = 0; i < RTE_DIM(key_sizes); i++) {\n+\t\thash_tables[i] = NULL;\n+\t\tsprintf(ut_params.name, \"test_k%d\", key_sizes[i]);\n+\t\tut_params.max_entries = MAX_KEYS;\n+\t\tut_params.key_len = key_sizes[i];\n+\t\tut_params.socket_id = rte_socket_id();\n+\t\thash_tables[i] = rte_tshash_find_existing(ut_params.name);\n+\t\tif (hash_tables[i] != NULL)\n+\t\t\trte_tshash_reset(hash_tables[i]);\n+\t\telse\n+\t\t\thash_tables[i] = rte_tshash_create(ut_params.name,\n+\t\t\t\t\t\t\t\t\tut_params.max_entries, ut_params.key_len,\n+\t\t\t\t\t\t\t\t\tut_params.socket_id, NULL);\n+\t\tRETURN_IF_ERROR(hash_tables[i] == NULL, \"hash creation failed\");\n+\t}\n+\treturn 0;\n+}\n+/*\n+ * Basic sequence of operations for a single key:\n+ *\t- add\n+ *\t- lookup (hit)\n+ *\t- delete\n+ *\t- lookup (miss)\n+ */\n+static int\n+test_hash_add_lookup_delete(void)\n+{\n+\t/* Test with standard add/lookup/delete functions */\n+\tunsigned i;\n+\tuint64_t hash_value, ret_data, data;\n+\tvoid *key;\n+\n+\tfor (i = 0; i < RTE_DIM(key_sizes); i++) {\n+\t\tkey = generate_key(key_sizes[i]);\n+\t\tdata = rte_rand();\n+\n+\t\tif (0 != rte_tshash_add_key(hash_tables[i], key, data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to add key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 != rte_tshash_lookup(hash_tables[i], key, &ret_data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to find key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tif (ret_data != data) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Data returned was not the one expected\\n\");\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 != rte_tshash_del_key(hash_tables[i], key, NULL)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to delete key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 == rte_tshash_lookup(hash_tables[i], key, &ret_data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Found key after deleting key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\trte_free(key);\n+\t\trte_tshash_reset(hash_tables[i]);\n+\t}\n+\n+\t/* Repeat test with precomputed hash values */\n+\tfor (i = 0; i < RTE_DIM(key_sizes); i++) {\n+\t\tkey = generate_key(key_sizes[i]);\n+\t\thash_value = rte_rand() % MAX_KEYS;\n+\t\tdata = rte_rand();\n+\n+\t\tif (0 != rte_tshash_add_key_with_hash(hash_tables[i], hash_value, key, data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to add key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 != rte_tshash_lookup_with_hash(hash_tables[i], hash_value, key, &ret_data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to find key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tif (ret_data != data) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Data returned was not the one expected\\n\");\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 != rte_tshash_del_key_with_hash(hash_tables[i], hash_value, key, NULL)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to delete key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 == rte_tshash_lookup_with_hash(hash_tables[i], hash_value, key, &ret_data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Found key after deleting key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\trte_free(key);\n+\t\trte_tshash_reset(hash_tables[i]);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Sequence of operations for a single key:\n+ *\t- delete: miss\n+ *\t- add\n+ *\t- lookup: hit\n+ *\t- add: miss\n+ *\t- lookup: hit\n+ *\t- delete: hit\n+ *\t- delete: miss\n+ *\t- lookup: miss\n+ */\n+static int\n+test_hash_add_lookup_delete_miss(void)\n+{\n+\tunsigned i;\n+\tuint64_t hash_value, ret_data, data;\n+\tvoid *key;\n+\n+\tfor (i = 0; i < RTE_DIM(key_sizes); i++) {\n+\t\tkey = generate_key(key_sizes[i]);\n+\t\thash_value = rte_rand() % MAX_KEYS;\n+\t\tdata = rte_rand();\n+\n+\t\tif (0 == rte_tshash_del_key_with_hash(hash_tables[i],\n+\t\t\t\t\t\t\t\t\t\t\thash_value, key, NULL)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Deleted key of %d bytes that should not exist\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 != rte_tshash_add_key_with_hash(hash_tables[i],\n+\t\t\t\t\t\t\t\t\t\t\thash_value, key, data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to add key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 != rte_tshash_lookup_with_hash(hash_tables[i],\n+\t\t\t\t\t\t\t\t\t\t\thash_value, key, &ret_data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to lookup key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tif (ret_data != data) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Data returned was not the one expected\\n\");\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 == rte_tshash_add_key_with_hash(hash_tables[i],\n+\t\t\t\t\t\t\t\t\t\t\thash_value, key, data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Added key of %d bytes that already existed\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 != rte_tshash_lookup_with_hash(hash_tables[i],\n+\t\t\t\t\t\t\t\t\t\t\thash_value, key, &ret_data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to lookup key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tif (ret_data != data) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Data returned was not the one expected\\n\");\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 != rte_tshash_del_key_with_hash(hash_tables[i],\n+\t\t\t\t\t\t\t\t\t\t\thash_value, key, NULL)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to delete key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 == rte_tshash_del_key_with_hash(hash_tables[i],\n+\t\t\t\t\t\t\t\t\t\t\thash_value, key, NULL)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Deleted key of %d bytes that should not exist\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 == rte_tshash_lookup_with_hash(hash_tables[i],\n+\t\t\t\t\t\t\t\t\t\t\thash_value, key, &ret_data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Found key after deleting key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\trte_free(key);\n+\t\trte_tshash_reset(hash_tables[i]);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Sequence of operations for a single key:\n+ *\t- add\n+ *\t- lookup: hit\n+ *\t- add: update\n+ *\t- lookup: hit\n+ *\t- delete: hit\n+ *\t- lookup: miss\n+ */\n+static int\n+test_hash_add_lookup_update_delete_miss(void)\n+{\n+\tstruct rte_tshash *hash_tables_update[RTE_DIM(key_sizes)];\n+\n+\tut_params.max_entries = MAX_KEYS;\n+\tut_params.socket_id = 0;\n+\te_args.malloc_mem = 0;\n+\te_args.max_load_factor = 0.5;\n+\te_args.rte_tshash_cmp_eq = NULL;\n+\te_args.hash_func = NULL;\n+\te_args.update_add = 1;\n+\n+\tunsigned i;\n+\tuint64_t hash_value, ret_data, data;\n+\tvoid *key;\n+\n+\tfor (i = 0; i < RTE_DIM(key_sizes); i++) {\n+\t\tsprintf(ut_params.name, \"test_k%d_update\", key_sizes[i]);\n+\t\tut_params.key_len = key_sizes[i];\n+\n+\t\thash_tables_update[i] = rte_tshash_find_existing(ut_params.name);\n+\t\tif (hash_tables_update[i] != NULL)\n+\t\t\trte_tshash_reset(hash_tables_update[i]);\n+\t\telse\n+\t\t\thash_tables_update[i] = rte_tshash_create(ut_params.name,\n+\t\t\t\t\t\t\t\t\t\t\t\t\tut_params.max_entries,\n+\t\t\t\t\t\t\t\t\t\t\t\t\tut_params.key_len,\n+\t\t\t\t\t\t\t\t\t\t\t\t\tut_params.socket_id,\n+\t\t\t\t\t\t\t\t\t\t\t\t\t&e_args);\n+\t\tRETURN_IF_ERROR(hash_tables_update[i] == NULL, \"hash creation failed\");\n+\n+\t\tkey = generate_key(key_sizes[i]);\n+\t\thash_value = rte_rand() % MAX_KEYS;\n+\t\tdata = rte_rand();\n+\n+\t\tif (0 != rte_tshash_add_key_with_hash(hash_tables_update[i],\n+\t\t\t\t\t\t\t\t\t\t\thash_value, key, data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to add key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 != rte_tshash_lookup_with_hash(hash_tables_update[i],\n+\t\t\t\t\t\t\t\t\t\t\thash_value, key, &ret_data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to lookup key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tif (ret_data != data) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Data returned was not the one expected\\n\");\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tdata = rte_rand() % MAX_KEYS;\n+\t\tif (0 != rte_tshash_add_key_with_hash(hash_tables_update[i],\n+\t\t\t\t\t\t\t\t\t\t\thash_value, key, data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to update data of key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 != rte_tshash_lookup_with_hash(hash_tables_update[i], hash_value,\n+\t\t\t\t\t\t\t\t\t\t\tkey, &ret_data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to lookup key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tif (ret_data != data) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Data returned was not the one expected\\n\");\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 != rte_tshash_del_key_with_hash(hash_tables_update[i], hash_value,\n+\t\t\t\t\t\t\t\t\t\t\tkey, NULL)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to delete key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 == rte_tshash_lookup_with_hash(hash_tables_update[i], hash_value,\n+\t\t\t\t\t\t\t\t\t\t\tkey, &ret_data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Found key after deleting key of %d bytes\\n\", key_sizes[i]);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\trte_free(key);\n+\t\trte_tshash_reset(hash_tables_update[i]);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Sequence of operations for find existing hash table\n+ *\n+ *  - find existing table: hit\n+ *  - find non-existing table: miss\n+ *\n+ */\n+static int\n+test_hash_find_existing(void)\n+{\n+\tstruct rte_tshash *result = NULL;\n+\tunsigned i;\n+\tchar test_name[RTE_TSHASH_NAMESIZE];\n+\n+\tfor (i = 0; i < RTE_DIM(key_sizes); i++) {\n+\t\t/* Try to find existing hash table */\n+\t\tsprintf(test_name, \"test_k%d\", key_sizes[i]);\n+\t\tresult = rte_tshash_find_existing(test_name);\n+\t\tRETURN_IF_ERROR(result != hash_tables[i], \"could not find existing hash table\");\n+\t}\n+\n+\t/* Try to find non-existing hash table */\n+\tresult = rte_tshash_find_existing(\"hash_find_non_existing\");\n+\tRETURN_IF_ERROR(!(result == NULL), \"found table that shouldn't exist\");\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Sequence of operations for 5 keys\n+ *\t- add keys\n+ *\t- lookup keys: hit\n+ *\t- delete keys : hit\n+ *\t- lookup keys: miss\n+ */\n+static int\n+test_hash_burst(void)\n+{\n+\tunsigned i, j, k;\n+\tvoid *keys[NUM_KEYS];\n+\tuint64_t data[NUM_KEYS];\n+\tuint64_t ret_data[NUM_KEYS];\n+\tuint64_t hash_values[NUM_KEYS];\n+\tuint64_t *hash_values_ptrs[NUM_KEYS];\n+\tuint64_t lookup_mask, hit_mask;\n+\tuint8_t hits;\n+\n+\tfor (i = 0; i < RTE_DIM(key_sizes); i++) {\n+\t\tfor (j = 0; j < NUM_KEYS; j++) {\n+\t\t\tkeys[j] = generate_key(key_sizes[i]);\n+\t\t\tdata[j] = rte_rand();\n+\t\t\tif (0 != rte_tshash_add_key(hash_tables[i], keys[j], data[j])) {\n+\t\t\t\tprintf(\"Error: Failed to add key of %d bytes\\n\", key_sizes[i]);\n+\t\t\t\tgoto fail_burst;\n+\t\t\t}\n+\t\t}\n+\t\tfor (j = 0; j < RTE_DIM(burst_sizes); j++) {\n+\t\t\tif (burst_sizes[j] == 64)\n+\t\t\t\tlookup_mask = 0xffffffffffffffff;\n+\t\t\telse\n+\t\t\t\tlookup_mask = (1llu << burst_sizes[j]) - 1;\n+\n+\t\t\thits = rte_tshash_lookup_bulk(hash_tables[i], lookup_mask,\n+\t\t\t\t\t\t\t\t\t\t(const void * const *)keys, ret_data,\n+\t\t\t\t\t\t\t\t\t\t&hit_mask);\n+\t\t\tif (hits != burst_sizes[j]) {\n+\t\t\t\tprintf(\"Error: Failed to find %d key(s) of %d bytes\\n\",\n+\t\t\t\t\t\tburst_sizes[j] - hits, key_sizes[i]);\n+\t\t\t\tgoto fail_burst;\n+\t\t\t}\n+\t\t\tfor (k = 0; k < burst_sizes[j]; k++) {\n+\t\t\t\tif (unlikely(ret_data[j] != data[j])) {\n+\t\t\t\t\tprintf(\"Error with value returned from lookup of key of %d bytes\",\n+\t\t\t\t\t\t\tkey_sizes[i]);\n+\t\t\t\t\tgoto fail_burst;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\n+\t\tfor (j = 0; j < NUM_KEYS; j++) {\n+\t\t\tif (0 != rte_tshash_del_key(hash_tables[i], keys[j], NULL)) {\n+\t\t\t\tprintf(\"Error: Failed to delete key of %d bytes\\n\", key_sizes[i]);\n+\t\t\t\tgoto fail_burst;\n+\t\t\t}\n+\t\t}\n+\n+\t\tlookup_mask = 0xffffffffffffffff;\n+\t\thits = rte_tshash_lookup_bulk(hash_tables[i], lookup_mask,\n+\t\t\t\t\t\t\t\t\t(const void * const *)keys, ret_data,\n+\t\t\t\t\t\t\t\t\t&hit_mask);\n+\t\tif (0 != hits) {\n+\t\t\tprintf(\"Error: Found  %d key(s) of %d bytes that should not exist\\n\",\n+\t\t\t\t\thits, key_sizes[i]);\n+\t\t\tgoto fail_burst;\n+\t\t}\n+\n+\t\tfor (j = 0; j < NUM_KEYS; j++)\n+\t\t\trte_free(keys[j]);\n+\t\trte_tshash_reset(hash_tables[i]);\n+\t}\n+\n+\t/* Repeat test with precomputed hash values */\n+\tfor (i = 0; i < RTE_DIM(key_sizes); i++) {\n+\t\tfor (j = 0; j < NUM_KEYS; j++) {\n+\t\t\tkeys[j] = generate_key(key_sizes[i]);\n+\t\t\thash_values[j] = rte_rand() % MAX_KEYS;\n+\t\t\thash_values_ptrs[j] = &hash_values[j];\n+\t\t\tdata[j] = rte_rand();\n+\t\t\tif (0 != rte_tshash_add_key_with_hash(hash_tables[i], hash_values[j],\n+\t\t\t\t\t\t\t\t\t\t\t\tkeys[j], data[j])) {\n+\t\t\t\tprintf(\"Error: Failed to add key of %d bytes\\n\", key_sizes[i]);\n+\t\t\t\tgoto fail_burst;\n+\t\t\t}\n+\t\t}\n+\t\tfor (j = 0; j < RTE_DIM(burst_sizes); j++) {\n+\t\t\tif (burst_sizes[j] == 64)\n+\t\t\t\tlookup_mask = 0xffffffffffffffff;\n+\t\t\telse\n+\t\t\t\tlookup_mask = (1llu << burst_sizes[j]) - 1;\n+\n+\t\t\thits = rte_tshash_lookup_bulk_with_hash(hash_tables[i], lookup_mask,\n+\t\t\t\t\t\t\t\t\t\t\t(uint64_t * const *)hash_values_ptrs,\n+\t\t\t\t\t\t\t\t\t\t\t(const void * const *)keys, ret_data,\n+\t\t\t\t\t\t\t\t\t\t\t&hit_mask);\n+\t\t\tif (hits != burst_sizes[j]) {\n+\t\t\t\tprintf(\"Error: Failed to find %d key(s) of %d bytes\\n\",\n+\t\t\t\t\t\tburst_sizes[j] - hits, key_sizes[i]);\n+\t\t\t\tgoto fail_burst;\n+\t\t\t}\n+\t\t\tfor (k = 0; k < burst_sizes[j]; k++) {\n+\t\t\t\tif (unlikely(ret_data[j] != data[j])) {\n+\t\t\t\t\tprintf(\"Error with value returned from lookup of key of %d bytes\",\n+\t\t\t\t\t\t\tkey_sizes[i]);\n+\t\t\t\t\tgoto fail_burst;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\n+\t\tfor (j = 0; j < NUM_KEYS; j++) {\n+\t\t\tif (0 != rte_tshash_del_key_with_hash(hash_tables[i], hash_values[j],\n+\t\t\t\t\t\t\t\t\t\t\t\tkeys[j], NULL)) {\n+\t\t\t\tprintf(\"Error: Failed to delete key of %d bytes\\n\", key_sizes[i]);\n+\t\t\t\tgoto fail_burst;\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\n+\t\tlookup_mask = 0xffffffffffffffff;\n+\t\thits = rte_tshash_lookup_bulk_with_hash(hash_tables[i], lookup_mask,\n+\t\t\t\t\t\t\t\t\t\t\t(uint64_t * const *)hash_values_ptrs,\n+\t\t\t\t\t\t\t\t\t\t\t(const void * const *)keys, ret_data,\n+\t\t\t\t\t\t\t\t\t\t\t&hit_mask);\n+\t\tif (0 != hits) {\n+\t\t\tprintf(\"Error: Found  %d key(s) of %d bytes that should not exist\\n\",\n+\t\t\t\t\thits, key_sizes[i]);\n+\t\t\tgoto fail_burst;\n+\t\t}\n+\n+\t\tfor (j = 0; j < NUM_KEYS; j++)\n+\t\t\trte_free(keys[j]);\n+\t\trte_tshash_reset(hash_tables[i]);\n+\t}\n+\n+\treturn 0;\n+\n+fail_burst:\n+\tfor (j = 0; j < NUM_KEYS; j++)\n+\t\trte_free(keys[j]);\n+\treturn -1;\n+}\n+\n+/*\n+ * Add keys to the same bucket until bucket full.\n+ *\t- add 5 keys to the same bucket (hash created with 4 keys per bucket):\n+ *\t  first 4 in first level, 5th in second one\n+ *\t- lookup the 5 keys: 4 hits in first level, 1 in second one\n+ *\t- delete the 5 keys: 5 OK\n+ *\t- lookup the 5 keys: 5 misses\n+ */\n+static int\n+test_hash_full_bucket(void)\n+{\n+\tstruct rte_tshash *handle;\n+\tunsigned i;\n+\tvoid *keys[NUM_KEYS];\n+\tuint64_t data[NUM_KEYS];\n+\tuint64_t ret_data[NUM_KEYS];\n+\n+\tut_params.max_entries = MAX_KEYS;\n+\tut_params.key_len = key_sizes[0];\n+\tut_params.socket_id = 0;\n+\tsprintf(ut_params.name, \"test_full_bucket\");\n+\te_args.max_load_factor = 0.5;\n+\te_args.hash_func = pseudo_hash;\n+\n+\thandle = rte_tshash_find_existing(ut_params.name);\n+\tif (handle != NULL)\n+\t\trte_tshash_reset(handle);\n+\telse\n+\t\thandle = rte_tshash_create(ut_params.name, ut_params.max_entries,\n+\t\t\t\t\t\t\t\tut_params.key_len, ut_params.socket_id, &e_args);\n+\tRETURN_IF_ERROR(handle == NULL, \"hash creation failed\");\n+\n+\n+\t/* Fill bucket */\n+\tfor (i = 0; i < RTE_TSHASH_BUCKET_SIZE; i++) {\n+\t\tkeys[i] = generate_key(ut_params.key_len);\n+\t\tdata[i] = rte_rand();\n+\n+\t\tif (0 != rte_tshash_add_key(handle, keys[i], data[i])) {\n+\t\t\tprintf(\"Error: Failed to add key of %d bytes\\n\", ut_params.key_len);\n+\t\t\tgoto fail_burst;\n+\t\t}\n+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS\n+\t\tconst struct rte_tshash_stats *stats  = rte_tshash_get_stats(handle);\n+\t\tif (stats->used_slots != (i+1) || stats->num_extra_buckets != 0) {\n+\t\t\tprintf(\"Error: used_slots = %u, expected = %u;  extra_buckets = %u\"\n+\t\t\t\t\t\" expected = 0 \\n\", stats->used_slots, i+1,\n+\t\t\t\t\tstats->num_extra_buckets);\n+\t\t\tgoto fail_burst;\n+\t\t}\n+#endif\n+\t}\n+\n+\t/* This new entry should go to the next bucket */\n+\tkeys[RTE_TSHASH_BUCKET_SIZE] = generate_key(ut_params.key_len);\n+\tdata[RTE_TSHASH_BUCKET_SIZE] = rte_rand();\n+\n+\tif (0 != rte_tshash_add_key(handle, keys[RTE_TSHASH_BUCKET_SIZE],\n+\t\t\t\t\t\t\t\tdata[RTE_TSHASH_BUCKET_SIZE])) {\n+\t\tprintf(\"Error: Failed to add key of %d bytes\\n\", ut_params.key_len);\n+\t\tgoto fail_burst;\n+\t}\n+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS\n+\t\tconst struct rte_tshash_stats *stats  = rte_tshash_get_stats(handle);\n+\t\tif (stats->used_slots != (RTE_TSHASH_BUCKET_SIZE+1)\n+\t\t\t\t|| stats->num_extra_buckets != 1) {\n+\t\t\tprintf(\"Error: used_slots = %u, expected = %u;  extra_buckets = %u\"\n+\t\t\t\t\" expected = 1 \\n\", stats->used_slots, RTE_TSHASH_BUCKET_SIZE+1,\n+\t\t\t\tstats->num_extra_buckets);\n+\t\t\tgoto fail_burst;\n+\t\t}\n+#endif\n+\n+\t/* Lookup */\n+\tfor (i = 0; i < RTE_TSHASH_BUCKET_SIZE+1; i++) {\n+\t\tif (0 != rte_tshash_lookup(handle, keys[i], &ret_data[i])) {\n+\t\t\tprintf(\"Error: Failed to find key of %d bytes\\n\", ut_params.key_len);\n+\t\t\tgoto fail_burst;\n+\t\t}\n+\t\tif (ret_data[i] != data[i]) {\n+\t\t\tprintf(\"Error: Data returned was not the one expected\\n\");\n+\t\t\tgoto fail_burst;\n+\t\t}\n+\t}\n+\n+\t/* Delete */\n+\tfor (i = 0; i < RTE_TSHASH_BUCKET_SIZE+1; i++) {\n+\t\tif (0 != rte_tshash_del_key(handle, keys[i], NULL)) {\n+\t\t\tprintf(\"Error: Failed to delete key of %d bytes\\n\", ut_params.key_len);\n+\t\t\tgoto fail_burst;\n+\t\t}\n+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS\n+\t\tconst struct rte_tshash_stats *stats  = rte_tshash_get_stats(handle);\n+\t\tif (stats->used_slots != (RTE_TSHASH_BUCKET_SIZE-i)\n+\t\t\t\t|| stats->num_extra_buckets != 1) {\n+\t\t\tprintf(\"Error: used_slots = %u, expected = %u;  extra_buckets = %u\"\n+\t\t\t\t\" expected = 1 \\n\", stats->used_slots, RTE_TSHASH_BUCKET_SIZE-i,\n+\t\t\t\tstats->num_extra_buckets);\n+\t\t\tgoto fail_burst;\n+\t\t}\n+#endif\n+\t}\n+\n+\t/* Lookup */\n+\tfor (i = 0; i < RTE_TSHASH_BUCKET_SIZE; i++) {\n+\t\tif (0 == rte_tshash_lookup(handle, keys[i], &ret_data[i])) {\n+\t\t\tprintf(\"Error: Found key after deleting key of %d bytes\\n\",\n+\t\t\t\t\tut_params.key_len);\n+\t\t\tgoto fail_burst;\n+\t\t}\n+\t}\n+\tfor (i = 0; i < RTE_TSHASH_BUCKET_SIZE + 1; i++)\n+\t\trte_free(keys[i]);\n+\n+\treturn 0;\n+fail_burst:\n+\tfor (i = 0; i < RTE_TSHASH_BUCKET_SIZE + 1; i++)\n+\t\trte_free(keys[i]);\n+\treturn -1;\n+}\n+\n+\n+/*\n+ * Do tests for hash creation with bad parameters.\n+ */\n+static int\n+test_hash_creation_with_bad_parameters(void)\n+{\n+\tstruct rte_tshash *handle;\n+\tut_params.max_entries = MAX_KEYS;\n+\tut_params.key_len = key_sizes[0];\n+\tut_params.socket_id = 0;\n+\n+\thandle = rte_tshash_create(NULL, ut_params.max_entries,\n+\t\t\tut_params.key_len, ut_params.socket_id, NULL);\n+\n+\tRETURN_IF_ERROR(handle != NULL,\n+\t\t\t\"Impossible creating hash sucessfully without a name\\n\");\n+\n+\tsprintf(ut_params.name, \"creation_with_bad_parameters_0\");\n+\tut_params.max_entries = RTE_TSHASH_MIN_ENTRIES - 1;\n+\thandle = rte_tshash_create(ut_params.name, ut_params.max_entries,\n+\t\t\tut_params.key_len, ut_params.socket_id, NULL);\n+\n+\tRETURN_IF_ERROR(handle != NULL,\n+\t\t\t\"Impossible creating hash sucessfully with maximum number of entries\"\n+\t\t\t\"less than minimum required\\n\");\n+\n+\tsprintf(ut_params.name, \"creation_with_bad_parameters_1\");\n+\tut_params.max_entries = MAX_KEYS;\n+\te_args.max_load_factor = RTE_TSHASH_MAXIMUM_LOAD_FACTOR + 0.1;\n+\te_args.malloc_mem = 0;\n+\te_args.rte_tshash_cmp_eq = NULL;\n+\te_args.hash_func = NULL;\n+\n+\thandle = rte_tshash_create(ut_params.name, ut_params.max_entries,\n+\t\t\tut_params.key_len, ut_params.socket_id, &e_args);\n+\n+\tRETURN_IF_ERROR(handle != NULL,\n+\t\t\t\"Impossible creating hash sucessfully with max_load_factor\"\n+\t\t\t\"in parameter exceeded\\n\");\n+\n+\tsprintf(ut_params.name, \"creation_with_bad_parameters_2\");\n+\te_args.max_load_factor = RTE_TSHASH_MAXIMUM_LOAD_FACTOR;\n+\tut_params.key_len = 13;\n+\n+\thandle = rte_tshash_create(ut_params.name, ut_params.max_entries,\n+\t\t\tut_params.key_len, ut_params.socket_id, &e_args);\n+\n+\tRETURN_IF_ERROR(handle != NULL,\n+\t\t\t\"Impossible creating hash sucessfully wif key size is not multiple\"\n+\t\t\t\"of 16 and there is no user defined key compare function\\n\");\n+\n+\tsprintf(ut_params.name, \"creation_with_bad_parameters_3\");\n+\tut_params.key_len = 0;\n+\n+\thandle = rte_tshash_create(ut_params.name, ut_params.max_entries,\n+\t\t\tut_params.key_len, ut_params.socket_id, NULL);\n+\n+\tRETURN_IF_ERROR(handle != NULL,\n+\t\t\t\"Impossible creating hash sucessfully if key_len in parameter is zero\\n\");\n+\n+\tsprintf(ut_params.name, \"creation_with_bad_parameters_4\");\n+\tut_params.socket_id = RTE_MAX_NUMA_NODES;\n+\tut_params.key_len = key_sizes[0];\n+\n+\thandle = rte_tshash_create(ut_params.name, ut_params.max_entries,\n+\t\t\tut_params.key_len, ut_params.socket_id, NULL);\n+\n+\tRETURN_IF_ERROR(handle != NULL,\n+\t\t\t\"Impossible creating hash sucessfully with invalid socket\\n\");\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Test the creation of a hash table with malloc function.\n+ */\n+static int\n+test_hash_creation_with_malloc(void)\n+{\n+\tstruct rte_tshash *handle;\n+\n+\tut_params.max_entries = MAX_KEYS;\n+\tut_params.key_len = key_sizes[0];\n+\tut_params.socket_id = 0;\n+\tsprintf(ut_params.name, \"test_malloc\");\n+\te_args.malloc_mem = 1;\n+\te_args.max_load_factor = 0.5;\n+\te_args.rte_tshash_cmp_eq = NULL;\n+\te_args.hash_func = NULL;\n+\n+\thandle = rte_tshash_find_existing(ut_params.name);\n+\tif (handle != NULL)\n+\t\trte_tshash_reset(handle);\n+\telse\n+\t\thandle = rte_tshash_create(ut_params.name, ut_params.max_entries,\n+\t\t\t\t\t\t\t\tut_params.key_len, ut_params.socket_id, &e_args);\n+\tRETURN_IF_ERROR(handle == NULL, \"hash creation with malloc failed\");\n+\n+\treturn 0;\n+}\n+\n+static int\n+generic_key_cmp(const void *key1, const void *key2, uint8_t key_size)\n+{\n+\treturn !memcmp(key1, key2, key_size);\n+}\n+\n+/*\n+ * Test add/lookup/delete functions with user-defined key compare function.\n+ */\n+static int\n+test_hash_odd_key_size(void)\n+{\n+\tstruct rte_tshash *handle;\n+\tuint64_t hash_value, ret_data, data;\n+\tvoid *key;\n+\n+\tut_params.max_entries = MAX_KEYS;\n+\tut_params.key_len = 26;\n+\tut_params.socket_id = 0;\n+\tsprintf(ut_params.name, \"test_odd_key_size\");\n+\te_args.max_load_factor = 0.5;\n+\te_args.rte_tshash_cmp_eq = generic_key_cmp;\n+\n+\thandle = rte_tshash_find_existing(ut_params.name);\n+\tif (handle != NULL)\n+\t\trte_tshash_reset(handle);\n+\telse\n+\t\thandle = rte_tshash_create(ut_params.name, ut_params.max_entries,\n+\t\t\t\t\t\t\t\t\tut_params.key_len, ut_params.socket_id,\n+\t\t\t\t\t\t\t\t\t&e_args);\n+\tRETURN_IF_ERROR(handle == NULL, \"hash creation with malloc failed\");\n+\n+\t/* Test with standard add/lookup/delete functions */\n+\tkey = generate_key(ut_params.key_len);\n+\tdata = rte_rand();\n+\n+\tif (0 != rte_tshash_add_key(handle, key, data)) {\n+\t\trte_free(key);\n+\t\tprintf(\"Error: Failed to add key of %d bytes\\n\", ut_params.key_len);\n+\t\treturn -1;\n+\t}\n+\n+\tif (0 != rte_tshash_lookup(handle, key, &ret_data)) {\n+\t\trte_free(key);\n+\t\tprintf(\"Error: Failed to find key of %d bytes\\n\", ut_params.key_len);\n+\t\treturn -1;\n+\t}\n+\tif (ret_data != data) {\n+\t\trte_free(key);\n+\t\tprintf(\"Error: Data returned was not the one expected\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\tif (0 != rte_tshash_del_key(handle, key, NULL)) {\n+\t\trte_free(key);\n+\t\tprintf(\"Error: Failed to delete key of %d bytes\\n\", ut_params.key_len);\n+\t\treturn -1;\n+\t}\n+\n+\tif (0 == rte_tshash_lookup(handle, key, &ret_data)) {\n+\t\trte_free(key);\n+\t\tprintf(\"Error: Found key after deleting key of %d bytes\\n\",\n+\t\t\t\tut_params.key_len);\n+\t\treturn -1;\n+\t}\n+\n+\trte_free(key);\n+\trte_tshash_reset(handle);\n+\n+\t/* Repeat test with precomputed hash values */\n+\tkey = generate_key(ut_params.key_len);\n+\thash_value = rte_rand() % MAX_KEYS;\n+\tdata = rte_rand();\n+\n+\tif (0 != rte_tshash_add_key_with_hash(handle, hash_value, key, data)) {\n+\t\trte_free(key);\n+\t\tprintf(\"Error: Failed to add key of %d bytes\\n\", ut_params.key_len);\n+\t\treturn -1;\n+\t}\n+\n+\tif (0 != rte_tshash_lookup_with_hash(handle, hash_value, key, &ret_data)) {\n+\t\trte_free(key);\n+\t\tprintf(\"Error: Failed to find key of %d bytes\\n\", ut_params.key_len);\n+\t\treturn -1;\n+\t}\n+\tif (ret_data != data) {\n+\t\trte_free(key);\n+\t\tprintf(\"Error: Data returned was not the one expected\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\tif (0 != rte_tshash_del_key_with_hash(handle, hash_value, key, NULL)) {\n+\t\trte_free(key);\n+\t\tprintf(\"Error: Failed to delete key of %d bytes\\n\", ut_params.key_len);\n+\t\treturn -1;\n+\t}\n+\n+\tif (0 == rte_tshash_lookup_with_hash(handle, hash_value, key, &ret_data)) {\n+\t\trte_free(key);\n+\t\tprintf(\"Error: Found key after deleting key of %d bytes\\n\",\n+\t\t\t\tut_params.key_len);\n+\t\treturn -1;\n+\t}\n+\n+\trte_free(key);\n+\trte_tshash_reset(handle);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Test add/lookup/delete functions with different hash functions (jhash and crc).\n+ */\n+static int\n+test_hash_different_hash_functions(void)\n+{\n+\tunsigned i;\n+\tstruct rte_tshash *handle;\n+\tuint64_t hash_value, ret_data, data;\n+\tvoid *key;\n+\n+\tfor (i = 0; i < RTE_DIM(hashtest_funcs); i++) {\n+\t\tut_params.max_entries = MAX_KEYS;\n+\t\tut_params.key_len = key_sizes[0];\n+\t\tut_params.socket_id = 0;\n+\t\tsprintf(ut_params.name, \"test_tshash_%s\", hash_func_strings[i]);\n+\t\te_args.max_load_factor = 0.5;\n+\t\te_args.rte_tshash_cmp_eq = NULL;\n+\t\te_args.malloc_mem = 0;\n+\t\te_args.hash_func = hashtest_funcs[i];\n+\n+\t\thandle = rte_tshash_find_existing(ut_params.name);\n+\t\tif (handle != NULL)\n+\t\t\trte_tshash_reset(handle);\n+\t\telse\n+\t\t\thandle = rte_tshash_create(ut_params.name, ut_params.max_entries,\n+\t\t\t\t\t\t\t\t\tut_params.key_len, ut_params.socket_id,\n+\t\t\t\t\t\t\t\t\t&e_args);\n+\t\tRETURN_IF_ERROR(handle == NULL, \"hash creation with %s hash function failed\",\n+\t\t\t\t\t\thash_func_strings[i]);\n+\n+\t\t/* Test with standard add/lookup/delete functions */\n+\t\tkey = generate_key(ut_params.key_len);\n+\t\tdata = rte_rand();\n+\n+\t\tif (0 != rte_tshash_add_key(handle, key, data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to add key of %d bytes\\n\", ut_params.key_len);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 != rte_tshash_lookup(handle, key, &ret_data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to find key of %d bytes\\n\", ut_params.key_len);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tif (ret_data != data) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Data returned was not the one expected\\n\");\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 != rte_tshash_del_key(handle, key, NULL)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to delete key of %d bytes\\n\", ut_params.key_len);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 == rte_tshash_lookup(handle, key, &ret_data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Found key after deleting key of %d bytes\\n\",\n+\t\t\t\t\tut_params.key_len);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\trte_free(key);\n+\t\trte_tshash_reset(handle);\n+\n+\t\t/* Repeat test with precomputed hash values */\n+\t\tkey = generate_key(ut_params.key_len);\n+\t\thash_value = rte_rand() % MAX_KEYS;\n+\t\tdata = rte_rand();\n+\n+\t\tif (0 != rte_tshash_add_key_with_hash(handle, hash_value, key, data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to add key of %d bytes\\n\", ut_params.key_len);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 != rte_tshash_lookup_with_hash(handle, hash_value, key, &ret_data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to find key of %d bytes\\n\", ut_params.key_len);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tif (ret_data != data) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Data returned was not the one expected\\n\");\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 != rte_tshash_del_key_with_hash(handle, hash_value, key, NULL)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Failed to delete key of %d bytes\\n\", ut_params.key_len);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (0 == rte_tshash_lookup_with_hash(handle, hash_value, key, &ret_data)) {\n+\t\t\trte_free(key);\n+\t\t\tprintf(\"Error: Found key after deleting key of %d bytes\\n\", ut_params.key_len);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\trte_free(key);\n+\t\trte_tshash_reset(handle);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Do all unit tests.\n+ */\n+static int\n+test_tshash(void)\n+{\n+\tprintf(\"\\n-- Test 0: Creating hash tables --\\n\");\n+\tif (create_hash_tables() < 0)\n+\t\treturn -1;\n+\tprintf(\"\\n-- Test1: add, lookup, delete --\\n\");\n+\tif (test_hash_add_lookup_delete() < 0)\n+\t\treturn -1;\n+\tprintf(\"\\n-- Test2: find existing hash table --\\n\");\n+\tif (test_hash_find_existing() < 0)\n+\t\treturn -1;\n+\tprintf(\"\\n-- Test3: add, lookup, delete misses --\\n\");\n+\tif (test_hash_add_lookup_delete_miss() < 0)\n+\t\treturn -1;\n+\tprintf(\"\\n-- Test4: add, lookup, update, delete misses --\\n\");\n+\tif (test_hash_add_lookup_update_delete_miss() < 0)\n+\t\treturn -1;\n+\tprintf(\"\\n-- Test5: lookup bulk --\\n\");\n+\tif (test_hash_burst() < 0)\n+\t\treturn -1;\n+\tprintf(\"\\n-- Test6: full bucket --\\n\");\n+\tif (test_hash_full_bucket() < 0)\n+\t\treturn -1;\n+\tprintf(\"\\n-- Test7: create hash table with bad parameters --\\n\");\n+\tif (test_hash_creation_with_bad_parameters() < 0)\n+\t\treturn -1;\n+\tprintf(\"\\n-- Test8: create hash table with malloc --\\n\");\n+\tif (test_hash_creation_with_malloc() < 0)\n+\t\treturn -1;\n+\tprintf(\"\\n-- Test9: add, lookup, delete with odd key size (not multiple of 16 --\\n\");\n+\tif (test_hash_odd_key_size() < 0)\n+\t\treturn -1;\n+\tprintf(\"\\n-- Test10: use different hash functions (jhash and CRC) --\\n\");\n+\tif (test_hash_different_hash_functions() < 0)\n+\t\treturn -1;\n+\n+\treturn 0;\n+}\n+#else /* RTE_LIBRTE_THREAD_SAFE_HASH_STATS */\n+\n+static int\n+test_tshash(void)\n+{\n+\tprintf(\"The Thread Safe Hash library stats are not included in this build\\n\");\n+\treturn 0;\n+}\n+\n+#endif /* RTE_LIBRTE_THREAD_SAFE_HASH_STATS */\n+\n+static struct test_command tshash_cmd = {\n+\t\t.command = \"thread_safe_hash_autotest\",\n+\t\t.callback = test_tshash,\n+};\n+REGISTER_TEST_COMMAND(tshash_cmd);\n+\ndiff --git a/app/test/test_tshash_multi_thread.c b/app/test/test_tshash_multi_thread.c\nnew file mode 100644\nindex 0000000..eacc5d5\n--- /dev/null\n+++ b/app/test/test_tshash_multi_thread.c\n@@ -0,0 +1,351 @@\n+/*-\n+ *   BSD LICENSE\n+ *\n+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.\n+ *   All rights reserved.\n+ *\n+ *   Redistribution and use in source and binary forms, with or without\n+ *   modification, are permitted provided that the following conditions\n+ *   are met:\n+ *\n+ *     * Redistributions of source code must retain the above copyright\n+ *       notice, this list of conditions and the following disclaimer.\n+ *     * Redistributions in binary form must reproduce the above copyright\n+ *       notice, this list of conditions and the following disclaimer in\n+ *       the documentation and/or other materials provided with the\n+ *       distribution.\n+ *     * Neither the name of Intel Corporation nor the names of its\n+ *       contributors may be used to endorse or promote products derived\n+ *       from this software without specific prior written permission.\n+ *\n+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ *   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+#include <stdio.h>\n+#include <stdint.h>\n+#include <inttypes.h>\n+#include <unistd.h>\n+#include <sys/queue.h>\n+\n+#include <cmdline_parse.h>\n+\n+#include <rte_common.h>\n+#include <rte_memory.h>\n+#include <rte_memzone.h>\n+#include <rte_per_lcore.h>\n+#include <rte_launch.h>\n+#include <rte_atomic.h>\n+#include <rte_tailq.h>\n+#include <rte_eal.h>\n+#include <rte_per_lcore.h>\n+#include <rte_lcore.h>\n+#include <rte_cycles.h>\n+#include <rte_random.h>\n+#include <rte_tshash.h>\n+\n+#include \"test.h\"\n+\n+#define TOTAL_ADDITIONS 300000\n+\n+#define MAX_KEYS 65536\n+#define KEYSIZE 16\n+#define MULTIPLIER 13\n+\n+/* Parameters used for hash table in unit test functions. Name set later. */\n+static struct hash_parameters {\n+\tchar name[RTE_TSHASH_NAMESIZE];\n+\tunsigned max_entries;\n+\tunsigned key_len;\n+\tuint8_t socket_id;\n+} ut_params;\n+\n+static struct ops_stats {\n+\tunsigned retries_add;\n+\tunsigned not_enough_mem_add;\n+\tunsigned already_exist_add;\n+\tunsigned no_free_slots_add;\n+\tunsigned success_add;\n+\n+\tunsigned retries_lookup;\n+\tunsigned errors_lookup;\n+\tunsigned misses_lookup;\n+\tunsigned success_lookup;\n+\n+\tunsigned num_additions;\n+\tunsigned num_deletions;\n+\tunsigned num_lookups;\n+} stats;\n+\n+static struct rte_tshash *hash_table;\n+\n+union hash_key {\n+\tuint64_t hashval;\n+\tchar key[KEYSIZE];\n+};\n+\n+unsigned num_sec_threads;\n+unsigned finished_additions;\n+\n+#define RETURN_IF_ERROR(cond, str, ...) do {\t\t\t\t\\\n+\tif (cond) {\t\t\t\t\t\t\t\\\n+\t\tprintf(\"ERROR line %d: \" str \"\\n\", __LINE__, ##__VA_ARGS__); \\\n+\t\treturn -1;\t\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\\\n+} while (0)\n+\n+static int\n+create_hash_table(void)\n+{\n+\tunsigned i;\n+\thash_table = NULL;\n+\tsprintf(ut_params.name, \"test_k%d_multi_thread\", KEYSIZE);\n+\tut_params.max_entries = MAX_KEYS;\n+\tut_params.key_len = KEYSIZE;\n+\tut_params.socket_id = rte_socket_id();\n+\n+\thash_table = rte_tshash_find_existing(ut_params.name);\n+\tif (hash_table != NULL)\n+\t\trte_tshash_reset(hash_table);\n+\telse\n+\t\thash_table = rte_tshash_create(ut_params.name,\n+\t\t\t\t\t\tut_params.max_entries, ut_params.key_len,\n+\t\t\t\t\t\tut_params.socket_id, NULL);\n+\tRETURN_IF_ERROR(hash_table == NULL, \"hash creation failed\");\n+\n+\tfor (i = 0; i < MAX_KEYS; i++) {\n+\t\tunion hash_key k = { .key = {0} };\n+\t\tk.hashval = i;\n+\t\tRETURN_IF_ERROR(rte_tshash_add_key_with_hash(hash_table,\n+\t\t\t\t\tk.hashval, (void *)k.key, k.hashval * MULTIPLIER) != 0,\n+\t\t\t\t\t\"Error adding entry number %u\\n\", i);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int\n+test_add_single(__attribute__((unused)) void *arg)\n+{\n+\tprintf(\"Core %u is performing additions\\n\", rte_lcore_id());\n+\twhile (stats.num_additions < TOTAL_ADDITIONS) {\n+\t\tunion hash_key k = { .key = {0} };\n+\t\tk.hashval = MAX_KEYS/2-1 + (rte_rand() % 2) * MAX_KEYS/2;\n+\t\tif (0 == rte_tshash_add_key_with_hash(hash_table, k.hashval,\n+\t\t\t\t\t\t\t\t\t\t\t(void *)k.key,\n+\t\t\t\t\t\t\t\t\t\t\tk.hashval * MULTIPLIER))\n+\t\t\tstats.num_additions++;\n+\t}\n+\n+\tprintf(\"Core %u has finished performing additions\\n\", rte_lcore_id());\n+\tfinished_additions = 1;\n+\treturn 0;\n+}\n+\n+static int\n+test_add_multi(__attribute__((unused)) void *arg)\n+{\n+\tint ret;\n+\tunsigned i;\n+\n+\tprintf(\"Core %u is performing additions\\n\", rte_lcore_id());\n+\n+\tfor (i = 0; i < MAX_KEYS; i++) {\n+\t\tunion hash_key k = { .key = {0} };\n+\t\tk.hashval = MAX_KEYS/2-1 + rte_rand() * MAX_KEYS/2;\n+\n+\t\tret = rte_tshash_add_key_with_hash(hash_table, k.hashval,\n+\t\t\t\t\t\t\t\t\t\t(void *)k.key, k.hashval * MULTIPLIER);\n+\n+\t\tswitch (ret) {\n+\t\tcase -EEXIST:\n+\t\t\t__sync_fetch_and_add(&stats.already_exist_add, 1);\n+\t\t\tbreak;\n+\t\tcase -EAGAIN:\n+\t\t\t__sync_fetch_and_add(&stats.retries_add, 1);\n+\t\t\tbreak;\n+\t\tcase -ENOMEM:\n+\t\t\t__sync_fetch_and_add(&stats.not_enough_mem_add, 1);\n+\t\t\tbreak;\n+\t\tcase -ENOSPC:\n+\t\t\t__sync_fetch_and_add(&stats.no_free_slots_add, 1);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\t__sync_fetch_and_add(&stats.success_add, 1);\n+\t\t\t__sync_fetch_and_add(&stats.num_additions, 1);\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+static int\n+test_lookup(__attribute__((unused)) void *arg)\n+{\n+\tint ret;\n+\n+\tprintf(\"Core %u is performing lookups\\n\", rte_lcore_id());\n+\twhile (finished_additions == 0) {\n+\t\tunion hash_key k = { .key = {0} };\n+\t\tuintptr_t retval = 0;\n+\t\tk.hashval = MAX_KEYS-1;\n+\n+\t\tret = rte_tshash_lookup_with_hash(hash_table, k.hashval,\n+\t\t\t\t\t\t\t\t\t\t(void *)k.key, &retval);\n+\t\t__sync_fetch_and_add(&stats.num_lookups, 1);\n+\t\tswitch (ret) {\n+\t\t/* Miss */\n+\t\tcase -1:\n+\t\t\t__sync_fetch_and_add(&stats.misses_lookup, 1);\n+\t\t\tbreak;\n+\t\t/* Out of sync */\n+\t\tcase -EAGAIN:\n+\t\t\t__sync_fetch_and_add(&stats.retries_lookup, 1);\n+\t\t\tbreak;\n+\t\t/* Hit */\n+\t\tdefault:\n+\t\t\t/* Corrupted data */\n+\t\t\tif (retval != k.hashval * MULTIPLIER) {\n+\t\t\t\t__sync_fetch_and_add(&stats.errors_lookup, 1);\n+\t\t\t\tprintf(\"Data corrupted!\\n!\");\n+\t\t\t\tprintf(\"Expected %\"PRIx64\" but got %\"PRIx64\"\\n\", k.hashval * MULTIPLIER, retval);\n+\t\t\t} else\n+\t\t\t\t__sync_fetch_and_add(&stats.success_lookup, 1);\n+\t\t}\n+\t}\n+\tprintf(\"Core %u has finished performing lookups\\n\", rte_lcore_id());\n+\n+\treturn 0;\n+}\n+\n+static int\n+test_delete(__attribute__((unused)) void *arg)\n+{\n+\tprintf(\"Core %u is performing deletions\\n\", rte_lcore_id());\n+\twhile (finished_additions == 0) {\n+\t\tunion hash_key k = { .key = {0} };\n+\t\tk.hashval = MAX_KEYS/2-1 + (rte_rand() % 2) * MAX_KEYS/2;\n+\t\trte_tshash_del_key_with_hash(hash_table, k.hashval, (void *)k.key, NULL);\n+\t}\n+\tprintf(\"Core %u has finished performing deletions\\n\", rte_lcore_id());\n+\n+\treturn 0;\n+}\n+\n+/* 1 thread adding entries, 1 thread deleting entries and rest looking up */\n+static int\n+test_multi_lookup(void) {\n+\tunsigned i, j;\n+\n+\tdo {\n+\t\tj = 0;\n+\t\tfinished_additions = 0;\n+\t\t/* Reset operation stats */\n+\t\tmemset(&stats, 0, sizeof(struct ops_stats));\n+\n+\t\tRTE_LCORE_FOREACH_SLAVE(i) {\n+\t\t\tswitch (j++) {\n+\t\t\tcase 0:\n+\t\t\t\trte_eal_remote_launch(test_add_single, NULL, i);\n+\t\t\t\tbreak;\n+\t\t\tcase 1:\n+\t\t\t\trte_eal_remote_launch(test_delete, NULL, i);\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\trte_eal_remote_launch(test_lookup, NULL, i);\n+\t\t\t}\n+\t\t}\n+\t\trte_eal_mp_wait_lcore();\n+\t}\n+\t/* There must be at least one attempt of collision (very likely) */\n+\twhile (stats.retries_lookup == 0);\n+\n+\tprintf(\"Retries on lookup op: %u/%u (%.7f%%)\\n\", stats.retries_lookup,\n+\t\t\tstats.num_lookups, ((double)stats.retries_lookup / stats.num_lookups * 100));\n+\tprintf(\"Errors on lookup op: %u/%u (%.7f%%)\\n\", stats.errors_lookup,\n+\t\t\tstats.num_lookups, ((double)stats.errors_lookup / stats.num_lookups * 100));\n+\tprintf(\"Misses on lookup op: %u/%u (%.7f%%)\\n\\n\", stats.misses_lookup,\n+\t\t\tstats.num_lookups, ((double)stats.misses_lookup / stats.num_lookups * 100));\n+\tprintf(\"Successes on lookup op: %u/%u (%.7f%%)\\n\\n\", stats.success_lookup,\n+\t\t\tstats.num_lookups, ((double)stats.success_lookup / stats.num_lookups * 100));\n+\n+\tRETURN_IF_ERROR(stats.errors_lookup != 0,\n+\t\t\t\t\t\"There was at least one error while looking up\");\n+\treturn 0;\n+\n+}\n+\n+/* All threads (minimum 2) adding entries */\n+static int\n+test_multi_add(void) {\n+\tunsigned i;\n+\n+\tdo {\n+\t\t/* Reset operation stats */\n+\t\trte_tshash_reset(hash_table);\n+\t\tmemset(&stats, 0, sizeof(struct ops_stats));\n+\n+\t\tRTE_LCORE_FOREACH_SLAVE(i) {\n+\t\t\trte_eal_remote_launch(test_add_multi, NULL, i);\n+\t\t}\n+\t\trte_eal_mp_wait_lcore();\n+\t}\n+\t/* There must be at least one attempt of collision (very likely) */\n+\twhile (stats.retries_add == 0);\n+\n+\tprintf(\"\\nRetries on add op: %u/%u (%.7f%%)\\n\", stats.retries_add,\n+\t\t\t(MAX_KEYS * num_sec_threads),\n+\t\t\t((double)stats.retries_add / (MAX_KEYS * num_sec_threads) * 100));\n+\tprintf(\"OOM on add op: %u/%u (%.7f%%)\\n\", stats.not_enough_mem_add,\n+\t\t\t(MAX_KEYS * num_sec_threads),\n+\t\t\t((double)stats.not_enough_mem_add / (MAX_KEYS * num_sec_threads) * 100));\n+\tprintf(\"Already existed on add op: %u/%u (%.7f%%)\\n\", stats.already_exist_add,\n+\t\t\t(MAX_KEYS * num_sec_threads),\n+\t\t\t((double)stats.already_exist_add / (MAX_KEYS * num_sec_threads) * 100));\n+\tprintf(\"No free slots on add op: %u/%u (%.7f%%)\\n\\n\", stats.no_free_slots_add,\n+\t\t\t(MAX_KEYS * num_sec_threads),\n+\t\t\t((double)stats.no_free_slots_add / (MAX_KEYS * num_sec_threads) * 100));\n+\tprintf(\"Successes on add op: %u/%u (%.7f%%)\\n\\n\", stats.success_add,\n+\t\t\t(MAX_KEYS * num_sec_threads),\n+\t\t\t((double)stats.success_add / (MAX_KEYS * num_sec_threads) * 100));\n+\n+\treturn 0;\n+}\n+\n+\n+static int\n+test_tshash_multi_thread(void)\n+{\n+\n+\t/* Master + 3 cores are needed (for add/lookup/delete) at least */\n+\tif (rte_lcore_count() < 4) {\n+\t\tprintf(\"At least 4 cores are needed\\n\");\n+\t\treturn -1;\n+\t}\n+\tnum_sec_threads = rte_lcore_count() - 1;\n+\n+\tif (create_hash_table() < 0)\n+\t\treturn -1;\n+\n+\tif (test_multi_lookup() < 0)\n+\t\treturn -1;\n+\n+\tif (test_multi_add() < 0)\n+\t\treturn -1;\n+\n+\treturn 0;\n+}\n+\n+static struct test_command tshash_multi_thread_cmd = {\n+\t\t.command = \"thread_safe_hash_multi_thread_autotest\",\n+\t\t.callback = test_tshash_multi_thread,\n+};\n+REGISTER_TEST_COMMAND(tshash_multi_thread_cmd);\ndiff --git a/app/test/test_tshash_perf.c b/app/test/test_tshash_perf.c\nnew file mode 100644\nindex 0000000..79235c1\n--- /dev/null\n+++ b/app/test/test_tshash_perf.c\n@@ -0,0 +1,631 @@\n+/*-\n+ *   BSD LICENSE\n+ *\n+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.\n+ *   All rights reserved.\n+ *\n+ *   Redistribution and use in source and binary forms, with or without\n+ *   modification, are permitted provided that the following conditions\n+ *   are met:\n+ *\n+ *     * Redistributions of source code must retain the above copyright\n+ *       notice, this list of conditions and the following disclaimer.\n+ *     * Redistributions in binary form must reproduce the above copyright\n+ *       notice, this list of conditions and the following disclaimer in\n+ *       the documentation and/or other materials provided with the\n+ *       distribution.\n+ *     * Neither the name of Intel Corporation nor the names of its\n+ *       contributors may be used to endorse or promote products derived\n+ *       from this software without specific prior written permission.\n+ *\n+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ *   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+#include <stdio.h>\n+#include <inttypes.h>\n+#include <rte_cycles.h>\n+#include <rte_tshash.h>\n+#include <rte_random.h>\n+#include <rte_string_fns.h>\n+#include \"test.h\"\n+\n+#define MAX_KEYS (1<<21)\n+#define MULTIPLIER 13\n+#define MAX_KEYSIZE 128\n+#define NUM_KEYSIZES 6\n+#define NUM_OPERATIONS 8\n+\n+#define NUM_LOOKUPS (1<<24)\n+#define BURST_SIZE 64\n+\n+union hash_key {\n+\tuint64_t hashval;\n+\tchar key[MAX_KEYSIZE];\n+};\n+\n+unsigned key_sizes[] = {16, 32, 48, 64, 96, 128};\n+struct rte_tshash *h[NUM_KEYSIZES];\n+uint64_t cycles[NUM_KEYSIZES][NUM_OPERATIONS][2];\n+\n+/*\n+ * Set up the table so every flow fits in first level bucket\n+ */\n+static int\n+setup_table(unsigned with_hash, unsigned table_index)\n+{\n+\tunsigned i;\n+\tchar name[RTE_TSHASH_NAMESIZE];\n+\tsprintf(name, \"test_phash%d\", key_sizes[table_index]);\n+\n+\th[table_index] = rte_tshash_find_existing(name);\n+\tif (h[table_index] == NULL) {\n+\t\th[table_index] = rte_tshash_create(name, MAX_KEYS,\n+\t\t\t\t\t\t\t\t\t\t\tkey_sizes[table_index],\n+\t\t\t\t\t\t\t\t\t\t\trte_socket_id(), NULL);\n+\t\tif (h[table_index] == NULL) {\n+\t\t\tprintf(\"Error creating table\\n\");\n+\t\t\treturn -1;\n+\t\t} else {\n+\t\t}\n+\t} else\n+\t\trte_tshash_reset(h[table_index]);\n+\n+\tconst uint64_t start_tsc = rte_rdtsc();\n+\n+\tfor (i = 0; i < MAX_KEYS; i++) {\n+\t\tunion hash_key k = { .key = {0} };\n+\t\tk.hashval = i;\n+\t\tif (with_hash) {\n+\t\t\tif (rte_tshash_add_key_with_hash(h[table_index], k.hashval,\n+\t\t\t\t\t\t\t\t\t\t\t(const void *)k.key,\n+\t\t\t\t\t\t\t\t\t\t\tk.hashval * MULTIPLIER) != 0) {\n+\t\t\t\t\tprintf(\"Error adding entry number %u\\n\", i);\n+\t\t\t\t\treturn -1;\n+\t\t\t} else\n+\t\t\t\tcontinue;\n+\t\t} else {\n+\t\t\tif (rte_tshash_add_key(h[table_index], (const void *)k.key,\n+\t\t\t\t\t\t\t\tk.hashval * MULTIPLIER != 0)) {\n+\t\t\t\tprintf(\"Error adding entry number %u\\n\", i);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tconst uint64_t end_tsc = rte_rdtsc();\n+\tconst uint64_t time_taken = end_tsc - start_tsc;\n+\tconst float seconds_taken = (float)time_taken/rte_get_tsc_hz();\n+\tcycles[table_index][0][with_hash] = time_taken/MAX_KEYS;\n+\n+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS\n+\tconst struct rte_tshash_stats *stats  = rte_tshash_get_stats(h[table_index]);\n+\tprintf(\"used_slots = %u, extra_buckets = %u\\n\", stats->used_slots,\n+\t\t\tstats->num_extra_buckets);\n+#endif\n+\n+\tprintf(\"\\n%\"PRIu64\" adds in %f seconds\\n\", (uint64_t)MAX_KEYS,\n+\t\t\tseconds_taken);\n+\tprintf(\"Average %\"PRIu64\" tsc ticks per add\\n\",\n+\t\t\tcycles[table_index][0][with_hash]);\n+\tprintf(\"Average %\"PRIu64\" adds per second\\n\", (MAX_KEYS * rte_get_tsc_hz())/time_taken);\n+\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Set up table so half flows are in second level buckets\n+ */\n+static int\n+setup_table_extd(unsigned with_hash, unsigned table_index)\n+{\n+\tunsigned i, j;\n+\tchar name[RTE_TSHASH_NAMESIZE];\n+\tsprintf(name, \"test_phash%d\", key_sizes[table_index]);\n+\n+\th[table_index] = rte_tshash_find_existing(name);\n+\tif (h[table_index] == NULL) {\n+\t\th[table_index] = rte_tshash_create(name, MAX_KEYS,\n+\t\t\t\t\t\tkey_sizes[table_index], rte_socket_id(), NULL);\n+\t\tif (h[table_index] == NULL) {\n+\t\t\tprintf(\"Error creating table\\n\");\n+\t\t\treturn -1;\n+\t\t} else {\n+\t\t}\n+\t} else\n+\t\trte_tshash_reset(h[table_index]);\n+\n+\tconst uint64_t start_tsc = rte_rdtsc();\n+\tif (with_hash) {\n+\t\tfor (i = 0; i < MAX_KEYS/8; i++) {\n+\t\t\tfor (j = 0; j < 8; j++) {\n+\t\t\t\tunion hash_key k = { .key = {0} };\n+\t\t\t\tk.hashval = i;\n+\t\t\t\tk.hashval |= (1lu<<(56+j));\n+\t\t\t\tk.key[key_sizes[table_index]-1] = j;\n+\t\t\t\tif (rte_tshash_add_key_with_hash(h[table_index], k.hashval,\n+\t\t\t\t\t\t\t\t\t\t\t\t(const void *)k.key,\n+\t\t\t\t\t\t\t\t\t\t\t\tk.hashval * MULTIPLIER) != 0) {\n+\t\t\t\t\tprintf(\"Error adding entry number %u\\n\", i*7+j);\n+\t\t\t\t\treturn -1;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t} else {\n+\t\tfor (i = 0; i < MAX_KEYS/8; i++) {\n+\t\t\tfor (j = 0; j < 8; j++) {\n+\t\t\t\tunion hash_key k = { .key = {0} };\n+\t\t\t\tk.hashval = i;\n+\t\t\t\tk.hashval |= (1lu<<(56+j));\n+\t\t\t\tk.key[key_sizes[table_index]-1] = j;\n+\t\t\t\tif (rte_tshash_add_key(h[table_index], (const void *)k.key,\n+\t\t\t\t\t\t\t\t\tk.hashval * MULTIPLIER) != 0) {\n+\t\t\t\t\tprintf(\"Error adding entry number %u\\n\", i*7+j);\n+\t\t\t\t\treturn -1;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t}\n+\tconst uint64_t end_tsc = rte_rdtsc();\n+\tconst uint64_t time_taken = end_tsc - start_tsc;\n+\tconst float seconds_taken = (float)time_taken/rte_get_tsc_hz();\n+\tcycles[table_index][1][with_hash] = time_taken/MAX_KEYS;\n+\n+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS\n+\tconst struct rte_tshash_stats *stats  = rte_tshash_get_stats(h[table_index]);\n+\tprintf(\"used_slots = %u, extra_buckets = %u\\n\", stats->used_slots,\n+\t\t\tstats->num_extra_buckets);\n+#endif\n+\n+\tprintf(\"\\n%\"PRIu64\" adds in %f seconds\\n\", (uint64_t) MAX_KEYS,\n+\t\t\tseconds_taken);\n+\tprintf(\"Average %\"PRIu64\" tsc ticks per add\\n\",\n+\t\t\tcycles[table_index][1][with_hash]);\n+\tprintf(\"Average %\"PRIu64\" adds per second\\n\",\n+\t\t\t(MAX_KEYS * rte_get_tsc_hz())/time_taken);\n+\n+\treturn 0;\n+}\n+\n+\n+static int\n+timed_lookups(unsigned with_hash, unsigned table_index)\n+{\n+\tuint64_t i;\n+\tuintptr_t retval;\n+\tconst uint64_t start_tsc = rte_rdtsc();\n+\n+\tfor (i = 0; i < NUM_LOOKUPS; i++) {\n+\t\tunion hash_key k = { .key = {0} };\n+\n+\t\tk.hashval = rte_rand() % MAX_KEYS;\n+\t\tif (with_hash) {\n+\t\t\tif (rte_tshash_lookup_with_hash(h[table_index], k.hashval,\n+\t\t\t\t\t\t\t\t\t\t(const void *)k.key, &retval) < 0) {\n+\t\t\t\tprintf(\"Error lookup up hash key %\"PRIu64\"\\n\", k.hashval);\n+\t\t\t\treturn -1;\n+\t\t\t} else\n+\t\t\t\tcontinue;\n+\t\t} else {\n+\t\t\tif (rte_tshash_lookup(h[table_index],\n+\t\t\t\t\t\t\t\t(const void *)k.key, &retval) < 0) {\n+\t\t\t\tprintf(\"Error lookup up hash key %\"PRIu64\"\\n\", k.hashval);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\t}\n+\tconst uint64_t end_tsc = rte_rdtsc();\n+\tconst uint64_t time_taken = end_tsc - start_tsc;\n+\tconst float seconds_taken = (float)time_taken/rte_get_tsc_hz();\n+\tcycles[table_index][2][with_hash] = time_taken/NUM_LOOKUPS;\n+\n+\tprintf(\"%\"PRIu64\" lookups in %f seconds\\n\", (uint64_t) NUM_LOOKUPS,\n+\t\t\tseconds_taken);\n+\tprintf(\"Average %\"PRIu64\" tsc ticks per lookup\\n\",\n+\t\t\tcycles[table_index][2][with_hash]);\n+\tprintf(\"Average %\"PRIu64\" lookups per second\\n\",\n+\t\t\t(NUM_LOOKUPS * rte_get_tsc_hz())/time_taken);\n+\treturn 0;\n+}\n+\n+static int\n+timed_lookups_extd(unsigned with_hash, unsigned table_index)\n+{\n+\tuint64_t i;\n+\tuintptr_t retval;\n+\tconst uint64_t start_tsc = rte_rdtsc();\n+\tfor (i = 0; i < NUM_LOOKUPS; i++) {\n+\t\tunion hash_key k = { .key = {0} };\n+\t\tunsigned tmp = rte_rand() % 8;\n+\t\tk.hashval = rte_rand() % MAX_KEYS/8;\n+\t\tk.hashval |= (1llu << (tmp+56));\n+\t\tk.key[key_sizes[table_index]-1] = tmp;\n+\t\tif (with_hash) {\n+\t\t\tif (rte_tshash_lookup_with_hash(h[table_index], k.hashval,\n+\t\t\t\t\t\t\t\t\t\t(const void *)k.key, &retval) < 0) {\n+\t\t\t\tprintf(\"Error lookup up hash key %\"PRIu64\"\\n\", k.hashval);\n+\t\t\t\treturn -1;\n+\t\t\t} else\n+\t\t\t\tcontinue;\n+\t\t} else {\n+\t\t\tif (rte_tshash_lookup(h[table_index], (const void *)k.key,\n+\t\t\t\t\t\t\t\t&retval) < 0) {\n+\t\t\t\tprintf(\"Error lookup up hash key %\"PRIu64\"\\n\", k.hashval);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tconst uint64_t end_tsc = rte_rdtsc();\n+\tconst uint64_t time_taken = end_tsc - start_tsc;\n+\tconst float seconds_taken = (float)time_taken/rte_get_tsc_hz();\n+\tcycles[table_index][3][with_hash] = time_taken/NUM_LOOKUPS;\n+\n+\tprintf(\"%\"PRIu64\" lookups in %f seconds\\n\", (uint64_t)NUM_LOOKUPS,\n+\t\t\tseconds_taken);\n+\tprintf(\"Average %\"PRIu64\" tsc ticks per lookup\\n\",\n+\t\t\tcycles[table_index][3][with_hash]);\n+\tprintf(\"Average %\"PRIu64\" lookups per second\\n\",\n+\t\t\t(NUM_LOOKUPS * rte_get_tsc_hz())/time_taken);\n+\treturn 0;\n+}\n+\n+static int\n+timed_lookups_multi(unsigned with_hash,  unsigned table_index)\n+{\n+\tuint64_t i;\n+#if BURST_SIZE == 64\n+\t\tconst uint64_t lookup_mask = 0xffffffffffffffff;\n+#else\n+\t\tconst uint64_t lookup_mask = (1llu << BURST_SIZE) - 1;\n+#endif\n+\tunion hash_key ks[BURST_SIZE] = { { .key = {0} } };\n+\tconst void *keys[BURST_SIZE];\n+\tuint64_t *h_ptrs[BURST_SIZE];\n+\n+\tfor (i = 0; i < BURST_SIZE; i++) {\n+\t\tkeys[i] = (void *)ks[i].key;\n+\t\th_ptrs[i] = &ks[i].hashval;\n+\t}\n+\n+\tconst uint64_t start_tsc = rte_rdtsc();\n+\tfor (i = 0; i < NUM_LOOKUPS/BURST_SIZE; i++) {\n+\t\tunsigned j;\n+\t\tuint64_t hashes[BURST_SIZE];\n+\t\tuintptr_t retvals[BURST_SIZE];\n+\t\tint hitcount;\n+\t\tuint64_t hitmask;\n+\n+\t\tfor (j = 0; j < BURST_SIZE; j++) {\n+\t\t\thashes[j] = rte_rand() % MAX_KEYS;\n+\t\t\tks[j].hashval = hashes[j];\n+\t\t}\n+\t\tif (with_hash) {\n+\t\t\thitcount = rte_tshash_lookup_bulk_with_hash(h[table_index], lookup_mask,\n+\t\t\t\t\t\t\t\t\t\t\t\t\t\th_ptrs, keys, retvals,\n+\t\t\t\t\t\t\t\t\t\t\t\t\t\t&hitmask);\n+\t\t\tif (unlikely(hitcount != BURST_SIZE)) {\n+\t\t\t\t\tprintf(\"Error lookup up hash keys. Expected retval = %u, got \"\n+\t\t\t\t\t\t\t\"%d\\n\", BURST_SIZE, hitcount);\n+\t\t\t\t\tprintf(\"Hit mask = %\"PRIx64\"\\n\", hitmask);\n+\t\t\t\t\treturn -1;\n+\t\t\t} else\n+\t\t\t\tcontinue;\n+\t\t} else {\n+\t\t\thitcount = rte_tshash_lookup_bulk(h[table_index], lookup_mask,\n+\t\t\t\t\t\t\t\t\t\t\t\tkeys, retvals, &hitmask);\n+\t\t\tif (unlikely(hitcount != BURST_SIZE)) {\n+\t\t\t\tprintf(\"Error lookup up hash keys. Expected retval = %u, got \"\n+\t\t\t\t\t\t\"%d\\n\", BURST_SIZE, hitcount);\n+\t\t\t\tprintf(\"Hit mask = %\"PRIx64\"\\n\", hitmask);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tconst uint64_t end_tsc = rte_rdtsc();\n+\tconst uint64_t time_taken = end_tsc - start_tsc;\n+\tconst float seconds_taken = (float)time_taken/rte_get_tsc_hz();\n+\tcycles[table_index][4][with_hash] = time_taken/NUM_LOOKUPS;\n+\n+\tprintf(\"%\"PRIu64\" lookups in %f seconds\\n\", (uint64_t)NUM_LOOKUPS,\n+\t\t\tseconds_taken);\n+\tprintf(\"Average %\"PRIu64\" tsc ticks per lookup\\n\",\n+\t\t\tcycles[table_index][4][with_hash]);\n+\tprintf(\"Average %\"PRIu64\" lookups per second\\n\",\n+\t\t\t(NUM_LOOKUPS * rte_get_tsc_hz())/time_taken);\n+\treturn 0;\n+}\n+\n+static int\n+timed_lookups_multi_extd(unsigned with_hash,  unsigned table_index)\n+{\n+\tuint64_t i;\n+#if BURST_SIZE == 64\n+\t\tconst uint64_t lookup_mask = 0xffffffffffffffff;\n+#else\n+\t\tconst uint64_t lookup_mask = (1llu << BURST_SIZE) - 1;\n+#endif\n+\tunion hash_key ks[BURST_SIZE] = { { .key = {0} } };\n+\tconst void *keys[BURST_SIZE];\n+\tuint64_t *h_ptrs[BURST_SIZE];\n+\n+\tfor (i = 0; i < BURST_SIZE; i++) {\n+\t\tkeys[i] = (void *)ks[i].key;\n+\t\th_ptrs[i] = &ks[i].hashval;\n+\t}\n+\n+\tconst uint64_t start_tsc = rte_rdtsc();\n+\tfor (i = 0; i < NUM_LOOKUPS/BURST_SIZE; i++) {\n+\t\tunsigned j;\n+\t\tuint64_t hashes[BURST_SIZE];\n+\t\tuintptr_t retvals[BURST_SIZE];\n+\t\tint hitcount;\n+\t\tuint64_t hitmask;\n+\n+\t\tfor (j = 0; j < BURST_SIZE; j++) {\n+\t\t\tunsigned tmp = rte_rand() % 8;\n+\t\t\thashes[j] = rte_rand() % MAX_KEYS/8;\n+\t\t\thashes[j] |= (1lu << (56 + tmp));\n+\t\t\tks[j].hashval = hashes[j];\n+\t\t\tks[j].key[key_sizes[table_index] - 1] = tmp;\n+\t\t}\n+\t\tif (with_hash) {\n+\t\t\thitcount = rte_tshash_lookup_bulk_with_hash(h[table_index], lookup_mask,\n+\t\t\t\t\t\t\t\t\t\t\t\t\t\th_ptrs, keys, retvals,\n+\t\t\t\t\t\t\t\t\t\t\t\t\t\t&hitmask);\n+\t\t\tif (unlikely(hitcount != BURST_SIZE)) {\n+\t\t\t\tprintf(\"Error lookup up hash keys. Expected retval = %u, got \"\n+\t\t\t\t\t\t\"%d\\n\", BURST_SIZE, hitcount);\n+\t\t\t\tprintf(\"Hit mask = %\"PRIx64\"\\n\", hitmask);\n+\t\t\t\treturn -1;\n+\t\t\t} else\n+\t\t\t\tcontinue;\n+\t\t} else {\n+\t\t\thitcount = rte_tshash_lookup_bulk(h[table_index], lookup_mask,\n+\t\t\t\t\t\t\t\tkeys, retvals, &hitmask);\n+\t\t\tif (unlikely(hitcount != BURST_SIZE)) {\n+\t\t\t\tprintf(\"Error lookup up hash keys. Expected retval = %u, got \"\n+\t\t\t\t\t\t\"%d\\n\", BURST_SIZE, hitcount);\n+\t\t\t\tprintf(\"Hit mask = %\"PRIx64\"\\n\", hitmask);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tconst uint64_t end_tsc = rte_rdtsc();\n+\tconst uint64_t time_taken = end_tsc - start_tsc;\n+\tconst float seconds_taken = (float)time_taken/rte_get_tsc_hz();\n+\tcycles[table_index][5][with_hash] = time_taken/NUM_LOOKUPS;\n+\n+\tprintf(\"%\"PRIu64\" lookups in %f seconds\\n\", (uint64_t) NUM_LOOKUPS,\n+\t\t\tseconds_taken);\n+\tprintf(\"Average %\"PRIu64\" tsc ticks per lookup\\n\",\n+\t\t\tcycles[table_index][5][with_hash]);\n+\tprintf(\"Average %\"PRIu64\" lookups per second\\n\",\n+\t\t\t(NUM_LOOKUPS * rte_get_tsc_hz())/time_taken);\n+\treturn 0;\n+}\n+\n+static int\n+timed_deletes(unsigned with_hash, unsigned table_index)\n+{\n+\tuint64_t i;\n+\tconst uint64_t start_tsc = rte_rdtsc();\n+\tfor (i = 0; i < MAX_KEYS; i++) {\n+\t\tunion hash_key k = { .key = {0} };\n+\n+\t\tk.hashval = i;\n+\t\tif (with_hash) {\n+\t\t\tif (rte_tshash_del_key_with_hash(h[table_index], k.hashval,\n+\t\t\t\t\t\t\t\t\t\t\t(const void *)k.key, NULL) < 0) {\n+\t\t\t\tprintf(\"Error deleting hash key %\"PRIu64\"\\n\", k.hashval);\n+\t\t\t\treturn -1;\n+\t\t\t} else\n+\t\t\t\tcontinue;\n+\t\t} else {\n+\t\t\tif (rte_tshash_del_key(h[table_index],\n+\t\t\t\t\t\t\t\t(const void *)k.key, NULL) < 0) {\n+\t\t\t\tprintf(\"Error deleting hash key %\"PRIu64\"\\n\", k.hashval);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\n+\t}\n+\tconst uint64_t end_tsc = rte_rdtsc();\n+\tconst uint64_t time_taken = end_tsc - start_tsc;\n+\tconst float seconds_taken = (float)time_taken/rte_get_tsc_hz();\n+\tcycles[table_index][6][with_hash] = time_taken/MAX_KEYS;\n+\n+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS\n+\tconst struct rte_tshash_stats *stats  = rte_tshash_get_stats(h[table_index]);\n+\tprintf(\"used_slots = %u, extra_buckets = %u\\n\", stats->used_slots,\n+\t\t\tstats->num_extra_buckets);\n+#endif\n+\n+\tprintf(\"\\n%\"PRIu64\" deletions in %f seconds\\n\", (uint64_t) MAX_KEYS,\n+\t\t\tseconds_taken);\n+\tprintf(\"Average %\"PRIu64\" tsc ticks per deletion\\n\",\n+\t\t\tcycles[table_index][6][with_hash]);\n+\tprintf(\"Average %\"PRIu64\" deletions per second\\n\",\n+\t\t\t(MAX_KEYS * rte_get_tsc_hz())/time_taken);\n+\n+\treturn 0;\n+}\n+\n+static int\n+timed_deletes_extd(unsigned with_hash, unsigned table_index)\n+{\n+\tuint64_t i, j;\n+\n+\tconst uint64_t start_tsc = rte_rdtsc();\n+\tfor (i = 0; i < MAX_KEYS/8; i++) {\n+\t\tfor (j = 0; j < 8; j++) {\n+\t\t\tunion hash_key k = { .key = {0} };\n+\t\t\tk.hashval = i;\n+\t\t\tk.hashval |= (1lu<<(56+j));\n+\t\t\tk.key[key_sizes[table_index]-1] = j;\n+\t\t\tif (with_hash) {\n+\t\t\t\tif (rte_tshash_del_key_with_hash(h[table_index], k.hashval,\n+\t\t\t\t\t\t\t\t\t\t\t\t(const void *)k.key, NULL)\n+\t\t\t\t\t\t!= 0) {\n+\t\t\t\t\tprintf(\"Error deleting hash key %\"PRIu64\"\\n\", k.hashval);\n+\t\t\t\t\treturn -1;\n+\t\t\t\t} else\n+\t\t\t\t\tcontinue;\n+\t\t\t} else {\n+\t\t\t\tif (rte_tshash_del_key(h[table_index], (const void *)k.key,\n+\t\t\t\t\t\t\t\t\t\tNULL) != 0) {\n+\t\t\t\t\tprintf(\"Error deleting hash key %\"PRIu64\"\\n\", k.hashval);\n+\t\t\t\t\treturn -1;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t}\n+\tconst uint64_t end_tsc = rte_rdtsc();\n+\tconst uint64_t time_taken = end_tsc - start_tsc;\n+\tconst float seconds_taken = (float)time_taken/rte_get_tsc_hz();\n+\tcycles[table_index][7][with_hash] = time_taken/MAX_KEYS;\n+\n+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS\n+\tconst struct rte_tshash_stats *stats  = rte_tshash_get_stats(h[table_index]);\n+\tprintf(\"used_slots = %u, extra_buckets = %u\\n\", stats->used_slots,\n+\t\t\tstats->num_extra_buckets);\n+#endif\n+\n+\tprintf(\"\\n%\"PRIu64\" deletions in %f seconds\\n\", (uint64_t)MAX_KEYS,\n+\t\t\tseconds_taken);\n+\tprintf(\"Average %\"PRIu64\" tsc ticks per deletion\\n\",\n+\t\t\tcycles[table_index][7][with_hash]);\n+\tprintf(\"Average %\"PRIu64\" deletions per second\\n\", (MAX_KEYS * rte_get_tsc_hz())/time_taken);\n+\n+\treturn 0;\n+}\n+\n+static int\n+reset_table(unsigned table_index) {\n+\n+\trte_tshash_reset(h[table_index]);\n+\treturn 0;\n+}\n+\n+static int\n+test_tshash_perf(void)\n+{\n+\tunsigned i, j;\n+\n+\tfor (i = 0; i < NUM_KEYSIZES; i++) {\n+\t\tprintf(\"\\n------ KEY SIZE = %u ----------\\n\\n\", key_sizes[i]);\n+\t\tprintf(\"\\n ----- WITH PRECALCULATED HASH VALUES -----\\n\\n\");\n+\t\tprintf(\"Table with only first-level buckets used\\n\");\n+\t\tif (setup_table(1, i) < 0)\n+\t\t\treturn -1;\n+\n+\t\tprintf(\"\\nTimed lookups\\n\");\n+\t\tprintf(\"------------------\\n\");\n+\t\tif (timed_lookups(1, i) < 0)\n+\t\t\treturn -1;\n+\n+\t\tprintf(\"\\nTimed lookups multi \\n\");\n+\t\tprintf(\"------------------\\n\");\n+\t\tif (timed_lookups_multi(1, i) < 0)\n+\t\t\treturn -1;\n+\n+\t\tprintf(\"\\nTimed deletions \\n\");\n+\t\tprintf(\"------------------\\n\");\n+\t\tif (timed_deletes(1, i) < 0)\n+\t\t\treturn -1;\n+\n+\t\tprintf(\"\\nResetting table to use extra buckets\\n\");\n+\t\tif (setup_table_extd(1, i) < 0)\n+\t\t\treturn -1;\n+\n+\t\tprintf(\"\\nTimed lookups extd\\n\");\n+\t\tprintf(\"------------------\\n\");\n+\t\tif (timed_lookups_extd(1, i) < 0)\n+\t\t\treturn -1;\n+\n+\t\tprintf(\"\\nTimed lookups multi extd\\n\");\n+\t\tprintf(\"------------------\\n\");\n+\t\tif (timed_lookups_multi_extd(1, i) < 0)\n+\t\t\treturn -1;\n+\n+\t\tprintf(\"\\nTimed deletions extd\\n\");\n+\t\tprintf(\"------------------\\n\");\n+\t\tif (timed_deletes_extd(1, i) < 0)\n+\t\t\treturn -1;\n+\n+\t\treset_table(i);\n+\n+\t\tprintf(\"\\n ----- WITH JUST KEYS -----\\n\\n\");\n+\n+\t\tprintf(\"Table with only first-level buckets used\\n\");\n+\t\tif (setup_table(0, i) < 0)\n+\t\t\treturn -1;\n+\n+\t\tprintf(\"\\nTimed lookups\\n\");\n+\t\tprintf(\"------------------\\n\");\n+\t\tif (timed_lookups(0, i) < 0)\n+\t\t\treturn -1;\n+\n+\t\tprintf(\"\\nTimed lookups multi \\n\");\n+\t\tprintf(\"------------------\\n\");\n+\t\tif (timed_lookups_multi(0, i) < 0)\n+\t\t\treturn -1;\n+\n+\t\tprintf(\"\\nTimed deletions \\n\");\n+\t\t\tprintf(\"------------------\\n\");\n+\t\tif (timed_deletes(0, i) < 0)\n+\t\t\treturn -1;\n+\n+\t\treset_table(i);\n+\n+\t}\n+\tprintf(\"\\nResults (in CPU cycles/operation)\\n\");\n+\tprintf(\"-----------------------------------\\n\");\n+\tprintf(\"\\nWith just keys (only 1st level buckets)\\n\");\n+\tprintf(\"\\n%-18s%-18s%-18s%-18s%-18s\\n\",\n+\t\t\t\"Keysize\", \"Add\", \"Lookup\", \"Lookup_bulk\", \"Delete\");\n+\tfor (i = 0; i < NUM_KEYSIZES; i++) {\n+\t\tprintf(\"%-18d\", key_sizes[i]);\n+\t\tfor (j = 0; j < 8; j = j+2)\n+\t\t\tprintf(\"%-18\"PRIu64, cycles[i][j][0]);\n+\t\tprintf(\"\\n\");\n+\t}\n+\n+\tprintf(\"\\nWith precalculated hash (only 1st level buckets)\\n\");\n+\tprintf(\"\\n%-18s%-18s%-18s%-18s%-18s\\n\",\n+\t\t\t\"Keysize\", \"Add\", \"Lookup\", \"Lookup_bulk\", \"Delete\");\n+\tfor (i = 0; i < NUM_KEYSIZES; i++) {\n+\t\tprintf(\"%-18d\", key_sizes[i]);\n+\t\tfor (j = 0; j < 8; j = j+2)\n+\t\t\tprintf(\"%-18\"PRIu64, cycles[i][j][1]);\n+\t\tprintf(\"\\n\");\n+\t}\n+\tprintf(\"\\nWith precalculated hash (with extended buckets)\\n\");\n+\tprintf(\"\\n%-18s%-18s%-18s%-18s%-18s\\n\",\n+\t\t\t\"Keysize\", \"Add\", \"Lookup\", \"Lookup_bulk\", \"Delete\");\n+\tfor (i = 0; i < NUM_KEYSIZES; i++) {\n+\t\tprintf(\"%-18d\", key_sizes[i]);\n+\t\tfor (j = 1; j < 8; j = j+2)\n+\t\t\tprintf(\"%-18\"PRIu64, cycles[i][j][1]);\n+\t\tprintf(\"\\n\");\n+\t}\n+\treturn 0;\n+}\n+\n+static struct test_command tshash_perf_cmd = {\n+\t\t.command = \"thread_safe_hash_perf_autotest\",\n+\t\t.callback = test_tshash_perf,\n+};\n+REGISTER_TEST_COMMAND(tshash_perf_cmd);\n",
    "prefixes": [
        "dpdk-dev",
        "3/3"
    ]
}