searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

设备虚拟化之VFIO原理和使用方式

2023-09-07 12:44:28
442
0

IO(Input/Output)指在内存和外部设备的数据交互过程,设备例如接收输入的键鼠,显示输出的屏幕,存储数据的磁盘,一个功能完备的虚拟机同样需要IO,在虚拟环境真实设备有限,虚拟机管理器对物理设备管理为虚拟机提供设备抽象,截获虚拟机的IO请求,转换为对物理设备的访问,实现对外部设备资源的复用。这用实现方式我们称为设备虚拟化。

将设备直接挂到虚拟机使用叫做设备直通,这种方式可以使虚拟机拥有较好的性能。传统的透传设备方式是QEMU/KVM实现的PCI passthrough,kvm需要和IOMMU、注册中断打交道,kvm完成了设备驱动的部分工作。VFIO是一种用户态驱动框架,利用硬件的IO虚拟化将设备直通给虚拟机,虚拟机具有更好的性能。

 

1、vfio基本思想和原理

设备直通就是将物理设备直接挂到虚拟机,

传统方式为PCI passthrough,KVM需要处理许多工作,包括IOMMU交互、注册中断处理函数等。

VFIO是安全的、用户态驱动框架,利用的是VT-d/AMD-Vi等硬件io虚拟化能力,用户态QEMU接管所有vm对设备的访问

a、基于DMA映射和隔离的硬件IOMMU:基于IOMMU 组

b、模块化IOMMU和总线驱动支持

        已经支持PCI和平台驱动

         IOMMU API(type1)和ppc64(SPAPR)模型 ----- type1主要是intel/amd

c、完全的设备访问、DMA和中断支持

        设备资源的读、写、映射支持

        映射用户内存到IO虚拟地址(iova)

        基于信号机制的eventfd和irqfd

用户态、内核态驱动需求都是一样的:设备资源访问,通过IOMMU隔离DMA映射,中断信号支持。

其他用户态驱动:

     DPDK、数据平面开发包(NFV)

     UNVMe、用户态nvme驱动

      rVFIO、Ruby封装的VFIO

VFIO的基本思想包括两个部分,

第一是将物理设备的各种资源分解,并将获取这些资源的接口向上导出到用户空间,QEMU等应用层软件可以利用这些接口获取硬件的所有资源,包括设备的配置空间、BAR空间和中断。

第二就是聚合,也就是将从硬件设备得到的各种资源聚合起来,对虚拟化展示一个完整的设备接口,这种聚合是在用户空间完成的

 

2、vfio是怎么工作的

2.1、设备驱动怎么控制PCI设备?

    控制IO:IN/OUT、read/write;

    PCI配置空间

VFIO设备文件描述符:

     划分为区域

    每个区域映射到一个设备资源,Ex. MMIO BAR、IO BAR、 PCI config space 

    通过ioctl发现区域信息和计数:文件偏移、可允许访问等等

Eg.

 

| region0 | region1 | region2 | region3 | region4 | region4| ...

0  --文件偏移→

通过ioctl操作

ioctl(fd, VFIO_DEVICE_GET_INFO, &dev_info);

struct vfio_device_info {

__u32 argsz;

__u32 flags;

#define VFIO_DEVICE_FLAGS_RESET (1 << 0) /* Device supports reset */

#define VFIO_DEVICE_FLAGS_PCI (1 << 1) /* vfio-pci device */

#define VFIO_DEVICE_FLAGS_PLATFORM (1 << 2) /* vfio-platform device */

#define VFIO_DEVICE_FLAGS_AMBA  (1 << 3) /* vfio-amba device */

#define VFIO_DEVICE_FLAGS_CCW (1 << 4) /* vfio-ccw device */

#define VFIO_DEVICE_FLAGS_AP (1 << 5) /* vfio-ap device */

__u32 num_regions; /* Max region index + 1 */

__u32 num_irqs; /* Max IRQ index + 1 */

};

 

VFIO_DEVICE_GET_REGION_INFO

struct vfio_region_info {

__u32 argsz;

__u32 flags;

#define VFIO_REGION_INFO_FLAG_READ (1 << 0) /* Region supports read */

#define VFIO_REGION_INFO_FLAG_WRITE (1 << 1) /* Region supports write */

#define VFIO_REGION_INFO_FLAG_MMAP (1 << 2) /* Region supports mmap */

#define VFIO_REGION_INFO_FLAG_CAPS (1 << 3) /* Info supports caps */

__u32 index; /* Region index */

__u32 cap_offset; /* Offset within info struct of first cap */

__u64 size; /* Region size (bytes) */

__u64 offset; /* Region offset from start of device fd */

};

 

VFIO_DEVICE_GET_IRQ_INFO

struct vfio_irq_info {

__u32 argsz;

__u32 flags;

#define VFIO_IRQ_INFO_EVENTFD (1 << 0)

#define VFIO_IRQ_INFO_MASKABLE (1 << 1)

#define VFIO_IRQ_INFO_AUTOMASKED (1 << 2)

#define VFIO_IRQ_INFO_NORESIZE (1 << 3)

__u32 index; /* IRQ index */

__u32 count; /* Number of IRQs within this index */

};

 

2.2、设备怎么给驱动信号?

通过中断,用户态中断是通过eventfd实现的

 

EVENTFD(2)                               Linux Programmer's Manual                               EVENTFD(2)

 

NAME

       eventfd - create a file descriptor for event notification

 

SYNOPSIS

       #include <sys/eventfd.h>

 

       int eventfd(unsigned int initval, int flags);

DESCRIPTION

       eventfd()  creates  an  "eventfd object" that can be used as an event wait/notify mechanism by user-

       space applications, and by the kernel to notify user-space applications of events.  

 

ioctl(s->device, VFIO_DEVICE_SET_IRQS, irq_set);

struct vfio_irq_set {

__u32 argsz;

__u32 flags;

#define VFIO_IRQ_SET_DATA_NONE (1 << 0) /* Data not present */

#define VFIO_IRQ_SET_DATA_BOOL (1 << 1) /* Data is bool (u8) */

#define VFIO_IRQ_SET_DATA_EVENTFD (1 << 2) /* Data is eventfd (s32) */

#define VFIO_IRQ_SET_ACTION_MASK (1 << 3) /* Mask interrupt */

#define VFIO_IRQ_SET_ACTION_UNMASK (1 << 4) /* Unmask interrupt */

#define VFIO_IRQ_SET_ACTION_TRIGGER (1 << 5) /* Trigger interrupt */

__u32 index;

__u32 start;

__u32 count;

__u8 data[];

};

2.3、设备怎么搬运数据?

直接内存访问–DMA

    IO设备可以读写:系统内存(RAM)、每个设备内存

    在CPU MMU控制之外,IO需要一个MMU,→IOMMU

           传输:I/O虚拟地址空间(iova),以前的IOMMU主要目的

           隔离:单设备传输,invalid accesses blocked

IOMMU问题:

           DMA替换(混叠?):不是所有设备需要唯一ID

           DMA隔离:点对点DMA传输

解决办法:IOMMU groups

           IOMMU驱动给设备分组(不是用户配置),组与组间DMA隔离

            影响因素有:IOMMU能力、端点设备隔离、总线和互联属性

            严重影响VFIO设计

内存问题:

        iova page fault不支持端到端,静态映射。

        用户内存可以被迁移,交换、合并等等

一些缺点:

       固定内存就是锁定内存,用户需要足够的锁定内存限制

       防止页面合并和交换

Eg:

三个pci设备,绑定设备到vfio-pci后得到分组节点,一个/dev/vfio/23, 两个/dev/vfio/

open(“/dev/vfio/vfio”) 创建一个container,

open(“/dev/vfio/42”) 创建一个group,

ioctl(group, VFIO_GROUP_SET_CONTAINER, &container)

ioctl(container, VFIO_GROUP_SET_IOMMU, VFIO_TYPE1_IOMMU)

open(“/dev/vfio/23”) 创建一个group2,

ioctl(group2, VFIO_GROUP_SET_CONTAINER, &container)

ioctl(container, VFIO_IOMMU_MAP_DMA, &map)

ioctl(container, VFIO_IOMMU_UNMAP_DMA, &unmap)

#container{ group:/dev/vfio/42(设备1,设备2), group2:/dev/vfio/23(设备3)}   <-→ IOMMU <-→ 系统内存lock/unlock

ioctl(group2, VFIO_GROUP_GET_DEVICE_FD, "0000:01:00.0")

上图是VFIO驱动将设备分解

下图是在用户空间QEMU重新聚合

 

 

3、QEMU 与vfio

3.1、guest如何操作设备

答:QEMU:MemoryRegions;VFIO:Device file descriptor regions

 

设备编程

          受限于hypercisor(qemu/kvm)

          内存区域查找执行,(读写)访问调用

          读写 vfio的区域       

内存区域分层:

“slow”  read/write  base layer

“Fast” mmap overlay

“Quirks” to correct device virtualization issues

3.2、设备如何给guest发信号

答:QEMU:EventNotifiers;VFIO/KVM:Eventfds/irqfds configured via ioctls

有选择的操作:

       直接 pass-through:通过读写配置区域

        虚拟化:MSX/X、BARs、ROM等

中断信号:

    QEMU通过ioctl配置vfio设备中断状态

     通过eventfd发中断信号,Eventnotifiers 触发qemu设备中断,两步:host –》qemu,qemu-》VM

     怎么才能更快呢?通过

irqfd:

     eventfds发信号事件,irqfds接收事件信号,eventfds可以发信号给irqfds

      KVM支持vm通过irqfd发中断,一步:host-》KVM,在用户态不退出

IRQ硬件加速:

     APIC Virtualiztion(Intel APICv):VM更少的中断退出

     VT-d 发中断:中断直接给vcpu

 

3.3、设备怎么传数据 

答:QEMU:MemoryListeners;VFIO:IOMMU mapping & pinning ioctls

 

映射整个虚拟机物理地址

guest看不到IOMMU:DMA是guest physical,host IOMMU映射guest physical 到host physical

通过QEMU memorylistener完成

 

4、VFIO框架

 

1、iommu driver是物理硬件实现,如intel/amd/arm的iommu;vfio_iommu是对iommu driver的封装,向上提供功能,如DMA Remapping以及Interrupt Remapping。

2、pci_bus driver是对物理PCI设备的驱动程序; vfio_pci是对设备驱动的封装,用来提供访问设备驱动的功能,如配置空间和模拟BAR

3、VFIO interface接口层,QEMU等用户态程序,可以通过ioctl与vfio设备交互

 

VFIO对各个设备分区,即使有IOMMU想要以单个设备作为隔离粒度也困难,所以VFIO设备有三个概念来实现(vf)设备粒度的隔离:container/group/device

group是IOMMU能够进行DMA隔离的最小单位,一个group有一个或多个device,取决于硬件IOMMU拓扑

一个group里的设备只能全部直通一个vm,如果部分dev在其他vm或者host,无法做到物理上DMA隔离。(而被利用DMA攻击)

device指IOMMU拓扑的设备,如果设备是一个硬件拓扑上独立的设备,它自己就构成一个IOMMU group;

       如果是一个multi-function设备,那么它和其他的function一起组成一个IOMMU group,因为多个function设备在物理硬件上是互联的,它们可以互相访问数据,所以必须放到一个group里隔离起来。

container是由多个group组成,虽然group是VFIO的最小隔离单元,但是有的时候并不是最好的分割粒度。如多个group可能会共享一组页表,通过将多个group组成一个container可以提高系统的性能,也能够方便用户。一般来讲,每个进程/虚拟机可以作为一个container

 

5、VFIO使用

04:00.0 Ethernet controller: 

# readlink /sys/bus/pci/devices/0000:04:00.0/iommu_group  #由内核生成的
../../../../../../kernel/iommu_groups/40

# ls  /sys/bus/pci/devices/0000:04:00.0/iommu_group/devices/  #这个group只有一个设备
0000:04:00.0

# echo "0000:04:00.0"  >  /sys/bus/pci/devices/0000:04:00.0/driver/unbind #设备解绑

#lspci -n -s 0000:04:00.0  #查看生产商和设备ID

04:00.0 0200: 19e5:1822 (rev 45)

# echo  19e5 1822 /sys/bus/pci/drivers/vfio-pci/new_id #将设备绑定到vfio-pci驱动,会创建一个新设备节点“/dev/vfio/15”,表示直通设备所属的group文件。

#修改节点的用户组为qemu:qemu

#unlimit -l 2621400 #设置能够锁定的内存,vm内存+io空间,(2048+512)*1024

#qemu-kvm  -device vfio-pci,host=0000:04:00.0,id=net0     #qemu启动

 

 

6、VFIO API编程

与KVM的dev/vm/vcpu类似,VFIO接口也分三类

container层面,通过打开“/dev/vfio/vfio”设备可以获得一个新的container,可以用在container上的ioctl包括

接口

功能

VFIOGETAPIVERSION

用来报告VFIO API的版本

VFIO_CHECK_EXTENSION

用来检测是否支持特定的扩展,如支持哪个IOMMU

VFIO_SET_IOMMU

用来指定IOMMU的类型,指定的IOMMU必须是通过VFIO_CHECK_EXTENSION确认驱动支持的

VFIO_IOMMU_GET_INFO

用来得到IOMMU的一些信息,这个ioctl只针对Type1的IOMMU

VFIO_IOMMU_MAP_DMA

用来指定设备端看到的IO地址到进程的虚拟地址之间的映射,类似于KVM中的KVM_SET_USER_MEMORY_REGION指定虚拟机物理地址到进程虚拟地址之间的映射。

这里IOMMU的类型指定的不同架构的IOMMU实现不一样,能够向

上提供的功能也不一样,所以会有不同类型的IOMMU,如内核针对

Intel VT-d和AMD-Vi的IOMMU就叫作Type1 IOMMU

group层面,通过打开“/dev/vfio/<groupid>”可以得到一个group,group层面的ioctl包括如下几个

接口

功能

VFIO_GROUP_GET_STATUS

用来得到指定group的状态信息,如是否可用、是否设置了container

VFIO_GROUP_SET_CONTAINER

用来设置container和group之间的管理,多个group可以属于单个container

VFIO_GROUP_GET_DEVICE_FD

用来返回一个新的文件描述符fd来描述具体设备,用户态进程可以通过该fd获取文件的诸多信息

设备层面,其fd是通过VFIO_GROUP_GET_DEVICE_FD接口返回的,device层面的ioctl包括如下几个。

接口

功能

VFIO_DEVICE_GET_REGION_INFO

用来得到设备的指定Region的数据,需要注意的是,这里的region不单单指BAR,还包括

ROM空间、PCI配置空间等

VFIO_DEVICE_GET_IRQ_INFO

得到设备的中断信息

VFIODEVICERESET

重置设备

 

使用过程:

1、创建container,并判断是否支持Type1类型的IOMMU,设置类型为VFIO_TYPE1_IOMMU

2、打开group,得到该group的信息并设置container

 

3、设置DMA mapping,这里设备视角0-1M映射到进程dma_map.vaddr开始的1M

4、得到直通设备的描述符,获取其各个region信息和irq信息

5、重置设备后,vm就可以使用vf设备了

 

 

0条评论
作者已关闭评论
y****n
4文章数
1粉丝数
y****n
4 文章 | 1 粉丝
y****n
4文章数
1粉丝数
y****n
4 文章 | 1 粉丝
原创

设备虚拟化之VFIO原理和使用方式

2023-09-07 12:44:28
442
0

IO(Input/Output)指在内存和外部设备的数据交互过程,设备例如接收输入的键鼠,显示输出的屏幕,存储数据的磁盘,一个功能完备的虚拟机同样需要IO,在虚拟环境真实设备有限,虚拟机管理器对物理设备管理为虚拟机提供设备抽象,截获虚拟机的IO请求,转换为对物理设备的访问,实现对外部设备资源的复用。这用实现方式我们称为设备虚拟化。

将设备直接挂到虚拟机使用叫做设备直通,这种方式可以使虚拟机拥有较好的性能。传统的透传设备方式是QEMU/KVM实现的PCI passthrough,kvm需要和IOMMU、注册中断打交道,kvm完成了设备驱动的部分工作。VFIO是一种用户态驱动框架,利用硬件的IO虚拟化将设备直通给虚拟机,虚拟机具有更好的性能。

 

1、vfio基本思想和原理

设备直通就是将物理设备直接挂到虚拟机,

传统方式为PCI passthrough,KVM需要处理许多工作,包括IOMMU交互、注册中断处理函数等。

VFIO是安全的、用户态驱动框架,利用的是VT-d/AMD-Vi等硬件io虚拟化能力,用户态QEMU接管所有vm对设备的访问

a、基于DMA映射和隔离的硬件IOMMU:基于IOMMU 组

b、模块化IOMMU和总线驱动支持

        已经支持PCI和平台驱动

         IOMMU API(type1)和ppc64(SPAPR)模型 ----- type1主要是intel/amd

c、完全的设备访问、DMA和中断支持

        设备资源的读、写、映射支持

        映射用户内存到IO虚拟地址(iova)

        基于信号机制的eventfd和irqfd

用户态、内核态驱动需求都是一样的:设备资源访问,通过IOMMU隔离DMA映射,中断信号支持。

其他用户态驱动:

     DPDK、数据平面开发包(NFV)

     UNVMe、用户态nvme驱动

      rVFIO、Ruby封装的VFIO

VFIO的基本思想包括两个部分,

第一是将物理设备的各种资源分解,并将获取这些资源的接口向上导出到用户空间,QEMU等应用层软件可以利用这些接口获取硬件的所有资源,包括设备的配置空间、BAR空间和中断。

第二就是聚合,也就是将从硬件设备得到的各种资源聚合起来,对虚拟化展示一个完整的设备接口,这种聚合是在用户空间完成的

 

2、vfio是怎么工作的

2.1、设备驱动怎么控制PCI设备?

    控制IO:IN/OUT、read/write;

    PCI配置空间

VFIO设备文件描述符:

     划分为区域

    每个区域映射到一个设备资源,Ex. MMIO BAR、IO BAR、 PCI config space 

    通过ioctl发现区域信息和计数:文件偏移、可允许访问等等

Eg.

 

| region0 | region1 | region2 | region3 | region4 | region4| ...

0  --文件偏移→

通过ioctl操作

ioctl(fd, VFIO_DEVICE_GET_INFO, &dev_info);

struct vfio_device_info {

__u32 argsz;

__u32 flags;

#define VFIO_DEVICE_FLAGS_RESET (1 << 0) /* Device supports reset */

#define VFIO_DEVICE_FLAGS_PCI (1 << 1) /* vfio-pci device */

#define VFIO_DEVICE_FLAGS_PLATFORM (1 << 2) /* vfio-platform device */

#define VFIO_DEVICE_FLAGS_AMBA  (1 << 3) /* vfio-amba device */

#define VFIO_DEVICE_FLAGS_CCW (1 << 4) /* vfio-ccw device */

#define VFIO_DEVICE_FLAGS_AP (1 << 5) /* vfio-ap device */

__u32 num_regions; /* Max region index + 1 */

__u32 num_irqs; /* Max IRQ index + 1 */

};

 

VFIO_DEVICE_GET_REGION_INFO

struct vfio_region_info {

__u32 argsz;

__u32 flags;

#define VFIO_REGION_INFO_FLAG_READ (1 << 0) /* Region supports read */

#define VFIO_REGION_INFO_FLAG_WRITE (1 << 1) /* Region supports write */

#define VFIO_REGION_INFO_FLAG_MMAP (1 << 2) /* Region supports mmap */

#define VFIO_REGION_INFO_FLAG_CAPS (1 << 3) /* Info supports caps */

__u32 index; /* Region index */

__u32 cap_offset; /* Offset within info struct of first cap */

__u64 size; /* Region size (bytes) */

__u64 offset; /* Region offset from start of device fd */

};

 

VFIO_DEVICE_GET_IRQ_INFO

struct vfio_irq_info {

__u32 argsz;

__u32 flags;

#define VFIO_IRQ_INFO_EVENTFD (1 << 0)

#define VFIO_IRQ_INFO_MASKABLE (1 << 1)

#define VFIO_IRQ_INFO_AUTOMASKED (1 << 2)

#define VFIO_IRQ_INFO_NORESIZE (1 << 3)

__u32 index; /* IRQ index */

__u32 count; /* Number of IRQs within this index */

};

 

2.2、设备怎么给驱动信号?

通过中断,用户态中断是通过eventfd实现的

 

EVENTFD(2)                               Linux Programmer's Manual                               EVENTFD(2)

 

NAME

       eventfd - create a file descriptor for event notification

 

SYNOPSIS

       #include <sys/eventfd.h>

 

       int eventfd(unsigned int initval, int flags);

DESCRIPTION

       eventfd()  creates  an  "eventfd object" that can be used as an event wait/notify mechanism by user-

       space applications, and by the kernel to notify user-space applications of events.  

 

ioctl(s->device, VFIO_DEVICE_SET_IRQS, irq_set);

struct vfio_irq_set {

__u32 argsz;

__u32 flags;

#define VFIO_IRQ_SET_DATA_NONE (1 << 0) /* Data not present */

#define VFIO_IRQ_SET_DATA_BOOL (1 << 1) /* Data is bool (u8) */

#define VFIO_IRQ_SET_DATA_EVENTFD (1 << 2) /* Data is eventfd (s32) */

#define VFIO_IRQ_SET_ACTION_MASK (1 << 3) /* Mask interrupt */

#define VFIO_IRQ_SET_ACTION_UNMASK (1 << 4) /* Unmask interrupt */

#define VFIO_IRQ_SET_ACTION_TRIGGER (1 << 5) /* Trigger interrupt */

__u32 index;

__u32 start;

__u32 count;

__u8 data[];

};

2.3、设备怎么搬运数据?

直接内存访问–DMA

    IO设备可以读写:系统内存(RAM)、每个设备内存

    在CPU MMU控制之外,IO需要一个MMU,→IOMMU

           传输:I/O虚拟地址空间(iova),以前的IOMMU主要目的

           隔离:单设备传输,invalid accesses blocked

IOMMU问题:

           DMA替换(混叠?):不是所有设备需要唯一ID

           DMA隔离:点对点DMA传输

解决办法:IOMMU groups

           IOMMU驱动给设备分组(不是用户配置),组与组间DMA隔离

            影响因素有:IOMMU能力、端点设备隔离、总线和互联属性

            严重影响VFIO设计

内存问题:

        iova page fault不支持端到端,静态映射。

        用户内存可以被迁移,交换、合并等等

一些缺点:

       固定内存就是锁定内存,用户需要足够的锁定内存限制

       防止页面合并和交换

Eg:

三个pci设备,绑定设备到vfio-pci后得到分组节点,一个/dev/vfio/23, 两个/dev/vfio/

open(“/dev/vfio/vfio”) 创建一个container,

open(“/dev/vfio/42”) 创建一个group,

ioctl(group, VFIO_GROUP_SET_CONTAINER, &container)

ioctl(container, VFIO_GROUP_SET_IOMMU, VFIO_TYPE1_IOMMU)

open(“/dev/vfio/23”) 创建一个group2,

ioctl(group2, VFIO_GROUP_SET_CONTAINER, &container)

ioctl(container, VFIO_IOMMU_MAP_DMA, &map)

ioctl(container, VFIO_IOMMU_UNMAP_DMA, &unmap)

#container{ group:/dev/vfio/42(设备1,设备2), group2:/dev/vfio/23(设备3)}   <-→ IOMMU <-→ 系统内存lock/unlock

ioctl(group2, VFIO_GROUP_GET_DEVICE_FD, "0000:01:00.0")

上图是VFIO驱动将设备分解

下图是在用户空间QEMU重新聚合

 

 

3、QEMU 与vfio

3.1、guest如何操作设备

答:QEMU:MemoryRegions;VFIO:Device file descriptor regions

 

设备编程

          受限于hypercisor(qemu/kvm)

          内存区域查找执行,(读写)访问调用

          读写 vfio的区域       

内存区域分层:

“slow”  read/write  base layer

“Fast” mmap overlay

“Quirks” to correct device virtualization issues

3.2、设备如何给guest发信号

答:QEMU:EventNotifiers;VFIO/KVM:Eventfds/irqfds configured via ioctls

有选择的操作:

       直接 pass-through:通过读写配置区域

        虚拟化:MSX/X、BARs、ROM等

中断信号:

    QEMU通过ioctl配置vfio设备中断状态

     通过eventfd发中断信号,Eventnotifiers 触发qemu设备中断,两步:host –》qemu,qemu-》VM

     怎么才能更快呢?通过

irqfd:

     eventfds发信号事件,irqfds接收事件信号,eventfds可以发信号给irqfds

      KVM支持vm通过irqfd发中断,一步:host-》KVM,在用户态不退出

IRQ硬件加速:

     APIC Virtualiztion(Intel APICv):VM更少的中断退出

     VT-d 发中断:中断直接给vcpu

 

3.3、设备怎么传数据 

答:QEMU:MemoryListeners;VFIO:IOMMU mapping & pinning ioctls

 

映射整个虚拟机物理地址

guest看不到IOMMU:DMA是guest physical,host IOMMU映射guest physical 到host physical

通过QEMU memorylistener完成

 

4、VFIO框架

 

1、iommu driver是物理硬件实现,如intel/amd/arm的iommu;vfio_iommu是对iommu driver的封装,向上提供功能,如DMA Remapping以及Interrupt Remapping。

2、pci_bus driver是对物理PCI设备的驱动程序; vfio_pci是对设备驱动的封装,用来提供访问设备驱动的功能,如配置空间和模拟BAR

3、VFIO interface接口层,QEMU等用户态程序,可以通过ioctl与vfio设备交互

 

VFIO对各个设备分区,即使有IOMMU想要以单个设备作为隔离粒度也困难,所以VFIO设备有三个概念来实现(vf)设备粒度的隔离:container/group/device

group是IOMMU能够进行DMA隔离的最小单位,一个group有一个或多个device,取决于硬件IOMMU拓扑

一个group里的设备只能全部直通一个vm,如果部分dev在其他vm或者host,无法做到物理上DMA隔离。(而被利用DMA攻击)

device指IOMMU拓扑的设备,如果设备是一个硬件拓扑上独立的设备,它自己就构成一个IOMMU group;

       如果是一个multi-function设备,那么它和其他的function一起组成一个IOMMU group,因为多个function设备在物理硬件上是互联的,它们可以互相访问数据,所以必须放到一个group里隔离起来。

container是由多个group组成,虽然group是VFIO的最小隔离单元,但是有的时候并不是最好的分割粒度。如多个group可能会共享一组页表,通过将多个group组成一个container可以提高系统的性能,也能够方便用户。一般来讲,每个进程/虚拟机可以作为一个container

 

5、VFIO使用

04:00.0 Ethernet controller: 

# readlink /sys/bus/pci/devices/0000:04:00.0/iommu_group  #由内核生成的
../../../../../../kernel/iommu_groups/40

# ls  /sys/bus/pci/devices/0000:04:00.0/iommu_group/devices/  #这个group只有一个设备
0000:04:00.0

# echo "0000:04:00.0"  >  /sys/bus/pci/devices/0000:04:00.0/driver/unbind #设备解绑

#lspci -n -s 0000:04:00.0  #查看生产商和设备ID

04:00.0 0200: 19e5:1822 (rev 45)

# echo  19e5 1822 /sys/bus/pci/drivers/vfio-pci/new_id #将设备绑定到vfio-pci驱动,会创建一个新设备节点“/dev/vfio/15”,表示直通设备所属的group文件。

#修改节点的用户组为qemu:qemu

#unlimit -l 2621400 #设置能够锁定的内存,vm内存+io空间,(2048+512)*1024

#qemu-kvm  -device vfio-pci,host=0000:04:00.0,id=net0     #qemu启动

 

 

6、VFIO API编程

与KVM的dev/vm/vcpu类似,VFIO接口也分三类

container层面,通过打开“/dev/vfio/vfio”设备可以获得一个新的container,可以用在container上的ioctl包括

接口

功能

VFIOGETAPIVERSION

用来报告VFIO API的版本

VFIO_CHECK_EXTENSION

用来检测是否支持特定的扩展,如支持哪个IOMMU

VFIO_SET_IOMMU

用来指定IOMMU的类型,指定的IOMMU必须是通过VFIO_CHECK_EXTENSION确认驱动支持的

VFIO_IOMMU_GET_INFO

用来得到IOMMU的一些信息,这个ioctl只针对Type1的IOMMU

VFIO_IOMMU_MAP_DMA

用来指定设备端看到的IO地址到进程的虚拟地址之间的映射,类似于KVM中的KVM_SET_USER_MEMORY_REGION指定虚拟机物理地址到进程虚拟地址之间的映射。

这里IOMMU的类型指定的不同架构的IOMMU实现不一样,能够向

上提供的功能也不一样,所以会有不同类型的IOMMU,如内核针对

Intel VT-d和AMD-Vi的IOMMU就叫作Type1 IOMMU

group层面,通过打开“/dev/vfio/<groupid>”可以得到一个group,group层面的ioctl包括如下几个

接口

功能

VFIO_GROUP_GET_STATUS

用来得到指定group的状态信息,如是否可用、是否设置了container

VFIO_GROUP_SET_CONTAINER

用来设置container和group之间的管理,多个group可以属于单个container

VFIO_GROUP_GET_DEVICE_FD

用来返回一个新的文件描述符fd来描述具体设备,用户态进程可以通过该fd获取文件的诸多信息

设备层面,其fd是通过VFIO_GROUP_GET_DEVICE_FD接口返回的,device层面的ioctl包括如下几个。

接口

功能

VFIO_DEVICE_GET_REGION_INFO

用来得到设备的指定Region的数据,需要注意的是,这里的region不单单指BAR,还包括

ROM空间、PCI配置空间等

VFIO_DEVICE_GET_IRQ_INFO

得到设备的中断信息

VFIODEVICERESET

重置设备

 

使用过程:

1、创建container,并判断是否支持Type1类型的IOMMU,设置类型为VFIO_TYPE1_IOMMU

2、打开group,得到该group的信息并设置container

 

3、设置DMA mapping,这里设备视角0-1M映射到进程dma_map.vaddr开始的1M

4、得到直通设备的描述符,获取其各个region信息和irq信息

5、重置设备后,vm就可以使用vf设备了

 

 

文章来自个人专栏
虚拟化技术
4 文章 | 2 订阅
0条评论
作者已关闭评论
作者已关闭评论
3
2