1 介绍
本文档对如何使用PCIE/PCI设备给出好的例子并解释了背后的原因。
注意PCIE特性仅在X86架构上使用q35机器类型和AArch64架构使用virt机器类型时有效。其他机器类型目前不支持PCIE。
下列文档可以与本文档一起阅读:
注意:使用的例子并不能代替完整的文档,请使用QEMU帮助来了解所有选项。
2 设备放置策略
QEMU并没有一个清晰的socket-device匹配机制,它允许任何PCI/PCIe设备被插入到任何PCI/PCIe插槽里。
插入一个PCI设备到PCIE插槽可能有时不能工作,因为对于裸机不能工作。
将一个PCIE设备插入到PCI插槽将隐藏掉扩展配置空间,这也是不推荐的。
推荐的是区分PCIE和PCI层次。PCIE设备仅插入到PCIE root port和PCIE downstream port中。
2.1 Root Bus(pcie.0)
仅下列类型的设备可以直接防止在RC上:
(1)PCIE设备(比如网卡,图像卡,IDE控制器),非控制器。仅将传统的PCI设备放置到RC上。这可以是集成Endpoint(注意集成Endpoint不是可插拔的)。
虽然PCIE手册不禁止PCIE设备是集成Endpoint,但目前存在的硬件几乎都集成了传统的PCI设备到RC。当PCIE设备被集成到RC时,Guest OS可能行为会很奇怪。
(2)PCIE root ports(比如ioh3420),用于开启专门的PCIE层次。
(3)PCE to PCI桥(pcie-pci-bridge),用于开启传统的PCI层次。
(4)额外RC(pxb-pcie),如果需要多个PCIE根总线。
2.1.1 将一个设备插入到pcie.0上作为RC集成Endpoint如下:
-device <dev>[,bus=pcie.0]
2.1.2 暴露一个新下PCIE根总线如下:
-device pxb-pcie,id=pcie.1,bus_nr=x[,numa_node=y][,addr=z]
它链接到pcie.1总线上:
-device ioh3420,id=root_port1[,bus=pcie.1][,chassis=x][,slot=y][,addr=z]
-device pcie-pci-bridge,id=pcie_pci_bridge1,bus=pcie.1
2.2 PCIE层次
通常使用PCIE root port来开始PCIE层次。
一个PCIE根总线最大支持32个设备。因为每个PCIE root port为一个function且多function设备可以达到8个设备,每个PCIE根总线上最大PCIE root port为256。
作者更喜欢将PCIE root port分组到多funcion设备中保持更简单的层级,这对于大多数场景足够。仅当没有空间给PCIE root port时在使用PCIE switch(x3130-upstream,xio3130-downstream)。
仅插入PCIE设备到PCIE port中。
2.2.1 将一个PCIE设备插入到PCIE root port中
-device ioh3420,id=root_port1,chassis=x,slot=y,[,bus=pcie.1][,chassis=x][,slot=y][,addr=z] \
- device <dev>,bus=root_port1
2.2.2 使用多function的PCIE root port
-device ioh3420,id=root_port1,multifunction=on,chassis=x,addr=z.0[,slot=y][,bus=pcie.0] \
-device ioh3420,id=root_port2,chassis=x1,addr=z.1[,slot=y1][,bus=pcie.0] \
-device ioh3420,id=root_port3,chassis=x2,addr=z.2[,slot=y2][,bus=pcie.0] \
2.2.3 将PCIE设备插入到一个switch中
-device ioh3420,id=root_port1,chassis=x,slot=y[,bus=pcie.0][,addr=z] \
-device x3130-upstream,id=upstream_port1,bus=root_port1[,addr=x] \
-device xio3130-downstream,id=downstream_port1,bus=upstream_port1,chassis=x1,slot=y1[,addr=z1]
-device <dev>,bus=downstream_port1
NOTE: (slot, chassis)对是强制要求的必须对PCIE root port唯一。当没有明确指明时,slot默认为0。’addr’参数对上述所有例子都可以为0。
2.3 PCI层次
传统的PCI设备可以作为集成Endpoint被插入到pcie.0上,但如第五部分提到的,这样的传统设备不具备热插拔能力。使用PCIE-PCI Bridge(pcie-pci-bridge)与PCI-PCI Bridge联合起来开启PCI层次。
作者更喜欢平坦的层次。对于大多数场景一个PCIE-PCI Bridge和多个PCI-PCI Bridge将可以支持上百传统设备。建议在PCIE-PCI Bridge下遍历PCI-PCI Bridge知道满后再插入新的PCI-PCI Bridge。
2.3.1 插入一个PCI设备到pcie.0上作为集成Endpoint
-device <dev>[,bus=pcie.0]
2.3.2 将一个PCI设备插入到PCI-PCI桥
-device pcie-pci-bridge,id=pcie_pci_bridge1[,bus=pcie.0] \
-device pcie-bridge,id=pcie_bridge1,bus=pcie_pci_bridge1[.chassis_nr=x][,addr=y] \
-device <dev>,bus=pci_bridge1[,addr=x]
注意如果shpc=off参数没有传递给PCI桥或PCIE-PCI桥,’addr’不能为0。
3 IO空间问题
PCIE root port和PCIE downstream port可以作为PCI-PCI桥被Firmware/Guest OS看到。PCI spec要求每个port可以保存4K IO范围,即使仅有一个设备被插入到每个port。这会导致IO空间利用不充分。
QEMU(SeaBIOS/OVMF)使用的firmware在下列情况下不对每个PCIE root/PCIE downstream port分配IO空间达到优化:
- port为空,或
- 在port后面的设备没有IO BAR。
IO空间被限制在65536 byte宽的IO port,同时也可能会被平台设备的固定的IO port脆片化,这会导致当存在IO BAR的设备在PCIE层次使用时,最多每个系统有10个PCIE root port或PCIE downstream port。使用 建议的设备放置策略可以解决这个问题。
PCIE spec要求PCIE设备不需要IO port时仍可以正常工作。PCI层次没有这个限制。
4 总线数目问题
每个PCI域可以有最多256个总线,即使外部RC(pxb-pcie)被使用QEMU PCIE不支持多PCI域。
PCIE层次的每个部件(RC, PCIE root port,PCIE downstream/upstream port)使用一个总线号。因为仅一个设备连接到PCIE root port或PCIE downstream port,建议提前计划预期的设备数量,以防总线数目短缺。
阻止PCIE switch使层次不需要消耗总线号在upstream port。
Pxb-pcie设备的属性bus_nr分区0..255总线号空间。在给定pxb-pcie设备的根总线分配给总线的所有总线号必须符合pxb-pcie设备的bus_nr属性,命令行为其他pxb-pcie设备设置bus_nr属性的最低位。
5 热插拔
PCIE根总线(pcie.0和pxb-pcie设备导出的总线)不支持热插拔,因此任何嵌入到RC的设备也不能热插拔:
- PCIE集成Endpoint
- PCIE root port
- PCIE-PCI 桥
- Pxb-pcie
注意PCIE downstream port不能热插入到一个已经存在的PCIE upsream port。
PCI设备可以热插入PCIE-PCI和PCI-PCI桥。PCI热插入到PCI-PCI桥是基于ACPI的。PCIE热插入到PCI桥是基于SHPC的。他们使用PCIE native hot-plug工作。
5.1 hot-plug
(1)PCI层次
让足够PCI-PCI桥空或增加一个或多个空PCI-PCI桥到PCIE。
对于每个PCI-PCI桥,Guest firmware可以保留4K IO空间和2M MMIO范围来用于所有设备。合适的PCI容量被设计。
因为硬件大约10个PCI桥的限制,不要使用超过9个PCI-PCI桥。留4K给集成Endpoint。
(2)PCIE层次
留足够PCIE root port。使用多function PCIE root port来使层次尽可以flat,这样更胜PCI总线号。如果你不是必须,不要使用PCIE switch,每个都使用额外的PCI总线。
5.3 hot-plug例子
使用HMP
device_add <dev>,id=<id>,bus=<PCIE root port id/PCIE downstream port id/PCI-PCI Bridge id>