作者簡介
周鶴洋(GitHub: losfair):目前就讀于南京航空航天大學,此前在字節跳動、Vercel 等公司實習,現正與 DatenLord 團隊合作完成畢業設計項目 WaveBPF 硬件加速器。開源軟硬件開發者、獨立產品 Planet 作者,發起和參與過 Blueboat、Wasmer、Next.js 等開源項目,業余開發過超標量處理器 Violet、內核態 WebAssembly 運行時 kernel-wasm 等 OS 與硬件項目。
TLDR
簡單來說,wBPF 是一個在硬件上直接執行 eBPF 程序的系統。
它支持兩種使用模式:
-
網絡設備加速:集成到智能網卡中,對進/出網口的數據流實現觀測與變換;
-
多指令集模式:在通用系統(比如 RISC-V + Linux)中,支持切換 CPU 進入 eBPF 指令集模式,從 Linux 內核直接調用硬件能力執行 eBPF 字節碼。
Why?
eBPF 是一種來源于軟件實現的“字節碼格式”,并非傳統的硬件指令集,常規的實現方法是解釋器、JIT 或 AOT 編譯成硬件指令片段執行。eBPF 設計時即以可向主流指令集架構 1-1 翻譯為目標,JIT 編譯的性能損失比傳統的二進制翻譯技術要低很多,為什么還要嘗試用硬件實現呢?
-
首先,在“內核調用 eBPF“這一場景下,eBPF 需要訪問內核緩沖區;針對 eBPF 的內存訪問特性,專用的硬件邏輯路徑可以實現比通用 MMU 更高效的內存管理機制。高性能處理器頁表切換的開銷很高,尤其在硬件與內核針對 Meltdown / Spectre 等微架構旁信道信息泄漏問題實現 mitigation 后更是這樣;而 eBPF 程序的內存數據依賴相對規整,可以用類似“分段”的方法實現虛擬內存。
-
其次,eBPF 是 MagiCore 多指令集支持的實驗性目標,所以 ”because it’s possible” 也是做 wBPF 的理由之一.
MagiCore
MagiCore 是我的亂序處理器核心,設計成譯碼邏輯可插拔的架構,可支持 RV32/RV64、 eBPF、MIPS 等多種指令集。為什么要這樣做?首先目前開源的亂序處理器核心似乎不多 (BOOM, 香山, NaxRiscv) ,為 FPGA 優化的實現更少;其次也是因為個人興趣,可能很多學 CS 的同學和我一樣會有“從 silicon 到產品做一個全鏈路屬于自己的現代系統”這樣的想法吧。
下圖展示了 MagiCore 的流水線結構。各結構單元以是否處理數據為界以分為前端與后端兩個部分;前端包含取指單元(負責取指、預測)與譯碼單元,負責為后端生成指令流;后端包含涉及數據調度與指令執行的所有結構單元,負責由指令流分析數據依賴關系,執行計算、存儲計算結果與生成副作用。
MagiCore 流水線結構圖
以上是結構的視角;從指令處理流程的視角看,一條指令根據其數據依賴與生成的狀態可以分為五個生命周期階段:前端階段、分發階段、執行階段、預提交階段與提交階段。各階段對應的數據狀態如下圖所示:
進入后端前的指令處理不涉及數據,均屬于前端階段,包括分支預測、取指與譯碼。本階段輸出取指上下文 FetchContext 與譯碼上下文 DecodeContext,提供指令地址、指令字、分支預測狀態、緩存異常、譯碼異常、中斷注入、架構寄存器與執行單元信息。
指令進入執行后端后的第一個階段是分發階段 (Dispatch),指令分發邏輯利用前階段的譯碼信號與物理寄存器狀態表為指令依賴的數據與產生的數據分配物理寄存器,同時分配重排序緩沖槽,生成重命名上下文 RenameContext 與分發上下文 DispatchContext.
指令的所有數據依賴就緒后即會被發射,進入執行階段。發射邏輯從物理寄存器堆中取得源寄存器數據,為指令生成發射上下文 IssueContext.
指令執行完畢后進入預提交階段,生成提交請求 CommitRequest 寫入重排序緩沖,將指令計算結果寫入目的寄存器,并生成喚醒信號。提交請求包含重排序緩沖編號 robIndex、全局迭代編號 epoch、指令輸出值 regWriteValue 與指令異常 exception.
預提交階段指令的提交請求到達重排序緩沖頭部后進入提交階段,輸出物理寄存器狀態被更新為已提交,寄存器提交映射表 (CMT) 中與輸出架構寄存器對應的項被更新為輸出物理寄存器編號,副作用被廣播到副作用總線上,指令生命周期結束。
發射邏輯
MagiCore 采用了共享亂序發射隊列 / 順序發射隊列的設計。
僅有數據依賴關系約束而沒有全局順序約束的指令由亂序發射隊列發射,指令分發單元將指令推入發射隊列,此時可確定該指令的數據依賴。每個時鐘周期后,發射選擇邏輯從發射窗口中選擇所有數據依賴均已就緒的指令,發射到功能單元。
MagiCore 的亂序發射隊列基于優先級決定指令發射順序。每條被分發的指令會被分配一個 2-bit 的優先級,由 0 開始,每新推入一條指令,所有舊指令的優先級增加 1 直到飽和。
而訪存、控制寄存器訪問等指令具有副作用,須保證其副作用的產生順序滿足指令集架構對副作用順序的約束,為以較低的硬件成本實現上述順序保證,選擇以順序發射隊列發射此類指令。
數據喚醒
數據喚醒是通知亂序發射隊列“數據可用”的行為,所有源操作數均被喚醒是發射窗口中的指令被發射的必要條件。MagiCore 實現了常規喚醒與 ALU 快速喚醒兩條數據喚醒路徑,分別為不同類型的指令提供了較優的喚醒機制。
訪存
TODO
二進制翻譯
二進制翻譯分為局部分析與全局分析兩個階段。局部分析是對包含多個對象文件的 eBPF 程序的各個對象文件分別獨立進行分析的階段,包含ELF文件解析、函數符號表分析、重定位分析,棧用量靜態分析、調用/返回序列生成五個子階段。全局分析是對 eBPF 程序進行整體分析并最終生成 RISC-V 指令序列、輸出 Protobuf 目標鏡像的階段,包含數據段提取、偽調用解析、數據重定位鏈接、全局無用代碼消除、入口跳板生成、RISC-V 代碼生成、代碼重定位鏈接、偏移量表生成八個子階段。
局部分析
-
ELF文件解析
二進制翻譯軟件的輸入是文件類型為ELF64、目標機器類型為EM_BPF的ELF文件,本設計調用Rust語言第三方庫goblin實現ELF文件的解析,并驗證文件類型。
-
函數符號表分析
此階段解析ELF符號表中函數類型的表項,建立函數名稱到包含段編號、段內偏移的符號信息的映射,為后續分析作準備。對函數內eBPF指令的解析亦在此階段完成。
-
重定位分析
此階段解析ELF重定位表,建立從函數內指令位置到重定位信息的映射。
-
棧用量靜態分析
此階段逐函數對指令序列進行靜態分析,計算棧用量。
eBPF標準規定每次函數調用可用的棧幀空間固定為512字節,但大多數函數實際使用的棧幀空間遠小于512字節,調用函數時分配固定的棧幀空間會導致內存浪費。本設計針對棧指針用法的特例實現了優化,若某函數內部對棧指針的用法僅限于相對棧指針進行Load/Store操作的指令,則該函數的棧用量可被靜態計算,無須分配完整的512字節棧幀;否則若任何其他類型的指令引用了棧指針作為源寄存器,則放棄當前函數的棧用量靜態分析,采取保守策略分配512字節棧幀。
-
調用/返回序列生成
主流硬件指令集的ABI(應用程序二進制接口)均規定了調用方保存寄存器與被調用方保存寄存器,編譯器須在函數入口與返回處生成相應的調用/返回序列,對被調用方保存的寄存器進行保存與恢復。eBPF規范雖然亦有對調用方/被調用方保存寄存器的規定,但eBPF程序中不含調用/返回序列,相應的寄存器保存與恢復邏輯由運行環境實現。
對于每個函數,此階段遍歷其全部指令,識別出被作為目的寄存器使用的所有被調用方保存寄存器,后向函數頭部與返回 (EXIT) 指令前插入必要的棧空間分配、寄存器保存與恢復指令,將需保存的寄存器保存到棧上。下圖為插入調用/返回序列后的函數棧幀結構示例。
函數棧幀結構示例
全局分析
-
數據段提取
此階段對所有對象文件遍歷ELF段標頭,將數據段序列化到輸出緩沖區內,記錄每個數據段的相對偏移量,為后續重定位邏輯作準備。
-
偽調用解析
eBPF的函數調用分為環境調用與偽調用 (Pseudo Call) 兩類,環境調用是對主機環境提供的、非eBPF函數的調用,偽調用是對其他eBPF函數的調用。偽調用分為靜態偽調用與動態偽調用兩類,靜態偽調用的調用目標函數在對象文件生成時即確定,動態偽調用在動態鏈接時進行重定位而確定。由于局部鏈接階段可能向原始eBPF程序中插入新指令,靜態偽調用的調用偏移量亦須更新,此階段解析所有對象文件中的靜態與動態偽調用,存儲為應被重定位的指令的注解。
-
數據重定位鏈接
此階段根據ELF重定位表與子階段(一)提供的數據相對偏移量信息更新指令對數據引用的偏移量。
-
全局無用代碼消除 (Global DCE)
此階段以用戶提供的入口點為根結點,遍歷所有可達函數,移除未被使用的函數,降低輸出鏡像體積。
-
入口跳板生成
此階段是寫入指令鏡像的第一個階段,生成入口跳板代碼,從內存中加載寄存器初始值并跳轉到用戶指定的目標地址。
-
RISC-V代碼生成
此階段由經過上述處理的、帶注解的eBPF指令序列生成RISC-V指令序列,寫入指令鏡像。
-
代碼重定位鏈接
此階段根據步驟(二)偽調用解析所提供的指令注解,對步驟(六)生成的RISC-V指令序列中對指令地址的引用進行重定位。
-
偏移量表生成
至此指令鏡像與數據鏡像均生成完成,此階段根據上述步驟提供的元數據計算各函數起始位置在指令鏡像中的偏移量,生成偏移量表以供加載執行時指定入口函數。
Linux 設備驅動
目前 wBPF 測試用的平臺是 Zynq-7020,由處理系統 (PS) 上運行的軟件控制可編程邏輯 (PL) 上的自定義硬件。wBPF 硬件的設備樹定義如下:
wbpf0@43c00000 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "bluelogic,wbpf1";
clocks = <&clkc 15>;
reg = <0x43c00000 0x10000 0x7aa00000 0x20000>;
interrupt-parent = <&intc>;
interrupts = <0 29 4>;
};
內核模塊加載后即注冊為 Linux 平臺設備驅動;探測到設備樹節點后,創建 /dev 設備節點,通過 ioctl 接口向用戶態提供執行 eBPF 的能力。
實現
wBPF 的硬件系統(包括 MagiCore)采用 SpinalHDL 硬件描述語言設計、采用 Cocotb 驗證;軟件系統采用 Rust + C 開發。
原文標題:wBPF WXMP post submission
文章出處:【微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。
-
處理器
+關注
關注
68文章
19799瀏覽量
233433 -
Linux
+關注
關注
87文章
11455瀏覽量
212720 -
字節碼
+關注
關注
0文章
5瀏覽量
7458
原文標題:wBPF WXMP post submission
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
評論