USB设备直通
创建的虚拟机有时需要使用主机的USB设备,例如U盘或者鼠标之类的,所以研究了下USB设备直通。
实验环境
宿主机系统(host):CentOS Linux release 7.4.1708 (Core)
libvirt:3.2.0
qemu: 2.9.0
USB总线架构
USB(Universal Serial Bus ),通用串行总线,是一种外部总线标准,用于规范电脑与外部设备的连接和通讯。
对于每个USB系统来说,都有一个称为主机控制器的设备,该控制器和一个根 Hub作为一个整体。这个根Hub下可以接多级的Hub,每个子Hub又可以接子Hub。每个USB设备作为一个节点接在不同级别的Hub上。 每条USB总线上最多可以接127个设备。如下图所示:
USB控制器类型
简单地讲,OHCI、UHCI都是USB1.1的接口标准,而EHCI是对应USB2.0的接口标准,最新的xHCI是USB3.0的接口标准。
- OHCI(Open Host Controller Interface)是支持USB1.1的标准,但它不仅仅是针对USB,还支持其他的一些接口,比如它还支持Apple的火线(Firewire,IEEE 1394)接口。与UHCI相比,OHCI的硬件复杂,硬件做的事情更多,所以实现对应的软件驱动的任务,就相对较简单。主要用于非x86的USB,如扩展卡、嵌入式开发板的USB主控。
- UHCI(Universal Host Controller Interface),是Intel主导的对USB1.0、1.1的接口标准,与OHCI不兼容。UHCI的软件驱动的任务重,需要做得比较复杂,但可以使用较便宜、较简单的硬件的USB控制器。Intel和VIA使用UHCI,而其余的硬件提供商使用OHCI。
- EHCI(Enhanced Host Controller Interface),是Intel主导的USB2.0的接口标准。EHCI仅提供USB2.0的高速功能,而依靠UHCI或OHCI来提供对全速(full-speed)或低速(low-speed)设备的支持。
- xHCI(eXtensible Host Controller Interface),是最新最火的USB3.0的接口标准,它在速度、节能、虚拟化等方面都比前面3中有了较大的提高。xHCI支持所有种类速度的USB设备(USB 3.0 SuperSpeed, USB 2.0 Low-, Full-, and High-speed, USB 1.1 Low- and Full-speed)。xHCI的目的是为了替换前面3中(UHCI/OHCI/EHCI)。
USB信息查询
usb信息查询可以通过lsusb命令,如果没有该命令,需要安装usbutils-007-5.el7.x86_64.rpm。
lsusb可以很方便查看到usb设备信息,推荐使用。
[root@localhost ~]# lsusb
Bus 002 Device 005: ID 12d1:0003 Huawei Technologies Co., Ltd.
Bus 002 Device 004: ID 048d:1234 Integrated Technology Express, Inc.
Bus 002 Device 002: ID 8087:8002 Intel Corp.
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 8087:800a Intel Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
每项的含义:
Bus 002
表示第二个USB主控制器(机器上总共有2个usb主控制器 -- 可以通过命令lspci | grep USB查看)
Device 005
表示系统给USB分配的设备号(devnum),同时也可以看到该设备是插入到了第二个usb主控制器
ID 12d1:0003
表示usb设备的ID(这个ID由芯片制造商设置,可以唯一表示该设备,idVendor:idProduct)
Huawei Technologies Co., Ltd.
表示设备的描述信息
用lsusb -t还可以看到USB设备的层级关系
[root@localhost xqk]# lsusb -t
/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/2p, 480M
|__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/8p, 480M
|__ Port 1: Dev 3, If 0, Class=Mass Storage, Driver=usb-storage, 480M
|__ Port 6: Dev 5, If 0, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 6: Dev 5, If 1, Class=Human Interface Device, Driver=usbhid, 12M
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/2p, 480M
|__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/6p, 480M
每项含义:
[root@localhost xqk]# lsusb -t
/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/2p, 480M
|__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/8p, 480M
|__ Port 1: Dev 3, If 0, Class=Mass Storage, Driver=usb-storage, 480M
Bus 02.Port 1
表示第二个USB主控制器,Port号为1
Dev 1, Class=root_hub
分配的设备号为1,类型是root_hub
Driver=ehci-pci/2p
root_hub的类型是ehci(usb 2.0),总共有两个port
|__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/8p, 480M
root_hub的其中一个port有个Hub设备,port的id是1,此Hub有8个port。
|__ Port 1: Dev 3, If 0, Class=Mass Storage, Driver=usb-storage, 480M
Hub的其中一个port有大容量USB设备,port的id为1.3(树状结构,依次以.作为低一级设备成员)
如果没有lsusb或者想确认下lsusb显示内容,可以进入目录
cd /sys/bus/usb/devices/
[root@localhost devices]# ls |grep -v ":" (过滤掉以:分割的文件夹)
1-1
2-1
2-1.1
2-1.6
usb1
usb2
其中数字编号含义为(bus num)-(port num),usb1与usb2,对应控制器中的roothub。
[root@localhost devices]# cd 2-1.6
[root@localhost 2-1.6]# ll
...
-r--r--r-- 1 root root 4096 Mar 9 10:26 devnum(devnum 号)
-r--r--r-- 1 root root 4096 Apr 3 15:07 devpath(port id)
-r--r--r-- 1 root root 4096 Mar 9 10:26 idProduct(即ID 12d1:0003中的0003)
-r--r--r-- 1 root root 4096 Mar 9 10:26 idVendor(即ID 12d1:0003中的12d1)
...
其实lsusb对于root_hub的port指定有些问题,port号应该为0,或许是版本的bug。
USB设备直通
USB控制器添加
对于要添加的usb类型要在虚拟机xml中定义相应的controller,libvirt目前控制器支持为:
<controller type='usb' index='2' model='piix3-uhci'>
<alias name='usb1'/>
</controller>
<controller type='usb' index='1' model='ehci'>
<alias name='usb'/>
</controller>
<controller type='usb' index='0' model='nec-xhci'>
<alias name='usb'/>
</controller>
参数含义:
index:控制器的顺序,优先级0最高,多个控制器时需要指定,一般情况下controller只需要有一个即可。
model:控制器类型,对应类型上文已说明
对于多个controller要慎重,usb会优先匹配index为0的控制器,而不会自动适配相应的控制器。例如插入U盘是2.0,如果第一个controller是1.0,则会挂载到1.0总线下面。
如果设备是usb 1.0/1.1:
<controller type='usb' index='0' model='piix3-uhci'>
<alias name='usb1'/>
</controller>
如果设备是usb 2.0:
<controller type='usb' index='0' model='ehci'>
<alias name='usb'/>
</controller>
如果有多个设备,既有1.0或2.0或者3.0,则用xhci,兼容uhci与ehci:
<controller type='usb' index='0' model='nec-xhci'>
<alias name='usb'/>
</controller>
USB设备直通
其实虚拟机使用USB设备可以通过两种方式:
-
pci设备直通
这种方式其实是通过PCI直通,把整条USB控制器给虚拟机用,这样usb控制器线上的所有设备虚拟机都可以使用,USB控制器的pci相关信息可以通过lspci grep USB查看得到。 [root@localhost vm_work]# lspci |grep USB 00:1a.0 USB controller: Intel Corporation C610/X99 series chipset USB Enhanced Host Controller #2 (rev 05) 00:1d.0 USB controller: Intel Corporation C610/X99 series chipset USB Enhanced Host Controller #1 (rev 05)
属于pci设备直通范围,此篇不做展开讲述。
-
设备直通
设备直通按照libvirt定义规范,在devices下直接添加就行
<hostdev mode='subsystem' type='usb' managed='yes'> <source> <vendor id='0x048d'/> <product id='0x1234'/> <address bus='0' device='2'/> </source> </hostdev>
相关的信息都可以用lsusb查询到。
vendor、product以及address并不需要同时填写,只要有足够的信息确定某一个usb即可。
例如,物理主机没有完全一样的厂家设备时,可以只填写:
<vendor id='0x048d'/> <product id='0x1234'/>
或者
<address bus='0' device='2'/>
qemu支持
qemu支持hostaddr,hostport以及vendorid,productid等信息的组合
(1) vendorid+productid -- match for a specific device, pass it to
the guest when it shows up somewhere in the host.
(2) hostbus+hostport -- match for a specific physical port in the
host, any device which is plugged in there gets passed to the
guest.
(3) hostbus+hostaddr -- most useful for ad-hoc pass through as the
hostaddr isn't stable, the next time you plug in the device it
gets a new one ...
相对于libvirt,多了hostport的支持,hostport即是主机usb的port。
注意点
-
devnum会变化
在host上同一个USB口上插拔U盘,会导致devnum的变化(增加),而port对应于USB的物理口,不会随着插拔U盘而变化,但是libvirt不支持传入port,只能自己适配开发。
-
是否需要开启vt-d
通常情况下,直通物理设备(PCI)时需要开启vt-d,USB设备有些不同,USB设备是由controller控制,而各种类型的控制器都是qemu模拟的,只不过最终直接打开物理机上的USB设备而已,数据还是由qemu控制。因此USB设备直通不需要开启vt-d。
-
No free USB ports
模拟的控制器对应的port数量有限,如果要直通多个USB设备,就会造成port数目不够。piix3-uhci有两个port,ehci与nec-xhci有6个port(一般够用)。当数目不够时,可以添加hub设备来增加额外的port。
<hub type='usb'/>
hub设备包含8个port。
-
不要直通主机hub设备
主机上的root_hub以及hub直通给虚拟机没有任何意义,一方面虚拟机中的root_hub是模拟控制器自带的,而hub是需要单独添加hub设备,另一方面直通hub设备,虚拟机占用主机hub,会导致主机上hub下的USB设备不可用。
参考文档