本文主要是介绍Reno与RACK对丢失/重传报文的标记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
主要涉及到两个变量,一是重传报文计数retrans_out;二是丢失报文计数lost_out。
RACK丢失报文判断
如下函数tcp_rack_detect_loss,如果报文具有丢失标志(TCPCB_LOST),但是没有重传标志(TCPCB_SACKED_RETRANS),表明丢失报文还未进行重传,不进行重复处理。否则,在RACK确认报文已经丢失之后,由函数tcp_mark_skb_lost进行丢失标记。
static void tcp_rack_detect_loss(struct sock *sk, u32 *reo_timeout)
{*reo_timeout = 0;reo_wnd = tcp_rack_reo_wnd(sk);list_for_each_entry_safe(skb, n, &tp->tsorted_sent_queue, tcp_tsorted_anchor) {struct tcp_skb_cb *scb = TCP_SKB_CB(skb);/* Skip ones marked lost but not yet retransmitted */if ((scb->sacked & TCPCB_LOST) &&!(scb->sacked & TCPCB_SACKED_RETRANS))continue;if (!tcp_rack_sent_after(tp->rack.mstamp,tcp_skb_timestamp_us(skb),tp->rack.end_seq, scb->end_seq))break;/* A packet is lost if it has not been s/acked beyond* the recent RTT plus the reordering window.*/remaining = tcp_rack_skb_timeout(tp, skb, reo_wnd);if (remaining <= 0) {tcp_mark_skb_lost(sk, skb);
在函数tcp_mark_skb_lost中,如果重传报文(标志TCPCB_SACKED_RETRANS)丢失,清除其重传标志TCPCB_SACKED_RETRANS,以便进行下一次重传。在以上tcp_rack_detect_loss丢失检测中,可见,对此种丢失未重传报文不进行重复处理。
注意,这里对重传丢失的报文,减少重传报文计数retrans_out。以下将会看到Reno在标记丢包时,不处理retrans_out计数。
void tcp_mark_skb_lost(struct sock *sk, struct sk_buff *skb)
{struct tcp_sock *tp = tcp_sk(sk);tcp_skb_mark_lost_uncond_verify(tp, skb);if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) {TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS;tp->retrans_out -= tcp_skb_pcount(skb);
与Reno不同,RACK(或者SACK)可标记重传队列中多个报文为丢失状态(TCPCB_LOST),Reno仅能表示重传队列首个报文。因而,在重传时,RACK(或SACK)也可重传多个丢失的报文。参见以下重传函数tcp_xmit_retransmit_queue中的判断,retrans_out不能大于lost_out,不能重传大于丢失数量的报文。
void tcp_xmit_retransmit_queue(struct sock *sk)
{rtx_head = tcp_rtx_queue_head(sk);skb = tp->retransmit_skb_hint ?: rtx_head;max_segs = tcp_tso_segs(sk, tcp_current_mss(sk));skb_rbtree_walk_from(skb) {...segs = tp->snd_cwnd - tcp_packets_in_flight(tp);if (segs <= 0) return;sacked = TCP_SKB_CB(skb)->sacked;/* In case tcp_shift_skb_data() have aggregated large skbs,* we need to make sure not sending too bigs TSO packets*/segs = min_t(int, segs, max_segs);if (tp->retrans_out >= tp->lost_out) {break; ...if (tcp_retransmit_skb(sk, skb, segs)) return;
Reno丢包计数
以下为Reno算法的丢包标记函数tcp_newreno_mark_lost,与RACK(或者SACK)不同,Reno没有足够的信息判断多个报文的丢失情况,根据重复ACK(dupack)仅能判断,SND.UNA序号开始的报文(重传队列首报文)丢失。所以,如果重传队列首个skb,包含多个报文,或者其长度大于MSS值,进行分片,TCPCB_LOST丢包标志只能设置于第一个长度不大于MSS的报文。
这里也就不需要像以上的RACK算法,或者SACK算法遍历重传队列,Reno调用tcp_skb_mark_lost_uncond_verify函数标记重传队列首报文(可能是分片后报文)丢失即可。
另外Reno与RACK/SACK的不同在于,后者在tcp_mark_skb_lost函数中检测重传报文的丢失,并且减少重传报文计数,而Reno不进行处理。Reno仅在接收到确认ACK(无论是确认原始还是重传报文)时将retrans_out减一。
void tcp_newreno_mark_lost(struct sock *sk, bool snd_una_advanced)
{ const u8 state = inet_csk(sk)->icsk_ca_state;struct tcp_sock *tp = tcp_sk(sk);if ((state < TCP_CA_Recovery && tp->sacked_out >= tp->reordering) ||(state == TCP_CA_Recovery && snd_una_advanced)) {struct sk_buff *skb = tcp_rtx_queue_head(sk);u32 mss;if (TCP_SKB_CB(skb)->sacked & TCPCB_LOST)return;mss = tcp_skb_mss(skb);if (tcp_skb_pcount(skb) > 1 && skb->len > mss)tcp_fragment(sk, TCP_FRAG_IN_RTX_QUEUE, skb,mss, mss, GFP_ATOMIC);tcp_skb_mark_lost_uncond_verify(tp, skb);
如下tcp_skb_mark_lost_uncond_verify,如果报文没有被确认丢失并且也没有被对端SACK确认接收,增加lost_out丢包计数,并且设置标志TCPCB_LOST。对于Reno而言,不可能被SACK确认,并且在以上函数tcp_newreno_mark_lost中,也已经确认了报文还没有设置TCPCB_LOST标志,所以这里的if判断必定成立。
另外,由于Reno仅标记一个报文,lost_out值递增1。而且在以上函数中判断只要首报文被设置了TCPCB_LOST标志,不继续重复标记,因而,lost_out的值实际上总是1。
void tcp_skb_mark_lost_uncond_verify(struct tcp_sock *tp, struct sk_buff *skb)
{ tcp_verify_retransmit_hint(tp, skb);tcp_sum_lost(tp, skb); if (!(TCP_SKB_CB(skb)->sacked & (TCPCB_LOST|TCPCB_SACKED_ACKED))) {tp->lost_out += tcp_skb_pcount(skb);TCP_SKB_CB(skb)->sacked |= TCPCB_LOST;
接下来看一看Reno的丢失计数lost_out的减少。在接收到ACK确认报文后,清理重传队列函数tcp_clean_rtx_queue中,如果检测到报文设置了TCPCB_LOST标志,将丢包计数lost_out减去相应的报文数量。对于Reno而言,仅有可能在重传队列的首报文中设置TCPCB_LOST标志。由于在设置TCPCB_LOST标志时,确保了重传队列第一个skb仅包含一个报文,这里lost_out相当于递减1,其值变为0。
注意,由于被对端确认接收,此报文将由重传队列中移除,这样队列中的下一个报文(成为首报文)才有可能被标记为TCPCB_LOST,参见以上函数tcp_newreno_mark_lost。
static int tcp_clean_rtx_queue(struct sock *sk, u32 prior_fack,u32 prior_snd_una, struct tcp_sacktag_state *sack)
{for (skb = skb_rb_first(&sk->tcp_rtx_queue); skb; skb = next) {struct tcp_skb_cb *scb = TCP_SKB_CB(skb);u8 sacked = scb->sacked;if (after(scb->end_seq, tp->snd_una)) {...} else {acked_pcount = tcp_skb_pcount(skb);}if (sacked & TCPCB_LOST)tp->lost_out -= acked_pcount;
Reno重传计数
在报文重传函数tcp_retransmit_skb中,重传成功之后,更新重传计数,由于在上节介绍的tcp_newreno_mark_lost函数中,将重传队列首个skb分片为单报文,此处retrans_out相当于递增1。并且为报文增加重传标志TCPCB_RETRANS。
int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs)
{struct tcp_sock *tp = tcp_sk(sk);int err = __tcp_retransmit_skb(sk, skb, segs);if (err == 0) {
#if FASTRETRANS_DEBUG > 0if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) {net_dbg_ratelimited("retrans_out leaked\n");}
#endifTCP_SKB_CB(skb)->sacked |= TCPCB_RETRANS;tp->retrans_out += tcp_skb_pcount(skb);
在以上函数tcp_retransmit_skb的调用函数tcp_xmit_retransmit_queue中,如果重传的报文数量retrans_out大于等于丢失报文数量lost_out,停止遍历重传队列。对于Reno,丢失报文计数lost_out为1,仅允许重传一个报文。此后,retrans_out的值也增加为1。
void tcp_xmit_retransmit_queue(struct sock *sk)
{rtx_head = tcp_rtx_queue_head(sk);skb = tp->retransmit_skb_hint ?: rtx_head;max_segs = tcp_tso_segs(sk, tcp_current_mss(sk));skb_rbtree_walk_from(skb) {...segs = tp->snd_cwnd - tcp_packets_in_flight(tp);if (segs <= 0) return;sacked = TCP_SKB_CB(skb)->sacked;/* In case tcp_shift_skb_data() have aggregated large skbs,* we need to make sure not sending too bigs TSO packets*/segs = min_t(int, segs, max_segs);if (tp->retrans_out >= tp->lost_out) {break; ...if (tcp_retransmit_skb(sk, skb, segs)) return;
在接收到ACK确认报文后,清理重传队列函数tcp_clean_rtx_queue中,如果报文设置了重传标志TCPCB_RETRANS,将retrans_out减少相应的报文数量,对于Reno而言,相当于递减1。同时在此函数中,也将丢失报文数量lost_out减去1。
static int tcp_clean_rtx_queue(struct sock *sk, u32 prior_fack,u32 prior_snd_una, struct tcp_sacktag_state *sack)
{for (skb = skb_rb_first(&sk->tcp_rtx_queue); skb; skb = next) {struct tcp_skb_cb *scb = TCP_SKB_CB(skb);u8 sacked = scb->sacked;if (after(scb->end_seq, tp->snd_una)) {...} else {acked_pcount = tcp_skb_pcount(skb);}if (unlikely(sacked & TCPCB_RETRANS)) {if (sacked & TCPCB_SACKED_RETRANS)tp->retrans_out -= acked_pcount;flag |= FLAG_RETRANS_DATA_ACKED;...if (sacked & TCPCB_LOST)tp->lost_out -= acked_pcount;
内核版本 5.0
这篇关于Reno与RACK对丢失/重传报文的标记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!