随着qemu2.1的发布,可以看到,qemu支持了vhost-user。从介绍可以看出,这是把原来vhost-backend从kernel移到了userspace,这和原来virtio架构有什么区别呢?并且这个特性带来了怎样的改进?
virtio
在虚拟机中,可以通过qemu模拟e1000网卡,这样的经典网卡一般各种客户操作系统都会提供inbox驱动,所以从兼容性上来看,使用类似e1000的模拟网卡是非常一个不错的选择。
但是,e1000网卡上也包含了复杂的io端口,寄存器,缓存配置,虚拟机每次收发包都会引起更多的io和mmio操作,使得虚拟机频繁的陷出,最终导致网络性能不佳。
为了解决性能问题,IBM在2005年提出了virtio, 虚拟机中的半虚拟化前端驱动和主机上的后端服务简单的使用virtqueue共享队列交换数据,大幅的减少了e1000模拟时复杂的io操作,从而可以较大程度的提升虚拟网络性能。
作为对比,从下面简单的iperf测试中,我们就能看出两者的性能差距:
With virtio
1 2 3 4 5 6 7 | ------------------------------------------------------------ Client connecting to virtio, TCP port 5001 TCP window size: 16.0 KByte (default) ------------------------------------------------------------ [ 3] local 10.0.1.172 port 54168 connected with 10.0.1.33 port 5001 [ 3] 0.0-10.0 sec 1.34 GBytes 1.15 Gbits/sec |
Without virtio
1
2
3
4
5
6
7
|
------------------------------------------------------------
Client
connecting
to
novirtio,
TCP
port
5001
TCP
window
size:
16.0
KByte
(default)
------------------------------------------------------------
[ 3]
local
10.0.1.172
port
34414
connected
with
10.0.1.13
port
5001
[ 3] 0.0-10.0
sec 375
MBytes 315
Mbits/sec
|
vhost
vhost技术对virtio-net进行了优化,在内核中加入了vhost-net.ko模块,使得对网络数据可以再内核态得到处理。
virtio的io路径
- guest设置好tx;
- kick host;
- guest陷出到kvm;
- kvm从内核切换到用户态的qemu进程;
- qemu将tx数据投递到tap设备;。
vhost的io路径
- guest设置好tx;
- kick host;
- guest陷出到kvm;
- vhost-net将tx数据投递到tap设备;
vhost将部分virio驱动的操作从用户态移到内核态,减少了用户态/内核态切换时间和包的拷贝次数,从而更进一步的提升了性能, 下面的网络延时测试可以看出vhost的优势:
vhost-user
随着技术的发展,将网络数据放入用户态处理将可以得到更灵活的形式。在原有的vhost架构中,进行了改动,增加了 一种新的vhost-backend,叫做vhost-user。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
+---------------+------+--+----------+--+
|
+------+ +----------+ |
|
user | | | | |
|
space
| | | guest
| |
|
| | | | |
| +-+-------+ |
qemu
| |
+-+------+ |
| | snabb | | | |
|
virtio
| |
| |
| | | |
|
driver
| |
| +---------+ +------+ +-+---++---+ |
|
|
|
|
|
| ^ |
|
|
| | v
|
|
v | |
+------------------------------+--+-----+
|nic
+
+-----------------------+
kvm.ko
|
|+---+
+--+-----+
| kernel
|
+---------------------------------------+
|
vhost-user的io路径
- guest设置好tx;
- kick host;
- guest陷出到kvm;
- kvm将通知snabb;
- snabb将tx数据直接发送到nic设备。
vhost-user的提交者是Luke Gorrie,他也是snabbswitch的作者。从图上可以看到vhost-backend从原来咋kernel中的vhost-net 变成了用户空间的snabbswitch。 snabbswitch直接接管物理网卡的驱动,从而直接控制网络信息的输入输出。
snabbswitch主要使用了下面的技术来提高性能
- 采用了大页来作为host和vm之间通信的内存空间
- 用户态操作网卡,使用类似于netmap的zero copy技术来加速对物理设备的访问
- 使用numa技术,加快中断响应速率
值得一提的是使用snabbswitch后,不用再使用原来的tap设备模拟的网卡。
使用vhost-user技术,从虚拟机到host上实现了数据的zero copy(通过大页共享),host到nic的zero copy(snabbswitch实现的驱动),能进一步加快数据的传输。
virtio架构
1, 前端驱动(如virtio_pci, virtio_net)等存在于虚拟机中
2, virtio backend存在于qemu中
3, 中间的两层virtio是队列,virtio-ring是环形缓存区
qemu的vhost-user特性
vhost是一种用户空间进程(vhost client与vhost backend)通过共享内存(virtqueues, unit socket), 文件描述符(ioeventfds), (中断号)irqfds实现快速交换数据的机制。其架构是:
这张是vhost的图:
下面这张图还是vhost的图
下面这张是我在一个ppt里梳理的virtio, vhost, vhost-user, snabb, openonload之间的关系。openonload相当于在snabb的基础上又多出一层用户态的tcp/ip栈
qemu目前支持使用vhost, 如下:
qemu -m 1024 -mem-path /bak/shared,prealloc=on,share=on -netdev type=vhost-user,id=net0,file=/path/to/socket -device virtio-net-pci,netdev=net0
a, -mem-path选项支持为一个虚机分配和其他进程共享内存的guest内在vring, vring是虚机的网卡数据的缓存
b, 再通过unix socket将vring的文件描述符、中断号、IO事件等传给同在用户空间的snabb app进程
c, snabb app进程就可以直接通过文件描述符去ving中取数据了
可以看出,这种机制,是不需要内核中的TAP设备的从而绕开了kernel,也是不需要再走QEMU的内核空间的从而绕开了QEMU.
UserSpace Virtio Application 与 Snabb
userspace virtio app结合了virtio与vhost,snabb是一个个基于userspace virtio app的应用:
a) Snabb用vhost-user(QEMU 2.1的新特性)去和VM通信(所以不再需要tap设备,也不需要Kernel)
b) Snabb用intel10g.lua驱动或OpenOnload提供的驱动(libcuil.so)去和硬件网卡打交道
基架构图如下:
所以对于snabb交换机,我的理解是:
Snabb与ovs的不同是,Snabb是一个userspace virtio application, 它能绕开kernel,和同样是usersapce的VM虚机进程直接通过共享内存实现zero-copy大大地提升性能;另一方面,kernel也会基于vhost技术为每个vring设备分配独立的中断号、事件中断、文件描述符等,并将这些通过unix socket传给其他用户空间进程,这样snabb app并能直接访问虚机网卡的文件描述符。Snabb就相当于一个neutron l2
agent,它会直接调用硬件网卡的相关功能去实现一些neutron软件做的事情,如tagging, security group, routers, firewalls, NATs等,现在这些SRIOV硬件网卡为了和neutron这些软件SDN产品抢市场,它们自己也实现了neutron的那些这功能的,一个例子,开启防火墙:
Ethtool -K ethX ntuple on
ethool -U ethX flow-type tcp4 src-ip 168.0.0.1 dst-ip 192.0.0.1 src-port 1000 dst-port 80 action 0