最近收到使能虚拟网卡多对列(Multiqueues Tap)的需求,坑哧坑哧地搞了两天, 总算是实现了。故在此记录一下。

我理解的网卡多对列是针对VM而言的,它可以使用VM的可用的多个vcpu增加网卡的接收效率,突破 VM的网络性能瓶颈。这边有一篇redhat的文档详细介绍了Multiqueue,感兴趣的小伙伴可以阅读一下。

到我这边,我要做的就是要为VM使用两块虚拟网卡tap0,tap3开启多对列,要求是8个对列。 首先,我启动VM的命令如下:

$ qemu-kvm -name vm1 -smp cpus=8 -m 8192 -drive file=/opt/kvm/vm1.qcow2 \
    -nic,ifname=tap0,script=no,downscript=no,mac=52:54:00:12:34:00 \
    -daemonize

其中,创建tap0使用的是以下命令:

$ tunctl -t tap0 -u root               # 创建虚拟网卡tap0
$ brctl addif br0 tap0                 # 将创建的br0和tap0连通
$ ifconfig tap0 up                     # 让虚拟网卡tap0启动

但是当我用命令ethtool -l eth0去看它可用的对列信息的时候,却发现输出这样的错误信息:

$ ethtool -l eth0
Channel parameters for eth0
Cannot get device channel parameters
: Operation not supported

然后,我根据Multiqueue手册,修改了VM的启动命令:

$ qemu-kvm -name vm1 -smp cpus=8 -m 8192 -drive file=/opt/kvm/vm1.qcow2,if=virtio \
    -nic,ifname=tap0,script=no,downscript=no,mac=52:54:00:12:34:00,vhost=on,model=virtio-net-pci,queues=8 \
    -daemonize

结果VM直接起不来啦。我寻思着是不是queues=8太大了,如果我的tap0不支持多对列的话,应该把 把对列长度改为1,VM就可以起来了。果真如此:

$ qemu-kvm -name vm1 -smp cpus=8 -m 8192 -drive file=/opt/kvm/vm1.qcow2,if=virtio \
    -nic,ifname=tap0,script=no,downscript=no,mac=52:54:00:12:34:00,vhost=on,model=virtio-net-pci,queues=1 \
    -daemonize

进入虚拟机之后,运行:

$ ethtool -l eth0
Pre-set maximums:
RX:             0
TX:             0
Other:          0
Combined:       1
Current hardware settings:
RX:             0
TX:             0
Other:          0
Combined:       1         # current has 1 queues enabled

发现可以了,但是只有一个对列,还是相当于没有开启多对列的功能。于是我想着是不是用tunctl创建的tap设备不 支持多对列,于是我开始调查如何在Host机器上创建支持多对列的tap设备。

我先是调查了tunctl的man page,发现没有关于使能多对列的参数。又看了kernel文档Universal TUN/TAP device driver 并写了程序调用文中给出的tun_alloc_mq函数,但是也没有用。

Google的搜索结果显示,大多数人使能多对列都是通过修改VM的xml配置文件完成的,这个xml 文件又是从virt-manager或virt-install命令创建VM的时候生成的。无奈virt-manager和virt-install在我这边的环境(Kylin V10 OS)总是报错,联系客服也没有结果,问题陷入僵局。

到了第二天,我从virt-manager的创建虚拟设备原理入手开始调查。我发现virt-manager是通过 一种叫做macvtap的东西,根据MacVTap - Linux Virtualization Wikimacvtap是一种传统的tap设备+网桥的等价物,它可以依附于一个实体 网卡,如eth0,并且在创建的时候会自动创建一个tap设备/dev/tapX,然后你用qemu-kvm 起VM的时候可以用-net,tap,fd=3 3<>/dev/tapX来指定tap设备。我尝试的macvtap命令如下:

$ ip link add link eth1 name macvtap0 type macvtap mode bridge # 创建一个bridge模式的macvtap0

$ ip link set set macvtap0 up

VM启动命令为:

$ qemu-kvm -name vm1 -smp cpus=8 -m 8192 -drive file=/opt/kvm/vm1.qcow2,if=virtio \
    -nic,tap,fd=3 3<>/dev/tap405415 \
    -daemonize

tap405415是我这边创建macvtap设备的时候自动生成的tap设备。 结果起来之后,别说开启网卡多对列了,连Host主机也Ping不通了。。。情况再度陷入僵局。

后来我无意中发现,当起VM的时候,如果不传一个已经创建好的tap设备进去,qemu-kvm会 自动创建一个符合条件的tap设备给VM使用。于是我突发奇想,那么我是否可以将对列的 参数都配好,然后让qemu-kvm给我创建一个符合条件的tap设备呢?

于是我的VM启动命令变成了这样:

qemu-kvm -name vm1 -smp cpus=8 -m 8192 \
    -drive file=/opt/kvm/vm1.qcow2,if=virtio \
    -netdev tap,id=dev0,script=no,downscript=no,ifname=tap0,vhost=on,queues=8 \
    -device virtio-net-pci,netdev=dev0,mac=52:54:00:56:78:90,mq=on,vectors=18 \
    -daemonize

创建成功!

我进入VM敲下:

$ ethtool -l eth0
Pre-set maximums:
RX:             0
TX:             0
Other:          0
Combined:       8
Current hardware settings:
RX:             0
TX:             0
Other:          0
Combined:       8         # current has 8 queues enabled

Nice!

不过我发现这样的tap设备虽然可以满足要求,但是它本身并不能连接到我们在 Host机器创建的网桥上,而且用ifconfig tap0发现它也没有UP。

小问题,可以用一个脚本来在VM成功启动之后手动添加,就像这样:

#!/bin/bash

qemu-kvm -name vm1 -smp cpus=8 -m 8192 \
        -drive file=/opt/kvm/vm1.qcow2,if=virtio \
        -netdev tap,id=dev0,script=no,downscript=no,ifname=tap0,vhost=on,queues=8 \
        -device virtio-net-pci,netdev=dev0,mac=52:54:00:56:78:90,mq=on,vectors=18 \
        -daemonize

if [ $? -eq 0 ];then
        sleep 5
        brctl addif br1 tap0
        ifconfig tap0 up
fi

Bingo!大功告成。

以上。