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.h" 6 7 namespace net { 8 9 namespace { 10 // Constants based on TCP defaults. 11 const int64 kHybridStartLowWindow = 16; 12 const QuicByteCount kMaxSegmentSize = kMaxPacketSize; 13 const QuicByteCount kDefaultReceiveWindow = 64000; 14 const int64 kInitialCongestionWindow = 10; 15 const int kMaxBurstLength = 3; 16 const int kInitialRttMs = 60; // At a typical RTT 60 ms. 17 const float kAlpha = 0.125f; 18 const float kOneMinusAlpha = (1 - kAlpha); 19 const float kBeta = 0.25f; 20 const float kOneMinusBeta = (1 - kBeta); 21 }; // namespace 22 23 TcpCubicSender::TcpCubicSender( 24 const QuicClock* clock, 25 bool reno, 26 QuicTcpCongestionWindow max_tcp_congestion_window) 27 : hybrid_slow_start_(clock), 28 cubic_(clock), 29 reno_(reno), 30 congestion_window_count_(0), 31 receiver_congestion_window_(kDefaultReceiveWindow), 32 last_received_accumulated_number_of_lost_packets_(0), 33 bytes_in_flight_(0), 34 update_end_sequence_number_(true), 35 end_sequence_number_(0), 36 congestion_window_(kInitialCongestionWindow), 37 slowstart_threshold_(max_tcp_congestion_window), 38 max_tcp_congestion_window_(max_tcp_congestion_window), 39 delay_min_(QuicTime::Delta::Zero()), 40 smoothed_rtt_(QuicTime::Delta::Zero()), 41 mean_deviation_(QuicTime::Delta::Zero()) { 42 } 43 44 TcpCubicSender::~TcpCubicSender() { 45 } 46 47 void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame( 48 const QuicCongestionFeedbackFrame& feedback, 49 QuicTime feedback_receive_time, 50 const SentPacketsMap& /*sent_packets*/) { 51 if (last_received_accumulated_number_of_lost_packets_ != 52 feedback.tcp.accumulated_number_of_lost_packets) { 53 int recovered_lost_packets = 54 last_received_accumulated_number_of_lost_packets_ - 55 feedback.tcp.accumulated_number_of_lost_packets; 56 last_received_accumulated_number_of_lost_packets_ = 57 feedback.tcp.accumulated_number_of_lost_packets; 58 if (recovered_lost_packets > 0) { 59 OnIncomingLoss(feedback_receive_time); 60 } 61 } 62 receiver_congestion_window_ = feedback.tcp.receive_window; 63 } 64 65 void TcpCubicSender::OnIncomingAck( 66 QuicPacketSequenceNumber acked_sequence_number, QuicByteCount acked_bytes, 67 QuicTime::Delta rtt) { 68 bytes_in_flight_ -= acked_bytes; 69 CongestionAvoidance(acked_sequence_number); 70 AckAccounting(rtt); 71 if (end_sequence_number_ == acked_sequence_number) { 72 DLOG(INFO) << "Start update end sequence number @" << acked_sequence_number; 73 update_end_sequence_number_ = true; 74 } 75 } 76 77 void TcpCubicSender::OnIncomingLoss(QuicTime /*ack_receive_time*/) { 78 // In a normal TCP we would need to know the lowest missing packet to detect 79 // if we receive 3 missing packets. Here we get a missing packet for which we 80 // enter TCP Fast Retransmit immediately. 81 if (reno_) { 82 congestion_window_ = congestion_window_ >> 1; 83 slowstart_threshold_ = congestion_window_; 84 } else { 85 congestion_window_ = 86 cubic_.CongestionWindowAfterPacketLoss(congestion_window_); 87 slowstart_threshold_ = congestion_window_; 88 } 89 // Sanity, make sure that we don't end up with an empty window. 90 if (congestion_window_ == 0) { 91 congestion_window_ = 1; 92 } 93 DLOG(INFO) << "Incoming loss; congestion window:" << congestion_window_; 94 } 95 96 void TcpCubicSender::SentPacket(QuicTime /*sent_time*/, 97 QuicPacketSequenceNumber sequence_number, 98 QuicByteCount bytes, 99 Retransmission is_retransmission) { 100 bytes_in_flight_ += bytes; 101 if (is_retransmission == NOT_RETRANSMISSION && update_end_sequence_number_) { 102 end_sequence_number_ = sequence_number; 103 if (AvailableCongestionWindow() == 0) { 104 update_end_sequence_number_ = false; 105 DLOG(INFO) << "Stop update end sequence number @" << sequence_number; 106 } 107 } 108 } 109 110 void TcpCubicSender::AbandoningPacket(QuicPacketSequenceNumber sequence_number, 111 QuicByteCount abandoned_bytes) { 112 bytes_in_flight_ -= abandoned_bytes; 113 } 114 115 QuicTime::Delta TcpCubicSender::TimeUntilSend( 116 QuicTime now, 117 Retransmission is_retransmission, 118 HasRetransmittableData has_retransmittable_data, 119 IsHandshake handshake) { 120 if (is_retransmission == IS_RETRANSMISSION || 121 has_retransmittable_data == NO_RETRANSMITTABLE_DATA || 122 handshake == IS_HANDSHAKE) { 123 // For TCP we can always send a retransmission, or an ACK immediately. 124 // We also immediately send any handshake packet (CHLO, etc.). We provide 125 // this special dispensation for handshake messages in QUIC, although the 126 // concept is not present in TCP. 127 return QuicTime::Delta::Zero(); 128 } 129 if (AvailableCongestionWindow() == 0) { 130 return QuicTime::Delta::Infinite(); 131 } 132 return QuicTime::Delta::Zero(); 133 } 134 135 QuicByteCount TcpCubicSender::AvailableCongestionWindow() { 136 if (bytes_in_flight_ > CongestionWindow()) { 137 return 0; 138 } 139 return CongestionWindow() - bytes_in_flight_; 140 } 141 142 QuicByteCount TcpCubicSender::CongestionWindow() { 143 // What's the current congestion window in bytes. 144 return std::min(receiver_congestion_window_, 145 congestion_window_ * kMaxSegmentSize); 146 } 147 148 QuicBandwidth TcpCubicSender::BandwidthEstimate() { 149 // TODO(pwestin): make a long term estimate, based on RTT and loss rate? or 150 // instantaneous estimate? 151 // Throughput ~= (1/RTT)*sqrt(3/2p) 152 return QuicBandwidth::Zero(); 153 } 154 155 QuicTime::Delta TcpCubicSender::SmoothedRtt() { 156 if (smoothed_rtt_.IsZero()) { 157 return QuicTime::Delta::FromMilliseconds(kInitialRttMs); 158 } 159 return smoothed_rtt_; 160 } 161 162 QuicTime::Delta TcpCubicSender::RetransmissionDelay() { 163 return QuicTime::Delta::FromMicroseconds( 164 smoothed_rtt_.ToMicroseconds() + 4 * mean_deviation_.ToMicroseconds()); 165 } 166 167 void TcpCubicSender::Reset() { 168 delay_min_ = QuicTime::Delta::Zero(); 169 hybrid_slow_start_.Restart(); 170 } 171 172 bool TcpCubicSender::IsCwndLimited() const { 173 const QuicByteCount congestion_window_bytes = congestion_window_ * 174 kMaxSegmentSize; 175 if (bytes_in_flight_ >= congestion_window_bytes) { 176 return true; 177 } 178 const QuicByteCount tcp_max_burst = kMaxBurstLength * kMaxSegmentSize; 179 const QuicByteCount left = congestion_window_bytes - bytes_in_flight_; 180 return left <= tcp_max_burst; 181 } 182 183 // Called when we receive and ack. Normal TCP tracks how many packets one ack 184 // represents, but quic has a separate ack for each packet. 185 void TcpCubicSender::CongestionAvoidance(QuicPacketSequenceNumber ack) { 186 if (!IsCwndLimited()) { 187 // We don't update the congestion window unless we are close to using the 188 // window we have available. 189 return; 190 } 191 if (congestion_window_ < slowstart_threshold_) { 192 // Slow start. 193 if (hybrid_slow_start_.EndOfRound(ack)) { 194 hybrid_slow_start_.Reset(end_sequence_number_); 195 } 196 // congestion_window_cnt is the number of acks since last change of snd_cwnd 197 if (congestion_window_ < max_tcp_congestion_window_) { 198 // TCP slow start, exponentail growth, increase by one for each ACK. 199 congestion_window_++; 200 } 201 DLOG(INFO) << "Slow start; congestion window:" << congestion_window_; 202 } else { 203 if (congestion_window_ < max_tcp_congestion_window_) { 204 if (reno_) { 205 // Classic Reno congestion avoidance provided for testing. 206 if (congestion_window_count_ >= congestion_window_) { 207 congestion_window_++; 208 congestion_window_count_ = 0; 209 } else { 210 congestion_window_count_++; 211 } 212 DLOG(INFO) << "Reno; congestion window:" << congestion_window_; 213 } else { 214 congestion_window_ = cubic_.CongestionWindowAfterAck(congestion_window_, 215 delay_min_); 216 DLOG(INFO) << "Cubic; congestion window:" << congestion_window_; 217 } 218 } 219 } 220 } 221 222 // TODO(pwestin): what is the timout value? 223 void TcpCubicSender::OnTimeOut() { 224 cubic_.Reset(); 225 congestion_window_ = 1; 226 } 227 228 void TcpCubicSender::AckAccounting(QuicTime::Delta rtt) { 229 if (rtt.IsInfinite() || rtt.IsZero()) { 230 return; 231 } 232 // RTT can't be negative. 233 DCHECK_LT(0, rtt.ToMicroseconds()); 234 235 // TODO(pwestin): Discard delay samples right after fast recovery, 236 // during 1 second?. 237 238 // First time call or link delay decreases. 239 if (delay_min_.IsZero() || delay_min_ > rtt) { 240 delay_min_ = rtt; 241 } 242 // First time call. 243 if (smoothed_rtt_.IsZero()) { 244 smoothed_rtt_ = rtt; 245 mean_deviation_ = QuicTime::Delta::FromMicroseconds( 246 rtt.ToMicroseconds() / 2); 247 } else { 248 mean_deviation_ = QuicTime::Delta::FromMicroseconds( 249 kOneMinusBeta * mean_deviation_.ToMicroseconds() + 250 kBeta * abs(smoothed_rtt_.ToMicroseconds() - rtt.ToMicroseconds())); 251 smoothed_rtt_ = QuicTime::Delta::FromMicroseconds( 252 kOneMinusAlpha * smoothed_rtt_.ToMicroseconds() + 253 kAlpha * rtt.ToMicroseconds()); 254 DLOG(INFO) << "Cubic; mean_deviation_:" << mean_deviation_.ToMicroseconds(); 255 } 256 257 // Hybrid start triggers when cwnd is larger than some threshold. 258 if (congestion_window_ <= slowstart_threshold_ && 259 congestion_window_ >= kHybridStartLowWindow) { 260 if (!hybrid_slow_start_.started()) { 261 // Time to start the hybrid slow start. 262 hybrid_slow_start_.Reset(end_sequence_number_); 263 } 264 hybrid_slow_start_.Update(rtt, delay_min_); 265 if (hybrid_slow_start_.Exit()) { 266 slowstart_threshold_ = congestion_window_; 267 } 268 } 269 } 270 271 } // namespace net 272