PCIe SR-IOV 虚拟化详解
单根I/O虚拟化技术
什么是SR-IOV?
SR-IOV(Single Root I/O Virtualization,单根I/O虚拟化)是PCIe的一项标准扩展,允许单个物理PCIe设备在硬件层面虚拟出多个虚拟设备,每个虚拟设备可以直接分配给不同的虚拟机使用。
为什么需要SR-IOV?
- 性能提升:虚拟机直接访问硬件,绕过Hypervisor软件层
- 降低延迟:减少I/O虚拟化带来的额外开销
- 提高吞吐量:接近物理机的I/O性能
- 节省资源:多个虚拟机共享一个物理设备
I/O虚拟化技术对比
| 技术 | 实现方式 | 性能 | 隔离性 | 适用场景 |
|---|---|---|---|---|
| 软件模拟 | Hypervisor模拟设备 | 低 | 好 | 兼容性测试 |
| Para-virtualization | 前端/后端驱动 | 中 | 好 | 通用虚拟化 |
| 设备直通(PCIe Passthrough) | 整个设备分配给VM | 高 | 好 | 单VM独占设备 |
| SR-IOV | 硬件虚拟化 | 高 | 好 | 多VM共享设备 |
PF和VF详解
SR-IOV定义了两种Function类型:Physical Function(PF)和Virtual Function(VF)。
Physical Function (PF)
物理功能PF是完整的PCIe Function,拥有完整的配置空间和管理能力。
主要能力
- 完整的配置空间访问
- VF的创建和配置
- 设备全局资源管理
- 错误处理和恢复
- 支持所有PCIe能力
典型用途
- Hypervisor/Host管理
- VF生命周期管理
- 设备监控和诊断
Virtual Function (VF)
虚拟功能VF是轻量级的PCIe Function,由PF创建,用于分配给虚拟机。
主要特点
- 轻量级配置空间
- 独立的I/O资源
- 独立的MSI/MSI-X中断
- 共享物理设备资源
- 无全局管理能力
典型用途
- 分配给虚拟机
- 容器I/O加速
- 多租户隔离
PF与VF对比
| 特性 | PF | VF |
|---|---|---|
| 配置空间大小 | 完整(256B/4KB) | 精简(部分寄存器) |
| BAR数量 | 6个 | 通常2-4个 |
| VF创建能力 | 支持 | 不支持 |
| 中断数量 | 完整 | 受限 |
| 错误处理 | 完整 | 仅本地错误 |
| DMA能力 | 支持 | 支持(IOMMU隔离) |
SR-IOV系统架构
SR-IOV拓扑结构
Host/Hypervisor
PF Driver
SR-IOV Device
PF
VF 1
VF 2
VF 3
VF n
VM 1
VF 1
VF 1
VM 2
VF 2
VF 2
VM 3
VF 3
VF 3
SR-IOV配置空间
SR-IOV扩展了PCIe配置空间,增加了SR-IOV Capability结构。
SR-IOV Capability寄存器
| 寄存器 | 偏移 | 功能 |
|---|---|---|
| (扩展能力头) | 0x00-0x03 | Ext Cap ID(0x0010)/ Next Cap / 版本 |
| SR-IOV Capabilities | 0x04 | SR-IOV能力标志(ARI层次、VF迁移、页大小粒度等) |
| SR-IOV Control | 0x08 | VF使能/禁用、Memory Space Enable、ARI Capable Hierarchy |
| SR-IOV Status | 0x0A | VF迁移状态 |
| InitialVFs | 0x0C | 设备支持的VF数量(复位后可立即使能) |
| TotalVFs | 0x0E | 可配置的VF总数 |
| NumVFs | 0x10 | 当前配置的VF数量 |
| Function Dependency Link | 0x12 | Function依赖关系 |
| First VF Offset | 0x14 | 第一个VF的RID偏移(相对PF) |
| VF Stride | 0x16 | 相邻VF的RID间隔 |
| VF Device ID | 0x1A | VF的设备ID |
| Supported / System Page Sizes | 0x1C / 0x20 | VF BAR 映射的页大小(SR-IOV 1.1 起已废弃,恒 0) |
| VF BAR0-5 | 0x24-0x38 | VF的基地址寄存器(与PF BAR独立,所有VF共享一份布局) |
SR-IOV配置流程
VF创建流程
1
检测SR-IOV能力
读取SR-IOV Capability,获取支持的VF数量
2
配置VF数量
写入NumVFs寄存器,设置要创建的VF数量
3
使能VF
设置VF Enable位,硬件创建VF
4
配置VF资源
为每个VF分配内存、中断等资源
5
分配VF给VM
将VF直通给虚拟机使用
Linux SR-IOV配置示例
# 1. 查看设备是否支持SR-IOV
$ lspci -s 03:00.0 -vvv | grep -i sriov
Capabilities: [160] Single Root I/O Virtualization (SR-IOV)
IOVCap: Migration-, Interrupt Message Number: 000
IOVCtl: Enable- Migration- Interrupt- MSE- ARIHierarchy-
IOVSta: Migration-
Initial VFs: 64, Total VFs: 64, Number of VFs: 0
# 2. 查看当前VF数量
$ cat /sys/bus/pci/devices/0000:03:00.0/sriov_numvfs
0
# 3. 创建4个VF
$ echo 4 | sudo tee /sys/bus/pci/devices/0000:03:00.0/sriov_numvfs
# 4. 查看创建的VF
$ lspci | grep Virtual
03:00.1 Ethernet controller: Intel Corporation Virtual Function
03:00.2 Ethernet controller: Intel Corporation Virtual Function
03:00.3 Ethernet controller: Intel Corporation Virtual Function
03:00.4 Ethernet controller: Intel Corporation Virtual Function
# 5. 查看VF详细信息
$ lspci -s 03:00.1 -vvv
# 6. 删除VF(设置为0)
$ echo 0 | sudo tee /sys/bus/pci/devices/0000:03:00.0/sriov_numvfs
KVM虚拟机使用VF
# 1. 解绑VF from host driver
$ echo 0000:03:00.1 | sudo tee /sys/bus/pci/drivers/ixgbevf/unbind
# 2. 绑定VF to vfio-pci(用于直通)
$ echo vfio-pci | sudo tee /sys/bus/pci/devices/0000:03:00.1/driver_override
$ echo 0000:03:00.1 | sudo tee /sys/bus/pci/drivers/vfio-pci/bind
# 3. 查看VF的IOMMU组
$ readlink /sys/bus/pci/devices/0000:03:00.1/iommu_group
../../../../../../kernel/iommu_groups/16
# 4. 启动虚拟机时附加VF
$ qemu-system-x86_64 \
-device vfio-pci,host=03:00.1,id=net0 \
...
# 或使用libvirt
$ virsh attach-device vm1 vf.xml
ACS与SR-IOV的配合
ACS(Access Control Services)是SR-IOV安全隔离的重要保障,确保VF之间的隔离性。
为什么需要ACS?
安全风险
在没有ACS的情况下,恶意VM可能通过VF:
- 访问其他VF的内存空间
- 发起针对其他VM的DMA攻击
- 通过P2P事务绕过IOMMU
ACS保护机制
Source Validation
验证TLP的源地址,防止伪造源地址攻击
P2P Request Redirect
将P2P请求重定向到RC,由IOMMU进行地址转换
Translation Blocking
阻止未授权的地址转换请求
SR-IOV安全架构
IOMMU
ACS (Switch/RC)
VF 1
VF 2
VF 3
VM 1
VM 2
VM 3
Linux ACS配置
# 查看设备的ACS能力
$ lspci -vvv -s 00:01.0 | grep -A 10 "Access Control Services"
# 强制启用ACS(某些情况下需要)
$ echo 1 | sudo tee /sys/bus/pci/devices/0000:00:01.0/acs_enable
# 检查IOMMU是否启用
$ dmesg | grep -i iommu
[ 0.000000] Command line: ... intel_iommu=on iommu=pt ...
# 查看IOMMU组
$ find /sys/kernel/iommu_groups/ -type l
SR-IOV性能优化
中断优化
MSI-X优化
- 为每个VF分配足够的中断向量
- 使用多队列网卡(RSS/RPS)
- 中断亲和性绑定(IRQ affinity)
内存优化
- 使用大页内存(HugePages)
- NUMA亲和性配置
- 避免跨节点内存访问
CPU优化
- CPU pinning(vCPU绑定物理核)
- 隔离专用CPU核心
- 禁用超线程(如有必要)
性能测试
# 测试VF网络性能(使用iperf3) # Host端 $ iperf3 -s # VM端(通过VF) $ iperf3 -c-t 30 -P 4 # 对比:使用virtio-net $ iperf3 -c -t 30 -P 4 # 查看VF统计 $ ethtool -S eth0 # 查看中断分布 $ cat /proc/interrupts | grep eth0
VFIO 内部模型:设备是怎么"交出去"的
VF 直通命令(vfio-pci 绑定、QEMU -device vfio-pci)背后是一套清晰的内核对象模型。理解它,才能回答"为什么分不到设备"、"为什么 IOMMU 组不能拆"这类现场问题。
三层对象:Device → Group → Container
| 层 | 对象 | 职责 |
|---|---|---|
| Device | 经 ioctl 从 group fd 取得 device fd | 具体设备(如一个 VF)。用户态通过 VFIO_GROUP_GET_DEVICE_FD 打开,随后 VFIO_DEVICE_GET_INFO 获取设备类型与 region;每个 region 支持 read/write/mmap——PCI 配置空间就是一个 region,BAR 空间是 mmap 的 region |
| Group | /dev/vfio/$GROUP |
IOMMU 组:IOMMU 保护/映射的最小粒度。组内所有设备要么全部绑定到 VFIO、要么全不绑——因为组内设备之间的 DMA 互通无法被安全隔离,拆分等于给用户态留后门 |
| Container | /dev/vfio/vfio |
承载一个或多个 group 的地址空间(页表集合)。用户态打开 container 文件、VFIO_SET_IOMMU 选 IOMMU 后端,再 VFIO_IOMMU_MAP_DMA 把用户态内存映射成设备的 IOVA 视图——这就是直通场景"DMA 能落进客户机内存"的机制 |
/* VFIO 典型用户态序(摘自 Documentation/driver-api/vfio.rst 的骨架) */
container = open("/dev/vfio/vfio", O_RDWR);
ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU);
group = open("/dev/vfio/<group id>", O_RDWR);
ioctl(group, VFIO_GROUP_SET_CONTAINER, &container);
/* 把客户机内存段注册为设备可见的 IOVA */
struct vfio_iommu_type1_dma_map dma_map = {
.vaddr = (uintptr_t)guest_ram, .iova = 0x0,
.size = guest_ram_size, .flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE,
};
ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map);
/* 取设备 fd,逐一 mmap region(BAR 空间) */
device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, "0000:06:0d.0");
ioctl(device, VFIO_DEVICE_GET_INFO, &device_info);
for (i = 0; i < device_info.num_regions; i++) {
struct vfio_region_info reg = { .argsz = sizeof(reg), .index = i };
ioctl(device, VFIO_DEVICE_GET_REGION_INFO, ®);
/* reg.offset/size/flags:mmap 到用户态直接操作 BAR */
}
vfio-pci 与 DMA 重映射
- vfio-pci 作为 PCI 驱动的"接盘方":绑定设备后内核常规驱动不再 probe;VFIO 核心保存设备的 BAR、配置空间、中断(MSI/MSI-X)等资源清单。
- 中断注入:VFIO 的
VFIO_DEVICE_SET_IRQS+ eventfd 把 MSI/MSI-X 向量映射为文件描述符事件——QEMU 收到 eventfd 后向客户机注入虚拟中断,这是直通中断低开销的关键路径。 - IOMMU 组为何不能拆:同一 IOMMU 组内的设备共享 IOMMU 页表/上下文;若只放行其中一个设备给用户态,其余设备的 DMA 不受约束即可越权访问。内核因此规定:组内设备必须全部交给同一个 VFIO container(多功能设备/未开 ACS 的 Switch 下游设备常因此被"捆绑")。
- 新版 iommufd:
VFIO_SET_IOMMU/VFIO_IOMMU_MAP_DMA的 IOMMU 逻辑正在向/dev/iommu(iommufd)迁移——VFIO 只负责设备模型,页表由 iommufd 统一管理,用户态也可不用 VFIO 直接用 iommufd 做 DMA。
现场常用命令
# 查看设备的 IOMMU 组号与组内设备
$ ls /sys/bus/pci/devices/0000:01:00.0/iommu_group/devices/
# 查看系统全部 IOMMU 组
$ for g in /sys/kernel/iommu_groups/*/devices/*; do echo "$g"; done
# 将 VF 绑定到 vfio-pci(驱动_override)
$ echo 8086 154c > /sys/bus/pci/drivers/vfio-pci/new_id
# 或先解绑原驱动再覆盖
$ echo 0000:01:00.1 > /sys/bus/pci/drivers/<orig-driver>/unbind
$ echo vfio-pci > /sys/bus/pci/devices/0000:01:00.1/driver_override
$ echo 0000:01:00.1 > /sys/bus/pci/drivers_probe
SR-IOV应用场景
NFV/电信云
虚拟化网络功能,高性能数据面处理
- vRouter/vFirewall
- 虚拟负载均衡
- DPI/流量分析
云数据库
高性能存储访问,低延迟I/O
- NVMe SSD虚拟化
- 高并发数据库
- 内存数据库
公有云
多租户I/O隔离,性能保障
- 裸金属服务
- 高性能计算实例
- GPU虚拟化
边缘计算
资源受限环境下的高效虚拟化
- 边缘网关
- 工业控制
- 5G MEC
总结
SR-IOV是PCIe虚拟化的核心技术,通过硬件层面的I/O虚拟化,实现了接近物理机的I/O性能。结合ACS和IOMMU,SR-IOV提供了安全、高效的I/O虚拟化解决方案。
关键要点
- SR-IOV通过PF和VF实现硬件虚拟化
- PF负责管理,VF分配给虚拟机使用
- ACS确保VF之间的安全隔离
- IOMMU提供DMA重映射和保护
- SR-IOV适用于高性能I/O虚拟化场景