Loading

网卡的RX Ring和TX Ring

1 简介

环形缓冲(ring buffer)是NIC处理数据包的一种通用数据结构,出现的原因是现代NIC基本使用DMA进行数据传输,作为一种高效简单[1]的数据结构,环形缓冲很

适合这种大吞吐的场景。在NIC中分成了RX Ring和TX Ring两种缓冲,分别负责接收和传输数据包。环形缓冲的使用维护涉及到NIC硬件、驱动、DMA控制器三

方。接下来的内容将以环形缓冲的组成和代码示例去讲解RX Ring和TX Ring。

2 Ring Buffer的组成

从整体上讲,Ring Buffer由NIC上的一组寄存器(基地址寄存器、长度寄存器、Head寄存器和Tail寄存器)来描述,同时Buffer的空间则是通过在驱动初始化的时

候通过DMA接口分配,而具体的操作是通过一个叫做包描述符的数据结构完成。

图2-1 Ring Buffer结构图

2.1 包描述符

描述符(Packet Descriptor)用来表达一个数据包在缓冲区内的地址以及数据包在NIC中的状态(是否有异常发生)。这是一个硬件相关的数据结构,由NIC去规定该

结构的内容。描述符分成了接收描述符 (rx descriptor) 和传输描述符 (tx descriptor) 接收描述符是一个用来描述网卡接收的数据缓冲区首地址和硬件用于存储包信

息的数据结构。如下图所示是Intel 8254x系统NIC的接收描述符的结构,共64位。Buffer Address是NIC DMA 数据包的地址,灰色的部门是一些状态字段,这些

字段在NIC接收到数据包之后通过DMA直接进行修改。

描述符实际存放的位置在驱动分配的DMA内存中,在驱动的Open接口(即启用该设备的时候)进行分配。在i40e中通过调用dma_alloc_coherent完成。

rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size, &rx_ring->dma, GFP_KERNEL);

i40e 使用共用体 i40e_rx_desc 表示一个接收描述符, 在open阶段驱动会给每个描述符分配一个pkt_addr,NIC用该地址作为DMA数据包的起始地址。当包完

整的接收到DMA内存中后,NIC又会通过该描述符设置状态。值得注意的是这些操作都是通过NIC固件完成的。在i40e_rx_desc 中,pkt_addr表示缓冲区DMA的

首地址;wb结构是NIC完成和DMA的包传输之后,向内存中写入的状态,rss_hash是NIC计算的哈

希。

union i40e_32byte_rx_desc {
	struct {
		__le64  pkt_addr; /* Packet buffer address */
		__le64  hdr_addr; /* Header buffer address */
			/* bit 0 of hdr_buffer_addr is DD bit */
		__le64  rsvd1;
		__le64  rsvd2;
	} read;
	struct {
		struct {
			struct {
				union {
					__le16 mirroring_status;
					__le16 fcoe_ctx_id;
				} mirr_fcoe;
				__le16 l2tag1;
			} lo_dword;
			union {
				__le32 rss; /* RSS Hash */
				__le32 fcoe_param; /* FCoE DDP Context id */
				/* Flow director filter id in case of
				 * Programming status desc WB
				 */
				__le32 fd_id;
			} hi_dword;
		} qword0;
		struct {
			/* status/error/pktype/length */
			__le64 status_error_len;
		} qword1;
		struct {
			__le16 ext_status; /* extended status */
			__le16 rsvd;
			__le16 l2tag2_1;
			__le16 l2tag2_2;
		} qword2;
		struct {
			union {
				__le32 flex_bytes_lo;
				__le32 pe_status;
			} lo_dword;
			union {
				__le32 flex_bytes_hi;
				__le32 fd_id;
			} hi_dword;
		} qword3;
	} wb;  /* writeback */
};

2.2 寄存器

上一节讲过环形缓冲通过NIC中一组寄存器去表示,本节我们就这些寄存器进行更详细的讨论。就rx而言,一共有四个寄存器,

图2-1 NIC RX Ring结构图(来自Intel文档)

描述符缓冲的基地址通过两个寄存器表示,分别是 RDBAL(Receive Descriptor Base Address Low) 用来表示 64bit 描述符基地址的低32位,类似的,

RDBAH(Receive Descriptor Base Address High) 表示 64bit 描述符基地址的高32位。 注意这里需要区分下,NIC在内存分配了两种缓冲区,一个是用于存储数据

包,一个用于存储描述符,这里的寄存器表示的都是描述符缓冲区的地址。

接受描述符长度寄存器,该值需要128字节(最大的cache line大小)对齐

接收描述符头指针寄存器,该寄存器是通过硬件控制。

接收描述符尾指针寄存器

2.3 数据包缓冲区

至此环形缓冲的内容就结束了,实际上环形缓冲指的是描述符的缓冲,真正存储数据包的起始地址存在描述符中。一个缓冲有一个对skb的引用,dma内存的地

址,和对物理页的引用构成。

struct i40e_rx_buffer {
	dma_addr_t dma;
#ifdef CONFIG_I40E_DISABLE_PACKET_SPLIT 
	struct sk_buff *skb;
#else
	struct page *page;
#if (BITS_PER_LONG > 32) || (PAGE_SIZE >= 65536)
	__u32 page_offset;
#else
	__u16 page_offset;
#endif
	__u16 pagecnt_bias;
#endif /* CONFIG_I40E_DISABLE_PACKET_SPLIT */
};

参考

https://stackoverflow.com/questions/36625892/descriptor-concept-in-nic

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

https://wenfh2020.com/2021/12/29/kernel-tcp-receive/


知识共享许可协议
本作品采用知识共享署名 4.0 国际许可协议进行许可。
posted @ 2022-05-30 15:20  成蹊0xc000  阅读(6560)  评论(0)    收藏  举报