亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

Chinaunix

標題: 【原】Linux-千兆網(wǎng)卡驅(qū)動實現(xiàn)機制淺析 [打印本頁]

作者: Minit    時間: 2009-03-25 20:08
標題: 【原】Linux-千兆網(wǎng)卡驅(qū)動實現(xiàn)機制淺析
Linux-千兆網(wǎng)卡驅(qū)動實現(xiàn)機制淺析

2009-3

by Minit

tangwen1123@163.com



     本人對網(wǎng)卡驅(qū)動沒有太多實踐經(jīng)驗,此分析純屬學習總結(jié)所為,希望各位CU的大牛能及時的提出其中的問題,也歡迎大家積極拍磚,我拋磚引玉,你們拋磚給我吧^_^。我相信這其中的分析一定有不太正確的地方,希望各位能夠不吝賜教。如果轉(zhuǎn)載請注明出處。


1.引言
    本分析主要針對e1000網(wǎng)卡,驅(qū)動源碼為7.3.20-k2。本文的目的不是為了講述如何編寫驅(qū)動程序,主要是分析網(wǎng)卡驅(qū)動內(nèi)部的實現(xiàn)機制。通過此分析,希望可以理解驅(qū)動程序中的各個部分的關(guān)系,對網(wǎng)卡發(fā)送和接收數(shù)據(jù)包有直觀的了解,同時也希望對設計網(wǎng)卡驅(qū)動程序有幫助。由于網(wǎng)卡驅(qū)動程序與硬件和操作系統(tǒng)都有很緊密的聯(lián)系,故要把某些問題完全弄清楚,需要很多的經(jīng)驗與相關(guān)知識,介于自身的水平有限,且自身經(jīng)驗較少,故肯定存在很多問題,希望本文的讀者發(fā)現(xiàn)了問題不吝與作者聯(lián)系。

2.網(wǎng)卡驅(qū)動的體系結(jié)構(gòu)
       網(wǎng)卡作為一個PCI設備,其必須遵守相應的PCI規(guī)范,即必須為網(wǎng)卡定義相應的標識號,每個PCI外設由一個總線編號、一個設備編號及一個功能編號來標識。網(wǎng)卡驅(qū)動程序則需要定義相應的pci_device_id結(jié)構(gòu)來表示其支持的PCI外設的標識,通過在驅(qū)動程序的pci_device_id中查找設備標識號,將驅(qū)動程序與設備聯(lián)系起來。網(wǎng)卡作為PCI設備,其包括兩類空間,一種是配置空間, CPU不能直接訪問,訪問這個空間,需要借助BIOS功能;另一種是普通的控制寄存器空間,這部分經(jīng)過映射后,CPU可以直接訪問控制。
       在硬件加電初始化時,BIOS統(tǒng)一檢查所有的PCI設備,并為每個設備分配一個物理地址,該地址通過BIOS獲得并寫到設備的配置空間內(nèi),驅(qū)動程序就可以將網(wǎng)卡的普通控制寄存器映射到一段內(nèi)存空間內(nèi),CPU通過訪問映射后的虛擬地址來操控網(wǎng)卡的寄存器。當操作系統(tǒng)初始化時,其為每個PCI設備分配一個pci_dev結(jié)構(gòu),并將前面分配的物理地址寫到pci_devresource字段中。在網(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)卡設備,則是通過向映射后的網(wǎng)卡的相應寄存器寫入命令實現(xiàn),其通過映射后的首地址及相應的寄存器偏移量找到該寄存器的位置,然后通過函數(shù)writeb()寫該寄存器。有關(guān)相關(guān)寄存器對應的偏移量,一般是通過網(wǎng)卡的相關(guān)的datasheet獲得。如果要獲取網(wǎng)卡的MAC地址,則一般通過函數(shù)readb()讀取首地址開始的前六位內(nèi)容即可得到。
    通過pci_read_config_pci_write_config_系列函數(shù)可以讀寫網(wǎng)卡的配置空間,如開啟網(wǎng)卡設備就是將網(wǎng)卡配置空間的command域置1,從而設備就可以將寄存器映射到內(nèi)存。如通過函數(shù)pci_read_config_byte(pci_dev pdev,PCI_INTERRUPT_LINE,&irq)獲得設備所分配的中斷號并保存在irq中。pci_read_config_pci_write_config_系列函數(shù)實際上是調(diào)用pci_bus_read_config_pci_bus_write_config_系列函數(shù)實現(xiàn)的,這些函數(shù)實際操作網(wǎng)卡對應的PCI總線結(jié)構(gòu)。有關(guān)PCI寄存器的配置空間可參考《Linux Device Driver 3rd》或《PCI Bus Demystified》。
       網(wǎng)卡作為一個規(guī)范的PCI設備,其對應的結(jié)構(gòu)體pci_dev代表了網(wǎng)卡設備,體現(xiàn)了作為PCI設備所應有的規(guī)范。網(wǎng)卡的網(wǎng)絡傳輸性質(zhì),實際是通過另一結(jié)構(gòu)體net_device來體現(xiàn)的,該結(jié)構(gòu)體的初始化由網(wǎng)卡驅(qū)動程序?qū)崿F(xiàn)。內(nèi)核中對網(wǎng)卡的操作,其實質(zhì)就是對net_device結(jié)構(gòu)的操作,pci_devnet_device都表示網(wǎng)卡設備,只是體現(xiàn)的角度不一樣。net_device是對特定適配器的抽象,其為上層協(xié)議提供了統(tǒng)一的接口,網(wǎng)卡驅(qū)動則基于特定適配器實現(xiàn)了這一抽象。

       PCI
設備的驅(qū)動程序由pci_driver結(jié)構(gòu)體表示,故網(wǎng)卡驅(qū)動應該是該結(jié)構(gòu)體的一個實例,在該結(jié)構(gòu)體中應該要定義實現(xiàn)與網(wǎng)卡相關(guān)的參數(shù)以及相應的操作。網(wǎng)卡驅(qū)動實際操作的特定適配器,是由與硬件相關(guān)的adapter所表示的結(jié)構(gòu)體,adapter體現(xiàn)了大部分與硬件相關(guān)的屬性,網(wǎng)卡驅(qū)動除了直接對pci_dev結(jié)構(gòu)操作外,其他對網(wǎng)卡設備的操作基本是對adapter結(jié)構(gòu)體的操作。adapter體現(xiàn)了net_devicepci_dev的關(guān)聯(lián),也實現(xiàn)了網(wǎng)絡設備的適配器無關(guān)性。與網(wǎng)卡設備pci_dev的通信是通過adapter來實現(xiàn)的,而這個實現(xiàn)則是網(wǎng)卡驅(qū)動所要完成的任務。

    下面圖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)相應的硬件無關(guān)性外,還管理了發(fā)送與接收數(shù)據(jù)包的相應緩沖空間,網(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)卡工作時起著最核心的作用,同時也是編寫驅(qū)動程序必須操作的結(jié)構(gòu)體。
2-1 網(wǎng)卡驅(qū)動程序的主要數(shù)據(jù)結(jié)構(gòu)


[ 本帖最后由 Minit 于 2009-3-25 20:35 編輯 ]
作者: Minit    時間: 2009-03-25 20:09
3.  網(wǎng)卡設備的注冊與初始化
        網(wǎng)卡設備的注冊與初始化是在其相關(guān)的驅(qū)動程序的e1000_probe()函數(shù)中實現(xiàn)的,有關(guān)設備如何與該驅(qū)動相關(guān)聯(lián),以及如何調(diào)用到e1000_probe()的,在此不作介紹。在函數(shù)e1000_probe()中首先調(diào)用函數(shù)pci_enable_device()啟用設備,然后聲明了DMA空間,接著調(diào)用函數(shù)alloc_etherdev()生成結(jié)構(gòu)體net_device,該結(jié)構(gòu)體就表示了網(wǎng)卡設備,對net_device的參數(shù)進行了初始化后,調(diào)用register_netdev()注冊該設備。
        以上僅是對設備的注冊,設備的初始化主要包括對兩個結(jié)構(gòu)體的賦值,一個是net_device,另一個則是e1000_adapter。對e1000_adapter的初始化包括對其中的e1000_hw結(jié)構(gòu)的初始化,其調(diào)用函數(shù)e1000_sw_init()實現(xiàn)。在對e1000_hw的初始化過程中使用了ioremap()實現(xiàn)了網(wǎng)卡硬件地址與內(nèi)存虛擬地址之間的映射。
        對網(wǎng)卡設備進行撤銷則調(diào)用函數(shù)free_netdev()實現(xiàn)。有關(guān)網(wǎng)卡設備注冊與初始化的更詳細的過程可以參考《Understanding Linux Network Internals》。


[ 本帖最后由 Minit 于 2009-3-25 20:15 編輯 ]
作者: Minit    時間: 2009-03-25 20:10
4.  網(wǎng)卡設備的啟動與關(guān)閉
       網(wǎng)卡設備啟動時首先調(diào)用函數(shù)e1000_open(),在該函數(shù)中調(diào)用e1000_request_irq()申請中斷號及其相應的中斷處理程序e1000_intr(),其實際是調(diào)用request_irq()函數(shù)來實現(xiàn)的。在函數(shù)e1000_open()中調(diào)用e1000_setup_all_tx_resources()根據(jù)發(fā)送隊列數(shù)建立發(fā)送緩沖區(qū),每個緩沖區(qū)的建立由函數(shù)e1000_setup_tx_resources()實現(xiàn),在e1000_setup_tx_resources()中,主要是對描述發(fā)送緩沖區(qū)的結(jié)構(gòu)體e1000_tx_ring的初始化,其將DMA緩沖區(qū)與網(wǎng)卡所映射的虛擬地址空間聯(lián)系起來,使用函數(shù)pci_alloc_consistent()實現(xiàn)一致性映射。而虛擬地址空間與網(wǎng)卡的物理地址相對應,故而這三種空間就對應了起來,DMA也就可以在此基礎上實現(xiàn)了,當數(shù)據(jù)包內(nèi)容被映射到DMA緩沖區(qū)后,其將完全由設備操控。DMA的緩沖區(qū)的初始化在驅(qū)動程序的e1000_probe()函數(shù)中實現(xiàn)。e1000_open()函數(shù)會調(diào)用e1000_up()對網(wǎng)卡的一些相關(guān)的軟硬件參數(shù)與空間進行配置,如硬件寄存器的讀寫,數(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進行初始化。接收緩沖空間的結(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()實現(xiàn),其會首先關(guān)閉中斷,然后釋放中斷號,并且會釋放網(wǎng)卡申請的相應的空間。



[ 本帖最后由 Minit 于 2009-3-25 20:56 編輯 ]
作者: Minit    時間: 2009-03-25 20:11
5.  發(fā)送與接收數(shù)據(jù)包
數(shù)據(jù)包的發(fā)送:

                                                                 圖5-1 發(fā)送數(shù)據(jù)包的結(jié)構(gòu)圖及相互關(guān)系

      根據(jù)發(fā)送隊列數(shù)num_tx_queues建立相應的發(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ù)的位置及準備接收的位置,當有新的數(shù)據(jù)包要發(fā)送時,首先由上層協(xié)議調(diào)用e1000_xmit_frame(),在該函數(shù)中接著調(diào)用e1000_tx_queue()根據(jù)相應的參數(shù)找到緩沖塊存放,緩沖塊的初始化則由函數(shù)e1000_tx_map()實現(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()進行流式映射,的映射方向為PCI_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ū)中的一段總線地址相對應,該總線地址由e1000_tx_ring結(jié)構(gòu)中的dma成員保存,這種映射關(guān)系在對開啟網(wǎng)卡時就實現(xiàn)了,其與在發(fā)送數(shù)據(jù)包時映射的總線地址有區(qū)別,后者是在發(fā)送時動態(tài)進行的。
數(shù)據(jù)包的接收

                                                                圖5-2 接收數(shù)據(jù)包的結(jié)構(gòu)圖及相互關(guān)系
      根據(jù)接收隊列數(shù)num_rx_queues建立相應的接收緩沖區(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ù)的位置及準備接收的位置,當有新的數(shù)據(jù)包要到來時,則根據(jù)這兩個參數(shù)找到相應的區(qū)域存放。對于需要分片接收的數(shù)據(jù)包則利用了ps_page和ps_page_dma來實現(xiàn),參數(shù)cpu指定了該接收緩沖隊列所屬的處理器?偩地址與要發(fā)送的虛擬地址間的映射方向為PCI_DMA_FROMDEVICE,控制總線會把總線地址的內(nèi)容映射到虛擬地址空間內(nèi)。接收數(shù)據(jù)包的相關(guān)結(jié)構(gòu)圖及相互關(guān)系如圖5-2所示。
     當有新的數(shù)據(jù)包到達時,首先觸動中斷處理函數(shù)e1000_intr(),在函數(shù)中會為新來的數(shù)據(jù)包在緩沖塊數(shù)組buffer_info中找到一個新的緩沖塊位置,并完成e1000_buffer結(jié)構(gòu)的賦值。數(shù)據(jù)包的接收其實就是將總線地址指向的內(nèi)容拷貝到skb中,然后根據(jù)skb中的協(xié)議將其傳給相應的上層協(xié)議的接收函數(shù)。


[ 本帖最后由 Minit 于 2009-3-25 20:57 編輯 ]
作者: Minit    時間: 2009-03-25 20:11
6.  網(wǎng)卡驅(qū)動程序的設計
      編寫網(wǎng)卡驅(qū)動程序,需要對以下三類結(jié)構(gòu)體進行相應的操作:
     1.與網(wǎng)絡協(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總線進行數(shù)據(jù)包傳送的DMA緩沖區(qū)以及表示網(wǎng)卡的pci_dev結(jié)構(gòu)體。
    設計網(wǎng)卡驅(qū)動時,需要針對以上的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)相應的功能,如對sk_buff結(jié)構(gòu)的操作實現(xiàn)對數(shù)據(jù)包的有效控制;對net_device結(jié)構(gòu)的操作可以對網(wǎng)卡進行操作(如開啟、關(guān)閉等),可以發(fā)送數(shù)據(jù)包以及輪詢數(shù)據(jù)包,可以制定網(wǎng)卡的相應的定時操作以及統(tǒng)計數(shù)據(jù)包,可以為用戶提供配置功能(ethtool)等。在設計網(wǎng)卡驅(qū)動時,需要考慮如何與上層協(xié)議的協(xié)調(diào)以及對底層總線地址的控制。
    有關(guān)網(wǎng)卡驅(qū)動程序更詳細的設計流程可以參考《Essential Linux Device Driver》及《Linux Device Driver 3rd》。


7.  總結(jié)
     本文的分析重點為網(wǎng)卡驅(qū)動中涉及到的重要數(shù)據(jù)結(jié)構(gòu),以及發(fā)送和接收數(shù)據(jù)包的實現(xiàn),對這些實現(xiàn)機制了解后,對于設計和實現(xiàn)驅(qū)動程序應該會有幫助,因為該機制本身難度很大,加上作者水平有限,其中的分析結(jié)論不能保證完全正確。


尚有不能明確的問題,希望大牛們能幫我解答:
1.PCI總線地址與網(wǎng)卡物理地址這兩個之間的關(guān)系,我一直不是很清楚,所以以上的分析都未明確說明,在畫圖時都忽略了這部分細節(jié);
2.DMA buffer在接收包和發(fā)包時這其中的ring是如何詳細調(diào)度的;
······未完待續(xù)

[ 本帖最后由 Minit 于 2009-3-25 21:00 編輯 ]
作者: scutan    時間: 2009-03-25 20:24
強悍,拜讀!
作者: Minit    時間: 2009-03-25 20:30
標題: 回復 #6 scutan 的帖子
我來支持版主的工作,呵呵~
這個帖子估計會有很多磚頭的,我做好心理準備了,O(∩_∩)O~
有時間的話我會再深入分析一下,這只做拋磚引玉咯。
作者: Godbach    時間: 2009-03-25 21:27
Minit兄有段時間沒來了。一來又出好文章了,最近偶也正在了解網(wǎng)卡驅(qū)動,正好參考這篇文章啊。
作者: Godbach    時間: 2009-03-25 21:48
歡迎Minit兄有時間的時候把分析e1000_probe()的總結(jié)分享一下。
作者: Solaris12    時間: 2009-03-25 22:08
原帖由 Minit 于 2009-3-25 20:11 發(fā)表
尚有不能明確的問題,希望大牛們能幫我解答:
1.PCI總線地址與網(wǎng)卡物理地址這兩個之間的關(guān)系,我一直不是很清楚,所以以上的分析都未明確說明,在畫圖時都忽略了這部分細節(jié);
2.DMA buffer在接收包和發(fā)包時這其中的ring是如何詳細調(diào)度的; ...


PCI總線地址你指什么?網(wǎng)卡物理地址你又指什么?

ring調(diào)度指什么?

偶自認對Intel的千兆網(wǎng)卡很熟悉,對PCI這塊兒的機制也應該很了解了,但是還是看不明白你問什么。
作者: Solaris12    時間: 2009-03-25 22:16
原帖由 Minit 于 2009-3-25 20:08 發(fā)表
網(wǎng)卡作為一個PCI設備,其必須遵守相應的PCI規(guī)范,即必須為網(wǎng)卡定義相應的標識號,每個PCI外設由一個總線編號、一個設備編號及一個功能編號來標識。網(wǎng)卡驅(qū)動程序則需要定義相應的pci_device_id結(jié)構(gòu)來表示其支持的PCI外設的標識,通過在驅(qū)動程序的pci_device_id中查找設備標識號,將驅(qū)動程序與設備聯(lián)系起來。網(wǎng)卡作為PCI設備,其包括兩類空間,一種是配置空間, CPU不能直接訪問,訪問這個空間,需要借助BIOS功能;另一種是普通的控制寄存器空間,這部分經(jīng)過映射后,CPU可以直接訪問控制 ...


上面的說法有問題。
配置空間訪問除了借助BIOS,還可以利用MMIO. 詳細的介紹可以看我的blog,PCI規(guī)范學習筆記。

實際上,操作系統(tǒng)在枚舉完設備后,對e1000配置空間訪問是靠MMIO. 最簡單的一般也就是讀下device id什么的。

當然,中斷注冊這快,是要訪問網(wǎng)卡的配置空間的相關(guān)區(qū)域的,intx和MSi訪問的位置不一樣,這看中斷注冊的實現(xiàn)即可。

至于內(nèi)存空間,e1000的shared code在Solaris上只提供了兩個BAR的定義。Linux的e1000應該和Solaris共享一套shared code,所以,應該差別不大。
作者: Solaris12    時間: 2009-03-25 22:21
原帖由 Minit 于 2009-3-25 20:08 發(fā)表
有關(guān)相關(guān)寄存器對應的偏移量,一般是通過網(wǎng)卡的相關(guān)的datasheet獲得。


其實Intel以多種License發(fā)布它的驅(qū)動的shared code,他們也叫hardware code, 幾乎所有的寄存器的偏移量都是在shared code的頭文件定義的。

BSD和Solaris和Linux的developer用的shared code是一樣的。
作者: 想飛的蝸牛    時間: 2009-03-25 22:23
很強大 拜讀
作者: Solaris12    時間: 2009-03-25 22:31
原帖由 Minit 于 2009-3-25 20:08 發(fā)表
通過pci_read_config_和pci_write_config_系列函數(shù)可以讀寫網(wǎng)卡的配置空間,如開啟網(wǎng)卡設備就是將網(wǎng)卡配置空間的command域置1,從而設備就可以將寄存器映射到內(nèi)存。如通過函數(shù)pci_read_config_byte(pci_dev pdev,PCI_INTERRUPT_LINE,&irq)獲得設備所分配的中斷號并保存在irq中。pci_read_config_和pci_write_config_系列函數(shù)實際上是調(diào)用pci_bus_read_config_和pci_bus_write_config_系列函數(shù)實現(xiàn)的,這些函數(shù)實際操作網(wǎng)卡對應的PCI總線結(jié)構(gòu)。有關(guān)PCI寄存器的配置空間可參考《Linux Device Driver 3rd》或《PCI Bus Demystified》。 ...


關(guān)于設備配置空間的訪問,還有一點就是其實支持MMIO的配置空間其實早就有一個物理內(nèi)存編址了。

所謂影射,是把物理地址影射到內(nèi)核虛擬地址空間,以便后續(xù)的配置空間訪問可以用mov指令來操作。
作者: Minit    時間: 2009-03-25 22:39
標題: 回復 #12 Solaris12 的帖子
感謝Solaris12大牛的以上回復,其實在把這個分析貼出來的時候,就猜測到這部分會有較大的問題,一方面尤其是涉及到PCI規(guī)范部分,這里一直沒有弄得很明白;另一方面就是對intel驅(qū)動的shared code部分的掌握,這可能是驅(qū)動編寫人員會經(jīng)常涉及到的部分。以上兩個部分就跟硬件很相關(guān)了,且需要較多的實踐經(jīng)驗才會很清楚,對于我自己,可能以前更多的關(guān)注的是軟件中網(wǎng)絡層部分的內(nèi)容,所以這里的分析絕對是種嘗試,也確實比較有難度,也正好借助這個平臺大家一起交流學習,
不過相信這個帖子可以給下一步的分析和深入找到比較好的切入點和方向,希望可以把linux下驅(qū)動這塊有個較全面和準確的了解。
再次謝謝Solaris12兄的指點,希望以后多多指教哈。

有關(guān)你上面提出的問題,我會好好研究并再認真分析下,有歧義的地方盡量修改。
作者: Solaris12    時間: 2009-03-25 22:40
原帖由 Minit 于 2009-3-25 20:10 發(fā)表
其將DMA緩沖區(qū)與網(wǎng)卡所映射的虛擬地址空間聯(lián)系起來,使用函數(shù)pci_alloc_consistent()實現(xiàn)一致性映射。而虛擬地址空間與網(wǎng)卡的物理地址相對應,故而這三種空間就對應了起來,DMA也就可以在此基礎上實現(xiàn)了


這塊兒顯然是有錯誤的。

e1000 PCI memory空間有兩個寄存器用來標識 ring的位置。ring實際上是一塊連續(xù)的物理內(nèi)存,是host memory而不是設備上的memory。

而ring里的descriptor要的是一個host memory buffer的物理地址,而且要求這個buffer的物理頁是連續(xù)的。任何塊物理內(nèi)存連續(xù)的buffer想做DMA buffer用,就必須要知道它的物理地址是什么。

邏輯上, ring里的descriptor指向的也是host memory,且物理地址連續(xù)。
作者: Solaris12    時間: 2009-03-25 22:45
原帖由 Minit 于 2009-3-25 22:39 發(fā)表
感謝Solaris12大牛的以上回復,其實在把這個分析貼出來的時候,就猜測到這部分會有較大的問題,一方面尤其是涉及到PCI規(guī)范部分,這里一直沒有弄得很明白;另一方面就是對intel驅(qū)動的shared code部分的掌握,這可 ...


大牛不敢自稱。
恰好我在Solaris的Intel的千兆網(wǎng)卡e1000/igb和PCI的底層實現(xiàn)這兩部分都有點經(jīng)驗。雖然Linux我不懂,但驅(qū)動和PCI這塊兒基本是硬件規(guī)定死了的,差異并不大。
作者: Minit    時間: 2009-03-25 22:45
標題: 回復 #8 Godbach 的帖子
呵呵~ 一起學習哈,我這個分析只是個初步的,有好多問題都還需要搞明白,以后多多交流哦。

這篇分析就是給像Solaris12等這樣的大牛來點評和指導的 :wink:
作者: Minit    時間: 2009-03-25 22:54
標題: 回復 #16 Solaris12 的帖子
嗯。這個地方也是我一直沒搞明白的地方,所以在前面會提出PCI總線地址與物理地址的關(guān)系,其實就是對DMA buffer和ring這塊沒理解清楚,經(jīng)過您的提示,我大概明白了這個意思,也會再深入的分析下這塊。:wink:
作者: emmoblin    時間: 2009-03-25 22:54
寫的很好,其實好多人都看過e1000的網(wǎng)卡驅(qū)動,但總結(jié)的人還是少啊
作者: Minit    時間: 2009-03-25 22:59
標題: 回復 #17 Solaris12 的帖子
呵呵~  感謝您的提示,希望你發(fā)現(xiàn)有其他問題時也不吝提出。

針對您提出的問題,我下來再好好的整理和完善下,有不懂的地方也希望可以向您請教。

PS:牛人都不是自稱的,是我們稱呼你的。
作者: Minit    時間: 2009-03-25 23:02
標題: 回復 #20 emmoblin 的帖子
所以我斗膽來拋磚引玉了~  呵呵
作者: Godbach    時間: 2009-03-25 23:07
牛人都不是自稱的,是我們稱呼你的。

通常技術(shù)做的越久,越發(fā)現(xiàn)不懂的更多
作者: Minit    時間: 2009-03-25 23:09
原帖由 Godbach 于 2009-3-25 21:48 發(fā)表
歡迎Minit兄有時間的時候把分析e1000_probe()的總結(jié)分享一下。


e1000_probe()這塊的分析太龐大了,我其實也只看了大概,有時間希望與Godbach兄交流下網(wǎng)卡驅(qū)動的學習心得哦,呵呵~ 這一塊我只是個初學者。
作者: Solaris12    時間: 2009-03-25 23:09
原帖由 Minit 于 2009-3-25 23:02 發(fā)表
所以我斗膽來拋磚引玉了~  呵呵



看明白,講出來,寫清楚 是三種不同的境界。

能堅持寫出來并且寫清楚對自己水平的提高有很大幫助的。
作者: Minit    時間: 2009-03-25 23:14
原帖由 Solaris12 于 2009-3-25 23:09 發(fā)表



看明白,講出來,寫清楚 是三種不同的境界。

能堅持寫出來并且寫清楚對自己水平的提高有很大幫助的。


嗯,是的。
作者: scutan    時間: 2009-03-25 23:22
原帖由 Solaris12 于 2009-3-25 23:09 發(fā)表



看明白,講出來,寫清楚 是三種不同的境界。

能堅持寫出來并且寫清楚對自己水平的提高有很大幫助的。


嗯,往往在寫的時候會強迫自己去把一些似是而非的東西搞明白。
作者: Godbach    時間: 2009-03-25 23:29
原帖由 Minit 于 2009-3-25 23:09 發(fā)表


e1000_probe()這塊的分析太龐大了,我其實也只看了大概,有時間希望與Godbach兄交流下網(wǎng)卡驅(qū)動的學習心得哦,呵呵~ 這一塊我只是個初學者。


呵呵,慚愧。我也是簡單的在看一下。前兩天看了DM9000A網(wǎng)卡的驅(qū)動,相對簡單一些。e1000的probe是比較麻煩。
作者: Minit    時間: 2009-03-25 23:32
原帖由 scutan 于 2009-3-25 23:22 發(fā)表


嗯,往往在寫的時候會強迫自己去把一些似是而非的東西搞明白。

嗯哈~ 其實在寫這個分析的時候,我還是鼓起了較大的勇氣的,因為對較多東西都是似是而非的。貼出來的時候也需要點點勇氣,呵呵,因為這個不像學生交作業(yè),這個是有可能誤導別人的。
作者: Godbach    時間: 2009-03-25 23:34
現(xiàn)在好像probe函數(shù)都隨著直接驅(qū)動注冊了。DM9000A上比較簡單,是在insmod的時候,先probe。簡單的讀取某個IO地址的數(shù)據(jù),得到設別的ID號,如果可以正確獲取,才會進一步request_region, 填充net_device。否則會直接返回錯誤,告訴系統(tǒng)沒有該設備。
作者: epegasus    時間: 2009-03-26 09:29
原帖由 Minit 于 2009-3-25 22:54 發(fā)表
嗯。這個地方也是我一直沒搞明白的地方,所以在前面會提出PCI總線地址與物理地址的關(guān)系,其實就是對DMA buffer和ring這塊沒理解清楚,經(jīng)過您的提示,我大概明白了這個意思,也會再深入的分析下這塊。:wink:

關(guān)于這塊我也是一直非常迷糊,期盼LZ下1個精華.
作者: eexplorer    時間: 2009-03-26 12:41
原帖由 Minit 于 2009-3-25 20:11 發(fā)表
尚有不能明確的問題,希望大牛們能幫我解答:
1.PCI總線地址與網(wǎng)卡物理地址這兩個之間的關(guān)系,我一直不是很清楚,所以以上的分析都未明確說明,在畫圖時都忽略了這部分細節(jié);
2.DMA buffer在接收包和發(fā)包時這其中的ring是如何詳細調(diào)度的;


我不是大牛,但是以前做過無線網(wǎng)卡的驅(qū)動,所以可以回答你的問題

以發(fā)包為例。在e1000_setup_tx_resources時,通過pci_alloc_consistent分配了一段物理內(nèi)存用于存放tx descriptors, 網(wǎng)卡和CPU之間的關(guān)于發(fā)包的控制就是通過tx descriptors進行的。在隨后的e1000_configure_tx中,會把這個tx ring buffer的dma地址和長度寫到TDLEN和TDBA兩個register里,這樣網(wǎng)卡就知道了tx ring buffer的dma地址。
發(fā)包時,它先通過DMA讀入tx descriptors,再根據(jù)tx desciptor里的信息:
1.  buffer_addr這是數(shù)據(jù)包的dma地址,根據(jù)這個地址,網(wǎng)卡通過DMA可以把數(shù)據(jù)包從主機內(nèi)存讀到板卡上的memroy里
2. 控制信息:如何處理這個數(shù)據(jù)包,要不要作hw checksum等等

在e1000_xmit_frame中,主要的工作就是,找到相應的tx ring buffer,根據(jù)next_to_use找到相應空閑的tx slot,把network stack傳下來的skb作相應的映射(e1000_tx_map, 通過pci_map_single得到sk_buffer->data的dma address),最后通過e1000_tx_queue,把數(shù)據(jù)包的dma地址寫到tx descriptor的buffer_addr中,再把next_to_use寫到相應的register中(tx_ring->tdt)。寫入這個register后,網(wǎng)卡就會知道有新的包要發(fā)送,它就啟動DMA先讀入tx descriptor, 再讀入數(shù)據(jù)包,最后把包發(fā)送出去。

數(shù)據(jù)包發(fā)送完成后,網(wǎng)卡會把發(fā)送狀態(tài)更新到tx descriptor中,并中斷cpu,在e1000_clean_tx_irq中,會根據(jù)tx_descriptor中的發(fā)送狀態(tài),去釋放相應的skb。

關(guān)于pci_alloc_consistent, pci_map_single可以參考Documentation/DMA-mapping.txt文檔。
作者: epegasus    時間: 2009-03-26 12:52
>>>>網(wǎng)卡通過DMA可以把數(shù)據(jù)包從主機內(nèi)存讀到板卡上的memroy里
LZ的可不可詳細點講將DMA,網(wǎng)卡,總線,CPU的關(guān)系?雖然微機原理也有講過,仍然感覺不清楚.
作者: Solaris12    時間: 2009-03-26 13:19
原帖由 eexplorer 于 2009-3-26 12:41 發(fā)表

1.  buffer_addr這是數(shù)據(jù)包的dma地址,根據(jù)這個地址,網(wǎng)卡通過DMA可以把數(shù)據(jù)包從主機內(nèi)存讀到板卡上的memroy里
2. 控制信息:如何處理這個數(shù)據(jù)包,要不要作hw checksum等等.



1. 對于e1000網(wǎng)卡rx和tx各有一個FIFO,DMA應該在host memory和FIFO之間做的。
例如,rx FIFO滿了,再進來的包,只能被drop掉了。

2. e1000g的可以分data descritpor和context descriptor,context  descriptor就是所謂存控制信息的地方,hw checksum, tso都用這個東東。

[ 本帖最后由 Solaris12 于 2009-3-26 13:22 編輯 ]
作者: Solaris12    時間: 2009-03-26 13:21
原帖由 epegasus 于 2009-3-26 12:52 發(fā)表
>>>>網(wǎng)卡通過DMA可以把數(shù)據(jù)包從主機內(nèi)存讀到板卡上的memroy里
LZ的可不可詳細點講將DMA,網(wǎng)卡,總線,CPU的關(guān)系?雖然微機原理也有講過,仍然感覺不清楚.


建議你看看書,然后問出具體的問題來
作者: Solaris12    時間: 2009-03-26 13:25
原帖由 eexplorer 于 2009-3-26 12:41 發(fā)表


在e1000_xmit_frame中,主要的工作就是,找到相應的tx ring buffer,根據(jù)next_to_use找到相應空閑的tx slot,把network stack傳下來的skb作相應的映射(e1000_tx_map,通過pci_map_single得到sk_buffer->data的dma address),最后通過e1000_tx_queue,把數(shù)據(jù)包的dma地址寫到tx descriptor的buffer_addr中,再把next_to_use寫到相應的register中(tx_ring->tdt)。寫入這個register后,網(wǎng)卡就會知道有新的包要發(fā)送,它就啟動DMA先讀入tx descriptor, 再讀入數(shù)據(jù)包,最后把包發(fā)送出去。...


所謂的 DMA address, 在這里就是DMA buffer的物理地址,DMA buffer是host memory, 所以就是一個host memory的物理地址。
作者: eexplorer    時間: 2009-03-26 13:33
原帖由 epegasus 于 2009-3-26 12:52 發(fā)表
>>>>網(wǎng)卡通過DMA可以把數(shù)據(jù)包從主機內(nèi)存讀到板卡上的memroy里
LZ的可不可詳細點講將DMA,網(wǎng)卡,總線,CPU的關(guān)系?雖然微機原理也有講過,仍然感覺不清楚.


假設現(xiàn)在系統(tǒng)有1G 物理memroy,映射在0-1G的物理地址空間。

在intel x86 架構(gòu)下,CPU要訪問內(nèi)存,需要將要訪問的物理地址通過system front bus發(fā)給北橋中的memory controller。
要訪問內(nèi)存中 0x0那塊memory的話,cpu會將0x0放在他的address bus,發(fā)送到memory controller.

PCI bus address的話是PCI device要訪問內(nèi)存所需要的地址。所以通常driver要告訴device一塊memory的bus address,這樣device自己內(nèi)部的dma engine要作dma時,通過bus address可以訪問到正確的memory。

在x86下,bus address == physical address,既device和CPU看到的physical memory mapping是一樣的。所以virt_to_bus == virt_to_phy

有些架構(gòu)下,bus address 和 physical address 不一樣,既device要訪問memory中0x0的那塊地方,它需要一個不同的地址(比如有一個2G的偏移)0x80000000。這樣的話,CPU如果要device去讀memory 0x0那塊地方的數(shù)據(jù),就需要把0x80000000作為dma address發(fā)給device.
作者: eexplorer    時間: 2009-03-26 13:40
原帖由 Solaris12 于 2009-3-26 13:25 發(fā)表


所謂的 DMA address, 在這里就是DMA buffer的物理地址,DMA buffer是host memory, 所以就是一個host memory的物理地址。


在x86架構(gòu)下,dma addr == physical addr。好像有別的架構(gòu)兩者不等。
作者: epegasus    時間: 2009-03-26 14:06
原帖由 Solaris12 于 2009-3-26 13:21 發(fā)表


建議你看看書,然后問出具體的問題來

這個目前沒有找到相關(guān)的書,明確的講解DMA問題,可能講的比較隱晦,我沒反映過來,僅從微機原理的書上猜測:
DMA可以直接讀取內(nèi)存,并請求總線的傳輸,和CPU應該處于一個級別,但是CPU又可以通過IO端口控制DMA行為.

1.DMA主要是進行數(shù)據(jù)傳輸用的,那么將內(nèi)存中的數(shù)據(jù)傳到網(wǎng)卡上去,一個問題是如果控制總線?細節(jié)是怎么樣?網(wǎng)卡設備又怎么通過總線提供給DMA讀寫的地址?

2.網(wǎng)卡設備接受到數(shù)據(jù)后又如何告訴DMA該傳輸該數(shù)據(jù)到內(nèi)存?是先發(fā)中斷到CPU,CPU控制DMA,還是直接就統(tǒng)治DMA了,等DMA傳完了再由DMA發(fā)送中斷到CPU.

特別是第一點十分困惑.
作者: Solaris12    時間: 2009-03-26 14:09
原帖由 eexplorer 于 2009-3-26 13:40 發(fā)表


在x86架構(gòu)下,dma addr == physical addr。好像有別的架構(gòu)兩者不等。


我說的就是x86。 x86上要支持IOMMU了,DMA address就會是虛擬地址。

如果你看Solaris的DMA接口的實現(xiàn),就會發(fā)現(xiàn),SPARC版的是DVMA不是DMA, 因為SPARC早就是用IOMMU了。

未來IOMMU是發(fā)展方向,特別是虛擬化。

[ 本帖最后由 Solaris12 于 2009-3-26 14:16 編輯 ]
作者: Solaris12    時間: 2009-03-26 14:13
原帖由 epegasus 于 2009-3-26 14:06 發(fā)表
1.DMA主要是進行數(shù)據(jù)傳輸用的,那么將內(nèi)存中的數(shù)據(jù)傳到網(wǎng)卡上去,一個問題是如果控制總線?細節(jié)是怎么樣?網(wǎng)卡設備又怎么通過總線提供給DMA讀寫的地址?

2.網(wǎng)卡設備接受到數(shù)據(jù)后又如何告訴DMA該傳輸該數(shù)據(jù)到內(nèi)存?是先發(fā)中斷到CPU,CPU控制DMA,還是直接就統(tǒng)治DMA了,等DMA傳完了再由DMA發(fā)送中斷到CPU.

...


現(xiàn)在的PCI設備是自帶DMA engine的,可以做bus master的DMA操作。

所以DMA是由設備發(fā)起的,CPU不需要參與,DMA結(jié)束后會產(chǎn)生中斷通知CPU來執(zhí)行網(wǎng)卡驅(qū)動的中斷處理程序。
作者: epegasus    時間: 2009-03-26 14:33
原帖由 Solaris12 于 2009-3-26 14:13 發(fā)表


現(xiàn)在的PCI設備是自帶DMA engine的,可以做bus master的DMA操作。

所以DMA是由設備發(fā)起的,CPU不需要參與,DMA結(jié)束后會產(chǎn)生中斷通知CPU來執(zhí)行網(wǎng)卡驅(qū)動的中斷處理程序。

這么一說,我就想通了,再看eexplorer的解釋更清楚了.
關(guān)于現(xiàn)代結(jié)構(gòu)和外設總線之類的書可不可推薦下?
作者: eexplorer    時間: 2009-03-26 14:36
原帖由 Solaris12 于 2009-3-26 14:09 發(fā)表


我說的就是x86。 x86上要支持IOMMU了,DMA address就會是虛擬地址。

如果你看Solaris的DMA接口的實現(xiàn),就會發(fā)現(xiàn),SPARC版的是DVMA不是DMA, 因為SPARC早就是用IOMMU了。

未來IOMMU是發(fā)展方向,特別是虛 ...


IOMMU的東東沒看過,只是知道有了IOMMU,dma addr 和 phy addr也要由IOMMU進行轉(zhuǎn)換了。

在linux kernel里所有的東西都包裝在pci_alloc_consistent, pci_map_single這類函數(shù)里了。沒仔細研究過
作者: eexplorer    時間: 2009-03-26 14:52
原帖由 epegasus 于 2009-3-26 14:33 發(fā)表

這么一說,我就想通了,再看eexplorer的解釋更清楚了.
關(guān)于現(xiàn)代結(jié)構(gòu)和外設總線之類的書可不可推薦下?


Addison.Wesley.PCI.Express.System.Architecture 這本書雖然是講pci express的,但是第一章的回顧講pci bus講得很不錯。
作者: Minit    時間: 2009-03-26 15:21
感謝eexplorer及Solarist12等的精彩回復,曾經(jīng)迷糊的某些東西終于變得清晰了:wink:
作者: epegasus    時間: 2009-03-26 15:29
原帖由 eexplorer 于 2009-3-26 14:52 發(fā)表


Addison.Wesley.PCI.Express.System.Architecture 這本書雖然是講pci express的,但是第一章的回顧講pci bus講得很不錯。

這本沒看過,但是PCI體系結(jié)構(gòu)是硬啃了一部分,感覺就是整個只從總線設計和構(gòu)造將的很清晰,但是無法看出設備應用如何關(guān)聯(lián),可能是我功力不夠
作者: dxqt001    時間: 2009-03-26 16:38
標題: 強悍
我的道行太淺,路過拜讀各位強人的貼子,受教了。
作者: cskyrain    時間: 2009-03-27 16:13
路過,又受傷了,小心的問一下各位牛人都修煉了多長時間。
作者: hzsjx    時間: 2009-03-30 12:45

作者: 學習者_wyg    時間: 2009-04-01 22:41
提示: 作者被禁止或刪除 內(nèi)容自動屏蔽
作者: linxinyu315    時間: 2009-04-09 17:39
標題: 我是程序員
我想知道的是。在執(zhí)行接收軟中斷的時候硬中斷也是關(guān)掉的,這時候數(shù)據(jù)也能正常放入DMA緩存區(qū)么
作者: linxinyu315    時間: 2009-04-10 11:08
標題: 我是程序員
PCI設備有6個BAR,E1000恰好是從BAR0開始存儲的是控制狀態(tài)寄存器CSR,這個是硬件相關(guān)的。pci設備地址和物理地址一般來說沒區(qū)別只不過的小端。通過ioremap就和我們編程用的虛擬地址沒區(qū)別的了吧只不過要用固定的函數(shù)訪問

中斷關(guān)閉后數(shù)據(jù)包照常放入DMA緩存,輪訓的時候是關(guān)中斷的,這也是NAPI區(qū)別中斷模式的重要區(qū)別
作者: linxinyu315    時間: 2009-04-10 11:29
標題: 回復 #36 Solaris12 的帖子
對的。數(shù)據(jù)包是直接放到設備內(nèi)存的,不是放到網(wǎng)卡再拷貝到內(nèi)存
作者: okr001610    時間: 2009-04-21 00:46
原帖由 linxinyu315 于 2009-4-9 17:39 發(fā)表
我想知道的是。在執(zhí)行接收軟中斷的時候硬中斷也是關(guān)掉的,這時候數(shù)據(jù)也能正常放入DMA緩存區(qū)么


中斷跟DMA能否正常接收數(shù)據(jù)沒有關(guān)系--網(wǎng)卡上有自己的DMA控制器,
CPU接到中斷時,完整的一幀數(shù)據(jù)已經(jīng)保存在DMA_DESC指向的DMA_RING里了,CPU跑過去讀就是。
如果驅(qū)動采用POLL方式,這時就關(guān)中斷,輪詢網(wǎng)卡上的狀態(tài)寄存器,發(fā)現(xiàn)有包就緒就讀DMA_RING,輪詢完后再開中斷
作者: sunfighter    時間: 2009-05-06 00:39
學習中
作者: accessory    時間: 2009-05-06 02:56
不錯。支持下
作者: xuxd32    時間: 2009-05-09 11:31
提示: 作者被禁止或刪除 內(nèi)容自動屏蔽
作者: wucywhut    時間: 2009-07-31 12:00
我簡直連小蝦都不算啊,差距太大。
作者: accessory    時間: 2010-02-24 06:55
現(xiàn)在又看不到圖片了。真是奇怪
作者: Godbach    時間: 2010-02-24 13:53
現(xiàn)在又看不到圖片了。真是奇怪
accessory 發(fā)表于 2010-02-24 06:55

是啊。最近論壇調(diào)整,問題比較多
作者: herjok    時間: 2010-03-03 11:28
慢慢來。這個。
作者: EZWORD    時間: 2010-08-31 20:20
慢慢來。這個。
作者: sb5216116    時間: 2017-08-02 08:27
拜讀,太牛逼了




歡迎光臨 Chinaunix (http://www.72891.cn/) Powered by Discuz! X3.2