| 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 是否被接受的影响?(此部分为可选,但建议填写。)
教员:cpu@
审核者:rashaeqbal@、jamesr@
咨询对象:mcgrathr@、maniscalco@、adanis@、eieio@
社会化:此提案已作为一页纸向 Zircon 团队进行了社会化。
设计
我们将向 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_offset, src_offset + length) 中的页面从 src_vmo 移至 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。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,用于将页面插入 VMO 的函数zx_pager_supply_pages假定存在一个分页器,在代码中称为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 克隆。不过,由于额外的复制开销,这会显著延长启动时间。