https://tonydeng.github.io/sdn-handbook/dpdk/queue.html
網卡多隊列,顧名思義,也就是傳統網卡的DMA
隊列有多個,網卡有基於多個DMA
隊列的分配機制。多隊列網卡已經是當前高速率網卡的主流。
RPS
圖片來源RPS/RFS
Linux
內核中,RPS
(Receive Packet Steering
)在接收端提供了這樣的機制。RPS主要是把軟中斷的負載均衡到CPU
的各個core
上,網卡驅動對每個流生成一個hash
標識,這個hash
值可以通過四元組(源IP地址SIP
,源四層端口SPOR
T,目的IP地址DIP
,目的四層端口DPORT
)來計算,然后由中斷處理的地方根據這個hash
標識分配到相應的core
上去,這樣就可以比較充分地發揮多核的能力了。
DPDK多隊列支持
DPDK Packet I/O
機制具有與生俱來的多隊列支持功能,可以根據不同的平台或者需求,選擇需要使用的隊列數目,並可以很方便地使用隊列,指定隊列發送或接收報文。由於這樣的特性,可以很容易實現CPU
核、緩存與網卡隊列之間的親和性,從而達到很好的性能。從DPDK
的典型應用l3fwd
可以看出,在某個核上運行的程序從指定的隊列上接收,往指定的隊列上發送,可以達到很高的cache命中率,效率也就會高。
除了方便地做到對指定隊列進行收發包操作外,DPDK的隊列管理機制還可以避免多核處理器中的多個收發進程采用自旋鎖產生的不必要等待。
以run to completion
模型為例,可以從核、內存與網卡隊列之間的關系來理解DPDK是如何利用網卡多隊列技術帶來性能的提升。
- 將網卡的某個接收隊列分配給某個核,從該隊列中收到的所有報文都應當在該指定的核上處理結束。
- 從核對應的本地存儲中分配內存池,接收報文和對應的報文描述符都位於該內存池。
- 為每個核分配一個單獨的發送隊列,發送報文和對應的報文描述符都位於該核和發送隊列對應的本地內存池中。
可以看出不同的核,操作的是不同的隊列,從而避免了多個線程同時訪問一個隊列帶來的鎖的開銷。但是,如果邏輯核的數目大於每個接口上所含的發送隊列的數目,那么就需要有機制將隊列分配給這些核。不論采用何種策略,都需要引入鎖來保護這些隊列的數據。
網卡是如何將網絡中的報文分發到不同的隊列呢?常用的方法有微軟提出的RSS與英特爾提出的Flow Director
技術,前者是根據哈希值希望均勻地將包分發到多個隊列中。后者是基於查找的精確匹配,將包分發到指定的隊列中。此外,網卡還可以根據優先級分配隊列提供對QoS
的支持。
流分類
高級的網卡設備(比如Intel XL710
)可以分析出包的類型,包的類型會攜帶在接收描述符中,應用程序可以根據描述符快速地確定包是哪種類型的包。DPDK
的Mbuf
結構中含有相應的字段來表示網卡分析出的包的類型。
RSS(Receive-Side Scaling,接收方擴展)
RSS
就是根據關鍵字通過哈希函數計算出哈希值,再由哈希值確定隊列。
關鍵字是如何確定的呢?
哈希函數一般選取微軟托普利茲算法(Microsoft Toeplitz Based Hash
)或者對稱哈希。
Flow Director
Flow Director
技術是Intel
公司提出的根據包的字段精確匹配,將其分配到某個特定隊列的技術:網卡上存儲了一個Flow Director
的表,表的大小受硬件資源限制,它記錄了需要匹配字段的關鍵字及匹配后的動作;驅動負責操作這張表,包括初始化、增加表項、刪除表項;網卡從線上收到數據包后根據關鍵字查Flow Director
的這張表,匹配后按照表項中的動作處理,可以是分配隊列、丟棄等。
相比RSS
的負載分擔功能,它更加強調特定性。比如,用戶可以為某幾個特定的TCP對話(S-IP+D-IP+S-Port+D-Port
)預留某個隊列,那么處理這些TCP
對話的應用就可以只關心這個特定的隊列,從而省去了CPU
過濾數據包的開銷,並且可以提高cache
的命中率。
服務質量
多隊列應用於服務質量(QoS
)流量類別:把發送隊列分配給不同的流量類別,可以讓網卡在發送側做調度;把收包隊列分配給不同的流量類別,可以做到基於流的限速。
流過濾
來自外部的數據包哪些是本地的、可以被接收的,哪些是不可以被接收的?可以被接收的數據包會被網卡送到主機或者網卡內置的管理控制器,其過濾主要集中在以太網的二層功能,包括VLAN
及MAC
過濾。
應用
針對Intel®XL710
網卡,PF使用i40e Linux Kernel
驅動,VF
使用DPDK i40e PMD
驅動。使用Linux
的Ethtool
工具,可以完成配置操作cloud filter
,將大量的數據包直接分配到VF
的隊列中,交由運行在VF
上的虛機應用來直接處理。
echo 1 > /sys/bus/pci/devices/0000:02:00.0/sriov_numvfs modprobe pci-stub echo "8086 154c" > /sys/bus/pci/drivers/pci-stub/new_id echo 0000:02:02.0 > /sys/bus/pci/devices/0000:2:02.0/driver/unbind echo 0000:02:02.0 > /sys/bus/pci/drivers/pci-stub/bind qemu-system-x86_64 -name vm0 -enable-kvm -cpu host -m 2048 -smp 4 -drive file=dpdk-vm0.img -vnc :4 -device pci-assign,host=02:02.0 ethtool -N ethx flow-type ip4 dst-ip 2.2.2.2 user-def 0xffffffff00000000 action 2 loc 1
參考