| RFC-0128:引入 `zx_vcpu_kick` | |
|---|---|
| 状态 | 已接受 |
| 区域 |
|
| 说明 | 一种新的系统调用 `zx_vcpu_kick`,可使正在运行的 VCPU 退出到主机并返回到用户空间。 |
| Gerrit 更改 | |
| 作者 | |
| 审核人 | |
| 提交日期(年-月-日) | 2021-08-26 |
| 审核日期(年-月-日) | 2021-09-25 |
摘要
我们建议添加一种新的系统调用 zx_vcpu_kick,可使正在运行的 VCPU 退出到主机并返回到用户空间。此外,我们建议将 zx_vcpu_resume 重命名为 zx_vcpu_enter,以便采用一致的命名法。
设计初衷
当 VCPU 运行时,它可能会在对 zx_vcpu_enter 的调用中无限期地被阻塞。这会给虚拟机管理器带来问题,因为要干净利落地关闭 VCPU,虚拟机管理器需要系统调用将控制权返回给调用线程,以便安全地释放所有相关资源。
此外,为了简化集成测试,最好有一种方法可以强制 VCPU 从访客退出。这样,测试就可以编写得更简洁、更精确。
利益相关方
教员: cpu
审核人: adanis、tamird、jamesr
已咨询: dgreenaway、brunodalbo
社交化: 该设计是在聊天串中讨论的,是 Connectivity 团队在 netemul 测试中面临的问题的解决方案的一部分。
设计
此提案包括对 Zircon 系统调用接口的以下更改:
- 添加
zx_vcpu_kick - 将
zx_vcpu_resume重命名为zx_vcpu_enter
其中,zx_vcpu_enter 和 zx_vcpu_kick 将定义为:
/// Enter a VCPU, and start or continue execution.
/// Rights: handle must be of type ZX_OBJ_TYPE_VCPU and have ZX_RIGHT_EXECUTE.
// @blocking
zx_status_t zx_vcpu_enter(
zx_handle_t handle,
uint32_t options,
zx_port_packet_t* packet
);
/// Exit from the current or next call to |vcpu_enter|.
/// Rights: handle must be of type ZX_OBJ_TYPE_VCPU and have ZX_RIGHT_EXECUTE.
zx_status_t zx_vcpu_kick(
zx_handle_t handle
);
当对 VCPU 句柄调用 zx_vcpu_kick 时,对同一句柄的任何当前正在运行的 zx_vcpu_enter 调用都将返回 ZX_ERR_CANCELED。此外,如果在调用 zx_vcpu_kick 时 zx_vcpu_enter 未运行,则对 zx_vcpu_enter 的下一次调用将立即返回 ZX_ERR_CANCELED。这样,虚拟机管理器就可以调用 zx_vcpu_kick,并保证 zx_vcpu_enter 下一次将返回 ZX_ERR_CANCELED。相反,这意味着,如果 zx_vcpu_enter 尚未返回 ZX_ERR_CANCELED,则无论调用 zx_vcpu_kick 多少次,它都只会返回一次。
之所以选择 ZX_ERR_CANCELED 作为要返回的状态,是为了让虚拟机管理器能够轻松区分退出原因。这样,虚拟机管理器就可以使用该状态来正常停止 VCPU,而无需在虚拟机管理器中进行任何额外的状态管理。当它看到 zx_vcpu_enter 返回 ZX_ERR_CANCELED 时,就可以关闭 VCPU 句柄并释放任何其他关联的资源。
此外,zx_vcpu_kick 的行为是停止 VCPU,而不是终止 VCPU。这意味着,在 zx_vcpu_enter 返回 ZX_ERR_CANCELED 后,虚拟机管理器可以通过再次调用 zx_vcpu_enter 来恢复 VCPU 的执行。除了处理返回值和绕过任何数据包处理之外,虚拟机管理器不需要执行任何特殊操作,并且可以立即调用 zx_vcpu_enter 来恢复执行。
实现
在 Hypervisor 中,zx_vcpu_enter 是以 zx_port_wait 为模型构建的。它具有几乎相同的 API,但有一个截止时间除外。它旨在等待访客数据包到达,然后将数据包返回到用户空间。
与 zx_thread_start 不同,它不是创建新的执行线程的方式,而是转换当前的执行线程。
也就是说,我们认为基于令牌的方法(例如 zx_task_suspend 中的方法)无法很好地中断 VCPU 的执行。基于令牌的方法不适合 vCPU 执行的模型,该模型需要在用户空间和内核之间不断来回切换。
相反,我们建议 zx_vcpu_kick 只是导致 zx_vcpu_enter 返回到用户空间,并返回一个错误代码,表明它被中断了。在 Hypervisor 中,zx_vcpu_kick 的实现与 zx_vcpu_interrupt 非常相似。它将:
- 设置状态。对于
zx_vcpu_kick,此状态是一个原子布尔值,用于 指示 VCPU 在退出访客时应返回。可以使用此变量 通过使用zx_object_get_info查询,主题为ZX_INFO_VCPU,相应的zx_info_vcpu_t类型。 - 检查 VCPU 当前是否正在运行,如果是,则 IPI 是 VCPU 当前正在运行的物理 CPU 。这会强制 VCPU 退出 访客以处理中断,从而允许它处理 返回请求。
原子布尔值跟踪 zx_vcpu_kick 是否已被调用,以及 VCPU 在不再有任何未完成的信息要返回到用户空间时应返回 ZX_ERR_CANCELED。这意味着,如果用户空间有未完成的访客数据包,则会在我们对 zx_vcpu_enter 的后续调用返回 ZX_ERR_CANCELED 之前成功返回该数据包。
这种方法使 zx_vcpu_enter 的行为类似于遇到超时的 zx_port_wait。
实现将在单个 CL 中完成,因为 Hypervisor 的所有已知用户都包含在 Fuchsia 代码库中。此更改将包括对系统调用的文档和语言绑定的更新,以及对 Hypervisor 和虚拟机管理器的更改。
性能
除了导致访客退出执行之外,对性能没有已知的影响。由于此功能的主要用例是正常终止 VCPU 的执行,因此这应该不会产生实际影响。
工效学设计
在 设计 部分讨论了 zx_vcpu_enter 和 zx_vcpu_kick 的工效学设计考虑因素
。
向后兼容性
Hypervisor 的所有已知用户都包含在 Fuchsia 代码库中,因此可以同时引入 zx_vcpu_kick 并将 zx_vcpu_resume 重命名为 zx_vcpu_enter。
安全注意事项
应审核使用 zx_vcpu_kick 的虚拟机管理器,以确保任何对 zx_vcpu_enter 的使用都考虑了 ZX_ERR_CANCELED 返回状态,并且如果虚拟机管理器打算恢复 VCPU 的运行,则必须忽略返回的 PortPacket。如果虚拟机管理器不忽略 PortPacket,它将对已归零的无效数据进行操作,这对于 zx_vcpu_enter 返回的任何错误状态都是如此。
隐私注意事项
此提案对隐私没有影响。
测试
实现 CL 落地后,我们可以为虚拟机管理器测试启用 ASAN 机器人,并证明我们可以在没有 ASAN 失败的情况下干净利落地关闭虚拟机。
文档
我们需要为 zx_vcpu_kick 系统调用添加额外的文档,并且需要扩展 zx_vcpu_enter 系统调用的文档,以包含新的返回状态和 PortPacket 处理建议。
缺点、替代方案和未知事项
此提案的缺点是引入了额外的系统调用。最初的提案是我们可以重复使用 zx_task_kill 或 zx_handle_close,但两者都有各自的缺点。
为了使 zx_task_kill 专门用于 VCPU,我们必须将 VCPU 视为任务,因此使所有与任务相关的系统调用都适用于 VCPU。
如果我们只让 zx_task_kill 适用于 VCPU,而不让其他与任务相关的系统调用适用于 VCPU,那将是不协调的。
相反,我们可以依赖于 zx_handle_close 的语义,并向 VcpuDispatcher 添加 on_zero_handles 处理程序。这样,如果句柄计数降至零,我们就可以终止 VCPU。但是,这有两个直接缺点:我们无法在 VCPU 停止后恢复 VCPU,并且我们会使 VCPU 句柄失效。使 VCPU 句柄失效尤其成问题,因为它本质上是一个竞争操作。当句柄失效时,另一个线程可能正在使用 VCPU 句柄注入和中断,因此会导致意外错误发生。
另一种选择是引入此系统调用的更通用版本,例如 zx_thread_cancel,其中对给定线程的任何阻塞操作都会立即返回 ZX_ERR_CANCELED。这样,我们就可以将系统调用扩展到未来使用与 VCPU 类似模型的用例。
在先技术和参考文档
Apple 的 Hypervisor Framework 中存在类似的操作: https://developer.apple.com/documentation/hypervisor/1441468-hv_vcpu_interrupt
以及 Linux 的 KVM(基于内核的虚拟机)中: https://www.kernel.org/doc/html/latest/virt/kvm/vcpu-requests.html#vcpu-kicks