目录:
- 虚拟化
- 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操作模式。 此外,还支持虚机热迁移特性。
图2:地址空间虚拟化实现
通过两次地址转化来支持地址空间虚拟化: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描述::
图3:EPT工作原理
首先根据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技术可以支持虚拟机的动态迁移以及灵活的流分类规则。
![](https://img.haomeiwen.com/i3143954/66397c0afaa8754a.jpg)
其中比较重要的几个概念是:
- 设备的配置:初始化、配置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\):
图7 SR-IOV架构图
以上图为例逐个解释关键词:- PF就是物理网卡所支持的一项PCI功能,PF可以扩展出若干个VF
- VF是支持SRIOV的物理网卡所虚拟出的一个“网卡”或者说虚出来的一个实例,它会以一个独立网卡的形式呈现出来,每一个VF有它自己独享的PCI配置区域,并且可能与其他VF共享着同一个物理资源(公用同一个物理网口)
- PF miniport driver即PF驱动是工作于Hyper-V虚拟化平台父区域的,并在VF之前最先加载
- VF miniport driver即VF驱动是工作于Hyper-V虚拟化平台子区域的,即guestOS;需要注意的是,VF及PF之间是隔离的,任何经由VF驱动或所执行的结果都不会影响到其他的VF或PF
- Network Interface Card即物理网卡,在启用SRIOV之后会生成若干vport,物理NIC所要做的就是转发physical port与vport之间的流量
- physical port顾名思义就是物理网口,在SRIOV场景中physical port充当一个面向对外的网络媒介
- VPort是个抽象出来的接口,类似于物理网口,它们被映射给每一个VF或者PF,供parentOS或guestOS来使用
启用SRIOV之后,物理NIC将通过VF与虚拟机(VF driver)进行数据交互,反之亦然。那么这样一来即可跳过中间的虚拟化堆栈(即VMM层),以达到近乎于纯物理环境的性能;这一点也是SRIOV最大的价值所在。
关于更详细的介绍资料和实验数据对比,可以参考这里和这里
关于dpdk使用SR-IOV的参考资料在这里。
摘自上面的资料,使用SR_IOV技术和纯物理机,以及用户态的ovs性能对比如下:
图8:不同技术的性能对比
比较典型的IMIX流量中小包占比会在50%~60%之间,从上表可以看到SR-IOV的测试数据中小包处理能力在70%左右,这就表明该技术在实际的使用环境中能够应对绝大多数场景;而OVS在此方面的优化还需要继续努力。
另外关于dpdk使用SR-IOV的配置,可以参考如下:
热迁移相关
从上面的介绍了解,要使用DPDK技术,在VM中可以使用virtio驱动,也可以使用硬件网卡提供的SR-IOV VF来支持。对于热迁移来说,就需要针对两种驱动单独考虑。
如何使用
DPDK关于使用两种驱动的测试用例在官网有提供,可以参考:
- Live Migration of VM with SR-IOV VF,由于这种驱动是硬件提供switch来完成报文到VF的分发,很难去感知VM的迁移,因此需要借助其他技术来实现迁移,文中提到的使用bond口就是当前的实现方案
- Live Migration of Vm with Virtio on host running vhost-user,这种驱动中使用的vswitch功能,因此还是比较好实现VM的迁移的。
结合ovs的测试方法,可以参考这里。
代码相关支持
对代码的修改主要是由以下patch来完成:
- Patch 1 handles VHOST_USER_SET_LOG_BASE, which tells us where
the dirty memory bitmap is.
//通过mmap将要迁移的dirty memory设置成shared状态,可供对端读写 static int vhost_user_set_log_base(struct virtio_net *dev, struct VhostUserMsg *msg) { int fd = msg->fds[0]; uint64_t size, off; void *addr; if (fd < 0) { RTE_LOG(ERR, VHOST_CONFIG, "invalid log fd: %d\n", fd); return -1; } if (msg->size != sizeof(VhostUserLog)) { RTE_LOG(ERR, VHOST_CONFIG, "invalid log base msg size: %"PRId32" != %d\n", msg->size, (int)sizeof(VhostUserLog)); return -1; } size = msg->payload.log.mmap_size; off = msg->payload.log.mmap_offset; RTE_LOG(INFO, VHOST_CONFIG, "log mmap size: %"PRId64", offset: %"PRId64"\n", size, off); /* * mmap from 0 to workaround a hugepage mmap bug: mmap will * fail when offset is not page size aligned. */ addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); if (addr == MAP_FAILED) { RTE_LOG(ERR, VHOST_CONFIG, "mmap log base failed!\n"); return -1; } /* * Free previously mapped log memory on occasionally * multiple VHOST_USER_SET_LOG_BASE. */ if (dev->log_addr) { munmap((void *)(uintptr_t)dev->log_addr, dev->log_size); } dev->log_addr = (uint64_t)(uintptr_t)addr; dev->log_base = dev->log_addr + off; dev->log_size = size; return 0; }
- Patch 2 introduces a vhost_log_write() helper function to log
pages we are gonna change. 对端vm通过同步这些page即可完成状态的迁移。
//rte_vhost_log_write->vhost_log_write static inline void __attribute__((always_inline)) vhost_log_write(struct virtio_net *dev, uint64_t addr, uint64_t len) { uint64_t page; if (likely(((dev->features & (1ULL << VHOST_F_LOG_ALL)) == 0) || !dev->log_base || !len)) return; if (unlikely(dev->log_size <= ((addr + len - 1) / VHOST_LOG_PAGE / 8))) return; /* To make sure guest memory updates are committed before logging */ rte_smp_wmb(); page = addr / VHOST_LOG_PAGE; while (page * VHOST_LOG_PAGE < addr + len) { vhost_log_page((uint8_t *)(uintptr_t)dev->log_base, page); page += 1; } } static inline void __attribute__((always_inline)) vhost_log_page(uint8_t *log_base, uint64_t page) { log_base[page / 8] |= 1 << (page % 8); }
- Patch 3 logs changes we made to used vring.
//rte_vhost_log_used_vring->vhost_log_used_vring static inline void __attribute__((always_inline)) vhost_log_used_vring(struct virtio_net *dev, struct vhost_virtqueue *vq, uint64_t offset, uint64_t len) { vhost_log_write(dev, vq->log_guest_addr + offset, len); }
- Patch 4 sets log_shmfd protocol feature bit, which actually
enables the vhost-user live migration support.
#define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1 #define VHOST_USER_PROTOCOL_FEATURES ((1ULL << VHOST_USER_PROTOCOL_F_MQ) | \ (1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD) |\ (1ULL << VHOST_USER_PROTOCOL_F_RARP) | \ (0ULL << VHOST_USER_PROTOCOL_F_REPLY_ACK) | \ (1ULL << VHOST_USER_PROTOCOL_F_NET_MTU))
RARP报文
构造免费ARP报文RARP来解决vm迁移后的丢包问题
研究拓展
TODO
(ovs+dpdk,SPDK,SFC,DPACC)
网友评论