簡介
TSO(TCP Segment Offload)
在不支持TSO的網卡上,TCP層向IP層傳送數據會考慮mss,使得TCP向下傳送的數據可以包含在一個IP分組中而不會造成分片, mss是在TCP初始建立連線時由網卡MTU確定並和對端協商的,所以在一個MTU=1500的網卡上,TCP向下傳送的數據不會大於min(mss_local, mss_remote)-ip頭-tcp頭。
而當網卡支持TSO時,TCP層會逐漸增大mss(總是整數倍數增加),當TCP層向下傳送大塊數據時,僅僅計算TCP頭,網卡接到到了IP層傳下的大數 據包後自己重新分成若干個IP數據包,添加IP頭,複製TCP頭並且重新計算校驗和等相關數據,這樣就把一部分CPU相關的處理工作轉移到由網卡來處理。 核心TCP/IP協定棧也必須考慮下發包數和實際包數不一致的情況,例如處理擁塞控制算法時必須做一些特殊的處理等等。
註:參考核心版本為2.6.9;
支持
1 TCP/IP協定棧對TSO的支持
1.1 逐漸增大mss(offload)
在不支持TSO的網卡 上,TCP層向IP層傳送數據會考慮mss,使得TCP向下傳送的數據可以包含在一個IP分組中而不會造成分片, mss是在TCP初始建立連線時根據網卡MTU確定並和對端協商的,所以在一個MTU=1500的網卡上,TCP向下傳送的數據不會大於min (mss_local, mss_remote)-ip頭-tcp頭。
在套用層向傳輸層傳輸數據時,對於TCP協定,最終會調用如下函式:
檔案 net/ipv4/tcp.c
int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t size)
該函式會調用如下函式
檔案 net/ipv4/tcp.c
unsigned int tcp_current_mss(struct sock *sk, int large)
獲得當前的mss值,如果網卡不支持TSO,則該函式返回的mss值將和原來相同,否則如果當前不是一個MSG_OOB類型的訊息,核心將嘗試增大 mss值,注意: 最大的mss值不會大於65535-ip頭-tcp。 核心根據/proc變數tcp_tso_win_divisor決定增大後的mss占當前擁塞控制視窗的比率(snd_cwnd)。最終的效果是:增大的mss總是原有mss值的整數倍,但是不會超過snd_cwnd/tcp_tso_win_divisor。
1.2 對skb計數的修正
在啟用TSO時,由於TCP層向下傳送一個skb, 有可能最終會發出n個IP數據包,即一個skb和一個IP packet可能不是一一對應的關係,而我們都知道,TCP擁塞控制算法需要精確跟蹤當前傳送、接收以及擁塞控制視窗來決定最終傳送多少數據包,TSO的 存在給計算帶來了一定的複雜性,所以核心在每一個skb的末尾維護了額外的數據(struct skb_shared_info,通過skb_shinfo取出),表示該skb包含多少個packet。核心提供下列函式操作這塊數據: tcp_skb_pcount
tcp_skb_mss
tcp_inc_pcount
tcp_inc_pcount_explicit
tcp_dec_pcount_explicit
tcp_dec_pcount
tcp_dec_pcount_approx
tcp_get_pcount
tcp_set_pcount
tcp_packets_out_inc
tcp_packets_out_dec
tcp_packets_in_flight
最終,當TCP協定棧在調用tcp_snd_test決定是否可以傳送當前skb時,會調用上述函式修正計算結果。