1. IOMMU主要功能包括DMA Remapping和Interrupt Remapping。
    DMA Remapping:
        设备驱动通过DMA描述符告诉设备DMA控制器,本次DMA操作的存储器地址。
        在宿主机上,DMA描述符上存储的是物理地址,DMA可正常进行。
        在虚拟机上,设备通过VT-d等技术被直接分配给虚拟机后,DMA描述符上存储的是GPA(即HVA);而RC上映射的是HPA,故DMA不能正常进行;此时需要iommu将TLP中的GPA转换为HPA,然后存储器才能接收到数据。
            疑问:设备 -> iommu -> rc -> 存储器 or 设备 -> rc -> iommu -> 存储器?答:倾向于前一种。

2. IOMMU作用:
    1)屏蔽物理地址,起到保护作用。一是用户态驱动;二是设备透传给虚拟机。
    2)将连续的GPA映射为不连续的HPA。

3. IOMMU工作原理:
        IOMMU与MMU类似,也是利用页表完成GPA到HPA的映射。
        如何找到页表?答:
            设备发起DMA请求的TLP包中包含有BDF(Bus, Device, Function),IOMMU根据基址寄存器RTADDR_REG和BDF找到页表首地址。
            属于同一虚拟机的不同设备须使用同一份页表。每个虚拟机(或者说每个IOMMU group)对应一个页表?答:是。
            由PCIe switch扩展出的PCI桥及桥下设备,发起DMA请求时,均使用PCIe switch的BDF,导致对应同一份IOMMU页表;因此这些设备须分配给同一个虚拟机。
        
4. VFIO概念
    VFIO是内核针对IOMMU提供的软件框架,支持DMA Remapping和Interrupt Remapping。VFIO利用IOMMU可屏蔽物理地址对上层的可见性,可用来开发用户态驱动,也可实现设备透传。
    Group:
        group是IOMMU能够进行DMA隔离的最小硬件单元;
        一个group内可以有一个或多个device,取决于物理平台上硬件的IOMMU拓扑结构;
        一个group中的所有设备只能透传给同一个虚拟机,也不分属于host和vm。
        疑问:iommu拓扑结构怎么改变?答:硬件拓扑,可通过将pci设备查到主板其他pci插槽上改变。
    Container:
        对于用户态驱动,可以是多个group的集合;对于虚拟机可以理解为一个VM Domain的物理内存空间(多个group中的所有设备可直通给同一个虚拟机?答:是)。

5. VFIO用户态驱动:
    /* 获取container文件句柄 */
    container = open("/dev/vfio/vfio", O_RDWR);
    /* 编写某个设备的用户态驱动前,首先将设备从原内核驱动detach,并attach到vfio-pci驱动,即加入到某个vfio group中;
       获取group文件句柄 */
    group = open("/dev/vfio/26", O_RDWR);
    /* 将group加入到container中 */
    ioctl(group, VFIO_GROUP_SET_CONTAINER, &container);
    ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU);
    
    dma_map.vaddr = mmap(0, 1024 * 1024, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); // 用户态虚拟地址
    dma_map.size = 1024 * 1024;
    dma_map.iova = 0; // iommu转换前,设备发起DMA时使用的地址;vaddr经MMU,iova经iommu转换后的物理地址是相同的。
    dma_map.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE;
    ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map);

    疑问:
        iova在用户态怎么赋值?始终赋值为0吗?
        关于vfio用户态驱动和vfio框架值得研究。

6. 设备透传
    pcie设备MMIO空间透传给虚拟机;而配置空间在host枚举时已配置好,不会透传给设备。
    虚拟机地址映射:
        GVA -> GPA -> HPA。
        Qemu维护了GPA和HVA的映射关系。
    设备透传时的地址转换:
        设备:DMA请求中使用GPA,经IOMMU转换为HPA1。
        虚拟机:GVA 经VM MMU-> GPA 经HOST MMU-> HPA2。
        如何确保HPA1与HPA2相等?
            设备在host上的vfio-pci驱动发挥什么作用?
                参考:ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map);
                    设备使用的GPA对应iova;
                    虚拟机GPA对应的HVA,对应vaddr;
                    如此,保证了HPA1与HPA2相等。
            qemu或kvm发挥什么作用?

    设备透传操作过程:
        将设备与host上的driver解绑定
            echo "0000:18:00.0" > /sys/bus/pci/devices/0000\:18\:00.1/driver/unbind
        将设备与host上的vfio-pci绑定
            modprobe vfio
            modprobe vfio-pci
            echo "8086 158b" > /sys/bus/pci/drivers/vfio-pci/new_id
        qemu命令行启动虚拟机时,添加参数:
            -device vfio-pci,host=18:00.0

        dmesg | grep iommu /* 内核启动后,设备属于哪个group就确定了。该命令可查看各个pci设备分别属于哪个group */
        ls /sys/kernel/iommu_groups /* 查看有哪些iommu group,以及各个group中有哪些设备 */
        ls /dev/vfio/ /* 只有待透传设备对应的group出现在/dev/vfio目录下,设备才可能透传成功 */

7. 测试记录:
    物理千兆网卡直接分配给虚拟机,工作正常。    
    82599万兆网卡的VF分配给虚拟机失败。
        原因:VF和PF在同一个iommu group中。
        疑问:主板上带有金属封边的pcie插槽是pci bridge ??
            现象:
                82599万兆网卡插在该插槽上后,新出现了一个pri bridge,且与两个万兆网卡的PF和VF一起被划分至iommu group 1中;无法将VF分配给虚拟机。
                将82599更换插槽后,pci bridge消失,两个万兆网卡的PF和VF分属于不同的iommu group, 且可以将VF分配给虚拟机。

参考:
    https://www.cnblogs.com/yi-mu-xi/p/12370626.html:vfio概述(vfio/iommu/device passthrough)。

-----------------------------------------------------------------分割线-------------------------------------------------------------------------------

linux-4.18.1/Documentation/vfio.txt

1. 什么是vfio?
    提供了DMA和中断重映射功能;无需root特权用户权限;

2. Groups, Devices, and IOMMUs
    没有IOMMU时的问题是什么?
        设备进行DMA操作时,可以访问整个物理内存空间,存在极大的安全隐患;
    一个iommu group中可能包含多个设备;
    group是vfio管理的最小粒度;
    一个container可以包含多个group,这些group共享相同的页表,通过open /sys/vfio/vfio就可以创建一个container;

3. VFIO使用示例说明
    1)获取设备的iommu group:readlink /sys/bus/pci/devices/domain:bus:dev.func/iommu_group    
    2)安装vfio-pci驱动:modprobe vfio-pci
    3)将设备与主机驱动解绑,并绑定到vfio-pci驱动:
        lspci -n -s 0000:06:0d.0
            // 输出:06:0d.0 0401: 1102:0002 (rev 08)
        echo 0000:06:0d.0 > /sys/bus/pci/devices/0000:06:0d.0/driver/unbind
        echo 1102 0002 > /sys/bus/pci/drivers/vfio-pci/new_id
    4)查看该设备所在iommu group中,是否仍有其他设备:ls -l /sys/bus/pci/devices/0000:06:0d.0/iommu_group/devices
        如果有,其他设备也需要绑定vfio-pci驱动;
        pcie-to-pci桥后的设备都位于同一个iommu group中;
        pcie-to-pci桥没有主机驱动,且vfio-pci驱动不支持pci桥;
    5)更改/sys/vfio/group id文件的所有者,以使后续操作无需root权限:
        chown user:user /dev/vfio/group id

4. VFIO使用示例说明
    更详细应用,可参考dpdk实现。
5. VFIO总线驱动api (如:vfio-pci)

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐