VFIO硬件依赖——IOMMU机制

  • VFIO是Linux下设备透传的主流解决方案,它的硬件基础是IOMMU。IOMMU提供了DMA重映射和中断重映射。本文主要介绍IOMMU的基本概念。

IOMMU动机

  • Intel提出IOMMU的主要目的,是解决IO虚拟化遇到的问题,因此首先介绍VMM实现IO虚拟化的主要几种方式,以KVM为例:
  1. 软件模拟方式。软件模拟方式实现IO虚拟化是常见的实现方案,比如qemu模拟pci设备,只要qemu做到模拟的pci设备和真实物理设备行为相同就可以。当guest初始化pci设备,读写设备配置空间,使用设备功能,qemu都能正确的反馈,就算做到了软件层面的设备模拟。软件模拟方式的优点是兼容性好,同时guest软件不需要任何适配,可移植性好。缺点是性能问题,因为都是通过软件模拟的。
  2. 新软件接口方式。这种方式和纯软件模拟类似,但它不会完全模拟设备,而是向guest提供一套高效的访问主机IO设备的新接口。常见的就是virito设备,guest想要访问qemu提供的virtio设备时需要按照virtio提供的方式来做,guest OS需要安装virtio驱动。软件接口的方式优点是性能好。但存在可移植性和兼容性问题。
  3. 分配方式。分配方式就是直接把主机上的设备分配给guest用。我们知道Linux系统下用户态进程不能直接访问设备,需要加载内核态驱动才能完成,qemu在主机看来也是一个用户态进程,如果把设备直接给qemu直接用,需要解决一个问题,就是qemu在访问设备的时候不能访问其它内核资源,否则会有安全问题。如果做到了这一点,就可以使用分配主机设备的方式实现IO虚拟化。分配方式就是我们常说的设备透传。比如以vfio-pci方式透传GPU设备,就是分配方式的IO虚拟化。透传方式性能好,设备兼容性和guest 软件移植性也好,但要求设备硬件具有资源隔离的能力。
  4. 分时复用方式。分时复用方式是分配方式的扩展,这种方式下要求设备本身有能力支持多个接口并行接收用户的请求,并且互不影响。分时复用方式兼具分配方式的优点,但对设备的硬件要求更高,它不仅需要资源隔离能力,还需要硬件具有并行处理请求的能力。virtio-gpu就是qemu利用GPU的并行处理能力,实现GPU在主机上的分时复用。然后将模拟的设备提供guest。
  • 分析了IO虚拟化的主要方案后,Intel提取了这些方案的主要需求,提供了硬件机制用来辅助IO虚拟化,这就是VT-d。它主要解决以下问题:
  1. IO设备分配。Intel提供将IO设备灵活分配给虚机的机制,同时保证虚机访问IO设备的安全性。
  2. 资源隔离。透传方式使用IO设备时,如果虚机使用设备DMA,那么它会访问到主机上的内存,这会产生安全风险,因此Intel要保证DMA访问的安全性,具体的硬件解决方案就是DMA重映射。和DMA一样,IO设备透传时,产生的中断也会投递到主机的CPU核,将器重映射到虚机,也是Intel硬件要解决的问题。
  3. 中断post。Intel支持中断以post的方式注入中断,这是一种高效的中断模拟方式,guest不需要退出。在IO虚拟化场景下,Intel需要解决透传设备怎么post中断到虚机的cpu问题。
  • 以上的种种,就是Intel的IO虚拟化方案要解决的问题,而IOMMU可以认为是Intel为解决这些问题提供的核心机制。本文主要参考intel的IO虚拟化手册

DMA Remapping

  • DMA重映射,它的意思是,当IO设备发起DMA访问请求时,IOMMU会将请求的地址进行一次转换,通过这样的方式,将guest的物理地址映射到host上真实的物理地址上去。DMA重映射的核心就是地址转换。
  • 提到地址转换,我们第一反应就是MMU,它是Intel的硬件内存管理单元,接收虚拟地址作为输入,根据其页表转换成物理地址,读写物理内存上的内容。总结,MMU的使用者是CPU,它完成的工作是HVA到HPA之前的转换。IOMMU的功能与MMU类似,不同的是它的使用这者不是CPU,而是IO设备,IOMMU因此得名,它完成的工作也是虚拟地址到HPA之前的转换,这里的虚拟地址可以包含很多种。在虚拟化的场景下,使用vfi-pci透传的方式,它的虚拟地址是GPA。
  • IOMMU要实现地址转换功能,需要满足以下条件:
  1. 首先,如果IOMMU需要地址转换,那么和MMU一样,需要提供页表机制。当IOMMU接受到IO设备的DMA请求时,首先将请求读写的虚拟内存地址IOVA,通过页表转换成物理地址IOPA,然后读写物理内存的内容。
  2. 其次,针对IO虚拟化的场景,需要把IO设备分配给虚机,而虚机在主机上只有一部分内存可以访问,因此IO设备的内存访问也只能局限于这部分内存。IOMMU要提供一种机制,来保证IO设备只能访问它所属的那个虚机拥有的物理空间。
  • 针对第一种情况,IOMMU提供了次级页表(Second-Level Page Table Structures)来存储一个IO设备所在虚机的IO内存映射。次级页表描述了一个虚机包含的IO设备可访问的物理页。如果两个IO设备都使用一个次级页表,那么他们必然属于同一个虚机。
  • 针对第二种情况,IOMMU要求发送内存访问请求的IO设备表明自己的source-id,对于pci设备source-id就是BDF,IOMMU用这个东西作为索引,就能查找到IO设备所在虚机拥有的页表结构。因此还需要以BDF为关键字的表,用来存放页表的物理地址。IOMMU将这个页表分为两级,分别是Root Table和Context Table。
  • 总结一下,IOMMU为实现DMA Remapping实现了两类表:Root Table/Context Table和Second-Level Page Table。同时要求访问物理内存的IO设备表明自己的source-id BDF。下面分别介绍:

Root Table/Context Table

在这里插入图片描述

  • Root Table有256个条目,可以索引所有的0-255总线号的pci设备,IOMMU从source-id中取出bus号,根据总线号在Root Table中索引对应的条目。找到Context Table。
  • Context Table以Device/Fuction号为索引,device范围0-31,fuction范围0-7,因此Context Table表的总条目数为32*8 = 256。IOMMU从source-id中取出device和fuction号,据此查找到IO设备所在虚机的IO地址空间页表结构,Second-Level Page Table。
  • 从上面可以看出,只要在IO设备发出地址空间访问时,根据BDF号查找到的Second-Level Page Table相同,就可以实现多个IO设备分配给同一个虚机,因此他们访问的物理地址空间都是一样的,同属于一个虚拟机。从上图中也能看到,Second-Level Page Table Structures在逻辑上关联了一个虚机的IO物理地址空间。

Second-Level Page Structure

  • Second-Level Page Structure和MMU的页表结构非常类似,下图是一个物理地址为48位,页表大小为4K的IOMMU 4级页表结构。当IOMMU进行地址转换时,48位物理地址的高9位物理地址作为PML4 Table的索引,确认PDPT的物理地址,找到PDPT之后,取48位物理地址的次9位物理地址作为PDPT的索引,确认PD的物理地址,以此类推,最终找到IO设备要访问的内存页,再用低12位作为偏移访问物理页的具体内容。
    在这里插入图片描述

IOMMU Group

  • 从IOMMU地址转换的机制可以知道,IOMMU根据source-id从Root Table/Context Table中查找页表,当两个IO设备都透传给同一个虚机时,IOMMU根据这两个IO设备的source-id查找到的页表必然是同一个。
  • 有这么一种情况,如下图所示。两个不同的IO设备在DMA请求时有相同的source-id。PCIe-PCI桥下的设备就属于这种情况。在发送DMA请求时,PCIe-PCI桥统一为它下面的设备生成Bus为总线号,device和function为0的source-id。这种情况下所有PCIe-PCI下的设备,在DMA请求的时候IOMMU查找到的Context Table Entry和页表都是相同的,因此它们只能被透传给同一个虚拟机,没有办法透传给不同虚机,因此做不到DMA访问的隔离。
    在这里插入图片描述
  • 因为以上的硬件问题,Intel的IOMMU没有办法做到device级别的DMA隔离。因此IOMMU定义了一个Group的概念,它表示IOMMU能够进行DMA隔离的最小单位,一个Group可能包含一个或者多个device。这和设备的硬件拓扑相关。当我们要透传设备给虚机使用时,只能以IOMMU Group为单位进行透传,保证DMA的隔离。

DMA Map

  • 当实现PCI设备的透传时,它最核心的工作就是限制PCI设备可访问的内存区域,对于具有DMA能力的PCI设备,在通用场景下它是可以访问主机内存的,比如GPU设备,它的显存就是一块主机上的内存,它对显存可以进行DMA操作。对于透传的GPU设备,当它需要进行DMA操作时,我们不允许它直接访问主机上的内存,原因就是上面提到的安全隐患。怎么办呢?方法就是为透传的GPU设备提前分配好它需要进行DMA操作的内存,当它进行DMA操作时,告诉它只能访问这块内存。整个流程步骤如下:
  1. 当Qemu要将PCI设备透传给虚机使用时,如果这个PCI设备有DMA的能力,Qemu需要提前分配一块内存。得到这块内存的起始虚拟地址(HVA)和大小。
  2. Qemu分配这块内存的目的是给PCI设备用,它想要达到的目的是,当虚机访问PCI设备的DMA内存区域时,它使用GPA最终经IOMMU页表转换,访问到的是Qemu提前分配好的这块内存,因此IOMMU的页表需要建立这样的映射关系:GPA通过IOMMU转换指向的内存页,和Qemu分配的内存指向的内存页是同一个。即GPA通过IOMMU转换得到的HPA和HVA通过MMU转换得到的HPA相同。
  3. 为了实现2中的功能,KVM模块提供了一个IOCTL命令字VFIO_IOMMU_MAP_DMA,它的功能是,根据IOCTL传入的一段已分配好的内存的HVA和一个GPA,KVM创建一条IOMMU页表项。最终的效果它是,输入GPA查询页表,得到的页表项存放的物理地址是HVA指向的内存页的物理地址。这样,虚机中PCI设备DMA访问的内存都是Qemu提前分配好的。
  • 6
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

享乐主

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值