LWN:更加动态的swiotlb!

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

A more dynamic software I/O TLB

By Jonathan Corbet
August 24, 2023
ChatGPT assisted translation
https://lwn.net/Articles/940973/

内核的软件 I/O 转换后备缓冲区("swiotlb", software I/O translation lookaside buffer)是 DMA 支持相关实现的一个隐蔽角落。最初引入 swiotlb 是为了使一些有特殊限制的设备能够进行 DMA,人们本可以预期随着新型外设的出现,它会逐渐消失。然而,swiotlb 已经被证明在其原始使用场景之外也非常有用。Petr Tesarik 的这组 patch 现在就在更新 swiotlb 功能以便将其继续无限期地使用下去。

任何具备合理功能的 I/O 设备的基本特征之一就是要能执行 DMA,可以直接访问主内存中的数据而无需通过 CPU。没有 DMA 的话,I/O 性能将严重受限。但是,有些设备比其他设备更适合 DMA。旧的 PC 级硬件设备就可以看出这个体系结构的历史,其中通常限制了 DMA 传输使用 24 位地址空间,这意味着它们只能访问系统中最低区域的 16MB 内存。这在早期来说是足够了,但随着内存容量的增长,很快这就变成了一个限制。另一个常见的问题是 32 位限制,也导致了只能访问低 4GB 内存。

内核具有一个更加通用的功能,希望尽可能地把任何设备都能支持起来。因此比如说在处理具有寻址限制的设备时,内核将尽力在设备可以访问到的范围内分配内存。然而有时这是无法做到的。例如无法在设备访问区域内分配出大块连续内存。但更常见的情况是要传输的数据没有考虑设备的限制。例如,用户空间的 buffer 和内核的 page cache 就不是仅限于低内存区域的,它们有可能被分配给具有地址限制的设备,这该怎么办。

在这种情况下的解决方案是使用“弹跳缓冲区(bounce buffer)”。内核维护了一些低地址内存用来支持 DMA 操作。在有需要时就将分配一部分内存供设备用于 I/O。内核会根据需要将数据从实际 buffer 复制到弹跳缓冲区里(用于写操作,发生在进行写入操作之前)或从弹跳缓冲区复制到实际 buffer(用于读操作,是在操作之后)。弹跳缓冲区会拖慢一些速度,但这总比无法执行 I/O 要好。

管理弹跳缓冲区的内存,就是 swiotlb 的工作了。这个小规模的子系统曾经可能被期望随着时间的推移就可以移除了。随着新型设备替代旧型设备,寻址限制应该会消失才对。对于寻址限制没有消失的情况,也有越来越多地设备使用了 I/O 内存管理单元(IOMMU),可以为设备提供了一个单独的地址空间,从而将缓冲区映射进去,来解决问题。无论采用哪种方式,内核都不应再通过 CPU 进行 I/O 数据的搬移动作。

然而,正如 Tesarik 在 swiotlb 这组 patch 的封面信中指出的那样,事情并没有完全按照人们的期望发展。硬件供应商继续推出一些系统(特别是在嵌入式领域),都使用了比其内置设备可以寻址的空间更多的内存。还出现了其他一些用例,其中一个就是机密计算(confidential-computing)应用,其中系统运行所使用的是加密的内存,外设就无法访问该内存。机密计算系统最终可能会对其所有 I/O 都使用弹跳缓冲区,不管使用的设备是否有寻址限制。

因此,看起来 swiotlb 将会在一段时间内继续保留下来,这就有必要修复其中一些限制了。默认情况下,swiotlb 在系统引导时分配 64MB 的内存;可以使用 swiotlb= 这个命令行参数来调整这个行为。这种分配区域故意设置得非常小,希望不要占用太多内存,同时希望能满足系统的少数的(希望如此)弹跳缓冲需求就好。但是,对于某些内存非常少的嵌入式系统来说,64MB 可能也已经太大了,而对于需要大量 I/O 进行弹跳缓冲的系统来说,这个数字又太小了。换句话说,swiotlb 需要变得更加动态可配才好。

具有寻址限制的设备通常也无法执行 scatter/gatter I/O,也就是说它们需要看起来是一个单一的、物理连续的 buffer。因此,swiotlb 必须能够分配物理连续的内存,这通常会随着系统运行一段时间之后内存变得碎片化而变得更加困难;这是为什么 swiotlb 内存要在引导过程的早期来进行分配的原因之一。然而,动态 swiotlb 将不得不需要在系统运行的任何阶段来随时分配内存。

好消息是,随着时间的推移,分配物理连续内存块变得更容易了。将可移动(movable)和可回收(reclaimable)的分配区域隔离开的工作使得内存整理(memory compaction)更加有效;这有助于分配 huge page 以及其他大型的、连续的内存范围,弹跳缓冲区就是其一。内核的连续内存分配器(CMA, contiguous memory allocator)在这方面也很有帮助。因此,期望可以在系统的运行过程中的任何时候分配弹跳缓冲区的内存已经是一个合理需求了。

应用了动态 swiotlb 的补丁的系统将在最开始时都先分配相同大小的内存,尽管用户可能希望将其减小。每当无法满足弹跳缓冲区的请求时,swiotlb 就会分配更多的内存。然而,这里情况会变得有点复杂;可能会在诸如中断处理程序之类的上下文中来进行弹跳缓冲区的分配,这些上下文是不能阻塞的。因此,swiotlb 不能在此时来创建个新的内存池来满足未来的一些请求。相反,必须要直接从内存管理子系统来分配出所需的空间,并通过 workqueue 来启动一个函数,在那种限制更加少的上下文中执行较大 size 的分配。

当 swiotlb 的内存池用完时,分配的“临时(transient)”缓冲区将在取消映射(unmap)后就还给系统。然而,常规内存池将永远保留下来;在内存池增大时,它也会保持较大值。这个策略可能是合理的;需要一定量的弹跳缓冲区的设备可能在未来也会继续需要这些缓冲区。与此同时,整个 swiotlb 可能会保持足够小,以至于保留的这些内存不应该成为一个问题。不过,未来很可能会看到 swiotlb 添加某种收缩(shrinker)机制。

这组补丁已经经历了七次修订,开发人员似乎多数人对它都感到满意。Christoph Hellwig 已经合入了这组 patch,预计将在 6.6 合并窗口期间推送到 mainline。当然,像其他内核项目一样,这里的工作永远不会真正完成;Tesarik 已经透露,正在开发后续的 patch。

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

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

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

format,png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值