NAPI技術(shù)在Linux網(wǎng)絡(luò)驅(qū)動(dòng)上的應(yīng)用和完善_第1頁(yè)
NAPI技術(shù)在Linux網(wǎng)絡(luò)驅(qū)動(dòng)上的應(yīng)用和完善_第2頁(yè)
NAPI技術(shù)在Linux網(wǎng)絡(luò)驅(qū)動(dòng)上的應(yīng)用和完善_第3頁(yè)
已閱讀5頁(yè),還剩14頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、NAPI 技術(shù)在 Linux 網(wǎng)絡(luò)驅(qū)動(dòng)上的應(yīng)用和完善前言:NAPI 是 Linux 上采納的一種提升網(wǎng)絡(luò)處理效率的技術(shù),它的核 心概念確實(shí)是不采納中斷的方式讀取數(shù)據(jù),而代之以第一采納中斷喚醒數(shù) 據(jù)接收的服務(wù)程序,然后 POLL 的方法來(lái)輪詢數(shù)據(jù), (類似于底半( botto m-half)處理模式);從我們?cè)趯?shí)驗(yàn)中所得到的數(shù)據(jù)來(lái)看,在隨著網(wǎng)絡(luò)的接 收速度的增加, NIC 觸發(fā)的中斷能做到持續(xù)減少,目前 NAPI 技術(shù)差不多 在網(wǎng)卡驅(qū)動(dòng)層和網(wǎng)絡(luò)層得到了廣泛的應(yīng)用, 驅(qū)動(dòng)層次上差不多有 E1000 系 列網(wǎng)卡, RTL8139 系列網(wǎng)卡, 3c50X 系列等主流的網(wǎng)絡(luò)適配器都采納了那 個(gè)技術(shù),而在

2、網(wǎng)絡(luò)層次上, NAPI 技術(shù)差不多完全被應(yīng)用到了聞名的 netif _rx 函數(shù)中間,同時(shí)提供了專門的 POLL 方法-process_backlog 來(lái)處理輪 詢的方法;按照實(shí)驗(yàn)數(shù)據(jù)表明采納 NAPI 技術(shù)能夠大大改善短長(zhǎng)度數(shù)據(jù)包接 收的效率,減少中斷觸發(fā)的時(shí)刻;由于 RTL8139CP 是一種應(yīng)用比較廣泛 的網(wǎng)絡(luò)適配器, 因此本文以其為例, 講明了 NAPI 技術(shù)在網(wǎng)絡(luò)適配器上的應(yīng) 用和差不多原理。然而 NAPI 存在一些比較嚴(yán)峻的缺陷:而關(guān)于上層的應(yīng)用程序而 言,系統(tǒng)不能在每個(gè)數(shù)據(jù)包接收到的時(shí)候都能夠及時(shí)地去處理它,而且隨 著傳輸速度增加,累計(jì)的數(shù)據(jù)包將會(huì)耗費(fèi)大量的內(nèi)存,通過(guò)實(shí)驗(yàn)表明在

3、Li nux 平臺(tái)上那個(gè)咨詢題會(huì)比在 FreeBSD 上要嚴(yán)峻一些;另外采納 NAPI 所造成的另外一個(gè)咨詢題是關(guān)于大的數(shù)據(jù)包處理比較困難,緣故是大的數(shù) 據(jù)包傳送到網(wǎng)絡(luò)層上的時(shí)候耗費(fèi)的時(shí)刻比短數(shù)據(jù)包長(zhǎng)專門多(即使是采納 DMA 方式),因此正如前面所講的那樣, NAPI 技術(shù)適用于對(duì)高速率的短 長(zhǎng)度數(shù)據(jù)包的處理, 在本文的末尾提出了 NAPI 的改善方法, 和實(shí)驗(yàn)數(shù)據(jù)。使用 NAPI 先決條件:驅(qū)動(dòng)能夠連續(xù)使用老的 2.4 內(nèi)核的網(wǎng)絡(luò)驅(qū)動(dòng)程序接口, NAPI 的 加入并可不能導(dǎo)致向前兼容性的喪失,然而 NAPI 的使用至少要得到下面 的保證:A. 要使用DMA的環(huán)形輸入隊(duì)列(也確實(shí)是 ring

4、_dma,那個(gè)在 2.4 驅(qū)動(dòng)中關(guān)于 Ethernet 的部分有詳細(xì)的介紹) ,或者是有足夠的內(nèi)存空間 緩存驅(qū)動(dòng)獲得的包。B. 在發(fā)送 /接收數(shù)據(jù)包產(chǎn)生中斷的時(shí)候有能力關(guān)斷 NIC 中斷的 事件處理, 同時(shí)在關(guān)斷 NIC 以后,并不阻礙數(shù)據(jù)包接收到網(wǎng)絡(luò)設(shè)備的環(huán)形 緩沖區(qū)(以下簡(jiǎn)稱rx-ri ng)處理隊(duì)列中。NAPI 對(duì)數(shù)據(jù)包到達(dá)的事件的處理采納輪詢方法, 在數(shù)據(jù)包達(dá)到的 時(shí)候, NAPI 就會(huì)強(qiáng)制執(zhí)行 dev->poll 方法。而和不象往常的驅(qū)動(dòng)那樣為了 減少包到達(dá)時(shí)刻的處理延遲,通常采納中斷的方法來(lái)進(jìn)行。應(yīng)當(dāng)注意的是,通過(guò)測(cè)試如果 DEC Tulip 系列( DE21x4x 芯片)

5、以及 National Semi 的部分網(wǎng)卡芯片,的測(cè)試表明如果把從前中斷處理的 部分都改換用設(shè)備的 POLL 方法去執(zhí)行,那么會(huì)造成輕微的延遲,因此在 進(jìn)行 MII (介質(zhì)無(wú)關(guān)) 的操作上就需要一些小小的訣竅 ,詳見(jiàn) mii_check_me dia的函數(shù)處理流程,本文不做詳細(xì)討論。在下面顯示的例子表示了在 8139 中如何把處理過(guò)程放在 dev 的 poll 方法中,把所有的原先中斷應(yīng)該處理的過(guò)程放在了 POLL 方法里 面,篇幅起見(jiàn),我們只介紹接收的 POLL 方法。在下面的 8139CP 驅(qū)動(dòng)程序介紹中表明了能夠把在中斷程序中所 做的任何情況放在 POLL 方法中去做,因此不同的 NI

6、C 在中斷中所要處 理的狀態(tài)和事件是不一樣的。C 有防止 NIC 隊(duì)列中排隊(duì)的數(shù)據(jù)包沖突的能力。當(dāng)關(guān)斷發(fā)送 /接收事件中斷的時(shí)候, NAPI 將在 POLL 中被調(diào)用處 理,由于 POLL 方法的時(shí)候, NIC 中斷差不多不能通知包到達(dá),那么那個(gè) 時(shí)候在如果在完成輪詢, 同時(shí)中斷打開(kāi)以后, 會(huì)趕忙有一個(gè) NIC 中斷產(chǎn)生, 從而觸發(fā)一次 POLL 事件,這種在中斷關(guān)斷時(shí)刻到達(dá)的包我們稱為 "rotting ";如此就會(huì)在 POLL 機(jī)制和 NIC 中斷之間產(chǎn)生一個(gè)競(jìng)爭(zhēng),解決的方法確 實(shí)是利用網(wǎng)卡的接收狀態(tài)位, 連續(xù)接收環(huán)形隊(duì)列緩沖 rx-ring 中的數(shù)據(jù), 直 到?jīng)]有數(shù)據(jù)

7、接收以后,才使能中斷。-2.網(wǎng)絡(luò)核心層(net core)調(diào)用設(shè)備驅(qū)動(dòng)程序使用循環(huán)方式發(fā)送 數(shù)據(jù)包,在設(shè)備驅(qū)動(dòng)層接收數(shù)據(jù)包的時(shí)候完全無(wú)鎖的接收,而網(wǎng)絡(luò)核心層 則同樣要保證每次只有一個(gè)處理器能夠使用軟中斷處理接收隊(duì)列。- 3. 在多個(gè)處理器對(duì) NIC 的 rx-ring 訪咨詢的時(shí)刻只能發(fā)生在 對(duì)循環(huán)隊(duì)列調(diào)用關(guān)閉(close)和掛起(suspend方法的時(shí)候(在那個(gè)時(shí)刻 會(huì)試圖清除接收循環(huán)隊(duì)列)- 4. 數(shù)據(jù)同步的咨詢題(關(guān)于接收循環(huán)隊(duì)列來(lái)講) ,驅(qū)動(dòng)程序是不 需要考慮的網(wǎng)絡(luò)層上的程序差不多把這些情況做完了。- 5. 如果沒(méi)有把全部的部分交給 POLL 方法處理,那么 NIC 中 斷仍舊需要使能

8、,接收鏈路狀態(tài)發(fā)生變化和發(fā)送完成中斷仍舊和往常的處 理步驟一樣,如此處理的假設(shè)是接收中斷是設(shè)備負(fù)載最大的的情形,因此 并不能講如此一定正確。下面的部分將詳細(xì)介紹在接收事件中調(diào)用設(shè)備的 POLL 方法。NAPI 提供的重要函數(shù)和數(shù)據(jù)結(jié)構(gòu)和函數(shù):核心數(shù)據(jù)結(jié)構(gòu):struct softnet_data 結(jié)構(gòu)內(nèi)的字段確實(shí)是 NIC 和網(wǎng)絡(luò)層之間處理 隊(duì)列,那個(gè)結(jié)構(gòu)是全局的,它從 NIC 中斷和 POLL 方法之間傳遞數(shù)據(jù)信息。 其中包含的字段有:struct softnet_dataintthrottle;/* 為 1 表示當(dāng)前隊(duì)列的數(shù)據(jù)包被禁止 */ intcng_level;/* 表示當(dāng)前處理器的數(shù)

9、據(jù)包處理?yè)砣潭?*/ intavg_blog;/*某個(gè)處理器的平均擁塞度*/struct sk_buff_headinput_pkt_queue;/*接收緩沖區(qū)的 sk_buff 隊(duì)列 */struct list_head poll_list;/*POLL 設(shè)備隊(duì)列頭 */struct net_device output_queue; /* 網(wǎng)絡(luò)設(shè)備發(fā)送隊(duì)列的隊(duì)列頭 */struct sk_buffcompletion_queue; /*完成發(fā)送的數(shù)據(jù)包等待開(kāi)釋的隊(duì)列*/審調(diào)用時(shí) 11 LFKstrUct.嗣喚轍中斷備*7丨 等待調(diào)罔時(shí)拭來(lái),_deviceba軟中斷kOg_dev;

10、/*表示當(dāng)一、/;核心API:V)那個(gè)函數(shù)被中E斷服1. netif rx sche卩網(wǎng)絡(luò)設(shè)ei39CP0fJPOLLr 法 cp_rx_poll,將設(shè)備I 層次的POLL處理隊(duì)列中去,排隊(duì)同時(shí)預(yù)備接收時(shí)返回的!數(shù)為1,同時(shí)觸發(fā)一個(gè)X_SOFTIRQ的軟中斷通知網(wǎng)絡(luò)層接收數(shù)握包p(dev)的 創(chuàng)帶0慚"方法添加到網(wǎng)絡(luò)調(diào)用 netif_rx_rescheduleY2. netif rx schedule數(shù)據(jù)包,在使用之前需要將創(chuàng)建新尿接字媛沖杷訓(xùn)mi許丁惜中匹 的物理排諜l提克皐 rx_ri Lg推備讓NI垸成確定設(shè)備處于運(yùn)行,而且設(shè)備還沒(méi)有被添加到網(wǎng)絡(luò)層的調(diào)度臺(tái)理隊(duì)列中,在調(diào)用n e

11、tif_rx_schedule之前會(huì)調(diào)用那步進(jìn)到下一,/x_r ing?T 行第中陸列的單社闊度臺(tái)3.n etif_rx_complete(dev)NET RPOLL 處把當(dāng)前指定的設(shè)備從 POLL隊(duì)列中清除,通常被設(shè)備的 指定設(shè)備方法調(diào)用,注意如果在 POLL隊(duì)列處于工 清除的,否則將會(huì)出錯(cuò)。作狀態(tài)的時(shí)沒(méi)育新的數(shù)噩到來(lái),調(diào) 用 電ti f rM_cc)fliplet«POLL如何在8139CP使用NAPI :從POLL方法的本質(zhì)意義上來(lái)講就在于盡量減少中斷的數(shù)目,專 門在于大量的小長(zhǎng)度的數(shù)據(jù)包的時(shí)候,減少中斷,以達(dá)到不要讓整個(gè)操作 系統(tǒng)花費(fèi)太多的時(shí)刻在中斷現(xiàn)場(chǎng)的愛(ài)護(hù)和復(fù)原上,以便把

12、贏得的時(shí)刻用來(lái) 在我網(wǎng)絡(luò)層上的處理數(shù)據(jù)的傳輸,例如在下面介紹的8139CP中斷的處理過(guò)程中,目的就在于盡快把產(chǎn)生中斷的設(shè)備掛在poll_list,同時(shí)關(guān)閉接收中斷,最后直截了當(dāng)調(diào)用設(shè)備的POLL方法來(lái)處理數(shù)據(jù)包的接收,直到收到數(shù) 據(jù)包收無(wú)可收,或者是達(dá)到一個(gè)時(shí)刻片內(nèi)的調(diào)度完成。RTL8139C+的數(shù)據(jù)接收環(huán)形緩沖隊(duì)列:RTL8139C+ 的接收方式是一種全新的緩沖方式,能明顯的降低 C PU 接收數(shù)據(jù)造成的花費(fèi),適合大型的服務(wù)器使用,適合 IP,TCP,UDP 等多 種方式的數(shù)據(jù)下載,以及連接IEEE802.1P, 802.1Q,VLAN 等網(wǎng)絡(luò)形式;在 8139CP 中分不有 64 個(gè)連續(xù)的

13、接收 / 發(fā)送描述符單元,對(duì)應(yīng)三個(gè)不同的 環(huán)形緩沖隊(duì)列 -一個(gè)是高優(yōu)先級(jí)傳輸描述符隊(duì)列, 一個(gè)是一般優(yōu)先級(jí)傳輸符 描述隊(duì)列,一個(gè)是接收符描述隊(duì)列,每個(gè)環(huán)形緩沖隊(duì)列右 64 個(gè) 4 個(gè)雙字 的連續(xù)描述符組成,每個(gè)描述符有 4 個(gè)連續(xù)的雙字組成,每個(gè)描述符的開(kāi) 始地址在 256 個(gè)字節(jié)的位置對(duì)齊,接收數(shù)據(jù)之前,軟件需要預(yù)先分配一個(gè) DMA 緩沖區(qū),一樣關(guān)于傳輸而言,緩沖區(qū)最大為 8Kbyte 同時(shí)把物理地 址鏈接在描述符的 DMA 地址描述單元,另外還有兩個(gè)雙字的單元表示對(duì) 應(yīng)的 DMA 緩沖區(qū)的接收狀態(tài)。在 /driver/net/8139CP.C 中關(guān)于環(huán)形緩沖隊(duì)列描述符的數(shù)據(jù)單元 如下表示:

14、struct cp_desc u32 opts1;/* 緩沖區(qū)狀態(tài)操縱符,包含緩沖區(qū)大 小,緩沖區(qū)傳輸啟動(dòng)位*/ u32 opts2;/*專門用于VLAN部分*/ u64 addr; /* 緩沖區(qū)的 DMA 地址 */ ;8139CP 的 NIC 中斷:static irqreturn_tcp_interrupt (int irq, void *dev_instance, struct pt_regs *regs)struct net_device *dev = dev_instance;struct cp_private *cp = dev->priv;u16 status;/* 檢查

15、 rx-ring 中是否有中斷到達(dá) */status = cpr16(IntrStatus);if (!status | (status = 0xFFFF)return IRQ_NONE;if (netif_msg_intr(cp)printk(KERN_DEBUG "%s: intr, status %04x cmd %02x cpcmd%04xn",dev->name, status, cpr8(Cmd), cpr16(CpCmd);/* 清除 NIC 中斷操縱器的內(nèi)容 */ cpw16(IntrStatus, status & cp_rx_intr_ma

16、sk); spin_lock(&cp->lock);/* 接收狀態(tài)寄存器表示有數(shù)據(jù)包到達(dá) */if (status & (RxOK | RxErr | RxEmpty | RxFIFOOvr) /*把當(dāng)前的產(chǎn)生中斷的 NIC設(shè)備掛在softnet_data中的POLL隊(duì)列 上,等待網(wǎng)絡(luò)上層上的應(yīng)用程序處理 */if (netif_rx_schedule_prep(dev) /*關(guān)閉接收中斷使能 */ cpw16_f(IntrMask, cp_norx_intr_mask);_netif_rx_schedule(dev);/*發(fā)送中斷的處理過(guò)程以及 8139C+的專門軟中斷

17、的處理過(guò)程,那 個(gè)地點(diǎn)我們不關(guān)懷 */if (status & (TxOK | TxErr | TxEmpty | SWInt) cp_tx(cp);/*如果發(fā)生鏈路變化的情形,需要檢查介質(zhì)無(wú)關(guān)接口(MII )的載波狀態(tài)同樣也發(fā)生變化,否則就要預(yù)備重新啟動(dòng) MII 接口*/if (status & LinkChg)mii_check_media(&cp->mii_if, netif_msg_link(cp), FALSE);/*如果PCI總線發(fā)生錯(cuò)誤,需要對(duì)8139C+的設(shè)備重新復(fù)位*/if (status & PciErr) u16 pci_status

18、;pci_read_config_word(cp->pdev, PCI_STATUS, &pci_status);pci_write_config_word(cp->pdev, PCI_STATUS, pci_status);printk(KERN_ERR "%s: PCI bus error, status=%04x, PCI status =%04xn",dev->name, status, pci_status);/* TODO: reset hardware */spin_unlock(&cp->lock);return IR

19、Q_HANDLED;把NIC掛在 POLL隊(duì)列(poll_list)上在 8139CP 的中斷程序能夠看到 _netif_rx_schedule 的調(diào)用方 式,它把 NIC 設(shè)備掛在 softnet_data 結(jié)構(gòu)中的 poll_list 隊(duì)列上,以便及時(shí) 的返回中斷,讓專門數(shù)據(jù)包處理 bottom-half 部分來(lái)進(jìn)行處理,我們先來(lái)看 一下 _netif_rx_schedule 的內(nèi)部工作流程。static inline void _netif_rx_schedule(struct net_device *dev)unsigned long flags;local_irq_save(flag

20、s);dev_hold(dev);/*把當(dāng)前NIC設(shè)備掛在POLL (poll_list)隊(duì)列中,等待喚醒軟中 斷以后進(jìn)行輪詢 */list_add_tail(&dev->poll_list, &_get_cpu_var(softnet_data).poll_lis t);/*確定當(dāng)前該設(shè)備所要預(yù)備接收的包大小 */if (dev->quota < 0)dev->quota += dev->weight;elsedev->quota = dev->weight;/*啟動(dòng)軟中斷,在表示所有中斷的狀態(tài)字irq_cpustat_t中關(guān)于軟中

21、斷字段 _softirq_pending 中,把關(guān)于網(wǎng)絡(luò)輪循接收軟中斷位置 1,等待調(diào)度時(shí)機(jī)來(lái)臨時(shí)候運(yùn)行該 中斷的句柄 net_rx_action。*/_raise_softirq_irqoff(NET_RX_SOFTIRQ); local_irq_restore(flags);由 _netif_rx_schedule 啟動(dòng)的軟中斷的處理過(guò)程分析 軟中斷事件觸發(fā)前差不多在此設(shè)備子系統(tǒng)初始化時(shí)刻調(diào)用 subsys _initcall(net_dev_init) 在軟中斷操縱臺(tái)上被激活,掛在任務(wù)隊(duì)列 tasklet 上 預(yù)備在任務(wù)調(diào)度 schedule 的時(shí)刻運(yùn)行它了,那個(gè)里面最要緊的部分是調(diào)用

22、了 8139C+網(wǎng)絡(luò)設(shè)備的 POLL方法(dev->poll),從網(wǎng)絡(luò)設(shè)備的rx-ring隊(duì) 列中獲得數(shù)據(jù),本來(lái)它應(yīng)當(dāng)放在網(wǎng)絡(luò)設(shè)備中斷服務(wù)程序中執(zhí)行的,按照我 們前面講明的那樣, POLL 方法以空間換取時(shí)刻的機(jī)制把它放在軟中斷部分 來(lái)執(zhí)行輪循機(jī)制 (采納類似老的 Bottom-half 機(jī)制也能夠達(dá)到同樣成效, 而 且更加容易明白得一些)在每次進(jìn)行進(jìn)程調(diào)度的時(shí)候就會(huì)執(zhí)行網(wǎng)絡(luò)設(shè)備軟 中斷,輪詢 rx-ring 對(duì) NIC 進(jìn)行數(shù)據(jù)的接收。軟中斷的處理過(guò)程:static void net_rx_action(struct softirq_action *h)struct softnet_d

23、ata *queue = &_get_cpu_var(softnet_data); unsigned long start_time = jiffies;int budget = netdev_max_backlog;/*表示隊(duì)列的最大長(zhǎng)度 */*鎖定當(dāng)前線程, 多處理器的情形之下不能被其他處理器中斷處理*/preempt_disable();local_irq_disable();數(shù)據(jù)*/while (!list_empty(&queue->poll_list) struct net_device *dev;/* 那個(gè)地點(diǎn)保證執(zhí)行當(dāng)前的 POLL 過(guò)程的時(shí)刻不超過(guò)一個(gè)時(shí)

24、刻 片,如此不至于被軟中斷占用太多的時(shí)刻,如此在一次調(diào)度的時(shí)刻內(nèi)執(zhí)行完畢當(dāng)前的 POLL 過(guò)程, budget 表示一個(gè)時(shí)刻片內(nèi)最大數(shù)據(jù)傳輸?shù)?"塊數(shù) ",塊的意思為每個(gè) POLL 所完成 sk_buff 數(shù)量,每塊中間的 sk_buf f 數(shù)量為 dev->quota 決定,在 8139CP 驅(qū)動(dòng)中,budget 為 300,而 quota 為 16 表示每給時(shí)刻片最多能夠接收到 4.8K 的 sk_buff 數(shù)量 */if (budget <= 0 | jiffies - start_time > 1)goto softnet_break; local

25、_irq_enable();/* 從公共的 softnet_data 數(shù)據(jù)結(jié)構(gòu)中的輪循隊(duì)列上獲得等待輪循 的設(shè)備結(jié)構(gòu) */dev = list_entry(queue->poll_list.next,struct net_device, poll_list);/* 調(diào)用設(shè)備的 POLL 方法從 NIC 上的 Ring Buffer 中讀入數(shù)據(jù) */ if (dev->quota <= 0 | dev->poll(dev, &budget) /* 完成一次 POLL 過(guò)程的數(shù)據(jù)的接收,重新定義設(shè)備接收數(shù)據(jù)的 " 配額"(事實(shí)上確實(shí)是 sk_bu

26、ff 緩沖區(qū)的數(shù)量,每次調(diào)用 POLL 方法的時(shí) 候能夠創(chuàng)建同時(shí)最多能夠向上層提交的 sk_buff 緩沖區(qū)數(shù)目 ,那個(gè)參數(shù)專門重要在高 速處理的時(shí)候有需要慎重優(yōu)化那個(gè)數(shù)值,在有大量數(shù)據(jù)接收的情形下,需要增加該數(shù)值) */ local_irq_disable();list_del(&dev->poll_list); list_add_tail(&dev->poll_list, &queue->poll_list);if (dev->quota < 0) dev->quota += dev->weight;else dev->

27、;quota = dev->weight; else /* 發(fā)生了錯(cuò)誤的數(shù)據(jù)接收狀況, 或者沒(méi)有完成 "規(guī)定 "配額的數(shù)據(jù)接 收,同時(shí)沒(méi)有新的數(shù)據(jù)進(jìn)來(lái),那個(gè)也可能表示差不多完成了傳輸?shù)倪^(guò)程,調(diào)用 _netif_rx_compl ete 把網(wǎng)絡(luò)設(shè)備從 POLL 隊(duì)列上清除(介紹 POLL 過(guò)程的時(shí)候詳細(xì)介紹) */ dev_put(dev);local_irq_disable();out: local_irq_enable(); preempt_enable(); return; softnet_break: _get_cpu_var(netdev_rx_stat).t

28、ime_squeeze+;_raise_softirq_irqoff(NET_RX_SOFTIRQ); goto out;8139CP 驅(qū)動(dòng)中的輪詢方法 dev->poll 方法:那個(gè)方法通常被網(wǎng)絡(luò)層在向驅(qū)動(dòng)的接收循環(huán)隊(duì)列獵取新的數(shù)據(jù)包 時(shí)刻調(diào)用,而驅(qū)動(dòng)的接收循環(huán)隊(duì)列中能夠向網(wǎng)絡(luò)層交付的包數(shù)量則在 dev-> quota 字段中表示,我們來(lái)看 8139cp 中 POLL 的原型:static int cp_rx_poll (struct net_device *dev, int *budget)參數(shù) budget 的上層任務(wù)所需要底層傳遞的數(shù)據(jù)包的數(shù)量, 那個(gè)數(shù) 值不能超過(guò) net

29、dev_max_backlog 的值??偠灾?POLL 方法被網(wǎng)絡(luò)層調(diào)用,只負(fù)責(zé)按照網(wǎng)絡(luò)層的要求 值( "預(yù)算"值)提交對(duì)應(yīng)數(shù)量的數(shù)據(jù)包。 8139CP 的 POLL 方法注冊(cè)通常 在設(shè)備驅(qū)動(dòng)程序模塊初始化(調(diào)用probe)的時(shí)候進(jìn)行,如下:static int cp_init_one (struct pci_dev *pdev, const struct pci_devi ce_id *ent)dev->poll = cp_rx_poll;設(shè)備的 POLL 方法正如前所講的是被網(wǎng)絡(luò)層上的軟中斷 net_rx_ action 調(diào)用,我們現(xiàn)在來(lái)看具體的流程:stat

30、ic int cp_rx_poll (struct net_device *dev, int *budget)struct cp_private *cp = netdev_priv(dev); unsigned rx_tail = cp->rx_tail;/*設(shè)定每次進(jìn)行調(diào)度的時(shí)候從設(shè)備發(fā)送到網(wǎng)絡(luò)層次最大的數(shù)據(jù)包 的大小 */unsigned rx_work = dev->quota; unsigned rx;rx_status_loop:rx = 0;/* 重新打開(kāi) NIC 中斷,在 cp_interrupt 中斷句柄中中斷關(guān)閉了, 現(xiàn) 在 POLl 差不多開(kāi)始處理環(huán)行緩沖隊(duì)列中

31、的數(shù)據(jù),因此中斷能夠打開(kāi),預(yù)備接收新的數(shù)據(jù)包 */ cpw16(IntrStatus, cp_rx_intr_mask);while (1) /*POLL 循環(huán)的開(kāi)始 */ u32 status, len;dma_addr_t mapping; struct sk_buff *skb, *new_skb; struct cp_desc *desc;unsigned buflen;/*從下標(biāo)為 rx_tail 的內(nèi)存中的環(huán)行緩沖隊(duì)列接收隊(duì)列rx_skb 上"摘下 "套接字緩沖區(qū) */skb = cp->rx_skbrx_tail.skb;if (!skb)BUG();d

32、esc = &cp->rx_ringrx_tail;/*檢查在NIC的環(huán)形隊(duì)列(rx_ring)上的最后的數(shù)據(jù)接收狀態(tài), 是否有顯現(xiàn)接收或者 FIFO 的錯(cuò)誤,是否 */status = le32_to_cpu(desc->opts1);if (status & DescOwn) break;len = (status & 0x1fff) - 4;mapping = cp->rx_skbrx_tail.mapping;if (status & (FirstFrag | LastFrag) != (FirstFrag | LastFrag) /*

33、 we don't support incoming fragmented frames.* instead, we attempt to ensure that the* pre-allocated RX skbs are properly sized such* that RX fragments are never encountered*/ cp_rx_err_acct(cp, rx_tail, status, len); cp->net_stats.rx_dropped+;cp->cp_stats.rx_frags+;goto rx_next;if (status

34、 & (RxError | RxErrFIFO) cp_rx_err_acct(cp, rx_tail, status, len); goto rx_next;if (netif_msg_rx_status(cp)printk(KERN_DEBUG "%s: rx slot %d status 0x%x len %dn", cp->dev->name, rx_tail, status, len);buflen = cp->rx_buf_sz + RX_OFFSET;/* 創(chuàng)建新的套接字緩沖區(qū) */ new_skb = dev_alloc_skb (

35、buflen);if (!new_skb) cp->net_stats.rx_dropped+;goto rx_next; skb_reserve(new_skb, RX_OFFSET); new_skb->dev = cp->dev;/* 解除原先映射的環(huán)行隊(duì)列上的映射區(qū)域 */ pci_unmap_single(cp->pdev, mapping, buflen, PCI_DMA_FROMDEVICE);/*檢查套接字緩沖區(qū)(sk_buff)上得到的數(shù)據(jù)校驗(yàn)和是否正確*/* Handle checksum offloading for incoming packet

36、s. */if (cp_rx_csum_ok(status)skb->ip_summed = CHECKSUM_UNNECESSARY;elseskb->ip_summed = CHECKSUM_NONE;/* 按照數(shù)據(jù)的實(shí)際大小重新定義套接字緩沖區(qū)的大小*/skb_put(skb, len);mapping =cp->rx_skbrx_tail.mapping =/*DMA 影射在前面新創(chuàng)建的套接字緩沖區(qū)虛擬地址 new_buf->tail 到實(shí)際的物理地址上,同時(shí)把那個(gè)物理地址掛在接收緩沖區(qū)的隊(duì)列中 */ pci_map_single(cp->pdev, ne

37、w_skb->tail, buflen, PCI_DMA_FROMDEVICE);/* 把新建立的緩沖區(qū)的虛擬地址掛在接收緩沖區(qū)的隊(duì)列中, 在下一 次訪咨詢 rx_skb 數(shù)組的那個(gè)結(jié)構(gòu)時(shí)候,POLL 方法會(huì)從那個(gè)虛擬地址讀出接收到的數(shù)據(jù)包 */ cp->rx_skbrx_tail.skb = new_skb;/*在cp_rx_skb調(diào)用netif_rx_skb,填充接收數(shù)據(jù)包隊(duì)列,等待網(wǎng)絡(luò)層在 Bottom half 隊(duì)列中調(diào)用 ip_rcv 接收網(wǎng)絡(luò)數(shù)據(jù),那個(gè)函數(shù)替代了往常使用的 netif_rx*/cp_rx_skb(cp, skb, desc);rx+;rx_next:/*

38、把前面映射的物理地址掛在 NIC 設(shè)備的環(huán)行隊(duì)列上 (也確實(shí)是 r x_ring 上,它是在和 NIC 中物理儲(chǔ)備區(qū)進(jìn)行了 DMA 映射的, 而不是驅(qū)動(dòng)在內(nèi)存中動(dòng)態(tài)建立的) ,預(yù)備提交給下層 (NIC) 進(jìn)行數(shù)據(jù) 傳輸*/cp->rx_ringrx_tail.opts2 = 0;cp->rx_ringrx_tail.addr = cpu_to_le64(mapping);/* 在相應(yīng)的傳輸寄存器中寫入操縱字,把rx_ring 的操縱權(quán)從驅(qū)動(dòng)程序交還給 NIC 硬件 */if (rx_tail = (CP_RX_RING_SIZE - 1)desc->opts1 = cpu_t

39、o_le32(DescOwn | RingEnd | cp->rx_buf_sz);elsedesc->opts1 = cpu_to_le32(DescOwn | cp->rx_buf_sz);/* 步進(jìn)到下一個(gè)接收緩沖隊(duì)列的下一個(gè)單元*/rx_tail = NEXT_RX(rx_tail);if (!rx_work-)break;cp->rx_tail = rx_tail;/*遞減配額值quota, 旦quota遞減到0表示這次的POLL傳輸 差不多完成了使命,就等待有數(shù)據(jù)到來(lái)的時(shí)候再次喚醒軟中斷執(zhí)行 POLL 方法*/ dev->quota -= rx;*bu

40、dget -= rx;/* if we did not reach work limit, then we're done with* this round of polling*/if (rx_work) /*如果仍舊有數(shù)據(jù)達(dá)到, 那么返回 POLL 方法循環(huán)的開(kāi)始, 連續(xù)接 收數(shù)據(jù) */if (cpr16(IntrStatus) & cp_rx_intr_mask)goto rx_status_loop;/*那個(gè)地點(diǎn)表示數(shù)據(jù)差不多接收完畢, 而且沒(méi)有新的接收中斷產(chǎn)生 了,那個(gè)時(shí)候使能 NIC 的接收中斷,同時(shí)調(diào)用 _netif_rx_complete 把差不多完成 POLL

41、 的設(shè)備從 poll_list 上摘除,等待下一次中斷產(chǎn)生的時(shí)候,再次把設(shè)備掛上 poll_list 隊(duì)列中。 */ local_irq_disable();cpw16_f(IntrMask, cp_intr_mask);_netif_rx_complete(dev);local_irq_enable();return 0;/* done */return 1;/* not done */其他的使用 NAPI 的驅(qū)動(dòng)程序和 8139CP 大同小異, 只是使用了 網(wǎng)絡(luò)層專門提供的 POLL 方法-proecess_backlog (/net/dev.©,在 NIC 中 斷接收到了數(shù)據(jù)包

42、后,調(diào)用網(wǎng)絡(luò)層上的netif_rx (/net/dev.c)將硬件中斷中接收到數(shù)據(jù)幀存入 sk_buff 結(jié)構(gòu) , 然后檢查硬件幀頭 ,識(shí)不幀類型 , 放入接 收隊(duì)列(softnet_data結(jié)構(gòu)中的input_pkt_queue隊(duì)列上),激活接收軟中斷 作進(jìn)一步處理.軟中斷函數(shù)(net_rx_action)提取接收包,而 process_backlog (也確實(shí)是 POLL 方法)向上層提交數(shù)據(jù)。能讓接收速度更快一些嗎? 我們現(xiàn)在來(lái)摸索一下如何提升 NAPI 效率的咨詢題,在講到效率 那個(gè)咨詢題之前我們先看一下在 linux 的文檔中 NAPI_HOWTO.txt 中提 供一個(gè)模型用來(lái)構(gòu)造自

43、己 NIC 的 POLL 方法,只是和 8139 有一些不一 樣,其中 NIC 設(shè)備描述中有一個(gè) dirty_rx 字段是在 8139CP 中沒(méi)有使用 到的。dirty_rx 確實(shí)是差不多開(kāi)創(chuàng)了 sk_buff 緩沖區(qū)指針和差不多提交 到 NIC 的 rx_ring 參與接收的緩沖, 然而還沒(méi)有完成傳輸?shù)木彌_區(qū)和差不 多完成傳輸?shù)木彌_區(qū)的數(shù)量總和, 與之相類似的是 cur_rx 那個(gè)表示的是下Fr & & bu£fcur一個(gè)參與傳輸?shù)木彌_區(qū)指針,我們?cè)贜API_I loWlOWt的舉例中能夠看到 那個(gè)字段的一些具體使用方式:/*cur_rx為下一個(gè)需要參與傳輸?shù)木彌_區(qū)

44、指針,如果cur_rx指針大于dirty_rx那么表示差不多有在rx-ring中開(kāi)創(chuàng) 的rx-ring中的每個(gè)傳輸緩沖差不多被耗盡了,那個(gè)時(shí)候需要調(diào)用refill_rx_ring把一些差不多向網(wǎng)絡(luò)層提交了數(shù) 據(jù)的rx-ring接收單元開(kāi)創(chuàng)新的緩沖區(qū),增加dirty_rx的數(shù)值,為下一次數(shù)據(jù)接收做預(yù)備,*/if (tp->cur_rx - tp->dirty_rx > RX_RING_SIZE/2 | tp->rx_bufferstp->dirty_rx % RX_RING_SIZE.skb = NULL) refill_rx_ri ng(dev);/*如果差不多當(dāng)

45、前的cur_rx和dirty_rx之間相差不超過(guò)總的rx_rin g接收單元的一半,而且剩下的一半中間有空的傳輸單元,那么我們不必?fù)?dān)憂了,因?yàn)檫€有足夠的緩沖區(qū)能夠使用(憑體會(huì)推斷的),就能夠退出當(dāng)前的程序,等待下一次軟中斷調(diào)用POLL來(lái)處理在這之間收到的數(shù)據(jù),(NAPI_HOWTO.txt中是重新啟動(dòng)時(shí)鐘計(jì)數(shù),如此做是在沒(méi)有使 能NIC中斷處理的情形下)*/if (tp->rx_bufferstp->dirty_rx % RX_RING_SIZE.skb = NUL L)restart_timer();/*如果執(zhí)行到那個(gè)地點(diǎn)了,那表示有幾種情形可能發(fā)生,第一當(dāng)前 的cur_rx和d

46、irty_rx之間相差不超過(guò)總的rx_ring接收單元的一半,調(diào)用refill_rx_ring后dirty_rx并未增加,(也許在rx-ring中大量的 單元收到數(shù)據(jù)沒(méi)有得到網(wǎng)絡(luò)層函數(shù)的處理),結(jié)果dirty_rx沒(méi)有增加,而且也沒(méi)有閑暇的單元來(lái)接收新到的數(shù)據(jù),如此就要重新調(diào)用n etif_rx_schedule來(lái)喚醒軟中斷,調(diào)用設(shè)備的 POLL 方法,采集在 rx-ring 的數(shù)據(jù)。 */else netif_rx_schedule(dev); /* we are back on the poll list */ 在 RTL-8169 的驅(qū)動(dòng)程序中就使用了 dirty_rx 那個(gè)字段,然而在

47、 8139CP 中并未使用,事實(shí)上那個(gè)并非 8139CP 驅(qū)動(dòng)不成熟的表現(xiàn), 大伙兒 閱讀 NAPI_HOWTO.txt 中能夠明白, 現(xiàn)在 8139CP 中并未嚴(yán)格按照 NAP I 所提出的要求去做,如果大伙兒有愛(ài)好的話,能夠比較一下 8139CP 和 RTL-8169 這兩個(gè)驅(qū)動(dòng)之間的不同,大伙兒會(huì)發(fā)覺(jué)盡管兩者都沒(méi)有在 NIC 中斷處理中去完成數(shù)據(jù)從驅(qū)動(dòng)層向網(wǎng)絡(luò)層上的轉(zhuǎn)發(fā),而都放在了軟中斷之 中完成, 然而在 8139 中利用了自己的一些專門的硬件特性, 使 NIC 在利 用關(guān)斷中斷接收數(shù)據(jù)的同時(shí)利用數(shù)據(jù)包到達(dá)位(RxOK)通知到達(dá)事件,然 后采納 POLL 方法把數(shù)據(jù)從 NIC 直截了當(dāng)

48、轉(zhuǎn)發(fā)到上層;而 RTL8169 還 需要借助 softnet_data結(jié)構(gòu)中的input_pkt_queue (套接字緩沖(sk_buff) 輸入隊(duì)列) 來(lái)完成從 NIC 中斷到軟中斷之間的 sk_buff 數(shù)據(jù)調(diào)度;如此關(guān) 于 8139CP 來(lái)講最大的好處確實(shí)是不要了 dirty_rx 字段和 cur_rx 字段來(lái) 讓 POLL 方法以及 NIC 中斷明白當(dāng)前的傳輸單元的狀況,還包括不需要 不時(shí)定期的調(diào)用 refill_rx_ring 來(lái)刷新 rx-ring 獲得閑暇的傳輸單元;講到 壞處我想確實(shí)是重寫了一個(gè) POLL 方法,完全不能借用 /net/core/dev.c 中 的 proces

49、s_backlog 來(lái)作為自己的 POLL 方法,只是那個(gè)代價(jià)值得。講了這么多,看起來(lái)都和提升效率沒(méi)有關(guān)系,事實(shí)上正好相反, 通過(guò)這些了解我們對(duì) soft net_data中的一些字段的意思應(yīng)該更加清晰了,下面所敘述的,提升效率的方法確實(shí)是在 8139CP 的基礎(chǔ)上借用了 NAPI_ HOWTO.txt 中的一些方法,從實(shí)際上的使用成效來(lái)看,在某些應(yīng)用場(chǎng)合之 下比 Linux 的 8139CP 的確是有了一定的提升, 我們第一看看在 Linux2.6. 6 的內(nèi)核使用 8139CP 在 x86(PIII-900Mhz) 平臺(tái)上的數(shù)據(jù)包接收處理情形: 比較表如下:Psize Ipps Tput Rxint Done 60 490000 254560 21 10128 358750 259946 27 11256 334454 450034 34 18512 234550 556670 201239 1934551024 119061 995645 884526 8823001440 74568 995645 995645 987154上表中表示:"Pszie"表示包的大小"Ipps" 每秒鐘系統(tǒng)能夠接收的包數(shù)量"Tput" 每次 POLL 超過(guò) 1M 個(gè)數(shù)據(jù)包的總量&

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論