

最近在一台 GPU 服务器上测试 ConnectX-6 SR-IOV VF 直通虚拟机。物理 IB 端口协商为 `200G`，虚拟机里的 RDMA 端口也处于 `ACTIVE`，但 `ib_write_bw` 单向吞吐始终只有 `57~60 Gbit/s`。

排查从虚拟化侧一路检查到测试工具和固件。最终的 PF 对 PF 测试仍然只有约 `60G`，而其中一张 ConnectX-6 的 PCIe 链路是 **Gen3 x8**。这个链路的理论单向有效带宽约为 `63 Gbit/s`，足以解释实测结果。

本文记录配置路径、测试对照和定位过程。重点是如何把问题从虚拟机、VF 和测试工具逐层缩小到物理 PCIe 链路。

<!--more-->

## 先理解 IB 与 RDMA

### IB 是什么

IB 是 InfiniBand 的简称，是一种面向高带宽、低延迟通信的网络互连技术。网卡在 IB 语境中通常称为 HCA（Host Channel Adapter），HCA 通过 PCIe 连接主机内存，再通过 IB 链路连接交换机和另一端 HCA。

因此，网卡标注的 `200G` 首先描述的是 HCA 与 IB Fabric 之间的端口链路能力。它不直接等于主机内存到另一台主机内存的端到端吞吐，这个吞吐还会受到主机侧 PCIe 链路和 NUMA 布局影响，驱动与测试参数也会改变结果。

### RDMA 是什么

RDMA（Remote Direct Memory Access）描述的是一种数据传输方式：通信双方建立资源并注册内存后，网卡可以按照队列中的请求直接读写远端内存，数据面通常不需要远端 CPU 参与，也避免了传统 TCP/IP 路径中的多次内存拷贝。

RDMA 的控制面仍然需要驱动和内核参与，例如创建 QP（Queue Pair）、注册内存和建立连接。应用数据面常通过 verbs 接口提交 Work Request，再从 CQ（Completion Queue）获取完成事件。`ib_write_bw` 使用的就是这条 verbs 路径。

### IB 与 RDMA 的关系

两者有直接关系，但不是同一个概念。IB（InfiniBand）是一整套网络互连与传输架构，定义了链路、交换、路由和传输服务；RDMA 是其中的一种通信语义，允许应用通过网卡执行远端内存读写。也就是说，IB 负责提供通信承载和传输机制，RDMA 负责说明数据如何以「远端内存访问」的方式被提交和完成。

在 InfiniBand 上，一次 RDMA Write 的路径可以简化为：

```text
应用
  -> verbs / QP
  -> RDMA core / mlx5_ib
  -> 本端 HCA
  -> IB transport / Fabric
  -> 对端 HCA
  -> 远端内存
```

本文测试的 `ib_write_bw` 就处在这条路径上：它通过 verbs 创建 QP，并使用 InfiniBand 的 RC（Reliable Connection）传输服务执行 RDMA Write。`Transport type: IB` 说明承载是 InfiniBand，RDMA Write 说明传输操作是远端内存写入；两者是同一次测试中的不同层次。

RDMA 并不只依赖 InfiniBand，也可以使用 RoCE 等其他承载。反过来，IPoIB 是把 IB 作为网络接口来承载普通 IP 包，不等于 RDMA verbs；它们可以共享同一个 HCA 和 IB Fabric，但使用的上层数据路径不同。

### IPoIB 与 RDMA 的关系

IPoIB（IP over InfiniBand）把 IB 设备表现为 Linux 网络接口，用来承载普通 IP 数据包。它和 verbs RDMA 是两条不同的数据路径：

```text
普通 IP 流量：应用 -> TCP/IP -> IPoIB netdev -> HCA -> IB Fabric
RDMA 流量：   应用 -> verbs/QP -> HCA -> IB Fabric -> HCA
```

所以，`/sys/class/net/ib*/mode` 显示 `datagram`，只能说明 IPoIB netdev 的工作模式，不能据此判断 RDMA 是否启用。后文会用 `ib_write_bw` 测试 IB 承载的 RDMA verbs 路径，再用 `iperf3` 测试 IPoIB 上的 TCP/IP 路径。

## 环境与拓扑

宿主机环境：

```text
Ubuntu 22.04
Linux 5.15
libvirt 8.x
QEMU 6.x
IOMMU: Enabled
```

服务器为双路 NUMA。两台虚拟机分别绑定到不同 NUMA Node，避免 CPU 和内存跨 NUMA 访问。

两张 IB 网卡的状态如下：

```text
ConnectX-6 #1
  Link: 200G InfiniBand
  PCIe: Gen4 x16
  FW:   20.39.3004

ConnectX-6 #2
  Link: 200G InfiniBand
  PCIe: Gen3 x8
  FW:   20.39.3004
```

测试流量经过一台 IB 交换机：

```text
VM1
  |
  | SR-IOV VF
  v
ConnectX-6 PF #1 (PCIe Gen4 x16)
  |
  | 200G IB
  v
IB Switch
  |
  | 200G IB
  v
ConnectX-6 PF #2 (PCIe Gen3 x8)
  ^
  | SR-IOV VF
  |
VM2
```

下面的配置和测试会解绑驱动、删除并重新创建 VF，还会把 PF 网卡移动到 network namespace。只能在测试主机上执行；如果主机上有其他业务，先停止相关虚拟机并记录原始配置。

## 配置 SR-IOV VF 直通

### 创建 VF

先确认 PF 与 Linux 设备名：

```bash
ibdev2netdev
lspci | grep -i mellanox
```

假设两张 PF 对应：

```text
mlx5_0 -> ib0
mlx5_3 -> ib1
```

每张 PF 创建一个 VF：

```bash
echo 1 > /sys/class/net/ib0/device/sriov_numvfs
echo 1 > /sys/class/net/ib1/device/sriov_numvfs
```

确认 VF 已出现：

```bash
lspci -nn | grep 'ConnectX-6 Virtual Function'
```

例如：

```text
98:00.1 Infiniband controller: Mellanox Technologies ConnectX-6 Virtual Function [15b3:101c]
b2:00.1 Infiniband controller: Mellanox Technologies ConnectX-6 Virtual Function [15b3:101c]
```

### 配置 VF GUID

InfiniBand VF 需要有效的 Node GUID、Port GUID，并且 policy 允许它跟随 PF。GUID 仍然为全 0，或 policy 不正确时，VF 可能无法进入 `ACTIVE`。

```bash
# PF mlx5_0 的 VF0
echo <VF0_NODE_GUID> > /sys/class/infiniband/mlx5_0/device/sriov/0/node
echo <VF0_PORT_GUID> > /sys/class/infiniband/mlx5_0/device/sriov/0/port
echo Follow > /sys/class/infiniband/mlx5_0/device/sriov/0/policy

# PF mlx5_3 的 VF0
echo <VF1_NODE_GUID> > /sys/class/infiniband/mlx5_3/device/sriov/0/node
echo <VF1_PORT_GUID> > /sys/class/infiniband/mlx5_3/device/sriov/0/port
echo Follow > /sys/class/infiniband/mlx5_3/device/sriov/0/policy
```

检查配置：

```bash
cat /sys/class/infiniband/mlx5_0/device/sriov/0/{node,port,policy}
cat /sys/class/infiniband/mlx5_3/device/sriov/0/{node,port,policy}
```

实际环境中必须为每个 VF 分配唯一 GUID，不能直接复制示例值。

### 绑定 vfio-pci

先把 VF 从 `mlx5_core` 解绑：

```bash
echo 0000:98:00.1 > /sys/bus/pci/drivers/mlx5_core/unbind
echo 0000:b2:00.1 > /sys/bus/pci/drivers/mlx5_core/unbind
```

让 `vfio-pci` 接管对应设备：

```bash
echo 15b3 101c > /sys/bus/pci/drivers/vfio-pci/new_id 2>/dev/null || true
```

检查绑定结果：

```bash
lspci -nnk -s 98:00.1
lspci -nnk -s b2:00.1
```

应该看到：

```text
Kernel driver in use: vfio-pci
```

写入 `new_id` 后，内核可能已经自动完成绑定。如果再次手动执行：

```bash
echo 0000:98:00.1 > /sys/bus/pci/drivers/vfio-pci/bind
```

并得到：

```text
Device or resource busy
```

先用 `lspci -nnk` 检查当前驱动。只要已经显示 `vfio-pci`，这个提示不一定代表绑定失败。

### 配置 libvirt 直通

在虚拟机 XML 中加入 PCI Host Device：

```xml
<hostdev mode='subsystem' type='pci' managed='yes'>
  <source>
    <address domain='0x0000' bus='0x98' slot='0x00' function='0x1'/>
  </source>
</hostdev>
```

第二台虚拟机使用另一个 VF 的 PCI 地址。GPU 和 PCIe 设备直通环境建议使用 Q35 芯片组与 OVMF/UEFI。

## 在虚拟机内配置 RDMA

虚拟机使用 Ubuntu 24.04，安装 RDMA 和测试工具：

```bash
sudo apt update
sudo apt install -y \
  rdma-core \
  ibverbs-utils \
  infiniband-diags \
  perftest \
  iperf3
```

加载 `mlx5_ib`：

```bash
sudo modprobe mlx5_ib
echo mlx5_ib | sudo tee /etc/modules-load.d/mlx5_ib.conf
```

检查设备：

```bash
ibv_devices
ibv_devinfo
rdma link
```

正常情况下可以看到类似信息：

```text
transport:      InfiniBand
state:          PORT_ACTIVE
active_mtu:     4096
link_layer:     InfiniBand
```

### 配置 IPoIB 地址

使用独立测试网段。VM1 的 netplan 配置：

```yaml
network:
  version: 2
  ethernets:
    ibp9s0:
      addresses:
        - 10.200.0.11/24
```

VM2 的配置：

```yaml
network:
  version: 2
  ethernets:
    ibp9s0:
      addresses:
        - 10.200.0.12/24
```

应用配置并先验证连通性：

```bash
sudo chmod 600 /etc/netplan/*.yaml
sudo netplan generate
sudo netplan apply
ping 10.200.0.12
```

## `datagram` 不代表没有 RDMA

查看 IPoIB netdev 模式：

```bash
cat /sys/class/net/ibp9s0/mode
```

输出可能是：

```text
datagram
```

`datagram` 描述的是 IPoIB netdev 的工作模式，不等于 RDMA 没有启用。`ib_write_bw` 使用 verbs/RC QP，不经过普通 TCP/IP 数据面。

例如测试输出中的以下字段已经说明测试路径是 RDMA：

```text
RDMA_Write BW Test
Transport type : IB
Connection type: RC
Link type      : IB
```

`ib_write_bw -R` 中的 `-R` 也不是「打开 RDMA」。`ib_write_bw` 本身就是 RDMA 测试，`-R` 只表示使用 `rdma_cm` 建立连接和 QP。

## VM 内的多组测试都停在约 60G

### 基准测试

VM1 作为 server：

```bash
ib_write_bw -d ibp9s0 -i 1 -s 1048576 -q 4
```

VM2 作为 client：

```bash
ib_write_bw -d ibp9s0 -i 1 -s 1048576 -q 4 10.200.0.11
```

结果稳定在：

```text
~7139 MB/s
~57.1 Gbit/s
```

### 改用 rdma_cm

server：

```bash
ib_write_bw \
  -d ibp9s0 \
  -R \
  --run_infinitely \
  --report_gbits
```

client：

```bash
ib_write_bw \
  -d ibp9s0 \
  -R \
  --run_infinitely \
  --report_gbits \
  10.200.0.11
```

结果约为 `59.1 Gbit/s`，与基准测试基本一致，因此问题不像是 `rdma_cm` 建连路径造成的。

### 测试双向吞吐

```bash
ib_write_bw \
  -d ibp9s0 \
  -i 1 \
  -s 1048576 \
  -q 4 \
  -b
```

结果为：

```text
~13960 MB/s
~111.7 Gbit/s aggregate
```

`-b` 报告的是双向 aggregate，不能直接与单向 `200G` 比较。按两个方向近似拆分：

```text
方向 A -> B: ~56G
方向 B -> A: ~56G
合计:       ~112G
```

### 用 iperf3 对照

再用普通 TCP/IPoIB 测试，确认是否只有 perftest 或 verbs 路径受影响。

server：

```bash
iperf3 -s
```

client：

```bash
iperf3 -c 10.200.0.11 -P 8 -t 30
```

结果约为 `58.7 Gbit/s`，同时 TCP 已经出现不少 retransmission。

不同测试方法的结果如下：

| 测试 | 数据路径 | 结果 |
| --- | --- | ---: |
| `ib_write_bw` | RDMA verbs | ~57.1 Gbit/s |
| `ib_write_bw -R` | RDMA + rdma_cm | ~59.1 Gbit/s |
| `ib_write_bw -b` | 双向 RDMA aggregate | ~111.7 Gbit/s |
| `iperf3 -P 8` | TCP/IPoIB | ~58.7 Gbit/s |

RDMA、`rdma_cm` 和 TCP/IPoIB 的单向测试都撞在接近 `60G` 的位置，继续调整 `ib_write_bw` 参数的收益已经很低。下一步应该隔离 KVM、VFIO 和虚拟机驱动。

## 用 PF 对 PF 测试隔离虚拟化层

关键问题是：这约 `60G` 的上限来自虚拟化，还是宿主机物理链路本身？

### 先避免本机路由短路

如果在同一台主机给两个接口配置地址，再直接访问另一个本机 IP：

```bash
ip route get 10.210.0.2
```

可能得到：

```text
local 10.210.0.2 dev lo
```

此时 Linux 会执行 local delivery，流量不会真正经过 HCA 和 IB 交换机，测试结果没有意义。

因此，需要把两个 IB netdev 放到不同的 network namespace，强制流量走真实的 IB 路径。

### 删除 VF 并移动 PF

先关闭使用 VF 的虚拟机，再删除 VF：

```bash
echo 0 > /sys/class/net/ib0/device/sriov_numvfs
echo 0 > /sys/class/net/ib1/device/sriov_numvfs
```

确认 RDMA namespace 配置：

```bash
rdma system show
```

本次输出为：

```text
netns shared
copy-on-fork on
```

创建两个 namespace 并移动 PF：

```bash
ip netns add ns0
ip netns add ns1

ip link set ib0 netns ns0
ip link set ib1 netns ns1
```

分别配置接口地址：

```bash
ip netns exec ns0 ip link set lo up
ip netns exec ns0 ip link set ib0 up
ip netns exec ns0 ip addr add 10.210.0.1/24 dev ib0

ip netns exec ns1 ip link set lo up
ip netns exec ns1 ip link set ib1 up
ip netns exec ns1 ip addr add 10.210.0.2/24 dev ib1
```

确认路由没有落到 `lo`：

```bash
ip netns exec ns0 ip route get 10.210.0.2
ip netns exec ns1 ip route get 10.210.0.1
```

应该类似：

```text
10.210.0.2 dev ib0 src 10.210.0.1
10.210.0.1 dev ib1 src 10.210.0.2
```

先测试连通性：

```bash
ip netns exec ns0 ping 10.210.0.2
```

### PF 对 PF 跑 RDMA

server：

```bash
ip netns exec ns1 \
  ib_write_bw -d mlx5_3 -R --report_gbits
```

client：

```bash
ip netns exec ns0 \
  ib_write_bw -d mlx5_0 -R --report_gbits 10.210.0.2
```

结果为：

```text
BW peak:    60.26 Gbit/s
BW average: 60.26 Gbit/s
```

这次测试绕过了：

- KVM/QEMU
- VFIO
- SR-IOV VF
- Guest Kernel
- Guest `mlx5` 驱动

PF 对 PF 仍然只有约 `60G`，所以问题已经可以从虚拟化层移开。

## 检查 PCIe 链路

查看两张 ConnectX-6 的链路能力和当前状态：

```bash
lspci -vv -s 98:00.0 | grep -E 'LnkCap:|LnkSta:'
lspci -vv -s b2:00.0 | grep -E 'LnkCap:|LnkSta:'
```

第一张网卡：

```text
LnkSta: Speed 16GT/s, Width x16
```

即当前为 `PCIe Gen4 x16`。

第二张网卡：

```text
LnkSta: Speed 8GT/s, Width x8
```

即当前为 `PCIe Gen3 x8`。ConnectX-6 本身支持更高 PCIe 规格，并不代表它在当前服务器上一定能协商到对应速率和宽度；最终要看 `LnkSta` 的实际状态。

### Gen3 x8 的带宽上限

PCIe 3.0 每 lane 为 `8 GT/s`，使用 `128b/130b` 编码。x8 链路的理论有效带宽约为：

```text
8 GT/s × 8 × 128/130
≈ 63.0 Gbit/s
≈ 7.88 GB/s
```

实测结果为：

```text
57~60 Gbit/s
≈ 7.1~7.5 GB/s
```

这已经非常接近 Gen3 x8 的有效上限。

因此，`ibstat` 或其他工具显示 IB 链路为 `200G`，并不意味着主机内存到另一端主机内存的端到端吞吐也一定是 `200G`。完整数据路径是：

```text
Memory
  |
PCIe
  |
HCA
  |
IB Fabric
  |
HCA
  |
PCIe
  |
Memory
```

HCA 到 IB Fabric 的链路速率和 HCA 到主机内存的 PCIe 带宽是两段不同的约束，端到端吞吐会受较慢的一段限制。

### 常见 PCIe 配置对 200G IB 的影响

| PCIe | 理论单向带宽 | 对 200G IB 的影响 |
| --- | ---: | --- |
| Gen3 x8 | ~63 Gbit/s | 明显不够 |
| Gen3 x16 | ~126 Gbit/s | 仍不足以跑满 200G |
| Gen4 x8 | ~126 Gbit/s | 仍不足以跑满 200G |
| Gen4 x16 | ~252 Gbit/s | 可以承载 200G |

如果目标是让 200G ConnectX-6 接近线速，`PCIe Gen4 x16` 是比较合理的配置。实际 RDMA 跑到 `180~195 Gbit/s`，已经属于接近 `200G` line rate 的状态。

## 固件与其他变量

测试时网卡固件为：

```text
20.39.3004
```

当时还有 `20.43.x` 版本可用。固件升级可能带来 bug fix、兼容性或性能变化，但在本次问题中，`PCIe Gen3 x8` 已经足以解释 `57~60G`，没有必要先把固件升级当成唯一变量。

在这个场景里，PCIe link speed 和 width 应该是首个检查项，NUMA locality 随后确认。虚拟化和测试工具相关配置留到这两项之后再看，firmware 则适合作为独立的 A/B 变量。

## 测试完成后恢复 VF

### 清理 network namespace

先把接口地址和状态清掉，再将 PF 移回 root namespace：

```bash
ip netns exec ns0 ip addr flush dev ib0
ip netns exec ns1 ip addr flush dev ib1

ip netns exec ns0 ip link set ib0 down
ip netns exec ns1 ip link set ib1 down

ip netns exec ns0 ip link set ib0 netns 1
ip netns exec ns1 ip link set ib1 netns 1

ip netns del ns0
ip netns del ns1
```

### 重新创建并绑定 VF

```bash
echo 1 > /sys/class/net/ib0/device/sriov_numvfs
echo 1 > /sys/class/net/ib1/device/sriov_numvfs
```

重新配置 VF GUID 和 policy：

```bash
echo <VF0_NODE_GUID> > /sys/class/infiniband/mlx5_0/device/sriov/0/node
echo <VF0_PORT_GUID> > /sys/class/infiniband/mlx5_0/device/sriov/0/port
echo Follow > /sys/class/infiniband/mlx5_0/device/sriov/0/policy

echo <VF1_NODE_GUID> > /sys/class/infiniband/mlx5_3/device/sriov/0/node
echo <VF1_PORT_GUID> > /sys/class/infiniband/mlx5_3/device/sriov/0/port
echo Follow > /sys/class/infiniband/mlx5_3/device/sriov/0/policy
```

重新解绑并让 `vfio-pci` 接管：

```bash
echo 0000:98:00.1 > /sys/bus/pci/drivers/mlx5_core/unbind
echo 0000:b2:00.1 > /sys/bus/pci/drivers/mlx5_core/unbind

echo 15b3 101c > /sys/bus/pci/drivers/vfio-pci/new_id 2>/dev/null || true
```

最后确认：

```bash
lspci -nnk -s 98:00.1
lspci -nnk -s b2:00.1
```

确保输出包含：

```text
Kernel driver in use: vfio-pci
```

## 结论

这次排查中，以下现象最容易造成误判：

1. `ibstat` 显示 `Rate: 200`，只说明 IB Fabric 链路能力，不代表应用端到端吞吐一定是 `200G`。
2. `/sys/class/net/ib*/mode` 显示 `datagram`，描述的是 IPoIB netdev 模式，不代表 RDMA 没有启用。
3. `ib_write_bw -R` 不是「开启 RDMA」；`ib_write_bw` 本身就是 RDMA verbs 测试，`-R` 只改变连接建立方式。
4. 双向 `-b` 的 aggregate 不能与单向带宽直接比较。
5. VM 中只有 `57G` 时，不能直接归因于 SR-IOV 或 VFIO；PF 对 PF 测试可以先把虚拟化层排除。
6. 同一台宿主机用两个本地 IP 互测，可能被 `local route` 短路；使用 network namespace 才能强制经过 HCA 和 IB 交换机。
7. 对 200G HCA，PCIe link speed 和 width 必须一起看；`Gen3 x8` 本身只能提供约 `63 Gbit/s` 的理论单向有效带宽。

决定性的对照数据是：

```text
VM VF RDMA:   ~57~59 Gbit/s
Host PF RDMA: ~60.26 Gbit/s
PCIe Gen3 x8: ~63 Gbit/s theoretical
```

三组数字基本对上后，问题就从「RDMA 为什么这么慢」收敛成了一个明确的 PCIe 带宽问题。

