Solarflare X2522->X4522 报单路径的静默退化

一句话总结:当我们从 Solarflare X2522 网卡迁移到 X4522 网卡的时候,如果原样套用之前的代码,报单路径会出现静默退化,显式选择 Express datapath 来启用 CTPIO 能力

问题代码

背景是这样的,一开始我们是大量使用 X2522 网卡报单,走 EFVI 的 CTPIO,后面 Solarflare 发新卡了,就逐渐引入了 X4522,用 onload 自带的测试工具 eflatency 单独做端到端延迟测试,测下来比 X2522 快个 400ns,也没啥别的问题,就一点点灰度,引入生产环境了(详细的测试数据放在文末)。

复用了之前 X2522 的代码,报单、撤单、回报,也都能丝滑平移跑起来,编译、启动、运行,日志里一条错误都没有,给人一种像是 Exanic x25 之于 x10 那样无缝迁移的感觉,但是问题就出在这里了。。。

问题代码大致如下:

void setup(){
  ef_driver_open(&dh_);
  ef_pd_alloc_by_name(&pd_, dh_, nic_.c_str(), EF_PD_DEFAULT);
  ef_vi_alloc_from_pd(&vi_, ...);
  mmap(...);
  ef_memreg_alloc(...);
}

void send(){
  ef_vi_transmit_ctpio(&vi_, pkt, frame_len, 64);
  ef_vi_transmit_ctpio_fallback(&vi_, pkt_dma, frame_len, idx);
  ef_eventq_poll(&vi_,...);
}

这一段代码在之前 X2522 工作的很好,搬到 X4522 上也 “工作得很好”,报单正常,也没有错误日志。

直到后面重新看这一段代码,网卡开了 PTP,然后同步到系统时间戳,统计这个开销才发现问题,报单时间 -> 网卡 TX timestamp (Host To Wire) 花了 760ns,明显太高了,不符合预期。

重新让 AI 看了一遍代码,才发现问题出在这一行:

ef_pd_alloc_by_name(&pd_, dh_, nic_.c_str(), EF_PD_DEFAULT);

出在这个宏:EF_PD_DEFAULT。

这个函数的最后一个参数的含义是说申请这个 PD 的时候要带哪些特性,EF_PD_DEFAULT 顾名思义就是默认能力,在 X2522 上默认能力就是包含 CTPIO 的,但是到了 4522,默认能力里面可就没有这一项了,所以如果还用 Default,那么就会走 Enterprise Data Path,走 fallback 的 DMA 报单,就常规网卡那一套,写个 ring buffer,发个 doorbell,网卡再通过 DMA 拉过去,这样就会有一次额外的 PCIe 往返读开销(以前在机器上实测的 Gen3 400ns,Gen5 300ns)。

需要显式启用才能正常 work:

ef_pd_alloc_by_name(&pd_, dh_, nic_.c_str(), EF_PD_DEFAULT | EF_PD_EXPRESS);

不过其实也不完全是静默失败,代码迁移过去之后,ef_vi_alloc_from_pd,其实会返回 -95但是之前被我忽略掉了。。 。

我一开始还纳闷,怎么会不支持 CTPIO 呢,后面想了一下,觉得返回 -95 是代表”X4 是 EFCT 架构,CTPIO 本来就是默认发送模式,不需要、也不允许显式打开。。。 所以改成用 EF_VI_FLAGS_DEFAULT来 allocate vi 了,后续报单发包也都完全正常。

修复这个问题之后,实测穿透耗时降到了大约 219ns,760ns -> 219ns,快了 540ns !!!

量级上差不多正好一次 PCIe 往返读开销再加一点额外的开销,

是报撤单路径 hotpath 上相当大的优化了。

X4522 Express 修复前后穿透耗时

其实这个问题在官方的 Migration Guide 里面是有提到的,如果事先好好把迁移文档看一遍,是也能提前发现这个问题的。

两条 DataPath

这个问题背后的原因在于,X4522 的两套引擎。

X4522 网卡一定程度上我们可以视为 2522 和 3522 的一个缝合产物~

这个卡的内部在软件(onload)和硬件(ASIC 电路) 上,整出来了两套 DataPath,并不只是软件 API 上的区别,它在 ASIC 芯片上也是两套,彻底独立的两套。

他把 2522 那一套基本原封不动地继承了,然后呢,整了个名字叫 Enterprise Data Path,主打的是企业级全功能场景,巨帧、组播复制、卸载,以前 3522 没有的高级功能,在这个上面都得到了保留,但是代价呢,就是延迟会不那么极致一点,另外 4522 在 2522 的基础上把 CTPIO 功能给拆掉了,CTPIO 是留给了另外一套去做。

X4522 在 3522 EFCT 架构的基础上,单独弄了一套 Data Path 叫做 Express,主打的就是一个极致低延迟,只能通过 CTPIO 报单,只有这一条报单路径。

两条 Data Path 是完全独立的,使用两条 Data Path 要申请完全独立的 PD 和 VI,数据内存也不能在两套引擎之间共享。

x4522-dual-datapath

Onload 版本兼容性

还有一个问题也顺带聊一下,就是 onload 版本在 4522 和 2522 上的兼容性的问题。

老版本也能跑 X4522,但是即使在老版本(onload 9.0)上使用 ef_vi_transmit_ctpio()报单,能正常跑通,但是 4522 的数据结构其实跟 2522 完全不一样了,不可能正常走 Express 的 CTPIO。

但是这里底层内部还是能 fall back 到 DMA 低速报单,所以也能跑起来。

onload 9.1 是 2025 年年底跟着 4522 一起发布的,这是第一个能正式支持 4522 的 onload 版本,所以要使用 X4522 的话必须要升级到 9.1 及以上。

官网的要求也是必须 9.1,老版本能跑是凑巧。

The minimum Onload version required for X4 is Onload-9.1. Users of earlier versions must update Onload.

向后兼容做的很不错,目前实测,onload 9.1 9.2 其实是能完全兼容 X2522 的正常运行的,我们线上已经有大量 X2522 的机器运行在 onload 9.2 版本了,一切正常。

所以反正统一升级最新的版本就好了,两套卡都能跑。

附:eflatency 实测数据

X2522 / X4522 端到端 RTT(单位 µs):

链路 32B (mean/p50/p99/max) 1000B (mean/p50/p99/max)
X2 → X2 2.415⁄2.390⁄2.520⁄4.97 4.795⁄4.770⁄4.940⁄6.20
X4 → X2 1.984⁄1.970⁄2.040/4.23 3.966⁄3.950⁄4.090/5.30
X2 → X4 1.981⁄1.960⁄2.060/4.25 3.962⁄3.940⁄4.070/5.79