DPDK pmd驱动收发函数与网卡

401 阅读2分钟

本篇主要讲网卡的工作原理

最近在做一个网卡仿真程序。主要目的是用程序代替网卡去向内存中填充报文。

网卡与内存的交互方式

1. rx阶段

网卡通过DMA向内存中发送数据包。
在内存中主要有三个数据结构
① DMA环(rx_ring), 其中存储了DMA描述符, DMA描述符指向了实际物理地址(todo), NIC将根据DMA描述符内的address把收到的报文放到相应的地址中。并更改DMA的RX_USED位, 让驱动程序知道NIC已经向这个槽里放置了报文。
② 软件环(sw_ring), 其中储存了报文池中的可用mbuf地址。

内存分布图:

image.png pmd收包函数源代码分析, 以下代码只保留了重要部分

uint16_t recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
							uint16_t nb_pkts)
{
	struct macb_rx_queue *rxq;
	unsigned int len;
	unsigned int entry, next_entry;
	struct macb_dma_desc *desc, *ndesc;
	uint16_t nb_rx;
	struct macb *bp;
	struct rte_mbuf *rxm;
	struct rte_mbuf *nmb;
	struct macb_rx_entry *rxe, *rxn;
	uint64_t dma_addr;
	nb_rx = 0;
	rxq = rx_queue;
	bp = rxq->bp;
        
        /* 一次轮询从tail开始检查nb_pkts个数据包 可以的话重置rxe和dma描述符 */
	while (nb_rx < nb_pkts) {
		u32 ctrl;
		bool rxused;
		struct rte_ether_hdr *eth_hdr;
		uint16_t ether_type;

		entry = macb_rx_ring_wrap(bp, rxq->rx_tail);
		rxe = macb_rx_entry(rxq, entry); // 获取当前待处理在pool中的mbuf的指针
		desc = macb_rx_desc(rxq, entry); // 获取当前待处理的DMA descriptor
		
                /* 
                 * 如果该描述符的RX_USED位是0 证明NIC没有将数据传进rxe中, 
                 * 所以可以这个槽位以及后面的槽位可以直接不用管了 
                 */
		rxused = (desc->addr & MACB_BIT(RX_USED)) ? true : false;
		if (!rxused)
			break;
		/* Ensure ctrl is at least as up-to-date as rxused */
		rte_smp_rmb();
		ctrl = desc->ctrl;
		rxq->rx_tail++;
                
                /* 重新在报文池中分配了一块新的m_buff空间*/
		nmb = rte_mbuf_raw_alloc(rxq->mb_pool);
                
		rxm = rxe->mbuf; /* rx_mbuf, 这个是要取得报文 */
		rxe->mbuf = nmb; /* 将rxe->mbuf赋值为新的m_buf 下次NIC可以再次使用 */

		dma_addr = rte_cpu_to_le_64(rte_mbuf_data_iova_default(nmb));

		eth_hdr = rte_pktmbuf_mtod(rxm, struct rte_ether_hdr *);
		ether_type = eth_hdr->ether_type;


		/* 将原本rxe的m_buf指针给rx_pkts, 相当于返回给调用函数 */
		rx_pkts[nb_rx++] = rxm;
                
                /* 给DMA描述符赋值 相当于重置了dma描述符 */
		macb_set_addr(bp, desc, dma_addr);
	}

	return nb_rx;
}

因此, 假设我们需要模拟NIC, 需要做的事情就是主动将USED位赋为1, 然后网sw_ring里对应槽位的mbuf里插入随机报文即可

2.tx阶段

tx阶段是rx阶段的逆过程, 当NIC将待发送报文从tx_queue里拿走时, TX_USED位会赋值为1, macb_reclaim_txd函数会从tx_queue队列的head遍历, 除掉所有TX_USED=1的槽位, 这样程序就可以继续往tx_queue里放置需要放置的报文, NIC就可以从tx_queue中复制报文并发送。

假设我们需要模拟NIC, 需要做的事情就是主动将USED位赋为1即可, 以及模拟数据。

pmd 发包源代码分析 略