linux nic cpu 10%,cpu – NIC中的描述符概念

快速回答:

>它们是遵循NIC硬件定义的软件构造,因此它们都能理解并且可以相互通信.

>根据供应商定义的合同,它们可以以任何方式填充.可能的情况可能包括但不限于:

>通过驱动程序(例如,由驱动程序准备的空缓冲区由硬件Rx接收;对于由驱动程序准备的数据包缓冲区由硬件Tx发送)

>通过NIC(例如,对于已完成的Rx数据包由硬件写回的数据包缓冲区;对于由硬件指示的已完成的Tx数据包缓冲区,它已发送)

更多建筑细节:

顾名思义,描述符描述了一个数据包.它不直接包含数据包数据(据我所知,对于NIC),而是描述数据包,即存储数据包字节的位置,数据包的长度等.

我将使用RX路径作为例子来说明它为什么有用.在接收到分组时,NIC将线路上的电子/光学/无线电信号转换为二进制数据字节.然后,NIC需要通知操作系统已收到某些内容.在过去,这是通过中断完成的,操作系统将从NIC上的预定义位置读取字节到RAM.然而这很慢,因为1)CPU需要参与从NIC到RAM的数据传输2)可能存在大量数据包,因此许多中断可能太多而无法处理CPU.然后DMA出现并解决了第一个问题.此外,人们设计了轮询模式驱动程序(或混合模式,如在Linux NAPI中),因此CPU可以从中断处理中解放出来并一次轮询多个数据包,从而解决了第二个问题.

NIC完成信号转换为字节,并希望DMA到RAM.但在此之前,NIC需要知道DMA的位置,因为它不能随意将数据放入RAM中,CPU不会知道哪里和哪些都不安全.

因此,在RX队列的初始化期间,NIC驱动程序预先分配一些数据包缓冲区以及数据包描述符数组.它根据NIC定义初始化每个数据包描述符.

以下是英特尔XL710 NIC使用的惯例(为了更好地理解,名称已经过简化):

/*

Rx descriptor used by XL710 is filled by both driver and NIC,

* but at different stage of operations. Thus to save space, it's

* defined as a union of read (by NIC) and writeback (by NIC).

*

* It must follow the description from the data sheet table above.

*

* __leXX below means little endian XX bit field.

* The endianness and length has to be explicit, the NIC can be used by different CPU with different word size and endianness.

*/

union 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 */

};

/*

* Rx Queue defines a circular ring of Rx descriptors

*/

struct rx_queue {

volatile rx_desc rx_ring[RING_SIZE]; /* RX ring of descriptors */

struct packet_buffer_pool *pool; /* packet pool */

struct packet_buffer *pkt_addr_backup; /* save a copy of packet buffer address for writeback descriptor reuse */

....

}

tx3Ul.png

Ayx8U.png

>驱动程序在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();

> Driver告诉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.与此同时,NIC可以预处理(卸载)数据包处理(如校验和验证,提取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读取TX描述符和DMA tx_ring [x] .buffer_addr到NIC.

此信息通常出现在NIC数据表中,例如Intel XL710 xl710-10-40-controller-datasheet,第8.3章& 8.4 LAN RX / TX数据路径.

您还可以检查开源驱动程序代码(Linux内核或某些用户空间库,如DPDK PMD),它将包含描述符结构定义.

– 编辑1 –

有关Realtek驱动程序的其他问题:

是的,这些位是特定于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,第70页),尽管它不像XL710那样,但至少包含所有寄存器/描述符信息.

– 编辑2 –

NIC描述符是一个非常依赖于供应商的定义.如上所示,Intel的NIC描述符使用相同的RX描述符环来提供要写入的NIC缓冲区,并使NIC能够写回RX信息.还有其他实现,如拆分RX提交/完成队列(在NVMe技术中更常见).例如,Broadcom的一些NIC有一个提交环(为NIC提供缓冲区)和多个完成环.它专为NIC决定并将数据包放入不同的环中,例如不同的流量类优先级,使驱动程序可以先获取最重要的流量.

gC0ZG.png(来自BCM5756M NIC程序员指南)

– 编辑3–

英特尔通常会将NIC数据表公开下载,而其他供应商可能只向ODM公开.有关Tx / Rx流程的简短摘要,请参见其Intel 82599系列数据表,第1.8节“架构和基本操作”.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值