32位网卡驱动 2008_DPDK之网卡收包流程

1.导读

98e39bf0e14b2bff45301135f5f12b88.png

一个网络报文从网卡接收到被应用处理,中间主要需要经历两个阶段:

  • 阶段一:网卡通过其DMA硬件将收到的报文写入到收包队列中(入队
  • 阶段二:应用从收包队列中读取报文(出队

下面以ixgbe网卡在dpdk框架下工作为例,分别介绍下收包队列构造、启动收包三个流程。

2.构造

a1f553fb232f0efb0169d6a374fd7143.png

收包队列的构造主要是通过调用网卡队列设置函数rte_eth_rx_queue_setup(dpdk rte_ethdev.h)来完成。

406a3c964f5e601dcc0f986a1685a787.png

收包队列的结构体为ixgbe_rx_queue,该结构体里包含两个重要的环形队列rx_ringsw_ring,rx_ring和sw_ring的关系可以简单如下认为。

  • rx_ring主要存储报文数据的物理地址,物理地址供网卡DMA使用,也称为DMA地址(硬件使用物理地址,将报文copy到报文物理位置上)。
  • sw_ring主要存储报文数据的虚拟地址,虚拟地址供应用使用(软件使用虚拟地址,读取报文)。

其中,报文数据的物理地址可以由报文数据的虚拟地址转化得到。下面详细介绍下rx_ring与sw_ring两个队列的构成。

2.1.rx_ring

ixgbe_adv_rx_desc

ixgbe_adv_rx_desc

......

ixgbe_adv_rx_desc

rx_ring是由一个动态申请的数组构建的环形队列,队列的元素是ixgbe_adv_rx_desc类型,队列的长度为(4096+4-1)。

22ed9be7c313015f0de31880432d827a.png
  • pkt_addr:报文数据的物理地址,网卡DMA将报文数据通过该物理地址写入对应的内存空间。
  • hdr_addr:报文的头信息,hdr_addr的最后一个bit为DD位,因为是union结构,即status_error的最后一个bit也对应DD位。

DD位(Descriptor Done Status)用于标志标识一个描述符buf是否可用。

  • 网卡每次来了新的数据包,就检查rx_ring当前这个buf的DD位是否为0,如果为0那么表示当前buf可以使用,就让DMA将数据包copy到这个buf中,然后设置DD为1。如果为1,那么网卡就认为rx_ring队列满了,直接会将这个包给丢弃掉,记录一次imiss。(0->1
  • 对于应用而言,DD位使用恰恰相反,在读取数据包时,先检查DD位是否为1,如果为1,表示网卡已经把数据包放到了内存中,可以读取,读取完后,再放入一个新的buf并把对应DD位设置为0。如果为0,就表示没有数据包可读。(1->0

2.2.sw_ring

ixgbe_rx_entry

ixgbe_rx_entry

……

ixgbe_rx_entry

sw_ring是由一个动态申请的数组构建的环形队列,队列的元素是ixgbe_rx_entry类型,队列的大小可配,一般最大可配4096。

  • mbuf:报文mbuf结构指针,mbuf用于管理一个报文,主要包含报文相关信息和报文数据。

eb5a2c0784790e611302cf60ae6f7639.png

3.启动

fa6e07a3814845eea5ce941d14648a5b.png

收包队列的启动主要是通过调用rte_eth_dev_start(dpdk rte_ethdev.h)函数完成,收包队列初始化的核心流程如下。

2aa899cce7262ea875d6d97c036714f1.png

循环从mbuf pool中申请mbuf,从mbuf中得到报文数据对应的物理地址,物理地址存入rx_ring中,mbuf指针存入sw_ring中。其中通过rxd->read.hdr_addr = 0,完成了DD位设置为0。

一切ok后,就可以开始收包了。

3.收包

收包由网卡入队和应用出队两个操作完成。

3.1入队

32e2c2496062a4ef1bb75359049ba2a6.png

入队的操作是由网卡DMA来完成的,DMA(Direct Memory Access,直接存储器访问)是系统和网卡(外设)打交道的一种方式,该种方式允许在网卡(外部设备)和系统内存之间直接读写数据,这样能有效减轻CPU的工作。

4a8dfac23d54bdd347937d47b2f022dd.png

网卡收到报文后,先存于网卡本地的buffer-Rx(Rx FIFO)中,然后由DMA通过PCI总线将报文数据写入操作系统的内存中,即数据报文完成入队操作。(PS:PCIe总线可能成为网卡带宽的瓶颈)

3.2.出队

e78c623901c5475c0e8716f57c5cfb66.png

应用调用rte_eth_rx_burst(dpdk rte_ethdev.h)函数开始批量收包,最大收包数量由参数nb_pkts决定(比如设置为64)。其核心流程由ixgbe_recv_pkts(dpdk ixgbe_rxtx.c)实现,从收包队列rx_tail位置开始收,循环读取一个报文、填空一个报文(空报文数据),读取64个后,重新标记rx_tail的位置,完成出队操作,将收取的报文作返回供应用处理。代码简化如下。

-------------------------------------------------

struct rte_mbuf *rxm;

//从队列的tail位置开始取包

rx_id = rxq->rx_tail;

//循环获取nb_pkts个包

while (nb_rx < nb_pkts)

{

......

rxdp = &rx_ring[rx_id];

//检查DD位是否为1,是1则说明该位置已放入数据包,否则表示没有报文,退出

staterr=rxdp->

wb.upper.status_error;

if(!(staterr

&rte_cpu_to_le_32(IXGBE_RXDADV_STAT_DD)))

break;

rxd = *rxdp;

//申请一个mbuf(nmb),用于交换

nmb = rte_mbuf_raw_alloc(rxq->mb_pool);

//从sw_ring中读取一个报文mbuf(存入rxm)

rxe = &sw_ring[rx_id];

rxm = rxe->mbuf;

//往sw_ring中填空一个新报文mbuf(nmb)

rxe->mbuf = nmb;

//新mbuf对应的报文数据物理地址填入rx_ring对应位置,并将hdr_addr置0(DD位置0)

dma_addr =rte_cpu_to_le_64

(rte_mbuf_data_dma_addr_default(nmb));

rxdp->read.hdr_addr = 0;

rxdp->read.pkt_addr = dma_addr;

//对读取mbuf的报文信息进行初始化

rxm->pkt_len = pkt_len;

rxm->data_len = pkt_len;

rxm->port = rxq->port_id;

......

//读取的报文mbuf存入rx_pkts

rx_pkts[nb_rx++] = rxm;

}

//重新标记rx_tail位置

rxq->rx_tail = rx_id;

-------------------------------------------------

推荐阅读

- 栈 vs 队列

- Intel 82599网卡光模块使用的坑

24a1d3cddde762d1db9f920100239769.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值