l2fwd是DPDK中的非常经典的例子。二层转发模型。

就是在相邻的网卡接口间互相传递报文。
网口0和网口1之间报文互传。
网口2和网口3之间报文互传。
。。。。。。。。。。。。

运行参数 .
在目录/home/yml/dpdk/dpdk-stable-16.07.2/examples/l2fwd/build 下面(当然要先编译这个例子 make)
  1. ./l2fwd [EAL options]---p PORTMASK [-q NQ]--[no-]mac-updating
EAL options : 就是EAL的参数 ,我这里设成
  1. -c 3-n 2
  1. -c 表示的是整个程序会使用哪些核
  2. 例如:-c f 表示 20+21+22+23
  3. 表示 四个核都会用上。
 
-n 表示内存的通道数。
 
-- -p  : 表示使用哪些网口, 
  1. ---p 3
表示使用网口0和1 。
  1. -q 1
表示每个逻辑核(lcore线程)上分配一个网口。
 
我这里直接用下面的参数运行。
  1. ./l2fwd -c3 -n2 ---p 3-q 1
 

 
  1. int
  2. main(int argc,char**argv)
  3. {
  4. struct lcore_queue_conf *qconf;
  5. struct rte_eth_dev_info dev_info;
  6. int ret;
  7. uint8_t nb_ports;
  8. uint8_t nb_ports_available;
  9. uint8_t portid, last_port;
  10. unsigned lcore_id, rx_lcore_id;
  11. unsigned nb_ports_in_mask =0;
  12. /* init EAL */
  13. ret = rte_eal_init(argc, argv); //EAL的初使化。
  14. if(ret <0)
  15. rte_exit(EXIT_FAILURE,"Invalid EAL arguments\n");
  16. argc -= ret;
  17. argv += ret;
  18. force_quit =false;
  19. signal(SIGINT, signal_handler);
  20. signal(SIGTERM, signal_handler);
  21. /* parse application arguments (after the EAL ones) */
  22. ret = l2fwd_parse_args(argc, argv);//这里是解析后面的---p 3-q 1 参数,用于获得这些参数的值。
  23. if(ret <0)
  24. rte_exit(EXIT_FAILURE,"Invalid L2FWD arguments\n");
  25. /* convert to number of cycles */
  26. timer_period *= rte_get_timer_hz();
  27. /* create the mbuf pool */
  28. l2fwd_pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", NB_MBUF,
  29. MEMPOOL_CACHE_SIZE,0, RTE_MBUF_DEFAULT_BUF_SIZE, //创建内存池,
  30. rte_socket_id());
  31. if(l2fwd_pktmbuf_pool == NULL)
  32. rte_exit(EXIT_FAILURE,"Cannot init mbuf pool\n");
  33. nb_ports = rte_eth_dev_count(); //获得当初可用的最大网口数,是从PCI中获得的。
  34. if(nb_ports ==0)
  35. rte_exit(EXIT_FAILURE,"No Ethernet ports - bye\n");
  36. /* reset l2fwd_dst_ports */
  37. for(portid =0; portid < RTE_MAX_ETHPORTS; portid++)
  38. l2fwd_dst_ports[portid]=0;
  39. last_port =0;
  40. /*
  41. * Each logical core is assigned a dedicated TX queue on each port.
  42. */
  43. for(portid =0; portid < nb_ports; portid++){ //这里其实就是确定每个网口的发包对象。如网口0的报文传给网口1,。。。。这里就是初使化一个数组。
  44. /* skip ports that are not enabled */
  45. if((l2fwd_enabled_port_mask &(1<< portid))==0)
  46. continue;
  47. if(nb_ports_in_mask %2){
  48. l2fwd_dst_ports[portid]= last_port;
  49. l2fwd_dst_ports[last_port]= portid;
  50. }
  51. else
  52. last_port = portid;
  53. nb_ports_in_mask++;
  54. rte_eth_dev_info_get(portid,&dev_info);
  55. }
  56. if(nb_ports_in_mask %2){
  57. printf("Notice: odd number of ports in portmask.\n");
  58. l2fwd_dst_ports[last_port]= last_port;
  59. }
  60. rx_lcore_id =0;
  61. qconf = NULL;
  62. /* Initialize the port/queue configuration of each logical core */
  63. for(portid =0; portid < nb_ports; portid++){//给每一个网口分配一个可用的,你配置过的逻辑核
  64. /* skip ports that are not enabled */
  65. if((l2fwd_enabled_port_mask &(1<< portid))==0)
  66. continue;
  67. /* get the lcore_id for this port */
  68. while(rte_lcore_is_enabled(rx_lcore_id)==0||
  69. lcore_queue_conf[rx_lcore_id].n_rx_port ==
  70. l2fwd_rx_queue_per_lcore){
  71. rx_lcore_id++;
  72. if(rx_lcore_id >= RTE_MAX_LCORE)
  73. rte_exit(EXIT_FAILURE,"Not enough cores\n");
  74. }
  75. if(qconf !=&lcore_queue_conf[rx_lcore_id])
  76. /* Assigned a new logical core in the loop above. */
  77. qconf =&lcore_queue_conf[rx_lcore_id];
  78. qconf->rx_port_list[qconf->n_rx_port]= portid;
  79. qconf->n_rx_port++;
  80. printf("Lcore %u: RX port %u\n", rx_lcore_id,(unsigned) portid);
  81. }
  82. nb_ports_available = nb_ports;
  83. /* Initialise each port */
  84. for(portid =0; portid < nb_ports; portid++){//就是初使化网口,给每个网口分配接收的缓存,分配接收和发送的队列。
  85. /* skip ports that are not enabled */
  86. if((l2fwd_enabled_port_mask &(1<< portid))==0){
  87. printf("Skipping disabled port %u\n",(unsigned) portid);
  88. nb_ports_available--;
  89. continue;
  90. }
  91. /* init port */
  92. printf("Initializing port %u... ",(unsigned) portid);
  93. fflush(stdout);
  94. ret = rte_eth_dev_configure(portid,1,1,&port_conf);//设置portid这个网口一个接收和一个发送的队列。
  95. if(ret <0)
  96. rte_exit(EXIT_FAILURE,"Cannot configure device: err=%d, port=%u\n",
  97. ret,(unsigned) portid);
  98. rte_eth_macaddr_get(portid,&l2fwd_ports_eth_addr[portid]);
  99. /* init one RX queue */
  100. fflush(stdout);
  101. ret = rte_eth_rx_queue_setup(portid,0, nb_rxd, //给对应的网口分配接收队列。
  102. rte_eth_dev_socket_id(portid),
  103. NULL,
  104. l2fwd_pktmbuf_pool);
  105. if(ret <0)
  106. rte_exit(EXIT_FAILURE,"rte_eth_rx_queue_setup:err=%d, port=%u\n",
  107. ret,(unsigned) portid);
  108. /* init one TX queue on each port */
  109. fflush(stdout);
  110. ret = rte_eth_tx_queue_setup(portid,0, nb_txd,//给对应的网口分配发送队列。
  111. rte_eth_dev_socket_id(portid),
  112. NULL);
  113. if(ret <0)
  114. rte_exit(EXIT_FAILURE,"rte_eth_tx_queue_setup:err=%d, port=%u\n",
  115. ret,(unsigned) portid);
  116. /* Initialize TX buffers */
  117. tx_buffer[portid]= rte_zmalloc_socket("tx_buffer",//分配接收缓存空间,就是收包的缓存。
  118. RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST),0,
  119. rte_eth_dev_socket_id(portid));
  120. if(tx_buffer[portid]== NULL)
  121. rte_exit(EXIT_FAILURE,"Cannot allocate buffer for tx on port %u\n",
  122. (unsigned) portid);
  123. rte_eth_tx_buffer_init(tx_buffer[portid], MAX_PKT_BURST);
  124. ret = rte_eth_tx_buffer_set_err_callback(tx_buffer[portid],//当报文发送失败后要调用的回调函数
  125. rte_eth_tx_buffer_count_callback,
  126. &port_statistics[portid].dropped);
  127. if(ret <0)
  128. rte_exit(EXIT_FAILURE,"Cannot set error callback for "
  129. "tx buffer on port %u\n",(unsigned) portid);
  130. /* Start device */
  131. ret = rte_eth_dev_start(portid);
  132. if(ret <0)
  133. rte_exit(EXIT_FAILURE,"rte_eth_dev_start:err=%d, port=%u\n",
  134. ret,(unsigned) portid);
  135. printf("done: \n");
  136. rte_eth_promiscuous_enable(portid);//设置该网口为混杂模式,就是接收所有的报文。
  137. printf("Port %u, MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n\n",//初使化报文统计
  138. (unsigned) portid,
  139. l2fwd_ports_eth_addr[portid].addr_bytes[0],
  140. l2fwd_ports_eth_addr[portid].addr_bytes[1],
  141. l2fwd_ports_eth_addr[portid].addr_bytes[2],
  142. l2fwd_ports_eth_addr[portid].addr_bytes[3],
  143. l2fwd_ports_eth_addr[portid].addr_bytes[4],
  144. l2fwd_ports_eth_addr[portid].addr_bytes[5]);
  145. /* initialize port stats */
  146. memset(&port_statistics,0,sizeof(port_statistics));
  147. }
  148. if(!nb_ports_available){
  149. rte_exit(EXIT_FAILURE,
  150. "All available ports are disabled. Please set portmask.\n");
  151. }
  152. check_all_ports_link_status(nb_ports, l2fwd_enabled_port_mask);//检查这些网口是否可以用。
  153. ret =0;
  154. /* launch per-lcore init on every lcore */
  155. rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, NULL, CALL_MASTER);//在所有的逻辑核(包括管理核,一般DPDK会默认将第一个可用的逻辑核当管理核)上执行l2fwd_launch_one_lcore这个函数。
  156. RTE_LCORE_FOREACH_SLAVE(lcore_id){
  157. if(rte_eal_wait_lcore(lcore_id)<0){//等待从逻辑核的结束。
  158. ret =-1;
  159. break;
  160. }
  161. }
  162. for(portid =0; portid < nb_ports; portid++){ //down掉网口,结束。
  163. if((l2fwd_enabled_port_mask &(1<< portid))==0)
  164. continue;
  165. printf("Closing port %d...", portid);
  166. rte_eth_dev_stop(portid);
  167. rte_eth_dev_close(portid);
  168. printf(" Done\n");
  169. }
  170. printf("Bye...\n");
  171. return ret;
  172. }
 
  1. staticvoid
  2. l2fwd_main_loop(void)
  3. {
  4. struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
  5. struct rte_mbuf *m;
  6. int sent;
  7. unsigned lcore_id;
  8. uint64_t prev_tsc, diff_tsc, cur_tsc, timer_tsc;
  9. unsigned i, j, portid, nb_rx;
  10. struct lcore_queue_conf *qconf;
  11. constuint64_t drain_tsc =(rte_get_tsc_hz()+ US_PER_S -1)/ US_PER_S *
  12. BURST_TX_DRAIN_US;
  13. struct rte_eth_dev_tx_buffer *buffer;
  14. prev_tsc =0;
  15. timer_tsc =0;
  16. lcore_id = rte_lcore_id();
  17. qconf =&lcore_queue_conf[lcore_id];//获得我们初使化的时候配置的逻辑核与网口的对应关系列表。
  18. if(qconf->n_rx_port ==0){
  19. RTE_LOG(INFO, L2FWD,"lcore %u has nothing to do\n", lcore_id);
  20. return;
  21. }
  22. RTE_LOG(INFO, L2FWD,"entering main loop on lcore %u\n", lcore_id);
  23. for(i =0; i < qconf->n_rx_port; i++){
  24. portid = qconf->rx_port_list[i];
  25. RTE_LOG(INFO, L2FWD," -- lcoreid=%u portid=%u\n", lcore_id,
  26. portid);
  27. }
  28. while(!force_quit){
  29. cur_tsc = rte_rdtsc();
  30. /*
  31. * TX burst queue drain
  32. */
  33. diff_tsc = cur_tsc - prev_tsc;
  34. if(unlikely(diff_tsc > drain_tsc)){//时间时隔到了就刷新发送所有网口上的待发送的报文。
  35. for(i =0; i < qconf->n_rx_port; i++){
  36. portid = l2fwd_dst_ports[qconf->rx_port_list[i]];
  37. buffer = tx_buffer[portid];
  38. sent = rte_eth_tx_buffer_flush(portid,0, buffer);
  39. if(sent)
  40. port_statistics[portid].tx += sent;
  41. }
  42. /* if timer is enabled */
  43. if(timer_period >0){
  44. /* advance the timer */
  45. timer_tsc += diff_tsc;
  46. /* if timer has reached its timeout */
  47. if(unlikely(timer_tsc >= timer_period)){
  48. /* do this only on master core */
  49. if(lcore_id == rte_get_master_lcore()){//如果是在逻辑核上,且符合条件,就打印统计信息。
  50. print_stats();
  51. /* reset the timer */
  52. timer_tsc =0;
  53. }
  54. }
  55. }
  56. prev_tsc = cur_tsc;
  57. }
  58. /*
  59. * Read packet from RX queues
  60. */
  61. for(i =0; i < qconf->n_rx_port; i++){//轮询每个网口,进行接收报文。
  62. portid = qconf->rx_port_list[i];
  63. nb_rx = rte_eth_rx_burst((uint8_t) portid,0,
  64. pkts_burst, MAX_PKT_BURST);
  65. port_statistics[portid].rx += nb_rx;
  66. for(j =0; j < nb_rx; j++){
  67. m = pkts_burst[j];
  68. rte_prefetch0(rte_pktmbuf_mtod(m,void*));
  69. l2fwd_simple_forward(m, portid);
  70. }
  71. }
  72. }
  73. }
  1. staticvoid
  2. l2fwd_simple_forward(struct rte_mbuf *m,unsigned portid)
  3. {
  4. struct ether_hdr *eth;
  5. void*tmp;
  6. unsigned dst_port;
  7. int sent;
  8. struct rte_eth_dev_tx_buffer *buffer;
  9. dst_port = l2fwd_dst_ports[portid];//获得发送的网口,相当于就是做转发。
  10. eth = rte_pktmbuf_mtod(m,struct ether_hdr *);
  11. /* 02:00:00:00:00:xx */
  12. tmp =&eth->d_addr.addr_bytes[0];
  13. *((uint64_t*)tmp)=0x000000000002+((uint64_t)dst_port <<40);
  14. /* src addr */
  15. ether_addr_copy(&l2fwd_ports_eth_addr[dst_port],&eth->s_addr);
  16. buffer = tx_buffer[dst_port];//获得初使化的时候配置的发送缓存。
  17. sent = rte_eth_tx_buffer(dst_port,0, buffer, m);//预发送,其实并没有真正的发送。
  18. if(sent)
  19. port_statistics[dst_port].tx += sent;
  20. }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

DPDK l2fwd 浅注的更多相关文章

  1. DPDK L2fwd 源码阅读

    代码部分 /* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2010-2016 Intel Corporation */ #include ...

  2. DPDK l2fwd

    dpdk的l2fwd主要做二层转发,代码分析如下. #include <stdio.h> #include <stdlib.h> #include <string.h&g ...

  3. dpdk l2fwd 应用流程分析

    int MAIN(int argc, char **argv) { struct lcore_queue_conf *qconf; struct rte_eth_dev_info dev_info; ...

  4. 浅谈.net core如何使用EFCore为一个上下文注类型注入多个实例用于连接主从数据库

    在很多一主多从数据库的场景下,很多开发同学为了复用DbContext往往采用创建一个包含所有DbSet<Model>父类通过继承派生出Write和ReadOnly类型来实现,其实可以通过命 ...

  5. 浅谈Hybrid技术的设计与实现第三弹——落地篇

    前言 接上文:(阅读本文前,建议阅读前两篇文章先) 浅谈Hybrid技术的设计与实现 浅谈Hybrid技术的设计与实现第二弹 根据之前的介绍,大家对前端与Native的交互应该有一些简单的认识了,很多 ...

  6. 浅谈Android编码规范及命名规范

    前言: 目前工作负责两个医疗APP项目的开发,同时使用LeanCloud进行云端配合开发,完全单挑. 现大框架已经完成,正在进行细节模块上的开发 抽空总结一下Android项目的开发规范:1.编码规范 ...

  7. 浅谈Vue.js

    作为一名Vue.js的忠实用户,我想有必要写点文章来歌颂这一门美好的语言了,我给它的总体评价是“简单却不失优雅,小巧而不乏大匠”,下面将围绕这句话给大家介绍Vue.js,希望能够激发你对Vue.js的 ...

  8. 浅谈php生成静态页面

    一.引 言 在速度上,静态页面要比动态页面的比方php快很多,这是毫无疑问的,但是由于静态页面的灵活性较差,如果不借助数据库或其他的设备保存相关信息的话,整体的管理上比较繁琐,比方修改编辑.比方阅读权 ...

  9. jsp内置对象浅谈

    jsp内置对象浅谈 | 浏览:1184 | 更新:2013-12-11 16:01 JSP内置对象:我们在使用JSP进行页面编程时可以直接使用而不需自己创建的一些Web容器已为用户创建好的JSP内置对 ...

随机推荐

  1. [ios2]蓝牙通信【转】

    iPhone开发应用中关于GameKit蓝牙实例讲解是本文要介绍的内容,主要是来了解并学习GameKit蓝牙实例.介绍一下这个实例实现的是两个带有蓝牙设备的touch之间的一个小游戏,在界面上有个可以 ...

  2. labview 调用 matlab script的神坑! Error 1050 occurred at LabVIEW

    显示变量没有被定义,原因是clear 关键字的问题,去掉即可!!! 未找到 文件路径,定位: 文件路径中不能有中文路径

  3. ssm框架中css被拦截

    最近用springmvc spring mybatis框架写程序,请求成功并获得数据,唯独css样式不能加载,但路径正确,css文件编码也是utf-8,用火狐debug总是显示未请求到(都快怀疑自己写 ...

  4. CodeForces 645C Enduring Exodus

    枚举,三分. 首先,这$n+1$个人一定是连续的放在一起的.可以枚举每一个起点$L$,然后就是在$[L,R]$中找到一个位置$p$,使得$p4最优,因为越往两边靠,距离就越大,在中间某位置取到最优解, ...

  5. CentOS Gnome 识别 NTFS-3G

    安装完NTFS-3G后,使用命令行已经可以正常挂载NTFS分区了 但如果是源码编译安装(epel yum 库也有),还需要添加一个软链接,才能点击Gnome的左边栏进行挂载,因为Gnome挂载NTFS ...

  6. 想要见识外太空?一款VR头显就能帮你实现梦想

    除了宇航员,我们中的大多数人一生都没有机会前往地球之外的宇宙空间,只能在图片和纪录片中感受浩瀚宇宙的震撼. 美国肯尼迪航天中心和BrandVR合作推出的VR头显 而NASA在VR中的投资,创造的新的V ...

  7. android studio依赖库工程Activity显示问题及库工程设置

    android studio引用库工程其实不难,直接添加依赖module即可,但是我在操作过程中出现一些奇怪的问题,苦扰我一整天,为了祭奠这苦命的一天特别mark一下. 首先描述一下我的错误现象: s ...

  8. Java 四舍五入并小数点后保存两位,千分位分隔

    import java.text.DecimalFormat; public class FileTest {    public static void main(String[] args) {  ...

  9. Scala减少代码重复

    高阶函数可以把其它函数当作函数参数,帮助我们减少代码重复,例如: object FileMatcher { private def fileHere = (new File(".\\file ...

  10. 特性(Attributes)

    用以将元数据或声明信息与代码(程序集.类型.方法.属性等)相关联.特性与程序实体相关联后,即可在运行时用反射技术查询特性. 例如,在一个方法前标注[Obsolete]特性,则调用该方法时VS则会提示该 ...