OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/quic/congestion_control/tcp_cubic_sender_packets.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/metrics/histogram_macros.h" | |
10 #include "net/quic/congestion_control/prr_sender.h" | |
11 #include "net/quic/congestion_control/rtt_stats.h" | |
12 #include "net/quic/crypto/crypto_protocol.h" | |
13 #include "net/quic/proto/cached_network_parameters.pb.h" | |
14 #include "net/quic/quic_bug_tracker.h" | |
15 #include "net/quic/quic_flags.h" | |
16 | |
17 using std::max; | |
18 using std::min; | |
19 | |
20 namespace net { | |
21 | |
22 namespace { | |
23 // Constants based on TCP defaults. | |
24 // The minimum cwnd based on RFC 3782 (TCP NewReno) for cwnd reductions on a | |
25 // fast retransmission. The cwnd after a timeout is still 1. | |
26 const QuicByteCount kMaxBurstBytes = 3 * kDefaultTCPMSS; | |
27 const float kRenoBeta = 0.7f; // Reno backoff factor. | |
28 const uint32_t kDefaultNumConnections = 2; // N-connection emulation. | |
29 const float kRateBasedExtraCwnd = 1.5f; // CWND for rate based sending. | |
30 } // namespace | |
31 | |
32 TcpCubicSenderBase::TcpCubicSenderBase(const QuicClock* clock, | |
33 const RttStats* rtt_stats, | |
34 bool reno, | |
35 QuicConnectionStats* stats) | |
36 : rtt_stats_(rtt_stats), | |
37 stats_(stats), | |
38 reno_(reno), | |
39 num_connections_(kDefaultNumConnections), | |
40 largest_sent_packet_number_(0), | |
41 largest_acked_packet_number_(0), | |
42 largest_sent_at_last_cutback_(0), | |
43 min4_mode_(false), | |
44 last_cutback_exited_slowstart_(false), | |
45 slow_start_large_reduction_(false), | |
46 rate_based_sending_(false), | |
47 no_prr_(false) {} | |
48 | |
49 TcpCubicSenderBase::~TcpCubicSenderBase() {} | |
50 | |
51 void TcpCubicSenderBase::SetFromConfig(const QuicConfig& config, | |
52 Perspective perspective) { | |
53 if (perspective == Perspective::IS_SERVER) { | |
54 if (config.HasReceivedConnectionOptions() && | |
55 ContainsQuicTag(config.ReceivedConnectionOptions(), kIW03)) { | |
56 // Initial window experiment. | |
57 SetCongestionWindowInPackets(3); | |
58 } | |
59 if (config.HasReceivedConnectionOptions() && | |
60 ContainsQuicTag(config.ReceivedConnectionOptions(), kIW10)) { | |
61 // Initial window experiment. | |
62 SetCongestionWindowInPackets(10); | |
63 } | |
64 if (config.HasReceivedConnectionOptions() && | |
65 ContainsQuicTag(config.ReceivedConnectionOptions(), kIW20)) { | |
66 // Initial window experiment. | |
67 SetCongestionWindowInPackets(20); | |
68 } | |
69 if (config.HasReceivedConnectionOptions() && | |
70 ContainsQuicTag(config.ReceivedConnectionOptions(), kIW50)) { | |
71 // Initial window experiment. | |
72 SetCongestionWindowInPackets(50); | |
73 } | |
74 if (config.HasReceivedConnectionOptions() && | |
75 ContainsQuicTag(config.ReceivedConnectionOptions(), kMIN1)) { | |
76 // Min CWND experiment. | |
77 SetMinCongestionWindowInPackets(1); | |
78 } | |
79 if (config.HasReceivedConnectionOptions() && | |
80 ContainsQuicTag(config.ReceivedConnectionOptions(), kMIN4)) { | |
81 // Min CWND of 4 experiment. | |
82 min4_mode_ = true; | |
83 SetMinCongestionWindowInPackets(1); | |
84 } | |
85 if (config.HasReceivedConnectionOptions() && | |
86 ContainsQuicTag(config.ReceivedConnectionOptions(), kSSLR)) { | |
87 // Slow Start Fast Exit experiment. | |
88 slow_start_large_reduction_ = true; | |
89 } | |
90 if (config.HasReceivedConnectionOptions() && | |
91 ContainsQuicTag(config.ReceivedConnectionOptions(), kNPRR)) { | |
92 // Use unity pacing instead of PRR. | |
93 no_prr_ = true; | |
94 } | |
95 if (FLAGS_quic_rate_based_sending && | |
96 config.HasReceivedConnectionOptions() && | |
97 ContainsQuicTag(config.ReceivedConnectionOptions(), kRATE)) { | |
98 // Rate based sending experiment | |
99 rate_based_sending_ = true; | |
100 } | |
101 } | |
102 } | |
103 | |
104 void TcpCubicSenderBase::ResumeConnectionState( | |
105 const CachedNetworkParameters& cached_network_params, | |
106 bool max_bandwidth_resumption) { | |
107 QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond( | |
108 max_bandwidth_resumption | |
109 ? cached_network_params.max_bandwidth_estimate_bytes_per_second() | |
110 : cached_network_params.bandwidth_estimate_bytes_per_second()); | |
111 QuicTime::Delta rtt = | |
112 QuicTime::Delta::FromMilliseconds(cached_network_params.min_rtt_ms()); | |
113 | |
114 SetCongestionWindowFromBandwidthAndRtt(bandwidth, rtt); | |
115 } | |
116 | |
117 void TcpCubicSenderBase::SetNumEmulatedConnections(int num_connections) { | |
118 num_connections_ = max(1, num_connections); | |
119 } | |
120 | |
121 float TcpCubicSenderBase::RenoBeta() const { | |
122 // kNConnectionBeta is the backoff factor after loss for our N-connection | |
123 // emulation, which emulates the effective backoff of an ensemble of N | |
124 // TCP-Reno connections on a single loss event. The effective multiplier is | |
125 // computed as: | |
126 return (num_connections_ - 1 + kRenoBeta) / num_connections_; | |
127 } | |
128 | |
129 void TcpCubicSenderBase::OnCongestionEvent( | |
130 bool rtt_updated, | |
131 QuicByteCount bytes_in_flight, | |
132 const CongestionVector& acked_packets, | |
133 const CongestionVector& lost_packets) { | |
134 if (rtt_updated && InSlowStart() && | |
135 hybrid_slow_start_.ShouldExitSlowStart( | |
136 rtt_stats_->latest_rtt(), rtt_stats_->min_rtt(), | |
137 GetCongestionWindow() / kDefaultTCPMSS)) { | |
138 ExitSlowstart(); | |
139 } | |
140 for (CongestionVector::const_iterator it = lost_packets.begin(); | |
141 it != lost_packets.end(); ++it) { | |
142 OnPacketLost(it->first, it->second, bytes_in_flight); | |
143 } | |
144 for (CongestionVector::const_iterator it = acked_packets.begin(); | |
145 it != acked_packets.end(); ++it) { | |
146 OnPacketAcked(it->first, it->second, bytes_in_flight); | |
147 } | |
148 } | |
149 | |
150 void TcpCubicSenderBase::OnPacketAcked(QuicPacketNumber acked_packet_number, | |
151 QuicByteCount acked_bytes, | |
152 QuicByteCount bytes_in_flight) { | |
153 largest_acked_packet_number_ = | |
154 max(acked_packet_number, largest_acked_packet_number_); | |
155 if (InRecovery()) { | |
156 if (!no_prr_) { | |
157 // PRR is used when in recovery. | |
158 prr_.OnPacketAcked(acked_bytes); | |
159 } | |
160 return; | |
161 } | |
162 MaybeIncreaseCwnd(acked_packet_number, acked_bytes, bytes_in_flight); | |
163 if (InSlowStart()) { | |
164 hybrid_slow_start_.OnPacketAcked(acked_packet_number); | |
165 } | |
166 } | |
167 | |
168 bool TcpCubicSenderBase::OnPacketSent( | |
169 QuicTime /*sent_time*/, | |
170 QuicByteCount /*bytes_in_flight*/, | |
171 QuicPacketNumber packet_number, | |
172 QuicByteCount bytes, | |
173 HasRetransmittableData is_retransmittable) { | |
174 if (InSlowStart()) { | |
175 ++(stats_->slowstart_packets_sent); | |
176 } | |
177 | |
178 // Only update bytes_in_flight_ for data packets. | |
179 if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) { | |
180 return false; | |
181 } | |
182 if (InRecovery()) { | |
183 // PRR is used when in recovery. | |
184 prr_.OnPacketSent(bytes); | |
185 } | |
186 DCHECK_LT(largest_sent_packet_number_, packet_number); | |
187 largest_sent_packet_number_ = packet_number; | |
188 hybrid_slow_start_.OnPacketSent(packet_number); | |
189 return true; | |
190 } | |
191 | |
192 QuicTime::Delta TcpCubicSenderBase::TimeUntilSend( | |
193 QuicTime /* now */, | |
194 QuicByteCount bytes_in_flight) const { | |
195 if (!no_prr_ && InRecovery()) { | |
196 // PRR is used when in recovery. | |
197 return prr_.TimeUntilSend(GetCongestionWindow(), bytes_in_flight, | |
198 GetSlowStartThreshold()); | |
199 } | |
200 if (GetCongestionWindow() > bytes_in_flight) { | |
201 return QuicTime::Delta::Zero(); | |
202 } | |
203 if (min4_mode_ && bytes_in_flight < 4 * kDefaultTCPMSS) { | |
204 return QuicTime::Delta::Zero(); | |
205 } | |
206 if (rate_based_sending_ && | |
207 GetCongestionWindow() * kRateBasedExtraCwnd > bytes_in_flight) { | |
208 return QuicTime::Delta::Zero(); | |
209 } | |
210 return QuicTime::Delta::Infinite(); | |
211 } | |
212 | |
213 QuicBandwidth TcpCubicSenderBase::PacingRate( | |
214 QuicByteCount bytes_in_flight) const { | |
215 // We pace at twice the rate of the underlying sender's bandwidth estimate | |
216 // during slow start and 1.25x during congestion avoidance to ensure pacing | |
217 // doesn't prevent us from filling the window. | |
218 QuicTime::Delta srtt = rtt_stats_->smoothed_rtt(); | |
219 if (srtt.IsZero()) { | |
220 srtt = QuicTime::Delta::FromMicroseconds(rtt_stats_->initial_rtt_us()); | |
221 } | |
222 const QuicBandwidth bandwidth = | |
223 QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(), srtt); | |
224 if (rate_based_sending_ && bytes_in_flight > GetCongestionWindow()) { | |
225 // Rate based sending allows sending more than CWND, but reduces the pacing | |
226 // rate when the bytes in flight is more than the CWND to 75% of bandwidth. | |
227 return 0.75 * bandwidth; | |
228 } | |
229 return bandwidth * (InSlowStart() ? 2 : (no_prr_ && InRecovery() ? 1 : 1.25)); | |
230 } | |
231 | |
232 QuicBandwidth TcpCubicSenderBase::BandwidthEstimate() const { | |
233 QuicTime::Delta srtt = rtt_stats_->smoothed_rtt(); | |
234 if (srtt.IsZero()) { | |
235 // If we haven't measured an rtt, the bandwidth estimate is unknown. | |
236 return QuicBandwidth::Zero(); | |
237 } | |
238 return QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(), srtt); | |
239 } | |
240 | |
241 QuicTime::Delta TcpCubicSenderBase::RetransmissionDelay() const { | |
242 if (rtt_stats_->smoothed_rtt().IsZero()) { | |
243 return QuicTime::Delta::Zero(); | |
244 } | |
245 return rtt_stats_->smoothed_rtt() + 4 * rtt_stats_->mean_deviation(); | |
246 } | |
247 | |
248 bool TcpCubicSenderBase::InSlowStart() const { | |
249 return GetCongestionWindow() < GetSlowStartThreshold(); | |
250 } | |
251 | |
252 bool TcpCubicSenderBase::IsCwndLimited(QuicByteCount bytes_in_flight) const { | |
253 const QuicByteCount congestion_window = GetCongestionWindow(); | |
254 if (bytes_in_flight >= congestion_window) { | |
255 return true; | |
256 } | |
257 const QuicByteCount available_bytes = congestion_window - bytes_in_flight; | |
258 const bool slow_start_limited = | |
259 InSlowStart() && bytes_in_flight > congestion_window / 2; | |
260 return slow_start_limited || available_bytes <= kMaxBurstBytes; | |
261 } | |
262 | |
263 bool TcpCubicSenderBase::InRecovery() const { | |
264 return largest_acked_packet_number_ <= largest_sent_at_last_cutback_ && | |
265 largest_acked_packet_number_ != 0; | |
266 } | |
267 | |
268 void TcpCubicSenderBase::OnRetransmissionTimeout(bool packets_retransmitted) { | |
269 largest_sent_at_last_cutback_ = 0; | |
270 if (!packets_retransmitted) { | |
271 return; | |
272 } | |
273 hybrid_slow_start_.Restart(); | |
274 HandleRetransmissionTimeout(); | |
275 } | |
276 | |
277 void TcpCubicSenderBase::OnConnectionMigration() { | |
278 hybrid_slow_start_.Restart(); | |
279 prr_ = PrrSender(); | |
280 largest_sent_packet_number_ = 0; | |
281 largest_acked_packet_number_ = 0; | |
282 largest_sent_at_last_cutback_ = 0; | |
283 last_cutback_exited_slowstart_ = false; | |
284 } | |
285 | |
286 } // namespace net | |
OLD | NEW |