DPDK 函数 rte_pktmbuf_alloc 和 rte_pktmbuf_free 线程安全吗?
Are the DPDK functions rte_pktmbuf_alloc and rte_pktmbuf_free thread safe?
我们有一个用例,其中有一个 RX 线程和两个 TX 线程在同一端口上运行。 RX 线程在处理接收到的数据包后使用 rte_pktmbuf_free 释放 mbuf。 TX 线程使用 rte_pktmbuf_alloc 调用分配 mbuf 并在端口上传输。
当为 RX 描述符初始化和 TX 线程中的 mbuf 分配使用相同的池时,我们发现有时会出现意外的 mbuf 泄漏和分配失败。如果我们为 RX 和每个 TX 线程使用单独的池,那么我们就不会看到这些问题
我们没有在 mempool_create 调用中使用任何标志。 DPDK 文档似乎表明内存池操作是线程安全的?
我们使用的 DPDK 版本:19.11
我们是否应该为每个 RX 和两个 TX 线程创建唯一的内存池?
EDIT1:根据 Vipin 的要求添加的代码片段
DPDK 主进程
mbuf_pool = rte_pktmbuf_pool_create("fast_pkt", 8192,
256, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
/* Configure the Ethernet device. */
retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
/* Allocate and set up 1 RX queue per Ethernet port. */
retval = rte_eth_rx_queue_setup(port, q, 128,
rte_eth_dev_socket_id(port), NULL, mbuf_pool);
/* Allocate and set up 2 TX queue */
for (q = 0; q < tx_rings; q++) {
retval = rte_eth_tx_queue_setup(port, q, 512,
rte_eth_dev_socket_id(port), NULL);
}
/* Start the Ethernet port. */
retval = rte_eth_dev_start(port);
二次加工
主线程
mbuf_pool = rte_mempool_lookup("fast_pkt");
/* Create rx thread attach it to vCPU #4 */
rte_eal_remote_launch( main_rx, NULL, 4 );
/* create two tx threads and assign affinity to different vCPUs*/
pthread_create(&thread_id, NULL, tx_1, NULL );
CPU_ZERO(&cpuset);
CPU_SET(5, &cpuset);
pthread_setaffinity_np(thread_id, sizeof(cpuset), &cpuset);
pthread_create(&thread_id, NULL, tx_2, NULL );
CPU_ZERO(&cpuset);
CPU_SET(6, &cpuset);
pthread_setaffinity_np(thread_id, sizeof(cpuset), &cpuset);
接收线程(main_rx)
for (;; )
{
nb_rx = rte_eth_rx_burst(port , 0, pkts_burst, 32);
for ( i = 0; i < nb_rx ; i++ )
{
process_packet( pkts_burst[ i] );
rte_pktmbuf_free(pkts_burst[ i]);
}
}
传输线程(tx_1、tx_2)
tx_q = <thread_sepcific queue ID>
for ( ;; )
{
/* Receive Internal message for TX*/
get_packet ( work_buffer [Burst]);
for ( iterate through each index in work_buffer )
{
tx_mbuf[i] = rte_pktmbuf_alloc(mbuf_pool);
/* combination of following calls to construct packet */
rte_pktmbuf_mtod( .. );
/* follow above call with copy from work buffer */
rte_pktmbuf_prepend( .. );
rte_pktmbuf_append( .. );
}
rte_eth_tx_burst(start_port_id, tx_q, tx_mbuf, num_burst)
/* loop through packets not processed by rte_eth_tx_burst */
for(.. )
{
rte_pktmbuf_free (... );
}
}
[下面共享的 EDIT-1 答案是共享的聚合问题并提供了说明]
- 问题 1
is mempool operations are thread-safe?
-(答案)是的,它是关于 Lcore DPDK 线程的。
- 问题 2
Are we supposed to create unique mempool for each of the RX and the two TX threads?
-(答案)不,可以跨不同的端口和类似的端口队列对安全地使用相同的内存池。
- question-3
there is mbuf leak and depletion of mbuf_pool (sample code flow shared in question)
-(答案)修改代码后,已经 运行ning 超过 12 小时没有泄漏。
- 问题 4
we wanted to understand if the rte_pktmbuf_alloc and rte_pktmbuf_free are intended to be thread-safe or not
-(答案)是
- 问题 5
Our RX thread uses "rte_eal_remote_launch" whereas our TX threads are created directly using pthread_create from the application. Do you think that makes a difference to thread safety? Should our TX threads also be launched using "rte_eal_remote_launch"?
-(答案)是
- question-6
we should not have multiple threads (I.e., pthreads) allocated to same lcore and accessing same memory pool.
- (answer) 是的,这是正确的理解
原因:DPDK lcore线程内部完成的事情比thread create and set affinity
多。请参考DPDK EAL Documentation for clarity。基于为 rte_eal_init
选择的选项
- coremask(十六进制)是 DPDK lcore 线程的 1:1 CPU 个逻辑核心。
- lcores(十进制)是 DPDK lcore 线程的 1:1 CPU 个逻辑核心。
- lcoresmask(十进制)为1:N,其中1CPU个逻辑核心可以拆分为
N
个DPDK lcore线程。
- 每个DPDK lcore线程内部存储线程局部变量
index
,也就是rte_lcore_id
- 因此,当使用
rte_pktmbuf_alloc
时,它会使用 id
来获取缓存内存池对象。在多个线程上使用相同的 id 违反了无锁模型,并且如果多个请求并发会导致危险。
因此,在主从模式下使用正确的 API 调用使用 DPDK 示例 skeleton
重写代码片段不会产生内存泄漏。 运行 下面分享的统计数据
######################## NIC statistics for port 0 ########################
RX-packets: 15048531 RX-errors: 0 RX-bytes: 1123238216
RX-nombuf: 0
TX-packets: 15048504 TX-errors: 0 TX-bytes: 902910240
############################################################################
######################## NIC statistics for port 0 ########################
RX-packets: 23424430063 RX-errors: 0 RX-bytes: 1405686130136
RX-nombuf: 0
TX-packets: 23424430036 TX-errors: 0 TX-bytes: 1405465802032
############################################################################
RX-nombuf
很容易显示 PMD 是否由于缺少 mbuf[=28= 而无法获取数据包]
内存池信息
========== show - MEMPOOL ==========
mempool <MBUF_POOL>@0x11ff9fad00
flags=10
socket_id=1
pool=0x11ff9eab00
iova=0xeff9fad00
nb_mem_chunks=1
size=8191
populated_size=8191
header_size=64
elt_size=2304
trailer_size=0
total_obj_size=2368
private_data_size=64
ops_index=7
ops_name: <ring_mp_mc>
avg bytes/object=2368.289098
internal cache infos:
cache_size=250
cache_count[32]=171
cache_count[34]=90
total_cache_count=261
common_pool_count=2874
注意:以上统计数据是通过 dpdk-proc-info 实用程序获取的。
我们有一个用例,其中有一个 RX 线程和两个 TX 线程在同一端口上运行。 RX 线程在处理接收到的数据包后使用 rte_pktmbuf_free 释放 mbuf。 TX 线程使用 rte_pktmbuf_alloc 调用分配 mbuf 并在端口上传输。
当为 RX 描述符初始化和 TX 线程中的 mbuf 分配使用相同的池时,我们发现有时会出现意外的 mbuf 泄漏和分配失败。如果我们为 RX 和每个 TX 线程使用单独的池,那么我们就不会看到这些问题
我们没有在 mempool_create 调用中使用任何标志。 DPDK 文档似乎表明内存池操作是线程安全的?
我们使用的 DPDK 版本:19.11
我们是否应该为每个 RX 和两个 TX 线程创建唯一的内存池?
EDIT1:根据 Vipin 的要求添加的代码片段
DPDK 主进程
mbuf_pool = rte_pktmbuf_pool_create("fast_pkt", 8192,
256, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
/* Configure the Ethernet device. */
retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
/* Allocate and set up 1 RX queue per Ethernet port. */
retval = rte_eth_rx_queue_setup(port, q, 128,
rte_eth_dev_socket_id(port), NULL, mbuf_pool);
/* Allocate and set up 2 TX queue */
for (q = 0; q < tx_rings; q++) {
retval = rte_eth_tx_queue_setup(port, q, 512,
rte_eth_dev_socket_id(port), NULL);
}
/* Start the Ethernet port. */
retval = rte_eth_dev_start(port);
二次加工
主线程
mbuf_pool = rte_mempool_lookup("fast_pkt");
/* Create rx thread attach it to vCPU #4 */
rte_eal_remote_launch( main_rx, NULL, 4 );
/* create two tx threads and assign affinity to different vCPUs*/
pthread_create(&thread_id, NULL, tx_1, NULL );
CPU_ZERO(&cpuset);
CPU_SET(5, &cpuset);
pthread_setaffinity_np(thread_id, sizeof(cpuset), &cpuset);
pthread_create(&thread_id, NULL, tx_2, NULL );
CPU_ZERO(&cpuset);
CPU_SET(6, &cpuset);
pthread_setaffinity_np(thread_id, sizeof(cpuset), &cpuset);
接收线程(main_rx)
for (;; )
{
nb_rx = rte_eth_rx_burst(port , 0, pkts_burst, 32);
for ( i = 0; i < nb_rx ; i++ )
{
process_packet( pkts_burst[ i] );
rte_pktmbuf_free(pkts_burst[ i]);
}
}
传输线程(tx_1、tx_2)
tx_q = <thread_sepcific queue ID>
for ( ;; )
{
/* Receive Internal message for TX*/
get_packet ( work_buffer [Burst]);
for ( iterate through each index in work_buffer )
{
tx_mbuf[i] = rte_pktmbuf_alloc(mbuf_pool);
/* combination of following calls to construct packet */
rte_pktmbuf_mtod( .. );
/* follow above call with copy from work buffer */
rte_pktmbuf_prepend( .. );
rte_pktmbuf_append( .. );
}
rte_eth_tx_burst(start_port_id, tx_q, tx_mbuf, num_burst)
/* loop through packets not processed by rte_eth_tx_burst */
for(.. )
{
rte_pktmbuf_free (... );
}
}
[下面共享的 EDIT-1 答案是共享的聚合问题并提供了说明]
- 问题 1
is mempool operations are thread-safe?
-(答案)是的,它是关于 Lcore DPDK 线程的。 - 问题 2
Are we supposed to create unique mempool for each of the RX and the two TX threads?
-(答案)不,可以跨不同的端口和类似的端口队列对安全地使用相同的内存池。 - question-3
there is mbuf leak and depletion of mbuf_pool (sample code flow shared in question)
-(答案)修改代码后,已经 运行ning 超过 12 小时没有泄漏。 - 问题 4
we wanted to understand if the rte_pktmbuf_alloc and rte_pktmbuf_free are intended to be thread-safe or not
-(答案)是 - 问题 5
Our RX thread uses "rte_eal_remote_launch" whereas our TX threads are created directly using pthread_create from the application. Do you think that makes a difference to thread safety? Should our TX threads also be launched using "rte_eal_remote_launch"?
-(答案)是 - question-6
we should not have multiple threads (I.e., pthreads) allocated to same lcore and accessing same memory pool.
- (answer) 是的,这是正确的理解
原因:DPDK lcore线程内部完成的事情比thread create and set affinity
多。请参考DPDK EAL Documentation for clarity。基于为 rte_eal_init
- coremask(十六进制)是 DPDK lcore 线程的 1:1 CPU 个逻辑核心。
- lcores(十进制)是 DPDK lcore 线程的 1:1 CPU 个逻辑核心。
- lcoresmask(十进制)为1:N,其中1CPU个逻辑核心可以拆分为
N
个DPDK lcore线程。 - 每个DPDK lcore线程内部存储线程局部变量
index
,也就是rte_lcore_id
- 因此,当使用
rte_pktmbuf_alloc
时,它会使用id
来获取缓存内存池对象。在多个线程上使用相同的 id 违反了无锁模型,并且如果多个请求并发会导致危险。
因此,在主从模式下使用正确的 API 调用使用 DPDK 示例 skeleton
重写代码片段不会产生内存泄漏。 运行 下面分享的统计数据
######################## NIC statistics for port 0 ########################
RX-packets: 15048531 RX-errors: 0 RX-bytes: 1123238216
RX-nombuf: 0
TX-packets: 15048504 TX-errors: 0 TX-bytes: 902910240
############################################################################
######################## NIC statistics for port 0 ########################
RX-packets: 23424430063 RX-errors: 0 RX-bytes: 1405686130136
RX-nombuf: 0
TX-packets: 23424430036 TX-errors: 0 TX-bytes: 1405465802032
############################################################################
RX-nombuf
很容易显示 PMD 是否由于缺少 mbuf[=28= 而无法获取数据包]
内存池信息
========== show - MEMPOOL ==========
mempool <MBUF_POOL>@0x11ff9fad00
flags=10
socket_id=1
pool=0x11ff9eab00
iova=0xeff9fad00
nb_mem_chunks=1
size=8191
populated_size=8191
header_size=64
elt_size=2304
trailer_size=0
total_obj_size=2368
private_data_size=64
ops_index=7
ops_name: <ring_mp_mc>
avg bytes/object=2368.289098
internal cache infos:
cache_size=250
cache_count[32]=171
cache_count[34]=90
total_cache_count=261
common_pool_count=2874
注意:以上统计数据是通过 dpdk-proc-info 实用程序获取的。