RFC-0128:引入 `zx_vcpu_kick` | |
---|---|
状态 | 已接受 |
区域 |
|
说明 | 新增了系统调用 `zx_vcpu_kick`,该调用可导致正在运行的 vCPU 退出到主机并返回到用户空间。 |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2021 年 8 月 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,而不是终止它。这意味着,虚拟机管理器可能会在 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 当前是否正在运行,如果是,则对 VCPU 当前正在运行的物理 CPU 发出 IPI。这会强制 VCPU 退出来处理中断,从而允许它处理返回请求。
原子布尔值用于跟踪是否已调用 zx_vcpu_kick
,以及当 VCPU 不再有任何待处理的信息要返回到用户空间时应返回 ZX_ERR_CANCELED
。这意味着,如果用户空间有未处理的访客数据包,则在我们在随后调用 zx_vcpu_enter
时返回 ZX_ERR_CANCELED
之前,系统会成功返回该数据包。
这种方法会使 zx_vcpu_enter
的行为与遇到超时的 zx_port_wait
类似。
由于 Hypervisor 的所有已知用户都包含在 Fuchsia 代码库中,因此实现将在单个 CL 中进行。此次变更将包括对系统调用的文档和语言绑定的更新,以及对 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 配合使用,而不与任何其他与任务相关的系统调用配合使用,那将不合适。
不过,我们可以依赖 zx_handle_close
的语义,并向 VcpuDispatcher
添加 on_zero_handles
处理脚本。这样一来,如果句柄数下降到零,我们就可以终止 VCPU。不过,这有两个直接缺点:我们无法在 VCPU 停止后恢复它,并且会使 VCPU 句柄失效。使 VCPU 句柄失效尤其有问题,因为这本身就是一项竞争操作。当句柄失效时,另一个线程可能正在使用 VCPU 句柄注入和中断,因此可能会导致意外错误。
另一种方法是引入此系统调用的更通用版本,例如 zx_thread_cancel
,其中对给定线程的任何阻塞操作都会立即返回 ZX_ERR_CANCELED
。这样,我们就可以将系统调用扩展到未来使用与 VCPU 类似模型的用例。
在先技术和参考文档
Apple 的 Hypervisor 框架中提供了类似的操作: 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