Added 3 new unit tests:

- Functional unit test: Tests creation and handling of
  a hash table with a single thread.

- Performance unit tests: Benchmark hash operations
  add/delete/lookup, returning number of CPU cycles/operation.

- Multi thread unit tests: Checks there is no data corruption
  due to multiple threads working on the same hash table.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch at intel.com>
---
 app/test/Makefile                   |    4 +
 app/test/test_tshash_func.c         | 1117 +++++++++++++++++++++++++++++++++++
 app/test/test_tshash_multi_thread.c |  351 +++++++++++
 app/test/test_tshash_perf.c         |  631 ++++++++++++++++++++
 4 files changed, 2103 insertions(+), 0 deletions(-)
 create mode 100644 app/test/test_tshash_func.c
 create mode 100644 app/test/test_tshash_multi_thread.c
 create mode 100644 app/test/test_tshash_perf.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 37a3772..71dd7c2 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -83,6 +83,10 @@ SRCS-y += test_memcpy_perf.c
 SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash.c
 SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_perf.c

+SRCS-$(CONFIG_RTE_LIBRTE_THREAD_SAFE_HASH) += test_tshash_func.c
+SRCS-$(CONFIG_RTE_LIBRTE_THREAD_SAFE_HASH) += test_tshash_perf.c
+SRCS-$(CONFIG_RTE_LIBRTE_THREAD_SAFE_HASH) += test_tshash_multi_thread.c
+
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm.c
 SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6.c

diff --git a/app/test/test_tshash_func.c b/app/test/test_tshash_func.c
new file mode 100644
index 0000000..7ec2e12
--- /dev/null
+++ b/app/test/test_tshash_func.c
@@ -0,0 +1,1117 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/queue.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_cycles.h>
+#include <rte_random.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_tailq.h>
+#include <rte_eal.h>
+#include <rte_ip.h>
+#include <rte_string_fns.h>
+#include <cmdline_parse.h>
+
+#include <rte_tshash.h>
+#include <rte_hash.h>
+#ifdef RTE_MACHINE_CPUFLAG_SSE4_2
+#include <rte_hash_crc.h>
+#endif
+#include <rte_jhash.h>
+
+#include "test.h"
+
+#define MAX_KEYS 1024
+#define NUM_KEYS 64
+
+#if defined RTE_LIBRTE_THREAD_SAFE_HASH_STATS
+
+#ifdef RTE_MACHINE_CPUFLAG_SSE4_2
+static rte_hash_function hashtest_funcs[] = {rte_jhash, rte_hash_crc};
+static const char * const hash_func_strings[] = {"jhash", "crc"};
+#else
+static rte_hash_function hashtest_funcs[] = {rte_jhash};
+static const char * const hash_func_strings[] = {"jhash"};
+#endif
+
+
+/*
+ * Check condition and return an error if true.
+ */
+#define RETURN_IF_ERROR(cond, str, ...) do {                           \
+       if (cond) {                                                     \
+               printf("ERROR line %d: " str "\n", __LINE__, ##__VA_ARGS__); \
+               return -1;                                              \
+       }                                                               \
+} while (0)
+
+/*
+ * Hash function that always returns the same value, to easily test what
+ * happens when a bucket is full.
+ */
+static uint32_t pseudo_hash(__attribute__((unused)) const void *keys,
+                           __attribute__((unused)) uint32_t key_len,
+                           __attribute__((unused)) uint32_t init_val)
+{
+       return 3;
+}
+
+static void *generate_key(uint8_t num_bytes)
+{
+       char *key = rte_zmalloc(NULL, num_bytes, 0);
+       unsigned i;
+
+       for (i = 0; i < num_bytes; i++)
+               key[i] = rand() % 255;
+
+       return (void *)key;
+}
+
+/* Parameters used for hash table in unit test functions. Name set later. */
+static struct rte_tshash_parameters {
+       char name[RTE_TSHASH_NAMESIZE];
+       unsigned max_entries;
+       unsigned key_len;
+       uint8_t socket_id;
+
+} ut_params;
+
+static uint8_t key_sizes[] = {16, 32, 48, 64, 96, 128};
+static struct rte_tshash *hash_tables[RTE_DIM(key_sizes)];
+static uint8_t burst_sizes[] = {13, 16, 32, 64};
+
+static struct rte_tshash_extra_args e_args = {
+               .malloc_mem = 0,
+               .max_load_factor = 0,
+               .rte_tshash_cmp_eq = NULL,
+               .hash_func = NULL
+
+};
+
+static int
+create_hash_tables(void)
+{
+       unsigned i;
+       for (i = 0; i < RTE_DIM(key_sizes); i++) {
+               hash_tables[i] = NULL;
+               sprintf(ut_params.name, "test_k%d", key_sizes[i]);
+               ut_params.max_entries = MAX_KEYS;
+               ut_params.key_len = key_sizes[i];
+               ut_params.socket_id = rte_socket_id();
+               hash_tables[i] = rte_tshash_find_existing(ut_params.name);
+               if (hash_tables[i] != NULL)
+                       rte_tshash_reset(hash_tables[i]);
+               else
+                       hash_tables[i] = rte_tshash_create(ut_params.name,
+                                                                       
ut_params.max_entries, ut_params.key_len,
+                                                                       
ut_params.socket_id, NULL);
+               RETURN_IF_ERROR(hash_tables[i] == NULL, "hash creation failed");
+       }
+       return 0;
+}
+/*
+ * Basic sequence of operations for a single key:
+ *     - add
+ *     - lookup (hit)
+ *     - delete
+ *     - lookup (miss)
+ */
+static int
+test_hash_add_lookup_delete(void)
+{
+       /* Test with standard add/lookup/delete functions */
+       unsigned i;
+       uint64_t hash_value, ret_data, data;
+       void *key;
+
+       for (i = 0; i < RTE_DIM(key_sizes); i++) {
+               key = generate_key(key_sizes[i]);
+               data = rte_rand();
+
+               if (0 != rte_tshash_add_key(hash_tables[i], key, data)) {
+                       rte_free(key);
+                       printf("Error: Failed to add key of %d bytes\n", 
key_sizes[i]);
+                       return -1;
+               }
+
+               if (0 != rte_tshash_lookup(hash_tables[i], key, &ret_data)) {
+                       rte_free(key);
+                       printf("Error: Failed to find key of %d bytes\n", 
key_sizes[i]);
+                       return -1;
+               }
+               if (ret_data != data) {
+                       rte_free(key);
+                       printf("Error: Data returned was not the one 
expected\n");
+                       return -1;
+               }
+
+               if (0 != rte_tshash_del_key(hash_tables[i], key, NULL)) {
+                       rte_free(key);
+                       printf("Error: Failed to delete key of %d bytes\n", 
key_sizes[i]);
+                       return -1;
+               }
+
+               if (0 == rte_tshash_lookup(hash_tables[i], key, &ret_data)) {
+                       rte_free(key);
+                       printf("Error: Found key after deleting key of %d 
bytes\n", key_sizes[i]);
+                       return -1;
+               }
+
+               rte_free(key);
+               rte_tshash_reset(hash_tables[i]);
+       }
+
+       /* Repeat test with precomputed hash values */
+       for (i = 0; i < RTE_DIM(key_sizes); i++) {
+               key = generate_key(key_sizes[i]);
+               hash_value = rte_rand() % MAX_KEYS;
+               data = rte_rand();
+
+               if (0 != rte_tshash_add_key_with_hash(hash_tables[i], 
hash_value, key, data)) {
+                       rte_free(key);
+                       printf("Error: Failed to add key of %d bytes\n", 
key_sizes[i]);
+                       return -1;
+               }
+
+               if (0 != rte_tshash_lookup_with_hash(hash_tables[i], 
hash_value, key, &ret_data)) {
+                       rte_free(key);
+                       printf("Error: Failed to find key of %d bytes\n", 
key_sizes[i]);
+                       return -1;
+               }
+               if (ret_data != data) {
+                       rte_free(key);
+                       printf("Error: Data returned was not the one 
expected\n");
+                       return -1;
+               }
+
+               if (0 != rte_tshash_del_key_with_hash(hash_tables[i], 
hash_value, key, NULL)) {
+                       rte_free(key);
+                       printf("Error: Failed to delete key of %d bytes\n", 
key_sizes[i]);
+                       return -1;
+               }
+
+               if (0 == rte_tshash_lookup_with_hash(hash_tables[i], 
hash_value, key, &ret_data)) {
+                       rte_free(key);
+                       printf("Error: Found key after deleting key of %d 
bytes\n", key_sizes[i]);
+                       return -1;
+               }
+
+               rte_free(key);
+               rte_tshash_reset(hash_tables[i]);
+       }
+
+       return 0;
+}
+
+/*
+ * Sequence of operations for a single key:
+ *     - delete: miss
+ *     - add
+ *     - lookup: hit
+ *     - add: miss
+ *     - lookup: hit
+ *     - delete: hit
+ *     - delete: miss
+ *     - lookup: miss
+ */
+static int
+test_hash_add_lookup_delete_miss(void)
+{
+       unsigned i;
+       uint64_t hash_value, ret_data, data;
+       void *key;
+
+       for (i = 0; i < RTE_DIM(key_sizes); i++) {
+               key = generate_key(key_sizes[i]);
+               hash_value = rte_rand() % MAX_KEYS;
+               data = rte_rand();
+
+               if (0 == rte_tshash_del_key_with_hash(hash_tables[i],
+                                                                               
        hash_value, key, NULL)) {
+                       rte_free(key);
+                       printf("Error: Deleted key of %d bytes that should not 
exist\n", key_sizes[i]);
+                       return -1;
+               }
+
+               if (0 != rte_tshash_add_key_with_hash(hash_tables[i],
+                                                                               
        hash_value, key, data)) {
+                       rte_free(key);
+                       printf("Error: Failed to add key of %d bytes\n", 
key_sizes[i]);
+                       return -1;
+               }
+
+               if (0 != rte_tshash_lookup_with_hash(hash_tables[i],
+                                                                               
        hash_value, key, &ret_data)) {
+                       rte_free(key);
+                       printf("Error: Failed to lookup key of %d bytes\n", 
key_sizes[i]);
+                       return -1;
+               }
+               if (ret_data != data) {
+                       rte_free(key);
+                       printf("Error: Data returned was not the one 
expected\n");
+                       return -1;
+               }
+
+               if (0 == rte_tshash_add_key_with_hash(hash_tables[i],
+                                                                               
        hash_value, key, data)) {
+                       rte_free(key);
+                       printf("Error: Added key of %d bytes that already 
existed\n", key_sizes[i]);
+                       return -1;
+               }
+
+               if (0 != rte_tshash_lookup_with_hash(hash_tables[i],
+                                                                               
        hash_value, key, &ret_data)) {
+                       rte_free(key);
+                       printf("Error: Failed to lookup key of %d bytes\n", 
key_sizes[i]);
+                       return -1;
+               }
+               if (ret_data != data) {
+                       rte_free(key);
+                       printf("Error: Data returned was not the one 
expected\n");
+                       return -1;
+               }
+
+               if (0 != rte_tshash_del_key_with_hash(hash_tables[i],
+                                                                               
        hash_value, key, NULL)) {
+                       rte_free(key);
+                       printf("Error: Failed to delete key of %d bytes\n", 
key_sizes[i]);
+                       return -1;
+               }
+
+               if (0 == rte_tshash_del_key_with_hash(hash_tables[i],
+                                                                               
        hash_value, key, NULL)) {
+                       rte_free(key);
+                       printf("Error: Deleted key of %d bytes that should not 
exist\n", key_sizes[i]);
+                       return -1;
+               }
+
+               if (0 == rte_tshash_lookup_with_hash(hash_tables[i],
+                                                                               
        hash_value, key, &ret_data)) {
+                       rte_free(key);
+                       printf("Error: Found key after deleting key of %d 
bytes\n", key_sizes[i]);
+                       return -1;
+               }
+
+               rte_free(key);
+               rte_tshash_reset(hash_tables[i]);
+       }
+
+       return 0;
+}
+
+/*
+ * Sequence of operations for a single key:
+ *     - add
+ *     - lookup: hit
+ *     - add: update
+ *     - lookup: hit
+ *     - delete: hit
+ *     - lookup: miss
+ */
+static int
+test_hash_add_lookup_update_delete_miss(void)
+{
+       struct rte_tshash *hash_tables_update[RTE_DIM(key_sizes)];
+
+       ut_params.max_entries = MAX_KEYS;
+       ut_params.socket_id = 0;
+       e_args.malloc_mem = 0;
+       e_args.max_load_factor = 0.5;
+       e_args.rte_tshash_cmp_eq = NULL;
+       e_args.hash_func = NULL;
+       e_args.update_add = 1;
+
+       unsigned i;
+       uint64_t hash_value, ret_data, data;
+       void *key;
+
+       for (i = 0; i < RTE_DIM(key_sizes); i++) {
+               sprintf(ut_params.name, "test_k%d_update", key_sizes[i]);
+               ut_params.key_len = key_sizes[i];
+
+               hash_tables_update[i] = 
rte_tshash_find_existing(ut_params.name);
+               if (hash_tables_update[i] != NULL)
+                       rte_tshash_reset(hash_tables_update[i]);
+               else
+                       hash_tables_update[i] = 
rte_tshash_create(ut_params.name,
+                                                                               
                        ut_params.max_entries,
+                                                                               
                        ut_params.key_len,
+                                                                               
                        ut_params.socket_id,
+                                                                               
                        &e_args);
+               RETURN_IF_ERROR(hash_tables_update[i] == NULL, "hash creation 
failed");
+
+               key = generate_key(key_sizes[i]);
+               hash_value = rte_rand() % MAX_KEYS;
+               data = rte_rand();
+
+               if (0 != rte_tshash_add_key_with_hash(hash_tables_update[i],
+                                                                               
        hash_value, key, data)) {
+                       rte_free(key);
+                       printf("Error: Failed to add key of %d bytes\n", 
key_sizes[i]);
+                       return -1;
+               }
+
+               if (0 != rte_tshash_lookup_with_hash(hash_tables_update[i],
+                                                                               
        hash_value, key, &ret_data)) {
+                       rte_free(key);
+                       printf("Error: Failed to lookup key of %d bytes\n", 
key_sizes[i]);
+                       return -1;
+               }
+               if (ret_data != data) {
+                       rte_free(key);
+                       printf("Error: Data returned was not the one 
expected\n");
+                       return -1;
+               }
+
+               data = rte_rand() % MAX_KEYS;
+               if (0 != rte_tshash_add_key_with_hash(hash_tables_update[i],
+                                                                               
        hash_value, key, data)) {
+                       rte_free(key);
+                       printf("Error: Failed to update data of key of %d 
bytes\n", key_sizes[i]);
+                       return -1;
+               }
+
+               if (0 != rte_tshash_lookup_with_hash(hash_tables_update[i], 
hash_value,
+                                                                               
        key, &ret_data)) {
+                       rte_free(key);
+                       printf("Error: Failed to lookup key of %d bytes\n", 
key_sizes[i]);
+                       return -1;
+               }
+               if (ret_data != data) {
+                       rte_free(key);
+                       printf("Error: Data returned was not the one 
expected\n");
+                       return -1;
+               }
+
+               if (0 != rte_tshash_del_key_with_hash(hash_tables_update[i], 
hash_value,
+                                                                               
        key, NULL)) {
+                       rte_free(key);
+                       printf("Error: Failed to delete key of %d bytes\n", 
key_sizes[i]);
+                       return -1;
+               }
+
+               if (0 == rte_tshash_lookup_with_hash(hash_tables_update[i], 
hash_value,
+                                                                               
        key, &ret_data)) {
+                       rte_free(key);
+                       printf("Error: Found key after deleting key of %d 
bytes\n", key_sizes[i]);
+                       return -1;
+               }
+
+               rte_free(key);
+               rte_tshash_reset(hash_tables_update[i]);
+       }
+
+       return 0;
+}
+
+/*
+ * Sequence of operations for find existing hash table
+ *
+ *  - find existing table: hit
+ *  - find non-existing table: miss
+ *
+ */
+static int
+test_hash_find_existing(void)
+{
+       struct rte_tshash *result = NULL;
+       unsigned i;
+       char test_name[RTE_TSHASH_NAMESIZE];
+
+       for (i = 0; i < RTE_DIM(key_sizes); i++) {
+               /* Try to find existing hash table */
+               sprintf(test_name, "test_k%d", key_sizes[i]);
+               result = rte_tshash_find_existing(test_name);
+               RETURN_IF_ERROR(result != hash_tables[i], "could not find 
existing hash table");
+       }
+
+       /* Try to find non-existing hash table */
+       result = rte_tshash_find_existing("hash_find_non_existing");
+       RETURN_IF_ERROR(!(result == NULL), "found table that shouldn't exist");
+
+       return 0;
+}
+
+/*
+ * Sequence of operations for 5 keys
+ *     - add keys
+ *     - lookup keys: hit
+ *     - delete keys : hit
+ *     - lookup keys: miss
+ */
+static int
+test_hash_burst(void)
+{
+       unsigned i, j, k;
+       void *keys[NUM_KEYS];
+       uint64_t data[NUM_KEYS];
+       uint64_t ret_data[NUM_KEYS];
+       uint64_t hash_values[NUM_KEYS];
+       uint64_t *hash_values_ptrs[NUM_KEYS];
+       uint64_t lookup_mask, hit_mask;
+       uint8_t hits;
+
+       for (i = 0; i < RTE_DIM(key_sizes); i++) {
+               for (j = 0; j < NUM_KEYS; j++) {
+                       keys[j] = generate_key(key_sizes[i]);
+                       data[j] = rte_rand();
+                       if (0 != rte_tshash_add_key(hash_tables[i], keys[j], 
data[j])) {
+                               printf("Error: Failed to add key of %d 
bytes\n", key_sizes[i]);
+                               goto fail_burst;
+                       }
+               }
+               for (j = 0; j < RTE_DIM(burst_sizes); j++) {
+                       if (burst_sizes[j] == 64)
+                               lookup_mask = 0xffffffffffffffff;
+                       else
+                               lookup_mask = (1llu << burst_sizes[j]) - 1;
+
+                       hits = rte_tshash_lookup_bulk(hash_tables[i], 
lookup_mask,
+                                                                               
(const void * const *)keys, ret_data,
+                                                                               
&hit_mask);
+                       if (hits != burst_sizes[j]) {
+                               printf("Error: Failed to find %d key(s) of %d 
bytes\n",
+                                               burst_sizes[j] - hits, 
key_sizes[i]);
+                               goto fail_burst;
+                       }
+                       for (k = 0; k < burst_sizes[j]; k++) {
+                               if (unlikely(ret_data[j] != data[j])) {
+                                       printf("Error with value returned from 
lookup of key of %d bytes",
+                                                       key_sizes[i]);
+                                       goto fail_burst;
+                               }
+                       }
+               }
+
+               for (j = 0; j < NUM_KEYS; j++) {
+                       if (0 != rte_tshash_del_key(hash_tables[i], keys[j], 
NULL)) {
+                               printf("Error: Failed to delete key of %d 
bytes\n", key_sizes[i]);
+                               goto fail_burst;
+                       }
+               }
+
+               lookup_mask = 0xffffffffffffffff;
+               hits = rte_tshash_lookup_bulk(hash_tables[i], lookup_mask,
+                                                                       (const 
void * const *)keys, ret_data,
+                                                                       
&hit_mask);
+               if (0 != hits) {
+                       printf("Error: Found  %d key(s) of %d bytes that should 
not exist\n",
+                                       hits, key_sizes[i]);
+                       goto fail_burst;
+               }
+
+               for (j = 0; j < NUM_KEYS; j++)
+                       rte_free(keys[j]);
+               rte_tshash_reset(hash_tables[i]);
+       }
+
+       /* Repeat test with precomputed hash values */
+       for (i = 0; i < RTE_DIM(key_sizes); i++) {
+               for (j = 0; j < NUM_KEYS; j++) {
+                       keys[j] = generate_key(key_sizes[i]);
+                       hash_values[j] = rte_rand() % MAX_KEYS;
+                       hash_values_ptrs[j] = &hash_values[j];
+                       data[j] = rte_rand();
+                       if (0 != rte_tshash_add_key_with_hash(hash_tables[i], 
hash_values[j],
+                                                                               
                keys[j], data[j])) {
+                               printf("Error: Failed to add key of %d 
bytes\n", key_sizes[i]);
+                               goto fail_burst;
+                       }
+               }
+               for (j = 0; j < RTE_DIM(burst_sizes); j++) {
+                       if (burst_sizes[j] == 64)
+                               lookup_mask = 0xffffffffffffffff;
+                       else
+                               lookup_mask = (1llu << burst_sizes[j]) - 1;
+
+                       hits = rte_tshash_lookup_bulk_with_hash(hash_tables[i], 
lookup_mask,
+                                                                               
        (uint64_t * const *)hash_values_ptrs,
+                                                                               
        (const void * const *)keys, ret_data,
+                                                                               
        &hit_mask);
+                       if (hits != burst_sizes[j]) {
+                               printf("Error: Failed to find %d key(s) of %d 
bytes\n",
+                                               burst_sizes[j] - hits, 
key_sizes[i]);
+                               goto fail_burst;
+                       }
+                       for (k = 0; k < burst_sizes[j]; k++) {
+                               if (unlikely(ret_data[j] != data[j])) {
+                                       printf("Error with value returned from 
lookup of key of %d bytes",
+                                                       key_sizes[i]);
+                                       goto fail_burst;
+                               }
+                       }
+               }
+
+               for (j = 0; j < NUM_KEYS; j++) {
+                       if (0 != rte_tshash_del_key_with_hash(hash_tables[i], 
hash_values[j],
+                                                                               
                keys[j], NULL)) {
+                               printf("Error: Failed to delete key of %d 
bytes\n", key_sizes[i]);
+                               goto fail_burst;
+                               return -1;
+                       }
+               }
+
+               lookup_mask = 0xffffffffffffffff;
+               hits = rte_tshash_lookup_bulk_with_hash(hash_tables[i], 
lookup_mask,
+                                                                               
        (uint64_t * const *)hash_values_ptrs,
+                                                                               
        (const void * const *)keys, ret_data,
+                                                                               
        &hit_mask);
+               if (0 != hits) {
+                       printf("Error: Found  %d key(s) of %d bytes that should 
not exist\n",
+                                       hits, key_sizes[i]);
+                       goto fail_burst;
+               }
+
+               for (j = 0; j < NUM_KEYS; j++)
+                       rte_free(keys[j]);
+               rte_tshash_reset(hash_tables[i]);
+       }
+
+       return 0;
+
+fail_burst:
+       for (j = 0; j < NUM_KEYS; j++)
+               rte_free(keys[j]);
+       return -1;
+}
+
+/*
+ * Add keys to the same bucket until bucket full.
+ *     - add 5 keys to the same bucket (hash created with 4 keys per bucket):
+ *       first 4 in first level, 5th in second one
+ *     - lookup the 5 keys: 4 hits in first level, 1 in second one
+ *     - delete the 5 keys: 5 OK
+ *     - lookup the 5 keys: 5 misses
+ */
+static int
+test_hash_full_bucket(void)
+{
+       struct rte_tshash *handle;
+       unsigned i;
+       void *keys[NUM_KEYS];
+       uint64_t data[NUM_KEYS];
+       uint64_t ret_data[NUM_KEYS];
+
+       ut_params.max_entries = MAX_KEYS;
+       ut_params.key_len = key_sizes[0];
+       ut_params.socket_id = 0;
+       sprintf(ut_params.name, "test_full_bucket");
+       e_args.max_load_factor = 0.5;
+       e_args.hash_func = pseudo_hash;
+
+       handle = rte_tshash_find_existing(ut_params.name);
+       if (handle != NULL)
+               rte_tshash_reset(handle);
+       else
+               handle = rte_tshash_create(ut_params.name, 
ut_params.max_entries,
+                                                               
ut_params.key_len, ut_params.socket_id, &e_args);
+       RETURN_IF_ERROR(handle == NULL, "hash creation failed");
+
+
+       /* Fill bucket */
+       for (i = 0; i < RTE_TSHASH_BUCKET_SIZE; i++) {
+               keys[i] = generate_key(ut_params.key_len);
+               data[i] = rte_rand();
+
+               if (0 != rte_tshash_add_key(handle, keys[i], data[i])) {
+                       printf("Error: Failed to add key of %d bytes\n", 
ut_params.key_len);
+                       goto fail_burst;
+               }
+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS
+               const struct rte_tshash_stats *stats  = 
rte_tshash_get_stats(handle);
+               if (stats->used_slots != (i+1) || stats->num_extra_buckets != 
0) {
+                       printf("Error: used_slots = %u, expected = %u;  
extra_buckets = %u"
+                                       " expected = 0 \n", stats->used_slots, 
i+1,
+                                       stats->num_extra_buckets);
+                       goto fail_burst;
+               }
+#endif
+       }
+
+       /* This new entry should go to the next bucket */
+       keys[RTE_TSHASH_BUCKET_SIZE] = generate_key(ut_params.key_len);
+       data[RTE_TSHASH_BUCKET_SIZE] = rte_rand();
+
+       if (0 != rte_tshash_add_key(handle, keys[RTE_TSHASH_BUCKET_SIZE],
+                                                               
data[RTE_TSHASH_BUCKET_SIZE])) {
+               printf("Error: Failed to add key of %d bytes\n", 
ut_params.key_len);
+               goto fail_burst;
+       }
+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS
+               const struct rte_tshash_stats *stats  = 
rte_tshash_get_stats(handle);
+               if (stats->used_slots != (RTE_TSHASH_BUCKET_SIZE+1)
+                               || stats->num_extra_buckets != 1) {
+                       printf("Error: used_slots = %u, expected = %u;  
extra_buckets = %u"
+                               " expected = 1 \n", stats->used_slots, 
RTE_TSHASH_BUCKET_SIZE+1,
+                               stats->num_extra_buckets);
+                       goto fail_burst;
+               }
+#endif
+
+       /* Lookup */
+       for (i = 0; i < RTE_TSHASH_BUCKET_SIZE+1; i++) {
+               if (0 != rte_tshash_lookup(handle, keys[i], &ret_data[i])) {
+                       printf("Error: Failed to find key of %d bytes\n", 
ut_params.key_len);
+                       goto fail_burst;
+               }
+               if (ret_data[i] != data[i]) {
+                       printf("Error: Data returned was not the one 
expected\n");
+                       goto fail_burst;
+               }
+       }
+
+       /* Delete */
+       for (i = 0; i < RTE_TSHASH_BUCKET_SIZE+1; i++) {
+               if (0 != rte_tshash_del_key(handle, keys[i], NULL)) {
+                       printf("Error: Failed to delete key of %d bytes\n", 
ut_params.key_len);
+                       goto fail_burst;
+               }
+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS
+               const struct rte_tshash_stats *stats  = 
rte_tshash_get_stats(handle);
+               if (stats->used_slots != (RTE_TSHASH_BUCKET_SIZE-i)
+                               || stats->num_extra_buckets != 1) {
+                       printf("Error: used_slots = %u, expected = %u;  
extra_buckets = %u"
+                               " expected = 1 \n", stats->used_slots, 
RTE_TSHASH_BUCKET_SIZE-i,
+                               stats->num_extra_buckets);
+                       goto fail_burst;
+               }
+#endif
+       }
+
+       /* Lookup */
+       for (i = 0; i < RTE_TSHASH_BUCKET_SIZE; i++) {
+               if (0 == rte_tshash_lookup(handle, keys[i], &ret_data[i])) {
+                       printf("Error: Found key after deleting key of %d 
bytes\n",
+                                       ut_params.key_len);
+                       goto fail_burst;
+               }
+       }
+       for (i = 0; i < RTE_TSHASH_BUCKET_SIZE + 1; i++)
+               rte_free(keys[i]);
+
+       return 0;
+fail_burst:
+       for (i = 0; i < RTE_TSHASH_BUCKET_SIZE + 1; i++)
+               rte_free(keys[i]);
+       return -1;
+}
+
+
+/*
+ * Do tests for hash creation with bad parameters.
+ */
+static int
+test_hash_creation_with_bad_parameters(void)
+{
+       struct rte_tshash *handle;
+       ut_params.max_entries = MAX_KEYS;
+       ut_params.key_len = key_sizes[0];
+       ut_params.socket_id = 0;
+
+       handle = rte_tshash_create(NULL, ut_params.max_entries,
+                       ut_params.key_len, ut_params.socket_id, NULL);
+
+       RETURN_IF_ERROR(handle != NULL,
+                       "Impossible creating hash sucessfully without a 
name\n");
+
+       sprintf(ut_params.name, "creation_with_bad_parameters_0");
+       ut_params.max_entries = RTE_TSHASH_MIN_ENTRIES - 1;
+       handle = rte_tshash_create(ut_params.name, ut_params.max_entries,
+                       ut_params.key_len, ut_params.socket_id, NULL);
+
+       RETURN_IF_ERROR(handle != NULL,
+                       "Impossible creating hash sucessfully with maximum 
number of entries"
+                       "less than minimum required\n");
+
+       sprintf(ut_params.name, "creation_with_bad_parameters_1");
+       ut_params.max_entries = MAX_KEYS;
+       e_args.max_load_factor = RTE_TSHASH_MAXIMUM_LOAD_FACTOR + 0.1;
+       e_args.malloc_mem = 0;
+       e_args.rte_tshash_cmp_eq = NULL;
+       e_args.hash_func = NULL;
+
+       handle = rte_tshash_create(ut_params.name, ut_params.max_entries,
+                       ut_params.key_len, ut_params.socket_id, &e_args);
+
+       RETURN_IF_ERROR(handle != NULL,
+                       "Impossible creating hash sucessfully with 
max_load_factor"
+                       "in parameter exceeded\n");
+
+       sprintf(ut_params.name, "creation_with_bad_parameters_2");
+       e_args.max_load_factor = RTE_TSHASH_MAXIMUM_LOAD_FACTOR;
+       ut_params.key_len = 13;
+
+       handle = rte_tshash_create(ut_params.name, ut_params.max_entries,
+                       ut_params.key_len, ut_params.socket_id, &e_args);
+
+       RETURN_IF_ERROR(handle != NULL,
+                       "Impossible creating hash sucessfully wif key size is 
not multiple"
+                       "of 16 and there is no user defined key compare 
function\n");
+
+       sprintf(ut_params.name, "creation_with_bad_parameters_3");
+       ut_params.key_len = 0;
+
+       handle = rte_tshash_create(ut_params.name, ut_params.max_entries,
+                       ut_params.key_len, ut_params.socket_id, NULL);
+
+       RETURN_IF_ERROR(handle != NULL,
+                       "Impossible creating hash sucessfully if key_len in 
parameter is zero\n");
+
+       sprintf(ut_params.name, "creation_with_bad_parameters_4");
+       ut_params.socket_id = RTE_MAX_NUMA_NODES;
+       ut_params.key_len = key_sizes[0];
+
+       handle = rte_tshash_create(ut_params.name, ut_params.max_entries,
+                       ut_params.key_len, ut_params.socket_id, NULL);
+
+       RETURN_IF_ERROR(handle != NULL,
+                       "Impossible creating hash sucessfully with invalid 
socket\n");
+
+       return 0;
+}
+
+/*
+ * Test the creation of a hash table with malloc function.
+ */
+static int
+test_hash_creation_with_malloc(void)
+{
+       struct rte_tshash *handle;
+
+       ut_params.max_entries = MAX_KEYS;
+       ut_params.key_len = key_sizes[0];
+       ut_params.socket_id = 0;
+       sprintf(ut_params.name, "test_malloc");
+       e_args.malloc_mem = 1;
+       e_args.max_load_factor = 0.5;
+       e_args.rte_tshash_cmp_eq = NULL;
+       e_args.hash_func = NULL;
+
+       handle = rte_tshash_find_existing(ut_params.name);
+       if (handle != NULL)
+               rte_tshash_reset(handle);
+       else
+               handle = rte_tshash_create(ut_params.name, 
ut_params.max_entries,
+                                                               
ut_params.key_len, ut_params.socket_id, &e_args);
+       RETURN_IF_ERROR(handle == NULL, "hash creation with malloc failed");
+
+       return 0;
+}
+
+static int
+generic_key_cmp(const void *key1, const void *key2, uint8_t key_size)
+{
+       return !memcmp(key1, key2, key_size);
+}
+
+/*
+ * Test add/lookup/delete functions with user-defined key compare function.
+ */
+static int
+test_hash_odd_key_size(void)
+{
+       struct rte_tshash *handle;
+       uint64_t hash_value, ret_data, data;
+       void *key;
+
+       ut_params.max_entries = MAX_KEYS;
+       ut_params.key_len = 26;
+       ut_params.socket_id = 0;
+       sprintf(ut_params.name, "test_odd_key_size");
+       e_args.max_load_factor = 0.5;
+       e_args.rte_tshash_cmp_eq = generic_key_cmp;
+
+       handle = rte_tshash_find_existing(ut_params.name);
+       if (handle != NULL)
+               rte_tshash_reset(handle);
+       else
+               handle = rte_tshash_create(ut_params.name, 
ut_params.max_entries,
+                                                                       
ut_params.key_len, ut_params.socket_id,
+                                                                       
&e_args);
+       RETURN_IF_ERROR(handle == NULL, "hash creation with malloc failed");
+
+       /* Test with standard add/lookup/delete functions */
+       key = generate_key(ut_params.key_len);
+       data = rte_rand();
+
+       if (0 != rte_tshash_add_key(handle, key, data)) {
+               rte_free(key);
+               printf("Error: Failed to add key of %d bytes\n", 
ut_params.key_len);
+               return -1;
+       }
+
+       if (0 != rte_tshash_lookup(handle, key, &ret_data)) {
+               rte_free(key);
+               printf("Error: Failed to find key of %d bytes\n", 
ut_params.key_len);
+               return -1;
+       }
+       if (ret_data != data) {
+               rte_free(key);
+               printf("Error: Data returned was not the one expected\n");
+               return -1;
+       }
+
+       if (0 != rte_tshash_del_key(handle, key, NULL)) {
+               rte_free(key);
+               printf("Error: Failed to delete key of %d bytes\n", 
ut_params.key_len);
+               return -1;
+       }
+
+       if (0 == rte_tshash_lookup(handle, key, &ret_data)) {
+               rte_free(key);
+               printf("Error: Found key after deleting key of %d bytes\n",
+                               ut_params.key_len);
+               return -1;
+       }
+
+       rte_free(key);
+       rte_tshash_reset(handle);
+
+       /* Repeat test with precomputed hash values */
+       key = generate_key(ut_params.key_len);
+       hash_value = rte_rand() % MAX_KEYS;
+       data = rte_rand();
+
+       if (0 != rte_tshash_add_key_with_hash(handle, hash_value, key, data)) {
+               rte_free(key);
+               printf("Error: Failed to add key of %d bytes\n", 
ut_params.key_len);
+               return -1;
+       }
+
+       if (0 != rte_tshash_lookup_with_hash(handle, hash_value, key, 
&ret_data)) {
+               rte_free(key);
+               printf("Error: Failed to find key of %d bytes\n", 
ut_params.key_len);
+               return -1;
+       }
+       if (ret_data != data) {
+               rte_free(key);
+               printf("Error: Data returned was not the one expected\n");
+               return -1;
+       }
+
+       if (0 != rte_tshash_del_key_with_hash(handle, hash_value, key, NULL)) {
+               rte_free(key);
+               printf("Error: Failed to delete key of %d bytes\n", 
ut_params.key_len);
+               return -1;
+       }
+
+       if (0 == rte_tshash_lookup_with_hash(handle, hash_value, key, 
&ret_data)) {
+               rte_free(key);
+               printf("Error: Found key after deleting key of %d bytes\n",
+                               ut_params.key_len);
+               return -1;
+       }
+
+       rte_free(key);
+       rte_tshash_reset(handle);
+
+       return 0;
+}
+
+/*
+ * Test add/lookup/delete functions with different hash functions (jhash and 
crc).
+ */
+static int
+test_hash_different_hash_functions(void)
+{
+       unsigned i;
+       struct rte_tshash *handle;
+       uint64_t hash_value, ret_data, data;
+       void *key;
+
+       for (i = 0; i < RTE_DIM(hashtest_funcs); i++) {
+               ut_params.max_entries = MAX_KEYS;
+               ut_params.key_len = key_sizes[0];
+               ut_params.socket_id = 0;
+               sprintf(ut_params.name, "test_tshash_%s", hash_func_strings[i]);
+               e_args.max_load_factor = 0.5;
+               e_args.rte_tshash_cmp_eq = NULL;
+               e_args.malloc_mem = 0;
+               e_args.hash_func = hashtest_funcs[i];
+
+               handle = rte_tshash_find_existing(ut_params.name);
+               if (handle != NULL)
+                       rte_tshash_reset(handle);
+               else
+                       handle = rte_tshash_create(ut_params.name, 
ut_params.max_entries,
+                                                                       
ut_params.key_len, ut_params.socket_id,
+                                                                       
&e_args);
+               RETURN_IF_ERROR(handle == NULL, "hash creation with %s hash 
function failed",
+                                               hash_func_strings[i]);
+
+               /* Test with standard add/lookup/delete functions */
+               key = generate_key(ut_params.key_len);
+               data = rte_rand();
+
+               if (0 != rte_tshash_add_key(handle, key, data)) {
+                       rte_free(key);
+                       printf("Error: Failed to add key of %d bytes\n", 
ut_params.key_len);
+                       return -1;
+               }
+
+               if (0 != rte_tshash_lookup(handle, key, &ret_data)) {
+                       rte_free(key);
+                       printf("Error: Failed to find key of %d bytes\n", 
ut_params.key_len);
+                       return -1;
+               }
+               if (ret_data != data) {
+                       rte_free(key);
+                       printf("Error: Data returned was not the one 
expected\n");
+                       return -1;
+               }
+
+               if (0 != rte_tshash_del_key(handle, key, NULL)) {
+                       rte_free(key);
+                       printf("Error: Failed to delete key of %d bytes\n", 
ut_params.key_len);
+                       return -1;
+               }
+
+               if (0 == rte_tshash_lookup(handle, key, &ret_data)) {
+                       rte_free(key);
+                       printf("Error: Found key after deleting key of %d 
bytes\n",
+                                       ut_params.key_len);
+                       return -1;
+               }
+
+               rte_free(key);
+               rte_tshash_reset(handle);
+
+               /* Repeat test with precomputed hash values */
+               key = generate_key(ut_params.key_len);
+               hash_value = rte_rand() % MAX_KEYS;
+               data = rte_rand();
+
+               if (0 != rte_tshash_add_key_with_hash(handle, hash_value, key, 
data)) {
+                       rte_free(key);
+                       printf("Error: Failed to add key of %d bytes\n", 
ut_params.key_len);
+                       return -1;
+               }
+
+               if (0 != rte_tshash_lookup_with_hash(handle, hash_value, key, 
&ret_data)) {
+                       rte_free(key);
+                       printf("Error: Failed to find key of %d bytes\n", 
ut_params.key_len);
+                       return -1;
+               }
+               if (ret_data != data) {
+                       rte_free(key);
+                       printf("Error: Data returned was not the one 
expected\n");
+                       return -1;
+               }
+
+               if (0 != rte_tshash_del_key_with_hash(handle, hash_value, key, 
NULL)) {
+                       rte_free(key);
+                       printf("Error: Failed to delete key of %d bytes\n", 
ut_params.key_len);
+                       return -1;
+               }
+
+               if (0 == rte_tshash_lookup_with_hash(handle, hash_value, key, 
&ret_data)) {
+                       rte_free(key);
+                       printf("Error: Found key after deleting key of %d 
bytes\n", ut_params.key_len);
+                       return -1;
+               }
+
+               rte_free(key);
+               rte_tshash_reset(handle);
+       }
+
+       return 0;
+}
+
+/*
+ * Do all unit tests.
+ */
+static int
+test_tshash(void)
+{
+       printf("\n-- Test 0: Creating hash tables --\n");
+       if (create_hash_tables() < 0)
+               return -1;
+       printf("\n-- Test1: add, lookup, delete --\n");
+       if (test_hash_add_lookup_delete() < 0)
+               return -1;
+       printf("\n-- Test2: find existing hash table --\n");
+       if (test_hash_find_existing() < 0)
+               return -1;
+       printf("\n-- Test3: add, lookup, delete misses --\n");
+       if (test_hash_add_lookup_delete_miss() < 0)
+               return -1;
+       printf("\n-- Test4: add, lookup, update, delete misses --\n");
+       if (test_hash_add_lookup_update_delete_miss() < 0)
+               return -1;
+       printf("\n-- Test5: lookup bulk --\n");
+       if (test_hash_burst() < 0)
+               return -1;
+       printf("\n-- Test6: full bucket --\n");
+       if (test_hash_full_bucket() < 0)
+               return -1;
+       printf("\n-- Test7: create hash table with bad parameters --\n");
+       if (test_hash_creation_with_bad_parameters() < 0)
+               return -1;
+       printf("\n-- Test8: create hash table with malloc --\n");
+       if (test_hash_creation_with_malloc() < 0)
+               return -1;
+       printf("\n-- Test9: add, lookup, delete with odd key size (not multiple 
of 16 --\n");
+       if (test_hash_odd_key_size() < 0)
+               return -1;
+       printf("\n-- Test10: use different hash functions (jhash and CRC) 
--\n");
+       if (test_hash_different_hash_functions() < 0)
+               return -1;
+
+       return 0;
+}
+#else /* RTE_LIBRTE_THREAD_SAFE_HASH_STATS */
+
+static int
+test_tshash(void)
+{
+       printf("The Thread Safe Hash library stats are not included in this 
build\n");
+       return 0;
+}
+
+#endif /* RTE_LIBRTE_THREAD_SAFE_HASH_STATS */
+
+static struct test_command tshash_cmd = {
+               .command = "thread_safe_hash_autotest",
+               .callback = test_tshash,
+};
+REGISTER_TEST_COMMAND(tshash_cmd);
+
diff --git a/app/test/test_tshash_multi_thread.c 
b/app/test/test_tshash_multi_thread.c
new file mode 100644
index 0000000..eacc5d5
--- /dev/null
+++ b/app/test/test_tshash_multi_thread.c
@@ -0,0 +1,351 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include <cmdline_parse.h>
+
+#include <rte_common.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_per_lcore.h>
+#include <rte_launch.h>
+#include <rte_atomic.h>
+#include <rte_tailq.h>
+#include <rte_eal.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_cycles.h>
+#include <rte_random.h>
+#include <rte_tshash.h>
+
+#include "test.h"
+
+#define TOTAL_ADDITIONS 300000
+
+#define MAX_KEYS 65536
+#define KEYSIZE 16
+#define MULTIPLIER 13
+
+/* Parameters used for hash table in unit test functions. Name set later. */
+static struct hash_parameters {
+       char name[RTE_TSHASH_NAMESIZE];
+       unsigned max_entries;
+       unsigned key_len;
+       uint8_t socket_id;
+} ut_params;
+
+static struct ops_stats {
+       unsigned retries_add;
+       unsigned not_enough_mem_add;
+       unsigned already_exist_add;
+       unsigned no_free_slots_add;
+       unsigned success_add;
+
+       unsigned retries_lookup;
+       unsigned errors_lookup;
+       unsigned misses_lookup;
+       unsigned success_lookup;
+
+       unsigned num_additions;
+       unsigned num_deletions;
+       unsigned num_lookups;
+} stats;
+
+static struct rte_tshash *hash_table;
+
+union hash_key {
+       uint64_t hashval;
+       char key[KEYSIZE];
+};
+
+unsigned num_sec_threads;
+unsigned finished_additions;
+
+#define RETURN_IF_ERROR(cond, str, ...) do {                           \
+       if (cond) {                                                     \
+               printf("ERROR line %d: " str "\n", __LINE__, ##__VA_ARGS__); \
+               return -1;                                              \
+       }                                                               \
+} while (0)
+
+static int
+create_hash_table(void)
+{
+       unsigned i;
+       hash_table = NULL;
+       sprintf(ut_params.name, "test_k%d_multi_thread", KEYSIZE);
+       ut_params.max_entries = MAX_KEYS;
+       ut_params.key_len = KEYSIZE;
+       ut_params.socket_id = rte_socket_id();
+
+       hash_table = rte_tshash_find_existing(ut_params.name);
+       if (hash_table != NULL)
+               rte_tshash_reset(hash_table);
+       else
+               hash_table = rte_tshash_create(ut_params.name,
+                                               ut_params.max_entries, 
ut_params.key_len,
+                                               ut_params.socket_id, NULL);
+       RETURN_IF_ERROR(hash_table == NULL, "hash creation failed");
+
+       for (i = 0; i < MAX_KEYS; i++) {
+               union hash_key k = { .key = {0} };
+               k.hashval = i;
+               RETURN_IF_ERROR(rte_tshash_add_key_with_hash(hash_table,
+                                       k.hashval, (void *)k.key, k.hashval * 
MULTIPLIER) != 0,
+                                       "Error adding entry number %u\n", i);
+       }
+
+       return 0;
+}
+
+static int
+test_add_single(__attribute__((unused)) void *arg)
+{
+       printf("Core %u is performing additions\n", rte_lcore_id());
+       while (stats.num_additions < TOTAL_ADDITIONS) {
+               union hash_key k = { .key = {0} };
+               k.hashval = MAX_KEYS/2-1 + (rte_rand() % 2) * MAX_KEYS/2;
+               if (0 == rte_tshash_add_key_with_hash(hash_table, k.hashval,
+                                                                               
        (void *)k.key,
+                                                                               
        k.hashval * MULTIPLIER))
+                       stats.num_additions++;
+       }
+
+       printf("Core %u has finished performing additions\n", rte_lcore_id());
+       finished_additions = 1;
+       return 0;
+}
+
+static int
+test_add_multi(__attribute__((unused)) void *arg)
+{
+       int ret;
+       unsigned i;
+
+       printf("Core %u is performing additions\n", rte_lcore_id());
+
+       for (i = 0; i < MAX_KEYS; i++) {
+               union hash_key k = { .key = {0} };
+               k.hashval = MAX_KEYS/2-1 + rte_rand() * MAX_KEYS/2;
+
+               ret = rte_tshash_add_key_with_hash(hash_table, k.hashval,
+                                                                               
(void *)k.key, k.hashval * MULTIPLIER);
+
+               switch (ret) {
+               case -EEXIST:
+                       __sync_fetch_and_add(&stats.already_exist_add, 1);
+                       break;
+               case -EAGAIN:
+                       __sync_fetch_and_add(&stats.retries_add, 1);
+                       break;
+               case -ENOMEM:
+                       __sync_fetch_and_add(&stats.not_enough_mem_add, 1);
+                       break;
+               case -ENOSPC:
+                       __sync_fetch_and_add(&stats.no_free_slots_add, 1);
+                       break;
+               default:
+                       __sync_fetch_and_add(&stats.success_add, 1);
+                       __sync_fetch_and_add(&stats.num_additions, 1);
+               }
+       }
+       return 0;
+}
+
+static int
+test_lookup(__attribute__((unused)) void *arg)
+{
+       int ret;
+
+       printf("Core %u is performing lookups\n", rte_lcore_id());
+       while (finished_additions == 0) {
+               union hash_key k = { .key = {0} };
+               uintptr_t retval = 0;
+               k.hashval = MAX_KEYS-1;
+
+               ret = rte_tshash_lookup_with_hash(hash_table, k.hashval,
+                                                                               
(void *)k.key, &retval);
+               __sync_fetch_and_add(&stats.num_lookups, 1);
+               switch (ret) {
+               /* Miss */
+               case -1:
+                       __sync_fetch_and_add(&stats.misses_lookup, 1);
+                       break;
+               /* Out of sync */
+               case -EAGAIN:
+                       __sync_fetch_and_add(&stats.retries_lookup, 1);
+                       break;
+               /* Hit */
+               default:
+                       /* Corrupted data */
+                       if (retval != k.hashval * MULTIPLIER) {
+                               __sync_fetch_and_add(&stats.errors_lookup, 1);
+                               printf("Data corrupted!\n!");
+                               printf("Expected %"PRIx64" but got 
%"PRIx64"\n", k.hashval * MULTIPLIER, retval);
+                       } else
+                               __sync_fetch_and_add(&stats.success_lookup, 1);
+               }
+       }
+       printf("Core %u has finished performing lookups\n", rte_lcore_id());
+
+       return 0;
+}
+
+static int
+test_delete(__attribute__((unused)) void *arg)
+{
+       printf("Core %u is performing deletions\n", rte_lcore_id());
+       while (finished_additions == 0) {
+               union hash_key k = { .key = {0} };
+               k.hashval = MAX_KEYS/2-1 + (rte_rand() % 2) * MAX_KEYS/2;
+               rte_tshash_del_key_with_hash(hash_table, k.hashval, (void 
*)k.key, NULL);
+       }
+       printf("Core %u has finished performing deletions\n", rte_lcore_id());
+
+       return 0;
+}
+
+/* 1 thread adding entries, 1 thread deleting entries and rest looking up */
+static int
+test_multi_lookup(void) {
+       unsigned i, j;
+
+       do {
+               j = 0;
+               finished_additions = 0;
+               /* Reset operation stats */
+               memset(&stats, 0, sizeof(struct ops_stats));
+
+               RTE_LCORE_FOREACH_SLAVE(i) {
+                       switch (j++) {
+                       case 0:
+                               rte_eal_remote_launch(test_add_single, NULL, i);
+                               break;
+                       case 1:
+                               rte_eal_remote_launch(test_delete, NULL, i);
+                               break;
+                       default:
+                               rte_eal_remote_launch(test_lookup, NULL, i);
+                       }
+               }
+               rte_eal_mp_wait_lcore();
+       }
+       /* There must be at least one attempt of collision (very likely) */
+       while (stats.retries_lookup == 0);
+
+       printf("Retries on lookup op: %u/%u (%.7f%%)\n", stats.retries_lookup,
+                       stats.num_lookups, ((double)stats.retries_lookup / 
stats.num_lookups * 100));
+       printf("Errors on lookup op: %u/%u (%.7f%%)\n", stats.errors_lookup,
+                       stats.num_lookups, ((double)stats.errors_lookup / 
stats.num_lookups * 100));
+       printf("Misses on lookup op: %u/%u (%.7f%%)\n\n", stats.misses_lookup,
+                       stats.num_lookups, ((double)stats.misses_lookup / 
stats.num_lookups * 100));
+       printf("Successes on lookup op: %u/%u (%.7f%%)\n\n", 
stats.success_lookup,
+                       stats.num_lookups, ((double)stats.success_lookup / 
stats.num_lookups * 100));
+
+       RETURN_IF_ERROR(stats.errors_lookup != 0,
+                                       "There was at least one error while 
looking up");
+       return 0;
+
+}
+
+/* All threads (minimum 2) adding entries */
+static int
+test_multi_add(void) {
+       unsigned i;
+
+       do {
+               /* Reset operation stats */
+               rte_tshash_reset(hash_table);
+               memset(&stats, 0, sizeof(struct ops_stats));
+
+               RTE_LCORE_FOREACH_SLAVE(i) {
+                       rte_eal_remote_launch(test_add_multi, NULL, i);
+               }
+               rte_eal_mp_wait_lcore();
+       }
+       /* There must be at least one attempt of collision (very likely) */
+       while (stats.retries_add == 0);
+
+       printf("\nRetries on add op: %u/%u (%.7f%%)\n", stats.retries_add,
+                       (MAX_KEYS * num_sec_threads),
+                       ((double)stats.retries_add / (MAX_KEYS * 
num_sec_threads) * 100));
+       printf("OOM on add op: %u/%u (%.7f%%)\n", stats.not_enough_mem_add,
+                       (MAX_KEYS * num_sec_threads),
+                       ((double)stats.not_enough_mem_add / (MAX_KEYS * 
num_sec_threads) * 100));
+       printf("Already existed on add op: %u/%u (%.7f%%)\n", 
stats.already_exist_add,
+                       (MAX_KEYS * num_sec_threads),
+                       ((double)stats.already_exist_add / (MAX_KEYS * 
num_sec_threads) * 100));
+       printf("No free slots on add op: %u/%u (%.7f%%)\n\n", 
stats.no_free_slots_add,
+                       (MAX_KEYS * num_sec_threads),
+                       ((double)stats.no_free_slots_add / (MAX_KEYS * 
num_sec_threads) * 100));
+       printf("Successes on add op: %u/%u (%.7f%%)\n\n", stats.success_add,
+                       (MAX_KEYS * num_sec_threads),
+                       ((double)stats.success_add / (MAX_KEYS * 
num_sec_threads) * 100));
+
+       return 0;
+}
+
+
+static int
+test_tshash_multi_thread(void)
+{
+
+       /* Master + 3 cores are needed (for add/lookup/delete) at least */
+       if (rte_lcore_count() < 4) {
+               printf("At least 4 cores are needed\n");
+               return -1;
+       }
+       num_sec_threads = rte_lcore_count() - 1;
+
+       if (create_hash_table() < 0)
+               return -1;
+
+       if (test_multi_lookup() < 0)
+               return -1;
+
+       if (test_multi_add() < 0)
+               return -1;
+
+       return 0;
+}
+
+static struct test_command tshash_multi_thread_cmd = {
+               .command = "thread_safe_hash_multi_thread_autotest",
+               .callback = test_tshash_multi_thread,
+};
+REGISTER_TEST_COMMAND(tshash_multi_thread_cmd);
diff --git a/app/test/test_tshash_perf.c b/app/test/test_tshash_perf.c
new file mode 100644
index 0000000..79235c1
--- /dev/null
+++ b/app/test/test_tshash_perf.c
@@ -0,0 +1,631 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <rte_cycles.h>
+#include <rte_tshash.h>
+#include <rte_random.h>
+#include <rte_string_fns.h>
+#include "test.h"
+
+#define MAX_KEYS (1<<21)
+#define MULTIPLIER 13
+#define MAX_KEYSIZE 128
+#define NUM_KEYSIZES 6
+#define NUM_OPERATIONS 8
+
+#define NUM_LOOKUPS (1<<24)
+#define BURST_SIZE 64
+
+union hash_key {
+       uint64_t hashval;
+       char key[MAX_KEYSIZE];
+};
+
+unsigned key_sizes[] = {16, 32, 48, 64, 96, 128};
+struct rte_tshash *h[NUM_KEYSIZES];
+uint64_t cycles[NUM_KEYSIZES][NUM_OPERATIONS][2];
+
+/*
+ * Set up the table so every flow fits in first level bucket
+ */
+static int
+setup_table(unsigned with_hash, unsigned table_index)
+{
+       unsigned i;
+       char name[RTE_TSHASH_NAMESIZE];
+       sprintf(name, "test_phash%d", key_sizes[table_index]);
+
+       h[table_index] = rte_tshash_find_existing(name);
+       if (h[table_index] == NULL) {
+               h[table_index] = rte_tshash_create(name, MAX_KEYS,
+                                                                               
        key_sizes[table_index],
+                                                                               
        rte_socket_id(), NULL);
+               if (h[table_index] == NULL) {
+                       printf("Error creating table\n");
+                       return -1;
+               } else {
+               }
+       } else
+               rte_tshash_reset(h[table_index]);
+
+       const uint64_t start_tsc = rte_rdtsc();
+
+       for (i = 0; i < MAX_KEYS; i++) {
+               union hash_key k = { .key = {0} };
+               k.hashval = i;
+               if (with_hash) {
+                       if (rte_tshash_add_key_with_hash(h[table_index], 
k.hashval,
+                                                                               
        (const void *)k.key,
+                                                                               
        k.hashval * MULTIPLIER) != 0) {
+                                       printf("Error adding entry number 
%u\n", i);
+                                       return -1;
+                       } else
+                               continue;
+               } else {
+                       if (rte_tshash_add_key(h[table_index], (const void 
*)k.key,
+                                                               k.hashval * 
MULTIPLIER != 0)) {
+                               printf("Error adding entry number %u\n", i);
+                               return -1;
+                       }
+               }
+       }
+
+       const uint64_t end_tsc = rte_rdtsc();
+       const uint64_t time_taken = end_tsc - start_tsc;
+       const float seconds_taken = (float)time_taken/rte_get_tsc_hz();
+       cycles[table_index][0][with_hash] = time_taken/MAX_KEYS;
+
+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS
+       const struct rte_tshash_stats *stats  = 
rte_tshash_get_stats(h[table_index]);
+       printf("used_slots = %u, extra_buckets = %u\n", stats->used_slots,
+                       stats->num_extra_buckets);
+#endif
+
+       printf("\n%"PRIu64" adds in %f seconds\n", (uint64_t)MAX_KEYS,
+                       seconds_taken);
+       printf("Average %"PRIu64" tsc ticks per add\n",
+                       cycles[table_index][0][with_hash]);
+       printf("Average %"PRIu64" adds per second\n", (MAX_KEYS * 
rte_get_tsc_hz())/time_taken);
+
+
+       return 0;
+}
+
+/*
+ * Set up table so half flows are in second level buckets
+ */
+static int
+setup_table_extd(unsigned with_hash, unsigned table_index)
+{
+       unsigned i, j;
+       char name[RTE_TSHASH_NAMESIZE];
+       sprintf(name, "test_phash%d", key_sizes[table_index]);
+
+       h[table_index] = rte_tshash_find_existing(name);
+       if (h[table_index] == NULL) {
+               h[table_index] = rte_tshash_create(name, MAX_KEYS,
+                                               key_sizes[table_index], 
rte_socket_id(), NULL);
+               if (h[table_index] == NULL) {
+                       printf("Error creating table\n");
+                       return -1;
+               } else {
+               }
+       } else
+               rte_tshash_reset(h[table_index]);
+
+       const uint64_t start_tsc = rte_rdtsc();
+       if (with_hash) {
+               for (i = 0; i < MAX_KEYS/8; i++) {
+                       for (j = 0; j < 8; j++) {
+                               union hash_key k = { .key = {0} };
+                               k.hashval = i;
+                               k.hashval |= (1lu<<(56+j));
+                               k.key[key_sizes[table_index]-1] = j;
+                               if 
(rte_tshash_add_key_with_hash(h[table_index], k.hashval,
+                                                                               
                (const void *)k.key,
+                                                                               
                k.hashval * MULTIPLIER) != 0) {
+                                       printf("Error adding entry number 
%u\n", i*7+j);
+                                       return -1;
+                               }
+                       }
+               }
+       } else {
+               for (i = 0; i < MAX_KEYS/8; i++) {
+                       for (j = 0; j < 8; j++) {
+                               union hash_key k = { .key = {0} };
+                               k.hashval = i;
+                               k.hashval |= (1lu<<(56+j));
+                               k.key[key_sizes[table_index]-1] = j;
+                               if (rte_tshash_add_key(h[table_index], (const 
void *)k.key,
+                                                                       
k.hashval * MULTIPLIER) != 0) {
+                                       printf("Error adding entry number 
%u\n", i*7+j);
+                                       return -1;
+                               }
+                       }
+               }
+       }
+       const uint64_t end_tsc = rte_rdtsc();
+       const uint64_t time_taken = end_tsc - start_tsc;
+       const float seconds_taken = (float)time_taken/rte_get_tsc_hz();
+       cycles[table_index][1][with_hash] = time_taken/MAX_KEYS;
+
+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS
+       const struct rte_tshash_stats *stats  = 
rte_tshash_get_stats(h[table_index]);
+       printf("used_slots = %u, extra_buckets = %u\n", stats->used_slots,
+                       stats->num_extra_buckets);
+#endif
+
+       printf("\n%"PRIu64" adds in %f seconds\n", (uint64_t) MAX_KEYS,
+                       seconds_taken);
+       printf("Average %"PRIu64" tsc ticks per add\n",
+                       cycles[table_index][1][with_hash]);
+       printf("Average %"PRIu64" adds per second\n",
+                       (MAX_KEYS * rte_get_tsc_hz())/time_taken);
+
+       return 0;
+}
+
+
+static int
+timed_lookups(unsigned with_hash, unsigned table_index)
+{
+       uint64_t i;
+       uintptr_t retval;
+       const uint64_t start_tsc = rte_rdtsc();
+
+       for (i = 0; i < NUM_LOOKUPS; i++) {
+               union hash_key k = { .key = {0} };
+
+               k.hashval = rte_rand() % MAX_KEYS;
+               if (with_hash) {
+                       if (rte_tshash_lookup_with_hash(h[table_index], 
k.hashval,
+                                                                               
(const void *)k.key, &retval) < 0) {
+                               printf("Error lookup up hash key %"PRIu64"\n", 
k.hashval);
+                               return -1;
+                       } else
+                               continue;
+               } else {
+                       if (rte_tshash_lookup(h[table_index],
+                                                               (const void 
*)k.key, &retval) < 0) {
+                               printf("Error lookup up hash key %"PRIu64"\n", 
k.hashval);
+                               return -1;
+                       }
+               }
+       }
+       const uint64_t end_tsc = rte_rdtsc();
+       const uint64_t time_taken = end_tsc - start_tsc;
+       const float seconds_taken = (float)time_taken/rte_get_tsc_hz();
+       cycles[table_index][2][with_hash] = time_taken/NUM_LOOKUPS;
+
+       printf("%"PRIu64" lookups in %f seconds\n", (uint64_t) NUM_LOOKUPS,
+                       seconds_taken);
+       printf("Average %"PRIu64" tsc ticks per lookup\n",
+                       cycles[table_index][2][with_hash]);
+       printf("Average %"PRIu64" lookups per second\n",
+                       (NUM_LOOKUPS * rte_get_tsc_hz())/time_taken);
+       return 0;
+}
+
+static int
+timed_lookups_extd(unsigned with_hash, unsigned table_index)
+{
+       uint64_t i;
+       uintptr_t retval;
+       const uint64_t start_tsc = rte_rdtsc();
+       for (i = 0; i < NUM_LOOKUPS; i++) {
+               union hash_key k = { .key = {0} };
+               unsigned tmp = rte_rand() % 8;
+               k.hashval = rte_rand() % MAX_KEYS/8;
+               k.hashval |= (1llu << (tmp+56));
+               k.key[key_sizes[table_index]-1] = tmp;
+               if (with_hash) {
+                       if (rte_tshash_lookup_with_hash(h[table_index], 
k.hashval,
+                                                                               
(const void *)k.key, &retval) < 0) {
+                               printf("Error lookup up hash key %"PRIu64"\n", 
k.hashval);
+                               return -1;
+                       } else
+                               continue;
+               } else {
+                       if (rte_tshash_lookup(h[table_index], (const void 
*)k.key,
+                                                               &retval) < 0) {
+                               printf("Error lookup up hash key %"PRIu64"\n", 
k.hashval);
+                               return -1;
+                       }
+               }
+       }
+
+       const uint64_t end_tsc = rte_rdtsc();
+       const uint64_t time_taken = end_tsc - start_tsc;
+       const float seconds_taken = (float)time_taken/rte_get_tsc_hz();
+       cycles[table_index][3][with_hash] = time_taken/NUM_LOOKUPS;
+
+       printf("%"PRIu64" lookups in %f seconds\n", (uint64_t)NUM_LOOKUPS,
+                       seconds_taken);
+       printf("Average %"PRIu64" tsc ticks per lookup\n",
+                       cycles[table_index][3][with_hash]);
+       printf("Average %"PRIu64" lookups per second\n",
+                       (NUM_LOOKUPS * rte_get_tsc_hz())/time_taken);
+       return 0;
+}
+
+static int
+timed_lookups_multi(unsigned with_hash,  unsigned table_index)
+{
+       uint64_t i;
+#if BURST_SIZE == 64
+               const uint64_t lookup_mask = 0xffffffffffffffff;
+#else
+               const uint64_t lookup_mask = (1llu << BURST_SIZE) - 1;
+#endif
+       union hash_key ks[BURST_SIZE] = { { .key = {0} } };
+       const void *keys[BURST_SIZE];
+       uint64_t *h_ptrs[BURST_SIZE];
+
+       for (i = 0; i < BURST_SIZE; i++) {
+               keys[i] = (void *)ks[i].key;
+               h_ptrs[i] = &ks[i].hashval;
+       }
+
+       const uint64_t start_tsc = rte_rdtsc();
+       for (i = 0; i < NUM_LOOKUPS/BURST_SIZE; i++) {
+               unsigned j;
+               uint64_t hashes[BURST_SIZE];
+               uintptr_t retvals[BURST_SIZE];
+               int hitcount;
+               uint64_t hitmask;
+
+               for (j = 0; j < BURST_SIZE; j++) {
+                       hashes[j] = rte_rand() % MAX_KEYS;
+                       ks[j].hashval = hashes[j];
+               }
+               if (with_hash) {
+                       hitcount = 
rte_tshash_lookup_bulk_with_hash(h[table_index], lookup_mask,
+                                                                               
                                h_ptrs, keys, retvals,
+                                                                               
                                &hitmask);
+                       if (unlikely(hitcount != BURST_SIZE)) {
+                                       printf("Error lookup up hash keys. 
Expected retval = %u, got "
+                                                       "%d\n", BURST_SIZE, 
hitcount);
+                                       printf("Hit mask = %"PRIx64"\n", 
hitmask);
+                                       return -1;
+                       } else
+                               continue;
+               } else {
+                       hitcount = rte_tshash_lookup_bulk(h[table_index], 
lookup_mask,
+                                                                               
                keys, retvals, &hitmask);
+                       if (unlikely(hitcount != BURST_SIZE)) {
+                               printf("Error lookup up hash keys. Expected 
retval = %u, got "
+                                               "%d\n", BURST_SIZE, hitcount);
+                               printf("Hit mask = %"PRIx64"\n", hitmask);
+                               return -1;
+                       }
+               }
+       }
+
+       const uint64_t end_tsc = rte_rdtsc();
+       const uint64_t time_taken = end_tsc - start_tsc;
+       const float seconds_taken = (float)time_taken/rte_get_tsc_hz();
+       cycles[table_index][4][with_hash] = time_taken/NUM_LOOKUPS;
+
+       printf("%"PRIu64" lookups in %f seconds\n", (uint64_t)NUM_LOOKUPS,
+                       seconds_taken);
+       printf("Average %"PRIu64" tsc ticks per lookup\n",
+                       cycles[table_index][4][with_hash]);
+       printf("Average %"PRIu64" lookups per second\n",
+                       (NUM_LOOKUPS * rte_get_tsc_hz())/time_taken);
+       return 0;
+}
+
+static int
+timed_lookups_multi_extd(unsigned with_hash,  unsigned table_index)
+{
+       uint64_t i;
+#if BURST_SIZE == 64
+               const uint64_t lookup_mask = 0xffffffffffffffff;
+#else
+               const uint64_t lookup_mask = (1llu << BURST_SIZE) - 1;
+#endif
+       union hash_key ks[BURST_SIZE] = { { .key = {0} } };
+       const void *keys[BURST_SIZE];
+       uint64_t *h_ptrs[BURST_SIZE];
+
+       for (i = 0; i < BURST_SIZE; i++) {
+               keys[i] = (void *)ks[i].key;
+               h_ptrs[i] = &ks[i].hashval;
+       }
+
+       const uint64_t start_tsc = rte_rdtsc();
+       for (i = 0; i < NUM_LOOKUPS/BURST_SIZE; i++) {
+               unsigned j;
+               uint64_t hashes[BURST_SIZE];
+               uintptr_t retvals[BURST_SIZE];
+               int hitcount;
+               uint64_t hitmask;
+
+               for (j = 0; j < BURST_SIZE; j++) {
+                       unsigned tmp = rte_rand() % 8;
+                       hashes[j] = rte_rand() % MAX_KEYS/8;
+                       hashes[j] |= (1lu << (56 + tmp));
+                       ks[j].hashval = hashes[j];
+                       ks[j].key[key_sizes[table_index] - 1] = tmp;
+               }
+               if (with_hash) {
+                       hitcount = 
rte_tshash_lookup_bulk_with_hash(h[table_index], lookup_mask,
+                                                                               
                                h_ptrs, keys, retvals,
+                                                                               
                                &hitmask);
+                       if (unlikely(hitcount != BURST_SIZE)) {
+                               printf("Error lookup up hash keys. Expected 
retval = %u, got "
+                                               "%d\n", BURST_SIZE, hitcount);
+                               printf("Hit mask = %"PRIx64"\n", hitmask);
+                               return -1;
+                       } else
+                               continue;
+               } else {
+                       hitcount = rte_tshash_lookup_bulk(h[table_index], 
lookup_mask,
+                                                               keys, retvals, 
&hitmask);
+                       if (unlikely(hitcount != BURST_SIZE)) {
+                               printf("Error lookup up hash keys. Expected 
retval = %u, got "
+                                               "%d\n", BURST_SIZE, hitcount);
+                               printf("Hit mask = %"PRIx64"\n", hitmask);
+                               return -1;
+                       }
+               }
+       }
+
+       const uint64_t end_tsc = rte_rdtsc();
+       const uint64_t time_taken = end_tsc - start_tsc;
+       const float seconds_taken = (float)time_taken/rte_get_tsc_hz();
+       cycles[table_index][5][with_hash] = time_taken/NUM_LOOKUPS;
+
+       printf("%"PRIu64" lookups in %f seconds\n", (uint64_t) NUM_LOOKUPS,
+                       seconds_taken);
+       printf("Average %"PRIu64" tsc ticks per lookup\n",
+                       cycles[table_index][5][with_hash]);
+       printf("Average %"PRIu64" lookups per second\n",
+                       (NUM_LOOKUPS * rte_get_tsc_hz())/time_taken);
+       return 0;
+}
+
+static int
+timed_deletes(unsigned with_hash, unsigned table_index)
+{
+       uint64_t i;
+       const uint64_t start_tsc = rte_rdtsc();
+       for (i = 0; i < MAX_KEYS; i++) {
+               union hash_key k = { .key = {0} };
+
+               k.hashval = i;
+               if (with_hash) {
+                       if (rte_tshash_del_key_with_hash(h[table_index], 
k.hashval,
+                                                                               
        (const void *)k.key, NULL) < 0) {
+                               printf("Error deleting hash key %"PRIu64"\n", 
k.hashval);
+                               return -1;
+                       } else
+                               continue;
+               } else {
+                       if (rte_tshash_del_key(h[table_index],
+                                                               (const void 
*)k.key, NULL) < 0) {
+                               printf("Error deleting hash key %"PRIu64"\n", 
k.hashval);
+                               return -1;
+                       }
+               }
+
+       }
+       const uint64_t end_tsc = rte_rdtsc();
+       const uint64_t time_taken = end_tsc - start_tsc;
+       const float seconds_taken = (float)time_taken/rte_get_tsc_hz();
+       cycles[table_index][6][with_hash] = time_taken/MAX_KEYS;
+
+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS
+       const struct rte_tshash_stats *stats  = 
rte_tshash_get_stats(h[table_index]);
+       printf("used_slots = %u, extra_buckets = %u\n", stats->used_slots,
+                       stats->num_extra_buckets);
+#endif
+
+       printf("\n%"PRIu64" deletions in %f seconds\n", (uint64_t) MAX_KEYS,
+                       seconds_taken);
+       printf("Average %"PRIu64" tsc ticks per deletion\n",
+                       cycles[table_index][6][with_hash]);
+       printf("Average %"PRIu64" deletions per second\n",
+                       (MAX_KEYS * rte_get_tsc_hz())/time_taken);
+
+       return 0;
+}
+
+static int
+timed_deletes_extd(unsigned with_hash, unsigned table_index)
+{
+       uint64_t i, j;
+
+       const uint64_t start_tsc = rte_rdtsc();
+       for (i = 0; i < MAX_KEYS/8; i++) {
+               for (j = 0; j < 8; j++) {
+                       union hash_key k = { .key = {0} };
+                       k.hashval = i;
+                       k.hashval |= (1lu<<(56+j));
+                       k.key[key_sizes[table_index]-1] = j;
+                       if (with_hash) {
+                               if 
(rte_tshash_del_key_with_hash(h[table_index], k.hashval,
+                                                                               
                (const void *)k.key, NULL)
+                                               != 0) {
+                                       printf("Error deleting hash key 
%"PRIu64"\n", k.hashval);
+                                       return -1;
+                               } else
+                                       continue;
+                       } else {
+                               if (rte_tshash_del_key(h[table_index], (const 
void *)k.key,
+                                                                               
NULL) != 0) {
+                                       printf("Error deleting hash key 
%"PRIu64"\n", k.hashval);
+                                       return -1;
+                               }
+                       }
+               }
+       }
+       const uint64_t end_tsc = rte_rdtsc();
+       const uint64_t time_taken = end_tsc - start_tsc;
+       const float seconds_taken = (float)time_taken/rte_get_tsc_hz();
+       cycles[table_index][7][with_hash] = time_taken/MAX_KEYS;
+
+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS
+       const struct rte_tshash_stats *stats  = 
rte_tshash_get_stats(h[table_index]);
+       printf("used_slots = %u, extra_buckets = %u\n", stats->used_slots,
+                       stats->num_extra_buckets);
+#endif
+
+       printf("\n%"PRIu64" deletions in %f seconds\n", (uint64_t)MAX_KEYS,
+                       seconds_taken);
+       printf("Average %"PRIu64" tsc ticks per deletion\n",
+                       cycles[table_index][7][with_hash]);
+       printf("Average %"PRIu64" deletions per second\n", (MAX_KEYS * 
rte_get_tsc_hz())/time_taken);
+
+       return 0;
+}
+
+static int
+reset_table(unsigned table_index) {
+
+       rte_tshash_reset(h[table_index]);
+       return 0;
+}
+
+static int
+test_tshash_perf(void)
+{
+       unsigned i, j;
+
+       for (i = 0; i < NUM_KEYSIZES; i++) {
+               printf("\n------ KEY SIZE = %u ----------\n\n", key_sizes[i]);
+               printf("\n ----- WITH PRECALCULATED HASH VALUES -----\n\n");
+               printf("Table with only first-level buckets used\n");
+               if (setup_table(1, i) < 0)
+                       return -1;
+
+               printf("\nTimed lookups\n");
+               printf("------------------\n");
+               if (timed_lookups(1, i) < 0)
+                       return -1;
+
+               printf("\nTimed lookups multi \n");
+               printf("------------------\n");
+               if (timed_lookups_multi(1, i) < 0)
+                       return -1;
+
+               printf("\nTimed deletions \n");
+               printf("------------------\n");
+               if (timed_deletes(1, i) < 0)
+                       return -1;
+
+               printf("\nResetting table to use extra buckets\n");
+               if (setup_table_extd(1, i) < 0)
+                       return -1;
+
+               printf("\nTimed lookups extd\n");
+               printf("------------------\n");
+               if (timed_lookups_extd(1, i) < 0)
+                       return -1;
+
+               printf("\nTimed lookups multi extd\n");
+               printf("------------------\n");
+               if (timed_lookups_multi_extd(1, i) < 0)
+                       return -1;
+
+               printf("\nTimed deletions extd\n");
+               printf("------------------\n");
+               if (timed_deletes_extd(1, i) < 0)
+                       return -1;
+
+               reset_table(i);
+
+               printf("\n ----- WITH JUST KEYS -----\n\n");
+
+               printf("Table with only first-level buckets used\n");
+               if (setup_table(0, i) < 0)
+                       return -1;
+
+               printf("\nTimed lookups\n");
+               printf("------------------\n");
+               if (timed_lookups(0, i) < 0)
+                       return -1;
+
+               printf("\nTimed lookups multi \n");
+               printf("------------------\n");
+               if (timed_lookups_multi(0, i) < 0)
+                       return -1;
+
+               printf("\nTimed deletions \n");
+                       printf("------------------\n");
+               if (timed_deletes(0, i) < 0)
+                       return -1;
+
+               reset_table(i);
+
+       }
+       printf("\nResults (in CPU cycles/operation)\n");
+       printf("-----------------------------------\n");
+       printf("\nWith just keys (only 1st level buckets)\n");
+       printf("\n%-18s%-18s%-18s%-18s%-18s\n",
+                       "Keysize", "Add", "Lookup", "Lookup_bulk", "Delete");
+       for (i = 0; i < NUM_KEYSIZES; i++) {
+               printf("%-18d", key_sizes[i]);
+               for (j = 0; j < 8; j = j+2)
+                       printf("%-18"PRIu64, cycles[i][j][0]);
+               printf("\n");
+       }
+
+       printf("\nWith precalculated hash (only 1st level buckets)\n");
+       printf("\n%-18s%-18s%-18s%-18s%-18s\n",
+                       "Keysize", "Add", "Lookup", "Lookup_bulk", "Delete");
+       for (i = 0; i < NUM_KEYSIZES; i++) {
+               printf("%-18d", key_sizes[i]);
+               for (j = 0; j < 8; j = j+2)
+                       printf("%-18"PRIu64, cycles[i][j][1]);
+               printf("\n");
+       }
+       printf("\nWith precalculated hash (with extended buckets)\n");
+       printf("\n%-18s%-18s%-18s%-18s%-18s\n",
+                       "Keysize", "Add", "Lookup", "Lookup_bulk", "Delete");
+       for (i = 0; i < NUM_KEYSIZES; i++) {
+               printf("%-18d", key_sizes[i]);
+               for (j = 1; j < 8; j = j+2)
+                       printf("%-18"PRIu64, cycles[i][j][1]);
+               printf("\n");
+       }
+       return 0;
+}
+
+static struct test_command tshash_perf_cmd = {
+               .command = "thread_safe_hash_perf_autotest",
+               .callback = test_tshash_perf,
+};
+REGISTER_TEST_COMMAND(tshash_perf_cmd);
-- 
1.7.7.6

Reply via email to