1 Mbuf庫
Mbuf庫提供了分配和釋放緩沖區(mbufs)的功能,DPDK應用程序可以使用這些mbufs來存儲消息緩沖。 消息緩沖存儲在內存池中,使用Mempool庫。
數據結構rte_mbuf通常用來承載網絡數據包buffers,但它實際上可以是任何數據(控制數據、事件……)。rte_mbuf頭部結構盡可能小,目前僅使用個cache line,最常用的字段位於第一個cache line。
2 數據包緩沖區的設計
為了存儲數據包數據(包括協議頭),考慮了兩種方法:
- 在單個存儲buffer中嵌入元數據,后面跟着數據包數據固定大小區域
- 為元數據和數據包數據分別使用獨立的存儲buffer。
第一種方法的優點是它只需要一個操作即可分配/釋放數據包的整個內存。 另一方面,第二種方法更加靈活,可以將元數據結構的分配與數據包數據的buffer分配完全分開。
DPDK選擇了第一種方法。 元數據包含控制信息,例如消息類型,長度,到數據開頭的偏移量以及用於允許緩沖區鏈接的附加mbuf結構的指針。
用於承載網絡數據包的消息緩沖可以處理需要由多個緩沖區存儲來保證據包完整的情況。許多mbuf組成的巨型幀就是這種情況,這些mbuf通過它們的next字段鏈接在一起。
對於新分配的mbuf,消息緩沖區中數據開始的區域為緩沖區開始之后的RTE_PKTMBUF_HEADROOM字節,該字節與緩存對齊。 消息緩沖區可用於在系統中不同實體之間承載控制信息、數據包、事件等。 消息緩沖也可以使用其buffer指針來指向其他消息緩沖或其他數據結構。
例1:只有一段的mbuf
例2:有三段的mbuf
Buffer管理器實現了一組相當標准的Buffer訪問功能來操縱網絡數據包。
3 存儲在內存池中的緩沖區
緩沖區管理器使用Mempool庫來申請buffer。 因此它確保數據包頭均衡分布在channels和ranks上並進行L3處理(channel和rank的概念查看mempool庫里的介紹)。 mbuf包含一個字段,用於表示它從哪個池中申請出來。當調用rte_pktmbuf_free(m)時,mbuf將被其所在的內存池回收。
4 構造函數
數據包mbuf構造函數由API提供。rte_pktmbuf_init()函數初始化mbuf結構中的某些字段,一旦創建,這些字段就不會被用戶修改(mbuf類型,源池,緩沖區起始地址等)。此函數在池創建時作為rte_mempool_create()函數的回掉函數給出。
5 分配和釋放mbufs
分配一個新mbuf需要用戶指定從哪個池中申請。對於任何新分配的mbuf,它包含一個長度為0的段。緩沖區到數據的偏移量被初始化,以便使得buffer具有一些字節headroom(RTE_PKTMBUF_HEADROOM字節)。
釋放mbuf意味着將其返回到其所在的內存池。當mbuf的內容存儲在一個池中(作為一個空閑的mbuf)時,mbuf的內容不會被修改。 由構造函數初始化的字段不需要在mbuf分配時重新初始化。
釋放包含多個段的數據包mbuf時,所有這些段都會被釋放並返回其所在的內存池。
6 操作mbufs
該庫提供了一些用於處理數據包mbuf中的數據的功能。 例如:
- 獲取數據長度
- 獲取指向數據開始的指針
- 在數據之前添加數據
- 數據后追加數據
- 刪除緩沖區開頭的數據(rte_pktmbuf_adj())
- 刪除緩沖區末尾的數據(rte_pktmbuf_trim())有關詳細信息,請參閱《 DPDK API參考》。
7 元數據
部分信息由網絡驅動程序檢索並存儲在mbuf中,以使得處理更加簡單。 例如,VLAN,RSS哈希結果(請參閱輪詢模式驅動程序)和一個指示校驗和是否由硬件計算的標志。
mbuf還包含輸入端口(它來自何處)以及鏈中mbuf段的數量。
對於鏈接的mbuf,只有鏈的第一個mbuf存儲此元信息。
例如,IEEE1588數據包的時間戳機制,VLAN標記和IP校驗和計算在RX端就是這種情況。
在TX端,如果應用程序支持,則也可以將某些處理委托給硬件。 例如,PKT_TX_IP_CKSUM標志允許卸載IPv4校驗和的計算。
以下示例說明了如何在封裝了vxlan的tcp數據包上配置不同的TX卸載:out_eth/out_ip/out_udp/vxlan/in_eth/in_ip/in_tcp/payload
- 計算out_ip的校驗和:
mb->l2_len = len(out_eth) mb->l3_len = len(out_ip) mb->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CSUM set out_ip checksum to 0 in the packet
配置DEV_TX_OFFLOAD_IPV4_CKSUM支持在硬件計算。
- 計算out_ip 和 out_udp的校驗和:
mb->l2_len = len(out_eth) mb->l3_len = len(out_ip) mb->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CSUM | PKT_TX_UDP_CKSUM set out_ip checksum to 0 in the packet set out_udp checksum to pseudo header using rte_ipv4_phdr_cksum()
配置DEV_TX_OFFLOAD_IPV4_CKSUM 和 DEV_TX_OFFLOAD_UDP_CKSUM支持在硬件上計算。
- 計算in_ip的校驗和:
mb->l2_len = len(out_eth + out_ip + out_udp + vxlan + in_eth) mb->l3_len = len(in_ip) mb->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CSUM set in_ip checksum to 0 in the packet
這以情況1類似,但是l2_len不同。 配置DEV_TX_OFFLOAD_IPV4_CKSUM支持硬件計算。 注意,只有外部L4校驗和為0時才可以工作。
- 計算in_ip 和 in_tcp的校驗和:
mb->l2_len = len(out_eth + out_ip + out_udp + vxlan + in_eth) mb->l3_len = len(in_ip) mb->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CSUM | PKT_TX_TCP_CKSUM 在報文中設置in_ip校驗和為0 使用rte_ipv4_phdr_cksum()將in_tcp校驗和設置為偽頭
這與情況2類似,但是l2_len不同。 配置DEV_TX_OFFLOAD_IPV4_CKSUM 和 DEV_TX_OFFLOAD_TCP_CKSUM支持硬件實現。 注意,只有外部L4校驗和為0才能工作。
- segment inner TCP:
mb->l2_len = len(out_eth + out_ip + out_udp + vxlan + in_eth) mb->l3_len = len(in_ip) mb->l4_len = len(in_tcp) mb->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM | PKT_TX_TCP_SEG; 在報文中設置in_ip校驗和為0 將in_tcp校驗和設置為偽頭部,而不使用IP載荷長度
配置DEV_TX_OFFLOAD_TCP_TSO支持硬件實現。 注意,只有L4校驗和為0時才能工作。
- 計算out_ip, in_ip, in_tcp的校驗和:
mb->outer_l2_len = len(out_eth) mb->outer_l3_len = len(out_ip) mb->l2_len = len(out_udp + vxlan + in_eth) mb->l3_len = len(in_ip) mb->ol_flags |= PKT_TX_OUTER_IPV4 | PKT_TX_OUTER_IP_CKSUM | PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM; 設置 out_ip 校驗和為0 設置 in_ip 校驗和為0 使用rte_ipv4_phdr_cksum()設置in_tcp校驗和為偽頭部
配置DEV_TX_OFFLOAD_IPV4_CKSUM, DEV_TX_OFFLOAD_UDP_CKSUM 和 DEV_TX_OFFLOAD_OUTER_IPV4_CKSUM支持硬件實現。
Flage標記的意義在mbuf API文檔(rte_mbuf.h)中有詳細描述。 更多詳細信息還可以參閱testpmd 源碼(特別是csumonly.c)。
8 直接及間接 Buffers
直接緩沖區是完全獨立且獨立的緩沖區。 間接緩沖區的行為類似於直接緩沖區,但事實上,緩沖區指針和其中的數據偏移量引用了另一個直接緩沖區中的數據。 這在需要復制或分段數據包的情況下很有用,因為間接緩沖區提供跨越多個緩沖區重用相同數據包數據的手段。
當使口 rte_pktmbuf_attach() 函數將緩沖區附加到直接緩沖區時,該緩沖區變成間接緩沖區。每個緩沖區有一個引用計數器字段,每當直接緩沖區附加一個間接緩沖區時,直接緩沖區上的應用計數器遞增。 類似的,每當間接緩沖區被分裂時,直接緩沖區上的引用計數器遞減。 如果生成的引用計數器為0,則直接緩沖區將被釋放,因為它不再使用。
處理間接緩沖區時需要注意幾件事情。 首先,間接緩沖區從不附加到另一個間接緩沖區。 嘗試將緩沖區A附加到間接緩沖區B(且B附加到C上了),rte_pktmbuf_attach()將自動把A附加到C上,從而有效地克隆了B。其次,為了使緩沖區變成間接緩沖區,其引用計數必須等於1,也就是說它不能被另一個間接緩沖區引用。最后,不能將間接緩沖區重新鏈接到直接緩沖區(除非這個間接緩沖區已經被分離了)。
雖然可以使用推薦的rte_pktmbuf_attach()和rte_pktmbuf_detach()函數直接調用附加/分離操作, 但建議使用更高級的rte_pktmbuf_clone()函數,該函數負責正確初始化間接緩沖區,並可以進行克隆具有多個段的緩沖區。
由於間接緩沖區不應該實際保存任何數據,因此應該配置間接緩沖區的內存池以指示減少內存消耗。可以在幾個示例應用程序中找到用於間接緩沖區的內存池初始化示例(以及用於間接緩沖區的用例示例),例如IPv4組播示例應用程序。
9 調試
在調試模式 (CONFIG_RTE_MBUF_DEBUG使能)下,mbuf庫的功能在任何操作之前執行完整性檢查(如緩沖區檢查、類型錯誤等)。
10 用例
所有網絡應用程序都應該使用mbufs來傳輸網絡數據包。
參考:
dpdk官方編程指南:http://doc.dpdk.org/guides-20.02/prog_guide/mbuf_lib.html