背景
Linux 作为 Guest OS 已经被多个虚拟化系统支持,比如Xen、KVM、VMWare、Lguest等。为了提升虚拟化的性能,CPU和内存的虚拟化已经从全虚拟化发展到使用VT-x、EPT等硬件辅助虚拟化技术,但是IO外设的虚拟化进展并不快,目前业界使用Guest有感的半虚拟化技术。但是每一个虚拟化平台都想要有自己的半虚拟化网络、块设备、console 驱动程序,这些驱动程序大都功能重复且很多没有很好的优化,这带来了较大的维护代价。因此,需要构建一个半虚拟化的统一模型,来解决设备虚拟化的性能以及分裂问题。
KVM 是必须依赖硬件虚拟化技术辅助(例如 Intel VT-x、AMD-V)的 Hypervisor:
• CPU:有 VMX root 和 non-root 模式的支持,其运行效率是比较高的
• 内存:有 Intel EPT/AMD NPT 的支持,内存虚拟化的效率也比较高
• I/O:KVM 客户机的 I/O 操作需要VM-Exit到用户态由 QEMU 进行模拟。传统的方式是使用纯软件的方式来模拟 I/O 设备,效率并不高。
virtio 是 Linux 平台下一种 I/O 半虚拟化框架,由澳大利亚程序员 Rusty Russell 开发。他当时的目的是支持自己的虚拟化解决方案 Lguest,而在 KVM 中也广泛使用了 Virtio 作为半虚拟化 I/O 框架。
virtio就是这个统一的半虚拟化模型,提供一系列维护良好的Linux驱动程序,通过Shim层适配不同的Hypervisor。virito中提供了一个成为vring的环形缓冲区实现,可以为Guest和Hypervisor提供高效的传输机制。将vring传输和设备配置结合就可以模拟一个PCI设备,这时如果Guest要支持一个新的设备,只需要基于这套virtio框架在Guest实现一个PCI驱动程序,并在Hypervisor中的虚拟设备中增加vring的支持即可。
virtio提供两个通用的 ABI,Virtqueue和 Linux API for virtual IO device,以及提供虚拟设备方便的 feature 协商机制以及维持向后兼容性。
架构
Virtio架构氛围三层,第一层是Gust中的Frontend驱动,中间层是Virtqueue vring数据交换,第三层是Backend驱动,执行真正的设备模拟逻辑。
vring数据交换层使用used vring和avail vring以及一个描述符表实现数据交换。Tx方向,Guest将数据填入avail ring,通知virtio设备,virtio设备设置used ring,并执行设备Tx操作。Rx方向,virtio设备将数据填入avail vring,通知Guest,Guest设置used ring,并执行驱动逻辑。
PCI抽象
当一个virtio设备被发现后,driver的probe函数就会被调用,参数中的virtio_device中有一个virtio_config_ops的指针,驱动可以使用它来进行设备配置。PCI 设备配置操作分成以下几个部分:
-
读写 feature bits;
-
读写配置空间;
-
读写 status bits;
-
Device reset;
-
Virtqueue 的创建和销毁
virtio_config_ops的定义如下:
struct virtio_config_ops {
bool (*feature)(struct virtio_device *vdev, unsigned bit);
void (*get)(struct virtio_device *vdev, unsigned offset, void *buf, unsigned len);
void (*set)(struct virtio_device *vdev, unsigned offset, const void *buf, unsigned len);
u8 (*get_status)(struct virtio_device *vdev);
void (*set_status)(struct virtio_device *vdev, u8 status);
void (*reset)(struct virtio_device *vdev);
struct virtqueue *(*find_vq)(struct virtio_device *vdev, unsigned index, void (*callback)(struct virtqueue *));
void (*del_vq)(struct virtqueue *vq);
};
Feature Bits
定义了设备支持的功能,例如 VIRTIO_NET_F_CSUM bit 表示网络设备是否支持 checksum offload。feature bits 机制提供了未来扩充功能的灵活性,以及兼容旧设备的能力。
配置空间
一般通过一个数据结构和一个虚拟设备关联,Guest 可以读写此空间。
Status bits
这是一个 8 bits 的长度,Guest 用来标识 device probe 的状态,当 VIRIO_CONFIG_S_DRIVE_OK 被设置,那么 Guest 已经完成了 feature 协商,可以跟 host 进行数据交互了。
Device reset
重置设备,配置和 status bits。
Virtqueue 创建和销毁
find_vq 提供了分配 virtqueue 内存,和 Host 的 IO 空间的初始化操作。
virtqueues抽象
virtqueues 主要包含了数据传输的操作。一个虚拟设备可能有一个 virtqueue,或者多个。例如 virtio-blk 只有一个 virtqueue,而 virtio-net/virtio-console 有两个 virtqueue,一个用于输入,一个用于输出。这是由于场景的不同:
-
对于 virtio-blk/virtio-scsi,对于读写,IO 发起方都是 Guest OS,所以发起方在进行 IO 操作的时候,均可以通过调用下面的 add_buf,在 avail ring 里面放置请求;
-
对于输入输出的驱动,例如 virtio-net,驱动需要随时准备好接受网络数据的缓冲区,也就是说需要提前准备好 avail ring,所以,需要单独占用一个 virtqueue,提前填满空的请求,hypervisor 在收到数据包之后,可以立即放置到接收的 virtqueue 中,并通知 Guest OS。
同时,为了性能,Qemu 和 Guest driver 可以支持为 virtio-blk 创建多个 virtqueues,来支持 multi queue 特性(注:需要块层的 blk-mq 支持)。
上图是virtio的核心架构图,其中IO的核心是virtqueue和virtqueue_ops,具体定义如下:
struct virtqueue_ops {
int (*add_buf)(struct virtqueue *vq, struct scatterlist sg[], unsigned int out_num, unsigned int in_num, void *data);
void (*kick)(struct virtqueue *vq);
void *(*get_buf)(struct virtqueue *vq, unsigned int *len);
void (*disable_cb)(struct virtqueue *vq);
bool (*enable_cb)(struct virtqueue *vq);
};
上述五个操作,定义了 virtuque 的 5 个操作,分成 2 类:
-
IO 机制实现:add_buf,get_buf
-
通知机制实现:kick,disable_cb,enable_cb
Guest OS driver 初始化 Virtqueue 以及提交一个标准的 IO 流程是:
-
Driver 初始化 virtqueue 结构,调用 find_vq,传入 IO 完成时的回调函数;
-
准备请求,调用 add_buf;
-
Kick 通知后端有新的请求(通过 iowrite 操作来写 PCI 对应的 IO 空间,触发 VMEXIT从而陷出到KVM),Qemu/KVM 后端处理请求,先进行地址转换,然后提取数据以及操作,提交给设备;
-
请求完成,Qemu/KVM 写 IO 空间触发提前定义好的 MSI 中断,进而进入到 VM,Guest OS 回调被调用,接着 get_buf 被调用,一次 IO 到此全部处理完成;
Split Virtqueues数据结构
virtio 1.1 之前的virtqueue只有split virtqueue,在1.1之后为了支持硬件加速增加了packed virtqueue。
Split virtqueue 顾名思义,queue 会有多个 ring,分别是 available ring、descriptor ring 和 used ring,每条记录都通过next 指针来标识下一条记录,它们在物理内存上是连续的,这样方便寻址和映射。Avail 存放已经有数据的 desc 的 idx,Used 存放已经完成的 desc 的 idx,各自都有一个头指针和尾指针,来表示可以消费的区间。这种方案对于硬件实现来说,指针的跳转、分别放置的数据都会带来开销比较高的 pci transaction,不利于性能提高。
virtio-blk
Guest放置请求
当Guest virtio-blk的driver使用 add_buf 添加一个请求后,描述符变化成下面的结构:
一次virtio-blk的io过程主要有三个关键的元信息:
-
读写的设备的偏移:virtio_blk_outhdr 头描述了读写类型、优先级和偏移,占用一个只读描述符项。
-
数据源或者目的地 buffer:Iovec 描述了 buffer 空间的地址和长度,支持 scatter 和 gather IO。对于每一段 scatter/gather 空间,都占用一个描述符项。
-
操作完成状态:Virtio_blk_inhdr 头描述了 IO 的状态,占用一个描述符项(下图中的status)。
上图的 Data 数组是用来存放请求的指针,作 callback 用。在处理完成事件时,通过 get_buf 可以拿到这个指针,然后可以执行完成相关的上层回调。
请求的头部,以及状态和数据部分,是不同的地址区间,依次填充到可用的描述符表里面,并标记是读或者写。最后在添加了新的请求后,avail table 里面会添加了一条记录,指向整个请求的第一个描述符的 index。实际上一个请求,占用的描述符项的数量等于(2 + scatter-gather list 的长度),这种结构有很好的扩展性,可以描述任意类型的 IO 请求。
此后通过写 PCI 的 IO 空间来触发 notify 操作,Host 检查 last_avail_idx 跟 avail->idx 来判断有多少请求需要处理。notify 也会触发 KVM 的 VMEXIT 事件,造成较大开销。virtio 可以利用 flags 以及 features 来控制双向的 notify 频率,降低 VMEXIT 的调用,提高性能。
地址转换
virtio 的一个目标就是提高虚拟设备性能,就要消除数据拷贝,采用共享内存的方式访问数据。virtio 的 virtqueue 在 Guest 和 Host 之间是共享的,但是由于在两个不同的地址空间,一个是 Guest Physical Address, 一个是 Host Virtual Address。virtqueue 在 Guest 端构建并初始化,Host 端只需要经过地址转换来建立对应的 virtqueue 结构即可,不用再重新初始化 virtqueue 结构。
Virtio 在初始化的时候进行第一步的地址映射:
-
Guest 构建好 desc table;
-
通过写 PCI IO 空间 VIRTIO_PCI_QUEUE_PFN 来告知 Host ,Guest 的 virtqueue 的 GPA 地址;
-
Host 收到了 GPA,然后转换成 Host 的虚拟地址。
因为 Host 是 Qemu 后端,Qemu 给虚拟机提供了内存,所以它知道 Guest OS 的物理地址范围。Qemu 根据自己记录的信息,可以将 gpa 转换成 hva。
Guest 收到完成通知
Host 在处理完请求之后,将 desc 的 head 编号放到 used table 里面,然后构造 irq,通过 ioctl 通知 KVM,有请求完成了。在 Guest driver 初始化的时候,提前注册了PCI 的 irq 的 handler。handler 调用 get_buf 来获取 last_used_idx 到 used->idx 区间已经完成的请求,从 data 数组里面找到 request 的指针,调用对应的回调即可。
至此,虚拟化设备的数据通路走通了,没有数据拷贝,高效的实现了数据在 Guest 和 Host 的传递。Guest 里面的 driver 一般叫做前端,Host 里面对应的虚拟硬件叫做后端。
virtio-networking
virtio已经演变为VM访问诸如块设备和网卡的标准开放接口,virito-net是一个虚拟以太网卡,是virtio中最复杂的一个设备。虚拟化主要有三大基础组件:
在讨论virtio-networking的时候,我们拆为两层进行讨论:
-
控制面:用于Guest和Host之间交换协商数据面的建立与终止
-
数据面:用于Guest和Host之间真正的数据交换
两层有不同的需求,控制面更注重于灵活性,便于支持不同的设备和厂商;数据面更注重于性能,更快的在Guest和Host间交换数据。
virtio可以分为两部分:
-
virtio spec:virtio spec由OASIS组织维护,定义了在Guest和Host之间如何创建一个控制面和数据面。
-
vhost protocol:协议中允许virtio数据面可以卸载到其他组件(比如用户进程或者是内核模块)来提升性能。
virtio的控制面实现在QEMU进程中,但是数据面并不是,这是由于性能的考虑。如果virtio的数据面也实现在QEMU进程中,每一个数据包的发送都需要增加KVM.ko到QEMU的上下文切换,并再次写Tap进入内核空间,有更多的拷贝和上下文切换。vhost是将数据面卸载到内核模块中实现,这样就可以直接在内核模块中直接实现KVM.ko到Tap的读写,减少了上下文切换和拷贝。vhost的实现实际上包括内核态的vhost-net和用户态的vhost-user方案。
virtio spec包括device和driver,典型实现中Hypervisor通过一些TransPort方式暴露virtio设备给Guest,Guest中看到跟物理设备一样的设备。PCI和PCIe总线是最通用的TransPort方式,设备提供MMIO访问方式,将设备内存映射到Guest内存地址空间。真正的PCI硬件使用一个特殊的物理内存地址区间来作为配置设备配置空间,在VM中Hypervisor捕获对这些内存区间的访问,执行设备模拟,暴露跟物理机一样的内存layout并提供一样的应答。
Guest启动的时候,使用PCI/PCIe自动发现机制,virito设备可以通过PCI vendor ID和Device ID来识别自己,之后内核根据识别结果加载相应的驱动,Linux内核中已经包括了virtio驱动。virtio驱动必须能够分配Hypervisor和Device都能够访问和读写的内存区域,数据面使用这些内存区域进行数据交换,控制面处理它们的初始化。
virtqueue是virtio设备批量数据交换的机制,它包含一个都是Guest分配内存的Queue,并定义了双向通知机制:
-
Avail Buffer通知:用于driver来通知Device有Buffer已经Ready需要进行处理
-
Used Buffer通知:用于Device来通知Driver它已经完成了一些Buffer的处理
在PCI场景中,Guest通过写特定的内存地址来发送Avail Buffer通知,Device(比如QEMU)使用vCPU中断来发送Used Buffer通知。virtio spec也允许动态的enable和disable通知,这样设备和驱动可以实现批量的Buffer通知,甚至是一直Polling,这样来实现更好的传输速率。
原始virtio-net后端
当virtio-net驱动发送网络数据包时,会将数据放置于Available Ring中之后,会触发一次通知(Notification)。这时QEMU会接管控制,将此网络包传递到TAP设备。接着QEMU将数据放于Used Ring中,并发出一次通知,这次通知会触发虚拟中断的注入。虚拟机收到这个中断后,就会到Used Ring中取得后端已经放置的数据。至此一次发送操作就完成了。接收网络数据包的行为也是类似,只不过这次virtio-net驱动是将空的buffer放置于队列之中,以便后端将收到的数据填充完成而已。
virtio的io路径:
-
guest设置好tx;
-
kick host;
-
guest陷出到kvm;
-
kvm从内核切换到用户态的qemu进程;
-
qemu将tx数据投递到tap设备;
vhost-net
在vhost出现之前,virtio的控制面和数据面都在QEMU用户态进程中实现。任何数据转发必须通过QEMU进程,QEMU进程模拟IO访问。vhost 是 virtio 的内核设备模拟实现方案,通常virtio 主机端的驱动是实现在用户空间的 qemu 中。vhost将virtio模拟代码直接放入内核中,这样设备模拟代码可以直接调用内核子系统,而不用通过QEMU进程再进行系统调用。从 guest 到 kvm.ko 的交互只有一次用户态的切换以及数据拷贝,性能会高一些。
vhost-net/virtio-net架构包含如下组件:
-
vhost-net作为backend跑在Host内核空间
-
virtio-net作为frontend跑在Guest内核空间
vhost-net驱动会创建一个/dev/vhost-net的字符设备,这个字符设备作为配置vhost-net实例的接口。当QEMU以-netdev tap,vhost=on启动的时候,它会打开/dev/vhost-net并使用ioctl系统调用来初始化实例,实现QEMU进程与vhost-net实例的关联,准备virtio特性协商,将Guest的物理地址映射到vhost-net驱动中。在初始化过程中,vhost驱动会创建一个内核线程vhost-$pid,其中$pid为QEMU的进程ID。这个线程被称为vhost worker线程。worker线程的目的就是为了处理IO事件,加速设备模拟。
vhost并不模拟完整的virtio PCI设备,只进行virtqueue的操作,依然需要QEMU来执行virtio特性协商和热迁移等。这意味着vhost驱动不是一个完整的virtio设备实现,用户空间处理控制面,内核空间处理数据面。vhost工作线程等待virtqueue通知,并处理放置在virtqueue中的缓存。在vhost-net中就是把包从tx virtqueue中取出,并通过tap文件描述符转发出去。文件描述的polling也是由vhost工作线程实现的。当tap文件描述符上有包到达时,vhost-net工作线程会被唤醒,它将包放入rx virtqueue中,这样Guest就能收到了。
vhost 与 virtio 前端的通信主要采用一种事件驱动 eventfd 的机制来实现,guest 通知 vhost 的事件要借助 kvm.ko 模块来完成,vhost 初始化期间,会启动一个工作线程 work 来监听 eventfd,一旦 guest 发出对 vhost 的 kick event,kvm.ko 触发 ioeventfd 通知到 vhost,vhost 通过 virtqueue 的 avail ring 获取数据,并设置 used ring。同样,从 vhost 工作线程向 guest 通信时,也采用同样的机制,只不过这种情况发的是一个回调的 call event,kvm.ko 触发 irqfd 通知 guest。整个vhost实例只需要知道Guest内存映射、kick eventfd和call eventfd。
vhost-net仍然通过读写TAP设备来与外界进行数据包交换。对比最原始的virtio网络实现,控制平面在原有的基础上转变为vhost协议定义的ioctl操作(对于前端而言仍是通过PCI传输层协议暴露的接口),基于共享内存实现的Vring转变为virtio-net与vhost-net共享,数据平面的另一方转变为vhost-net,并且前后端通知方式也转为基于eventfd的实现。
vhost的io路径:
-
guest设置好tx;
-
kick host;
-
guest陷出到kvm;
-
kvm将通知vhost-net工作线程;
-
vhost-net将tx数据投递到tap设备;
虚拟机与本机和其他物理机上的其他虚拟机通信是通过类似Open vSwitch (OVS)之类的软件交换机实现的。OVS是一个软件实现的交换机,在内核中进行包的转发,它包括两部分:
-
用户空间:包括一个数据库(ovsdb-server)和一个OVS守护进程用于管理和控制switch(ovs-vswitchd)
-
内核空间:OVS内核模块用于转发面的数据包转发
vhost-user
vhost-net方案对于不同 host 之间的通信,或者Guest到Host NIC之间的通信是比较好的,但是对于某些用户态进程间的通信,比如数据面的通信方案,OpenvSwitch 和与之类似的 SDN 的解决方案,Guest 需要和Host 用户态的 vSwitch 进行数据交换,如果采用 vhost 的方案,Guest 和 Host 之间又存在多次的上下文切换和数据拷贝,为了避免这种情况,业界就想出将 vhost 从内核态移到用户态。这就是 vhost-user 的实现。
vhost的架构并不与KVM绑定,vhost是一个用户空间接口,并不依赖于KVM内核模块。vhost-user 和 vhost 的实现原理是一样,都是采用 vring 完成共享内存,eventfd 机制完成事件通知。不同在于 vhost 实现在内核中,而 vhost-user 实现在用户空间中,用于用户空间中两个进程之间的通信,其采用共享内存的通信方式。vhost-user 基于 C/S 的模式,采用 UNIX 域套接字(UNIX domain socket)来完成进程间的事件通知和数据交互,相比 vhost 中采用 ioctl 的方式,vhost-user 采用 socket 的方式大大简化了操作。
vhost-user基于 vring 这套通用的共享内存通信方案。他的提交者是Luke Gorrie,也是snabbswitch的作者: vhost-backend从原来kernel中的vhost-net 变成了用户空间的snabbswitch,snabbswitch直接接管物理网卡的驱动,从而直接控制网络信息的输入输出。snabbswitch主要使用了下面的技术来提高性能:
-
采用了大页来作为host和vm之间通信的内存空间
-
用户态操作网卡,使用类似于netmap的zero copy技术来加速对物理设备的访问
-
使用numa技术,加快中断响应速率
如果使用 qemu 作为 vhost-user 的 server 端实现,在启动 qemu 时,我们需要指定 -mem-path 和 -netdev 参数,如:
qemu -m 1024 -mem-path /hugetlbfs,prealloc=on,share=on \ -netdev type=vhost-user,id=net0,file=/path/to/socket \ -device virtio-net-pci,netdev=net0
指定 -mem-path 意味着 qemu 会在 guest OS 的内存中创建一个文件,share=on 选项允许其他进程访问这个文件,也就意味着能访问 Guest OS 内存,达到共享内存的目的。
-netdev type=vhost-user 指定通信方案,file=/path/to/socket 指定 socket 文件。
当 qemu 启动之后,首先会进行 vring 的初始化,并通过 socket 建立 C/S 的共享内存区域和事件机制,然后 client 通过 eventfd 将 virtio kick 事件通知到 server 端,server 端同样通过 eventfd 进行响应,完成整个数据交互。
vhost-user的io路径
-
guest设置好tx;
-
kick host;
-
guest陷出到kvm;
-
kvm将通知vhost-backend;
-
vhost-backend将tx数据直接发送到nic设备。
SR-IOV
术语
MMU:
TLB:
IOMMU:
vIOMMU:
VT-d:
SRIOV:
VFIO:
Scalable IO:
对于SR-IOV用于Guest实例的场景,需要解决的一个问题是如何进行映射内存,并通过NIC进行高效的数据包收发。这里有两种方法解决这个问题,要么采用Guest Kernel Driver,要么采用Guest DPDK PMD Driver。下图是使用DPDK PMD Driver的示例。
Data Plane跟供应商绑定,直接进入VF。对于SRIOV,供应商NIC驱动需要在Host内核(PF Driver)和Guest用户空间(VF PMD)同时存在来支持整个方案。Host内核驱动和Guest用户空间PMD驱动不直接通信,PF/VF驱动使用其他接口进行配置,比如Host PF驱动通过libvirt进行配置。Guest用户空间的VF PMD驱动用于配置VF,Host内核空间的PF驱动用于管理整个NIC。(Host上的主要工作是:vf bind 至 vfio,指定下 pci addr 给 libvirt passthrough给虚机,也就是host侧vf用的是vfio,虚机内用的是guest kernel 的 vf driver,虚机启动后就没有控制面就不需要额外做了)
虽然这种方式能够实现Guest线速访问NIC,但是也有一些局限性。Guest中的驱动需要跟物理网卡匹配,如果物理网卡的FW升级了,Guest中驱动也可能需要进行升级。NIC更换供应商的话,Guest中的驱动也需要进行升级。对于VM热迁移,必须要在相同供应商,相同软硬件版本才可以支持。为了解决这些问题,亟需要使用virtio这种通用标准的接口。
VirtIO Full HW Offload
VirtIO Full HW Offload是将virtio的data plane和control plane都卸载到硬件,这依赖于硬件NIC支持virtio协议,比如发现、特性协商、建立/终止数据面等。硬件NIC也需要支持virtio的ring内存布局,这样当内存完成映射之后,NIC和Guest之间就能直接通信了。
这种方案Guest可以直接与NIC通过PCI通信,在Host内核上就不需要额外的驱动了。这就需要NIC厂商在NIC上完整的实现virtio的协议,包括通常在Host OS上使用软件实现的控制面,现在就需要实现在NIC上了。下图是Virtio硬件全卸载的示意图:
控制面非常的复杂,需要跟IOMMU和vIOMMU进行交互。为了简化流程,Host Kernel、QEMU进程和Guest Kernel都需要加一些东西。
vDPA
vDPA是Virtual data path acceleration的简写,它本质上是一种使用virtio ring布局的标准化NIC SRIOV数据面,在Host上增加一个通用的控制面和软件架构,在Guest使用virtio驱动,与供应商解耦。鉴于它是SRIOV之上的抽象层,它还可以支持Scalable IOV等新兴技术。与Virtio全硬件卸载数据面类似,Guest使用virtio驱动能够直接与NIC通信,但是每个NIC厂商还可以继续使用它们自己的驱动,在内核中增加一个通用的vDPA驱动将NIC驱动/控制面翻译到virtio控制面。vDPA方案相比virtio全硬件卸载方案只需要NIC在支持virtio ring布局做出较少的改动就能在数据面上达到同样的线速。
vDPA方案有一些潜在的优势:
-
开放的协议标准,不必绑定任何一家供应商
-
线速性能,跟SRIOV差不多,中间没有mediator或者是translator
-
硬件新技术的潜力,可以支持Scalable IOV等新技术
-
统一驱动,在Guest中使用统一的virtio驱动,不必关注底层NIC厂商和驱动
-
透明保护,Guest使用一个virtio接口,后端可以使用2个接口实现透明保护。例如vDPA NIC断开连接,Host Kernel能够快速检测出来,并自动切换到另一个比如vhost-net的后端上
-
热迁移,Guest中采用标准的Ring布局,可以实现不同厂商NIC和版本之间的热迁移
-
为容器提供标准的加速接口
-
裸机场景下,可以将virtio-net作为统一NIC驱动,配合内核中的vDPA软件架构可以实现统一NIC驱动来适配不同物理硬件NIC(就像存储设备的NVMe驱动一样)
对比
下表是四种VM网络虚拟化方案的对比:
百度云现状
CDS最开始修改QEMU,与CDS-Agent通过fifo进行通信。目前切到了vhost-user方式,直接使用共享内存方式,并尝试将接口切到SPDK。VPC最开始使用vhost-net配合ovs-kernel。目前切到了vhost-user方式,重构OVS-DPDK的数据面形成百度云自己的BVS,并尝试使用CX5进行硬件卸载。
IOMMU
参考
virtio 虚拟化系列之一:从 virtio 论文开始 - 知乎
Deep dive into Virtio-networking and vhost-net
https://open.tech2real.com/info_detail_page?id=40763#index