Linux 核心網路協議棧 ------ tcp重傳資料包 tcp_retransmit_skb 函式
阿新 • • 發佈:2019-01-08
/* This retransmits one SKB. Policy decisions and retransmit queue * state updates are done by the caller. Returns non-zero if an * error occurred which prevented the send. */ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb) { struct tcp_sock *tp = tcp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); unsigned int cur_mss = tcp_current_mss(sk, 0); int err; /* Inconslusive MTU probe */ if (icsk->icsk_mtup.probe_size) { icsk->icsk_mtup.probe_size = 0; } /* Do not sent more than we queued. 1/4 is reserved for possible * copying overhead: fragmentation, tunneling, mangling etc. */ // 如果消耗很多的記憶體做其他事,那麼就沒有多餘的來做佇列的處理了~ if (atomic_read(&sk->sk_wmem_alloc) > // sk_wmem_alloc:傳輸佇列大小 min(sk->sk_wmem_queued + (sk->sk_wmem_queued >> 2), sk->sk_sndbuf)) // sk_wmem_queud:固定的佇列大小 return -EAGAIN; if (before(TCP_SKB_CB(skb)->seq, tp->snd_una)) {// 若這樣,說明是有一部分資料才需要重傳,形如:seq---snd_una---end_seq,前面一半已收到ACK if (before(TCP_SKB_CB(skb)->end_seq, tp->snd_una)) // 若這樣,說明全部ACK,無需重傳,BUG BUG(); if (tcp_trim_head(sk, skb, tp->snd_una - TCP_SKB_CB(skb)->seq)) // 一些控制資訊檢查 return -ENOMEM; } /* If receiver has shrunk his window, and skb is out of * new window, do not retransmit it. The exception is the * case, when window is shrunk to zero. In this case * our retransmit serves as a zero window probe. */ if (!before(TCP_SKB_CB(skb)->seq, tcp_wnd_end(tp)) // 如果資料在視窗後面,不會發送 && TCP_SKB_CB(skb)->seq != tp->snd_una) return -EAGAIN; if (skb->len > cur_mss) { // 如果skb長度 > MSS if (tcp_fragment(sk, skb, cur_mss, cur_mss)) // 先分片。再傳送 return -ENOMEM; /* We'll try again later. */ } /* Collapse two adjacent packets if worthwhile and we can. */ // 我*,這麼多條件 if (!(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_SYN) && // SYN包 (skb->len < (cur_mss >> 1)) && // 長度<半個MSS (tcp_write_queue_next(sk, skb) != tcp_send_head(sk)) && // 不是結尾 (!tcp_skb_is_last(sk, skb)) && // 不是最後一個 (skb_shinfo(skb)->nr_frags == 0 && // 沒有分頁資料 skb_shinfo(tcp_write_queue_next(sk, skb))->nr_frags == 0) && (tcp_skb_pcount(skb) == 1 && // gso_segs=1 tcp_skb_pcount(tcp_write_queue_next(sk, skb)) == 1) && (sysctl_tcp_retrans_collapse != 0)) tcp_retrans_try_collapse(sk, skb, cur_mss); // 這個函式不是很明白,待看~~~~~~~~~~~~~~~~~~~~~~~~~ if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk)) // 根據目的地址等條件獲取路由,如果獲取路由失敗就不能傳送 return -EHOSTUNREACH; /* Routing failure or similar. */ /* Some Solaris stacks overoptimize and ignore the FIN on a * retransmit when old data is attached. So strip it off * since it is cheap to do so and saves bytes on the network. *///Solaris系統的協議棧有時候會忽略重傳SKB上帶有的FIN標誌的payload,將payload全部剝離掉,節省網路流量 if (skb->len > 0 && (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN) && tp->snd_una == (TCP_SKB_CB(skb)->end_seq - 1)) { if (!pskb_trim(skb, 0)) { /* Reuse, even though it does some unnecessary work */ tcp_init_nondata_skb(skb, TCP_SKB_CB(skb)->end_seq - 1, TCP_SKB_CB(skb)->flags); skb->ip_summed = CHECKSUM_NONE; } } /* Make a copy, if the first transmission SKB clone we made * is still in somebody's hands, else make a clone. */ TCP_SKB_CB(skb)->when = tcp_time_stamp; err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC); // 這個才是正在的傳輸函式~~~~~~~~~~~~~~~~~~~~後面再說~~~~~~~~~~~~~~~ // 這個函式就是將資料包傳送到下面一層,再慢慢傳輸出去~~~~~~~~~~~~~~ if (err == 0) { // 傳送成功,那麼就需要更新TCP統計資訊 /* Update global TCP statistics. */ TCP_INC_STATS(TCP_MIB_RETRANSSEGS); tp->total_retrans++; // 整體重傳數量++ #if FASTRETRANS_DEBUG > 0 if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) { if (net_ratelimit()) printk(KERN_DEBUG "retrans_out leaked.\n"); } #endif if (!tp->retrans_out) tp->lost_retrans_low = tp->snd_nxt; TCP_SKB_CB(skb)->sacked |= TCPCB_RETRANS; tp->retrans_out += tcp_skb_pcount(skb); // 重傳出去的數量+=。。。 /* Save stamp of the first retransmit. */ if (!tp->retrans_stamp) tp->retrans_stamp = TCP_SKB_CB(skb)->when; // 第一次重傳時間戳 tp->undo_retrans++; /* snd_nxt is stored to detect loss of retransmitted segment, * see tcp_input.c tcp_sacktag_write_queue(). */ TCP_SKB_CB(skb)->ack_seq = tp->snd_nxt; } return err; }