虚拟化外设传输
在虚拟化云桌面中,关于外设的使用一直有两种基本功能, 设备的透传(passthrough)与重定向(redirection).
透传:设备位于Host,将主机本身拥有的设备直接交给虚拟机使用。
重定向:远程虚拟机时,设备位于客户端,通过网络,将设备数据传递给虚拟机(guest).
设备透传(passthrough)
设备透传,主要面向IDV场景。或者说 (Host Device Pass-Through,Direct I/O).
设备透传主要依赖硬件支持,cpu/主板芯片组支持VT-D技术,软件层面/BIOS/操作系统支持IO-MMU。当前主要支持基于PCI总线透传。
当前的主要设备透传,USB设备/网卡透传/显卡透传。
主要透传步骤:
代码语言:javascript复制#1.BIOS修改 VT-d 使能检测,未开启,则开启
#2.内核启动参数修改: intel_iommu=on
#3.查找设备地址 lspci 根据设备型号获取设备地址 lspci -n -s address 获取设备 iommu group 地址
#4.验证是否支持 iommu (dmesg |grep -i iommu|grep address) 或者 (readlink pci address)
#5.设置 SR_IOV 的 VF 端口数量
#6.解绑设备 echo "address" > /sys/bus/pci/devices/0000:address/driver/unbind
#7.绑定设备(bind to vfio-pci) (modprobe vfio modprobe vfio-pci)
#8.设置启动参数 (qemu -device vfio-pci,host=address,in=net0)(libvirt 设置xml启动参数)
Egg.显卡透传:
代码语言:javascript复制#!/bin/bash
#
modprobe vfio
modprobe vfio-pci
dev=0000:00:02.0
vendor=`cat /sys/bus/pci/devices/$dev/vendor`
device=`cat /sys/bus/pci/devices/$dev/device`
systemctl restart getty@tty1.service
#关闭显示窗口,解除进程对显卡的占用
sleep 1
rm -f /etc/apparmor.d/libvirt/libvirt-*
echo "$vendor $device" > /sys/bus/pci/drivers/vfio-pci/new_id
echo "0000:00:02.0" > /sys/bus/pci/devices/0000:00:02.0/driver/unbind
echo "0000:00:02.0" > /sys/bus/pci/drivers/vfio-pci/bind
echo "$vendor $device" > /sys/bus/pci/drivers/vfio-pci/remove_id
显卡回挂:
代码语言:javascript复制#!/bin/bash
#
echo -n "0000:00:02.0" > /sys/bus/pci/devices/0000:00:02.0/driver/unbind
echo -n "0000:00:02.0" > /sys/bus/pci/drivers/i915/bind
systemctl restart display-manager #重新启动显示窗口
libvirt 设备透传(鼠标为例)
- API调用
#include <stdio.h>
#include <stdlib.h>
#include <libvirt/libvirt.h>
#include <string.h>
#define MAX_LEN (1024*10)
int main(int argc, char** argv)
{
int res;
virConnectPtr conn;
conn = virConnectOpen("qemu:///system");
if(conn == NULL){
printf("failed to open connection to qemun");
return 1;
}
printf("success to open connection to qemun");
char* xml = malloc(MAX_LEN);
memset(xml,0,MAX_LEN);
FILE* fp = NULL;
fp = fopen(argv[1],"r");
if(fp != NULL){
int len = fread(xml,1,MAX_LEN,fp);
printf("len = %dn",len);
}
if(fp != NULL){
fclose(fp);
}
virDomainPtr dom;
dom = virDomainDefineXML(conn,xml);
if(!dom){
printf("domain definition failedn");
}
char* usbxml = malloc(MAX_LEN);
memset(usbxml,0,MAX_LEN);
FILE* usbfp = NULL;
usbfp = fopen(argv[2],"r");
if(fp != NULL){
int len = fread(usbxml,1,MAX_LEN,usbfp);
printf("len = %dn",len);
}
if(usbfp != NULL){
fclose(usbfp);
}
res = virDomainAttachDeviceFlags(dom,usbxml,VIR_DOMAIN_AFFECT_CURRENT | VIR_DOMAIN_AFFECT_LIVE);
printf("virDomainAttachDeviceFlags res = %dn",res);
return 0;
}
- xml文件
<hostdev mode='subsystem' type='usb' managed='yes'>
<source>
<vendor id='0x413c'/>
<product id='0x301a'/>
</source>
</hostdev>
设备重定向(redirection)
设备重定向通常在云桌面中使用,大体分为两种端口重定向和设备重定向。通常使用的为端口重定向,在常见的协议的中,例如spice协议。
端口重定向,通常是走总线驱动虚拟化。
设备重定向,设备驱动虚拟化。
端口重定向
以USB端口为例,下述事SPICE协议(端口重定向)的使用方法。
- libvirt-xml文件
<!- USB2.0>
<controller type='usb' index='0' model='ich9-ehci1'/>
<controller type='usb' index='0' model='ich9-uhci1'>
<master startport='0'/>
</controller>
<controller type='usb' index='0' model='ich9-uhci2'>
<master startport='2'/>
</controller>
<controller type='usb' index='0' model='ich9-uhci3'>
<master startport='4'/>
</controller>
<redirdev bus='usb' type='spicevmc'/>
<redirdev bus='usb' type='spicevmc'/>
<redirdev bus='usb' type='spicevmc'/>
<!- USB3.0>
<controller type='usb' index='0' model='nec-xhci'/>
<redirdev bus='usb' type='spicevmc'/>
<redirdev bus='usb' type='spicevmc'/>
<redirdev bus='usb' type='spicevmc'/>
- QEMU
#USB2.0
-device ich9-usb-ehci1,id=usb
-device ich9-usb-uhci1,masterbus=usb.0,firstport=0,multifunction=on
-device ich9-usb-uhci2,masterbus=usb.0,firstport=2
-device ich9-usb-uhci3,masterbus=usb.0,firstport=4
-chardev spicevmc,name=usbredir,id=usbredirchardev1
-device usb-redir,chardev=usbredirchardev1,id=usbredirdev1
-chardev spicevmc,name=usbredir,id=usbredirchardev2
-device usb-redir,chardev=usbredirchardev2,id=usbredirdev2
-chardev spicevmc,name=usbredir,id=usbredirchardev3
-device usb-redir,chardev=usbredirchardev3,id=usbredirdev3
#USB3.0
-device nec-usb-xhci,id=usb
-chardev spicevmc,name=usbredir,id=usbredirchardev1
-device usb-redir,chardev=usbredirchardev1,id=usbredirdev1
-chardev spicevmc,name=usbredir,id=usbredirchardev2
-device usb-redir,chardev=usbredirchardev2,id=usbredirdev2
-chardev spicevmc,name=usbredir,id=usbredirchardev3
-device usb-redir,chardev=usbredirchardev3,id=usbredirdev3
- remote-viewer
#自动连接
remote-viewer --spice-usbredir-redirect-on-connect="0x03,-1,-1,-1,0|-1,-1,-1,-1,1"
#热拔插时支持USB设备
remote-viewer --spice-usbredir-auto-redirect-filter="0x03,-1,-1,-1,0|-1,-1,-1,-1,1"
设备重定向
常规的重定向方法通常为端口重定向,但是例如一些设备为USB设备,但是不是使用通用USB设备,就需要使用设备重定向,需要针对开发虚拟机内部的虚拟化设备驱动,虚拟设备驱动层,但需要考虑兼容性问题,不具备通用性,成本也相对比较高。
设备重定向在应用层实现,在虚拟机上创建一个虚拟的USB外设驱动,是的应用程序获得数据。
设备重定向做为端口重定向的补充,优势时数据传输效率更高,延时更低。在使用类似于USB摄像头的场景时,就需要使用USB设备重定向或者其他特定的USB设备时,就需要使用设备重定向。
Egg:
设备重定向在实际业务场景中,还有一些其他关键因素需要考虑,设备的黑白名单,设备热拔插,还有设备重定向的需求中数据传输需要怎么做。
以摄像头为例:
代码语言:javascript复制#USB设备列表获取
#USB热拔插事件监听(libusb/libusbevent/libudev)
#USB黑白名单过滤
#USB重定向
#--摄像头--
#USB摄像头数据获取(视频流编码<硬编>)
#USB数据传输