DPDK — PMD,DPDK 的核心優化

目錄

前文列表

DPDK — 安裝部署
DPDK — 數據平面開發技術
DPDK — 架構解析
DPDK — IGB_UIO,與 UIO Framework 進行交互的內核模塊

PMD,DPDK 的核心優化

我們知道,Linux 內核在收包時有兩種方式可供選擇,一種是中斷方式,另外一種是輪詢方式。

從哲學的角度來說,中斷是外界強加給你的信號,你必須被動應對,而輪詢則是你主動地處理事情。前者最大的影響就是打斷你當前工作的連續性,而後者則不會,事務的安排自在掌握。

中斷對性能的影響有多大?在 x86 體系結構中,一次中斷處理需要將 CPU 的狀態寄存器保存到堆棧,並運行中斷服務程序,最後再將保存的狀態寄存器信息從堆棧中恢復。整個過程需要至少 300 個處理器時鐘週期。

輪詢對性能的提升有多大?網卡收到報文後,可以藉助 DDIO(Direct Data I/O)技術直接將報文保存到 CPU 的 Cache 中,或者保存到內存中(沒有 DDIO 技術的情況下),並設置報文到達的標誌位。應用程序則可以週期性地輪詢報文到達的標誌位,檢測是否有新報文需要處理。整個過程中完全沒有中斷處理過程,因此應用程序的網絡報文處理能力得以極大提升。

故此,想要 CPU 執行始終高效,就必然需要一個內核線程去主動 Poll(輪詢)網卡,而這種行爲與當前的內核協議棧是不相容的,即便當前內核協議棧可以使用 NAPI 中斷+輪詢的方式,但依舊沒有根本上解決問題。除非再重新實現一套全新的內核協議棧,顯然這並不現實,但幸運的是,我們可以在用戶態實現這一點。

針對 Intel 網卡,DPDK 實現了基於輪詢方式的 PMD(Poll Mode Drivers)網卡驅動。該驅動由用戶態的 API 以及 PMD Driver 構成,內核態的 UIO Driver 屏蔽了網卡發出的中斷信號,然後由用戶態的 PMD Driver 採用主動輪詢的方式。除了鏈路狀態通知仍必須採用中斷方式以外,均使用無中斷方式直接操作網卡設備的接收和發送隊列。

PMD Driver 從網卡上接收到數據包後,會直接通過 DMA 方式傳輸到預分配的內存中,同時更新無鎖環形隊列中的數據包指針,不斷輪詢的應用程序很快就能感知收到數據包,並在預分配的內存地址上直接處理數據包,這個過程非常簡潔。

PMD 極大提升了網卡 I/O 性能。此外,PMD 還同時支持物理和虛擬兩種網絡接口,支持 Intel、Cisco、Broadcom、Mellanox、Chelsio 等整個行業生態系統的網卡設備,以及支持基於 KVM、VMware、 Xen 等虛擬化網絡接口。PMD 實現了 Intel 1GbE、10GbE 和 40GbE 網卡下基於輪詢收發包。

UIO+PMD,前者旁路了內核,後者主動輪詢避免了硬中斷,DPDK 從而可以在用戶態進行收發包的處理。帶來了零拷貝(Zero Copy)、無系統調用(System call)的優化。同時,還避免了軟中斷的異步處理,也減少了上下文切換帶來的 Cache Miss。

值得注意的是,運行在 PMD 的 Core 會處於用戶態 CPU 100% 的狀態,如下圖:
在這裏插入圖片描述
由於,網絡空閒時 CPU 會長期處於空轉狀態,帶來了電力能耗的問題。所以,DPDK 引入了 Interrupt DPDK(中斷 DPDK)模式。Interrupt DPDK 的原理和 NAPI 很像,就是 PMD 在沒數據包需要處理時自動進入睡眠,改爲中斷通知,接收到收包中斷信號後,激活主動輪詢。這就是所謂的鏈路狀態中斷通知。並且 Interrupt DPDK 還可以和其他進程共享一個 CPU Core,但 DPDK 進程仍具有更高的調度優先級。

在這裏插入圖片描述

PMD 與 UIP 的交互實現

對於 PMD 的實現來說,重點是處於用戶態的 PMD 驅動程序如何通過 igb_uio 內核驅動模塊與 UIO 進行交互,從而實現數據包處理的內核旁路。

  1. 調用 igbuio_setup_bars,設置 uio_info 的 uio_mem 和 uio_port。這一步驟的細節在前文中已有提到,igb_uio 內核模塊在發現了 PCI 設備的 Memory BAR 和 IO BAR 之後會將這些 resources 的信息保存到 uioX 設備的 maps 中,這樣處於用戶態的 PMD 就可以訪問這些原本只能被內核訪問的 BAR 空間了。
    在這裏插入圖片描述
  2. 設置 uio_info 的其他成員。
    在這裏插入圖片描述
  3. 調用 uio_register_device,註冊 uioX 設備。PMD 通過 uioX 設備與 igb_uio 內核驅動模塊進行交互。

在這裏插入圖片描述

  1. 打開 uioX 設備,應用層已經可以使用 uioX 設備了。DPDK 的應用層代碼,會打開 uioX 設備。在函數 pci_uio_alloc_resource 中。
    在這裏插入圖片描述

打開對應的 uioX 設備時,對應的內核操作爲 uio_open(),其又會調用 igb_uio 的 open(),流程圖如下:

在這裏插入圖片描述

  1. 設置中斷信息,igb_uio 默認的中斷模式爲 RTE_INTR_MODE_MSIX,在 igbuio_pci_enable_interrupts 的關鍵代碼如下:
    在這裏插入圖片描述
  2. 註冊中斷。當打開 uio 設備時,igb_uio 就會註冊了一箇中斷。
    在這裏插入圖片描述
    那麼爲什麼作爲輪詢模式的 PMD 驅動需要註冊中斷呢?這是因爲,即使應用層可以通過 UIO 來實現設備驅動,但是設備的某些事件還是需要內核進行響應,然後通知應用層的。當然,PMD 的中斷處理已經非常簡單了。

在這裏插入圖片描述
其中的關鍵步驟是調用 uio_event_notify。

在這裏插入圖片描述

這個函數很簡單:

  1. 增加 uioX 設備的 “事件” 數;
  2. 喚醒在 idev->wait 等待隊列中的 task;
  3. 使用信號異步通知 async_queue 隊列中的進程;

注意,因爲目前 DPDK 沒有使用異步 IO 的方式,所有對於 DPDK PMD 來說,只有前兩個語句有用。

  1. 調用 UIO 的 mmap(),將註冊的 uioX 設備的 “內存空間” 映射到應用空間,讓 PMD 得以真正的從用戶態中去訪問內存。其 mmap 函數爲 uio_mmap(),關鍵代碼如下:
    在這裏插入圖片描述

至此,UIO 框架和 igb_uio 內核模塊已經可以讓 PMD 訪問 PCI 設備的大部分資源了。

PMD 的應用層實現

當 DPDK Application 啓動時,會首先進行 EAL 初始化,如下圖:

在這裏插入圖片描述

在 pci_uio_alloc_resource 中,主要是打開 DPDK Application 要管理的 uioX 設備。
在這裏插入圖片描述

同時,DPDK App 還需要把 PCI 設備的 BAR 映射到應用層。在 pci_uio_map_resource() 中,除了調用上圖中的 pci_uio_alloc_resource,還會調用 pci_uio_map_resource_by_index 做資源映射。

在這裏插入圖片描述

下面就是 PMD 在應用層的驅動實現了。以最簡單的 e1000 驅動爲例,其初始化函數 eth_igb_dev_init 如下。

在這裏插入圖片描述

上面我們提到了,當 uioX 設備有事件觸發時,由 eth_igb_interrupt_handler() 負責處理,實現了用戶態的中斷處理。

在這裏插入圖片描述
eth_igb_interrupt_handler 的實現非常簡單,只是處理設備的狀態變化事件,如:Link Status。

接下來,就是最重要的了,PMD 如何讀取網卡數據。DPDK App 會調用 rte_eth_rx_burst 讀取數據報文。

在這裏插入圖片描述

在這個函數中,會調用驅動 dev->rx_pkt_burst 來做實際的操作。以 e1000 爲例,即 eth_igb_recv_pkts。

在這裏插入圖片描述

在這裏插入圖片描述

這裏的實現很簡單。如果網卡接收 buffer descriptor 表示已經完成一個報文的接收,有 E1000_RXD_STAT_DD 標誌,則 rte_mbuf_raw_alloc 一個 mbuf,進行處理。如果沒有報文,直接跳出循環。

對應 RTC 模型的 DPDK App 來說,就是不斷的調用 rte_eth_rx_burst 去 “詢問” 網卡是否有新的報文。如果有,就取走所有的報文或達到參數 nb_pkts 的上限。然後進行報文處理,處理完畢,再次循環。

參考文章

https://cloud.tencent.com/developer/article/1411982

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章