Linux TCP GSO 和 TSO 實現

發表於2017-07-03
(注:kernel版本:linux 2.6.32)

概念

TSO(TCP Segmentation Offload): 是一種利用網路卡來對大資料包進行自動分段,降低CPU負載的技術。 其主要是延遲分段。

GSO(Generic Segmentation Offload): GSO是協議棧是否推遲分段,在傳送到網路卡之前判斷網路卡是否支援TSO,如果網路卡支援TSO則讓網路卡分段,否則協議棧分完段再交給驅動。 如果TSO開啟,GSO會自動開啟。

以下是TSO和GSO的組合關係:

  • GSO開啟, TSO開啟: 協議棧推遲分段,並直接傳遞大資料包到網路卡,讓網路卡自動分段
  • GSO開啟, TSO關閉: 協議棧推遲分段,在最後傳送到網路卡前才執行分段
  • GSO關閉, TSO開啟: 同GSO開啟, TSO開啟
  • GSO關閉, TSO關閉: 不推遲分段,在tcp_sendmsg中直接傳送MSS大小的資料包

開啟GSO/TSO

驅動程式在註冊網路卡裝置的時候預設開啟GSO: NETIF_F_GSO

驅動程式會根據網路卡硬體是否支援來設定TSO: NETIF_F_TSO

可以通過ethtool -K來開關GSO/TSO

是否推遲分段

從上面我們知道GSO/TSO是否開啟是儲存在dev->features中,而裝置和路由關聯,當我們查詢到路由後就可以把配置儲存在sock中。

比如在tcp_v4_connect和tcp_v4_syn_recv_sock都會呼叫sk_setup_caps來設定GSO/TSO配置。

需要注意的是,只要開啟了GSO,即使硬體不支援TSO,也會設定NETIF_F_TSO,使得sk_can_gso(sk)在GSO開啟或者TSO開啟的時候都返回true

l  sk_setup_caps

從上面可以看出,如果裝置開啟了GSO,sock都會將TSO標誌開啟,但是注意這和硬體是否開啟TSO無關,硬體的TSO取決於硬體自身特性的支援。下面看下sk_can_gso的邏輯。

l  sk_can_gso

l  net_gso_ok

由於對於tcp 在sk_setup_caps中sk->sk_route_caps也被設定有SKB_GSO_TCPV4,所以整個sk_can_gso成立。

GSO的資料包長度

對緊急資料包或GSO/TSO都不開啟的情況,才不會推遲傳送, 預設使用當前MSS。開啟GSO後,tcp_send_mss返回mss和單個skb的GSO大小,為mss的整數倍。

l  tcp_send_mss

l  tcp_xmit_size_goal

l  tcp_sendmsg

應用程式send()資料後,會在tcp_sendmsg中嘗試在同一個skb,儲存size_goal大小的資料,然後再通過tcp_push把這些包通過tcp_write_xmit發出去

最終會呼叫tcp_push傳送skb,而tcp_push又會呼叫tcp_write_xmit。tcp_sendmsg已經把資料按照GSO最大的size,放到一個個的skb中, 最終呼叫tcp_write_xmit傳送這些GSO包。tcp_write_xmit會檢查當前的擁塞視窗,還有nagle測試,tsq檢查來決定是否能傳送整個或者部分的skb, 如果只能傳送一部分,則需要呼叫tso_fragment做切分。最後通過tcp_transmit_skb傳送, 如果傳送視窗沒有達到限制,skb中存放的資料將達到GSO最大值。

l  tcp_write_xmit

其中tcp_init_tso_segs會設定skb的gso資訊後文分析。我們看到tcp_write_xmit 會呼叫tso_fragment進行“tcp分段”。而分段的條件是skb->len > limit。這裡的關鍵就是limit的值,我們看到在tso_segs > 1時,也就是開啟gso的時候,limit的值是由tcp_mss_split_point得到的,也就是min(skb->len, window),即傳送視窗允許的最大值。在沒有開啟gso時limit就是當前的mss。

l  tcp_init_tso_segs

tcp_write_xmit最後會呼叫ip_queue_xmit傳送skb,進入ip層。

ip分片,tcp分段,GSO,TSO

之後的邏輯就是之前另一篇文章中分析的GSO邏輯了。下面我們看下整個協議棧中ip分片,tcp分段,GSO,TSO的關係。我將這個流程由下圖表示。

相關文章