万字长文|深入理解XDP全景指南
譯者序
本文翻譯自 2018 年 ACM?CoNEXT?大會上的一篇文章:
The eXpress Data Path: Fast Programmable Packet Processing in the Operating System Kernel
作者陣容豪華,包括來自 Cilium 的 Daniel Borkmann、John Fastabend 等。
論文引用信息:
Toke H?iland-J?rgensen, Jesper Dangaard Brouer, Daniel Borkmann, John Fastabend, Tom Herbert, David Ahern, and David Miller. 2018. The eXpress Data Path: Fast Programmable Packet Processing in the Operating System Kernel. In CoNEXT ’18: International Conference on emerging Networking EXperiments and Technologies, December 4–7, 2018, Heraklion, Greece. ACM, New York, NY, USA, 13 pages. https://doi.org/10.1145/3281411.3281443
由于譯者水平有限,本文不免存在遺漏或錯誤之處。如有疑問,請查閱原文。
以下是譯文。
譯者序
摘要
1 引言
1.1 現(xiàn)有方案(kernel bypass)存在的問題
1.2 新方案:給內(nèi)核網(wǎng)絡(luò)棧添加可編程能力
1.3 新方案(XDP)的優(yōu)點
1.4 本文組織結(jié)構(gòu)
2 相關(guān)工作
2.1 用戶態(tài)輪詢 vs. XDP
2.2 內(nèi)核模塊 vs. XDP
2.3 可編程硬件 vs. XDP
2.4 小結(jié)
3 XDP 設(shè)計
唯一加載入口:bpf()?系統(tǒng)調(diào)用
校驗器工作原理:two-pass DAG
內(nèi)存越界和空指針檢查:職責(zé)上移到程序自身/開發(fā)者
跟蹤數(shù)據(jù)訪問操作和值范圍
不同類型數(shù)據(jù)的校驗信息來源(source of truth)
校驗器的目的
在設(shè)備驅(qū)動中執(zhí)行,無需上下文切換
在軟件最早能處理包的位置執(zhí)行,性能最優(yōu)
XDP 程序典型執(zhí)行流
3.0 XDP 系統(tǒng)架構(gòu)
3.1 XDP driver hook
3.2 eBPF 虛擬機
3.3 BPF maps
3.4 eBPF verifier
3.5 XDP 程序示例
3.6 小結(jié)
4 性能評估
XDP 未做底層代碼優(yōu)化
通用目的操作系統(tǒng),首要目標:更好的擴展和配置,而非極致性能
轉(zhuǎn)發(fā)吞吐(pps)
轉(zhuǎn)發(fā)延遲
4.1 直接棄包(packet drop)性能
4.2 CPU Usage
4.3 包轉(zhuǎn)發(fā)性能
4.4 討論:XDP 性能與 DPDK 還有差距的原因
5 真實場景使用案例
Facebook Katran
性能
模擬 Cloudflare 防御架構(gòu)
程序邏輯
性能
內(nèi)核數(shù)據(jù)平面 & 控制平面(BIRD/FRR)
XDP:直接查詢內(nèi)核路由表并轉(zhuǎn)發(fā)
測試:XDP routing + 全球 BGP 路由表
性能:2.5x
5.1 案例一:軟件路由(software routing)
5.2 案例二:Inline DoS Mitigation
5.3 案例三:負載均衡(load balancing)
6 XDP 的未來方向
校驗邏輯偏保守
缺少標準庫
一個網(wǎng)卡接口只能 attach 一個 XDP 程序
6.1 eBPF 程序的限制
6.2 用戶體驗和調(diào)試
6.3 驅(qū)動支持
6.4 性能提升
6.5 QoS 和 Rate Transitions
6.6 加速傳輸層協(xié)議
6.7 內(nèi)核-用戶空間零拷貝(zero-copy to userspace)
6.8 XDP 作為基礎(chǔ)構(gòu)建模塊(XDP as a building block)
7 總結(jié)
致謝
參考文獻
摘要
近些年業(yè)界流行通過內(nèi)核旁路(kernel bypass)的方式實現(xiàn)?可編程的包處理過程(programmable packet processing)。實現(xiàn)方式是 將網(wǎng)絡(luò)硬件完全交由某個專門的用戶空間應(yīng)用(userspace application) 接管,從而避免內(nèi)核和用戶態(tài)上下文切換的昂貴性能開銷。
但是,操作系統(tǒng)被旁路(繞過)之后,它的應(yīng)用隔離(application isolation) 和安全機制(security mechanisms)就都失效了;一起失效的還有各種經(jīng)過已經(jīng) 充分測試的配置、部署和管理工具。
為解決這個問題,我們提出一種新的可編程包處理方式:eXpress Data Path (XDP)。
XDP 提供了一個仍然基于操作系統(tǒng)內(nèi)核的安全執(zhí)行環(huán)境,在設(shè)備驅(qū)動上下文?(device driver context)中執(zhí)行,可用于定制各種包處理應(yīng)用。
XDP 是主線內(nèi)核(mainline Linux kernel)的一部分,與現(xiàn)有的內(nèi)核 網(wǎng)絡(luò)棧(kernel’s networking stack)完全兼容,二者協(xié)同工作。
XDP 應(yīng)用(application)通過 C 等高層語言編寫,然后編譯成特定字節(jié)碼;出于安 全考慮,內(nèi)核會首先對這些字節(jié)碼執(zhí)行靜態(tài)分析,然后再將它們翻譯成?處理器原生指令(native instructions)。
測試結(jié)果顯示,XDP 能達到?24Mpps/core?的處理性能。
為展示 XDP 靈活的編程模型,本文還將給出三個程序示例,
layer-3 routing(三層路由轉(zhuǎn)發(fā))
inline DDoS protection(DDoS 防護)
layer-4 load balancing(四層負載均衡)
1 引言
軟件實現(xiàn)高性能包處理的場景,對每個包的處理耗時有著極高的要求。通用目的操作系統(tǒng)中 的網(wǎng)絡(luò)棧更多是針對靈活性的優(yōu)化,這意味著它們花在每個包上 的指令太多了,不適合網(wǎng)絡(luò)高吞吐的場景。
因此,隨后出現(xiàn)了一些專門用于包處理的軟件開發(fā)工具,例如 Data Plane Development Kit (DPDK) [16]。這些工具一般都會完全繞過內(nèi)核,將網(wǎng)絡(luò)硬件直接交 給用戶態(tài)的網(wǎng)絡(luò)應(yīng)用,并需要獨占一個或多個 CPU。
1.1 現(xiàn)有方案(kernel bypass)存在的問題
內(nèi)核旁路方式可以顯著提升性能,但缺點也很明顯:
很難與現(xiàn)有系統(tǒng)集成;
上層應(yīng)用必須要將內(nèi)核中已經(jīng)非常成熟的模塊在用戶態(tài)重新實現(xiàn)一遍,例如路由表、高層協(xié)議棧等;
最壞的情況下,這種包處理應(yīng)用只能工作在一個完全隔絕的環(huán)境,因為內(nèi)核提供的常見工具和部署方式在這種情況下都不可用了。
導(dǎo)致系統(tǒng)越來越復(fù)雜,而且破壞了操作系統(tǒng)內(nèi)核在把控的安全邊界。?在基礎(chǔ)設(shè)施逐漸遷移到 Kubernetes/Docker 等容器環(huán)境的背景下,這一點顯得尤其嚴重, 因為在這種場景下,內(nèi)核擔(dān)負著資源抽象和隔離的重任。
1.2 新方案:給內(nèi)核網(wǎng)絡(luò)棧添加可編程能力
對此,本文提供了另一種解決方案:給內(nèi)核網(wǎng)絡(luò)棧添加可編程能力。這使得我們能在?兼容各種現(xiàn)有系統(tǒng)、復(fù)用已有網(wǎng)絡(luò)基礎(chǔ)設(shè)施的前提下,仍然實現(xiàn)高速包處理。?這個框架稱為 XDP,
XDP 定義了一個受限的執(zhí)行環(huán)境(a limited execution environment),運行在一個?eBPF 指令虛擬機中。eBPF 是 BSD Packet Filter (BPF) [37] 的擴展。
XDP 程序運行在內(nèi)核上下文中,此時內(nèi)核自身都還沒有接觸到包數(shù)據(jù)( before the kernel itself touches the packet data),這使得我們能在網(wǎng)卡收到包后?最早能處理包的位置,做一些自定義數(shù)據(jù)包處理(包括重定向)。
內(nèi)核在加載(load)時執(zhí)行靜態(tài)校驗,以確保用戶提供的 XDP 程序的安全。
之后,程序會被動態(tài)編譯成原生機器指令(native machine instructions),以獲得高性能。
XDP 已經(jīng)在過去的幾個內(nèi)核 release 中逐漸合并到內(nèi)核,但在本文之前,還沒有關(guān)于 XDP 系統(tǒng)的 完整架構(gòu)介紹。本文將對 XDP 做一個高層介紹。
1.3 新方案(XDP)的優(yōu)點
我們的測試結(jié)果顯示 XDP 能取得?24Mpps/core?的處理性能,這雖然與 DPDK 還有差距, 但相比于后者這種 kernel bypass 的方式,XDP 有非常多的優(yōu)勢。
具體地,XDP:
與內(nèi)核網(wǎng)絡(luò)棧協(xié)同工作,將硬件的控制權(quán)完全留在內(nèi)核范圍內(nèi)。帶來的好處:
保持了內(nèi)核的安全邊界
無需對網(wǎng)絡(luò)配置或管理工具做任何修改
無需任何特殊硬件特性,任何有 Linux 驅(qū)動的網(wǎng)卡都可以支持, 現(xiàn)有的驅(qū)動只需做一些修改,就能支持 XDP hooks。
可以選擇性地復(fù)用內(nèi)核網(wǎng)絡(luò)棧中的現(xiàn)有功能,例如路由表或 TCP/IP 協(xié)議棧,在保持配置接口不變的前提下,加速關(guān)鍵性能路徑(critical performance paths)。
保證 eBPF 指令集和 XDP 相關(guān)的編程接口(API)的穩(wěn)定性。
與常規(guī) socket 層交互時,沒有從用戶態(tài)將包重新注入內(nèi)核的昂貴開銷。
對應(yīng)用透明。這創(chuàng)造了一些新的部署場景/方式,例如直接在應(yīng)用所 在的服務(wù)器上部署 DoS 防御(而非中心式/網(wǎng)關(guān)式 DoS 防御)。
服務(wù)不中斷的前提下動態(tài)重新編程(dynamically re-program), 這意味著可以按需加入或移除功能,而不會引起任何流量中斷,也能動態(tài)響應(yīng)系統(tǒng)其他部分的的變化。
無需預(yù)留專門的 CPU 做包處理,這意味著 CPU 功耗與流量高低直接相關(guān),更節(jié)能。
1.4 本文組織結(jié)構(gòu)
接下來的內(nèi)容介紹 XDP 的設(shè)計,并做一些性能分析。結(jié)構(gòu)組織如下:
Section 2?介紹相關(guān)工作;
Section 3?介紹 XDP 系統(tǒng)的設(shè)計;
Section 4?做一些性能分析;
Section 5?提供了幾個真實 XDP 場景的程序例子;
Section 6?討論 XDP 的未來發(fā)展方向;
Section 7?總結(jié)。
2 相關(guān)工作
XDP當(dāng)然不是第一個支持可編程包處理的系統(tǒng) —— 這一領(lǐng)域在過去幾年發(fā)展勢頭良好, 并且趨勢還在持續(xù)。業(yè)內(nèi)已經(jīng)有了幾種可編程包處理框架,以及基于這些框架的新型應(yīng)用,包括:
單一功能的應(yīng)用,如 switching [47], routing [19], named-based forwarding [28], classification [48], caching [33] or traffic generation [14]。
更加通用、且高度可定制的包處理解決方案,能夠處理從多種源收來的數(shù)據(jù)包 [12, 20, 31, 34, 40, 44]。
要基于通用(Common Off The Shelf,COTS)硬件實現(xiàn)高性能包處理,就必須解決?網(wǎng)卡(NIC)和包處理程序之間的所有瓶頸。由于性能瓶頸主要來源于內(nèi)核和用戶態(tài)應(yīng)用之間的接口(系統(tǒng)調(diào)用開銷非常大,另外,內(nèi)核功能豐富,但也非常復(fù)雜), 低層(low-level)框架必須通過這樣或那樣的方式來降低這些開銷。
現(xiàn)有的一些框架通過幾種不同的方式實現(xiàn)了高性能,XDP構(gòu)建在其中一些技術(shù)之上。?接下來對 XDP 和它們的異同做一些比較分析。
2.1 用戶態(tài)輪詢 vs. XDP
DPDK?[16] 可能是使用最廣泛的高性能包處理框架。它最初只支持 Intel 網(wǎng)卡,后來逐 步擴展到其他廠商的網(wǎng)卡。DPDK 也稱作內(nèi)核旁路框架(kernel bypass framework), 因為它將網(wǎng)絡(luò)硬件的控制權(quán)從內(nèi)核轉(zhuǎn)移到了用戶態(tài)的網(wǎng)絡(luò)應(yīng)用,完全避免了內(nèi)核-用戶態(tài) 之間的切換開銷。
與DPDK 類似的還有?PF_RING?ZC module [45] 和 hardware-specific Solarflare OpenOnload [24]。
在現(xiàn)有的所有框架中,內(nèi)核旁路方式性能是最高的?[18];但如引言中指 出,這種方式在管理、維護和安全方面都存在不足。
XDP 采用了一種與內(nèi)核旁路截然相反的方式:相比于將網(wǎng)絡(luò)硬件的控制權(quán)上移到用戶空間, XDP 將性能攸關(guān)的包處理操作直接放在內(nèi)核中,在操作系統(tǒng)的網(wǎng)絡(luò)棧之前執(zhí)行。
這種方式同樣避免了內(nèi)核-用戶態(tài)切換開銷(所有操作都在內(nèi)核);
但仍然由內(nèi)核來管理硬件,因此保留了操作系統(tǒng)提供的管理接口和安全防護能力;
這里的主要創(chuàng)新是:使用了一個虛擬執(zhí)行環(huán)境,它能對加載的 程序進行校驗,確保它們不會對內(nèi)核造成破壞。
2.2 內(nèi)核模塊 vs. XDP
在 XDP 之前,以內(nèi)核模塊(kernel module)方式實現(xiàn)包處理功能代價非常高, 因為程序執(zhí)行出錯時可能會導(dǎo)致整個系統(tǒng)崩潰,而且內(nèi)核的內(nèi)部 API 也會隨著時間發(fā)生變化。?因此也就不難理解為什么只有很少的系統(tǒng)采用了這種方式。其中做的比較好包括
虛擬交換機?Open vSwitch [44]
Click [40]
虛擬機路由器?Contrail [41]
這幾個系統(tǒng)都支持靈活的配置,適用于多種場景,取得比較小的平攤代價。
XDP通過:
提供一個安全的執(zhí)行環(huán)境,以及內(nèi)核社區(qū)支持,提供與那些暴露到用戶空間一樣穩(wěn)定的內(nèi)核 API
極大地降低了那些將處理過程下沉到內(nèi)核的應(yīng)用(applications of moving processing into the kernel)的成本。
此外,XDP 程序也能夠完全繞過內(nèi)核網(wǎng)絡(luò)棧(completely bypass), 與在內(nèi)核網(wǎng)絡(luò)棧中做 hook 的傳統(tǒng)內(nèi)核模塊相比,性能也更高。
XDP 除了能將處理過程下沉到內(nèi)核以獲得最高性能之外,還支持在程序中執(zhí)行重定向?(redirection)操作,完全繞過內(nèi)核網(wǎng)絡(luò)棧,將包送到特殊類型的用戶空間 socket;?甚至能工作在 zero-copy 模式,進一步降低開銷。
這種模式與 Netmap [46] 和 PF_RING [11] 方式類似,但后者是在沒有完全繞過內(nèi) 核的情況下,通過降低從網(wǎng)絡(luò)設(shè)備到用戶態(tài)應(yīng)用(network device to userspace application)之間的傳輸開銷,實現(xiàn)高性能包處理。
內(nèi)核模塊方式的另一個例子是 Packet I/O engine,這是 PacketShader [19] 的組成部分, 后者專用于 Arrakis [43] and ClickOS [36] 之類的特殊目的操作系統(tǒng)。
2.3 可編程硬件 vs. XDP
可編程硬件設(shè)備也是一種實現(xiàn)高性能包處理的方式。
一個例子是 NetFPGA [32],通過對它暴露的 API 進行編程,能夠在這種基于 FPGA 的專 用設(shè)備上運行任何包處理任務(wù)。
P4 編程語言 [7] 致力于將這種可編程能力擴展到更廣泛的包處理硬件上?(巧合的是,它還包括了一個 XDP backend [51])。
某種意義上來說,XDP 可以認為是一種?offload?方式:
性能敏感的處理邏輯下放到網(wǎng)卡驅(qū)動中,以提升性能;
其他的處理邏輯仍然走內(nèi)核網(wǎng)絡(luò)棧;
如果沒有用到內(nèi)核 helper 函數(shù),那整個 XDP 程序都可以 offload 到網(wǎng)卡(目前 Netronome smart-NICs [27] 已經(jīng)支持)。
2.4 小結(jié)
XDP 提供了一種高性能包處理方式,與已有方式相比,在性能、與現(xiàn)有系統(tǒng)的集成、靈活性 等方面取得了更好的平衡。接下來介紹 XDP 是如何取得這種平衡的。
3 XDP 設(shè)計
XDP 的設(shè)計理念:
高性能包處理
集成到操作系統(tǒng)內(nèi)核(kernel)并與之協(xié)同工作,同時
確保系統(tǒng)其它部分的安全性(safety)和完整性(integrity)
這種與內(nèi)核的深度集成顯然會給設(shè)計帶來一些限制,在 XDP 組件合并到 Linux 的過程中,我們也收到了許多來自社區(qū)的反饋,促使我們不斷調(diào)整 XDP 的設(shè)計,但 這些設(shè)計反思不在本文討論范圍之內(nèi)。
3.0 XDP 系統(tǒng)架構(gòu)
圖 1 描繪了整個 XDP 系統(tǒng),四個主要組成部分:
XDP driver hook:XDP 程序的主入口,在網(wǎng)卡收到包執(zhí)行。
eBPF virtual machine:執(zhí)行 XDP 程序的字節(jié)碼,以及對字節(jié)碼執(zhí)行 JIT 以提升性能。
BPF maps:內(nèi)核中的 key/value 存儲,作為圖中各系統(tǒng)的主要通信通道。
eBPF verifier:加載程序時對其執(zhí)行靜態(tài)驗證,以確保它們不會導(dǎo)致內(nèi)核崩潰。
Fig 1. XDP 與 Linux 網(wǎng)絡(luò)棧的集成。這里只畫了 ingress 路徑,以免圖過于復(fù)雜。
上圖是 ingress 流程。網(wǎng)卡收到包之后,在處理包數(shù)據(jù)(packet data)之前,會先執(zhí)行 main XDP hook 中的 eBPF 程序。?這段程序可以選擇:
丟棄(drop)這個包;或者
通過當(dāng)前網(wǎng)卡將包再發(fā)送(send)出去;或者
將包重定向(redirect)到其他網(wǎng)絡(luò)接口(包括虛擬機的虛擬網(wǎng)卡),或者通過 AF_XDP socket 重定向到用戶空間;或者
放行(allow)這個包,如果后面沒有其他原因?qū)е碌?drop,這個包就會進入常規(guī)的內(nèi)核網(wǎng)絡(luò)棧。如果是這種情況,也就是放行包進入內(nèi)核網(wǎng)絡(luò)棧,那接下來在將包放到發(fā)送隊列之前(before packets are queued for transmission), 還有一個能執(zhí)行 BPF 程序的地方:TC BPF hook。
此外,圖 1 中還可以看出,不同的 eBPF 程序之間、eBPF 程序和用戶空間應(yīng)用之間,都能夠通過 BPF maps 進行通信。
3.1 XDP driver hook
在設(shè)備驅(qū)動中執(zhí)行,無需上下文切換
XDP 程序在網(wǎng)絡(luò)設(shè)備驅(qū)動中執(zhí)行,網(wǎng)絡(luò)設(shè)備每收到一個包,程序就執(zhí)行一次。
相關(guān)代碼實現(xiàn)為一個內(nèi)核庫函數(shù)(library function),因此程序直接 在設(shè)備驅(qū)動中執(zhí)行,無需切換到用戶空間上下文。
在軟件最早能處理包的位置執(zhí)行,性能最優(yōu)
回到上面圖 1 可以看到:程序在網(wǎng)卡收到包之后最早能處理包的位置?執(zhí)行 —— 此時內(nèi)核還沒有為包分配?struct sk_buff?結(jié)構(gòu)體, 也沒有執(zhí)行任何解析包的操作。
XDP 程序典型執(zhí)行流
下圖是一個典型的 XDP 程序執(zhí)行流:
Fig 2. 典型 XDP 程序的執(zhí)行流。
網(wǎng)卡收到一個包時,XDP程序依次執(zhí)行:
提取包頭中的信息(例如 IP、MAC、Port、Proto 等),
執(zhí)行到程序時,系統(tǒng)會傳遞給它一個上下文對象(context object)作為參賽?(即?struct xdp_md *ctx,后面有例子),其中包括了指向原 始包數(shù)據(jù)的指針,以及描述這個包是從哪個網(wǎng)卡的哪個接口接收上來的等元數(shù)據(jù)字段。
讀取或更新一些資源的元信息(例如更新統(tǒng)計信息);
解析包數(shù)據(jù)之后,XDP 程序可以讀取?ctx?中的包元數(shù)據(jù)(packet metadata) 字段,例如從哪個網(wǎng)卡的哪個接口收上來的(ifindex)。除此之外,ctx 對象還允許 程序訪問與包數(shù)據(jù)毗鄰的一塊特殊內(nèi)存區(qū)域(cb, control buffer), 在包穿越整個系統(tǒng)的過程中,可以將自定義的數(shù)據(jù)塞在這里。
除了 per-packet metadata,XDP 程序還可以通過 BPF map 定義和訪問自己的持久數(shù)據(jù)?,以及通過各種 helper 函數(shù)訪問內(nèi)核基礎(chǔ)設(shè)施。
BPF map 使 BPF 程序能與系統(tǒng)的其他部分之間通信;
Helpers 使 BPF 程序能利用到某些已有的內(nèi)核功能(例如路由表), 而無需穿越整個內(nèi)核網(wǎng)絡(luò)棧。
如果有需要,對這個包進行?rewrite header?操作,
程序能修改包數(shù)據(jù)的任何部分,包括添加或刪除包頭。這使得 XDP 程序能執(zhí)行封裝/接封裝操作,以及重寫(rewrite)地址字段然后轉(zhuǎn)發(fā)等操作。
內(nèi)核 helper 函數(shù)各有不同用途,例如修改一個包之后,計算新的校驗和(checksum)。
進行最后的判決(verdict),確定接下來對這個包執(zhí)行什么操作;
判決結(jié)果包括:
重定向功能的用途:
這些不同的路徑,在圖 1 對應(yīng)的是幾條實線。
將重定向判決(verdict)與重定向目標(target)分開,使得重定向目標類型很容易擴展;?另外,由于重定向參數(shù)(目標)是通過 BPF map 查詢的,因此無需修 改 XDP 程序,就能動態(tài)修改重定向目標。
三種簡單返回碼:丟棄這個包、通過接收時的網(wǎng)卡將包重新發(fā)送出去、允許這個包進入內(nèi)核網(wǎng)絡(luò)棧;
第四種返回碼 redirect:允許 XDP 程序指定網(wǎng)卡、CPU、用戶態(tài) socket?等,將包重定向過去。
將原始包通過另一個網(wǎng)卡(包括虛擬機的虛擬網(wǎng)卡)發(fā)送出去;
轉(zhuǎn)發(fā)給指定?CPU?做進一步處理;
轉(zhuǎn)發(fā)給?AF_XDP 類型的 socket?做進一步處理;
程序還能通過尾調(diào)用(tail call),將控制權(quán)交給另一個 XDP 程序;?通過這種方式,可以將一個大程序拆分成幾個邏輯上的小程序(例如,根據(jù) IPv4/IPv6)。
由于 XDP 程序可包含任意指令,因此前三步(讀取包數(shù)據(jù)、處理元數(shù)據(jù)、重寫包數(shù)據(jù))?順序可以是任意的,而且支持多層嵌套。?但實際中為了獲得高性能,大部分情況下還是將執(zhí)行結(jié)構(gòu)組織成這順序的三步。
3.2 eBPF 虛擬機
XDP 程序在 Extended BPF (eBPF) 虛擬機中執(zhí)行。eBPF 是早期 BSD packet filter (BPF) [37] 的擴展,后者在過去的幾十年中廣泛 應(yīng)用于各種包處理工具。
BPF 使用?基于寄存器的(register-based) virtual machine 來描述?過濾動作(filtering actions)。
eBPF 虛擬機支持動態(tài)加載(loading)和重加載(re-loading)程序,內(nèi)核管理所有 BPF 程序的生命周期。
3.3 BPF maps
eBPF 程序在觸發(fā)內(nèi)核事件時執(zhí)行(例如,觸發(fā) XDP 程序執(zhí)行的,是收包事件)。?程序每次執(zhí)行時,初始狀態(tài)都是相同的(即程序是無狀態(tài)的),它們無法直接訪問?內(nèi)核中的持久存儲(BPF map)。為此,內(nèi)核提供了訪問 BPF map 的 helper 函數(shù)。
BPF map 是 key/value 存儲,在加載 eBPF 程序時定義(defined upon loading an eBPF program)。
用途:
持久存儲。例如一個 eBPF 程序每次執(zhí)行時,都會從里面獲取上一次的狀態(tài)。
用于協(xié)調(diào)兩個或多個 eBPF 程序。例如一個往里面寫數(shù)據(jù),一個從里面讀數(shù)據(jù)。
用于用戶態(tài)程序和內(nèi)核 eBPF 程序之間的通信。
3.4 eBPF verifier
唯一加載入口:bpf()?系統(tǒng)調(diào)用
由于 eBPF 代碼直接運行在內(nèi)核地址空間,因此它能直接訪問 —— 也可 能是破壞 —— 任何內(nèi)存。為防止這種情況發(fā)生,內(nèi)核規(guī)定只能通過唯一入口(?bpf()?系統(tǒng)調(diào)用)加載 BPF 程序。
加載 BPF 程序時,位于內(nèi)核中的校驗器首先會對字節(jié)碼程序進行靜態(tài)分析,以確保
程序中沒有任何不安全的操作(例如訪問任意內(nèi)存),
程序會終止(terminate)。通過下面這兩點來實現(xiàn):
禁止循環(huán)操作
限制程序最大指令數(shù)
校驗器工作原理:two-pass DAG
校驗器的工作原理:首先根據(jù)程序的控制流構(gòu)建一個有向無環(huán)圖(DAG), 然后對 DAG 執(zhí)行如下校驗:
首先,對 DAG 進行一次深度優(yōu)先搜索(depth-first search),以 確保它是無環(huán)的(acyclic),例如,沒有循環(huán),也不包含不支持或無法執(zhí)行到的指令。
然后,再掃描一遍,這次會遍歷 DAG 的所有可能路徑。這次掃描的目的是:
程序執(zhí)行?load?或?call?指令時,如果參數(shù)不合法,就會在這里被拒絕。參數(shù)合法 性是通過在程序執(zhí)行期間跟蹤所有寄存器和棧變量的狀態(tài)(states of registers and stack variables)來實現(xiàn)的。
確保程序的內(nèi)存訪問都是安全的,
調(diào)用?helper 函數(shù)時傳的參數(shù)類型是對的。
內(nèi)存越界和空指針檢查:職責(zé)上移到程序自身/開發(fā)者
這種跟蹤寄存器狀態(tài)的機制是為了在無法預(yù)知內(nèi)存邊界的情況下,仍然確保程序 的內(nèi)存訪問不會越界。無法預(yù)知內(nèi)存邊界是因為:
包的大小是不固定的;
map 的內(nèi)容也無法提前預(yù)知,因此也無法判斷一次 map 查找操作是否會成功。
為解決這個問題,校驗器會檢查已加載的程序自身是否會做如下檢查:
解引用指針前做了內(nèi)存邊界檢查,
查詢 map 之前是檢查了 map 指針是否為空。
這種方式將處理邏輯中的安全檢查和遇到錯誤時如何處理的控制權(quán)都?交給了 BPF 程序的編寫者。
跟蹤數(shù)據(jù)訪問操作和值范圍
為跟蹤數(shù)據(jù)訪問,校驗器會跟蹤
數(shù)據(jù)類型
指針偏置(pointer offsets)
所有寄存器的可能值范圍
程序開始時,
R1?寄存器中存儲的是指向 context metadata 的指針(struct xdp_md *ctx),
R10?是棧指針(stack pointer),
其他所有寄存器都是未初始化狀態(tài)。
接下來程序每執(zhí)行一步,寄存器狀態(tài)就會更新一次。當(dāng)寄存器中存入一個新值時,這個寄存器 還會繼承與這個值相關(guān)的狀態(tài)變量(inherits the state variables from the source of the value)。
算術(shù)操作會影響標量類型的值的范圍(value ranges of scalar types),以及指針類型的 offset。?可能的最大范圍(max possible range)存儲在狀態(tài)變量中,例如往寄存器中 load 一個字節(jié)時, 這個寄存器的可能值范圍就設(shè)置為 0~255。指令圖(instruction graph)中的?各邏輯分支就會根據(jù)操作結(jié)果更新寄存器狀態(tài)。例如,比較操作?R1 > 10,
校驗器在一個分支?if R1 > 10?中會將 R1 最小值設(shè)為 11,
在另一個?else?分支中將其最大值設(shè)為 10。
不同類型數(shù)據(jù)的校驗信息來源(source of truth)
利用狀態(tài)變量中存儲的范圍信息,校驗器就能預(yù)測每個 load 指令能訪問的所有 內(nèi)存范圍,確保它執(zhí)行的都是合法內(nèi)存訪問。
對于包數(shù)據(jù)(packet data)的訪問,會與 context 對象中的?data_end?變量做比較;
對于?BPF map 中獲取的值,或用到 map 定義中聲明的 data size 信息;
對于棧上存儲的值,會檢查狀態(tài)變量中記錄的值范圍;
對于指針算術(shù)操作(pointer arithmetic)還會施加額外的限制,指針通常不能被轉(zhuǎn)換成整形值。
只要校驗器無法證明某個操作是安全,該?BPF 程序在加載時(load time)就會被拒絕。?除此之外,校驗器還會利用范圍信息確保內(nèi)存的對齊訪問(enforce aligned memory access)。
校驗器的目的
需要說明的是,校驗器的目的是避免將內(nèi)核內(nèi)部(the internals of the kernel )暴露給惡意或有缺陷的 eBPF 程序,而非確保程序中函數(shù)的實現(xiàn)已經(jīng)是最高效的。
換句話說,如果 XDP 程序中處理邏輯過多,也可能會導(dǎo)致機器變慢 ;如果代碼寫的有問題,也可能會破壞包數(shù)據(jù)。出于這些原因,加載 BPF 程序需要 管理員權(quán)限(root)。避免這些 bug 的責(zé)任在程序員,但選擇將哪些程序加載 到系統(tǒng)的權(quán)限在管理員。
3.5 XDP 程序示例
下面是一個簡單的 XDP 程序,展示了前面介紹的一些特性。?程序會解析包數(shù)據(jù),判斷如果是 UDP 包,直接交換源和目的 MAC 地址,然后將包從相同網(wǎng)卡再發(fā)送回去,
雖然這是一個非常簡單的例子,但真實世界中的 XDP 程序用到的組件和特性,這里基本都具備了。
// 從內(nèi)核 BPF 代碼示例 xdp2_kern.c 修改而來。1 // 用于統(tǒng)計包數(shù) 2 struct bpf_map_def SEC("maps") rxcnt = { 3 .type = BPF_MAP_TYPE_PERCPU_ARRAY, 4 .key_size = sizeof(u32), // IP 協(xié)議類型,即 IPv4/IPv6 5 .value_size = sizeof(long), // 包數(shù) 6 .max_entries = 256, 7 }; 8 9 // 直接操作包數(shù)據(jù)(direct packet data access),交換 MAC 地址 10 static void swap_src_dst_mac(void *data) 11 { 12 unsigned short *p = data; 13 unsigned short dst[3]; 14 dst[0] = p[0]; dst[1] = p[1]; dst[2] = p[2]; 15 p[0] = p[3]; p[1] = p[4]; p[2] = p[5]; 16 p[3] = dst[0]; p[4] = dst[1]; p[5] = dst[2]; 17 } 18 19 static int parse_ipv4(void *data, u64 nh_off, void *data_end) 20 { 21 struct iphdr *iph = data + nh_off; 22 if (iph + 1 > data_end) 23 return 0; 24 return iph->protocol; 25 } 26 27 SEC("xdp1") // marks main eBPF program entry point 28 int xdp_prog1(struct xdp_md *ctx) 29 { 30 void *data_end = (void *)(long)ctx->data_end; 31 void *data = (void *)(long)ctx->data; 32 struct ethhdr *eth = data; int rc = XDP_DROP; 33 long *value; u16 h_proto; u64 nh_off; u32 ipproto; 34 35 nh_off = sizeof(*eth); 36 if (data + nh_off > data_end) 37 return rc; 38 39 h_proto = eth->h_proto; 40 41 /* check VLAN tag; could be repeated to support double-tagged VLAN */ 42 if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) { 43 struct vlan_hdr *vhdr; 44 45 vhdr = data + nh_off; 46 nh_off += sizeof(struct vlan_hdr); 47 if (data + nh_off > data_end) 48 return rc; 49 h_proto = vhdr->h_vlan_encapsulated_proto; 50 } 51 52 if (h_proto == htons(ETH_P_IP)) 53 ipproto = parse_ipv4(data, nh_off, data_end); 54 else if (h_proto == htons(ETH_P_IPV6)) 55 ipproto = parse_ipv6(data, nh_off, data_end); 56 else 57 ipproto = 0; 58 59 /* lookup map element for ip protocol, used for packet counter */ 60 value = bpf_map_lookup_elem(&rxcnt, &ipproto); 61 if (value) 62 *value += 1; 63 64 /* swap MAC addrs for UDP packets, transmit out this interface */ 65 if (ipproto == IPPROTO_UDP) { 66 swap_src_dst_mac(data); 67 rc = XDP_TX; 68 } 69 return rc; 70 }具體地:
定義了一個 BPF map 存儲統(tǒng)計信息。用戶態(tài)程序可以 poll 這個 map 來獲取統(tǒng)計信息。
context 對象?struct xdp_md *ctx?中有包數(shù)據(jù)的 start/end 指針,可用于直接訪問包數(shù)據(jù)。
將數(shù)據(jù)指針和?data_end?比較,確保內(nèi)存訪問不會越界。
程序必須自己解析包,包括 VLAN headers 等東西。
直接通過指針(direct packet data access)修改包頭。
內(nèi)核提供的 map lookup helper。這是程序中唯一的真實函數(shù)調(diào)用;其他函數(shù)都是內(nèi)聯(lián),包括?htons()。
最終針對這個包的判決通過程序返回值傳遞給調(diào)用方。
將這段程序安裝到網(wǎng)卡接口上時,它首先會被編譯成 eBPF 字節(jié)碼,然后經(jīng)受校驗器檢查。?這里的檢查項包括:
無循環(huán)操作;程序大小(指令數(shù)量);
訪問包數(shù)據(jù)之前,做了內(nèi)存邊界檢查;
傳遞給 map lookup 函數(shù)的參數(shù),類型與 map 定義相匹配;
map lookup 的返回值(value 的內(nèi)存地址)在使用之前,檢查了是否為 NULL。
3.6 小結(jié)
XDP 系統(tǒng)由四個主要部分組成:
XDP device driver hook:網(wǎng)卡收到包之后直接運行;
eBPF虛擬機:執(zhí)行 XDP 程序(以及內(nèi)核其他模塊加載的 BPF 程序);
BPF maps:使不同 BPF 程序之間、BPF 程序與用戶空間應(yīng)用之間能夠通信;
eBPF verifier:確保程序不包含任何可能會破壞內(nèi)核的操作。
這四部分加在一起,創(chuàng)造了一個編寫自定義包處理應(yīng)用的強大環(huán)境,它能加速包處理的關(guān)鍵 路徑,同時還與內(nèi)核及現(xiàn)有基礎(chǔ)設(shè)施密切集成。
接下來看一下 XDP 應(yīng)用的性能。
4 性能評估
DPDK 是目前性能最高的包處理框架 [18],因此本文將 XDP 與 DPDK 及 Linux 內(nèi)核網(wǎng)絡(luò) 棧的性能做一個對比。測試機器環(huán)境:
CPU:一塊 hexa-core Intel Xeon E5-1650 v4 CPU running at 3.60GHz, 支持 Intel Data Direct I/O (DDIO) 技術(shù),網(wǎng)絡(luò)硬件通過 DMA 能直接將包放到 CPU 緩存。
關(guān)閉超線性(Hyperthreading)。
網(wǎng)卡:兩塊?Mellanox ConnectX-5 Ex VPI dual-port 100Gbps,mlx5 驅(qū)動。
內(nèi)核:Linux 4.18
使用基于 DPDK 的 TRex packet generator [9] 生成測試流量。所有測試腳本位于 [22]。
在測試中,我們主要關(guān)心三個 metric:
直接棄包(packet drop)性能。
為展示最高的包處理性能,我們將用最簡單的操作 ——?丟棄接收到的包?—— 來測試。?這個測試能有效測量系統(tǒng)的整體開銷,也是真正的包處理應(yīng)用能達到的性能上限。
CPU 使用量。
如引言中指出,XDP 的優(yōu)點之一是 CPU 使用量與流量大小是正相關(guān)的,而無需預(yù)留專 門的 CPU 給它用。我們通過測量 CPU 利用率隨網(wǎng)絡(luò)負載的變化來量化這個指標。
包轉(zhuǎn)發(fā)性能。
轉(zhuǎn)發(fā)的復(fù)雜性要更高一些,例如,涉及到與多塊網(wǎng)卡的交互、重寫二層頭等等。?這里會將轉(zhuǎn)發(fā)延遲也考慮進去。
我們已經(jīng)驗證,使用 MTU(1500 字節(jié))包時,我們的系統(tǒng)單核就能達到線速(100 Gbps), 而且 CPU 有 50% 是空閑的。顯然,真正的挑戰(zhàn)在于 PPS,而非帶寬,其他一些測試也已經(jīng)指出了這一點 [46]。?出于這個原因,我們用最小包(64 字節(jié))測試,衡量指標是 PPS。
對于 XDP 和 Linux 內(nèi)核網(wǎng)絡(luò)棧的測試,由于它們沒有顯式指定某些 CPU 來處理網(wǎng)絡(luò)包的方式,因此我們通過配置硬件 RSS(Receive Side Scaling)來講流量定向到指定 CPU。
對網(wǎng)卡、內(nèi)核的一些配置調(diào)優(yōu),見代碼倉庫 [22]。
4.1 直接棄包(packet drop)性能
Fig 3. 直接棄包(packet drop)性能。DPDK 需要預(yù)留一個 CPU 運行控制任務(wù),因此只剩下 5 個 CPU 做包處理。
上圖是性能與 CPU 數(shù)量的關(guān)系。
XDP 基準性能是?24Mpps/core,DPDK 是?43.5Mpps/core。
二者在分別達到各自的峰值之前,PPS 都是隨 CPU 數(shù)量線性增長的。
最終全局性能受限于 PCI 總線,啟用 PCI descriptor compression(在 CPU cycles 和 PCI 總線帶寬之間取舍)之后,能達到 115Mpps。
再看圖中 Linux 網(wǎng)絡(luò)棧在兩種配置下的性能:
通過?iptables 的 raw table?丟棄流量,這是?Linux 網(wǎng)絡(luò)棧中最早能丟棄包的地方;
通過?conntrack(連接跟蹤)模塊,這個模塊的開銷非常大,但在很多 Linux 發(fā)行版中都是默認開啟的。
conntrack 模式達到了 1.8Mpps/core,raw 模式是 4.8Mpps/core ;這兩種模式均未達到硬件瓶頸。?最終的性能,XDP 比常規(guī)網(wǎng)絡(luò)棧的最快方式快了 5 倍。
Linux raw mode test 中,我們還測量了 XDP 程序不丟棄包,而是更新包數(shù)統(tǒng)計然后將包 送到內(nèi)核網(wǎng)絡(luò)棧的場景。?這種情況下,XDP 單核的處理性能會下降到 4.5Mpps/core,有 13.3ns 處理延遲。?圖中并未給出這個測試結(jié)果,因為這個開銷太小了。
4.2 CPU Usage
Fig 4. 直接棄包(packet drop)場景下的 CPU 利用率。
用系統(tǒng)提供的?mpstat?命令測量 CPU 利用率。結(jié)果如圖 4 。
DPDK 是 busy poll 模式,因此 CPU 永遠是 100%。
XDP 和 Linux 內(nèi)核網(wǎng)絡(luò)棧都是隨流量平滑增長:前面一小段是非線性的,后面基本是線性的。
前面非線性主要是硬中斷帶來的固定開銷,在流量很小時這一部分占比較大。
4.3 包轉(zhuǎn)發(fā)性能
這個測試中,轉(zhuǎn)發(fā)應(yīng)用執(zhí)行非常簡單的 MAC 地址重寫:直接交換源和目的 MAC 地址,然后轉(zhuǎn)發(fā)。?這是轉(zhuǎn)發(fā)場景下最精簡的步驟了,因此結(jié)果代表了所有真實轉(zhuǎn)發(fā)應(yīng)用的性能上限。
圖中包括了同網(wǎng)卡轉(zhuǎn)發(fā)和不同網(wǎng)卡轉(zhuǎn)發(fā)(XDP?程序返回碼不同)的結(jié)果。
DPDK 示例程序只支持通過另一個網(wǎng)卡轉(zhuǎn)發(fā),因此這里只列出了這種情況下的性能。
Linux 網(wǎng)絡(luò)棧不支持這種極簡轉(zhuǎn)發(fā)模式(minimal forwarding mode),需要設(shè)置完整的 橋接或路由查找(bridging or routing lookup)才能轉(zhuǎn)發(fā)包;路由查找是非常耗時的 ,由于其他幾種應(yīng)用并沒有這一步,因此結(jié)果直接對比是沒意義的。因此這里略去了 Linux 網(wǎng)絡(luò)棧的結(jié)果。
轉(zhuǎn)發(fā)吞吐(pps)
Fig 5. 轉(zhuǎn)發(fā)性能。在同一網(wǎng)卡接口上收發(fā)會占用同一 PCI port 的帶寬, 這意味著在 70Mpps XDP same-nic 組就已經(jīng)達到了 PCI 總線的瓶頸
如圖 5 所示,性能隨 CPU 數(shù)量線性擴展,直到達到全局性能瓶頸。XDP 在同網(wǎng)卡轉(zhuǎn)發(fā)的性能遠高于 DPDK 異網(wǎng)卡性能,原因是內(nèi)存處理方式不同:
packet buffer 是設(shè)備驅(qū)動分配的,與接收接口(receiving interface)相關(guān)聯(lián)。
因此,異網(wǎng)卡場景下,當(dāng)包轉(zhuǎn)發(fā)到另一個接口時,memory buffer 需要還給與之關(guān)聯(lián)的接口。
轉(zhuǎn)發(fā)延遲
表 1. 轉(zhuǎn)發(fā)延遲。機器網(wǎng)卡的兩個接口直連,在轉(zhuǎn)發(fā)速率分別為 100pps 和 1Mpps 的條件下,持續(xù) 50s 測量端到端延遲
高 pps 場景下,XDP 的延遲已經(jīng)接近 DPDK。但在低 pps 場景下,XDP 延遲比 DPDK 大的多,原因是?XDP 是基于中斷的,中斷處理時間( interrupt processing time)此時占大頭;而 DPDK 是輪詢模式,延遲相對比較固定。
4.4 討論:XDP 性能與 DPDK 還有差距的原因
XDP 未做底層代碼優(yōu)化
上一節(jié)已經(jīng)看到,XDP 相比于常規(guī) Linux 網(wǎng)絡(luò)棧性能有了顯著提升。但對于大部分 XDP 場景來說,性能還是與 DPDK 有差距。我們認為,這是主要是因為 DPDK 做了相當(dāng)多的底層 代碼優(yōu)化。舉個例子來解釋,考慮 packet drop 例子:
XDP 24Mpps/core,對應(yīng)?41.6ns/packet
DPDK 43.5Mpps,對應(yīng)?22.9ns/packet
多出來的 18.7ns 在我們的 3.6GHz 機器上對應(yīng) 67 個時鐘周期。因此,很顯然?每個很小的優(yōu)化在這里都會產(chǎn)生很大的影響。例如,我們測量出在測試 機器上,每次函數(shù)調(diào)用需要 1.3ns。mlx5 驅(qū)動處理每個包都有 10 次 函數(shù)調(diào)用,總計就是 13ns。
通用目的操作系統(tǒng),首要目標:更好的擴展和配置,而非極致性能
另外,在 Linux 這樣的通用目的操作系統(tǒng)中,某些開銷是不可避免的, 因為設(shè)備驅(qū)動或子系統(tǒng)的組織方式是為了實現(xiàn)更好的擴展和配置,而非極致性能。
但是,我們認為有些優(yōu)化還是有必要的。例如,我們嘗試將內(nèi)核中與測試網(wǎng)卡無關(guān)的 DMA 函數(shù)調(diào)用刪掉, 這樣將前面提到的 10 個函數(shù)調(diào)用降低到了 6 個,測試結(jié)果顯示這將單核性能提升到了 29Mpps/core。?依此推測的話,將另外 6 個函數(shù)調(diào)用也優(yōu)化掉,能將 XDP 的性能提升到?37.6Mpps。?實際上我們不可能將 6 個全部去掉,但去掉其中幾個,再加上一些其他優(yōu)化,我 們相信 XDP 和 DPDK 的性能差距將越來越小。
其他驅(qū)動的測試結(jié)果也是類似的,例如 i40e driver for 40 Gbps Intel cards。
基于以上討論,我們相信未來 XDP 與 DPDK 的性能差距將越來越小。
另一方面,考慮到 XDP 在靈活性和與內(nèi)核集成方面的優(yōu)勢, XDP 已經(jīng)是很多實際場景中的非常有競爭力的方式。下文給出幾個例子。
5 真實場景使用案例
本節(jié)給出三個例子來具體展示 XDP 在真實世界中的應(yīng)用。?這幾個案例都是已經(jīng)真實在用的,但本文出于解釋目的,將使用簡化的版本。?同時也建議讀者參考 [38],后者是獨立的文章,介紹使用 XDP 解決實際工作中網(wǎng)絡(luò)服務(wù)所面臨的一些挑戰(zhàn)。
本節(jié)目的是展示真實 XDP 方案的可行性,因此不會將重點放在與業(yè)界最新的實現(xiàn)做詳盡性能對比上。?我們會拿常規(guī)的 Linux 內(nèi)核網(wǎng)絡(luò)棧的性能作為 baseline,來對比 XDP 應(yīng)用的性能。
5.1 案例一:軟件路由(software routing)
內(nèi)核數(shù)據(jù)平面 & 控制平面(BIRD/FRR)
Linux 內(nèi)核實現(xiàn)了一個功能完整的路由表,作為數(shù)據(jù)平面,支持
policy routing
source-specific routing
multipath load balancing, and more.
對于控制平面,Bird [10] 或 FRR [17] 這樣的路由守護進程( routing daemons)實現(xiàn)了多種路由控制平面協(xié)議。Linux 提供的這套生態(tài)系統(tǒng)功能如此豐富 ,因此再在另一個包處理框架中重新實現(xiàn)一套類似的路由棧代價將非常高, 更實際的方式是對 Linux 內(nèi)核的數(shù)據(jù)平面進行優(yōu)化。
XDP:直接查詢內(nèi)核路由表并轉(zhuǎn)發(fā)
XDP 非常適合做這件事情,尤其是它提供了一個 helper 函數(shù),能從 XDP 程序中直接查詢內(nèi)核路由表。
如果查詢成功,會返回?egress interface 和下一跳 MAC 地址, XDP 程序利用這些信息足夠?qū)⒓崔D(zhuǎn)發(fā)出去。
如果下一跳 MAC 還是未知的(因為之前還沒進行過 neighbour lookup),XDP 程序就 能將包傳給內(nèi)核網(wǎng)絡(luò)棧,后者會解析 neighbor 地址,這樣隨后的包 就能直接被 XDP 程序轉(zhuǎn)發(fā)了。
測試:XDP routing + 全球 BGP 路由表
為展示 XDP 路由的性能,我們用 Linux 內(nèi)核代碼中的 XDP routing 例子 [1],與常規(guī) Linux 內(nèi)核網(wǎng)絡(luò)棧的性能做對比。?兩組測試:
路由表中只有一條路由;
路由表中有從 routeviews.org 中 dump 而來的全球 BGP 路由表(global BGP routing table)。?包含 752,138 條路由。隨機生成 4000 個目的 IP 地址,以確保能充分利用到這種路由表。
如果目的 IP 地址少于 4000 個,實際用到的路由表部分會較小,能夠保存在 CPU 緩存中,使得結(jié)果不準確。?增大 IP 數(shù)量至 4000 個以上,不會對轉(zhuǎn)發(fā)性能造成影響,但可以避免緩存導(dǎo)致的結(jié)果不準問題。
對于兩組測試,下一跳 MAC 地址都是與我們的發(fā)送網(wǎng)卡直接相關(guān)的接口的地址。
性能:2.5x
Fig 6. 軟件路由的性能。由于性能隨核數(shù)線性增加,這里只給出單核的結(jié)果。
測試結(jié)果如上圖所示。
full table lookup 性能提升了 2.5 倍;
smaller routing table 組,提升了 3 倍。
這說明,XDP 路由程序 + 單核 + 10Gbps 網(wǎng)卡?的軟硬件配置,就能?處理整張全球 BGP 路由表(保守估計每個包平均 300 字節(jié))。
5.2 案例二:Inline DoS Mitigation
DoS 攻擊還是像瘟疫一樣糾纏著互聯(lián)網(wǎng),現(xiàn)在通常的方式是:通過已經(jīng)入侵的大量設(shè)備發(fā)起分布式(DDoS)攻擊。
有了XDP 之后,我們能直接在應(yīng)用服務(wù)器(application servers)上?部署包過濾程序來防御此類攻擊(inline DoS mitigation),?無需修改應(yīng)用代碼。如果應(yīng)用是部署在虛擬機里,那 XDP 程序還可以 部署在宿主機(hypervisor)上,這樣單個程序就能保護機器上所有的虛擬機。
模擬 Cloudflare 防御架構(gòu)
為展示工作原理,我們用 XDP 作為過濾機制,模擬 Cloudflare 的 DDoS 防御架構(gòu)?[6]。?他們的 Gatebot architecture ,首先在各 PoP 點機器上采樣,然后統(tǒng)一收起來做分析, 根據(jù)分析結(jié)果生成防御規(guī)則。
防御規(guī)則的形式是對包數(shù)據(jù)(payload)進行一系列簡單檢查, 能直接編譯成 eBPF 代碼然后分發(fā)到 PoP 點的所有服務(wù)器上。這里說的代碼是 XDP 程序 ,它會將匹配到規(guī)則的所有流量丟棄,同時將統(tǒng)計信息更新到 BPF map。
程序邏輯
為驗證這種方案的性能,我們編寫一個 XDP 程序,它
解析包頭,執(zhí)行一些簡單驗證。對每個包:執(zhí)行四次讀取操作,以解析外層包頭。
將符合攻擊特性的流量丟棄。具體:丟棄 UDP + 特定端口的流量。
將其他流量通過 CPU redirect 方式重定向給另一個 CPU?做進一步處理;
性能
我們用 netperf 做性能壓測 [26]。
用 netperf TCP round-trip benchmark,單個 TCP 連接來回小的 request/reply,統(tǒng)計 transactions/second。
模擬的是交互式應(yīng)用,例如小的遠程過程調(diào)用(RPC)。
實驗在單核上進行,模擬多個流量(正常流量+攻擊流量)競爭同一物理資源的場景。
在?beseline 35K 業(yè)務(wù) TPS(transactions per second)基礎(chǔ)上,打少量 UDP 流量作為攻擊流量。逐漸加大攻擊流量,觀察 TPS 的變化。
Fig 7. DDoS 性能。業(yè)務(wù)吞吐(TPS)隨攻擊流量的變化。
結(jié)果如上圖所示,
沒有 XDP 的一組,性能急劇下降:攻擊流量在 3Mpps 時性能減半,3.5Mpps 時基本跌零;
有 XDP 程序的一組,攻擊流量達到 19.5Mpps 之前,業(yè)務(wù)吞吐保持在 28.5K TPS 以上,過了這個臨界點性能才開始急劇下降。
以上結(jié)果表明,XDP 防御 DDoS 攻擊在實際中是完全可行的,單核就能輕松處理 10Gbps 的、都是最小包(minimum-packet)的 DoS 流量。?這種 DDoS 防御的部署更加靈活,無需硬件或應(yīng)用做任何改動。
5.3 案例三:負載均衡(load balancing)
Facebook Katran
負載均衡的場景,我們用 Facebook 開源的 Katran 作為例子 [15]。Katran 的工作原理是對外通告服務(wù)的 IP,這樣目標是這個 IP 的流量就會被路由到 XDP 實現(xiàn)的負載均衡器。
負載均衡器對包頭(source packet header)進行哈希,以此選擇目標應(yīng)用服務(wù)器。
然后將對包進行封裝(encap),發(fā)送給應(yīng)用服務(wù)器;
應(yīng)用服務(wù)器解封裝(decap),處理請求,然后直接將回包發(fā)給客戶端(DSR 模式)。
在這個過程中,XDP 程序負責(zé)哈希、封裝以及將包從接收網(wǎng)卡再發(fā)出去的任務(wù)。?配置信息存儲在 BPF map 中,整個封裝邏輯是完全在 eBPF 中實現(xiàn)的。
性能
為測試性能,我們給 Katran XDP 程序配置幾個固定的目標機器。?對照組是 IPVS,它是 Linux 內(nèi)核的一部分。性能如表 2 所示,隨 CPU 數(shù)量線性增長, XDP 比 IPVS 性能高 4.3 倍。
表 2. 負載均衡器性能(Mpps)
配置:1 VIP/core, 100 DstIPs/VIP.
6 XDP 的未來方向
XDP已經(jīng)能用于解決真實問題,但作為Linux內(nèi)核的一部分,XDP 還在快速開發(fā)過程中。
6.1 eBPF 程序的限制
前面提到,加載到 eBPF 虛擬機的程序必須保證其安全性(不會破壞內(nèi)核),因此對 eBPF 程序作了一下限制,歸結(jié)為兩方面:
確保程序會終止:在實現(xiàn)上是通過禁止循環(huán)和限制程序的最大指令數(shù)(max size of the program);
確保內(nèi)存訪問的安全:通過 3.4 小結(jié)介紹的寄存器狀態(tài)跟蹤(register state tracking)來實現(xiàn)。
校驗邏輯偏保守
由于校驗器的首要職責(zé)是保證內(nèi)核的安全,因此其校驗邏輯比較保守, 凡是它不能證明為安全的,一律都拒絕。有時這會導(dǎo)致假陰性(false negatives), 即某些實際上是安全的程序被拒絕加載;這方面在持續(xù)改進。
校驗器的錯誤提示也已經(jīng)更加友好,以幫助開發(fā)者更快定位問題。
近期已經(jīng)支持了 BPF 函數(shù)調(diào)用(function calls)。
正在計劃支持有限循環(huán)(bounded loops)。
正在提升校驗器效率,以便處理更大的 BPF 程序。
缺少標準庫
相比于用戶空間 C 程序,eBPF 程序的另一個限制是缺少標準庫,包括?內(nèi)存分配、線程、鎖等等庫。
內(nèi)核的生命周期和執(zhí)行上下文管理(life cycle and execution context management )部分地彌補了這一不足,(例如,加載的 XDP 程序會為每個收到的包執(zhí)行),
內(nèi)核提供的 helper 函數(shù)也部分地彌補了一不足。
一個網(wǎng)卡接口只能 attach 一個 XDP 程序
這個限制其實也是可以繞過的:將 XDP 程序組織成程序數(shù)組,通過尾 調(diào)用,根據(jù)包上下文在程序之間跳轉(zhuǎn),或者是將幾個程序做 chaining。
6.2 用戶體驗和調(diào)試
XDP 程序運行在內(nèi)核,因此常規(guī)的用戶空間 debug 工具是用不了的,但內(nèi)核自帶的 debug 和 introspection 功能是可以用在 XDP (及其他 eBPF 程序)上的。?包括:
tracepoints and kprobes [13]
performance counters that are part of the perf subsystem [42]
但不熟悉內(nèi)核生態(tài)系統(tǒng)的開發(fā)者可能會對這些工具感到非常陌生,難以使用。因此,也出 現(xiàn)了一些更方便普通開發(fā)者的工具,包括 BCC [50]、bpftool [8]、libbpf 函數(shù)庫 [30] 等等。
6.3 驅(qū)動支持
設(shè)備要支持 XDP,需要實現(xiàn)內(nèi)核核心網(wǎng)絡(luò)棧暴露出的一個 API。?寫作本文時 Linux 4.18 已經(jīng)有 12 種驅(qū)動支持 XDP,包括了大部分高速網(wǎng)卡。?最新列表見 [2]。
隨著 XDP 系統(tǒng)的不斷成熟,核心代碼逐漸上移到內(nèi)核中,驅(qū)動需要維護的代碼越 來越少。例如,redirection action 支持新的 target 時,無需驅(qū)動做任何改動。
最后,對于那些不支持 XDP 的驅(qū)動,內(nèi)核提供了?Generic XDP?feature [39],這是軟件實現(xiàn)的 XDP,性能會低一些, 在實現(xiàn)上就是將 XDP 的執(zhí)行上移到了核心網(wǎng)絡(luò)棧(core networking stack)。
XDP 在內(nèi)核收包函數(shù)?receive_skb() 之前,
Generic XDP?在?receive_skb() 之后,
更多關(guān)于 Generic XDP,可參考參考:容器網(wǎng)絡(luò)|深入理解Cilium
6.4 性能提升
XDP 和 DPDK 之間還有一些性能差距,一些改進工作正在進行中:
驅(qū)動代碼 micro-optimisations
刪除核心 XDP 代碼中的非必要操作
通過批處理平攤處理開銷
6.5 QoS 和 Rate Transitions
當(dāng)前,XDP 還沒有任何 QoS 機制。?尤其是,如果對端已經(jīng)過載(例如兩端的網(wǎng)絡(luò)速度或特性不匹配),XDP 程序是收不到任何背壓(back-pressure)的,
雖然 XDP 中缺少 QoS,但 Linux 內(nèi)核網(wǎng)絡(luò)棧中卻有很多業(yè)界最佳的 Active Queue Management (AQM) 特性和 packet scheduling algorithms [23]。?這些特性中,部分并不適用于 XDP,但我們相信能夠 以一種對包處理應(yīng)用完全透明的方式,選擇其中部分集成到 XDP。?我們計劃對這一方向進行更深入研究。
6.6 加速傳輸層協(xié)議
我們已經(jīng)證明 XDP 能在保留操作系統(tǒng)原有功能的前提下,集成到操作系統(tǒng)中,實現(xiàn)高速包數(shù)據(jù)。
目前的 XDP 還是用于無狀態(tài)包處理(stateless packet processing) ,如果將這個模型擴展到有狀態(tài)傳輸層協(xié)議(stateful transport protocols),例如 TCP,它能給依賴可靠/有狀態(tài)傳輸?shù)膽?yīng)用提供類似的性能提升。
實際上,已經(jīng)有一些研究證明,相比于操作系統(tǒng)的協(xié)議棧,accelerated transport protocols 能顯著提升性能[5, 25, 35, 52]。其中的一個解決方案 [52] 表明,在保留內(nèi) 核 TCP 協(xié)議棧的的前提下,原始包處理性能(raw packet processing)存在巨大的提升 空間。
XDP 非常適用于這種場景,目前也已經(jīng)有一些關(guān)于如何實現(xiàn)的初步討論 [21], 雖然離實際使用還很遠,但仍然是一個令人振奮的、擴展 XDP 系統(tǒng) scope 的方向。
6.7 內(nèi)核-用戶空間零拷貝(zero-copy to userspace)
3.1 小節(jié)提到,XDP 程序能將數(shù)據(jù)包重定向到用戶空間應(yīng)用(userspace application)打 開的特殊類型 socket。這可以用于加速客戶端和服務(wù)端在同一臺機器?的網(wǎng)絡(luò)密集型應(yīng)用(network-heavy applications running on the local machine)。
更多信息可參考:?(譯) 利用 ebpf sockmap/redirection 提升 socket 性能(2020)。?這里使用的是 BPF 而非 XDP,但核心原理是一樣的,只是程序執(zhí)行的位置(hook)不同。?譯注。
但在目前的實現(xiàn)中,這種方式在底層仍然需要拷貝包數(shù)據(jù),因此性能會打折扣。
目前已經(jīng)有工作在進行,通過 AF_XDP 實現(xiàn)真正的數(shù)據(jù)零拷貝。但這項工作需要?對網(wǎng)絡(luò)設(shè)備的內(nèi)存處理過程有一些限制,因此需要設(shè)備驅(qū)動的顯式支持。?第一個支持這個功能的 patch 已經(jīng)合并到?4.19?內(nèi)核,更多驅(qū)動的支持 正在添加中。初步的性能測試結(jié)果還是很樂觀的,顯示能達到 20Mpps/core 的內(nèi)核到用戶 空間傳遞(transfer)速度。
6.8 XDP 作為基礎(chǔ)構(gòu)建模塊(XDP as a building block)
正如 DPDK 用于高層包處理框架的底層構(gòu)建模塊(例如 [31]),XDP 有望成為高層應(yīng)用的運行時環(huán)境 (runtime environment for higher-level applications)。
實際上,我們看到一些基于 XDP 的應(yīng)用和框架已經(jīng)出現(xiàn)了。包括
Cilium security middle-ware [3]
Suricata network monitor [4]
Open vSwitch [49]
P4-to-XDP compiler project [51]
甚至還有人嘗試將 XDP 作為 DPDK 的一種底層驅(qū)動 [53]。
7 總結(jié)
本文描述了 XDP,一個安全、快速、可編程、集成到操作系統(tǒng)內(nèi)核的包處理框架。?測試結(jié)果顯示,XDP 能提供 24Mpps/core 的高處理性能,這一數(shù)字雖然與基于 kernel bypass 的 DPDK 仍有差距,但提供了其他一些非常有競爭力的優(yōu)勢:
兼容內(nèi)核安全和管理框架(kernel bypass 方式在 bypass 內(nèi)核網(wǎng)絡(luò)棧的同時,也將安全和設(shè)備管理等這些極其重要的基礎(chǔ)設(shè)施 bypass 了);
兼容內(nèi)核網(wǎng)絡(luò)棧,可選擇性利用內(nèi)核已有的基礎(chǔ)設(shè)施和功能;
提供與內(nèi)核 API 一樣穩(wěn)定的編程接口;
對應(yīng)用完全透明;
更新、替換程序的過程不會引起服務(wù)中斷;
無需專門硬件,無需獨占 CPU 等資源。
相比于 kernel bypass 這種非此即彼、完全繞開內(nèi)核的方式,我們相信 XDP 有更廣闊的的應(yīng)用前景。Facebook、Cloudflare 等公司實際落地的 XDP 應(yīng)用,更加增強了我們的這種信心。
最后,XDP系統(tǒng)還在快速發(fā)展,前面也列出了一些正在未來可能會做的開發(fā)/優(yōu)化工作。
致謝
XDP has been developed by the Linux networking community for a number of years, and the authors would like to thank everyone who has been involved. In particular,
Alexei Starovoitov has been instrumental in the development of the eBPF VM and verifier;
Jakub Kicinski has been a driving force behind XDP hardware offloading and the bpftool utility;
Bj?rn T?pel and Magnus Karlsson have been leading the AF_XDP and userspace zero-copy efforts.
We also wish to extend our thanks to the anonymous reviewers, and to our shepherd Srinivas Narayana, for their helpful comments.
參考文獻
https://arthurchiao.art/blog/xdp-paper-acm-2018-zh/
- END -
看完一鍵三連在看,轉(zhuǎn)發(fā),點贊
是對文章最大的贊賞,極客重生感謝你
推薦閱讀
深入理解Linux內(nèi)核之內(nèi)存尋址
2022新年重磅技術(shù)分享|深入理解Linux操作系統(tǒng)
一些優(yōu)秀的后端開源項目!
你好,這里是極客重生,我是阿榮,大家都叫我榮哥,從華為->外企->到互聯(lián)網(wǎng)大廠,目前是大廠資深工程師,多次獲得五星員工,多年職場經(jīng)驗,技術(shù)扎實,專業(yè)后端開發(fā)和后臺架構(gòu)設(shè)計,熱愛底層技術(shù),豐富的實戰(zhàn)經(jīng)驗,分享技術(shù)的本質(zhì)原理,希望幫助更多人蛻變重生,拿BAT大廠offer,培養(yǎng)高級工程師能力,成為技術(shù)專家,實現(xiàn)高薪夢想,期待你的關(guān)注!點擊藍字查看我的成長之路。
校招/社招/簡歷/面試技巧/大廠技術(shù)棧分析/后端開發(fā)進階/優(yōu)秀開源項目/直播分享/技術(shù)視野/實戰(zhàn)高手等,?極客星球希望成為最有技術(shù)價值星球,盡最大努力為星球的同學(xué)提供技術(shù)和成長幫助!詳情查看->極客星球
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 求點贊,在看,分享三連
的昂貴性能開銷。
但是,操作系統(tǒng)被旁路(繞過)之后,它的應(yīng)用隔離(application isolation) 和安全機制(security mechanisms)就都失效了;一起失效的還有各種經(jīng)過已經(jīng) 充分測試的配置、部署和管理工具。
為解決這個問題,我們提出一種新的可編程包處理方式:eXpress Data Path (XDP)。
XDP 提供了一個仍然基于操作系統(tǒng)內(nèi)核的安全執(zhí)行環(huán)境,在設(shè)備驅(qū)動上下文?(device driver context)中執(zhí)行,可用于定制各種包處理應(yīng)用。
XDP 是主線內(nèi)核(mainline Linux kernel)的一部分,與現(xiàn)有的內(nèi)核 網(wǎng)絡(luò)棧(kernel’s networking stack)完全兼容,二者協(xié)同工作。
XDP 應(yīng)用(application)通過 C 等高層語言編寫,然后編譯成特定字節(jié)碼;出于安 全考慮,內(nèi)核會首先對這些字節(jié)碼執(zhí)行靜態(tài)分析,然后再將它們翻譯成?處理器原生指令(native instructions)。
測試結(jié)果顯示,XDP 能達到?24Mpps/core?的處理性能。
為展示 XDP 靈活的編程模型,本文還將給出三個程序示例,
layer-3 routing(三層路由轉(zhuǎn)發(fā))
inline DDoS protection(DDoS 防護)
layer-4 load balancing(四層負載均衡)
1 引言
軟件實現(xiàn)高性能包處理的場景,對每個包的處理耗時有著極高的要求。通用目的操作系統(tǒng)中 的網(wǎng)絡(luò)棧更多是針對靈活性的優(yōu)化,這意味著它們花在每個包上 的指令太多了,不適合網(wǎng)絡(luò)高吞吐的場景。
因此,隨后出現(xiàn)了一些專門用于包處理的軟件開發(fā)工具,例如 Data Plane Development Kit (DPDK) [16]。這些工具一般都會完全繞過內(nèi)核,將網(wǎng)絡(luò)硬件直接交 給用戶態(tài)的網(wǎng)絡(luò)應(yīng)用,并需要獨占一個或多個 CPU。
1.1 現(xiàn)有方案(kernel bypass)存在的問題
內(nèi)核旁路方式可以顯著提升性能,但缺點也很明顯:
很難與現(xiàn)有系統(tǒng)集成;
上層應(yīng)用必須要將內(nèi)核中已經(jīng)非常成熟的模塊在用戶態(tài)重新實現(xiàn)一遍,例如路由表、高層協(xié)議棧等;
最壞的情況下,這種包處理應(yīng)用只能工作在一個完全隔絕的環(huán)境,因為內(nèi)核提供的常見工具和部署方式在這種情況下都不可用了。
導(dǎo)致系統(tǒng)越來越復(fù)雜,而且破壞了操作系統(tǒng)內(nèi)核在把控的安全邊界。?在基礎(chǔ)設(shè)施逐漸遷移到 Kubernetes/Docker 等容器環(huán)境的背景下,這一點顯得尤其嚴重, 因為在這種場景下,內(nèi)核擔(dān)負著資源抽象和隔離的重任。
1.2 新方案:給內(nèi)核網(wǎng)絡(luò)棧添加可編程能力
對此,本文提供了另一種解決方案:給內(nèi)核網(wǎng)絡(luò)棧添加可編程能力。這使得我們能在?兼容各種現(xiàn)有系統(tǒng)、復(fù)用已有網(wǎng)絡(luò)基礎(chǔ)設(shè)施的前提下,仍然實現(xiàn)高速包處理。?這個框架稱為XDP,
XDP定義了一個受限的執(zhí)行環(huán)境(a limited execution environment),運行在一個?eBPF 指令虛擬機中。eBPF 是 BSD Packet Filter (BPF) [37] 的擴展。
XDP 程序運行在內(nèi)核上下文中,此時內(nèi)核自身都還沒有接觸到包數(shù)據(jù)( before the kernel itself touches the packet data),這使得我們能在網(wǎng)卡收到包后?最早能處理包的位置,做一些自定義數(shù)據(jù)包處理(包括重定向)。
內(nèi)核在加載(load)時執(zhí)行靜態(tài)校驗,以確保用戶提供的 XDP 程序的安全。
之后,程序會被動態(tài)編譯成原生機器指令(native machine instructions),以獲得高性能。
XDP已經(jīng)在過去的幾個內(nèi)核 release 中逐漸合并到內(nèi)核,但在本文之前,還沒有關(guān)于 XDP 系統(tǒng)的 完整架構(gòu)介紹。本文將對 XDP 做一個高層介紹。
1.3 新方案(XDP)的優(yōu)點
我們的測試結(jié)果顯示 XDP 能取得?24Mpps/core?的處理性能,這雖然與 DPDK 還有差距, 但相比于后者這種 kernel bypass 的方式,XDP 有非常多的優(yōu)勢。
本文是 2018 年的測試結(jié)果,更新的一些性能(及場景)對比可參考?(譯) 為容器時代設(shè)計的高級 eBPF 內(nèi)核特性(FOSDEM, 2021)。?譯注。
具體地,XDP:
與內(nèi)核網(wǎng)絡(luò)棧協(xié)同工作,將硬件的控制權(quán)完全留在內(nèi)核范圍內(nèi)。帶來的好處:
保持了內(nèi)核的安全邊界
無需對網(wǎng)絡(luò)配置或管理工具做任何修改
無需任何特殊硬件特性,任何有 Linux 驅(qū)動的網(wǎng)卡都可以支持, 現(xiàn)有的驅(qū)動只需做一些修改,就能支持 XDP hooks。
可以選擇性地復(fù)用內(nèi)核網(wǎng)絡(luò)棧中的現(xiàn)有功能,例如路由表或 TCP/IP 協(xié)議棧,在保持配置接口不變的前提下,加速關(guān)鍵性能路徑(critical performance paths)。
保證 eBPF 指令集和 XDP 相關(guān)的編程接口(API)的穩(wěn)定性。
與常規(guī) socket 層交互時,沒有從用戶態(tài)將包重新注入內(nèi)核的昂貴開銷。
對應(yīng)用透明。這創(chuàng)造了一些新的部署場景/方式,例如直接在應(yīng)用所 在的服務(wù)器上部署 DoS 防御(而非中心式/網(wǎng)關(guān)式 DoS 防御)。
服務(wù)不中斷的前提下動態(tài)重新編程(dynamically re-program), 這意味著可以按需加入或移除功能,而不會引起任何流量中斷,也能動態(tài)響應(yīng)系統(tǒng)其他部分的的變化。
無需預(yù)留專門的 CPU 做包處理,這意味著 CPU 功耗與流量高低直接相關(guān),更節(jié)能。
1.4 本文組織結(jié)構(gòu)
接下來的內(nèi)容介紹 XDP 的設(shè)計,并做一些性能分析。結(jié)構(gòu)組織如下:
Section 2?介紹相關(guān)工作;
Section 3?介紹 XDP 系統(tǒng)的設(shè)計;
Section 4?做一些性能分析;
Section 5?提供了幾個真實 XDP 場景的程序例子;
Section 6?討論 XDP 的未來發(fā)展方向;
Section 7?總結(jié)。
2 相關(guān)工作
XDP 當(dāng)然不是第一個支持可編程包處理的系統(tǒng) —— 這一領(lǐng)域在過去幾年發(fā)展勢頭良好, 并且趨勢還在持續(xù)。業(yè)內(nèi)已經(jīng)有了幾種可編程包處理框架,以及基于這些框架的新型應(yīng)用,包括:
單一功能的應(yīng)用,如 switching [47], routing [19], named-based forwarding [28], classification [48], caching [33] or traffic generation [14]。
更加通用、且高度可定制的包處理解決方案,能夠處理從多種源收來的數(shù)據(jù)包 [12, 20, 31, 34, 40, 44]。
要基于通用(Common Off The Shelf,COTS)硬件實現(xiàn)高性能包處理,就必須解決?網(wǎng)卡(NIC)和包處理程序之間的所有瓶頸。由于性能瓶頸主要來源于內(nèi)核和用戶態(tài)應(yīng)用之 間的接口(系統(tǒng)調(diào)用開銷非常大,另外,內(nèi)核功能豐富,但也非常復(fù)雜), 低層(low-level)框架必須通過這樣或那樣的方式來降低這些開銷。
現(xiàn)有的一些框架通過幾種不同的方式實現(xiàn)了高性能,XDP 構(gòu)建在其中一些技術(shù)之上。?接下來對 XDP 和它們的異同做一些比較分析。
2.1 用戶態(tài)輪詢 vs. XDP
DPDK?[16] 可能是使用最廣泛的高性能包處理框架。它最初只支持 Intel 網(wǎng)卡,后來逐 步擴展到其他廠商的網(wǎng)卡。DPDK 也稱作內(nèi)核旁路框架(kernel bypass framework), 因為它將網(wǎng)絡(luò)硬件的控制權(quán)從內(nèi)核轉(zhuǎn)移到了用戶態(tài)的網(wǎng)絡(luò)應(yīng)用,完全避免了內(nèi)核-用戶態(tài) 之間的切換開銷。
與DPDK類似的還有PF_RING ZC module [45] 和 hardware-specific Solarflare OpenOnload [24]。
在現(xiàn)有的所有框架中,內(nèi)核旁路方式性能是最高的?[18];但如引言中指 出,這種方式在管理、維護和安全方面都存在不足。
XDP 采用了一種與內(nèi)核旁路截然相反的方式:相比于將網(wǎng)絡(luò)硬件的控制權(quán)上移到用戶空間, XDP 將性能攸關(guān)的包處理操作直接放在內(nèi)核中,在操作系統(tǒng)的網(wǎng)絡(luò)棧之前執(zhí)行。
這種方式同樣避免了內(nèi)核-用戶態(tài)切換開銷(所有操作都在內(nèi)核),
但仍然由內(nèi)核來管理硬件,因此保留了操作系統(tǒng)提供的管理接口和安全防護能力。
這里的主要創(chuàng)新是:使用了一個虛擬執(zhí)行環(huán)境,它能對加載的 程序進行校驗,確保它們不會對內(nèi)核造成破壞。
2.2 內(nèi)核模塊 vs. XDP
在 XDP 之前,以內(nèi)核模塊(kernel module)方式實現(xiàn)包處理功能代價非常高, 因為程序執(zhí)行出錯時可能會導(dǎo)致整個系統(tǒng)崩潰,而且內(nèi)核的內(nèi)部 API 也會隨著時間發(fā)生變化。?因此也就不難理解為什么只有很少的系統(tǒng)采用了這種方式。其中做的比較好包括
虛擬交換機?Open vSwitch [44]
Click [40]
虛擬機路由器?Contrail [41]
這幾個系統(tǒng)都支持靈活的配置,適用于多種場景,取得比較小的平攤代價。
XDP通過:
提供一個安全的執(zhí)行環(huán)境,以及內(nèi)核社區(qū)支持,提供與那些暴露到用戶空間一樣穩(wěn)定的內(nèi)核 API
極大地降低了那些將處理過程下沉到內(nèi)核的應(yīng)用(applications of moving processing into the kernel)的成本。
此外,XDP 程序也能夠完全繞過內(nèi)核網(wǎng)絡(luò)棧(completely bypass), 與在內(nèi)核網(wǎng)絡(luò)棧中做 hook 的傳統(tǒng)內(nèi)核模塊相比,性能也更高。
XDP 除了能將處理過程下沉到內(nèi)核以獲得最高性能之外,還支持在程序中執(zhí)行重定向?(redirection)操作,完全繞過內(nèi)核網(wǎng)絡(luò)棧,將包送到特殊類型的用戶空間 socket;?甚至能工作在 zero-copy 模式,進一步降低開銷。
這種模式與 Netmap [46] 和 PF_RING?[11] 方式類似,但后者是在沒有完全繞過內(nèi) 核的情況下,通過降低從網(wǎng)絡(luò)設(shè)備到用戶態(tài)應(yīng)用(network device to userspace application)之間的傳輸開銷,實現(xiàn)高性能包處理。
內(nèi)核模塊方式的另一個例子是 Packet I/O engine,這是 PacketShader [19] 的組成部分, 后者專用于 Arrakis [43] and ClickOS [36] 之類的特殊目的操作系統(tǒng)。
2.3 可編程硬件 vs. XDP
可編程硬件設(shè)備也是一種實現(xiàn)高性能包處理的方式。
一個例子是?NetFPGA?[32],通過對它暴露的 API 進行編程,能夠在這種基于 FPGA 的專 用設(shè)備上運行任何包處理任務(wù)。
P4 編程語言 [7] 致力于將這種可編程能力擴展到更廣泛的包處理硬件上?(巧合的是,它還包括了一個 XDP backend [51])。
某種意義上來說,XDP 可以認為是一種?offload?方式:
性能敏感的處理邏輯下放到網(wǎng)卡驅(qū)動中,以提升性能;
其他的處理邏輯仍然走內(nèi)核網(wǎng)絡(luò)棧;
如果沒有用到內(nèi)核 helper 函數(shù),那整個 XDP 程序都可以 offload 到網(wǎng)卡(目前 Netronome smart-NICs [27] 已經(jīng)支持)。
2.4 小結(jié)
XDP 提供了一種高性能包處理方式,與已有方式相比,在性能、與現(xiàn)有系統(tǒng)的集成、靈活性 等方面取得了更好的平衡。接下來介紹 XDP 是如何取得這種平衡的。
3 XDP 設(shè)計
XDP 的設(shè)計理念:
高性能包處理
集成到操作系統(tǒng)內(nèi)核(kernel)并與之協(xié)同工作,同時
確保系統(tǒng)其它部分的安全性(safety)和完整性(integrity)
這種與內(nèi)核的深度集成顯然會給設(shè)計帶來一些限制,在 XDP 組件合并到 Linux 的過程中,我們也收到了許多來自社區(qū)的反饋,促使我們不斷調(diào)整 XDP 的設(shè)計,但 這些設(shè)計反思不在本文討論范圍之內(nèi)。
3.0 XDP 系統(tǒng)架構(gòu)
圖 1 描繪了整個 XDP 系統(tǒng),四個主要組成部分:
XDP driver hook:XDP 程序的主入口,在網(wǎng)卡收到包執(zhí)行。
eBPF virtual machine:執(zhí)行 XDP 程序的字節(jié)碼,以及對字節(jié)碼執(zhí)行 JIT 以提升性能。
BPF maps:內(nèi)核中的 key/value 存儲,作為圖中各系統(tǒng)的主要通信通道。
eBPF verifier:加載程序時對其執(zhí)行靜態(tài)驗證,以確保它們不會導(dǎo)致內(nèi)核崩潰。
Fig 1. XDP 與 Linux 網(wǎng)絡(luò)棧的集成。這里只畫了 ingress 路徑,以免圖過于復(fù)雜。
上圖是 ingress 流程。網(wǎng)卡收到包之后,在處理包數(shù)據(jù)(packet data)之前,會先執(zhí)行 main XDP hook 中的 eBPF 程序。?這段程序可以選擇:
丟棄(drop)這個包;或者
通過當(dāng)前網(wǎng)卡將包再發(fā)送(send)出去;或者
將包重定向(redirect)到其他網(wǎng)絡(luò)接口(包括虛擬機的虛擬網(wǎng)卡),或者通過 AF_XDP socket 重定向到用戶空間;或者
放行(allow)這個包,如果后面沒有其他原因?qū)е碌?drop,這個包就會進入常規(guī)的內(nèi)核網(wǎng)絡(luò)棧。
如果是這種情況,也就是放行包進入內(nèi)核網(wǎng)絡(luò)棧,那接下來在將包放到發(fā)送隊列之前(before packets are queued for transmission), 還有一個能執(zhí)行 BPF 程序的地方:TC BPF hook。
更多信息,可參考?(譯) [論文] 邁向完全可編程 tc 分類器(cls_bpf)(NetdevConf,2016)。?譯注。
此外,圖 1 中還可以看出,不同的 eBPF 程序之間、eBPF 程序和用戶空間應(yīng)用之間,都能夠通過 BPF maps 進行通信。
3.1 XDP driver hook
在設(shè)備驅(qū)動中執(zhí)行,無需上下文切換
XDP 程序在網(wǎng)絡(luò)設(shè)備驅(qū)動中執(zhí)行,網(wǎng)絡(luò)設(shè)備每收到一個包,程序就執(zhí)行一次。
相關(guān)代碼實現(xiàn)為一個內(nèi)核庫函數(shù)(library function),因此程序直接 在設(shè)備驅(qū)動中執(zhí)行,無需切換到用戶空間上下文。
在軟件最早能處理包的位置執(zhí)行,性能最優(yōu)
回到上面圖 1 可以看到:程序在網(wǎng)卡收到包之后最早能處理包的位置?執(zhí)行 —— 此時內(nèi)核還沒有為包分配?struct sk_buff?結(jié)構(gòu)體, 也沒有執(zhí)行任何解析包的操作。
XDP 程序典型執(zhí)行流
下圖是一個典型的 XDP 程序執(zhí)行流:
Fig 2. 典型 XDP 程序的執(zhí)行流。
網(wǎng)卡收到一個包時,XDP 程序依次執(zhí)行:
提取包頭中的信息(例如 IP、MAC、Port、Proto 等),
執(zhí)行到程序時,系統(tǒng)會傳遞給它一個上下文對象(context object)作為參賽?(即?struct xdp_md *ctx,后面有例子),其中包括了指向原 始包數(shù)據(jù)的指針,以及描述這個包是從哪個網(wǎng)卡的哪個接口接收上來的等元數(shù)據(jù)字段。
讀取或更新一些資源的元信息(例如更新統(tǒng)計信息);
解析包數(shù)據(jù)之后,XDP 程序可以讀取?ctx?中的包元數(shù)據(jù)(packet metadata) 字段,例如從哪個網(wǎng)卡的哪個接口收上來的(ifindex)。除此之外,ctx 對象還允許 程序訪問與包數(shù)據(jù)毗鄰的一塊特殊內(nèi)存區(qū)域(cb, control buffer), 在包穿越整個系統(tǒng)的過程中,可以將自定義的數(shù)據(jù)塞在這里。
除了 per-packet metadata,XDP 程序還可以通過 BPF map 定義和訪問自己的持久數(shù)據(jù)?,以及通過各種 helper 函數(shù)訪問內(nèi)核基礎(chǔ)設(shè)施。
BPF map 使 BPF 程序能與系統(tǒng)的其他部分之間通信;
Helpers 使 BPF 程序能利用到某些已有的內(nèi)核功能(例如路由表), 而無需穿越整個內(nèi)核網(wǎng)絡(luò)棧。
如果有需要,對這個包進行?rewrite header?操作,
程序能修改包數(shù)據(jù)的任何部分,包括添加或刪除包頭。這使得 XDP 程序能執(zhí)行封裝/接封裝操作,以及重寫(rewrite)地址字段然后轉(zhuǎn)發(fā)等操作。
內(nèi)核 helper 函數(shù)各有不同用途,例如修改一個包之后,計算新的校驗和(checksum)。
進行最后的判決(verdict),確定接下來對這個包執(zhí)行什么操作;
判決結(jié)果包括:
重定向功能的用途:
這些不同的路徑,在圖 1 對應(yīng)的是幾條實線。
將重定向判決(verdict)與重定向目標(target)分開,使得重定向目標類型很容易擴展;?另外,由于重定向參數(shù)(目標)是通過 BPF map 查詢的,因此無需修 改 XDP 程序,就能動態(tài)修改重定向目標。
三種簡單返回碼:丟棄這個包、通過接收時的網(wǎng)卡將包重新發(fā)送出去、允許這個包進入內(nèi)核網(wǎng)絡(luò)棧;
第四種返回碼 redirect:允許?XDP?程序指定網(wǎng)卡、CPU、用戶態(tài) socket?等,將包重定向過去。
將原始包通過另一個網(wǎng)卡(包括虛擬機的虛擬網(wǎng)卡)發(fā)送出去;
轉(zhuǎn)發(fā)給指定?CPU?做進一步處理;
轉(zhuǎn)發(fā)給?AF_XDP 類型的 socket?做進一步處理;
程序還能通過尾調(diào)用(tail call),將控制權(quán)交給另一個 XDP 程序;?通過這種方式,可以將一個大程序拆分成幾個邏輯上的小程序(例如,根據(jù) IPv4/IPv6)。
由于 XDP 程序可包含任意指令,因此前三步(讀取包數(shù)據(jù)、處理元數(shù)據(jù)、重寫包數(shù)據(jù))?順序可以是任意的,而且支持多層嵌套。?但實際中為了獲得高性能,大部分情況下還是將執(zhí)行結(jié)構(gòu)組織成這順序的三步。
3.2 eBPF 虛擬機
XDP 程序在 Extended BPF (eBPF) 虛擬機中執(zhí)行。eBPF 是早期 BSD packet filter (BPF) [37] 的擴展,后者在過去的幾十年中廣泛 應(yīng)用于各種包處理工具。
BPF 使用?基于寄存器的(register-based) virtual machine 來描述?過濾動作(filtering actions)。
eBPF 虛擬機支持動態(tài)加載(loading)和重加載(re-loading)程序,內(nèi)核管理所有 BPF 程序的生命周期。
3.3 BPF maps
eBPF 程序在觸發(fā)內(nèi)核事件時執(zhí)行(例如,觸發(fā) XDP 程序執(zhí)行的,是收包事件)。?程序每次執(zhí)行時,初始狀態(tài)都是相同的(即程序是無狀態(tài)的),它們無法直接訪問?內(nèi)核中的持久存儲(BPF map)。為此,內(nèi)核提供了訪問 BPF map 的?helper 函數(shù)。
BPF map 是 key/value 存儲,在加載 eBPF 程序時定義(defined upon loading an eBPF program)。
用途:
持久存儲。例如一個 eBPF 程序每次執(zhí)行時,都會從里面獲取上一次的狀態(tài)。
用于協(xié)調(diào)兩個或多個 eBPF 程序。例如一個往里面寫數(shù)據(jù),一個從里面讀數(shù)據(jù)。
用于用戶態(tài)程序和內(nèi)核 eBPF 程序之間的通信。
3.4 eBPF verifier
唯一加載入口:bpf()?系統(tǒng)調(diào)用
由于 eBPF 代碼直接運行在內(nèi)核地址空間,因此它能直接訪問 —— 也可 能是破壞 —— 任何內(nèi)存。為防止這種情況發(fā)生,內(nèi)核規(guī)定只能通過唯一入口(?bpf()?系統(tǒng)調(diào)用)加載 BPF 程序。
加載 BPF 程序時,位于內(nèi)核中的校驗器首先會對字節(jié)碼程序進行靜態(tài)分析,以確保
程序中沒有任何不安全的操作(例如訪問任意內(nèi)存),
程序會終止(terminate)。通過下面這兩點來實現(xiàn):
禁止循環(huán)操作
限制程序最大指令數(shù)
校驗器工作原理:two-pass DAG
校驗器的工作原理:首先根據(jù)程序的控制流構(gòu)建一個有向無環(huán)圖(DAG), 然后對 DAG 執(zhí)行如下校驗:
首先,對 DAG 進行一次深度優(yōu)先搜索(depth-first search),以 確保它是無環(huán)的(acyclic),例如,沒有循環(huán),也不包含不支持或無法執(zhí)行到的指令。
然后,再掃描一遍,這次會遍歷 DAG 的所有可能路徑。這次掃描的目的是:
程序執(zhí)行?load?或?call?指令時,如果參數(shù)不合法,就會在這里被拒絕。參數(shù)合法 性是通過在程序執(zhí)行期間跟蹤所有寄存器和棧變量的狀態(tài)(states of registers and stack variables)來實現(xiàn)的。
確保程序的內(nèi)存訪問都是安全的,
調(diào)用?helper 函數(shù)時傳的參數(shù)類型是對的。
內(nèi)存越界和空指針檢查:職責(zé)上移到程序自身/開發(fā)者
這種跟蹤寄存器狀態(tài)的機制是為了在無法預(yù)知內(nèi)存邊界的情況下,仍然確保程序 的內(nèi)存訪問不會越界。無法預(yù)知內(nèi)存邊界是因為:
包的大小是不固定的;
map 的內(nèi)容也無法提前預(yù)知,因此也無法判斷一次 map 查找操作是否會成功。
為解決這個問題,校驗器會檢查已加載的程序自身是否會做如下檢查:
解引用指針前做了內(nèi)存邊界檢查,
查詢 map 之前是檢查了 map 指針是否為空。
這種方式將處理邏輯中的安全檢查和遇到錯誤時如何處理的控制權(quán)都?交給了 BPF 程序的編寫者。
跟蹤數(shù)據(jù)訪問操作和值范圍
為跟蹤數(shù)據(jù)訪問,校驗器會跟蹤
數(shù)據(jù)類型
指針偏置(pointer offsets)
所有寄存器的可能值范圍
程序開始時:
R1?寄存器中存儲的是指向 context metadata 的指針(struct xdp_md *ctx),
R10?是棧指針(stack pointer),
其他所有寄存器都是未初始化狀態(tài)。
接下來程序每執(zhí)行一步,寄存器狀態(tài)就會更新一次。當(dāng)寄存器中存入一個新值時,這個寄存器 還會繼承與這個值相關(guān)的狀態(tài)變量(inherits the state variables from the source of the value)。
算術(shù)操作會影響標量類型的值的范圍(value ranges of scalar types),以及指針類型的 offset。?可能的最大范圍(max possible range)存儲在狀態(tài)變量中,例如往寄存器中 load?一個字節(jié)時, 這個寄存器的可能值范圍就設(shè)置為 0~255。指令圖(instruction graph)中的?各邏輯分支就會根據(jù)操作結(jié)果更新寄存器狀態(tài)。例如,比較操作 R1 > 10,
校驗器在一個分支?if R1 > 10?中會將 R1 最小值設(shè)為 11,
在另一個?else?分支中將其最大值設(shè)為 10。
不同類型數(shù)據(jù)的校驗信息來源(source of truth)
利用狀態(tài)變量中存儲的范圍信息,校驗器就能預(yù)測每個 load 指令能訪問的所有 內(nèi)存范圍,確保它執(zhí)行的都是合法內(nèi)存訪問。
對于包數(shù)據(jù)(packet data)的訪問,會與 context 對象中的?data_end?變量做比較;
對于?BPF?map 中獲取的值,或用到 map 定義中聲明的 data size 信息;
對于棧上存儲的值,會檢查狀態(tài)變量中記錄的值范圍;
對于指針算術(shù)操作(pointer arithmetic)還會施加額外的限制,指針通常不能被轉(zhuǎn)換成整形值。
只要校驗器無法證明某個操作是安全,該?BPF 程序在加載時(load time)就會被拒絕。?除此之外,校驗器還會利用范圍信息確保內(nèi)存的對齊訪問(enforce aligned memory access)。
校驗器的目的
需要說明的是,校驗器的目的是避免將內(nèi)核內(nèi)部(the internals of the kernel )暴露給惡意或有缺陷的 eBPF 程序,而非確保程序中函數(shù)的實現(xiàn)已經(jīng)是最高效的。
換句話說,如果 XDP 程序中處理邏輯過多,也可能會導(dǎo)致機器變慢 ;如果代碼寫的有問題,也可能會破壞包數(shù)據(jù)。出于這些原因,加載 BPF 程序需要 管理員權(quán)限(root)。避免這些 bug 的責(zé)任在程序員,但選擇將哪些程序加載 到系統(tǒng)的權(quán)限在管理員。
3.5 XDP 程序示例
下面是一個簡單的 XDP 程序,展示了前面介紹的一些特性。?程序會解析包數(shù)據(jù),判斷如果是 UDP 包,直接交換源和目的 MAC 地址,然后將包從相同網(wǎng)卡再發(fā)送回去,
雖然這是一個非常簡單的例子,但真實世界中的 XDP 程序用到的組件和特性,這里基本都具備了。
// 從內(nèi)核 BPF 代碼示例 xdp2_kern.c 修改而來。1 // 用于統(tǒng)計包數(shù) 2 struct bpf_map_def SEC("maps") rxcnt = { 3 .type = BPF_MAP_TYPE_PERCPU_ARRAY, 4 .key_size = sizeof(u32), // IP 協(xié)議類型,即 IPv4/IPv6 5 .value_size = sizeof(long), // 包數(shù) 6 .max_entries = 256, 7 }; 8 9 // 直接操作包數(shù)據(jù)(direct packet data access),交換 MAC 地址 10 static void swap_src_dst_mac(void *data) 11 { 12 unsigned short *p = data; 13 unsigned short dst[3]; 14 dst[0] = p[0]; dst[1] = p[1]; dst[2] = p[2]; 15 p[0] = p[3]; p[1] = p[4]; p[2] = p[5]; 16 p[3] = dst[0]; p[4] = dst[1]; p[5] = dst[2]; 17 } 18 19 static int parse_ipv4(void *data, u64 nh_off, void *data_end) 20 { 21 struct iphdr *iph = data + nh_off; 22 if (iph + 1 > data_end) 23 return 0; 24 return iph->protocol; 25 } 26 27 SEC("xdp1") // marks main eBPF program entry point 28 int xdp_prog1(struct xdp_md *ctx) 29 { 30 void *data_end = (void *)(long)ctx->data_end; 31 void *data = (void *)(long)ctx->data; 32 struct ethhdr *eth = data; int rc = XDP_DROP; 33 long *value; u16 h_proto; u64 nh_off; u32 ipproto; 34 35 nh_off = sizeof(*eth); 36 if (data + nh_off > data_end) 37 return rc; 38 39 h_proto = eth->h_proto; 40 41 /* check VLAN tag; could be repeated to support double-tagged VLAN */ 42 if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) { 43 struct vlan_hdr *vhdr; 44 45 vhdr = data + nh_off; 46 nh_off += sizeof(struct vlan_hdr); 47 if (data + nh_off > data_end) 48 return rc; 49 h_proto = vhdr->h_vlan_encapsulated_proto; 50 } 51 52 if (h_proto == htons(ETH_P_IP)) 53 ipproto = parse_ipv4(data, nh_off, data_end); 54 else if (h_proto == htons(ETH_P_IPV6)) 55 ipproto = parse_ipv6(data, nh_off, data_end); 56 else 57 ipproto = 0; 58 59 /* lookup map element for ip protocol, used for packet counter */ 60 value = bpf_map_lookup_elem(&rxcnt, &ipproto); 61 if (value) 62 *value += 1; 63 64 /* swap MAC addrs for UDP packets, transmit out this interface */ 65 if (ipproto == IPPROTO_UDP) { 66 swap_src_dst_mac(data); 67 rc = XDP_TX; 68 } 69 return rc; 70 }具體地:
定義了一個 BPF map 存儲統(tǒng)計信息。用戶態(tài)程序可以 poll 這個 map 來獲取統(tǒng)計信息。
context 對象?struct xdp_md *ctx?中有包數(shù)據(jù)的 start/end 指針,可用于直接訪問包數(shù)據(jù)。
將數(shù)據(jù)指針和?data_end?比較,確保內(nèi)存訪問不會越界。
程序必須自己解析包,包括 VLAN headers 等東西。
直接通過指針(direct packet data access)修改包頭。
內(nèi)核提供的 map lookup helper。這是程序中唯一的真實函數(shù)調(diào)用;其他函數(shù)都是內(nèi)聯(lián),包括?htons()。
最終針對這個包的判決通過程序返回值傳遞給調(diào)用方。
將這段程序安裝到網(wǎng)卡接口上時,它首先會被編譯成 eBPF 字節(jié)碼,然后經(jīng)受校驗器檢查。?這里的檢查項包括:
無循環(huán)操作;程序大小(指令數(shù)量);
訪問包數(shù)據(jù)之前,做了內(nèi)存邊界檢查;
傳遞給 map lookup 函數(shù)的參數(shù),類型與 map 定義相匹配;
map lookup 的返回值(value 的內(nèi)存地址)在使用之前,檢查了是否為 NULL。
3.6 小結(jié)
XDP 系統(tǒng)由四個主要部分組成:
XDP device driver hook:網(wǎng)卡收到包之后直接運行;
eBPF 虛擬機:執(zhí)行 XDP 程序(以及內(nèi)核其他模塊加載的 BPF 程序);
BPF maps:使不同 BPF 程序之間、BPF 程序與用戶空間應(yīng)用之間能夠通信;
eBPF verifier:確保程序不包含任何可能會破壞內(nèi)核的操作。
這四部分加在一起,創(chuàng)造了一個編寫自定義包處理應(yīng)用的強大環(huán)境,它能加速包處理的關(guān)鍵 路徑,同時還與內(nèi)核及現(xiàn)有基礎(chǔ)設(shè)施密切集成。
接下來看一下 XDP 應(yīng)用的性能。
4 性能評估
DPDK 是目前性能最高的包處理框架 [18],因此本文將 XDP 與 DPDK 及 Linux 內(nèi)核網(wǎng)絡(luò) 棧的性能做一個對比。測試機器環(huán)境:
CPU:一塊 hexa-core Intel Xeon E5-1650 v4 CPU running at 3.60GHz, 支持 Intel Data Direct I/O (DDIO) 技術(shù),網(wǎng)絡(luò)硬件通過 DMA 能直接將包放到 CPU 緩存。
關(guān)閉超線性(Hyperthreading)。
網(wǎng)卡:兩塊?Mellanox ConnectX-5 Ex VPI dual-port 100Gbps,mlx5 驅(qū)動。
內(nèi)核:Linux 4.18
使用基于 DPDK 的 TRex packet generator [9] 生成測試流量。所有測試腳本位于 [22]。
在測試中,我們主要關(guān)心三個 metric:
直接棄包(packet drop)性能。
為展示最高的包處理性能,我們將用最簡單的操作 ——?丟棄接收到的包?—— 來測試。?這個測試能有效測量系統(tǒng)的整體開銷,也是真正的包處理應(yīng)用能達到的性能上限。
CPU 使用量。
如引言中指出,XDP 的優(yōu)點之一是 CPU 使用量與流量大小是正相關(guān)的,而無需預(yù)留專 門的 CPU 給它用。我們通過測量 CPU 利用率隨網(wǎng)絡(luò)負載的變化來量化這個指標。
包轉(zhuǎn)發(fā)性能。
轉(zhuǎn)發(fā)的復(fù)雜性要更高一些,例如,涉及到與多塊網(wǎng)卡的交互、重寫二層頭等等。?這里會將轉(zhuǎn)發(fā)延遲也考慮進去。
我們已經(jīng)驗證,使用 MTU(1500 字節(jié))包時,我們的系統(tǒng)單核就能達到線速(100 Gbps), 而且 CPU 有 50% 是空閑的。顯然,真正的挑戰(zhàn)在于 PPS,而非帶寬,其他一些測試也已經(jīng)指出了這一點 [46]。?出于這個原因,我們用最小包(64 字節(jié))測試,衡量指標是 PPS。
對于 XDP 和 Linux 內(nèi)核網(wǎng)絡(luò)棧的測試,由于它們沒有顯式指定某些 CPU 來處理網(wǎng)絡(luò)包的方式,因此我們通過配置硬件 RSS(Receive Side Scaling)來講流量定向到指定 CPU。
對網(wǎng)卡、內(nèi)核的一些配置調(diào)優(yōu),見代碼倉庫 [22]。
4.1 直接棄包(packet drop)性能
Fig 3. 直接棄包(packet drop)性能。DPDK 需要預(yù)留一個 CPU 運行控制任務(wù),因此只剩下 5 個 CPU 做包處理。
上圖是性能與 CPU 數(shù)量的關(guān)系。
XDP 基準性能是?24Mpps/core,DPDK 是?43.5Mpps/core。
二者在分別達到各自的峰值之前,PPS 都是隨 CPU 數(shù)量線性增長的。
最終全局性能受限于 PCI 總線,啟用 PCI descriptor compression(在 CPU cycles 和 PCI 總線帶寬之間取舍)之后,能達到 115Mpps。
再看圖中 Linux 網(wǎng)絡(luò)棧在兩種配置下的性能:
通過?iptables 的 raw table?丟棄流量,這是?Linux 網(wǎng)絡(luò)棧中最早能丟棄包的地方;
通過?conntrack(連接跟蹤)模塊,這個模塊的開銷非常大,但在很多 Linux 發(fā)行版中都是默認開啟的。
conntrack 模式達到了 1.8Mpps/core,raw 模式是 4.8Mpps/core ;這兩種模式均未達到硬件瓶頸。?最終的性能,XDP 比常規(guī)網(wǎng)絡(luò)棧的最快方式快了 5 倍。
Linux raw mode test 中,我們還測量了 XDP 程序不丟棄包,而是更新包數(shù)統(tǒng)計然后將包 送到內(nèi)核網(wǎng)絡(luò)棧的場景。?這種情況下,XDP 單核的處理性能會下降到 4.5Mpps/core,有 13.3ns 處理延遲。?圖中并未給出這個測試結(jié)果,因為這個開銷太小了。
4.2 CPU Usage
Fig 4. 直接棄包(packet drop)場景下的 CPU 利用率。
用系統(tǒng)提供的?mpstat?命令測量 CPU 利用率。結(jié)果如圖 4 。
DPDK 是 busy poll 模式,因此 CPU 永遠是 100%。
XDP 和 Linux 內(nèi)核網(wǎng)絡(luò)棧都是隨流量平滑增長:前面一小段是非線性的,后面基本是線性的。
前面非線性主要是硬中斷帶來的固定開銷,在流量很小時這一部分占比較大。
4.3 包轉(zhuǎn)發(fā)性能
這個測試中,轉(zhuǎn)發(fā)應(yīng)用執(zhí)行非常簡單的 MAC 地址重寫:直接交換源和目的 MAC 地址,然后轉(zhuǎn)發(fā)。?這是轉(zhuǎn)發(fā)場景下最精簡的步驟了,因此結(jié)果代表了所有真實轉(zhuǎn)發(fā)應(yīng)用的性能上限。
圖中包括了同網(wǎng)卡轉(zhuǎn)發(fā)和不同網(wǎng)卡轉(zhuǎn)發(fā)(XDP?程序返回碼不同)的結(jié)果。
DPDK 示例程序只支持通過另一個網(wǎng)卡轉(zhuǎn)發(fā),因此這里只列出了這種情況下的性能。
Linux 網(wǎng)絡(luò)棧不支持這種極簡轉(zhuǎn)發(fā)模式(minimal forwarding mode),需要設(shè)置完整的 橋接或路由查找(bridging or routing lookup)才能轉(zhuǎn)發(fā)包;路由查找是非常耗時的 ,由于其他幾種應(yīng)用并沒有這一步,因此結(jié)果直接對比是沒意義的。因此這里略去了 Linux 網(wǎng)絡(luò)棧的結(jié)果。
轉(zhuǎn)發(fā)吞吐(pps)
Fig 5. 轉(zhuǎn)發(fā)性能。在同一網(wǎng)卡接口上收發(fā)會占用同一 PCI port 的帶寬, 這意味著在 70Mpps XDP same-nic 組就已經(jīng)達到了 PCI 總線的瓶頸
如圖 5 所示,性能隨 CPU 數(shù)量線性擴展,直到達到全局性能瓶頸。XDP 在同網(wǎng)卡轉(zhuǎn)發(fā)的性能遠高于 DPDK 異網(wǎng)卡性能,原因是內(nèi)存處理方式不同:
packet buffer 是設(shè)備驅(qū)動分配的,與接收接口(receiving interface)相關(guān)聯(lián)。
因此,異網(wǎng)卡場景下,當(dāng)包轉(zhuǎn)發(fā)到另一個接口時,memory buffer 需要還給與之關(guān)聯(lián)的接口。
轉(zhuǎn)發(fā)延遲
表 1. 轉(zhuǎn)發(fā)延遲。機器網(wǎng)卡的兩個接口直連,在轉(zhuǎn)發(fā)速率分別為 100pps 和 1Mpps 的條件下,持續(xù) 50s 測量端到端延遲
高 pps 場景下,XDP 的延遲已經(jīng)接近 DPDK。但在低 pps 場景下,XDP 延遲比 DPDK 大的多,原因是?XDP 是基于中斷的,中斷處理時間( interrupt processing time)此時占大頭;而 DPDK 是輪詢模式,延遲相對比較固定。
4.4 討論:XDP 性能與 DPDK 還有差距的原因
XDP 未做底層代碼優(yōu)化
上一節(jié)已經(jīng)看到,XDP 相比于常規(guī) Linux 網(wǎng)絡(luò)棧性能有了顯著提升。但對于大部分 XDP 場景來說,性能還是與 DPDK 有差距。我們認為,這是主要是因為 DPDK 做了相當(dāng)多的底層 代碼優(yōu)化。舉個例子來解釋,考慮 packet drop 例子:
XDP 24Mpps/core,對應(yīng)?41.6ns/packet
DPDK 43.5Mpps,對應(yīng)?22.9ns/packet
多出來的 18.7ns 在我們的 3.6GHz 機器上對應(yīng) 67 個時鐘周期。因此,很顯然?每個很小的優(yōu)化在這里都會產(chǎn)生很大的影響。例如,我們測量出在測試 機器上,每次函數(shù)調(diào)用需要 1.3ns。mlx5 驅(qū)動處理每個包都有 10 次 函數(shù)調(diào)用,總計就是 13ns。
通用目的操作系統(tǒng),首要目標:更好的擴展和配置,而非極致性能
另外,在 Linux 這樣的通用目的操作系統(tǒng)中,某些開銷是不可避免的, 因為設(shè)備驅(qū)動或子系統(tǒng)的組織方式是為了實現(xiàn)更好的擴展和配置,而非極致性能。
但是,我們認為有些優(yōu)化還是有必要的。例如,我們嘗試將內(nèi)核中與測試網(wǎng)卡無關(guān)的 DMA 函數(shù)調(diào)用刪掉, 這樣將前面提到的 10 個函數(shù)調(diào)用降低到了 6 個,測試結(jié)果顯示這將單核性能提升到了 29Mpps/core。?依此推測的話,將另外 6 個函數(shù)調(diào)用也優(yōu)化掉,能將 XDP 的性能提升到?37.6Mpps。?實際上我們不可能將 6 個全部去掉,但去掉其中幾個,再加上一些其他優(yōu)化,我 們相信 XDP 和 DPDK 的性能差距將越來越小。
其他驅(qū)動的測試結(jié)果也是類似的,例如 i40e driver for 40 Gbps Intel cards。
基于以上討論,我們相信未來 XDP 與 DPDK 的性能差距將越來越小。
另一方面,考慮到 XDP 在靈活性和與內(nèi)核集成方面的優(yōu)勢, XDP 已經(jīng)是很多實際場景中的非常有競爭力的方式。下文給出幾個例子。
5 真實場景使用案例
本節(jié)給出三個例子來具體展示 XDP 在真實世界中的應(yīng)用。?這幾個案例都是已經(jīng)真實在用的,但本文出于解釋目的,將使用簡化的版本。?同時也建議讀者參考 [38],后者是獨立的文章,介紹使用 XDP 解決實際工作中網(wǎng)絡(luò)服務(wù)所面臨的一些挑戰(zhàn)。
本節(jié)目的是展示真實 XDP 方案的可行性,因此不會將重點放在與業(yè)界最新的實現(xiàn)做詳盡性能對比上。?我們會拿常規(guī)的 Linux 內(nèi)核網(wǎng)絡(luò)棧的性能作為 baseline,來對比 XDP 應(yīng)用的性能。
5.1 案例一:軟件路由(software routing)
內(nèi)核數(shù)據(jù)平面 & 控制平面(BIRD/FRR)
Linux 內(nèi)核實現(xiàn)了一個功能完整的路由表,作為數(shù)據(jù)平面,支持
policy routing
source-specific routing
multipath load balancing, and more.
對于控制平面,Bird [10] 或 FRR [17] 這樣的路由守護進程( routing daemons)實現(xiàn)了多種路由控制平面協(xié)議。Linux 提供的這套生態(tài)系統(tǒng)功能如此豐富 ,因此再在另一個包處理框架中重新實現(xiàn)一套類似的路由棧代價將非常高, 更實際的方式是對 Linux 內(nèi)核的數(shù)據(jù)平面進行優(yōu)化。
XDP:直接查詢內(nèi)核路由表并轉(zhuǎn)發(fā)
XDP 非常適合做這件事情,尤其是它提供了一個 helper 函數(shù),能從 XDP 程序中直接查詢內(nèi)核路由表。
如果查詢成功,會返回?egress interface 和下一跳 MAC 地址, XDP 程序利用這些信息足夠?qū)⒓崔D(zhuǎn)發(fā)出去。
如果下一跳 MAC 還是未知的(因為之前還沒進行過 neighbour lookup),XDP 程序就 能將包傳給內(nèi)核網(wǎng)絡(luò)棧,后者會解析 neighbor 地址,這樣隨后的包 就能直接被 XDP 程序轉(zhuǎn)發(fā)了。
測試:XDP routing + 全球 BGP 路由表
為展示 XDP 路由的性能,我們用 Linux 內(nèi)核代碼中的 XDP routing 例子 [1],與常規(guī) Linux 內(nèi)核網(wǎng)絡(luò)棧的性能做對比。?兩組測試:
路由表中只有一條路由;
路由表中有從 routeviews.org 中 dump 而來的全球 BGP 路由表(global BGP routing table)。?包含 752,138 條路由。隨機生成 4000 個目的 IP 地址,以確保能充分利用到這種路由表。
如果目的 IP 地址少于 4000 個,實際用到的路由表部分會較小,能夠保存在 CPU 緩存中,使得結(jié)果不準確。?增大 IP 數(shù)量至 4000 個以上,不會對轉(zhuǎn)發(fā)性能造成影響,但可以避免緩存導(dǎo)致的結(jié)果不準問題。
對于兩組測試,下一跳 MAC 地址都是與我們的發(fā)送網(wǎng)卡直接相關(guān)的接口的地址。
性能:2.5x
Fig 6. 軟件路由的性能。由于性能隨核數(shù)線性增加,這里只給出單核的結(jié)果。
測試結(jié)果如上圖所示。
full table lookup 性能提升了 2.5 倍;
smaller routing table 組,提升了 3 倍。
這說明,XDP 路由程序 + 單核 + 10Gbps 網(wǎng)卡?的軟硬件配置,就能?處理整張全球 BGP 路由表(保守估計每個包平均 300 字節(jié))。
5.2 案例二:Inline DoS Mitigation
DoS 攻擊還是像瘟疫一樣糾纏著互聯(lián)網(wǎng),現(xiàn)在通常的方式是:通過已經(jīng)入侵的大量設(shè)備發(fā)起分布式(DDoS)攻擊。
有了XDP 之后,我們能直接在應(yīng)用服務(wù)器(application servers)上?部署包過濾程序來防御此類攻擊(inline DoS mitigation),?無需修改應(yīng)用代碼。如果應(yīng)用是部署在虛擬機里,那 XDP 程序還可以 部署在宿主機(hypervisor)上,這樣單個程序就能保護機器上所有的虛擬機。
模擬 Cloudflare 防御架構(gòu)
為展示工作原理,我們用 XDP 作為過濾機制,模擬 Cloudflare 的 DDoS 防御架構(gòu)?[6]。?他們的 Gatebot architecture ,首先在各 PoP 點機器上采樣,然后統(tǒng)一收起來做分析, 根據(jù)分析結(jié)果生成防御規(guī)則。
防御規(guī)則的形式是對包數(shù)據(jù)(payload)進行一系列簡單檢查, 能直接編譯成 eBPF 代碼然后分發(fā)到 PoP 點的所有服務(wù)器上。這里說的代碼是 XDP 程序 ,它會將匹配到規(guī)則的所有流量丟棄,同時將統(tǒng)計信息更新到 BPF map。
程序邏輯
為驗證這種方案的性能,我們編寫一個 XDP 程序,它
解析包頭,執(zhí)行一些簡單驗證。對每個包:執(zhí)行四次讀取操作,以解析外層包頭。
將符合攻擊特性的流量丟棄。具體:丟棄 UDP + 特定端口的流量。
將其他流量通過 CPU redirect 方式重定向給另一個 CPU?做進一步處理;
性能
我們用 netperf 做性能壓測 [26]。
用 netperf TCP round-trip benchmark,單個 TCP 連接來回小的 request/reply,統(tǒng)計 transactions/second。
模擬的是交互式應(yīng)用,例如小的遠程過程調(diào)用(RPC)。
實驗在單核上進行,模擬多個流量(正常流量+攻擊流量)競爭同一物理資源的場景。
在?beseline 35K 業(yè)務(wù) TPS(transactions per second)基礎(chǔ)上,打少量 UDP 流量作為攻擊流量。逐漸加大攻擊流量,觀察 TPS 的變化。
Fig 7. DDoS 性能。業(yè)務(wù)吞吐(TPS)隨攻擊流量的變化。
結(jié)果如上圖所示,
沒有 XDP 的一組,性能急劇下降:攻擊流量在 3Mpps 時性能減半,3.5Mpps 時基本跌零;
有 XDP 程序的一組,攻擊流量達到 19.5Mpps 之前,業(yè)務(wù)吞吐保持在 28.5K TPS 以上,過了這個臨界點性能才開始急劇下降。
以上結(jié)果表明,XDP 防御 DDoS 攻擊在實際中是完全可行的,單核就能輕松處理 10Gbps 的、都是最小包(minimum-packet)的 DoS 流量。?這種 DDoS 防御的部署更加靈活,無需硬件或應(yīng)用做任何改動。
5.3 案例三:負載均衡(load balancing)
Facebook Katran
負載均衡的場景,我們用 Facebook 開源的 Katran 作為例子 [15]。Katran 的工作原理是對外通告服務(wù)的 IP,這樣目標是這個 IP 的流量就會被路由到 XDP 實現(xiàn)的負載均衡器。
負載均衡器對包頭(source packet header)進行哈希,以此選擇目標應(yīng)用服務(wù)器。
然后將對包進行封裝(encap),發(fā)送給應(yīng)用服務(wù)器;
應(yīng)用服務(wù)器解封裝(decap),處理請求,然后直接將回包發(fā)給客戶端(DSR 模式)。
在這個過程中,XDP 程序負責(zé)哈希、封裝以及將包從接收網(wǎng)卡再發(fā)出去的任務(wù)。?配置信息存儲在 BPF map 中,整個封裝邏輯是完全在 eBPF 中實現(xiàn)的。
性能
為測試性能,我們給 Katran XDP 程序配置幾個固定的目標機器。?對照組是 IPVS,它是 Linux 內(nèi)核的一部分。性能如表 2 所示,隨 CPU 數(shù)量線性增長, XDP 比 IPVS 性能高 4.3 倍。
表 2. 負載均衡器性能(Mpps)
配置:1 VIP/core, 100 DstIPs/VIP.
6 XDP 的未來方向
XDP 已經(jīng)能用于解決真實問題,但作為 Linux 內(nèi)核的一部分,XDP 還在快速開發(fā)過程中。
6.1 eBPF 程序的限制
前面提到,加載到 eBPF 虛擬機的程序必須保證其安全性(不會破壞內(nèi)核),因此對 eBPF 程序作了一下限制,歸結(jié)為兩方面:
確保程序會終止:在實現(xiàn)上是通過禁止循環(huán)和限制程序的最大指令數(shù)(max size of the program);
確保內(nèi)存訪問的安全:通過 3.4 小結(jié)介紹的寄存器狀態(tài)跟蹤(register state tracking)來實現(xiàn)。
校驗邏輯偏保守
由于校驗器的首要職責(zé)是保證內(nèi)核的安全,因此其校驗邏輯比較保守, 凡是它不能證明為安全的,一律都拒絕。有時這會導(dǎo)致假陰性(false negatives), 即某些實際上是安全的程序被拒絕加載;這方面在持續(xù)改進。
校驗器的錯誤提示也已經(jīng)更加友好,以幫助開發(fā)者更快定位問題。
近期已經(jīng)支持了 BPF 函數(shù)調(diào)用(function calls)。
正在計劃支持有限循環(huán)(bounded loops)。
正在提升校驗器效率,以便處理更大的 BPF 程序。
缺少標準庫
相比于用戶空間 C 程序,eBPF 程序的另一個限制是缺少標準庫,包括?內(nèi)存分配、線程、鎖等等庫。
內(nèi)核的生命周期和執(zhí)行上下文管理(life cycle and execution context management )部分地彌補了這一不足,(例如,加載的 XDP 程序會為每個收到的包執(zhí)行),
內(nèi)核提供的 helper 函數(shù)也部分地彌補了一不足。
一個網(wǎng)卡接口只能 attach 一個 XDP 程序
這個限制其實也是可以繞過的:將 XDP 程序組織成程序數(shù)組,通過尾 調(diào)用,根據(jù)包上下文在程序之間跳轉(zhuǎn),或者是將幾個程序做 chaining。
6.2 用戶體驗和調(diào)試
XDP 程序運行在內(nèi)核,因此常規(guī)的用戶空間 debug 工具是用不了的,但內(nèi)核自帶的 debug 和 introspection 功能是可以用在 XDP (及其他 eBPF 程序)上的。?包括:
tracepoints and kprobes [13]
performance counters that are part of the perf subsystem [42]
但不熟悉內(nèi)核生態(tài)系統(tǒng)的開發(fā)者可能會對這些工具感到非常陌生,難以使用。因此,也出 現(xiàn)了一些更方便普通開發(fā)者的工具,包括 BCC [50]、bpftool [8]、libbpf 函數(shù)庫 [30] 等等。
6.3 驅(qū)動支持
設(shè)備要支持 XDP,需要實現(xiàn)內(nèi)核核心網(wǎng)絡(luò)棧暴露出的一個 API。?寫作本文時 Linux 4.18 已經(jīng)有 12 種驅(qū)動支持 XDP,包括了大部分高速網(wǎng)卡。?最新列表見 [2]。
隨著 XDP 系統(tǒng)的不斷成熟,核心代碼逐漸上移到內(nèi)核中,驅(qū)動需要維護的代碼越 來越少。例如,redirection action 支持新的 target 時,無需驅(qū)動做任何改動。
最后,對于那些不支持 XDP 的驅(qū)動,內(nèi)核提供了?Generic XDP?feature [39],這是軟件實現(xiàn)的 XDP,性能會低一些, 在實現(xiàn)上就是將 XDP 的執(zhí)行上移到了核心網(wǎng)絡(luò)棧(core networking stack)。
XDP 在內(nèi)核收包函數(shù)?receive_skb() 之前,
Generic XDP?在?receive_skb() 之后,
更多關(guān)于 Generic XDP,可參考參考:容器網(wǎng)絡(luò)|深入理解Cilium
6.4 性能提升
XDP 和 DPDK 之間還有一些性能差距,一些改進工作正在進行中:
驅(qū)動代碼 micro-optimisations
刪除核心 XDP 代碼中的非必要操作
通過批處理平攤處理開銷
6.5 QoS 和 Rate Transitions
當(dāng)前,XDP 還沒有任何 QoS 機制。?尤其是,如果對端已經(jīng)過載(例如兩端的網(wǎng)絡(luò)速度或特性不匹配),XDP 程序是收不到任何背壓(back-pressure)的,
雖然 XDP 中缺少 QoS,但 Linux 內(nèi)核網(wǎng)絡(luò)棧中卻有很多業(yè)界最佳的 Active Queue Management (AQM) 特性和 packet scheduling algorithms [23]。?這些特性中,部分并不適用于 XDP,但我們相信能夠 以一種對包處理應(yīng)用完全透明的方式,選擇其中部分集成到 XDP。?我們計劃對這一方向進行更深入研究。
6.6 加速傳輸層協(xié)議
我們已經(jīng)證明 XDP 能在保留操作系統(tǒng)原有功能的前提下,集成到操作系統(tǒng)中,實現(xiàn)高速包數(shù)據(jù)。
目前的 XDP 還是用于無狀態(tài)包處理(stateless packet processing) ,如果將這個模型擴展到有狀態(tài)傳輸層協(xié)議(stateful transport protocols),例如 TCP,它能給依賴可靠/有狀態(tài)傳輸?shù)膽?yīng)用提供類似的性能提升。
實際上,已經(jīng)有一些研究證明,相比于操作系統(tǒng)的協(xié)議棧,accelerated transport protocols 能顯著提升性能[5, 25, 35, 52]。其中的一個解決方案 [52] 表明,在保留內(nèi) 核 TCP 協(xié)議棧的的前提下,原始包處理性能(raw packet processing)存在巨大的提升 空間。
XDP 非常適用于這種場景,目前也已經(jīng)有一些關(guān)于如何實現(xiàn)的初步討論 [21], 雖然離實際使用還很遠,但仍然是一個令人振奮的、擴展 XDP 系統(tǒng) scope 的方向。
6.7 內(nèi)核-用戶空間零拷貝(zero-copy to userspace)
3.1 小節(jié)提到,XDP 程序能將數(shù)據(jù)包重定向到用戶空間應(yīng)用(userspace application)打 開的特殊類型 socket。這可以用于加速客戶端和服務(wù)端在同一臺機器?的網(wǎng)絡(luò)密集型應(yīng)用(network-heavy applications running on the local machine)。
更多信息可參考:?(譯) 利用 ebpf sockmap/redirection 提升 socket 性能(2020)。?這里使用的是 BPF 而非 XDP,但核心原理是一樣的,只是程序執(zhí)行的位置(hook)不同。?譯注。
但在目前的實現(xiàn)中,這種方式在底層仍然需要拷貝包數(shù)據(jù),因此性能會打折扣。
目前已經(jīng)有工作在進行,通過 AF_XDP 實現(xiàn)真正的數(shù)據(jù)零拷貝。但這項工作需要?對網(wǎng)絡(luò)設(shè)備的內(nèi)存處理過程有一些限制,因此需要設(shè)備驅(qū)動的顯式支持。?第一個支持這個功能的 patch 已經(jīng)合并到?4.19?內(nèi)核,更多驅(qū)動的支持 正在添加中。初步的性能測試結(jié)果還是很樂觀的,顯示能達到 20Mpps/core 的內(nèi)核到用戶 空間傳遞(transfer)速度。
6.8 XDP 作為基礎(chǔ)構(gòu)建模塊(XDP as a building block)
正如 DPDK 用于高層包處理框架的底層構(gòu)建模塊(例如 [31]),XDP 有望成為高層應(yīng)用的運行時環(huán)境 (runtime environment for higher-level applications)。
實際上,我們看到一些基于 XDP 的應(yīng)用和框架已經(jīng)出現(xiàn)了。包括
Cilium security middle-ware [3]
Suricata network monitor [4]
Open vSwitch [49]
P4-to-XDP compiler project [51]
甚至還有人嘗試將 XDP 作為 DPDK 的一種底層驅(qū)動 [53]。
7 總結(jié)
本文描述了XDP,一個安全、快速、可編程、集成到操作系統(tǒng)內(nèi)核的包處理框架。?測試結(jié)果顯示,XDP能提供 24Mpps/core 的高處理性能,這一數(shù)字雖然與基于 kernel bypass 的DPDK仍有差距,但提供了其他一些非常有競爭力的優(yōu)勢:
兼容內(nèi)核安全和管理框架(kernel bypass 方式在bypass 內(nèi)核網(wǎng)絡(luò)棧的同時,也將安全和設(shè)備管理等這些極其重要的基礎(chǔ)設(shè)施 bypass 了);
兼容內(nèi)核網(wǎng)絡(luò)棧,可選擇性利用內(nèi)核已有的基礎(chǔ)設(shè)施和功能;
提供與內(nèi)核 API 一樣穩(wěn)定的編程接口;
對應(yīng)用完全透明;
更新、替換程序的過程不會引起服務(wù)中斷;
無需專門硬件,無需獨占CPU等資源。
相比于 kernel bypass 這種非此即彼、完全繞開內(nèi)核的方式,我們相信 XDP 有更廣闊的的應(yīng)用前景。Facebook、Cloudflare 等公司實際落地的 XDP 應(yīng)用,更加增強了我們的這種信心。
最后,XDP系統(tǒng)還在快速發(fā)展,前面也列出了一些正在未來可能會做的開發(fā)/優(yōu)化工作。
致謝
XDP has been developed by the Linux networking community for a number of years, and the authors would like to thank everyone who has been involved. In particular,
Alexei Starovoitov has been instrumental in the development of the eBPF VM and verifier;
Jakub Kicinski has been a driving force behind XDP hardware offloading and the bpftool utility;
Bj?rn T?pel and Magnus Karlsson have been leading the AF_XDP and userspace zero-copy efforts.
We also wish to extend our thanks to the anonymous reviewers, and to our shepherd Srinivas Narayana, for their helpful comments.
參考文獻
https://arthurchiao.art/blog/xdp-paper-acm-2018-zh/
- END -
看完一鍵三連在看,轉(zhuǎn)發(fā),點贊
是對文章最大的贊賞,極客重生感謝你
推薦閱讀
深入理解Linux內(nèi)核之內(nèi)存尋址
2022新年重磅技術(shù)分享|深入理解Linux操作系統(tǒng)
一些優(yōu)秀的后端開源項目!
你好,這里是極客重生,我是阿榮,大家都叫我榮哥,從華為->外企->到互聯(lián)網(wǎng)大廠,目前是大廠資深工程師,多次獲得五星員工,多年職場經(jīng)驗,技術(shù)扎實,專業(yè)后端開發(fā)和后臺架構(gòu)設(shè)計,熱愛底層技術(shù),豐富的實戰(zhàn)經(jīng)驗,分享技術(shù)的本質(zhì)原理,希望幫助更多人蛻變重生,拿BAT大廠offer,培養(yǎng)高級工程師能力,成為技術(shù)專家,實現(xiàn)高薪夢想,期待你的關(guān)注!點擊藍字查看我的成長之路。
校招/社招/簡歷/面試技巧/大廠技術(shù)棧分析/后端開發(fā)進階/優(yōu)秀開源項目/直播分享/技術(shù)視野/實戰(zhàn)高手等,?極客星球希望成為最有技術(shù)價值星球,盡最大努力為星球的同學(xué)提供技術(shù)和成長幫助!詳情查看->極客星球
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 求點贊,在看,分享三連
總結(jié)
以上是生活随笔為你收集整理的万字长文|深入理解XDP全景指南的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入理解缓存系统|单机QPS突破千万优化
- 下一篇: 从C10K到C10M高性能网络的探索与实