DPDK内存管理 ----- (四) rte_mbuf

时间:2020-11-29 22:28:14

本文主要介绍 rte_mbuf 与 rte_mempool 数据结构之间的组织关系、以及网卡接收到的数据是如何存储在 rte_mbuf 中的。


一、   rte_mbuf、rte_mempool及网卡收到的数据包在内存中的组织结构


DPDK内存管理 ----- (四) rte_mbuf


调用 rte_mempool_create() 函数创建 rte_mempool 的时候,指定申请多少个 rte_mbuff 及每个 rte_mbuf 中 elt_size 的大小。

elt_size是为网卡接收的数据包预先分配的内存的大小,该内存块就是 rte_mbuf->pkt.data 的实际存储区域。具体如上图所示。

在申请的 rte_mempool 内存块中,最前面存储 structrte_mempool 数据结构,后面紧接着是 rte_pktmbuf_pool_private 数据,再后面就是N个 rte_mbuf 内存块。

每个 rte_mbuf 内存中,最前面同样存储的是 structrte_mbuf 数据结果,后面是 RTE_PKTMBUF_HEADROOM,最后面就是实际网卡接收到的数据,

如下:


struct rte_mbuf *m = _m;

uint32_t buf_len = mp->elt_size - sizeof(struct rte_mbuf);

RTE_MBUF_ASSERT(mp->elt_size >= sizeof(struct rte_mbuf));

memset(m, 0, mp->elt_size);

/* start of buffer is just after mbuf structure */

m->buf_addr = (char *)m + sizeof(struct rte_mbuf);

m->buf_physaddr = rte_mempool_virt2phy(mp, m) + sizeof(struct rte_mbuf);

m->buf_len = (uint16_t)buf_len;


/* keep some headroom between start of buffer and data */

m->pkt.data = (char*) m->buf_addr + RTE_MIN(RTE_PKTMBUF_HEADROOM,m->buf_len);


/* init some constant fields */

m->type = RTE_MBUF_PKT;

m->pool = mp;

m->pkt.nb_segs = 1;

m->pkt.in_port= 0xff;


二、   网卡接收的数据是如何存储到rte_mbuf中的?

以 e1000 网卡为例,在网卡初始化的时候,调用 eth_igb_rx_init() 初始化网卡的收包队列。

每个收包队列数据结果如下:

/*

 *Structure associated with each RX queue.

 */

struct igb_rx_queue {

   struct rte_mempool  *mb_pool;   /**< mbuf pool to populate RX ring. */

   volatile union e1000_adv_rx_desc *rx_ring; /**< RX ring virtualaddress. */

   uint64_t           rx_ring_phys_addr; /**< RX ring DMA address. */

   volatile uint32_t   *rdt_reg_addr;/**< RDT register address. */

   volatile uint32_t   *rdh_reg_addr;/**< RDH register address. */

   struct igb_rx_entry *sw_ring;  /**< address of RX software ring. */

   struct rte_mbuf *pkt_first_seg; /**< First segment of current packet.*/

   struct rte_mbuf *pkt_last_seg; /**< Last segment of current packet. */

   uint16_t            nb_rx_desc;/**< number of RX descriptors. */

   uint16_t            rx_tail;    /**< current value of RDT register. */

   uint16_t            nb_rx_hold;/**< number of held free RX desc. */

   uint16_t           rx_free_thresh; /**< max free RX desc to hold. */

   uint16_t            queue_id;   /**< RX queue index. */

   uint16_t            reg_idx;    /**< RX queue register index. */

   uint8_t             port_id;    /**< Device port identifier. */

   uint8_t             pthresh;    /**< Prefetch threshold register. */

   uint8_t             hthresh;    /**< Host threshold register. */

   uint8_t             wthresh;    /**< Write-back threshold register. */

   uint8_t             crc_len;    /**< 0 if CRC stripped, 4 otherwise. */

   uint8_t             drop_en;  /**< If not 0, set SRRCTL.Drop_En. */

};

 

我们只关注其中两个成员变量,rx_ring 和 sw_ring。

rx_ring 记录的是 union e1000_adv_rx_desc 数组,每个 unione1000_adv_rx_desc 中指定了网卡接收数据的 DMA 地址,网卡收到数据后,直接往该地址写数据。

sw_ring 数组记录的是每个具体的 rte_mbuf 地址,每个 rte_mbuf 的 rte_mbuff->buf_phyaddr +RTE_PKTMBUF_HEADROOM 映射后的DMA地址就存储在 rx_ring 队列的 union e1000_adv_rx_desc 数据结构中。

rte_mbuff->buf_phyaddr + RTE_PKTMBUF_HEADROOM 指向的就是 rte_mbuf->pkt.data 的地址。


此时,rte_mbuf、rte_mbuf->pkt.data,已经网卡的收包队列就关联起来了。

具体如下:

static int

igb_alloc_rx_queue_mbufs(structigb_rx_queue *rxq)

{

   struct igb_rx_entry *rxe = rxq->sw_ring;

   uint64_t dma_addr;

   unsigned i;


   /* Initialize software ring entries. */

   for (i = 0; i < rxq->nb_rx_desc; i++)  {

       volatile union e1000_adv_rx_desc *rxd;

       struct rte_mbuf *mbuf = rte_rxmbuf_alloc(rxq->mb_pool);

 

       if (mbuf == NULL) {

           PMD_INIT_LOG(ERR, "RX mbuf alloc failed "

                "queue_id=%hu\n",rxq->queue_id);

           return (-ENOMEM);

       }

       dma_addr =

           rte_cpu_to_le_64(RTE_MBUF_DATA_DMA_ADDR_DEFAULT(mbuf));

       rxd = &rxq->rx_ring[i];

       rxd->read.hdr_addr = dma_addr;

       rxd->read.pkt_addr = dma_addr;

       rxe[i].mbuf = mbuf;

    }

 

   return 0;

}

 

网卡收到数据后,向 rx_ring 指定的DMA地址上写数据,其实,就是往每个rte_mbuf->pkt.data写数据。

应用程序在调用 rte_eth_rx_burst() 收包时,以e1000网卡为例,最后调用的是 eth_igb_recv_pkts(),就是从每个收包队列中,从 sw_ring 数组中将 rte_mbuf 取出来,然后重启申请新的 rte_mbuf 替换到 rx_ring 中,重新关联 rte_mbuf、union e1000_adv_rx_desc、sw_ring 以及 rte_mbuf->pkt.data 的DMA地址。

如下简图所示。


DPDK内存管理 ----- (四) rte_mbuf


文章来源

http://www.cnblogs.com/MerlinJ/p/4284706.html