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

在上一篇的结尾处,提到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如何发送报文
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,下面是使用dpdk20.11版中pdump功能的详细实例。 1. 首先,在DPDK应用程序中包含pdump头文件: ```c #include <rte_pdump.h> ``` 2. 初始化pdump库,这应该在rte_eal_init()函数之后调用: ```c int ret; ret = rte_pdump_init(); if (ret < 0) { // 初始化失败,处理错误 } ``` 3. 创建pdump文件,调用rte_pdump_open()函数: ```c const char *filename = "pdump.pcap"; uint16_t portid = 0; // 捕获端口的ID uint32_t snaplen = 65535; // 每个数据包的最大长度 struct rte_pdump_params pdump_params = { .file_name = filename, .port_id = portid, .snaplen = snaplen, }; struct rte_pdump *pdump = rte_pdump_open(&pdump_params); if (pdump == NULL) { // 创建pdump文件失败,处理错误 } ``` 4. 启用pdump功能,调用rte_pdump_enable()函数: ```c ret = rte_pdump_enable(pdump); if (ret < 0) { // 启用pdump失败,处理错误 } ``` 5. 在需要捕获数据包的代码中,调用rte_eth_rx_burst()函数来获取数据包: ```c struct rte_mbuf *bufs[BURST_SIZE]; const uint16_t nb_rx = rte_eth_rx_burst(portid, queueid, bufs, BURST_SIZE); if (nb_rx > 0) { // 成功接收数据包,处理数据包 } ``` 6. 在代码中处理完数据包后,将数据包写入pdump文件,调用rte_pdump_write()函数: ```c for (int i = 0; i < nb_rx; i++) { rte_pdump_write(pdump, bufs[i], NULL); } ``` 7. 在退出应用程序之前,关闭pdump文件,调用rte_pdump_close()函数: ```c rte_pdump_close(pdump); ``` 这就是使用dpdk20.11版中pdump功能的详细实例。其中,启用pdump功能的rte_pdump_enable()函数可以在需要启用pdump功能的时候调用,例如在收到一个特定的命令或信号后。调用rte_pdump_enable()函数之前,必须先创建pdump文件并使用rte_pdump_open()函数打开它。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值