DPDK内存篇(四):18.11及早期版本

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

导读

这是有关DPDK内存管理功能系列文章的最后一篇,之前的文章集中于介绍DPDK内存管理背后的一般概念,从而深入概述了各种输入输出虚拟地址(IOVA)相关选项,并描述了DPDK 17.11中与内存相关的功能,本文涵盖了有关DPDK内存管理的所有最新的和最重要的知识。

DPDK 18.11版本是长期支持的发行版本,其内存管理功能是在18.05和18. 08发行周期内重做DPDK内存子系统所完成的工作成果。与DPDK 17.11相比,面向用户的API貌似没有什么变化,但实际上内部变化翻天覆地,还新加入了许多以前根本不可能实现的新功能和API。

动态内存管理

DPDK 18.11的最大变化是可以在运行时增加和减少内存使用量,从而消除了DPDK内存映射为静态的情况,这带来了许多可用性方面的改进。

在DPDK 17.11中,运行没有任何环境抽象层(EAL)参数的DPDK应用会保留所有可用的大页内存供其使用,且不会为其他应用程序或其他DPDK实例留下任何大页内存。对于DPDK18.11而言情况有所不同,DPDK仅保留应用运行所必须的内存量。在这种意义上,DPDK现在性能更好,并且使DPDK与其他应用程序完美配合所需的工作也更少。

同样,不再需要事先知道应用程序的内存需求,DPDK的内存映射可以动态增加和减少,因此DPDK内存子系统可以根据需要自动增加其内存使用量,并在不再需要时将内存返回给系统。这意味着部署DPDK应用程序所需的工作更少,因为现在DPDK可以自行管理其内存需求。

DPDK 18.11和IOVA连续性

DPDK 18.11中的一项基本后台更改是,不能保证虚拟地址(VA)连续内存是IOVA连续的;两者之间不再有任何关联。在作为VA模式的IOVA下,IOVA布局仍然像以前一样遵循VA布局,但是在作为PA模式的IOVA下,PA布局是任意的(物理页面要向后映射并不罕见)。

图1. DPDK版本之间的IOVA布局比较

这种改变并不像看起来那样具有颠覆性,因为实际上没有多少数据结构需要IOVA连续内存。所有软件数据结构(环,内存池,哈希表等)仅需要VA连续内存,而不在意底层物理内存布局。这使得在早期DPDK版本上工作的大多数用户应用程序可以无缝过渡到新版本,而无需更改代码。

尽管如此,某些数据结构确实需要IOVA连续内存(例如,硬件队列结构),对于这些情况,引入了新的分配器标志。使用此标志,可以使memzone分配器尝试分配IOVA连续的内存。

旧版模式

早期版本的DPDK(17.11或更早版本)通过在初始化时按其物理内存地址对所有大页面进行排序来保留大量IOVA连续内存。在18.11中,情况不再如此,因为内存是在运行时以逐页的粒度保留并释放的。这意味着除非DPDK在以VA模式的IOVA中运行,否则可能不会有大块的IOVA连续内存可用,这通常不是问题,即使是驱动程序所需的最大的IOVA连续区域,大小也不会超过大页的大小。

尽管如此,即使大多数基于DPDK早期版本的应用程序都可以在新的DPDK版本上正常运行,某些用例仍需要大量IOVA连续内存,并且由于某种原因,不能将IOVA作为VA模式。由于DPDK不再按物理地址对内存进行排序,因此在作为PA模式的IOVA中运行的DPDK 18.11不可能保留大量IOVA连续内存。

为了解决这些罕见的用例,DPDK 18.11提供了带有 —legacy-mem EAL命令行参数使之按照传统模式保留内存。旧模式细致地模拟了早期DPDK版本的工作方式。也就是说,要在初始化时保留内存,对其排序,并使内存映射保持静态。而它的缺陷是完美的保留了所有旧的限制,例如无法在运行时保留/释放内存,以及对基础物理内存布局的依赖。

新的EAL功能和命令行标志

在DPDK 18.11中,某些EAL参数已更改其含义,并且还添加了一些新参数,以增加新功能或替换旧参数,当然它们不是必须使用的,并且其默认配置中的DPDK 18.11比以前的DPDK版本要好得多。但是,了解这些新功能在某些特定情况下可能会有用。

注意:这些新功能均无法在旧版模式下工作。

控制DPDK内存使用

更改了与内存保留相关的参数—m和—socket-mem以指示DPDK将使用的最小数量。例如,使用—socket-mem 1024,1024初始化DPDK会在非均匀内存访问中保留1 GB(NUMA)节点0和1,并且在应用程序的生存期内将永远不会释放该内存。但是,如果需要,更多的内存可以并且也会被添加。

因此,对于DPDK 17.11及更早版本,必须使用-m或—socket-mem标志运行DPDK,但现在它们是可选的,仅在应用程序需要保证的最小保留内存可用的情况下可供每时每刻使用。

也可以使用—socket-limit EAL命令行参数来限制每个NUMA节点的DPDK内存使用,它与—socket-mem参数相似,因为它接受每个NUMA节点的值列表,并且将设置允许DPDK从系统保留多少内存的上限。

图2. 解释—socket-mem和—socket-limit

例如,如果一个用例将DPDK的内存使用限制在256 MB和1 GB之间,则可以使用以下命令行:

./app —socket-mem 256,256 —socket-limit 1024,1024

在双插槽系统上,这将为每个NUMA节点保留256 MB,并将每个NUMA节点的内存使用限制为1 GB。这样一来,DPDK的内存使用量将从每个NUMA节点的256 MB开始,并根据需要进行增长和减少,但永远不会低于256 MB或高于1 GB。

单个文件段

较旧的DPDK版本在hugetlbfs文件系统中的每个大页上存储一个文件,这适用于大多数用例,但有时会出现问题,特别是,vhost-user后端的Virtio将与后端共享文件,并且有可共享文件描述符数量的硬性限制。当使用大页(例如1 GB的页面)时,它可以很好地工作,但是在页面大小较小的情况下,文件数量会很快超过文件描述符限制。

为了解决此问题,版本18.11中引入了一种新模式,即单文件段模式,该模式通过—single-file-segments EAL命令行标志启用,这使得EAL在hugetlbfs中创建的文件更少,并且使具有vhost-user后端的Virtio甚至可以在最小页面大小下工作。

内存模式

在DPDK 17.11中,有一个—huge-unlink选项可以在创建和映射大页面文件之后立即从hugetlbfs文件系统中删除它们。在DPDK 18.11中,这仍然有效,但是有一个新的EAL命令行参数—in-memory,将激活所谓的内存模式,建议使用它代替—huge-unlink。

在这种模式下,DPDK不会在任何文件系统(hugetlbfs或其他文件系统)上创建文件。DPDK首先避免创建任何文件,而不是创建然后删除文件(因此仍然需要hugetlbfs文件系统)。实际上,在此模式下甚至不需要greattlbfs挂载点,因此使用此模式使DPDK更加易于设置,在hugetlbfs挂载点不常见的环境中工作(例如云本地场景)。

此外,与—huge-unlink仅处理大页文件并且不会阻止EAL创建任何其他文件不同的是,—in-memory模式还覆盖了EAL创建的其他文件,这实际上允许DPDK运行和关闭只读文件系统,同时避免申请对该系统的写访问权。

由于此选项是—huge-unlink选项的超集,因此关于无法使用辅助进程的限制也适用于此。

DPDK 18.11中的新API

除了提供EAL命令行参数的新功能外,DPDK 18.11还添加了许多新的API,使用户可以更好地利用此版本中添加的新功能。

其他与内存相关的回调

以前,DPDK内存映射是静态的,因此任何依赖于对DPDK内存映射结构的了解的工具都只能扫描内存映射一次,并确信其认识始终是最新的。从DPDK 18.05开始,情况就不再是这样:内存布局可以随时更改,因此此类功能必须遵循内存映射更新。

为了便于跟踪内存映射更新,DPDK 18.11添加了一个新的API,通过回调注册和侦听内存映射更新,每次保留新段以及每次要删除段时都会触发这些更新。这样,诸如VFIO子系统之类的东西就可以无缝跟踪内存映射更新,而无需任何用户交互。

除此之外,还提供了另一组回调,以提供对分配的一些控制。—socket-limit EAL参数设置的分配限制是静态且无条件的,还有一个API用于注册回调,当分配超过某个(每个NUMA节点) 阈值时触发回调,并让用户有机会拒绝分配。例如,这将允许对分配设置软限制,可以接受超出限制的几百兆字节,但拒绝超出限制千兆字节。

图3.与内存相关的回调流程

便利功能

以前,遍历内存映射并查找段或页面地址是一项手动工作。每个这样的迭代都一遍又一遍地复制了相同的迭代代码。现在,DPDK内存映射的内部更加复杂,这种方法不再可行(如果有的话)。为解决这个问题,DPDK 18.11 API还带来了许多便利功能,可提供一种统一的方式来处理这些微小但有用的事情。

例如,现在可以使用各种功能来迭代内部DPDK页表,还可以使用函数将虚拟地址映射到单个页或页表,以及将IOVA地址反向映射到虚拟地址。鼓励应用程序在需要获取有关DPDK内存映射的信息时使用这些功能,因为这些功能还可以正确执行所有必要的内部锁定。

多进程注意事项

DPDK 18.11的内存映射是动态变化的,这影响了主进程和辅助进程的工作方式。主进程处理所有的分配/取消分配和同步,而辅助进程仅跟随主进程中发生的内存映射更新。因此,如果主进程停止执行,则内存映射将变为静态。

在何时可以使用某些函数(与内存相关的回调,分配函数等)上也有一些限制,这些在DPDK API文档以及“ DPDK程序员指南”中都有详细记录,因此我们建议您熟悉这些功能限制,然后再尝试使用新功能。

带有Vhost-user后端的Virtio的进一步改进

一些用例(例如,带有vhost-user后端的Virtio)需要访问与每个大页内存段相对应的文件描述符。在DPDK 18.11中,这些文件描述符现在存储在内部,并通过API公开,从而可以改进有vhost-user后端的Virtio的可靠性,以及可以在内存模式(即,没有任何hugetlbfs挂载点)中将Virtio与vhost-user后端一起使用。此外,在DPDK 19.02和更高版本中,这些文件描述符也可以在非Hugepage模式下使用,只要使用IOVA作为VA模式(以及最近的内核),就可以在带有vhost-user后端的Virtio上使用DPDK,而完全不需要大页。

外部内存

DPDK 18. 11的另一个新功能是支持外部分配的内存。这是一组新的API,旨在允许使用原本不是DPDK保留的内存,但仍使用DPDK的内存管理工具(例如在该内存上分配一个内存池)。这些API允许本机使用DPDK中的任何类型的内存(例如内存映射存储)。

此外,在版本19.02中添加了另一组与外部内存相关的API。这些API允许向DPDK注册外部内存,但避免与其一起使用内置的分配器功能。这些API允许使用其中外部存储器为在DPDK外部进行完全管理,但是DPDK的内部结构(例如驱动程序)需要访问有关此内存的信息。

结论

在本系列的四篇文章中,我们介绍了DPDK的内存子系统为何按其方式工作,提供了有关DPDK如何处理物理寻址的深入解释,并跟踪了其发展进程:从高性能,但静态且僵化,到17.11时的更加动态,再到兼具高性能和延展度高的18.11版。

可以在DPDK文档和代码中找到更深入的信息,还邀请每个人加入我们充满活力的开源社区,并参与讨论,代码审查,会议和行业活动。有关社区和开发过程的更多信息,请访问DPDK社区网站。

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


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

登录后才可以评论

SDNLAB君 发表于20-10-23
1