1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2016 Intel Corporation 3 * Copyright(c) 2018 Arm Limited 4 */ 5 6 #include <string.h> 7 #include <stdint.h> 8 #include <errno.h> 9 #include <stdio.h> 10 #include <stdarg.h> 11 #include <sys/queue.h> 12 13 #include <rte_common.h> 14 #include <rte_memory.h> /* for definition of RTE_CACHE_LINE_SIZE */ 15 #include <rte_log.h> 16 #include <rte_prefetch.h> 17 #include <rte_branch_prediction.h> 18 #include <rte_malloc.h> 19 #include <rte_eal.h> 20 #include <rte_eal_memconfig.h> 21 #include <rte_per_lcore.h> 22 #include <rte_errno.h> 23 #include <rte_string_fns.h> 24 #include <rte_cpuflags.h> 25 #include <rte_rwlock.h> 26 #include <rte_spinlock.h> 27 #include <rte_ring_elem.h> 28 #include <rte_compat.h> 29 #include <rte_vect.h> 30 #include <rte_tailq.h> 31 32 #include "rte_hash.h" 33 #include "rte_cuckoo_hash.h" 34 35 /* Mask of all flags supported by this version */ 36 #define RTE_HASH_EXTRA_FLAGS_MASK (RTE_HASH_EXTRA_FLAGS_TRANS_MEM_SUPPORT | \ 37 RTE_HASH_EXTRA_FLAGS_MULTI_WRITER_ADD | \ 38 RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY | \ 39 RTE_HASH_EXTRA_FLAGS_EXT_TABLE | \ 40 RTE_HASH_EXTRA_FLAGS_NO_FREE_ON_DEL | \ 41 RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF) 42 43 #define FOR_EACH_BUCKET(CURRENT_BKT, START_BUCKET) \ 44 for (CURRENT_BKT = START_BUCKET; \ 45 CURRENT_BKT != NULL; \ 46 CURRENT_BKT = CURRENT_BKT->next) 47 48 TAILQ_HEAD(rte_hash_list, rte_tailq_entry); 49 50 static struct rte_tailq_elem rte_hash_tailq = { 51 .name = "RTE_HASH", 52 }; 53 EAL_REGISTER_TAILQ(rte_hash_tailq) 54 55 struct __rte_hash_rcu_dq_entry { 56 uint32_t key_idx; 57 uint32_t ext_bkt_idx; 58 }; 59 60 struct rte_hash * 61 rte_hash_find_existing(const char *name) 62 { 63 struct rte_hash *h = NULL; 64 struct rte_tailq_entry *te; 65 struct rte_hash_list *hash_list; 66 67 hash_list = RTE_TAILQ_CAST(rte_hash_tailq.head, rte_hash_list); 68 69 rte_mcfg_tailq_read_lock(); 70 TAILQ_FOREACH(te, hash_list, next) { 71 h = (struct rte_hash *) te->data; 72 if (strncmp(name, h->name, RTE_HASH_NAMESIZE) == 0) 73 break; 74 } 75 rte_mcfg_tailq_read_unlock(); 76 77 if (te == NULL) { 78 rte_errno = ENOENT; 79 return NULL; 80 } 81 return h; 82 } 83 84 static inline struct rte_hash_bucket * 85 rte_hash_get_last_bkt(struct rte_hash_bucket *lst_bkt) 86 { 87 while (lst_bkt->next != NULL) 88 lst_bkt = lst_bkt->next; 89 return lst_bkt; 90 } 91 92 void rte_hash_set_cmp_func(struct rte_hash *h, rte_hash_cmp_eq_t func) 93 { 94 h->cmp_jump_table_idx = KEY_CUSTOM; 95 h->rte_hash_custom_cmp_eq = func; 96 } 97 98 static inline int 99 rte_hash_cmp_eq(const void *key1, const void *key2, const struct rte_hash *h) 100 { 101 if (h->cmp_jump_table_idx == KEY_CUSTOM) 102 return h->rte_hash_custom_cmp_eq(key1, key2, h->key_len); 103 else 104 return cmp_jump_table[h->cmp_jump_table_idx](key1, key2, h->key_len); 105 } 106 107 /* 108 * We use higher 16 bits of hash as the signature value stored in table. 109 * We use the lower bits for the primary bucket 110 * location. Then we XOR primary bucket location and the signature 111 * to get the secondary bucket location. This is same as 112 * proposed in Bin Fan, et al's paper 113 * "MemC3: Compact and Concurrent MemCache with Dumber Caching and 114 * Smarter Hashing". The benefit to use 115 * XOR is that one could derive the alternative bucket location 116 * by only using the current bucket location and the signature. 117 */ 118 static inline uint16_t 119 get_short_sig(const hash_sig_t hash) 120 { 121 return hash >> 16; 122 } 123 124 static inline uint32_t 125 get_prim_bucket_index(const struct rte_hash *h, const hash_sig_t hash) 126 { 127 return hash & h->bucket_bitmask; 128 } 129 130 static inline uint32_t 131 get_alt_bucket_index(const struct rte_hash *h, 132 uint32_t cur_bkt_idx, uint16_t sig) 133 { 134 return (cur_bkt_idx ^ sig) & h->bucket_bitmask; 135 } 136 137 struct rte_hash * 138 rte_hash_create(const struct rte_hash_parameters *params) 139 { 140 struct rte_hash *h = NULL; 141 struct rte_tailq_entry *te = NULL; 142 struct rte_hash_list *hash_list; 143 struct rte_ring *r = NULL; 144 struct rte_ring *r_ext = NULL; 145 char hash_name[RTE_HASH_NAMESIZE]; 146 void *k = NULL; 147 void *buckets = NULL; 148 void *buckets_ext = NULL; 149 char ring_name[RTE_RING_NAMESIZE]; 150 char ext_ring_name[RTE_RING_NAMESIZE]; 151 unsigned num_key_slots; 152 unsigned int hw_trans_mem_support = 0, use_local_cache = 0; 153 unsigned int ext_table_support = 0; 154 unsigned int readwrite_concur_support = 0; 155 unsigned int writer_takes_lock = 0; 156 unsigned int no_free_on_del = 0; 157 uint32_t *ext_bkt_to_free = NULL; 158 uint32_t *tbl_chng_cnt = NULL; 159 struct lcore_cache *local_free_slots = NULL; 160 unsigned int readwrite_concur_lf_support = 0; 161 uint32_t i; 162 163 rte_hash_function default_hash_func = (rte_hash_function)rte_jhash; 164 165 hash_list = RTE_TAILQ_CAST(rte_hash_tailq.head, rte_hash_list); 166 167 if (params == NULL) { 168 RTE_LOG(ERR, HASH, "rte_hash_create has no parameters\n"); 169 return NULL; 170 } 171 172 /* Check for valid parameters */ 173 if ((params->entries > RTE_HASH_ENTRIES_MAX) || 174 (params->entries < RTE_HASH_BUCKET_ENTRIES) || 175 (params->key_len == 0)) { 176 rte_errno = EINVAL; 177 RTE_LOG(ERR, HASH, "rte_hash_create has invalid parameters\n"); 178 return NULL; 179 } 180 181 if (params->extra_flag & ~RTE_HASH_EXTRA_FLAGS_MASK) { 182 rte_errno = EINVAL; 183 RTE_LOG(ERR, HASH, "rte_hash_create: unsupported extra flags\n"); 184 return NULL; 185 } 186 187 /* Validate correct usage of extra options */ 188 if ((params->extra_flag & RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY) && 189 (params->extra_flag & RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF)) { 190 rte_errno = EINVAL; 191 RTE_LOG(ERR, HASH, "rte_hash_create: choose rw concurrency or " 192 "rw concurrency lock free\n"); 193 return NULL; 194 } 195 196 /* Check extra flags field to check extra options. */ 197 if (params->extra_flag & RTE_HASH_EXTRA_FLAGS_TRANS_MEM_SUPPORT) 198 hw_trans_mem_support = 1; 199 200 if (params->extra_flag & RTE_HASH_EXTRA_FLAGS_MULTI_WRITER_ADD) { 201 use_local_cache = 1; 202 writer_takes_lock = 1; 203 } 204 205 if (params->extra_flag & RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY) { 206 readwrite_concur_support = 1; 207 writer_takes_lock = 1; 208 } 209 210 if (params->extra_flag & RTE_HASH_EXTRA_FLAGS_EXT_TABLE) 211 ext_table_support = 1; 212 213 if (params->extra_flag & RTE_HASH_EXTRA_FLAGS_NO_FREE_ON_DEL) 214 no_free_on_del = 1; 215 216 if (params->extra_flag & RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF) { 217 readwrite_concur_lf_support = 1; 218 /* Enable not freeing internal memory/index on delete. 219 * If internal RCU is enabled, freeing of internal memory/index 220 * is done on delete 221 */ 222 no_free_on_del = 1; 223 } 224 225 /* Store all keys and leave the first entry as a dummy entry for lookup_bulk */ 226 if (use_local_cache) 227 /* 228 * Increase number of slots by total number of indices 229 * that can be stored in the lcore caches 230 * except for the first cache 231 */ 232 num_key_slots = params->entries + (RTE_MAX_LCORE - 1) * 233 (LCORE_CACHE_SIZE - 1) + 1; 234 else 235 num_key_slots = params->entries + 1; 236 237 snprintf(ring_name, sizeof(ring_name), "HT_%s", params->name); 238 /* Create ring (Dummy slot index is not enqueued) */ 239 r = rte_ring_create_elem(ring_name, sizeof(uint32_t), 240 rte_align32pow2(num_key_slots), params->socket_id, 0); 241 if (r == NULL) { 242 RTE_LOG(ERR, HASH, "memory allocation failed\n"); 243 goto err; 244 } 245 246 const uint32_t num_buckets = rte_align32pow2(params->entries) / 247 RTE_HASH_BUCKET_ENTRIES; 248 249 /* Create ring for extendable buckets. */ 250 if (ext_table_support) { 251 snprintf(ext_ring_name, sizeof(ext_ring_name), "HT_EXT_%s", 252 params->name); 253 r_ext = rte_ring_create_elem(ext_ring_name, sizeof(uint32_t), 254 rte_align32pow2(num_buckets + 1), 255 params->socket_id, 0); 256 257 if (r_ext == NULL) { 258 RTE_LOG(ERR, HASH, "ext buckets memory allocation " 259 "failed\n"); 260 goto err; 261 } 262 } 263 264 snprintf(hash_name, sizeof(hash_name), "HT_%s", params->name); 265 266 rte_mcfg_tailq_write_lock(); 267 268 /* guarantee there's no existing: this is normally already checked 269 * by ring creation above */ 270 TAILQ_FOREACH(te, hash_list, next) { 271 h = (struct rte_hash *) te->data; 272 if (strncmp(params->name, h->name, RTE_HASH_NAMESIZE) == 0) 273 break; 274 } 275 h = NULL; 276 if (te != NULL) { 277 rte_errno = EEXIST; 278 te = NULL; 279 goto err_unlock; 280 } 281 282 te = rte_zmalloc("HASH_TAILQ_ENTRY", sizeof(*te), 0); 283 if (te == NULL) { 284 RTE_LOG(ERR, HASH, "tailq entry allocation failed\n"); 285 goto err_unlock; 286 } 287 288 h = (struct rte_hash *)rte_zmalloc_socket(hash_name, sizeof(struct rte_hash), 289 RTE_CACHE_LINE_SIZE, params->socket_id); 290 291 if (h == NULL) { 292 RTE_LOG(ERR, HASH, "memory allocation failed\n"); 293 goto err_unlock; 294 } 295 296 buckets = rte_zmalloc_socket(NULL, 297 num_buckets * sizeof(struct rte_hash_bucket), 298 RTE_CACHE_LINE_SIZE, params->socket_id); 299 300 if (buckets == NULL) { 301 RTE_LOG(ERR, HASH, "buckets memory allocation failed\n"); 302 goto err_unlock; 303 } 304 305 /* Allocate same number of extendable buckets */ 306 if (ext_table_support) { 307 buckets_ext = rte_zmalloc_socket(NULL, 308 num_buckets * sizeof(struct rte_hash_bucket), 309 RTE_CACHE_LINE_SIZE, params->socket_id); 310 if (buckets_ext == NULL) { 311 RTE_LOG(ERR, HASH, "ext buckets memory allocation " 312 "failed\n"); 313 goto err_unlock; 314 } 315 /* Populate ext bkt ring. We reserve 0 similar to the 316 * key-data slot, just in case in future we want to 317 * use bucket index for the linked list and 0 means NULL 318 * for next bucket 319 */ 320 for (i = 1; i <= num_buckets; i++) 321 rte_ring_sp_enqueue_elem(r_ext, &i, sizeof(uint32_t)); 322 323 if (readwrite_concur_lf_support) { 324 ext_bkt_to_free = rte_zmalloc(NULL, sizeof(uint32_t) * 325 num_key_slots, 0); 326 if (ext_bkt_to_free == NULL) { 327 RTE_LOG(ERR, HASH, "ext bkt to free memory allocation " 328 "failed\n"); 329 goto err_unlock; 330 } 331 } 332 } 333 334 const uint32_t key_entry_size = 335 RTE_ALIGN(sizeof(struct rte_hash_key) + params->key_len, 336 KEY_ALIGNMENT); 337 const uint64_t key_tbl_size = (uint64_t) key_entry_size * num_key_slots; 338 339 k = rte_zmalloc_socket(NULL, key_tbl_size, 340 RTE_CACHE_LINE_SIZE, params->socket_id); 341 342 if (k == NULL) { 343 RTE_LOG(ERR, HASH, "memory allocation failed\n"); 344 goto err_unlock; 345 } 346 347 tbl_chng_cnt = rte_zmalloc_socket(NULL, sizeof(uint32_t), 348 RTE_CACHE_LINE_SIZE, params->socket_id); 349 350 if (tbl_chng_cnt == NULL) { 351 RTE_LOG(ERR, HASH, "memory allocation failed\n"); 352 goto err_unlock; 353 } 354 355 /* 356 * If x86 architecture is used, select appropriate compare function, 357 * which may use x86 intrinsics, otherwise use memcmp 358 */ 359 #if defined(RTE_ARCH_X86) || defined(RTE_ARCH_ARM64) 360 /* Select function to compare keys */ 361 switch (params->key_len) { 362 case 16: 363 h->cmp_jump_table_idx = KEY_16_BYTES; 364 break; 365 case 32: 366 h->cmp_jump_table_idx = KEY_32_BYTES; 367 break; 368 case 48: 369 h->cmp_jump_table_idx = KEY_48_BYTES; 370 break; 371 case 64: 372 h->cmp_jump_table_idx = KEY_64_BYTES; 373 break; 374 case 80: 375 h->cmp_jump_table_idx = KEY_80_BYTES; 376 break; 377 case 96: 378 h->cmp_jump_table_idx = KEY_96_BYTES; 379 break; 380 case 112: 381 h->cmp_jump_table_idx = KEY_112_BYTES; 382 break; 383 case 128: 384 h->cmp_jump_table_idx = KEY_128_BYTES; 385 break; 386 default: 387 /* If key is not multiple of 16, use generic memcmp */ 388 h->cmp_jump_table_idx = KEY_OTHER_BYTES; 389 } 390 #else 391 h->cmp_jump_table_idx = KEY_OTHER_BYTES; 392 #endif 393 394 if (use_local_cache) { 395 local_free_slots = rte_zmalloc_socket(NULL, 396 sizeof(struct lcore_cache) * RTE_MAX_LCORE, 397 RTE_CACHE_LINE_SIZE, params->socket_id); 398 if (local_free_slots == NULL) { 399 RTE_LOG(ERR, HASH, "local free slots memory allocation failed\n"); 400 goto err_unlock; 401 } 402 } 403 404 /* Default hash function */ 405 #if defined(RTE_ARCH_X86) 406 default_hash_func = (rte_hash_function)rte_hash_crc; 407 #elif defined(RTE_ARCH_ARM64) 408 if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_CRC32)) 409 default_hash_func = (rte_hash_function)rte_hash_crc; 410 #endif 411 /* Setup hash context */ 412 strlcpy(h->name, params->name, sizeof(h->name)); 413 h->entries = params->entries; 414 h->key_len = params->key_len; 415 h->key_entry_size = key_entry_size; 416 h->hash_func_init_val = params->hash_func_init_val; 417 418 h->num_buckets = num_buckets; 419 h->bucket_bitmask = h->num_buckets - 1; 420 h->buckets = buckets; 421 h->buckets_ext = buckets_ext; 422 h->free_ext_bkts = r_ext; 423 h->hash_func = (params->hash_func == NULL) ? 424 default_hash_func : params->hash_func; 425 h->key_store = k; 426 h->free_slots = r; 427 h->ext_bkt_to_free = ext_bkt_to_free; 428 h->tbl_chng_cnt = tbl_chng_cnt; 429 *h->tbl_chng_cnt = 0; 430 h->hw_trans_mem_support = hw_trans_mem_support; 431 h->use_local_cache = use_local_cache; 432 h->local_free_slots = local_free_slots; 433 h->readwrite_concur_support = readwrite_concur_support; 434 h->ext_table_support = ext_table_support; 435 h->writer_takes_lock = writer_takes_lock; 436 h->no_free_on_del = no_free_on_del; 437 h->readwrite_concur_lf_support = readwrite_concur_lf_support; 438 439 #if defined(RTE_ARCH_X86) 440 if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_SSE2)) 441 h->sig_cmp_fn = RTE_HASH_COMPARE_SSE; 442 else 443 #elif defined(RTE_ARCH_ARM64) 444 if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_NEON)) 445 h->sig_cmp_fn = RTE_HASH_COMPARE_NEON; 446 else 447 #endif 448 h->sig_cmp_fn = RTE_HASH_COMPARE_SCALAR; 449 450 /* Writer threads need to take the lock when: 451 * 1) RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY is enabled OR 452 * 2) RTE_HASH_EXTRA_FLAGS_MULTI_WRITER_ADD is enabled 453 */ 454 if (h->writer_takes_lock) { 455 h->readwrite_lock = rte_malloc(NULL, sizeof(rte_rwlock_t), 456 RTE_CACHE_LINE_SIZE); 457 if (h->readwrite_lock == NULL) 458 goto err_unlock; 459 460 rte_rwlock_init(h->readwrite_lock); 461 } 462 463 /* Populate free slots ring. Entry zero is reserved for key misses. */ 464 for (i = 1; i < num_key_slots; i++) 465 rte_ring_sp_enqueue_elem(r, &i, sizeof(uint32_t)); 466 467 te->data = (void *) h; 468 TAILQ_INSERT_TAIL(hash_list, te, next); 469 rte_mcfg_tailq_write_unlock(); 470 471 return h; 472 err_unlock: 473 rte_mcfg_tailq_write_unlock(); 474 err: 475 rte_ring_free(r); 476 rte_ring_free(r_ext); 477 rte_free(te); 478 rte_free(local_free_slots); 479 rte_free(h); 480 rte_free(buckets); 481 rte_free(buckets_ext); 482 rte_free(k); 483 rte_free(tbl_chng_cnt); 484 rte_free(ext_bkt_to_free); 485 return NULL; 486 } 487 488 void 489 rte_hash_free(struct rte_hash *h) 490 { 491 struct rte_tailq_entry *te; 492 struct rte_hash_list *hash_list; 493 494 if (h == NULL) 495 return; 496 497 hash_list = RTE_TAILQ_CAST(rte_hash_tailq.head, rte_hash_list); 498 499 rte_mcfg_tailq_write_lock(); 500 501 /* find out tailq entry */ 502 TAILQ_FOREACH(te, hash_list, next) { 503 if (te->data == (void *) h) 504 break; 505 } 506 507 if (te == NULL) { 508 rte_mcfg_tailq_write_unlock(); 509 return; 510 } 511 512 TAILQ_REMOVE(hash_list, te, next); 513 514 rte_mcfg_tailq_write_unlock(); 515 516 if (h->dq) 517 rte_rcu_qsbr_dq_delete(h->dq); 518 519 if (h->use_local_cache) 520 rte_free(h->local_free_slots); 521 if (h->writer_takes_lock) 522 rte_free(h->readwrite_lock); 523 rte_ring_free(h->free_slots); 524 rte_ring_free(h->free_ext_bkts); 525 rte_free(h->key_store); 526 rte_free(h->buckets); 527 rte_free(h->buckets_ext); 528 rte_free(h->tbl_chng_cnt); 529 rte_free(h->ext_bkt_to_free); 530 rte_free(h); 531 rte_free(te); 532 } 533 534 hash_sig_t 535 rte_hash_hash(const struct rte_hash *h, const void *key) 536 { 537 /* calc hash result by key */ 538 return h->hash_func(key, h->key_len, h->hash_func_init_val); 539 } 540 541 int32_t 542 rte_hash_max_key_id(const struct rte_hash *h) 543 { 544 RETURN_IF_TRUE((h == NULL), -EINVAL); 545 if (h->use_local_cache) 546 /* 547 * Increase number of slots by total number of indices 548 * that can be stored in the lcore caches 549 */ 550 return (h->entries + ((RTE_MAX_LCORE - 1) * 551 (LCORE_CACHE_SIZE - 1))); 552 else 553 return h->entries; 554 } 555 556 int32_t 557 rte_hash_count(const struct rte_hash *h) 558 { 559 uint32_t tot_ring_cnt, cached_cnt = 0; 560 uint32_t i, ret; 561 562 if (h == NULL) 563 return -EINVAL; 564 565 if (h->use_local_cache) { 566 tot_ring_cnt = h->entries + (RTE_MAX_LCORE - 1) * 567 (LCORE_CACHE_SIZE - 1); 568 for (i = 0; i < RTE_MAX_LCORE; i++) 569 cached_cnt += h->local_free_slots[i].len; 570 571 ret = tot_ring_cnt - rte_ring_count(h->free_slots) - 572 cached_cnt; 573 } else { 574 tot_ring_cnt = h->entries; 575 ret = tot_ring_cnt - rte_ring_count(h->free_slots); 576 } 577 return ret; 578 } 579 580 /* Read write locks implemented using rte_rwlock */ 581 static inline void 582 __hash_rw_writer_lock(const struct rte_hash *h) 583 { 584 if (h->writer_takes_lock && h->hw_trans_mem_support) 585 rte_rwlock_write_lock_tm(h->readwrite_lock); 586 else if (h->writer_takes_lock) 587 rte_rwlock_write_lock(h->readwrite_lock); 588 } 589 590 static inline void 591 __hash_rw_reader_lock(const struct rte_hash *h) 592 { 593 if (h->readwrite_concur_support && h->hw_trans_mem_support) 594 rte_rwlock_read_lock_tm(h->readwrite_lock); 595 else if (h->readwrite_concur_support) 596 rte_rwlock_read_lock(h->readwrite_lock); 597 } 598 599 static inline void 600 __hash_rw_writer_unlock(const struct rte_hash *h) 601 { 602 if (h->writer_takes_lock && h->hw_trans_mem_support) 603 rte_rwlock_write_unlock_tm(h->readwrite_lock); 604 else if (h->writer_takes_lock) 605 rte_rwlock_write_unlock(h->readwrite_lock); 606 } 607 608 static inline void 609 __hash_rw_reader_unlock(const struct rte_hash *h) 610 { 611 if (h->readwrite_concur_support && h->hw_trans_mem_support) 612 rte_rwlock_read_unlock_tm(h->readwrite_lock); 613 else if (h->readwrite_concur_support) 614 rte_rwlock_read_unlock(h->readwrite_lock); 615 } 616 617 void 618 rte_hash_reset(struct rte_hash *h) 619 { 620 uint32_t tot_ring_cnt, i; 621 unsigned int pending; 622 623 if (h == NULL) 624 return; 625 626 __hash_rw_writer_lock(h); 627 628 if (h->dq) { 629 /* Reclaim all the resources */ 630 rte_rcu_qsbr_dq_reclaim(h->dq, ~0, NULL, &pending, NULL); 631 if (pending != 0) 632 RTE_LOG(ERR, HASH, "RCU reclaim all resources failed\n"); 633 } 634 635 memset(h->buckets, 0, h->num_buckets * sizeof(struct rte_hash_bucket)); 636 memset(h->key_store, 0, h->key_entry_size * (h->entries + 1)); 637 *h->tbl_chng_cnt = 0; 638 639 /* reset the free ring */ 640 rte_ring_reset(h->free_slots); 641 642 /* flush free extendable bucket ring and memory */ 643 if (h->ext_table_support) { 644 memset(h->buckets_ext, 0, h->num_buckets * 645 sizeof(struct rte_hash_bucket)); 646 rte_ring_reset(h->free_ext_bkts); 647 } 648 649 /* Repopulate the free slots ring. Entry zero is reserved for key misses */ 650 if (h->use_local_cache) 651 tot_ring_cnt = h->entries + (RTE_MAX_LCORE - 1) * 652 (LCORE_CACHE_SIZE - 1); 653 else 654 tot_ring_cnt = h->entries; 655 656 for (i = 1; i < tot_ring_cnt + 1; i++) 657 rte_ring_sp_enqueue_elem(h->free_slots, &i, sizeof(uint32_t)); 658 659 /* Repopulate the free ext bkt ring. */ 660 if (h->ext_table_support) { 661 for (i = 1; i <= h->num_buckets; i++) 662 rte_ring_sp_enqueue_elem(h->free_ext_bkts, &i, 663 sizeof(uint32_t)); 664 } 665 666 if (h->use_local_cache) { 667 /* Reset local caches per lcore */ 668 for (i = 0; i < RTE_MAX_LCORE; i++) 669 h->local_free_slots[i].len = 0; 670 } 671 __hash_rw_writer_unlock(h); 672 } 673 674 /* 675 * Function called to enqueue back an index in the cache/ring, 676 * as slot has not being used and it can be used in the 677 * next addition attempt. 678 */ 679 static inline void 680 enqueue_slot_back(const struct rte_hash *h, 681 struct lcore_cache *cached_free_slots, 682 uint32_t slot_id) 683 { 684 if (h->use_local_cache) { 685 cached_free_slots->objs[cached_free_slots->len] = slot_id; 686 cached_free_slots->len++; 687 } else 688 rte_ring_sp_enqueue_elem(h->free_slots, &slot_id, 689 sizeof(uint32_t)); 690 } 691 692 /* Search a key from bucket and update its data. 693 * Writer holds the lock before calling this. 694 */ 695 static inline int32_t 696 search_and_update(const struct rte_hash *h, void *data, const void *key, 697 struct rte_hash_bucket *bkt, uint16_t sig) 698 { 699 int i; 700 struct rte_hash_key *k, *keys = h->key_store; 701 702 for (i = 0; i < RTE_HASH_BUCKET_ENTRIES; i++) { 703 if (bkt->sig_current[i] == sig) { 704 k = (struct rte_hash_key *) ((char *)keys + 705 bkt->key_idx[i] * h->key_entry_size); 706 if (rte_hash_cmp_eq(key, k->key, h) == 0) { 707 /* The store to application data at *data 708 * should not leak after the store to pdata 709 * in the key store. i.e. pdata is the guard 710 * variable. Release the application data 711 * to the readers. 712 */ 713 __atomic_store_n(&k->pdata, 714 data, 715 __ATOMIC_RELEASE); 716 /* 717 * Return index where key is stored, 718 * subtracting the first dummy index 719 */ 720 return bkt->key_idx[i] - 1; 721 } 722 } 723 } 724 return -1; 725 } 726 727 /* Only tries to insert at one bucket (@prim_bkt) without trying to push 728 * buckets around. 729 * return 1 if matching existing key, return 0 if succeeds, return -1 for no 730 * empty entry. 731 */ 732 static inline int32_t 733 rte_hash_cuckoo_insert_mw(const struct rte_hash *h, 734 struct rte_hash_bucket *prim_bkt, 735 struct rte_hash_bucket *sec_bkt, 736 const struct rte_hash_key *key, void *data, 737 uint16_t sig, uint32_t new_idx, 738 int32_t *ret_val) 739 { 740 unsigned int i; 741 struct rte_hash_bucket *cur_bkt; 742 int32_t ret; 743 744 __hash_rw_writer_lock(h); 745 /* Check if key was inserted after last check but before this 746 * protected region in case of inserting duplicated keys. 747 */ 748 ret = search_and_update(h, data, key, prim_bkt, sig); 749 if (ret != -1) { 750 __hash_rw_writer_unlock(h); 751 *ret_val = ret; 752 return 1; 753 } 754 755 FOR_EACH_BUCKET(cur_bkt, sec_bkt) { 756 ret = search_and_update(h, data, key, cur_bkt, sig); 757 if (ret != -1) { 758 __hash_rw_writer_unlock(h); 759 *ret_val = ret; 760 return 1; 761 } 762 } 763 764 /* Insert new entry if there is room in the primary 765 * bucket. 766 */ 767 for (i = 0; i < RTE_HASH_BUCKET_ENTRIES; i++) { 768 /* Check if slot is available */ 769 if (likely(prim_bkt->key_idx[i] == EMPTY_SLOT)) { 770 prim_bkt->sig_current[i] = sig; 771 /* Store to signature and key should not 772 * leak after the store to key_idx. i.e. 773 * key_idx is the guard variable for signature 774 * and key. 775 */ 776 __atomic_store_n(&prim_bkt->key_idx[i], 777 new_idx, 778 __ATOMIC_RELEASE); 779 break; 780 } 781 } 782 __hash_rw_writer_unlock(h); 783 784 if (i != RTE_HASH_BUCKET_ENTRIES) 785 return 0; 786 787 /* no empty entry */ 788 return -1; 789 } 790 791 /* Shift buckets along provided cuckoo_path (@leaf and @leaf_slot) and fill 792 * the path head with new entry (sig, alt_hash, new_idx) 793 * return 1 if matched key found, return -1 if cuckoo path invalided and fail, 794 * return 0 if succeeds. 795 */ 796 static inline int 797 rte_hash_cuckoo_move_insert_mw(const struct rte_hash *h, 798 struct rte_hash_bucket *bkt, 799 struct rte_hash_bucket *alt_bkt, 800 const struct rte_hash_key *key, void *data, 801 struct queue_node *leaf, uint32_t leaf_slot, 802 uint16_t sig, uint32_t new_idx, 803 int32_t *ret_val) 804 { 805 uint32_t prev_alt_bkt_idx; 806 struct rte_hash_bucket *cur_bkt; 807 struct queue_node *prev_node, *curr_node = leaf; 808 struct rte_hash_bucket *prev_bkt, *curr_bkt = leaf->bkt; 809 uint32_t prev_slot, curr_slot = leaf_slot; 810 int32_t ret; 811 812 __hash_rw_writer_lock(h); 813 814 /* In case empty slot was gone before entering protected region */ 815 if (curr_bkt->key_idx[curr_slot] != EMPTY_SLOT) { 816 __hash_rw_writer_unlock(h); 817 return -1; 818 } 819 820 /* Check if key was inserted after last check but before this 821 * protected region. 822 */ 823 ret = search_and_update(h, data, key, bkt, sig); 824 if (ret != -1) { 825 __hash_rw_writer_unlock(h); 826 *ret_val = ret; 827 return 1; 828 } 829 830 FOR_EACH_BUCKET(cur_bkt, alt_bkt) { 831 ret = search_and_update(h, data, key, cur_bkt, sig); 832 if (ret != -1) { 833 __hash_rw_writer_unlock(h); 834 *ret_val = ret; 835 return 1; 836 } 837 } 838 839 while (likely(curr_node->prev != NULL)) { 840 prev_node = curr_node->prev; 841 prev_bkt = prev_node->bkt; 842 prev_slot = curr_node->prev_slot; 843 844 prev_alt_bkt_idx = get_alt_bucket_index(h, 845 prev_node->cur_bkt_idx, 846 prev_bkt->sig_current[prev_slot]); 847 848 if (unlikely(&h->buckets[prev_alt_bkt_idx] 849 != curr_bkt)) { 850 /* revert it to empty, otherwise duplicated keys */ 851 __atomic_store_n(&curr_bkt->key_idx[curr_slot], 852 EMPTY_SLOT, 853 __ATOMIC_RELEASE); 854 __hash_rw_writer_unlock(h); 855 return -1; 856 } 857 858 if (h->readwrite_concur_lf_support) { 859 /* Inform the previous move. The current move need 860 * not be informed now as the current bucket entry 861 * is present in both primary and secondary. 862 * Since there is one writer, load acquires on 863 * tbl_chng_cnt are not required. 864 */ 865 __atomic_store_n(h->tbl_chng_cnt, 866 *h->tbl_chng_cnt + 1, 867 __ATOMIC_RELEASE); 868 /* The store to sig_current should not 869 * move above the store to tbl_chng_cnt. 870 */ 871 __atomic_thread_fence(__ATOMIC_RELEASE); 872 } 873 874 /* Need to swap current/alt sig to allow later 875 * Cuckoo insert to move elements back to its 876 * primary bucket if available 877 */ 878 curr_bkt->sig_current[curr_slot] = 879 prev_bkt->sig_current[prev_slot]; 880 /* Release the updated bucket entry */ 881 __atomic_store_n(&curr_bkt->key_idx[curr_slot], 882 prev_bkt->key_idx[prev_slot], 883 __ATOMIC_RELEASE); 884 885 curr_slot = prev_slot; 886 curr_node = prev_node; 887 curr_bkt = curr_node->bkt; 888 } 889 890 if (h->readwrite_concur_lf_support) { 891 /* Inform the previous move. The current move need 892 * not be informed now as the current bucket entry 893 * is present in both primary and secondary. 894 * Since there is one writer, load acquires on 895 * tbl_chng_cnt are not required. 896 */ 897 __atomic_store_n(h->tbl_chng_cnt, 898 *h->tbl_chng_cnt + 1, 899 __ATOMIC_RELEASE); 900 /* The store to sig_current should not 901 * move above the store to tbl_chng_cnt. 902 */ 903 __atomic_thread_fence(__ATOMIC_RELEASE); 904 } 905 906 curr_bkt->sig_current[curr_slot] = sig; 907 /* Release the new bucket entry */ 908 __atomic_store_n(&curr_bkt->key_idx[curr_slot], 909 new_idx, 910 __ATOMIC_RELEASE); 911 912 __hash_rw_writer_unlock(h); 913 914 return 0; 915 916 } 917 918 /* 919 * Make space for new key, using bfs Cuckoo Search and Multi-Writer safe 920 * Cuckoo 921 */ 922 static inline int 923 rte_hash_cuckoo_make_space_mw(const struct rte_hash *h, 924 struct rte_hash_bucket *bkt, 925 struct rte_hash_bucket *sec_bkt, 926 const struct rte_hash_key *key, void *data, 927 uint16_t sig, uint32_t bucket_idx, 928 uint32_t new_idx, int32_t *ret_val) 929 { 930 unsigned int i; 931 struct queue_node queue[RTE_HASH_BFS_QUEUE_MAX_LEN]; 932 struct queue_node *tail, *head; 933 struct rte_hash_bucket *curr_bkt, *alt_bkt; 934 uint32_t cur_idx, alt_idx; 935 936 tail = queue; 937 head = queue + 1; 938 tail->bkt = bkt; 939 tail->prev = NULL; 940 tail->prev_slot = -1; 941 tail->cur_bkt_idx = bucket_idx; 942 943 /* Cuckoo bfs Search */ 944 while (likely(tail != head && head < 945 queue + RTE_HASH_BFS_QUEUE_MAX_LEN - 946 RTE_HASH_BUCKET_ENTRIES)) { 947 curr_bkt = tail->bkt; 948 cur_idx = tail->cur_bkt_idx; 949 for (i = 0; i < RTE_HASH_BUCKET_ENTRIES; i++) { 950 if (curr_bkt->key_idx[i] == EMPTY_SLOT) { 951 int32_t ret = rte_hash_cuckoo_move_insert_mw(h, 952 bkt, sec_bkt, key, data, 953 tail, i, sig, 954 new_idx, ret_val); 955 if (likely(ret != -1)) 956 return ret; 957 } 958 959 /* Enqueue new node and keep prev node info */ 960 alt_idx = get_alt_bucket_index(h, cur_idx, 961 curr_bkt->sig_current[i]); 962 alt_bkt = &(h->buckets[alt_idx]); 963 head->bkt = alt_bkt; 964 head->cur_bkt_idx = alt_idx; 965 head->prev = tail; 966 head->prev_slot = i; 967 head++; 968 } 969 tail++; 970 } 971 972 return -ENOSPC; 973 } 974 975 static inline uint32_t 976 alloc_slot(const struct rte_hash *h, struct lcore_cache *cached_free_slots) 977 { 978 unsigned int n_slots; 979 uint32_t slot_id; 980 981 if (h->use_local_cache) { 982 /* Try to get a free slot from the local cache */ 983 if (cached_free_slots->len == 0) { 984 /* Need to get another burst of free slots from global ring */ 985 n_slots = rte_ring_mc_dequeue_burst_elem(h->free_slots, 986 cached_free_slots->objs, 987 sizeof(uint32_t), 988 LCORE_CACHE_SIZE, NULL); 989 if (n_slots == 0) 990 return EMPTY_SLOT; 991 992 cached_free_slots->len += n_slots; 993 } 994 995 /* Get a free slot from the local cache */ 996 cached_free_slots->len--; 997 slot_id = cached_free_slots->objs[cached_free_slots->len]; 998 } else { 999 if (rte_ring_sc_dequeue_elem(h->free_slots, &slot_id, 1000 sizeof(uint32_t)) != 0) 1001 return EMPTY_SLOT; 1002 } 1003 1004 return slot_id; 1005 } 1006 1007 static inline int32_t 1008 __rte_hash_add_key_with_hash(const struct rte_hash *h, const void *key, 1009 hash_sig_t sig, void *data) 1010 { 1011 uint16_t short_sig; 1012 uint32_t prim_bucket_idx, sec_bucket_idx; 1013 struct rte_hash_bucket *prim_bkt, *sec_bkt, *cur_bkt; 1014 struct rte_hash_key *new_k, *keys = h->key_store; 1015 uint32_t ext_bkt_id = 0; 1016 uint32_t slot_id; 1017 int ret; 1018 unsigned lcore_id; 1019 unsigned int i; 1020 struct lcore_cache *cached_free_slots = NULL; 1021 int32_t ret_val; 1022 struct rte_hash_bucket *last; 1023 1024 short_sig = get_short_sig(sig); 1025 prim_bucket_idx = get_prim_bucket_index(h, sig); 1026 sec_bucket_idx = get_alt_bucket_index(h, prim_bucket_idx, short_sig); 1027 prim_bkt = &h->buckets[prim_bucket_idx]; 1028 sec_bkt = &h->buckets[sec_bucket_idx]; 1029 rte_prefetch0(prim_bkt); 1030 rte_prefetch0(sec_bkt); 1031 1032 /* Check if key is already inserted in primary location */ 1033 __hash_rw_writer_lock(h); 1034 ret = search_and_update(h, data, key, prim_bkt, short_sig); 1035 if (ret != -1) { 1036 __hash_rw_writer_unlock(h); 1037 return ret; 1038 } 1039 1040 /* Check if key is already inserted in secondary location */ 1041 FOR_EACH_BUCKET(cur_bkt, sec_bkt) { 1042 ret = search_and_update(h, data, key, cur_bkt, short_sig); 1043 if (ret != -1) { 1044 __hash_rw_writer_unlock(h); 1045 return ret; 1046 } 1047 } 1048 1049 __hash_rw_writer_unlock(h); 1050 1051 /* Did not find a match, so get a new slot for storing the new key */ 1052 if (h->use_local_cache) { 1053 lcore_id = rte_lcore_id(); 1054 cached_free_slots = &h->local_free_slots[lcore_id]; 1055 } 1056 slot_id = alloc_slot(h, cached_free_slots); 1057 if (slot_id == EMPTY_SLOT) { 1058 if (h->dq) { 1059 __hash_rw_writer_lock(h); 1060 ret = rte_rcu_qsbr_dq_reclaim(h->dq, 1061 h->hash_rcu_cfg->max_reclaim_size, 1062 NULL, NULL, NULL); 1063 __hash_rw_writer_unlock(h); 1064 if (ret == 0) 1065 slot_id = alloc_slot(h, cached_free_slots); 1066 } 1067 if (slot_id == EMPTY_SLOT) 1068 return -ENOSPC; 1069 } 1070 1071 new_k = RTE_PTR_ADD(keys, slot_id * h->key_entry_size); 1072 /* The store to application data (by the application) at *data should 1073 * not leak after the store of pdata in the key store. i.e. pdata is 1074 * the guard variable. Release the application data to the readers. 1075 */ 1076 __atomic_store_n(&new_k->pdata, 1077 data, 1078 __ATOMIC_RELEASE); 1079 /* Copy key */ 1080 memcpy(new_k->key, key, h->key_len); 1081 1082 /* Find an empty slot and insert */ 1083 ret = rte_hash_cuckoo_insert_mw(h, prim_bkt, sec_bkt, key, data, 1084 short_sig, slot_id, &ret_val); 1085 if (ret == 0) 1086 return slot_id - 1; 1087 else if (ret == 1) { 1088 enqueue_slot_back(h, cached_free_slots, slot_id); 1089 return ret_val; 1090 } 1091 1092 /* Primary bucket full, need to make space for new entry */ 1093 ret = rte_hash_cuckoo_make_space_mw(h, prim_bkt, sec_bkt, key, data, 1094 short_sig, prim_bucket_idx, slot_id, &ret_val); 1095 if (ret == 0) 1096 return slot_id - 1; 1097 else if (ret == 1) { 1098 enqueue_slot_back(h, cached_free_slots, slot_id); 1099 return ret_val; 1100 } 1101 1102 /* Also search secondary bucket to get better occupancy */ 1103 ret = rte_hash_cuckoo_make_space_mw(h, sec_bkt, prim_bkt, key, data, 1104 short_sig, sec_bucket_idx, slot_id, &ret_val); 1105 1106 if (ret == 0) 1107 return slot_id - 1; 1108 else if (ret == 1) { 1109 enqueue_slot_back(h, cached_free_slots, slot_id); 1110 return ret_val; 1111 } 1112 1113 /* if ext table not enabled, we failed the insertion */ 1114 if (!h->ext_table_support) { 1115 enqueue_slot_back(h, cached_free_slots, slot_id); 1116 return ret; 1117 } 1118 1119 /* Now we need to go through the extendable bucket. Protection is needed 1120 * to protect all extendable bucket processes. 1121 */ 1122 __hash_rw_writer_lock(h); 1123 /* We check for duplicates again since could be inserted before the lock */ 1124 ret = search_and_update(h, data, key, prim_bkt, short_sig); 1125 if (ret != -1) { 1126 enqueue_slot_back(h, cached_free_slots, slot_id); 1127 goto failure; 1128 } 1129 1130 FOR_EACH_BUCKET(cur_bkt, sec_bkt) { 1131 ret = search_and_update(h, data, key, cur_bkt, short_sig); 1132 if (ret != -1) { 1133 enqueue_slot_back(h, cached_free_slots, slot_id); 1134 goto failure; 1135 } 1136 } 1137 1138 /* Search sec and ext buckets to find an empty entry to insert. */ 1139 FOR_EACH_BUCKET(cur_bkt, sec_bkt) { 1140 for (i = 0; i < RTE_HASH_BUCKET_ENTRIES; i++) { 1141 /* Check if slot is available */ 1142 if (likely(cur_bkt->key_idx[i] == EMPTY_SLOT)) { 1143 cur_bkt->sig_current[i] = short_sig; 1144 /* Store to signature and key should not 1145 * leak after the store to key_idx. i.e. 1146 * key_idx is the guard variable for signature 1147 * and key. 1148 */ 1149 __atomic_store_n(&cur_bkt->key_idx[i], 1150 slot_id, 1151 __ATOMIC_RELEASE); 1152 __hash_rw_writer_unlock(h); 1153 return slot_id - 1; 1154 } 1155 } 1156 } 1157 1158 /* Failed to get an empty entry from extendable buckets. Link a new 1159 * extendable bucket. We first get a free bucket from ring. 1160 */ 1161 if (rte_ring_sc_dequeue_elem(h->free_ext_bkts, &ext_bkt_id, 1162 sizeof(uint32_t)) != 0 || 1163 ext_bkt_id == 0) { 1164 if (h->dq) { 1165 if (rte_rcu_qsbr_dq_reclaim(h->dq, 1166 h->hash_rcu_cfg->max_reclaim_size, 1167 NULL, NULL, NULL) == 0) { 1168 rte_ring_sc_dequeue_elem(h->free_ext_bkts, 1169 &ext_bkt_id, 1170 sizeof(uint32_t)); 1171 } 1172 } 1173 if (ext_bkt_id == 0) { 1174 ret = -ENOSPC; 1175 goto failure; 1176 } 1177 } 1178 1179 /* Use the first location of the new bucket */ 1180 (h->buckets_ext[ext_bkt_id - 1]).sig_current[0] = short_sig; 1181 /* Store to signature and key should not leak after 1182 * the store to key_idx. i.e. key_idx is the guard variable 1183 * for signature and key. 1184 */ 1185 __atomic_store_n(&(h->buckets_ext[ext_bkt_id - 1]).key_idx[0], 1186 slot_id, 1187 __ATOMIC_RELEASE); 1188 /* Link the new bucket to sec bucket linked list */ 1189 last = rte_hash_get_last_bkt(sec_bkt); 1190 last->next = &h->buckets_ext[ext_bkt_id - 1]; 1191 __hash_rw_writer_unlock(h); 1192 return slot_id - 1; 1193 1194 failure: 1195 __hash_rw_writer_unlock(h); 1196 return ret; 1197 1198 } 1199 1200 int32_t 1201 rte_hash_add_key_with_hash(const struct rte_hash *h, 1202 const void *key, hash_sig_t sig) 1203 { 1204 RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL); 1205 return __rte_hash_add_key_with_hash(h, key, sig, 0); 1206 } 1207 1208 int32_t 1209 rte_hash_add_key(const struct rte_hash *h, const void *key) 1210 { 1211 RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL); 1212 return __rte_hash_add_key_with_hash(h, key, rte_hash_hash(h, key), 0); 1213 } 1214 1215 int 1216 rte_hash_add_key_with_hash_data(const struct rte_hash *h, 1217 const void *key, hash_sig_t sig, void *data) 1218 { 1219 int ret; 1220 1221 RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL); 1222 ret = __rte_hash_add_key_with_hash(h, key, sig, data); 1223 if (ret >= 0) 1224 return 0; 1225 else 1226 return ret; 1227 } 1228 1229 int 1230 rte_hash_add_key_data(const struct rte_hash *h, const void *key, void *data) 1231 { 1232 int ret; 1233 1234 RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL); 1235 1236 ret = __rte_hash_add_key_with_hash(h, key, rte_hash_hash(h, key), data); 1237 if (ret >= 0) 1238 return 0; 1239 else 1240 return ret; 1241 } 1242 1243 /* Search one bucket to find the match key - uses rw lock */ 1244 static inline int32_t 1245 search_one_bucket_l(const struct rte_hash *h, const void *key, 1246 uint16_t sig, void **data, 1247 const struct rte_hash_bucket *bkt) 1248 { 1249 int i; 1250 struct rte_hash_key *k, *keys = h->key_store; 1251 1252 for (i = 0; i < RTE_HASH_BUCKET_ENTRIES; i++) { 1253 if (bkt->sig_current[i] == sig && 1254 bkt->key_idx[i] != EMPTY_SLOT) { 1255 k = (struct rte_hash_key *) ((char *)keys + 1256 bkt->key_idx[i] * h->key_entry_size); 1257 1258 if (rte_hash_cmp_eq(key, k->key, h) == 0) { 1259 if (data != NULL) 1260 *data = k->pdata; 1261 /* 1262 * Return index where key is stored, 1263 * subtracting the first dummy index 1264 */ 1265 return bkt->key_idx[i] - 1; 1266 } 1267 } 1268 } 1269 return -1; 1270 } 1271 1272 /* Search one bucket to find the match key */ 1273 static inline int32_t 1274 search_one_bucket_lf(const struct rte_hash *h, const void *key, uint16_t sig, 1275 void **data, const struct rte_hash_bucket *bkt) 1276 { 1277 int i; 1278 uint32_t key_idx; 1279 struct rte_hash_key *k, *keys = h->key_store; 1280 1281 for (i = 0; i < RTE_HASH_BUCKET_ENTRIES; i++) { 1282 /* Signature comparison is done before the acquire-load 1283 * of the key index to achieve better performance. 1284 * This can result in the reader loading old signature 1285 * (which matches), while the key_idx is updated to a 1286 * value that belongs to a new key. However, the full 1287 * key comparison will ensure that the lookup fails. 1288 */ 1289 if (bkt->sig_current[i] == sig) { 1290 key_idx = __atomic_load_n(&bkt->key_idx[i], 1291 __ATOMIC_ACQUIRE); 1292 if (key_idx != EMPTY_SLOT) { 1293 k = (struct rte_hash_key *) ((char *)keys + 1294 key_idx * h->key_entry_size); 1295 1296 if (rte_hash_cmp_eq(key, k->key, h) == 0) { 1297 if (data != NULL) { 1298 *data = __atomic_load_n( 1299 &k->pdata, 1300 __ATOMIC_ACQUIRE); 1301 } 1302 /* 1303 * Return index where key is stored, 1304 * subtracting the first dummy index 1305 */ 1306 return key_idx - 1; 1307 } 1308 } 1309 } 1310 } 1311 return -1; 1312 } 1313 1314 static inline int32_t 1315 __rte_hash_lookup_with_hash_l(const struct rte_hash *h, const void *key, 1316 hash_sig_t sig, void **data) 1317 { 1318 uint32_t prim_bucket_idx, sec_bucket_idx; 1319 struct rte_hash_bucket *bkt, *cur_bkt; 1320 int ret; 1321 uint16_t short_sig; 1322 1323 short_sig = get_short_sig(sig); 1324 prim_bucket_idx = get_prim_bucket_index(h, sig); 1325 sec_bucket_idx = get_alt_bucket_index(h, prim_bucket_idx, short_sig); 1326 1327 bkt = &h->buckets[prim_bucket_idx]; 1328 1329 __hash_rw_reader_lock(h); 1330 1331 /* Check if key is in primary location */ 1332 ret = search_one_bucket_l(h, key, short_sig, data, bkt); 1333 if (ret != -1) { 1334 __hash_rw_reader_unlock(h); 1335 return ret; 1336 } 1337 /* Calculate secondary hash */ 1338 bkt = &h->buckets[sec_bucket_idx]; 1339 1340 /* Check if key is in secondary location */ 1341 FOR_EACH_BUCKET(cur_bkt, bkt) { 1342 ret = search_one_bucket_l(h, key, short_sig, 1343 data, cur_bkt); 1344 if (ret != -1) { 1345 __hash_rw_reader_unlock(h); 1346 return ret; 1347 } 1348 } 1349 1350 __hash_rw_reader_unlock(h); 1351 1352 return -ENOENT; 1353 } 1354 1355 static inline int32_t 1356 __rte_hash_lookup_with_hash_lf(const struct rte_hash *h, const void *key, 1357 hash_sig_t sig, void **data) 1358 { 1359 uint32_t prim_bucket_idx, sec_bucket_idx; 1360 struct rte_hash_bucket *bkt, *cur_bkt; 1361 uint32_t cnt_b, cnt_a; 1362 int ret; 1363 uint16_t short_sig; 1364 1365 short_sig = get_short_sig(sig); 1366 prim_bucket_idx = get_prim_bucket_index(h, sig); 1367 sec_bucket_idx = get_alt_bucket_index(h, prim_bucket_idx, short_sig); 1368 1369 do { 1370 /* Load the table change counter before the lookup 1371 * starts. Acquire semantics will make sure that 1372 * loads in search_one_bucket are not hoisted. 1373 */ 1374 cnt_b = __atomic_load_n(h->tbl_chng_cnt, 1375 __ATOMIC_ACQUIRE); 1376 1377 /* Check if key is in primary location */ 1378 bkt = &h->buckets[prim_bucket_idx]; 1379 ret = search_one_bucket_lf(h, key, short_sig, data, bkt); 1380 if (ret != -1) 1381 return ret; 1382 /* Calculate secondary hash */ 1383 bkt = &h->buckets[sec_bucket_idx]; 1384 1385 /* Check if key is in secondary location */ 1386 FOR_EACH_BUCKET(cur_bkt, bkt) { 1387 ret = search_one_bucket_lf(h, key, short_sig, 1388 data, cur_bkt); 1389 if (ret != -1) 1390 return ret; 1391 } 1392 1393 /* The loads of sig_current in search_one_bucket 1394 * should not move below the load from tbl_chng_cnt. 1395 */ 1396 __atomic_thread_fence(__ATOMIC_ACQUIRE); 1397 /* Re-read the table change counter to check if the 1398 * table has changed during search. If yes, re-do 1399 * the search. 1400 * This load should not get hoisted. The load 1401 * acquires on cnt_b, key index in primary bucket 1402 * and key index in secondary bucket will make sure 1403 * that it does not get hoisted. 1404 */ 1405 cnt_a = __atomic_load_n(h->tbl_chng_cnt, 1406 __ATOMIC_ACQUIRE); 1407 } while (cnt_b != cnt_a); 1408 1409 return -ENOENT; 1410 } 1411 1412 static inline int32_t 1413 __rte_hash_lookup_with_hash(const struct rte_hash *h, const void *key, 1414 hash_sig_t sig, void **data) 1415 { 1416 if (h->readwrite_concur_lf_support) 1417 return __rte_hash_lookup_with_hash_lf(h, key, sig, data); 1418 else 1419 return __rte_hash_lookup_with_hash_l(h, key, sig, data); 1420 } 1421 1422 int32_t 1423 rte_hash_lookup_with_hash(const struct rte_hash *h, 1424 const void *key, hash_sig_t sig) 1425 { 1426 RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL); 1427 return __rte_hash_lookup_with_hash(h, key, sig, NULL); 1428 } 1429 1430 int32_t 1431 rte_hash_lookup(const struct rte_hash *h, const void *key) 1432 { 1433 RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL); 1434 return __rte_hash_lookup_with_hash(h, key, rte_hash_hash(h, key), NULL); 1435 } 1436 1437 int 1438 rte_hash_lookup_with_hash_data(const struct rte_hash *h, 1439 const void *key, hash_sig_t sig, void **data) 1440 { 1441 RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL); 1442 return __rte_hash_lookup_with_hash(h, key, sig, data); 1443 } 1444 1445 int 1446 rte_hash_lookup_data(const struct rte_hash *h, const void *key, void **data) 1447 { 1448 RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL); 1449 return __rte_hash_lookup_with_hash(h, key, rte_hash_hash(h, key), data); 1450 } 1451 1452 static int 1453 free_slot(const struct rte_hash *h, uint32_t slot_id) 1454 { 1455 unsigned lcore_id, n_slots; 1456 struct lcore_cache *cached_free_slots = NULL; 1457 1458 /* Return key indexes to free slot ring */ 1459 if (h->use_local_cache) { 1460 lcore_id = rte_lcore_id(); 1461 cached_free_slots = &h->local_free_slots[lcore_id]; 1462 /* Cache full, need to free it. */ 1463 if (cached_free_slots->len == LCORE_CACHE_SIZE) { 1464 /* Need to enqueue the free slots in global ring. */ 1465 n_slots = rte_ring_mp_enqueue_burst_elem(h->free_slots, 1466 cached_free_slots->objs, 1467 sizeof(uint32_t), 1468 LCORE_CACHE_SIZE, NULL); 1469 RETURN_IF_TRUE((n_slots == 0), -EFAULT); 1470 cached_free_slots->len -= n_slots; 1471 } 1472 } 1473 1474 enqueue_slot_back(h, cached_free_slots, slot_id); 1475 return 0; 1476 } 1477 1478 static void 1479 __hash_rcu_qsbr_free_resource(void *p, void *e, unsigned int n) 1480 { 1481 void *key_data = NULL; 1482 int ret; 1483 struct rte_hash_key *keys, *k; 1484 struct rte_hash *h = (struct rte_hash *)p; 1485 struct __rte_hash_rcu_dq_entry rcu_dq_entry = 1486 *((struct __rte_hash_rcu_dq_entry *)e); 1487 1488 RTE_SET_USED(n); 1489 keys = h->key_store; 1490 1491 k = (struct rte_hash_key *) ((char *)keys + 1492 rcu_dq_entry.key_idx * h->key_entry_size); 1493 key_data = k->pdata; 1494 if (h->hash_rcu_cfg->free_key_data_func) 1495 h->hash_rcu_cfg->free_key_data_func(h->hash_rcu_cfg->key_data_ptr, 1496 key_data); 1497 1498 if (h->ext_table_support && rcu_dq_entry.ext_bkt_idx != EMPTY_SLOT) 1499 /* Recycle empty ext bkt to free list. */ 1500 rte_ring_sp_enqueue_elem(h->free_ext_bkts, 1501 &rcu_dq_entry.ext_bkt_idx, sizeof(uint32_t)); 1502 1503 /* Return key indexes to free slot ring */ 1504 ret = free_slot(h, rcu_dq_entry.key_idx); 1505 if (ret < 0) { 1506 RTE_LOG(ERR, HASH, 1507 "%s: could not enqueue free slots in global ring\n", 1508 __func__); 1509 } 1510 } 1511 1512 int 1513 rte_hash_rcu_qsbr_add(struct rte_hash *h, struct rte_hash_rcu_config *cfg) 1514 { 1515 struct rte_rcu_qsbr_dq_parameters params = {0}; 1516 char rcu_dq_name[RTE_RCU_QSBR_DQ_NAMESIZE]; 1517 struct rte_hash_rcu_config *hash_rcu_cfg = NULL; 1518 1519 if (h == NULL || cfg == NULL || cfg->v == NULL) { 1520 rte_errno = EINVAL; 1521 return 1; 1522 } 1523 1524 const uint32_t total_entries = h->use_local_cache ? 1525 h->entries + (RTE_MAX_LCORE - 1) * (LCORE_CACHE_SIZE - 1) + 1 1526 : h->entries + 1; 1527 1528 if (h->hash_rcu_cfg) { 1529 rte_errno = EEXIST; 1530 return 1; 1531 } 1532 1533 hash_rcu_cfg = rte_zmalloc(NULL, sizeof(struct rte_hash_rcu_config), 0); 1534 if (hash_rcu_cfg == NULL) { 1535 RTE_LOG(ERR, HASH, "memory allocation failed\n"); 1536 return 1; 1537 } 1538 1539 if (cfg->mode == RTE_HASH_QSBR_MODE_SYNC) { 1540 /* No other things to do. */ 1541 } else if (cfg->mode == RTE_HASH_QSBR_MODE_DQ) { 1542 /* Init QSBR defer queue. */ 1543 snprintf(rcu_dq_name, sizeof(rcu_dq_name), 1544 "HASH_RCU_%s", h->name); 1545 params.name = rcu_dq_name; 1546 params.size = cfg->dq_size; 1547 if (params.size == 0) 1548 params.size = total_entries; 1549 params.trigger_reclaim_limit = cfg->trigger_reclaim_limit; 1550 if (params.max_reclaim_size == 0) 1551 params.max_reclaim_size = RTE_HASH_RCU_DQ_RECLAIM_MAX; 1552 params.esize = sizeof(struct __rte_hash_rcu_dq_entry); 1553 params.free_fn = __hash_rcu_qsbr_free_resource; 1554 params.p = h; 1555 params.v = cfg->v; 1556 h->dq = rte_rcu_qsbr_dq_create(¶ms); 1557 if (h->dq == NULL) { 1558 rte_free(hash_rcu_cfg); 1559 RTE_LOG(ERR, HASH, "HASH defer queue creation failed\n"); 1560 return 1; 1561 } 1562 } else { 1563 rte_free(hash_rcu_cfg); 1564 rte_errno = EINVAL; 1565 return 1; 1566 } 1567 1568 hash_rcu_cfg->v = cfg->v; 1569 hash_rcu_cfg->mode = cfg->mode; 1570 hash_rcu_cfg->dq_size = params.size; 1571 hash_rcu_cfg->trigger_reclaim_limit = params.trigger_reclaim_limit; 1572 hash_rcu_cfg->max_reclaim_size = params.max_reclaim_size; 1573 hash_rcu_cfg->free_key_data_func = cfg->free_key_data_func; 1574 hash_rcu_cfg->key_data_ptr = cfg->key_data_ptr; 1575 1576 h->hash_rcu_cfg = hash_rcu_cfg; 1577 1578 return 0; 1579 } 1580 1581 static inline void 1582 remove_entry(const struct rte_hash *h, struct rte_hash_bucket *bkt, 1583 unsigned int i) 1584 { 1585 int ret = free_slot(h, bkt->key_idx[i]); 1586 1587 if (ret < 0) { 1588 RTE_LOG(ERR, HASH, 1589 "%s: could not enqueue free slots in global ring\n", 1590 __func__); 1591 } 1592 } 1593 1594 /* Compact the linked list by moving key from last entry in linked list to the 1595 * empty slot. 1596 */ 1597 static inline void 1598 __rte_hash_compact_ll(const struct rte_hash *h, 1599 struct rte_hash_bucket *cur_bkt, int pos) { 1600 int i; 1601 struct rte_hash_bucket *last_bkt; 1602 1603 if (!cur_bkt->next) 1604 return; 1605 1606 last_bkt = rte_hash_get_last_bkt(cur_bkt); 1607 1608 for (i = RTE_HASH_BUCKET_ENTRIES - 1; i >= 0; i--) { 1609 if (last_bkt->key_idx[i] != EMPTY_SLOT) { 1610 cur_bkt->sig_current[pos] = last_bkt->sig_current[i]; 1611 __atomic_store_n(&cur_bkt->key_idx[pos], 1612 last_bkt->key_idx[i], 1613 __ATOMIC_RELEASE); 1614 if (h->readwrite_concur_lf_support) { 1615 /* Inform the readers that the table has changed 1616 * Since there is one writer, load acquire on 1617 * tbl_chng_cnt is not required. 1618 */ 1619 __atomic_store_n(h->tbl_chng_cnt, 1620 *h->tbl_chng_cnt + 1, 1621 __ATOMIC_RELEASE); 1622 /* The store to sig_current should 1623 * not move above the store to tbl_chng_cnt. 1624 */ 1625 __atomic_thread_fence(__ATOMIC_RELEASE); 1626 } 1627 last_bkt->sig_current[i] = NULL_SIGNATURE; 1628 __atomic_store_n(&last_bkt->key_idx[i], 1629 EMPTY_SLOT, 1630 __ATOMIC_RELEASE); 1631 return; 1632 } 1633 } 1634 } 1635 1636 /* Search one bucket and remove the matched key. 1637 * Writer is expected to hold the lock while calling this 1638 * function. 1639 */ 1640 static inline int32_t 1641 search_and_remove(const struct rte_hash *h, const void *key, 1642 struct rte_hash_bucket *bkt, uint16_t sig, int *pos) 1643 { 1644 struct rte_hash_key *k, *keys = h->key_store; 1645 unsigned int i; 1646 uint32_t key_idx; 1647 1648 /* Check if key is in bucket */ 1649 for (i = 0; i < RTE_HASH_BUCKET_ENTRIES; i++) { 1650 key_idx = __atomic_load_n(&bkt->key_idx[i], 1651 __ATOMIC_ACQUIRE); 1652 if (bkt->sig_current[i] == sig && key_idx != EMPTY_SLOT) { 1653 k = (struct rte_hash_key *) ((char *)keys + 1654 key_idx * h->key_entry_size); 1655 if (rte_hash_cmp_eq(key, k->key, h) == 0) { 1656 bkt->sig_current[i] = NULL_SIGNATURE; 1657 /* Free the key store index if 1658 * no_free_on_del is disabled. 1659 */ 1660 if (!h->no_free_on_del) 1661 remove_entry(h, bkt, i); 1662 1663 __atomic_store_n(&bkt->key_idx[i], 1664 EMPTY_SLOT, 1665 __ATOMIC_RELEASE); 1666 1667 *pos = i; 1668 /* 1669 * Return index where key is stored, 1670 * subtracting the first dummy index 1671 */ 1672 return key_idx - 1; 1673 } 1674 } 1675 } 1676 return -1; 1677 } 1678 1679 static inline int32_t 1680 __rte_hash_del_key_with_hash(const struct rte_hash *h, const void *key, 1681 hash_sig_t sig) 1682 { 1683 uint32_t prim_bucket_idx, sec_bucket_idx; 1684 struct rte_hash_bucket *prim_bkt, *sec_bkt, *prev_bkt, *last_bkt; 1685 struct rte_hash_bucket *cur_bkt; 1686 int pos; 1687 int32_t ret, i; 1688 uint16_t short_sig; 1689 uint32_t index = EMPTY_SLOT; 1690 struct __rte_hash_rcu_dq_entry rcu_dq_entry; 1691 1692 short_sig = get_short_sig(sig); 1693 prim_bucket_idx = get_prim_bucket_index(h, sig); 1694 sec_bucket_idx = get_alt_bucket_index(h, prim_bucket_idx, short_sig); 1695 prim_bkt = &h->buckets[prim_bucket_idx]; 1696 1697 __hash_rw_writer_lock(h); 1698 /* look for key in primary bucket */ 1699 ret = search_and_remove(h, key, prim_bkt, short_sig, &pos); 1700 if (ret != -1) { 1701 __rte_hash_compact_ll(h, prim_bkt, pos); 1702 last_bkt = prim_bkt->next; 1703 prev_bkt = prim_bkt; 1704 goto return_bkt; 1705 } 1706 1707 /* Calculate secondary hash */ 1708 sec_bkt = &h->buckets[sec_bucket_idx]; 1709 1710 FOR_EACH_BUCKET(cur_bkt, sec_bkt) { 1711 ret = search_and_remove(h, key, cur_bkt, short_sig, &pos); 1712 if (ret != -1) { 1713 __rte_hash_compact_ll(h, cur_bkt, pos); 1714 last_bkt = sec_bkt->next; 1715 prev_bkt = sec_bkt; 1716 goto return_bkt; 1717 } 1718 } 1719 1720 __hash_rw_writer_unlock(h); 1721 return -ENOENT; 1722 1723 /* Search last bucket to see if empty to be recycled */ 1724 return_bkt: 1725 if (!last_bkt) 1726 goto return_key; 1727 1728 while (last_bkt->next) { 1729 prev_bkt = last_bkt; 1730 last_bkt = last_bkt->next; 1731 } 1732 1733 for (i = 0; i < RTE_HASH_BUCKET_ENTRIES; i++) { 1734 if (last_bkt->key_idx[i] != EMPTY_SLOT) 1735 break; 1736 } 1737 /* found empty bucket and recycle */ 1738 if (i == RTE_HASH_BUCKET_ENTRIES) { 1739 prev_bkt->next = NULL; 1740 index = last_bkt - h->buckets_ext + 1; 1741 /* Recycle the empty bkt if 1742 * no_free_on_del is disabled. 1743 */ 1744 if (h->no_free_on_del) { 1745 /* Store index of an empty ext bkt to be recycled 1746 * on calling rte_hash_del_xxx APIs. 1747 * When lock free read-write concurrency is enabled, 1748 * an empty ext bkt cannot be put into free list 1749 * immediately (as readers might be using it still). 1750 * Hence freeing of the ext bkt is piggy-backed to 1751 * freeing of the key index. 1752 * If using external RCU, store this index in an array. 1753 */ 1754 if (h->hash_rcu_cfg == NULL) 1755 h->ext_bkt_to_free[ret] = index; 1756 } else 1757 rte_ring_sp_enqueue_elem(h->free_ext_bkts, &index, 1758 sizeof(uint32_t)); 1759 } 1760 1761 return_key: 1762 /* Using internal RCU QSBR */ 1763 if (h->hash_rcu_cfg) { 1764 /* Key index where key is stored, adding the first dummy index */ 1765 rcu_dq_entry.key_idx = ret + 1; 1766 rcu_dq_entry.ext_bkt_idx = index; 1767 if (h->dq == NULL) { 1768 /* Wait for quiescent state change if using 1769 * RTE_HASH_QSBR_MODE_SYNC 1770 */ 1771 rte_rcu_qsbr_synchronize(h->hash_rcu_cfg->v, 1772 RTE_QSBR_THRID_INVALID); 1773 __hash_rcu_qsbr_free_resource((void *)((uintptr_t)h), 1774 &rcu_dq_entry, 1); 1775 } else if (h->dq) 1776 /* Push into QSBR FIFO if using RTE_HASH_QSBR_MODE_DQ */ 1777 if (rte_rcu_qsbr_dq_enqueue(h->dq, &rcu_dq_entry) != 0) 1778 RTE_LOG(ERR, HASH, "Failed to push QSBR FIFO\n"); 1779 } 1780 __hash_rw_writer_unlock(h); 1781 return ret; 1782 } 1783 1784 int32_t 1785 rte_hash_del_key_with_hash(const struct rte_hash *h, 1786 const void *key, hash_sig_t sig) 1787 { 1788 RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL); 1789 return __rte_hash_del_key_with_hash(h, key, sig); 1790 } 1791 1792 int32_t 1793 rte_hash_del_key(const struct rte_hash *h, const void *key) 1794 { 1795 RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL); 1796 return __rte_hash_del_key_with_hash(h, key, rte_hash_hash(h, key)); 1797 } 1798 1799 int 1800 rte_hash_get_key_with_position(const struct rte_hash *h, const int32_t position, 1801 void **key) 1802 { 1803 RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL); 1804 1805 struct rte_hash_key *k, *keys = h->key_store; 1806 k = (struct rte_hash_key *) ((char *) keys + (position + 1) * 1807 h->key_entry_size); 1808 *key = k->key; 1809 1810 if (position != 1811 __rte_hash_lookup_with_hash(h, *key, rte_hash_hash(h, *key), 1812 NULL)) { 1813 return -ENOENT; 1814 } 1815 1816 return 0; 1817 } 1818 1819 int 1820 rte_hash_free_key_with_position(const struct rte_hash *h, 1821 const int32_t position) 1822 { 1823 /* Key index where key is stored, adding the first dummy index */ 1824 uint32_t key_idx = position + 1; 1825 1826 RETURN_IF_TRUE(((h == NULL) || (key_idx == EMPTY_SLOT)), -EINVAL); 1827 1828 const uint32_t total_entries = h->use_local_cache ? 1829 h->entries + (RTE_MAX_LCORE - 1) * (LCORE_CACHE_SIZE - 1) + 1 1830 : h->entries + 1; 1831 1832 /* Out of bounds */ 1833 if (key_idx >= total_entries) 1834 return -EINVAL; 1835 if (h->ext_table_support && h->readwrite_concur_lf_support) { 1836 uint32_t index = h->ext_bkt_to_free[position]; 1837 if (index) { 1838 /* Recycle empty ext bkt to free list. */ 1839 rte_ring_sp_enqueue_elem(h->free_ext_bkts, &index, 1840 sizeof(uint32_t)); 1841 h->ext_bkt_to_free[position] = 0; 1842 } 1843 } 1844 1845 /* Enqueue slot to cache/ring of free slots. */ 1846 return free_slot(h, key_idx); 1847 1848 } 1849 1850 static inline void 1851 compare_signatures(uint32_t *prim_hash_matches, uint32_t *sec_hash_matches, 1852 const struct rte_hash_bucket *prim_bkt, 1853 const struct rte_hash_bucket *sec_bkt, 1854 uint16_t sig, 1855 enum rte_hash_sig_compare_function sig_cmp_fn) 1856 { 1857 unsigned int i; 1858 1859 /* For match mask the first bit of every two bits indicates the match */ 1860 switch (sig_cmp_fn) { 1861 #if defined(__SSE2__) 1862 case RTE_HASH_COMPARE_SSE: 1863 /* Compare all signatures in the bucket */ 1864 *prim_hash_matches = _mm_movemask_epi8(_mm_cmpeq_epi16( 1865 _mm_load_si128( 1866 (__m128i const *)prim_bkt->sig_current), 1867 _mm_set1_epi16(sig))); 1868 /* Compare all signatures in the bucket */ 1869 *sec_hash_matches = _mm_movemask_epi8(_mm_cmpeq_epi16( 1870 _mm_load_si128( 1871 (__m128i const *)sec_bkt->sig_current), 1872 _mm_set1_epi16(sig))); 1873 break; 1874 #elif defined(__ARM_NEON) 1875 case RTE_HASH_COMPARE_NEON: { 1876 uint16x8_t vmat, vsig, x; 1877 int16x8_t shift = {-15, -13, -11, -9, -7, -5, -3, -1}; 1878 1879 vsig = vld1q_dup_u16((uint16_t const *)&sig); 1880 /* Compare all signatures in the primary bucket */ 1881 vmat = vceqq_u16(vsig, 1882 vld1q_u16((uint16_t const *)prim_bkt->sig_current)); 1883 x = vshlq_u16(vandq_u16(vmat, vdupq_n_u16(0x8000)), shift); 1884 *prim_hash_matches = (uint32_t)(vaddvq_u16(x)); 1885 /* Compare all signatures in the secondary bucket */ 1886 vmat = vceqq_u16(vsig, 1887 vld1q_u16((uint16_t const *)sec_bkt->sig_current)); 1888 x = vshlq_u16(vandq_u16(vmat, vdupq_n_u16(0x8000)), shift); 1889 *sec_hash_matches = (uint32_t)(vaddvq_u16(x)); 1890 } 1891 break; 1892 #endif 1893 default: 1894 for (i = 0; i < RTE_HASH_BUCKET_ENTRIES; i++) { 1895 *prim_hash_matches |= 1896 ((sig == prim_bkt->sig_current[i]) << (i << 1)); 1897 *sec_hash_matches |= 1898 ((sig == sec_bkt->sig_current[i]) << (i << 1)); 1899 } 1900 } 1901 } 1902 1903 static inline void 1904 __bulk_lookup_l(const struct rte_hash *h, const void **keys, 1905 const struct rte_hash_bucket **primary_bkt, 1906 const struct rte_hash_bucket **secondary_bkt, 1907 uint16_t *sig, int32_t num_keys, int32_t *positions, 1908 uint64_t *hit_mask, void *data[]) 1909 { 1910 uint64_t hits = 0; 1911 int32_t i; 1912 int32_t ret; 1913 uint32_t prim_hitmask[RTE_HASH_LOOKUP_BULK_MAX] = {0}; 1914 uint32_t sec_hitmask[RTE_HASH_LOOKUP_BULK_MAX] = {0}; 1915 struct rte_hash_bucket *cur_bkt, *next_bkt; 1916 1917 __hash_rw_reader_lock(h); 1918 1919 /* Compare signatures and prefetch key slot of first hit */ 1920 for (i = 0; i < num_keys; i++) { 1921 compare_signatures(&prim_hitmask[i], &sec_hitmask[i], 1922 primary_bkt[i], secondary_bkt[i], 1923 sig[i], h->sig_cmp_fn); 1924 1925 if (prim_hitmask[i]) { 1926 uint32_t first_hit = 1927 __builtin_ctzl(prim_hitmask[i]) 1928 >> 1; 1929 uint32_t key_idx = 1930 primary_bkt[i]->key_idx[first_hit]; 1931 const struct rte_hash_key *key_slot = 1932 (const struct rte_hash_key *)( 1933 (const char *)h->key_store + 1934 key_idx * h->key_entry_size); 1935 rte_prefetch0(key_slot); 1936 continue; 1937 } 1938 1939 if (sec_hitmask[i]) { 1940 uint32_t first_hit = 1941 __builtin_ctzl(sec_hitmask[i]) 1942 >> 1; 1943 uint32_t key_idx = 1944 secondary_bkt[i]->key_idx[first_hit]; 1945 const struct rte_hash_key *key_slot = 1946 (const struct rte_hash_key *)( 1947 (const char *)h->key_store + 1948 key_idx * h->key_entry_size); 1949 rte_prefetch0(key_slot); 1950 } 1951 } 1952 1953 /* Compare keys, first hits in primary first */ 1954 for (i = 0; i < num_keys; i++) { 1955 positions[i] = -ENOENT; 1956 while (prim_hitmask[i]) { 1957 uint32_t hit_index = 1958 __builtin_ctzl(prim_hitmask[i]) 1959 >> 1; 1960 uint32_t key_idx = 1961 primary_bkt[i]->key_idx[hit_index]; 1962 const struct rte_hash_key *key_slot = 1963 (const struct rte_hash_key *)( 1964 (const char *)h->key_store + 1965 key_idx * h->key_entry_size); 1966 1967 /* 1968 * If key index is 0, do not compare key, 1969 * as it is checking the dummy slot 1970 */ 1971 if (!!key_idx & 1972 !rte_hash_cmp_eq( 1973 key_slot->key, keys[i], h)) { 1974 if (data != NULL) 1975 data[i] = key_slot->pdata; 1976 1977 hits |= 1ULL << i; 1978 positions[i] = key_idx - 1; 1979 goto next_key; 1980 } 1981 prim_hitmask[i] &= ~(3ULL << (hit_index << 1)); 1982 } 1983 1984 while (sec_hitmask[i]) { 1985 uint32_t hit_index = 1986 __builtin_ctzl(sec_hitmask[i]) 1987 >> 1; 1988 uint32_t key_idx = 1989 secondary_bkt[i]->key_idx[hit_index]; 1990 const struct rte_hash_key *key_slot = 1991 (const struct rte_hash_key *)( 1992 (const char *)h->key_store + 1993 key_idx * h->key_entry_size); 1994 1995 /* 1996 * If key index is 0, do not compare key, 1997 * as it is checking the dummy slot 1998 */ 1999 2000 if (!!key_idx & 2001 !rte_hash_cmp_eq( 2002 key_slot->key, keys[i], h)) { 2003 if (data != NULL) 2004 data[i] = key_slot->pdata; 2005 2006 hits |= 1ULL << i; 2007 positions[i] = key_idx - 1; 2008 goto next_key; 2009 } 2010 sec_hitmask[i] &= ~(3ULL << (hit_index << 1)); 2011 } 2012 next_key: 2013 continue; 2014 } 2015 2016 /* all found, do not need to go through ext bkt */ 2017 if ((hits == ((1ULL << num_keys) - 1)) || !h->ext_table_support) { 2018 if (hit_mask != NULL) 2019 *hit_mask = hits; 2020 __hash_rw_reader_unlock(h); 2021 return; 2022 } 2023 2024 /* need to check ext buckets for match */ 2025 for (i = 0; i < num_keys; i++) { 2026 if ((hits & (1ULL << i)) != 0) 2027 continue; 2028 next_bkt = secondary_bkt[i]->next; 2029 FOR_EACH_BUCKET(cur_bkt, next_bkt) { 2030 if (data != NULL) 2031 ret = search_one_bucket_l(h, keys[i], 2032 sig[i], &data[i], cur_bkt); 2033 else 2034 ret = search_one_bucket_l(h, keys[i], 2035 sig[i], NULL, cur_bkt); 2036 if (ret != -1) { 2037 positions[i] = ret; 2038 hits |= 1ULL << i; 2039 break; 2040 } 2041 } 2042 } 2043 2044 __hash_rw_reader_unlock(h); 2045 2046 if (hit_mask != NULL) 2047 *hit_mask = hits; 2048 } 2049 2050 static inline void 2051 __bulk_lookup_lf(const struct rte_hash *h, const void **keys, 2052 const struct rte_hash_bucket **primary_bkt, 2053 const struct rte_hash_bucket **secondary_bkt, 2054 uint16_t *sig, int32_t num_keys, int32_t *positions, 2055 uint64_t *hit_mask, void *data[]) 2056 { 2057 uint64_t hits = 0; 2058 int32_t i; 2059 int32_t ret; 2060 uint32_t prim_hitmask[RTE_HASH_LOOKUP_BULK_MAX] = {0}; 2061 uint32_t sec_hitmask[RTE_HASH_LOOKUP_BULK_MAX] = {0}; 2062 struct rte_hash_bucket *cur_bkt, *next_bkt; 2063 uint32_t cnt_b, cnt_a; 2064 2065 for (i = 0; i < num_keys; i++) 2066 positions[i] = -ENOENT; 2067 2068 do { 2069 /* Load the table change counter before the lookup 2070 * starts. Acquire semantics will make sure that 2071 * loads in compare_signatures are not hoisted. 2072 */ 2073 cnt_b = __atomic_load_n(h->tbl_chng_cnt, 2074 __ATOMIC_ACQUIRE); 2075 2076 /* Compare signatures and prefetch key slot of first hit */ 2077 for (i = 0; i < num_keys; i++) { 2078 compare_signatures(&prim_hitmask[i], &sec_hitmask[i], 2079 primary_bkt[i], secondary_bkt[i], 2080 sig[i], h->sig_cmp_fn); 2081 2082 if (prim_hitmask[i]) { 2083 uint32_t first_hit = 2084 __builtin_ctzl(prim_hitmask[i]) 2085 >> 1; 2086 uint32_t key_idx = 2087 primary_bkt[i]->key_idx[first_hit]; 2088 const struct rte_hash_key *key_slot = 2089 (const struct rte_hash_key *)( 2090 (const char *)h->key_store + 2091 key_idx * h->key_entry_size); 2092 rte_prefetch0(key_slot); 2093 continue; 2094 } 2095 2096 if (sec_hitmask[i]) { 2097 uint32_t first_hit = 2098 __builtin_ctzl(sec_hitmask[i]) 2099 >> 1; 2100 uint32_t key_idx = 2101 secondary_bkt[i]->key_idx[first_hit]; 2102 const struct rte_hash_key *key_slot = 2103 (const struct rte_hash_key *)( 2104 (const char *)h->key_store + 2105 key_idx * h->key_entry_size); 2106 rte_prefetch0(key_slot); 2107 } 2108 } 2109 2110 /* Compare keys, first hits in primary first */ 2111 for (i = 0; i < num_keys; i++) { 2112 while (prim_hitmask[i]) { 2113 uint32_t hit_index = 2114 __builtin_ctzl(prim_hitmask[i]) 2115 >> 1; 2116 uint32_t key_idx = 2117 __atomic_load_n( 2118 &primary_bkt[i]->key_idx[hit_index], 2119 __ATOMIC_ACQUIRE); 2120 const struct rte_hash_key *key_slot = 2121 (const struct rte_hash_key *)( 2122 (const char *)h->key_store + 2123 key_idx * h->key_entry_size); 2124 2125 /* 2126 * If key index is 0, do not compare key, 2127 * as it is checking the dummy slot 2128 */ 2129 if (!!key_idx & 2130 !rte_hash_cmp_eq( 2131 key_slot->key, keys[i], h)) { 2132 if (data != NULL) 2133 data[i] = __atomic_load_n( 2134 &key_slot->pdata, 2135 __ATOMIC_ACQUIRE); 2136 2137 hits |= 1ULL << i; 2138 positions[i] = key_idx - 1; 2139 goto next_key; 2140 } 2141 prim_hitmask[i] &= ~(3ULL << (hit_index << 1)); 2142 } 2143 2144 while (sec_hitmask[i]) { 2145 uint32_t hit_index = 2146 __builtin_ctzl(sec_hitmask[i]) 2147 >> 1; 2148 uint32_t key_idx = 2149 __atomic_load_n( 2150 &secondary_bkt[i]->key_idx[hit_index], 2151 __ATOMIC_ACQUIRE); 2152 const struct rte_hash_key *key_slot = 2153 (const struct rte_hash_key *)( 2154 (const char *)h->key_store + 2155 key_idx * h->key_entry_size); 2156 2157 /* 2158 * If key index is 0, do not compare key, 2159 * as it is checking the dummy slot 2160 */ 2161 2162 if (!!key_idx & 2163 !rte_hash_cmp_eq( 2164 key_slot->key, keys[i], h)) { 2165 if (data != NULL) 2166 data[i] = __atomic_load_n( 2167 &key_slot->pdata, 2168 __ATOMIC_ACQUIRE); 2169 2170 hits |= 1ULL << i; 2171 positions[i] = key_idx - 1; 2172 goto next_key; 2173 } 2174 sec_hitmask[i] &= ~(3ULL << (hit_index << 1)); 2175 } 2176 next_key: 2177 continue; 2178 } 2179 2180 /* all found, do not need to go through ext bkt */ 2181 if (hits == ((1ULL << num_keys) - 1)) { 2182 if (hit_mask != NULL) 2183 *hit_mask = hits; 2184 return; 2185 } 2186 /* need to check ext buckets for match */ 2187 if (h->ext_table_support) { 2188 for (i = 0; i < num_keys; i++) { 2189 if ((hits & (1ULL << i)) != 0) 2190 continue; 2191 next_bkt = secondary_bkt[i]->next; 2192 FOR_EACH_BUCKET(cur_bkt, next_bkt) { 2193 if (data != NULL) 2194 ret = search_one_bucket_lf(h, 2195 keys[i], sig[i], 2196 &data[i], cur_bkt); 2197 else 2198 ret = search_one_bucket_lf(h, 2199 keys[i], sig[i], 2200 NULL, cur_bkt); 2201 if (ret != -1) { 2202 positions[i] = ret; 2203 hits |= 1ULL << i; 2204 break; 2205 } 2206 } 2207 } 2208 } 2209 /* The loads of sig_current in compare_signatures 2210 * should not move below the load from tbl_chng_cnt. 2211 */ 2212 __atomic_thread_fence(__ATOMIC_ACQUIRE); 2213 /* Re-read the table change counter to check if the 2214 * table has changed during search. If yes, re-do 2215 * the search. 2216 * This load should not get hoisted. The load 2217 * acquires on cnt_b, primary key index and secondary 2218 * key index will make sure that it does not get 2219 * hoisted. 2220 */ 2221 cnt_a = __atomic_load_n(h->tbl_chng_cnt, 2222 __ATOMIC_ACQUIRE); 2223 } while (cnt_b != cnt_a); 2224 2225 if (hit_mask != NULL) 2226 *hit_mask = hits; 2227 } 2228 2229 #define PREFETCH_OFFSET 4 2230 static inline void 2231 __bulk_lookup_prefetching_loop(const struct rte_hash *h, 2232 const void **keys, int32_t num_keys, 2233 uint16_t *sig, 2234 const struct rte_hash_bucket **primary_bkt, 2235 const struct rte_hash_bucket **secondary_bkt) 2236 { 2237 int32_t i; 2238 uint32_t prim_hash[RTE_HASH_LOOKUP_BULK_MAX]; 2239 uint32_t prim_index[RTE_HASH_LOOKUP_BULK_MAX]; 2240 uint32_t sec_index[RTE_HASH_LOOKUP_BULK_MAX]; 2241 2242 /* Prefetch first keys */ 2243 for (i = 0; i < PREFETCH_OFFSET && i < num_keys; i++) 2244 rte_prefetch0(keys[i]); 2245 2246 /* 2247 * Prefetch rest of the keys, calculate primary and 2248 * secondary bucket and prefetch them 2249 */ 2250 for (i = 0; i < (num_keys - PREFETCH_OFFSET); i++) { 2251 rte_prefetch0(keys[i + PREFETCH_OFFSET]); 2252 2253 prim_hash[i] = rte_hash_hash(h, keys[i]); 2254 2255 sig[i] = get_short_sig(prim_hash[i]); 2256 prim_index[i] = get_prim_bucket_index(h, prim_hash[i]); 2257 sec_index[i] = get_alt_bucket_index(h, prim_index[i], sig[i]); 2258 2259 primary_bkt[i] = &h->buckets[prim_index[i]]; 2260 secondary_bkt[i] = &h->buckets[sec_index[i]]; 2261 2262 rte_prefetch0(primary_bkt[i]); 2263 rte_prefetch0(secondary_bkt[i]); 2264 } 2265 2266 /* Calculate and prefetch rest of the buckets */ 2267 for (; i < num_keys; i++) { 2268 prim_hash[i] = rte_hash_hash(h, keys[i]); 2269 2270 sig[i] = get_short_sig(prim_hash[i]); 2271 prim_index[i] = get_prim_bucket_index(h, prim_hash[i]); 2272 sec_index[i] = get_alt_bucket_index(h, prim_index[i], sig[i]); 2273 2274 primary_bkt[i] = &h->buckets[prim_index[i]]; 2275 secondary_bkt[i] = &h->buckets[sec_index[i]]; 2276 2277 rte_prefetch0(primary_bkt[i]); 2278 rte_prefetch0(secondary_bkt[i]); 2279 } 2280 } 2281 2282 2283 static inline void 2284 __rte_hash_lookup_bulk_l(const struct rte_hash *h, const void **keys, 2285 int32_t num_keys, int32_t *positions, 2286 uint64_t *hit_mask, void *data[]) 2287 { 2288 uint16_t sig[RTE_HASH_LOOKUP_BULK_MAX]; 2289 const struct rte_hash_bucket *primary_bkt[RTE_HASH_LOOKUP_BULK_MAX]; 2290 const struct rte_hash_bucket *secondary_bkt[RTE_HASH_LOOKUP_BULK_MAX]; 2291 2292 __bulk_lookup_prefetching_loop(h, keys, num_keys, sig, 2293 primary_bkt, secondary_bkt); 2294 2295 __bulk_lookup_l(h, keys, primary_bkt, secondary_bkt, sig, num_keys, 2296 positions, hit_mask, data); 2297 } 2298 2299 static inline void 2300 __rte_hash_lookup_bulk_lf(const struct rte_hash *h, const void **keys, 2301 int32_t num_keys, int32_t *positions, 2302 uint64_t *hit_mask, void *data[]) 2303 { 2304 uint16_t sig[RTE_HASH_LOOKUP_BULK_MAX]; 2305 const struct rte_hash_bucket *primary_bkt[RTE_HASH_LOOKUP_BULK_MAX]; 2306 const struct rte_hash_bucket *secondary_bkt[RTE_HASH_LOOKUP_BULK_MAX]; 2307 2308 __bulk_lookup_prefetching_loop(h, keys, num_keys, sig, 2309 primary_bkt, secondary_bkt); 2310 2311 __bulk_lookup_lf(h, keys, primary_bkt, secondary_bkt, sig, num_keys, 2312 positions, hit_mask, data); 2313 } 2314 2315 static inline void 2316 __rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys, 2317 int32_t num_keys, int32_t *positions, 2318 uint64_t *hit_mask, void *data[]) 2319 { 2320 if (h->readwrite_concur_lf_support) 2321 __rte_hash_lookup_bulk_lf(h, keys, num_keys, positions, 2322 hit_mask, data); 2323 else 2324 __rte_hash_lookup_bulk_l(h, keys, num_keys, positions, 2325 hit_mask, data); 2326 } 2327 2328 int 2329 rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys, 2330 uint32_t num_keys, int32_t *positions) 2331 { 2332 RETURN_IF_TRUE(((h == NULL) || (keys == NULL) || (num_keys == 0) || 2333 (num_keys > RTE_HASH_LOOKUP_BULK_MAX) || 2334 (positions == NULL)), -EINVAL); 2335 2336 __rte_hash_lookup_bulk(h, keys, num_keys, positions, NULL, NULL); 2337 return 0; 2338 } 2339 2340 int 2341 rte_hash_lookup_bulk_data(const struct rte_hash *h, const void **keys, 2342 uint32_t num_keys, uint64_t *hit_mask, void *data[]) 2343 { 2344 RETURN_IF_TRUE(((h == NULL) || (keys == NULL) || (num_keys == 0) || 2345 (num_keys > RTE_HASH_LOOKUP_BULK_MAX) || 2346 (hit_mask == NULL)), -EINVAL); 2347 2348 int32_t positions[num_keys]; 2349 2350 __rte_hash_lookup_bulk(h, keys, num_keys, positions, hit_mask, data); 2351 2352 /* Return number of hits */ 2353 return __builtin_popcountl(*hit_mask); 2354 } 2355 2356 2357 static inline void 2358 __rte_hash_lookup_with_hash_bulk_l(const struct rte_hash *h, 2359 const void **keys, hash_sig_t *prim_hash, 2360 int32_t num_keys, int32_t *positions, 2361 uint64_t *hit_mask, void *data[]) 2362 { 2363 int32_t i; 2364 uint32_t prim_index[RTE_HASH_LOOKUP_BULK_MAX]; 2365 uint32_t sec_index[RTE_HASH_LOOKUP_BULK_MAX]; 2366 uint16_t sig[RTE_HASH_LOOKUP_BULK_MAX]; 2367 const struct rte_hash_bucket *primary_bkt[RTE_HASH_LOOKUP_BULK_MAX]; 2368 const struct rte_hash_bucket *secondary_bkt[RTE_HASH_LOOKUP_BULK_MAX]; 2369 2370 /* 2371 * Prefetch keys, calculate primary and 2372 * secondary bucket and prefetch them 2373 */ 2374 for (i = 0; i < num_keys; i++) { 2375 rte_prefetch0(keys[i]); 2376 2377 sig[i] = get_short_sig(prim_hash[i]); 2378 prim_index[i] = get_prim_bucket_index(h, prim_hash[i]); 2379 sec_index[i] = get_alt_bucket_index(h, prim_index[i], sig[i]); 2380 2381 primary_bkt[i] = &h->buckets[prim_index[i]]; 2382 secondary_bkt[i] = &h->buckets[sec_index[i]]; 2383 2384 rte_prefetch0(primary_bkt[i]); 2385 rte_prefetch0(secondary_bkt[i]); 2386 } 2387 2388 __bulk_lookup_l(h, keys, primary_bkt, secondary_bkt, sig, num_keys, 2389 positions, hit_mask, data); 2390 } 2391 2392 static inline void 2393 __rte_hash_lookup_with_hash_bulk_lf(const struct rte_hash *h, 2394 const void **keys, hash_sig_t *prim_hash, 2395 int32_t num_keys, int32_t *positions, 2396 uint64_t *hit_mask, void *data[]) 2397 { 2398 int32_t i; 2399 uint32_t prim_index[RTE_HASH_LOOKUP_BULK_MAX]; 2400 uint32_t sec_index[RTE_HASH_LOOKUP_BULK_MAX]; 2401 uint16_t sig[RTE_HASH_LOOKUP_BULK_MAX]; 2402 const struct rte_hash_bucket *primary_bkt[RTE_HASH_LOOKUP_BULK_MAX]; 2403 const struct rte_hash_bucket *secondary_bkt[RTE_HASH_LOOKUP_BULK_MAX]; 2404 2405 /* 2406 * Prefetch keys, calculate primary and 2407 * secondary bucket and prefetch them 2408 */ 2409 for (i = 0; i < num_keys; i++) { 2410 rte_prefetch0(keys[i]); 2411 2412 sig[i] = get_short_sig(prim_hash[i]); 2413 prim_index[i] = get_prim_bucket_index(h, prim_hash[i]); 2414 sec_index[i] = get_alt_bucket_index(h, prim_index[i], sig[i]); 2415 2416 primary_bkt[i] = &h->buckets[prim_index[i]]; 2417 secondary_bkt[i] = &h->buckets[sec_index[i]]; 2418 2419 rte_prefetch0(primary_bkt[i]); 2420 rte_prefetch0(secondary_bkt[i]); 2421 } 2422 2423 __bulk_lookup_lf(h, keys, primary_bkt, secondary_bkt, sig, num_keys, 2424 positions, hit_mask, data); 2425 } 2426 2427 static inline void 2428 __rte_hash_lookup_with_hash_bulk(const struct rte_hash *h, const void **keys, 2429 hash_sig_t *prim_hash, int32_t num_keys, 2430 int32_t *positions, uint64_t *hit_mask, void *data[]) 2431 { 2432 if (h->readwrite_concur_lf_support) 2433 __rte_hash_lookup_with_hash_bulk_lf(h, keys, prim_hash, 2434 num_keys, positions, hit_mask, data); 2435 else 2436 __rte_hash_lookup_with_hash_bulk_l(h, keys, prim_hash, 2437 num_keys, positions, hit_mask, data); 2438 } 2439 2440 int 2441 rte_hash_lookup_with_hash_bulk(const struct rte_hash *h, const void **keys, 2442 hash_sig_t *sig, uint32_t num_keys, int32_t *positions) 2443 { 2444 RETURN_IF_TRUE(((h == NULL) || (keys == NULL) || 2445 (sig == NULL) || (num_keys == 0) || 2446 (num_keys > RTE_HASH_LOOKUP_BULK_MAX) || 2447 (positions == NULL)), -EINVAL); 2448 2449 __rte_hash_lookup_with_hash_bulk(h, keys, sig, num_keys, 2450 positions, NULL, NULL); 2451 return 0; 2452 } 2453 2454 int 2455 rte_hash_lookup_with_hash_bulk_data(const struct rte_hash *h, 2456 const void **keys, hash_sig_t *sig, 2457 uint32_t num_keys, uint64_t *hit_mask, void *data[]) 2458 { 2459 RETURN_IF_TRUE(((h == NULL) || (keys == NULL) || 2460 (sig == NULL) || (num_keys == 0) || 2461 (num_keys > RTE_HASH_LOOKUP_BULK_MAX) || 2462 (hit_mask == NULL)), -EINVAL); 2463 2464 int32_t positions[num_keys]; 2465 2466 __rte_hash_lookup_with_hash_bulk(h, keys, sig, num_keys, 2467 positions, hit_mask, data); 2468 2469 /* Return number of hits */ 2470 return __builtin_popcountl(*hit_mask); 2471 } 2472 2473 int32_t 2474 rte_hash_iterate(const struct rte_hash *h, const void **key, void **data, uint32_t *next) 2475 { 2476 uint32_t bucket_idx, idx, position; 2477 struct rte_hash_key *next_key; 2478 2479 RETURN_IF_TRUE(((h == NULL) || (next == NULL)), -EINVAL); 2480 2481 const uint32_t total_entries_main = h->num_buckets * 2482 RTE_HASH_BUCKET_ENTRIES; 2483 const uint32_t total_entries = total_entries_main << 1; 2484 2485 /* Out of bounds of all buckets (both main table and ext table) */ 2486 if (*next >= total_entries_main) 2487 goto extend_table; 2488 2489 /* Calculate bucket and index of current iterator */ 2490 bucket_idx = *next / RTE_HASH_BUCKET_ENTRIES; 2491 idx = *next % RTE_HASH_BUCKET_ENTRIES; 2492 2493 /* If current position is empty, go to the next one */ 2494 while ((position = __atomic_load_n(&h->buckets[bucket_idx].key_idx[idx], 2495 __ATOMIC_ACQUIRE)) == EMPTY_SLOT) { 2496 (*next)++; 2497 /* End of table */ 2498 if (*next == total_entries_main) 2499 goto extend_table; 2500 bucket_idx = *next / RTE_HASH_BUCKET_ENTRIES; 2501 idx = *next % RTE_HASH_BUCKET_ENTRIES; 2502 } 2503 2504 __hash_rw_reader_lock(h); 2505 next_key = (struct rte_hash_key *) ((char *)h->key_store + 2506 position * h->key_entry_size); 2507 /* Return key and data */ 2508 *key = next_key->key; 2509 *data = next_key->pdata; 2510 2511 __hash_rw_reader_unlock(h); 2512 2513 /* Increment iterator */ 2514 (*next)++; 2515 2516 return position - 1; 2517 2518 /* Begin to iterate extendable buckets */ 2519 extend_table: 2520 /* Out of total bound or if ext bucket feature is not enabled */ 2521 if (*next >= total_entries || !h->ext_table_support) 2522 return -ENOENT; 2523 2524 bucket_idx = (*next - total_entries_main) / RTE_HASH_BUCKET_ENTRIES; 2525 idx = (*next - total_entries_main) % RTE_HASH_BUCKET_ENTRIES; 2526 2527 while ((position = h->buckets_ext[bucket_idx].key_idx[idx]) == EMPTY_SLOT) { 2528 (*next)++; 2529 if (*next == total_entries) 2530 return -ENOENT; 2531 bucket_idx = (*next - total_entries_main) / 2532 RTE_HASH_BUCKET_ENTRIES; 2533 idx = (*next - total_entries_main) % RTE_HASH_BUCKET_ENTRIES; 2534 } 2535 __hash_rw_reader_lock(h); 2536 next_key = (struct rte_hash_key *) ((char *)h->key_store + 2537 position * h->key_entry_size); 2538 /* Return key and data */ 2539 *key = next_key->key; 2540 *data = next_key->pdata; 2541 2542 __hash_rw_reader_unlock(h); 2543 2544 /* Increment iterator */ 2545 (*next)++; 2546 return position - 1; 2547 } 2548