一次RX Mbuf耗尽引发的DPDK性能雪崩——彻底理解DPDK RX Refill机制与Descriptor Ownership(下) 接上文一次RX Mbuf耗尽引发的DPDK性能雪崩——彻底理解DPDK RX Refill机制与Descriptor Ownership上-CSDN博客十一、真正负责补充RX Buffer的是谁很多人第一次阅读ixgbe_rxtx.c或ice_rxtx.c时都会找到类似下面的逻辑不同PMD实现细节略有差异if (rxq-rxrearm_nb rxq-rx_free_thresh) ixgbe_rxq_rearm(rxq);很多人看到这里都会误认为rxrearm_nb表示还有多少个空闲Buffer。实际上并不是。真正的含义更接近自上次Rearm以来已经消费了多少个RX Descriptor。也就是说PMD并不会收到一个包补一个Buffer。而是累计直到达到rx_free_thresh才统一Rearm。十二、Rearm函数到底干了什么真正的Rearm过程。可以抽象成Mempool ↓ Bulk Alloc ↓ 获得32个mbuf ↓ 写入32个RX Descriptor ↓ 更新DMA地址 ↓ Memory Barrier ↓ 更新RX Tail ↓ Ownership交给NIC真正耗时最大的并不是申请mbuf。而是Descriptor更新DMA地址写入以及Doorbell。所以一次完成32个远远快于执行32次。十三、为什么一定要Bulk Alloc很多人会说现在rte_pktmbuf_alloc(已经很快了。为什么还要Bulk原因就在于DPDK真正调用的是rte_mempool_get_bulk()而不是rte_mempool_get()原因很简单。一次Ring操作。例如CAS Memory Barrier Producer Index Consumer Index这些固定成本。如果申请1个对象成本就是1次。申请32个对象成本仍然只有1次。于是平均下来每个mbuf成本下降几十倍。这也是DPDK所有高速路径几乎全部采用Bulk。十四、为什么Vector RX要求32对齐很多人配置rx_free_thresh都会发现官方建议32 64很少推荐17 21 37原因就在于Vector RX。Intel AVX2 AVX-512 PMD。很多时候不是一个Descriptor、一个Descriptor处理。而是一次处理多个。例如AVX2。一次可以加载多个Descriptor。于是Rearm也按照Vector宽度一起完成。这样SIMD能够充分发挥。如果Threshold设置很奇怪。Vector很难连续工作。性能开始下降。这也是Intel PMD推荐32、64而不是其它数字。十五、为什么RX Ring几乎永远不会空很多新人都会担心如果CPU处理不过来。RX Ring是不是马上就空了实际上正常情况下不会。因为PMD始终遵循一个原则消费一批补充一批。例如收到32个包 ↓ 处理32个包 ↓ Rearm32个Descriptor ↓ 重新交给NIC整个Ring始终保持接近满。只有Rearm失败才会越来越少。最后出现RX No Buffer十六、真正导致Rearm失败的原因是什么很多文章都会说mbuf耗尽。其实真正生产环境更常见的是Mempool短时间无法提供足够数量的连续对象。例如某个Worker因为异常流量。突然连续收到几万个小包。处理速度开始慢于DMA写入。Rearm需要一次申请32个mbuf。但是当前Bulk Alloc失败。于是PMD只能先返回。Descriptor没有及时补齐。NIC继续DMA。越来越多Descriptor变成Unavailable。最终RX No Buffer持续增加。这里需要说明的是真正的DPDK mempool通常不会因为连续对象不足而失败它按对象而不是连续物理地址进行管理。实际工程中更常见的原因包括整个mempool中的可用mbuf确实不足某些数据包仍在应用层持有尚未释放RX与TX之间形成了短时间的对象供需失衡多个RX Queue共享同一个mempool在突发流量下局部耗尽。因此Rearm失败的根本原因通常是可回收mbuf供应不足而不是内存连续性问题。十七、现网故障最终是如何定位的研发团队最终增加了几项统计rx_rearm_count rx_no_buffer mempool_avail bulk_alloc_fail结果发现高峰期间Bulk Alloc失败开始持续增加。进一步分析业务。发现某个新版本为了镜像分析大量数据包在应用层延迟释放。导致mbuf长时间不能回到Mempool。最终RX Queue无法及时Rearm。NIC开始No Buffer。整个吞吐迅速下降。十八、最终修复方案整个系统没有修改NIC配置。没有修改RSS。没有修改Burst。而是进行了三项优化。第一缩短mbuf持有时间。应用层完成业务处理后立即释放mbuf不再因为日志、统计或镜像等原因长期持有。第二重新评估mempool规模。根据实际峰值流量和最长处理时延适当增加RX所需的mbuf数量避免突发流量期间出现对象短缺。第三保持批量Rearm策略。继续使用PMD默认的批量补充机制不为了追求实时补充而改成逐包Rearm。修改以后指标故障前修复后吞吐12.6Gbps98.1GbpsRX No Buffer持续增长0Bulk Alloc Fail很高0CPU100%100%RX Drop持续增加0整个系统没有增加CPU。没有更换NIC。只是恢复RX Buffer正常循环。性能立即恢复。十九、RX Refill为什么是DPDK性能的核心很多开发者认为DPDK真正重要的是Burst、Cache、NUMA。实际上RX Refill同样决定整个收包路径是否能够持续运行。因为NIC不会主动申请内存。它永远只会DMA到Descriptor指定的位置。所以PMD真正承担的是持续为NIC准备好可写入的Buffer。如果Buffer供应中断。NIC即使拥有100G也只能丢包。二十、写在最后DPDK的数据面设计本质上是一条不断循环的资源链Mempool ↓ RX Descriptor ↓ NIC DMA ↓ CPU处理 ↓ 释放mbuf ↓ 回到Mempool ↓ 再次Rearm RX Ring真正限制系统长期稳定运行的并不仅仅是CPU算力或网卡带宽而是这条资源循环能否持续闭合。rx_free_thresh、批量Rearm、Bulk Alloc、Vector RX这些看似只是PMD中的实现细节实际上都是为了保证RX Ring始终拥有足够的可用Buffer同时尽可能减少Descriptor更新和同步开销。因此在排查DPDK收包性能问题时如果发现CPU始终100%PCIe正常Cache正常TLB正常NIC没有硬件错误但rx_no_buffer持续增加那么排查重点就应放在mbuf是否被长期占用mempool容量是否满足峰值需求应用是否存在释放延迟RX Buffer是否能够持续完成Rearm。很多看似是网卡收不上包的问题本质上其实是CPU没有及时把新的Buffer交还给NIC。理解这一点也就真正理解了DPDK接收路径能够长期稳定运行的核心设计思想。