]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
659a8ad5 YC |
2 | #include <linux/tcp.h> |
3 | #include <net/tcp.h> | |
4f41b1c5 | 4 | |
d716bfdb | 5 | void tcp_mark_skb_lost(struct sock *sk, struct sk_buff *skb) |
db8da6bb YC |
6 | { |
7 | struct tcp_sock *tp = tcp_sk(sk); | |
8 | ||
9 | tcp_skb_mark_lost_uncond_verify(tp, skb); | |
10 | if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) { | |
11 | /* Account for retransmits that are lost again */ | |
12 | TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS; | |
13 | tp->retrans_out -= tcp_skb_pcount(skb); | |
ecde8f36 YC |
14 | NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPLOSTRETRANSMIT, |
15 | tcp_skb_pcount(skb)); | |
db8da6bb YC |
16 | } |
17 | } | |
18 | ||
9a568de4 | 19 | static bool tcp_rack_sent_after(u64 t1, u64 t2, u32 seq1, u32 seq2) |
1d0833df | 20 | { |
9a568de4 | 21 | return t1 > t2 || (t1 == t2 && after(seq1, seq2)); |
1d0833df YC |
22 | } |
23 | ||
1f7455c3 | 24 | static u32 tcp_rack_reo_wnd(const struct sock *sk) |
20b654df YC |
25 | { |
26 | struct tcp_sock *tp = tcp_sk(sk); | |
27 | ||
7ec65372 | 28 | if (!tp->reord_seen) { |
20b654df YC |
29 | /* If reordering has not been observed, be aggressive during |
30 | * the recovery or starting the recovery by DUPACK threshold. | |
31 | */ | |
32 | if (inet_csk(sk)->icsk_ca_state >= TCP_CA_Recovery) | |
33 | return 0; | |
34 | ||
35 | if (tp->sacked_out >= tp->reordering && | |
36 | !(sock_net(sk)->ipv4.sysctl_tcp_recovery & TCP_RACK_NO_DUPTHRESH)) | |
37 | return 0; | |
38 | } | |
39 | ||
40 | /* To be more reordering resilient, allow min_rtt/4 settling delay. | |
41 | * Use min_rtt instead of the smoothed RTT because reordering is | |
42 | * often a path property and less related to queuing or delayed ACKs. | |
43 | * Upon receiving DSACKs, linearly increase the window up to the | |
44 | * smoothed RTT. | |
45 | */ | |
46 | return min((tcp_min_rtt(tp) >> 2) * tp->rack.reo_wnd_steps, | |
47 | tp->srtt_us >> 3); | |
48 | } | |
49 | ||
b8fef65a YC |
50 | s32 tcp_rack_skb_timeout(struct tcp_sock *tp, struct sk_buff *skb, u32 reo_wnd) |
51 | { | |
52 | return tp->rack.rtt_us + reo_wnd - | |
2fd66ffb | 53 | tcp_stamp_us_delta(tp->tcp_mstamp, tcp_skb_timestamp_us(skb)); |
b8fef65a YC |
54 | } |
55 | ||
a0370b3f YC |
56 | /* RACK loss detection (IETF draft draft-ietf-tcpm-rack-01): |
57 | * | |
58 | * Marks a packet lost, if some packet sent later has been (s)acked. | |
4f41b1c5 YC |
59 | * The underlying idea is similar to the traditional dupthresh and FACK |
60 | * but they look at different metrics: | |
61 | * | |
62 | * dupthresh: 3 OOO packets delivered (packet count) | |
63 | * FACK: sequence delta to highest sacked sequence (sequence space) | |
64 | * RACK: sent time delta to the latest delivered packet (time domain) | |
65 | * | |
66 | * The advantage of RACK is it applies to both original and retransmitted | |
67 | * packet and therefore is robust against tail losses. Another advantage | |
68 | * is being more resilient to reordering by simply allowing some | |
69 | * "settling delay", instead of tweaking the dupthresh. | |
70 | * | |
a0370b3f YC |
71 | * When tcp_rack_detect_loss() detects some packets are lost and we |
72 | * are not already in the CA_Recovery state, either tcp_rack_reo_timeout() | |
73 | * or tcp_time_to_recover()'s "Trick#1: the loss is proven" code path will | |
74 | * make us enter the CA_Recovery state. | |
4f41b1c5 | 75 | */ |
7c1c7308 | 76 | static void tcp_rack_detect_loss(struct sock *sk, u32 *reo_timeout) |
4f41b1c5 YC |
77 | { |
78 | struct tcp_sock *tp = tcp_sk(sk); | |
043b87d7 | 79 | struct sk_buff *skb, *n; |
e636f8b0 | 80 | u32 reo_wnd; |
4f41b1c5 | 81 | |
57dde7f7 | 82 | *reo_timeout = 0; |
20b654df | 83 | reo_wnd = tcp_rack_reo_wnd(sk); |
043b87d7 YC |
84 | list_for_each_entry_safe(skb, n, &tp->tsorted_sent_queue, |
85 | tcp_tsorted_anchor) { | |
4f41b1c5 | 86 | struct tcp_skb_cb *scb = TCP_SKB_CB(skb); |
bef06223 | 87 | s32 remaining; |
4f41b1c5 | 88 | |
bef06223 YC |
89 | /* Skip ones marked lost but not yet retransmitted */ |
90 | if ((scb->sacked & TCPCB_LOST) && | |
91 | !(scb->sacked & TCPCB_SACKED_RETRANS)) | |
92 | continue; | |
57dde7f7 | 93 | |
2fd66ffb ED |
94 | if (!tcp_rack_sent_after(tp->rack.mstamp, |
95 | tcp_skb_timestamp_us(skb), | |
bef06223 YC |
96 | tp->rack.end_seq, scb->end_seq)) |
97 | break; | |
57dde7f7 | 98 | |
bef06223 YC |
99 | /* A packet is lost if it has not been s/acked beyond |
100 | * the recent RTT plus the reordering window. | |
101 | */ | |
b8fef65a | 102 | remaining = tcp_rack_skb_timeout(tp, skb, reo_wnd); |
428aec5e | 103 | if (remaining <= 0) { |
d716bfdb | 104 | tcp_mark_skb_lost(sk, skb); |
bef06223 YC |
105 | list_del_init(&skb->tcp_tsorted_anchor); |
106 | } else { | |
428aec5e YC |
107 | /* Record maximum wait time */ |
108 | *reo_timeout = max_t(u32, *reo_timeout, remaining); | |
4f41b1c5 YC |
109 | } |
110 | } | |
e636f8b0 YC |
111 | } |
112 | ||
128eda86 | 113 | void tcp_rack_mark_lost(struct sock *sk) |
e636f8b0 YC |
114 | { |
115 | struct tcp_sock *tp = tcp_sk(sk); | |
57dde7f7 | 116 | u32 timeout; |
e636f8b0 | 117 | |
a0370b3f | 118 | if (!tp->rack.advanced) |
e636f8b0 | 119 | return; |
57dde7f7 | 120 | |
e636f8b0 YC |
121 | /* Reset the advanced flag to avoid unnecessary queue scanning */ |
122 | tp->rack.advanced = 0; | |
7c1c7308 | 123 | tcp_rack_detect_loss(sk, &timeout); |
57dde7f7 | 124 | if (timeout) { |
bb4d991a | 125 | timeout = usecs_to_jiffies(timeout) + TCP_TIMEOUT_MIN; |
57dde7f7 YC |
126 | inet_csk_reset_xmit_timer(sk, ICSK_TIME_REO_TIMEOUT, |
127 | timeout, inet_csk(sk)->icsk_rto); | |
128 | } | |
4f41b1c5 YC |
129 | } |
130 | ||
deed7be7 YC |
131 | /* Record the most recently (re)sent time among the (s)acked packets |
132 | * This is "Step 3: Advance RACK.xmit_time and update RACK.RTT" from | |
133 | * draft-cheng-tcpm-rack-00.txt | |
134 | */ | |
1d0833df | 135 | void tcp_rack_advance(struct tcp_sock *tp, u8 sacked, u32 end_seq, |
9a568de4 | 136 | u64 xmit_time) |
659a8ad5 | 137 | { |
deed7be7 YC |
138 | u32 rtt_us; |
139 | ||
9a568de4 | 140 | rtt_us = tcp_stamp_us_delta(tp->tcp_mstamp, xmit_time); |
6065fd0d | 141 | if (rtt_us < tcp_min_rtt(tp) && (sacked & TCPCB_RETRANS)) { |
659a8ad5 YC |
142 | /* If the sacked packet was retransmitted, it's ambiguous |
143 | * whether the retransmission or the original (or the prior | |
144 | * retransmission) was sacked. | |
145 | * | |
146 | * If the original is lost, there is no ambiguity. Otherwise | |
147 | * we assume the original can be delayed up to aRTT + min_rtt. | |
148 | * the aRTT term is bounded by the fast recovery or timeout, | |
149 | * so it's at least one RTT (i.e., retransmission is at least | |
150 | * an RTT later). | |
151 | */ | |
6065fd0d | 152 | return; |
659a8ad5 | 153 | } |
659a8ad5 | 154 | tp->rack.advanced = 1; |
6065fd0d YC |
155 | tp->rack.rtt_us = rtt_us; |
156 | if (tcp_rack_sent_after(xmit_time, tp->rack.mstamp, | |
157 | end_seq, tp->rack.end_seq)) { | |
158 | tp->rack.mstamp = xmit_time; | |
159 | tp->rack.end_seq = end_seq; | |
160 | } | |
659a8ad5 | 161 | } |
57dde7f7 YC |
162 | |
163 | /* We have waited long enough to accommodate reordering. Mark the expired | |
164 | * packets lost and retransmit them. | |
165 | */ | |
166 | void tcp_rack_reo_timeout(struct sock *sk) | |
167 | { | |
168 | struct tcp_sock *tp = tcp_sk(sk); | |
57dde7f7 YC |
169 | u32 timeout, prior_inflight; |
170 | ||
57dde7f7 | 171 | prior_inflight = tcp_packets_in_flight(tp); |
7c1c7308 | 172 | tcp_rack_detect_loss(sk, &timeout); |
57dde7f7 YC |
173 | if (prior_inflight != tcp_packets_in_flight(tp)) { |
174 | if (inet_csk(sk)->icsk_ca_state != TCP_CA_Recovery) { | |
175 | tcp_enter_recovery(sk, false); | |
176 | if (!inet_csk(sk)->icsk_ca_ops->cong_control) | |
177 | tcp_cwnd_reduction(sk, 1, 0); | |
178 | } | |
179 | tcp_xmit_retransmit_queue(sk); | |
180 | } | |
181 | if (inet_csk(sk)->icsk_pending != ICSK_TIME_RETRANS) | |
182 | tcp_rearm_rto(sk); | |
183 | } | |
1f255691 PJ |
184 | |
185 | /* Updates the RACK's reo_wnd based on DSACK and no. of recoveries. | |
186 | * | |
187 | * If DSACK is received, increment reo_wnd by min_rtt/4 (upper bounded | |
188 | * by srtt), since there is possibility that spurious retransmission was | |
189 | * due to reordering delay longer than reo_wnd. | |
190 | * | |
191 | * Persist the current reo_wnd value for TCP_RACK_RECOVERY_THRESH (16) | |
192 | * no. of successful recoveries (accounts for full DSACK-based loss | |
193 | * recovery undo). After that, reset it to default (min_rtt/4). | |
194 | * | |
195 | * At max, reo_wnd is incremented only once per rtt. So that the new | |
196 | * DSACK on which we are reacting, is due to the spurious retx (approx) | |
197 | * after the reo_wnd has been updated last time. | |
198 | * | |
199 | * reo_wnd is tracked in terms of steps (of min_rtt/4), rather than | |
200 | * absolute value to account for change in rtt. | |
201 | */ | |
202 | void tcp_rack_update_reo_wnd(struct sock *sk, struct rate_sample *rs) | |
203 | { | |
204 | struct tcp_sock *tp = tcp_sk(sk); | |
205 | ||
206 | if (sock_net(sk)->ipv4.sysctl_tcp_recovery & TCP_RACK_STATIC_REO_WND || | |
207 | !rs->prior_delivered) | |
208 | return; | |
209 | ||
210 | /* Disregard DSACK if a rtt has not passed since we adjusted reo_wnd */ | |
211 | if (before(rs->prior_delivered, tp->rack.last_delivered)) | |
212 | tp->rack.dsack_seen = 0; | |
213 | ||
214 | /* Adjust the reo_wnd if update is pending */ | |
215 | if (tp->rack.dsack_seen) { | |
216 | tp->rack.reo_wnd_steps = min_t(u32, 0xFF, | |
217 | tp->rack.reo_wnd_steps + 1); | |
218 | tp->rack.dsack_seen = 0; | |
219 | tp->rack.last_delivered = tp->delivered; | |
220 | tp->rack.reo_wnd_persist = TCP_RACK_RECOVERY_THRESH; | |
221 | } else if (!tp->rack.reo_wnd_persist) { | |
222 | tp->rack.reo_wnd_steps = 1; | |
223 | } | |
224 | } | |
6ac06ecd YC |
225 | |
226 | /* RFC6582 NewReno recovery for non-SACK connection. It simply retransmits | |
227 | * the next unacked packet upon receiving | |
228 | * a) three or more DUPACKs to start the fast recovery | |
229 | * b) an ACK acknowledging new data during the fast recovery. | |
230 | */ | |
231 | void tcp_newreno_mark_lost(struct sock *sk, bool snd_una_advanced) | |
232 | { | |
233 | const u8 state = inet_csk(sk)->icsk_ca_state; | |
234 | struct tcp_sock *tp = tcp_sk(sk); | |
235 | ||
236 | if ((state < TCP_CA_Recovery && tp->sacked_out >= tp->reordering) || | |
237 | (state == TCP_CA_Recovery && snd_una_advanced)) { | |
238 | struct sk_buff *skb = tcp_rtx_queue_head(sk); | |
239 | u32 mss; | |
240 | ||
241 | if (TCP_SKB_CB(skb)->sacked & TCPCB_LOST) | |
242 | return; | |
243 | ||
244 | mss = tcp_skb_mss(skb); | |
245 | if (tcp_skb_pcount(skb) > 1 && skb->len > mss) | |
246 | tcp_fragment(sk, TCP_FRAG_IN_RTX_QUEUE, skb, | |
247 | mss, mss, GFP_ATOMIC); | |
248 | ||
249 | tcp_skb_mark_lost_uncond_verify(tp, skb); | |
250 | } | |
251 | } |