NIC中的描述符概念

时间:2016-04-14 14:16:03

标签: cpu cpu-architecture

我试图理解网络驱动程序代码中使用的Rx和Tx描述符的概念。

  1. 是软件(RAM)或硬件(NIC卡)中的描述符。
  2. 他们如何填补。
  3. 编辑:所以在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卡特有的?它们是否会由制造商在数据表中定义?

    由于 拿烟

1 个答案:

答案 0 :(得分:38)

快速回答:

  1. 它们是遵循NIC硬件定义的软件构造,因此它们都能理解并可以相互通信。
  2. 根据供应商定义的合同,它们可以以任何方式填充。可能的情况可能包括但不限于:
    • 通过驱动程序(例如,由驱动程序准备的空缓冲区由硬件Rx接收;对于由驱动程序准备的数据包缓冲区由硬件Tx传输)
    • 通过NIC(例如,对于已完成的Rx数据包由硬件写回的数据包缓冲区;对于由硬件指示的已完成的Tx数据包缓冲区,它已被传输)
  3. 更多建筑详情:

    注意:我假设您了解环数据结构,DMA的概念。 https://en.wikipedia.org/wiki/Circular_buffer
    https://en.wikipedia.org/wiki/Direct_memory_access

    描述符,顾名思义,描述了一个数据包。它并不直接包含数据包数据(据我所知,对于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使用的惯例(为了更好地理解,名称已经过简化):

    XL710 RX read Descriptor XL710 Rx Writeback Descriptor

    /*
       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 */
    ....
    }
    

    RX Descriptor Data Structure

    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。与此同时,NIC可以预处理(卸载)数据包处理(如校验和验证,提取VLAN标记),因此它还需要一些地方来保留软件的这些信息。这里,描述符在 writeback 上重用于此目的(参见描述符联合中的第二个结构)。然后,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,并且环头指针将被更新以指示自由描述符。

    7. RX路径到此结束。 TX路径几乎是相反的:上层产生数据包,驱动程序复制数据包数据到packet_buffer_pool,让tx_ring [x] .buffer_addr指向它。驱动程序还在TX描述符中准备一些TX卸载标志(例如硬件校验和,TSO)。 NIC从TX读取TX描述符和DMA tx_ring [x] .buffer_addr。

      此信息通常出现在NIC数据表中,例如Intel XL710 xl710-10-40-controller-datasheet,第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 -

      有关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决定并将数据包放入不同的环中,例如不同的流量类优先级,使驱动程序可以先获取最重要的流量。 enter image description here(来自BCM5756M NIC程序员指南)

      - 编辑3 -

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