DPDK L2 fwd代码走读报告(代码流程分析)

更新时间:2023-09-09 08:10:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

****

DPDK 学习

L2 fwd代码走读报告

导师:*** 学生:*** 2016-2-18

目录

一、对于DPDK的认识: ................................................................................................................ 2 二、对L2 fwd的认识: .................................................................................................................. 3

2.1运行配置............................................................................................................................. 3 2.2运行环境搭建 ..................................................................................................................... 3 2.3功能分析: ......................................................................................................................... 6 2.4详细流程图(调用关系)如下: ..................................................................................... 6 2.5运行截图............................................................................................................................. 8 2.6详细代码注释分析: ......................................................................................................... 8

一、对于DPDK的认识:

主要应用x86通用平台,转发处理网络数据包,定位在不需要专用网络处理器,但通用网络处理器对数据处理性能又不能满足需求的客户。

DPDK,搭载x86服务器,成本变化不大,但对数据的处理性能又有非常显著的提高,对传统linux技术做一定的优化,特别之处在于:hugepage,uio,zero copy, cpu affinity等。

关于hugetlbpage(在这块大页面上做自己的内存管理系统),之前讲过,它的主要好处当然是通过利用大内存页提高内存使用效率,。由于DPDK是应用层平台,所以与此紧密相连的网卡驱动程序(当然,主要是intel自身的千兆igb与万兆ixgbe驱动程序)都通过uio(用户层驱动、轮询、0拷贝)机制运行在用户态下。cpu affinity(多核架构,核线程绑定物理核)机制是多核cpu发展的结果,,在越来越多核心的cpu机器上,如何提高外设以及程序工作效率的最直观想法就是让各个cpu核心各自干专门的事情,比如两个网卡eth0和eth1都收包,可以让cpu0专心处理eth0,cpu1专心处理eth1,没必要cpu0一下处理eth0,一下又处理eth1,还有一个网卡多队列的情况也是类似,等等,DPDK利用cpu affinity主要是将控制面线程以及各个数据面线程绑定到不同的cpu,省却了来回反复调度的性能消耗,各个线程一个while死循环,专心致志的做事,互不干扰(当然还是有通信的,比如控制面接收用户配置,转而传递给数据面的参数设置等)。

总结如下:

1、 使用大页缓存支持来提高内存访问效率。

2、 利用UIO支持,提供应用空间下驱动程序的支持,也就是说网卡驱动是运行在用户空间 的,减下了报文在用户空间和应用空间的多次拷贝。

3、 利用LINUX亲和性支持,把控制面线程及各个数据面线程绑定到不同的CPU核,节省了 线程在各个CPU核来回调度。

4、 提供内存池和无锁环形缓存管理,加快内存访问效率。

在x86服务器,1G/10G/40G网卡包转发,64Byte小包,基本能做到70%以上的转发,而传统linux系统只能达5%左右,在网络大数据流时代,DPDK加码,优势明显。

二、对L2fwd的认识:

2.1运行配置

虚拟机软件:VMWare WorkStation 12.0.0 build-2985596 CPU: 2个CPU, 每个CPU2个核心 内存: 1GB+

网卡:intel网卡*2, 用于dpdk试验;另一块网卡用于和宿主系统进行通信

2.2运行环境搭建

在root权限下:

1)编译dpdk

进入dpdk主目录,输入

make install T=x86_64-native-linuxapp-gcc进行编译

2)配置大页内存(非NUMA)

echo 128 >

/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

mkdir /mnt/huge

mount -t hugetlbfs nodev /mnt/huge

可以用以下命令查看大页内存状态:

cat /proc/meminfo | grep Huge

3)安装igb_uio驱动

modprobe uio

insmod x86_64-native-linuxapp-gcc/kmod/igb_uio.ko

4)绑定网卡

先看一下当前网卡的状态

./tools/dpdk_nic_bind.py --status

图1 网卡已经绑定好

进行绑定:

./tools/dpdk_nic_bind.py -b igb_uio 0000:02:06.0

./tools/dpdk_nic_bind.py -b igb_uio 0000:02:05.0

如果网卡有接口名,如eth1, eth2, 也可以在-b igb_uio后面使用接口名, 而不使用pci地址。

5 ) 设置环境变量:

export RTE_SDK=/home/lv/dpdk/dpdk-1.7.0

export RTE_TARGET=x86_64-native-linuxapp-gcc

之后进入/examples/l2,运行make,成功会生成build目录,其中有编译好的l2fwd程序。

6)运行程序

./build/l2fwd -c f -n 2 -- -q 1 -p 0x3

2.3功能分析:

DPDK搭建环境完成后,网卡绑定到相应IGB_UIO驱动接口上,所有的网络数

据包都会到DPDK,网卡接收网络数据包,再从另一个网卡转发出去

2.4详细流程图(调用关系)如下:

(初学者,欢迎讨论QQ:780102849,望各位指错)

DPDK L2fwd详细流程图(调用关系)

main rte_eal_init Pthread_create init Other inits(libs,drivers) Pthread_create init Pthread_create init rte_eal_mp_remote_launch Lcore 1 (SLAVE) rte_eal_remote_launch (send messages to cores) Lcore 2 (SLAVE) Lcore …… (SLAVE) lcore_config[master].ret = f(arg); Master Lcore rte_eth_rx _burst (Read packet from RX queues) l2fwd_send_burst (enough pkts to be sent) rte_eth_rx_burst (Read packet from RX queues) l2fwd_send_burst (enoughpkts to be sent) eth0 eth1 eth 2 eth 3 lcore_config[master].state = FINISHED; return 0 (end)

2.5运行截图

2.6详细代码注释分析:

1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 #include 11 #include 12 #include 13 #include 14 15 #include 16 #include 17 #include 18 #include 19 #include 20 #include 21 #include 22 #include 23 #include 24 #include 25 #include 26 #include 27 #include 28 #include 29 #include 30 #include 31 #include 32 #include 33 #include 34 #include 35 #include 36 #include 37 #include 38 39#define RTE_LOGTYPE_L2FWD RTE_LOGTYPE_USER1 40

41#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) 42#define NB_MBUF 8192 43 44#define MAX_PKT_BURST 32 45#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */ 46 47/* 48 * Configurable number of RX/TX ring descriptors 49*/ 50#define RTE_TEST_RX_DESC_DEFAULT 128 51#define RTE_TEST_TX_DESC_DEFAULT 512 52static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT; 53static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT; 54 55/*物理端口的mac地址的数组 ethernet addresses of ports */ 56staticstruct ether_addr l2fwd_ports_eth_addr[RTE_MAX_ETHPORTS]; 57 58/*已经启用的物理端口的掩码/位图 mask of enabled ports */ 59static uint32_t l2fwd_enabled_port_mask = 0; 60 61/*已经启用的目的物理端口编号的数组 list of enabled ports */ 62static uint32_t l2fwd_dst_ports[RTE_MAX_ETHPORTS]; 63 64static unsigned int l2fwd_rx_queue_per_lcore = 1; //默认值,每个lcore负责的接收队列数量 65 66struct mbuf_table { //mbuf数组,可以存放32个数据包 67 unsigned len; 68struct rte_mbuf *m_table[MAX_PKT_BURST]; 69 }; 70 71#define MAX_RX_QUEUE_PER_LCORE 16 72#define MAX_TX_QUEUE_PER_PORT 16 73struct lcore_queue_conf { 74 unsigned n_rx_port; //用于接收数据包的物理端口的实际数量 75 unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE]; 76struct mbuf_table tx_mbufs[RTE_MAX_ETHPORTS]; //保存发送数据包的缓存区 77 78 } __rte_cache_aligned; 79struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE]; 80 81staticconststruct rte_eth_conf port_conf = { 82 .rxmode = { 83 .split_hdr_size = 0, 84 .header_split = 0, /**< Header Split disabled */ 85 .hw_ip_checksum = 0, /**< IP checksum offload disabled */ 86 .hw_vlan_filter = 0, /**< VLAN filtering disabled */ 87 .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ 88 .hw_strip_crc = 0, /**< CRC stripped by hardware */ 89 }, 90 .txmode = { 91 .mq_mode = ETH_MQ_TX_NONE, 92 }, 93 }; 94 95struct rte_mempool * l2fwd_pktmbuf_pool = NULL; 96 97/*每个物理端口的统计结构体 Per-port statistics struct */ 98struct l2fwd_port_statistics { 99 uint64_t tx; 100 uint64_t rx; 101 uint64_t dropped; 102 } __rte_cache_aligned; 103struct l2fwd_port_statistics port_statistics[RTE_MAX_ETHPORTS]; //数据包的统计信息的全局数组 104 105/* A tsc-based timer responsible for triggering statistics printout */ 106#define TIMER_MILLISECOND 2ULL /* around 1ms at 2 Ghz */ 107#define MAX_TIMER_PERIOD 86400 /* 1 day max */ 108static int64_t timer_period = 10 * TIMER_MILLISECOND * 1; /* default period is 10 seconds */ 109 110/* Print out statistics on packets dropped */ staticvoid//打印数据包丢失等统计信息 112 print_stats(void) 113 { 114 uint64_t total_packets_dropped, total_packets_tx, total_packets_rx; 115 unsigned portid; 116 117 total_packets_dropped = 0; 118 total_packets_tx = 0; 119 total_packets_rx = 0; 120 121constchar clr[] = { 27, '[', '2', 'J', '\\0' }; 122constchar topLeft[] = { 27, '[', '1', ';', '1', 'H','\\0' }; 123 124/* Clear screen and move to top left */ 125 printf(\, clr, topLeft); 126 127 printf(\); 128 129for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) { 130/* skip disabled ports */ 131if ((l2fwd_enabled_port_mask & (1<< portid)) == 0) 132continue; 133 printf(\ 134\PRIu64 135\PRIu64 136\PRIu64, 137 portid, 138 port_statistics[portid].tx, 139 port_statistics[portid].rx, 140 port_statistics[portid].dropped); 141 142 total_packets_dropped += port_statistics[portid].dropped; 143 total_packets_tx += port_statistics[portid].tx; 144 total_packets_rx += port_statistics[portid].rx; 145 } 146 printf(\ 147\PRIu64 148\PRIu64 149\PRIu64, 150 total_packets_tx, 151 total_packets_rx, 152 total_packets_dropped); 153 printf(\); 154 } 155 156/* Send the burst of packets on an output interface */ 157staticint//在一个输出接口上burst发送数据包 158 l2fwd_send_burst(struct lcore_queue_conf *qconf, unsigned n, uint8_t port) 159 { 160struct rte_mbuf **m_table; 161 unsigned ret; 162 unsigned queueid =0; 163 164 m_table = (struct rte_mbuf **)qconf->tx_mbufs[port].m_table; 165//burst输出数据包 166 ret = rte_eth_tx_burst(port, (uint16_t) queueid, m_table, (uint16_t) n); 167 port_statistics[port].tx += ret; //记录发包数量 168if (unlikely(ret < n)) { 169 port_statistics[port].dropped += (n - ret); //记录丢包数量 170do { 171 rte_pktmbuf_free(m_table[ret]); 172 } while (++ret < n); 173 } 174 175return0; 176 } 177 178/* Enqueue packets for TX and prepare them to be sent */ 179staticint//把数据包入队到发送缓冲区 180 l2fwd_send_packet(struct rte_mbuf *m, uint8_t port) 181 { 182 unsigned lcore_id, len; 183struct lcore_queue_conf *qconf; 184 185 lcore_id = rte_lcore_id(); //取得正在运行的lcore编号

186 187 qconf = &lcore_queue_conf[lcore_id];//取得lcore_queue的配置 188 len = qconf->tx_mbufs[port].len; //得到发包缓存区中数据包的个数 189 qconf->tx_mbufs[port].m_table[len] = m;//指向数据包 190 len++; 191 192/* enough pkts to be sent */ 193if (unlikely(len == MAX_PKT_BURST)) { //如果累计到32个数据包 194 l2fwd_send_burst(qconf, MAX_PKT_BURST, port); //实际发送数据包 195 len = 0; 196 } 197 198 qconf->tx_mbufs[port].len = len;//更新发包缓存区中的数据包的个数 199return0; 200 } 201 202staticvoid 203 l2fwd_simple_forward(struct rte_mbuf *m, unsigned portid) 204 { 205//想要满足文生提出的需求,主要在这里修改ip层和tcp层的数据内容。 206 207struct ether_hdr *eth; 208void *tmp; 209 unsigned dst_port; 210 211 dst_port = l2fwd_dst_ports[portid]; 212 eth = rte_pktmbuf_mtod(m, struct ether_hdr *); 213 214/* 02:00:00:00:00:xx 修改目的mac地址 */ 215 tmp = ð->d_addr.addr_bytes[0]; 216 *((uint64_t *)tmp) = 0x002 + ((uint64_t)dst_port <<40); 217 218/* src addr 修改进入包的目的mac地址为转发包的源mac地址 */ 219 ether_addr_copy(&l2fwd_ports_eth_addr[dst_port], ð->s_addr); 220 221 l2fwd_send_packet(m, (uint8_t) dst_port); //在dst_port上发送数据包 } 223 224/* main processing loop */ 225staticvoid//线程的主处理循环 226 l2fwd_main_loop(void) 227 { 228struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; 229struct rte_mbuf *m; 230 unsigned lcore_id; 231 uint64_t prev_tsc, diff_tsc, cur_tsc, timer_tsc; 232 unsigned i, j, portid, nb_rx; 233struct lcore_queue_conf *qconf; 234const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) / US_PER_S * BURST_TX_DRAIN_US; 235 236 prev_tsc = 0; 237 timer_tsc = 0; 238 239 lcore_id = rte_lcore_id(); //获取当期lcore的编号 240 qconf = &lcore_queue_conf[lcore_id]; //读取此lcore上的配置信息 241 242if (qconf->n_rx_port == 0) { //如果此lcore上的用于接收的物理端口数量为0 243 RTE_LOG(INFO, L2FWD, \, lcore_id); 244return; //那么结束该线程 245 } 246 247 RTE_LOG(INFO, L2FWD, \, lcore_id); 248 249for (i = 0; i < qconf->n_rx_port; i++) { //遍历所有的用于接收数据包的物理端口 250 251 portid = qconf->rx_port_list[i];//一个lcore可能负责多个接收用的物理端口 252 RTE_LOG(INFO, L2FWD, \, lcore_id, 253 portid); 254 } 255 256while (1) { //死循环 257 258 cur_tsc = rte_rdtsc(); 259 260/* 261 * TX burst queue drain 262*/ 263 diff_tsc = cur_tsc - prev_tsc; 264if (unlikely(diff_tsc > drain_tsc)) { 265 266for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) { 267if (qconf->tx_mbufs[portid].len == 0) 268continue; 269 l2fwd_send_burst(&lcore_queue_conf[lcore_id], 270 qconf->tx_mbufs[portid].len, 271 (uint8_t) portid); 272 qconf->tx_mbufs[portid].len = 0; 273 } 274 275/* if timer is enabled */ 276if (timer_period >0) { //如果定时器启动 277 278/* advance the timer */ 279 timer_tsc += diff_tsc; 280 281/* if timer has reached its timeout */ 282if (unlikely(timer_tsc >= (uint64_t) timer_period)) { 283 284/* do this only on master core */ 285if (lcore_id == rte_get_master_lcore()) { 286 print_stats(); //十秒钟打印一次收包统计信息 287/* reset the timer */ 288 timer_tsc = 0; 289 } 290 } 291 } 292 293 prev_tsc = cur_tsc; 294 } 295 296/* 297 * Read packet from RX queues 298*/ 299for (i = 0; i < qconf->n_rx_port; i++) { //遍历所有的用于接收数据包的物理端口 300 301 portid = qconf->rx_port_list[i]; //第i个物理端口 302 nb_rx = rte_eth_rx_burst((uint8_t) portid, 0, //接收数据包,返回实际个数 303 pkts_burst, MAX_PKT_BURST); 304 305 port_statistics[portid].rx += nb_rx; //记录物理端口上收包数量 306 307for (j = 0; j < nb_rx; j++) { //遍历实际接收到的所有的数据包 308 m = pkts_burst[j]; 309 rte_prefetch0(rte_pktmbuf_mtod(m, void *)); //预取 310 l2fwd_simple_forward(m, portid);//简单的二层转发数据包 311 } 312 } 313 } 314 } 315 316staticint 317 l2fwd_launch_one_lcore(__attribute__((unused)) void *dummy) 318 { 319 l2fwd_main_loop();//线程执行函数 320return0; 321 } 322 323/* display usage */ 324staticvoid 325 l2fwd_usage(constchar *prgname) 326 { 327 printf(\ 328\ 329\

619if (ret <0) 620 rte_exit(EXIT_FAILURE, \=%u\\n\, 621 ret, (unsigned) portid); 622 623 rte_eth_macaddr_get(portid,&l2fwd_ports_eth_addr[portid]);//获取mac地址 624 625/* 在每个物理端口上建立一个接收队列 init one RX queue */ 626 fflush(stdout); 627 ret = rte_eth_rx_queue_setup(portid, 0, nb_rxd, //第二步,0代表接收队列的编号 628 rte_eth_dev_socket_id(portid), 629 NULL, 630 l2fwd_pktmbuf_pool); 631if (ret <0) 632 rte_exit(EXIT_FAILURE, \=%u\\n\, 633 ret, (unsigned) portid); 634 635/* 在每个物理端口上建立一个发送队列 init one TX queue on each port */ 636 fflush(stdout); 637 ret = rte_eth_tx_queue_setup(portid, 0, nb_txd,//第三步,0代表发送队列的编号 638 rte_eth_dev_socket_id(portid), 639 NULL); 640if (ret <0) 641 rte_exit(EXIT_FAILURE, \=%u\\n\, 642 ret, (unsigned) portid); 643 644/*启动设备 Start device */ 645 ret = rte_eth_dev_start(portid); //第四步,启动物理端口 646if (ret <0) 647 rte_exit(EXIT_FAILURE, \\, 648 ret, (unsigned) portid); 649 650 printf(\); 651 652 rte_eth_promiscuous_enable(portid); 653 654 printf(\\, 655 (unsigned) portid, 656 l2fwd_ports_eth_addr[portid].addr_bytes[0], 657 l2fwd_ports_eth_addr[portid].addr_bytes[1], 658 l2fwd_ports_eth_addr[portid].addr_bytes[2], 659 l2fwd_ports_eth_addr[portid].addr_bytes[3], 660 l2fwd_ports_eth_addr[portid].addr_bytes[4], 661 l2fwd_ports_eth_addr[portid].addr_bytes[5]); 662 663/*清空物理端口的统计信息 initialize port stats */ 664 memset(&port_statistics, 0, sizeof(port_statistics)); 665 } 667if (!nb_ports_available) { 668 rte_exit(EXIT_FAILURE, 669\); 670 } 671 672 check_all_ports_link_status(nb_ports, l2fwd_enabled_port_mask); 673 674/* 发送消息给每个lcore,让Lcore启动线程开始工作*/ 675 rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, NULL, CALL_MASTER); 676 RTE_LCORE_FOREACH_SLAVE(lcore_id) { 677if (rte_eal_wait_lcore(lcore_id) <0) //等待线程完成工作 678return -1; 679 } 680 681return0; 682 } 代码参考http://www.lxway.com/2506686.htm

330\ble, 10 default, 86400 maximum)\\n\, 331 prgname); 332 } 334staticint 335 l2fwd_parse_portmask(constchar *portmask) 336 { 337char *end = NULL; 338 unsigned long pm; 339/* 解析十六进制字符串 */ 340/* parse hexadecimal string */ 341 pm = strtoul(portmask, &end, 16); 342if ((portmask[0] == '\\0') (end == NULL) (*end != '\\0')) 343return -1; 344 345if (pm == 0) 346return -1; 347 348return pm; 349 } 350 351static unsigned int 352 l2fwd_parse_nqueue(constchar *q_arg) 353 { 354char *end = NULL; 355 unsigned long n; 356 357/* parse hexadecimal string */ 358 n = strtoul(q_arg, &end, 10); //转换为十进制 359if ((q_arg[0] == '\\0') (end == NULL) (*end != '\\0')) 360return0; 361if (n == 0) 362return0; 363if (n >= MAX_RX_QUEUE_PER_LCORE) 364return0; 365 366return n; 367 } 368 369staticint 370 l2fwd_parse_timer_period(constchar *q_arg) 371 { 372char *end = NULL; 373int n; 374 375/* parse number string */ 376 n = strtol(q_arg, &end, 10); //转换为十进制 377if ((q_arg[0] == '\\0') (end == NULL) (*end != '\\0')) 378return -1; 379if (n >= MAX_TIMER_PERIOD) 380return -1; 381 382return n; 383 } 384 385/*在应用程序的命令行中给出的参数解析Parse the argument given in the command line of the application */ 386staticint 387 l2fwd_parse_args(int argc, char **argv) 388 { 389int opt, ret; 390char **argvopt; 391int option_index; 392char *prgname = argv[0]; 393staticstruct option lgopts[] = { 394 {NULL, 0, 0, 0} 395 }; 396 397 argvopt = argv; 398 399while ((opt = getopt_long(argc, argvopt, \, 400 lgopts, &option_index)) != EOF) { 401 402switch (opt) { 403/* portmask */ 404case'p': //物理端口的掩码 405 l2fwd_enabled_port_mask = l2fwd_parse_portmask(optarg); 406if (l2fwd_enabled_port_mask == 0) { 407 printf(\); 408 l2fwd_usage(prgname); 409return -1; 410 } 411break; 412 413/* nqueue */ 414case'q': //lcore负责的队列的数量 415 l2fwd_rx_queue_per_lcore = l2fwd_parse_nqueue(optarg);//修改默认值 416if (l2fwd_rx_queue_per_lcore == 0) { 417 printf(\); 418 l2fwd_usage(prgname); 419return -1; 420 } 421break; 422 423/* timer period */ 424case'T': //定时的长度 425 timer_period = l2fwd_parse_timer_period(optarg) * 1 * TIMER_MILLISECOND; 426if (timer_period <0) { 427 printf(\); 428 l2fwd_usage(prgname); 429return -1; 430 } 431break; 432 433/* long options */ 434case0: 435 l2fwd_usage(prgname); 436return -1; 437 438default: 439 l2fwd_usage(prgname); 440return -1; 441 } 442 } 443 if (optind >= 0) 445 argv[optind-1] = prgname; 446 447 ret = optind-1; 448 optind = 0; /* reset getopt lib */ 449return ret; 450 } 451 452/* Check the link status of all ports in up to 9s, and print them finally */ 453staticvoid//检查物理端口的连接状态 454 check_all_ports_link_status(uint8_t port_num, uint32_t port_mask) 455 { 456#define CHECK_INTERVAL 100 /* 100ms */ 457#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */ 458 uint8_t portid, count, all_ports_up, print_flag = 0; 459struct rte_eth_link link; 460 461 printf(\); 462 fflush(stdout); 463for (count = 0; count <= MAX_CHECK_TIME; count++) { 464 all_ports_up = 1; 465for (portid = 0; portid < port_num; portid++) { 466if ((port_mask & (1<< portid)) == 0) 467continue; 468 memset(&link, 0, sizeof(link)); 469 rte_eth_link_get_nowait(portid, &link); 470/* print link status if flag set */ 471if (print_flag == 1) { 472if (link.link_status) 473 printf(\ 474\, (uint8_t)portid,

475 (unsigned)link.link_speed, 476 (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? 477 (\) : (\)); 478else 479 printf(\, 480 (uint8_t)portid); 481continue; 482 } 483/* clear all_ports_up flag if any link down */ 484if (link.link_status == 0) { 485 all_ports_up = 0; 486break; 487 } 488 } 489/* after finally printing all link status, get out */ 490if (print_flag == 1) 491break; 492 493if (all_ports_up == 0) { 494 printf(\); 495 fflush(stdout); 496 rte_delay_ms(CHECK_INTERVAL); 497 } 498 499/* set the print_flag if all ports up or timeout */ 500if (all_ports_up == 1 count == (MAX_CHECK_TIME - 1)) { 501 print_flag = 1; 502 printf(\); 503 } 504 } 505 } 506 507int//主函数 508 main(int argc, char **argv) 509 { 510struct lcore_queue_conf *qconf; 511struct rte_eth_dev_info dev_info; 512int ret; 513 uint8_t nb_ports; 514 uint8_t nb_ports_available; 515 uint8_t portid, last_port; 516 unsigned lcore_id, rx_lcore_id; 517 unsigned nb_ports_in_mask = 0; 518 519/* init EAL */ 520 ret = rte_eal_init(argc, argv); //初始化环境抽象层,并解析相关参数 521if (ret <0) 522 rte_exit(EXIT_FAILURE, \); 523 argc -= ret; 524 argv += ret; 525 526/* parse application arguments (after the EAL ones) */ 527 ret = l2fwd_parse_args(argc, argv); //解析l2fwd相关的参数: -p -q -P 528if (ret <0) 529 rte_exit(EXIT_FAILURE, \); 530 531/* create the mbuf pool */ 532 l2fwd_pktmbuf_pool = //创建mbuf pool 533 rte_mempool_create(\, NB_MBUF, 534 MBUF_SIZE, 32, 535sizeof(struct rte_pktmbuf_pool_private), 536 rte_pktmbuf_pool_init, NULL, 537 rte_pktmbuf_init, NULL, 538 rte_socket_id(), 0); 539if (l2fwd_pktmbuf_pool == NULL) 540 rte_exit(EXIT_FAILURE, \); 541 542 nb_ports = rte_eth_dev_count(); //得到物理端口的实际数量 543if (nb_ports == 0) 544 rte_exit(EXIT_FAILURE, \); 545 546if (nb_ports > RTE_MAX_ETHPORTS) //如果物理端口的数量超过限制 547 nb_ports = RTE_MAX_ETHPORTS; 548 549/* 重置目的物理端口的数组 reset l2fwd_dst_ports */ 550for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) 551 l2fwd_dst_ports[portid] = 0;//清零 552 last_port = 0; 553 554/* 每个lcore用在一个专用的发送队列上 * Each logical core is assigned a dedicated TX queue on each port. 556*/ 557for (portid = 0; portid < nb_ports; portid++) {//遍历所有的物理端口 558/* 忽略未启用的物理端口 skip ports that are not enabled */ 559if ((l2fwd_enabled_port_mask & (1<< portid)) == 0) 560continue; 561 562if (nb_ports_in_mask % 2) { //如果是有偶数个物理端口,设为相邻两个物理端口对发 563 l2fwd_dst_ports[portid] = last_port; //奇数号的目的物理端口为偶数号 564 l2fwd_dst_ports[last_port] = portid; //偶数号的目的物理端口为奇数号 565 } 566else//如果是奇数个物理端口 567 last_port = portid; 568 569 nb_ports_in_mask++; //更新已启用的物理端口的总数 570 571 rte_eth_dev_info_get(portid, &dev_info); 572 } 573if (nb_ports_in_mask % 2) { //如果已启用的物理端口的总数是奇数 574 printf(\); 575 l2fwd_dst_ports[last_port] = last_port;//last_port的目的物理端口还是last_port 576 } 577 578 rx_lcore_id = 0; 579 qconf = NULL; 580 581/* Initialize the port/queue configuration of each logical core */ 582for (portid = 0; portid < nb_ports; portid++) { //遍历所有的物理端口 583/* 忽略未启用的物理端口 skip ports that are not enabled */ 584if ((l2fwd_enabled_port_mask & (1<< portid)) == 0) 585continue; 586 587/* 得到此物理端口的lcore编号 get the lcore_id for this port */ 588while (rte_lcore_is_enabled(rx_lcore_id) == 0//如果此lcore未启用 589 lcore_queue_conf[rx_lcore_id].n_rx_port == //如果lcore上负责接收的物理端口的实际数量等于 590 l2fwd_rx_queue_per_lcore) {//每个lcore负责的接收队列的实际数量(-q参数值) 591 rx_lcore_id++;//接收lcore的编号自增 592if (rx_lcore_id >= RTE_MAX_LCORE) //如果接收lcore编号超过lcore最大数量 593 rte_exit(EXIT_FAILURE, \); 594 } 595 596if (qconf != &lcore_queue_conf[rx_lcore_id]) 597/* Assigned a new logical core in the loop above. */ 598 qconf = &lcore_queue_conf[rx_lcore_id]; 599 600 qconf->rx_port_list[qconf->n_rx_port] = portid; 601 qconf->n_rx_port++;//用于接收数据包的物理端口数量自增 602 printf(\, rx_lcore_id, (unsigned) portid); 603 } 604 605 nb_ports_available = nb_ports; 606 607/*初始化每个物理端口 Initialise each port */ 608for (portid = 0; portid < nb_ports; portid++) { //遍历所有的物理端口 609/* 忽略未使能的物理端口 skip ports that are not enabled */ 610if ((l2fwd_enabled_port_mask & (1<< portid)) == 0) { 611 printf(\, (unsigned) portid); 612 nb_ports_available--; 613continue; 614 } 615/* 初始化某个物理端口 init port */ 616 printf(\, (unsigned) portid); 617 fflush(stdout); 618 ret = rte_eth_dev_configure(portid, 1, 1, &port_conf); //第一步,设为1个发送队列和1个接收队列

本文来源:https://www.bwwdw.com/article/03kh.html

Top