【51CTO.com快译】Linux PCI子系统是Linux内核中最重要的子系统之一。本文介绍了使用QEMU来仿真不同的PCI/PCIe配置,以帮助研究Linux PCI子系统。这种能力便于Linux管理员或开发人员研究、调试和开发Linux内核,因为使用QEMU定制PCI/PCIe配置要容易得多。比如说,结合SeaBIOS源代码,研究PCI初始化和探测过程会容易得多。此外,与重启裸机服务器相比,重启QEMU/KVM虚拟机的速度也快得多。
就本文中的所有示例而言,KVM虚拟机运行Oracle Linux 8,虚拟机内核版本为5.10.0,QEMU版本为 5.2.0。
所有示例都将启动盘(ol8.qcow2)作为默认IDE来运行。由于本文的目的是研究PCI/PCIe,我们以virtio-scsi-pci HBA为例,不会将任何SCSI LUN连接到HBA。参阅之前的这篇博文,即可了解如何将SCSI LUN连接到 virtio-scsi-pci HBA。
文章重点介绍使用QEMU和PCI/PCIe,不涉及有关PCI/PCIe规范的任何基础知识。
PCI桥
这部分演示如何通过PCI-2-PCI桥创建PCI辅助总线。辅助总线通过“pci-bridge”来创建。
- qemu-system-x86_64 -machine pc,accel=kvm -vnc :8 -smp 4 -m 4096M \
- -net nic -net user,hostfwd=tcp::5028-:22 \
- -hda ol8.qcow2 -serial stdio \
- -device pci-bridge,id=bridge0,chassis_nr=1 \
- -device virtio-scsi-pci,id=scsi0,bus=bridge0,addr=0x3 \
- -device pci-bridge,id=bridge1,chassis_nr=2 \
- -device virtio-scsi-pci,id=scsi1,bus=bridge1,addr=0x3 \
- -device virtio-scsi-pci,id=scsi2,bus=bridge1,addr=0x4
上述QEMU命令行创建两条PCI辅助总线。一条辅助总线(01:00.0)有一个virtio-scsi-pci HBA(01:03.0),第二条辅助总线(02:00.0)有两个virtio-scsi-pci HBA(02:03.0和02:04.0)。
- [root@vm ~]# lspci
- 00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02)
- 00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]
- 00:01.1 IDE interface: Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II]
- 00:01.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 03)
- 00:02.0 VGA compatible controller: Device 1234:1111 (rev 02)
- 00:03.0 Ethernet controller: Intel Corporation 82540EM Gigabit Ethernet Controller (rev 03)
- 00:04.0 PCI bridge: Red Hat, Inc. QEMU PCI-PCI bridge
- 00:05.0 PCI bridge: Red Hat, Inc. QEMU PCI-PCI bridge
- 01:03.0 SCSI storage controller: Red Hat, Inc. Virtio SCSI
- 02:03.0 SCSI storage controller: Red Hat, Inc. Virtio SCSI
- 02:04.0 SCSI storage controller: Red Hat, Inc. Virtio SCSI
下列lspci输出和数字描述了该示例的PCI总线拓扑结构。
- [root@vm ~]# lspci -t
- [0000:00]-+-00.0
- +-01.0
- +-01.1
- +-01.3
- +-02.0
- +-03.0
- +-04.0-[01]----03.0
- \-05.0-[02]--+-03.0
- \-04.0
图1
PCI根总线
这部分演示如何通过“轻量级”PXB(PCI扩展桥)主机桥创建额外的PCI根总线。它是QEMU命令行中的“pxb”。它仅针对i440fx实现,只可以放在总线0上。
- qemu-system-x86_64 -machine pc,accel=kvm -vnc :8 -smp 4 -m 4096M \
- -net nic -net user,hostfwd=tcp::5028-:22 \
- -hda ol8.qcow2 -serial stdio \
- -device pxb,id=bridge1,bus=pci.0,bus_nr=3 \
- -device virtio-scsi-pci,bus=bridge1,addr=0x3 \
- -device pxb,id=bridge2,bus=pci.0,bus_nr=8 \
- -device virtio-scsi-pci,bus=bridge2,addr=0x3 \
- -device virtio-scsi-pci,bus=bridge2,addr=0x4
上述QEMU命令行创建两条额外的PCI 根总线。第一条根总线(04:00.0)有一个virtio-scsi-pci HBA(04:03.0),第二条根总线(09:00.0)有二个virtio-scsi-pci HBA(09:03.0和09:04.0)。
- [root@vm ~]# lspci
- 00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02)
- 00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]
- 00:01.1 IDE interface: Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II]
- 00:01.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 03)
- 00:02.0 VGA compatible controller: Device 1234:1111 (rev 02)
- 00:03.0 Ethernet controller: Intel Corporation 82540EM Gigabit Ethernet Controller (rev 03)
- 00:04.0 Host bridge: Red Hat, Inc. QEMU PCI Expander bridge
- 00:05.0 Host bridge: Red Hat, Inc. QEMU PCI Expander bridge
- 03:00.0 PCI bridge: Red Hat, Inc. QEMU PCI-PCI bridge
- 04:03.0 SCSI storage controller: Red Hat, Inc. Virtio SCSI
- 08:00.0 PCI bridge: Red Hat, Inc. QEMU PCI-PCI bridge
- 09:03.0 SCSI storage controller: Red Hat, Inc. Virtio SCSI
- 09:04.0 SCSI storage controller: Red Hat, Inc. Virtio SCSI
下列lspci输出和数字描述了该示例的PCI总线拓扑结构。
- [root@vm ~]# lspci -t
- +-[0000:08]---00.0-[09]--+-03.0
- | \-04.0
- +-[0000:03]---00.0-[04]----03.0
- \-[0000:00]-+-00.0
- +-01.0
- +-01.1
- +-01.3
- +-02.0
- +-03.0
- +-04.0
- \-05.0
图2
PCIe根联合体
这部分演示如何通过额外的根联合体(Root Complex)创建额外的PCIe根总线。据QEMU源代码显示,PCIe特性只由x86架构上的“q35”机器类型和AArch64上的“virt”机器类型所支持。根联合体通过在QEMU命令行上使用“pxb-pcie”来创建。
- qemu-system-x86_64 -machine q35,accel=kvm -vnc :8 -smp 4 -m 4096M \
- -net nic -net user,hostfwd=tcp::5028-:22 \
- -hda ol8.qcow2 -serial stdio \
- -device pxb-pcie,id=pcie.1,bus_nr=2,bus=pcie.0 \
- -device ioh3420,id=pcie_port1,bus=pcie.1,chassis=1 \
- -device virtio-scsi-pci,bus=pcie_port1 \
- -device ioh3420,id=pcie_port2,bus=pcie.1,chassis=2 \
- -device virtio-scsi-pci,bus=pcie_port2 \
- -device pxb-pcie,id=pcie.2,bus_nr=8,bus=pcie.0 \
- -device ioh3420,id=pcie_port3,bus=pcie.2,chassis=3 \
- -device virtio-scsi-pci,bus=pcie_port3
上述QEMU命令行创建两个额外的PCIe根联合体。第一个根联合体有一个virtio-scsi-pci HBA(09:00.0),第二个根联合体有两个virtio-scsi-pci HBA(03:00.0和04:00.0)。
- [root@vm ~]# lspci
- 00:00.0 Host bridge: Intel Corporation 82G33/G31/P35/P31 Express DRAM Controller
- 00:01.0 VGA compatible controller: Device 1234:1111 (rev 02)
- 00:02.0 Ethernet controller: Intel Corporation 82574L Gigabit Network Connection
- 00:03.0 Host bridge: Red Hat, Inc. QEMU PCIe Expander bridge
- 00:04.0 Host bridge: Red Hat, Inc. QEMU PCIe Expander bridge
- 00:1f.0 ISA bridge: Intel Corporation 82801IB (ICH9) LPC Interface Controller (rev 02)
- 00:1f.2 SATA controller: Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA Controller [AHCI mode] (rev 02)
- 00:1f.3 SMBus: Intel Corporation 82801I (ICH9 Family) SMBus Controller (rev 02)
- 02:00.0 PCI bridge: Intel Corporation 7500/5520/5500/X58 I/O Hub PCI Express Root Port 0 (rev 02)
- 02:01.0 PCI bridge: Intel Corporation 7500/5520/5500/X58 I/O Hub PCI Express Root Port 0 (rev 02)
- 03:00.0 SCSI storage controller: Red Hat, Inc. Virtio SCSI (rev 01)
- 04:00.0 SCSI storage controller: Red Hat, Inc. Virtio SCSI (rev 01)
- 08:00.0 PCI bridge: Intel Corporation 7500/5520/5500/X58 I/O Hub PCI Express Root Port 0 (rev 02)
- 09:00.0 SCSI storage controller: Red Hat, Inc. Virtio SCSI (rev 01)
下列lspci输出和数字描述了该示例的PCIe拓扑结构。
- [root@vm ~]# lspci -t
- -+-[0000:08]---00.0-[09]----00.0
- +-[0000:02]-+-00.0-[03]----00.0
- | \-01.0-[04]----00.0
- \-[0000:00]-+-00.0
- +-01.0
- +-02.0
- +-03.0
- +-04.0
- +-1f.0
- +-1f.2
- \-1f.3
图3
PCI交换器
这部分演示了如何创建PCIe交换器。
- qemu-system-x86_64 -machine q35,accel=kvm -vnc :8 -smp 4 -m 4096M \
- -net nic -net user,hostfwd=tcp::5028-:22 \
- -hda ol8.qcow2 -serial stdio \
- -device ioh3420,id=root_port1,bus=pcie.0 \
- -device x3130-upstream,id=upstream1,bus=root_port1 \
- -device xio3130-downstream,id=downstream1,bus=upstream1,chassis=9 \
- -device virtio-scsi-pci,bus=downstream1 \
- -device xio3130-downstream,id=downstream2,bus=upstream1,chassis=10 \
- -device virtio-scsi-pci,bus=downstream2
上述QEMU命令行创建了一个PCIe交换器,它连接有两个virtio-scsi-pci HBA。上游端口连接至根总线,而每个下游端口连接至virtio-scsi-pci HBA(03:00.0和04:00.0)。
- [root@vm ~]# lspci
- 00:00.0 Host bridge: Intel Corporation 82G33/G31/P35/P31 Express DRAM Controller
- 00:01.0 VGA compatible controller: Device 1234:1111 (rev 02)
- 00:02.0 Ethernet controller: Intel Corporation 82574L Gigabit Network Connection
- 00:03.0 PCI bridge: Intel Corporation 7500/5520/5500/X58 I/O Hub PCI Express Root Port 0 (rev 02)
- 00:1f.0 ISA bridge: Intel Corporation 82801IB (ICH9) LPC Interface Controller (rev 02)
- 00:1f.2 SATA controller: Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA Controller [AHCI mode] (rev 02)
- 00:1f.3 SMBus: Intel Corporation 82801I (ICH9 Family) SMBus Controller (rev 02)
- 01:00.0 PCI bridge: Texas Instruments XIO3130 PCI Express Switch (Upstream) (rev 02)
- 02:00.0 PCI bridge: Texas Instruments XIO3130 PCI Express Switch (Downstream) (rev 01)
- 02:01.0 PCI bridge: Texas Instruments XIO3130 PCI Express Switch (Downstream) (rev 01)
- 03:00.0 SCSI storage controller: Red Hat, Inc. Virtio SCSI (rev 01)
- 04:00.0 SCSI storage controller: Red Hat, Inc. Virtio SCSI (rev 01)
下列lspci输出和数字描述了该示例的PCIe拓扑结构。
- [root@vm ~]# lspci -t
- -[0000:00]-+-00.0
- +-01.0
- +-02.0
- +-03.0-[01-04]----00.0-[02-04]--+-00.0-[03]----00.0
- | \-01.0-[04]----00.0
- +-1f.0
- +-1f.2
- \-1f.3
图4
IOMMU
现在,IOMMU始终被裸机所使用。QEMU能够仿真IOMMU,帮助开发人员调试和研究Linux内核IOMMU相关源代码以及DMA重映射和中断重映射的工作原理。
下列QEMU命令行演示了如何为虚拟机创建英特尔IOMMU(启用中断重映射)。除了QEMU命令行外,还应该将“intel_iommu=on”添加到虚拟机Linux内核命令行的末尾。
- qemu-system-x86_64 -machine q35,accel=kvm,kernel-irqchip=split -vnc :8 -smp 4 -m 4096M \
- -net nic -net user,hostfwd=tcp::5028-:22 \
- -hda ol8.qcow2 -serial stdio \
- -device nvme,drive=nvme0,serial=deadbeaf1,max_ioqpairs=4 \
- -drive file=disk1.qcow2,if=none,id=nvme0 \
- -device intel-iommu,intremap=on
据虚拟机系统日志显示,IOMMU可用,并被Linux内核启用。
- [root@vm ~]# dmesg | egrep "iommu|IOMMU"
- ... ...
- [ 0.019828] DMAR: IOMMU enabled
- [ 0.203209] DMAR-IR: IOAPIC id 0 under DRHD base 0xfed90000 IOMMU 0
- [ 0.628348] iommu: Default domain type: Passthrough
- [ 1.078994] pci 0000:00:00.0: Adding to iommu group 0
- [ 1.079892] pci 0000:00:01.0: Adding to iommu group 1
- [ 1.080775] pci 0000:00:02.0: Adding to iommu group 2
- [ 1.081654] pci 0000:00:03.0: Adding to iommu group 3
- [ 1.082545] pci 0000:00:1f.0: Adding to iommu group 4
- [ 1.083432] pci 0000:00:1f.2: Adding to iommu group 4
- [ 1.084315] pci 0000:00:1f.3: Adding to iommu group 4
原文标题:A study of the Linux kernel PCI subsystem with QEMU,作者:Dongli Zhang
【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】