主机显卡直通到KVM虚拟机

4,458 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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主机设备中,找到显卡和声卡添加即可。

添加硬件.png