tcp_clean_rtx_queue函数

来源:互联网 发布:最新家暴数据 编辑:程序博客网 时间:2024/05/18 01:31
/* 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,       u32 prior_snd_una, int *acked,       struct tcp_sacktag_state *sack,       struct skb_mstamp *now){const struct inet_connection_sock *icsk = inet_csk(sk);struct skb_mstamp first_ackt, last_ackt;struct tcp_sock *tp = tcp_sk(sk);u32 prior_sacked = tp->sacked_out;u32 reord = tp->packets_out;bool fully_acked = true;long sack_rtt_us = -1L;long seq_rtt_us = -1L;long ca_rtt_us = -1L;struct sk_buff *skb;u32 pkts_acked = 0;u32 last_in_flight = 0;bool rtt_update;int flag = 0;first_ackt.v64 = 0;//tcp_send_head表示下一个将要发送的数据,还没有被发送while ((skb = tcp_write_queue_head(sk)) && skb != tcp_send_head(sk)) {struct tcp_skb_cb *scb = TCP_SKB_CB(skb);u8 sacked = scb->sacked;u32 acked_pcount;tcp_ack_tstamp(sk, skb, prior_snd_una);/* Determine how many packets and what bytes were acked, tso and else *///scb->end_seq在UNA之前表示整个skb都被应答了(不管是大包还是小包)//scb->end_seq在UNA之后表示整个SKB部分数据被应答了,TSO的情况if (after(scb->end_seq, tp->snd_una)) {//如果是大包那么tcp_gso_segs肯定不为1//UNA在skb起始序号之前//上面两种情况都属于异常情况直接break终止报文处理,也即这种报文下载在处理if (tcp_skb_pcount(skb) == 1 ||    !after(tp->snd_una, scb->seq))break;//大包的时候检查应答了多少个报文acked_pcount = tcp_tso_acked(sk, skb);if (!acked_pcount)break;//fully_acked表示SKB整包是否被应答fully_acked = false;} else {/* Speedup tcp_unlink_write_queue() and next loop *///预期SKBprefetchw(skb->next);acked_pcount = tcp_skb_pcount(skb);}//进入这里表示这个SKB已经被应答了//当前处理的SKB曾经被重传过if (unlikely(sacked & TCPCB_RETRANS)) {//并且是被SACK触发的重传if (sacked & TCPCB_SACKED_RETRANS)//当前ACK应答了被SACK触发重传的报文,所以需要更新tp->retrans_out计数器tp->retrans_out -= acked_pcount;//应答了重传的报文flag |= FLAG_RETRANS_DATA_ACKED;//当前ACK应答了没有被重传过的报文,也没不是被SACK应答,也即是正常ACK顺序应答} else if (!(sacked & TCPCB_SACKED_ACKED)) {//处理被正顺序应答的报文需要更新乱序数据包量统计reord和更新last_acktlast_ackt = skb->skb_mstamp;WARN_ON_ONCE(last_ackt.v64 == 0);if (!first_ackt.v64)first_ackt = last_ackt;last_in_flight = TCP_SKB_CB(skb)->tx.in_flight;//数据包被正顺序应答,所以可以减少乱序计数reord = min(pkts_acked, reord);if (!after(scb->end_seq, tp->high_seq))flag |= FLAG_ORIG_SACK_ACKED;}//当前处理的报文曾经是被SACK应大过if (sacked & TCPCB_SACKED_ACKED) {//sacked_out更新,tp->sacked_out记录当前SACK了多少报文,当前SACK的报文被移除时计数需要更新tp->sacked_out -= acked_pcount;} else if (tcp_is_sack(tp)) {tp->delivered += acked_pcount;//没有打开TIMSTAMP,此处必然被执行//如果是sack应答数据不会更新rack.mstampif (!tcp_skb_spurious_retrans(tp, skb))tcp_rack_advance(tp, &skb->skb_mstamp, sacked);}//如果当前处理的SKB被判定为丢失,那么在移除之际需要更新tp->lost_outif (sacked & TCPCB_LOST)tp->lost_out -= acked_pcount;//更新packets_outtp->packets_out -= acked_pcount;//pkts_acked记录当前ACK应答触发的tcp_clean_rtx_queue函数调用一共处理了多少数据pkts_acked += acked_pcount;//通过ACK应答情况计算发送速率tcp_rate_skb_delivered(sk, skb, sack->rate);/* Initial outgoing SYN's get put onto the write_queue * just like anything else we transmit.  It is not * true data, and if we misinform our callers that * this ACK acks real data, we will erroneously exit * connection startup slow start one packet too * quickly.  This is severely frowned upon behavior. */ //区分是DATA应答还是SYN应答if (likely(!(scb->tcp_flags & TCPHDR_SYN))) {flag |= FLAG_DATA_ACKED;} else {flag |= FLAG_SYN_ACKED;tp->retrans_stamp = 0;}//描述当前处理的大包是被整包应答还是部分应答if (!fully_acked)break;//将SKB从发送队列中摘除tcp_unlink_write_queue(skb, sk);//释放SKB的内存空间sk_wmem_free_skb(sk, skb);//下一个将要被重传的报文是当前处理的SKB,那么当前是顺序应答将retransmit_skb_hint置空//tp->retransmit_skb_hint描述的是下一次重传的其实位置if (unlikely(skb == tp->retransmit_skb_hint))tp->retransmit_skb_hint = NULL;//tp->lost_skb_hint描述的是下一次丢包标记的其实位置if (unlikely(skb == tp->lost_skb_hint))tp->lost_skb_hint = NULL;}if (likely(between(tp->snd_up, prior_snd_una, tp->snd_una)))tp->snd_up = tp->snd_una;if (skb && (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED))flag |= FLAG_SACK_RENEGING;if (likely(first_ackt.v64) && !(flag & FLAG_RETRANS_DATA_ACKED)) {seq_rtt_us = skb_mstamp_us_delta(now, &first_ackt);ca_rtt_us = skb_mstamp_us_delta(now, &last_ackt);}if (sack->first_sackt.v64) {sack_rtt_us = skb_mstamp_us_delta(now, &sack->first_sackt);ca_rtt_us = skb_mstamp_us_delta(now, &sack->last_sackt);}sack->rate->rtt_us = ca_rtt_us; /* RTT of last (S)ACKed packet, or -1 */rtt_update = tcp_ack_update_rtt(sk, flag, seq_rtt_us, sack_rtt_us,ca_rtt_us);if (flag & FLAG_ACKED) {tcp_rearm_rto(sk);if (unlikely(icsk->icsk_mtup.probe_size &&     !after(tp->mtu_probe.probe_seq_end, tp->snd_una))) {tcp_mtup_probe_success(sk);}if (tcp_is_reno(tp)) {tcp_remove_reno_sacks(sk, pkts_acked);} else {int delta;/* Non-retransmitted hole got filled? That's reordering */if (reord < prior_fackets)tcp_update_reordering(sk, tp->fackets_out - reord, 0);delta = tcp_is_fack(tp) ? pkts_acked :  prior_sacked - tp->sacked_out;tp->lost_cnt_hint -= min(tp->lost_cnt_hint, delta);}tp->fackets_out -= min(pkts_acked, tp->fackets_out);} else if (skb && rtt_update && sack_rtt_us >= 0 &&   sack_rtt_us > skb_mstamp_us_delta(now, &skb->skb_mstamp)) {/* Do not re-arm RTO if the sack RTT is measured from data sent * after when the head was last (re)transmitted. Otherwise the * timeout may continue to extend in loss recovery. */tcp_rearm_rto(sk);}if (icsk->icsk_ca_ops->pkts_acked) {struct ack_sample sample = { .pkts_acked = pkts_acked,     .rtt_us = ca_rtt_us,     .in_flight = last_in_flight };icsk->icsk_ca_ops->pkts_acked(sk, &sample);}#if FASTRETRANS_DEBUG > 0WARN_ON((int)tp->sacked_out < 0);WARN_ON((int)tp->lost_out < 0);WARN_ON((int)tp->retrans_out < 0);if (!tp->packets_out && tcp_is_sack(tp)) {icsk = inet_csk(sk);if (tp->lost_out) {pr_debug("Leak l=%u %d\n", tp->lost_out, icsk->icsk_ca_state);tp->lost_out = 0;}if (tp->sacked_out) {pr_debug("Leak s=%u %d\n", tp->sacked_out, icsk->icsk_ca_state);tp->sacked_out = 0;}if (tp->retrans_out) {pr_debug("Leak r=%u %d\n", tp->retrans_out, icsk->icsk_ca_state);tp->retrans_out = 0;}}#endif*acked = pkts_acked;return flag;}

0 0