本文内容主要转载自:Shared Virtual Memory(SVM)介绍

1. Definition

Shared Virtual Addressing (SVA) allows the processor and device to use the same virtual addresses. SVA is what PCIe calls Shared Virtual Memory (SVM).

VT-d SVM: Extends root complex IOMMU to comprehend x86 page table formats

2. History

共享虚拟内存(SVM)技术最初是为了解决在GPU场景下,设备(GPU)和host(CPU)之间共享内存的。目的是在设备GPU和CPU之间可以直接传递指针(地址),为了让设备可以直接使用进程空间的地址,简化编程模型。我们知道通常host侧采用的地址是主机的虚拟地址(VA),而设备侧通常使用的是物理地址(PA)或IOVA。

如下图,传统内存访问的三个途径:

  1. CPU访存通过MMU(CPU的页表)将VA转为PA访问物理地址
  2. GPU访存通过GPU的页表访问物理地址
  3. PCIe设备直接使用物理地址PA访问

在引入了iommu(VT-d)后,如下图,PCIe设备也可以使用虚拟地址(IOVA)来访存了,也有设备自己对应的页表(iommu页表)完成IOVA到物理地址(PA)的映射。

这种情况下CPU进程和设备的内存通信一般采用如下流程:

  1. CPU进程分配一块内存,并采用系统调用syscall或ioctl请求内核准备接收操作
  2. 内核初始化设备的DMA操作,这里面有两种情况:一种是内核重新分配一块内核空间的内存,将其物理地址传递给设备进行DMA,另一种是如果应用程序将用户空间的内存pin住(这段内存的虚拟地址空间和物理地址空间不会发生变化)则可直接将用户空间的buffer地址传递给设备进行DMA
  3. 设备将数据DMA到内存,对应上面这里也有两种情况,如果是内核设置的内核buffer的地址,则设备会先将数据DMA到内核buffer,再由内核将数据由内核空间拷贝到用户空间的buffer(我们通常使用内核协议栈进行收发报文的应用程序就是这种),另一种如果用户空间直接将内存pin住,则设备直接将数据DMA到应用程序的buffer(我们采用DPDK收发报文就是这种)

3. 引入SVM后的变化

下面我们看引入SVM后的效果,最大的区别是设备访问地址在经过iommu的DMAR转换时会参考引用CPU的mmu页表,在地址缺页时同样会产生缺页中断。为什么要这样设计呢?因为要想设备直接使用进程空间的虚拟地址可能采用的有两种方法。一种是把整个进程地址空间全部pin住,但这点一般是不现实的,除非类似DPDK应用程序全部采用静态内存,否则如果进程动态分配一个内存,那么这个地址设备是不感知的。另一种方法就是采用动态映射,就像进程访问虚拟地址一样,mmu发现缺页就会动态映射,所以从设备发来的地址请求也会经过CPU缺页处理,并将映射关系同步到iommu的页表中。

有了以上的流程,CPU和设备的内存交互流程就变成了如下图所示。主要是第三步的变化,设备直接将数据DMA到进程空间的地址,并且不需要进程pin内存,而是通过page fault触发缺页处理进行映射。

4. 支持SVM的条件

那么支持SVM需要软硬件具备什么条件呢。首先是设备角度:

  1. 要支持PASID,因为一个设备会被多个进程访问,对应多个设备DMAR页表,需要通过PASID来区分采用哪个页表
  2. 支持dma page fault处理,当访问的虚拟地址引发缺页时能够等待或重试

从驱动角度来说,

  1. 操作设备的API需要通过PASID来区分不同进程


参考资料:

  1. Shared Virtual Addressing (SVA) with ENQCMD
  2. SVM on Intel Graphics
  3. Shared Virtual Addressing in KVM kvm forum 2018
  4. Shared Virtual Addressing in KVM