KVM虚拟化:(十三)设备直接分配

admin 2023年06月06日 660次浏览

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' 成功定义。