1. 程式人生 > >Linux 核心網路協議棧 ------ tcp重傳資料包 tcp_retransmit_skb 函式

Linux 核心網路協議棧 ------ tcp重傳資料包 tcp_retransmit_skb 函式

/* 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;
}