FD.io VPP軟體架構(一):vppinfra(基礎結構層)

rtoax發表於2020-10-09
FD.io VPP:使用者文件 軟體架構 vppinfra(基礎結構層)


RToax
2020年9月

VPP /軟體架構
Software Architecture

fd.io vpp實現是第三代向量資料包處理實現。請注意,Apache-2許可證專門授予非專有的專利許可證。

為了提高效能,vpp資料平面由轉發節點的有向圖組成,該轉發圖每次呼叫處理多個資料包。這種模式可實現多種微處理器優化:流水線和預取以覆蓋相關的讀取延遲,固有的I快取階段行為,向量指令。除了硬體輸入和硬體輸出節點之外,整個轉發圖都是可移植的程式碼。

根據當前的情況,我們經常啟動多個工作執行緒,這些工作執行緒使用相同的轉發圖副本處理來自多個佇列的進入雜湊資料包

在這裡插入圖片描述

VPP軟體分層如下圖:

在這裡插入圖片描述

vpp資料平面包括四個不同的層:

  • VPP Infra-VPP基礎結構層,其中包含核心庫原始碼。該層執行儲存功能,與向量和環配合使用,在雜湊表中執行鍵查詢,並與用於排程圖形節點的計時器一起使用。
  • VLIB-向量處理庫。vlib層還處理各種應用程式管理功能:緩衝區,記憶體和圖形節點管理,維護和匯出計數器,執行緒管理,資料包跟蹤。Vlib實現了除錯CLI(命令列介面)。
  • VNET-VPP的網路介面(第2層,第3層和第4層)配合使用,執行會話和流量管理,並與裝置和資料控制平面配合使用。
  • Plugins-包含越來越豐富的資料平面外掛集,如上圖所示。
  • VPP-與以上所有內容連結的容器應用程式。
    重要的是要對每一層都有一定的瞭解。最好在API級別處理大多數實現,否則就不去管它了。

1. VPPINFRA(基礎設施)

與VPP基礎結構層關聯的檔案位於./src/vppinfra資料夾中。

VPPinfra是基本c庫服務的集合,足以建立直接在裸機上執行的獨立程式。它還提供高效能的動態陣列,雜湊,點陣圖,高精度的實時時鐘支援,細粒度的事件記錄和資料結構序列化。

關於vppinfra的一個合理的評論/合理的警告:您不能總是僅通過名稱來從普通函式中的行內函數中告訴巨集。巨集通常用於避免函式呼叫,並引起(有意的)副作用。

詳細介紹請參見:VPPINFRA , wiki VLIB

1.1. 向量 vectors

Vppinfra向量是使用者定義的“標頭”隨處可見的動態調整大小的陣列。許多vpppinfra資料結構(例如雜湊,堆,池)是具有各種不同標頭的向量。

The memory layout looks like this:

                   User header (optional, uword aligned)
                   Alignment padding (if needed)
                   Vector length in elements
 User's pointer -> Vector element 0
                   Vector element 1
                   ...
                   Vector element N-1

如上所示,向量API處理指向向量第0個元素的指標。空指標是長度為零的有效向量。

為避免破壞記憶體分配器,通常在保留記憶體分配的同時將向量的長度重置為零。通過vec_reset_length(v)巨集將向量長度欄位設定為零。[使用巨集!NULL指標很聰明。]

通常,使用者頭不存在。使用者標頭允許將其他資料結構構建在vppinfra向量之上。使用者可以通過vec * _aligned巨集指定資料元素的對齊方式。

向量元素可以是任何C型別,例如(int,double,struct bar)。對於在向量之上構建的資料型別(例如,堆,池等)也是如此。許多巨集具有支援向量資料對齊的_a變體和支援非零長度向量頭的_h變體。_ha變體都支援。

標頭和/或與對齊相關的巨集變體用法不一致會導致延遲,混亂的故障。

標準程式設計錯誤:儲存指向向量ith元素的指標,然後展開向量。向量擴充套件了3/2,因此此類程式碼似乎可以工作一段時間。正確的程式碼幾乎總是記住向量索引,向量索引在重新分配中是不變的。

在典型的應用程式映像中,一個提供了一組全域性函式,這些函式旨在從gdb呼叫。這裡有一些例子:

  • vl(v) - prints vec_len(v)
  • pe§ - prints pool_elts§
  • pifi(p, index) - prints pool_is_free_index(p, index)
  • debug_hex_bytes (p, nbytes) - hex memory dump nbytes starting at p

1.2. 點陣圖 bitmap

Vppinfra點陣圖是動態的,是使用vppinfra向量API構建的。非常適合各種工作。

1.3. 池 pool

Vppinfra池結合了向量和點陣圖,可以快速分配和釋放具有獨立生存期的固定大小的資料結構。池非常適合分配每個會話的結構。

1.4. 雜湊 hash

Vppinfra提供了幾種雜湊樣式。涉及資料包分類/會話查詢的資料平面問題經常使用... / src / vppinfra / bihash_template。[ch]有界索引可擴充套件雜湊。這些模板被例項化多次,以有效地服務於不同的固定鍵大小。

Biashhes是執行緒安全的。不需要讀鎖定。簡單的自旋鎖可確保一次僅一個執行緒寫入一個條目。

... / src / vppinfra / hash。[ch]中的原始vppinfra雜湊實現易於使用,並且經常用於需要精確字串匹配的控制平面程式碼中。

無論哪種情況,幾乎總是在雜湊表中查詢鍵,以獲得相關向量或池中的索引。這些API非常簡單,但是在使用不受管理的任意大小的金鑰變體時,一定要小心。Hash_set_mem(雜湊表,key_pointer,值)儲存key_pointer。將向量元素的地址作為第二個引數傳遞給hash_set_mem通常是一個嚴重的錯誤。最好在文字段中儲存常量字串地址。

1.5. 格式 format

Vppinfra格式大致等效於printf。

格式具有一些值得一提的屬性。Format的第一個引數是一個(u8 *)向量,在該向量後附加了當前format操作的結果。連結呼叫非常簡單:

u8 * result;

result = format (0, "junk = %d, ", junk);
result = format (result, "more junk = %d\n", more_junk);

如前所述,NULL指標是完全正確的0長度向量。格式返回(u8 *)向量,而不是C字串。如果要列印(u8 *)向量,請使用“%v”格式字串。如果您需要一個(u8 *)向量,它也是一個適當的C字串,則可以使用以下兩種方案之一:

vec_add1 (result, 0)
or 
result = format (result, "<whatever>%c", 0); 

請記住,如果合適,請對vec_free()結果進行處理。注意不要通過格式化未初始化的u8 *。

格式通過“%U”格式規範實現了一種特別方便的使用者格式方案。例如:

u8 * format_junk (u8 * s, va_list *va)
{
  junk = va_arg (va, u32);
  s = format (s, "%s", junk);
  return s;
}

result = format (0, "junk = %U, format_junk, "This is some junk");

如果需要,format_junk()可以呼叫其他使用者格式的函式。程式設計師負責引數型別檢查。如果va_arg(va,<type>)巨集與呼叫者的現實想法不符,通常會導致使用者格式函式崩潰。

1.6. 取消格式 unformat

Vppinfra unformat與scanf隱約相關,但更為籠統。

典型的用例涉及從C字串或(u8 *)向量初始化unformat_input_t,然後通過unformat()進行解析,如下所示:

unformat_input_t input;

unformat_init_string (&input, "<some-C-string>");
/* or */
unformat_init_vector (&input, <u8-vector>);

然後迴圈解析單個元素:

while (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT) 
{
  if (unformat (&input, "value1 %d", &value1))
    ;/* unformat sets value1 */
  else if (unformat (&input, "value2 %d", &value2)
    ;/* unformat sets value2 */
  else
    return clib_error_return (0, "unknown input '%U'", format_unformat_error, 
                              input);
} 

與格式化一樣,取消格式化通過“%U”使用者取消格式化功能方案實現了使用者取消格式化功能。

1.7. Vppinfra錯誤和警告

vpp資料平面中的許多函式的返回值型別為clib_error_t *。Clib_error_t是任意字串,帶有一些後設資料[致命,警告],並且易於宣佈。返回NULL clib_error_t *表示“沒問題,沒有錯誤”。

Clib_warning(<format-args>)是新增除錯輸出的便捷方法。clib警告優先於function:line info以明確定位訊息源。Clib_unix_warning()新增了perror()樣式的Linux系統呼叫資訊。在生產映像中,clib_warnings會生成syslog條目。

1.8. 序列化

Vppinfra序列化支援使程式設計師可以輕鬆地序列化和反序列化複雜的資料結構。

基本的原始序列化/反序列化函式使用網路位元組順序,因此在小端位元組序的主機上序列化和在大端位元組序的主機上反序列化沒有結構性問題。

1.9. 事件記錄器,圖形事件日誌檢視器

vppinfra事件記錄器提供了非常輕量級(不到100ns),帶有時間戳的精確事件記錄服務。參見... / src / vppinfra / {elog.c,elog.h}

序列化支援使其易於儲存,並最終組合了一組事件日誌。在通過本地LAN執行NTP的分散式系統中,我們發現從多個系統元素收集的事件日誌可以與不小於50us的時間不確定性結合使用。

典型的事件定義和日誌記錄呼叫如下所示:

ELOG_TYPE_DECLARE (e) = 
{
  .format = "tx-msg: stream %d local seq %d attempt %d",
  .format_args = "i4i4i4",
};
struct { u32 stream_id, local_sequence, retry_count; } * ed;
ed = ELOG_DATA (m->elog_main, e);
ed->stream_id = stream_id;
ed->local_sequence = local_sequence;
ed->retry_count = retry_count;

ELOG_DATA巨集返回一個指向20個位元組的任意事件資料的指標,該資料將按照format_args的說明進行格式化(離線,而不是在執行時)。除了明顯的整數格式外,CLIB事件記錄器還提供了一些有趣的功能。“ t4”格式漂亮地列印列舉值:

ELOG_TYPE_DECLARE (e) = 
{
  .format = "get_or_create: %s",
  .format_args = "t4",
  .n_enum_strings = 2,
  .enum_strings = { "old", "new", },
};

t”格式說明符表示相應的資料是事件的列舉字串集中的索引,如先前的事件型別定義中所示。

T”格式說明符表示相應的資料是事件日誌的字串堆中的索引。這使程式設計師可以發出任意格式的字串。人們通常將此功能與雜湊表結合使用,以防止事件日誌字串堆任意增大。

注意每個日誌條目資料欄位限制為20個八位位元組,事件日誌格式化程式支援這些資料型別的任意組合。如:“format”欄位可能包含以下的一個或多個例項:

  • i1-8位無符號整數
  • i2-16位無符號整數
  • i4-32位無符號整數
  • i8-64位無符號整數
  • F4-浮動
  • f8-雙
  • s-以NULL結尾的字串-注意
  • sN-N位元組字元陣列
  • t1,2,4-每個事件的列舉ID
  • T4-事件日誌字串表偏移量

vpp引擎事件日誌是執行緒安全的,並且由所有執行緒共享。注意不要序列化計算。儘管事件記錄器的速度儘可能快,但它不適用於硬資料平面程式碼中的每個資料包。它最適合捕獲罕見事件-上下連結事件,特定的控制皮膚事件等。

vpp引擎具有幾個除錯CLI命令,用於處理其事件日誌:

vpp# event-logger clear
vpp# event-logger save <filename> # for security, writes into /tmp/<filename>.
                                  # <filename> must not contain '.' or '/' characters
vpp# show event-logger [all] [<nnn>] # display the event log
                                   # by default, the last 250 entries

事件日誌預設為128K條目。命令列引數“ … vlib {elog-events <nnn>}”配置事件日誌的大小。

如上所述,vpp引擎事件日誌是執行緒安全的並且是共享的。為避免混淆工作執行緒記錄的事件的不出現,請確保編碼&vlib_global_main.elog_main-而不是&vm-> elog_main。後一種形式在主執行緒中是正確的,但幾乎可以肯定會在輔助執行緒中產生不好的結果。

1.10. G2圖形事件檢視器

g2圖形事件檢視器可以直接或通過c2cpel工具顯示序列化的vppinfra事件日誌。請參閱g2 Wiki頁面
G2圖形事件檢視器可以直接或通過c2cpel工具顯示序列化的vppinfra事件日誌。G2是一個細粒度的事件日誌檢視器。它具有高度的可擴充套件性,支援O(1e7事件)和O(1e3離散顯示“軌道”)。G2顯示由vppinfra“ elog。[ch]”記錄器元件生成的二進位制資料,並且還支援CPEL檔案格式,如本節所述。

G2館:此連結描述了如何構建G2

1.10.1. 設定顯示首選項

檔案$ < HOMEDIR > /。g2包含顯示首選項,可以覆蓋它們。只需取消註釋以下所示的節之一,或根據需要進行實驗。

/*
 * Property / parameter settings for G2
 *
 * Setting for a 1024x768 display:
 * event_selector_lines=20
 * drawbox_height=800
 * drawbox_width=600
 *
 * new mac w/ no monitor:
 * event_selector_lines=20
 * drawbox_height=1200
 * drawbox_width=700
 *
 * 1600x1200:
 * drawbox_width=1200
 * drawbox_height=1000
 * event_selector_lines=25
 *
 * for making screenshots on a Macbook Pro
 * drawbox_width=1200
 * drawbox_height=600
 * event_selector_lines=20
 */

1.10.2. 螢幕分類法

這是帶註釋的G2檢視器螢幕快照,對應於BGP字首下載期間的活動。此資料在Cisco IOS-XR系統捕獲了:

在這裡插入圖片描述

檢視器具有兩個主滾動條:水平軸滾動條可在時間上移動主繪圖區域;水平滾動條可在時間上移動主繪圖區域。垂直軸更改可見的過程跡線集。放大/縮小運算子更改時間刻度。

事件選擇器PolyCheckMenu更改顯示的事件集。使用這些工具以及一些耐心,您可以瞭解給定的事件日誌。

1.10.3. 滑鼠手勢

G2具有三個相當複雜的滑鼠手勢介面,值得詳細說明。首先,在顯示事件上單擊滑鼠左鍵會彈出每個事件的詳細資訊框。

在這裡插入圖片描述

滑鼠左鍵單擊事件詳細資訊框將其關閉。要縮放到顯示區域,請按住滑鼠左鍵,然後向右或向左拖動直到出現縮放柵欄對:

在這裡插入圖片描述

縮放操作完成後,顯示如下:

在這裡插入圖片描述

點選任意圖將以全解析度顯示它們,右鍵點選將在新標籤頁中開啟圖,

1.10.4. 時間尺

要使用時間標尺,請按住滑鼠右鍵;向右或向左拖動,直到標尺測量感興趣的區域。如果時間軸刻度很粗,則事件框的時間寬度可能很大,因此在使用時間標尺時,請在每個事件框中使用“參考點”。

在這裡插入圖片描述

1.10.5. 活動選擇

更改事件選擇器設定可明顯控制顯示的點集。在這裡,我們抑制所有事件,除了“此執行緒現在正在CPU上執行”:

在這裡插入圖片描述

設定相同,顯示所有事件:
在這裡插入圖片描述

請注意,先前顯示的事件詳細資訊框由於重新選擇事件程式碼而被抑制,當重新選擇事件程式碼時,該事件詳細資訊框將重新出現。在上面的示例中,“ THREAD / THREADY pid:491720 tid:12”詳細資訊框以這種方式出現。

1.10.6. 快照

g2主視窗左下角的三個按鈕控制快照環。快照只是儲存的檢視:將檢視器調整為“有趣的”配置,然後按“快照”按鈕將快照新增到環中。

單擊“下一步”還原下一個可用快照。本德爾鍵刪除當前快照。

請參閱下面的熱鍵部分,以訪問快速簡便的方法來儲存和恢復快照環。最終,我們可能會新增安全/行動式/受支援的機制,以從CPEL和vppinfra事件日誌檔案中儲存/恢復快照環。

1.10.7. 追逐事件

事件跟蹤按發生的最後一個選定事件對跟蹤軸進行排序。例如,如果選擇一個事件,表示“ CPU上正在執行執行緒”,則顯示的前N個跟蹤將是要執行的前M個執行緒(N <= M;一個執行緒可能執行不止一次。此功能解決了導致分析的問題通過繪圖區域的有限大小。

在標準(NoChaseEvent)模式下,看起來只有BGP執行緒5和9是活動的:
在這裡插入圖片描述

按下ChaseEvent按鈕後,我們看到了另一幅圖片:

在這裡插入圖片描述

1.10.8. 埋葬無聊的足跡

序列<ctrl> <left-mouse-click>將滑鼠下方的軌道移動到軌道集的末尾,從而有效地將其掩埋。序列<shift> <left-mouse-click>將滑鼠下的軌道移到軌道集的開頭。後者的功能可能不完全正確–我認為我們最終可能會提供“撤消”堆疊來提供精確的執行緒挖掘。

1.10.9. 摘要模式

摘要模式通過將事件呈現為短的垂直線段而不是編號的框來使螢幕混亂。事件詳細資訊顯示不受影響。G2以摘要模式啟動,並充分縮小以顯示軌跡中的所有事件。給定大量事件,摘要模式可將初始螢幕繪製時間減少到可容忍的值。充分放大後,鍵入“e”-進入事件模式,以啟用裝箱的數字事件顯示。

1.10.10. 熱鍵

G2支援以下熱鍵操作,根據該功能的原始作者,據說(大約在1996年)類似Quake:

略,點選檢視


以上內容由RToax翻譯整理自網路。

相關文章