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 QuicTcpCongestionWindow 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 QuicTcpCongestionWindow 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 receive_window_(kDefaultSocketReceiveBuffer), | |
42 prr_out_(0), | |
43 prr_delivered_(0), | |
44 ack_count_since_loss_(0), | |
45 bytes_in_flight_before_loss_(0), | |
46 largest_sent_sequence_number_(0), | 42 largest_sent_sequence_number_(0), |
47 largest_acked_sequence_number_(0), | 43 largest_acked_sequence_number_(0), |
48 largest_sent_at_last_cutback_(0), | 44 largest_sent_at_last_cutback_(0), |
49 congestion_window_(kInitialCongestionWindow), | 45 congestion_window_(kInitialCongestionWindow), |
50 previous_congestion_window_(0), | 46 previous_congestion_window_(0), |
51 slowstart_threshold_(max_tcp_congestion_window), | 47 slowstart_threshold_(max_tcp_congestion_window), |
52 previous_slowstart_threshold_(0), | 48 previous_slowstart_threshold_(0), |
53 last_cutback_exited_slowstart_(false), | 49 last_cutback_exited_slowstart_(false), |
54 max_tcp_congestion_window_(max_tcp_congestion_window) { | 50 max_tcp_congestion_window_(max_tcp_congestion_window) { |
55 } | 51 } |
56 | 52 |
57 TcpCubicSender::~TcpCubicSender() { | 53 TcpCubicSender::~TcpCubicSender() { |
58 UMA_HISTOGRAM_COUNTS("Net.QuicSession.FinalTcpCwnd", congestion_window_); | 54 UMA_HISTOGRAM_COUNTS("Net.QuicSession.FinalTcpCwnd", congestion_window_); |
59 } | 55 } |
60 | 56 |
61 void TcpCubicSender::SetFromConfig(const QuicConfig& config, bool is_server) { | 57 void TcpCubicSender::SetFromConfig(const QuicConfig& config, bool is_server) { |
62 if (is_server) { | 58 if (is_server) { |
63 if (config.HasReceivedConnectionOptions() && | 59 if (config.HasReceivedConnectionOptions() && |
64 ContainsQuicTag(config.ReceivedConnectionOptions(), kIW10)) { | 60 ContainsQuicTag(config.ReceivedConnectionOptions(), kIW10)) { |
65 // Initial window experiment. Ignore the initial congestion | 61 // Initial window experiment. Ignore the initial congestion |
66 // window suggested by the client and use the default ICWND of | 62 // window suggested by the client and use the default ICWND of |
67 // 10 instead. | 63 // 10 instead. |
68 congestion_window_ = kInitialCongestionWindow; | 64 congestion_window_ = kInitialCongestionWindow; |
69 } else if (config.HasReceivedInitialCongestionWindow()) { | 65 } else if (config.HasReceivedInitialCongestionWindow()) { |
70 // Set the initial window size. | 66 // Set the initial window size. |
71 congestion_window_ = min(kMaxInitialWindow, | 67 congestion_window_ = min(kMaxInitialWindow, |
72 config.ReceivedInitialCongestionWindow()); | 68 config.ReceivedInitialCongestionWindow()); |
73 } | 69 } |
74 } | 70 } |
75 if (config.HasReceivedSocketReceiveBuffer()) { | |
76 // Set the initial socket receive buffer size in bytes. | |
77 receive_window_ = config.ReceivedSocketReceiveBuffer(); | |
78 } | |
79 } | 71 } |
80 | 72 |
81 void TcpCubicSender::SetNumEmulatedConnections(int num_connections) { | 73 void TcpCubicSender::SetNumEmulatedConnections(int num_connections) { |
82 num_connections_ = max(1, num_connections); | 74 num_connections_ = max(1, num_connections); |
83 cubic_.SetNumConnections(num_connections_); | 75 cubic_.SetNumConnections(num_connections_); |
84 } | 76 } |
85 | 77 |
86 void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame( | |
87 const QuicCongestionFeedbackFrame& feedback, | |
88 QuicTime feedback_receive_time) { | |
89 if (feedback.type == kTCP) { | |
90 receive_window_ = feedback.tcp.receive_window; | |
91 } | |
92 } | |
93 | |
94 void TcpCubicSender::OnCongestionEvent( | 78 void TcpCubicSender::OnCongestionEvent( |
95 bool rtt_updated, | 79 bool rtt_updated, |
96 QuicByteCount bytes_in_flight, | 80 QuicByteCount bytes_in_flight, |
97 const CongestionVector& acked_packets, | 81 const CongestionVector& acked_packets, |
98 const CongestionVector& lost_packets) { | 82 const CongestionVector& lost_packets) { |
99 if (rtt_updated && InSlowStart() && | 83 if (rtt_updated && InSlowStart() && |
100 hybrid_slow_start_.ShouldExitSlowStart(rtt_stats_->latest_rtt(), | 84 hybrid_slow_start_.ShouldExitSlowStart(rtt_stats_->latest_rtt(), |
101 rtt_stats_->MinRtt(), | 85 rtt_stats_->MinRtt(), |
102 congestion_window_)) { | 86 congestion_window_)) { |
103 slowstart_threshold_ = congestion_window_; | 87 slowstart_threshold_ = congestion_window_; |
104 } | 88 } |
105 for (CongestionVector::const_iterator it = lost_packets.begin(); | 89 for (CongestionVector::const_iterator it = lost_packets.begin(); |
106 it != lost_packets.end(); ++it) { | 90 it != lost_packets.end(); ++it) { |
107 OnPacketLost(it->first, bytes_in_flight); | 91 OnPacketLost(it->first, bytes_in_flight); |
108 } | 92 } |
109 for (CongestionVector::const_iterator it = acked_packets.begin(); | 93 for (CongestionVector::const_iterator it = acked_packets.begin(); |
110 it != acked_packets.end(); ++it) { | 94 it != acked_packets.end(); ++it) { |
111 OnPacketAcked(it->first, it->second.bytes_sent, bytes_in_flight); | 95 OnPacketAcked(it->first, it->second.bytes_sent, bytes_in_flight); |
112 } | 96 } |
113 } | 97 } |
114 | 98 |
115 void TcpCubicSender::OnPacketAcked( | 99 void TcpCubicSender::OnPacketAcked( |
116 QuicPacketSequenceNumber acked_sequence_number, | 100 QuicPacketSequenceNumber acked_sequence_number, |
117 QuicByteCount acked_bytes, | 101 QuicByteCount acked_bytes, |
118 QuicByteCount bytes_in_flight) { | 102 QuicByteCount bytes_in_flight) { |
119 largest_acked_sequence_number_ = max(acked_sequence_number, | 103 largest_acked_sequence_number_ = max(acked_sequence_number, |
120 largest_acked_sequence_number_); | 104 largest_acked_sequence_number_); |
121 if (InRecovery()) { | 105 if (InRecovery()) { |
122 PrrOnPacketAcked(acked_bytes); | 106 // PRR is used when in recovery. |
| 107 prr_.OnPacketAcked(acked_bytes); |
123 return; | 108 return; |
124 } | 109 } |
125 MaybeIncreaseCwnd(acked_sequence_number, bytes_in_flight); | 110 MaybeIncreaseCwnd(acked_sequence_number, bytes_in_flight); |
126 // 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? |
127 hybrid_slow_start_.OnPacketAcked(acked_sequence_number, InSlowStart()); | 112 hybrid_slow_start_.OnPacketAcked(acked_sequence_number, InSlowStart()); |
128 } | 113 } |
129 | 114 |
130 void TcpCubicSender::OnPacketLost(QuicPacketSequenceNumber sequence_number, | 115 void TcpCubicSender::OnPacketLost(QuicPacketSequenceNumber sequence_number, |
131 QuicByteCount bytes_in_flight) { | 116 QuicByteCount bytes_in_flight) { |
132 // 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 |
133 // 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. |
134 if (sequence_number <= largest_sent_at_last_cutback_) { | 119 if (sequence_number <= largest_sent_at_last_cutback_) { |
135 if (last_cutback_exited_slowstart_) { | 120 if (last_cutback_exited_slowstart_) { |
136 ++stats_->slowstart_packets_lost; | 121 ++stats_->slowstart_packets_lost; |
137 } | 122 } |
138 DVLOG(1) << "Ignoring loss for largest_missing:" << sequence_number | 123 DVLOG(1) << "Ignoring loss for largest_missing:" << sequence_number |
139 << " because it was sent prior to the last CWND cutback."; | 124 << " because it was sent prior to the last CWND cutback."; |
140 return; | 125 return; |
141 } | 126 } |
142 ++stats_->tcp_loss_events; | 127 ++stats_->tcp_loss_events; |
143 last_cutback_exited_slowstart_ = InSlowStart(); | 128 last_cutback_exited_slowstart_ = InSlowStart(); |
144 if (InSlowStart()) { | 129 if (InSlowStart()) { |
145 ++stats_->slowstart_packets_lost; | 130 ++stats_->slowstart_packets_lost; |
146 } | 131 } |
147 PrrOnPacketLost(bytes_in_flight); | 132 |
| 133 prr_.OnPacketLost(bytes_in_flight); |
148 | 134 |
149 if (reno_) { | 135 if (reno_) { |
150 congestion_window_ = congestion_window_ >> 1; | 136 congestion_window_ = congestion_window_ >> 1; |
151 } else { | 137 } else { |
152 congestion_window_ = | 138 congestion_window_ = |
153 cubic_.CongestionWindowAfterPacketLoss(congestion_window_); | 139 cubic_.CongestionWindowAfterPacketLoss(congestion_window_); |
154 } | 140 } |
155 slowstart_threshold_ = congestion_window_; | 141 slowstart_threshold_ = congestion_window_; |
156 // Enforce TCP's minimum congestion window of 2*MSS. | 142 // Enforce TCP's minimum congestion window of 2*MSS. |
157 if (congestion_window_ < kMinimumCongestionWindow) { | 143 if (congestion_window_ < kMinimumCongestionWindow) { |
158 congestion_window_ = kMinimumCongestionWindow; | 144 congestion_window_ = kMinimumCongestionWindow; |
159 } | 145 } |
160 largest_sent_at_last_cutback_ = largest_sent_sequence_number_; | 146 largest_sent_at_last_cutback_ = largest_sent_sequence_number_; |
161 // reset packet count from congestion avoidance mode. We start | 147 // reset packet count from congestion avoidance mode. We start |
162 // counting again when we're out of recovery. | 148 // counting again when we're out of recovery. |
163 congestion_window_count_ = 0; | 149 congestion_window_count_ = 0; |
164 DVLOG(1) << "Incoming loss; congestion window: " << congestion_window_ | 150 DVLOG(1) << "Incoming loss; congestion window: " << congestion_window_ |
165 << " slowstart threshold: " << slowstart_threshold_; | 151 << " slowstart threshold: " << slowstart_threshold_; |
166 } | 152 } |
167 | 153 |
168 bool TcpCubicSender::OnPacketSent(QuicTime /*sent_time*/, | 154 bool TcpCubicSender::OnPacketSent(QuicTime /*sent_time*/, |
169 QuicByteCount /*bytes_in_flight*/, | 155 QuicByteCount /*bytes_in_flight*/, |
170 QuicPacketSequenceNumber sequence_number, | 156 QuicPacketSequenceNumber sequence_number, |
171 QuicByteCount bytes, | 157 QuicByteCount bytes, |
172 HasRetransmittableData is_retransmittable) { | 158 HasRetransmittableData is_retransmittable) { |
173 // Only update bytes_in_flight_ for data packets. | 159 // Only update bytes_in_flight_ for data packets. |
174 if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) { | 160 if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) { |
175 return false; | 161 return false; |
176 } | 162 } |
177 | 163 if (InRecovery()) { |
178 prr_out_ += bytes; | 164 // PRR is used when in recovery. |
| 165 prr_.OnPacketSent(bytes); |
| 166 } |
179 DCHECK_LT(largest_sent_sequence_number_, sequence_number); | 167 DCHECK_LT(largest_sent_sequence_number_, sequence_number); |
180 largest_sent_sequence_number_ = sequence_number; | 168 largest_sent_sequence_number_ = sequence_number; |
181 hybrid_slow_start_.OnPacketSent(sequence_number); | 169 hybrid_slow_start_.OnPacketSent(sequence_number); |
182 return true; | 170 return true; |
183 } | 171 } |
184 | 172 |
185 QuicTime::Delta TcpCubicSender::TimeUntilSend( | 173 QuicTime::Delta TcpCubicSender::TimeUntilSend( |
186 QuicTime /* now */, | 174 QuicTime /* now */, |
187 QuicByteCount bytes_in_flight, | 175 QuicByteCount bytes_in_flight, |
188 HasRetransmittableData has_retransmittable_data) const { | 176 HasRetransmittableData has_retransmittable_data) const { |
189 if (has_retransmittable_data == NO_RETRANSMITTABLE_DATA) { | 177 if (has_retransmittable_data == NO_RETRANSMITTABLE_DATA) { |
190 // For TCP we can always send an ACK immediately. | 178 // For TCP we can always send an ACK immediately. |
191 return QuicTime::Delta::Zero(); | 179 return QuicTime::Delta::Zero(); |
192 } | 180 } |
193 if (InRecovery()) { | 181 if (InRecovery()) { |
194 return PrrTimeUntilSend(bytes_in_flight); | 182 // PRR is used when in recovery. |
| 183 return prr_.TimeUntilSend(GetCongestionWindow(), bytes_in_flight, |
| 184 slowstart_threshold_); |
195 } | 185 } |
196 if (SendWindow() > bytes_in_flight) { | 186 if (GetCongestionWindow() > bytes_in_flight) { |
197 return QuicTime::Delta::Zero(); | 187 return QuicTime::Delta::Zero(); |
198 } | 188 } |
199 return QuicTime::Delta::Infinite(); | 189 return QuicTime::Delta::Infinite(); |
200 } | 190 } |
201 | 191 |
202 QuicByteCount TcpCubicSender::SendWindow() const { | |
203 // What's the current send window in bytes. | |
204 return min(receive_window_, GetCongestionWindow()); | |
205 } | |
206 | |
207 QuicBandwidth TcpCubicSender::PacingRate() const { | 192 QuicBandwidth TcpCubicSender::PacingRate() const { |
208 // 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 |
209 // 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 |
210 // doesn't prevent us from filling the window. | 195 // doesn't prevent us from filling the window. |
211 return BandwidthEstimate().Scale(InSlowStart() ? 2 : 1.25); | 196 return BandwidthEstimate().Scale(InSlowStart() ? 2 : 1.25); |
212 } | 197 } |
213 | 198 |
214 QuicBandwidth TcpCubicSender::BandwidthEstimate() const { | 199 QuicBandwidth TcpCubicSender::BandwidthEstimate() const { |
215 if (rtt_stats_->SmoothedRtt().IsZero()) { | 200 if (rtt_stats_->SmoothedRtt().IsZero()) { |
216 LOG(DFATAL) << "In BandwidthEstimate(), smoothed RTT is zero!"; | 201 LOG(DFATAL) << "In BandwidthEstimate(), smoothed RTT is zero!"; |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
326 void TcpCubicSender::RevertRetransmissionTimeout() { | 311 void TcpCubicSender::RevertRetransmissionTimeout() { |
327 if (previous_congestion_window_ == 0) { | 312 if (previous_congestion_window_ == 0) { |
328 LOG(DFATAL) << "No previous congestion window to revert to."; | 313 LOG(DFATAL) << "No previous congestion window to revert to."; |
329 return; | 314 return; |
330 } | 315 } |
331 congestion_window_ = previous_congestion_window_; | 316 congestion_window_ = previous_congestion_window_; |
332 slowstart_threshold_ = previous_slowstart_threshold_; | 317 slowstart_threshold_ = previous_slowstart_threshold_; |
333 previous_congestion_window_ = 0; | 318 previous_congestion_window_ = 0; |
334 } | 319 } |
335 | 320 |
336 void TcpCubicSender::PrrOnPacketLost(QuicByteCount bytes_in_flight) { | |
337 prr_out_ = 0; | |
338 bytes_in_flight_before_loss_ = bytes_in_flight; | |
339 prr_delivered_ = 0; | |
340 ack_count_since_loss_ = 0; | |
341 } | |
342 | |
343 void TcpCubicSender::PrrOnPacketAcked(QuicByteCount acked_bytes) { | |
344 prr_delivered_ += acked_bytes; | |
345 ++ack_count_since_loss_; | |
346 } | |
347 | |
348 QuicTime::Delta TcpCubicSender::PrrTimeUntilSend( | |
349 QuicByteCount bytes_in_flight) const { | |
350 DCHECK(InRecovery()); | |
351 // Return QuicTime::Zero In order to ensure limited transmit always works. | |
352 if (prr_out_ == 0 || bytes_in_flight < kMaxSegmentSize) { | |
353 return QuicTime::Delta::Zero(); | |
354 } | |
355 if (SendWindow() > bytes_in_flight) { | |
356 // During PRR-SSRB, limit outgoing packets to 1 extra MSS per ack, instead | |
357 // of sending the entire available window. This prevents burst retransmits | |
358 // when more packets are lost than the CWND reduction. | |
359 // limit = MAX(prr_delivered - prr_out, DeliveredData) + MSS | |
360 if (prr_delivered_ + ack_count_since_loss_ * kMaxSegmentSize <= prr_out_) { | |
361 return QuicTime::Delta::Infinite(); | |
362 } | |
363 return QuicTime::Delta::Zero(); | |
364 } | |
365 // Implement Proportional Rate Reduction (RFC6937) | |
366 // Checks a simplified version of the PRR formula that doesn't use division: | |
367 // AvailableSendWindow = | |
368 // CEIL(prr_delivered * ssthresh / BytesInFlightAtLoss) - prr_sent | |
369 if (prr_delivered_ * slowstart_threshold_ * kMaxSegmentSize > | |
370 prr_out_ * bytes_in_flight_before_loss_) { | |
371 return QuicTime::Delta::Zero(); | |
372 } | |
373 return QuicTime::Delta::Infinite(); | |
374 } | |
375 | |
376 CongestionControlType TcpCubicSender::GetCongestionControlType() const { | 321 CongestionControlType TcpCubicSender::GetCongestionControlType() const { |
377 return reno_ ? kReno : kCubic; | 322 return reno_ ? kReno : kCubic; |
378 } | 323 } |
379 | 324 |
380 } // namespace net | 325 } // namespace net |
OLD | NEW |