[dpdk] 熟悉SDK与初步使用 (三)(IP Fragmentation源码分析)

对例子IP Fragmentation的熟悉,使用,以及源码分析。

问题一:

  main()函数大概是这样的:标红的三行将与下面叙述的事情相关

int
main(int argc, char **argv)
{
        ... ...
        /* init EAL */
        ret = rte_eal_init(argc, argv);
        if (ret < 0)
                rte_exit(EXIT_FAILURE, "rte_eal_init failed");
        ... ...

        /* launch per-lcore init on every lcore */
        rte_eal_mp_remote_launch(main_loop, NULL, CALL_MASTER);
        RTE_LCORE_FOREACH_SLAVE(lcore_id) {
                if (rte_eal_wait_lcore(lcore_id) < 0)
                        return -1;
        }

        return 0;
}

  其中,函数 rte_eal_wait_lcore 的实现如下:

/*
 * Wait until a lcore finished its job.
 */
int
rte_eal_wait_lcore(unsigned slave_id)
{
        if (lcore_config[slave_id].state == WAIT)
                return 0;     

        while (lcore_config[slave_id].state != WAIT &&
               lcore_config[slave_id].state != FINISHED);                                                                                                              

        rte_rmb();

        /* we are in finished state, go to wait state */
        lcore_config[slave_id].state = WAIT;
        return lcore_config[slave_id].ret;
}

   阅读红色部分,可以很明显的发现,这是一个死循环啊!!! 从字面意义上来看,main函数在完成了remote_launch之后,主进程会在这个函数里等等子进程结束。

这样的话,用一个死循环来等,难道不会有问题吗??? 所以我要的debug它一下看看怎么回事。 于是,为了达到这个目的,我分别经历了下文中的问题二三四。终于debug成功了。解答如下:

  解答起来其实也很简单,只需要看下 rte_eal_mp_remote_launch() 函数的代码,就明白了。它的代码如下:

 66 /*
 67  * Check that every SLAVE lcores are in WAIT state, then call
 68  * rte_eal_remote_launch() for all of them. If call_master is true
 69  * (set to CALL_MASTER), also call the function on the master lcore.
 70  */
 71 int
 72 rte_eal_mp_remote_launch(int (*f)(void *), void *arg,
 73                          enum rte_rmt_call_master_t call_master)
 74 {
 75         int lcore_id;
 76         int master = rte_get_master_lcore();
 77
 78         /* check state of lcores */
 79         RTE_LCORE_FOREACH_SLAVE(lcore_id) {
 80                 if (lcore_config[lcore_id].state != WAIT)
 81                         return -EBUSY;
 82         }
 83
 84         /* send messages to cores */
 85         RTE_LCORE_FOREACH_SLAVE(lcore_id) {
 86                 rte_eal_remote_launch(f, arg, lcore_id);
 87         }
 88
 89         if (call_master == CALL_MASTER) {
 90                 lcore_config[master].ret = f(arg);
 91                 lcore_config[master].state = FINISHED;
 92         }
 93
 94         return 0;
 95 }

  从第90行可以看出。主进程在这里进入了业务逻辑,所以直到程序退出之前。它都没有机会执行前边的那个死循环。也就是说,主进程当进入死循环的时候,也说明其他进程即将结束。并不会存在长期空跑CPU的情况。 不过,如果业务逻辑写错了呢? 子进程并没有如逾期退出的话,是否会进入循环? 这里暂时先留下这个疑问。

  另一个需要纪录下来的东西是。所有有需要的函数,实际上在rte_eal_init() 函数中便都创建完成了。remote_launch()函数实际上只是为其他进程传递一个启动运行的消息。

具体消息内容,目前我没有深入分析。

问题二:

  运行不起来,启用DEBUG,gdb跟踪一下。

这个makefile也是很那难用的。摸索了一下,有几个命令,比较有用的如下:

[[email protected] dpdk]# make help
[[email protected] dpdk]# make V=yes D=yes

以上命令并没有用,到各模块的MAKEFILE里,将-O3手工改成-g,重新编译,才奏效。

问题三:

  通过gdb发现,启动不了跟网卡特效有关系。

  a。初始化函数中默认的参数是启用 硬checksum 等 offload 特性的。由于我模拟的网卡不支持,只能关掉。

static const struct rte_eth_conf port_conf = {
        .rxmode = {
                .max_rx_pkt_len = JUMBO_FRAME_MAX_SIZE,
                .split_hdr_size = 0,
                .header_split   = 0, /**< Header Split disabled */
                .hw_ip_checksum = 0, /**< IP checksum offload enabled */
                .hw_vlan_filter = 0, /**< VLAN filtering disabled */
                .jumbo_frame    = 1, /**< Jumbo Frame Support enabled */
                .hw_strip_crc   = 0, /**< CRC stripped by hardware */
        },
        .txmode = {
                .mq_mode = ETH_MQ_TX_NONE,
        },
};

  b. 另一处修改

               /* init one TX queue per couple (lcore,port) */
                queueid = 0;
                for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
                        if (rte_lcore_is_enabled(lcore_id) == 0)
                                continue;

                        socket = (int) rte_lcore_to_socket_id(lcore_id);
                        printf("txq=%u,%d ", lcore_id, queueid);
                        fflush(stdout);

                        rte_eth_dev_info_get(portid, &dev_info);
                        txconf = &dev_info.default_txconf;
                        txconf->txq_flags = 0 | ETH_TXQ_FLAGS_NOXSUMS;
                        ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd,
                                                     socket, txconf);
                        if (ret < 0) {
                                printf("\n");
                                rte_exit(EXIT_FAILURE, "rte_eth_tx_queue_setup: "
                                        "err=%d, port=%d\n", ret, portid);
                        }

                        qconf = &lcore_queue_conf[lcore_id];
                        qconf->tx_queue_id[portid] = queueid;
                        queueid++;
                }

  c. 我之前模拟的网卡不支持多队列,经过学习研究,让 qemu/kvm 支持了多队列。另写了一篇,如下:

    [Virtualization][qemu][kvm][virtio] 使用 QEMU/KVM 模拟网卡多队列

  启动成功:

[[email protected] build]# ./ip_fragmentation -l 6,7 -- -p 3
EAL: Detected 8 lcore(s)
EAL: Probing VFIO support...
EAL: WARNING: cpu flags constant_tsc=yes nonstop_tsc=no -> using unreliable clock cycles !
PMD: bnxt_rte_pmd_init() called for (null)
EAL: PCI device 0000:00:03.0 on NUMA socket -1
EAL:   probe driver: 1af4:1000 rte_virtio_pmd
EAL: PCI device 0000:00:04.0 on NUMA socket -1
EAL:   probe driver: 1af4:1000 rte_virtio_pmd
EAL: PCI device 0000:00:05.0 on NUMA socket -1
EAL:   probe driver: 1af4:1000 rte_virtio_pmd
IP_FRAG: Creating direct mempool on socket 1
IP_FRAG: Creating indirect mempool on socket 1
IP_FRAG: Creating LPM table on socket 1
IP_FRAG: Creating LPM6 table on socket 1
Initializing port 0 on lcore 6... Address:00:00:00:01:00:01
txq=6,0 txq=7,1
Initializing port 1 on lcore 7... Address:00:00:00:01:00:02
txq=6,0 txq=7,1 

IP_FRAG: Socket 1: adding route 100.10.0.0/16 (port 0)
IP_FRAG: Socket 1: adding route 100.20.0.0/16 (port 1)
IP_FRAG: Socket 1: adding route 100.30.0.0/16 (port 2)
IP_FRAG: Socket 1: adding route 100.40.0.0/16 (port 3)
IP_FRAG: Socket 1: adding route 100.50.0.0/16 (port 4)
IP_FRAG: Socket 1: adding route 100.60.0.0/16 (port 5)
IP_FRAG: Socket 1: adding route 100.70.0.0/16 (port 6)
IP_FRAG: Socket 1: adding route 100.80.0.0/16 (port 7)
IP_FRAG: Socket 1: adding route 0101:0101:0101:0101:0101:0101:0101:0101/48 (port 0)
IP_FRAG: Socket 1: adding route 0201:0101:0101:0101:0101:0101:0101:0101/48 (port 1)
IP_FRAG: Socket 1: adding route 0301:0101:0101:0101:0101:0101:0101:0101/48 (port 2)
IP_FRAG: Socket 1: adding route 0401:0101:0101:0101:0101:0101:0101:0101/48 (port 3)
IP_FRAG: Socket 1: adding route 0501:0101:0101:0101:0101:0101:0101:0101/48 (port 4)
IP_FRAG: Socket 1: adding route 0601:0101:0101:0101:0101:0101:0101:0101/48 (port 5)
IP_FRAG: Socket 1: adding route 0701:0101:0101:0101:0101:0101:0101:0101/48 (port 6)
IP_FRAG: Socket 1: adding route 0801:0101:0101:0101:0101:0101:0101:0101/48 (port 7)

Checking link status
done
Port 0 Link Up - speed 10000 Mbps - full-duplex
Port 1 Link Up - speed 10000 Mbps - full-duplex
IP_FRAG: entering main loop on lcore 7
IP_FRAG:  -- lcoreid=7 portid=1
IP_FRAG: entering main loop on lcore 6
IP_FRAG:  -- lcoreid=6 portid=0

问题四:

  如何查看编译选项,使用的静态库。修改编译选项,启动debug等? 唯一的办法是makefile。结构还是很清晰的。但是,依然需要花很长的时间读。

  打印编译命令的方法如下:

  修改文件 /sdk/@dpdk/dpdk-stable-16.07.1 mk/internal/rte.compile-pre.mk 中的 C_TO_O_DO 变量: 第101行,为新增内容。

 99 C_TO_O_DO = @set -e; \
100         echo $(C_TO_O_DISP); 101         echo $(C_TO_O); 102         $(C_TO_O) && \
103         $(PMDINFO_TO_O) && 104         echo $(C_TO_O_CMD) > $(call obj2cmd,$(@)) && 105         sed ‘s,‘[email protected]‘:,dep_‘[email protected]‘ =,‘ $(call obj2dep,$(@)).tmp > $(call obj2dep,$(@)) && \
106         rm -f $(call obj2dep,$(@)).tmp
107 

  打印链接命令的方法如下:

  修改文件 /sdk/@dpdk/dpdk-stable-16.07.1 mk/rte.app.mk 中的 O_TO_EXE_DO 变量: 第209行,为新增内容。

207 O_TO_EXE_DO = @set -e; 208         echo $(O_TO_EXE_DISP); 209         echo $(O_TO_EXE); 210         $(O_TO_EXE) && 211         echo $(O_TO_EXE_CMD) > $(call exe2cmd,$(@))
212 

  实现效果如下:

[[email protected] ip_fragmentation]# make
echo "xxxxccccxxxx"
xxxxccccxxxx
  CC main.o
gcc -Wp,-MD,./.main.o.d.tmp -m64 -pthread -march=native -DRTE_MACHINE_CPUFLAG_SSE -DRTE_MACHINE_CPUFLAG_SSE2 -DRTE_MACHINE_CPUFLAG_SSE3 -DRTE_MACHINE_CPUFLAG_SSSE3 -DRTE_MACHINE_CPUFLAG_SSE4_1 -DRTE_MACHINE_CPUFLAG_SSE4_2 -I/root/src/sdk/@dpdk/dpdk-stable-16.07.1/examples/ip_fragmentation/build/include -I/root/dpdk//x86_64-native-linuxapp-gcc/include -include /root/dpdk//x86_64-native-linuxapp-gcc/include/rte_config.h -g -W -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wold-style-definition -Wpointer-arith -Wcast-align -Wnested-externs -Wcast-qual -Wformat-nonliteral -Wformat-security -Wundef -Wwrite-strings -Wno-return-type -o main.o -c /root/src/sdk/@dpdk/dpdk-stable-16.07.1/examples/ip_fragmentation/main.c
  LD ip_fragmentation
gcc -o ip_fragmentation -m64 -pthread -march=native -DRTE_MACHINE_CPUFLAG_SSE -DRTE_MACHINE_CPUFLAG_SSE2 -DRTE_MACHINE_CPUFLAG_SSE3 -DRTE_MACHINE_CPUFLAG_SSSE3 -DRTE_MACHINE_CPUFLAG_SSE4_1 -DRTE_MACHINE_CPUFLAG_SSE4_2 -I/root/src/sdk/@dpdk/dpdk-stable-16.07.1/examples/ip_fragmentation/build/include -I/root/dpdk//x86_64-native-linuxapp-gcc/include -include /root/dpdk//x86_64-native-linuxapp-gcc/include/rte_config.h -g -W -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wold-style-definition -Wpointer-arith -Wcast-align -Wnested-externs -Wcast-qual -Wformat-nonliteral -Wformat-security -Wundef -Wwrite-strings main.o -L/root/dpdk//x86_64-native-linuxapp-gcc/lib -Wl,-lrte_kni -Wl,-lrte_pipeline -Wl,-lrte_table -Wl,-lrte_port -Wl,-lrte_pdump -Wl,-lrte_distributor -Wl,-lrte_reorder -Wl,-lrte_ip_frag -Wl,-lrte_meter -Wl,-lrte_sched -Wl,-lrte_lpm -Wl,--whole-archive -Wl,-lrte_acl -Wl,--no-whole-archive -Wl,-lrte_jobstats -Wl,-lrte_power -Wl,--whole-archive -Wl,-lrte_timer -Wl,-lrte_hash -Wl,-lrte_vhost -Wl,-lrte_kvargs -Wl,-lrte_mbuf -Wl,-lethdev -Wl,-lrte_cryptodev -Wl,-lrte_mempool -Wl,-lrte_ring -Wl,-lrte_eal -Wl,-lrte_cmdline -Wl,-lrte_cfgfile -Wl,-lrte_pmd_bond -Wl,-lrte_pmd_af_packet -Wl,-lrte_pmd_bnxt -Wl,-lrte_pmd_cxgbe -Wl,-lrte_pmd_e1000 -Wl,-lrte_pmd_ena -Wl,-lrte_pmd_enic -Wl,-lrte_pmd_fm10k -Wl,-lrte_pmd_i40e -Wl,-lrte_pmd_ixgbe -Wl,-lrte_pmd_null -Wl,-lrte_pmd_ring -Wl,-lrte_pmd_virtio -Wl,-lrte_pmd_vhost -Wl,-lrte_pmd_vmxnet3_uio -Wl,-lrte_pmd_null_crypto -Wl,--no-whole-archive -Wl,-lrt -Wl,-lm -Wl,-ldl -Wl,-export-dynamic -Wl,-export-dynamic -L/root/src/sdk/@dpdk/dpdk-stable-16.07.1/examples/ip_fragmentation/build/lib -L/root/dpdk//x86_64-native-linuxapp-gcc/lib -Wl,--as-needed -Wl,-Map=ip_fragmentation.map -Wl,--cref
  INSTALL-APP ip_fragmentation
  INSTALL-MAP ip_fragmentation.map
[[email protected] ip_fragmentation]# 

  

时间: 2024-11-20 18:19:46

[dpdk] 熟悉SDK与初步使用 (三)(IP Fragmentation源码分析)的相关文章

[dpdk] 熟悉SDK与初步使用 (二)(skeleton源码分析)

接续前节:[dpdk] 熟悉SDK与初步使用 (一)(qemu搭建实验环境) 程序逻辑: 运行参数: 关键API: 入口函数: int rte_eal_init(int argc, char **argv) 内存池函数: rte_pktmbuf_pool_create.  它是函数 rte_mempool_create 的一个封装. struct rte_mempool * rte_pktmbuf_pool_create(const char *name, unsigned n, unsigne

u-boot学习(三):u-boot源码分析

建立域模型和关系数据模型有着不同的出发点: 域模型: 由程序代码组成, 通过细化持久化类的的粒度可提高代码的可重用性, 简化编程 在没有数据冗余的情况下, 应该尽可能减少表的数目, 简化表之间的参照关系, 以便提高数据的访问速度 Hibernate 把持久化类的属性分为两种: 值(value)类型: 没有 OID, 不能被单独持久化, 生命周期依赖于所属的持久化类的对象的生命周期 实体(entity)类型: 有 OID, 可以被单独持久化, 有独立的生命周期(如果实体类型包含值类型,这个值类型就

Neural Turing Machines-NTM系列(三)ntm-lasagne源码分析

Neural Turing Machines-NTM系列(三)ntm-lasagne源码分析 在NTM系列文章(二)中,我们已经成功运行了一个ntm工程的源代码.在这一章中,将对它的源码实现进行分析. 1.网络结构 1.1 模块结构图 在图中可以看到,输入的数据在经过NTM的处理之后,输出经过NTM操作后的,跟之前大小相同的数据块.来看下CopyTask的完整输出图: 图中右侧的Input是输入数据,Output是目标数据,Prediction是通过NTM网络预测出来的输出数据,可以看出预测数据

Java 序列化和反序列化(三)Serializable 源码分析 - 2

目录 Java 序列化和反序列化(三)Serializable 源码分析 - 2 1. ObjectStreamField 1.1 数据结构 1.2 构造函数 2. ObjectStreamClass Java 序列化和反序列化(三)Serializable 源码分析 - 2 在上一篇文章中围绕 ObjectOutputStream#writeObject 讲解了一下序列化的整个流程,这中间很多地方涉及到了 ObjectStreamClass 和 ObjectStreamField 这两个类.

Struts2【三】 StrutsPrepareAndExecuteFilter 源码分析&lt;一&gt;

先把关键的类总体一览一下 用JadClipse反编译debug源码 都知道Filter三个方法,init,doFilter,destory 先看init方法初始化了什么 先按名字记住几个关键类,initOperation初始化处理器,Dispatcher派发器,PrepareOperations预处理器,ExecuteOperations执行处理器 55.FilterHostConfig包装了FilterConfig 56.nit.initLogging不用管,这个貌似是过滤器初始化参数指定的日

[dpdk] 熟悉SDK与初步使用 (一)(qemu搭建实验环境)

搭建实验环境: troubleshoot 第一步加载驱动 第二步切换驱动 使用了所有qemu支持的卡 [[email protected]:~/VM/dpdk] % cat start.sh sudo qemu-system-x86_64 -nographic -vnc 127.0.0.1:1 -enable-kvm -m 2G -cpu Nehalem -smp cores=2,threads=2,sockets=2 -numa node,mem=1G,cpus=0-3,nodeid=0 -n

TCP三次握手源码分析

TCP握手分为三个阶段,在握手开始之前,通信双方的套接字状态均为“TCP_CLOSE”,以下是这三个阶段: (1)客户端发送一个标志位中SYN位为1的报文给服务端,并设套接字状态为“TCP_SYNSENT” (2)服务端接到SYN报文,设套接字状态为“TCP_SYNRCV”,并回送一个SYN+ACK位均为1的报文 (3)客户端接到SYN+ACK报文,回送一个ACK位为1的报文,设套接字状态为“TCP_ESTABLISHED”,服务端接到ACK报文后,同样设置为“TCP_ESTABLISHED”

三)CodeIgniter源码分析之Common.php

1 <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 2 3 // ------------------------------------------------------------------------ 4 5 /** 6 * Common Functions 7 */ 8 9 /** 10 * 为什么还要定义这些全局函数呢?比如说,下面有很多函数,如get_config().confi

Webpack-源码三,从源码分析如何写一个plugin

经过上一篇博客分析webpack从命令行到打包完成的整体流程,我们知道了webpage的plugin是基于事件机制工作的,这样最大的好处是易于扩展.社区里很多webpack的plugin,但是具体到我们的项目并不一定适用,这篇博客告诉你如何入手写一个plugin,然后分析源码相关部分告诉你你的plugin是如何工作.知其然且知其所以然. 该系列博客的所有测试代码. 从黑盒角度学习写一个plugin 所谓黑盒,就是先不管webpack的plugin如何运作,只去看官网介绍. Compiler和Co