/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2010-2014 Intel Corporation
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <sys/queue.h>
#include <rte_memory.h>
#include <rte_launch.h>
#include <rte_eal.h>
#include <rte_per_lcore.h>
#include <rte_lcore.h>
#include <rte_debug.h>
#include <rte_hash.h>
#include <rte_hash_crc.h>
typedef struct tuple_space {
int sport;
int dport;
}Tuple;
static struct rte_hash*
htable_create(void)
{
struct rte_hash *hash_table;
struct rte_hash_parameters hash_params;
(void)memset(&hash_params, sizeof(struct rte_hash_parameters), 0);
hash_params.entries = 10;
hash_params.key_len = sizeof(struct tuple_space);
hash_params.hash_func = rte_hash_crc;
hash_params.hash_func_init_val = 0;
hash_params.name = "htable_0";
hash_params.socket_id = rte_lcore_to_socket_id(rte_get_master_lcore());
hash_params.extra_flag = 0;
//hash_params.key_mode = RTE_HASH_KEY_MODE_DUP;
/* Find if the hash table was created before */
hash_table = rte_hash_find_existing(hash_params.name);
if (hash_table != NULL) {
printf("hash_table[%s] exist!\n", hash_params.name);
return hash_table;
} else {
hash_table = rte_hash_create(&hash_params);
if (!hash_table) {
return NULL;
}
}
printf("create hash_table[%s] success!\n", hash_params.name);
return hash_table;
}
int
main(int argc, char **argv)
{
int ret, i, idx;
struct rte_hash *htable;
Tuple key[] = {
{
.sport=10000,
.dport=20000,
},
{
.sport=30000,
.dport=40000,
},
};
ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_panic("Cannot init EAL\n");
htable = htable_create();
if (!htable) {
printf("create hashtable failed!\n");
return -1;
}
/* 插入两个key */
for(i=0;i<2;i++) {
idx = rte_hash_add_key(htable, (void *)&key[i]);
if (idx < 0) {
printf("rte_hash_add_key %d failed!\n", i);
} else {
printf("rte_hash_add_key %d success! idx:%d\n", i, idx);
}
}
/* 获取key个数 */
size =rte_hash_count(htable);
printf("htable size=%d\n", size);
/* 查找某个key */
idx = rte_hash_lookup(htable, (void *)&key[1]);
if (idx < 0) {
printf("rte_hash_lookup key failed!\n");
} else {
printf("rte_hash_lookup key success! idx:%d\n", idx);
}
if (htable) {
rte_hash_free(htable);
}
return 0;
}
rte_hash说明
wiki:
http://doc.dpdk.org/api/rte__hash_8h.html#a62ff0b837d39bbff00863feabf15e224
rte_hash_parameters 结构体
struct rte_hash_parameters {
const char *name; /**< Name of the hash. */
uint32_t entries; /**< Total hash table entries. */
uint32_t reserved; /**< Unused field. Should be set to 0 */
uint32_t key_len; /**< Length of hash key. */
rte_hash_function hash_func; /**< Primary Hash function used to calculate hash. */
uint32_t hash_func_init_val; /**< Init value used by hash_func. */
int socket_id; /**< NUMA Socket ID for memory. */
uint8_t extra_flag; /**< Indicate if additional parameters are present. */
};
填写哈希表的名字,表最大数目,key长度,哈希函数,哈希函数初始值,NUMA套接字, 额外参数标志
重要的是选择 hash 函数
typedef uint32_t (*rte_hash_function)(const void *key, uint32_t key_len,
uint32_t init_val);
rte_jhash.h 提供了一些函数如 rte_jhash, rte_jhash_1words, rte_jash_2words
其余相关函数有:
设置比较函数
void rte_hash_set_cmp_func(struct rte_hash *h, rte_hash_cmp_eq_t func);
查看是否已经有同名的表
struct rte_hash *
rte_hash_find_existing(const char *name);
删除表
void
rte_hash_free(struct rte_hash *h);
创建函数
struct rte_hash *
rte_hash_create(const struct rte_hash_parameters *params);
添加key、data
int32_t
rte_hash_add_key(const struct rte_hash *h, const void *key);
int
rte_hash_add_key_data(const struct rte_hash *h, const void *key, void *data);
int32_t
rte_hash_add_key_with_hash_data(const struct rte_hash *h, const void *key,
hash_sig_t sig, void *data);
int32_t
rte_hash_add_key_with_hash_data(const struct rte_hash *h, const void *key,
hash_sig_t sig, void *data);
int32_t
rte_hash_del_key(const struct rte_hash *h, const void *key);
删除key、data
int32_t
rte_hash_del_key(const struct rte_hash *h, const void *key);
int32_t
rte_hash_del_key_with_hash(const struct rte_hash *h, const void *key, hash_sig_t sig);
查找
int
rte_hash_get_key_with_position(const struct rte_hash *h, const int32_t position,
void **key);
int
rte_hash_lookup_data(const struct rte_hash *h, const void *key, void **data);
int
rte_hash_lookup_with_hash_data(const struct rte_hash *h, const void *key,
hash_sig_t sig, void **data);
int32_t
rte_hash_lookup(const struct rte_hash *h, const void *key);
int32_t
rte_hash_lookup_with_hash(const struct rte_hash *h,
const void *key, hash_sig_t sig);
hash_sig_t
rte_hash_hash(const struct rte_hash *h, const void *key);
int
rte_hash_lookup_bulk_data(const struct rte_hash *h, const void **keys,
uint32_t num_keys, uint64_t *hit_mask, void *data[]);
int
rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys,
uint32_t num_keys, int32_t *positions);
int32_t
rte_hash_iterate(const struct rte_hash *h, const void **key, void **data, uint32_t *next);
extra_flag
哈希库可以在多进程环境中使用。 唯一的只能在单进程模式下使用的函数是rte_hash_set_cmp_func(),该函数用于设置一个自定义的比较函数,由于该函数被分配给当前进程的函数指针(因此在多进程模式下不支持)
创建hash表时可以看到有hash_params.extra_flag 参数,功能如下:
- 多线程支持
哈希库支持多线程,并且用户可以在哈希表创建时通过设置适当的标志来指定所需的操作模式。 在所有操作模式下,查找都是线程安全的,这意味着可以从多个线程同时调用查找。
对于并发写入和并发读写,以下标志值定义了相应的操作模式:
-
如果设置了“多个写者”的标志(RTE_HASH_EXTRA_FLAGS_MULTI_WRITER_ADD),则允许多个线程写入表。键的添加、删除和表重置受到保护。如果仅设置此标志,无法保护“读者”免受正在进行的写入操作的影响。
-
如果设置了读/写并发(RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY),则多线程读/写操作是安全的(即,应用程序不需要“停止读者访问哈希表,一直等待写者完成更新为止”。读者和写者可以同时在表上进行操作 )。该库使用读写锁提供并发。
-
除了这两个标志值外,如果还设置了事务性内存标志(RTE_HASH_EXTRA_FLAGS_TRANS_MEM_SUPPORT),那么如果硬件支持,读写锁将使用硬件事务性内存(例如Intel®TSX)来保证线程安全。如果平台支持Intel®TSX,建议设置事务性内存标志,因为这将加快并发表操作。否则,由于软件锁定机制相关的开销,并发操作将变慢。
-
如果设置了无锁读/写并发(RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF),则将提供不使用读写锁的读/写并发。对于不支持事务性内存的平台(例如,当前基于ARM的平台),建议设置此标志以实现更高的性能可伸缩性。如果设置了此标志,则默认设置(RTE_HASH_EXTRA_FLAGS_NO_FREE_ON_DEL)标志。
-
如果设置了“在删除时不释放”(RTE_HASH_EXTRA_FLAGS_NO_FREE_ON_DEL)标志,则在调用delete()时不会释放哈希表中条目的位置。当设置无锁读/写并发标志时,默认情况下启用此标志。在所有读者都停止引用该位置之后,应用程序应释放该位置。在需要时,应用程序可以利用RCU机制来确定读者何时停止引用该位置。
- 可扩展的桶(Bucket )功能支持
通过额外的标志来启用此功能(默认情况下未设置)。设置(RTE_HASH_EXTRA_FLAGS_EXT_TABLE)时(在极不可能的情况下,由于哈希冲突过多而导致无法插入键盘),哈希桶将使用链表进行扩展,以插入这些失败的键。 对于插入哈希表的键个数能达到容量的100%且不能容忍任何键插入失败(即使很少)的工作负载(例如,电信工作负载),此功能非常重要。
PS:启用“无锁读写并发”标志(RTE_HASH_EXTRA_FLAGS_NO_FREE_ON_DEL or RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF)后,用户 在调用rte_hash_del_key_XXX 后还需要调用
“ rte_hash_free_key_with_position” API才能释放空存储桶和已删除的键,以保持100%的容量保证