一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第28天,点击查看活动详情。
一、有关原理
PCI设备直通,是将PCI-E设备的资源直接分配给虚拟机(或者说子机domU)的意思。使用pci设备直通的前提是主板芯片组和CPU都支持并开启VT-d (Intel Virtualization Technology for Directed I/O)
技术。
VFIO是实现PCI设备直通的一种方法,它可以把设备通过IOMMU映射的DMA物理内存地址映射到用户态中,让用户态程序可以自行操纵数据的传输,还可以自行注册中断处理函数,从而在用户态下实现设备的驱动程序。因此VFIO
的基础是IOMMU
。
IOMMU :input/output memory management unit
的缩写,连接DMA、io、bus和主存,完成从设备虚拟地址到物理地址的映射、以及提供对故障设备的内存保护的功能。
需要注意的是,利用VFIO将PCI设备透传到虚拟机之后,Host将无法使用该设备。
通过把host的device和对应driver解绑,然后绑定在VFIO的driver上,就会在
/dev/vfio/
目录下出现一个group
,这个group
就是IOMMU_GROUP
号,如果需要在该group
上使用VFIO
,需要将该group
下的所有device与其对应的驱动都解绑.
二、显卡直通步骤
这里以笔记本电脑的GeForce GTX 1050 Ti Mobile显卡为例
1.确认主板芯片组和CPU都支持并开启VT-d
如确认主板和CPU都支持VT-d,需要在BIOS中进行开启(也就是设为enable)。
2.确认内核是否开启iommu
因为VFIO的底层依赖IOMMU,因此要使用VFIO,还必须在Linux启动时添加启动项intel_iommu=on
。
- 1.输入
cat /proc/cmdline | grep iommu
,如果没有输出,则需添加intel_iommu=on
到grub的启动参数- 2.输入:
sudo vim /etc/default/grub
- 3.找到:
GRUB_CMDLINE_LINUX_DEFAULT="参数1 参数2 ..."
- 4.修改为
GRUB_CMDLINE_LINUX_DEFAULT="参数1 参数2 ... intel_iommu=on"
,保存退出(wq!)- 5.更新grub:执行
sudo update-grub
命令后重启
$ sudo vim /etc/default/grub #下面是需要修改的行修改后的样子
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on"
3.载入VFIO-PCI
模块
$ sudo modprobe vfio_mdev
这样就产生了 /dev/vfio/vfio 设备,如果加载成功,可以在dmesg中看到相关log。
hollowman@hollowman-F117:~$ dmesg | grep IOMMU
[ 0.033232] DMAR: IOMMU enabled
[ 0.088529] DMAR-IR: IOAPIC id 2 under DRHD base 0xfed91000 IOMMU 1
[ 0.344619] DMAR: IOMMU feature fl1gp_support inconsistent
[ 0.344620] DMAR: IOMMU feature pgsel_inv inconsistent
[ 0.344620] DMAR: IOMMU feature nwfs inconsistent
[ 0.344621] DMAR: IOMMU feature pasid inconsistent
[ 0.344622] DMAR: IOMMU feature eafs inconsistent
[ 0.344623] DMAR: IOMMU feature prs inconsistent
[ 0.344623] DMAR: IOMMU feature nest inconsistent
[ 0.344624] DMAR: IOMMU feature mts inconsistent
[ 0.344624] DMAR: IOMMU feature sc_support inconsistent
[ 0.344625] DMAR: IOMMU feature dev_iotlb_support inconsistent
hollowman@hollowman-F117:~$ dmesg | grep VFIO
[ 0.459612] VFIO - User Level meta-driver version: 0.3
4.查看显卡设备
hollowman@hollowman-F117:~$ lspci | grep VGA
00:02.0 VGA compatible controller: Intel Corporation HD Graphics 630 (rev 04)
01:00.0 VGA compatible controller: NVIDIA Corporation GP107M [GeForce GTX 1050 Ti Mobile] (rev a1)
可以看到主机上有2块显卡,这里选择NVIDIA独显(pci总线的设备id号为01:00.0 )来实践。
5.查看设备所在的IOMMU Group以及 该Group内的所有设备
在做直通的时候,需要将同组内的所有设备全部直通给虚拟机(也就是一个独立完整的设备直通给虚拟机,不能一个设备一部分直通给虚拟机一部分在宿主机),比如刚才上面的显卡包括显卡和声卡,都需要一起直通给虚拟机。
hollowman@hollowman-F117:~$ readlink /sys/bus/pci/devices/0000:01:00.0/iommu_group #查看0000:01:00.0设备的IOMMU Group,可以知道它所在的IOMMU Group是1。
../../../kernel/iommu_groups/1
hollowman@hollowman-F117:~$ ls /sys/bus/pci/devices/0000:01:00.0/iommu_group/devices/ #查看指定设备所在IOMMU Group的所有设备.发现该Group内有3个设备。其中0000:01:00.1是声卡设备。
0000:00:01.0 0000:01:00.0 0000:01:00.1
下面的命令也可以查看设备的IOMMU Group号以及IOMMU Group对应的设备:
hollowman@hollowman-F117:~$ find /sys/kernel/iommu_groups/ -type l
/sys/kernel/iommu_groups/7/devices/0000:00:1c.0
/sys/kernel/iommu_groups/5/devices/0000:00:16.0
/sys/kernel/iommu_groups/3/devices/0000:00:08.0
/sys/kernel/iommu_groups/11/devices/0000:03:00.0
/sys/kernel/iommu_groups/1/devices/0000:00:01.0
/sys/kernel/iommu_groups/1/devices/0000:01:00.0 #这就是上面查到的IOMMU Group组号为1,可以发现1对应的设备只有3个
/sys/kernel/iommu_groups/1/devices/0000:01:00.1
/sys/kernel/iommu_groups/8/devices/0000:00:1c.4
/sys/kernel/iommu_groups/6/devices/0000:00:17.0
/sys/kernel/iommu_groups/4/devices/0000:00:14.2
/sys/kernel/iommu_groups/4/devices/0000:00:14.0
/sys/kernel/iommu_groups/2/devices/0000:00:02.0
/sys/kernel/iommu_groups/10/devices/0000:02:00.0
/sys/kernel/iommu_groups/0/devices/0000:00:00.0
/sys/kernel/iommu_groups/9/devices/0000:00:1f.2
/sys/kernel/iommu_groups/9/devices/0000:00:1f.0
/sys/kernel/iommu_groups/9/devices/0000:00:1f.3
/sys/kernel/iommu_groups/9/devices/0000:00:1f.4
6.将设备与对应的驱动解绑
为了将设备透传到虚拟机中,需要将设备与其对应的驱动解绑,这样该设备就可以使用VFIO的驱动了。注意,不仅要将要透传的设备解绑,还要将与设备同iommu_group的设备都解绑,才能透传成功。 不过,解绑之前需要在hosts主机将显卡驱动禁用掉。
hollowman@hollowman-F117:~$ lspci -vv -s 01:00.0 | grep driver #查看显卡驱动
Kernel driver in use: nvidia
hollowman@hollowman-F117:~$ lspci -vv -s 01:00.1 | grep driver #查看声卡驱动
Kernel driver in use: snd_hda_intel
hollowman@hollowman-F117:~$ sudo vim /etc/modprobe.d/blacklist.conf #修改blacklist.conf文件,写入下面2行,禁用上面查看出来的两个驱动
blacklist nvidia
blacklist snd_hda_intel
在
2.6.13-rc3
内核之前,Linux的设备与驱动之间只能通过insmod(modprobe)
和rmmod
来绑定和解绑,且这种绑定和解绑都是针对驱动和所有设备的。 而从2.6.13-rc3
内核开始,Linux提供了用户空间,可动态的绑定和解绑定设备和设备驱动之间关系的功能,并且可以设置驱动和单个设备之间的联系。
解绑一个设备,只需将设备的pci总线bdf号写入/sys/bus/pci/drivers/驱动名/unbind即可:
hollowman@hollowman-F117:~$ sudo su - root #驱动绑定与解绑必须在root用户模式,sudo都不行
root@hollowman-F117:~# ls /sys/bus/pci/drivers/snd_hda_intel/ #先查看一下驱动目录,0000:01:00.1就表示当前绑定设备id号
0000:00:1f.3 0000:01:00.1 bind module new_id remove_id uevent unbind
root@hollowman-F117:~# echo -n "0000:01:00.1" > /sys/bus/pci/drivers/snd_hda_intel/unbind #解绑设备
root@hollowman-F117:~# ls /sys/bus/pci/drivers/snd_hda_intel/ #再次查看,0000:01:00.1就已经不在了。
0000:00:1f.3 bind module new_id remove_id uevent unbind
解绑显卡驱动同上面的操作。
7.将设备绑定到vfio-pci module
先查看设备的Vendor和DeviceID
root@hollowman-F117:~# lspci -n | grep 01:00.0
01:00.0 0300: 10de:1c8c (rev a1)
root@hollowman-F117:~# lspci -n | grep 01:00.1
01:00.1 0403: 10de:0fb9 (rev ff)
接下来进行绑定操作
root@hollowman-F117:~# echo -n "10de 1c8c" > /sys/bus/pci/drivers/vfio-pci/new_id #绑定显卡
root@hollowman-F117:~# ls /sys/bus/pci/drivers/vfio-pci/ #查看是否增加设备名0000:01:00.0
0000:01:00.0 bind module new_id remove_id uevent unbind
root@hollowman-F117:~# echo -n "10de 0fb9" > /sys/bus/pci/drivers/vfio-pci/new_id #绑定声卡
root@hollowman-F117:~# ls /sys/bus/pci/drivers/vfio-pci/
0000:01:00.0 0000:01:00.1 bind module new_id remove_id uevent unbind
root@hollowman-F117:~# ls /dev/vfio/ #如果绑定成功,/dev/vfio目录下会出现该device所属的iommu_group号.
1 vfio
8.添加硬件到虚拟机
打开kvm,选中虚拟机,添加硬件,在pci主机设备中,找到显卡和声卡添加即可。