DPDK 进阶编程指南
发表于更新于
北京
零、前言
一些 DPDK 的基础内容(安装、配置、rte_flow等)可以参考前文:
一、哈希库rte_hash
DPDK 提供了一个标准的哈希表的实现,能用来根据键值快速进行索引。
a. API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| struct rte_hash_parameters flow_hash_table_parameter = { .name = table_name, .entries = MAX_HASH_ENTRIES, .key_len = sizeof(union ipv4_5tuple_host), .hash_func = ipv4_hash_crc, .hash_func_init_val = 0, }; flow_hash_table = rte_hash_create(&flow_hash_table_parameter);
int rte_hash_add_key_data(const struct rte_hash *h, const void *key, void *data);
int rte_hash_lookup_data(const struct rte_hash *h, const void *key, void **data);
int32_t rte_hash_del_key(const struct rte_hash *h, const void *key);
int rte_hash_get_key_with_position(const struct rte_hash *h, const int32_t position, void **key);
const void *key = 0; void *data = 0; uint32_t *next = 0; uint32_t current = rte_hash_iterate(flow_hash_table, &key, &data, next); for (; current != -ENOENT; current = rte_hash_iterate(flow_hash_table, &key, &data, next)) { int32_t del_key = rte_hash_del_key(flow_hash_table, key); rte_hash_free_key_with_position(flow_hash_table, del_key); rte_free(data); } rte_hash_free(flow_hash_table);
|
b. 自定义哈希
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #if defined(RTE_ARCH_X86) || defined(__ARM_FEATURE_CRC32) #define EM_HASH_CRC 1 #endif
#ifdef EM_HASH_CRC #include <rte_hash_crc.h> #define DEFAULT_HASH_FUNC rte_hash_crc #else #include <rte_jhash.h> #define DEFAULT_HASH_FUNC rte_jhash #endif
static inline uint32_t ipv4_hash_crc(const void *data, __rte_unused uint32_t data_len, uint32_t init_val) { const union ipv4_5tuple_host *k; uint32_t t; const uint32_t *p;
k = data; t = k->proto; p = (const uint32_t *) &k->port_src;
#ifdef EM_HASH_CRC init_val = rte_hash_crc_4byte(t, init_val); init_val = rte_hash_crc_4byte(k->ip_src, init_val); init_val = rte_hash_crc_4byte(k->ip_dst, init_val); init_val = rte_hash_crc_4byte(*p, init_val); #else init_val = rte_jhash_1word(t, init_val); init_val = rte_jhash_1word(k->ip_src, init_val); init_val = rte_jhash_1word(k->ip_dst, init_val); init_val = rte_jhash_1word(*p, init_val); #endif
return init_val; }
|
c. 快速获取五元组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| union ipv4_5tuple_host key = {.xmm=0}; mask0 = (rte_xmm_t) {.u32 = {BIT_8_TO_15, ALL_32_BITS, ALL_32_BITS, ALL_32_BITS}};
static __rte_always_inline void get_ipv4_5tuple(struct rte_mbuf *m0, __m128i mask0, union ipv4_5tuple_host *key) { __m128i tmpdata0 = _mm_loadu_si128( rte_pktmbuf_mtod_offset(m0, __m128i *, sizeof(struct rte_ether_hdr) + offsetof(struct rte_ipv4_hdr, time_to_live)));
key->xmm = _mm_and_si128(tmpdata0, mask0); }
static inline void get_ipv4_5tuple(struct rte_mbuf *m0, int32x4_t mask0, union ipv4_5tuple_host *key) { int32x4_t tmpdata0 = vld1q_s32(rte_pktmbuf_mtod_offset(m0, int32_t *, sizeof(struct rte_ether_hdr) + offsetof(struct rte_ipv4_hdr, time_to_live)));
key->xmm = vandq_s32(tmpdata0, mask0); }
|
二、高性能获取时间
可以通过获取处理器寄存器中的周期数来高性能的获取精准时间,但要保证处理器运行在一个稳定的频率上,这样时间才是准确的。
1 2 3 4 5 6 7
| uint64_t rte_rdtsc()
rte_get_tsc_hz()
time = rte_rdtsc() / rte_get_tsc_hz()
|
三、能耗管理库rte_power
由于获取时间需要稳定处理器频率,所以可以利用 DPDK 提供的 API 对处理器进行配置,稳定的频率也有利于保证业务的稳定,能去除尖刺。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| rte_power_init(lcore_id);
uint32_t freqs[30]; rte_power_freqs(lcore_id, freqs, 30); for (int i = 0; i < 30; ++i) { printf("\t%u", freqs[i]); }
ret = rte_power_set_freq(lcore_id, 2);
rte_power_exit(lcore_id);
|
四、搭配CMake使用
1 2 3 4 5 6 7 8 9 10 11
| find_package(PkgConfig REQUIRED) pkg_search_module(LIBDPDK REQUIRED libdpdk)
find_package(Threads REQUIRED)
link_directories(${LIBDPDK_LIBRARY_DIRS}) include_directories(${LIBDPDK_INCLUDE_DIRS})
target_link_libraries(smart_offload ${LIBDPDK_LIBRARIES} Threads::Threads)
|