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,
			       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 */
			//预期SKB
			prefetchw(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_ackt
			last_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.mstamp
			if (!tcp_skb_spurious_retrans(tp, skb))
				tcp_rack_advance(tp, &skb->skb_mstamp, sacked);
		}

		//如果当前处理的SKB被判定为丢失,那么在移除之际需要更新tp->lost_out
		if (sacked & TCPCB_LOST)
			tp->lost_out -= acked_pcount;

		//更新packets_out
		tp->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 > 0
	WARN_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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值