赵占旭的博客

Cisco VPP Node

Node类型


  • 注册的时候注册到node_registrations的node,这个是main函数运行之前就生成的链表
  • 将注册的node存储到vlib_node_main_t->vlib_node_t ** nodes,这个是运行register_node时产生的,主要是存储。
  • 将2中的node存储到vlib_node_main_t->vlib_process_t ** processes,运行register_node时产生的,运行时执行此处的node.

Node注册


VLIB_REGISTER_NODE主要是用来定义node,并且注册node到vlib_main_t->vlib_node_main_t->node_registrations,这个链表在main()函数之前创建,比如ip4-input的最初创建如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
VLIB_REGISTER_NODE (ip4_input_node) = {
.function = ip4_input,//mbuf传入node之后的操作函数,以及下一级node的确定
.name = "ip4-input",//name必须唯一,因为串联node使用的标识为名字
.vector_size = sizeof (u32),

.n_errors = IP4_N_ERROR,//报错的计数,可以用来报错,也可以记录正常的数据包数量,show errors命令显示
.error_strings = ip4_error_strings,//显示计数的时候,对计数的提示,比如正常的ipv4数据包,不正确的ipv4数据包数量

.n_next_nodes = IP4_INPUT_N_NEXT,//next node的数量
.next_nodes = {
[IP4_INPUT_NEXT_DROP] = "error-drop",//下一级node丢弃
[IP4_INPUT_NEXT_PUNT] = "error-punt",
[IP4_INPUT_NEXT_LOOKUP] = "ip4-lookup",//下一级node查表
[IP4_INPUT_NEXT_LOOKUP_MULTICAST] = "ip4-lookup-multicast",
[IP4_INPUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",//报错
},

.format_buffer = format_ip4_header,
.format_trace = format_ip4_input_trace,//show trace显示路径的打印,一般是数据包走到这个node时需要输出的信息
};

Node初始化


vlib_main()->vlib_register_all_static_nodes()->register_node()主要是将node链表中的所有node进行初始化,并且根据node之间的关系进行串联。
所有注册的node都会存到vlib_main_t->vlib_node_main_t->vlib_node_t ** nodes中,该nodes存在vec_header_t结构中的u8 vector_data[0]

1
2
3
4
typedef struct {
u64 len; /**记录注册了多少个node*/
u8 vector_data[0]; /**< Vector data . 记录注册的node,相比链表,更容易找到某个node,但是每次添加都要重新申请内存,类似realloc */
} vec_header_t;

vec_add1 (nm->nodes, n);主要是将新申请的node添加到数组中,使用的是0数组的方式,余下需要做的就是将前面注册的node成员赋值给当前node中。主要成员为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct vlib_node_t {
vlib_node_function_t * function;//执行函数
u8 * name;//名字
vlib_node_type_t type;//node类型
u32 index;//是由nodes长度算出来的index
u16 flags;//node 标识
u8 state;//node 状态
u16 scalar_size, vector_size;//标量矢量大小
char ** next_node_names;//下一级node名字
char * sibling_of;
u64 * n_vectors_by_next_node;//送到下一级node的vector数量
format_function_t * format_buffer;//以下三项是格式化输出一些信息
unformat_function_t * unformat_buffer;
format_function_t * format_trace;
} vlib_node_t;

vlib_node_main_t->vlib_node_t ** nodes的process类型的node存储到vlib_node_main_t->vlib_process_t ** processes的node_runtime中,也是利用存在vec_header_t结构中的u8 vector_data[0],这个主要是偏操作的node结点,比如startup-config-process(启动配置处理),admin-up-down-process(端口up down的处理),其他所有类型的node都存储到vlib_node_main_t->vlib_node_runtime_t * nodes_by_type中。

1
2
3
4
5
if(n->type == VLIB_NODE_TYPE_PROCESS)如果是process类型
vec_add1 (nm->processes, p);//将新申请的process添加到数组中,后面将nodes中的主要成员赋值给processes中的node_runtime
else
vec_add2_aligned (nm->nodes_by_type[n->type], rt, 1,
/* align */ CLIB_CACHE_LINE_BYTES);//将当前不是process类型的node添加到nodes_by_types中,主要是后面node操作中会用到

Node 操作


vlib_main_loop()主要是去处理node中的操作。核心操作包含以下两个点:(忽略VLIB_NODE_TYPE_PROCESS类型结点和VLIB_NODE_TYPE_PRE_INPUT类型结点操作)

收包的入口函数,比如dpdk-input的node,里面主要主要执行node->function,暂时忽略中断模式。

1
2
3
4
/* Next process input nodes. */
vec_foreach (n, nm->nodes_by_type[VLIB_NODE_TYPE_INPUT])
cpu_time_now = dispatch_node (vm, n, VLIB_NODE_TYPE_INPUT,
VLIB_NODE_STATE_POLLING, /* frame */ 0, cpu_time_now);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
u64 dispatch_node (vlib_main_t * vm, vlib_node_runtime_t * node,
vlib_node_type_t type, vlib_node_state_t dispatch_state,
vlib_frame_t * frame, u64 last_time_stamp)
{
//执行node->function
n = node->function (vm, node, frame);

//更新node_runtime里面的一些状态,比如处理时间、vector数据包数量。
v = vlib_node_runtime_update_stats (stat_vm, node, /* n_calls */ 1,
/* n_vectors */ n, /* n_clocks */ t - last_time_stamp);

/*中断模式下,vector速率超过阈值,切换到polling模式*/
if ((DPDK == 0 && dispatch_state == VLIB_NODE_STATE_INTERRUPT)
|| (DPDK == 0 && dispatch_state == VLIB_NODE_STATE_POLLING
&& (node->flags
& VLIB_NODE_FLAG_SWITCH_FROM_INTERRUPT_TO_POLLING_MODE))) {
}
}

node->function中会计算下一级node,并且最终调用vlib_put_next_frame,将下一级的node加入到nm->pending_frames中,比如ethernet-input node的操作如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static_always_inline uword ethernet_input_inline (vlib_main_t * vm,
vlib_node_runtime_t * node, vlib_frame_t * from_frame,
ethernet_input_variant_t variant)
{
next_index = node->cached_next_index;
stats_sw_if_index = node->runtime_data[0];
stats_n_packets = stats_n_bytes = 0;

while (n_left_from > 0) {
while (n_left_from > 0 && n_left_to_next > 0) {
//确定下一级node
determine_next_node(em, variant, is_l20, type0, b0, &error0, &next0);

// verify speculative enqueue
vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
to_next, n_left_to_next, bi0, next0);
}
//将下一级的node加入到 nm->pending_frames中
vlib_put_next_frame (vm, node, next_index, n_left_to_next);
}

return from_frame->n_vectors;
}

继续执行依赖的node,最终dispatch_pending_node还是调用dispatch_node.

1
2
3
for (i = 0; i < _vec_len (nm->pending_frames); i++) 
cpu_time_now = dispatch_pending_node (vm, nm->pending_frames + i,
cpu_time_now);

注意:所有文章非特别说明皆为原创。为保证信息与源同步,转载时请务必注明文章出处!谢谢合作 :-)

原始链接:http://zhaozhanxu.com/2016/04/24/VPP/2016-04-24-VPP-Node/

许可协议: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。