logo资料库

思科vpp源码分析.pdf

第1页 / 共57页
第2页 / 共57页
第3页 / 共57页
第4页 / 共57页
第5页 / 共57页
第6页 / 共57页
第7页 / 共57页
第8页 / 共57页
资料共57页,剩余部分请下载后查看
vlib_frame_t
VPP 源码分析 Version Date Version Revision History Description 2018-04-21 vpp-18.01 VPP 源码分析 Author 陈漂评
目 录 1. 架构介绍 1.1 线程模型 node 机制 1.2 1.2.1 node 图 1.2.2 node 的类型 1.2.3 node 的注册 1.2.4 node 图的初始化 1.2.5 node 的调度 2. 编译 3. 4. 5. 6. 7. 8. plugin vlib 4.1 4.2 error elog vppinfra 5.1 heap api 6.1 6.2 6.3 api 的配置 vlibmomery vlibsocket feature vnet 8.1 8.2 fib arp 9. dpdk 10. 11. cli test 3 3 3 4 5 5 14 22 56 56 56 56 56 56 56 56 56 57 57 57 57 57 57 57 57 57 Page 2 of 57
1. 架构介绍 1.1 线程模型 vpp 采用多线程模型,按照功能来划分当前线程有四类:hqos、stats、worker 和 main。 介绍每个核的 runtime 结构,如何同步 介绍同步锁机制 线程的初始化 线程和 vm 的关系 (待完善) 1.2 node 机制 vpp 把一个包的处理流程划分成多个阶段,每个阶段只做一部分处理工作,报文经过所有的阶段处 理后完成报文的处理。一个阶段称为 node(中文叫做节点),即一个报文从某个口收上来后会经过 一系列的 node 处理,然后从某个口发送出去。如下图所示: Page 3 of 57
按照上图,ipv4 报文经过的 node 依次为:dpdk-inputip4-inputip4-lookupip4-rewriteinterface- outputinterface-tx;ipv6 报文经过的 node 依次为:dpdk-inputip6-inputip6-lookupip6- rewriteinterface-outputinterface-tx。这里只是为了说明 vpp 的报文处理流程,暂时不关系这些 node 都对报文做了什么处理。因此,理解 vpp 的转发框架的重点在于理解 node 的结构、node 之间 的关系等机制。那么,一个 node 处理完报文以后怎么知道扔给哪个 node 处理呢?可以根据报文的 类型来决定,如 ethernet-input 把 ipv4 和 ipv6 的报文分别送给 ip4-input 和 ip6-input 节点去处理,也 可以是根据预定义的规则来决定往哪个 node 走。总之,往哪个 node 走由当前处理报文的 node 说了 算。但是一般情况下只会把报文发送给自己的孩子节点处理,但并不是说不能发送给非孩子节点。 1.2.1 node 图 如上图所示所有的 node 组成一张有向图,我们按照树的叫法,把相邻的 node 之间的关系称为父节 点和孩子节点。比如上图中 dpdk-input 是 ethernet-input 的父节点,而 ethernet-input 是 dpdk-input 孩 子节点。任意一个节点可以有多个孩子节点,也可以有多个父节点,比如 ethernet-input 有两个孩子 节点 ip4-input 和 ip6-input,interface-output 有两个父节点 ip4-rewrite 和 ip6-rewrite。节点之间还有 另外一种关系,叫做兄弟关系,兄弟节点之间具有相同的孩子节点。 Page 4 of 57 dpdk-inputethernet-inputip6-inputip4-inputip6-lookupip4-lookupip6-rewriteip6-rewriteinterface-outputinterface-tx
1.2.2 node 的类型 node 的类型按其在 node 图中的位置以及自身的特点分为 INTERNAL、INPUT、PRE_INPUT、 PROCESS 等四种类型: INTERNAL:为 node 图中的 node; INPUT:为 node 图提供报文输入的 node,这类 node 在收包线程中每次循环都被调用,比如收包 node,每次循环都被调用以从网卡收包; PRE_INPUT:为在 INPUT 类型的 node 之前需要被调用的 node; PROCESS:为可以被挂起和重新被执行的 node。 1.2.3 node 的注册 node 的注册,即在 node 图中添加一个新的 node,vpp 提供宏 VLIB_REGISTER_NODE 用来注册 node,下面以 ip4-frag 这个 node 为例说明 node 的注册需要填写的内容: 为该 node 对应的执行函数,每个 node 都需要提供一个 function 来做具体的事情; function : name:node 的名字; type:node 的类型; vector_size:存放一个包需要的字节数 format_trace:是一个函数,show trace 命令会调用每个 node 的 format_trace 函数来输出改 node 的信 息,每个 node 自己实现该函数,自己决定在输入 show trace 时需要输出的和该 node 相关的信息; n_errors、error_strings:这两个主要是为了实现错误统计计数而存在的,n_errors 为错误统计个数, error_strings 为具体的错误信息,是一个字符串数组; n_next_nodes、next_nodes:孩子节点的信息,n_next_nodes 为孩子节点的个数、next_nodes 为具体 Page 5 of 57
的孩子节点的名称,是一个字符串数组。再来看看 VLIB_REGISTER_NODE 这个宏的实现: 可知,该宏展开就是一个名为__vlib_add_node_registration_##x 的函数,该函数就是把类型为 vlib_node_registration_t 的实例 x 添加到 vm->node_main.node_registrations 链表上, vlib_node_registration_t 结构正是 node 的注册结构。需要注意的是该函数有 __attribute__((__constructor__))修饰,为 GNU C 的函数属性,表示该函数为构造函数,在 main 函数 之前被执行。最终所有 node 的注册结构由 vm->node_main.node_registrations 这个链表串起来: 总结一下,所谓 node 的注册是在 main 函数执行之前通过宏 VLIB_REGISTER_NODE 只是定义并初 始化一个类型为 vlib_node_registration_t 的结构,并把该结构插入到链表 vm- >node_main.node_registrations 上。其实 node 的注册还未完成,还需要在 main 函数里再做进一步的 “注册”,main 函数里的注册则是把以上建立好的链表中的所有 vlib_node_registration_t 结构分配 vlib_node_t 结构。vlib_node_registration_t 只是注册时存放用户提供的 node 相关信息的结构,而 vlib_node_t 才是真正的 node 对应的结构,转流程中访问的也是 vlib_node_t 结构。如下图所示: Page 6 of 57 vlib_node_registration_tvm->node_main.node_registrationsvlib_node_registration_tvlib_node_registration_tvlib_node_registration_t
vm->node_main.nodes 是一个 vector 结构(见 vector 分析章节,这里先认为 vector 就是一个数组)。 在分析 vm->node_main.nodes 的初始化源码之前有必要先了解一下 vlib_node_registration_t 和 vlib_node_t 两个结构中每个字段的含义,如下表所示,建议第一次看的时候先大概浏览一下,后面 结合具体的代码流程再返回来查阅。 了解 node 相关结构之后再看下 node 的注册代码流程: Page 7 of 57 vlib_node_registration_tvm->node_main.node_registrationsvlib_node_tvlib_node_tvlib_node_tvlib_node_tvm->node_main.nodesvlib_node_registration_tvlib_node_registration_tvlib_node_registration_tregisterregisterregisterregister
由上面的代码可知,先定义一个名为“null-node”的 node,并调用 register_node 函数注册该 node, 最后遍历 vm->node_main.node_registrations 调用 register_node 函数注册该链表上的所有 node。再来 register_node 函数的实现,由于该函数稍微有点长,我们分段来分析: Page 8 of 57
分享到:
收藏