1. 博客/

qemu-kvm虚拟机配置NVMe直通

·849 字·4 分钟
Kvm Nvme Qemu

概述
#

PCI设备直通(PCI Passthrough)是指将物理PCI设备像网卡、存储卡等直接分配给虚拟机,而不是通过模拟或仿真的方式。优点是性能提升显著,完全消除了虚拟化开销,可充分发挥物理设备的硬件能力,如GPU直接执行计算任务。

虚拟机配置设备直通依赖CPU VT-d虚拟化技术。

VT-d是Intel的I/O虚拟化技术。它允许虚拟机直接与硬件设备通信,而无需宿主机操作系统的介入,从而提高性能并减少对CPU的依赖。VT-d技术包括集成的I/O内存管理单元(IOMMU),支持DMA重映射和中断重映射。 Intel VT-d 的关键特性包括:

  1. 设备直通:允许虚拟机直接访问和控制物理硬件设备,无需经过宿主机的操作系统或内核驱动程序,这样可以减少性能开销。
  2. IOMMU 支持:VT-d 包括一个集成的 I/O 内存管理单元(IOMMU),它负责将设备的 I/O 地址空间映射到虚拟机的地址空间,同时保证内存访问的安全性和隔离性。
  3. 中断重映射:VT-d 支持将设备的硬件中断重映射到虚拟机,使得虚拟机可以像使用本地中断一样响应硬件事件。
  4. DMA 重映射:VT-d 允许将设备的直接内存访问(DMA)操作重映射到虚拟机的地址空间,这样虚拟机就可以直接与设备进行数据交换。
  5. 安全性:通过隔离每个虚拟机的 I/O 操作,VT-d 提高了系统的安全性,确保虚拟机之间的操作不会相互干扰。
  6. 兼容性:VT-d 支持广泛的硬件设备,包括但不限于网络卡、存储控制器和图形处理器。
  7. 性能:通过减少虚拟机与硬件设备通信的延迟,VT-d 有助于提高虚拟化环境的整体性能。
  8. 灵活性:VT-d 允许虚拟机使用与物理机相同的设备驱动程序,从而简化了虚拟机的驱动程序开发和维护。

IOMMU(Input/Output Memory Management Unit,输入/输出内存管理单元)是一种计算机硬件组件,它在计算机的操作系统和硬件设备之间提供一种抽象层,用于管理设备的内存访问。IOMMU的主要功能是将设备的I/O地址空间映射到系统的物理内存地址空间,从而允许多个设备安全地访问内存,而不会相互冲突或破坏系统的内存完整性。IOMMU是保证虚拟机直接访问硬件设备,同时保持隔离和安全性的关键组件。

测试机配置
#

本次测试机CPU型号是Intel Xeon 4214, 官网查看这个型号支持VT-d虚拟化技术

登录服务器,dmesg可以看到"DMAR"(Direct Memory Access Remapping)的信息,说明VT-d功能已经开启

本次测试机没有NVMe磁盘,所以使用嵌套 qemu-kvm虚拟机来模拟NVMe设备直通

nested-kvm

创建L1虚拟机
#

创建L1虚拟机nested-kvm并虚拟一块NVMe硬盘,并使用virt-install工具启动,关键参数:

参数备注
–machine q35
–qemu-commandline="-machine q35,accel=kvm,kernel-irqchip=split"
–qemu-commandline="-device intel-iommu,intremap=on,device-iotlb=on"

L1-kvm支持设备直通需 开启vIOMMU
–cpu=host-passthrough
–vcpus sockets=1,cores=8,threads=2,placement=‘static’,cpuset=16-23,40-47
L1-kvm使用测试机CPU
cpuset指定cpus避免跨NUMA
–disk path=img-nested.qcow2已创建好的Centos7 base镜像
–network bridge=bridge0测试机已创建好bridge0设备
virt-install \
    --machine q35 \
    --virt-type=kvm \
    --name=nested-kvm \
    --feature kvm_hidden=on \
    --vcpus sockets=1,cores=8,threads=2,placement='static',cpuset=16-23,40-47 \
    --cpu=host-passthrough \
    --memory=32768 \
    --disk path=img-nested.qcow2,size=50,format=qcow2,cache=none,bus='virtio' \
    --network bridge=bridge0,model='virtio',driver_name='vhost',driver_queues="8" \
    --network bridge=bridge0,model='virtio',driver_name='vhost',driver_queues="8" \
    --qemu-commandline="-machine q35,accel=kvm,kernel-irqchip=split" \
    --qemu-commandline="-device intel-iommu,intremap=on,device-iotlb=on" \
    --qemu-commandline="-drive file=/kvm/nested/nvme-em/nvme.raw,if=none,id=nvm,format=raw" \
    --qemu-commandline="-device nvme,drive=nvm,serial=1234" \
    --nographics \
    --print-xml  >   nested-kvm.xml 

virsh define nested-kvm.xml
virsh start nested-kvm

配置L1虚拟机
#

开启IOMMU
#

登录L1-kvm,配置内核参数intel_iommu=on

内核参数intel_iommu=on是在启动Linux系统时传递给内核的一个选项,用于启用Intel的虚拟化技术,特别是与I/O相关的虚拟化特性,即Intel VT-d(Virtualization Technology for Directed I/O)

#dmesg可以看到L1-kvm已经支持VT-d
[root@L1-kvm] ~$ dmesg -T|grep DMAR
[Thu Jul 25 18:26:40 2020] ACPI: DMAR 0x000000007FFE2596 000050 (v01 BOCHS  BXPCDMAR 00000001 BXPC 00000001)
[Thu Jul 25 18:26:40 2020] ACPI: Reserving DMAR table memory at [mem 0x7ffe2596-0x7ffe25e5]
[Thu Jul 25 18:26:40 2020] DMAR: Host address width 39
[Thu Jul 25 18:26:40 2020] DMAR: DRHD base: 0x000000fed90000 flags: 0x1
[Thu Jul 25 18:26:40 2020] DMAR: dmar0: reg_base_addr fed90000 ver 1:0 cap d2008c22260206 ecap f00f5e
[Thu Jul 25 18:26:40 2020] DMAR: ATSR flags: 0x1
[Thu Jul 25 18:26:40 2020] DMAR-IR: IOAPIC id 0 under DRHD base  0xfed90000 IOMMU 0
[Thu Jul 25 18:26:40 2020] DMAR-IR: Queued invalidation will be enabled to support x2apic and Intr-remapping.
[Thu Jul 25 18:26:40 2020] DMAR-IR: Enabled IRQ remapping in x2apic mode

[root@L1-kvm] ~$ grubby --update-kernel ALL --args intel_iommu=on
[root@L1-kvm] ~$ grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
Adding boot menu entry for UEFI Firmware Settings ...
done
[root@L1-kvm] ~$ reboot

重启后登录,dmesg可以看到L1-kvm已开启IOMMU

[root@L1-kvm] ~$ dmesg -T
[Thu Jul 25 19:25:34 2020] DMAR: IOMMU enabled
[Thu Jul 25 19:25:34 2020] DMAR: Intel(R) Virtualization Technology for Directed I/O

安装qemu-kvm
#

配置bond0和bridge0,并安装libvirt、qemu-kvm

[root@L1-kvm] ~$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state UP group default qlen 1000
    link/ether 52:54:00:21:e5:c6 brd ff:ff:ff:ff:ff:ff
    altname enp1s0
3: eth1: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state UP group default qlen 1000
    link/ether 52:54:00:21:e5:c6 brd ff:ff:ff:ff:ff:ff permaddr 52:54:00:68:07:f5
    altname enp2s0
4: bridge0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 52:54:00:21:e5:c6 brd ff:ff:ff:ff:ff:ff
    inet 192.168.9.131/24 brd 192.168.9.255 scope global noprefixroute bridge0
       valid_lft forever preferred_lft forever
    inet6 fe80::7e35:f05c:bd47:a713/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
5: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 1500 qdisc noqueue master bridge0 state UP group default qlen 1000
    link/ether 52:54:00:21:e5:c6 brd ff:ff:ff:ff:ff:ff

[root@L1-kvm] ~$ yum install libvirt libguestfs-tools qemu-kvm virt-install -y -q
[root@L1-kvm] ~$ systemctl start libvirtd

创建L2虚拟机
#

获取L1的NVMe盘BDF
#

[root@L1-kvm] ~$ lspci -Dnn|grep -i NVM|awk '{print $1}'
0000:00:02.0

[root@L1-kvm] ~$ lspci  -k -s 00:02.0
02:00.0 Non-Volatile memory controller: Red Hat, Inc. QEMU NVM Express Controller (rev 02)
	Subsystem: Red Hat, Inc. Device 1100
	Kernel driver in use: nvme
	Kernel modules: nvme

BDF(Bus Device Function)是PCI设备的唯一标识符。0000:00:02.0表示

PCI标识符字段含义
0000Domain用于区分不同PCI系统
00Bus表示PCI设备挂接在哪一条PCI总线上,范围从0开始编号
02Device在同一条总线上,用来区分不同的PCI设备,从0开始编号
0Function表示多功能设备的不同功能,一般PCI设备功能号为0

启动L2虚拟机
#

创建L2虚拟机nvmetest,并使用virt-install工具启动,参数--hostdev pci_0000_00_02_0配置PCI设备直通

[root@L1-kvm] ~$ virsh nodedev-list --cap pci
pci_0000_00_00_0
pci_0000_00_01_0
pci_0000_00_01_1
pci_0000_00_01_2
pci_0000_00_01_3
pci_0000_00_01_4
pci_0000_00_01_5
pci_0000_00_02_0
pci_0000_00_1d_0
pci_0000_00_1d_1
pci_0000_00_1d_2
pci_0000_00_1d_7
pci_0000_00_1f_0
pci_0000_00_1f_2
pci_0000_00_1f_3
pci_0000_01_00_0
pci_0000_02_00_0
pci_0000_03_00_0
pci_0000_04_00_0
pci_0000_05_00_0

[root@L1-kvm] ~$ virsh nodedev-dumpxml pci_0000_00_02_0
<device>
  <name>pci_0000_00_02_0</name>
  <path>/sys/devices/pci0000:00/0000:00:02.0</path>
  <parent>computer</parent>
  <driver>
    <name>nvme</name>
  </driver>
  <capability type='pci'>
    <class>0x010802</class>
    <domain>0</domain>
    <bus>0</bus>
    <slot>2</slot>
    <function>0</function>
    <product id='0x0010'>QEMU NVM Express Controller</product>
    <vendor id='0x1b36'>Red Hat, Inc.</vendor>
    <iommuGroup number='7'>
      <address domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
    </iommuGroup>
    <numa node='0'/>
    <pci-express/>
  </capability>
</device>

[root@L1-kvm] ~$ virt-install \
    --virt-type=kvm \
    --name=nvmetest  \
    --vcpus=4 \
    --memory=8192 \
    --disk path=nvmetest.qcow2,size=50,format=qcow2,cache=none,bus='virtio' \
    --network bridge=bridge0,model='virtio',driver_name='vhost',driver_queues="2" \
    --hostdev pci_0000_00_02_0 \
    --hvm \
    --nographics \
    --os-variant=centos7 \
    --print-xml  >   nvmetest.xml

[root@L1-kvm] ~$ virsh define nvmetest.xml
[root@L1-kvm] ~$ virsh start nvmetest


# L2虚拟机配置直通后,L1-kvm已经看不到nvme盘了
# vfio-pci驱动管理 https://www.kernel.org/doc/Documentation/vfio.txt
[root@L1-kvm] ~$ lspci -k -s 00:02.0
00:02.0 Non-Volatile memory controller: Red Hat, Inc. QEMU NVM Express Controller (rev 02)
	Subsystem: Red Hat, Inc. Device 1100
	Kernel driver in use: vfio-pci
	Kernel modules: nvme
[root@L1-kvm] ~$ nvme list
Node                  Generic               SN                   Model                                    Namespace  Usage                      Format           FW Rev
--------------------- --------------------- -------------------- ---------------------------------------- ---------- -------------------------- ---------------- --------

登录L2-kvm
#

可以看到NVMe盘,挂载后正常使用

[root@L1-kvm] ~$ virsh console nvmetest

[root@L2-kvm] ~$ lspci -Dnn|grep NVM
0000:05:00.0 Non-Volatile memory controller [0108]: Red Hat, Inc. QEMU NVM Express Controller [1b36:0010] (rev 02)

[root@L2-kvm] ~$ nvme list
Node                  Generic               SN                   Model                                    Namespace  Usage                      Format           FW Rev
--------------------- --------------------- -------------------- ---------------------------------------- ---------- -------------------------- ---------------- --------
/dev/nvme0n1          /dev/ng0n1            1234                 QEMU NVMe Ctrl                           0x1        107.37  GB / 107.37  GB    512   B +  0 B   1.0
[root@L2-kvm] ~$ mkfs.xfs /dev/nvme0n1
meta-data=/dev/nvme0n1           isize=512    agcount=4, agsize=6553600 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=1    bigtime=1 inobtcount=1
data     =                       bsize=4096   blocks=26214400, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=12800, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
[root@L2-kvm] ~$ mkdir /data
[root@L2-kvm] ~$ mount /dev/nvme0n1 /data
[root@L2-kvm] /data$ df -Th /data
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme0n1   xfs   100G  746M  100G   1% /data

Related

使用QEMU虚拟NVMe设备
·330 字·2 分钟
Kvm Nvme Qemu
httpd+svn服务器部署
·1757 字·9 分钟
Svn Httpd Openssl
kvm虚拟机磁盘扩容
·2036 字·10 分钟
Kvm Linux WindowsServer