Inetl VT-d介绍
Intel VT-d的全称是Intel Virtualization Technology for Direct I/O,它是Intel虚拟化技术的一部分,主要针对的是I/O子系统,它的实现主要是通过在硬件上引入重定向单元,该硬件重定向单元用于对I/O子系统的DMA操作和中断传递进行重定向,从而辅助VMM(Virtual Machine Monitor)实现I/O子系统的虚拟化。
VT-d是一个位于CPU、内存和I/O设备之间的硬件设备,通常位于PCI设备树的根部,或者类似的位于I/O子系统的根部,当VT-d重定向硬件设备启用的时候,它会拦截位于它下面的所有I/O设备产生的中断请求和通过DMA方式对虚拟机内存访问的请求,然后通过查找中断重定向表或者I/O页表的方式(类似分页机制)来重新定位中断转发的目标LAPIC或者是I/O设备访问的目标主机物理内存地址。如下图所示:
KVM虚拟机支持将宿主机的PCI、PCI-E设备附加到虚拟化的客户机中,从而让客户机以独占方式访问这个PCI(或PCI-E)设备,通过硬件支持的VT-d技术将设备分配给客户机后,在客户机看来,设备是物理上连接在其PCI(或PCI-E)总线上的,客户机对该设备的I/O交互和实际的物理设备操作完全一样,不需要(或者很少需要)Hypervisor的参与。在KVM中通过VT-d技术使用一个PCI-E网卡的系统架构示例图如下:
Inetl VT-d使用
首先需要在BIOS当中开启,不同主板的开启方法不同,我的是戴尔的R630,在Processor Settings当中找到Virtualization Technology,设置为Enable就可以了。
如果你的BISO已经开启了,一般来说现在的Linux内核也是默认启用的,使用下面命令就可以查看
[root@localhost ~]# dmesg | grep DMAR
[ 0.008663] ACPI: DMAR 0x000000007BAFE000 000108 (v01 DELL PE_SC3 00000001 DELL 00000001)
[ 0.008686] ACPI: Reserving DMAR table memory at [mem 0x7bafe000-0x7bafe107]
[ 0.943354] DMAR: Host address width 46
[ 0.943356] DMAR: DRHD base: 0x000000fbffc000 flags: 0x0
[ 0.943363] DMAR: dmar0: reg_base_addr fbffc000 ver 1:0 cap 8d2078c106f0466 ecap f020df
[ 0.943364] DMAR: DRHD base: 0x000000c7ffc000 flags: 0x1
[ 0.943367] DMAR: dmar1: reg_base_addr c7ffc000 ver 1:0 cap 8d2078c106f0466 ecap f020df
[ 0.943368] DMAR: ATSR flags: 0x0
[ 0.943369] DMAR: ATSR flags: 0x0
[ 0.943370] DMAR-IR: IOAPIC id 10 under DRHD base 0xfbffc000 IOMMU 0
[ 0.943371] DMAR-IR: IOAPIC id 8 under DRHD base 0xc7ffc000 IOMMU 1
[ 0.943372] DMAR-IR: IOAPIC id 9 under DRHD base 0xc7ffc000 IOMMU 1
[ 0.943373] DMAR-IR: HPET id 0 under DRHD base 0xc7ffc000
[ 0.943374] DMAR-IR: x2apic is disabled because BIOS sets x2apic opt out bit.
[ 0.943374] DMAR-IR: Use 'intremap=no_x2apic_optout' to override the BIOS setting.
[ 0.943959] DMAR-IR: Enabled IRQ remapping in xapic mode
可以看到类似 DMAR-IR: Enabled IRQ remapping in xxxxx mode
的输出即可。
隔离隐藏宿主设备
可以使用vfio_pci这个内核模块来对需要分配给客户机的设备进行隐藏,从而让宿主机和未被分配该设备的客户机都无法使用该设备,达到隔离和安全使用的目的。
查看是否开启
[root@localhost ~]# lsmod |grep vfio_pci
如果没有输出表示还未开启,我们可以通过以下命令开启
[root@localhost ~]# modprobe vfio_pci
[root@localhost ~]# lsmod |grep vfio_pci
vfio_pci 53248 0
vfio_virqfd 16384 1 vfio_pci
vfio 32768 2 vfio_iommu_type1,vfio_pci
irqbypass 16384 2 vfio_pci,kvm
当然如果你的内核比较特殊,即vfio_pci已经被编译到内核当中,而不是作为module,则检查/sys/bus/pci/drivers/vfio_pci是否存在即可。
我们可以通过lspci找到设备的BDF,然后将该设备绑定到vfio_pci驱动,这里我们从pci列表当中随便找一个BDF为00:1a.0的设备,这个设备是网卡相关的。
[root@localhost ~]# lspci -s 01:00.3 -k
01:00.3 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)
Subsystem: Dell Gigabit 4P I350-t rNDC
Kernel driver in use: igb
Kernel modules: igb
可以看到目前是绑定到igb这个驱动上,我们先进行解绑,解绑我们需要知道设备在PCI/PCI-E总线的具体位置
[root@localhost ~]# lspci -s 01:00.3 -Dn
0000:01:00.3 0200: 8086:1521 (rev 01)
依次是domain(0000)、bus(01)、slot(00)、function(3),其中domain的值一般为0(如果机器有多个host bridge时,其取值范围是0~0xffff),bus的取值范围是0~0xff,slot取值范围是0~0x1f,function取值范围是0~0x7,其中后三个值一般简称为BDF(即bus:device:function)。
解绑
[root@localhost ~]# echo 0000:01:00.3 > /sys/bus/pci/drivers/igb/unbind
[root@localhost ~]# lspci -s 01:00.3 -k
01:00.3 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)
Subsystem: Dell Gigabit 4P I350-t rNDC
Kernel modules: igb
绑定到vfio_pci上
[root@localhost ~]# echo 0000:01:00.3 > /sys/bus/pci/drivers/vfio-pci/bind
[root@localhost ~]# lspci -s 01:00.3 -k
01:00.3 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)
Subsystem: Dell Gigabit 4P I350-t rNDC
Kernel driver in use: vfio_pci
Kernel modules: igb
然后我们可以通过以下命令查看qemu当中的vfio_pci驱动的应用。
[root@localhost ~]# qemu-system-x86_64 -device vfio-pci,help
vfio-pci options:
addr=<int32> - Slot and optional function number, example: 06.0 or 06 (default: -1)
bootindex=<int32>
display=<OnOffAuto> - on/off/auto (default: "off")
failover_pair_id=<str>
host=<str> - Address (bus/device/function) of the host device, example: 04:10.0
multifunction=<bool> - on/off (default: false)
rombar=<uint32> - (default: 1)
romfile=<str>
sysfsdev=<str>
x-balloon-allowed=<bool> - (default: false)
x-enable-migration=<bool> - (default: false)
x-igd-gms=<uint32> - (default: 0)
x-igd-opregion=<bool> - on/off (default: false)
x-intx-mmap-timeout-ms=<uint32> - (default: 1100)
x-msix-relocation=<OffAutoPCIBAR> - off/auto/bar0/bar1/bar2/bar3/bar4/bar5 (default: "off")
x-no-geforce-quirks=<bool> - (default: false)
x-no-kvm-intx=<bool> - (default: false)
x-no-kvm-ioeventfd=<bool> - (default: false)
x-no-kvm-msi=<bool> - (default: false)
x-no-kvm-msix=<bool> - (default: false)
x-no-mmap=<bool> - (default: false)
x-no-vfio-ioeventfd=<bool> - (default: false)
x-nv-gpudirect-clique=<uint4> - NVIDIA GPUDirect Clique ID (0 - 15)
x-pci-device-id=<uint32> - (default: 4294967295)
x-pci-sub-device-id=<uint32> - (default: 4294967295)
x-pci-sub-vendor-id=<uint32> - (default: 4294967295)
x-pci-vendor-id=<uint32> - (default: 4294967295)
x-pcie-extcap-init=<bool> - on/off (default: true)
x-pcie-lnksta-dllla=<bool> - on/off (default: true)
x-pre-copy-dirty-page-tracking=<OnOffAuto> - on/off/auto (default: "on")
x-req=<bool> - on/off (default: true)
x-vga=<bool> - on/off (default: false)
xres=<uint32> - (default: 0)
yres=<uint32> - (default: 0)
其中 addr=<int32> - Slot and optional function number, example: 06.0 or 06 (default: -1)
属性表示设备在客户机中的PCI的slot编号(即BDF的D-device的值), host=<str> - Address (bus/device/function) of the host device, example: 04:10.0
属性host表示的是指定分配的PCI设备在宿主机中的地址(BDF号)
使用的时候使用类似下述命令就可以使用vfio_pci来独占设备了。
[root@localhost ~]# qemu-system-x86_64 -enable-kvm -smp 4 -m 8G rhel7.3.img -device vfio-pci,host=01:00.3.addr=00