DPDK内存篇(二): 深入学习 IOVA

作者简介:Anatoly Burakov,英特尔软件工程师,目前在维护DPDK中的VFIO和内存子系统。

引言

在《DPDK内存篇(一): 基本概念》中,我们介绍了数据平面开发工具包(DPDK)内存管理背后的主要概念和原理,也探讨了它们对DPDK拥有绝佳性能所做出的贡献。但是,DPDK远比这些概念与原理复杂,需要正确配置才能充分的利用系统的潜力。而其中,选择正确的内核驱动和IOVA模式可能是最为关键的,但这也取决于要运行的应用及其运行前的环境。本文将介绍可选的几种不同配置选项,并为读者日后的选择提供建议。

环境抽象层(EAL)参数

环境抽象层(EAL)是DPDK的核心。它是DPDK的一个库。顾名思义,它负责抽象出环境(硬件、操作系统等)并为软件提供统一的接口。EAL要处理很多东西,所以也不难理解为何它会是DPDK中最复杂的一个部分。由EAL负责处理的包括:

· 管理CPU核心和非统一内存访问(NUMA)节点
· 将硬件设备的寄存器映射到内存中,使它们可受DPDK轮询模式驱动程序(PMDs)驱动
· 管理硬件和软件中断
· 抽象出平台差异,如字节序、缓存行大小等
· 管理内存和多进程同步
· 提供与原子、内存屏障和其他同步原语共通的独立于平台和操作系统的方法
· 加载和枚举硬件总线,设备和PMD

以上只罗列了EAL的部分功能,但这足以体现它对于DPDK的重要性。因此,DPDK中大量配置与EAL有关也不足为奇。目前,可以通过(直接或间接地)为DPDK初始化例程指定命令行参数对EAL进行配置。DPDK应用命令行通常如下所示:

某些使用DPDK的应用(如OvS-DPDK)可能会对用户隐藏此步骤,因此可能不需要明确指定EAL命令行参数,但尽管如此此步骤还是会在后台进行。

IO虚拟地址(IOVA)模式

DPDK是一个用户态应用框架,使用DPDK的软件可以像其他软件一样使用常规虚拟地址。但除此之外,DPDK还提供了用户态PMD和一组API,以实现完全以用户态执行IO操作。本系列的前一篇也已经提到过,硬件不能读取用户空间虚拟地址;它使用的是IO地址——物理地址(PA)或IO虚拟地址(IOVA)。

DPDK API对物理和IO虚拟地址不作区分,即使不是由IO内存管理单元(IOMMU)提供VA部分,也都以IOVA来代表两种地址。但DPDK却会区分物理地址用作IOVA的情况,和IOVA与用户空间虚拟地址匹配的情况。它们在DPDK API中被称为IOVA模式,可分为两种:作为PA的IOVA模式,和作为VA的IOVA模式。

作为物理地址(PA)的IOVA模式

作为PA的IOVA模式下,分配到整个DPDK存储区的IOVA地址都是实际的物理地址,而虚拟内存的分配与物理内存的分配相匹配。该模式的一大优点就是它很简单:它适用于所有硬件(也就是说,不需要IOMMU),并且它适用于内核空间(将真实物理地址转换为内核空间地址的开销是微不足道的)。实际上,这就是DPDK长期以来的运作方式,在很多方面它都被认为是默认的选项。

然而,作为PA的IOVA模式也存在一些缺点。其中一个就是它需要根用户特权——如果无法访问系统的页面映射,DPDK就无法获取内存区域的真实物理地址。因此,如果系统中没有root权限,就无法以该模式运行。

图1.作为PA的IOVA模式

作为PA的IOVA模式还有另外一个值得一提的限制——虚拟内存分配要遵循物理内存分配。这意味着如果物理内存空间被分段(被分成许多小段而不是几个大段)时,虚拟内存空间也要遵循同样的分段。极端情况下,分段可能过于严重,导致被分割出来物理上连续的片段数量过多,耗尽DPDK用于存储这些片段相关信息的内部数据结构,就会让DPDK初始化失败。

图2.作为PA的IOVA模式下PA分段示例

应对这些问题,DPDK社区提出了解决方法。举例来说,一种减少分段影响的方式是使用更大的分页——问题虽然没被解决,但是单独的1千兆字节(GB)段比独立的2兆字节(MB)段能大幅度减小分段的数量。另外一种广泛使用的解决方式则是在启动时引导系统并保留大页,而不是在运行时。但上述的解决方法都不能根本地解决问题,而且整个DPDK社区都习惯了要去解决这些问题,每个DPDK用户(有意或无意)在使用时都会采取相同的思维模式——“我需要X MB内存,但以防万一,我要保留X + Y MB!”

作为虚拟地址(VA)的IOVA模式

相比之下,作为VA的IOVA模式不需遵循底层物理内存的分布。而是重新分配物理内存,与虚拟内存的分配匹配。DPDK EAL依靠内核基础设施来实现这一点。内核基础设施又反过来使用IOMMU重新映射物理内存。

图3.作为VA的IOVA模式

这种方式的优点显而易见:作为VA的IOVA模式下,所有内存都是VA-和IOVA-连续的。这意味着所有需要大量IOVA连续内存的内存分配更有可能成功,因为对硬件来说,即使底层物理内存可能不存在,内存看上去还是IOVA连续的。由于重新映射,IOVA空间片段化的问题就变得无关紧要。不管物理内存被分段得多么严重,它总能被重新映射为IOVA-连续的大块内存。

图4.作为VA的IOVA模式下的分段示例

作为VA的IOVA模式还有另一个优点,它不需要任何权限。这是因为它不需要访问系统页面映射。这样就可以允许以非root用户身份运行DPDK,而且在特权访问不受欢迎的环境中,如云原生环境就可以更加容易地使用DPDK。

当然, 作为VA的IOVA模式也有一个缺点。出于各种原因,有时候可能不能选择使用IOMMU。这种情况可能包括:

· 硬件不支持IOMMU
· 平台可能本身就没有IOMMU(比如没有IOMMU模拟的VM)
· 软件设备(例如,DPDK的内核网络接口(KNI)PMD)不支持作为VA的IOVA模式
· 一些IOMMU(通常是模拟的IOMMU)的地址宽度可能有限,虽然这不妨碍用作VA的IOVA模式,但限制了其有效性
· 在非Linux *的操作系统上使用DPDK

但是,这些情况还是相对较少,绝大多数情况下,作为VA的IOVA模式都可以正常工作。

IOVA模式的选择

很多情况下,DPDK默认选择作为PA的IOVA模式,因为从硬件角度这是最安全的模式。所有给定的硬件(或软件)PMD至少都可以保证支持作为PA的IOVA模式。尽管如此,如果条件允许,还是强烈建议所有DPDK用户使用作为VA的IOVA模式,毕竟此模式具有不可否认的优势。

但是,用户不必非要在两者中选择一个。可以自动检测出最合适的IOVA模式,而且默认选项绝对适用于大多数情况,因此不需要用户来做此选择。如果默认选项并不合适,用户可以使用--iova-mode EAL命令行参数尝试使用EAL标志(适用于DPDK 17.11及更高版本)来代替IOVA模式:

1 ./app --iova-mode=pa # use IOVA as PA mode

2./app --iova-mode=va # use IOVA as VA mode

大多数情况下,VA和PA模式不会互相排斥,可以使用任一模式,但在某些情况下,作为PA模式的IOVA是唯一可用的选择。当不能使用作为VA模式的IOVA时,即使EAL参数要求使用作为VA模式的IOVA,DPDK也会自动切换为作为PA模式的IOVA。

DPDK还提供了一个API,可查询运行时正在使用的IOVA模式,但通常这不会在用户应用中使用,因为只有像是DPDK PMD和总线驱动程序才会要求获取这种信息。

IOVA模式和DPDK PCI驱动程序

DPDK本身并不执行所有硬件设备寄存器和中断映射,它需要内核的帮助。为此,DPDK要使用的所有硬件设备都需要绑定到一个通用外围组件互连(Peripheral Component Interconnect, PCI)内核驱动程序。和一般的设备内核驱动程序不同的是,通用此驱动程序并未被锁定到特定的PCI ID集,即针对某类设备的常规驱动程序,可以与任何类型的PCI设备一起使用。

要将设备绑定到通用驱动程序,DPDK用户应该参考DPDK文档。该文档描述了在所有支持的操作系统中如何进行此项操作。但是,关于DPDK支持的各种用户态IO驱动程序及其支持的IOVA模式,还需要有所说明。虽然内核驱动程序和支持的IOVA模式之间看起来可能存在1:1的对应关系,但事实并非如此。下面的部分将讨论Linux上可用的驱动程序。

用户空间IO(UIO)驱动程序

DPDK代码库中最早的内核驱动程序是igb_uio驱动程序。在DPDK最初的发展阶段,这个驱动程序就已经存在了,因此它是DPDK开发人员使用最广泛也是最熟悉的驱动程序。

此驱动程序依赖内核用户空间IO(UIO)基础结构运作,并为所有中断类型(遗留、消息信号中断(MSI)和MSI-X)提供支持,以及创建虚拟功能。它还公开硬件设备通过/dev/uio文件系统注册和中断句柄,然后DPDK EAL将它们用于将它们映射到用户空间并使它们可用于DPDK PMD。

igb_uio驱动程序非常简单,能做的也并不多,因此它不支持使用IOMMU也就不足为奇了。或者,更确切地说,它确实支持IOMMU,但仅在传输模式下,它在IOVA和物理内存地址之间建立1:1映射。igb_uio不支持使用完整的IOMMU模式。因此, igb_uio驱动程序仅支持IOVA作为PA模式,并且根本无法在IOVA中作为VA模式工作。

类似于igb_uio的驱动程序在内核中可用:uio_pci_generic。它的工作方式与igb_uio非常相似,只是它的功能更加有限。例如,igb_uio支持所有中断类型(传统,MSI和MSI) -X),而uio_pci_generic只支持遗留中断。更重要的是,igb_uio可以创建虚拟函数(Virtual Function, VF),而uio_pci_generic则不能;因此,如果在使用DPDK物理函数(Physical Function, PF)驱动程序时创建VF是必需的一步,igb_uio是唯一的选择。

因此,在大多数情况下,igb_uio与uio_pci_generic相同或更可取。关于使用IOMMU的所有限制同样适用于igb_uio和uio_pci_generic驱动程序 - 它们不能使用完整的IOMMU功能,因此仅支持IOVA作为PA模式。

VFIO内核驱动程序

上述驱动程序的替代方案是vfio-pci驱动程序。它是虚拟功能I / O(VFIO)内核基础结构的一部分,并在Linux 3.6版中引入。VFIO基础设施使设备寄存器和设备中断可供用户空间应用程序使用,并可使用IOMMU设置IOVA映射以从用户空间执行IO。后一部分至关重要 - 此驱动程序专为与IOMMU一起使用而开发,在较旧的内核上,如果没有启用IOMMU,它甚至都无法工作。

与直观看法相反,使用VFIO驱动程序允许使用IOVA作为PA和IOVA作为VA模式。这是因为,虽然建议使用IOVA作为VA模式来利用该模式的所有好处,但没有什么能阻止DPDK的设置IOMMU映射的EAL以遵循物理内存布局1:1的方式;毕竟IOVA映射是任意的。在这种情况下,即使使用IOMMU,DPDK也可以在IOVA中作为PA模式工作,从而允许DPDK KNI等工作。但是,仍然需要root权限才能将IOVA用作PA模式。

在更新的内核(4.5+,向后移植到一些旧版本)上,有一enable_unsafe_noiommu_mode选项,允许在没有IOMMU的情况下使用VFIO。这种模式适用于与基于UIO的驱动程序相同的所有意图和目的,并具有所有相同的优点与限制。

内核驱动程序的选择

一般来说,在应用的角度我们并不需要选择内核驱动程序。通常来说,内核驱动程序的选择是由具体的情况来决定的。下面的流程图可以帮助决定在哪些特定情况下可以使用哪个驱动程序:

图5.通用PCI内核驱动程序选择流程图

软件轮询模式驱动程序(PMD)

除此之外,DPDK附带的一系列软件PMD不需要通用内核PCI驱动程序,而是依靠标准内核基础架构来提供硬件支持。这样一来,DPDK几乎可以与任何硬件同时使用,即使DPDK本身不支持它。

PCAP库是用于网络硬件的广泛使用和支持的数据包捕获库。目前,DPDK具有可用于PCAP库的PMD。DPDK还支持具有AF_PACKET PMD的Linux网络,而且还可以在DPDK中本地支持AF_XDP。虽然使用这些PMD会有性能成本(有时相当可观的),但其优点是设置很容易,而且这些PMD一般完全不在乎IOVA模式。

总结

本文深入介绍了DPDK如何处理物理内存,也概述了在使用各种Linux *内核驱动程序时DPDK提供的物理寻址功能。

这是关于DPDK内存管理系列文章的第二篇。第一篇文章概述了DPDK内存管理子系统基础的关键原则。本系列接下来的文章将以历史的视角,回顾DPDK长期支持(LTS)版本17.11及更早版本中提供的内存管理功能,同时也会介绍18.11及更高版本DPDK版本中做出的更改和提供的新功能。

文章转载自DPDK与SPDK开源社区


  • 本站原创文章仅代表作者观点,不代表SDNLAB立场。所有原创内容版权均属SDNLAB,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用,转载须注明来自 SDNLAB并附上本文链接。 本站中所有编译类文章仅用于学习和交流目的,编译工作遵照 CC 协议,如果有侵犯到您权益的地方,请及时联系我们。
  • 本文链接https://www.sdnlab.com/23551.html
分享到:
相关文章
条评论

登录后才可以评论

SDNLAB君 发表于19-09-12
1