IO虚拟化相关原理

1.0 直通对于虚拟io的支持

整体架构图:
在这里插入图片描述### 直通技术(Passthrough)对虚拟化 I/O 的支持原理

在虚拟化场景中,设备直通(Device Passthrough) 是一种将物理设备直接分配给虚拟机(VM)的技术,使得虚拟机能够绕过 Hypervisor 的模拟层,直接控制硬件设备。这种技术显著提升了 I/O 性能(接近原生速度),常用于 GPU、NVMe 存储、高性能网卡等场景。其核心原理可分解为以下几个关键点:

1.1. 硬件支持:IOMMU 与中断隔离

(1) IOMMU(I/O 内存管理单元)
  • 作用:IOMMU 提供 DMA 重映射地址隔离,确保设备只能访问虚拟机显式申请的内存。
    • 地址翻译:设备发起的 DMA 请求中的 I/O 虚拟地址(IOVA)会被 IOMMU 转换为物理地址(PA)。
    • 权限控制:限制设备对内存的读写权限(如只读、不可执行)。
  • 硬件实现
    • Intel VT-d:通过 Root TableContext Table 管理设备到域(Domain)的映射。
    • AMD-Vi:使用 Device TableIO Page Table 实现类似功能。
(2) 中断隔离
  • 中断重映射(Interrupt Remapping)
    • 设备的中断请求(MSI/MSI-X)会被 IOMMU 重定向到虚拟机所属的 CPU 核心。
    • 防止设备直接向宿主机发送中断,确保中断隔离。
  • 虚拟中断注入:Hypervisor(如 KVM)将设备中断转换为虚拟中断(如 KVM 的 IRQFD 机制),直接注入虚拟机。

1.2. 软件框架:VFIO 与用户态驱动

(1) VFIO(Virtual Function I/O)
  • 功能:Linux 内核提供的 安全设备直通框架,通过以下机制支持直通:
    • 设备绑定:从宿主机解绑设备驱动,绑定到 vfio-pci 驱动。
    • DMA 映射:通过 vfio_dma_map 等接口管理 IOVA 到 PA 的映射(见前一回答)。
    • 中断转发:将设备中断路由到用户态(如 QEMU),再注入虚拟机。
  • 安全性
    • 用户态程序(如 QEMU)需显式申请 DMA 内存区域。
    • IOMMU 强制隔离,阻止设备越界访问。
(2) 用户态驱动协作
  • QEMU/KVM 的角色
    • 通过 VFIO 容器(Container) 管理设备的 DMA 空间和中断。
    • 将虚拟机的内存映射到设备的 IOVA 空间(通过 VFIO_IOMMU_MAP_DMA ioctl)。
  • 设备配置空间模拟
    • 对设备的 PCI 配置空间访问由 QEMU 拦截,部分字段(如 BAR)需模拟以适配虚拟机。

1.3. 直通工作流程(以 PCI 设备为例)

  1. 设备初始化

    • 宿主机加载 vfio-pci 驱动,解绑原生驱动(如 igb)。
    • 设备被标记为 vfio 管理模式,DMA 和中断由 VFIO 接管。
  2. 虚拟机启动

    • QEMU 通过 -device vfio-pci 参数将设备分配给虚拟机。
    • QEMU 调用 VFIO_GROUP_GET_DEVICE_FD 获取设备控制句柄。
  3. 内存映射

    • 虚拟机申请内存(如 mmap),QEMU 通过 vfio_dma_map 将其锁定并映射到设备的 IOVA 空间。
    • IOMMU 更新页表,建立 IOVA→PA 的转换关系。
  4. 中断处理

    • 设备触发中断 → IOMMU 重映射中断目标 CPU → Hypervisor 注入虚拟中断到虚拟机。
    • 虚拟机的中断处理程序直接响应设备中断。
  5. DMA 操作

    • 设备通过 DMA 直接读写虚拟机的内存(无需 Hypervisor 参与)。
    • IOMMU 确保 DMA 地址在映射范围内,且权限合法。

** 1.4. 性能优化技术**

(1) 减少 VM-Exit
  • 避免配置空间模拟:对性能关键路径(如网卡的数据面),透传设备的 PCI 配置空间,减少退出到 Hypervisor 的开销。
  • MSI-X 直通:允许设备中断直接路由到虚拟机,无需 Hypervisor 转发。
(2) 大页与 IOMMU 映射优化
  • 大页支持:使用 2MB/1GB 大页减少 IOMMU 页表项数量,降低 TLB Miss 概率。
  • 预映射连续内存:虚拟机申请物理连续的内存区域(如 DPDK 大页内存),减少 IOMMU 映射的碎片化。
(3) SR-IOV 硬件虚拟化
  • 物理功能(PF)与虚拟功能(VF)
    • PF 由宿主机管理,VF 直通给虚拟机。
    • 每个 VF 有独立配置空间、队列和中断,实现硬件级隔离。
  • 优势:绕过软件虚拟化层,性能接近物理设备。

** 1.5. 安全与隔离机制**

  • IOMMU 域隔离:每个虚拟机拥有独立的 IOMMU 域,设备只能访问所属域的内存。
  • DMA 攻击防护:恶意设备无法绕过 IOMMU 访问其他虚拟机或宿主机的内存。
  • 中断过滤:仅允许设备发送已注册的中断类型和向量。

** 1.6. 典型应用场景**

  1. GPU 直通:虚拟机直接使用物理 GPU 进行图形渲染或计算(如 CUDA)。
  2. 低延迟网络:SR-IOV 网卡直通(如 Mellanox ConnectX),用于 HFT 或 NFV。
  3. 高性能存储:NVMe SSD 直通,避免虚拟磁盘层的性能损耗。

** 1.7与传统 PCI Passthrough 的对比**

特性VFIO-PCI传统 PCI Passthrough(如 pci-stub)
安全性依赖 IOMMU,隔离 DMA无 IOMMU 保护,DMA 不安全
性能接近原生原生
多设备支持支持设备组(Device Group)单设备直通
中断处理支持直接注入虚拟机依赖宿主机转发
适用场景生产环境测试环境(非安全场景)

7.0 Root Table 和 Context Table 管理设备到域(Domain)的映射过程

2 通过 Root Table 和 Context Table 管理设备到域的映射(Intel VT-d 实现)

Intel VT-d 技术通过 Root TableContext Table 实现设备到虚拟机域(Domain)的映射,其核心是通过硬件表结构将 PCI 设备与内存隔离域(IOMMU Domain)关联。以下是详细流程和伪代码演示。

** 2.1. 硬件表结构定义**

(1) Root Table
  • 作用:每个 PCI Segment(总线组)对应一个 Root Table,用于索引设备所属的 Context Table。
  • 结构
    • Root Table 是一个数组,每个条目(Root Entry)对应一个 PCI Bus 编号。
    • 每个 Root Entry 包含 Context Table 的基地址(Context Table Pointer)和有效位(Present)。
  • 格式(简化):
struct root_entry {
      uint64_t context_table_base : 40; // Context Table 物理基地址
      uint64_t reserved : 20;
      uint64_t present : 1;            // 条目是否有效
  };
(2) Context Table
  • 作用:每个 Context Table 条目(Context Entry)对应一个 PCI 设备(由 Bus/Device/Function 标识),定义设备所属的域(Domain)和地址转换规则。
  • 结构
    • 每个 Context Entry 包含域的地址空间根指针(ASR,即 IOMMU 页表基地址)、权限控制位等。
  • 格式(简化):
    struct context_entry {
    uint64_t asr : 40; // 地址空间根指针(IOMMU 页表基地址)
    uint64_t reserved : 12;
    uint64_t translation_type : 2; // 地址转换类型(如 Passthrough、Nested)
    uint64_t present : 1; // 条目是否有效
    };

** 2.2. 映射流程(以 PCI 设备直通为例)**

步骤 1:初始化 Root Table
  1. 操作系统为每个 PCI Segment 分配 Root Table 的物理内存。
  2. 遍历所有 PCI Bus,填充 Root Table 条目:
    • 设置 context_table_base 指向对应的 Context Table。
    • 设置 present 位为有效。
步骤 2:创建 Context Table
  1. 当设备需要直通给虚拟机时,分配一个 Context Table。
  2. 根据设备的 BDF(Bus/Device/Function)计算在 Context Table 中的索引:
    • 索引公式index = (Device Number << 3) | Function Number
  3. 填充 Context Entry:
    • asr:设置为虚拟机域的 IOMMU 页表基地址。
    • translation_type:设置为直通模式(如 TT_PASSTHROUGH)。
    • present:置 1。
步骤 3:绑定设备到 Domain
  1. 虚拟机启动时,Hypervisor(如 KVM)为虚拟机分配一个 IOMMU Domain。
  2. 通过 Intel VT-d 寄存器(如 DMAR_RTADDR_REG)设置 Root Table 的物理地址。
  3. 设备发起 DMA 请求时,IOMMU 根据 Root Table → Context Table → Domain 的路径完成地址转换。

在这里插入图片描述

** 2.3. 伪代码示例**

以下是概念性伪代码,展示 Root Table 和 Context Table 的初始化与绑定逻辑(实际代码在 Linux 内核的 drivers/iommu/intel/iommu.c 中):

(1) Root Table 初始化
// 分配 Root Table 物理内存
struct root_entry *root_table = alloc_phys_page();

// 遍历所有 PCI Bus,初始化 Root Table
for (int bus = 0; bus < MAX_BUS; bus++) {
    struct context_entry *ctx_table = alloc_phys_page(); // 分配 Context Table
    root_table[bus].context_table_base = virt_to_phys(ctx_table);
    root_table[bus].present = 1;
}

// 将 Root Table 基地址写入 VT-d 寄存器
writeq(virt_to_phys(root_table), DMAR_RTADDR_REG);
(2) 设备绑定到 Domain
// 获取设备的 BDF(Bus 1, Device 2, Function 0)
uint16_t bus = 1, dev = 2, func = 0;

// 计算 Context Table 索引
int ctx_index = (dev << 3) | func;

// 获取对应的 Context Table
struct context_entry *ctx_table = root_table[bus].context_table_base;

// 填充 Context Entry
ctx_table[ctx_index].asr = domain->iommu_pgtbl_base; // 虚拟机域页表基地址
ctx_table[ctx_index].translation_type = TT_PASSTHROUGH;
ctx_table[ctx_index].present = 1;

// 刷新 IOMMU TLB
iommu_flush_cache();

** 2.4. 地址转换流程**

  1. 设备发起 DMA 请求
    • PCI 设备生成 DMA 请求,携带 I/O 虚拟地址(IOVA)。
  2. Root Table 查找
    • IOMMU 根据 PCI Bus 号索引 Root Table,获取 Context Table 基地址。
  3. Context Table 查找
    • 根据设备 BDF 计算索引,找到对应的 Context Entry。
  4. 地址转换
    • 使用 Context Entry 中的 asr(IOMMU 页表基地址)和 IOVA,查询多级页表,得到物理地址(PA)。
  5. 权限检查
    • 检查 Context Entry 中的权限位(如读/写),若非法则触发错误。

** 2.5. 实际代码参考(Linux 内核)**

在 Linux 内核中,Intel IOMMU 的实现位于 drivers/iommu/intel/iommu.c

  • Root Table 初始化intel_iommu_init()iommu_init_domains()
  • Context Entry 填充domain_context_mapping()context_set_address_root()
  • 地址转换__iommu_dma_map()intel_map_page()
关键代码片段:
// 绑定设备到 Domain(drivers/iommu/intel/iommu.c)
static int domain_context_mapping(struct dmar_domain *domain, struct device *dev)
{
    struct context_entry *context;
    unsigned long flags;
    int ret;

    // 获取设备的 BDF
    bus = dev->bus->number;
    devfn = dev->devfn;

    // 获取 Context Table
    context = get_context_addr(dmar_domain, bus, devfn);

    // 设置 Context Entry
    context_set_address_root(context, domain->pgd);
    context_set_translation_type(context, PASSTHROUGH);
    context_set_present(context);

    // 刷新缓存
    iommu_flush_context(domain->iommu, domain->id, bus, devfn);
    return 0;
}

** 2.6. 总结**

通过 Root Table → Context Table → Domain 的层级映射,Intel VT-d 实现了:

  1. 设备隔离:每个设备绑定到独立的 Domain,限制其 DMA 范围。
  2. 地址转换:IOVA 到 PA 的转换由硬件自动完成,性能接近原生。
  3. 权限控制:通过 Context Entry 中的权限位限制设备的读写操作。

此机制是虚拟机设备直通(如 VFIO)的硬件基础,确保了安全性与高性能的平衡。

3.0 vfio_dma_map分析

3.1. VFIO 框架背景

VFIO(Virtual Function I/O)是 Linux 内核提供的 用户态设备直通框架,允许用户态程序(如 QEMU)直接控制 PCI/PCIe 设备,同时通过 IOMMU(如 Intel VT-d、AMD-Vi)保证安全性。其核心功能包括:

  • DMA 重映射:将设备 DMA 的 I/O 虚拟地址(IOVA)转换为物理地址(PA)。
  • 中断隔离:防止设备直接向主机发起中断。
  • 内存保护:确保设备只能访问用户态显式映射的内存区域。

vfio_dma_map 是 VFIO 实现 DMA 地址映射的核心接口。


3.2. vfio_dma_map 函数逻辑解析

函数原型
int vfio_dma_map(struct vfio_iommu *iommu, dma_addr_t iova, 
                unsigned long vaddr, size_t size, int prot);
  • 参数解析
    • iommu:指向 VFIO IOMMU 实例的指针,管理 DMA 地址空间。
    • iova:设备视角的 I/O 虚拟地址(起始地址)。
    • vaddr:用户态内存的虚拟地址(需已通过 mmap 映射)。
    • size:映射的内存区域大小。
    • prot:保护标志(如 IOMMU_READIOMMU_WRITE)。
核心逻辑流程
  1. 参数合法性检查

    • 验证 iovasize 是否对齐到 IOMMU 页大小(例如 4KB)。
    • 检查 vaddr 对应的用户内存是否已被锁定(通过 mlock 或 VFIO 自动锁定机制),防止换出。
  2. 内存锁定(Pin)

    • 调用 vfio_pin_pages 锁定用户内存的物理页,确保 DMA 期间物理地址不变。
    • 若内存为匿名映射或文件映射,可能触发缺页异常并分配物理页。
  3. IOMMU 页表更新

    • 通过 IOMMU 驱动(如 intel_iommu_map)建立 iova 到物理地址的映射。
    • 若支持嵌套页表(如 SVM),可能更新 PASID 表。
  4. 反向映射维护

    • iova 映射关系记录到 VFIO 的 dma_list 红黑树中,用于快速查找和冲突检测。
  5. TLB 刷新

    • 调用 iommu_flush_iotlb 刷新设备侧的 TLB 缓存,确保新映射立即生效。

3.3. 关键设计原理

(1) IOVA 隔离性
  • IOVA 空间分区:每个 VFIO 容器(Container)拥有独立的 IOVA 空间,避免不同虚拟机或用户进程间的 DMA 地址冲突。
  • 红黑树冲突检测:在 dma_list 中检查新映射的 iova 范围是否与已有映射重叠,确保安全性。
(2) 物理内存锁定
  • 防止换页:DMA 操作是异步的,若内存被换出会导致设备写入错误地址。vfio_pin_pages 通过增加页的引用计数锁定内存。
  • 大页优化:自动合并连续物理页,尝试使用 2MB/1GB 大页减少 IOMMU 页表项数量。
(3) IOMMU 交互
  • 地址转换粒度:IOMMU 页表通常以 4KB 为粒度,但支持大页映射以提高性能。
  • 安全扩展
    • 支持 IOMMU_CACHE 控制缓存一致性(如 WC/WT/UC)。
    • 启用 IOMMU_PRIV 标志时,仅允许特权 DMA 请求(需硬件支持)。
(4) 性能权衡
  • 延迟敏感vfio_dma_map 的调用频率直接影响虚拟机启动速度和设备性能,需尽量减少小规模映射。
  • 批量映射优化:用户态应尽量合并多次 VFIO_IOMMU_MAP_DMA ioctl 调用,减少上下文切换开销。

3.4. 典型代码路径

Intel IOMMU 为例,调用链如下:

vfio_dma_map()
  -> vfio_pin_pages()              // 锁定物理内存
  -> iommu_map()                   // 调用 IOMMU 驱动
     -> intel_iommu_map()          // Intel IOMMU 实现
        -> domain_pfn_mapping()    // 更新页表
  -> vfio_link_dma()               // 记录到 dma_list
  -> iommu_flush_iotlb()           // 刷新 TLB

3.5. 安全与隔离

  • DMA 攻击防护:通过 IOMMU 强制设备仅能访问显式映射的 IOVA 区域,阻止恶意设备读取任意主机内存。
  • 权限控制prot 参数限制设备对内存的读写权限,例如只允许 DMA 读不可写。
  • 地址空间隔离:不同 VFIO 容器的 IOVA 空间完全隔离,避免跨虚拟机 DMA 干扰。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值