快速解答:
- 它們是由NIC硬件這樣既理解並能互相交談下面的定義軟件結構。
- 它們由驅動程序(例如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故事。 NIC完成信號轉換爲字節,並且想要做DMA到RAM。但在此之前,NIC需要知道在哪裏進行DMA,因爲它不能隨機將數據放入RAM中,哪些CPU不知道哪裏並且不安全。
因此,在RX隊列的初始化期間,NIC驅動程序預先分配一些數據包緩衝區以及數據包描述符。它根據NIC定義初始化每個包描述符。
我將利用英特爾XL710驅動程序作爲一個代碼示例(有些瓦爾被重命名爲更好地理解):
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 */
};
驅動程序分配在RAM中的數據包緩衝器的一些數(存儲在packet_buffer_pool數據結構中)。
pool = alloc_packet_buffer_pool(buffer_size=2048, num_buffer=512);
驅動放每個分組緩衝器的地址中的描述符字段,像
rx_ring[i]->read.pkt_addr = pool.get_free_buffer();
驅動程序通知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 */
現在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 */
}
驅動讀取新的尾指針偏移並發現{X,Y,Z}是用新的數據包。它會從pkt_addr_backup [{x,y,z}]中讀取數據包以及相關的預處理結果。
當上層軟件使用數據包完成時,{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決定並將數據包放入不同的環中,例如不同的流量級別優先級,以便驅動程序能夠首先獲得最重要的數據包 (從BCM5756M NIC程序員指南)
- 編輯3--
我通常發現Intel的網卡數據表是最開放和信息對他們的設計。關於Tx/Rx流程的簡要總結,請參閱其Intel 82599系列數據表,第1.8節「架構和基本操作」。
只是爲了澄清,請參閱編輯的問題! – Haswell
請參閱我的編輯。謝謝。 –