概述
tcp_write_xmit函数完成对待发送数据的分段发送,过程中会遍历发送队列,进行窗口检查,需要TSO分段则分段,然后调用tcp_transmit_skb发送数据段;
源码分析
1 static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
2 int push_one, gfp_t gfp)
3 {
4 struct tcp_sock *tp = tcp_sk(sk);
5 struct sk_buff *skb;
6 unsigned int tso_segs, sent_pkts;
7 int cwnd_quota;
8 int result;
9 bool is_cwnd_limited = false, is_rwnd_limited = false;
10 u32 max_segs;
11
12 /* 已发送数据段数量 */
13 sent_pkts = 0;
14
15 /* 发送多个数据段 */
16 if (!push_one) {
17 /* Do MTU probing. */
18 /* 发送路径mtu探测 */
19 result = tcp_mtu_probe(sk);
20 /* 失败 */
21 if (!result) {
22 return false;
23 }
24 /* 成功,设置已发送数据段数为1 */
25 else if (result > 0) {
26 sent_pkts = 1;
27 }
28 }
29
30 /* 获取最大tso分段 */
31 max_segs = tcp_tso_segs(sk, mss_now);
32
33 /* 有数据段要发送 */
34 while ((skb = tcp_send_head(sk))) {
35 unsigned int limit;
36
37 /* 初始化tso分段相关 */
38 tso_segs = tcp_init_tso_segs(skb, mss_now);
39 BUG_ON(!tso_segs);
40
41 if (unlikely(tp->repair) && tp->repair_queue == TCP_SEND_QUEUE) {
42 /* "skb_mstamp" is used as a start point for the retransmit timer */
43 skb_mstamp_get(&skb->skb_mstamp);
44 goto repair; /* Skip network transmission */
45 }
46
47 /* 检测拥塞窗口大小 */
48 cwnd_quota = tcp_cwnd_test(tp, skb);
49 /* 为0 */
50 if (!cwnd_quota) {
51 /* 尾部丢失探测段,设置为1 */
52 if (push_one == 2)
53 /* Force out a loss probe pkt. */
54 cwnd_quota = 1;
55 /* 其他情况,跳出 */
56 else
57 break;
58 }
59
60 /* 检查tcp的数据段是否在发送窗口之内 */
61 if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now))) {
62 /* 不在,标记,跳出 */
63 is_rwnd_limited = true;
64 break;
65 }
66
67 /* 不需要tso分段 */
68 if (tso_segs == 1) {
69 /* 检查nagle算法是否允许发送数据段 */
70 if (unlikely(!tcp_nagle_test(tp, skb, mss_now,
71 (tcp_skb_is_last(sk, skb) ?
72 nonagle : TCP_NAGLE_PUSH))))
73 break;
74 }
75 /* 需要tso分段 */
76 else {
77 /* 检查是否可以延迟发送 */
78 if (!push_one &&
79 tcp_tso_should_defer(sk, skb, &is_cwnd_limited,
80 max_segs))
81 break;
82 }
83
84 /* 设置分段长度限制为mss */
85 limit = mss_now;
86
87 /* 需要分段 && 非紧急模式,重新确定分段长度限制 */
88 if (tso_segs > 1 && !tcp_urg_mode(tp))
89 limit = tcp_mss_split_point(sk, skb, mss_now,
90 min_t(unsigned int,
91 cwnd_quota,
92 max_segs),
93 nonagle);
94
95 /* skb中数据段长度>分段长度限制,则进行分段,会申请新的skb */
96 if (skb->len > limit &&
97 unlikely(tso_fragment(sk, skb, limit, mss_now, gfp)))
98 break;
99
100 if (test_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags))
101 clear_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags);
102 if (tcp_small_queue_check(sk, skb, 0))
103 break;
104
105 /* 发送分段数据 */
106 if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp)))
107 break;
108
109 repair:
110 /* Advance the send_head. This one is sent out.
111 * This call will increment packets_out.
112 */
113 /* 进行发送之后的数据更新,包括统计计数和定时器等 */
114 tcp_event_new_data_sent(sk, skb);
115
116 /* 更新最新发送小包的结束序号 */
117 tcp_minshall_update(tp, mss_now, skb);
118
119 /* 更新发送数据段数量 */
120 sent_pkts += tcp_skb_pcount(skb);
121
122 /* 只发送一个段,则跳出 */
123 if (push_one)
124 break;
125 }
126
127 if (is_rwnd_limited)
128 tcp_chrono_start(sk, TCP_CHRONO_RWND_LIMITED);
129 else
130 tcp_chrono_stop(sk, TCP_CHRONO_RWND_LIMITED);
131
132 /* 本次有数据发送,拥塞相关数据更新 */
133 if (likely(sent_pkts)) {
134 if (tcp_in_cwnd_reduction(sk))
135 tp->prr_out += sent_pkts;
136
137 /* Send one loss probe per tail loss episode. */
138 /* 每次发送一个尾部丢失探测 */
139 if (push_one != 2)
140 tcp_schedule_loss_probe(sk);
141
142 /* 拥塞窗口校验 */
143 is_cwnd_limited |= (tcp_packets_in_flight(tp) >= tp->snd_cwnd);
144 tcp_cwnd_validate(sk, is_cwnd_limited);
145 return false;
146 }
147
148
149 /* 本次无数据发送,已发出未确认的数据段不为0或者发送队列为空,认为成功 */
150 return !tp->packets_out && tcp_send_head(sk);
151 }