| RFC-0270:Zircon 虛擬中斷信號 | |
|---|---|
| 狀態 | 已接受 |
| 區域 |
|
| 說明 | 建議虛擬中斷物件的新信號。 |
| 問題 | |
| Gerrit 變更 | |
| 作者 | |
| 審查人員 | |
| 提交日期 (年-月-日) | 2025-04-02 |
| 審查日期 (年-月-日) | 2025-04-16 |
問題陳述
我們需要更完善的方式來多工處理共用中斷,並管理虛擬中斷的確認/未確認狀態。詳情請參閱下方的「擴充問題陳述」一節。
摘要
本 RFC 建議在虛擬中斷物件中新增 Zircon 信號,當虛擬中斷物件處於「未觸發」狀態時,該信號會維持為「已啟用」。
背景
實體中斷與虛擬中斷
Zircon 中斷物件分為兩種:實體中斷和虛擬中斷。
實體中斷物件在邏輯上代表透過系統的中斷控制器傳送的中斷信號 (有時透過 PCIe 匯流排,有時則透過平台或 SoC 特定路徑直接傳送至控制器)。相較之下,虛擬中斷物件不會直接代表來自非 CPU 硬體的訊號。程式會建立虛擬中斷,模擬實體中斷,但只有在軟體對物件呼叫 zx_interrupt_trigger 時,虛擬中斷才會發出信號。
自訂等待介面
中斷物件目前不會使用標準 Zircon 信號系統,通知用戶端已收到中斷。雖然這些中斷處理常式可以繫結至通訊埠,並在收到 IRQ 時傳送通訊埠封包,也可以用來封鎖執行緒,直到收到 IRQ 為止,但不會使用標準 zx_object_wait_one/zx_object_wait_many 或 zx_object_wait_async 系統呼叫來達成此行為。如要接收連接埠封包,使用者必須改用 zx_interrupt_bind 將中斷物件繫結至連接埠物件,如要封鎖執行緒,則必須使用 zx_interrupt_wait。一次只能對中斷物件執行一個邏輯等待作業,不支援任意數量的等待作業。執行緒解除封鎖或傳送埠封包後,必須「確認」中斷,才能再次發出信號。
使用者透過 zx_interrupt_wait 等待中斷時,使用者執行緒下次因中斷而遭到封鎖時,系統會再次呼叫 zx_interrupt_wait,確認中斷。使用者透過 zx_interrupt_bind 將中斷繫結至通訊埠後,即可在中斷物件上呼叫 zx_interrupt_ack 來確認中斷,以便傳送新的通訊埠封包。
雖然這個非標準信號介面並非最佳選擇,且我們希望設計與標準 Zircon 信號機制相容的新介面 (除瞭解決其他痛點),但全面重新設計 Zircon 中斷 API 並轉換至新系統是一項複雜的作業,可能需要大量時間和精力,超出本提案的有限範圍。
虛擬中斷的目的
虛擬中斷在 Fuchsia 中有兩個主要用途,兩者都源自相同的動機。包括測試和多工解訊。
由於驅動程式需要使用中斷物件所用的特殊等待介面,因此很難輕鬆地以其他核心物件 (可能是事件) 取代中斷物件,並讓驅動程式庫程式碼「正常運作」。撰寫驅動程式庫測試時,測試環境會模擬硬體,因此會出現問題。無法向驅動程式庫提供實體中斷,即使可以,也很難控制特定測試的這類項目,而且這類項目非常具體。
測試可以改用虛擬中斷物件。從驅動程式庫消費者的角度來看,他們使用相同的 API,基本上無法區分兩者,因此程式碼可以進行測試,不必使用任何特殊案例程式碼,瞭解測試環境和實際環境之間的差異。
需要解多工處理個別中斷時,也可能發生類似情況。以需要引發中斷的外部晶片驅動程式庫為例。我們希望為外部晶片提供單一驅動程式庫,與特定產品使用的特定 SoC 無關,但該產品會使用外部晶片。晶片可能具有連線至 SoC 的 IRQ 訊號。在某些 SoC 設計中,這個 IRQ 訊號可能會直接傳送至 SoC 的中斷控制器,也就是說,系統可以例項化專屬的實體中斷,並在初始化期間傳遞至驅動程式庫。只要管理中斷時只需要與系統的中斷控制器互動,核心就能透過實體中斷物件直接管理 IRQ。
另一方面,其他設計可能會將這個中斷訊號連線至 GPIO,做為中斷區塊的一部分 (通常為 16 或 32 個區塊)。當設定為中斷的區塊中,有任何 GPIO 發出信號時,系統就會提高 GPIO 硬體的中斷優先順序。GPIO 驅動程式庫接著可以喚醒並找出已觸發的多個 GPIO 中斷,然後遮蔽中斷,直到可以提供服務為止。但仍需向中斷作業的驅動程式庫發出信號。我們不希望 irq-consumer-驅動程式庫需要瞭解這些情況之間的差異,也不希望因為電路板設計將多個中斷路由至同一個 GPIO 區塊,而導致不同外部晶片的驅動程式需要存在於同一個程序中。因此,在這種情況下,GPIO 驅動程式庫可以為共用硬體中設定為中斷的每個 GPIO 建立虛擬中斷物件,有效解多工處理共用相同實體中斷的多個中斷,且不需要驅動程式庫有程式碼來處理這兩種情況。
問題陳述 (擴充)
但就目前而言,這種做法存在實際問題。請考慮 GPIO 解多工情境。有多個 GPIO 中斷共用相同的硬體區塊,且驅動程式庫已為每個中斷建立並分配一個虛擬中斷。其中一個中斷觸發時,系統會透過區塊的共用實體中斷訊號傳送 GPIO。然後,GPIO 驅動程式庫可以使用 SoC 專用的 GPIO 硬體遮蓋特定位元,以及zx_interrupt_trigger相關聯的虛擬中斷。現在所有待處理的 GPIO 中斷都已發出信號,因此可以確認自己的實體中斷,並返回等待新的中斷聲明。
在驅動程式庫/消費者端,中斷處理常式會喚醒、處理中斷,最後確認中斷。此時,GPIO 驅動程式庫需要採取行動。具體來說,必須先喚醒 GPIO 區塊中的特定位元,並取消遮罩/重新啟動,才能發出任何新的中斷信號。
但目前沒有好的方法可以做到這一點。沒有訊號會透過中斷物件從用戶端驅動程式庫傳送至 GPIO 驅動程式庫,但如果沒有這類訊號,GPIO 驅動程式庫就無法得知何時可以安全地取消遮罩並重新啟動中斷。
可以導入其他物件或建立通訊協定來提供這類信號,但這樣一來,中斷的消費者就必須知道這是虛擬中斷,而非實體中斷,這會破壞有效區分這兩種情境的目標。
利害關係人
講師:cpu@google.com
審查人員: + 'maniscalco@google.com' + 'bradenkell@google.com' + 'rudymathu@google.com' + 'cpu@google.com'
已諮詢:
Zircon 驅動程式庫架構團隊。
社交:
這項提案已 (以 Google 文件形式) 與 Zircon 團隊分享。
需求條件
使用中斷物件的「下游」驅動程式必須與虛擬物件無關。
設計
中斷物件和標準 Zircon 信號
如前所述,Zircon 中斷物件目前不會使用標準 Zircon 信號系統,從硬體發出中斷要求。這不代表他們完全不參與標準信號系統。使用者可以自由發布非同步等待,中斷物件或使用 zx_object_wait_one/zx_object_wait_many 封鎖執行緒。也就是說,只有在等待幾乎每個 Zircon 物件上都有的 8 個「使用者信號」之一,且軟體已判斷該信號時,這些等待作業才會完成。目前沒有為中斷物件定義任何「系統信號」,供核心發出信號。
ZX_VIRTUAL_INTERRUPT_UNTRIGGERED
因此,我們來導入新的信號 (稱為 ZX_VIRTUAL_INTERRUPT_UNTRIGGERED),並僅用於虛擬中斷。以下是訊號的基本屬性:
- 虛擬中斷物件不會在建立後立即觸發,因此這個訊號會在任何新建立的虛擬中斷物件上斷言。
- 使用者在虛擬中斷物件上呼叫
zx_interrupt_trigger時,物件會進入觸發狀態,且信號會取消判斷。 - 使用者確認觸發的虛擬中斷物件後,該物件會進入未觸發狀態,並會判斷信號。
實際使用範例
請考慮使用實體中斷 I 的 GPIO 驅動程式庫,並公開三個虛擬中斷 A、B 和 C,全部位於同一個 GPIO 區塊中。
- 啟動時,驅動程式庫會建立及分配
A、B和C,然後將I繫結至通訊埠P。 - 此外,它也會建立專屬的 IRQ 執行緒 (
T)、設定適當的設定檔,然後在P中封鎖T,等待封包。 I會將封包傳送至P,並在過程中解除封鎖T。T會檢查 GPIO 狀態,並發現與A和C相關聯的 GPIO 已判斷為有效。T在A和C上呼叫zx_interrupt_trigger。A和C會進入觸發狀態,並清除ZX_VIRTUAL_INTERRUPT_UNTRIGGERED。T會在A和C上呼叫zx_object_async_wait,將這些物件與其連接埠建立關聯,並等待虛擬中斷物件上斷言ZX_VIRTUAL_INTERRUPT_UNTRIGGERED。T會遮蓋與A和C相關聯的 GPIO。T會在I上呼叫zx_interrupt_ack,然後再次封鎖通訊埠,等待新封包。- 在某個時間點,使用
C的驅動程式庫會喚醒、處理中斷,然後在其控制代碼上呼叫zx_interrupt_ack,以C,並將ZX_VIRTUAL_INTERRUPT_UNTRIGGERED斷言為副作用。 - 等待
C未觸發信號的非同步等待作業已完成,因此系統會產生並傳送埠封包至P,解除封鎖T。 T會喚醒,並注意到C已變成「未觸發」,因此會使用其硬體專屬暫存器,取消遮罩並重新啟動與C相關聯的 GPIO 中斷。T只會返回等待連接埠。當I再次成為斷言,或A的ZX_VIRTUAL_INTERRUPT_UNTRIGGERED信號成為斷言時,我們就會收到連接埠封包。目前你無須採取進一步行動。
確認待處理的中斷
對於部分中斷物件,可能會觸發中斷,且在確認時已有第二個 IRQ 待處理。雖然這種情況最常發生在 MSI 中斷,但只要在用戶端管理中斷前連續呼叫觸發程序兩次,就有可能透過虛擬中斷物件產生這種情況。
如果使用者確認中斷,但另一個 IRQ 待處理,結果是中斷物件會立即觸發。透過 zx_interrupt_wait 確認的執行緒會立即解除封鎖。透過 zx_interrupt_ack 確認執行緒會立即產生新的連接埠封包。
UNTRIGGERED 信號會發生什麼情況?由於中斷邏輯上已取消觸發,然後立即重新觸發,訊號的行為應為閃爍。也就是說,目前等待 UNTRIGGERED 信號的所有執行緒都應解除封鎖,而目前發布的任何非同步等待作業都應滿足條件並產生埠封包。不過,確認作業後緊接的訊號狀態會取消判斷。
實作
這個做法預計可輕鬆實作,風險也較低。
核心程式碼目前有兩個中斷物件的調度器,分別是 InterruptDispatcher 和 VirtualInterruptDispatcher,後者是前者的子類別。雖然兩者共用許多程式碼,但成為信號的路徑不同。InterruptDispatcher 會在硬體 IRQ 時間透過 InterruptHandler 方法發出信號,而虛擬中斷則會在 zx_interrupt_trigger 時間透過 Trigger 方法發出信號。在 InterruptDispatcher 上呼叫 Trigger 是違法的,這麼做會導致 BAD_STATE 錯誤。因此,觸發作業只會在虛擬中斷物件上執行,且一律會在系統呼叫環境中執行,因為在該環境中,取得操作物件信號狀態所需的鎖定是合法的。
當下一次呼叫 zx_interrupt_wait (適用於非連接埠繫結中斷) 或呼叫 zx_interrupt_ack (適用於連接埠繫結中斷) 時,系統會確認中斷。同樣地,與 zx_interrupt_trigger 類似,呼叫是在系統呼叫環境中進行,可供訊號操控。
最後,當最後一個控制代碼關閉,或執行緒呼叫 zx_interrupt_destroy 時,中斷就會遭到破壞。同樣地,所有這些作業都會在可取得核心互斥鎖,且可斷言使用者信號的環境中進行,因此在明確終止物件時,可輕鬆斷言未觸發的信號 (解除封鎖任何等待者)。
因此,實作這項功能時,只要在觸發、確認、等待和終止這四項作業期間,簡單地多載 VirtualInterruptDispatcher 的行為即可。過載版本可能會在執行作業本身之前取得調度器鎖定,並在作業結束時視需要更新信號狀態。
這樣就完成了。UpdateState 和現有的標準 Zircon 信號機制,已處理所有複雜的解除封鎖執行緒和傳送埠封包作業。實體中斷物件不會公開信號,也不會使用任何虛擬調度器程式碼,因此不需要將任何信號狀態與硬體 IRQ 時間執行的作業同步。
人體工學
這項產品的人體工學設計預計會簡單且可接受。不需要對現有的中斷消費者進行任何變更,確認信號行為完全是自動的。虛擬中斷產生器只需使用現有的信號處理模式,即可產生信號。
安全性考量
這項功能新增後,中斷消費者和中斷生產者之間就多了一個新的通訊管道,生產者可以藉此得知消費者何時確認虛擬中斷。這是多工中斷系統運作的必要條件,不屬於獨立或不必要的旁路管道,因此不會造成安全風險。
隱私權注意事項
這項功能不會處理使用者資料或其他可能屬於私密的資訊,因此沒有已知的隱私權考量需要處理。
測試
測試應是現有單元測試的簡單擴充功能。我們基本上需要新增測試,確保:
- 建立虛擬中斷物件時,系統一開始會判斷
ZX_VIRTUAL_INTERRUPT_UNTRIGGERED信號。 - 系統會回應對
zx_interrupt_trigger的呼叫,清除信號。 - 系統會回應
zx_interrupt_ack的呼叫,並設定信號。 - 如果存在其他待處理的 IRQ,訊號會在確認時正確實作「閃爍」行為。
說明文件
虛擬中斷物件的文件將更新,說明新的訊號、指出訊號僅適用於虛擬中斷物件,並摘要說明上述行為規則 (初始斷言、觸發時清除、在 ack 上設定、在待處理的 ack 上閃爍)。
缺點、替代方案和未知事項
這種做法目前沒有明顯缺點。符合使用者需求。導入和測試作業應簡單且低風險。
Zircon 驅動程式庫團隊已討論多項替代提案,但這些提案都有相同的缺點。虛擬中斷的驅動程式消費者必須明確瞭解自己正在使用虛擬中斷,然後明確參與某些使用者層級定義的通訊協定,向中斷產生器發出確認訊號。
雖然這些方法可行,但似乎並不可取。如果使用虛擬中斷,驅動程式庫程式作者必須參與定義的任何通訊協定,並在實體中斷的情況下採取不同行為,這會大幅增加額外負擔。此外,現有的驅動程式族群也需要更新才能使用新通訊協定,如果核心以透明方式處理 ACK 訊號,則可完全略過這項作業。
無論實作哪種通訊協定,都完全是在使用者模式中實作,因此必須仔細考量潛在惡意人士造成的信任問題。透過 Zircon 核心調解信號機制,有助於簡化此處的分析。
最後,Zircon 中斷 已經中斷的使用者必須確認中斷 (隱含或明確)。很難想像有任何額外的頻外 ACK 通訊協定,能像在現有 ACK 發生時,單純操控核心物件的信號位元一樣有效率。