OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/quic/congestion_control/tcp_cubic_sender.h" | 5 #include "net/quic/congestion_control/tcp_cubic_sender.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
| 10 #include "net/quic/congestion_control/prr_sender.h" |
10 #include "net/quic/congestion_control/rtt_stats.h" | 11 #include "net/quic/congestion_control/rtt_stats.h" |
11 #include "net/quic/crypto/crypto_protocol.h" | 12 #include "net/quic/crypto/crypto_protocol.h" |
12 | 13 |
13 using std::max; | 14 using std::max; |
14 using std::min; | 15 using std::min; |
15 | 16 |
16 namespace net { | 17 namespace net { |
17 | 18 |
18 namespace { | 19 namespace { |
19 // Constants based on TCP defaults. | 20 // Constants based on TCP defaults. |
20 // The minimum cwnd based on RFC 3782 (TCP NewReno) for cwnd reductions on a | 21 // The minimum cwnd based on RFC 3782 (TCP NewReno) for cwnd reductions on a |
21 // fast retransmission. The cwnd after a timeout is still 1. | 22 // fast retransmission. The cwnd after a timeout is still 1. |
22 const QuicPacketCount kMinimumCongestionWindow = 2; | 23 const QuicPacketCount kMinimumCongestionWindow = 2; |
23 const QuicByteCount kMaxSegmentSize = kDefaultTCPMSS; | 24 const QuicByteCount kMaxSegmentSize = kDefaultTCPMSS; |
24 const int64 kInitialCongestionWindow = 10; | 25 const int64 kInitialCongestionWindow = 10; |
25 const int kMaxBurstLength = 3; | 26 const int kMaxBurstLength = 3; |
26 }; // namespace | 27 } // namespace |
27 | 28 |
28 TcpCubicSender::TcpCubicSender( | 29 TcpCubicSender::TcpCubicSender( |
29 const QuicClock* clock, | 30 const QuicClock* clock, |
30 const RttStats* rtt_stats, | 31 const RttStats* rtt_stats, |
31 bool reno, | 32 bool reno, |
32 QuicPacketCount max_tcp_congestion_window, | 33 QuicPacketCount max_tcp_congestion_window, |
33 QuicConnectionStats* stats) | 34 QuicConnectionStats* stats) |
34 : hybrid_slow_start_(clock), | 35 : hybrid_slow_start_(clock), |
35 cubic_(clock, stats), | 36 cubic_(clock, stats), |
36 rtt_stats_(rtt_stats), | 37 rtt_stats_(rtt_stats), |
37 stats_(stats), | 38 stats_(stats), |
38 reno_(reno), | 39 reno_(reno), |
39 num_connections_(2), | 40 num_connections_(2), |
40 congestion_window_count_(0), | 41 congestion_window_count_(0), |
41 prr_out_(0), | |
42 prr_delivered_(0), | |
43 ack_count_since_loss_(0), | |
44 bytes_in_flight_before_loss_(0), | |
45 largest_sent_sequence_number_(0), | 42 largest_sent_sequence_number_(0), |
46 largest_acked_sequence_number_(0), | 43 largest_acked_sequence_number_(0), |
47 largest_sent_at_last_cutback_(0), | 44 largest_sent_at_last_cutback_(0), |
48 congestion_window_(kInitialCongestionWindow), | 45 congestion_window_(kInitialCongestionWindow), |
49 previous_congestion_window_(0), | 46 previous_congestion_window_(0), |
50 slowstart_threshold_(max_tcp_congestion_window), | 47 slowstart_threshold_(max_tcp_congestion_window), |
51 previous_slowstart_threshold_(0), | 48 previous_slowstart_threshold_(0), |
52 last_cutback_exited_slowstart_(false), | 49 last_cutback_exited_slowstart_(false), |
53 max_tcp_congestion_window_(max_tcp_congestion_window) { | 50 max_tcp_congestion_window_(max_tcp_congestion_window) { |
54 } | 51 } |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
99 } | 96 } |
100 } | 97 } |
101 | 98 |
102 void TcpCubicSender::OnPacketAcked( | 99 void TcpCubicSender::OnPacketAcked( |
103 QuicPacketSequenceNumber acked_sequence_number, | 100 QuicPacketSequenceNumber acked_sequence_number, |
104 QuicByteCount acked_bytes, | 101 QuicByteCount acked_bytes, |
105 QuicByteCount bytes_in_flight) { | 102 QuicByteCount bytes_in_flight) { |
106 largest_acked_sequence_number_ = max(acked_sequence_number, | 103 largest_acked_sequence_number_ = max(acked_sequence_number, |
107 largest_acked_sequence_number_); | 104 largest_acked_sequence_number_); |
108 if (InRecovery()) { | 105 if (InRecovery()) { |
109 PrrOnPacketAcked(acked_bytes); | 106 // PRR is used when in recovery. |
| 107 prr_.OnPacketAcked(acked_bytes); |
110 return; | 108 return; |
111 } | 109 } |
112 MaybeIncreaseCwnd(acked_sequence_number, bytes_in_flight); | 110 MaybeIncreaseCwnd(acked_sequence_number, bytes_in_flight); |
113 // TODO(ianswett): Should this even be called when not in slow start? | 111 // TODO(ianswett): Should this even be called when not in slow start? |
114 hybrid_slow_start_.OnPacketAcked(acked_sequence_number, InSlowStart()); | 112 hybrid_slow_start_.OnPacketAcked(acked_sequence_number, InSlowStart()); |
115 } | 113 } |
116 | 114 |
117 void TcpCubicSender::OnPacketLost(QuicPacketSequenceNumber sequence_number, | 115 void TcpCubicSender::OnPacketLost(QuicPacketSequenceNumber sequence_number, |
118 QuicByteCount bytes_in_flight) { | 116 QuicByteCount bytes_in_flight) { |
119 // TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets | 117 // TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets |
120 // already sent should be treated as a single loss event, since it's expected. | 118 // already sent should be treated as a single loss event, since it's expected. |
121 if (sequence_number <= largest_sent_at_last_cutback_) { | 119 if (sequence_number <= largest_sent_at_last_cutback_) { |
122 if (last_cutback_exited_slowstart_) { | 120 if (last_cutback_exited_slowstart_) { |
123 ++stats_->slowstart_packets_lost; | 121 ++stats_->slowstart_packets_lost; |
124 } | 122 } |
125 DVLOG(1) << "Ignoring loss for largest_missing:" << sequence_number | 123 DVLOG(1) << "Ignoring loss for largest_missing:" << sequence_number |
126 << " because it was sent prior to the last CWND cutback."; | 124 << " because it was sent prior to the last CWND cutback."; |
127 return; | 125 return; |
128 } | 126 } |
129 ++stats_->tcp_loss_events; | 127 ++stats_->tcp_loss_events; |
130 last_cutback_exited_slowstart_ = InSlowStart(); | 128 last_cutback_exited_slowstart_ = InSlowStart(); |
131 if (InSlowStart()) { | 129 if (InSlowStart()) { |
132 ++stats_->slowstart_packets_lost; | 130 ++stats_->slowstart_packets_lost; |
133 } | 131 } |
134 PrrOnPacketLost(bytes_in_flight); | 132 |
| 133 prr_.OnPacketLost(bytes_in_flight); |
135 | 134 |
136 if (reno_) { | 135 if (reno_) { |
137 congestion_window_ = congestion_window_ >> 1; | 136 congestion_window_ = congestion_window_ >> 1; |
138 } else { | 137 } else { |
139 congestion_window_ = | 138 congestion_window_ = |
140 cubic_.CongestionWindowAfterPacketLoss(congestion_window_); | 139 cubic_.CongestionWindowAfterPacketLoss(congestion_window_); |
141 } | 140 } |
142 slowstart_threshold_ = congestion_window_; | 141 slowstart_threshold_ = congestion_window_; |
143 // Enforce TCP's minimum congestion window of 2*MSS. | 142 // Enforce TCP's minimum congestion window of 2*MSS. |
144 if (congestion_window_ < kMinimumCongestionWindow) { | 143 if (congestion_window_ < kMinimumCongestionWindow) { |
145 congestion_window_ = kMinimumCongestionWindow; | 144 congestion_window_ = kMinimumCongestionWindow; |
146 } | 145 } |
147 largest_sent_at_last_cutback_ = largest_sent_sequence_number_; | 146 largest_sent_at_last_cutback_ = largest_sent_sequence_number_; |
148 // reset packet count from congestion avoidance mode. We start | 147 // reset packet count from congestion avoidance mode. We start |
149 // counting again when we're out of recovery. | 148 // counting again when we're out of recovery. |
150 congestion_window_count_ = 0; | 149 congestion_window_count_ = 0; |
151 DVLOG(1) << "Incoming loss; congestion window: " << congestion_window_ | 150 DVLOG(1) << "Incoming loss; congestion window: " << congestion_window_ |
152 << " slowstart threshold: " << slowstart_threshold_; | 151 << " slowstart threshold: " << slowstart_threshold_; |
153 } | 152 } |
154 | 153 |
155 bool TcpCubicSender::OnPacketSent(QuicTime /*sent_time*/, | 154 bool TcpCubicSender::OnPacketSent(QuicTime /*sent_time*/, |
156 QuicByteCount /*bytes_in_flight*/, | 155 QuicByteCount /*bytes_in_flight*/, |
157 QuicPacketSequenceNumber sequence_number, | 156 QuicPacketSequenceNumber sequence_number, |
158 QuicByteCount bytes, | 157 QuicByteCount bytes, |
159 HasRetransmittableData is_retransmittable) { | 158 HasRetransmittableData is_retransmittable) { |
160 // Only update bytes_in_flight_ for data packets. | 159 // Only update bytes_in_flight_ for data packets. |
161 if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) { | 160 if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) { |
162 return false; | 161 return false; |
163 } | 162 } |
164 | 163 if (InRecovery()) { |
165 prr_out_ += bytes; | 164 // PRR is used when in recovery. |
| 165 prr_.OnPacketSent(bytes); |
| 166 } |
166 DCHECK_LT(largest_sent_sequence_number_, sequence_number); | 167 DCHECK_LT(largest_sent_sequence_number_, sequence_number); |
167 largest_sent_sequence_number_ = sequence_number; | 168 largest_sent_sequence_number_ = sequence_number; |
168 hybrid_slow_start_.OnPacketSent(sequence_number); | 169 hybrid_slow_start_.OnPacketSent(sequence_number); |
169 return true; | 170 return true; |
170 } | 171 } |
171 | 172 |
172 QuicTime::Delta TcpCubicSender::TimeUntilSend( | 173 QuicTime::Delta TcpCubicSender::TimeUntilSend( |
173 QuicTime /* now */, | 174 QuicTime /* now */, |
174 QuicByteCount bytes_in_flight, | 175 QuicByteCount bytes_in_flight, |
175 HasRetransmittableData has_retransmittable_data) const { | 176 HasRetransmittableData has_retransmittable_data) const { |
176 if (has_retransmittable_data == NO_RETRANSMITTABLE_DATA) { | 177 if (has_retransmittable_data == NO_RETRANSMITTABLE_DATA) { |
177 // For TCP we can always send an ACK immediately. | 178 // For TCP we can always send an ACK immediately. |
178 return QuicTime::Delta::Zero(); | 179 return QuicTime::Delta::Zero(); |
179 } | 180 } |
180 if (InRecovery()) { | 181 if (InRecovery()) { |
181 return PrrTimeUntilSend(bytes_in_flight); | 182 // PRR is used when in recovery. |
| 183 return prr_.TimeUntilSend(GetCongestionWindow(), bytes_in_flight, |
| 184 slowstart_threshold_); |
182 } | 185 } |
183 if (GetCongestionWindow() > bytes_in_flight) { | 186 if (GetCongestionWindow() > bytes_in_flight) { |
184 return QuicTime::Delta::Zero(); | 187 return QuicTime::Delta::Zero(); |
185 } | 188 } |
186 return QuicTime::Delta::Infinite(); | 189 return QuicTime::Delta::Infinite(); |
187 } | 190 } |
188 | 191 |
189 QuicBandwidth TcpCubicSender::PacingRate() const { | 192 QuicBandwidth TcpCubicSender::PacingRate() const { |
190 // We pace at twice the rate of the underlying sender's bandwidth estimate | 193 // We pace at twice the rate of the underlying sender's bandwidth estimate |
191 // during slow start and 1.25x during congestion avoidance to ensure pacing | 194 // during slow start and 1.25x during congestion avoidance to ensure pacing |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
308 void TcpCubicSender::RevertRetransmissionTimeout() { | 311 void TcpCubicSender::RevertRetransmissionTimeout() { |
309 if (previous_congestion_window_ == 0) { | 312 if (previous_congestion_window_ == 0) { |
310 LOG(DFATAL) << "No previous congestion window to revert to."; | 313 LOG(DFATAL) << "No previous congestion window to revert to."; |
311 return; | 314 return; |
312 } | 315 } |
313 congestion_window_ = previous_congestion_window_; | 316 congestion_window_ = previous_congestion_window_; |
314 slowstart_threshold_ = previous_slowstart_threshold_; | 317 slowstart_threshold_ = previous_slowstart_threshold_; |
315 previous_congestion_window_ = 0; | 318 previous_congestion_window_ = 0; |
316 } | 319 } |
317 | 320 |
318 void TcpCubicSender::PrrOnPacketLost(QuicByteCount bytes_in_flight) { | |
319 prr_out_ = 0; | |
320 bytes_in_flight_before_loss_ = bytes_in_flight; | |
321 prr_delivered_ = 0; | |
322 ack_count_since_loss_ = 0; | |
323 } | |
324 | |
325 void TcpCubicSender::PrrOnPacketAcked(QuicByteCount acked_bytes) { | |
326 prr_delivered_ += acked_bytes; | |
327 ++ack_count_since_loss_; | |
328 } | |
329 | |
330 QuicTime::Delta TcpCubicSender::PrrTimeUntilSend( | |
331 QuicByteCount bytes_in_flight) const { | |
332 DCHECK(InRecovery()); | |
333 // Return QuicTime::Zero In order to ensure limited transmit always works. | |
334 if (prr_out_ == 0 || bytes_in_flight < kMaxSegmentSize) { | |
335 return QuicTime::Delta::Zero(); | |
336 } | |
337 if (GetCongestionWindow() > bytes_in_flight) { | |
338 // During PRR-SSRB, limit outgoing packets to 1 extra MSS per ack, instead | |
339 // of sending the entire available window. This prevents burst retransmits | |
340 // when more packets are lost than the CWND reduction. | |
341 // limit = MAX(prr_delivered - prr_out, DeliveredData) + MSS | |
342 if (prr_delivered_ + ack_count_since_loss_ * kMaxSegmentSize <= prr_out_) { | |
343 return QuicTime::Delta::Infinite(); | |
344 } | |
345 return QuicTime::Delta::Zero(); | |
346 } | |
347 // Implement Proportional Rate Reduction (RFC6937) | |
348 // Checks a simplified version of the PRR formula that doesn't use division: | |
349 // AvailableSendWindow = | |
350 // CEIL(prr_delivered * ssthresh / BytesInFlightAtLoss) - prr_sent | |
351 if (prr_delivered_ * slowstart_threshold_ * kMaxSegmentSize > | |
352 prr_out_ * bytes_in_flight_before_loss_) { | |
353 return QuicTime::Delta::Zero(); | |
354 } | |
355 return QuicTime::Delta::Infinite(); | |
356 } | |
357 | |
358 CongestionControlType TcpCubicSender::GetCongestionControlType() const { | 321 CongestionControlType TcpCubicSender::GetCongestionControlType() const { |
359 return reno_ ? kReno : kCubic; | 322 return reno_ ? kReno : kCubic; |
360 } | 323 } |
361 | 324 |
362 } // namespace net | 325 } // namespace net |
OLD | NEW |