即使rte_eth_rx_burst没有返回完整的突发,也会丢弃数据包

时间:2018-03-25 09:57:00

标签: network-programming dpdk

我有一个奇怪的掉落问题,要理解我的问题,最好的方法是看一下这个简单的片段:

while( 1 )
{
    if( config->running == false ) {
        break;
    }
    num_of_pkt = rte_eth_rx_burst( config->port_id,
                                   config->queue_idx,
                                   buffers,
                                   MAX_BURST_DEQ_SIZE);
    if( unlikely( num_of_pkt == MAX_BURST_DEQ_SIZE ) ) {
        rx_ring_full = true; //probably not the best name
    }

    if( likely( num_of_pkt > 0 ) )
    {
        pk_captured += num_of_pkt;

        num_of_enq_pkt = rte_ring_sp_enqueue_bulk(config->incoming_pkts_ring,
                                               (void*)buffers,
                                               num_of_pkt,
                                               &rx_ring_free_space);
        //if num_of_enq_pkt == 0 free the mbufs..
     }
}

此循环正在从设备检索数据包并将其推入队列以供另一个核心进一步处理。

当我使用Mellanox卡以2.5M p / s发送20M(20878300)数据包进行测试时,循环似乎错过了一些数据包,而pk_captured总是像19M或类似。

rx_ring_full永远不会为真,这意味着num_of_pkt总是< MAX_BURST_DEQ_SIZE,因此根据文档,我不会在HW级别下降。此外,num_of_enq_pkt永远不会为0,这意味着所有数据包都已入队。

现在,如果从那个snipped中删除了rte_ring_sp_enqueue_bulk调用(并确保释放所有mbuf),那么pk_captured总是完全等于我发送给NIC的数据包数量。

所以看来(但是我无法解决这个问题)rte_ring_sp_enqueue_bulk在某种程度上太慢了,在一次调用rte_eth_rx_burst和另外一些数据包由于NIC上的完全响铃而被丢弃,但是,为什么num_of_pkt(来自rte_eth_rx_burst)是总是小于MAX_BURST_DEQ_SIZE(小得多),好像总是有足够的空间容纳数据包?

注意,MAX_BURST_DEQ_SIZE为512。

编辑1:

也许这些信息可能会有所帮助:rte_eth_stats_get似乎也可以看到丢弃,或者更正确,没有报告丢弃(imissed和ierrors为0)但是ipackets的值等于我的计数器pk_captured(丢失的数据包)刚刚消失了??)

编辑2:

根据ethtools,rx_crc_errors_phy为零,所有数据包都以PHY级别接收(rx_packets_phy使用正确数量的传输数据包进行更新)。

来自rte_eth_stats的rx_nombuf的值似乎包含垃圾(这是我们的测试应用程序中的打印件):

OUT(4):端口1统计:ipkt:19439285,opkt:0,ierr:0,oerr:0,imiss:0,rxnobuf:2061021195718

对于20M数据包的传输,你可以看到rxnobuf是垃圾或它有一个我不明白的含义。日志由以下内容生成:

  log("Port %"PRIu8" stats: ipkt:%"PRIu64",opkt:%"PRIu64",ierr:%"PRIu64",oerr:%"PRIu64",imiss:%"PRIu64", rxnobuf:%"PRIu64,
        config->port_id,
        stats.ipackets, stats.opackets,
        stats.ierrors, stats.oerrors,
        stats.imissed, stats.rx_nombuf);

其中统计数据来自rte_eth_stats_get。

数据包不是即时生成的,而是从现有的PCAP重放。

编辑3

在得到Adriy的答案之后(谢谢!)我已经为Mellanox卡包含了xstats输出,同时用较小的数据包集再现同样的问题我可以看到rx_mbuf_allocation_errors得到更新,但它似乎包含垃圾:

OUT(4): rx_good_packets = 8094164
OUT(4): tx_good_packets = 0
OUT(4): rx_good_bytes = 4211543077
OUT(4): tx_good_bytes = 0
OUT(4): rx_missed_errors = 0
OUT(4): rx_errors = 0
OUT(4): tx_errors = 0
OUT(4): rx_mbuf_allocation_errors = 146536495542

这些计数器似乎也很有趣:

OUT(4): tx_errors_phy = 0
OUT(4): rx_out_of_buffer = 257156
OUT(4): tx_packets_phy = 9373
OUT(4): rx_packets_phy = 8351320

其中rx_packets_phy是我发送的确切数据包数量,并将rx_out_of_buffer与rx_good_packets相加,我得到的确切数量。因此,似乎mbufs耗尽并且一些数据包被丢弃。

我在原始代码中进行了调整,现在我正在使用link从RX环中复制mbuf,并立即释放内存,另一个lcore在副本上进行进一步处理。这不能解决这个问题,事实证明,要解决这个问题我要禁用数据包处理并释放数据包副本(在另一个lcore上),这没有任何意义。

嗯,会做更多的调查,但至少rx_mbuf_allocation_errors似乎需要在这里修复。

2 个答案:

答案 0 :(得分:1)

我想,调试rx_nombuf计数器是一种可行的方法。它可能看起来像垃圾,但事实上这个计数器并不反映丢弃的数据包的数量(如ierrorsimissed),而是反映的RX尝试失败次数。

以下是MLX5 PMD的摘要:

uint16_t
mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
{
    [...]
    while (pkts_n) {
        [...]
        rep = rte_mbuf_raw_alloc(rxq->mp);
        if (unlikely(rep == NULL)) {
            ++rxq->stats.rx_nombuf;
            if (!pkt) {
                /*
                 * no buffers before we even started,
                 * bail out silently.
                 */
                break;

因此,该问题的合理情况如下:

  1. RX队列中有一个数据包。
  2. 相应的mempool中没有缓冲区。
  3. 应用程序轮询新数据包,即循环调用:num_of_pkt = rte_eth_rx_burst(...)

  4. 每次拨打rte_eth_rx_burst()时,rx_nombuf计数器都会增加。

  5. 请同时查看rte_eth_xstats_get()。对于MLX5 PMD,有一个硬件rx_out_of_buffer计数器,可能会证实这一理论。

答案 1 :(得分:0)

丢失数据包的解决方案是将Ring API从批量更改为突发。在dpdk中,有两种模式:振铃操作批量和突发操作。在批量出队模式下,如果请求的元素为32,并且有31个元素,则API返回0。

我也遇到过类似的问题。