| RFC-0223:zx_vmo_transfer_data | |
|---|---|
| 状态 | 已接受 |
| 涉及的领域 |
|
| 说明 | 引入了一个名为 zx_vmo_transfer_data 的新系统调用,可高效地将数据从一个 VMO 移至另一个 VMO。 |
| 问题 |
|
| Gerrit 更改 | |
| 作者 | |
| 审核人 | |
| 提交日期(年-月-日) | 2023-05-22 |
| 审核日期(年-月-日) | 2023-08-14 |
摘要
我们建议添加一个新的系统调用 zx_vmo_transfer_data,以便调用方将页面从一个 VMO 移至另一个 VMO。您可以将其视为一种效率提升,它允许数据移动,而不会产生复制开销。
设计初衷
过去对 Fuchsia 性能的分析发现,Bootfs 的深层分层 CoW 克隆链在查找页面时会导致搜索时间过长。 此系统调用将通过将克隆链替换为包含 Bootfs 各个条目的“拼接”VMO 来减少此开销。
虽然 Bootfs 是当前的设计初衷,但未来可能还会有其他情况,在这些情况下,移动页面的能力可以提高性能。
利益相关方
哪些人会关心此 RFC 是否被接受?(此部分是可选的,但建议填写。)
Facilitator: cpu@
Reviewers: rashaeqbal@, jamesr@
Consulted: mcgrathr@, maniscalco@, adanis@, eieio@
Socialization: This proposal was socialized as a one pager to the Zircon team.
设计
我们将向 Zircon 系统调用 API 添加以下系统调用:
zx_status_t zx_vmo_transfer_data(zx_handle_t dst_vmo,
uint32_t options,
uint64_t offset,
uint64_t length,
zx_handle_t src_vmo,
uint64_t src_offset);
其中:
dst_vmo是目标 VMO 的句柄。此句柄必须具有ZX_RIGHT_WRITE。options是当前未使用的字段,允许将来扩展 API。offset是将页面移入目标的偏移量。length是要移至目标的字节数。src_vmo是源 VMO 的句柄。此句柄必须具有ZX_RIGHT_READ和ZX_RIGHT_WRITE。src_offset是从源检索页面的偏移量。
此系统调用会将 src_vmo 中的 [src_offset, src_offset + length) 范围内的页面移至 dst_vmo 中的 [offset, offset + length) 范围。它在功能上等同于从 src_vmo 到 dst_vmo 的 memmove,然后取消提交 src_vmo 中的关联页面。但是,实现此目的的机制不同;支持页面实际上是在 VMO 之间移动,而不是复制数据。这样可以实现更好的性能。尽管机制不同,但此系统调用与 memmove 具有相同的语义,即支持提供重叠的源区域和目标区域。
读者可能想知道,如果系统调用移动页面,为什么它被称为 zx_vmo_transfer_data 而不是 zx_vmo_transfer_pages。这是经过深思熟虑的选择,以防我们将来想要放宽严格的页面对齐要求。例如,我们可能希望支持用户请求传输一个半页面的用例。在这种情况下,我们会移动第一页,然后复制剩余的半页。这仍然比用户进行简单的复制更高效,因为它允许我们绕过将目标页面归零。请注意,初始实现不支持此用例;此处提及它仅是为了提供有关命名选择的背景信息。
目标范围内的现有页面将被覆盖。目标 VMO 的所有映射也将看到新内容。如果目标 VMO 有子项,则子项的类型将影响子项看到的内容。
类型为 ZX_VMO_CHILD_SNAPSHOT 和 ZX_VMO_CHILD_SNAPSHOT_MODIFIED
的子项将继续看到旧内容。所有其他类型的子项都将看到新内容。移动完成后,src_vmo 中的页面将被归零。
移动可能会因多种原因而失败。请参阅下面的“错误”部分,了解可能出现的错误代码及其返回场景的完整枚举。
如果移动失败,则 src_vmo VMO 中的任意数量的页面可能已移至 dst_vmo。我们不保证移动了多少数据。
但是,我们可以保证在满足以下条件时调用将成功:
- 不满足会导致下面列出的错误的任何条件。
- 在运行此操作时,
src_vmo和dst_vmo不会被任何其他线程修改。
在此上下文中,“修改”是指直接对 VMO 或对 VMO 的引用(例如切片、引用子项等)执行写入/调整大小/固定操作。
修改任何类型的快照的父项、子项或同级项都不应导致任何错误,但根据快照的不同,您可能会遇到写入撕裂。如果您操作 SNAPSHOT_AT_LEAST_ON_WRITE VMO 的父项,则可能会发生写入撕裂,因为实际传输没有承诺的原子性。请注意,在从 SNAPSHOT 子项传输页面的情况下,如果该特定页面尚未进行写入时复制,我们可能需要执行复制,即分配新页面。
以下是此系统调用可以返回的错误及其含义:
ZX_ERR_BAD_HANDLE:src_vmo或dst_vmo不是有效的 VMO 句柄。ZX_ERR_INVALID_ARGS:offset、length或src_offset未与页面 对齐。如前所述,我们可能希望将来移除此限制。ZX_ERR_ACCESS_DENIED:src_vmo没有ZX_RIGHT_WRITE和ZX_RIGHT_READ,或者dst_vmo没有ZX_RIGHT_WRITE。ZX_ERR_BAD_STATE:src_vmo或dst_vmo中指定范围内的页面已固定。ZX_ERR_NOT_SUPPORTED:src_vmo或dst_vmo是物理 VMO、连续 VMO 或分页器支持的 VMO。我们将来或许能够支持分页器支持的 VMO。ZX_ERR_OUT_OF_RANGE:dst_vmo或src_vmo中指定的范围无效。ZX_ERR_NO_MEMORY:因内存不足而失败。
实现
这应该是一组相对简单的 CL,因为我们有一个现有的系统调用 zx_pager_supply_pages,它为分页器支持的 VMO 执行非常类似的操作。因此,我们可以重复使用支持该系统调用的大部分代码。
但是,我们需要进行一些更改来支持此新用例:
zx_pager_supply_pages验证提供的 VMO 是否由给定的分页器对象支持。我们需要在新的系统调用中移除此验证。SupplyPages,即zx_pager_supply_pages用于将页面插入 VMO 的函数,假定存在分页器,在代码中称为page_source_。我们必须通过在对page_source_进行操作之前添加 NULL 检查并移除对其存在性的任何断言来移除此假设。SupplyPages还会解压缩任何压缩页面,并在将页面拼接到目标之前添加标记,因为它希望目标是分页器支持的。对于匿名 VMO,这不是必需的,因此我们需要根据目标是否由分页器支持来使其成为有条件的。SupplyPages目前会跳过目标中存在的任何页面,但仍会释放源中的页面。如前所述,我们会将其更改为始终覆盖目标。SupplyPages目前不考虑具有父项的 VMO。在大多数情况下,这不是问题。但是,如果目标是类型为ZX_VMO_CHILD_SNAPSHOT的子项,我们需要更新父项中的拆分位,以表明 VMO 已与隐藏的父项分离。
性能
我们预计这将显著提高 Bootfs 中的 VMO 页面查找性能。 具体来说,从当前使用的 CoW 克隆链切换到使用此系统调用移动页面应该会使 bootfs 查找性能提高约 20%。请注意,我们可以通过创建 VMO 的副本而不是像此系统调用建议的那样移动页面来获得类似的 bootfs 查找性能提升。但是,这种方法会将系统启动时间降低高达 70%(具体取决于我们运行的硬件目标)。使用此 RFC 提出的方法可以弥补大部分这种降低。
向后兼容性
我们的目标不是移除现有的 zx_pager_supply_pages 系统调用,因此我们预计不会出现任何向后兼容性问题。
安全注意事项
我们预计此提案不会产生任何安全影响,因为新操作旨在在功能上等同于现有操作 (memcpy() + uncommit),但性能更好。它并非旨在允许进程执行其无法执行的操作。
不过,它增加了攻击面。如果系统调用实现中存在可利用的 bug,则任何进程都可能利用这些 bug。
隐私注意事项
我们预计此提案不会产生任何隐私影响。
测试
我们将添加利用新系统调用的核心测试,并验证上述所有行为(页面移动、约束和源页面归零)。我们还将添加一个基准,用于衡量此系统调用的性能,然后我们可以将其与复制的性能进行比较。
文档
我们将添加描述 zx_vmo_transfer_data 的新文档。
缺点、替代方案和未知事项
我们可以将 zx_pager_supply_pages 泛化为与匿名 VMO 配合使用。这将大大增加该系统调用的实现复杂性,并且可能仍然需要修改 API 以接受匿名 VMO 作为输入。
如果我们的唯一目标是提高 bootfs 中 CoW 克隆层次结构中的页面查找性能,我们也可以只使用 VMO 的副本,而不是创建 CoW 克隆。但是,由于额外的复制开销,这会显著降低启动时间。