网络虚拟化——virtio-user

在上一篇文章(网络虚拟化——vhost-user_dillanzhou的博客-CSDN博客)中,介绍了通过DPDK框架,将vhost移入用户态的技术——vhost-user的原理。其中也提到了可以将virtio设备驱动也放到用户态,从而实现更高效率的基于virtio设备的网络收发应用。DPDK中提供了virtio网卡的用户态驱动,称为virtio-pmd。更进一步,DPDK还支持了virtio-user,能够在DPDK进程中自己创建virtio设备并与vhost完成初始化配置,能够在没有kvm/qemu参与的非虚拟化场景下使用virtio设备。

从前面几篇文章的介绍中我们可以发现,virtio设备和驱动从虚拟机角度来看和普通的网卡设备没有什么区别,只是数据面和控制面接口符合virtio规范而已。因此virtio-pmd驱动和DPDK提供的其他pmd驱动没有什么本质区别。因此本文将从DPDK的用户态驱动原理出发,探讨一下在用户态实现virtio设备驱动所需要的相关技术。此外,和DPDK提供的其他用户态驱动相比,virtio-pmd还是有一个特殊点,那就是支持vdev虚拟设备模式,即virtio-user。在这种模式下,DPDK驱动的virtio设备是一个纯软件的DPDK内部虚拟设备,和通过qemu/kvm模拟的virtio-pci设备不同,这种设备由DPDK自己管理,DPDK明确知道这个设备是自己虚拟出来的,会在驱动中直接与vhost-net/vhost-user交互配置和初始化,并直接通过操作eventfd来实现和vhost端的事件通知。

本文将对上述两方面的内容做具体介绍。

本文内容部分参考了:https://www.redhat.com/zh/blog/journey-vhost-users-realm

问题

  1. 用户态驱动的原理是什么?如何在用户态直接操作设备?
  2. 用户态驱动框架uio和vfio有什么区别?
  3. virtio-pmd与virtio-user有哪些使用场景?在非虚拟化场景下如何运行?

用户态驱动与virtio-pmd

一般情况下,Linux系统中的设备驱动都是运行在内核态的。原因包括:

  • 驱动需要访问设备内存地址并处理设备中断,这些信息只有在Linux内核态下才能直接访问。
  • 将驱动实现在内核态能够将较稳定的物理设备功能和较灵活的应用逻辑使用内核/用户态接口(例如syscall)自然的隔离开,使得用户态的应用能够使用稳定通用的接口,而不必关心实际操作的底层设备和驱动。
  • 内核作为集中式的系统核心组件,在其中实现设备驱动可以更方便的实现设备的全系统共享。如果驱动实现在用户态,那么多个应用进程要共享一个设备就需要与用户态的驱动进程分别进行IPC通信和同步,很容易造成多进程和驱动的混乱和损坏。

DPDK最主要的特点,就是实现了用户态网卡驱动,使得网络数据的处理逻辑可以和网卡的收发逻辑紧密结合在一起,减少了内核/用户态切换、内存拷贝和不必要的复杂封装逻辑。用户态驱动需要内核模块的支持,通过内核模块提供的接口使得用户态程序能够映射设备地址空间,处理设备中断。

用户态驱动技术有两种:UIO和VFIO。早期版本的DPDK只支持UIO框架,从DPDK1.7开始也支持VFIO。目前VFIO已经是DPDK的主要用户态驱动框架,从DPDK20.02版本开始基于UIO框架的igb_uio模块不再默认编译,文档也不建议再使用UIO。

UIO

UIO是“Userspace I/O”的缩写,是一种相对简单的用户态驱动框架。linux内核中包含了uio.ko内核模块,提供了框架功能。UIO的原理是为每个注册使用UIO的设备生成一个/dev/uioX的字符设备,通过这个字符设备,用户态程序可以实现设备内存空间映射(mmap)、设备中断开关(write)、设备中断获取(read)等操作。

UIO模块本身只提供一个框架,使用UIO框架的设备需要再实现一个内核驱动模块,在DPDK中就是igb_uio。igb_uio本身是一个PCI设备驱动,提供标准的probe接口来向内核接管目标设备。igb_uoi会调用UIO的注册接口,从而真正提供UIO的用户态文件接口。在内核看来,网卡设备的驱动仍然是内核态的igb_uio,但实际上igb_uio通过UIO框架将设备内存地址映射到了用户态,用户态程序可以直接向网卡收发数据。在DPDK默认的polling mode中,用户态驱动初始化后就几乎不需要igb_uio参与后续的工作了。但在中断模式下,用户态获取设备中断仍然需要通过UIO提供的/dev/uioX文件。igb_uio需要在内核态处理设备中断,在中断处理函数中将事件反映到uioX文件的read/poll系统调用上。

上图是DPDK应用使用UIO框架实现用户态驱动的示意图。

需要注意的是,由于UIO框架中没有使用IOMMU,因此注册给NIC的DMA内存地址必须是物理地址。这就要求DPDK在用户态翻译出所使用内存的物理地址并注册给NIC。

从上面的介绍可以发现,UIO的接口和功能比较简单,主要有几个方面的限制:

  1. 不支持IOMMU,因此如果要让设备DMA,必须在用户态获取进程的物理内存地址空间并注册给设备。这个操作本身就需要root权限,而且必须在内核中pin住物理页避免地址映射关系变化,如果物理地址空间设置错误还会造成系统异常或者崩溃。
  2. 每个设备只支持一个中断。对于polling模式的DPDK应用来说可能影响不大,但对于需要响应多个中断的应用来说功能就很受限制,包括使用中断模式的DPDK应用。

VFIO

VFIO是“Virtual Function I/O”的缩写,但事实上VFIO并不专用于Virtual Function。DPDK的物理网卡pmd也都使用VFIO支持。因此内核VFIO模块的maintainer Alex Williamson建议将其称作“Versatile Framework for userspace I/O”——全能用户态IO框架。从这个解释就能看出,VFIO是对UIO的增强,特别是在前面提到的UIO的几个局限性上,VFIO给出了更完善的解决方案。

VFIO主要通过IOMMU(Input/Output Memory Management Unit)来实现设备的DMA访问和中断重定向。IOMMU是CPU中集成的一个芯片部件,其功能和MMU类似。CPU执行内存访问指令时,MMU会通过查询页表,将指令中提供的虚拟地址转换为物理地址,然后才能访问内存数据。与MMU类似,IOMMU则会为IO设备维护页表,当IO设备需要DMA访问内存时,IOMMU可以将设备访问的虚拟地址转换为物理地址,从而实现物理内存访问。通过IOMMU,VFIO可以将用户态进程的虚拟内存地址注册给网卡设备,并维护相应的页表。设备只能访问页表中维护了的虚拟地址空间,不需要也不能够直接访问物理内存空间。这就避免了UIO框架让用户态程序注册物理DMA地址的高危操作,也让VFIO的操作不再需要在特权模式下执行。

VFIO的用法和UIO类似,也有对应的内核驱动模块。需要将目标IO设备绑定到VFIO驱动上,再通过操作/dev/vfio/目录下的设备文件实现设备驱动功能。

与UIO类似,VFIO也分为接口层的框架模块和具体的设备驱动模块,分别为vfio和vfio-pci(PCI设备驱动)。通过将网卡设备绑定到vfio_pci驱动,vfio-pci就可以通过vfio提供的/dev/vfio文件接口,向用户态提供设备访问和IOMMU管理功能。

VFIO的文件接口有两个,分别是/dev/vfio/vfio和/dev/vfio/$IOMMU_GROUP_ID。

$IOMMU_GROUP_ID是设备所属的IOMMU group的编号,一个IOMMU group中的设备必须全部绑定到VFIO驱动中(或者不绑定驱动)才能使用IOMMU功能。IOMMU group是IOMMU管理设备地址空间映射的最小单元。关于IOMMU group,可以参考《IOMMU是如何划分PCI device group的? - 知乎》这篇文章的介绍。一般情况下,一个PCI设备IOMMU group中只有一个device。

/dev/vfio/vfio用于创建IOMMU container,可以在一个container中管理多个IOMMU group。

VFIO向用户态提供的功能主要有两类:访问设备和配置IOMMU。

VFIO访问设备的接口和UIO类似,也是对设备fd执行mmap/read/write操作。不同的是设备fd的获取方式,需要如下步骤:

  1. group_fd = open("/dev/vfio/$IOMMU_GROUP_ID", ...),打开iommu group文件
  2. device_fd = ioctl(group_fd, VFIO_GROUP_GET_DEVICE_FD, "$device_bdf"),获取设备fd。
  3. ioctl(device_fd, VFIO_DEVICE_GET_REGION_INFO/VFIO_DEVICE_GET_IRQ_INFO/VFIO_DEVICE_SET_IRQS, ...),获取设备配置信息,获取中断信息,设置中断对应eventfd。
  4. ioctl(device_fd, /VFIO_DEVICE_GET_IRQ_INFO/VFIO_DEVICE_SET_IRQS, ...),获取中断信息,设置中断对应eventfd。

配置IOMMU的方式如下:

  1. container_fd = open("/dev/vfio/vfio", ...),得到container文件描述符
  2. ioctl(container_fd, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU),设置一种IOMMU模型
  3. group_fd = open("/dev/vfio/$IOMMU_GROUP_ID", ...),打开iommu group文件
  4. ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container_fd),将IOMMU group加入container
  5. ioctl(container_fd, VFIO_IOMMU_MAP_DMA, &dma_map),将此IOMMU group的DMA地址映射至进程虚拟地址空间。dma_map中包含进程通过mmap分配的虚拟地址空间。

通过上述方式,VFIO提供了用户态设备驱动接口,并解决了UIO存在的问题:

  1. 通过配置IOMMU,避免了在用户态获取和使用物理地址,提升了用户态驱动的安全性和易用性
  2. 通过注册eventfd的方式,为设备的每个irq中断提供了用户态的接收方式

virtio-pmd

virtio-pmd是DPDK中实现的virtio设备的PMD(poll mode driver)。virtio-pmd通过VFIO接口实现对虚拟/物理virtio网卡的用户态配置和数据处理。与其他网卡设备的PMD相比,virtio-pmd没有什么明显的特别之处。

virtio-user

virtio-user是在virtio-pmd的基础上,完全在用户态实现的virtio虚拟设备。virtio-user与传统的虚拟化技术完全无关,不再需要qemu/kvm等hypervisor介入。virtio-user相当于在用户态进程内同时实现了virtio-pmd和qemu的virtio后端控制面配置能力。

在virtio-user模式下,virtio设备挂载在DPDK内部虚拟的vdev总线上。virtio-user直接与vhost-net/vhost-user交互配置和初始化,通过ioctl/unix socket方式将virtqueue配置和内存布局、以及eventfd发送给vhost-net/vhost-user。之后,virtio-pmd会直接通过操作eventfd的方式来实现和vhost端的事件通知。

小结

本文介绍了用户态驱动的原理和virtio驱动的用户态实现——virtio-pmd,以及在此基础上实现的纯用户态虚拟virtio设备virtio-user。最后来看一下文章开头的问题:

1. 用户态驱动的原理是什么?如何在用户态直接操作设备?

用户态驱动仍然依赖内核驱动框架。支持用户态驱动的UIO/VFIO内核框架主要向用户态提供了两类功能接口:设备地址空间映射和设备中断处理。通过这些接口,用户态驱动可以直接读写设备内存,获取设备中断事件,从而实现对设备的操作和设备中断事件的处理。

2. 用户态驱动框架uio和vfio有什么区别?

和UIO相比,VFIO增强了两方面的能力:通过IOMMU支持了设备通过虚拟地址访问主存,提升了用户态驱动的安全性;通过eventfd通知中断事件,支持了单个设备上的多个中断。

3. virtio-pmd和virtio-user有哪些使用场景?在非虚拟化场景下如何运行?

virtio-pmd最基本的使用场景还是在虚拟机中,可以用于加速虚拟机中的网络应用。

另一种场景是物理网卡直接将PF或VF以virtio设备的形式呈现,使用virtio-pmd可以直接向物理网卡收发报文。例如阿里云的神龙网卡就提供这种网络接口。

virtio-user的一种场景是在容器中使用,DPDK支持在用户态创建virtio虚拟设备,由于这种场景下没有qemu存在,DPDK会主动通过vhost-net/vhost-user的配置接口配置virtio信息。通过这种方式,容器中的应用在没有VF passthrough的情况下,也可以通过一种相对通用的方式实现高性能的网络数据处理。

virtio-user的另一种用途是通过vhost-net模块与内核交互报文。在virtio-user之前,DPDK支持的内核交互方式有KNI、tap、raw socket等,但这些方式的效率都较低,需要频繁调用syscall来注入报文。使用virtio-user后,用户态进程与内核通过共享内存的方式就能实现报文交互,性能有明显改善。

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值