虚拟化外设传输

2023-03-10 13:06:11 浏览数 (4)

虚拟化外设传输

在虚拟化云桌面中,关于外设的使用一直有两种基本功能, 设备的透传(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调用
代码语言:javascript复制
#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文件
代码语言:javascript复制
<hostdev mode='subsystem' type='usb' managed='yes'>
    <source>
        <vendor id='0x413c'/>
        <product id='0x301a'/>
    </source>
</hostdev>

设备重定向(redirection)

设备重定向通常在云桌面中使用,大体分为两种端口重定向和设备重定向。通常使用的为端口重定向,在常见的协议的中,例如spice协议。

端口重定向,通常是走总线驱动虚拟化。

设备重定向,设备驱动虚拟化。

端口重定向

以USB端口为例,下述事SPICE协议(端口重定向)的使用方法。

  • libvirt-xml文件
代码语言:javascript复制
<!- 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
代码语言:javascript复制
#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
代码语言:javascript复制
#自动连接
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数据传输

1 人点赞