1. 程式人生 > >如何實現核心旁路(Kernel bypass)?

如何實現核心旁路(Kernel bypass)?

在前兩篇文章中,我們討論了《如何生成每秒百萬級別的HTTP 請求?》 以及 如何減少往返時間 。我們在 Linux 上做試驗,因為它是一個性能非常好的通用作業系統。

不幸的是,對於一些更加專業的工作,Vanilla Linux(譯註:Linux 的核心版本,代號“香草”) 核心的網路速度是不夠的。舉個例子,在 CloudFlare,我們持續地處理洪水般的資料包。 Vanilla Linux 處理速度僅能達到約 1M pps (譯註:單位 packet per seconds),這在我們的工作環境下是不夠的,特別是網絡卡有能力處理大量的資料包。現代 10Gbps 網絡卡的處理能力通常至少達到 10M pps 。

核心不給力

我們做一個小實驗來說明修改 Linux 確實是有必要的。我們看看理想狀態下核心能處理多少資料包。把資料包傳遞到使用者空間的代價是高昂的,讓我們嘗試一下在網路驅動程式收到資料包後就立刻丟棄它們。據我所知,Linux 上不修改核心丟棄資料包最快的方法是在 PREROUTING iptables 上設定一些丟棄規則。

Shell
1234567 $sudo iptables-traw-IPREROUTING-pudp--dport4321--dst192.168.254.1-jDROP$sudo ethtool-Xeth2 weight1$watch'ethtool -S eth2|grep rx'rx_packets:12.2m/srx-0.rx_packets:1.4m/srx-1.rx_packets:0/s...

如上所示, Ethtool(譯者注:Ethtool 是 Linux 下用於查詢及設定網絡卡引數的命令

)的統計顯示,網絡卡能達到每秒接收 12M 資料包的速度。通過 ethtool -X 來操作網絡卡上的間接表,可以將所有的資料包引向 0 號 RX 佇列。正如我們看到的,在一顆 CPU 上,核心處理佇列的速度可以達到 1.4M pps。

在單核上能達到 1.4M pps 是一個相當不錯的結果,但不幸的是協議棧卻不能擴充套件。當資料包被分配到多核上,這個成績會急劇下降。讓我們看看把資料包分到 4 個 RX 佇列的結果。

Shell
1234567 $sudo ethtool-Xeth2 weight1111$watch'ethtool -S eth2|grep rx'rx_packets:12.1m/srx-0.rx_packets:477.8k/srx-1.rx_packets:447.5k/srx-2.rx_packets:482.6k/srx-3.rx_packets:455.9k/s

此時每個核的處理速度是 480k pps。這是個糟糕的訊息。即使樂觀地假設增加多個核心不會進一步地造成效能的下降,處理資料包的核心也要多達 20 個才能達到線速度。所以核心是不起作用的。

核心旁路前來救駕

CC BY 2.0 image by Matt Brown

關於 Linux 核心網路效能的侷限早已不是什麼新鮮事了。在過去的幾年中,人們多次嘗試解決這個問題。最常用的技術包括建立特別的 API,來幫助高速環境下的硬體去接資料包。不幸的是,這些技術總是在變動,至今沒有出現一個被廣泛採用的技術。

這裡列出一些廣為人知的核心旁路技術。

PACKET_MMAP

PF_RING

PF_RING 是另一個已知的技術,用來提升捕獲資料包的速度。不像 packet_mmap,PF_RING 不在核心主線中,需要一些特殊模組。通過 ZC 驅動和把模式設定成 transparent_mode = 2(譯者注:是 PF_RING 的一種模式),只把資料包傳遞給 PF_RING 客戶端,而不會經過核心網路協議棧。由於核心比較緩慢,這樣可以確保高速運轉。

Snabbswitch

Snabbswitch 是一個 Lua 網路框架,主要用來寫 L2 應用。它可以完全接管一個網絡卡,並且在使用者空間實現硬體驅動。它在一個 PCI 裝置上實現了使用者空間 IO(UIO),把裝置暫存器對映到 sysfs 上(譯者注:sysfs 是 Linux 核心中設計較新的一種虛擬的基於記憶體的檔案系統) 。這樣就可以非常快地操作,但是這意味著資料包完全跳過了核心網路協議棧。

DPDK

DPDK 是一個用 C 語言實現的網路框架,專門為 Intel 晶片建立。它本質上和 snabbswitch 類似,因為它也是一個基於UIO 的完整框架。

Netmap

Netmap 也是一個豐富的網路框架,但是和 UIO 技術不同,它是由幾個核心模組來實現的。為了和網路硬體整合在一起,使用者需要給核心網路驅動打補丁。增加複雜性的最大好處是有一個詳細文件說明的、

由於核心旁路技術的目的是不再讓核心處理資料包,所以我們排除了 packet_mmap。因為它不能接收資料包 —— 它只是一個嗅探資料包的快速介面。同樣,沒有 ZC 模組的 PF_RING 也沒有什麼吸引力,因為它的主要目標是加速 libpcap(譯者注:libpcap是unix/linux平臺下的網路資料包捕獲函式包,大多數網路監控軟體都以它為基礎)。

我們已經排除了兩種技術,但很不幸的是,在餘下的解決方案中,也沒有我們能夠使用的!

讓我告訴你原因。為了用 剩下的技術 實現核心旁路技術:Snabbswitch、DPDK 和 netmap 會接管整個網絡卡,不允許網絡卡的任何流量經過核心。我們在 CloudFlare,根本不可能讓一個分擔負載的應用程式獨佔整個網絡卡。

話說回來,很多人使用上面的技術。在其他環境中佔用一個網絡卡,來實現旁路也許是可以接受的。

Solarflare 上的 EF_VI

雖然上面列出的技術需要佔用整個網絡卡,但還有其它的選擇。

Solarflare 網絡卡支援 OpenOnload,一個神奇的網絡卡加速器。它通過如下方式來實現核心旁路,在使用者空間實現網路協議棧,並使用 LD_PRELOAD 覆蓋目標程式的網路系統呼叫。在底層訪問網絡卡時依靠 “EF_VI” 庫。這個庫可以直接使用並且有很好的說明文件

EF_VI 作為一個專用庫,僅能用在 Solarflare 網絡卡上,你可能想知道它實際是如何工作的。 EF_VI 是以一種非常聰明的方式重新使用網絡卡的通用功能。

在底層,每個 EF_VI 程式可以訪問一條特定的 RX 佇列,這條 RX 佇列對核心不可見的。預設情況下,這個佇列不接收資料,直到你建立了一個 EF_VI “過濾器”。這個過濾器只是一個隱藏的流控制規則。你用 ethtool -n 也看不到,但實際上這個規則已經存在網絡卡中了。對於 EF_VI 來說,除了分配 RX 佇列並且管理流控制規則,剩下的任務就是提供一個API 讓使用者空間可以訪問這個佇列。

分叉驅動

雖然 EF_VI 是 Solarflare 所特有的,其他網絡卡還是可以複製這個技術。首先我們需要一個支援多佇列的網絡卡,同時它還支援流控制和操作間接表。

有了這些功能,我們可以:

  •  正常啟動網絡卡,讓核心來管理一切。
  •  修改間接表以確保沒有資料包流向任一 RX 佇列。比如說我們選擇 16 號 RX 佇列。
  •  通過流控制規則將一個特定的網路流引到 16號 RX 佇列。

完成這些,剩下的步驟就是提供一個使用者空間的 API ,從 16 號 RX 佇列上接收資料包,並且不會影響其他任何佇列。

這個想法在 DPDK 社群被稱為“分叉驅動”。它們打算在  2014 年建立分叉驅動不幸的是 這個補丁 還沒進入核心的主線。

虛擬化方法

針對 intel 82599 還有另外一種選擇。我們可以利用網絡卡上的虛擬化功能來實現核心旁路,而不需要通過分叉驅動程式。

首先我簡單說下背景。有結果證明,在虛擬化世界中將資料包從主機傳遞到客戶機,虛擬機器通常是瓶頸。因此,這些年對虛擬化效能的需求與日俱增,通過軟體模擬網路硬體的模擬技術成為了影響效能的主要障礙。

網絡卡廠商增加一些特性來加速虛擬客戶端。其中一項虛擬化技術,要求網絡卡虛擬成多個 PCI 裝置。虛擬客戶端可以操作這些虛擬介面,無需與主機作業系統進行任何合作。我演示一下它是如何工作的。舉個例子,這是我本機上的 82599 網絡卡。這個“真實的”裝置被稱為 PF(物理功能)介面:

Shell
12 $lspci04:00.1Ethernet controller:Intel Corporation82599EB10-Gigabit SFI/SFP+Network Connection(rev01)

我們要求這個裝置建立一個 VF(虛擬功能)裝置:

Shell
1234 $echo1>/sys/class/net/eth3/device/sriov_numvfs$lspci04:00.1Ethernet controller:Intel Corporation82599EB10-Gigabit SFI/SFP+Network Connection(rev01)04:10.1Ethernet controller:Intel Corporation82599Ethernet Controller Virtual Function(rev01)

比如說一個 KVM 客戶端很容易使用這個假的 PCI 裝置。同時,我們還是能夠使用主機環境。要做到這些僅需要載入 “ixgbevf” 核心模組,之後會出現另一個 “ethX” 介面。

你或許想知道核心旁路技術幹了什麼。核心沒有利用“ixgbevf”裝置正常聯網,我們可以把它專門用在核心旁路上。這樣看起來可以在 “ixgbevf” 裝置執行 DPDK

概括來說:這個想法可以讓 PF 裝置正常處理核心工作,而 VF 介面專門用在核心旁路技術上。由於 VF 是專用的,所以我們可以執行“接管整個網絡卡”的技術。

這聽起來似乎不錯,實際上卻沒那麼簡單。首先,只有 DPDK 支援“ixgbevf”裝置,netmap,snabbswtich 和 PF_RING 是不支援的。預設情況下, VF 介面不能接收任何資料包。若通過 PF 傳送資料給 VF ,你需要給 ixgbe 打上這個補丁。有了它,你可以對 VF 進行定址,即在ethtool中對“活動”“佇列號的高位進行編碼。

Shell
1 $ethtool-Neth3 flow-typetcp4 dst-ip192.168.254.30dst-port80action4294967296

最後一個障礙出現了,在 82599 晶片上啟用 VF 功能,RSS 組的最大規模變小了(譯者注:Really Simple Syndication,簡易資訊聚合)。沒有虛擬化時,82599 可以在 16 個 CPU 核上進行 RSS 。但隨著 VF 的啟用,這個數量卻變成了 4。如果 PF 上的流量比較低,只使用 4 個核來發布可能還好。不幸的是,我們在 Cloudflare 需要處理大規模的 RSS 組。

結束語

完成核心旁路技術沒有那麼簡單。雖然存在很多開源的技術,但它們看起來都需要一塊專用的的網絡卡。這裡我們展示了 3 個可以選擇的框架:

  •  類似 EF_VI, 隱藏 RX 佇列
  •  DPDK 分叉驅動
  •  VF 技術

不幸的是,在我們的環境下,這麼多技術中能起作用的似乎只有 EF_VI。我們祈禱開源的核心旁路 API 趕緊出現,唯一的要求是不需要一塊專用的網絡卡。

打賞支援我翻譯更多好文章,謝謝!

打賞譯者

打賞支援我翻譯更多好文章,謝謝!

任選一種支付方式