Linux多隊(duì)列網(wǎng)卡的硬件的實(shí)現(xiàn)詳解

多隊(duì)列網(wǎng)卡是一種技術(shù),最初是用來解決網(wǎng)絡(luò)IO QoS (quality of service)問題的,后來隨著網(wǎng)絡(luò)IO的帶寬的不斷提升,單核CPU不能完全處滿足網(wǎng)卡的需求,通過多隊(duì)列網(wǎng)卡驅(qū)動(dòng)的支持,將各個(gè)隊(duì)列通過中斷綁定到不同的核上,以滿足網(wǎng)卡的需求。
常見的有Intel的82575、82576,Boardcom的57711等,下面以公司的服務(wù)器使用較多的Intel 82575網(wǎng)卡為例,分析一下多隊(duì)列網(wǎng)卡的硬件的實(shí)現(xiàn)以及l(fā)inux內(nèi)核軟件的支持。
1.多隊(duì)列網(wǎng)卡硬件實(shí)現(xiàn)
圖1.1是Intel 82575硬件邏輯圖,有四個(gè)硬件隊(duì)列。當(dāng)收到報(bào)文時(shí),通過hash包頭的SIP、Sport、DIP、Dport四元組,將一條流總是收到相同的隊(duì)列。同時(shí)觸發(fā)與該隊(duì)列綁定的中斷。
圖1.1 82575硬件邏輯圖
2. 2.6.21以前網(wǎng)卡驅(qū)動(dòng)實(shí)現(xiàn)
kernel從2.6.21之前不支持多隊(duì)列特性,一個(gè)網(wǎng)卡只能申請一個(gè)中斷號(hào),因此同一個(gè)時(shí)刻只有一個(gè)核在處理網(wǎng)卡收到的包。如圖2.1,協(xié)議棧通過NAPI輪詢收取各個(gè)硬件queue中的報(bào)文到圖2.2的net_device數(shù)據(jù)結(jié)構(gòu)中,通過QDisc隊(duì)列將報(bào)文發(fā)送到網(wǎng)卡。
圖2.1 2.6.21之前內(nèi)核協(xié)議棧
圖2.2 2.6.21之前net_device
3. 2.6.21后網(wǎng)卡驅(qū)動(dòng)實(shí)現(xiàn)
2.6.21開始支持多隊(duì)列特性,當(dāng)網(wǎng)卡驅(qū)動(dòng)加載時(shí),通過獲取的網(wǎng)卡型號(hào),得到網(wǎng)卡的硬件queue的數(shù)量,并結(jié)合CPU核的數(shù)量,最終通過Sum=Min(網(wǎng)卡queue,CPU core)得出所要激活的網(wǎng)卡queue數(shù)量(Sum),并申請Sum個(gè)中斷號(hào),分配給激活的各個(gè)queue。
如圖3.1,當(dāng)某個(gè)queue收到報(bào)文時(shí),觸發(fā)相應(yīng)的中斷,收到中斷的核,將該任務(wù)加入到協(xié)議棧負(fù)責(zé)收包的該核的NET_RX_SOFTIRQ隊(duì)列中(NET_RX_SOFTIRQ在每個(gè)核上都有一個(gè)實(shí)例),在NET_RX_SOFTIRQ中,調(diào)用NAPI的收包接口,將報(bào)文收到CPU中如圖3.2的有多個(gè)netdev_queue的net_device數(shù)據(jù)結(jié)構(gòu)中。
這樣,CPU的各個(gè)核可以并發(fā)的收包,就不會(huì)應(yīng)為一個(gè)核不能滿足需求,導(dǎo)致網(wǎng)絡(luò)IO性能下降。
圖3.1 2.6.21之后內(nèi)核協(xié)議棧
圖3.2 2.6.21之后net_device
4.中斷綁定
當(dāng)CPU可以平行收包時(shí),就會(huì)出現(xiàn)不同的核收取了同一個(gè)queue的報(bào)文,這就會(huì)產(chǎn)生報(bào)文亂序的問題,解決方法是將一個(gè)queue的中斷綁定到唯一的一個(gè)核上去,從而避免了亂序問題。同時(shí)如果網(wǎng)絡(luò)流量大的時(shí)候,可以將軟中斷均勻的分散到各個(gè)核上,避免CPU成為瓶頸。
圖4.1 /proc/interrupts
5.中斷親合糾正
一些多隊(duì)列網(wǎng)卡驅(qū)動(dòng)實(shí)現(xiàn)的不是太好,在初始化后會(huì)出現(xiàn)圖4.1中同一個(gè)隊(duì)列的tx、rx中斷綁定到不同核上的問題,這樣數(shù)據(jù)在core0與core1之間流動(dòng),導(dǎo)致核間數(shù)據(jù)交互加大,cache命中率降低,降低了效率。
圖5.1 不合理中斷綁定
linux network子系統(tǒng)的負(fù)責(zé)人David Miller提供了一個(gè)腳本,首先檢索/proc/interrupts文件中的信息,按照圖4.1中eth0-rx-0($VEC)中的VEC得出中斷MASK,并將MASK
寫入中斷號(hào)53對(duì)應(yīng)的smp_affinity中。由于eth-rx-0與eth-tx-0的VEC相同,實(shí)現(xiàn)同一個(gè)queue的tx與rx中斷綁定到一個(gè)核上,如圖4.3所示。
圖4.2 set_irq_affinity
圖4.3 合理的中斷綁定
set_irq_affinity腳本位于http://mirror.oa.com/tlinux/tools/set_irq_affinity.sh。
6.多隊(duì)列網(wǎng)卡識(shí)別
#lspci -vvv
Ethernet controller的條目內(nèi)容,如果有MSI-X && Enable+ && TabSize > 1,則該網(wǎng)卡是多隊(duì)列網(wǎng)卡,如圖4.4所示。
圖4.4 lspci內(nèi)容
Message Signaled Interrupts(MSI)是PCI規(guī)范的一個(gè)實(shí)現(xiàn),可以突破CPU 256條interrupt的限制,使每個(gè)設(shè)備具有多個(gè)中斷線變成可能,多隊(duì)列網(wǎng)卡驅(qū)動(dòng)給每個(gè)queue申請了MSI。MSI-X是MSI數(shù)組,Enable+指使能,TabSize是數(shù)組大小。
# setting up irq affinity according to /proc/interrupts
# 2008-11-25 Robert Olsson
# 2009-02-19 updated by Jesse Brandeburg
#
# > Dave Miller:
# (To get consistent naming in /proc/interrups)
# I would suggest that people use something like:
# char buf[IFNAMSIZ+6];
#
# sprintf(buf, "%s-%s-%d",
# netdev->name,
# (RX_INTERRUPT ? "rx" : "tx"),
# queue->index);
#
# Assuming a device with two RX and TX queues.
# This script will assign:
#
# eth0-rx-0 CPU0
# eth0-rx-1 CPU1
# eth0-tx-0 CPU0
# eth0-tx-1 CPU1
#
set_affinity()
{
MASK=$((1<<$VEC))
printf "%s mask=%X for /proc/irq/%d/smp_affinity\n" $DEV $MASK $IRQ
printf "%X" $MASK > /proc/irq/$IRQ/smp_affinity
#echo $DEV mask=$MASK for /proc/irq/$IRQ/smp_affinity
#echo $MASK > /proc/irq/$IRQ/smp_affinity
}
if [ "$1" = "" ] ; then
echo "Description:"
echo " This script attempts to bind each queue of a multi-queue NIC"
echo " to the same numbered core, ie tx0|rx0 --> cpu0, tx1|rx1 --> cpu1"
echo "usage:"
echo " $0 eth0 [eth1 eth2 eth3]"
fi
# check for irqbalance running
IRQBALANCE_ON=`ps ax | grep -v grep | grep -q irqbalance; echo $?`
if [ "$IRQBALANCE_ON" == "0" ] ; then
echo " WARNING: irqbalance is running and will"
echo " likely override this script's affinitization."
echo " Please stop the irqbalance service and/or execute"
echo " 'killall irqbalance'"
fi
#
# Set up the desired devices.
#
for DEV in $*
do
for DIR in rx tx TxRx
do
MAX=`grep $DEV-$DIR /proc/interrupts | wc -l`
if [ "$MAX" == "0" ] ; then
MAX=`egrep -i "$DEV:.*$DIR" /proc/interrupts | wc -l`
fi
if [ "$MAX" == "0" ] ; then
echo no $DIR vectors found on $DEV
continue
#exit 1
fi
for VEC in `seq 0 1 $MAX`
do
IRQ=`cat /proc/interrupts | grep -i $DEV-$DIR-$VEC"$" | cut -d: -f1 | sed "s/ //g"`
if [ -n "$IRQ" ]; then
set_affinity
else
IRQ=`cat /proc/interrupts | egrep -i $DEV:v$VEC-$DIR"$" | cut -d: -f1 | sed "s/ //g"`
if [ -n "$IRQ" ]; then
set_affinity
fi
fi
done
done
done
相關(guān)文章
- Linux多個(gè)網(wǎng)卡怎么添加永久路由?當(dāng)Linux系統(tǒng)中有多個(gè)網(wǎng)卡的時(shí)候,我們需要為另外的一些網(wǎng)卡添加路由,才能發(fā)揮網(wǎng)卡的實(shí)際作用,下面我們就來看看詳細(xì)的教程,需要的朋友可2016-08-30
Linux系統(tǒng)基礎(chǔ)筆記之網(wǎng)卡安裝一般步驟簡介
這篇文章主要介紹了Linux系統(tǒng)基礎(chǔ)筆記之網(wǎng)卡安裝一般步驟簡介,需要的朋友可以參考下2016-05-17Linux系統(tǒng)中配置和獲取無線網(wǎng)卡信息的命令用法
這篇文章主要介紹了Linux系統(tǒng)中配置和獲取無線網(wǎng)卡信息的命令用法,分別介紹了iwpriv和iwlist的使用,需要的朋友可以參考下2016-01-18Linux系統(tǒng)復(fù)制vmware 虛擬機(jī)后網(wǎng)卡不能啟動(dòng)該怎么辦?
Linux系統(tǒng)復(fù)制vmware 虛擬機(jī)后網(wǎng)卡不能啟動(dòng)該怎么辦?Linux系統(tǒng)復(fù)制虛擬機(jī)后發(fā)現(xiàn)Linux系統(tǒng)中的網(wǎng)卡不能啟動(dòng)了,沒辦法上網(wǎng),出現(xiàn)這個(gè)問題是因?yàn)榫W(wǎng)卡配置文件中的mac地址變2015-12-14linux下安裝編譯網(wǎng)卡驅(qū)動(dòng)的方法
這篇文章主要介紹了linux下安裝編譯網(wǎng)卡驅(qū)動(dòng)的方法,需要的朋友可以參考下2015-09-03linux系統(tǒng)怎么重啟網(wǎng)卡?linux重啟網(wǎng)卡的三種教程
linux系統(tǒng)怎么重啟網(wǎng)卡?在實(shí)際工作中,經(jīng)常會(huì)遇到Linux系統(tǒng)進(jìn)行重啟網(wǎng)卡的操作。在這里整理一下,進(jìn)行多種方法的網(wǎng)卡重啟,需要的朋友可以參考下2015-07-10在Linux系統(tǒng)下實(shí)現(xiàn)雙網(wǎng)卡綁定的教程
這篇文章主要介紹了在Linux系統(tǒng)下實(shí)現(xiàn)雙網(wǎng)卡綁定的教程,本文以CentOS系統(tǒng)為環(huán)境進(jìn)行演示,需要的朋友可以參考下2015-06-10新裝Linux系統(tǒng)沒有網(wǎng)卡驅(qū)動(dòng)的解決辦法和步驟
這篇文章主要介紹了新裝Linux系統(tǒng)沒有網(wǎng)卡驅(qū)動(dòng)的解決辦法和步驟,本文先是補(bǔ)充了幾個(gè)需要用到的命令相關(guān)知識(shí),然后給出了詳解解決步驟,需要的朋友可以參考下2015-05-08- 這篇文章主要介紹了Linux下配置vlan網(wǎng)卡教程,本文使用7步完成vlan網(wǎng)卡設(shè)置,需要的朋友可以參考下2015-02-09
Linux下簡單限制網(wǎng)卡帶寬的設(shè)置方法
Linux下限制網(wǎng)卡的帶寬,可用來模擬服務(wù)器帶寬耗盡,從而測試服務(wù)器在此時(shí)的訪問效果2013-02-13