将物理 PCI 设备附加到虚拟机¶
OpenStack 中的 PCI 直通功能允许虚拟机完全访问和直接控制物理 PCI 设备。此机制适用于任何类型的 PCI 设备,包括网络接口卡 (NIC)、图形处理单元 (GPU) 或可以连接到 PCI 总线的任何其他设备。正确的驱动程序安装是虚拟机正确使用设备的唯一要求。
某些 PCI 设备提供单根 I/O 虚拟化和共享 (SR-IOV) 功能。使用 SR-IOV 时,物理设备被虚拟化,并显示为多个 PCI 设备。虚拟 PCI 设备被分配给相同或不同的虚拟机。在 PCI 直通的情况下,整个物理设备仅分配给一个虚拟机,不能共享。
PCI 设备通过 flavor 附加规格请求,具体而言是通过 pci_passthrough:alias flavor 附加规格。本指南演示了如何为具有供应商 ID 为 8086 和产品 ID 为 154d 的 PCI 设备类型启用 PCI 直通 - Intel X520 网络适配器 - 通过将其映射到别名 a1。您应该根据具有潜在不同功能的其他设备调整说明。
注意
有关创建具有 SR-IOV 网络接口的服务器的信息,请参阅 网络指南。
Limitations
在 22.0.0 Victoria 版本发布之前,不支持将 SR-IOV 端口附加到现有的服务器。由于 libvirt 和 qemu 中的各种错误,我们建议使用至少 libvirt 版本 6.0.0 和至少 qemu 版本 4.2。
在 14.0.0 Newton 版本发布之前,不支持带有附加 SR-IOV 设备的服务器的冷迁移(resize),请参阅 bug 1512800 以获取详细信息。
注意
Nova 仅支持 PCI 地址,其中字段限制为以下最大值
域 - 0xFFFF
总线 - 0xFF
插槽 - 0x1F
功能 - 0x7
如果地址超出这些范围,Nova 将忽略超visor 报告的 PCI 设备。
在版本 26.0.0 中更改: (Zed):PCI 直通设备清单现在可以跟踪在 Placement 中。有关更多信息,请参阅 Placement 中的 PCI 跟踪。
在版本 26.0.0 中更改: (Zed):如果同时配置了父 PF 及其子 VF 在 pci.device_spec 中,nova-compute 服务将拒绝启动。有关更多信息,请参阅 Placement 中的 PCI 跟踪。
在版本 26.0.0 中更改: (Zed):nova-compute 服务将拒绝使用 pci.device_spec 配置,该配置使用 devname 字段启动。
在版本 27.0.0 中更改: (2023.1 Antelope):Nova 提供基于 Placement 的调度支持,用于基于 flavor 的 PCI 请求的服务器。默认情况下禁用此支持。
在版本 31.0.0 中更改: (2025.1 Epoxy)
添加 managed 标签以定义 PCI 设备是否由 libvirt 管理(从主机附加/分离),这对于支持使用新的内核变体驱动程序接口的 SR-IOV 设备是必需的。
添加 live_migratable 标签以定义 PCI 设备是否支持实时迁移。
将 live_migratable 标签添加到别名定义中,以允许请求实时迁移或非实时迁移的设备。
启用 PCI 直通¶
配置计算主机¶
要在基于 x86、Linux 的计算节点上启用 PCI 直通,需要以下内容
BIOS 中启用 VT-d
在主机操作系统上启用 IOMMU,例如,通过将
intel_iommu=on或amd_iommu=on参数添加到内核参数可分配的 PCIe 设备
配置 nova-compute¶
为主机配置 PCI 直通后,必须配置 nova-compute 以允许 PCI 设备直通到 VM。这是使用 pci.device_spec 选项完成的。例如,假设我们的示例 PCI 设备在每个主机上具有 PCI 地址 41:00.0
[pci]
device_spec = { "address": "0000:41:00.0" }
有关语法信息,请参阅 pci.device_spec。
或者,要启用具有相同产品和供应商 ID 的所有设备的直通
[pci]
device_spec = { "vendor_id": "8086", "product_id": "154d" }
如果使用供应商和产品 ID,则所有匹配 vendor_id 和 product_id 的 PCI 设备都将添加到可用于直通到 VM 的 PCI 设备池中。
此外,有必要配置 pci.alias 选项,这是一个 JSON 样式的配置选项,允许您将给定的设备类型(由标准的 PCI vendor_id 和(可选)product_id 字段标识)映射到任意名称或 *别名*。然后可以使用 pci_passthrough:alias flavor 附加规格(如前所述)请求 PCI 设备。对于具有供应商 ID 为 0x8086 和产品 ID 为 0x154d 的示例设备,这将是
[pci]
alias = { "vendor_id":"8086", "product_id":"154d", "device_type":"type-PF", "name":"a1" }
重要的是要注意添加了 device_type 字段。这是必要的,因为此 PCI 设备支持 SR-IOV。 nova-compute 服务根据设备报告的功能将设备分类为以下三种类型之一
type-PF该设备支持 SR-IOV 并且是父设备或根设备。
type-VF该设备是支持 SR-IOV 的设备的子设备。
type-PCI该设备不支持 SR-IOV。
默认情况下,只能使用 PCI 直通附加 type-PCI 设备。如果您希望附加 type-PF 或 type-VF 设备,则必须在 config 选项中指定 device_type 字段。如果该设备是不支持 SR-IOV 的设备,则可以省略 device_type 字段。
有关语法信息,请参阅 pci.alias。
重要提示
此选项还必须在控制器节点上配置。稍后在此文档中讨论了这一点。
配置完成后,重新启动 nova-compute 服务。
配置 nova-scheduler¶
必须配置 nova-scheduler 服务以启用 PciPassthroughFilter。为此,请将此过滤器添加到 filter_scheduler.enabled_filters 中指定的过滤器列表中,并将 filter_scheduler.available_filters 设置为默认值 nova.scheduler.filters.all_filters。例如
[filter_scheduler]
enabled_filters = ...,PciPassthroughFilter
available_filters = nova.scheduler.filters.all_filters
完成操作后,重新启动 nova-scheduler 服务。
配置 nova-api¶
有必要也在控制器上配置 pci.alias 配置选项。此配置应与计算节点上找到的配置匹配。例如
[pci]
alias = { "vendor_id":"8086", "product_id":"154d", "device_type":"type-PF", "name":"a1", "numa_policy":"preferred" }
有关语法信息,请参阅 pci.alias。有关 numa_policy 信息,请参阅 Affinity。
配置完成后,重新启动 nova-api-wsgi 服务。
配置 flavor 或镜像¶
配置别名后,它就可以用于 flavor 附加规格。例如,要请求两个引用别名 a1 的 PCI 设备,请运行
$ openstack flavor set m1.large --property "pci_passthrough:alias"="a1:2"
有关 pci_passthrough:alias 语法的更多信息,请参阅 文档。
PCI-NUMA 亲和性策略¶
默认情况下,libvirt 驱动程序对 PCI 设备(无论是 PCI 直通设备还是 neutron SR-IOV 接口)强制执行严格的 NUMA 亲和性。这意味着默认情况下,PCI 设备必须从与实例的至少一个 CPU 相同的宿主机 NUMA 节点分配。然而,这并不总是必要的,您可以使用 hw:pci_numa_affinity_policy flavor extra spec 或等效的镜像元数据属性来配置此策略。允许三种可能的值
- 必需
此策略意味着 nova 将仅在实例的至少一个 NUMA 节点与这些 PCI 设备关联时才启动具有 PCI 设备的实例。这意味着如果无法确定某些 PCI 设备的 NUMA 节点信息,则这些 PCI 设备将无法被实例使用。这提供了最大的性能。
- socket
此策略意味着 PCI 设备必须与至少一个 guest NUMA 节点的宿主机 socket 相同。例如,考虑一个具有两个 socket 的系统,每个 socket 具有两个 NUMA 节点,编号为 socket 0 上的节点 0 和节点 1,以及 socket 1 上的节点 2 和节点 3。有一个 PCI 设备与节点 0 关联。具有两个 guest NUMA 节点和
socket策略的 PCI 实例可以关联到以下任一节点 0 和节点 1
节点 0 和节点 2
节点 0 和节点 3
节点 1 和节点 2
节点 1 和节点 3
实例无法关联到节点 2 和节点 3,因为它们都不是与 PCI 设备相同的 socket。如果其他节点被其他实例占用,并且只有节点 2 和 3 可用,则实例将无法启动。
- preferred
此策略意味着
nova-scheduler将选择计算主机,对 PCI 设备的 NUMA 亲和性考虑最少。nova-compute将尝试基于 NUMA 亲和性进行 PCI 设备的最佳选择,但是,如果这是不可能的,则nova-compute将回退到在与 PCI 设备不关联的 NUMA 节点上进行调度。- legacy
这是默认策略,它描述了当前的 nova 行为。通常我们有关于 PCI 设备与 NUMA 节点关联的信息。但是,某些 PCI 设备不提供这些信息。
legacy值意味着 nova 将在以下情况下启动具有 PCI 设备的实例PCI 设备与实例将要启动的至少一个 NUMA 节点关联
没有关于 PCI-NUMA 亲和性的信息
例如,要配置 flavor 以对用户附加的任何 neutron SR-IOV 接口使用 preferred PCI NUMA 亲和性策略
$ openstack flavor set $FLAVOR \
--property hw:pci_numa_affinity_policy=preferred
您还可以通过在 alias 配置中通过 pci.alias 指定策略来配置 PCI 直通设备。有关更多信息,请参阅 文档。
Placement 中的 PCI 跟踪¶
注意
以下描述的功能是可选的,并且在 nova 26.0.0 (Zed) 中默认禁用。仍然支持并启用了旧的 PCI 跟踪器代码路径。可以通过 pci.report_in_placement 配置启用 Placement PCI 跟踪。
警告
请注意,一旦在给定的计算主机上启用,就无法在那里禁用它了。
从 nova 26.0.0 (Zed) 开始,PCI 直通设备清单在 Placement 中跟踪。如果超visor 上存在 PCI 设备并且匹配通过 pci.device_spec 配置的设备规范之一,那么 Placement 将拥有该设备的表示形式。每种类型的 PCI 设备 type-PCI 和 type-PF 都被建模为 Placement 资源提供者 (RP),名称为 <hypervisor_hostname>_<pci_address>。类型为 type-VF 的设备由其父 PCI 设备 PF 表示为资源提供者。
默认情况下,nova 将使用 CUSTOM_PCI_<vendor_id>_<product_id> 作为 Placement 中 PCI 清单的资源类。但是,资源类的名称可以通过 pci.device_spec 选项中的 resource_class 标签进行自定义。此外,该配置中还有一个新的 traits 标签,允许指定要添加到表示匹配 PCI 设备的资源提供者的 Placement traits 列表。
注意
在 nova 26.0.0 (Zed) 中,Placement 资源跟踪的 PCI 设备不支持打算通过 Neutron 端口消耗的 SR-IOV 设备,并且在 pci.device_spec 中具有 physical_network 标签。此类设备通过 Nova 中的旧 PCI 跟踪器代码路径支持。
注意
不支持对同一父 PF 下的 VFs 配置不同的资源类或 traits,nova-compute 服务将拒绝使用此类配置启动。
重要提示
虽然 nova 过去支持配置 PF 及其子 VF 进行 PCI 直通,但它只允许消耗父 PF 或其子 VF。从 26.0.0 (Zed) 开始,nova-compute 服务将对配置强制执行相同的规则,如果同时配置了父 PF 及其 VF,则将拒绝启动。
重要提示
虽然 nova 过去支持通过 pci.device_spec 中的 devname 参数按设备名称配置 PCI 设备,但事实证明这存在问题,因为 PCI 设备的 netdev 名称可能会因超visor 重启等多种原因而更改。因此,从 nova 26.0.0 (Zed) 开始,nova-compute 服务将拒绝使用此类配置启动。建议使用设备的 PCI 地址代替。
重要提示
虽然 nova 支持配置 pci.alias,其中 alias 名称重复,因此与多个 alias 规范相关联,但在启用 Placement 中的 PCI 跟踪时,不支持此类配置。
nova-compute 服务确保 nova DB 中具有 PCI 分配的现有实例将在 placement 中具有相应的 PCI 分配。这种分配修复也适用于任何新实例,无论调度部分的此功能的状态如何,以确保 nova DB 和 placement 同步。修复逻辑有一个限制。它假定在 nova-compute 服务升级时没有进行中的迁移。如果存在进行中的迁移,则迁移源主机的 PCI 分配将不会被修复。迁移完成后或回滚后,placement 视图将保持一致。
重新配置超visor 上的 PCI 设备或更改 pci.device_spec 配置选项并重新启动 nova-compute 服务在以下情况下受支持
添加了新设备
删除了没有分配的设备
不支持删除具有分配的设备。如果删除了具有任何分配的设备,nova-compute 服务将保留该设备,并且分配存在于 nova DB 和 placement 中,并记录警告。如果以删除已分配的 PF 并配置来自同一 PF 的 VF(反之亦然)的方式重新配置设备,nova-compute 将拒绝启动,因为它会创建一个可以同时使用父 PF 和其 VF 的情况。
从 nova 27.0.0 (2023.1 Antelope) 开始,可以通过 nova-api、nova-scheduler 和 nova-conductor 配置中设置的 filter_scheduler.pci_in_placement 配置选项,也可以启用 Placement 中 PCI 设备的调度和分配。请注意,这仅应在系统中所有计算节点都配置为通过启用 pci.report_in_placement 将 PCI 清单报告到 Placement 后才能启用。在 Antelope 中,基于 flavor 的 PCI 请求受支持,但基于 Neutron 端口的 PCI 请求在 Placement 中未处理。
如果您正在从具有现有服务器和 PCI 使用量的早期版本升级,则必须首先在所有具有 PCI 分配的计算节点上启用 pci.report_in_placement,然后重新启动 nova-compute 服务,然后再启用 filter_scheduler.pci_in_placement。计算服务将在启动期间修复 placement 中缺失的 PCI 分配,并将继续修复未来服务器的缺失分配,直到启用调度支持为止。
如果 flavor 通过 pci_passthrough:alias 请求多个 type-VF 设备,那么考虑 group_policy 的值也很重要。值 none 允许 nova 从同一父 PF 中选择 VF 以满足请求。值 isolate 限制 nova 从不同的父 PF 中选择每个 VF 以满足请求。如果在 flavor 中未提供 group_policy,则默认值为 none。
与 pci.device_spec 的 resource_class 和 traits 字段类似,pci.alias 配置选项支持通过 alias 中的 resource_class 字段按 Placement 资源类名称请求设备,并且还支持通过 alias 中的 traits 字段请求要在所选设备上存在的 traits。如果在 alias 中未指定 resource_class 字段,则 nova 默认设置为 CUSTOM_PCI_<vendor_id>_<product_id>。每个 alias 必须提供 product_id 和 vendor_id 或 resource_class 字段。
有关更深入的技术细节,请阅读 nova 规范。
支持多种类型的 VF¶
SR-IOV 设备(例如 GPU)可以配置为在相同的 vendor ID 和 product ID 下提供具有各种特征的 VF。
为了使 Nova 能够对此建模,如果您使用不同的资源分配配置 VF,则需要为每个 VF 使用单独的 resource_class。
可以按照以下步骤实现
启用 Placement 中的 PCI:这是必要的,以便在 placement 服务中跟踪具有自定义资源类的 PCI 设备。
定义设备规范:使用自定义资源类来表示特定的 VF 类型,并确保超visor 上存在的 VF 通过 VF 的 PCI 地址进行匹配。
指定特定于类型的 Flavor:定义具有与资源类匹配的 alias 的 flavor,以确保正确的分配。
示例
注意
以下示例演示了设备规范和 alias 配置,利用“PCI in placement”功能中的资源类。
[pci]
device_spec = { "vendor_id": "10de", "product_id": "25b6", "address": "0000:25:00.4", "resource_class": "CUSTOM_A16_16A", "managed": "no" }
device_spec = { "vendor_id": "10de", "product_id": "25b6", "address": "0000:25:00.5", "resource_class": "CUSTOM_A16_8A", "managed": "no" }
alias = { "device_type": "type-VF", "resource_class": "CUSTOM_A16_16A", "name": "A16_16A" }
alias = { "device_type": "type-VF", "resource_class": "CUSTOM_A16_8A", "name": "A16_8A" }
配置 PCI 设备的实时迁移¶
具有 PCI 设备的实例的实时迁移需要在设备和 alias 级别进行特定配置,以确保迁移能够成功。本节介绍如何配置 PCI 直通以支持实时迁移。
配置 PCI 设备规范¶
管理员必须显式定义 PCI 设备是否支持实时迁移。这通过在 pci.device_spec 配置中的设备规范中添加 live_migratable 属性来完成。
注意
当然,这需要硬件支持,以及正确的系统和虚拟机管理程序配置。
示例配置
[pci]
dev_spec = {'vendor_id': '8086', 'product_id': '1515', 'live_migratable': 'yes'}
dev_spec = {'vendor_id': '8086', 'product_id': '1516', 'live_migratable': 'no'}
为用户配置 PCI 别名¶
可以通过 flavor extra_specs 请求 PCI 设备。要请求可实时迁移的 PCI 设备,pci.alias 配置中的 PCI 别名定义必须包含 live_migratable 键。
示例配置
[pci]
alias = {'name': 'vf_live', 'vendor_id': '8086', 'product_id': '1515', 'device_type': 'type-VF', 'live_migratable': 'yes'}
alias = {'name': 'vf_no_migrate', 'vendor_id': '8086', 'product_id': '1516', 'device_type': 'type-VF', 'live_migratable': 'no'}
虚拟 IOMMU 支持¶
通过提供的 hw:viommu_model flavor extra spec 或等效的镜像元数据属性 hw_viommu_model,并且在来宾 CPU 架构和操作系统允许的情况下,我们可以在 libvirt 驱动程序中启用 vIOMMU。
注意
启用 vIOMMU 可能会引入显著的性能开销。您可以在 KVM Forum 2021 AMD vIOMMU 会议 中查看性能比较表。出于上述原因,vIOMMU 仅应为需要它的工作流程启用。
以下是允许的 hw:viommu_model (和 hw_viommu_model) 的四个可能值
- virtio
自 Libvirt 8.3.0 起支持,适用于 Q35 和 ARM virt 来宾。
- smmuv3
自 Libvirt 5.5.0 起支持,适用于 ARM virt 来宾。
- intel
支持 Q35 来宾。
- auto
如果 Libvirt 支持,此选项将转换为
virtio,否则在 X86 (Q35) 上转换为intel,在 AArch64 上转换为smmuv3。
对于 viommu 属性
intremap、caching_mode和iotlb选项用于 viommu (这些属性是定义在 Libvirt IOMMU Domain 中的驱动程序属性) 将直接启用。eim如果机器类型是 Q35,将直接启用。eim是定义在 Libvirt IOMMU Domain 中的驱动程序属性。
注意
EIM(扩展中断模式)属性(具有 on 和 off 两种可能值)可用于配置扩展中断模式。具有拆分 I/O APIC(如超visor 功能中所述)且 IOMMU 为中断重映射和 EIM 均启用的 q35 域,将能够使用超过 255 个 vCPU。自 3.4.0 起(仅限 QEMU/KVM)。
aw_bits属性可用于设置地址宽度,以允许在来宾中映射更大的 iova 地址。由于 Qemu 当前支持的值为 39 和 48,因此如果 Libvirt 支持,我们将直接将其设置为更大的宽度 (48)。aw_bits是定义在 Libvirt IOMMU Domain 中的驱动程序属性。
已知问题¶
已知问题是,对于包含 physical_network 标记的设备,live_migratable 标志会被忽略。因此,使用此类设备的实例的行为并非非实时迁移,而是继续使用旧的 VIF 断开/实时迁移/VIF 插入过程进行迁移。
一个示例配置,其中 live_migratable 标志被忽略
[pci]
device_spec = { "vendor_id":"8086", "product_id":"10ca", "address": "0000:06:", "physical_network": "physnet2", "live_migratable": false}
该问题的修复计划在 Epoxy 版本的后续版本中进行。上游错误报告 在此。
一次性设备¶
某些设备在从一个用户释放后并连接到另一个用户之前可能需要注意。这对于直接透传设备尤其如此,因为实例在连接时对它们拥有完全控制权,并且 Nova 不了解设备本身的具体信息,这与更云端的常规资源不同。示例包括
安全擦除 NVMe 设备,以确保数据残余不会从一个用户传递到另一个用户
重新安装已知的良好固件到设备,以避免劫持攻击
在每个用户之前将固件更新到最新版本
检查设备的属性,以确定它是否需要维修或更换,然后再将其交给另一个用户(例如,NVMe 写损耗指示器)
一些自定义行为、重置等
Nova 的范围不包括上述内容,但它支持一项功能,使操作员更容易协调此类任务。通过将设备标记为“一次性”(以下简称 OTU),Nova 将分配一个设备一次,之后它将保持“已保留”状态,以避免分配给另一个实例。在操作员的工作流程执行完毕并且设备应该返回到可用资源池后,可以取消保留标志,Nova 将认为它可再次使用。
注意
此功能需要 Placement 中的 PCI 跟踪 才能工作。需要计算配置,但过渡调度程序配置是可选的(在过渡期间,但为了安全起见是必需的)。
可以通过在 device_spec 中添加标记来将设备标记为 OTU
device_spec = {"address": "0000:00:1.0", "one_time_use": true}
通过这样标记设备,Nova 将在分配给计算节点上的 PCI 设备时,将 reserved 库存值设置为完全覆盖该设备(即 reserved=total。当实例被删除时,used 值将返回零,但 reserved 将保持不变。操作员有责任在设备准备好重新分配时将 reserved 值重置为零。
最好通过侦听 Nova 的 instance.delete.end 事件的通知来处理此问题,以便可以立即发生后处理工作流程。但是,由于可能会丢失或错过通知,因此应定期进行轮询。表示 Nova 正在应用 OTU 行为的设备提供程序将具有 HW_PCI_ONE_TIME_USE 特性,从而更容易识别它们。例如
$ openstack resource provider list --required HW_PCI_ONE_TIME_USE
+--------------------------------------+--------------------+------------+--------------------------------------+--------------------------------------+
| uuid | name | generation | root_provider_uuid | parent_provider_uuid |
+--------------------------------------+--------------------+------------+--------------------------------------+--------------------------------------+
| b9e67d7d-43db-49c7-8ce8-803cad08e656 | jammy_0000:00:01.0 | 39 | 2ee402e8-c5c6-4586-9ac7-58e7594d27d1 | 2ee402e8-c5c6-4586-9ac7-58e7594d27d1 |
+--------------------------------------+--------------------+------------+--------------------------------------+--------------------------------------+
将找到所有此类提供程序。对于每个提供程序,检查库存以查找具有 used=0 和 reserved=1 的设备将识别需要处理的设备。要使用上面的示例
$ openstack resource provider inventory list b9e67d7d-43db-49c7-8ce8-803cad08e656
+----------------------+------------------+----------+----------+----------+-----------+-------+------+
| resource_class | allocation_ratio | min_unit | max_unit | reserved | step_size | total | used |
+----------------------+------------------+----------+----------+----------+-----------+-------+------+
| CUSTOM_PCI_1B36_0100 | 1.0 | 1 | 1 | 1 | 1 | 1 | 0 |
+----------------------+------------------+----------+----------+----------+-----------+-------+------+
要将上面的设备返回到可分配的资源池,我们可以将保留计数重置为零
$ openstack resource provider inventory set --amend \
--resource CUSTOM_PCI_1B36_0100:reserved=0 \
b9e67d7d-43db-49c7-8ce8-803cad08e656
+----------------------+------------------+----------+----------+----------+-----------+-------+
| resource_class | allocation_ratio | min_unit | max_unit | reserved | step_size | total |
+----------------------+------------------+----------+----------+----------+-----------+-------+
| CUSTOM_PCI_1B36_0100 | 1.0 | 1 | 1 | 0 | 1 | 1 |
+----------------------+------------------+----------+----------+----------+-----------+-------+