Linux環境中的網路分段解除安裝技術 GSO/TSO/UFO/LRO/GRO

rtoax 發表於 2020-09-23

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


目錄

1. 為什麼需要 Segmentation offloading

2.  Segmentation offloading 技術

2.1 TSO (TCP Segmentation Offloading)

2.1.1 TCP Segmentation (TCP 分段)

2.1.2 TSO  

2.2 UFO - UDP Fragmentation Offload

2.2.1 IP fragmentation (分片)

2.2.2 UFO

2.3 GSO - Generic Segemetation Offload

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

2.3.2 GSO for UDP 程式碼分析

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

2.3.4 GSO for TCP 程式碼邏輯分析

2.4 LRO (Large Receive Offload)   

2.5 GRO (Generic Receive Offloading)

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

2.6 TCP/UDP Segementation Offload 小結

2.6.1 小結

2.6.2 效能對比 

2.6.3  Offloading 帶來的潛在問題

參考連結:


 

 

1. 為什麼需要 Segmentation offloading

Linux環境中的網路分段解除安裝技術 GSO/TSO/UFO/LRO/GRO

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)。

Linux環境中的網路分段解除安裝技術 GSO/TSO/UFO/LRO/GRO

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 的情形的對比:

Linux環境中的網路分段解除安裝技術 GSO/TSO/UFO/LRO/GRO

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 小的( MSS = MTU - IP 資料包包頭大小20Bytes - TCP 資料段的包頭大小20Bytes),因此,每個 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"

“ IPv4 / IPv6:UFO(UDP碎片分載)分散收集方法:UFO是一種功能,Linux核心網路堆疊會將大型UDP資料包的IP分片功能分擔給硬體。這將減少在分割大型UDP資料包時的堆疊開銷。 UDP資料包到MTU大小的資料包”

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 的情形

Linux環境中的網路分段解除安裝技術 GSO/TSO/UFO/LRO/GRO

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

  • 當沒有 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 的情形

Linux環境中的網路分段解除安裝技術 GSO/TSO/UFO/LRO/GRO

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

2.3.4 GSO for TCP 程式碼邏輯分析

Linux環境中的網路分段解除安裝技術 GSO/TSO/UFO/LRO/GRO

(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 負荷的減輕是顯然的。

Linux環境中的網路分段解除安裝技術 GSO/TSO/UFO/LRO/GRO

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 的情形

Linux環境中的網路分段解除安裝技術 GSO/TSO/UFO/LRO/GRO

2.6 TCP/UDP Segementation Offload 小結

2.6.1 小結

Offload傳輸段還是接收端   針對的協議   Offloading 的位置ethtool 命令輸出中的專案ethtool 命令中的 option網路卡/Linux 核心支援情況
TSO傳輸段TCPNICtcp-segmentation-offloadtso

Linux 核心從 2.5.33 引入 (2002)

網路卡普遍支援

UFO傳輸段UDPNICudp-fragmentation-offloadufo

linux 2.6.15 引入 (2006)

網路卡普遍不支援

GSO傳輸段TCP/UDPNIC 或者 離開 IP 協議棧進入網路卡驅動之前generic-segmentation-offloadgso

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

GSO/UDP: linux 3.16 (2014)

       
LRO接收段TCPNIClarge-receive-offloadlro

Linux 核心 2.6.24 引入(2008)

網路卡普遍支援

GRO接收段TCPNIC 或者離開網路卡驅動進入 IP 協議棧之前generic-receive-offloadgro

Linux 核心 2.6.18 引入(2006)

網路卡普遍支援

2.6.2 效能對比 

Linux環境中的網路分段解除安裝技術 GSO/TSO/UFO/LRO/GRO

 [TSO/GSO for TCP/IPv4]

Linux環境中的網路分段解除安裝技術 GSO/TSO/UFO/LRO/GRO

 [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 技術時如果發現莫名的網路問題,建議先將這些技術關閉後再看看情況有沒有改變。

 

參考連結: