RFC-0265:計數器:用於跨程序同步的簡易 Zircon 物件

RFC-0265:計數器:用於跨程序同步處理的簡單 Zircon 物件
狀態已接受
區域
  • Kernel
說明

建議使用新的 Zircon 物件 zx::counter。

問題
Gerrit 變更
作者
審查人員
提交日期 (年-月-日)2024-12-09
審查日期 (年-月-日)2025-02-12

問題陳述

這項 RFC 的動機是兩個不同的用途:Starnix 暫停和時間戳記圖形柵欄。雖然 Starnix 暫停是主要用途,但時間戳記圖形柵欄也是重要的次要用途。

Starnix 停權

我們需要讓 Starnix Kernel 和 Starnix Runner 協調運作,並在 Fuchsia 平台元件有待處理的訊息時,防止系統暫停運作。

這項作業包含三個程序:

  1. 部分 Fuchsia 平台元件,會在管道上傳送訊息。

  2. Starnix 核心,需要讀取並處理該訊息。

  3. Starnix Runner,其角色說明如下。

Starnix Runner 負責將所有執行緒放入 ZX_THREAD_SUSPENDED 狀態,並觀察 Fuchsia 平台元件傳送至 Starnix Kernel 的訊息,藉此暫停 Starnix Kernel。Starnix Runner 會做為 Proxy,代表 Starnix Kernel 接收訊息,然後在 Starnix Kernel 未暫停時轉送訊息,或在暫停時恢復並轉送訊息。

我們希望確保不會有任何「遺失的喚醒」,因此 Starnix Runner 和 Starnix Kernel 必須就是否有任何未處理或「傳輸中」的訊息達成共識,這些訊息應會阻止 Starnix Kernel 暫停。

目前 Starnix Runner 和 Starnix Kernel 是使用單一 zx::event 進行協調。這個事件用於指出有訊息正在傳輸。 以這種方式使用事件可正常運作,並避免喚醒遺失。不過,這項功能有項限制,就是一次最多只能代理一則訊息。如果沒有這項限制,Starnix Runner 就無法判斷 Starnix Kernel 是否已處理所有傳輸中的訊息,還是只處理已收到的訊息。

我們希望移除這項限制,支援多封傳輸中的郵件。為支援多個傳輸中訊息,我們需要某種方式來計算這些訊息,以便在計數達到零時暫停。

如果有多個傳輸中的訊息,單獨使用 zx::event 就不夠。不過,zx::event 加上共用整數就足夠了。如果 Starnix Runner 和 Starnix Kernel 共用位址空間,我們只要將整數儲存在記憶體中即可。不過,這些程序不會共用位址空間,因此我們需要將整數儲存在其他共用資源中,例如 VMO。由於這些程序位於相同的邏輯信任網域,因此完全可以透過 VMO 進行協調。不過,如果我們有新的 Zircon 物件,可做為具有計數的事件,就能簡化作業。

附有時間戳記的圖形柵欄

ZirconVmoSemaphore 用於實作含時間戳記的圖形柵欄。這項作業的運作方式類似 zx::event,外加一個時間戳記,指出物件發出信號的時間。圖像堆疊會為每個影格建立多個這類物件。每個 VMO 都會佔用一頁記憶體 (加上一些額外負荷)。使用 VMO 實作含時間戳記的圖形柵欄可行,但效率稍低。如果我們有一個類似 zx::event 加時間戳記的物件,就能做得更好。

另請參閱「VkFence」。

摘要

我們建議建立新的 Zircon 物件 zx::counter,該物件會保存整數、可遞增/遞減,並在值大於零或小於/等於零時發出信號。

利害關係人

主要利害關係人為 Zircon、Starnix 和 Graphics。

協助人員:davemoore@google.com

審查人員:emircan@google.com、lindkvist@google.com、mcgrathr@google.com

諮詢對象:adanis@google.com、eieio@google.com、rudymathu@google.com

討論:在 RFC 初稿發布前,我們已透過電子郵件與多位人員討論過這項提案的各種變化。另請參閱 go/zx-counter 的內部討論。

目標和需求

  1. 解決當前問題 - 啟用 Starnix Kernel 和 Starnix Runner,在 Fuchsia 平台元件有待處理的訊息時,協調並防止暫停。啟用圖形堆疊,以更有效率的同步工具,取代稍微較為耗用資源的 VMO 型 ZirconVmoSemaphore

  2. 保持簡單 - 解決當下問題所用的任何方法都應保持簡單。請勿建構超出必要範圍的功能。如果出現更多用途,我們可以針對這些用途開發解決方案 (或建立不同的解決方案!)。

設計

我們推出了新的 Zircon 物件 Counter。計數器類似於事件,但具有可遞增、遞減、讀取或寫入的帶正負號 64 位元整數。

當計數器的值大於零時,訊號 ZX_COUNTER_POSITIVE 會經過判斷,而 ZX_COUNTER_NON_POSITIVE 則會取消判斷。當值小於或等於零時,ZX_COUNTER_POSITIVE 會取消斷言,而 ZX_COUNTER_NON_POSITIVE 會斷言。

雖然 Starnix 暫停用例可透過遞增和遞減作業 (判斷/取消判斷信號) 滿足,但 Timestamped Graphics Fence 用例需要讀取和寫入作業。做為柵欄的計數器會從零開始,代表未發出信號的柵欄。正值代表已發出信號的柵欄,值本身是柵欄發出信號的時間。

如要發出柵欄信號,圖形堆疊中的信號元件會使用寫入作業,將計數器的值設為目前時間 (請參閱 zx_instant_mono_t)。當計數器的值從零變更為這個正值時,系統會判斷 ZX_COUNTER_POSITIVE。圖形堆疊中的信號元件隨後可以讀取計數器的值,判斷柵欄發出信號的時間。

為支援這兩種用途,我們新增了四個系統呼叫 (zx_counter_createzx_counter_addzx_counter_readzx_counter_write) 和兩個額外信號:

// Asserted when a counter's value is less than or equal to zero.
#define ZX_COUNTER_NON_POSITIVE __ZX_OBJECT_SIGNAL_4

// Asserted when a counter's value is greater than zero.
#define ZX_COUNTER_POSITIVE __ZX_OBJECT_SIGNAL_5

計數器是使用 zx_counter_create 建立:

## Summary

Create a counter.

## Declaration

zx_status_t zx_counter_create(uint32_t options, zx_handle_t* out);

## Description

zx_counter_create() creates a counter, which is an object that encapsulates
a signed 64-bit integer value that can be incremented, decremented, read, or
written.

When the value is greater than zero, the signal ZX_COUNTER_POSITIVE is
asserted.  Otherwise ZX_COUNTER_NON_POSITIVE is asserted.  Exactly one of
these two signals is always asserted, and never both at once.

The newly-created handle will have rights ZX_RIGHTS_BASIC, ZX_RIGHTS_IO, and
ZX_RIGHT_SIGNAL.  The value will be zero and the signal
ZX_COUNTER_NON_POSITIVE will be asserted on the newly-created object.

## Rights

Caller job policy must allow ZX_POL_NEW_COUNTER.

## Return value

zx_counter_create() returns ZX_OK and a valid event handle (via *out*) on
success.

On failure, an error value is returned.

## Errors

ZX_ERR_INVALID_ARGS  *out* is an invalid pointer, or *options* is non-zero.

ZX_ERR_NO_MEMORY  Failure due to lack of memory.
There is no good way for userspace to handle this (unlikely) error.
In a future build this error will no longer occur.

您可以使用 zx_counter_add 遞增或遞減計數器:

## Summary

Add an amount to a counter.

## Declaration

zx_status_t zx_counter_add(zx_handle_t handle, int64_t amount);

## Description

zx_counter_add() adds amount to the counter referenced by handle.

After the result of the addition, if the counter's value is:

* less than or equal to zero - ZX_COUNTER_NON_POSITIVE will be asserted
and ZX_COUNTER_POSITIVE will be deasserted.

* greater than zero - ZX_COUNTER_POSITIVE will be asserted and
ZX_COUNTER_NON_POSITIVE will be deasserted.

## Rights

handle must have both ZX_RIGHT_READ and ZX_RIGHT_WRITE.  Because a counter's
value could be determined by checking for ZX_ERR_OUT_OF_RANGE on a series of
carefully crafted zx_counter_add calls, there is no way to create a counter
that cannot be read, but which can be added.

## Return value

zx_counter_add() returns ZX_OK on success.

On failure, an error value is returned.

## Errors

ZX_ERR_WRONG_TYPE  if handle is not a counter handle.

ZX_ERR_ACCESS_DENIED  if handle does not have ZX_RIGHT_WRITE.

ZX_ERR_OUT_OF_RANGE  if the result of the addition would overflow or underflow.

可以使用 zx_counter_read 讀取計數器:

## Summary

Read the value of a counter.

## Declaration

zx_status_t zx_counter_read(zx_handle_t handle, int64_t* value);

## Description

zx_counter_read() reads the value of the counter referenced by handle into the
integer value points at.

## Rights

handle must have ZX_RIGHT_READ.

## Return value

zx_counter_read() returns ZX_OK on success.

On failure, an error value is returned.

## Errors

ZX_ERR_WRONG_TYPE  if handle is not a counter handle.

ZX_ERR_ACCESS_DENIED  if handle does not have ZX_RIGHT_READ.

ZX_ERR_INVALID_ARGS  if value is an invalid pointer.

計數器可使用 zx_counter_write 撰寫:

## Summary

Write the value to a counter.

## Declaration

zx_status_t zx_counter_write(zx_handle_t handle, int64_t value);

## Description

zx_counter_write() writes value to the counter referenced by handle,
asserting/deasserting signals as necessary.

Because concurrent operations on a counter may be interleaved with one another,
an implementation of a "counting semaphore" synchronization protocol should use
zx_counter_add() instead of a sequence of zx_counter_read(), modify,
zx_counter_write().

## Rights

handle must have ZX_RIGHT_WRITE.

## Return value

zx_counter_write() returns ZX_OK on success.

On failure, an error value is returned.

## Errors

ZX_ERR_WRONG_TYPE  if handle is not a counter handle.

ZX_ERR_ACCESS_DENIED  if handle does not have ZX_RIGHT_WRITE.

ZX_ERR_INVALID_ARGS  if value is an invalid pointer.

實作

系統會透過幾個 CL (可能是一個) 在 Zircon 中實作計數器。Starnix 核心和 Starnix Runner,以及圖形堆疊稍後會更新為使用計數器。

各種語言繫結、FIDL 控制代碼支援、fidlcat/fidl_coded 等都會更新。

效能

我們預期 zx_counter_create 的效能會與zx_event_create類似。我們預期 zx_counter_addzx_counter_write 的效能與 zx_object_signal 類似。

zx_counter_read 應與此類似,但可能也會執行 usercopy out 來傳回值。讀取是否執行使用者副本,實際上是實作細節。我們可以先實作執行使用者副本的作業,然後變更 vDSO/核心介面,透過暫存器傳回值,並更新 vDSO 來儲存傳回的值,藉此進行最佳化。

我們預期 Starnix Suspension 用途不會對效能造成重大影響 (改善或回歸)。

就 Timestamped Graphics Fence 用途而言,我們預期 CPU 和記憶體都會有小幅改善。與以 VMO 為基礎的信號燈相比,計數器需要的記憶體較少 (幾十個位元組與超過一頁),建立成本也較低。

人體工學

Counter 的設計用途是搭配現有的非同步等待模式 (例如 Zircon Ports)。

回溯相容性

Counter 是新的 Zircon 物件,因此沒有回溯相容性問題。

安全性考量

沒有安全考量。

隱私權注意事項

無隱私權注意事項。

測試

我們會新增核心測試,測試新的 Zircon 物件。

說明文件

系統會新增/更新系統呼叫文件。

缺點、替代方案和未知事項

自行製作

如上所述,zx::event 不足以用於 Starnix 用途。不過,zx::event 加上共用的整數和議定的通訊協定就足夠了。除了導入新的 Zircon 物件,Starnix Runner 和 Starnix Kernel 也可以使用 zx::event,以及包含整數的共用對應 VMO,並以原子方式操控整數。雖然這種解決方案可行,但較為複雜,而且有些細微之處可能難以掌握。我們認為 zx::counter 是自然的 Zircon 物件,且新增這個物件造成的核心複雜度增加幅度極小。

一般用途和完整性與狹窄和有限性

設計及建構功能更完整的計數器或信號燈物件,與建構僅滿足目前用途的物件之間存在張力。我們過去曾考慮提供類似物件,但由於使用情境不夠具體,因此暫緩推出。這次我們要建構的相對簡單,旨在滿足一或兩個案例。我們的目標是隨著更多用途出現,演進或取代這個物件,而不是建立大量類似但不太相同的物件。

另請參閱「未來發展方向」。

不帶正負號的計數

主要用途只需要未簽署的計數器。我們可以變更行為,讓值在邏輯上為不帶正負號。我們也可以使用選項旗標,指定是否為已簽署或未簽署,並在未簽署時將遞減至零以下視為錯誤。由於我們沒有找到充分的論據來支援有符號和無符號的語意,且有符號的語意更具彈性,可涵蓋更多未來的用途,因此建議只提供有符號的語意。

缺少初始值

我們考慮過替代方案,也就是將初始值傳遞至 create 方法。不過,呼叫端只要在允許控制代碼「逸出」前呼叫 add,即可達到相同目的,因此我們認為這項功能並無必要。

未來發展方向

我們預期在近期到中期內,zx::counter不會廣泛採用。在未來的某個時間點,我們可能會以更一般且完整的信號燈物件,取代 zx::counter (或許也包括 zx::event)。