• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TcpCubicSender(const QuicClock * clock,bool reno,QuicTcpCongestionWindow max_tcp_congestion_window)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 
~TcpCubicSender()57 TcpCubicSender::~TcpCubicSender() {
58   UMA_HISTOGRAM_COUNTS("Net.QuicSession.FinalTcpCwnd", congestion_window_);
59 }
60 
SetMaxPacketSize(QuicByteCount)61 void TcpCubicSender::SetMaxPacketSize(QuicByteCount /*max_packet_size*/) {
62 }
63 
SetFromConfig(const QuicConfig & config,bool is_server)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 
OnIncomingQuicCongestionFeedbackFrame(const QuicCongestionFeedbackFrame & feedback,QuicTime feedback_receive_time,const SentPacketsMap &)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 
OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,QuicByteCount acked_bytes,QuicTime::Delta rtt)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 
OnPacketLost(QuicPacketSequenceNumber sequence_number,QuicTime)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 
OnPacketSent(QuicTime,QuicPacketSequenceNumber sequence_number,QuicByteCount bytes,TransmissionType transmission_type,HasRetransmittableData is_retransmittable)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 
OnPacketAbandoned(QuicPacketSequenceNumber sequence_number,QuicByteCount abandoned_bytes)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 
TimeUntilSend(QuicTime,TransmissionType transmission_type,HasRetransmittableData has_retransmittable_data,IsHandshake handshake)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 
AvailableSendWindow()187 QuicByteCount TcpCubicSender::AvailableSendWindow() {
188   if (bytes_in_flight_ > SendWindow()) {
189     return 0;
190   }
191   return SendWindow() - bytes_in_flight_;
192 }
193 
SendWindow()194 QuicByteCount TcpCubicSender::SendWindow() {
195   // What's the current send window in bytes.
196   return std::min(receive_window_, GetCongestionWindow());
197 }
198 
BandwidthEstimate() const199 QuicBandwidth TcpCubicSender::BandwidthEstimate() const {
200   return QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(),
201                                               SmoothedRtt());
202 }
203 
SmoothedRtt() const204 QuicTime::Delta TcpCubicSender::SmoothedRtt() const {
205   if (smoothed_rtt_.IsZero()) {
206     return QuicTime::Delta::FromMilliseconds(kInitialRttMs);
207   }
208   return smoothed_rtt_;
209 }
210 
RetransmissionDelay() const211 QuicTime::Delta TcpCubicSender::RetransmissionDelay() const {
212   return QuicTime::Delta::FromMicroseconds(
213       smoothed_rtt_.ToMicroseconds() + 4 * mean_deviation_.ToMicroseconds());
214 }
215 
GetCongestionWindow() const216 QuicByteCount TcpCubicSender::GetCongestionWindow() const {
217   return congestion_window_ * kMaxSegmentSize;
218 }
219 
Reset()220 void TcpCubicSender::Reset() {
221   delay_min_ = QuicTime::Delta::Zero();
222   hybrid_slow_start_.Restart();
223 }
224 
IsCwndLimited() const225 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.
CongestionAvoidance(QuicPacketSequenceNumber ack)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 
OnRetransmissionTimeout()276 void TcpCubicSender::OnRetransmissionTimeout() {
277   cubic_.Reset();
278   congestion_window_ = kMinimumCongestionWindow;
279 }
280 
AckAccounting(QuicTime::Delta rtt)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