在一個tcp連接中,如果一方過了rto時間內都沒收到對方的ACK,會觸發重傳並調用tcp_write_timer定時器處理函數
其中RTO表示重傳時間,RTO是動態計算的,需要考慮到當前的重傳次數。
tcp_write_timer調用tcp_retransmit_timer重傳處理函數,後者會調用tcp_write_timeout判斷重傳次數是否超過最大次數(默認12次,約9分鐘)
是的話關閉當前連接,否則重傳一個skb,代碼如下
static void tcp_write_timer(unsigned long data)
{
struct sock *sk = (struct sock *)data;
struct inet_connection_sock *icsk = inet_csk(sk);
int event;
bh_lock_sock(sk); //加鎖,防止其他下部分任務同時執行
if (sock_owned_by_user(sk)) { //sock結構被用戶進程鎖住,暫時放棄並重設定時器(HZ / 20)秒後再執行:HZ是時鐘中斷每秒發生的次數
/* Try again later */
sk_reset_timer(sk, &icsk->icsk_retransmit_timer, jiffies + (HZ / 20));
goto out_unlock;
}
if (sk->sk_state == TCP_CLOSE || !icsk->icsk_pending) //關閉了或者定時器處理函數還沒初始化
goto out;
if (time_after(icsk->icsk_timeout, jiffies)) { //還沒到時間呢
sk_reset_timer(sk, &icsk->icsk_retransmit_timer, icsk->icsk_timeout);
goto out;
}
event = icsk->icsk_pending; //TCP有七個定時器,但是隻用了四個來實現,複用了一些不可能同時使用的定時器
icsk->icsk_pending = 0; //通過icsk_pending判斷類別
switch (event) {
case ICSK_TIME_RETRANS: //重傳定時器
tcp_retransmit_timer(sk); //主要在該函數處理重傳
break;
case ICSK_TIME_PROBE0: //持續定時器
tcp_probe_timer(sk);
break;
}
TCP_CHECK_TIMER(sk);
out:
sk_mem_reclaim(sk);
out_unlock:
bh_unlock_sock(sk);
sock_put(sk);
}
void tcp_retransmit_timer(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct inet_connection_sock *icsk = inet_csk(sk);
if (!tp->packets_out) //沒有要發送的數據
goto out;
WARN_ON(tcp_write_queue_empty(sk));
if (!tp->snd_wnd && !sock_flag(sk, SOCK_DEAD) &&
!((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))) { //發送窗口已關閉&&sock不處於DEAD狀態&&不在三次握手狀態
if (tcp_time_stamp - tp->rcv_tstamp > TCP_RTO_MAX) { //當前時間減去最近收到的ACK,即超過TCP_RTO_MAX時間內沒有收到對方的ACK
tcp_write_err(sk); //需要關閉該接口
goto out;
}
tcp_enter_loss(sk, 0);
tcp_retransmit_skb(sk, tcp_write_queue_head(sk)); <span style="white-space:pre"> </span>//重傳隊列頭部的skb並重設定時器
__sk_dst_reset(sk);
goto out_reset_timer;
}
if (tcp_write_timeout(sk)) //判斷是否已超過最大重傳數
goto out;
if (icsk->icsk_retransmits == 0) { //第一次重傳
int mib_idx;
if (icsk->icsk_ca_state == TCP_CA_Recovery) {
if (tcp_is_sack(tp)) //啓用了SACK
mib_idx = LINUX_MIB_TCPSACKRECOVERYFAIL;
else
mib_idx = LINUX_MIB_TCPRENORECOVERYFAIL;
} else if (icsk->icsk_ca_state == TCP_CA_Loss) {
mib_idx = LINUX_MIB_TCPLOSSFAILURES;
} else if ((icsk->icsk_ca_state == TCP_CA_Disorder) ||
tp->sacked_out) {
if (tcp_is_sack(tp))
mib_idx = LINUX_MIB_TCPSACKFAILURES;
else
mib_idx = LINUX_MIB_TCPRENOFAILURES;
} else {
mib_idx = LINUX_MIB_TCPTIMEOUTS;
}
NET_INC_STATS_BH(sock_net(sk), mib_idx); //統計信息
}
if (tcp_use_frto(sk)) { //快速重傳?
tcp_enter_frto(sk);
} else {
tcp_enter_loss(sk, 0);
}
if (tcp_retransmit_skb(sk, tcp_write_queue_head(sk)) > 0) { <span style="white-space:pre"> </span>//重傳隊列頭部skb
/* Retransmission failed because of local congestion,
* do not backoff.
*/ //這裏重傳失敗是發生在本地,所以是擁塞造成的
if (!icsk->icsk_retransmits)
icsk->icsk_retransmits = 1;
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
min(icsk->icsk_rto, TCP_RESOURCE_PROBE_INTERVAL),
TCP_RTO_MAX); //根據RTO設置重傳定時器
goto out;
}
icsk->icsk_backoff++; //backoff加1,每次重傳會根據這個值當做數組下標得到相應的RTO(指數退避算法的RTO)
icsk->icsk_retransmits++; <span style="white-space:pre"> </span>//重傳次數加1
out_reset_timer: //重設重傳定時器等信息
if (sk->sk_state == TCP_ESTABLISHED &&
(tp->thin_lto || sysctl_tcp_thin_linear_timeouts) &&
tcp_stream_is_thin(tp) &&
icsk->icsk_retransmits <= TCP_THIN_LINEAR_RETRIES) {
icsk->icsk_backoff = 0;
icsk->icsk_rto = min(__tcp_set_rto(tp), TCP_RTO_MAX);
} else {
/* Use normal (exponential) backoff */
icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
}
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);
if (retransmits_timed_out(sk, sysctl_tcp_retries1 + 1, 0, 0))
__sk_dst_reset(sk);
out:;
}
/* A write timeout has occurred. Process the after effects. */
static int tcp_write_timeout(struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
int retry_until;
bool do_reset, syn_set = 0;
if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { <span style="white-space:pre"> </span>//處理三次握手期間的情況
if (icsk->icsk_retransmits) //SYN發生過重傳,需要檢測sock中的路由緩存
dst_negative_advice(sk);
retry_until = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries; //獲取SYN重傳次數最大值
syn_set = 1;
} else {
if (retransmits_timed_out(sk, sysctl_tcp_retries1, 0, 0)) { //sysctl_tcp_retries1是一個閾值(默認3),當重傳次數超過該值時需要
/* Black hole detection */
tcp_mtu_probing(icsk, sk); //檢測MTP是否有效
dst_negative_advice(sk); //檢測路由緩存
}
retry_until = sysctl_tcp_retries2; //sysctl_tcp_retries2是更大的閾值(15?),重傳超過該值之後必須斷開連接了
if (sock_flag(sk, SOCK_DEAD)) { //sock處於DEAD,必要時需要釋放資源
const int alive = (icsk->icsk_rto < TCP_RTO_MAX);
retry_until = tcp_orphan_retries(sk, alive);
do_reset = alive ||
!retransmits_timed_out(sk, retry_until, 0, 0);
if (tcp_out_of_resources(sk, do_reset))
return 1;
}
}
//重傳次數達到對應的上限時應該關閉連接
if (retransmits_timed_out(sk, retry_until,
syn_set ? 0 : icsk->icsk_user_timeout, syn_set)) {
/* Has it gone just too far? */
tcp_write_err(sk);
return 1;
}
return 0;
}