RFC-0128:引入 `zx_vcpu_kick`

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 从 guest 退出。这样,测试就可以写得更简洁、更精确。

利益相关方

Facilitator: cpu

审核者:adanis、tamird、jamesr

已咨询:dgreenaway、brunodalbo

社会化:该设计是在聊天串中讨论的,是 Connectivity 团队在 netemul 测试中遇到的问题解决方案的一部分。

设计

此提案包含对 Zircon 系统调用接口的以下更改:

  1. 添加了 zx_vcpu_kick
  2. zx_vcpu_resume 重命名为 zx_vcpu_enter

其中,zx_vcpu_enterzx_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_kickzx_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 返回到用户空间,并返回一个表明其被中断的错误代码。在虚拟机监控器中,zx_vcpu_kick 的实现方式与 zx_vcpu_interrupt 非常相似。它将:

  1. 设置状态。对于 zx_vcpu_kick,此状态是一个原子布尔值,用于指示 VCPU 在退出 guest 时是否应返回。可以使用主题为 ZX_INFO_VCPUzx_object_get_info 查询此变量,并使用相应的 zx_info_vcpu_t 类型。
  2. 检查 VCPU 是否正在运行,如果正在运行,则向 VCPU 当前运行的物理 CPU 发送 IPI。这会强制 VCPU 退出 guest 来处理中断,从而允许它处理返回请求。

原子布尔值用于跟踪 zx_vcpu_kick 是否已被调用,以及当 VCPU 不再有任何未完成的信息要返回到用户空间时,是否应返回 ZX_ERR_CANCELED。这意味着,如果存在未完成的用户空间访客数据包,则会在后续调用 zx_vcpu_enter 时返回 ZX_ERR_CANCELED 之前成功返回该数据包。

此方法使 zx_vcpu_enter 的行为类似于遇到超时的 zx_port_wait

由于 hypervisor 的所有已知用户都包含在 Fuchsia 代码库中,因此实现将在单个 CL 中完成。此变更将包括对系统调用的文档和语言绑定的更新,以及对虚拟机监控程序和虚拟机管理器的更改。

性能

除了导致 guest 退出执行之外,对性能没有其他已知影响。由于此功能的主要用例是正常终止 VCPU 的执行,因此这应该不会产生实际影响。

工效学设计

设计部分讨论了 zx_vcpu_enterzx_vcpu_kick 的人体工程学注意事项。

向后兼容性

虚拟机监控程序的所有已知用户都包含在 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 bot,并证明我们可以干净地关闭,而不会出现 ASAN 故障。

文档

我们需要为 zx_vcpu_kick 系统调用添加更多文档,还需要扩展 zx_vcpu_enter 系统调用的文档,以纳入新的返回状态和 PortPacket 处理建议。

缺点、替代方案和未知因素

此提案的缺点是引入了额外的系统调用。最初的提议是我们可以重复使用 zx_task_killzx_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