分享

Cisco VPP(3) 启动流程

 mrjbydd 2018-03-15

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


参照前面的帖子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的最初创建如下:

  1. VLIB_REGISTER_NODE (ip4_input_node) = {  
  2.   .function = ip4_input,//mbuf传入node之后的操作函数,以及下一级node的确定  
  3.   .name = "ip4-input",//name必须唯一,因为串联node使用的标识为名字  
  4.   .vector_size = sizeof (u32),  
  5.   
  6.   .n_errors = IP4_N_ERROR,//报错的计数,可以用来报错,也可以记录正常的数据包数量,show errors命令显示  
  7.   .error_strings = ip4_error_strings,//显示计数的时候,对计数的提示,比如正常的ipv4数据包,不正确的ipv4数据包数量  
  8.   
  9.   .n_next_nodes = IP4_INPUT_N_NEXT,//next node的数量  
  10.   .next_nodes = {  
  11.     [IP4_INPUT_NEXT_DROP] = "error-drop",//下一级node丢弃  
  12.     [IP4_INPUT_NEXT_PUNT] = "error-punt",  
  13.     [IP4_INPUT_NEXT_LOOKUP] = "ip4-lookup",//下一级node查表  
  14.     [IP4_INPUT_NEXT_LOOKUP_MULTICAST] = "ip4-lookup-multicast",  
  15.     [IP4_INPUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",//报错  
  16.   },  
  17.   
  18.   .format_buffer = format_ip4_header,  
  19.   .format_trace = format_ip4_input_trace,//show trace显示路径的打印,一般是数据包走到这个node时需要输出的信息  
  20. };  

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]。

  1. typedef struct {  
  2.   u64 len; /**记录注册了多少个node*/  
  3.  u8 vector_data[0];  /**< Vector data . 记录注册的node,相比链表,更容易找到某个node,但是每次添加都要重新申请内存,类似realloc */  
  4. } vec_header_t;  
  1. vec_add1 (nm->nodes, n);主要是将新申请的node添加到数组中,使用的是0数组的方式,余下需要做的就是将前面注册的node成员赋值给当前node中。主要成员为:  
  2. typedef struct vlib_node_t {  
  3.   vlib_node_function_t * function;//执行函数  
  4.   u8 * name;//名字  
  5.   vlib_node_type_t type;//node类型  
  6.   u32 index;//是由nodes长度算出来的index  
  7.   u16 flags;//node 标识  
  8.   u8 state;//node 状态  
  9.   u16 scalar_size, vector_size;//标量矢量大小  
  10.   char ** next_node_names;//下一级node名字  
  11.   char * sibling_of;  
  12.   u64 * n_vectors_by_next_node;//送到下一级node的vector数量  
  13.   format_function_t * format_buffer;//以下三项是格式化输出一些信息  
  14.   unformat_function_t * unformat_buffer;  
  15.   format_function_t * format_trace;  
  16. } 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中

  1. if(n->type == VLIB_NODE_TYPE_PROCESS)如果是process类型  
  2.     vec_add1 (nm->processes, p);//将新申请的process添加到数组中,后面将nodes中的主要成员赋值给processes中的node_runtime  
  3. else  
  4.     vec_add2_aligned (nm->nodes_by_type[n->type], rt, 1,  
  5.               /* 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,暂时忽略中断模式。

  1. /* Next process input nodes. */  
  2.        vec_foreach (n, nm->nodes_by_type[VLIB_NODE_TYPE_INPUT])  
  3.        cpu_time_now = dispatch_node (vm, n,  
  4.                                      VLIB_NODE_TYPE_INPUT,  
  5.                                      VLIB_NODE_STATE_POLLING,  
  6.                                      /* frame */ 0,  
  7.                                      cpu_time_now);  
  1. u64 dispatch_node (vlib_main_t * vm,  
  2.                vlib_node_runtime_t * node,  
  3.                vlib_node_type_t type,  
  4.                vlib_node_state_t dispatch_state,  
  5.                vlib_frame_t * frame,  
  6.                u64 last_time_stamp)  
  7. {  
  8.   
  9.         //执行node->function  
  10.         n = node->function (vm, node, frame);  
  11.   
  12.           
  13.         //更新node_runtime里面的一些状态,比如处理时间、vector数据包数量。  
  14.         v = vlib_node_runtime_update_stats (stat_vm, node,  
  15.                                             /* n_calls */ 1,  
  16.                                             /* n_vectors */ n,  
  17.                                             /* n_clocks */ t - last_time_stamp);  
  18.   
  19.         /*中断模式下,vector速率超过阈值,切换到polling模式*/  
  20.         if ((DPDK == 0 && dispatch_state == VLIB_NODE_STATE_INTERRUPT)  
  21.                 || (DPDK == 0 && dispatch_state == VLIB_NODE_STATE_POLLING  
  22.                     && (node->flags  
  23.                         & VLIB_NODE_FLAG_SWITCH_FROM_INTERRUPT_TO_POLLING_MODE)))  
  24.         {  
  25.         }  
  26. }  

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

  1. static_always_inline uword  
  2. ethernet_input_inline (vlib_main_t * vm,  
  3.                vlib_node_runtime_t * node,  
  4.                vlib_frame_t * from_frame,  
  5.                ethernet_input_variant_t variant)  
  6. {  
  7.   
  8.   next_index = node->cached_next_index;  
  9.   stats_sw_if_index = node->runtime_data[0];  
  10.   stats_n_packets = stats_n_bytes = 0;  
  11.   
  12.   while (n_left_from > 0)  
  13.   {  
  14.       while (n_left_from > 0 && n_left_to_next > 0)  
  15.       {  
  16.           //确定下一级node  
  17.           determine_next_node(em, variant, is_l20, type0, b0, &error0, &next0);  
  18.         
  19.           // verify speculative enqueue  
  20.       vlib_validate_buffer_enqueue_x1 (vm, node, next_index,  
  21.                        to_next, n_left_to_next,  
  22.                        bi0, next0);  
  23.       }  
  24.       //将下一级的node加入到 nm->pending_frames中  
  25.       vlib_put_next_frame (vm, node, next_index, n_left_to_next);  
  26.    }  
  27.   
  28.   return from_frame->n_vectors;  
  29. }  



继续执行依赖的node,最终 dispatch_pending_node 还是调用 dispatch_node
  1. for (i = 0; i < _vec_len (nm->pending_frames); i++)  
  2.            cpu_time_now = dispatch_pending_node (vm, nm->pending_frames + i,  
  3.                                                  cpu_time_now);  

资料来源:https:///


欢迎加入VPP讨论群:417538415

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多