還在初學(xué)階段,如果有誤,希望多批評指正。
參照前面的帖子
Cisco VPP(3) 啟動(dòng)流程這里面的node主要有3種:
1、注冊的時(shí)候注冊到node_registrations的node,這個(gè)是main函數運行之前就生成的鏈表
2、將注冊的node存儲到vlib_node_main_t->vlib_node_t ** nodes,這個(gè)是運行register_node時(shí)產(chǎn)生的,主要是存儲
3、將2中的node存儲到vlib_node_main_t->vlib_process_t ** processes,運行register_node時(shí)產(chǎn)生的,運行時(shí)執行此處的node
0x0 注冊
VLIB_REGISTER_NODE主要是用來(lái)定義node,并且注冊node到vlib_main_t->vlib_node_main_t->node_registrations,這個(gè)鏈表在main()函數之前創(chuàng )建,比如ip4-input的最初創(chuàng )建如下:
[cpp]
view plain copyVLIB_REGISTER_NODE (ip4_input_node) = {
.function = ip4_input,//mbuf傳入node之后的操作函數,以及下一級node的確定
.name = "ip4-input",//name必須唯一,因為串聯(lián)node使用的標識為名字
.vector_size = sizeof (u32),
.n_errors = IP4_N_ERROR,//報錯的計數,可以用來(lái)報錯,也可以記錄正常的數據包數量,show errors命令顯示
.error_strings = ip4_error_strings,//顯示計數的時(shí)候,對計數的提示,比如正常的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顯示路徑的打印,一般是數據包走到這個(gè)node時(shí)需要輸出的信息
};
0x1 初始化
vlib_main()->vlib_register_all_static_nodes()->register_node()主要是將node鏈表中的所有node進(jìn)行初始化,并且根據node之間的關(guān)系進(jìn)行串聯(lián)。
1、所有注冊的node都會(huì )存到vlib_main_t->vlib_node_main_t->vlib_node_t ** nodes中,該nodes存在vec_header_t結構中的u8 vector_data[0]。
[cpp]
view plain copytypedef struct {
u64 len; /**記錄注冊了多少個(gè)node*/
u8 vector_data[0]; /**< Vector data . 記錄注冊的node,相比鏈表,更容易找到某個(gè)node,但是每次添加都要重新申請內存,類(lèi)似realloc */
} vec_header_t;
[cpp]
view plain copyvec_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類(lèi)型
u32 index;//是由nodes長(cháng)度算出來(lái)的index
u16 flags;//node 標識
u8 state;//node 狀態(tài)
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類(lèi)型的node存儲到vlib_node_main_t->vlib_process_t ** processes的node_runtime中,也是利用存在vec_header_t結構中的u8 vector_data[0],這個(gè)主要是偏操作的node結點(diǎn),比如startup-config-process(啟動(dòng)配置處理),admin-up-down-process(端口up down的處理),其他所有類(lèi)型的node都存儲到vlib_node_main_t->vlib_node_runtime_t * nodes_by_type中
[cpp]
view plain copyif(n->type == VLIB_NODE_TYPE_PROCESS)如果是process類(lèi)型
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類(lèi)型的node添加到nodes_by_types中,主要是后面node操作中會(huì )用到
0x2 node操作
vlib_main_loop()主要是去處理node中的操作。核心操作包含以下兩個(gè)點(diǎn):(忽略VLIB_NODE_TYPE_PROCESS類(lèi)型結點(diǎn)和VLIB_NODE_TYPE_PRE_INPUT類(lèi)型結點(diǎn)操作)
收包的入口函數,比如dpdk-input的node,里面主要主要執行node->function,暫時(shí)忽略中斷模式。
[cpp]
view plain copy/* 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);
[cpp]
view plain copyu64 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里面的一些狀態(tài),比如處理時(shí)間、vector數據包數量。
v = vlib_node_runtime_update_stats (stat_vm, node,
/* n_calls */ 1,
/* n_vectors */ n,
/* n_clocks */ t - last_time_stamp);
/*中斷模式下,vector速率超過(guò)閾值,切換到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中會(huì )計算下一級node,并且最終調用vlib_put_next_frame,將下一級的node加入到nm->pending_frames中,比如ethernet-input node的操作如下:
[cpp]
view plain copystatic_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;
}
繼續執行依賴(lài)的node,最終 dispatch_pending_node 還是調用 dispatch_node[cpp]
view plain copyfor (i = 0; i < _vec_len (nm->pending_frames); i++)
cpu_time_now = dispatch_pending_node (vm, nm->pending_frames + i,
cpu_time_now);
資料來(lái)源:
https://fd.io/歡迎加入VPP討論群:417538415