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

Linux 内核网络协议栈 ------ TCP拥塞状态机 tcp_fastretrans_alert


这里主要说的是TCP拥塞情况下的状态状态处理

/* Process an event, which can update packets-in-flight not trivially.
 * Main goal of this function is to calculate new estimate for left_out,
 * taking into account both packets sitting in receiver's buffer and
 * packets lost by network.
 *
 * Besides that it does CWND reduction, when packet loss is detected
 * and changes state of machine.
 *
 * It does _not_ decide what to send, it is made in function
 * tcp_xmit_retransmit_queue().
 */
static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, int flag)
{
         struct inet_connection_sock *icsk = inet_csk(sk);     
         struct tcp_sock *tp = tcp_sk(sk);
         int is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP));  // 判断是不是重复的ACK
         int do_lost = is_dupack || ((flag & FLAG_DATA_SACKED) &&    // 判断是不是丢包:若是重复ACK 或者 SACK而且提前确认中没有到的包数量>重拍指标
                                     (tcp_fackets_out(tp) > tp->reordering)); // 后面会单独说说SACK和FACK内容,觉得总是理解不好
         int fast_rexmit = 0;
 
         if (WARN_ON(!tp->packets_out && tp->sacked_out))  // 如果packet_out为0,那么不可能有sacked_out
                 tp->sacked_out = 0;
         if (WARN_ON(!tp->sacked_out && tp->fackets_out))
                 tp->fackets_out = 0;
 
         /* Now state machine starts.   // 下面开始状态处理
          * A. ECE, hence prohibit cwnd undoing, the reduction is required. */
         if (flag & FLAG_ECE)           // 如果是ECE
                 tp->prior_ssthresh = 0;// 禁止拥塞窗口撤销,并开始减小拥塞窗口
 
         /* B. In all the states check for reneging SACKs. */
         if (tcp_check_sack_reneging(sk, flag))  // 检查ACK是不是确认了已经被SACK选择确认的包了
                 return;
 
         /* C. Process data loss notification, provided it is valid. */
         if (tcp_is_fack(tp) && (flag & FLAG_DATA_LOST) &&   // 提前确认、数据丢失
             before(tp->snd_una, tp->high_seq) &&   // 我们需要注意high_seq 可以标志为LOST的段序号的最大值
             icsk->icsk_ca_state != TCP_CA_Open &&  // 状态不是OPEN
             tp->fackets_out > tp->reordering) {    // 同上面说的
                 tcp_mark_head_lost(sk, tp->fackets_out - tp->reordering);   // 发现丢包,需要标志出丢失的包。 (1) 这个函数后面看
                 NET_INC_STATS_BH(LINUX_MIB_TCPLOSS);
         }
 
         /* D. Check consistency of the current state. */
         tcp_verify_left_out(tp); // #define tcp_verify_left_out(tp) WARN_ON(tcp_left_out(tp) > tp->packets_out)
                                  // 检查丢失的包应该比发送出去的包小,即确定确定left_out < packets_out
         /* E. Check state exit conditions. State can be terminated
          *    when high_seq is ACKed. */  // 下面检测状态退出条件!当high_seq 被确认的时候,这个状态就可以终止了
         if (icsk->icsk_ca_state == TCP_CA_Open) {   // 如果是open状态
                 BUG_TRAP(tp->retrans_out == 0);  // 重传数量应该=0才是合理的
                 tp->retrans_stamp = 0;           // 将重传发送时间置0
         } else if (!before(tp->snd_una, tp->high_seq)) {  // 如果high_seq已经被确认
                 switch (icsk->icsk_ca_state) {  // 
                 case TCP_CA_Loss:   // 
                         icsk->icsk_retransmits = 0;    // 超时重传次数归零
                         if (tcp_try_undo_recovery(sk)) // 尝试将前面的拥塞窗口的调整撤销,在这种情况下弄不清楚包的情况(2)
                                 return;    // 如果使用了SACK,那么不管undo成功与否,都会返回Open态
                         break;
 
                 case TCP_CA_CWR:   // 发生某些道路拥塞,需要减慢发送速度
                         /* CWR is to be held something *above* high_seq
                          * is ACKed for CWR bit to reach receiver. */
                         if (tp->snd_una != tp->high_seq) {