本文主要介绍 rte_mbuf 与 rte_mempool 数据结构之间的组织关系、以及网卡接收到的数据是如何存储在 rte_mbuf 中的。
一、 rte_mbuf、rte_mempool及网卡收到的数据包在内存中的组织结构
调用 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地址。
如下简图所示。
文章来源
http://www.cnblogs.com/MerlinJ/p/4284706.html