Cisco VPP(4) node

来源:互联网 发布:24小时推广软件 编辑:程序博客网 时间:2024/06/01 10:25

还在初学阶段,如果有误,希望多批评指正。


参照前面的帖子Cisco VPP(3) 启动流程


这里面的node主要有3种:

1、注册的时候注册到node_registrations的node,这个是main函数运行之前就生成的链表

2、将注册的node存储到vlib_node_main_t->vlib_node_t ** nodes,这个是运行register_node时产生的,主要是存储

3、将2中的node存储到vlib_node_main_t->vlib_process_t ** processes,运行register_node时产生的,运行时执行此处的node


0x0 注册

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

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时需要输出的信息};

0x1 初始化

vlib_main()->vlib_register_all_static_nodes()->register_node()主要是将node链表中的所有node进行初始化,并且根据node之间的关系进行串联。

1、所有注册的node都会存到vlib_main_t->vlib_node_main_t->vlib_node_t ** nodes中,该nodes存在vec_header_t结构中的u8 vector_data[0]。

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中。主要成员为: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;
2、将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中

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


0x2 node操作

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

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

 /* 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);
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的操作如下:

static_always_inline uwordethernet_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
 for (i = 0; i < _vec_len (nm->pending_frames); i++)            cpu_time_now = dispatch_pending_node (vm, nm->pending_frames + i,                                                  cpu_time_now);

资料来源:https://fd.io/


欢迎加入VPP讨论群:417538415

1 0