1、概述
在 QEMU/KVM 中,客户机可以使用的设备大致可分为3种类型:
-
Emulated device:QEMU 纯软件模拟的设备
-
virtio device:实现 VIRTIO API 的半虚拟化驱动的设备
-
PCI device assignment:PCI 设备直接分配
模拟 I/O 设备方式的优点是对硬件平台依赖性较低,可以方便地模拟一些流行的和较老久的设备,不需要宿主机和客户机的额外支持,因此兼容性高;而其缺点是 I/O 路径较长,VM-Exit 次数很多,因此性能较差。一般适用于对I/O性能要求不高的场景,或者模拟一些老旧设备
virtio 半虚拟化设备方式的优点是实现了 VIRTIO API,减少了 VM-Exit 次数,提高了客户机I /O 执行效率,比普通模拟 I/O 的效率高很多;而其缺点是需要客户机中与 virtio 相关驱动的支持,因此兼容性较差,而且 I/O 频繁时 CPU 使用率较高
而第三种是 PCI 设备直接分配,其允许将宿主机中的物理 PCI 设备直接分配给客户机完全使用
1.1、设备直接分配优点
KVM 虚拟机支持将宿主机中的 PCI 设备附加到虚拟化的客户机中,从而让客户机以独占方式访问这个 PCI 设备。通过硬件支持的 VT-d 技术将设备分配给客户机后,在客户机看来,设备是物理上连接在其 PCI 总线上的,客户机对该设备的 I/O 交互操作和实际的物理设备操作完全一样,不需要(或者很少需要)Hypervisor 的参与
1.2、设备直接分配缺点
- 一台服务器主板上的空间比较有限,允许添加的 PCI 设备是有限的,如果一台宿主机上有较多数量的客户机,则很难向每台客户机都独立分配 VT-d 的设备
- 对于使用 VT-d 直接分配了设备的客户机,其动态迁移功能将会受限
1.3、解决方案
- 在一台物理宿主机上,仅对 I/O 性能要求较高的少数客户机使用 VT-d 直接分配设备(如网卡),而对其余的客户机使用纯模拟或使用 virtio,以达到多个客户机共享同一个设备的目的
- 对于网络 I/O 的解决方法,可以选择 SR-IOV,使一个网卡产生多个独立的虚拟网卡,将每个虚拟网卡分别分配给一个客户机使用
2、VT-d设备直接分配
要使用设备直接分配,需要在 BIOS 中设置 VT-d 为 enable
2.1、宿主机内核的配置
[root@kvm ~]# grep IOMMU /boot/config-5.14.0-162.23.1.el9_1.x86_64
CONFIG_IRQ_MSI_IOMMU=y
CONFIG_VFIO_IOMMU_TYPE1=m
CONFIG_VFIO_NOIOMMU=y
CONFIG_IOMMU_IOVA=y
CONFIG_IOMMU_API=y
CONFIG_IOMMU_SUPPORT=y
CONFIG_IOMMU_IO_PGTABLE=y
CONFIG_IOMMU_DEFAULT_DMA_LAZY=y
CONFIG_IOMMU_DMA=y
CONFIG_IOMMU_SVA=y
CONFIG_AMD_IOMMU=y
CONFIG_AMD_IOMMU_V2=m
CONFIG_INTEL_IOMMU=y
CONFIG_INTEL_IOMMU_SVM=y
# CONFIG_INTEL_IOMMU_DEFAULT_ON is not set
CONFIG_INTEL_IOMMU_FLOPPY_WA=y
CONFIG_HYPERV_IOMMU=y
CONFIG_VIRTIO_IOMMU=y
[root@kvm ~]# grep CONFIG_VFIO /boot/config-5.14.0-162.23.1.el9_1.x86_64
CONFIG_VFIO=m
CONFIG_VFIO_IOMMU_TYPE1=m
CONFIG_VFIO_VIRQFD=m
CONFIG_VFIO_NOIOMMU=y
CONFIG_VFIO_PCI_CORE=m
CONFIG_VFIO_PCI_MMAP=y
CONFIG_VFIO_PCI_INTX=y
CONFIG_VFIO_PCI=m
CONFIG_VFIO_MDEV=m
[root@kvm ~]# grep CONFIG_KVM_VFIO /boot/config-5.14.0-162.23.1.el9_1.x86_64
CONFIG_KVM_VFIO=y
2.2、查看VT-d状态
[root@kvm ~]# dmesg | grep -i dmar
[ 0.010620] ACPI: DMAR 0x0000000076D1B560 0000DC (v01 ALASKA A M I 00000001 INTL 20091013)
[ 0.010645] ACPI: Reserving DMAR table memory at [mem 0x76d1b560-0x76d1b63b]
[ 0.095715] DMAR: Host address width 46
[ 0.095716] DMAR: DRHD base: 0x000000fbffd000 flags: 0x0
[ 0.095722] DMAR: dmar0: reg_base_addr fbffd000 ver 1:0 cap 8d2008c10ef0466 ecap f0205b
[ 0.095724] DMAR: DRHD base: 0x000000fbffc000 flags: 0x1
[ 0.095728] DMAR: dmar1: reg_base_addr fbffc000 ver 1:0 cap 8d2078c106f0466 ecap f020df
[ 0.095730] DMAR: RMRR base: 0x000000773e0000 end: 0x000000773f0fff
[ 0.095732] DMAR: ATSR flags: 0x0
[ 0.095733] DMAR: RHSA base: 0x000000fbffc000 proximity domain: 0x0
[ 0.095735] DMAR-IR: IOAPIC id 1 under DRHD base 0xfbffc000 IOMMU 1
[ 0.095737] DMAR-IR: IOAPIC id 2 under DRHD base 0xfbffc000 IOMMU 1
[ 0.095738] DMAR-IR: HPET id 0 under DRHD base 0xfbffc000
[ 0.095739] DMAR-IR: x2apic is disabled because BIOS sets x2apic opt out bit.
[ 0.095740] DMAR-IR: Use 'intremap=no_x2apic_optout' to override the BIOS setting.
[ 0.096394] DMAR-IR: Enabled IRQ remapping in xapic mode
[root@kvm ~]# dmesg | grep -i iommu
[ 0.095735] DMAR-IR: IOAPIC id 1 under DRHD base 0xfbffc000 IOMMU 1
[ 0.095737] DMAR-IR: IOAPIC id 2 under DRHD base 0xfbffc000 IOMMU 1
[ 0.339158] iommu: Default domain type: Translated
[ 0.339158] iommu: DMA domain TLB invalidation policy: lazy mode
-
果只有 DMAR:IOMMU enabled 输出,则需要检查 BIOS 中 VT-d 是否已打开
-
如果 BIOS 中已经开启 VT-d,那么就需要在 grub 引导时开启 iommu
[root@kvm ~]# vim /etc/default/grub GRUB_CMDLINE_LINUX="intel_iommu=on" [root@kvm ~]# grub2-mkconfig -o /boot/grub2/grub.cfg Generating grub configuration file ... Adding boot menu entry for UEFI Firmware Settings ... done [root@kvm ~]# reboot
-
再次查看状态
[root@kvm ~]# dmesg | grep -i dmar [ 0.010953] ACPI: DMAR 0x000000007AD1B560 0000DC (v01 ALASKA A M I 00000001 INTL 20091013) [ 0.010983] ACPI: Reserving DMAR table memory at [mem 0x7ad1b560-0x7ad1b63b] [ 0.024389] DMAR: IOMMU enabled [ 0.110385] DMAR: Host address width 46 [ 0.110444] DMAR: DRHD base: 0x000000fbffd000 flags: 0x0 [ 0.110510] DMAR: dmar0: reg_base_addr fbffd000 ver 1:0 cap 8d2008c10ef0466 ecap f0205b [ 0.110590] DMAR: DRHD base: 0x000000fbffc000 flags: 0x1 [ 0.110653] DMAR: dmar1: reg_base_addr fbffc000 ver 1:0 cap 8d2078c106f0466 ecap f020df [ 0.110732] DMAR: RMRR base: 0x0000007b3e0000 end: 0x0000007b3f0fff [ 0.110796] DMAR: ATSR flags: 0x0 [ 0.110854] DMAR: RHSA base: 0x000000fbffc000 proximity domain: 0x0 [ 0.110919] DMAR-IR: IOAPIC id 1 under DRHD base 0xfbffc000 IOMMU 1 [ 0.110982] DMAR-IR: IOAPIC id 2 under DRHD base 0xfbffc000 IOMMU 1 [ 0.111045] DMAR-IR: HPET id 0 under DRHD base 0xfbffc000 [ 0.111107] DMAR-IR: x2apic is disabled because BIOS sets x2apic opt out bit. [ 0.111108] DMAR-IR: Use 'intremap=no_x2apic_optout' to override the BIOS setting. [ 0.111912] DMAR-IR: Enabled IRQ remapping in xapic mode [ 0.450898] DMAR: No SATC found [ 0.450958] DMAR: IOMMU feature sc_support inconsistent [ 0.451019] DMAR: IOMMU feature dev_iotlb_support inconsistent [ 0.451081] DMAR: dmar0: Using Queued invalidation [ 0.451205] DMAR: dmar1: Using Queued invalidation [ 0.468351] DMAR: Intel(R) Virtualization Technology for Directed I/O [root@kvm ~]# dmesg | grep -i iommu [ 0.000000] Command line: BOOT_IMAGE=(hd0,msdos1)/vmlinuz-5.14.0-162.23.1.el9_1.x86_64 root=UUID=5a905822-9bab-43a4-8132-f909db145f14 ro intel_iommu=on [ 0.024334] Kernel command line: BOOT_IMAGE=(hd0,msdos1)/vmlinuz-5.14.0-162.23.1.el9_1.x86_64 root=UUID=5a905822-9bab-43a4-8132-f909db145f14 ro intel_iommu=on [ 0.024389] DMAR: IOMMU enabled [ 0.110919] DMAR-IR: IOAPIC id 1 under DRHD base 0xfbffc000 IOMMU 1 [ 0.110982] DMAR-IR: IOAPIC id 2 under DRHD base 0xfbffc000 IOMMU 1 [ 0.376363] iommu: Default domain type: Translated [ 0.376409] iommu: DMA domain TLB invalidation policy: lazy mode [ 0.450958] DMAR: IOMMU feature sc_support inconsistent [ 0.451019] DMAR: IOMMU feature dev_iotlb_support inconsistent [ 0.451372] pci 0000:ff:0b.0: Adding to iommu group 0 [ 0.451453] pci 0000:ff:0b.1: Adding to iommu group 0 [ 0.451529] pci 0000:ff:0b.2: Adding to iommu group 0 ...... [ 0.459239] pci 0000:07:00.1: Adding to iommu group 48
2.3、分配示例
2.3.1、分配USB设备到虚拟机
# 查看USB设备bus和device值
[root@kvm ~]# lsusb
Bus 002 Device 002: ID 8087:8000 Intel Corp. Integrated Rate Matching Hub
Bus 004 Device 002: ID 152d:9561 JMicron Technology Corp. / JMicron USA Technology Corp. goreche
......
# 将USB设备分配到虚拟机中
[root@kvm ~]# virt-xml rocky9.1 --add-device --hostdev 004.002 --update
设备热插入成功。
域 'rocky9.1' 成功定义。
# 在虚拟机中查看设备
[root@vm-rocky9 ~]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 149.1G 0 disk
└─sda1 8:1 0 149G 0 part
# 在虚拟机中测试设备
[root@vm-rocky9 ~]# mount /dev/sda1 /mnt
[root@vm-rocky9 ~]# ls /mnt
omv6.qcow2.tar.xz windows10.qcow2.tar.xz windows11.qcow2.tar.xz
# 从虚拟机中移除设备
[root@kvm ~]# virt-xml rocky9.1 --remove-device --hostdev 004.002 --update
设备热拔出成功。
域 'rocky9.1' 成功定义。
注意:如果需要在运行中的虚拟机中插入或移除设备,需要添加 --update
参数;如果是在关闭的虚拟机中添加,就不需要该参数
2.3.2、分配网卡到虚拟机
[root@kvm ~]# lspci | grep -i eth
07:00.0 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)
07:00.1 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)
[root@kvm ~]# virt-xml rocky9.1 --add-device --hostdev 07:00.1 --update
设备热插入成功。
域 'rocky9.1' 成功定义
[root@vm-rocky9 ~]# nmcli
enp1s0:已连接 到 enp1s0
"Red Hat Virtio"
ethernet (virtio_net), 52:54:00:xx:xx:xx, 硬件, mtu 1500
......
enp7s0:已断开
"Intel I350"
ethernet (igb), 00:E0:C0:xx:xx:xx, 硬件, mtu 1500
[root@kvm ~]# virt-xml rocky9.1 --remove-device --hostdev 07:00.1 --update
设备热拔出成功。
域 'rocky9.1' 成功定义。