9.4 ER(Early Retransmit)タイマ
9.4.1 Why
TCPが送信するデータが失われると、高速再送アルゴリズムは、再送タイマがタイムアウトするまで待たずに、直ちにデータを再送し、データを迅速に復元する.送信側が十分な数(一般的には3個)のACKを受信できない場合、高速再送アルゴリズムは機能せず、このときRTOタイムアウトを待つしかない.ERアルゴリズムは主にこの問題を解決するためである.次の条件では、十分なACKが受信されません.
(1)混雑窓が比較的小さい
(2)ウィンドウ内の大きな数のセグメントが失われたり,伝送の最後にパケットが失われたりする.
上記の2つの条件が満たされると、送信側は、十分な数のACKを受信できないため、高速再送アルゴリズムが有効にならないことが発生する.例えば、混雑したウィンドウが3であり、その後、最初のセグメントが失われると、理論的には、最大送信セグメントは2つの重複したACKしか受信できない.この場合、高速再送によって3つの重複したackが要求されるため、送信側はRTOタイムアウトを待ってから、最初のセグメントを再送する.
上の第2の条件では,ERアルゴリズムが第1の可能性(すなわち,連続する多数のセグメントが失われる場合)を解決するための2つの可能性がある.2つ目のケースはTLP(Tail Loss Probe)で解決する必要がある.
次にERのアルゴリズムについて述べる.ERは、バイトベース、セグメントベース、LinuxのERはセグメントベースの2つのモードに基づいてもよい.ERアルゴリズムは、小さなウィンドウの下(flight countが4未満)で、高速再送をトリガする反復ACKのしきい値を1または2に減少させる.Linuxの実装では偽タイムアウトを防止するために遅延再伝送データが付加され,この機能はERタイマによって実現される.
9.4.2 When
TCPはACK受信時にtcp_を呼び出すfastretrans_alert関数は、高速再送が必要かどうかを判断します.
いつRecovery状態に入るかを決定します.
(1)パケットロスイベント発生
(2)反復ACKが乱順に等しい閾値より小さい
(3)FACKが開かれていないか、確認されていないか、キューヘッダが送信されたがタイムアウトしていない
(4)送信されたデータ>乱数のしきい値、またはSACKセグメントの数がしきい値より小さいか、またはskbの送信を許可する
(5)thin_dupack機能がオンになっていない、または現在のリンクがthinではない、または重複ACKの数が1より大きい、またはSACKがオンになっていない、または送信するデータがある
(6)do_early_retransオープン
(7)再送完了していないが確認されていないメッセージ
(8)SACKされたメッセージがある
(9)ネットワークにおけるメッセージ数は、SACKによるメッセージ数より少なくとも1つ多い
(10)ネットワーク内のメッセージ数が4未満
(11)現在データの送信が許可されていない
(12)sysctl_tcp_early_retransの値は2または3です
(13)ACKにECEタグがない
(14)tp->srtt(smoothed round trip time)の値が0より大きい
(15)icsk->icsk_retransmit_timerタイムアウト時間は遅延時間後
以上の条件がすべて満たされると、ERタイマが設定され、そのタイムアウト時間は、より小さな値である.
紛失検知タイマの装着、再送タイマの装着、タイマの継続時にERタイマがクリアされます.
9.4.3 What
ERタイマのタイムアウト関数はtcp_resume_early_retransmit:
TCPが送信するデータが失われると、高速再送アルゴリズムは、再送タイマがタイムアウトするまで待たずに、直ちにデータを再送し、データを迅速に復元する.送信側が十分な数(一般的には3個)のACKを受信できない場合、高速再送アルゴリズムは機能せず、このときRTOタイムアウトを待つしかない.ERアルゴリズムは主にこの問題を解決するためである.次の条件では、十分なACKが受信されません.
(1)混雑窓が比較的小さい
(2)ウィンドウ内の大きな数のセグメントが失われたり,伝送の最後にパケットが失われたりする.
上記の2つの条件が満たされると、送信側は、十分な数のACKを受信できないため、高速再送アルゴリズムが有効にならないことが発生する.例えば、混雑したウィンドウが3であり、その後、最初のセグメントが失われると、理論的には、最大送信セグメントは2つの重複したACKしか受信できない.この場合、高速再送によって3つの重複したackが要求されるため、送信側はRTOタイムアウトを待ってから、最初のセグメントを再送する.
上の第2の条件では,ERアルゴリズムが第1の可能性(すなわち,連続する多数のセグメントが失われる場合)を解決するための2つの可能性がある.2つ目のケースはTLP(Tail Loss Probe)で解決する必要がある.
次にERのアルゴリズムについて述べる.ERは、バイトベース、セグメントベース、LinuxのERはセグメントベースの2つのモードに基づいてもよい.ERアルゴリズムは、小さなウィンドウの下(flight countが4未満)で、高速再送をトリガする反復ACKのしきい値を1または2に減少させる.Linuxの実装では偽タイムアウトを防止するために遅延再伝送データが付加され,この機能はERタイマによって実現される.
9.4.2 When
TCPはACK受信時にtcp_を呼び出すfastretrans_alert関数は、高速再送が必要かどうかを判断します.
2745 static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked,
2746 int prior_sacked, int prior_packets,
2747 bool is_dupack, int flag)
2748 {
...
2828 if (!tcp_time_to_recover(sk, flag)) {
2829 tcp_try_to_open(sk, flag, newly_acked_sacked);
2830 return;
2831 }
...
tcp_time_to_recover関数いつRecovery状態に入るかを決定します.
2076 static bool tcp_time_to_recover(struct sock *sk, int flag)
2077 {
2078 struct tcp_sock *tp = tcp_sk(sk);
2079 __u32 packets_out;
2080
2081 /* Trick#1: The loss is proven. */
2082 if (tp->lost_out)
2083 return true;
2084
2085 /* Not-A-Trick#2 : Classic rule... */
2086 if (tcp_dupack_heuristics(tp) > tp->reordering)
2087 return true;
2088
2089 /* Trick#3 : when we use RFC2988 timer restart, fast
2090 * retransmit can be triggered by timeout of queue head.
2091 */
2092 if (tcp_is_fack(tp) && tcp_head_timedout(sk))
2093 return true;
2094
2095 /* Trick#4: It is still not OK... But will it be useful to delay
2096 * recovery more?
2097 */
2098 packets_out = tp->packets_out;
2099 if (packets_out <= tp->reordering &&
2100 tp->sacked_out >= max_t(__u32, packets_out/2, sysctl_tcp_reordering) &&
2101 !tcp_may_send_now(sk)) { // Nagle
2102 /* We have nothing to send. This connection is limited
2103 * either by receiver window or by application.
2104 */
2105 return true;
2106 }
2107
2108 /* If a thin stream is detected, retransmit after first
2109 * received dupack. Employ only if SACK is supported in order
2110 * to avoid possible corner-case series of spurious retransmissions
2111 * Use only if there are no unsent data.
2112 */
2113 if ((tp->thin_dupack || sysctl_tcp_thin_dupack) &&
2114 tcp_stream_is_thin(tp) && tcp_dupack_heuristics(tp) > 1 &&
2115 tcp_is_sack(tp) && !tcp_send_head(sk))
2116 return true;
2117
2118 /* Trick#6: TCP early retransmit, per RFC5827. To avoid spurious
2119 * retransmissions due to small network reorderings, we implement
2120 * Mitigation A.3 in the RFC and delay the retransmission for a short
2121 * interval if appropriate.
2122 */
2123 if (tp->do_early_retrans && !tp->retrans_out && tp->sacked_out &&
2124 (tp->packets_out >= (tp->sacked_out + 1) && tp->packets_out < 4) &&
2125 !tcp_may_send_now(sk))
2126 return !tcp_pause_early_retransmit(sk, flag);
2127
2128 return false;
2129 }
tcp_pause_early_retransmit関数は、ERタイマを設定できる唯一の関数です.1947 static bool tcp_pause_early_retransmit(struct sock *sk, int flag)
1948 {
1949 struct tcp_sock *tp = tcp_sk(sk);
1950 unsigned long delay;
1951
1952 /* Delay early retransmit and entering fast recovery for
1953 * max(RTT/4, 2msec) unless ack has ECE mark, no RTT samples
1954 * available, or RTO is scheduled to fire first.
1955 */
1956 if (sysctl_tcp_early_retrans < 2 || sysctl_tcp_early_retrans > 3 ||
1957 (flag & FLAG_ECE) || !tp->srtt)
1958 return false;
1959
1960 delay = max_t(unsigned long, (tp->srtt >> 5), msecs_to_jiffies(2));
1961 if (!time_after(inet_csk(sk)->icsk_timeout, (jiffies + delay))) <span><span class="comment">/* */</span></span>
1962 return false;
1963
1964 inet_csk_reset_xmit_timer(sk, ICSK_TIME_EARLY_RETRANS, delay,
1965 TCP_RTO_MAX);
1966 return true;
1967 }
コードから分かるように、LinuxはERタイマーを設置するために多くの障害を設けている.(1)パケットロスイベント発生
(2)反復ACKが乱順に等しい閾値より小さい
(3)FACKが開かれていないか、確認されていないか、キューヘッダが送信されたがタイムアウトしていない
(4)送信されたデータ>乱数のしきい値、またはSACKセグメントの数がしきい値より小さいか、またはskbの送信を許可する
(5)thin_dupack機能がオンになっていない、または現在のリンクがthinではない、または重複ACKの数が1より大きい、またはSACKがオンになっていない、または送信するデータがある
(6)do_early_retransオープン
(7)再送完了していないが確認されていないメッセージ
(8)SACKされたメッセージがある
(9)ネットワークにおけるメッセージ数は、SACKによるメッセージ数より少なくとも1つ多い
(10)ネットワーク内のメッセージ数が4未満
(11)現在データの送信が許可されていない
(12)sysctl_tcp_early_retransの値は2または3です
(13)ACKにECEタグがない
(14)tp->srtt(smoothed round trip time)の値が0より大きい
(15)icsk->icsk_retransmit_timerタイムアウト時間は遅延時間後
以上の条件がすべて満たされると、ERタイマが設定され、そのタイムアウト時間は、より小さな値である.
紛失検知タイマの装着、再送タイマの装着、タイマの継続時にERタイマがクリアされます.
9.4.3 What
ERタイマのタイムアウト関数はtcp_resume_early_retransmit:
2961 void tcp_resume_early_retransmit(struct sock *sk)
2962 {
2963 struct tcp_sock *tp = tcp_sk(sk);
2964
2965 tcp_rearm_rto(sk); // RTO,
2966
2967 /* Stop if ER is disabled after the delayed ER timer is scheduled */
2968 if (!tp->do_early_retrans) //ER
2969 return;
2970
2971 tcp_enter_recovery(sk, false); //
2972 tcp_update_scoreboard(sk, 1); // , skb
2973 tcp_xmit_retransmit_queue(sk); //
2974 }
tcp_xmit_retransmit_Queue関数は、データを再送する機能を実行します.2447 void tcp_xmit_retransmit_queue(struct sock *sk)
2448 {
...
2457 if (!tp->packets_out) //
2458 return;
...
2463 if (tp->retransmit_skb_hint) {
2464 skb = tp->retransmit_skb_hint;
2465 last_lost = TCP_SKB_CB(skb)->end_seq;
2466 if (after(last_lost, tp->retransmit_high))
2467 last_lost = tp->retransmit_high;
2468 } else {
2469 skb = tcp_write_queue_head(sk);
2470 last_lost = tp->snd_una;
2471 }
2472
2473 tcp_for_write_queue_from(skb, sk) { // skb
2474 __u8 sacked = TCP_SKB_CB(skb)->sacked;
2475
2476 if (skb == tcp_send_head(sk))
2477 break;
...
2489 if (tcp_packets_in_flight(tp) >= tp->snd_cwnd) //
2490 return;
...
2523 if (sacked & (TCPCB_SACKED_ACKED|TCPCB_SACKED_RETRANS)) //skb SACK
2524 continue;
2525
2526 if (tcp_retransmit_skb(sk, skb)) { // skb
2527 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPRETRANSFAIL);
2528 return;
2529 }
...
2535 if (skb == tcp_write_queue_head(sk))
2536 inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
2537 inet_csk(sk)->icsk_rto,
2538 TCP_RTO_MAX);
2539 }
...
は、高速再送時にSACKまたは再送されたskbを再送しないことに注意すべきであり、これらのskbは順調に受信できるかもしれないが、ここで再送しないとネットワークの混雑を低減する.