2016-04-14 95 views
6

我想了解網絡驅動程序代碼中使用的Rx和Tx描述符的概念。NIC中的描述符概念

  1. 是軟件(RAM)或硬件(NIC卡)中的描述符。
  2. 他們如何得到填補。

編輯:所以在一個Realtek卡驅動程序代碼。我有一個以下的結構定義。

struct Desc 
{ 
     uint32_t opts1; 
     uint32_t opts2; 
     uint64_t addr; 
}; 

txd->addr = cpu_to_le64(mapping); 
txd->opts2 = cpu_to_le32(opts2); 
txd->opts1 = cpu_to_le32(opts1 & ~DescOwn); 

所以是opts1 and opts2有特定位像DescOwn卡?他們會在數據表中由製造商定義嗎?

感謝 拿煙

回答

24

快速解答:

  1. 它們是由NIC硬件這樣既理解並能互相交談下面的定義軟件結構。
  2. 它們由驅動程序(例如RX空緩衝區分配)或NIC(RX回寫)填充。請參閱下面的更多細節。

更多建築細節:

注:我假設你有環比數據結構,DMA的概念的知識。 https://en.wikipedia.org/wiki/Circular_buffer

https://en.wikipedia.org/wiki/Direct_memory_access

首先考慮RX路徑。在接收到數據包後,NIC將線路上的電子/光學/無線電信號轉換爲二進制數據字節。然後NIC需要通知操作系統它已經收到了一些東西。在過去,這是通過中斷完成的,操作系統會從NIC上的預定義位置讀取字節到RAM。然而,這是緩慢的,因爲1)CPU需要參與從NIC到RAM的數據傳輸2)可能有很多數據包,因此很多中斷可能太多以至於不能處理CPU。然後DMA出現並解決了第一個問題。此外,人們設計了輪詢模式驅動程序(或混合模式,如在Linux NAPI中),因此可以將CPU從中斷處理中解放出來並且一次輪詢多個數據包,從而解決第二個問題。

描述符是一種幫助NIC輕鬆實現DMA的機制。顧名思義,它描述了一個數據包。所以它不包含數據包數據(據我所知NIC),而是描述數據的位置。 RX Descriptor Data Structure

讓我們回到RX故事。 NIC完成信號轉換爲字節,並且想要做DMA到RAM。但在此之前,NIC需要知道在哪裏進行DMA,因爲它不能隨機將數據放入RAM中,哪些CPU不知道哪裏並且不安全。

因此,在RX隊列的初始化期間,NIC驅動程序預先分配一些數據包緩衝區以及數據包描述符。它根據NIC定義初始化每個包描述符。

我將利用英特爾XL710驅動程序作爲一個代碼示例(有些瓦爾被重命名爲更好地理解): XL710 Descriptor format

struct i40e_rx_queue { 
    struct packet_buffer_pool *pool; /* < packet pool */ 
    volatile i40e_16byte_rx_desc *rx_ring; /* < RX ring of descriptors */ 
    struct packet_buffer *pkt_addr_backup; /* save a copy of packet buffer address for writeback descriptor reuse */ 
.... 
} 

union i40e_16byte_rx_desc { 
    struct { 
     __le64 pkt_addr; /* Packet buffer address, points to a free packet buffer in packet_buffer_pool */ 
     __le64 hdr_addr; /* Header buffer address, normally isn't used */ 
    } read; /* initialized by driver */ 

    struct { 
     struct { 
      struct { 
       union { 
        __le16 mirroring_status; 
        __le16 fcoe_ctx_id; 
       } mirr_fcoe; 
       __le16 l2tag1; 
      } lo_dword; 
      union { 
       __le32 rss; /* RSS Hash */ 
       __le32 fd_id; /* Flow director filter id */ 
       __le32 fcoe_param; /* FCoE DDP Context id */ 
      } hi_dword; 
     } qword0; 
     struct { 
      /* ext status/error/pktype/length */ 
      __le64 status_error_len; 
     } qword1; 
    } wb; /* writeback by NIC */ 
}; 

enter image description here

  1. 驅動程序分配在RAM中的數據包緩衝器的一些數(存儲在packet_buffer_pool數據結構中)。

    pool = alloc_packet_buffer_pool(buffer_size=2048, num_buffer=512); 
    
  2. 驅動放每個分組緩衝器的地址中的描述符字段,像

    rx_ring[i]->read.pkt_addr = pool.get_free_buffer(); 
    
  3. 驅動程序通知NIC rx_ring的起始位置,其長度和頭/尾。因此NIC會知道哪些描述符是空閒的(因此這些描述符指向的數據包緩衝區是空閒的)。此過程由驅動程序將這些信息寫入NIC寄存器完成(固定,可在NIC數據表中找到)。

    rx_ring_addr_reg = &rx_ring; 
    rx_ring_len_reg = sizeof(rx_ring); 
    rx_ring_head = 0; /* meaning all free at start */ 
    /* rx_ring_tail is a register in NIC as NIC updates it */ 
    
  4. 現在NIC知道描述rx_ring [{X,Y,Z}]是免費的,{X,Y,Z} .pkt_addr可以把新的分組數據。它繼續並DMA新數據包到{x,y,z} .pkt_addr。與此同時,網卡可以預處理(卸載)數據包處理(如檢查總和驗證,提取VLAN標記),因此它還需要一些地方將這些信息留給軟件。這裏,在回寫(參見描述符聯合中的第二個結構)上重複使用描述符。然後NIC提前rx_ring尾指針偏移量,表明一個新的描述符已被NIC寫回[這裏有一個疑問,因爲描述符被重新用於預處理結果,所以驅動程序必須保存{x,y,z}。備份數據結構中的pkt_addr]。

    /* below is done in hardware, shown just for illustration purpose */ 
    if (rx_ring_head != rx_ring_tail) { /* ring not full */ 
        copy(rx_ring[rx_ring_tail].read.pkt_addr, raw_packet_data); 
        result = do_offload_procesing(); 
    
        if (pre_processing(raw_packet_data) & BAD_CHECKSUM))  
         rx_ring[rx_ring_tail].writeback.qword1.stats_error_len |= RX_BAD_CHECKSUM_ERROR; 
        rx_ring_head++; /* actually driver sets a Descriptor done indication flag */ 
            /* along in writeback descriptor so driver can figure out */ 
            /* current HEAD, thus saving a PCIe write message */ 
    } 
    
  5. 驅動讀取新的尾指針偏移並發現{X,Y,Z}是用新的數據包。它會從pkt_addr_backup [{x,y,z}]中讀取數據包以及相關的預處理結果。

  6. 當上層軟件使用數據包完成時,{x,y,z}將被放回到rx_ring中並且環頭指針將被更新以指示空閒描述符。

這就結束了RX路徑。 TX路徑幾乎相反:上層生成包,驅動程序將包數據複製到packet_buffer_pool中,並讓tx_ring [x] .buffer_addr指向它。驅動程序還會在TX描述符中準備一些TX卸載標誌(例如硬件校驗和,TSO)。 NIC從RAM向NIC讀取TX描述符和DMA tx_ring [x] .buffer_addr。

這些信息通常出現在NIC數據表中,例如Intel XL710 xl710-10-40控制器數據表,第8.3章& 8.4 LAN RX/TX數據路徑。你

http://www.intel.com/content/www/us/en/embedded/products/networking/xl710-10-40-controller-datasheet.html

還可以檢查開源驅動程序代碼(無論是Linux內核或某些用戶空間庫像DPDK PMD),其中將包括描述的結構定義。

順便說一句,我建議你也用「網絡」標記問題。

- 編輯1 -

您針對瑞昱驅動程序的其他問題: 是的,那些位NIC具體。一個提示是線喜歡

desc->opts1 = cpu_to_le32(DescOwn | RingEnd | cp->rx_buf_sz); 

DescOwn是一個比特的標誌,其通過設置它告訴NIC,它現在擁有這個描述符和相關聯的緩衝器。此外,它需要將CPU字節序(可能是CPU的電源,即BE)轉換爲NIC同意理解的Little Endian。

您可以在http://realtek.info/pdf/rtl8139cp.pdf(例如DescOwn)中找到相關信息,雖然它不如XL710那樣透明,但至少包含所有寄存器/描述符信息。

- 編輯2 -

的NIC描述符是非常依賴於供應商定義。如上所示,Intel的NIC描述符使用相同的 RX描述符環來提供NIC緩衝區以寫入,並且NIC將RX信息寫回。還有其他的實現,如分割接收提交/完成隊列(在NVMe技術中更普遍)。例如,一些Broadcom的NIC具有一個提交環(爲緩衝區提供NIC)和多個完成環。它專爲NIC決定並將數據包放入不同的環中,例如不同的流量級別優先級,以便驅動程序能夠首先獲得最重要的數據包 enter image description here(從BCM5756M NIC程序員指南)

- 編輯3--

我通常發現Intel的網卡數據表是最開放和信息對他們的設計。關於Tx/Rx流程的簡要總結,請參閱其Intel 82599系列數據表,第1.8節「架構和基本操作」。

+0

只是爲了澄清,請參閱編輯的問題! – Haswell

+0

請參閱我的編輯。謝謝。 –