RFC-0128:推出「zx_vcpu_kick」

RFC-0128:導入 `zx_vcpu_kick`
狀態已接受
區域
  • Kernel
說明

新的系統呼叫 `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

社交化:我們在即時通訊執行緒中討論設計,這是解決連線團隊在 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,但不會終止 VCPU。也就是說,虛擬機器管理工具可能會在 zx_vcpu_enter 傳回 ZX_ERR_CANCELED 後,再次呼叫 zx_vcpu_enter,繼續執行 VCPU。除了處理傳回值和略過任何封包處理作業,虛擬機器管理員不需要執行任何特殊作業,可以立即呼叫 zx_vcpu_enter 繼續執行。

實作

在管理程序中,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 應在結束訪客時傳回。這個變數可透過 zx_object_get_info 查詢,主題為 ZX_INFO_VCPU 和對應的 zx_info_vcpu_t 型別。
  2. 檢查 VCPU 目前是否正在執行,如果是,則 IPI 是 VCPU 目前執行的實體 CPU。這會強制 VCPU 離開訪客,以處理中斷,進而允許 VCPU 處理要求以返回。

原子布林值會追蹤 zx_vcpu_kick 是否已呼叫,以及 VCPU 是否應在不再有任何待處理資訊要傳回使用者空間時傳回 ZX_ERR_CANCELED。也就是說,如果使用者空間有未處理的訪客封包,系統會先成功傳回該封包,再於後續呼叫 zx_vcpu_enter 時傳回 ZX_ERR_CANCELED

這個方法會讓 zx_vcpu_enter 的行為與發生逾時的 zx_port_wait 類似。

由於 Fuchsia 存放區內含有所有已知的 Hypervisor 使用者,因此實作作業會在單一 CL 中執行。這項異動包括更新系統呼叫的說明文件和語言繫結,以及變更管理程序和虛擬機器管理工具。

效能

除了導致訪客結束執行作業外,目前已知不會對效能造成影響。由於這項功能的主要用途是正常終止 VCPU 的執行作業,因此不會造成實質影響。

人體工學

設計」一節已討論 zx_vcpu_enterzx_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_killzx_handle_close,但這兩者都有缺點。

如要專門處理 VCPU,我們必須將 VCPU 視為工作,因此要讓所有與工作相關的系統呼叫都適用於 VCPU。zx_task_kill如果我們只讓 zx_task_kill 搭配 VCPU 使用,而沒有其他與工作相關的系統呼叫,就會顯得不一致。

我們可以改用 zx_handle_close 的語意,並將 on_zero_handles 處理常式新增至 VcpuDispatcher。這樣一來,如果控制代碼計數降至零,我們就能終止 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