| 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
社交化:我們在即時通訊執行緒中討論設計,這是解決連線團隊在 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 繼續執行。
實作
在管理程序中,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 非常類似。這項功能可:
- 設定狀態。對於
zx_vcpu_kick,這個狀態是原子布林值,用於指出 VCPU 應在結束訪客時傳回。這個變數可透過zx_object_get_info查詢,主題為ZX_INFO_VCPU和對應的zx_info_vcpu_t型別。 - 檢查 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_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,但這兩者都有缺點。
如要專門處理 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