doc: add Meson coding style to contributors guide
[dpdk.git] / lib / librte_member / rte_member_ht.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2017 Intel Corporation
3  */
4
5 #include <rte_errno.h>
6 #include <rte_malloc.h>
7 #include <rte_prefetch.h>
8 #include <rte_random.h>
9 #include <rte_log.h>
10 #include <rte_vect.h>
11
12 #include "rte_member.h"
13 #include "rte_member_ht.h"
14
15 #if defined(RTE_ARCH_X86)
16 #include "rte_member_x86.h"
17 #endif
18
19 /* Search bucket for entry with tmp_sig and update set_id */
20 static inline int
21 update_entry_search(uint32_t bucket_id, member_sig_t tmp_sig,
22                 struct member_ht_bucket *buckets,
23                 member_set_t set_id)
24 {
25         uint32_t i;
26
27         for (i = 0; i < RTE_MEMBER_BUCKET_ENTRIES; i++) {
28                 if (buckets[bucket_id].sigs[i] == tmp_sig) {
29                         buckets[bucket_id].sets[i] = set_id;
30                         return 1;
31                 }
32         }
33         return 0;
34 }
35
36 static inline int
37 search_bucket_single(uint32_t bucket_id, member_sig_t tmp_sig,
38                 struct member_ht_bucket *buckets,
39                 member_set_t *set_id)
40 {
41         uint32_t iter;
42
43         for (iter = 0; iter < RTE_MEMBER_BUCKET_ENTRIES; iter++) {
44                 if (tmp_sig == buckets[bucket_id].sigs[iter] &&
45                                 buckets[bucket_id].sets[iter] !=
46                                 RTE_MEMBER_NO_MATCH) {
47                         *set_id = buckets[bucket_id].sets[iter];
48                         return 1;
49                 }
50         }
51         return 0;
52 }
53
54 static inline void
55 search_bucket_multi(uint32_t bucket_id, member_sig_t tmp_sig,
56                 struct member_ht_bucket *buckets,
57                 uint32_t *counter,
58                 uint32_t matches_per_key,
59                 member_set_t *set_id)
60 {
61         uint32_t iter;
62
63         for (iter = 0; iter < RTE_MEMBER_BUCKET_ENTRIES; iter++) {
64                 if (tmp_sig == buckets[bucket_id].sigs[iter] &&
65                                 buckets[bucket_id].sets[iter] !=
66                                 RTE_MEMBER_NO_MATCH) {
67                         set_id[*counter] = buckets[bucket_id].sets[iter];
68                         (*counter)++;
69                         if (*counter >= matches_per_key)
70                                 return;
71                 }
72         }
73 }
74
75 int
76 rte_member_create_ht(struct rte_member_setsum *ss,
77                 const struct rte_member_parameters *params)
78 {
79         uint32_t i, j;
80         uint32_t size_bucket_t;
81         uint32_t num_entries = rte_align32pow2(params->num_keys);
82
83         if ((num_entries > RTE_MEMBER_ENTRIES_MAX) ||
84                         !rte_is_power_of_2(RTE_MEMBER_BUCKET_ENTRIES) ||
85                         num_entries < RTE_MEMBER_BUCKET_ENTRIES) {
86                 rte_errno = EINVAL;
87                 RTE_MEMBER_LOG(ERR,
88                         "Membership HT create with invalid parameters\n");
89                 return -EINVAL;
90         }
91
92         uint32_t num_buckets = num_entries / RTE_MEMBER_BUCKET_ENTRIES;
93
94         size_bucket_t = sizeof(struct member_ht_bucket);
95
96         struct member_ht_bucket *buckets = rte_zmalloc_socket(NULL,
97                         num_buckets * size_bucket_t,
98                         RTE_CACHE_LINE_SIZE, ss->socket_id);
99
100         if (buckets == NULL) {
101                 RTE_MEMBER_LOG(ERR, "memory allocation failed for HT "
102                                                 "setsummary\n");
103                 return -ENOMEM;
104         }
105
106         ss->table = buckets;
107         ss->bucket_cnt = num_buckets;
108         ss->bucket_mask = num_buckets - 1;
109         ss->cache = params->is_cache;
110
111         for (i = 0; i < num_buckets; i++) {
112                 for (j = 0; j < RTE_MEMBER_BUCKET_ENTRIES; j++)
113                         buckets[i].sets[j] = RTE_MEMBER_NO_MATCH;
114         }
115 #if defined(RTE_ARCH_X86)
116         if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX2) &&
117                         RTE_MEMBER_BUCKET_ENTRIES == 16 &&
118                         rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_256)
119                 ss->sig_cmp_fn = RTE_MEMBER_COMPARE_AVX2;
120         else
121 #endif
122                 ss->sig_cmp_fn = RTE_MEMBER_COMPARE_SCALAR;
123
124         RTE_MEMBER_LOG(DEBUG, "Hash table based filter created, "
125                         "the table has %u entries, %u buckets\n",
126                         num_entries, num_buckets);
127         return 0;
128 }
129
130 static inline void
131 get_buckets_index(const struct rte_member_setsum *ss, const void *key,
132                 uint32_t *prim_bkt, uint32_t *sec_bkt, member_sig_t *sig)
133 {
134         uint32_t first_hash = MEMBER_HASH_FUNC(key, ss->key_len,
135                                                 ss->prim_hash_seed);
136         uint32_t sec_hash = MEMBER_HASH_FUNC(&first_hash, sizeof(uint32_t),
137                                                 ss->sec_hash_seed);
138         /*
139          * We use the first hash value for the signature, and the second hash
140          * value to derive the primary and secondary bucket locations.
141          *
142          * For non-cache mode, we use the lower bits for the primary bucket
143          * location. Then we xor primary bucket location and the signature
144          * to get the secondary bucket location. This is called "partial-key
145          * cuckoo hashing" proposed by B. Fan, et al's paper
146          * "Cuckoo Filter: Practically Better Than Bloom". The benefit to use
147          * xor is that one could derive the alternative bucket location
148          * by only using the current bucket location and the signature. This is
149          * generally required by non-cache mode's eviction and deletion
150          * process without the need to store alternative hash value nor the full
151          * key.
152          *
153          * For cache mode, we use the lower bits for the primary bucket
154          * location and the higher bits for the secondary bucket location. In
155          * cache mode, keys are simply overwritten if bucket is full. We do not
156          * use xor since lower/higher bits are more independent hash values thus
157          * should provide slightly better table load.
158          */
159         *sig = first_hash;
160         if (ss->cache) {
161                 *prim_bkt = sec_hash & ss->bucket_mask;
162                 *sec_bkt =  (sec_hash >> 16) & ss->bucket_mask;
163         } else {
164                 *prim_bkt = sec_hash & ss->bucket_mask;
165                 *sec_bkt =  (*prim_bkt ^ *sig) & ss->bucket_mask;
166         }
167 }
168
169 int
170 rte_member_lookup_ht(const struct rte_member_setsum *ss,
171                 const void *key, member_set_t *set_id)
172 {
173         uint32_t prim_bucket, sec_bucket;
174         member_sig_t tmp_sig;
175         struct member_ht_bucket *buckets = ss->table;
176
177         *set_id = RTE_MEMBER_NO_MATCH;
178         get_buckets_index(ss, key, &prim_bucket, &sec_bucket, &tmp_sig);
179
180         switch (ss->sig_cmp_fn) {
181 #if defined(RTE_ARCH_X86) && defined(__AVX2__)
182         case RTE_MEMBER_COMPARE_AVX2:
183                 if (search_bucket_single_avx(prim_bucket, tmp_sig, buckets,
184                                 set_id) ||
185                                 search_bucket_single_avx(sec_bucket, tmp_sig,
186                                         buckets, set_id))
187                         return 1;
188                 break;
189 #endif
190         default:
191                 if (search_bucket_single(prim_bucket, tmp_sig, buckets,
192                                 set_id) ||
193                                 search_bucket_single(sec_bucket, tmp_sig,
194                                         buckets, set_id))
195                         return 1;
196         }
197
198         return 0;
199 }
200
201 uint32_t
202 rte_member_lookup_bulk_ht(const struct rte_member_setsum *ss,
203                 const void **keys, uint32_t num_keys, member_set_t *set_id)
204 {
205         uint32_t i;
206         uint32_t num_matches = 0;
207         struct member_ht_bucket *buckets = ss->table;
208         member_sig_t tmp_sig[RTE_MEMBER_LOOKUP_BULK_MAX];
209         uint32_t prim_buckets[RTE_MEMBER_LOOKUP_BULK_MAX];
210         uint32_t sec_buckets[RTE_MEMBER_LOOKUP_BULK_MAX];
211
212         for (i = 0; i < num_keys; i++) {
213                 get_buckets_index(ss, keys[i], &prim_buckets[i],
214                                 &sec_buckets[i], &tmp_sig[i]);
215                 rte_prefetch0(&buckets[prim_buckets[i]]);
216                 rte_prefetch0(&buckets[sec_buckets[i]]);
217         }
218
219         for (i = 0; i < num_keys; i++) {
220                 switch (ss->sig_cmp_fn) {
221 #if defined(RTE_ARCH_X86) && defined(__AVX2__)
222                 case RTE_MEMBER_COMPARE_AVX2:
223                         if (search_bucket_single_avx(prim_buckets[i],
224                                         tmp_sig[i], buckets, &set_id[i]) ||
225                                 search_bucket_single_avx(sec_buckets[i],
226                                         tmp_sig[i], buckets, &set_id[i]))
227                                 num_matches++;
228                         else
229                                 set_id[i] = RTE_MEMBER_NO_MATCH;
230                         break;
231 #endif
232                 default:
233                         if (search_bucket_single(prim_buckets[i], tmp_sig[i],
234                                         buckets, &set_id[i]) ||
235                                         search_bucket_single(sec_buckets[i],
236                                         tmp_sig[i], buckets, &set_id[i]))
237                                 num_matches++;
238                         else
239                                 set_id[i] = RTE_MEMBER_NO_MATCH;
240                 }
241         }
242         return num_matches;
243 }
244
245 uint32_t
246 rte_member_lookup_multi_ht(const struct rte_member_setsum *ss,
247                 const void *key, uint32_t match_per_key,
248                 member_set_t *set_id)
249 {
250         uint32_t num_matches = 0;
251         uint32_t prim_bucket, sec_bucket;
252         member_sig_t tmp_sig;
253         struct member_ht_bucket *buckets = ss->table;
254
255         get_buckets_index(ss, key, &prim_bucket, &sec_bucket, &tmp_sig);
256
257         switch (ss->sig_cmp_fn) {
258 #if defined(RTE_ARCH_X86) && defined(__AVX2__)
259         case RTE_MEMBER_COMPARE_AVX2:
260                 search_bucket_multi_avx(prim_bucket, tmp_sig, buckets,
261                         &num_matches, match_per_key, set_id);
262                 if (num_matches < match_per_key)
263                         search_bucket_multi_avx(sec_bucket, tmp_sig,
264                                 buckets, &num_matches, match_per_key, set_id);
265                 return num_matches;
266 #endif
267         default:
268                 search_bucket_multi(prim_bucket, tmp_sig, buckets, &num_matches,
269                                  match_per_key, set_id);
270                 if (num_matches < match_per_key)
271                         search_bucket_multi(sec_bucket, tmp_sig,
272                                 buckets, &num_matches, match_per_key, set_id);
273                 return num_matches;
274         }
275 }
276
277 uint32_t
278 rte_member_lookup_multi_bulk_ht(const struct rte_member_setsum *ss,
279                 const void **keys, uint32_t num_keys, uint32_t match_per_key,
280                 uint32_t *match_count,
281                 member_set_t *set_ids)
282 {
283         uint32_t i;
284         uint32_t num_matches = 0;
285         struct member_ht_bucket *buckets = ss->table;
286         uint32_t match_cnt_tmp;
287         member_sig_t tmp_sig[RTE_MEMBER_LOOKUP_BULK_MAX];
288         uint32_t prim_buckets[RTE_MEMBER_LOOKUP_BULK_MAX];
289         uint32_t sec_buckets[RTE_MEMBER_LOOKUP_BULK_MAX];
290
291         for (i = 0; i < num_keys; i++) {
292                 get_buckets_index(ss, keys[i], &prim_buckets[i],
293                                 &sec_buckets[i], &tmp_sig[i]);
294                 rte_prefetch0(&buckets[prim_buckets[i]]);
295                 rte_prefetch0(&buckets[sec_buckets[i]]);
296         }
297         for (i = 0; i < num_keys; i++) {
298                 match_cnt_tmp = 0;
299
300                 switch (ss->sig_cmp_fn) {
301 #if defined(RTE_ARCH_X86) && defined(__AVX2__)
302                 case RTE_MEMBER_COMPARE_AVX2:
303                         search_bucket_multi_avx(prim_buckets[i], tmp_sig[i],
304                                 buckets, &match_cnt_tmp, match_per_key,
305                                 &set_ids[i*match_per_key]);
306                         if (match_cnt_tmp < match_per_key)
307                                 search_bucket_multi_avx(sec_buckets[i],
308                                         tmp_sig[i], buckets, &match_cnt_tmp,
309                                         match_per_key,
310                                         &set_ids[i*match_per_key]);
311                         match_count[i] = match_cnt_tmp;
312                         if (match_cnt_tmp != 0)
313                                 num_matches++;
314                         break;
315 #endif
316                 default:
317                         search_bucket_multi(prim_buckets[i], tmp_sig[i],
318                                 buckets, &match_cnt_tmp, match_per_key,
319                                 &set_ids[i*match_per_key]);
320                         if (match_cnt_tmp < match_per_key)
321                                 search_bucket_multi(sec_buckets[i], tmp_sig[i],
322                                         buckets, &match_cnt_tmp, match_per_key,
323                                         &set_ids[i*match_per_key]);
324                         match_count[i] = match_cnt_tmp;
325                         if (match_cnt_tmp != 0)
326                                 num_matches++;
327                 }
328         }
329         return num_matches;
330 }
331
332 static inline int
333 try_insert(struct member_ht_bucket *buckets, uint32_t prim, uint32_t sec,
334                 member_sig_t sig, member_set_t set_id)
335 {
336         int i;
337         /* If not full then insert into one slot */
338         for (i = 0; i < RTE_MEMBER_BUCKET_ENTRIES; i++) {
339                 if (buckets[prim].sets[i] == RTE_MEMBER_NO_MATCH) {
340                         buckets[prim].sigs[i] = sig;
341                         buckets[prim].sets[i] = set_id;
342                         return 0;
343                 }
344         }
345         /* If prim failed, we need to access second bucket */
346         for (i = 0; i < RTE_MEMBER_BUCKET_ENTRIES; i++) {
347                 if (buckets[sec].sets[i] == RTE_MEMBER_NO_MATCH) {
348                         buckets[sec].sigs[i] = sig;
349                         buckets[sec].sets[i] = set_id;
350                         return 0;
351                 }
352         }
353         return -1;
354 }
355
356 static inline int
357 try_update(struct member_ht_bucket *buckets, uint32_t prim, uint32_t sec,
358                 member_sig_t sig, member_set_t set_id,
359                 enum rte_member_sig_compare_function cmp_fn)
360 {
361         switch (cmp_fn) {
362 #if defined(RTE_ARCH_X86) && defined(__AVX2__)
363         case RTE_MEMBER_COMPARE_AVX2:
364                 if (update_entry_search_avx(prim, sig, buckets, set_id) ||
365                                 update_entry_search_avx(sec, sig, buckets,
366                                         set_id))
367                         return 0;
368                 break;
369 #endif
370         default:
371                 if (update_entry_search(prim, sig, buckets, set_id) ||
372                                 update_entry_search(sec, sig, buckets,
373                                         set_id))
374                         return 0;
375         }
376         return -1;
377 }
378
379 static inline int
380 evict_from_bucket(void)
381 {
382         /* For now, we randomly pick one entry to evict */
383         return rte_rand() & (RTE_MEMBER_BUCKET_ENTRIES - 1);
384 }
385
386 /*
387  * This function is similar to the cuckoo hash make_space function in hash
388  * library
389  */
390 static inline int
391 make_space_bucket(const struct rte_member_setsum *ss, uint32_t bkt_idx,
392                         unsigned int *nr_pushes)
393 {
394         unsigned int i, j;
395         int ret;
396         struct member_ht_bucket *buckets = ss->table;
397         uint32_t next_bucket_idx;
398         struct member_ht_bucket *next_bkt[RTE_MEMBER_BUCKET_ENTRIES];
399         struct member_ht_bucket *bkt = &buckets[bkt_idx];
400         /* MSB is set to indicate if an entry has been already pushed */
401         member_set_t flag_mask = 1U << (sizeof(member_set_t) * 8 - 1);
402
403         /*
404          * Push existing item (search for bucket with space in
405          * alternative locations) to its alternative location
406          */
407         for (i = 0; i < RTE_MEMBER_BUCKET_ENTRIES; i++) {
408                 /* Search for space in alternative locations */
409                 next_bucket_idx = (bkt->sigs[i] ^ bkt_idx) & ss->bucket_mask;
410                 next_bkt[i] = &buckets[next_bucket_idx];
411                 for (j = 0; j < RTE_MEMBER_BUCKET_ENTRIES; j++) {
412                         if (next_bkt[i]->sets[j] == RTE_MEMBER_NO_MATCH)
413                                 break;
414                 }
415
416                 if (j != RTE_MEMBER_BUCKET_ENTRIES)
417                         break;
418         }
419
420         /* Alternative location has spare room (end of recursive function) */
421         if (i != RTE_MEMBER_BUCKET_ENTRIES) {
422                 next_bkt[i]->sigs[j] = bkt->sigs[i];
423                 next_bkt[i]->sets[j] = bkt->sets[i];
424                 return i;
425         }
426
427         /* Pick entry that has not been pushed yet */
428         for (i = 0; i < RTE_MEMBER_BUCKET_ENTRIES; i++)
429                 if ((bkt->sets[i] & flag_mask) == 0)
430                         break;
431
432         /* All entries have been pushed, so entry cannot be added */
433         if (i == RTE_MEMBER_BUCKET_ENTRIES ||
434                         ++(*nr_pushes) > RTE_MEMBER_MAX_PUSHES)
435                 return -ENOSPC;
436
437         next_bucket_idx = (bkt->sigs[i] ^ bkt_idx) & ss->bucket_mask;
438         /* Set flag to indicate that this entry is going to be pushed */
439         bkt->sets[i] |= flag_mask;
440
441         /* Need room in alternative bucket to insert the pushed entry */
442         ret = make_space_bucket(ss, next_bucket_idx, nr_pushes);
443         /*
444          * After recursive function.
445          * Clear flags and insert the pushed entry
446          * in its alternative location if successful,
447          * or return error
448          */
449         bkt->sets[i] &= ~flag_mask;
450         if (ret >= 0) {
451                 next_bkt[i]->sigs[ret] = bkt->sigs[i];
452                 next_bkt[i]->sets[ret] = bkt->sets[i];
453                 return i;
454         } else
455                 return ret;
456 }
457
458 int
459 rte_member_add_ht(const struct rte_member_setsum *ss,
460                 const void *key, member_set_t set_id)
461 {
462         int ret;
463         unsigned int nr_pushes = 0;
464         uint32_t prim_bucket, sec_bucket;
465         member_sig_t tmp_sig;
466         struct member_ht_bucket *buckets = ss->table;
467         member_set_t flag_mask = 1U << (sizeof(member_set_t) * 8 - 1);
468
469         if (set_id == RTE_MEMBER_NO_MATCH || (set_id & flag_mask) != 0)
470                 return -EINVAL;
471
472         get_buckets_index(ss, key, &prim_bucket, &sec_bucket, &tmp_sig);
473
474         /*
475          * If it is cache based setsummary, we try overwriting (updating)
476          * existing entry with the same signature first. In cache mode, we allow
477          * false negatives and only cache the most recent keys.
478          *
479          * For non-cache mode, we do not update existing entry with the same
480          * signature. This is because if two keys with same signature update
481          * each other, false negative may happen, which is not the expected
482          * behavior for non-cache setsummary.
483          */
484         if (ss->cache) {
485                 ret = try_update(buckets, prim_bucket, sec_bucket, tmp_sig,
486                                         set_id, ss->sig_cmp_fn);
487                 if (ret != -1)
488                         return ret;
489         }
490         /* If not full then insert into one slot */
491         ret = try_insert(buckets, prim_bucket, sec_bucket, tmp_sig, set_id);
492         if (ret != -1)
493                 return ret;
494
495         /* Random pick prim or sec for recursive displacement */
496         uint32_t select_bucket = (tmp_sig && 1U) ? prim_bucket : sec_bucket;
497         if (ss->cache) {
498                 ret = evict_from_bucket();
499                 buckets[select_bucket].sigs[ret] = tmp_sig;
500                 buckets[select_bucket].sets[ret] = set_id;
501                 return 1;
502         }
503
504         ret = make_space_bucket(ss, select_bucket, &nr_pushes);
505         if (ret >= 0) {
506                 buckets[select_bucket].sigs[ret] = tmp_sig;
507                 buckets[select_bucket].sets[ret] = set_id;
508                 ret = 1;
509         }
510
511         return ret;
512 }
513
514 void
515 rte_member_free_ht(struct rte_member_setsum *ss)
516 {
517         rte_free(ss->table);
518 }
519
520 int
521 rte_member_delete_ht(const struct rte_member_setsum *ss, const void *key,
522                 member_set_t set_id)
523 {
524         int i;
525         uint32_t prim_bucket, sec_bucket;
526         member_sig_t tmp_sig;
527         struct member_ht_bucket *buckets = ss->table;
528
529         get_buckets_index(ss, key, &prim_bucket, &sec_bucket, &tmp_sig);
530
531         for (i = 0; i < RTE_MEMBER_BUCKET_ENTRIES; i++) {
532                 if (tmp_sig == buckets[prim_bucket].sigs[i] &&
533                                 set_id == buckets[prim_bucket].sets[i]) {
534                         buckets[prim_bucket].sets[i] = RTE_MEMBER_NO_MATCH;
535                         return 0;
536                 }
537         }
538
539         for (i = 0; i < RTE_MEMBER_BUCKET_ENTRIES; i++) {
540                 if (tmp_sig == buckets[sec_bucket].sigs[i] &&
541                                 set_id == buckets[sec_bucket].sets[i]) {
542                         buckets[sec_bucket].sets[i] = RTE_MEMBER_NO_MATCH;
543                         return 0;
544                 }
545         }
546         return -ENOENT;
547 }
548
549 void
550 rte_member_reset_ht(const struct rte_member_setsum *ss)
551 {
552         uint32_t i, j;
553         struct member_ht_bucket *buckets = ss->table;
554
555         for (i = 0; i < ss->bucket_cnt; i++) {
556                 for (j = 0; j < RTE_MEMBER_BUCKET_ENTRIES; j++)
557                         buckets[i].sets[j] = RTE_MEMBER_NO_MATCH;
558         }
559 }