关注了就能看到更多这么棒的文章哦~
Warming up to frozen pages for networking
By Jonathan Corbet
March 13, 2025
Gemini-1.5-flash translation
https://lwn.net/Articles/1013408/
当 6.14 内核在本月晚些时候发布时,它将包含一系列常见的内部更改,用户通常不会注意到这些更改,但可能会注意到性能改进相关的更改。其中一项更改是 frozen pages(冻结页),这是一种内存管理优化,通常不会引起太多关注。然而,当 Hannes Reinecke 报告了 6.14 中的一个崩溃时,冻结页突然进入了人们的视野。目前有一个针对此问题的权宜之计,但要彻底解决这个问题,似乎还需要做大量预料之外的工作。
内核使用引用计数来跟踪哪些内存页正在使用中。例如,一个共享内存页被映射到多个进程的地址空间中,那么该页将跟踪来自每个进程的引用。只要至少有一个进程存在并保持该页的映射,该页就不会被释放。然而,引用计数的管理并非没有成本;它们的操作需要昂贵的原子操作,并且计数本身也会占用内存。这就导致了一种期望,即在没有严格必要的情况下,可以不用引用计数。例如,slab (slab 分配器) 分配器跟踪其管理的每个页内的对象,而不需要为整个页单独设置引用计数。但在 6.14 之前的内核中,slab 页仍然会被适当地进行引用计数。
引入冻结页是为了在可能的情况下消除这种开销;在冻结页中,引用计数设置为零并保持不变。由于页的生命周期是单独跟踪的,因此无需递增或递减其计数,从而避免了这种开销。最终,将有可能完全消除冻结页的引用计数(而不仅仅是将其保持为零),但要达到这一点,还需要做更多的工作。
Reinecke 在网络子系统中遇到了一个内核崩溃;在仔细地对问题进行二分查找后,他确定了 将 slab 分配器切换到冻结页的提交是罪魁祸首。随后进行了一些广泛的调试和讨论,最终弄清楚是网络代码试图对一个冻结页的引用计数进行递增操作,导致各种内部混乱,并最终导致崩溃。
通过网络发送数据可能是一个复杂的操作,涉及分散在物理内存中的多个页。网络子系统和其他子系统一样,通过创建一个描述要传输的各种数据块的向量来处理这种复杂性。包含在该向量中的所有页都需要在操作进行期间保持存在和有效,因此每个页的引用计数在开始时递增,并在操作完成后递减。内核中的许多 I/O (Input/Output,输入/输出) 路径传统上都遵循相同的模式。
然而,随着向 folios (更高级的页管理结构) 的转变以及避免不必要的引用计数操作的愿望,这种模式已经发生了变化。I/O 路径需要尽可能避免引用计数操作,尤其是在根本无法进行这些操作时,因此这些路径已经发生了改变以适应这种情况。至少,它们在内核的某些部分已经改变了;Matthew Wilcox 表示有些惊讶,因为他了解到这项工作只完成了一部分:
我认为我们已经完成了所有必要的工作,以消除这些毫无意义的引用计数增加。结果证明这只在块设备端(例如提交 e4cc64657bec)。那么,网络需要什么才能理解某些 iovecs 不需要处理引用计数?
Reinecke 回答说,这种改变并不容易;代码很复杂,获取引用的地方可能与必须释放引用的地方相距甚远,而且实际上可能位于完全不同的层中。与此同时,Wilcox 发布了 一个补丁,在内存管理代码中添加了一些检查,以防止尝试操作 slab 页上的引用计数,slab 页是 6.14 内核中唯一的冻结页。这个改变被描述为“一个快速的 hack”,旨在避免完全恢复使用冻结页。
即便如此,Vlastimil Babka 又进行了 一次更改,直接修改了网络代码,才使问题消失。Reinecke 承认了这个修复有效,但抱怨说需要跟踪特定页是否需要更新其引用计数:
以前我们可以简单地进行 bvec 迭代,获取每个页的引用,然后开始处理。现在突然调用者必须检查它是否是一个 slab 页,并且不获取该页的引用。不仅如此,他还必须记住在完成时 不要 释放引用。当然,还要通过所有层跟踪 get_page() 和相应的 put_page() 调用。真要这么做吗?
然而,他的抱怨并没有引起太多的同情。相反,Wilcox 断言,网络层需要停止使用页的引用计数,这样既可以删除内存管理 hack,又可以提高网络性能。他补充说:“让我担心的是,网络方面的人还没有回复这个帖子。他们不在乎吗?”为了唤起这些回应,他将邮件主题更改为:“搞网络的人闻起来很滑稽,而且作出了错误的人生选择”。
即使这样,也没有激发网络子系统这边给出任何重要的回应。唯一的回复来自 Cong Wang,他 建议 “使用 AI (Artificial Intelligence,人工智能) copilot 或任何自动化工具”可能会有所帮助——这个建议似乎没有获得任何支持。Wilcox 已经 发布了他的权宜之计,作为一个单独的补丁,人们预计它会在 6.14 发布之前进入该版本。
截至本文撰写之时,情况就是这样。目前的问题应该已经修复,但关于跨内核的页引用计数的更广泛问题仍然没有答案。也许即将到来的 Linux Storage, Filesystem, Memory-Management, and BPF Summit (Linux 存储、文件系统、内存管理和 BPF 峰会) 将会包含关于这个问题的讨论;敬请关注。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~