詳細解讀 DPDK 的收發包流程
一、 前言
DPDK 是 intel 工程師開發的一款用來快速處理數據包的框架,最初的目的是爲了證明傳統網絡數據包處理性能低不是 intel 處理器導致的,而是傳統數據的處理流程導致,後來隨着 dpdk 的開源及其生態的快速發展,dpdk 成爲了高性能網絡數據處理的優秀框架。本篇文章主要介紹 DPDK 接收與發送報文的流程,包括 CPU 與網卡 DMA 協同工作的整個交互流程、數據包在內存、CPU、網卡之間遊走的過程。
二、場景
DPDK 從 2013 開始開源,經過前輩們的縫縫補補到現在爲止 DPDK 框架比較成熟、使用比較方便,使得現在開發者在不需要深入瞭解底層數據包收發原理的情況下也可以做簡單的項目開發。但是個人感覺做簡單的項目尚且可以應付,如果需要做性能優化等類似的需求時就需要取全面的瞭解 DPDK 的收發包機制,因爲收發包性能與驅動工作流程、前期初始化配置息息相關。話不多說,下面我們進入正題。
三、收發包處理流程
數據包接收的大體流程:
-
數據包到達網卡
-
網卡經過 DMA 操作將數據包從網卡拷貝到收包隊列
-
DPDK 應用從收包隊列中取包
數據包發送的大體流程:
-
DPDK 應用將數據包送到發包隊列
-
網卡經過 DMA 操作將網卡隊列中的數據包拷貝到網卡
-
數據包從網卡發出
收發包流程中的關鍵操作,主要是網卡如何與 DPDK 應用交互:
-
網卡的初始化配置操作有哪些,因爲網卡要想正常工作肯定需要進行初始化配置一些屬性
-
網卡的 DMA 操作怎麼找到 DMA 地址,進而將數據包拷貝到系統主存供 DPDK 讀取
-
網卡把數據包成功放到隊列後如何通知 DPDK 應用去隊列中讀取
-
DPDK 從隊列中取完數據包後需要做哪些操作通知網卡爲下一次收包做準備
-
DPDK 將數據包送到發送隊列中需要做哪些預操作
-
網卡從發送隊列中取包需要做哪些操作
四、收包軟件處理流程
DPDk 在初始化階段通過 igb_alloc_rx_queue_mbufs 負責將描述符,mbuf, dma, 接收隊列給關聯起來,如下圖所示。
1、模塊 / 硬件介紹
Network interface: 指以太網卡,它工作在 OSI 的下兩層(物理層、數據鏈路層),工作在物理層的芯片稱爲 PHY,工作在數據鏈路層的芯片稱爲 MAC 控制器,即 Media Access Control, 即媒體訪問控制子層協議. 該協議位於 OSI 七層協議中數據鏈路層的下半部分,但是目前好多網卡是將 MAC 和 PHY 功能做到了一顆芯片中,但是 MAC 和 PHY 的機制還是單獨存在的,只是對外體現爲一顆芯片。MAC 控制器的功能主要是數據幀的構建、數據差錯檢查、傳送控制、向網絡層提供標準的數據接口等功能;PHY 芯片的主要功能是將從 PHY 來的並行數據轉換爲穿行流數據,再按照物理層的編碼規則把數字信號進行編碼,最後再轉換爲模擬信號把數據發出去。
RX_FIFO: 數據接收緩衝區
TX_FIFO: 數據發送緩衝區
DMA Engine:Direct Memory Access,即直接寄存器訪問,是一種告訴的數據傳輸方式,允許在外部設備和存儲器之間直接讀寫數據,數據的讀寫不消耗 CPU 資源,DMA 控制器通過一組描述符環形隊列與 CPU 互相操作完成數據包的收發。CPU 通過操作 DMA 寄存器來與 DMA 控制器進行部分通信與初始化配置,主要寄存器有 Base、Size、Tail、Head,head 寄存器用於 DMA 往 rx_ring 裏插入時使用,tail 是應用通過寫寄存器通知給 DMA 控制器當前可用的最後一個描述符(head->next 爲 tail 時表示當前 rx_ring 存滿了,再來報文會被記錄 rx_missed_error)。
Rx_queue: 收包隊列結構體:我們主要關注兩個環形隊列 rx_ring、sw_ring
Rx_ring:一個地址連續環形隊列,存儲的是描述符,描述符中包含將來存放數據包的物理地址、DD 標誌(下面會介紹 DD 標誌)等,上面圖中只畫了存放數據包的物理地址,物理地址供網卡 DMA 模塊使用,也稱爲 DMA 地址(硬件使用物理地址,當網卡收到數據包後會通過 DMA 操作將數據包拷貝到物理地址,物理地址是通過虛擬地址轉換得到的,下面分析源碼時會介紹)
Sw_ring: 存儲的是將來存放數據包的虛擬地址,虛擬地址供應用使用(軟件應用使用虛擬地址,應用往虛擬地址讀寫數據包)
DD 標誌:用於標識一個描述符 buf 是否可用,無論網卡是工作在輪詢方式下還是中斷方式,判斷數據包接收成功或者是否發送成功都需要檢查描述符中的完成狀態位(Description Done)DD,該狀態位由 DMA 控制器在完成操作後進行回寫
Mbuf:對應於 Mbuf 內存池中的元素,通過 alloc 或者 free 操作內存池獲取或者釋放 mbuf 對象,這裏需要說的一點是 mbuf 池創建的時候是連續的,但是 rx_ring 和 sw_ring 裏指向的數據地址不一定是連續的,下面分析收包流程時會介紹
PCIE 總線:採用高速串行通信互聯標準,自上而下分爲事務傳輸層、數據鏈路層、物理層,網卡與 CPU 之間數據包的傳輸、CPU 對網卡寄存器的 MMIO 操作都通過 PCIE 進行傳輸
DMA 寄存器:CPU 配置網卡的操作通過操作網卡的寄存器,寄存器主要
2、收包流程
Rx_ring 收數據時的狀態如下:
DMA 控制器收到數據後往 head 寫,當 head=tail 時表示當前隊列爲空,head->bext = tail 表示當前隊列已存滿,dpdk 啓動剛初始化完後如下圖所示:
可以看出來 cpu 對 tail 寄存器的更新並不是在 rx_ring 描述符中填充完 dma 地址後立馬就執行,而是等 dma 可用描述符低於一定閾值時才執行寫寄存器更新 tail
具體詳細流程如下:
(1)CPU 填緩衝地址(mbuf 中的 data)到收接收側描述符(在 dpdk 初始化時就會第一次填充),也就是上圖中 rx_ring 會指向 mbuf 池中的 部分 mbuf 用於接收數據包;另外 CPU 通過操作網卡的 base、size 寄存器,將 rx_ring 環形隊列的起始地址和內存卡大小告訴給 DMA 控制器,將描述符隊列的物理地址寫入到寄存器後,dma 通過讀這個寄存器就知道了描述符隊列的地址,進而 dma 收到報文後,會將報文保存到描述符指向的 mbuf 空間
(2)網卡讀取 rx_ring 隊列裏接收側的描述符進而獲取系統緩衝區地址
(3)從外部到達網卡的報文數據先存儲到網卡本地的 RX_FIFO 緩衝區
(4)DMA 通過 PCIE 總線將報文數據寫到系統的緩衝區地址
(5)網卡回寫 rx_ring 接收側描述符的更新狀態 DD 標誌,置 1 表示接收完畢
(6)CPU 讀取描述符 sw_ring 隊列中元素的 DD 狀態,如果爲 1 則表示網卡已經接收完畢,應用可以讀取數據包
(7)CPU 從 sw_ring 中讀取完數據包後有個 “狸貓換太子” 的動作,重新從 mbuf 池中申請一個 mbuf 替換到 sw_ring 的該描述符中 將新分配的 mbuf 虛擬地址轉換爲物理地址更新到 rx_ring 中該條目位置的 dma 物理地址、更新描述符 rx_ring 隊列裏的 DD 標誌置 0,這樣網卡就可以持續往 rx_ring 緩衝區寫數據了
(8)CPU 判斷 rx_ring 裏可用描述符小於配置的閾值時更新 tail 寄存器,而不是回填一個 mbuf 到描述符就更新下 tail 寄存器(因爲 CPU 高頻率的操作寄存器是性能的殺手,所以改用此機制)
(9)至此,應用接收數據包完畢
注意:這裏有兩個非常關鍵的隊列 rx_ring、sw_ring,rx_ring 描述符裏存放的是 mbuf 裏 data 區的起始物理地址供 DMA 控制器收到報文後往該地址寫入(硬件 DMA 直接操作物理地址,不需要 cpu 參與);sw_ring 描述符裏存放的是 mbuf 的起始虛擬地址供應用讀取數據包
3、代碼的實現過程
我們以 e1000 驅動類型網卡舉例,來分析數據包接收相關的軟件結構及接口
(1)接收隊列的結構如下圖,圖中標出了兩個重要的環形隊列(上面已介紹):
(2)數據包的軟件接收流程如下圖
(3)分析下 eth_igb_recv_pkts 接口源碼,與第四章的收包內部流程相對應
五、發包軟件處理流程
發包流程與第四章的收包流程有些區別,DPDk 在初始化階段不會從 mbuf 池中獲取緩衝區插到描述符隊列空間裏,只有在真正的發送過程中才會從 mbuf 池中獲取 mbuf,然後插入到描述符隊列空間裏。
1、模塊 / 硬件介紹
Tx_queue: 發包隊列結構體:我們主要關注兩個環形隊列 tx_ring、sw_ring
Tx_ring:一個地址連續環形隊列,存儲的是描述符,描述符中包含將來存放數據包的物理地址、DD 標誌(下面會介紹 DD 標誌)等,上面圖中只畫了存放數據包的物理地址,物理地址供網卡 DMA 模塊使用,也稱爲 DMA 地址(硬件使用物理地址,當網卡收到數據包後會通過 DMA 操作將數據包拷貝到物理地址,物理地址是通過虛擬地址轉換得到的,下面分析源碼時會介紹)
2、發包流程
Tx_ring 發數據時的狀態如下:
dpdk 初始化完成時 tx_ring 的隊列爲空
DMA 控制器通過 head 去判斷當前的 DD 狀態,如果爲 0 則可以執行發送動作
CPU 需要對 tx_ring 上網卡發送成功的描述符的緩存空間進行釋放操作,待應用下次繼續寫入
(1)CPU 讀取發送側描述符 tx_ring 隊列,檢查 DD 標誌是否爲 1,爲 1 則說明發送完畢
(2)針對發送完畢的描述符需要釋放該描述符裏對應的緩衝區
(3)CPU 將準備發送的緩衝區 mbuf 的虛擬地址填充到描述符 sw_ring
(4)CPU 通過將準備發送的緩衝區 mbuf 的虛擬地址轉換得到該 mbuf 裏 data 數據部分的物理地址填充到發送測描述符 tx_ring 隊列中,並將 DD 標誌清 0
(5)DMA 控制器讀取 base 寄存器,獲取發送側描述符,根據發送測描述符獲取 tx_ring 隊列地址,讀取 head 指針裏的元素,判斷 DD 標誌釋放爲 0 則從描述符中獲取數據緩存區地址,通過 PCIE 總線將數據拷貝到網卡硬件 Tx_FIFO 緩存中往外發送數據
(6)DMA 控制器回寫該描述符中隊列裏 DD 的標誌置 1,通知 CPU 該緩存中數據已成功發送
(7)至此,應用發送數據包完畢
注意:這裏有兩個非常關鍵的隊列 tx_ring、sw_ring,tx_ring 描述符裏存放的是 mbuf 裏 data 區的起始物理地址供 DMA 控制器讀取報文(硬件 DMA 直接操作物理地址,不需要 cpu 參與);sw_ring 描述符裏存放的是 mbuf 的起始虛擬地址供應用寫入數據包
3、代碼的實現過程
我們以 e1000 驅動類型網卡舉例,來分析數據包發送相關的軟件結構及接口
(1)發送隊列的結構如下圖,圖中標出了兩個重要的環形隊列(上面已介紹):
(2)數據包的軟件發送流程如下圖
(3) eth_igb_xmit_pkts 接口源碼,發包邏輯參考上面小節,這裏不做補充了
六 、總結
到目前位置分析完了數據包在網卡、CPU、主存上游走的流程,可以看到 DPDK 收發包的流程設計的還是比較震撼的,充分考慮到了高性能收發數據,其中一些處理流程、數據結構的設計經驗等值得我們在平時的開發項目中去運用和學習。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/f8iSTk4lyDaHy0jK3Dz7fw