Forword RTO(F-RTO)算法

来源:互联网 发布:蜂窝数据 3g 编辑:程序博客网 时间:2024/04/30 06:05

简介

主要目的是为了检测由RTO偏小引起的伪RTO。主要算法是:

当重传定时器超时后,记这时候的snd.nxt为highmark。然后看收到的两个确认是否符合这个要求:ack > snd.una && ack < highmark,如果都符合,那么就宣布这是个伪RTO。

FRTO先重传第一个未被确认的包,并且在收到第一个确认后做如下判断:a. 如果这个ack确认了新的数据但是序列号小于highmark,那么再发2个新的数据包,等收到第二个确认再来判断。b.这个ack是个重复确认或者大于等于highmark,说明原来的包是真丢了,那么说明不是一个伪FRTO,还是进入到传统的RTO恢复当中去。对于收到的第二个确认,也是做和第一个确认相同的判断,只不过对符合要求的情况不需要在发包了,直接宣布是伪RTO。

实现

F-RTO is implemented (mainly) in fourfunctions:

 *   -tcp_use_frto() is used to determine if TCP is can use F-RTO

 *   -tcp_enter_frto() prepares TCP state on RTO if F-RTO is used, it is

 *    called when tcp_use_frto() showed green light

 *   -tcp_process_frto() handles incoming ACKs during F-RTO algorithm

 *   -tcp_enter_frto_loss() is called if there is not enough evidence to prove thatthe *      RTO is indeed spurious. Ittransfers the control from F-RTO to the conventional RTO recovery

 */

 以下代码来自linux-2.6内核版本

1. tcp_enter_frto()进入FRTO状态

void tcp_enter_frto(struct sock *sk){const struct inet_connection_sock *icsk = inet_csk(sk);struct tcp_sock *tp = tcp_sk(sk);struct sk_buff *skb;tp->frto_counter = 1; //表示刚刚进入FRTO阶段if (icsk->icsk_ca_state <= TCP_CA_Disorder ||            tp->snd_una == tp->high_seq ||            (icsk->icsk_ca_state == TCP_CA_Loss && !icsk->icsk_retransmits)) {/*进入FRTO的处理,如果当前网络状况还可以,比如ca_state 是OPEN或者Disorder;snd_una == high_seq, 只差一个包没有被确认; 虽然是LOSS状态,但是之前没有发生过超时重传, 那么就记录下现在的ssthresh,并告知拥塞控制算法出现了EVENT_FRTO事件*/tp->prior_ssthresh = tcp_current_ssthresh(sk);tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk);tcp_ca_event(sk, CA_EVENT_FRTO);}/* Have to clear retransmission markers here to keep the bookkeeping * in shape, even though we are not yet in Loss state. * If something was really lost, it is eventually caught up * in tcp_enter_frto_loss. *//* 为进入LOSS状态做点准备工作*/tp->retrans_out = 0;tp->undo_marker = tp->snd_una;tp->undo_retrans = 0;/* 把发送队列write_queue上所有已经发送过的包都置为未重传*/sk_stream_for_retrans_queue(skb, sk) {TCP_SKB_CB(skb)->sacked &= ~TCPCB_RETRANS;}tcp_sync_left_out(tp);/*  FRTO 状态实际上还是OPEN状态啦 */tcp_set_ca_state(sk, TCP_CA_Open);/* 记录下进入FRTO时的snd_nxt */tp->frto_highmark = tp->snd_nxt;}

2. 处理FRTO状态

当接收方收到ACK后,在tcp_ack()中会检测当前是否处于FRTO状态, 如果是的话(tp->frto_counter != 0),就会调用tcp_process_frto来处理。

static void tcp_process_frto(struct sock *sk, u32 prior_snd_una){struct tcp_sock *tp = tcp_sk(sk);tcp_sync_left_out(tp); /* 如果收到当前的确认后,snd_una并没有前进,说明又是一个DACK;或者snd_una >= 进入FRTO时的snd_nxt,所有发出去的包都收到了,那么很有可能是重传的包填了hole,也就是说第一次重传的数据是真丢了。这两种情况都可以判断出之前的包是真丢了,所以还是进入LOSS状态吧 */if (tp->snd_una == prior_snd_una ||    !before(tp->snd_una, tp->frto_highmark)) {/* RTO was caused by loss, start retransmitting in * go-back-N slow start */tcp_enter_frto_loss(sk);return;}/* 如果收到的确认是大于snd.una且小于frto_highmark的,那就符合spurious RTO的预期了*/if (tp->frto_counter == 1) {/*  这是进入FRTO后收到的第一个ACK *//* First ACK after RTO advances the window: allow two new * segments out. */tp->snd_cwnd = tcp_packets_in_flight(tp) + 2;} else {/*  这是进入FRTO后收到的第2个ACK *//* Also the second ACK after RTO advances the window. * The RTO was likely spurious. Reduce cwnd and continue * in congestion avoidance */tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh);tcp_moderate_cwnd(tp);}/* F-RTO affects on two new ACKs following RTO. * At latest on third ACK the TCP behavior is back to normal. */   /* frto_counter 的取值为0,1,2,1表示刚刚进入FRTO,还没有收到ACK; 2表示已经收到了一个ACK, 这是第二个ACK; 0表示已经收到了2个ACK, FRTO阶段已经结束了,在tcp_ack里也不会再调用tcp_process_ftro了。*/tp->frto_counter = (tp->frto_counter + 1) % 3;}

3. 没有足够的证据表明是个伪RTO, 那么就进入到Loss状态去吧

static void tcp_enter_frto_loss(struct sock *sk){struct tcp_sock *tp = tcp_sk(sk);struct sk_buff *skb;int cnt = 0;tp->sacked_out = 0;tp->lost_out = 0;tp->fackets_out = 0;sk_stream_for_retrans_queue(skb, sk) {//遍历write_queue里面所有已经发了的包cnt += tcp_skb_pcount(skb);TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST; //TCPCB_LOST位置为0if (!(TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_ACKED)) { //如果之前没有被SACK确认并且序列号小于frto_highmark的包,都标记为丢了/* Do not mark those segments lost that were * forward transmitted after RTO */if (!after(TCP_SKB_CB(skb)->end_seq,   tp->frto_highmark))TCP_SKB_CB(skb)->sacked |= TCPCB_LOST;tp->lost_out += tcp_skb_pcount(skb);}} else { //被SACK确认过tp->sacked_out += tcp_skb_pcount(skb);tp->fackets_out = cnt;}}tcp_sync_left_out(tp);/* 看看之前在FRTO期间发了几个包,如果frto_counter = 1的话,表示收到了一个确认,但是没有发新包;如果frto_counter = 2的话,收到了2个确认,没有新包; 如果frto_counter = 0的话,表示收到了2个确认,发了2个新包。所以 + frto_counter会使得in_flight的数目保持不变。*/tp->snd_cwnd = tp->frto_counter + tcp_packets_in_flight(tp)+1;tp->snd_cwnd_cnt = 0;tp->snd_cwnd_stamp = tcp_time_stamp;tp->undo_marker = 0; /* 不能撤销窗口调整*/tp->frto_counter = 0;tp->reordering = min_t(unsigned int, tp->reordering,     sysctl_tcp_reordering);tcp_set_ca_state(sk, TCP_CA_Loss);tp->high_seq = tp->frto_highmark; //进入FRTO是的snd_nxtTCP_ECN_queue_cwr(tp);clear_all_retrans_hints(tp); //清空所有的重传提示,重传的时候从write_queue的起点开始遍历}






0 0
原创粉丝点击