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应运而生,一个物理设备可以虚拟出多个虚拟设备给虚拟机使用。
目录
概述
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。
总结一下就是,
- CPU必须支持IOMMU(例如VT-d或AMD-Vi)。
- Firmware必须支持IOMMU。
- CPU root ports必须支持ACS或等同ACS能力。
- 所有位于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
- 确保BIOS中开启了SR-IOV和IOMMU(arm环境下叫SMMU)。
- 开启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 | cat /sys/class/net/enp49s0f1/device/sriov_totalvfs |
设置网卡的VF个数。
1 | echo '7' > /sys/class/net/enp49s0f1/device/sriov_numvfs |
可以同ip命令查看网络设备如下。
1 | ip a | grep enp49s0f1 |
使用lspci查看网卡pci设备。
1 | lspci -D | grep Ethernet |
设置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 | [pci] |
控制节点配置neutron-server
编辑neutron-server的配置文件ml2_conf.ini,一般为/etc/neutron/plugins/ml2/ml2_conf.ini。添加驱动sriovnicswitch。
1 | [ml2] |
最后重启neutron-server。
1 | systemctl restart neutron-server |
控制节点配置nova-scheduler
编辑nova-sheduler服务的配置文件nova.conf,一般为/etc/nova/nova.conf。确保available_filters是全部的filters,并且添加PciPassthroughFilter到enabled_filters中。
1 | [filter_scheduler] |
最后重启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 | [sriov_nic] |
3.启动neutron-sriov-agent。
1 | systemctl enable 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 | [agent] |
重启neutron-openvswitch-agent或者neutron-linuxbridge-agent服务。
验证
1.查看neutron-sriov-nic-agent服务的状态。
1 | openstack network agent list --agent-type nic |
2.创建sriov网络。
1 | openstack network create --provider-physical-network sriovnet1 \ |
3.创建sriov子网。
1 | openstack subnet create --network sriov-net \ |
4.创建port。
1 | openstack port create --network sriov-net --vnic-type direct \ |
5.使用port创建实例。
1 | port_id=$(openstack port show sriov-port -c id -f value) |
或者添加到已有实例上。
1 | openstack server add port test $port_id |
6.进入实例内部查看。
查看网卡设备的信息,可以看到ens4网卡的驱动为igbvf,说明透传成功。
出现的问题
1.创建实例或者添加port时nova-compute报错。
1 | attach device xml: <interface type="hostdev" managed="yes"> |
这是因为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。