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

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

客戶機 pac 復制 如果 高速 har 運行 ebs 底層

轉到 :http://blog.jobbole.com/94976/

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

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

技術分享

內核不給力

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

Shell
1 2 3 4 5 6 7 $ sudo iptables -t raw -I PREROUTING -p udp --dport 4321 --dst 192.168.254.1 -j DROP
$ sudo ethtool -X eth2 weight 1 $ watch ‘ethtool -S eth2|grep rx‘ rx_packets: 12.2m/s rx-0.rx_packets: 1.4m/s rx-1.rx_packets: 0/s ...

如上所示, Ethtool(譯者註:Ethtool 是 Linux 下用於查詢及設置網卡參數的命令)的統計顯示,網卡能達到每秒接收 12M 數據包的速度。通過 ethtool -X 來操作網卡上的間接表,可以將所有的數據包引向 0 號 RX 隊列。正如我們看到的,在一顆 CPU 上,內核處理隊列的速度可以達到 1.4M pps。

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

Shell
1 2 3 4 5 6 7 $ sudo ethtool -X eth2 weight 1 1 1 1 $ watch ‘ethtool -S eth2|grep rx‘ rx_packets: 12.1m/s rx-0.rx_packets: 477.8k/s rx-1.rx_packets: 447.5k/s rx-2.rx_packets: 482.6k/s rx-3.rx_packets: 455.9k/s

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

內核旁路前來救駕

技術分享

CC BY 2.0 image by Matt Brown

關於 Linux 內核網絡性能的局限早已不是什麽新鮮事了。在過去的幾年中,人們多次嘗試解決這個問題。最常用的技術包括創建特別的 API,來幫助高速環境下的硬件去接收數據包。不幸的是,這些技術總是在變動,至今沒有出現一個被廣泛采用的技術。

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

PACKET_MMAP

Packet mmap 是 Linux 上的API,用來實現數據包快速嗅探。然而它不是嚴格意義上的內核旁路技術,它是技術列表中的一個特例 —— 可以在 Vanilla 內核上使用。

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 技術不同,它是由幾個內核模塊來實現的。為了和網絡硬件集成在一起,用戶需要給內核網絡驅動打補丁。增加復雜性的最大好處是有一個詳細文檔說明的、設備廠商無關的和清晰的 API。

由於內核旁路技術的目的是不再讓內核處理數據包,所以我們排除了 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
1 2 $ lspci 04:00.1 Ethernet controller: Intel Corporation 82599EB 10-Gigabit SFI/SFP+ Network Connection (rev 01)

我們要求這個設備創建一個 VF(虛擬功能)設備:

Shell
1 2 3 4 $ echo 1 > /sys/class/net/eth3/device/sriov_numvfs $ lspci 04:00.1 Ethernet controller: Intel Corporation 82599EB 10-Gigabit SFI/SFP+ Network Connection (rev 01) 04:10.1 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01)

比如說一個 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 -N eth3 flow-type tcp4 dst-ip 192.168.254.30 dst-port 80 action 4294967296

最後一個障礙出現了,在 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 趕緊出現,唯一的要求是不需要一塊專用的網卡。

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