hash: fix multi-process support
authorPablo de Lara <pablo.de.lara.guarch@intel.com>
Fri, 1 Apr 2016 15:03:49 +0000 (16:03 +0100)
committerThomas Monjalon <thomas.monjalon@6wind.com>
Fri, 1 Apr 2016 16:56:27 +0000 (18:56 +0200)
Hash library used a function pointer to choose a different
key compare function, depending on the key size.
As a result, multiple processes could not use the same hash table,
as the function addresses vary from one process to another.

Instead, a jump table is used, so each process has its own
function addresses, accessing this table with an index stored
in the hash table (note that using a custom key compare function
is not supported in multi-process mode).

Fixes: 48a399119619 ("hash: replace with cuckoo hash implementation")

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
doc/guides/prog_guide/hash_lib.rst
doc/guides/rel_notes/release_16_04.rst
lib/librte_hash/rte_cuckoo_hash.c

index 8f2cdbf..7944640 100644 (file)
@@ -87,6 +87,14 @@ or stored in the hash table itself.
 The example hash tables in the L2/L3 Forwarding sample applications defines which port to forward a packet to based on a packet flow identified by the five-tuple lookup.
 However, this table could also be used for more sophisticated features and provide many other functions and actions that could be performed on the packets and flows.
 
 The example hash tables in the L2/L3 Forwarding sample applications defines which port to forward a packet to based on a packet flow identified by the five-tuple lookup.
 However, this table could also be used for more sophisticated features and provide many other functions and actions that could be performed on the packets and flows.
 
+Multi-process support
+---------------------
+
+The hash library can be used in a multi-process environment, minding that only lookups are thread-safe.
+The only function that can only be used in single-process mode is rte_hash_set_cmp_func(), which sets up
+a custom compare function, which is assigned to a function pointer (therefore, it is not supported in
+multi-process mode).
+
 Implementation Details
 ----------------------
 
 Implementation Details
 ----------------------
 
index 79d76e1..e6cc34d 100644 (file)
@@ -404,6 +404,14 @@ Libraries
   Fix crc32c hash functions to return a valid crc32c value for data lengths
   not multiple of 4 bytes.
 
   Fix crc32c hash functions to return a valid crc32c value for data lengths
   not multiple of 4 bytes.
 
+* **hash: Fixed hash library to support multi-process mode.**
+
+  Fix hash library to support multi-process mode, using a jump table,
+  instead of storing a function pointer to the key compare function.
+  Multi-process mode only works with the built-in compare functions,
+  however a custom compare function (not in the jump table) can only
+  be used in single-process mode.
+
 * **librte_port: Fixed segmentation fault for ring and ethdev writer nodrop.**
 
   Fixed core dump issue on txq and swq when dropless is set to yes.
 * **librte_port: Fixed segmentation fault for ring and ethdev writer nodrop.**
 
   Fixed core dump issue on txq and swq when dropless is set to yes.
index b9b0b65..04e95b7 100644 (file)
@@ -102,6 +102,63 @@ EAL_REGISTER_TAILQ(rte_hash_tailq)
 
 #define LCORE_CACHE_SIZE               8
 
 
 #define LCORE_CACHE_SIZE               8
 
+#if defined(RTE_ARCH_X86) || defined(RTE_ARCH_ARM64)
+/*
+ * All different options to select a key compare function,
+ * based on the key size and custom function.
+ */
+enum cmp_jump_table_case {
+       KEY_CUSTOM = 0,
+       KEY_16_BYTES,
+       KEY_32_BYTES,
+       KEY_48_BYTES,
+       KEY_64_BYTES,
+       KEY_80_BYTES,
+       KEY_96_BYTES,
+       KEY_112_BYTES,
+       KEY_128_BYTES,
+       KEY_OTHER_BYTES,
+       NUM_KEY_CMP_CASES,
+};
+
+/*
+ * Table storing all different key compare functions
+ * (multi-process supported)
+ */
+const rte_hash_cmp_eq_t cmp_jump_table[NUM_KEY_CMP_CASES] = {
+       NULL,
+       rte_hash_k16_cmp_eq,
+       rte_hash_k32_cmp_eq,
+       rte_hash_k48_cmp_eq,
+       rte_hash_k64_cmp_eq,
+       rte_hash_k80_cmp_eq,
+       rte_hash_k96_cmp_eq,
+       rte_hash_k112_cmp_eq,
+       rte_hash_k128_cmp_eq,
+       memcmp
+};
+#else
+/*
+ * All different options to select a key compare function,
+ * based on the key size and custom function.
+ */
+enum cmp_jump_table_case {
+       KEY_CUSTOM = 0,
+       KEY_OTHER_BYTES,
+       NUM_KEY_CMP_CASES,
+};
+
+/*
+ * Table storing all different key compare functions
+ * (multi-process supported)
+ */
+const rte_hash_cmp_eq_t cmp_jump_table[NUM_KEY_CMP_CASES] = {
+       NULL,
+       memcmp
+};
+
+#endif
+
 struct lcore_cache {
        unsigned len; /**< Cache len */
        void *objs[LCORE_CACHE_SIZE]; /**< Cache objects */
 struct lcore_cache {
        unsigned len; /**< Cache len */
        void *objs[LCORE_CACHE_SIZE]; /**< Cache objects */
@@ -115,7 +172,10 @@ struct rte_hash {
        uint32_t key_len;               /**< Length of hash key. */
        rte_hash_function hash_func;    /**< Function used to calculate hash. */
        uint32_t hash_func_init_val;    /**< Init value used by hash_func. */
        uint32_t key_len;               /**< Length of hash key. */
        rte_hash_function hash_func;    /**< Function used to calculate hash. */
        uint32_t hash_func_init_val;    /**< Init value used by hash_func. */
-       rte_hash_cmp_eq_t rte_hash_cmp_eq; /**< Function used to compare keys. */
+       rte_hash_cmp_eq_t rte_hash_custom_cmp_eq;
+       /**< Custom function used to compare keys. */
+       enum cmp_jump_table_case cmp_jump_table_idx;
+       /**< Indicates which compare function to use. */
        uint32_t bucket_bitmask;        /**< Bitmask for getting bucket index
                                                from hash signature. */
        uint32_t key_entry_size;         /**< Size of each key entry. */
        uint32_t bucket_bitmask;        /**< Bitmask for getting bucket index
                                                from hash signature. */
        uint32_t key_entry_size;         /**< Size of each key entry. */
@@ -187,7 +247,16 @@ rte_hash_find_existing(const char *name)
 
 void rte_hash_set_cmp_func(struct rte_hash *h, rte_hash_cmp_eq_t func)
 {
 
 void rte_hash_set_cmp_func(struct rte_hash *h, rte_hash_cmp_eq_t func)
 {
-       h->rte_hash_cmp_eq = func;
+       h->rte_hash_custom_cmp_eq = func;
+}
+
+static inline int
+rte_hash_cmp_eq(const void *key1, const void *key2, const struct rte_hash *h)
+{
+       if (h->cmp_jump_table_idx == KEY_CUSTOM)
+               return h->rte_hash_custom_cmp_eq(key1, key2, h->key_len);
+       else
+               return cmp_jump_table[h->cmp_jump_table_idx](key1, key2, h->key_len);
 }
 
 struct rte_hash *
 }
 
 struct rte_hash *
@@ -291,35 +360,35 @@ rte_hash_create(const struct rte_hash_parameters *params)
        /* Select function to compare keys */
        switch (params->key_len) {
        case 16:
        /* Select function to compare keys */
        switch (params->key_len) {
        case 16:
-               h->rte_hash_cmp_eq = rte_hash_k16_cmp_eq;
+               h->cmp_jump_table_idx = KEY_16_BYTES;
                break;
        case 32:
                break;
        case 32:
-               h->rte_hash_cmp_eq = rte_hash_k32_cmp_eq;
+               h->cmp_jump_table_idx = KEY_32_BYTES;
                break;
        case 48:
                break;
        case 48:
-               h->rte_hash_cmp_eq = rte_hash_k48_cmp_eq;
+               h->cmp_jump_table_idx = KEY_48_BYTES;
                break;
        case 64:
                break;
        case 64:
-               h->rte_hash_cmp_eq = rte_hash_k64_cmp_eq;
+               h->cmp_jump_table_idx = KEY_64_BYTES;
                break;
        case 80:
                break;
        case 80:
-               h->rte_hash_cmp_eq = rte_hash_k80_cmp_eq;
+               h->cmp_jump_table_idx = KEY_80_BYTES;
                break;
        case 96:
                break;
        case 96:
-               h->rte_hash_cmp_eq = rte_hash_k96_cmp_eq;
+               h->cmp_jump_table_idx = KEY_96_BYTES;
                break;
        case 112:
                break;
        case 112:
-               h->rte_hash_cmp_eq = rte_hash_k112_cmp_eq;
+               h->cmp_jump_table_idx = KEY_112_BYTES;
                break;
        case 128:
                break;
        case 128:
-               h->rte_hash_cmp_eq = rte_hash_k128_cmp_eq;
+               h->cmp_jump_table_idx = KEY_128_BYTES;
                break;
        default:
                /* If key is not multiple of 16, use generic memcmp */
                break;
        default:
                /* If key is not multiple of 16, use generic memcmp */
-               h->rte_hash_cmp_eq = memcmp;
+               h->cmp_jump_table_idx = KEY_OTHER_BYTES;
        }
 #else
        }
 #else
-       h->rte_hash_cmp_eq = memcmp;
+       h->cmp_jump_table_idx = KEY_OTHER_BYTES;
 #endif
 
        snprintf(ring_name, sizeof(ring_name), "HT_%s", params->name);
 #endif
 
        snprintf(ring_name, sizeof(ring_name), "HT_%s", params->name);
@@ -593,7 +662,7 @@ __rte_hash_add_key_with_hash(const struct rte_hash *h, const void *key,
                                prim_bkt->signatures[i].alt == alt_hash) {
                        k = (struct rte_hash_key *) ((char *)keys +
                                        prim_bkt->key_idx[i] * h->key_entry_size);
                                prim_bkt->signatures[i].alt == alt_hash) {
                        k = (struct rte_hash_key *) ((char *)keys +
                                        prim_bkt->key_idx[i] * h->key_entry_size);
-                       if (h->rte_hash_cmp_eq(key, k->key, h->key_len) == 0) {
+                       if (rte_hash_cmp_eq(key, k->key, h) == 0) {
                                /* Enqueue index of free slot back in the ring. */
                                enqueue_slot_back(h, cached_free_slots, slot_id);
                                /* Update data */
                                /* Enqueue index of free slot back in the ring. */
                                enqueue_slot_back(h, cached_free_slots, slot_id);
                                /* Update data */
@@ -613,7 +682,7 @@ __rte_hash_add_key_with_hash(const struct rte_hash *h, const void *key,
                                sec_bkt->signatures[i].current == alt_hash) {
                        k = (struct rte_hash_key *) ((char *)keys +
                                        sec_bkt->key_idx[i] * h->key_entry_size);
                                sec_bkt->signatures[i].current == alt_hash) {
                        k = (struct rte_hash_key *) ((char *)keys +
                                        sec_bkt->key_idx[i] * h->key_entry_size);
-                       if (h->rte_hash_cmp_eq(key, k->key, h->key_len) == 0) {
+                       if (rte_hash_cmp_eq(key, k->key, h) == 0) {
                                /* Enqueue index of free slot back in the ring. */
                                enqueue_slot_back(h, cached_free_slots, slot_id);
                                /* Update data */
                                /* Enqueue index of free slot back in the ring. */
                                enqueue_slot_back(h, cached_free_slots, slot_id);
                                /* Update data */
@@ -724,7 +793,7 @@ __rte_hash_lookup_with_hash(const struct rte_hash *h, const void *key,
                                bkt->signatures[i].sig != NULL_SIGNATURE) {
                        k = (struct rte_hash_key *) ((char *)keys +
                                        bkt->key_idx[i] * h->key_entry_size);
                                bkt->signatures[i].sig != NULL_SIGNATURE) {
                        k = (struct rte_hash_key *) ((char *)keys +
                                        bkt->key_idx[i] * h->key_entry_size);
-                       if (h->rte_hash_cmp_eq(key, k->key, h->key_len) == 0) {
+                       if (rte_hash_cmp_eq(key, k->key, h) == 0) {
                                if (data != NULL)
                                        *data = k->pdata;
                                /*
                                if (data != NULL)
                                        *data = k->pdata;
                                /*
@@ -747,7 +816,7 @@ __rte_hash_lookup_with_hash(const struct rte_hash *h, const void *key,
                                bkt->signatures[i].alt == sig) {
                        k = (struct rte_hash_key *) ((char *)keys +
                                        bkt->key_idx[i] * h->key_entry_size);
                                bkt->signatures[i].alt == sig) {
                        k = (struct rte_hash_key *) ((char *)keys +
                                        bkt->key_idx[i] * h->key_entry_size);
-                       if (h->rte_hash_cmp_eq(key, k->key, h->key_len) == 0) {
+                       if (rte_hash_cmp_eq(key, k->key, h) == 0) {
                                if (data != NULL)
                                        *data = k->pdata;
                                /*
                                if (data != NULL)
                                        *data = k->pdata;
                                /*
@@ -839,7 +908,7 @@ __rte_hash_del_key_with_hash(const struct rte_hash *h, const void *key,
                                bkt->signatures[i].sig != NULL_SIGNATURE) {
                        k = (struct rte_hash_key *) ((char *)keys +
                                        bkt->key_idx[i] * h->key_entry_size);
                                bkt->signatures[i].sig != NULL_SIGNATURE) {
                        k = (struct rte_hash_key *) ((char *)keys +
                                        bkt->key_idx[i] * h->key_entry_size);
-                       if (h->rte_hash_cmp_eq(key, k->key, h->key_len) == 0) {
+                       if (rte_hash_cmp_eq(key, k->key, h) == 0) {
                                remove_entry(h, bkt, i);
 
                                /*
                                remove_entry(h, bkt, i);
 
                                /*
@@ -862,7 +931,7 @@ __rte_hash_del_key_with_hash(const struct rte_hash *h, const void *key,
                                bkt->signatures[i].sig != NULL_SIGNATURE) {
                        k = (struct rte_hash_key *) ((char *)keys +
                                        bkt->key_idx[i] * h->key_entry_size);
                                bkt->signatures[i].sig != NULL_SIGNATURE) {
                        k = (struct rte_hash_key *) ((char *)keys +
                                        bkt->key_idx[i] * h->key_entry_size);
-                       if (h->rte_hash_cmp_eq(key, k->key, h->key_len) == 0) {
+                       if (rte_hash_cmp_eq(key, k->key, h) == 0) {
                                remove_entry(h, bkt, i);
 
                                /*
                                remove_entry(h, bkt, i);
 
                                /*
@@ -979,7 +1048,7 @@ lookup_stage3(unsigned idx, const struct rte_hash_key *key_slot, const void * co
        unsigned hit;
        unsigned key_idx;
 
        unsigned hit;
        unsigned key_idx;
 
-       hit = !h->rte_hash_cmp_eq(key_slot->key, keys[idx], h->key_len);
+       hit = !rte_hash_cmp_eq(key_slot->key, keys[idx], h);
        if (data != NULL)
                data[idx] = key_slot->pdata;
 
        if (data != NULL)
                data[idx] = key_slot->pdata;