DPDK学习记录7 - eth dev初始化3之after rte_eal_init

本文深入解析DPDK中网络设备的配置流程,包括端口队列的初始化、描述符环的设置及DMA区域的分配。重点介绍了rte_eth_dev_configure、rte_eth_tx_queue_setup和rte_eth_rx_queue_setup等函数的作用,以及它们如何配合实现端口的启动和报文的收发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在上一篇的结尾处,提到eth_dev->data字段中的rx_queues/tx_queues/nb_rx_queues/nb_tx_queues是没有配置的。在rte_eal_init之后,再进行配置,每个端口可以配置多个queues。

1 rte_eth_dev_configure

int
rte_eth_dev_configure(uint16_t port_id, uint16_t nb_rx_q, uint16_t nb_tx_q,
const struct rte_eth_conf *dev_conf)

端口级别对队列总个数的配置,用户的配置结构体为rte_eth_conf,设置到eth dev->data->dev_conf中,并用dev_ops中的get接口读取出dev info进行check。
在这里插入图片描述

check通过之后,rte_eth_dev_rx_queue_config函数通过rte_zmalloc申请二维数组的第一维空间,赋值到dev->data->rx_queues。
在这里插入图片描述

接着rte_eth_dev_tx_queue_config,通过rte_zmalloc申请二维数组的第一维空间,赋值到dev->data->tx_queues

在这里插入图片描述

2 rte_eth_tx_queue_setup

int
rte_eth_tx_queue_setup(uint16_t port_id, uint16_t tx_queue_id,
uint16_t nb_tx_desc, unsigned int socket_id,
const struct rte_eth_txconf *tx_conf)

2.1 队列级别的配置,遍历一个端口的多个队列,每个队列的配置都会进入rte_eth_tx_queue_setup,其中port id和queue id通过参数传入,若描述符个数nb_tx_desc为0,则会赋值成默认值256。
在这里插入图片描述
2.2 调用dev ops中的tx queue setup函数,这里是eth_em_tx_queue_setup,这个函数很关键。对二维数组dev->data->tx_queues的第二维每个队列中的描述符申请空间。

	struct em_tx_queue *txq;
    //申请硬件rx ring,这里用的是dma申请函数
	tz = rte_eth_dma_zone_reserve(dev, "tx_ring", queue_idx, tsize,
				      RTE_CACHE_LINE_SIZE, socket_id);

	/* Allocate the tx queue data structure. 整个队列的结构体申请 */
	if ((txq = rte_zmalloc("ethdev TX queue", sizeof(*txq),
			RTE_CACHE_LINE_SIZE)) == NULL)
		return -ENOMEM;

	/* Allocate software ring 这里存放描述符。*/
	if ((txq->sw_ring = rte_zmalloc("txq->sw_ring",
			sizeof(txq->sw_ring[0]) * nb_desc,
			RTE_CACHE_LINE_SIZE)) == NULL) {
		em_tx_queue_release(txq);
		return -ENOMEM;
	}

	txq->nb_tx_desc = nb_desc;
	txq->queue_id = queue_idx;
	txq->port_id = dev->data->port_id;

	txq->tdt_reg_addr = E1000_PCI_REG_ADDR(hw, E1000_TDT(queue_idx)); //每次发送burst之后,需要置这个寄存器通知DMA发送。
	txq->tx_ring_phys_addr = tz->iova; //dma iova
	txq->tx_ring = (struct e1000_data_desc *) tz->addr; //硬件tx ring指向dma地址。
	em_reset_tx_queue(txq);

	dev->data->tx_queues[queue_idx] = txq; //真正把队列挂到eth dev中。

在这里插入图片描述

在这里插入图片描述

3 rte_eth_rx_queue_setup

int
rte_eth_rx_queue_setup(uint16_t port_id, uint16_t rx_queue_id,
uint16_t nb_rx_desc, unsigned int socket_id,
const struct rte_eth_rxconf *rx_conf,
struct rte_mempool *mp)

接收侧队列的setup,与发送侧的有些不同,需要一个mempool,接收的时候dma把报文放入rx ring的mempool中。发送侧则是由软件app存入tx ring,由dma读取,所以发送不需要。rx的mempool在此之前,内存初始化中,已经基于socket id创建成功。

先检查一下配置之后,然后调用pmd的rx queue setup函数。

	ret = (*dev->dev_ops->rx_queue_setup)(dev, rx_queue_id, nb_rx_desc,
					      socket_id, &local_conf, mp);

int
eth_em_rx_queue_setup(struct rte_eth_dev *dev,
		uint16_t queue_idx,
		uint16_t nb_desc,
		unsigned int socket_id,
		const struct rte_eth_rxconf *rx_conf,
		struct rte_mempool *mp)
{
	struct em_rx_queue *rxq;
    
    //申请DMA
	rz = rte_eth_dma_zone_reserve(dev, "rx_ring", queue_idx, rsize,
				      RTE_CACHE_LINE_SIZE, socket_id);

	/* Allocate the RX queue data structure. */
	if ((rxq = rte_zmalloc("ethdev RX queue", sizeof(*rxq),
			RTE_CACHE_LINE_SIZE)) == NULL)
		return -ENOMEM;

	/* Allocate software ring. */
	if ((rxq->sw_ring = rte_zmalloc("rxq->sw_ring",
			sizeof (rxq->sw_ring[0]) * nb_desc,
			RTE_CACHE_LINE_SIZE)) == NULL) {
		em_rx_queue_release(rxq);
		return -ENOMEM;
	}

	rxq->mb_pool = mp; //挂上mempool
	rxq->nb_rx_desc = nb_desc;
	rxq->queue_id = queue_idx;
	rxq->port_id = dev->data->port_id;

	rxq->rdt_reg_addr = E1000_PCI_REG_ADDR(hw, E1000_RDT(queue_idx)); 
	rxq->rdh_reg_addr = E1000_PCI_REG_ADDR(hw, E1000_RDH(queue_idx));
	rxq->rx_ring_phys_addr = rz->iova; //dma 的iova
	rxq->rx_ring = (struct e1000_rx_desc *) rz->addr; //rx ring上是dma的地址

	dev->data->rx_queues[queue_idx] = rxq; //挂到eth dev中。

在这里插入图片描述

4 rte_eth_dev_start

int
rte_eth_dev_start(uint16_t port_id)

端口级别,经过前几步之后,端口的资源都已经分配好,需要最后start一下。

rte_eth_dev_start(uint16_t port_id)
{
	diag = (*dev->dev_ops->dev_start)(dev);
	if (diag == 0)
		dev->data->dev_started = 1;
	else
		return eth_err(port_id, diag);
}

static int
eth_em_start(struct rte_eth_dev *dev)
{
	em_hardware_init(hw);
	eth_em_tx_init(dev); //真正配置寄存器,将写入tx dma地址
	eth_em_rx_init(dev); //真正配置寄存器,将写入rx dma地址

}

5 总结

在rte_eal_init之后,就是对eth dev->data下面的tx queues/rx queue二维指针,申请内存资源进行配置,最后start port是设置寄存器。

下面截图是eth dev [0]的信息,对比上一篇最后的eth dev信息,可以看到队列信息都已经有值了。其中rx_queues和tx_queues是二维指针,可以有多个队列指针。每个队列指针下面有rx_ring/tx_ring/sw_ring等信息。

至此,端口的注册/扫描/配置/启动,队列的setup等均完成,User可以通过rte_eth_rx_burst/rte_eth_tx_burst对端口进行收发报文。
(详见rte_eth_rx_burst如何接收报文rte_eth_tx_burst如何发送报文
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值