理解 Linux 網絡棧(2):非虛擬化Linux 環境中的 Segmentation Offloading 技術


本系列文章總結 Linux 網絡棧,包括:

(1)Linux 網絡協議棧總結

(2)非虛擬化Linux環境中的網絡分段卸載技術 GSO/TSO/UFO/LRO/GRO

(3)QEMU/KVM + VxLAN 環境下的 Segmentation Offloading 技術(發送端) 

(4)QEMU/KVM + VxLAN 環境下的 Segmentation Offloading 技術(接收端)

 

第一篇文章總結了Linux 網絡協議棧的概括和功能。本文總結非虛擬化環境中的各種 Segmentation Offloading 技術。

1. 為什么需要 Segmentation offloading

   從第一篇文章的介紹中我們知道,Linux 內核傳輸層和網絡層都要做大量的計算工作,具體見上圖,這些計算都在服務器的主 CPU 中進行。這里有一些網絡協議棧計算所需要的 CPU 資源的一些參考數據。大體上,發送或者接收 1 bit/s 的數據需要 1 赫茲的 CPU 處理能力,也就是說,5 Git/s (625 MB/s) 的網絡流量大概需要 5 GHz 的 CPU 處理能力,相當於此時需要 2 個 2.5 Ghz 的多核處理器。因為以太網是單向的,發送和接收 10 Gbit/s (吞吐量就是 20 10 Gbit/s)時,大概需要 8 個 2.5 GHz 的 CPU 內核。

   這些計算大概可以分為兩類:(1)數據計算,比如校驗和計算和驗證、分包和組包等,這個和所處理的 packets 的數量有關(2)數據傳輸和上下文切換帶來的 overhead,這個和傳輸和切換的次數有關。

  為了解決問題,考慮到越來越多的物理網卡具有較強的處理能力,就出現了兩個思路:

(1)如果網卡能夠支持某些 Linux 內核協議棧所承擔的計算任務,那么就可以將這些計算從協議棧 offload (卸載)到物理網卡。

(2)如果網卡不能支持這些計算,那么盡可能地將這些計算在 Linux 內核網絡棧中延后(傳輸過程)和提前(接收過程)來減少 overhead。以 TCP 分組或者 IP 分片為例,延遲該過程,可以減少在網絡棧中傳輸和處理的 packets 的數目,從而減少數據傳輸和上下文切換所需要的主 CPU 計算能力。

2.  Segmentation offloading 技術

2.1 TSO (TCP Segmentation Offloading)

2.1.1 TCP Segmentation (TCP 分段)

  MSS(Maxium Segment Size): MSS 是 TCP 數據段每次能夠傳輸的最大數據分段的長度。為了達到最佳的傳輸效能,TCP 協議在建立連接的時候通常要協商雙方的 MSS 值,這個值 TCP 協議在實現的時候往往用 MTU 值代替( MSS = MTU - IP 數據包包頭大小20Bytes - TCP 數據段的包頭大小20Bytes),所以在默認以太網 MTU 為 1500 bytes 時,MSS為 1460。

  TCP 分段:當網絡應用發給 TCP 的 message 的長度超過 MSS 時,TCP 會對它按照 MSS 的大小將其分為多個小的 packet,並且在每個 packet 上添加 TCP Header 成為一個 TCP 段(segement)。

2.1.2 TSO  

  TSO 是一種利用網卡分割大數據包,減小 CPU 負荷的一種技術,也被叫做 LSO (Large segment offload) ,如果數據包的類型只能是 TCP,則被稱之為 TSO,如果硬件支持 TSO 功能的話,也需要同時支持硬件的 TCP 校驗計算和分散 - 聚集 (Scatter Gather) 功能。可以看到 TSO 的實現,需要一些基本條件,而這些其實是由軟件和硬件結合起來完成的,對於硬件,具體說來,硬件能夠對大的數據包進行分片,分片之后,還要能夠對每個分片附着相關的頭部。

  TSO 就是將由 TCP 協議棧所做的 TCP 分段交給具有這種能力的物理網卡去做,因此它需要如下支持:

  • 物理網卡支持。
  • Linux 網卡驅動支持。可以使用 ethtool -K ethX tso on 命令打開網卡和驅動對 TSO 的支持,如果返回錯誤則表示不支持。
  • 還需要 Net:TCP checksum offloading and Net:Scatter Gather 支持。

  使用 TSO 以后,應用發出的大的數據塊在不超過 64k 的情況下,將會直接經過Linux 網絡棧發到網卡的驅動的 driver queue,然后在網卡中根據 skb 中的預設分組數據(主要是 MSS)對它執行 TCP 分段。下圖是使用 TSO 和不使用 TSO 的情形的對比:

2.2 UFO - UDP Fragmentation Offload

   UDP 數據報,由於它不會自己進行分段,因此當長度超過了 MTU 時,會在網絡層進行 IP 分片。同樣,ICMP(在網絡層中)同樣會出現IP分片情況。

2.2.1 IP fragmentation (分片)

MTU 和 IP 分片:

  • MTU:上文已經說過了,MTU 是鏈路層中的網絡對數據幀的一個限制,依然以以太網為例,默認 MTU 為1500字節。
  • IP 分片:一個 IP 數據報在以太網中傳輸,如果它的長度大於該 MTU 值,就要進行分片傳輸,使得每片數據報的長度小於MTU。分片傳輸的 IP 數據報不一定按序到達,但 IP 首部中的信息能讓這些數據報片按序組裝。IP數據報的分片與重組是在網絡層進完成的。

IP 分片和 TCP 分段的區別:

  • IP 數據報分片后,只有第一片帶有UDP首部或ICMP首部,其余的分片只有IP頭部,到了端點后根據IP頭部中的信息再網絡層進行重組。而 TCP 報文段的每個分段中都有TCP 首部,到了端點后根據 TCP 首部的信息在傳輸層進行重組。IP數據報分片后,只有到達目的地后才進行重組,而不是向其他網絡協議,在下一站就要進行重組。
  • 對 IP 分片的 TCP segment (段)來說,即使只丟失一片數據, TCP 層也要重新傳整個數據報。這是因為IP層本身沒有超時重傳機制------由更高層(比如TCP)來負責超時和重傳。當來自TCP報文段的某一段(在IP數據報的某一片中)丟失后,TCP在超時后會重發整個TCP報文段,該報文段對應於一份IP數據報(可能有多個IP分片),沒有辦法只重傳數據報中的一個數據分片。這就是為什么對 TCP 來說要盡量避免 IP 分片的原因。

IP 分片和 TCP 分段的關系:

  • 在非虛擬化環境中,MSS 肯定是要比 MTU 小的,因此,每個 TCP 分組不再需要 IP 分片就可以直接交給網卡去傳輸。
  • 在虛擬戶環境中,如果配置不當,虛機網絡應用的 TCP 連接的 MSS 比宿主機物理網卡的 MTU 大的情況下,宿主機上還是會執行 IP 分片的。

2.2.2 UFO

  UDP 協議層本身不對大的數據報進行分片,而是交給 IP 層去做。因此,UFO 就是將 IP 分片 offload 到網卡(NIC)中進行。其原理同 TSO。

  "IPv4/IPv6: UFO (UDP Fragmentation Offload) Scatter-gather approach: UFO is a feature wherein the Linux kernel network stack will offload the IP fragmentation functionality of large UDP datagram to hardware. This will reduce the overhead of stack in fragmenting the large UDP datagram to MTU sized packets"

2.3 GSO - Generic Segemetation Offload

   TSO 是使得網絡協議棧能夠將大塊 buffer 推送至網卡,然后網卡執行分片工作,這樣減輕了 CPU 的負荷,但 TSO 需要硬件來實現分片功能;而性能上的提高,主要是因為延緩分片而減輕了 CPU 的負載,因此,可以考慮將 TSO 技術一般化,因為其本質實際是延緩分片,這種技術,在 Linux 中被叫做 GSO(Generic Segmentation Offload)。它比 TSO 更通用,原因在於它不需要硬件的支持分片就可使用,對於支持 TSO 功能的硬件,則先經過 GSO 功能,然后使用網卡的硬件分片能力執行分片;而對於不支持 TSO 功能的網卡,將分片的執行,放在了將數據推送的網卡的前一刻,也就是在調用驅動的 xmit 函數前。 

2.3.1 對於 UDP,在物理網卡不支持 UFO 時,使用和不使用 GSO 的情形

注意這兩者中間的重要區別:

  • 當沒有 GSO 時,UDP 包會在 IP 層做 IP 分片,這會帶來比較嚴重的問題,包括:依賴於 PMTU,這個技術在很多的實際網絡中有時候無法工作;在高速網絡中,IPv4 packet ID 有時候會重復而導致數據損壞(Breaks down on high-bandwidth links because the IPv4 16-bit packet ID value can wrap around, causing data corruption);它將 UDP 頭算在 payload 內,因此只有第一個分片有 UDP 頭,因此一個分片丟失會導致整個IP包的損失。
  • 當有 GSO 時,由 Linux UDP 協議棧提供 UDP 分片邏輯而不是 IP 分片邏輯,這使得每個分片都有完整的 UDP 包頭,然后繼續 IP 層的 GSO 分片。所以 GSO 本身是對 UFO 的優化。

2.3.2 GSO for UDP 代碼分析

GSO for UDP 代碼在 http://www.mit.edu/afs.new/sipb/contrib/linux/net/ipv4/udp_offload.c

  • UDP GSO 回調函數:
static const struct net_offload udpv4_offload = {
    .callbacks = {
        .gso_segment = udp4_ufo_fragment,
        .gro_receive  =    udp4_gro_receive,
        .gro_complete =    udp4_gro_complete,
    },
}

函數 udp4_ufo_fragment 最終調用 skb_segment 函數進行分片:

     /**
  *      skb_segment - Perform protocol segmentation on skb.
  *      @head_skb: buffer to segment
  *      @features: features for the output path (see dev->features)
  *
  *      This function performs segmentation on the given skb.  It returns
  *      a pointer to the first in a list of new skbs for the segments.
  *      In case of error it returns ERR_PTR(err).
  */
 struct sk_buff *skb_segment(struct sk_buff *head_skb,
                             netdev_features_t features)
  • 在函數 static int ip_finish_output_gso(struct net *net, struct sock *sk,  struct sk_buff *skb, unsigned int mtu) 中能看到,首先按照 MSS 做 GSO,然后在調用 ip_fragment 做 IP 分片。可見,在通常情況下(虛機 TCP MSS 要比物理網卡 MTU 小),只做 UDP GSO 分段,IP 分片是不需要做的;只有在特殊情況下 (虛機 TCP MSS 超過了宿主機物理網卡 MTU),IP 分片才會做。這個和試驗中看到的效果是相同的。

2.3.3 對 TCP,在網卡不支持 TSO 時,使用和不使用 GSO 的情形

兩者都是 TCP 分片,只是位置不同。

2.3.4 GSO for TCP 代碼邏輯分析

(1)tcp_output 函數

1. Checks if GSO is enabled:
    sysctl net.inet.tcp.gso = 1
    sysctl net.gso.”ifname”.enable_gso = 1
2. Checks if the packet length exceeds the MTU

If 1 and 2 are true, sets GSO flag: m->m_pkthdr.csum_flags |= GSO_TO_CSUM(GSO_TCP4);

(2)ip_output 函數

If GSO is enabled and required, then avoids checksum (IP & TCP) and avoids IP Fragmentation

(3)ether_output 函數

If GSO is enabled and required: calls gso_dispatch() instead of ifp->transmit()

(4)gso_dispatch 函數

int gso_dispatch(struct ifnet *ifp, struct mbuf *m, u_int mac_hlen)
{
  …
  gso_flags = CSUM_TO_GSO(m->m_pkthdr.csum_flags);
  …
  error = gso_functions[gso_flags](ifp, m, mac_hlen);
  return error;
}

(5)gso_functions 函數

gso_functions[GSO_TCP4]
 gso_ip4_tcp(…) - GSO on TCP/IPv4 packet
 
1. m_seg(struct mbuf *m0, int hdr_len, int mss, …)
   returns the mbuf queue that contains the segments of the original packet (m0).
   hdr_len - first bytes of m0 that are copied in each new segments
   mss - maximum segment size
2. fixes TCP and IP headers in each new segments
3. sends new segments to the device driver [ifp->if_transmit()] 

2.4 LRO (Large Receive Offload)   

    Linux 在 2.6.24 中加入了支持 IPv4 TCP 協議的 LRO (Large Receive Offload) ,它通過將多個 TCP 數據聚合在一個 skb 結構,在稍后的某個時刻作為一個大數據包交付給上層的網絡協議棧,以減少上層協議棧處理 skb 的開銷,提高系統接收 TCP 數據包的能力。當然,這一切都需要網卡驅動程序支持。理解 LRO 的工作原理,需要理解 sk_buff 結構體對於負載的存儲方式,在內核中,sk_buff 可以有三種方式保存真實的負載:

  1. 數據被保存在 skb->data 指向的由 kmalloc 申請的內存緩沖區中,這個數據區通常被稱為線性數據區,數據區長度由函數 skb_headlen 給出
  2. 數據被保存在緊隨 skb 線性數據區尾部的共享結構體 skb_shared_info 中的成員 frags 所表示的內存頁面中,skb_frag_t 的數目由 nr_frags 給出,skb_frags_t 中有數據在內存頁面中的偏移量和數據區的大小
  3. 數據被保存於 skb_shared_info 中的成員 frag_list 所表示的 skb 分片隊列中

    合並了多個 skb 的超級 skb,能夠一次性通過網絡協議棧,而不是多次,這對 CPU 負荷的減輕是顯然的。

 

2.5 GRO (Generic Receive Offloading)

  前面的 LRO 的核心在於:在接收路徑上,將多個數據包聚合成一個大的數據包,然后傳遞給網絡協議棧處理,但 LRO 的實現中存在一些瑕疵:

  • 數據包合並可能會破壞一些狀態
  • 數據包合並條件過於寬泛,導致某些情況下本來需要區分的數據包也被合並了,這對於路由器是不可接收的
  • 在虛擬化條件下,需要使用橋接功能,但 LRO 使得橋接功能無法使用
  • 實現中,只支持 IPv4 的 TCP 協議

  而解決這些問題的辦法就是新提出的 GRO。首先,GRO 的合並條件更加的嚴格和靈活,並且在設計時,就考慮支持所有的傳輸協議,因此,后續的驅動,都應該使用 GRO 的接口,而不是 LRO,內核可能在所有先有驅動遷移到 GRO 接口之后將 LRO 從內核中移除。GRO 和 LRO 的最大區別在於,GRO 保留了每個接收到的數據包的熵信息,這對於像路由器這樣的應用至關重要,並且實現了對各種協議的支持。以 IPv4 的 TCP 為例,匹配的條件有:

  • 源 / 目的地址匹配
  • TOS/ 協議字段匹配
  • 源 / 目的端口匹配

  這篇文章 linux kernel 網絡協議棧之GRO(Generic receive offload) 詳細分析了 GRO 代碼。

2.5.1 在不支持 LRO 的情況下,對 TCP 使用和不使用 GRO 的情形

2.6 TCP/UDP Segementation Offload 小結

2.6.1 小結

Offload 傳輸段還是接收端    針對的協議    Offloading 的位置 ethtool 命令輸出中的項目 ethtool 命令中的 option 網卡/Linux 內核支持情況
TSO 傳輸段 TCP NIC tcp-segmentation-offload tso

Linux 內核從 2.5.33 引入 (2002)

網卡普遍支持

UFO 傳輸段 UDP NIC udp-fragmentation-offload ufo

linux 2.6.15 引入 (2006)

網卡普遍不支持

GSO 傳輸段 TCP/UDP NIC 或者 離開 IP 協議棧進入網卡驅動之前 generic-segmentation-offload gso

GSO/TCP: Linux 2.6.18 中引入(2006)

GSO/UDP: linux 3.16 (2014)

             
LRO 接收段 TCP NIC large-receive-offload lro

Linux 內核 2.6.24 引入(2008)

網卡普遍支持

GRO 接收段 TCP NIC 或者離開網卡驅動進入 IP 協議棧之前 generic-receive-offload gro

Linux 內核 2.6.18 引入(2006)

網卡普遍支持

2.6.2 性能對比 

   

                              [TSO/GSO for TCP/IPv4]                                                                [GSO for UDP/IPv4]

從這圖也可以看出:

  • 對 TCP 來說,在 CPU 資源充足的情況下,TSO/GSO 能帶來的效果不大,但是在CPU資源不足的情況下,其帶來的改觀還是很大的。
  • 對 UDP 來說,其改進效果一般,改進效果不超過 20%。所以在 VxLAN 環境中,其實是可以把 GSO 關閉,從而避免它帶來的一些潛在問題。

2.6.3  Offloading 帶來的潛在問題

  分段offloading 可能會帶來潛在的問題,比如網絡傳輸的延遲 latency,因為 packets 的大小的增加,大大增加了 driver queue 的容量(capacity)。比如說,系統一方面在使用大的 packet size 傳輸大量的數據,同時在運行許多的交換式應用(interactive application)。因為交互式應用會定時發送許多小的packet,這時候可能會應為這些小的 packets 被淹沒在大的 packets 之中,需要等待較長的時間才能被處理,這可能會帶來不可接受的延遲。

  在網絡上也能看到一些建議,在使用這些 offloading 技術時如果發現莫名的網絡問題,建議先將這些技術關閉后再看看情況有沒有改變。

 

參考鏈接:


免責聲明!

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



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