在tcp_ack接收ACK處理函數中,如果確認當前走慢速路徑,那么會調用tcp_ack_update_window函數檢查窗口是否需要更新並更新之,並且更新未確認數據的位置,即更新窗口左邊沿;
1 static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) 2 { 3 /* 快速路徑&& ack確認了新數據 */ 4 if (!(flag & FLAG_SLOWPATH) && after(ack, prior_snd_una)) { 5 ; 6 } 7 /* 慢速路徑 */ 8 else { 9 /* 更新發送窗口 */ 10 flag |= tcp_ack_update_window(sk, skb, ack, ack_seq); 11 } 12 }
tcp_ack_update_window執行窗口更新主流程,函數首先根據窗口擴大因子計算實際的窗口大小,然后判斷是否需要更新窗口,若需要則對窗口進行更新,注意,只有當窗口不相等的情況下才會實際更新窗口,否則只更新最后一次窗口更新ack序號;窗口更新需要更新窗口,同步MSS,檢查是否開啟快速路徑等;函數最后還將設置未確認數據位置,即窗口左邊沿;
1 /* Update our send window. 2 * 3 * Window update algorithm, described in RFC793/RFC1122 (used in linux-2.2 4 * and in FreeBSD. NetBSD's one is even worse.) is wrong. 5 */ 6 static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32 ack, 7 u32 ack_seq) 8 { 9 struct tcp_sock *tp = tcp_sk(sk); 10 int flag = 0; 11 u32 nwin = ntohs(tcp_hdr(skb)->window); 12 13 /* 根據擴大因子計算窗口大小 */ 14 if (likely(!tcp_hdr(skb)->syn)) 15 nwin <<= tp->rx_opt.snd_wscale; 16 17 /* 需要更新窗口的話 */ 18 if (tcp_may_update_window(tp, ack, ack_seq, nwin)) { 19 /* 窗口更新標記 */ 20 flag |= FLAG_WIN_UPDATE; 21 22 /* 記錄窗口更新的ack序號 */ 23 tcp_update_wl(tp, ack_seq); 24 25 26 /* 發送窗口與通告窗口不等時 */ 27 if (tp->snd_wnd != nwin) { 28 29 /* 更新發送窗口*/ 30 tp->snd_wnd = nwin; 31 32 /* Note, it is the only place, where 33 * fast path is recovered for sending TCP. 34 */ 35 /* 判斷是否開啟快路標志 */ 36 tp->pred_flags = 0; 37 tcp_fast_path_check(sk); 38 39 /* 有數據要發送 */ 40 if (tcp_send_head(sk)) 41 tcp_slow_start_after_idle_check(sk); 42 43 /* 窗口大於以前記錄的最大窗口 */ 44 if (nwin > tp->max_window) { 45 /* 更新最大窗口 */ 46 tp->max_window = nwin; 47 /* 更新mss */ 48 tcp_sync_mss(sk, inet_csk(sk)->icsk_pmtu_cookie); 49 } 50 } 51 } 52 53 /* 更新未確認的數據位置,即窗口左邊沿 */ 54 tcp_snd_una_update(tp, ack); 55 56 return flag; 57 }
tcp_may_update_window用於判斷窗口是否需要更新,滿足以下條件之一則更新:
(1) ACK確認了新的數據;
(2) 未滿足(1),ACK未確認數據,通過上面snd_una<=ack的條件,此時只能是snd_una=ack,即未確認新數據,是個重復ack,但是這個ack的序號比之前更新窗口的序號要新,則需要更新snd_wl1;
(3) 未滿足(1)(2),未確認數據,ack需要也未更新,但是窗口有所改變,則說明單單發送了一個窗口更新通知;
1 /* Check that window update is acceptable. 2 * The function assumes that snd_una<=ack<=snd_next. 3 */ 4 static inline bool tcp_may_update_window(const struct tcp_sock *tp, 5 const u32 ack, const u32 ack_seq, 6 const u32 nwin) 7 { 8 /* 9 更新條件 10 ack確認序號確認了數據,意味着窗口要收縮 11 ack確認序號未確認新數據,ack序號比上一個更新窗口ack序號要新 12 ack序號與上一個更新裝ack序號一致,但是窗口比以前的窗口大 13 */ 14 return after(ack, tp->snd_una) || 15 after(ack_seq, tp->snd_wl1) || 16 (ack_seq == tp->snd_wl1 && nwin > tp->snd_wnd); 17 }