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@
审核员:rasheqbal@, jamesr@
咨询者:mcgrathr@、maniscalco@、adanis@、eieio@
共享:此提案以一页纸的形式向 Zircon 团队共享。
设计
我们将向 Zircon syscall 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)
中的页面从 dst_vmo
中的 src_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 的引用(例如 slice、引用子项等)进行写入/调整大小/固定。修改任何类型的快照的父级、子级或同级兄弟姐妹应该不会导致任何错误,但具体取决于快照,您可能会遇到写入画面撕裂。如果您操控 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
是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 克隆。不过,由于额外的复制开销,这会显著延长启动时间。