Home | History | Annotate | Download | only in congestion_control
      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 #include <algorithm>
      8 
      9 #include "base/metrics/histogram.h"
     10 
     11 using std::max;
     12 
     13 namespace net {
     14 
     15 namespace {
     16 // Constants based on TCP defaults.
     17 // The minimum cwnd based on RFC 3782 (TCP NewReno) for cwnd reductions on a
     18 // fast retransmission.  The cwnd after a timeout is still 1.
     19 const QuicTcpCongestionWindow kMinimumCongestionWindow = 2;
     20 const int64 kHybridStartLowWindow = 16;
     21 const QuicByteCount kMaxSegmentSize = kDefaultTCPMSS;
     22 const QuicByteCount kDefaultReceiveWindow = 64000;
     23 const int64 kInitialCongestionWindow = 10;
     24 const int kMaxBurstLength = 3;
     25 // Constants used for RTT calculation.
     26 const int kInitialRttMs = 60;  // At a typical RTT 60 ms.
     27 const float kAlpha = 0.125f;
     28 const float kOneMinusAlpha = (1 - kAlpha);
     29 const float kBeta = 0.25f;
     30 const float kOneMinusBeta = (1 - kBeta);
     31 };  // namespace
     32 
     33 TcpCubicSender::TcpCubicSender(
     34     const QuicClock* clock,
     35     bool reno,
     36     QuicTcpCongestionWindow max_tcp_congestion_window)
     37     : hybrid_slow_start_(clock),
     38       cubic_(clock),
     39       reno_(reno),
     40       congestion_window_count_(0),
     41       receive_window_(kDefaultReceiveWindow),
     42       last_received_accumulated_number_of_lost_packets_(0),
     43       bytes_in_flight_(0),
     44       update_end_sequence_number_(true),
     45       end_sequence_number_(0),
     46       largest_sent_sequence_number_(0),
     47       largest_acked_sequence_number_(0),
     48       largest_sent_at_last_cutback_(0),
     49       congestion_window_(kInitialCongestionWindow),
     50       slowstart_threshold_(max_tcp_congestion_window),
     51       max_tcp_congestion_window_(max_tcp_congestion_window),
     52       delay_min_(QuicTime::Delta::Zero()),
     53       smoothed_rtt_(QuicTime::Delta::Zero()),
     54       mean_deviation_(QuicTime::Delta::Zero()) {
     55 }
     56 
     57 TcpCubicSender::~TcpCubicSender() {
     58   UMA_HISTOGRAM_COUNTS("Net.QuicSession.FinalTcpCwnd", congestion_window_);
     59 }
     60 
     61 void TcpCubicSender::SetMaxPacketSize(QuicByteCount /*max_packet_size*/) {
     62 }
     63 
     64 void TcpCubicSender::SetFromConfig(const QuicConfig& config, bool is_server) {
     65   if (is_server) {
     66     // Set the initial window size.
     67     congestion_window_ = config.server_initial_congestion_window();
     68   }
     69 }
     70 
     71 void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame(
     72     const QuicCongestionFeedbackFrame& feedback,
     73     QuicTime feedback_receive_time,
     74     const SentPacketsMap& /*sent_packets*/) {
     75   if (last_received_accumulated_number_of_lost_packets_ !=
     76       feedback.tcp.accumulated_number_of_lost_packets) {
     77     int recovered_lost_packets =
     78         last_received_accumulated_number_of_lost_packets_ -
     79         feedback.tcp.accumulated_number_of_lost_packets;
     80     last_received_accumulated_number_of_lost_packets_ =
     81         feedback.tcp.accumulated_number_of_lost_packets;
     82     if (recovered_lost_packets > 0) {
     83       // Assume the loss could be as late as the last acked packet.
     84       OnPacketLost(largest_acked_sequence_number_, feedback_receive_time);
     85     }
     86   }
     87   receive_window_ = feedback.tcp.receive_window;
     88 }
     89 
     90 void TcpCubicSender::OnPacketAcked(
     91     QuicPacketSequenceNumber acked_sequence_number,
     92     QuicByteCount acked_bytes,
     93     QuicTime::Delta rtt) {
     94   DCHECK_GE(bytes_in_flight_, acked_bytes);
     95   bytes_in_flight_ -= acked_bytes;
     96   largest_acked_sequence_number_ = max(acked_sequence_number,
     97                                        largest_acked_sequence_number_);
     98   CongestionAvoidance(acked_sequence_number);
     99   AckAccounting(rtt);
    100   if (end_sequence_number_ == acked_sequence_number) {
    101     DVLOG(1) << "Start update end sequence number @" << acked_sequence_number;
    102     update_end_sequence_number_ = true;
    103   }
    104 }
    105 
    106 void TcpCubicSender::OnPacketLost(QuicPacketSequenceNumber sequence_number,
    107                                   QuicTime /*ack_receive_time*/) {
    108   // TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets
    109   // already sent should be treated as a single loss event, since it's expected.
    110   if (sequence_number <= largest_sent_at_last_cutback_) {
    111     DVLOG(1) << "Ignoring loss for largest_missing:" << sequence_number
    112                << " because it was sent prior to the last CWND cutback.";
    113     return;
    114   }
    115 
    116   // In a normal TCP we would need to know the lowest missing packet to detect
    117   // if we receive 3 missing packets. Here we get a missing packet for which we
    118   // enter TCP Fast Retransmit immediately.
    119   if (reno_) {
    120     congestion_window_ = congestion_window_ >> 1;
    121     slowstart_threshold_ = congestion_window_;
    122   } else {
    123     congestion_window_ =
    124         cubic_.CongestionWindowAfterPacketLoss(congestion_window_);
    125     slowstart_threshold_ = congestion_window_;
    126   }
    127   // Enforce TCP's minimimum congestion window of 2*MSS.
    128   if (congestion_window_ < kMinimumCongestionWindow) {
    129     congestion_window_ = kMinimumCongestionWindow;
    130   }
    131   largest_sent_at_last_cutback_ = largest_sent_sequence_number_;
    132   DVLOG(1) << "Incoming loss; congestion window:" << congestion_window_;
    133 }
    134 
    135 bool TcpCubicSender::OnPacketSent(QuicTime /*sent_time*/,
    136                                   QuicPacketSequenceNumber sequence_number,
    137                                   QuicByteCount bytes,
    138                                   TransmissionType transmission_type,
    139                                   HasRetransmittableData is_retransmittable) {
    140   // Only update bytes_in_flight_ for data packets.
    141   if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) {
    142     return false;
    143   }
    144 
    145   bytes_in_flight_ += bytes;
    146   if (largest_sent_sequence_number_ < sequence_number) {
    147     // TODO(rch): Ensure that packets are really sent in order.
    148     // DCHECK_LT(largest_sent_sequence_number_, sequence_number);
    149     largest_sent_sequence_number_ = sequence_number;
    150   }
    151   if (transmission_type == NOT_RETRANSMISSION && update_end_sequence_number_) {
    152     end_sequence_number_ = sequence_number;
    153     if (AvailableSendWindow() == 0) {
    154       update_end_sequence_number_ = false;
    155       DVLOG(1) << "Stop update end sequence number @" << sequence_number;
    156     }
    157   }
    158   return true;
    159 }
    160 
    161 void TcpCubicSender::OnPacketAbandoned(QuicPacketSequenceNumber sequence_number,
    162                                        QuicByteCount abandoned_bytes) {
    163   DCHECK_GE(bytes_in_flight_, abandoned_bytes);
    164   bytes_in_flight_ -= abandoned_bytes;
    165 }
    166 
    167 QuicTime::Delta TcpCubicSender::TimeUntilSend(
    168     QuicTime /* now */,
    169     TransmissionType transmission_type,
    170     HasRetransmittableData has_retransmittable_data,
    171     IsHandshake handshake) {
    172   if (transmission_type == NACK_RETRANSMISSION ||
    173       has_retransmittable_data == NO_RETRANSMITTABLE_DATA ||
    174       handshake == IS_HANDSHAKE) {
    175     // For TCP we can always send an ACK immediately.
    176     // We also immediately send any handshake packet (CHLO, etc.).  We provide
    177     // this special dispensation for handshake messages in QUIC, although the
    178     // concept is not present in TCP.
    179     return QuicTime::Delta::Zero();
    180   }
    181   if (AvailableSendWindow() == 0) {
    182     return QuicTime::Delta::Infinite();
    183   }
    184   return QuicTime::Delta::Zero();
    185 }
    186 
    187 QuicByteCount TcpCubicSender::AvailableSendWindow() {
    188   if (bytes_in_flight_ > SendWindow()) {
    189     return 0;
    190   }
    191   return SendWindow() - bytes_in_flight_;
    192 }
    193 
    194 QuicByteCount TcpCubicSender::SendWindow() {
    195   // What's the current send window in bytes.
    196   return std::min(receive_window_, GetCongestionWindow());
    197 }
    198 
    199 QuicBandwidth TcpCubicSender::BandwidthEstimate() const {
    200   return QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(),
    201                                               SmoothedRtt());
    202 }
    203 
    204 QuicTime::Delta TcpCubicSender::SmoothedRtt() const {
    205   if (smoothed_rtt_.IsZero()) {
    206     return QuicTime::Delta::FromMilliseconds(kInitialRttMs);
    207   }
    208   return smoothed_rtt_;
    209 }
    210 
    211 QuicTime::Delta TcpCubicSender::RetransmissionDelay() const {
    212   return QuicTime::Delta::FromMicroseconds(
    213       smoothed_rtt_.ToMicroseconds() + 4 * mean_deviation_.ToMicroseconds());
    214 }
    215 
    216 QuicByteCount TcpCubicSender::GetCongestionWindow() const {
    217   return congestion_window_ * kMaxSegmentSize;
    218 }
    219 
    220 void TcpCubicSender::Reset() {
    221   delay_min_ = QuicTime::Delta::Zero();
    222   hybrid_slow_start_.Restart();
    223 }
    224 
    225 bool TcpCubicSender::IsCwndLimited() const {
    226   const QuicByteCount congestion_window_bytes = congestion_window_ *
    227       kMaxSegmentSize;
    228   if (bytes_in_flight_ >= congestion_window_bytes) {
    229     return true;
    230   }
    231   const QuicByteCount tcp_max_burst = kMaxBurstLength * kMaxSegmentSize;
    232   const QuicByteCount left = congestion_window_bytes - bytes_in_flight_;
    233   return left <= tcp_max_burst;
    234 }
    235 
    236 // Called when we receive an ack. Normal TCP tracks how many packets one ack
    237 // represents, but quic has a separate ack for each packet.
    238 void TcpCubicSender::CongestionAvoidance(QuicPacketSequenceNumber ack) {
    239   if (!IsCwndLimited()) {
    240     // We don't update the congestion window unless we are close to using the
    241     // window we have available.
    242     return;
    243   }
    244   if (congestion_window_ < slowstart_threshold_) {
    245     // Slow start.
    246     if (hybrid_slow_start_.EndOfRound(ack)) {
    247       hybrid_slow_start_.Reset(end_sequence_number_);
    248     }
    249     // congestion_window_cnt is the number of acks since last change of snd_cwnd
    250     if (congestion_window_ < max_tcp_congestion_window_) {
    251       // TCP slow start, exponential growth, increase by one for each ACK.
    252       congestion_window_++;
    253     }
    254     DVLOG(1) << "Slow start; congestion window:" << congestion_window_;
    255   } else {
    256     if (congestion_window_ < max_tcp_congestion_window_) {
    257       if (reno_) {
    258         // Classic Reno congestion avoidance provided for testing.
    259         if (congestion_window_count_ >= congestion_window_) {
    260           congestion_window_++;
    261           congestion_window_count_ = 0;
    262         } else {
    263           congestion_window_count_++;
    264         }
    265         DVLOG(1) << "Reno; congestion window:" << congestion_window_;
    266       } else {
    267         congestion_window_ = std::min(
    268             max_tcp_congestion_window_,
    269             cubic_.CongestionWindowAfterAck(congestion_window_, delay_min_));
    270         DVLOG(1) << "Cubic; congestion window:" << congestion_window_;
    271       }
    272     }
    273   }
    274 }
    275 
    276 void TcpCubicSender::OnRetransmissionTimeout() {
    277   cubic_.Reset();
    278   congestion_window_ = kMinimumCongestionWindow;
    279 }
    280 
    281 void TcpCubicSender::AckAccounting(QuicTime::Delta rtt) {
    282   if (rtt.IsInfinite() || rtt.IsZero()) {
    283     DVLOG(1) << "Ignoring rtt, because it's "
    284                << (rtt.IsZero() ? "Zero" : "Infinite");
    285     return;
    286   }
    287   // RTT can't be negative.
    288   DCHECK_LT(0, rtt.ToMicroseconds());
    289 
    290   // TODO(pwestin): Discard delay samples right after fast recovery,
    291   // during 1 second?.
    292 
    293   // First time call or link delay decreases.
    294   if (delay_min_.IsZero() || delay_min_ > rtt) {
    295     delay_min_ = rtt;
    296   }
    297   // First time call.
    298   if (smoothed_rtt_.IsZero()) {
    299     smoothed_rtt_ = rtt;
    300     mean_deviation_ = QuicTime::Delta::FromMicroseconds(
    301         rtt.ToMicroseconds() / 2);
    302   } else {
    303     mean_deviation_ = QuicTime::Delta::FromMicroseconds(
    304         kOneMinusBeta * mean_deviation_.ToMicroseconds() +
    305         kBeta * abs(smoothed_rtt_.ToMicroseconds() - rtt.ToMicroseconds()));
    306     smoothed_rtt_ = QuicTime::Delta::FromMicroseconds(
    307         kOneMinusAlpha * smoothed_rtt_.ToMicroseconds() +
    308         kAlpha * rtt.ToMicroseconds());
    309     DVLOG(1) << "Cubic; smoothed_rtt_:" << smoothed_rtt_.ToMicroseconds()
    310                << " mean_deviation_:" << mean_deviation_.ToMicroseconds();
    311   }
    312 
    313   // Hybrid start triggers when cwnd is larger than some threshold.
    314   if (congestion_window_ <= slowstart_threshold_ &&
    315       congestion_window_ >= kHybridStartLowWindow) {
    316     if (!hybrid_slow_start_.started()) {
    317       // Time to start the hybrid slow start.
    318       hybrid_slow_start_.Reset(end_sequence_number_);
    319     }
    320     hybrid_slow_start_.Update(rtt, delay_min_);
    321     if (hybrid_slow_start_.Exit()) {
    322       slowstart_threshold_ = congestion_window_;
    323     }
    324   }
    325 }
    326 
    327 }  // namespace net
    328