Early Retransmit for TCP原理以及實現
Early Retransmit for TCP(ER)是google為了解決快重傳的一些侷限,從而對快重傳(fast retransmit)做出的一些改變,其中ER在linux kernel 3.5進入了核心,他的paper在這裡:
首先我們要知道快重傳演算法的弱點很多,比如如果傳送端接收不到足夠數量(一般來說是3個)的ack,那麼快重傳演算法就無法起作用,這個時候就只能等待RTO超時。ER主要就是為了解決這個問題的。在下面的條件下,就會導致收不到足夠的ack。
- 擁塞視窗比較小
- 視窗中一個很大的段丟失或者在傳輸的結尾處發生了丟包
如果滿足了上面的兩個條件,那麼就會發生髮送端由於接收不到足夠數量的ack,導致快重傳演算法無法生效。舉個例子,比如擁塞視窗是3,然後第一個段丟失了,那麼理論上最多傳送段只可能收到2個重複的(duplicate)ack,此時由於快重傳要求3個重複的ack,那麼傳送端將會等待RTO超時,然後重傳第一個段.
在上面的第二個條件中,有兩種可能性,其中ER是為了解決第一種可能性(也就是當一個比較大的段丟失),而第二種情況則需要TLP來解決(TLP應該會合並進下個版本的核心,我後續也會分析TLP).
下面是我對ER做的一些摘要筆記:
- 在linux下early retransit 和thin dupack是互斥的。
- early retransmit也就是會在小視窗下(flight count 小於4),減小快重傳的數量,比如減小到1或者2。而在linux的實現裡面,會人為的加上一個延遲,為了防止假超時。
- ER開啟的條件有兩個,第一個是outstand的資料很小,第二個是前一個未傳送的資料不能被髮送
- 由於early retransmit必須很小心,因此必須通過sack來猜測中間的一個段已經丟失才會啟動early retransmit,比如有3個 in flight的段,那麼sack必須有2個段,也就是這樣才會啟動early retrainsmit.
- 快重傳也只是每次新的資料傳送過來之後才會啟用,因此最少視窗要有4個段。
當接收端收到一個outstand的資料的話,必須立即傳送一個重複的ack,這個就是為了快重傳(rfc2581/5681)
一般來說是通過sack來判斷快重傳(sack_outs),也就是說每次快重傳的ack(重複ack)都會帶sack.
接下來來描述一下ER的演算法,ER可以基於兩種模式,一種是基於位元組的,一種是基於段(segment-based)的,兩種演算法基本差不多,我這裡主要描述基於段的,因為linux kernel中實現的就是基於段的。
當一個ack到來後,傳送端啟動early retransmit(ER)只有滿足下面兩個條件。
- outstanding段的個數(oseg),小於4.
- 要麼沒有未傳送的資料,要麼advertised 接受視窗不允許新的段被髮送.
當上面的兩個條件滿足並且當前tcp連結不提供sack,那麼duplicate ack threshold(快重傳的ack個數閾值),必須被減少為 ER_thresh=oseg – 1.
當上面的兩個條件滿足,並且當前tcp連線支援sack,那麼只有當(oseg-1)個數的段已經被sacked時,ER才會被使用。
ER的優點我們都知道了,接下來來看看ER的缺點,或者說可能會引起的問題。當應用層(application)不是連續的傳送資料的時候,ER會導致很多不必要的重傳。舉個例子,假設一個application傳送兩個段的資料,然後緊跟著一段idle期。那麼此時如果網路reorder這兩個段,那麼傳送者將會發送沒必要的一個段(ER的重傳).如果application持續的這樣傳送資料,那麼將會有1/3的段是沒必要傳送的。
接下來來看對應的linux kernel實現,我這裡核心版本是3.7.
首先核心增加了一個sysctl的選項tcp_early_retrans,這個選項用來開關ER,這個選項預設值是2,也就是開啟ER,並且開啟delay定時器。
tcp_early_retrans – INTEGER
Enable Early Retransmit (ER), per RFC 5827. ER lowers the threshold
for triggering fast retransmit when the amount of outstanding data is
small and when no previously unsent data can be transmitted (such
that limited transmit could be used).
Possible values:
0 disables ER
1 enables ER
2 enables ER but delays fast recovery and fast retransmit
by a fourth of RTT. This mitigates connection falsely
recovers when network has a small degree of reordering
(less than 3 packets).
Default: 2
首先來看ER的初始化函式,當每一個socket建立的時候,都會呼叫tcp_enable_early_retrans來設定對應的ER選項值,這裡主要是兩個值,一個是do_early_retrans,表示是否開啟ER,一個是early_retrans_delayed,表示delay是否已經開啟。
1 |
|
通過上面的程式碼我們可以看到ER開啟的三個條件分別是
- 首先sysctl的選項必須開啟
- 第二thin dupack必須關閉
- 第三tcp_reordering必須為3
然後我們來看tcp_time_to_recover這個函式中ER的相關部分。tcp_time_to_recover如果返回true則我們可能會進入快重傳,否則將會繼續保持open狀態.
1 |
|
上面的程式碼很好理解(上一篇blog分析過),因為判斷條件基本和paper描述一致,其中下面這個條件說明,如果收到oseg -1 個dup ack,就會進入ER處理。因為packets_out就表示oseg的段的個數。
1 |
|
然後來看tcp_pause_early_retransmit這個函式,它主要是ER對快重傳做一定的延遲.這裡通過early_retrans_delayed來標記這個延遲.這個延遲時間一般是設定為RTT/4. 也就是當ER判斷需要快重傳的時候,並不會立即啟動快重傳,而是啟動一個delay定時器,等定時器超時後再重傳。
1 |
|
然後我們來看當delay定時器到期後會發生什麼,由於通過上面的程式碼我們可以看到ER的delay定時器是重傳定時器,因此當delay 超時後,將會在重傳定時器回撥中呼叫tcp_resume_early_retransmit。
1 |
|