SR-IOV全称Single Root I/O Virtualization,是Intel在2007年提出的一种基于硬件的虚拟化解决方案。在虚拟化场景中,CPU与内存是最先解决的,但是I/O设备一直没有很好的解决办法,Intel有VT-d(Virtualization Technology for Directed I/O)可以将物理服务器的PCIe设备直接提供给虚拟机使用,也就是常说的“直通”(passthrough),但是直通面临一个问题是PCIe设备只能给一个虚拟机使用,这肯定是不行的,所以SR-IOV应运而生,一个物理设备可以虚拟出多个虚拟设备给虚拟机使用。

目录

  1. 概述
    1. 基本概念
    2. 先决条件
  2. SR-IOV的安装配置
    1. 计算节点上创建VF
    2. 计算节点配置PCI设备
    3. 控制节点配置neutron-server
    4. 控制节点配置nova-scheduler
    5. 计算节点上启动neutron-sriov-agent
    6. 打开L2 FDB(可选)
    7. 验证
    8. 出现的问题
  3. 总结
    1. 优点
    2. 缺点
  4. 其他

概述

SR-IOV是一种规范,使得单根端口下的单个快速外围组件互连 (PCIe) 物理设备显示为管理程序或客户机操作系统的多个单独的物理设备,既有直通设备的性能优势,又可以支持多个虚拟机,一举两得。

基本概念

SR-IOV使用physical functions(PF)和virtual functions(VF)为SR-IOV设备管理全局功能。
PF包含SR-IOV功能的完整PCIe设备,PF作为普通的PCIe设备被发现、管理和配置。PF通过分配VF来配置和管理SR-IOV功能。禁用SR-IOV后,主机将在一个物理网卡上创建一个PF。
VF是轻量级PCIe功能(I/O 处理)的PCIe设备,每个VF都是通过PF来生成管理的,VF的具体数量限制受限于PCIe设备自身配置及驱动程序的支持,启用SR-IOV后,主机将在一个物理NIC上创建单个PF和多个VF。 VF的数量取决于配置和驱动程序支持。
每个SR-IOV设备都可有一个PF(Physical Functions),并且每个PF最多可有64,000个与其关联的VF(Virtual Function)。PF可以通过寄存器创建VF,这些寄存器设计有专用于此目的的属性。一旦在PF中启用了SR-IOV,就可以通过PF的总线、设备和功能编号(路由ID)访问各个VF的PCI配置空间。
每个VF都具有一个PCI内存空间,用于映射其寄存器集。VF设备驱动程序对寄存器集进行操作以启用其功能,并且显示为实际存在的PCI设备。创建VF 后,可以直接将其指定给虚拟机或各个应用程序。此功能使得虚拟功能可以共享物理设备,并在没有CPU和虚拟机管理程序软件开销的情况下执行I/O。

先决条件

PCIe Passthrough的方式是直接把一个PCIe设备指派给guest虚拟机,这样guest虚拟机可以完全控制设备并提供接近于原生性能。但是,PCIe Passthrough的实现是和SR-IOV冲突的,因为在SR-IOV实现中,虚拟机是直接分配一个VF。这样多个虚拟机可以通过分配的VF来使用同一个PCIe设备。
设备指定分配需要CPU和firmware都支持IOMMU(I/O Memory Management Unit)。IOMMU负责I/O虚拟地址(I/O Virtual Address)和物理内存地址转换。这样虚拟机就能够使用guest物理地址来对设备编程,通过IOMMU转换成物理主机内存地址。
IOMMU groups是一组和系统中其他设备隔离的设备集合。也就是说,IOMMU groups代表了具有IOMMU粒度(也就是必须将整个IOMMU group分配给同一个虚拟机)和与系统中所有其他IOMMU group隔离。这种方式允许IOMMU和其他IOMMU group区别进行数据处理,即IOMMU group的内部和外部隔离。
设备分配的关键是虚拟机和PCIe设备虚拟化功能(virtual functions, VFs)隔离数据处理。在PCIe和服务器标准定义的访问控制服务(Access Control Service, ACS)能力是保证IOMMU groups隔离的运行标准。如果没有原生的ACS,或者不能确保硬件厂商提供该能力,则会破坏IOMMU的保护功能导致暴露点对点(peer-to-peer)DMA。
服务器的根端口(root port)也要提供原生的ACS支持,否则会导致安装设备被一股脑分组打包。有两种root ports:基于处理器(北桥)root ports和基于控制器hub(南桥)root ports。

总结一下就是,

  1. CPU必须支持IOMMU(例如VT-d或AMD-Vi)。
  2. Firmware必须支持IOMMU。
  3. CPU root ports必须支持ACS或等同ACS能力。
  4. 所有位于PCIe设备和root ports之间的PCIe switches和bridges都应该支持ACS。例如,如果switch不支持ACS,则所有这个Swtich之后的设备都会共享一个相同的IOMMU group,也就只能分配给一个虚拟机了。

SR-IOV的安装配置

为了启用SR-IOV,需要执行以下步骤:
1.计算节点上创建VF。
2.计算节点配置nova-compute的PCI设备。
3.控制节点配置neutron-server。
4.控制节点配置nova-scheduler。
5.计算节点上启动neutron-sriov-agent。

计算节点上创建VF

  1. 确保BIOS中开启了SR-IOV和IOMMU(arm环境下叫SMMU)。
  2. 开启linux内核中的IOMMU功能。

编辑/etc/default/grub文件

1
GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on iommu=pt"

ARM环境下为

1
GRUB_CMDLINE_LINUX_DEFAULT="iommu.passthrough=on iommu=pt"

生成新的内核启动文件。

1
grub2-mkconfig -o /boot/grub2/grub.cfg

在计算节点查看对应网卡的最大VF的个数。

1
2
cat /sys/class/net/enp49s0f1/device/sriov_totalvfs
7

其中enp49s0f1是网卡名称,可以看到最大支持7个VF,之后配置的个数不能超过这个值。

设置网卡的VF个数。

1
echo '7' > /sys/class/net/enp49s0f1/device/sriov_numvfs

可以同ip命令查看网络设备如下。

1
2
3
4
5
6
7
8
9
ip a | grep enp49s0f1
3: enp49s0f1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
34: enp49s0f1v0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
35: enp49s0f1v1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
36: enp49s0f1v2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
37: enp49s0f1v3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
38: enp49s0f1v4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
40: enp49s0f1v6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
45: enp49s0f1v5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000

使用lspci查看网卡pci设备。

1
2
3
4
5
6
7
8
9
10
lspci -D | grep Ethernet
0000:31:00.0 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)
0000:31:00.1 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)
0000:31:10.1 Ethernet controller: Intel Corporation I350 Ethernet Controller Virtual Function (rev 01)
0000:31:10.5 Ethernet controller: Intel Corporation I350 Ethernet Controller Virtual Function (rev 01)
0000:31:11.1 Ethernet controller: Intel Corporation I350 Ethernet Controller Virtual Function (rev 01)
0000:31:11.5 Ethernet controller: Intel Corporation I350 Ethernet Controller Virtual Function (rev 01)
0000:31:12.1 Ethernet controller: Intel Corporation I350 Ethernet Controller Virtual Function (rev 01)
0000:31:12.5 Ethernet controller: Intel Corporation I350 Ethernet Controller Virtual Function (rev 01)
0000:31:13.1 Ethernet controller: Intel Corporation I350 Ethernet Controller Virtual Function (rev 01)

设置VF持久化。

1
echo "echo '7' > /sys/class/net/enp49s0f1/device/sriov_numvfs" >> /etc/rc.local

计算节点配置PCI设备

编辑nova-compute服务的配置文件nova.conf,一般为/etc/nova/nova.conf。

1
2
3
4
5
[pci]
passthrough_whitelist = { "address": "[[[[<domain>]:]<bus>]:][<slot>][.[<function>]]", "physical_network": "sriovnet1" }
根据上面lspci的结果可以配置如下的pci设备。
[pci]
passthrough_whitelist = [{"physical_network": "sriovnet1", "address": "*:31:12.*"}]

其中*通配符匹配所有字符,所以地址为0000:31:12.1和0000:31:12.5的两个pci设备会被匹配。

控制节点配置neutron-server

编辑neutron-server的配置文件ml2_conf.ini,一般为/etc/neutron/plugins/ml2/ml2_conf.ini。添加驱动sriovnicswitch。

1
2
3
4
5
[ml2]
mechanism_drivers = openvswitch,sriovnicswitch
修改vlan范围。
[ml2_type_vlan]
network_vlan_ranges = sriovnet1

最后重启neutron-server。

1
systemctl restart neutron-server

控制节点配置nova-scheduler

编辑nova-sheduler服务的配置文件nova.conf,一般为/etc/nova/nova.conf。确保available_filters是全部的filters,并且添加PciPassthroughFilter到enabled_filters中。

1
2
3
[filter_scheduler]
available_filters = nova.scheduler.filters.all_filters
enabled_filters = AvailabilityZoneFilter, ComputeFilter, ComputeCapabilitiesFilter, ImagePropertiesFilter, ServerGroupAntiAffinityFilter, ServerGroupAffinityFilter, PciPassthroughFilter

最后重启nova-scheduler。

1
systemctl restart openstack-nova-scheduler

计算节点上启动neutron-sriov-agent

1.安装neutron-sriov-agent。

1
yum install -y openstack-neutron-sriov-nic-agent

2.编辑neutron-sriov-nic-agent的配置文件,一般为/etc/neutron/plugins/ml2/sriov_agent.ini。

1
2
3
4
5
6
[sriov_nic]
physical_device_mappings = sriovnet1:enp49s0f1
exclude_devices =

[securitygroup]
firewall_driver = neutron.agent.firewall.NoopFirewallDriver

3.启动neutron-sriov-agent。

1
2
systemctl enable neutron-sriov-nic-agent
systemctl start neutron-sriov-nic-agent

打开L2 FDB(可选)

FDB(Forwarding DataBase)是对OVS agent或Linux agent的L2层agent的扩展。它的目的是更新使用普通端口(Port)的现有实例的FDB表,这使SR-IOV实例与正常实例之间的沟通成为了可能。FDB扩展的用例是:
1.直接端口(direct port)和普通端口(normal port)实例位于同一计算节点上。
2.使用浮动IP地址的直接端口(direct port)实例和网络节点位于同一宿主机上。

编辑计算节点上的openvswitch_agent.ini或者linuxbridge_agent.ini。

1
2
3
4
[agent]
extensions = fdb
[FDB]
shared_physical_device_mappings = sriovnet1:enp49s0f1

重启neutron-openvswitch-agent或者neutron-linuxbridge-agent服务。

验证

1.查看neutron-sriov-nic-agent服务的状态。

1
openstack network agent list --agent-type nic

2.创建sriov网络。

1
2
3
openstack network create --provider-physical-network sriovnet1 \
--provider-network-type vlan --provider-segment 1000 \
sriov-net

3.创建sriov子网。

1
2
3
4
openstack subnet create --network sriov-net \
--subnet-range 10.12.21.0/24 \
--allocation-pool start=10.12.21.131,end=10.12.21.132 \
sriov-subnet

4.创建port。

1
2
openstack port create --network sriov-net --vnic-type direct \
sriov-port

5.使用port创建实例。

1
2
3
4
5
6
port_id=$(openstack port show sriov-port -c id -f value)

openstack server create --flavor m1.large \
--image uniontechos-server-20-1060a-amd64-beta \
--nic port-id=$port_id \
test

或者添加到已有实例上。

1
openstack server add port test $port_id

6.进入实例内部查看。

查看网卡设备的信息,可以看到ens4网卡的驱动为igbvf,说明透传成功。

出现的问题

1.创建实例或者添加port时nova-compute报错。

1
2
3
4
5
6
7
8
9
10
11
attach device xml: <interface type="hostdev" managed="yes">
<mac address="fa:16:3e:fe:47:d2"/>
<source>
<address type="pci" domain="0x0000" bus="0x0b" slot="0x10" function="0x5"/>
</source>
<vlan>
<tag id="1000"/>
</vlan>
</interface>
attach_device /usr/lib/python3.6/site-packages/nova/virt/libvirt/guest.py:304
attaching network adapter failed.: libvirt.libvirtError: 内部错误:无法执行 QEMU 命令 'device_add':vfio 0000:0b:10.5: group 3 is not viable

这是因为IOMMU给内存创建隔离区的最小粒度不是Device,而是group。需要硬件设备支持PCIe ACS,或者通过Linux Patch的方式获得PCIe ACS的能力。

2.创建完实例后实例获取不到IP地址。
因为创建sriov-net是基于物理网络设备的,所以需要物理网络中存在DHCP服务器,可以手动进行IP地址的配置。

3.创建完实例后,IP访问失败。
创建的sriov网络类型是vlan,流量从实例中流出的时候会打上对应的vlan tag导致IP访问失败。其中vlan tag的作用用于隔离Provider网络,这样可以在同一网络下连接无sriov port的实例和带有sriov port的实例。

总结

SR-IOV的优缺点。

优点

1.性能好,可以从虚拟机直接访问宿主机的硬件,同时提高包的转发率,低延迟,减少主机CPU消耗。
2.比PCI直通的方式更加灵活,成本降低节省资本和运营开销。
3.通过IOMMU实现隔离更安全。

缺点

1.使用SR-IOV时,不支持防火墙,需要在neutron的配置文件中关闭。
2.宿主机无法监控VF的状态。
3.SR-IOV不支持vxlan。
4.需要硬件支持。

其他

1.Train版本后支持了SR-IOV实例的热迁移。
2.indirect模式(vnic-type: macvtap或virtio-forwarder)的SR-IOV Port可以透明地迁移到虚拟机,direct模式(vnic-type: direct或direct-physical)下的SR-IOV Port在迁移前预先分离,迁移完成后再进行连接,这对用户来说是不透明的。为了避免在使用direct模式SR-IOV进行热迁移时失去网络连接,用户应在实例中创建故障转移bond,例如vnic类型的normal或indirect模式的SR-IOV的Port。
3.Victoria版本之后支持向已有实例添加SR-IOV的Port。