为什么 DPDK VirtIO 只能跑到 40Gbps?—— 一次 KVM 虚拟交换机性能瓶颈定位全过程(上)

一、一个令人困惑的现网问题

某云计算厂商上线了一套基于DPDK + KVM的高性能虚拟交换平台。

整体网络架构如下:

┌──────────────┐ │ Physical NIC │ │ Intel E810 │ └──────┬───────┘ │ DPDK vSwitch │ vhost-user │ QEMU/KVM │ VirtIO-Net │ Guest DPDK

设计目标非常明确:

  • 物理网络:100GbE
  • Guest:DPDK PMD
  • 宿主机:DPDK vSwitch
  • Zero Copy
  • 单 VM 满速接近 100Gbps

实验室测试中,小包、大包都表现正常。

然而真正上线以后,监控平台却发现了一个奇怪的现象:

包长理论吞吐实际吞吐
64 Bytes100Gbps36~40Gbps
512 Bytes100Gbps42Gbps 左右
1500 Bytes接近 100Gbps92~95Gbps

也就是说:只有小包性能异常。

更奇怪的是:

宿主机 CPU:

平均利用率: 48%

Guest CPU:

42%

PMD:没有忙轮询瓶颈。

Descriptor:没有耗尽。

没有任何:

RX Miss TX Error No Mbuf Ring Full

整个系统看起来一切正常。

但是吞吐率就是上不去。


二、所有人都认为是 PMD 性能不足

第一次排查时。

整个团队几乎一致认为:

一定是 PMD 跑满了。

于是开始查看:

dpdk-proc-info --stats

所有 Port:

统计如下:

RX packets : 正常 TX packets : 正常 RX errors : 0 TX errors : 0 RX no mbuf : 0 RX missed : 0

继续查看PMD Loop:

CPU 42% Idle 58%

这意味着:

PMD并没有跑满。

于是有人开始怀疑:

RSS是不是分布不均?

检查:

RSS所有 Queue:

Queue0 Queue1 Queue2 Queue3 ...... Packets 基本一致

RSS正常。

继续NUMA。

结果:

Guest Memory Local Host HugePage Local NIC Local

NUMA也没有问题。

到这里整个团队几乎陷入了僵局。


三、问题真的发生在 VirtIO 吗?

为了进一步定位。

我们绕开 Guest。

直接HostDPDK转发:

E810:

结果:

98Gbps

完全正常。

说明:DPDK没有问题。

继续绕开VirtIO。

改成SR-IOV VF直通Guest:

再次测试:

96Gbps

依然正常。

问题:

第一次真正开始收敛。

如果:Host正常、Guest、DPDK正常、SR-IOV正常。

那么:

唯一发生变化的。

只有:

VirtIO + vhost-user

整个团队开始把目光放到VirtIO。


四、Perf 火焰图暴露了真正的热点

为了定位到底CPU时间花在哪里。

我们在Host运行:

perf record -g

随后生成火焰图。

得到如下热点:

vhost_worker() │ vring_avail_event() │ vring_need_event() │ eventfd_write() │ syscall

占比超过:

31%

继续:

Guest采样结果:

热点几乎全部集中:

virtio_recv_pkts() ↓ virtqueue_get_buf() ↓ used_idx

这里第一次出现一个陌生名字:

Virtqueue。

也是VirtIO性能真正开始分化的地方。


五、VirtIO 真正的数据通路

很多人认为:

VirtIO就是共享内存。

实际上:

真正的数据流远比想象复杂。

整体过程如下:

Guest mbuf │ ▼ VirtIO Driver │ ▼ Descriptor Table │ ▼ Available Ring │ ▼ Kick Host │ ▼ vhost-user │ ▼ Host DPDK │ ▼ NIC DMA

如果反方向:则Host完成DMA以后。

继续:

Used Ring ↓ Notify Guest ↓ Guest PMD ↓ mbuf

看到这里。

很多开发者都会觉得没有什么特别。

其实真正的问题。

并不是Descriptor。

而是后面的两个动作。

Kick Notify

六、真正拖慢性能的不是拷贝,而是通知(Notification)

很多开发者第一次接触 VirtIO 时,都会重点关注:

  • Descriptor 是否连续?
  • 是否 Zero Copy?
  • 是否发生数据拷贝?

这些当然重要。

但在高 PPS(Packets Per Second)场景下,真正限制性能的往往不是数据路径,而是控制路径

对于每一批发送完成的数据,Guest 都需要通知 Host:

“新的 Descriptor 已经准备好了。”

这个动作称为:

Kick

同样:

Host处理完成以后。

又需要通知Guest:

Notify

如果每一个Burst都Kick。

Host就需要不停响应通知。

CPU不断发生:

Guest ↓ VM Exit ↓ KVM ↓ vhost-user ↓ 继续处理

而:

VM Exit正是整个虚拟化里面最昂贵的操作之一。

为什么小包只能40Gbps?

真正答案开始慢慢浮现。


未完待续