前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >思科VPP系列砖题三:VPP节点注册

思科VPP系列砖题三:VPP节点注册

作者头像
通信行业搬砖工
发布2023-03-30 21:13:07
9260
发布2023-03-30 21:13:07
举报
文章被收录于专栏:网络虚拟化网络虚拟化

在前面的章节中,我们讲述了如何编译思科开源的fdio/VPP

Ubuntu22.04如何编译vpp-master版本

和介绍了VPP的启动流程介绍

思科VPP系列砖题二:VPP启动流程分析

本章节将要介绍VPP node的注册机制,在介绍VPP的node机制之前,我们首先介绍一下VPP的软件架构核和设计思想。

软件架构描述:(图片来着腾讯大佬,公众号:Flowlet

工作原理描述:

矢量图节点的数据处理架构:

矢量处理逻辑描述:

二、VPP节点注册流程

函数名称:

void vlib_register_all_static_nodes (vlib_main_t * vm)

调用关系

vlib_main()初始化时候调用;

函数调用关系描述:

函数vlib_register_all_static_nodes 处理逻辑如下所示:

代码语言:javascript
复制
void
vlib_register_all_static_nodes (vlib_main_t * vm)
{
  vlib_global_main_t *vgm = vlib_get_global_main ();
  vlib_node_registration_t *r;

  static char *null_node_error_strings[] = {
    "blackholed packets",
  };

  /* 定义一个null_node节点,作为第一个节点,其id为0 */
  static vlib_node_registration_t null_node_reg = {
    .function = null_node_fn,
    .vector_size = sizeof (u32),
    .name = "null-node",
    .n_errors = 1,
    .error_strings = null_node_error_strings,
  };

  /* make sure that node index 0 is not used by
     real node */
  register_node (vm, &null_node_reg);

  /* 从 vlib_global_main_t -> node_registrations 链表的起始地址开始遍历*/
  r = vgm->node_registrations;
  while (r)
    {
      /* 将添加的所有静态节点的链表进行遍历,并且注册 */
      register_node (vm, r);
      r = r->next_registration;
    }
}

如下所示:

函数register_node处理逻辑如下所示:

代码语言:javascript
复制

u32
vlib_register_node (vlib_main_t *vm, vlib_node_registration_t *r, char *fmt,
        ...)
{
  vlib_node_main_t *nm = &vm->node_main;
  vlib_node_t *n;
  va_list va;
  u32 size;
  int i;

  if (CLIB_DEBUG > 0)
    {
      /* Default (0) type should match INTERNAL. */
      vlib_node_t zero = { 0 };
      ASSERT (VLIB_NODE_TYPE_INTERNAL == zero.type);
    }

  if (r->node_fn_registrations)
    {
      /* to avoid confusion, please remove ".function " statiement from
         CLIB_NODE_REGISTRATION() if using function function candidates */
      ASSERT (r->function == 0);
      /* 注册节点的处理函数,按照优先级选择处理函数 */
      r->function =
  vlib_node_get_preferred_node_fn_variant (vm, r->node_fn_registrations);
    }

  ASSERT (r->function != 0);

  /* 分配节点需要的空间,并且填充节点vlib_node_t 的字段 */
  n = clib_mem_alloc_no_fail (sizeof (n[0]));
  clib_memset (n, 0, sizeof (n[0]));
  n->index = vec_len (nm->nodes);
  n->node_fn_registrations = r->node_fn_registrations;
  n->protocol_hint = r->protocol_hint;

  vec_add1 (nm->nodes, n);

  va_start (va, fmt);
  n->name = va_format (0, fmt, &va);
  va_end (va);

  /* 创建hash查找关系,需要注意的是node的名字不能重复 */
  if (!nm->node_by_name)
    nm->node_by_name = hash_create_vec ( /* size */ 32,
          sizeof (n->name[0]), sizeof (uword));

  /* Node names must be unique. */
  {
    /* vlib_get_node_by_name() expects NULL-terminated strings 
     * 此处需要注意的是format格式化的字符串结尾不带0 */
    u8 *name = format (0, "%v%c", n->name, 0);
    vlib_node_t *o = vlib_get_node_by_name (vm, name);
    /* 释放name */
    vec_free (name);
    if (o)
      clib_error ("more than one node named `%v'", n->name);
  }
  /* 哈希绑定 */
  hash_set (nm->node_by_name, n->name, n->index);

  /* 节点index和节点名称 */
  r->index = n->index;    /* save index in registration */
  n->function = r->function;

  /* Node index of next sibling will be filled in by vlib_node_main_init. */
  n->sibling_of = r->sibling_of;
  if (r->sibling_of && r->n_next_nodes > 0)
    clib_error ("sibling node should not have any next nodes `%v'", n->name);

  if (r->type == VLIB_NODE_TYPE_INTERNAL)
    ASSERT (r->vector_size > 0);

#define _(f) n->f = r->f

  _(type);
  _(flags);
  _(state);
  _(format_buffer);
  _(unformat_buffer);
  _(format_trace);
  _(validate_frame);

  size = round_pow2 (sizeof (vlib_frame_t), VLIB_FRAME_DATA_ALIGN);

  /* scalar data size */
  if (r->scalar_size)
    {
      n->scalar_offset = size;
      size += round_pow2 (r->scalar_size, VLIB_FRAME_DATA_ALIGN);
    }
  else
    n->scalar_offset = 0;

  /* Vecor data size */
  n->vector_offset = size;
  size += r->vector_size * VLIB_FRAME_SIZE;

  /* Allocate a few extra slots of vector data to support
     speculative vector enqueues which overflow vector data in next frame. */
  size += r->vector_size * VLIB_FRAME_SIZE_EXTRA;

  /* space for VLIB_FRAME_MAGIC */
  n->magic_offset = size;
  size += sizeof (u32);

  /* round size to VLIB_FRAME_DATA_ALIGN */
  size = round_pow2 (size, VLIB_FRAME_DATA_ALIGN);

  if (r->aux_size)
    {
      n->aux_offset = size;
      size += r->aux_size * VLIB_FRAME_SIZE;
    }
  else
    n->aux_offset = 0;

  /* final size */
  n->frame_size = size = round_pow2 (size, CLIB_CACHE_LINE_BYTES);
  ASSERT (size <= __UINT16_MAX__);

  vlib_frame_size_t *fs = 0;

  n->frame_size_index = (u16) ~0;
  vec_foreach (fs, nm->frame_sizes)
    if (fs->frame_size == size)
      {
  n->frame_size_index = fs - nm->frame_sizes;
  break;
      }

  if (n->frame_size_index == (u16) ~0)
    {
      vec_add2 (nm->frame_sizes, fs, 1);
      fs->frame_size = size;
      n->frame_size_index = fs - nm->frame_sizes;
    }

  /* Register error counters. */
  vlib_register_errors (vm, n->index, r->n_errors, r->error_strings,
      r->error_counters);
  node_elog_init (vm, n->index);

  _(runtime_data_bytes);
  if (r->runtime_data_bytes > 0)
    {
      vec_resize (n->runtime_data, r->runtime_data_bytes);
      if (r->runtime_data)
  clib_memcpy (n->runtime_data, r->runtime_data, r->runtime_data_bytes);
    }

  vec_resize (n->next_node_names, r->n_next_nodes);
  for (i = 0; i < r->n_next_nodes; i++)
    n->next_node_names[i] = r->next_nodes[i];

  vec_validate_init_empty (n->next_nodes, r->n_next_nodes - 1, ~0);
  vec_validate (n->n_vectors_by_next_node, r->n_next_nodes - 1);

  n->owner_node_index = n->owner_next_index = ~0;

  /* Initialize node runtime. */
  {
    vlib_node_runtime_t *rt;
    u32 i;

    if (n->type == VLIB_NODE_TYPE_PROCESS)
      {
  vlib_process_t *p;
  uword log2_n_stack_bytes;

  log2_n_stack_bytes = clib_max (r->process_log2_n_stack_bytes,
               VLIB_PROCESS_LOG2_STACK_SIZE);
  log2_n_stack_bytes = clib_max (log2_n_stack_bytes,
               clib_mem_get_log2_page_size ());

  p = clib_mem_alloc_aligned (sizeof (p[0]), CLIB_CACHE_LINE_BYTES);
  clib_memset (p, 0, sizeof (p[0]));
  p->log2_n_stack_bytes = log2_n_stack_bytes;

  p->stack = clib_mem_vm_map_stack (1ULL << log2_n_stack_bytes,
            CLIB_MEM_PAGE_SZ_DEFAULT,
            "process stack: %U",
            format_vlib_node_name, vm,
            n->index);

  if (p->stack == CLIB_MEM_VM_MAP_FAILED)
    clib_panic ("failed to allocate process stack (%d bytes)",
          1ULL << log2_n_stack_bytes);

  /* Process node's runtime index is really index into process
     pointer vector. */
  n->runtime_index = vec_len (nm->processes);
  /* 将注册的添加到 vlib_main_t 的process */
  vec_add1 (nm->processes, p);

  /* Paint first stack word with magic number so we can at least
     detect process stack overruns. */
  p->stack[0] = VLIB_PROCESS_STACK_MAGIC;

  /* Node runtime is stored inside of process. */
  rt = &p->node_runtime;
      }
    else
      {
  vec_add2_aligned (nm->nodes_by_type[n->type], rt, 1,
        /* align */ CLIB_CACHE_LINE_BYTES);
  if (n->type == VLIB_NODE_TYPE_INPUT)
    clib_interrupt_resize (&nm->interrupts,
         vec_len (nm->nodes_by_type[n->type]));
  n->runtime_index = rt - nm->nodes_by_type[n->type];
      }

    if (n->type == VLIB_NODE_TYPE_INPUT)
      nm->input_node_counts_by_state[n->state] += 1;

    rt->function = n->function;
    rt->flags = n->flags;
    rt->state = n->state;
    rt->node_index = n->index;

    rt->n_next_nodes = r->n_next_nodes;
    rt->next_frame_index = vec_len (nm->next_frames);

    vec_resize (nm->next_frames, rt->n_next_nodes);
    for (i = 0; i < rt->n_next_nodes; i++)
      vlib_next_frame_init (nm->next_frames + rt->next_frame_index + i);

    vec_resize (rt->errors, r->n_errors);
    for (i = 0; i < vec_len (rt->errors); i++)
      rt->errors[i] = n->error_heap_index + i;

    STATIC_ASSERT_SIZEOF (vlib_node_runtime_t, 128);
    ASSERT (vec_len (n->runtime_data) <= VLIB_NODE_RUNTIME_DATA_SIZE);

    if (vec_len (n->runtime_data) > 0)
      clib_memcpy (rt->runtime_data, n->runtime_data,
       vec_len (n->runtime_data));
    else
      clib_memset (rt->runtime_data, 0, VLIB_NODE_RUNTIME_DATA_SIZE);

    vec_free (n->runtime_data);
  }
#undef _
  return r->index;
}

在注册节点vlib_register_node的流程逻辑中关键的逻辑片段处理分析:

node例子举例:以vxlan 节点为例子

代码语言:javascript
复制
/* *INDENT-OFF* */
VLIB_REGISTER_NODE (vxlan4_input_node) =
{
  .name = "vxlan4-input",
  .vector_size = sizeof (u32),
  .n_errors = VXLAN_N_ERROR,
  .error_strings = vxlan_error_strings,
  .n_next_nodes = VXLAN_INPUT_N_NEXT,
  .format_trace = format_vxlan_rx_trace,
  .next_nodes = {
#define _(s,n) [VXLAN_INPUT_NEXT_##s] = n,
    foreach_vxlan_input_next
#undef _
  },
};

具体的流量在vpp上的数据报文分发,请参考上图矢量处理逻辑图片描述。

三、Node的初始化

请参考如上图描述的node节点的注册过程的具体逻辑实现。其调用关系如下所示:vlib_main()->vlib_register_all_static_nodes()->register_node()

代码语言:javascript
复制
  /* 分配节点需要的空间,并且填充节点vlib_node_t 的字段         */
  n = clib_mem_alloc_no_fail (sizeof (n[0]));
  clib_memset (n, 0, sizeof (n[0]));
  n->index = vec_len (nm->nodes);
  n->node_fn_registrations = r->node_fn_registrations;
  n->protocol_hint = r->protocol_hint;

  vec_add1 (nm->nodes, n);

  va_start (va, fmt);
  n->name = va_format (0, fmt, &va);
  va_end (va);

vec_add1表示当前新插入的节点n,向保存注册的nm->nodes这个vector中每次插入一个元素,并且新插入的元素n是从vector的尾部插入。

vlib_node_t 结构体描述如下所示:

感兴趣的朋友请参考node.h中的完整定义。

写在最后:

本章节我们主要讲述了 VPP的节点注册流程,讲述节点在初始化的过程中如何注册,和调用逻辑分析,以及以vxlan 节点为例子讲述注册的字段的含义。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-03-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 通信行业搬砖工 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档