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的最初创建如下:

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]

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;

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_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,暂时忽略中断模式。

/* 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 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.

for (i = 0; i < _vec_len (nm->pending_frames); i++)
    cpu_time_now = dispatch_pending_node (vm, nm->pending_frames + i,
        cpu_time_now);
最后修改:2021 年 08 月 18 日
如果觉得我的文章对你有用,请随意赞赏