課程e1000 網(wǎng)卡驅(qū)動分析_第1頁
課程e1000 網(wǎng)卡驅(qū)動分析_第2頁
課程e1000 網(wǎng)卡驅(qū)動分析_第3頁
課程e1000 網(wǎng)卡驅(qū)動分析_第4頁
課程e1000 網(wǎng)卡驅(qū)動分析_第5頁
已閱讀5頁,還剩40頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

e1000網(wǎng)卡驅(qū)動分析Linux-千兆網(wǎng)卡驅(qū)動實(shí)現(xiàn)機(jī)制淺析作者:Minit,出處:博客,責(zé)任編輯:羅麗艷,2009-03-2900:00網(wǎng)卡作為一個PCI設(shè)備,其必須遵守相應(yīng)的PCI規(guī)范,即必須為網(wǎng)卡定義相應(yīng)的標(biāo)識號,每個PCI外設(shè)由一個總線編號、一個設(shè)備編號及一個功能編號來標(biāo)識。網(wǎng)卡驅(qū)動程序則需要定義相應(yīng)的pci_device_id結(jié)構(gòu)來表示其支持的PCI外設(shè)的標(biāo)識……1.引言本分析主要針對e1000網(wǎng)卡,驅(qū)動源碼為7.3.20-k2。本文的目的不是為了講述如何編寫驅(qū)動程序,主要是分析網(wǎng)卡驅(qū)動內(nèi)部的實(shí)現(xiàn)機(jī)制。通過此分析,希望可以理解驅(qū)動程序中的各個部分的關(guān)系,對網(wǎng)卡發(fā)送和接收數(shù)據(jù)包有直觀的了解,同時(shí)也希望對設(shè)計(jì)網(wǎng)卡驅(qū)動程序有幫助。由于網(wǎng)卡驅(qū)動程序與硬件和操作系統(tǒng)都有很緊密的聯(lián)系,故要把某些問題完全弄清楚,需要很多的經(jīng)驗(yàn)與相關(guān)知識,介于自身的水平有限,且自身經(jīng)驗(yàn)較少,故肯定存在很多問題,希望本文的讀者發(fā)現(xiàn)了問題不吝與作者聯(lián)系。2.網(wǎng)卡驅(qū)動的體系結(jié)構(gòu)網(wǎng)卡作為一個PCI設(shè)備,其必須遵守相應(yīng)的PCI規(guī)范,即必須為網(wǎng)卡定義相應(yīng)的標(biāo)識號,每個PCI外設(shè)由一個總線編號、一個設(shè)備編號及一個功能編號來標(biāo)識。網(wǎng)卡驅(qū)動程序則需要定義相應(yīng)的pci_device_id結(jié)構(gòu)來表示其支持的PCI外設(shè)的標(biāo)識,通過在驅(qū)動程序的pci_device_id中查找設(shè)備標(biāo)識號,將驅(qū)動程序與設(shè)備聯(lián)系起來。網(wǎng)卡作為PCI設(shè)備,其包括兩類空間,一種是配置空間,CPU不能直接訪問,訪問這個空間,需要借助BIOS功能;另一種是普通的控制寄存器空間,這部分經(jīng)過映射后,CPU可以直接訪問控制。在硬件加電初始化時(shí),BIOS統(tǒng)一檢查所有的PCI設(shè)備,并為每個設(shè)備分配一個物理地址,該地址通過BIOS獲得并寫到設(shè)備的配置空間內(nèi),驅(qū)動程序就可以將網(wǎng)卡的普通控制寄存器映射到一段內(nèi)存空間內(nèi),CPU通過訪問映射后的虛擬地址來操控網(wǎng)卡的寄存器。當(dāng)操作系統(tǒng)初始化時(shí),其為每個PCI設(shè)備分配一個pci_dev結(jié)構(gòu),并將前面分配的物理地址寫到pci_dev的resource字段中。在網(wǎng)卡驅(qū)動程序中則可以通過讀取pci_dev中的resource字段獲得網(wǎng)卡的寄存器配置空間地址,其由函數(shù)pci_resource_start()和pci_resource_end()獲得該空間的起始位置,通過ioremap()將該段位置映射到主存中,以便CPU訪問控制網(wǎng)卡的I/O和內(nèi)存空間。如重啟網(wǎng)卡設(shè)備,則是通過向映射后的網(wǎng)卡的相應(yīng)寄存器寫入命令實(shí)現(xiàn),其通過映射后的首地址及相應(yīng)的寄存器偏移量找到該寄存器的位置,然后通過函數(shù)writeb()寫該寄存器。有關(guān)相關(guān)寄存器對應(yīng)的偏移量,一般是通過網(wǎng)卡的相關(guān)的datasheet(數(shù)據(jù)表)獲得。如果要獲取網(wǎng)卡的MAC地址,則一般通過函數(shù)readb()讀取首地址開始的前六位內(nèi)容即可得到。通過pci_read_config_和pci_write_config_系列函數(shù)可以讀寫網(wǎng)卡的配置空間,如開啟網(wǎng)卡設(shè)備就是將網(wǎng)卡配置空間的command域置1,從而設(shè)備就可以將寄存器映射到內(nèi)存。如通過函數(shù)pci_read_config_byte(pci_devpdev,PCI_INTERRUPT_LINE,&irq)獲得設(shè)備所分配的中斷號并保存在irq中。pci_read_config_和pci_write_config_系列函數(shù)實(shí)際上是調(diào)用pci_bus_read_config_和pci_bus_write_config_系列函數(shù)實(shí)現(xiàn)的,這些函數(shù)實(shí)際操作網(wǎng)卡對應(yīng)的PCI總線結(jié)構(gòu)。有關(guān)PCI寄存器的配置空間可參考《LinuxDeviceDriver3rd》或《PCIBusDemystified》。網(wǎng)卡作為一個規(guī)范的PCI設(shè)備,其對應(yīng)的結(jié)構(gòu)體pci_dev代表了網(wǎng)卡設(shè)備,體現(xiàn)了作為PCI設(shè)備所應(yīng)有的規(guī)范。網(wǎng)卡的網(wǎng)絡(luò)傳輸性質(zhì),實(shí)際是通過另一結(jié)構(gòu)體net_device來體現(xiàn)的,該結(jié)構(gòu)體的初始化由網(wǎng)卡驅(qū)動程序?qū)崿F(xiàn)。內(nèi)核中對網(wǎng)卡的操作,其實(shí)質(zhì)就是對net_device結(jié)構(gòu)的操作,pci_dev和net_device都表示網(wǎng)卡設(shè)備,只是體現(xiàn)的角度不一樣。net_device是對特定適配器的抽象,其為上層協(xié)議提供了統(tǒng)一的接口,網(wǎng)卡驅(qū)動則基于特定適配器實(shí)現(xiàn)了這一抽象。PCI設(shè)備的驅(qū)動程序由pci_driver結(jié)構(gòu)體表示,故網(wǎng)卡驅(qū)動應(yīng)該是該結(jié)構(gòu)體的一個實(shí)例,在該結(jié)構(gòu)體中應(yīng)該要定義實(shí)現(xiàn)與網(wǎng)卡相關(guān)的參數(shù)以及相應(yīng)的操作。網(wǎng)卡驅(qū)動實(shí)際操作的特定適配器,是由與硬件相關(guān)的adapter所表示的結(jié)構(gòu)體,adapter體現(xiàn)了大部分與硬件相關(guān)的屬性,網(wǎng)卡驅(qū)動除了直接對pci_dev結(jié)構(gòu)操作外,其他對網(wǎng)卡設(shè)備的操作基本是對adapter結(jié)構(gòu)體的操作。adapter體現(xiàn)了net_device與pci_dev的關(guān)聯(lián),也實(shí)現(xiàn)了網(wǎng)絡(luò)設(shè)備的適配器無關(guān)性。與網(wǎng)卡設(shè)備pci_dev的通信是通過adapter來實(shí)現(xiàn)的,而這個實(shí)現(xiàn)則是網(wǎng)卡驅(qū)動所要完成的任務(wù)。下面圖2-1描述了三個重要數(shù)據(jù)結(jié)構(gòu)間的關(guān)系,pci_dev結(jié)構(gòu)體現(xiàn)了網(wǎng)卡的配置空間和I/O與內(nèi)存區(qū)域,net_device結(jié)構(gòu)則向內(nèi)核提供了操作網(wǎng)卡的抽象接口,其參數(shù)值可按照功能分為5個部分。e1000_adapter結(jié)構(gòu)除了體現(xiàn)相應(yīng)的硬件無關(guān)性外,還管理了發(fā)送與接收數(shù)據(jù)包的相應(yīng)緩沖空間,網(wǎng)卡的物理地址空間映射后的虛擬地址也在此結(jié)構(gòu)中保存。e1000_adapter結(jié)構(gòu)中的e1000_hw結(jié)構(gòu)主要保存網(wǎng)卡的硬件參數(shù),其值就是通過讀取pci_dev的內(nèi)容獲取而來的。以上的數(shù)據(jù)結(jié)構(gòu)在網(wǎng)卡工作時(shí)起著最核心的作用,同時(shí)也是編寫驅(qū)動程序必須操作的結(jié)構(gòu)體。圖2-1網(wǎng)卡驅(qū)動程序的主要數(shù)據(jù)結(jié)構(gòu)3.網(wǎng)卡設(shè)備的注冊與初始化網(wǎng)卡設(shè)備的注冊與初始化是在其相關(guān)的驅(qū)動程序的e1000_probe()函數(shù)中實(shí)現(xiàn)的,有關(guān)設(shè)備如何與該驅(qū)動相關(guān)聯(lián),以及如何調(diào)用到e1000_probe()的,在此不作介紹。在函數(shù)e1000_probe()中首先調(diào)用函數(shù)pci_enable_device()啟用設(shè)備,然后聲明了DMA空間,接著調(diào)用函數(shù)alloc_etherdev()生成結(jié)構(gòu)體net_device,該結(jié)構(gòu)體就表示了網(wǎng)卡設(shè)備,對net_device的參數(shù)進(jìn)行了初始化后,調(diào)用register_netdev()注冊該設(shè)備。以上僅是對設(shè)備的注冊,設(shè)備的初始化主要包括對兩個結(jié)構(gòu)體的賦值,一個是net_device,另一個則是e1000_adapter。對e1000_adapter的初始化包括對其中的e1000_hw結(jié)構(gòu)的初始化,其調(diào)用函數(shù)e1000_sw_init()實(shí)現(xiàn)。在對e1000_hw的初始化過程中使用了ioremap()實(shí)現(xiàn)了網(wǎng)卡硬件地址與內(nèi)存虛擬地址之間的映射。對網(wǎng)卡設(shè)備進(jìn)行撤銷則調(diào)用函數(shù)free_netdev()實(shí)現(xiàn)。有關(guān)網(wǎng)卡設(shè)備注冊與初始化的更詳細(xì)的過程可以參考《UnderstandingLinuxNetworkInternals》。4.網(wǎng)卡設(shè)備的啟動與關(guān)閉網(wǎng)卡設(shè)備啟動時(shí)首先調(diào)用函數(shù)e1000_open(),在該函數(shù)中調(diào)用e1000_request_irq()申請中斷號及其相應(yīng)的中斷處理程序e1000_intr(),其實(shí)際是調(diào)用request_irq()函數(shù)來實(shí)現(xiàn)的。在函數(shù)e1000_open()中調(diào)用e1000_setup_all_tx_resources()根據(jù)發(fā)送隊(duì)列數(shù)建立發(fā)送緩沖區(qū),每個緩沖區(qū)的建立由函數(shù)e1000_setup_tx_resources()實(shí)現(xiàn),在e1000_setup_tx_resources()中,主要是對描述發(fā)送緩沖區(qū)的結(jié)構(gòu)體e1000_tx_ring的初始化,其將DMA緩沖區(qū)與網(wǎng)卡所映射的虛擬地址空間聯(lián)系起來,使用函數(shù)pci_alloc_consistent()實(shí)現(xiàn)一致性映射。而虛擬地址空間與網(wǎng)卡的物理地址相對應(yīng),故而這三種空間就對應(yīng)了起來,DMA也就可以在此基礎(chǔ)上實(shí)現(xiàn)了,當(dāng)數(shù)據(jù)包內(nèi)容被映射到DMA緩沖區(qū)后,其將完全由設(shè)備操控。DMA的緩沖區(qū)的初始化在驅(qū)動程序的e1000_probe()函數(shù)中實(shí)現(xiàn)。e1000_open()函數(shù)會調(diào)用e1000_up()對網(wǎng)卡的一些相關(guān)的軟硬件參數(shù)與空間進(jìn)行配置,如硬件寄存器的讀寫,數(shù)據(jù)包接收與發(fā)送空間的處理函數(shù)的初始化等。發(fā)送緩沖空間的初始化結(jié)構(gòu)及相互間的關(guān)系如圖4-1所示。接收緩沖區(qū)的初始化與上述類似,由e1000_setup_all_rx_resources()調(diào)用e1000_setup_rx_resources()對結(jié)構(gòu)體e1000_rx_ring進(jìn)行初始化。接收緩沖空間的結(jié)構(gòu)如圖4-2所示。圖4-1發(fā)送緩沖區(qū)的結(jié)構(gòu)圖圖4-2接收緩沖區(qū)的結(jié)構(gòu)圖網(wǎng)卡的關(guān)閉由函數(shù)e1000_close()實(shí)現(xiàn),其會首先關(guān)閉中斷,然后釋放中斷號,并且會釋放網(wǎng)卡申請的相應(yīng)的空間。5.發(fā)送與接收數(shù)據(jù)包數(shù)據(jù)包的發(fā)送:圖5-1發(fā)送數(shù)據(jù)包的結(jié)構(gòu)圖及相互關(guān)系根據(jù)發(fā)送隊(duì)列數(shù)num_tx_queues建立相應(yīng)的發(fā)送緩沖區(qū)結(jié)構(gòu)e1000_tx_ring,在該結(jié)構(gòu)中有描述該區(qū)域的指向e1000_tx_desc結(jié)構(gòu)的desc,該緩沖區(qū)指向的dma總線地址,用于接收硬件傳送來的用e1000_buffer結(jié)構(gòu)描述的緩沖塊數(shù)組buffer_info[],另外的幾個參數(shù)則主要用于描述這些緩沖塊,其中count表示緩沖塊的個數(shù),next_to_use和next_to_clean主要描述緩沖塊的使用狀態(tài),如已經(jīng)接收接收了數(shù)據(jù)的位置及準(zhǔn)備接收的位置,當(dāng)有新的數(shù)據(jù)包要發(fā)送時(shí),首先由上層協(xié)議調(diào)用e1000_xmit_frame(),在該函數(shù)中接著調(diào)用e1000_tx_queue()根據(jù)相應(yīng)的參數(shù)找到緩沖塊存放,緩沖塊的初始化則由函數(shù)e1000_tx_map()實(shí)現(xiàn)。buffer_info指向的環(huán)形緩沖塊區(qū)域主要用來接收總線地址映射來的數(shù)據(jù)包,所有的緩沖塊用next_to_match連接成一個環(huán),每個緩沖塊用結(jié)構(gòu)體e1000_buffer表示,在該結(jié)構(gòu)中,skb存放數(shù)據(jù)包的內(nèi)容,dma表示該數(shù)據(jù)包所在的總線地址。此處使用函數(shù)pci_map_single()進(jìn)行流式映射,的映射方向?yàn)镻CI_DMA_TODEVICE,控制總線會把虛擬地址空間所指內(nèi)容映射到總線地址,然后將該內(nèi)容由網(wǎng)卡傳送出去。發(fā)送數(shù)據(jù)包的相關(guān)結(jié)構(gòu)圖及相互關(guān)系如圖5-1所示。e1000_tx_ring結(jié)構(gòu)中的desc所指向的buffer_addr記錄了每次發(fā)送的緩沖塊所映射的總線地址,即buffer_addr記錄的是總線地址。而desc本是一個虛擬地址,該虛擬地址是通過pci_alloc_consistent()映射的發(fā)送緩沖區(qū)的地址,其與DMA緩沖區(qū)中的一段總線地址相對應(yīng),該總線地址由e1000_tx_ring結(jié)構(gòu)中的dma成員保存,這種映射關(guān)系在對開啟網(wǎng)卡時(shí)就實(shí)現(xiàn)了,其與在發(fā)送數(shù)據(jù)包時(shí)映射的總線地址有區(qū)別,后者是在發(fā)送時(shí)動態(tài)進(jìn)行的。數(shù)據(jù)包的接收圖5-2接收數(shù)據(jù)包的結(jié)構(gòu)圖及相互關(guān)系根據(jù)接收隊(duì)列數(shù)num_rx_queues建立相應(yīng)的接收緩沖區(qū)結(jié)構(gòu)e1000_rx_ring,在該結(jié)構(gòu)中有描述該區(qū)域的指向e1000_rx_desc結(jié)構(gòu)的desc,該緩沖區(qū)指向的dma總線地址,用于接收硬件傳送來的用e1000_buffer結(jié)構(gòu)描述的緩沖塊數(shù)組buffer_info[],另外的幾個參數(shù)則主要用于描述這些緩沖塊,其中count表示緩沖塊的個數(shù),next_to_use和next_to_clean主要描述緩沖塊的使用狀態(tài),如已經(jīng)接收接收了數(shù)據(jù)的位置及準(zhǔn)備接收的位置,當(dāng)有新的數(shù)據(jù)包要到來時(shí),則根據(jù)這兩個參數(shù)找到相應(yīng)的區(qū)域存放。對于需要分片接收的數(shù)據(jù)包則利用了ps_page和ps_page_dma來實(shí)現(xiàn),參數(shù)cpu指定了該接收緩沖隊(duì)列所屬的處理器。總線地址與要發(fā)送的虛擬地址間的映射方向?yàn)镻CI_DMA_FROMDEVICE,控制總線會把總線地址的內(nèi)容映射到虛擬地址空間內(nèi)。接收數(shù)據(jù)包的相關(guān)結(jié)構(gòu)圖及相互關(guān)系如圖5-2所示。當(dāng)有新的數(shù)據(jù)包到達(dá)時(shí),首先觸動中斷處理函數(shù)e1000_intr(),在函數(shù)中會為新來的數(shù)據(jù)包在緩沖塊數(shù)組buffer_info中找到一個新的緩沖塊位置,并完成e1000_buffer結(jié)構(gòu)的賦值。數(shù)據(jù)包的接收其實(shí)就是將總線地址指向的內(nèi)容拷貝到skb中,然后根據(jù)skb中的協(xié)議將其傳給相應(yīng)的上層協(xié)議的接收函數(shù)。6.網(wǎng)卡驅(qū)動程序的設(shè)計(jì)編寫網(wǎng)卡驅(qū)動程序,需要對以下三類結(jié)構(gòu)體進(jìn)行相應(yīng)的操作:1.與網(wǎng)絡(luò)協(xié)議棧相關(guān)的結(jié)構(gòu)體,如sk_buff結(jié)構(gòu)體。2.網(wǎng)卡和協(xié)議棧接口相關(guān)的結(jié)構(gòu)體,如net_device結(jié)構(gòu)體。3.與I/O總線相關(guān)的結(jié)構(gòu)體,如利用PCI總線進(jìn)行數(shù)據(jù)包傳送的DMA緩沖區(qū)以及表示網(wǎng)卡的pci_dev結(jié)構(gòu)體。設(shè)計(jì)網(wǎng)卡驅(qū)動時(shí),需要針對以上的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)相應(yīng)的功能,如對sk_buff結(jié)構(gòu)的操作實(shí)現(xiàn)對數(shù)據(jù)包的有效控制;對net_device結(jié)構(gòu)的操作可以對網(wǎng)卡進(jìn)行操作(如開啟、關(guān)閉等),可以發(fā)送數(shù)據(jù)包以及輪詢數(shù)據(jù)包,可以制定網(wǎng)卡的相應(yīng)的定時(shí)操作以及統(tǒng)計(jì)數(shù)據(jù)包,可以為用戶提供配置功能(ethtool)等。在設(shè)計(jì)網(wǎng)卡驅(qū)動時(shí),需要考慮如何與上層協(xié)議的協(xié)調(diào)以及對底層總線地址的控制。有關(guān)網(wǎng)卡驅(qū)動程序更詳細(xì)的設(shè)計(jì)流程可以參考《EssentialLinuxDeviceDriver》及《LinuxDeviceDriver3rd》。7.總結(jié)本文的分析重點(diǎn)為網(wǎng)卡驅(qū)動中涉及到的重要數(shù)據(jù)結(jié)構(gòu),以及發(fā)送和接收數(shù)據(jù)包的實(shí)現(xiàn),對這些實(shí)現(xiàn)機(jī)制了解后,對于設(shè)計(jì)和實(shí)現(xiàn)驅(qū)動程序應(yīng)該會有幫助,因?yàn)樵摍C(jī)制本身難度很大,加上作者水平有限,其中的分析結(jié)論不能保證完全正確。文檔是寫給會的人看的,開始只能理解個大概,關(guān)鍵還是代碼,今天準(zhǔn)備研究驅(qū)動代碼寫這篇應(yīng)該達(dá)到:理解網(wǎng)卡工作原理,(思考一個問題:高性能提高方法:小包和大包情況分開來論),其他不需要太多了解細(xì)節(jié)問題linux模塊分析入口:moduleinit函數(shù)【2.6.31】1設(shè)備發(fā)現(xiàn)過程staticint__inite1000_init_module(void){intret;printk(KERN_INFO"%s:Intel(R)PRO/1000NetworkDriver-%s\n",e1000e_driver_name,e1000e_driver_version);printk(KERN_INFO"%s:Copyright(c)1999-2008IntelCorporation.\n",e1000e_driver_name);//由此可見,82574的偵測發(fā)現(xiàn),是pci框架下發(fā)現(xiàn)并使用的。ret=pci_register_driver(&e1000_driver);pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY,e1000e_driver_name,PM_QOS_DEFAULT_VALUE);returnret;}/*PCIDeviceAPIDriver*/staticstructpci_drivere1000_driver={.name=e1000e_driver_name,/*猜測一下下面這個table,pci通用驅(qū)動啟動的時(shí)候會掃描pci設(shè)備,如果這個id表中的id對應(yīng),然后去調(diào)用probe函數(shù),structpci_dev*pdev參數(shù)應(yīng)該是動態(tài)申請的內(nèi)存,并通過讀取configurespace獲得一些通用的信息*/.id_table=e1000_pci_tbl,//一切始于此,e1000_be=e1000_probe,.remove=__devexit_p(e1000_remove),#ifdefCONFIG_PM/*PowerManagementHooks*/.suspend=e1000_suspend,.resume=e1000_resume,#endif.shutdown=e1000_shutdown,.err_handler=&e1000_err_handler};2分析下probe函數(shù)staticint__devinite1000_probe(structpci_dev*pdev,conststructpci_device_id*ent){structnet_device*netdev;structe1000_adapter*adapter;structe1000_hw*hw;conststructe1000_info*ei=e1000_info_tbl[ent->driver_data];resource_size_tmmio_start,mmio_len;resource_size_tflash_start,flash_len;staticintcards_found;inti,err,pci_using_dac;u16eeprom_data=0;u16eeprom_apme_mask=E1000_EEPROM_APME;e1000e_disable_l1aspm(pdev);err=pci_enable_device_mem(pdev);if(err)returnerr;pci_using_dac=0;err=pci_set_dma_mask(pdev,DMA_BIT_MASK(64));if(!err){err=pci_set_consistent_dma_mask(pdev,DMA_BIT_MASK(64));if(!err)pci_using_dac=1;}else{err=pci_set_dma_mask(pdev,DMA_BIT_MASK(32));if(err){err=pci_set_consistent_dma_mask(pdev,DMA_BIT_MASK(32));if(err){dev_err(&pdev->dev,"NousableDMA""configuration,aborting\n");gotoerr_dma;}}}err=pci_request_selected_regions_exclusive(pdev,pci_select_bars(pdev,IORESOURCE_MEM),e1000e_driver_name);if(err)gotoerr_pci_reg;/*AER(AdvancedErrorReporting)hooks*/err=pci_enable_pcie_error_reporting(pdev);if(err){dev_err(&pdev->dev,"pci_enable_pcie_error_reportingfailed""0x%x\n",err);/*non-fatal,continue*/}pci_set_master(pdev);/*PCIconfigspaceinfo*/err=pci_save_state(pdev);if(err)gotoerr_alloc_etherdev;err=-ENOMEM;//e1000_addapter在net_device結(jié)構(gòu)體的后面,一塊申請下來了,采用了內(nèi)存對齊。netdev=alloc_etherdev(sizeof(structe1000_adapter));if(!netdev)gotoerr_alloc_etherdev;//由此可以串起來從pci_dev-->net_device-->e1000_adapterSET_NETDEV_DEV(netdev,&pdev->dev);pci_set_drvdata(pdev,netdev);adapter=netdev_priv(netdev);hw=&adapter->hw;//e1000_adapter也可以往回找到net_device和pci_devadapter->netdev=netdev;adapter->pdev=pdev;adapter->ei=ei;adapter->pba=ei->pba;adapter->flags=ei->flags;adapter->flags2=ei->flags2;adapter->hw.adapter=adapter;adapter->hw.mac.type=ei->mac;adapter->max_hw_frame_size=ei->max_hw_frame_size;adapter->msg_enable=(1<<NETIF_MSG_DRV|NETIF_MSG_PROBE)-1;//0表示設(shè)備映射的內(nèi)存的的barmmio_start=pci_resource_start(pdev,0);mmio_len=pci_resource_len(pdev,0);err=-EIO;//ioremap是內(nèi)核提供的用來映射外設(shè)寄存器到主存的函數(shù)adapter->hw.hw_addr=ioremap(mmio_start,mmio_len);if(!adapter->hw.hw_addr)gotoerr_ioremap;//1表示設(shè)備映射的flash的地址if((adapter->flags&FLAG_HAS_FLASH)&&(pci_resource_flags(pdev,1)&IORESOURCE_MEM)){flash_start=pci_resource_start(pdev,1);flash_len=pci_resource_len(pdev,1);adapter->hw.flash_address=ioremap(flash_start,flash_len);if(!adapter->hw.flash_address)gotoerr_flashmap;}/*constructthenet_devicestruct*//*現(xiàn)在這個函數(shù)是個核心操作函數(shù)*/netdev->netdev_ops=&e1000e_netdev_ops;/*ethtool提供的功能*/e1000e_set_ethtool_ops(netdev);netdev->watchdog_timeo=5*HZ;/*napi功能開啟*/netif_napi_add(netdev,&adapter->napi,e1000_clean,64);strncpy(netdev->name,pci_name(pdev),sizeof(netdev->name)-1);netdev->mem_start=mmio_start;netdev->mem_end=mmio_start+mmio_len;adapter->bd_number=cards_found++;e1000e_check_options(adapter);/*setupadapterstruct*//*接受對列的大小,幀大小,申請隊(duì)列內(nèi)存。關(guān)閉中斷*/err=e1000_sw_init(adapter);if(err)gotoerr_sw_init;err=-EIO;memcpy(&hw->mac.ops,ei->mac_ops,sizeof(hw->mac.ops));memcpy(&hw->nvm.ops,ei->nvm_ops,sizeof(hw->nvm.ops));memcpy(&hw->phy.ops,ei->phy_ops,sizeof(hw->phy.ops));err=ei->get_variants(adapter);if(err)gotoerr_hw_init;if((adapter->flags&FLAG_IS_ICH)&&(adapter->flags&FLAG_READ_ONLY_NVM))e1000e_write_protect_nvm_ich8lan(&adapter->hw);hw->mac.ops.get_bus_info(&adapter->hw);adapter->hw.phy.autoneg_wait_to_complete=0;/*Copperoptions*/if(adapter->hw.phy.media_type==e1000_media_type_copper){adapter->hw.phy.mdix=AUTO_ALL_MODES;adapter->hw.phy.disable_polarity_correction=0;adapter->hw.phy.ms_type=e1000_ms_hw_default;}if(e1000_check_reset_block(&adapter->hw))e_info("PHYresetisblockedduetoSOL/IDERsession.\n");netdev->features=NETIF_F_SG|NETIF_F_HW_CSUM|NETIF_F_HW_VLAN_TX|NETIF_F_HW_VLAN_RX;if(adapter->flags&FLAG_HAS_HW_VLAN_FILTER)netdev->features|=NETIF_F_HW_VLAN_FILTER;netdev->features|=NETIF_F_TSO;netdev->features|=NETIF_F_TSO6;netdev->vlan_features|=NETIF_F_TSO;netdev->vlan_features|=NETIF_F_TSO6;netdev->vlan_features|=NETIF_F_HW_CSUM;netdev->vlan_features|=NETIF_F_SG;if(pci_using_dac)netdev->features|=NETIF_F_HIGHDMA;if(e1000e_enable_mng_pass_thru(&adapter->hw))adapter->flags|=FLAG_MNG_PT_ENABLED;/**beforereadingtheNVM,resetthecontrollerto*putthedeviceinaknowngoodstartingstate*/adapter->hw.mac.ops.reset_hw(&adapter->hw);/**systemswithASPMandothersmayseethechecksumfailonthefirst*attempt.Let'sgiveitafewtries*/for(i=0;;i++){if(e1000_validate_nvm_checksum(&adapter->hw)>=0)break;if(i==2){e_err("TheNVMChecksumIsNotValid\n");err=-EIO;gotoerr_eeprom;}}e1000_eeprom_checks(adapter);/*copytheMACaddressoutoftheNVM*/if(e1000e_read_mac_addr(&adapter->hw))e_err("NVMReadErrorwhilereadingMACaddress\n");memcpy(netdev->dev_addr,adapter->hw.mac.addr,netdev->addr_len);memcpy(netdev->perm_addr,adapter->hw.mac.addr,netdev->addr_len);if(!is_valid_ether_addr(netdev->perm_addr)){e_err("InvalidMACAddress:%pM\n",netdev->perm_addr);err=-EIO;gotoerr_eeprom;}/*watchdog定時(shí)器初始化*/init_timer(&adapter->watchdog_timer);adapter->watchdog_timer.function=&e1000_watchdog;adapter->watchdog_timer.data=(unsignedlong)adapter;init_timer(&adapter->phy_info_timer);adapter->phy_info_timer.function=&e1000_update_phy_info;adapter->phy_info_timer.data=(unsignedlong)adapter;/*幾個work_struct*/INIT_WORK(&adapter->reset_task,e1000_reset_task);INIT_WORK(&adapter->watchdog_task,e1000_watchdog_task);INIT_WORK(&adapter->downshift_task,e1000e_downshift_workaround);INIT_WORK(&adapter->update_phy_task,e1000e_update_phy_task);/*Initializelinkparameters.Usercanchangethemwithethtool*/adapter->hw.mac.autoneg=1;adapter->fc_autoneg=1;adapter->hw.fc.requested_mode=e1000_fc_default;adapter->hw.fc.current_mode=e1000_fc_default;adapter->hw.phy.autoneg_advertised=0x2f;/*ringsizedefaults*/adapter->rx_ring->count=256;adapter->tx_ring->count=256;/**InitialWakeonLANsetting-IfAPMwakeisenabledin*theEEPROM,enabletheACPIMagicPacketfilter*/if(adapter->flags&FLAG_APME_IN_WUC){/*APMEbitinEEPROMismappedtoWUC.APME*/eeprom_data=er32(WUC);eeprom_apme_mask=E1000_WUC_APME;if(eeprom_data&E1000_WUC_PHY_WAKE)adapter->flags2|=FLAG2_HAS_PHY_WAKEUP;}elseif(adapter->flags&FLAG_APME_IN_CTRL3){if(adapter->flags&FLAG_APME_CHECK_PORT_B&&(adapter->hw.bus.func==1))e1000_read_nvm(&adapter->hw,NVM_INIT_CONTROL3_PORT_B,1,&eeprom_data);elsee1000_read_nvm(&adapter->hw,NVM_INIT_CONTROL3_PORT_A,1,&eeprom_data);}/*fetchWoLfromEEPROM*/if(eeprom_data&eeprom_apme_mask)adapter->eeprom_wol|=E1000_WUFC_MAG;/**nowthatwehavetheeepromsettings,applythespecialcases*wheretheeeprommaybewrongortheboardsimplywon'tsupport*wakeonlanonaparticularport*/if(!(adapter->flags&FLAG_HAS_WOL))adapter->eeprom_wol=0;/*initializethewolsettingsbasedontheeepromsettings*/adapter->wol=adapter->eeprom_wol;device_set_wakeup_enable(&adapter->pdev->dev,adapter->wol);/*saveoffEEPROMversionnumber*/e1000_read_nvm(&adapter->hw,5,1,&adapter->eeprom_vers);/*resetthehardwarewiththenewsettings*/e1000e_reset(adapter);/**IfthecontrollerhasAMT,donotsetDRV_LOADuntiltheinterface*isup.Forallothercases,letthef/wknowthattheh/wisnow*underthecontrolofthedriver.*/if(!(adapter->flags&FLAG_HAS_AMT))e1000_get_hw_control(adapter);strcpy(netdev->name,"eth%d");/*初始化工作都做完了,可以注冊net_device啦*/err=register_netdev(netdev);if(err)gotoerr_register;/*carrieroffreportingisimportanttoethtoolevenBEFOREopen*/netif_carrier_off(netdev);/*打印一些信息出來*/e1000_print_device_info(adapter);return0;err_register:if(!(adapter->flags&FLAG_HAS_AMT))e1000_release_hw_control(adapter);err_eeprom:if(!e1000_check_reset_block(&adapter->hw))e1000_phy_hw_reset(&adapter->hw);err_hw_init:kfree(adapter->tx_ring);kfree(adapter->rx_ring);err_sw_init:if(adapter->hw.flash_address)iounmap(adapter->hw.flash_address);e1000e_reset_interrupt_capability(adapter);err_flashmap:iounmap(adapter->hw.hw_addr);err_ioremap:free_netdev(netdev);err_alloc_etherdev:pci_release_selected_regions(pdev,pci_select_bars(pdev,IORESOURCE_MEM));err_pci_reg:err_dma:pci_disable_device(pdev);returnerr;}下面是e1000e正式工作的代碼/***e1000_open-Calledwhenanetworkinterfaceismadeactive*@netdev:networkinterfacedevicestructure**Returns0onsuccess,negativevalueonfailure**Theopenentrypointiscalledwhenanetworkinterfaceismade*activebythesystem(IFF_UP).Atthispointallresourcesneeded*fortransmitandreceiveoperationsareallocated,theinterrupt*handlerisregisteredwiththeOS,thewatchdogtimerisstarted,*andthestackisnotifiedthattheinterfaceisready.**/staticinte1000_open(structnet_device*netdev){structe1000_adapter*adapter=netdev_priv(netdev);structe1000_hw*hw=&adapter->hw;interr;/*disallowopenduringtest*/if(test_bit(__E1000_TESTING,&adapter->state))return-EBUSY;netif_carrier_off(netdev);/*allocatetransmitdescriptors*/err=e1000e_setup_tx_resources(adapter);if(err)gotoerr_setup_tx;/*allocatereceivedescriptors*/err=e1000e_setup_rx_resources(adapter);if(err)gotoerr_setup_rx;e1000e_power_up_phy(adapter);adapter->mng_vlan_id=E1000_MNG_VLAN_NONE;if((adapter->hw.mng_cookie.status&E1000_MNG_DHCP_COOKIE_STATUS_VLAN))e1000_update_mng_vlan(adapter);/**IfAMTisenabled,letthefirmwareknowthatthenetwork*interfaceisnowopen*/if(adapter->flags&FLAG_HAS_AMT)e1000_get_hw_control(adapter);/**beforeweallocateaninterrupt,wemustbereadytohandleit.*SettingDEBUG_SHIRQinthekernelmakesitfireaninterrupt*assoonaswecallpci_request_irq,sowehavetosetupour*clean_rxhandlerbeforewedoso.*/e1000_configure(adapter);err=e1000_request_irq(adapter);if(err)gotoerr_req_irq;/**WorkaroundPCIeerratawithMSIinterruptscausingsomechipsetsto*ignoree1000eMSImessages,whichmeansweneedtotestourMSI*interruptnow*/if(adapter->int_mode!=E1000E_INT_MODE_LEGACY){err=e1000_test_msi(adapter);if(err){e_err("Interruptallocationfailed\n");gotoerr_req_irq;}}/*Fromhereonthecodeisthesamease1000e_up()*/clear_bit(__E1000_DOWN,&adapter->state);napi_enable(&adapter->napi);e1000_irq_enable(adapter);netif_start_queue(netdev);/*firealinkstatuschangeinterrupttostartthewatchdog*/ew32(ICS,E1000_ICS_LSC);return0;err_req_irq:e1000_release_hw_control(adapter);e1000_power_down_phy(adapter);e1000e_free_rx_resources(adapter);err_setup_rx:e1000e_free_tx_resources(adapter);err_setup_tx:e1000e_reset(adapter);returnerr;}linuxkernelppp框架分析2010-07-0920:12:40分類:LINUX1pppoekernel分析【2.6.16】staticint__initpppoe_init(void){interr=proto_register(&pppoe_sk_proto,0);//注冊socket協(xié)議,協(xié)議名稱PPPOEif(err)gotoout;err=register_pppox_proto(PX_PROTO_OE,&pppoe_proto);/*注冊pppoe協(xié)議類型,這個是在pppox內(nèi)部注冊這個注冊后將為socket提高一個重要的調(diào)用pppoe_create(structsocket*sock)在這個函數(shù)中sk->sk_backlog_rcv=pppoe_rcv_core;//這個函數(shù)提供了接收報(bào)文處理函數(shù),當(dāng)sk_state置PPPOX_BOUND標(biāo)志位的話,這個報(bào)文將首先在內(nèi)核處理ppp_input(),如果沒有改標(biāo)志位的話交到sock接收隊(duì)列sock_queue_rcv_skb(sk,skb)。將在用戶空間進(jìn)行處理sk->sk_state=PPPOX_NONE;sk->sk_type=SOCK_STREAM;sk->sk_family=PF_PPPOX;sk->sk_protocol=PX_PROTO_OE;*/if(err)gotoout_unregister_pppoe_proto;err=pppoe_proc_init();if(err)gotoout_unregister_pppox_proto;dev_add_pack(&pppoes_ptype);dev_add_pack(&pppoed_ptype);register_netdevice_notifier(&pppoe_notifier);out:returnerr;out_unregister_pppox_proto:unregister_pppox_proto(PX_PROTO_OE);out_unregister_pppoe_proto:proto_unregister(&pppoe_sk_proto);gotoout;}2pppGenericppp_input(),這個函數(shù)將判斷ppp報(bào)文是鏈路控制報(bào)文還是數(shù)據(jù)報(bào)文來決定處理方式如果為LinkControlProtocol那么就會將包圍掛到pch->file.rq,pppd來負(fù)責(zé)接收控制報(bào)文的隊(duì)列如果為其他類型的,將通過ppp_do_recv來對報(bào)文(如果是壓縮報(bào)文:解壓),去ppp頭。最后調(diào)用netif_rx(skb);來實(shí)現(xiàn)ppp網(wǎng)口收報(bào)文的處理。3netif_rx的作用?{structsoftnet_data*queue;unsignedlongflags;/*ifnetpollwantsit,pretendweneversawit*/if(netpoll_rx(skb))returnNET_RX_DROP;//首先如果存在netpoll啟用netpoll機(jī)制來接收處理報(bào)文if(!skb->tstamp.off_sec)net_timestamp(skb);//如果沒有注冊netpoll處理函數(shù),則放到cpu的接收隊(duì)列/**Thecodeisrearrangedsothatthepathisthemost*shortwhenCPUiscongested,butisstilloperating.*/local_irq_save(flags);queue=&__get_cpu_var(softnet_data);__get_cpu_var(netdev_rx_stat).total++;if(queue->input_pkt_queue.qlen<=netdev_max_backlog){if(queue->input_pkt_queue.qlen){enqueue:dev_hold(skb->dev);__skb_queue_tail(&queue->input_pkt_queue,skb);local_irq_restore(flags);returnNET_RX_SUCCESS;}//數(shù)據(jù)報(bào)文放到cpu接收隊(duì)列,由cpu默認(rèn)net_dev提供的poll函數(shù)即:process_backlog處理報(bào)文//下面引發(fā)軟中斷netif_rx_schedule(&queue->backlog_dev);gotoenqueue;}__get_cpu_var(netdev_rx_stat).dropped++;local_irq_restore(flags);kfree_skb(skb);returnNET_RX_DROP;}4疑問pppoe為什么還要占用pty/tty設(shè)備文件呢?5netif_receive_skb()函數(shù)分析負(fù)責(zé)把數(shù)據(jù)報(bào)文按packet_type丟到上層協(xié)議處理,所有數(shù)據(jù)往上送到協(xié)議棧的必經(jīng)之路ptype_all優(yōu)先處理,這個是RAWsocket走過的路徑之地,socket沒打開一個會加到這個鏈表中來。處理過程數(shù)據(jù)包被復(fù)制一份處理??梢钥吹綐蚰J教幚碓谡嬲臄?shù)據(jù)報(bào)文處理之前intnetif_receive_skb(structsk_buff*skb){structpacket_type*ptype,*pt_prev;structnet_device*orig_dev;intret=NET_RX_DROP;unsignedshorttype;/*ifwe'vegottenherethroughNAPI,checknetpoll*/if(skb->dev->poll&&netpoll_rx(skb))returnNET_RX_DROP;if(!skb->tstamp.off_sec)net_timestamp(skb);if(!skb->input_dev)skb->input_dev=skb->dev;orig_dev=skb_bond(skb);__get_cpu_var(netdev_rx_stat).total++;skb->h.raw=skb->nh.raw=skb->data;skb->mac_len=skb->nh.raw-skb->mac.raw;pt_prev=NULL;rcu_read_lock();#ifdefCONFIG_NET_CLS_ACTif(skb->tc_verd&TC_NCLS){skb->tc_verd=CLR_TC_NCLS(skb->tc_verd);gotoncls;}#endif//RAWsocket優(yōu)先處理,pppoediscovery要走這里list_for_each_entry_rcu(ptype,&ptype_all,list){if(!ptype->dev||ptype->dev==skb->dev){if(pt_prev)ret=deliver_skb(skb,pt_prev,orig_dev);pt_prev=ptype;}}#ifdefCONFIG_NET_CLS_ACTif(pt_prev){ret=deliver_skb(skb,pt_prev,orig_dev);pt_prev=NULL;/*nooneelseshouldprocessthisafter*/}else{skb->tc_verd=SET_TC_OK2MUNGE(skb->tc_verd);}ret=ing_filter(skb);if(ret==TC_ACT_SHOT||(ret==TC_ACT_STOLEN)){kfree_skb(skb);gotoout;}skb->tc_verd=0;ncls:#endifhandle_diverter(skb);//先處理橋模式情況if(handle_bridge(&skb,&pt_prev,&ret,orig_dev))gotoout;type=skb->protocol;//再如果橋模式?jīng)]有轉(zhuǎn)發(fā)掉,進(jìn)入路由模式處理list_for_each_entry_rcu(ptype,&ptype_base[ntohs(type)&15],list){if(ptype->type==type&&(!ptype->dev||ptype->dev==skb->dev)){if(pt_prev)ret=deliver_skb(skb,pt_prev,orig_dev);pt_prev=ptype;}}if(pt_prev){ret=pt_prev->func(skb,skb->dev,pt_prev,orig_dev);}else{kfree_skb(skb);/*Jamal,nowyouwillnotabletoescapeexplaining*mehowyouweregoingtousethis.:-)*/ret=NET_RX_DROP;}out:rcu_read_unlock();returnret;}6net_dev_init這個函數(shù)告訴我們很多東西staticint__initnet_dev_init(void){inti,rc=-ENOMEM;BUG_ON(!dev_boot_phase);net_random_init();if(dev_proc_init())gotoout;if(netdev_sysfs_init())gotoout;INIT_LIST_HEAD(&ptype_all);for(i=0;i<16;i++)INIT_LIST_HEAD(&ptype_base[i]);for(i=0;i<ARRAY_SIZE(dev_name_head);i++)INIT_HLIST_HEAD(&dev_name_head[i]);for(i=0;i<ARRAY_SIZE(dev_index_head);i++)INIT_HLIST_HEAD(&dev_

溫馨提示

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

最新文檔

評論

0/150

提交評論