LWN:slab 分配器的sheaves以及任意上下文的分配功能!

关注了就能看到更多这么棒的文章哦~

Slab allocator: sheaves and any-context allocations

By Jonathan Corbet
April 1, 2025
LSFMM+BPF
Gemini-1.5-flash translation
https://lwn.net/Articles/1016001/

内核的 slab(缓存)分配器负责按需提供小 size 的对象;它的性能和可靠性对于整个系统的运作至关重要。在 2025 年的 Linux 存储、文件系统、内存管理和 BPF 峰会上,内存管理主题的两个相邻会议深入探讨了 slab 分配器的当前工作。第一个会议的重点是新的 sheaves 特性,而第二个会议讨论了一组可以在任何上下文中安全调用的分配函数。

Ask sheaves

Sheaves 是用于 slab 分配器的一个新增的缓存层,旨在提高重度用户的性能;有关其工作方式的详细描述,请参见LWN 之前文章 。 slab 分配器的维护者 Vlastimil Babka 主持了第一次会议,讨论了这个特性及其未来。他首先表示,由于大量的 slab 分配,某些工作负载的速度正在下降;其中一些可以用 slab 分配器如何使用嵌入式空闲列表来跟踪 folios(页框)中的空闲对象来解释。还必须跟踪包含 slab 的页框,并且维护 NUMA(Non-Uniform Memory Access,非一致内存访问)局部性增加了另一个复杂性级别。

Sheaves 通过维护一个简单的 per-CPU 缓存来绕过大部分工作,该缓存的形式是每个 CPU 两个 sheaves(空闲对象数组)。目前这是一个可选择加入的特性,但他想知道是否应该为所有 slab 启用它。缓存使释放对象变得廉价,并且与 read-copy-update(RCU,读-复制-更新)系统集成,以降低 kfree_rcu() 的成本。分配器还可以根据请求提供预填充的 sheaves,这些 sheaves 可用于保证在受限上下文中进行分配。最后一个特性几乎是没有额外代价的,因为幸运的是,当请求到达时,分配器会有一个具有足够对象的 sheaf 存在。

总而言之,他说,来自 sheaves 层的初步结果是 promising(很有希望的),但它与 slab 调试的交互需要改进。他遇到的一个问题是:相对来说缺乏 NUMA 感知是否是一个大问题? slab 分配器中完整对象释放路径的大部分复杂性在于它需要费力地将释放的对象返回到正确的 NUMA 节点。 Sheaves 简化了所有这些逻辑,只是将空闲对象存储在本地 CPU 的 sheaf 中,结果是会有更多的跨节点分配。在具有较高的跨节点访问成本的系统上,这可能会改变某些工作负载的性能特征。对于 maple trees(枫树),它是 sheaves 的第一个用户,其分配在系统范围内使用,因此 NUMA 局部性不太重要。

他再次想知道是否应该为所有用户启用 sheaves ,尽管他担心这样的举动可能会将当前的 slab 分配器变成更像它的前身(并且最近已被删除)。他建议,也许 sheaves 可以取代当前在 slab 分配器中完成的其他一些缓存,甚至可以完全取代当前的空闲对象跟踪。这将带来一些挑战;如果没有空闲对象跟踪,分配器将需要另一种方法来识别给定的 slab 已被完全释放并且可以返回到页面分配器。

当会议结束时,Rik van Riel 说锁竞争现在在 slab 分配器中可能是一个真正的问题,并且 sheaves 看起来可以提供帮助。 Hoang Nhat Pham 同意,他说他工作的系统具有大量的 CPU,但只有一个 NUMA 节点,从而导致大量的锁竞争,而 per-CPU 缓存可以改善这种情况。

kmalloc() for any context

Alexei Starovoitov 随后接管,与 BPF 轨道共同主持一个关于新增一个版本的 kmalloc() (它是 slab 分配器的一部分)实现的工作会议,该版本期望可以从任何上下文中的 BPF 程序中调用。一些 BPF 附加点(attachment points),包括 tracepoints(跟踪点),可以从任何上下文调用,甚至包括 non-maskable interrupts(NMI,不可屏蔽中断)上下文。系统满足分配请求的自由度在这些上下文中可能会受到高度限制,因此必须小心。几年来,BPF 子系统使用自定义分配器来满足此需求,但是希望减少内核中的分配器(和对象缓存)的数量。有关他提出的解决方案的概述,请参见LWN 文章 。

Starovoitov 首先感谢 Babka 在 2024 年 LSFMM+BPF 上的关于 slab 分配器的会议 ,如果没有那次会议,他完全没有勇气尝试这项工作。他说,内核中有很多围绕 slab 分配器的 wrappers(包装器);每个包装器的存在都有不同的理由。对于 BPF 来说,主要理由是需要在任何上下文中进行分配;性能也很重要,但这是一个次要问题。

他说,11 年前,只有 tracing(跟踪)和 networking(网络)子系统支持 BPF 程序。 Tracing 程序不分配内存,并且 networking 程序仅在进程上下文中调用,因此内存分配不是问题。但是,随着时间的流逝,BPF 程序中对内存分配的需求不断增长;最初是通过 BPF “maps(映射)”特性来解决的。映射可以具有预分配的内存,但是每个映射最终可能会预分配 10KB 的内存。这使得解决方案快速,但也导致大量内存浪费。已经进行了一些尝试来改善这种情况,但是它们往往效果不佳并且没有被大量使用。

因此,添加了 BPF 内存分配器。它像 kmalloc() 一样提供 per-CPU buckets(桶)(用于分配不同大小的对象),但它被设计为 NMI 安全且快速。它的核心是一个名为 bpf_mem_alloc() 的函数来执行实际的分配。它不像映射预分配那样浪费,但是仍然有很多内存只是位于其缓存中。可以通过像 slab 分配器那样合并缓存来解决其中的一些浪费问题 — 但这只是沿着重新实现 slab 分配器已经具有的特性的路径上的又一步。

因此,如果能够仅使用 kmalloc() ,那将是很好的,但是该分配路径很复杂,并且在需要满足请求时会进入越来越慢的路径。最慢的步骤可能涉及从 zone(区域)分配器获取块或唤醒 kswapd 线程。在错误的上下文中,该路径可能导致内核死锁;他说,使用它将是“一颗定时炸弹”。

因此,他添加了 try_alloc_pages() ,它处理任务的较低级别的页面分配部分。它遵循相同的分配路径,但停止采取可能导致系统死锁的措施。因此,与其他分配路径相比,它更有可能失败。他说,所需的大部分逻辑已经到位,只需要进行一些更改。 Van Riel 想知道,由于在此路径中使用了 conditional locking(条件锁),如果尝试获取锁以将分配记入 control group(控制组)失败会发生什么? Starovoitov 回答说,在这种情况下,整个分配将失败。

try_alloc_pages() ,从其名称可以看出,是一个 page-level(页面级别)分配器;下一步是创建一个 all-context(全上下文)版本的 kmalloc() 。他说,这应该相当简单,主要涉及机械性的更改,但他正在考虑放宽 kmalloc() 所坚持的节点局部性要求。分配一些东西总比因为唯一可用的内存位于远程节点上而导致请求失败要好。如果 slab 分配器需要更多内存来满足请求,它将使用 try_alloc_pages() 来尝试分配它。他说,代码已编写完成,但尚未准备好进行审查。

Van Riel 询问了此新接口中的释放工作方式;Starovoitov 回答说,如果无法获取所需的锁来正确释放内存对象,则会将该内存以无锁方式添加到 linked list(链表)中,并且下一个非原子释放调用将处理它。

最初发布此代码时,内存管理子系统维护者 Andrew Morton 表示反对。在本次会议期间,他一直保持沉默,直到最后,Starovoitov 问到,这种沉默是否意味着他的疑虑已得到解决。 Morton 回答说,他对这项工作仍然不太满意,这项工作解决的 use cases(用例)相对于核心页面分配器而言根本不重要。他说,这增加了一些运行时和维护开销,最好将其放置在其他地方,以便只有其用户才能承担成本。 Starovoitov 说,这对页面分配器没有可衡量的性能影响; Morton 表示怀疑,并说可维护性也很重要,但似乎勉强接受了此更改。

try_alloc_pages() 的更改在 6.15 合并窗口期间 合入了 mainline (主线)。

Starovoitov 发布了 本次会议的幻灯片 。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值