目录:
- 虚拟化
- dpdk的实现研究
- virtio
- vhost
- SR-IOV
- 热迁移相关
- 研究拓展
本文记录近期对dpdk在虚拟化和云计算领域应用的研究成果,内容梳理如下。
虚拟化
虚拟化,抽象来说,就是将物理资源逻辑化。具体来说,虚拟技术的实现是在系统中加入一个虚拟化层(也就是hypervisor),将下层的物理资源(如disk,nic,cpu,memory等)抽象成另一种形式的资源,提供给上层应用,通过空间上的分割,时间上的分时以及模拟,将一份资源抽象成多份。
虚拟化能带来的好处不言而喻,可以显著提高物理资源的使用效率,能够进行动态分配、资源管理和负载的相互隔离,并提供高安全性和自动化。虚拟化还为云计算提供支持,主要提供按需的服务配置和软件定义的资源编排等。
X86平台的虚拟化实现主要有三部分:CPU虚拟化、内存虚拟化和IO虚拟化。
- CPU虚拟化
intel引入VT-x来提升CPU虚拟化效率和虚拟机安全性(参见图1)。VT-x扩展了传统的x86处理器架构,它引入了两种操作模式:VMX root operation(根虚拟化操作)和VMX non-root operation(非根虚拟化操作),统称为VMX操作模式。 此外,还支持虚机热迁移特性。
为了建立这种两个操作模式的架构,VT-x设计了一个Virtual-Machine Control Structure(VMCS,虚拟机控制结构)的数据结构,包括了Guest-State Area(客户状态区)和Host-State Area(主机状态区),用来保存虚拟机以及主机的各种状态参数,并提供了VM entry和VM exit两种操作在虚拟机与VMM之间切换,并在切换时会自动查询和更新VMCS,加速guest状态切换时间。这样非根模式下敏感指令不再是直接执行或者通过陷入再模拟的方式执行,而是通过VM exit和VM entry这两个操作完成切换,既解决了虚拟机的隔离问题,又解决了性能问题。
关于VT-x的详细介绍,还可以参考这里和这里。
- 内存虚拟化
内存虚拟化的核心任务是实现地址空间虚拟化,一般的实现原理是这样的:
通过两次地址转化来支持地址空间虚拟化:GVA(Guest Virtual Address)->GPA(Guest Physical Address)->GMA(Host Physical Address).其中VA->PA的转换由guest完成,通常是通过VMCS中的客户机状态域CR3指向的页表来指定;PA->MA的转换由宿主机完成,一般在guest建立时就分配好固定的物理内存,并采用一定的数据结构记录响应的映射关系。
传统的IA架构只支持一次地址转换,即CR3指向的页表来实现虚拟地址到物理地址的转化(即VA->PA的转化),这和上面的过程中要求的两次地址转换是矛盾的,因此为解决这个问题,Intel引入了VT-x技术,在原有的一次地址转换基础上,又引入了EPT页表实现PA->MA的转换,从而在硬件上支持了两次地址转化,大大提高了地址转换的性能。
关于EPT的工作原理如图3描述::
首先根据VA的地址和CR3指向的页表计算出PA,在通过EPT页表实现PA->MA的地址转化。关于内存虚拟化的详细介绍,还可以参考这里和这里。
- IO虚拟化
IO虚拟化包括管理虚拟设备和物理硬件之间的IO请求的路由选择。实现方式可以划分为:全虚拟化、半虚拟化,IO透传,SR-IOV。
其中全虚拟化是指客户机的所有功能或总线结构都可以在宿主机上进行模拟,宿主机通过截获客户机的I/O请求,通过软件来完全模拟硬件。尽管这样模拟得很彻底,但效率却比较低(需要由VMM来捕获特权指令和翻译地址)。
半虚拟化是指客户机能够感知自己是虚拟机,执行特权指令时直接向hypervisor call调用,省去指令的翻译过程,从而提升性能。
I/O透传是指直接将物理设备分配给虚拟使用,这种方式需要硬件平台具备I/O透传技术,能获取到近乎本地的性能,且CPU开销小。透传的使用通常结合intel VT-D来使用。
SR-IOV主要用来解决透传时一个物理硬件只能被一台虚拟子机访问的问题。SR-IOV需要网卡硬件支持,支持SR-IOV功能的网卡(PF)可以在Hypervior里面注册成多个网卡(VF)(每个网卡都独立的中断ID、收发队列、QOS管理机制),每个VF可以通过pass-through方式分配给虚拟子机。
关于这块的资料比较多,就不展开介绍,想了解的可以点这里和这里。
DPDK通过virtio和vhost PMD来实现IO的半虚拟化功能。此外,DPDK还支持I/O透传,SR-IOV等特性,进一步提升IO性能。
除了X86服务器平台的虚拟化,还有些比较重要的领域就是网络虚拟化(NFV)和软件定义网络(SDN)。
- NFV
即网络功能虚拟化,Network Function Virtualization。通过使用x86等通用性硬件以及虚拟化技术,来承载很多功能的软件处理。从而降低网络昂贵的设备成本。可以通过软硬件解耦及功能抽象,使网络设备功能不再依赖于专用硬件,资源可以充分灵活共享,实现新业务的快速开发和部署,并基于实际业务需求进行自动部署、弹性伸缩、故障隔离和自愈等。关于NFV的概念可以参考这里。
其中NFV框架中所有的软件功能都由虚拟的VNF来实现,虚机本身的性能就存在很大的优化空间。当考虑VNF性能时,需要考虑本身的架构设计,以及NFVI能够提供的硬件资源能力和交互接口等等。
一般上在系统整体架构上需要考虑如下几点:
- VNF本身特性:计算密集型?IO密集型?内存密集型?有可能是多种特性集一身
- 系统资源的分配:评估VNF或者VNF子模块对处理器、内存、存储、网络的需求
- 网卡虚拟化接口的选择:是否独占物理网卡,独占的化使用透传技术,否则需要共享。还需要考虑接口的性能、迁移性、维护性、安全性等
- 网卡轮询和中断模式的选择:轮询模式CPU占比高,但网络吞吐性能高,100%占有一个core来进行收包是否合理? 中断模式CPU占有率低,但处理小包的性能不高
- 硬件加速功能的考虑:支持硬件卸载的网卡,定制的FPGA,QAT加速卡等是否可以和业务配合使用?
- QOS保证:多VNF运行在同一台服务器时,由于物理资源共享,各VNF对资源的使用率又不尽相同,可能会造成互相干扰性能下降
- 是否需要支持动态迁移:这个对IO,内存,CPU等都会提出特殊要求
- SDN
SDN主要是一种实现网络框架,最重要的三个概念是:可编程(开放的API接口)、控制平面与数据平面分离,以及集中式控制模型。基于SDN的网络架构可以更容易地实现网络虚拟化。关于SDN的概念讨论可以参考这里。
目前DPDK对SDN的支持可以落在以下几个点上:
- 对数据转发面的优化,包括提升VNF的性能、和ovs的结合
- SFC(软件服务链)转发性能优化,多个SF之间的数据交互,可以不用过vswitch,而是直接通过virtio-pci进行传输。
DPDK的实现
DPDK对I/O虚拟化的支持主要集中在I/O半虚拟化,通过提供virtio PMD 和 vhost后端加速驱动来提升I/O处理性能;此外,对于SR-IOV虚拟出来的PF和VF也提供了VMDQ来支持,下面来分别展开介绍。
virtio
virtio是一种半虚拟化的设备抽象接口规范,在guest操作系统中实现的前端驱动程序一般直接称为virtio,在host操作系统实现的后端驱动从程序通常称为vhost。与guest端纯软件模拟I/O(如e1000,rt18139)相比,virtio可以提供很好的I/O性能,虽然同I/O透传技术或者SR-IOV技术相比,目前在网络吞吐率、时延以及抖动性各方面相比都不具备优势,相关的优化工作正在进行当中。此外,使用virtio技术可以支持虚拟机的动态迁移以及灵活的流分类规则。
virtio主要有两个版本,0.95和1.0,其规定的实现接口有PCI,MMIO和Channel IO方式,其中Channel IO方式是在1.0版本中新增的。PCI是现代计算机系统中普遍使用的一种总线接口,最新规范为PCI-e,DPDK目前只支持PCI接口方式。
Virtio 使用 virtqueue 来实现其 I/O 机制,每个 virtqueue 就是一个承载大量数据的 queue。vring 是 virtqueue 的具体实现方式,针对 vring 会有相应的描述符表格进行描述。框架如下图所示:
其中比较重要的几个概念是:
- 设备的配置:初始化、配置PCI设备空间和特性、中断配置和专属配置
- 虚拟队列的配置:virtqueue、vring、descriptor table、avaliable ring和used ring的使用
- 设备的使用
- 驱动向设备提供缓冲区并写入数据
- 设备使用数据及归还缓冲区
关于virtio的基本概念和设备操作可以参考这里,对于补充virtio相关基础知识个人认为介绍的足够了。
dpdk对virtio的实现
virtio在linux内核和dpdk都有相应的驱动,其中linux内核版本功能更加全面,dpdk版本更注重性能。可以先参考下内核中对virtio的实现抽象层次:
- 第一层抽象:底层PCI-e设备层,负责检测PCI-e设备,并初始化设备对应的驱动程序,提供两个抽象类:virtio_driver和virtio_device
- 第二层抽像:中间virio虚拟队列层,实现virtqueue,提供类:vring_virtqueue,vring等
- 第三层抽象:上层网络设备层,实现底层的两个抽象类:virtio_net_driver和dev,能够供应用软件将其看成普通的网口使用
对应的dpdk驱动也是按照这个思路来进行实现的,pmd驱动文件的组成见下图(参考17.05版本,目录为:dpdk-17.05\drivers\net\virtio\):
除了上图中框出的文件,还有和virtio_user相关的文件主要用来实现类似KNI的exception path,这块内容放到其它篇幅再继续研究,这里先跳过。
第一层抽象
//drivers\net\virio\virtio_pic.h
/*第一大块:virtio设备的配置相关宏定义*/
/* VirtIO PCI vendor/device ID. */
#define VIRTIO_PCI_VENDORID 0x1AF4
#define VIRTIO_PCI_LEGACY_DEVICEID_NET 0x1000
#define VIRTIO_PCI_MODERN_DEVICEID_NET 0x1041
/*
* VirtIO Header, located in BAR 0
* 具体的相关宏定义可参考virtio设备标准
*/
#define VIRTIO_PCI_HOST_FEATURES 0 /* host's supported features (32bit, RO)*/
#define VIRTIO_PCI_GUEST_FEATURES 4 /* guest's supported features (32, RW) */
#define VIRTIO_PCI_QUEUE_PFN 8 /* physical address of VQ (32, RW) */
#define VIRTIO_PCI_QUEUE_NUM 12 /* number of ring entries (16, RO) */
#define VIRTIO_PCI_QUEUE_SEL 14 /* current VQ selection (16, RW) */
#define VIRTIO_PCI_QUEUE_NOTIFY 16 /* notify host regarding VQ (16, RW) */
#define VIRTIO_PCI_STATUS 18 /* device status register (8, RW) */
#define VIRTIO_PCI_ISR 19 /* interrupt status register, reading
* also clears the register (8, RO) */
/* Only if MSIX is enabled: */
#define VIRTIO_MSI_CONFIG_VECTOR 20 /* configuration change vector (16, RW) */
#define VIRTIO_MSI_QUEUE_VECTOR 22 /* vector for selected VQ notifications
(16, RW) */
/* The bit of the ISR which indicates a device has an interrupt. */
#define VIRTIO_PCI_ISR_INTR 0x1
/* The bit of the ISR which indicates a device configuration change. */
#define VIRTIO_PCI_ISR_CONFIG 0x2
/* Vector value used to disable MSI for queue. */
#define VIRTIO_MSI_NO_VECTOR 0xFFFF
/* VirtIO device IDs. virtio不止有网卡,还有存储、内存等等*/
#define VIRTIO_ID_NETWORK 0x01
#define VIRTIO_ID_BLOCK 0x02
#define VIRTIO_ID_CONSOLE 0x03
#define VIRTIO_ID_ENTROPY 0x04
#define VIRTIO_ID_BALLOON 0x05
#define VIRTIO_ID_IOMEMORY 0x06
#define VIRTIO_ID_9P 0x09
/* Status byte for guest to report progress.
* 当驱动初始化一个virtio设备时,通过设备状态来反应进度
*/
#define VIRTIO_CONFIG_STATUS_RESET 0x00
#define VIRTIO_CONFIG_STATUS_ACK 0x01
#define VIRTIO_CONFIG_STATUS_DRIVER 0x02
#define VIRTIO_CONFIG_STATUS_DRIVER_OK 0x04<