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

Linux 内核网络协议栈 ------ 清理重传队列中函数 tcp_clean_rtx_queue


如果重传队列中的一些数据已经被确认,那么, 需要从重传队列中清除出去,需要使用这个函数:tcp_clean_rtx_queue

/* Remove acknowledged frames from the retransmission queue. If our packet
 * is before the ack sequence we can discard it as it's confirmed to have
 * arrived at the other end.
 */
static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets)
{
         struct tcp_sock *tp = tcp_sk(sk);   // 获得tcp_sock
         const struct inet_connection_sock *icsk = inet_csk(sk); // 获得连接sock
         struct sk_buff *skb;
         u32 now = tcp_time_stamp;   // 当前时间,用于计算RTT
         int fully_acked = 1;        // 表示数据段是否完全被确认
         int flag = 0;
         u32 pkts_acked = 0;
         u32 reord = tp->packets_out; // 发送出去,还在网络上跑,但是还没有被确认的数据包们
         s32 seq_rtt = -1;
         s32 ca_seq_rtt = -1;
         ktime_t last_ackt = net_invalid_timestamp();  // 把last_ackt设置位0
         // 下面就是遍历sk_write_queue队列,遇到snd_una就停止,如果没有更新过,开始就直接退出了
         while ((skb = tcp_write_queue_head(sk)) && skb != tcp_send_head(sk)) {
                 struct tcp_skb_cb *scb = TCP_SKB_CB(skb);   // 获得这个重传队列的一个skb的cb字段
                 u32 end_seq;
                 u32 acked_pcount;
                 u8 sacked = scb->sacked;
 
                 /* Determine how many packets and what bytes were acked, tso and else */
                 if (after(scb->end_seq, tp->snd_una)) {     // 注意这个scb是我们发出去的数据的skb中的一个scb哦!,不是接受到的数据!小心
                         if (tcp_skb_pcount(skb) == 1 ||     // 这里的意思就是发出去的数据最后一个字节在已经确认的snd_una之后,说明还有没有确认的字节
                             !after(tp->snd_una, scb->seq))  // 如果没有设置了TSO 或者 seq不在snd_una之前,即不是 seq---snd_una---end_seq这样情况
                                 break;                      // 那么说明没有必要把重传元素去掉,(如果是seq---snd_una---end_seq)那么前面半部分的就可以
                                                             // 冲队列中删除!!!  注意是需要理解cb[]数组中seq和end_seq意义!在分片的情况下也是不变的!
                         acked_pcount = tcp_tso_acked(sk, skb);  // 如果只确认了TSO段中的一部分,则从skb删除已经确认的segs,并统计确认了多少段( 1 )
                         if (!acked_pcount)  // 处理出错
                                 break;
 
                         fully_acked = 0;  // 表示TSO只处理了一部分,其他还没处理完
                         end_seq = tp->snd_una; 
                 } else {
                         acked_pcount = tcp_skb_pcount(skb);  // 即 !after(scb->end_seq, tp->snd_una),说明已经完全确认OK!
                         end_seq = scb->end_seq;
                 }
 
                 /* MTU probing checks */
                 if (fully_acked && icsk->icsk_mtup.probe_size &&      // 探测mtu,暂时不多说
                     !after(tp->mtu_probe.probe_seq_end, scb->end_seq)) {
                         tcp_mtup_probe_success(sk, skb);
                 }
                 // 下面通过sack的信息得到这是一个被重传的过包
                 if (sacked & TCPCB_RETRANS) {
                         if (sacked & TCPCB_SACKED_RETRANS)   // 如果之前重传过,&& 之前还没收到回复
                                 tp->retrans_out -= acked_pcount; // 现在需要更新重传的且没有收到ACK的包
                         flag |= FLAG_RETRANS_DATA_ACKED;  // 重传包收到ACK
                         ca_seq_rtt = -1;
                         seq_rtt = -1;
                         if ((flag & FLAG_DATA_ACKED) || (acked_pcount > 1))
                                 flag |= FLAG_NONHEAD_RETRANS_ACKED;
                 } else { // 如果此数据段没有被重传过
                         ca_seq_rtt = now - scb->when; // 通过ACK确认获得RTT值
                         last_ackt = skb->tstamp;      // 获得skb的发送时间
                         if (seq_rtt < 0) {
                                 seq_rtt = ca_seq_rtt;
                        }
                         if (!(sacked & TCPCB_SACKED_ACKED))   // 如果SACK存在一段