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