Previous implementation was lacking a function
to look up a burst of entries, given precalculated hash values.
This patch implements such function, quite useful for
looking up keys from packets that have precalculated hash values
from a 5-tuple key.

Added the function in the hash unit test as well.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch at intel.com>
---
 app/test/test_hash.c                 |  19 ++-
 lib/librte_hash/rte_cuckoo_hash.c    | 222 ++++++++++++++++++++++++++++++++++-
 lib/librte_hash/rte_hash.h           |  27 +++++
 lib/librte_hash/rte_hash_version.map |   8 ++
 4 files changed, 272 insertions(+), 4 deletions(-)

diff --git a/app/test/test_hash.c b/app/test/test_hash.c
index 0176219..52de1bd 100644
--- a/app/test/test_hash.c
+++ b/app/test/test_hash.c
@@ -456,6 +456,7 @@ static int test_five_keys(void)
 {
        struct rte_hash *handle;
        const void *key_array[5] = {0};
+       hash_sig_t hashes[5];
        int pos[5];
        int expected_pos[5];
        unsigned i;
@@ -475,12 +476,24 @@ static int test_five_keys(void)
        }

        /* Lookup */
-       for(i = 0; i < 5; i++)
+       for (i = 0; i < 5; i++) {
                key_array[i] = &keys[i];
+               hashes[i] = rte_hash_hash(handle, &keys[i]);
+       }

        ret = rte_hash_lookup_multi(handle, &key_array[0], 5, (int32_t *)pos);
-       if(ret == 0)
-               for(i = 0; i < 5; i++) {
+       if (ret == 0)
+               for (i = 0; i < 5; i++) {
+                       print_key_info("Lkp", key_array[i], pos[i]);
+                       RETURN_IF_ERROR(pos[i] != expected_pos[i],
+                                       "failed to find key (pos[%u]=%d)", i, 
pos[i]);
+               }
+
+       /* Lookup with precalculated hashes */
+       ret = rte_hash_lookup_multi_with_hash(handle, &key_array[0], hashes,
+                                       5, (int32_t *)pos);
+       if (ret == 0)
+               for (i = 0; i < 5; i++) {
                        print_key_info("Lkp", key_array[i], pos[i]);
                        RETURN_IF_ERROR(pos[i] != expected_pos[i],
                                        "failed to find key (pos[%u]=%d)", i, 
pos[i]);
diff --git a/lib/librte_hash/rte_cuckoo_hash.c 
b/lib/librte_hash/rte_cuckoo_hash.c
index e19b179..f1b6df0 100644
--- a/lib/librte_hash/rte_cuckoo_hash.c
+++ b/lib/librte_hash/rte_cuckoo_hash.c
@@ -709,6 +709,21 @@ lookup_stage0(unsigned *idx, uint64_t *lookup_mask,
        *lookup_mask &= ~(1llu << *idx);
 }

+/* Lookup bulk stage 0: Get primary hash value and calculate secondary hash 
value */
+static inline void
+lookup_stage0_with_hash(unsigned *idx, uint64_t *lookup_mask,
+               hash_sig_t *primary_hash, hash_sig_t *secondary_hash,
+               const hash_sig_t *hash_vals)
+{
+       *idx = __builtin_ctzl(*lookup_mask);
+       if (*lookup_mask == 0)
+               *idx = 0;
+
+       *primary_hash = hash_vals[*idx];
+       *secondary_hash = rte_hash_secondary_hash(*primary_hash);
+
+       *lookup_mask &= ~(1llu << *idx);
+}

 /* Lookup bulk stage 1: Prefetch primary/secondary buckets */
 static inline void
@@ -725,7 +740,7 @@ lookup_stage1(hash_sig_t primary_hash, hash_sig_t 
secondary_hash,
 }

 /*
- * Lookup bulk stage 2:  Search for match hashes in primary/secondary locations
+ * Lookup bulk stage 2: Search for match hashes in primary/secondary locations
  * and prefetch first key slot
  */
 static inline void
@@ -971,6 +986,198 @@ __rte_hash_lookup_bulk(const struct rte_hash *h, const 
void **keys,
        return 0;
 }

+static inline int
+__rte_hash_lookup_bulk_with_hash(const struct rte_hash *h, const void **keys,
+                       const hash_sig_t *hash_vals, uint32_t num_keys,
+                       int32_t *positions)
+{
+       uint64_t hits = 0;
+       uint64_t next_mask = 0;
+       uint64_t extra_hits_mask = 0;
+       uint64_t lookup_mask;
+       unsigned idx;
+       const void *key_store = h->key_store;
+
+       unsigned idx00, idx01, idx10, idx11, idx20, idx21, idx30, idx31;
+       const struct rte_hash_bucket *primary_bkt10, *primary_bkt11;
+       const struct rte_hash_bucket *secondary_bkt10, *secondary_bkt11;
+       const struct rte_hash_bucket *primary_bkt20, *primary_bkt21;
+       const struct rte_hash_bucket *secondary_bkt20, *secondary_bkt21;
+       const void *k_slot20, *k_slot21, *k_slot30, *k_slot31;
+       hash_sig_t primary_hash00, primary_hash01;
+       hash_sig_t secondary_hash00, secondary_hash01;
+       hash_sig_t primary_hash10, primary_hash11;
+       hash_sig_t secondary_hash10, secondary_hash11;
+       hash_sig_t primary_hash20, primary_hash21;
+       hash_sig_t secondary_hash20, secondary_hash21;
+
+       if (num_keys == RTE_HASH_LOOKUP_BULK_MAX)
+               lookup_mask = 0xffffffffffffffff;
+       else
+               lookup_mask = (1ULL << num_keys) - 1;
+
+       lookup_stage0_with_hash(&idx00, &lookup_mask, &primary_hash00,
+                       &secondary_hash00, hash_vals);
+       lookup_stage0_with_hash(&idx01, &lookup_mask, &primary_hash01,
+                       &secondary_hash01, hash_vals);
+
+       primary_hash10 = primary_hash00;
+       primary_hash11 = primary_hash01;
+       secondary_hash10 = secondary_hash00;
+       secondary_hash11 = secondary_hash01;
+       idx10 = idx00, idx11 = idx01;
+
+       lookup_stage0_with_hash(&idx00, &lookup_mask, &primary_hash00,
+                       &secondary_hash00, hash_vals);
+       lookup_stage0_with_hash(&idx01, &lookup_mask, &primary_hash01,
+                       &secondary_hash01, hash_vals);
+       lookup_stage1(primary_hash10, secondary_hash10, &primary_bkt10,
+               &secondary_bkt10, h);
+       lookup_stage1(primary_hash11, secondary_hash11, &primary_bkt11,
+               &secondary_bkt11, h);
+
+       primary_bkt20 = primary_bkt10;
+       primary_bkt21 = primary_bkt11;
+       secondary_bkt20 = secondary_bkt10;
+       secondary_bkt21 = secondary_bkt11;
+       primary_hash20 = primary_hash10;
+       primary_hash21 = primary_hash11;
+       secondary_hash20 = secondary_hash10;
+       secondary_hash21 = secondary_hash11;
+       idx20 = idx10, idx21 = idx11;
+       primary_hash10 = primary_hash00;
+       primary_hash11 = primary_hash01;
+       secondary_hash10 = secondary_hash00;
+       secondary_hash11 = secondary_hash01;
+       idx10 = idx00, idx11 = idx01;
+
+       lookup_stage0_with_hash(&idx00, &lookup_mask, &primary_hash00,
+                       &secondary_hash00, hash_vals);
+       lookup_stage0_with_hash(&idx01, &lookup_mask, &primary_hash01,
+                       &secondary_hash01, hash_vals);
+       lookup_stage1(primary_hash10, secondary_hash10, &primary_bkt10,
+               &secondary_bkt10, h);
+       lookup_stage1(primary_hash11, secondary_hash11, &primary_bkt11,
+               &secondary_bkt11, h);
+       lookup_stage2(idx20, primary_hash20, secondary_hash20, primary_bkt20,
+               secondary_bkt20, &k_slot20, positions, &extra_hits_mask,
+               key_store, h);
+       lookup_stage2(idx21, primary_hash21, secondary_hash21, primary_bkt21,
+               secondary_bkt21, &k_slot21, positions, &extra_hits_mask,
+               key_store, h);
+
+       while (lookup_mask) {
+               k_slot30 = k_slot20, k_slot31 = k_slot21;
+               idx30 = idx20, idx31 = idx21;
+               primary_bkt20 = primary_bkt10;
+               primary_bkt21 = primary_bkt11;
+               secondary_bkt20 = secondary_bkt10;
+               secondary_bkt21 = secondary_bkt11;
+               primary_hash20 = primary_hash10;
+               primary_hash21 = primary_hash11;
+               secondary_hash20 = secondary_hash10;
+               secondary_hash21 = secondary_hash11;
+               idx20 = idx10, idx21 = idx11;
+               primary_hash10 = primary_hash00;
+               primary_hash11 = primary_hash01;
+               secondary_hash10 = secondary_hash00;
+               secondary_hash11 = secondary_hash01;
+               idx10 = idx00, idx11 = idx01;
+
+               lookup_stage0_with_hash(&idx00, &lookup_mask, &primary_hash00,
+                               &secondary_hash00, hash_vals);
+               lookup_stage0_with_hash(&idx01, &lookup_mask, &primary_hash01,
+                               &secondary_hash01, hash_vals);
+               lookup_stage1(primary_hash10, secondary_hash10,
+                       &primary_bkt10, &secondary_bkt10, h);
+               lookup_stage1(primary_hash11, secondary_hash11,
+                       &primary_bkt11, &secondary_bkt11, h);
+               lookup_stage2(idx20, primary_hash20, secondary_hash20,
+                       primary_bkt20, secondary_bkt20, &k_slot20, positions,
+                       &extra_hits_mask, key_store, h);
+               lookup_stage2(idx21, primary_hash21, secondary_hash21,
+                       primary_bkt21, secondary_bkt21, &k_slot21, positions,
+                       &extra_hits_mask, key_store, h);
+               lookup_stage3(idx30, k_slot30, keys, positions, &hits, h);
+               lookup_stage3(idx31, k_slot31, keys, positions, &hits, h);
+       }
+
+       k_slot30 = k_slot20, k_slot31 = k_slot21;
+       idx30 = idx20, idx31 = idx21;
+       primary_bkt20 = primary_bkt10;
+       primary_bkt21 = primary_bkt11;
+       secondary_bkt20 = secondary_bkt10;
+       secondary_bkt21 = secondary_bkt11;
+       primary_hash20 = primary_hash10;
+       primary_hash21 = primary_hash11;
+       secondary_hash20 = secondary_hash10;
+       secondary_hash21 = secondary_hash11;
+       idx20 = idx10, idx21 = idx11;
+       primary_hash10 = primary_hash00;
+       primary_hash11 = primary_hash01;
+       secondary_hash10 = secondary_hash00;
+       secondary_hash11 = secondary_hash01;
+       idx10 = idx00, idx11 = idx01;
+
+       lookup_stage1(primary_hash10, secondary_hash10, &primary_bkt10,
+               &secondary_bkt10, h);
+       lookup_stage1(primary_hash11, secondary_hash11, &primary_bkt11,
+               &secondary_bkt11, h);
+       lookup_stage2(idx20, primary_hash20, secondary_hash20, primary_bkt20,
+               secondary_bkt20, &k_slot20, positions, &extra_hits_mask,
+               key_store, h);
+       lookup_stage2(idx21, primary_hash21, secondary_hash21, primary_bkt21,
+               secondary_bkt21, &k_slot21, positions, &extra_hits_mask,
+               key_store, h);
+       lookup_stage3(idx30, k_slot30, keys, positions, &hits, h);
+       lookup_stage3(idx31, k_slot31, keys, positions, &hits, h);
+
+       k_slot30 = k_slot20, k_slot31 = k_slot21;
+       idx30 = idx20, idx31 = idx21;
+       primary_bkt20 = primary_bkt10;
+       primary_bkt21 = primary_bkt11;
+       secondary_bkt20 = secondary_bkt10;
+       secondary_bkt21 = secondary_bkt11;
+       primary_hash20 = primary_hash10;
+       primary_hash21 = primary_hash11;
+       secondary_hash20 = secondary_hash10;
+       secondary_hash21 = secondary_hash11;
+       idx20 = idx10, idx21 = idx11;
+
+       lookup_stage2(idx20, primary_hash20, secondary_hash20, primary_bkt20,
+               secondary_bkt20, &k_slot20, positions, &extra_hits_mask,
+               key_store, h);
+       lookup_stage2(idx21, primary_hash21, secondary_hash21, primary_bkt21,
+               secondary_bkt21, &k_slot21, positions, &extra_hits_mask,
+               key_store, h);
+       lookup_stage3(idx30, k_slot30, keys, positions, &hits, h);
+       lookup_stage3(idx31, k_slot31, keys, positions, &hits, h);
+
+       k_slot30 = k_slot20, k_slot31 = k_slot21;
+       idx30 = idx20, idx31 = idx21;
+
+       lookup_stage3(idx30, k_slot30, keys, positions, &hits, h);
+       lookup_stage3(idx31, k_slot31, keys, positions, &hits, h);
+
+       /* handle extra_hits_mask */
+       next_mask |= extra_hits_mask;
+
+       /* ignore any items we have already found */
+       next_mask &= ~hits;
+
+       if (unlikely(next_mask)) {
+               /* run a single search for each remaining item */
+               do {
+                       idx = __builtin_ctzl(next_mask);
+                       positions[idx] = rte_hash_lookup_with_hash(h, keys[idx],
+                                                               hash_vals[idx]);
+                       next_mask &= ~(1llu << idx);
+               } while (next_mask);
+       }
+
+       return 0;
+}
+
 int
 rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys,
                      uint32_t num_keys, int32_t *positions)
@@ -982,6 +1189,19 @@ rte_hash_lookup_bulk(const struct rte_hash *h, const void 
**keys,
        return __rte_hash_lookup_bulk(h, keys, num_keys, positions);
 }

+int
+rte_hash_lookup_bulk_with_hash(const struct rte_hash *h, const void **keys,
+                       const hash_sig_t *hash_vals, uint32_t num_keys,
+                       int32_t *positions)
+{
+       RETURN_IF_TRUE(((h == NULL) || (keys == NULL) || (num_keys == 0) ||
+                       (num_keys > RTE_HASH_LOOKUP_BULK_MAX) ||
+                       (positions == NULL)), -EINVAL);
+
+       return __rte_hash_lookup_bulk_with_hash(h, keys, hash_vals, num_keys,
+                                       positions);
+}
+
 /* Functions to compare multiple of 16 byte keys (up to 128 bytes) */
 static int
 rte_hash_k16_cmp_eq(const void *key1, const void *key2, size_t key_len 
__rte_unused)
diff --git a/lib/librte_hash/rte_hash.h b/lib/librte_hash/rte_hash.h
index 13fad73..7f7e75f 100644
--- a/lib/librte_hash/rte_hash.h
+++ b/lib/librte_hash/rte_hash.h
@@ -263,6 +263,7 @@ hash_sig_t
 rte_hash_hash(const struct rte_hash *h, const void *key);

 #define rte_hash_lookup_multi rte_hash_lookup_bulk
+#define rte_hash_lookup_multi_with_hash rte_hash_lookup_bulk_with_hash
 /**
  * Find multiple keys in the hash table.
  * This operation is multi-thread safe.
@@ -286,6 +287,32 @@ int
 rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys,
                      uint32_t num_keys, int32_t *positions);

+/**
+ * Find multiple keys in the hash table.
+ * This operation is multi-thread safe.
+ *
+ * @param h
+ *   Hash table to look in.
+ * @param keys
+ *   A pointer to a list of keys to look for.
+ * @param hash_vals
+ *   A pointer to a list of pre-calculated hash values.
+ * @param num_keys
+ *   How many keys are in the keys list (less than RTE_HASH_LOOKUP_BULK_MAX).
+ * @param positions
+ *   Output containing a list of values, corresponding to the list of keys that
+ *   can be used by the caller as an offset into an array of user data. These
+ *   values are unique for each key, and are the same values that were returned
+ *   when each key was added. If a key in the list was not found, then -ENOENT
+ *   will be the value.
+ * @return
+ *   -EINVAL if there's an error, otherwise 0.
+ */
+int
+rte_hash_lookup_bulk_with_hash(const struct rte_hash *h, const void **keys,
+                               const hash_sig_t *hash_vals, uint32_t num_keys,
+                               int32_t *positions);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_hash/rte_hash_version.map 
b/lib/librte_hash/rte_hash_version.map
index 0b749e8..fd92def 100644
--- a/lib/librte_hash/rte_hash_version.map
+++ b/lib/librte_hash/rte_hash_version.map
@@ -17,3 +17,11 @@ DPDK_2.0 {

        local: *;
 };
+
+DPDK_2.1 {
+       global:
+
+       rte_hash_lookup_bulk_with_hash;
+
+       local: *;
+} DPDK_2.0;
-- 
2.4.2

Reply via email to