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