VPP 插件plugin 節點node 編排feature 創建--關鍵流程與框架


Pt.1 Cisco VPP啟動流程

Step.0 自定義上電時配置·文件讀取

1 VLIB_CONFIG_FUNCTION           //
2 or  
3 VLIB_EARLY_CONFIG_FUNCTION     //

配置文件:

/etc/vpp/startup.conf

e.g. 
VLIB_EARLY_CONFIG_FUNCTION (unix_config, "unix");
從配置文件/etc/vpp/startup.conf 中 添加你的參數列表,格式如下:

unix { arg1 arg2 arg3 … }

 

Step.1 初始化

 1 VLIB_INIT_FUNCTION      //用來定義"插件/節點、API、CLI等,運行環境的初始化函數" 

注冊函數到vlib_main_t->init_function_registrations,這個鏈表在main()函數之前創建。

vlib_main()->vlib_call_all_init_functions()注冊的函數在這里被調用初始化,最后執行函數vlib_main_loop()。

像這樣由 宏定義和構造函數創建的全局鏈表 的方式還有如下幾個:

 1 ·VLIB_API_INIT_FUNCTION
 2 
 3 ·VLIB_CLI_COMMAND         //用來初始化“插件CLI”  4 
 5 ·VLIB_CONFIG_FUNCTION
 6 
 7 ·VLIB_EARLY_CONFIG_FUNCTION
 8 
 9 ·VLIB_MAIN_LOOP_ENTER_FUNCTION
10 
11 ·VLIB_MAIN_LOOP_EXIT_FUNCTION
12 
13 ·VLIB_REGISTER_NODE
14 
15 ·VLIB_PLUGIN_REGISTER        //用來初始化"插件描述": 版本與名稱

 

Step.2  vpp/vnet/main.c的main()函數:主main線程

程序的入口,設置vlib_plugin_main.handoff_structure_get_cb函數指針,指向vpp/vnet/main.c中的函數vnet_get_handoff_structure。

vlib/unix/plugin.c中的vnet_get_handoff_structure()函數調用上面的的函數指針handoff_structure_get_cb。

vnet_get_handoff_structure() 定義了一個靜態變量,這個靜態變量包含了主要數據指針,比如vlib_main,vnet_main和ethernet_main(看代碼沒有vlib_main)。每個插件注冊的時候,都會通過vlib_plugin_register()將以上數據傳遞給插件。

Step.3 vlib/unix/main.c的vlib_unix_main()函數:插件加載

vlib_plugin_early_init()函數會通過dlopen加載插件目錄下的所有插件,這個目錄可以通過命令行指定。默認的插件路徑是/usr/lib/vpp_plugins。

dlopen每個插件后,VPP會獲取函數vlib_plugin_register的符號地址,所以每個插件都要求實現該函數,之前說過這個函數會傳遞非常重要的數據。

vlib_call_all_config_functions()函數解析所有的命令行選項,並且針對前期需求配置。

為以下線程創建線程棧,主要有三種類型的線程需要實現:

普通線程:比如統計采集。

EAL線程:處理包的工作。

Processes:這些都是定期執行、相互協作的多線程。比如DHCP租期續訂的線程等。VPP主線程的超時到期之后會執行這些。

最后,該函數跳轉到thread0()函數。

Step.4  vlib/unix/main.c的thread0()函數

調用vlib/main.c的vlib_main()函數

Step.5 vlib/main.c的vlib_main()函數

VLIB_REGISTER_NODE定義圖節點,注冊到vlib_main_t->node_registrations,vlib_register_all_static_nodes()遍歷這個鏈表,創建圖結點(不是連接,是創建)。

VLIB_INIT_FUNCTION聲明的函數,由vlib_call_all_init_functions()調用初始化。

如果結點被創建,vlib/node.c的vlib_node_main_init()會對圖結點進行初始化。

VLIB_MAIN_LOOP_ENTER_FUNCTION 注冊一個鏈表,vlib_call_all_main_loop_enter_functions()函數遍歷該鏈表。

調用vlib_main_loop()

Step.6 vlib/main.c的vlib_main_loop()函數

創建前面提到的相互協作的多線程,在while(1)循環中處理不同類型的圖結點

·VLIB_NODE_TYPE_PRE_INPUT:類似DBG_CLI的結點

·VLIB_NODE_TYPE_INPUT:這些是主要結點,主要從網卡或者硬件加速器獲取數據包

·進程等待信號,這個很重要,因為所有的客戶端都要通過共享內存和VPP通信。客戶端向共享內存發送一些API消息,並且向VPP發送信號(SIGUSR1)。

輸入結點組織數據包,並且將他們發送到合適的中間結點。由dispatch_pending_node()進一步處理這些數據包。

 

 Pt. 2 新建插件plugin

本質是“.so的動態庫制品

 參考:https://developer.aliyun.com/article/674304

1 VLIB_INIT_FUNCTION(xxx_init)      //插件運行環境,初始化

2 VLIB_PLUGIN_REGISTER              //插件描述, 初始化

3 VLIB_CLI_COMMAND                 //插件CLI命令,初始化 

 

2.1 編譯流程

    autoreconf  -i -f     //生成 configure

    ./configure            //生成 makefile

    make                   //

    make clean         //

 Pt. 3 節點NODE

本質是“邏輯功能單元

3.1 全局結構體 

vlib_main_t:每個線程一份,記錄着線程使用到的全局數據信息.
比如

/* Node graph main structure. */ vlib_node_main_t node_main; /* Command line interface. */ vlib_cli_main_t cli_main; /* Packet trace buffer. */ vlib_trace_main_t trace_main;

 3.2 Node相關結構體

vlib_node_t:                    結點的主結構,包括結點的處理功能函數,名稱,結點類型等,主要保存一些相對靜態信息,幾乎不怎么修改的參數,狀態信息保存在這里
vlib_node_main_t:           Node graph相關的全局信息,nodes數組、vlib_next_frame_t、vlib_pending_frame_t等數據
vlib_node_registration_t: 注冊node結點時使用,保存結點業務邏輯的函數地址,結點類型,結點狀態,結點名稱等
vlib_node_runtime_t:       實際在調度node過程中使用的結構,主要記錄在處理過程中的“頻繁變動”的信息變動
vlib_frame_t:                    每個node都有一個對應的vlib_frame_t,用來保存供node使用的數據集合(標量、矢量),這是每個node最終處理數據的內存所在地。
vlib_next_frame_t:           主要是node內部邏輯使用,用於定位該node的下一結點所對應的frame地址
vlib_pending_frame_t:     當一個node處理完數據包,則填充該待處理幀管理表數據結,調度框架便能在下一次調度時找到需要接收該數據包的下一個node。

vlib_process_t:          VLIB_NODE_TYPE_PROCESS類型node專用結構,記錄用於模擬task的基礎結構:heap上的運行時棧,2種返回時寄存器備份,等

 

 

 3.3 vlib_node_type_t

 

VLIB_NODE_TYPE_INTERNAL  :對數據包真正處理的業務node。
VLIB_NODE_TYPE_INPUT        :收包邏輯node,比如:dpdk,pcap等。
VLIB_NODE_TYPE_PRE_INPUT :目前只有一個epoll node,對socket相關邏輯提供服務,主要使用在控制業務上。
VLIB_NODE_TYPE_PROCESS   :該類型node可以被掛起也可以被恢復,有獨立的分配在heap上的運行時棧。類似於在一個線程中實現了多任務調度機制,jmp機制的協程。主要用來修改vpp node內部參數。

 

VLIB_NODE_TYPE_INTERNAL
  內部節點,最典型的節點, 僅在要處理的pending frame時運行;接收緩沖向量,執行操作。

       vpp大部分節點是這個角色,主要對數據流做內部處理,比如ip4-input-no-checksum/ip4-icmp-input等內部功能節點

VLIB_NODE_TYPE_INPUT
  輸入節點,通常是設備輸入節點。從零開始創建框架並分派到內部節點(internal), 比如dpdk-input/af-packet-input節點,
  input節點收包模式分為輪詢和中斷兩種模式vlib_node_state_t.

VLIB_NODE_TYPE_PRE_INPUT
  在其他所有node之前運行。目前只有一個epoll node,對socket相關邏輯提供服務,主要使用在控制業務上。

VLIB_NODE_TYPE_PROCESS

       該類型的node可以被掛起也可以被恢復,有獨立的分配在heap上的運行棧。類似與在一個線程中實現了多任務的調度機制,主要用來修改vpp node內部參數。
  線程節點,和線程一樣,可以可以暫停、等待事件、恢復,不同於pthread_thread,他是基於setjump/longjump實現的 “協程”.
  等待一個事件:always_inline f64 vlib_process_wait_for_event_or_clock (vlib_main_t * vm, f64 dt)
  發送一個事件: always_inline void vlib_process_signal_event (vlib_main_t * vm, uword node_index, uword type_opaque, uword data)

 3.4  驗證處理結果,決定下一處理流程

      vlib_validate_buffer_enqueue_x1                          // 決定下一走向。

 Pt. 4 feature

/* Hook up input features */

VNET_FEATURE_INIT (ip4_snat_in2out, static)    =

{

.arc_name = "ip4-unicast",

.node_name = "nat44-in2out", /* 必須存在nat44-in2out節點 */

.runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),

};

VNET_FEATURE_ARC_INIT (ip4_unicast, static)  =

{

.arc_name = "ip4-unicast",

.start_nodes = VNET_FEATURES ("ip4-input", "ip4-input-no-checksum"),/* 起始節點,必須存在node */

.last_in_arc = "ip4-lookup", /* 整個arc的最后一個feature必須是ip4-lookup,否則不能初始化成功 */

.arc_index_ptr = &ip4_main.lookup_main.ucast_feature_arc_index,/* 需要將arc索引寫入到ucast_feature_arc_index中,供對應模塊使用 */

};

 Pt. 4 CLI 命令

ref:  https://blog.csdn.net/qq_29044159/article/details/108136177

插件CLI初始化注冊:

 

 

 插件CLI處理過程:

 

 

 

 

插件CLI


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM