kk Blog —— 通用基础


date [-d @int|str] [+%s|"+%F %T"]
netstat -ltunp
sar -n DEV 1

tcp重传数据包 tcp_retransmit_skb 函数

http://blog.csdn.net/shanshanpt/article/details/22202999

基于CentOS6.5 2.6.32-504.16.2.el6.x86_64

tcp_retransmit_skb 重传数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/* This retransmits one SKB.  Policy decisions and retransmit queue
 * state updates are done by the caller.  Returns non-zero if an
 * error occurred which prevented the send.
 */ // 如果消耗很多的内存做其他事,那么就没有多余的来做队列的处理了
int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
{
	struct tcp_sock *tp = tcp_sk(sk);
	struct inet_connection_sock *icsk = inet_csk(sk);
	unsigned int cur_mss;
	int err;

	/* Inconslusive MTU probe */
	if (icsk->icsk_mtup.probe_size) {
		icsk->icsk_mtup.probe_size = 0;
	}

	/* Do not sent more than we queued. 1/4 is reserved for possible
	 * copying overhead: fragmentation, tunneling, mangling etc.
	 */
	if (atomic_read(&sk->sk_wmem_alloc) >                                    // sk_wmem_alloc:传输队列大小
		min(sk->sk_wmem_queued + (sk->sk_wmem_queued >> 2), sk->sk_sndbuf))  // sk_wmem_queud:固定的队列大小
		return -EAGAIN;

	if (before(TCP_SKB_CB(skb)->seq, tp->snd_una)) {         // 若这样,说明是有一部分数据才需要重传,形如:seq---snd_una---end_seq,前面一半已收到ACK
		if (before(TCP_SKB_CB(skb)->end_seq, tp->snd_una))   // 若这样,说明全部ACK,无需重传,BUG
			BUG();
		if (tcp_trim_head(sk, skb, tp->snd_una - TCP_SKB_CB(skb)->seq))      // 将无须重传的部分去掉
			return -ENOMEM;
	}

	if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk))
		return -EHOSTUNREACH; /* Routing failure or similar. */

	cur_mss = tcp_current_mss(sk);

	/* If receiver has shrunk his window, and skb is out of
	 * new window, do not retransmit it. The exception is the
	 * case, when window is shrunk to zero. In this case
	 * our retransmit serves as a zero window probe.
	 */
	if (!before(TCP_SKB_CB(skb)->seq, tcp_wnd_end(tp))       // 如果数据在窗口后面,不会发送
		&& TCP_SKB_CB(skb)->seq != tp->snd_una)
		return -EAGAIN;
	if (skb->len > cur_mss) {                                // 如果skb长度 > MSS
		if (tcp_fragment(sk, skb, cur_mss, cur_mss))         // 先分片,并调整packet_out等统计值。再传送
			return -ENOMEM; /* We'll try again later. */
	} else {
		int oldpcount = tcp_skb_pcount(skb);

		if (unlikely(oldpcount > 1)) {
			tcp_init_tso_segs(sk, skb, cur_mss);             // 按当前mss重置skb->gso_XXX
			tcp_adjust_pcount(sk, skb, oldpcount - tcp_skb_pcount(skb)); // 调整packet_out等统计值
		}
	}

	tcp_retrans_try_collapse(sk, skb, cur_mss);              // 尝试和后几个包合并后一起重传出去,加快速度

	/* Some Solaris stacks overoptimize and ignore the FIN on a
	 * retransmit when old data is attached.  So strip it off
	 * since it is cheap to do so and saves bytes on the network.
	 */ //Solaris系统的协议栈有时候会忽略重传SKB上带有的FIN标志的payload,将payload全部剥离掉,节省网络流量
	if (skb->len > 0 &&
		(TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) &&
		tp->snd_una == (TCP_SKB_CB(skb)->end_seq - 1)) {
		if (!pskb_trim(skb, 0)) {
			/* Reuse, even though it does some unnecessary work */
			tcp_init_nondata_skb(skb, TCP_SKB_CB(skb)->end_seq - 1,
						 TCP_SKB_CB(skb)->tcp_flags);
			skb->ip_summed = CHECKSUM_NONE;
		}
	}

	/* Make a copy, if the first transmission SKB clone we made
	 * is still in somebody's hands, else make a clone.
	 */
	TCP_SKB_CB(skb)->when = tcp_time_stamp;

	/* make sure skb->data is aligned on arches that require it
	 * and check if ack-trimming & collapsing extended the headroom
	 * beyond what csum_start can cover.
	 */
	if (unlikely((NET_IP_ALIGN && ((unsigned long)skb->data & 3)) ||
			 skb_headroom(skb) >= 0xFFFF)) {
		struct sk_buff *nskb = __pskb_copy(skb, MAX_TCP_HEADER,
						   GFP_ATOMIC);
		err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) :
				 -ENOBUFS;
	} else {
		err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);     // 这个才是正在的传输函数
	}

	if (err == 0) {                                         // 发送成功,那么就需要更新TCP统计信息
		/* Update global TCP statistics. */
		TCP_INC_STATS(sock_net(sk), TCP_MIB_RETRANSSEGS);

		tp->total_retrans++;                                // 整体重传数量++

#if FASTRETRANS_DEBUG > 0
		if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) {
			if (net_ratelimit())
				printk(KERN_DEBUG "retrans_out leaked.\n");
		}
#endif
		if (!tp->retrans_out)
			tp->lost_retrans_low = tp->snd_nxt;
		TCP_SKB_CB(skb)->sacked |= TCPCB_RETRANS;
		tp->retrans_out += tcp_skb_pcount(skb);             // 重传出去的数量+=。。。

		/* Save stamp of the first retransmit. */
		if (!tp->retrans_stamp)
			tp->retrans_stamp = TCP_SKB_CB(skb)->when;      // 第一次重传时间戳

		tp->undo_retrans += tcp_skb_pcount(skb);

		/* snd_nxt is stored to detect loss of retransmitted segment,
		 * see tcp_input.c tcp_sacktag_write_queue().
		 */
		TCP_SKB_CB(skb)->ack_seq = tp->snd_nxt;
	}
	return err;
}

tcp_retrans_try_collapse 重传时尝试和后几个包合并后传出去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// 只做简单合并,所以条件设置严格
/* Check if coalescing SKBs is legal. */
static int tcp_can_collapse(struct sock *sk, struct sk_buff *skb)
{
	if (tcp_skb_pcount(skb) > 1)         // skb只包含一个数据包,没有TSO分包
		return 0;
	/* TODO: SACK collapsing could be used to remove this condition */
	if (skb_shinfo(skb)->nr_frags != 0)  // 数据都在线性空间,非线性空间中没有数据
		return 0;
	if (skb_cloned(skb))                 // 不是clone
		return 0;
	if (skb == tcp_send_head(sk))
		return 0;
	/* Some heurestics for collapsing over SACK'd could be invented */
	if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)  // 已经被sack的当然不用重传
		return 0;

	return 1;
}

/* Collapse packets in the retransmit queue to make to create
 * less packets on the wire. This is only done on retransmission.
 */
static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *to,
					 int space)
{
	struct tcp_sock *tp = tcp_sk(sk);
	struct sk_buff *skb = to, *tmp;
	int first = 1;

	if (!sysctl_tcp_retrans_collapse)
		return;
	if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_SYN)  // SYN包不合并
		return;

	tcp_for_write_queue_from_safe(skb, tmp, sk) {
		if (!tcp_can_collapse(sk, skb))           // 要和并的包判断是否符合条件
			break;

		space -= skb->len;

		if (first) {
			first = 0;
			continue;
		}

		if (space < 0)
			break;
		/* Punt if not enough space exists in the first SKB for
		 * the data in the second
		 */
		if (skb->len > skb_tailroom(to))          // 第一个包的tailroom空间足够容下该包
			break;

		if (after(TCP_SKB_CB(skb)->end_seq, tcp_wnd_end(tp))) // 大于窗口不合并
			break;

		tcp_collapse_retrans(sk, to);             // 进行两个包的合并
	}
}

tcp_collapse_retrans 重传合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/* Collapses two adjacent SKB's during retransmission. */
static void tcp_collapse_retrans(struct sock *sk, struct sk_buff *skb)
{
	struct tcp_sock *tp = tcp_sk(sk);
	struct sk_buff *next_skb = tcp_write_queue_next(sk, skb);
	int skb_size, next_skb_size;

	skb_size = skb->len;
	next_skb_size = next_skb->len;

	BUG_ON(tcp_skb_pcount(skb) != 1 || tcp_skb_pcount(next_skb) != 1);

	tcp_highest_sack_combine(sk, next_skb, skb);

	tcp_unlink_write_queue(next_skb, sk);    // 将要合并的包从队列中删掉

	skb_copy_from_linear_data(next_skb, skb_put(skb, next_skb_size),
				  next_skb_size);            // 将数据copy到前一个包上,调整前一个的len,tail

	if (next_skb->ip_summed == CHECKSUM_PARTIAL)
		skb->ip_summed = CHECKSUM_PARTIAL;

	if (skb->ip_summed != CHECKSUM_PARTIAL)
		skb->csum = csum_block_add(skb->csum, next_skb->csum, skb_size);

	/* Update sequence range on original skb. */
	TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(next_skb)->end_seq;  // end_seq 等于后一个包的end_seq,所以如果skb->end_seq > next_skb->seq,就会合并出一个len>end_seq-seq的异常数据(内核保证了sk_write_queue不会出现这情况)

	/* Merge over control information. This moves PSH/FIN etc. over */
	TCP_SKB_CB(skb)->tcp_flags |= TCP_SKB_CB(next_skb)->tcp_flags;

	/* All done, get rid of second SKB and account for it so
	 * packet counting does not break.
	 */
	TCP_SKB_CB(skb)->sacked |= TCP_SKB_CB(next_skb)->sacked & TCPCB_EVER_RETRANS;

	/* changed transmit queue under us so clear hints */
	tcp_clear_retrans_hints_partial(tp);
	if (next_skb == tp->retransmit_skb_hint)
		tp->retransmit_skb_hint = skb;

	tcp_adjust_pcount(sk, next_skb, tcp_skb_pcount(next_skb)); // 调整pcount

	sk_wmem_free_skb(sk, next_skb);        // 合并到了前一个包上,所以释放这个包
}

拥塞避免处理函数 tcp_reno_cong_avoid

http://blog.csdn.net/shanshanpt/article/details/22201847

慢启动和快速重传拥塞避免算法,函数tcp_reno_cong_avoid
在“慢开始”阶段,每收到一个ACK,cwnd++一次,那么一个RTT之后,cwnd就会加倍
拥塞避免阶段,其实就是在一个RTT时间内将cwnd++一次( 注意在不丢包的情况下 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*
 * TCP Reno congestion control
 * This is special case used for fallback as well.
 */
/* This is Jacobson's slow start and congestion avoidance.
 * SIGCOMM '88, p. 328.
 */
void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
{
	struct tcp_sock *tp = tcp_sk(sk);         // 获取tcp_sock
	// 函数返回1说明拥塞窗口被限制,我们需要增加拥塞窗口,否则的话,就不需要增加拥塞窗口。
	if (!tcp_is_cwnd_limited(sk, in_flight))  // 是否已经达到拥塞窗口的限制值(1)
		return;

	/* In "safe" area, increase. */
	if (tp->snd_cwnd <= tp->snd_ssthresh)     // 如果发送窗口大小还 比 慢开始门限小,那么还是慢开始处理
		tcp_slow_start(tp);                   // 下面进入慢开始处理 (2)
	/* In dangerous area, increase slowly. */
	else if (sysctl_tcp_abc) {                // 否则进入拥塞避免阶段!!每个RTT时间就加1
		/* RFC3465: Appropriate Byte Count
		 * increase once for each full cwnd acked              // 基本思想就是:经过一个RTT时间就将snd_cwnd增加一个单位!
		 */                                                    // 一个RTT时间可以认为是当前拥塞窗口发送出去的数据的所有ACK都被接收到
		if (tp->bytes_acked >= tp->snd_cwnd*tp->mss_cache) {   // 当前的拥塞窗口的所有段都被ack了,窗口才被允许增加。
			tp->bytes_acked -= tp->snd_cwnd*tp->mss_cache;     // ACK处理过的及删除去了
			if (tp->snd_cwnd < tp->snd_cwnd_clamp)             // 不允许发送窗口大小超过snd_cwnd_clamp值
				tp->snd_cwnd++;
		}
	} else {                                       // 每接收到一个ACK,窗口增大(1/snd_cwnd),使用cnt计数
		/* In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd */
		if (tp->snd_cwnd_cnt >= tp->snd_cwnd) {    // 线性增长计数器 >= 阈值
			if (tp->snd_cwnd < tp->snd_cwnd_clamp) // 如果窗口还没有达到阈值
				tp->snd_cwnd++;                    // 那么++增大窗口
			tp->snd_cwnd_cnt = 0;
		} else
			tp->snd_cwnd_cnt++;                    // 否则仅仅是增大线性递增计数器
	}
}

下面看一下“慢开始”算法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void tcp_slow_start(struct tcp_sock *tp)           // 每到达一个ACK,snd_cwnd就加1。这意味着每个RTT,拥塞窗口就会翻倍。
{
	int cnt; /* increase in packets */

	/* RFC3465: ABC Slow start
	 * Increase only after a full MSS of bytes is acked
	 *
	 * TCP sender SHOULD increase cwnd by the number of
	 * previously unacknowledged bytes ACKed by each incoming
	 * acknowledgment, provided the increase is not more than L
	 */
	if (sysctl_tcp_abc && tp->bytes_acked < tp->mss_cache)                     // 如果ack确认的数据少于一个MSS大小,不需要增大窗口
		return;
	// 限制cnt的值
	if (sysctl_tcp_max_ssthresh > 0 && tp->snd_cwnd > sysctl_tcp_max_ssthresh) // 发送窗口超过最大门限值
		cnt = sysctl_tcp_max_ssthresh >> 1;     /* limited slow start */       // 窗口减半~~~~~
	else
		cnt = tp->snd_cwnd;          /* exponential increase */                // 否则还是原来的窗口

	/* RFC3465: ABC
	 * We MAY increase by 2 if discovered delayed ack
	 */
	if (sysctl_tcp_abc > 1 && tp->bytes_acked >= 2*tp->mss_cache) // 如果启动了延迟确认,那么当接收到的ACK大于等于两个MSS的时候才加倍窗口大小
		cnt <<= 1;
	tp->bytes_acked = 0;  // 清空

	tp->snd_cwnd_cnt += cnt;
	while (tp->snd_cwnd_cnt >= tp->snd_cwnd) {  // 这里snd_cwnd_cnt是snd_cwnd的几倍,拥塞窗口就增加几。
		tp->snd_cwnd_cnt -= tp->snd_cwnd;       // ok
		if (tp->snd_cwnd < tp->snd_cwnd_clamp)  // 判断窗口大小
			tp->snd_cwnd++;  // + +
	}
}

最后看一下这个函数:tcp_is_cwnd_limited,基本的意思就是判断需不需要增大拥塞窗口。

关于gso:主要功能就是尽量的延迟数据包的传输,以便与在最恰当的时机传输数据包。如果支持gso,就有可能是tso 延迟了数据包,因此这里会进行几个相关的判断,来看需不需要增加拥塞窗口。

关于burst:主要用来控制网络流量的突发性增大,也就是说当left数据(还能发送的数据段数)大于burst值的时候,我们需要暂时停止增加窗口,因为此时有可能我们这边数据发送过快。其实就是一个平衡权值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight)  // 第二个参数是正在网络中传输,还没有收到确认的报数量
{
	const struct tcp_sock *tp = tcp_sk(sk);
	u32 left;

	if (in_flight >= tp->snd_cwnd)    // 比较发送未确认和发送拥塞窗口的大小
		return 1;                     // 如果未确认的大,那么需要增大拥塞窗口

	if (!sk_can_gso(sk))              // 如果没有gso延时处理所有包,不需要增大窗口
		return 0;

	left = tp->snd_cwnd - in_flight;  // 得到还能发送的数据包的数量
	if (sysctl_tcp_tso_win_divisor)
		return left * sysctl_tcp_tso_win_divisor < tp->snd_cwnd;
	else
		return left <= tcp_max_burst(tp); // 如果还可以发送的数量>burst,说明发送太快,不需要增大窗口。
}

清理重传队列中函数 tcp_clean_rtx_queue

http://blog.csdn.net/shanshanpt/article/details/22194029

如果重传队列中的一些数据已经被确认,那么, 需要从重传队列中清除出去,需要使用这个函数:tcp_clean_rtx_queue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/* 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)
{
	struct tcp_sock *tp = tcp_sk(sk);   // 获得tcp_sock
	const struct inet_connection_sock *icsk = inet_csk(sk); // 获得连接sock
	struct sk_buff *skb;
	u32 now = tcp_time_stamp;           // 当前时间,用于计算RTT
	int fully_acked = 1;                // 表示数据段是否完全被确认
	int flag = 0;
	u32 pkts_acked = 0;
	u32 reord = tp->packets_out;        // 发送出去,还在网络上跑,但是还没有被确认的数据包们
	s32 seq_rtt = -1;
	s32 ca_seq_rtt = -1;
	ktime_t last_ackt = net_invalid_timestamp();    // 把last_ackt设置位0
	// 下面就是遍历sk_write_queue队列,遇到snd_una就停止,如果没有更新过,开始就直接退出了
	while ((skb = tcp_write_queue_head(sk)) && skb != tcp_send_head(sk)) {
		struct tcp_skb_cb *scb = TCP_SKB_CB(skb);   // 获得这个重传队列的一个skb的cb字段
		u32 end_seq;
		u32 acked_pcount;
		u8 sacked = scb->sacked;

		/* Determine how many packets and what bytes were acked, tso and else */
		if (after(scb->end_seq, tp->snd_una)) {     // 注意这个scb是我们发出去的数据的skb中的一个scb哦!,不是接受到的数据!小心
			if (tcp_skb_pcount(skb) == 1 ||         // 这里的意思就是发出去的数据最后一个字节在已经确认的snd_una之后,说明还有没有确认的字节
			!after(tp->snd_una, scb->seq))          // 如果没有设置了TSO 或者 seq不在snd_una之前,即不是 seq---snd_una---end_seq这样情况
				break;                              // 那么说明没有必要把重传元素去掉,(如果是seq---snd_una---end_seq)那么前面半部分的就可以从队列中删除!!!

			acked_pcount = tcp_tso_acked(sk, skb);  // 如果只确认了TSO段中的一部分,则从skb删除已经确认的segs,并统计确认了多少段( 1 )
			if (!acked_pcount)                      // 处理出错
				break;

			fully_acked = 0;                        // 表示TSO只处理了一部分,其他还没处理完
			end_seq = tp->snd_una;
		} else {
			acked_pcount = tcp_skb_pcount(skb);     // 即 !after(scb->end_seq, tp->snd_una),说明已经完全确认OK!
			end_seq = scb->end_seq;
		}

		/* MTU probing checks */
		if (fully_acked && icsk->icsk_mtup.probe_size &&      // 探测mtu,暂时不多说
		!after(tp->mtu_probe.probe_seq_end, scb->end_seq)) {
			tcp_mtup_probe_success(sk, skb);
		}
		// 下面通过sack的信息得到这是一个被重传的过包
		if (sacked & TCPCB_RETRANS) {
			if (sacked & TCPCB_SACKED_RETRANS)      // 如果之前重传过,&& 之前还没收到回复
				tp->retrans_out -= acked_pcount;    // 现在需要更新重传的且没有收到ACK的包
			flag |= FLAG_RETRANS_DATA_ACKED;        // 重传包收到ACK
			ca_seq_rtt = -1;
			seq_rtt = -1;
			if ((flag & FLAG_DATA_ACKED) || (acked_pcount > 1))
				flag |= FLAG_NONHEAD_RETRANS_ACKED;
		} else { // 如果此数据段没有被重传过
			ca_seq_rtt = now - scb->when;           // 通过ACK确认获得RTT值
			last_ackt = skb->tstamp;                // 获得skb的发送时间
			if (seq_rtt < 0) {
				seq_rtt = ca_seq_rtt;
			}
			if (!(sacked & TCPCB_SACKED_ACKED))     // 如果SACK存在一段没有被确认,那么保存其中序号最小号的
				reord = min(pkts_acked, reord);
		}

		if (sacked & TCPCB_SACKED_ACKED)            // 如果是有sack标识
			tp->sacked_out -= acked_pcount;         // 那么更新sack的发出没有接受到确认的数量
		if (sacked & TCPCB_LOST)                    // 如果是丢包标识,那么更新数量
			tp->lost_out -= acked_pcount;

		if (unlikely(tp->urg_mode && !before(end_seq, tp->snd_up)))  // 紧急模式
			tp->urg_mode = 0;

		tp->packets_out -= acked_pcount;            // 发送的包没有确认的数量-=acked_pcount
		pkts_acked += acked_pcount;                 // 接收到确认的包数量+=acked_pcount

		/* 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.
		 */
		if (!(scb->flags & TCPCB_FLAG_SYN)) {       // 如果不是SYN握手包
			flag |= FLAG_DATA_ACKED;                // 标识是数据确认
		} else {
			flag |= FLAG_SYN_ACKED;                 // 标识是SYN包标识
			tp->retrans_stamp = 0;                  // 清除重传戳
		}

		if (!fully_acked)                           // 如果TSO段没被完全确认,则到此为止
			break;

		tcp_unlink_write_queue(skb, sk);            // 从发送队列上移除这个skb!!!这个函数其实很简单,其实就是从链表中移除这个skb而已
		sk_wmem_free_skb(sk, skb);                  // 删除skb内存对象
		tcp_clear_all_retrans_hints(tp);
	}                                               // while循环结束

	if (skb && (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED))  // 虚假的SACK
		flag |= FLAG_SACK_RENEGING;

	if (flag & FLAG_ACKED) {                        // 如果ACK更新了数据,是的snd_una更新了
		const struct tcp_congestion_ops *ca_ops
			= inet_csk(sk)->icsk_ca_ops;            // 拥塞信息

		tcp_ack_update_rtt(sk, flag, seq_rtt);      // 更新RTT
		tcp_rearm_rto(sk);                          // 重置超时重传计时器

		if (tcp_is_reno(tp)) {                      // 如果没有SACK处理
			tcp_remove_reno_sacks(sk, pkts_acked);  // 处理乱序的包
		} else {
			/* Non-retransmitted hole got filled? That's reordering */
			if (reord < prior_fackets)
				tcp_update_reordering(sk, tp->fackets_out - reord, 0);  // 更新乱序队列大小
		}

		tp->fackets_out -= min(pkts_acked, tp->fackets_out);    // 更新提前确认算法得出的尚未得到确认的包的数量

		if (ca_ops->pkts_acked) {   // 这是一个钩子函数
			s32 rtt_us = -1;

			/* Is the ACK triggering packet unambiguous? */
			if (!(flag & FLAG_RETRANS_DATA_ACKED)) {            // 如果是确认了非重传的包
				/* High resolution needed and available? */
				if (ca_ops->flags & TCP_CONG_RTT_STAMP &&       // 下面都是测量RTT,精读不同而已
				!ktime_equal(last_ackt,
						 net_invalid_timestamp()))
					rtt_us = ktime_us_delta(ktime_get_real(),
								last_ackt);
				else if (ca_seq_rtt > 0)
					rtt_us = jiffies_to_usecs(ca_seq_rtt);
			}

			ca_ops->pkts_acked(sk, pkts_acked, rtt_us);
		}
	}

#if FASTRETRANS_DEBUG > 0  // 下面用于调试
	BUG_TRAP((int)tp->sacked_out >= 0);
	BUG_TRAP((int)tp->lost_out >= 0);
	BUG_TRAP((int)tp->retrans_out >= 0);
	if (!tp->packets_out && tcp_is_sack(tp)) {
		icsk = inet_csk(sk);
		if (tp->lost_out) {
			printk(KERN_DEBUG "Leak l=%u %d\n",
				tp->lost_out, icsk->icsk_ca_state);
			tp->lost_out = 0;
		}
		if (tp->sacked_out) {
			printk(KERN_DEBUG "Leak s=%u %d\n",
				tp->sacked_out, icsk->icsk_ca_state);
			tp->sacked_out = 0;
		}
		if (tp->retrans_out) {
			printk(KERN_DEBUG "Leak r=%u %d\n",
				tp->retrans_out, icsk->icsk_ca_state);
			 tp->retrans_out = 0;
		}
	}
#endif
	return flag;
}

下面看一下tcp_tso_acked函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* If we get here, the whole TSO packet has not been acked. */
static u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb)       // TSO 包并没有全部被确认,现在需要统计已经被确认的数量
{
	struct tcp_sock *tp = tcp_sk(sk);                                // 获得tcp_sock
	u32 packets_acked;

	BUG_ON(!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una));           // seq---end_seq---snd_una  这种情况不可能进来

	packets_acked = tcp_skb_pcount(skb);                             // TSO段总共包括几个
	if (tcp_trim_head(sk, skb, tp->snd_una - TCP_SKB_CB(skb)->seq))  // 对于已经确认的部分,更新skb中的信息。例如len之类信息都变了
		return 0;                                                    // 然后重新计算出新的剩余的segs
	packets_acked -= tcp_skb_pcount(skb);                            // 之前总的segs - 现在剩余的segs == 被确认的segs

	if (packets_acked) {
		BUG_ON(tcp_skb_pcount(skb) == 0);
		BUG_ON(!before(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq));
	}

	return packets_acked;                                            // 返回被确认的数量
}