是否可以对齐DPDK rte_mbufs以在Linux上启用直接文件IO(O_DIRECT

toiithl6  于 2023-04-29  发布在  Linux
关注(0)|答案(1)|浏览(102)

背景

我正在编写一个应用程序,它要求我通过网络接口流式传输数据,并以非常高的吞吐量将其写入磁盘。网络和文件IO组件是单独实现的,两者都能够独立实现项目所需的吞吐量。网络端利用DPDK(更相关),文件IO端利用io_uring(不太相关)。为了实现我所需要的高文件IO吞吐量,我必须使用直接IO(O_DIRECT);无论用于实现文件IO的技术如何,这都是真实的。使用页面缓存不是一个简单的选择。应用程序必须从NIC零拷贝到我们用于存储的NVMes。

问题

我无法对齐DPDK消息缓冲区(rte_mbuf)以启用直接IO。这严重限制了我的文件IO吞吐量,如果这是不可能的,我可能需要找到DPDK的替代品,当然,这是我想避免的。**有人知道如何实现这种内存对齐吗?**消息缓冲区应与4096的倍数地址对齐。

代码片段

有许多方法可以设置DPDK内存池(rte_mempoool)和消息缓冲区。现在,我使用的是rte_pktmbuf_pool_create()(如下所示),它创建一个内存池,并通过一个函数调用分配消息缓冲区,但如果它有助于我获得所需的对齐,我愿意采用不同的方法。

初始化mempool
rte_pktmbuf_pool_create(name, num_bufs, DPDK_MBUF_CACHE_SIZE, 0, mbuf_size, cpu_socket);

在哪里...

  • DPDK_MBUF_CACHE_SIZE由硬件决定,设置为315
  • mbuf_size为9000 + RTE_PKTMBUF_HEADROOM(由DPDK定义为128)+ RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN
agxfikkp

agxfikkp1#

请参阅下面的代码片段,它们提供了一个解决方案。在你的项目中尝试实现类似的东西之前,一定要从头到尾读一遍。此外,请注意,为了简洁起见,所有关键错误处理都已删除,并且应重新添加到任何类似的实现中。
register_external_buffers()在巨大的页面中分配外部存储器区域,并将它们注册到DPDK中。

unsigned register_external_buffers(rte_device* device, uint32_t num_mbufs, uint16_t mbuf_size, unsigned socket, rte_pktmbuf_extmem **ext_mem)
{
    rte_pktmbuf_extmem *extmem_array; // Array of external memory descriptors
    unsigned elements_per_zone; // Memory is reserved and registered in zones
    unsigned n_zones; // Number of zones needed to accomodate all mbufs
    uint16_t element_size; // Size, in bytes, of one mbuf element
    int status; // Used to store error codes / return values

    element_size = RTE_ALIGN_CEIL(mbuf_size, 4096);
    elements_per_zone = RTE_PGSIZE_1G / element_size;
    n_zones = (num_mbufs / elements_per_zone) + ((num_mbufs % elements_per_zone) ? 1 : 0);
    extmem_array = new rte_pktmbuf_extmem[n_zones];

    for (int extmem_index = 0; extmem_index < n_zones; extmem_index++) 
    {
        rte_pktmbuf_extmem *current_extmem = extmem_array + extmem_index;
        current_extmem->buf_ptr = mmap(NULL, RTE_PGSIZE_1G, PROT_READ | PROT_WRITE, MAP_HUGETLB | MAP_SHARED | MAP_ANONYMOUS | MAP_POPULATE | MAP_LOCKED, -1, 0);
        current_extmem->buf_iova = NULL;
        current_extmem->buf_len = RTE_PGSIZE_1G;
        current_extmem->elt_size = element_size;

        rte_extmem_register(current_extmem->buf_ptr, current_extmem->buf_len, NULL, 0, RTE_PGSIZE_1G);
        rte_dev_dma_map(device, current_extmem->buf_ptr, (rte_iova_t) current_extmem->buf_ptr, current_extmem->buf_len);
    }
    *ext_mem = extmem_array;
    return n_zones;
}

然后register_external_buffers可以如下使用:

rte_eth_dev_info dev_info;
rte_eth_dev_info_get(port_id, &dev_info);
unsigned length = register_external_buffers(dev_info.device, num_bufs, mbuf_size, cpu_socket, &extmem);

m_rx_pktbuf_pools.at(cpu_socket) = rte_pktmbuf_pool_create_extbuf(name, num_bufs, DPDK_MBUF_CACHE_SIZE, 0, mbuf_size, cpu_socket, extmem, length);

虽然这确实会导致所有mbuf数据都在外部内存区域中存储和对齐,但它们会对齐到巨大的页面--而不是典型的4k页面。这意味着,虽然最初的问题得到了解决,但由于页面边界的数量非常有限,因此该解决方案对于该用例来说并不是非常实用。

相关问题