日期:2014-05-16  浏览次数:20802 次

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)) {