一、报文的接收流程

传统方式接收报文时,当网卡接收到报文后会产生硬件中断,进而报文会通过协议栈,最后到达应用层,这个过程需要内核协议栈的处理。 和传统报文接收不同,当应用层想要接收来自网卡的报文时, 应用层通过while死循环的方式,调用rte_eth_rx_burst接口轮询接收来自网卡的报文,相当于绕过了内核协议栈,将内核旁路了。通过轮询的方式,报文不经过内核,提高了网络转发性能,同时也降低了内核与用户态系统调用的开销。

来看下报文接收的整体流程。当网卡接收到报文时将会产生硬件中断,通知dma控制器接收报文。dma控制器会从网卡接收队列中将报文拷贝到硬件接收空间(也就是描述符空间)指向的地址位置,也就是mbuf。最终dma控制器将报文从网卡接收队列中拷贝到mbuf来。 应用层通过while死循环,轮询调用rte_eth_rx_burst接口,从这个mbuf软件接收空间中获取报文。
来看下代码的实现过程。应用层调用rte_eth_rx_burst接口来接收报文,函数内部会调用pmd用户态驱动的接收报文接口。如果是e1000网卡,则pmd用户态驱动接收报文的接口为eth_igb_recv_pkts

uint16_t rte_eth_rx_burst(uint8_t port_id,  uint16_t queue_id, struct rte_mbuf **rx_pkts,  uint16_t nb_pkts)
{//如果是e1000网卡,则接收报文的接口为eth_igb_recv_pktsreturn (*dev->rx_pkt_burst)(dev->data->rx_queues[queue_id], rx_pkts, nb_pkts);
}

现在来看下eth_igb_recv_pkts接口的实现过程。

1、从描述符中获取报文返回给应用层

首先根据应用层最后一次获取报文的位置,进而从描述符队列找到待被应用层接收的描述符。此时会判断描述符中的status_error是否已经打上了dd标记,有dd标记说明dma控制器已经把报文放到mbuf中了。这里解释下dd标记,当dma控制器将接收到的报文保存到描述符指向的mbuf空间时,由dma控制器打上dd标记,表示dma控制器已经把报文放到mbuf中了。应用层在获取完报文后,需要清除dd标记。

找到了描述符的位置,也就找到了mbuf空间。此时会根据描述符里面保存的信息,填充mbuf结构。例如填充报文的长度,vlanid, rss等信息。填充完mbuf后,将这个mbuf保存到应用层传进来的结构中,返回给应用层,这样应用层就获取到了这个报文。

uint16_t eth_igb_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts)
{while (nb_rx < nb_pkts) {//从描述符队列中找到待被应用层最后一次接收的那个描述符位置rxdp = &rx_ring[rx_id];staterr = rxdp->wb.upper.status_error;//检查状态是否为dd, 不是则说明驱动还没有把报文放到接收队列,直接退出if (! (staterr & rte_cpu_to_le_32(E1000_RXD_STAT_DD))){break;};//找到了描述符的位置,也就从软件队列中找到了mbufrxe = &sw_ring[rx_id];rx_id++;rxm = rxe->mbuf;     //填充mbufpkt_len = (uint16_t) (rte_le_to_cpu_16(rxd.wb.upper.length) - rxq->crc_len);rxm->data_off = RTE_PKTMBUF_HEADROOM;rxm->nb_segs = 1;rxm->pkt_len = pkt_len;rxm->data_len = pkt_len;rxm->port = rxq->port_id;rxm->hash.rss = rxd.wb.lower.hi_dword.rss;rxm->vlan_tci = rte_le_to_cpu_16(rxd.wb.upper.vlan);//保存到应用层rx_pkts[nb_rx++] = rxm;}
}

2、从内存池中获取新的mbuf告诉dma控制器

当应用层从软件队列中获取到mbuf后, 需要重新从内存池申请一个mbuf空间,并将mbuf地址放到描述符队列中, 相当于告诉dma控制器,后续将收到的报文保存到这个新的mbuf中, 这也是狸猫换太子的过程。描述符是mbuf与dma控制器的中介,那dma控制器怎么知道描述符队列的地址呢?这在上一篇文章中已经介绍过了,将描述符队列的地址写入到了寄存器中,dma控制器通过读取寄存器就知道描述符队列的地址。

需要注意的是,将mbuf的地址保存到描述符中,此时会将dd标记给清0,这样dma控制器就认为这个mbuf里面的内容已经被应用层接收了,收到新报文后可以重新放到这个mbuf中。

uint16_t eth_igb_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts)
{while (nb_rx < nb_pkts) {//申请一个新的mbufnmb = rte_rxmbuf_alloc(rxq->mb_pool);//因为原来的mbuf被应用层取走了。这里替换原来的软件队列mbuf,这样网卡收到报文后可以放到这个新的mbufrxe->mbuf = nmb;dma_addr = rte_cpu_to_le_64(RTE_MBUF_DATA_DMA_ADDR_DEFAULT(nmb));//将mbuf地址保存到描述符中,相当于高速dma控制器mbuf的地址。rxdp->read.hdr_addr = dma_addr;           //这里会将dd标记清0rxdp->read.pkt_addr = dma_addr;         }
}

二、报文的发送流程

先整体来看下报文的发送流程。当应用层需要发送报文时,调用rte_eth_tx_burst接口,将报文放到软件发送空间,也就是mbuf空间中。同时将mbuf的地址写入到硬件发送空间,也就是描述符空间。dma控制器读取描述符空间,就知道需要从描述符指向的位置,也就是mbuf中获取报文,然后通过网卡发送出去。

来看下代码的实现过程。应用层调用rte_eth_tx_burst接口来发送报文,函数内部会调用pmd用户态驱动的发送报文接口。如果是e1000网卡,则pmd用户态驱动发送报文的接口为eth_igb_xmit_pkts

//发送报文
uint16_t rte_eth_tx_burst(uint8_t port_id, uint16_t queue_id, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
{//如果是e1000网卡,则发送报文的接口为eth_igb_xmit_pktsreturn (*dev->tx_pkt_burst)(dev->data->tx_queues[queue_id], tx_pkts, nb_pkts);
}

1、将应用层的报文放到软件队列中

当应用层要发包时,eth_igb_xmit_pkts内部会将应用层的报文放到软件队列中。在软件队列中找到了最后一次发送的的位置后,就可以将报文放到这个软件队列相应位置上。应用层要发送的报文有可能是需要分片的,每一个分片报文都会占用软件队列中的一个元素。某个报文的所有分片报文所占用的软件队列元素,last_id都指向同一个软件队列元素。

uint16_t eth_igb_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
{//将分片报文放到发送队列中m_seg = tx_pkt;do {//将mbuf放到发送软件队列中txe->mbuf = m_seg;//同一个分片的软件队列元素,last_id指向同一个队列位置txe->last_id = tx_last;tx_id = txe->next_id;//指向下一个软件队列txe = txn;m_seg = m_seg->next;} while (m_seg != NULL);
}

2、将mbuf地址信息保存到描述符中

对于要发送的每一个mbuf报文,都需要将mbuf的地址信息保存到描述符队列中。而这个描述符队列的地址已经写入到寄存器了,这个在上一篇文章已经分析过了。dma控制器通过读取寄存器就可以知道描述符队列的地址,也就知道需要从描述符队列指向的位置,也就是从mbuf中获取报文,然后从网卡发送出去。

需要注意的是,这个发送过程,也是将mbuf、dma控制器、描述符队列关联起来的过程。

uint16_t eth_igb_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
{//将分片报文放到发送队列中m_seg = tx_pkt;do {//找到最后一次发送的位置,也就是找到最后一次可以使用的描述符txd = &txr[tx_id];//将mbuf地址信息保存到发送描述符中slen = (uint16_t) m_seg->data_len;buf_dma_addr = RTE_MBUF_DATA_DMA_ADDR(m_seg);txd->read.buffer_addr = rte_cpu_to_le_64(buf_dma_addr);txd->read.cmd_type_len = rte_cpu_to_le_32(cmd_type_len | slen);txd->read.olinfo_status = rte_cpu_to_le_32(olinfo_status);m_seg = m_seg->next;} while (m_seg != NULL);
}

到此为止,dpdk报文的发送,接收流程已经分析完成了

原文链接:https://blog.csdn.net/ApeLife/article/details/102469243?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-3

DPDK 报文收发流程(二十五)相关推荐

  1. dpdk报文收发流程--理解dma控制器、UIO驱动、描述符空间、mbuf空间、KNI

    1. dpdk报文收发流程 1.1 报文接收流程 传统方式接收报文时,当网卡接收到报文后会产生硬件中断,进而报文会通过协议栈,最后到达应用层,这个过程需要内核协议栈的处理. 和传统报文接收不同,当应用 ...

  2. DPDK KNI实现(二十五)

    一.为什么要用kni 通常情况下dpdk用于二三层报文转发,接收到来自网卡的报文后,如果是二层报文则查找fdb表: 如果是三层报文,则进行dnat, snat处理后,查找路由表, 将报文转发给下一跳路 ...

  3. 《深入浅出DPDK》读书笔记(十五):DPDK应用篇(Open vSwitch(OVS)中的DPDK性能加速)

    Table of Contents Open vSwitch(OVS)中的DPDK性能加速 174.虚拟交换机简介 175.OVS简介 176.DPDK加速的OVS 177.OVS的数据通路 178. ...

  4. 2021年大数据Hadoop(二十五):YARN通俗介绍和基本架构

    全网最详细的Hadoop文章系列,强烈建议收藏加关注! 后面更新文章都会列出历史文章目录,帮助大家回顾知识重点. 目录 本系列历史文章 前言 YARN通俗介绍和基本架构 Yarn通俗介绍 Yarn基本 ...

  5. linux exec 二程序,二十五、Linux 进程与信号---exec函数

    25.1 介绍 在用 fork 函数创建子进程后,子进程往往要调用一种 exec 函数以执行另一个程序 当进程调用一种 exec 函数时,该进程完全由新程序代换,替换原有进程的正文,而新程序则从其 m ...

  6. SAP UI5 初学者教程之二十五 - 使用代理服务器解决 SAP UI5 应用访问远端 OData 服务的跨域问题试读版

    一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 初学者教程之一:Hello World SAP UI5 初学者教程之二:SAP UI5 ...

  7. osgEarth的Rex引擎原理分析(二十五)地形瓦片大小尺寸和LOD的关系

    目标:(十八)中的问题55 osgEarth::TerrainOption中_tileSize默认大小为17,LOD的默认范围为0-23,这两个值的关系是什么? 还有瓦片的像素尺寸_tilePixel ...

  8. 二十五个软件测试经典面试题,你确定不收藏一波?

    二十五个软件测试经典面试题全在这里了,有兴趣的朋友建议收藏一波,或者留言交流! 1.在搜索引擎中输入汉字就可以解析到对应的域名,请问如何用LoadRunner进行测试? 建立测试计划,确定测试标准和测 ...

  9. Android进阶知识(二十五):Bitmap简介及其高效加载

    Android进阶知识(二十五):Bitmap简介及其高效加载 一.Bitmap   Bitmap代表一个位图,在Android中指的是一张图片,可以是png.jpg等格式的图片.BitmapDraw ...

最新文章

  1. STM32F030 ADC1的DMA采样问题
  2. MATLAB在数学建模中的应用(三)
  3. Linux学习笔记02
  4. Linux 上安装 SQL Server
  5. mysql-ubuntu卸载安装mysql
  6. 7-177 输出全排列 (20 分)
  7. xslt 定义表格html表格样式,使用 XSLT 作为 HTML 的样式表.doc
  8. PK 想说爱你不容易
  9. hibernate 执行sql在哪个类_一篇文章让你马上入门Hibernate
  10. 一天一个小技巧(4)——利用Python和MATLAB进行图片二值化
  11. linux之awk命令获取最后一列
  12. python 逻辑回归_不会Python没问题!用Excel实现简单的逻辑回归!
  13. mysql2008 精简版_精简版 SqlServer2008 的安装和使用
  14. openwrt开机启动设置
  15. libnativehelper 加载库失败
  16. Linux 安装MySql 步骤
  17. 21省人均GDP超过1万美元,北京以19.01万元继续稳居榜首
  18. 百度商业系统大规模微服务分布式监控实践
  19. 使用STM8S003K3 ADC简介以及初始化
  20. 上海纽约大学计算机专业怎样,上海纽约大学王牌专业有哪些

热门文章

  1. 从零开始学python网络爬虫-从零开始学Python网络爬虫 PDF 下载
  2. python3.8.5怎么用-Python 3.8.5 正式发布
  3. python实现简单的api接口-简单实现Python调用有道API接口(最新的)
  4. python 基础命令-Python 命令行(CLI)基础库
  5. 学习python需要什么基础-学习Python需要哪些基础知识?
  6. python 加注拼音-一个将汉字转换成汉语拼音的python库的代码
  7. python上海培训哪里比较好-上海哪个python培训机构好
  8. python编写程序-30分钟学会用Python编写简单程序
  9. java excel md5,excel表格数据md5加密-excel 怎么把文本转化成md5
  10. LeetCode H-Index II(二分法)