| 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 |