• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 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 "quiche/quic/core/congestion_control/bbr2_misc.h"
6 
7 #include "quiche/quic/core/congestion_control/bandwidth_sampler.h"
8 #include "quiche/quic/core/quic_bandwidth.h"
9 #include "quiche/quic/core/quic_time.h"
10 #include "quiche/quic/core/quic_types.h"
11 #include "quiche/quic/platform/api/quic_flag_utils.h"
12 #include "quiche/quic/platform/api/quic_flags.h"
13 #include "quiche/quic/platform/api/quic_logging.h"
14 
15 namespace quic {
16 
RoundTripCounter()17 RoundTripCounter::RoundTripCounter() : round_trip_count_(0) {}
18 
OnPacketSent(QuicPacketNumber packet_number)19 void RoundTripCounter::OnPacketSent(QuicPacketNumber packet_number) {
20   QUICHE_DCHECK(!last_sent_packet_.IsInitialized() ||
21                 last_sent_packet_ < packet_number);
22   last_sent_packet_ = packet_number;
23 }
24 
OnPacketsAcked(QuicPacketNumber last_acked_packet)25 bool RoundTripCounter::OnPacketsAcked(QuicPacketNumber last_acked_packet) {
26   if (!end_of_round_trip_.IsInitialized() ||
27       last_acked_packet > end_of_round_trip_) {
28     round_trip_count_++;
29     end_of_round_trip_ = last_sent_packet_;
30     return true;
31   }
32   return false;
33 }
34 
RestartRound()35 void RoundTripCounter::RestartRound() {
36   end_of_round_trip_ = last_sent_packet_;
37 }
38 
MinRttFilter(QuicTime::Delta initial_min_rtt,QuicTime initial_min_rtt_timestamp)39 MinRttFilter::MinRttFilter(QuicTime::Delta initial_min_rtt,
40                            QuicTime initial_min_rtt_timestamp)
41     : min_rtt_(initial_min_rtt),
42       min_rtt_timestamp_(initial_min_rtt_timestamp) {}
43 
Update(QuicTime::Delta sample_rtt,QuicTime now)44 void MinRttFilter::Update(QuicTime::Delta sample_rtt, QuicTime now) {
45   if (sample_rtt <= QuicTime::Delta::Zero()) {
46     return;
47   }
48   if (sample_rtt < min_rtt_ || min_rtt_timestamp_ == QuicTime::Zero()) {
49     min_rtt_ = sample_rtt;
50     min_rtt_timestamp_ = now;
51   }
52 }
53 
ForceUpdate(QuicTime::Delta sample_rtt,QuicTime now)54 void MinRttFilter::ForceUpdate(QuicTime::Delta sample_rtt, QuicTime now) {
55   if (sample_rtt <= QuicTime::Delta::Zero()) {
56     return;
57   }
58   min_rtt_ = sample_rtt;
59   min_rtt_timestamp_ = now;
60 }
61 
Bbr2NetworkModel(const Bbr2Params * params,QuicTime::Delta initial_rtt,QuicTime initial_rtt_timestamp,float cwnd_gain,float pacing_gain,const BandwidthSampler * old_sampler)62 Bbr2NetworkModel::Bbr2NetworkModel(const Bbr2Params* params,
63                                    QuicTime::Delta initial_rtt,
64                                    QuicTime initial_rtt_timestamp,
65                                    float cwnd_gain, float pacing_gain,
66                                    const BandwidthSampler* old_sampler)
67     : params_(params),
68       bandwidth_sampler_([](QuicRoundTripCount max_height_tracker_window_length,
69                             const BandwidthSampler* old_sampler) {
70         if (old_sampler != nullptr) {
71           return BandwidthSampler(*old_sampler);
72         }
73         return BandwidthSampler(/*unacked_packet_map=*/nullptr,
74                                 max_height_tracker_window_length);
75       }(params->initial_max_ack_height_filter_window, old_sampler)),
76       min_rtt_filter_(initial_rtt, initial_rtt_timestamp),
77       cwnd_gain_(cwnd_gain),
78       pacing_gain_(pacing_gain) {}
79 
OnPacketSent(QuicTime sent_time,QuicByteCount bytes_in_flight,QuicPacketNumber packet_number,QuicByteCount bytes,HasRetransmittableData is_retransmittable)80 void Bbr2NetworkModel::OnPacketSent(QuicTime sent_time,
81                                     QuicByteCount bytes_in_flight,
82                                     QuicPacketNumber packet_number,
83                                     QuicByteCount bytes,
84                                     HasRetransmittableData is_retransmittable) {
85   // Updating the min here ensures a more realistic (0) value when flows exit
86   // quiescence.
87   if (bytes_in_flight < min_bytes_in_flight_in_round_) {
88     min_bytes_in_flight_in_round_ = bytes_in_flight;
89   }
90   if (bytes_in_flight + bytes >= inflight_hi_) {
91     inflight_hi_limited_in_round_ = true;
92   }
93   round_trip_counter_.OnPacketSent(packet_number);
94 
95   bandwidth_sampler_.OnPacketSent(sent_time, packet_number, bytes,
96                                   bytes_in_flight, is_retransmittable);
97 }
98 
OnCongestionEventStart(QuicTime event_time,const AckedPacketVector & acked_packets,const LostPacketVector & lost_packets,Bbr2CongestionEvent * congestion_event)99 void Bbr2NetworkModel::OnCongestionEventStart(
100     QuicTime event_time, const AckedPacketVector& acked_packets,
101     const LostPacketVector& lost_packets,
102     Bbr2CongestionEvent* congestion_event) {
103   const QuicByteCount prior_bytes_acked = total_bytes_acked();
104   const QuicByteCount prior_bytes_lost = total_bytes_lost();
105 
106   congestion_event->event_time = event_time;
107   congestion_event->end_of_round_trip =
108       acked_packets.empty() ? false
109                             : round_trip_counter_.OnPacketsAcked(
110                                   acked_packets.rbegin()->packet_number);
111 
112   BandwidthSamplerInterface::CongestionEventSample sample =
113       bandwidth_sampler_.OnCongestionEvent(event_time, acked_packets,
114                                            lost_packets, MaxBandwidth(),
115                                            bandwidth_lo(), RoundTripCount());
116 
117   if (sample.extra_acked == 0) {
118     cwnd_limited_before_aggregation_epoch_ =
119         congestion_event->prior_bytes_in_flight >= congestion_event->prior_cwnd;
120   }
121 
122   if (sample.last_packet_send_state.is_valid) {
123     congestion_event->last_packet_send_state = sample.last_packet_send_state;
124   }
125 
126   // Avoid updating |max_bandwidth_filter_| if a) this is a loss-only event, or
127   // b) all packets in |acked_packets| did not generate valid samples. (e.g. ack
128   // of ack-only packets). In both cases, total_bytes_acked() will not change.
129   if (prior_bytes_acked != total_bytes_acked()) {
130     QUIC_LOG_IF(WARNING, sample.sample_max_bandwidth.IsZero())
131         << total_bytes_acked() - prior_bytes_acked << " bytes from "
132         << acked_packets.size()
133         << " packets have been acked, but sample_max_bandwidth is zero.";
134     congestion_event->sample_max_bandwidth = sample.sample_max_bandwidth;
135     if (!sample.sample_is_app_limited ||
136         sample.sample_max_bandwidth > MaxBandwidth()) {
137       max_bandwidth_filter_.Update(congestion_event->sample_max_bandwidth);
138     }
139   }
140 
141   if (!sample.sample_rtt.IsInfinite()) {
142     congestion_event->sample_min_rtt = sample.sample_rtt;
143     min_rtt_filter_.Update(congestion_event->sample_min_rtt, event_time);
144   }
145 
146   congestion_event->bytes_acked = total_bytes_acked() - prior_bytes_acked;
147   congestion_event->bytes_lost = total_bytes_lost() - prior_bytes_lost;
148 
149   if (congestion_event->prior_bytes_in_flight >=
150       congestion_event->bytes_acked + congestion_event->bytes_lost) {
151     congestion_event->bytes_in_flight =
152         congestion_event->prior_bytes_in_flight -
153         congestion_event->bytes_acked - congestion_event->bytes_lost;
154   } else {
155     QUIC_LOG_FIRST_N(ERROR, 1)
156         << "prior_bytes_in_flight:" << congestion_event->prior_bytes_in_flight
157         << " is smaller than the sum of bytes_acked:"
158         << congestion_event->bytes_acked
159         << " and bytes_lost:" << congestion_event->bytes_lost;
160     congestion_event->bytes_in_flight = 0;
161   }
162 
163   if (congestion_event->bytes_lost > 0) {
164     bytes_lost_in_round_ += congestion_event->bytes_lost;
165     loss_events_in_round_++;
166   }
167 
168   if (congestion_event->bytes_acked > 0 &&
169       congestion_event->last_packet_send_state.is_valid &&
170       total_bytes_acked() >
171           congestion_event->last_packet_send_state.total_bytes_acked) {
172     QuicByteCount bytes_delivered =
173         total_bytes_acked() -
174         congestion_event->last_packet_send_state.total_bytes_acked;
175     max_bytes_delivered_in_round_ =
176         std::max(max_bytes_delivered_in_round_, bytes_delivered);
177   }
178   // TODO(ianswett) Consider treating any bytes lost as decreasing inflight,
179   // because it's a sign of overutilization, not underutilization.
180   if (congestion_event->bytes_in_flight < min_bytes_in_flight_in_round_) {
181     min_bytes_in_flight_in_round_ = congestion_event->bytes_in_flight;
182   }
183 
184   // |bandwidth_latest_| and |inflight_latest_| only increased within a round.
185   if (sample.sample_max_bandwidth > bandwidth_latest_) {
186     bandwidth_latest_ = sample.sample_max_bandwidth;
187   }
188 
189   if (sample.sample_max_inflight > inflight_latest_) {
190     inflight_latest_ = sample.sample_max_inflight;
191   }
192 
193   // Adapt lower bounds(bandwidth_lo and inflight_lo).
194   AdaptLowerBounds(*congestion_event);
195 
196   if (!congestion_event->end_of_round_trip) {
197     return;
198   }
199 
200   if (!sample.sample_max_bandwidth.IsZero()) {
201     bandwidth_latest_ = sample.sample_max_bandwidth;
202   }
203 
204   if (sample.sample_max_inflight > 0) {
205     inflight_latest_ = sample.sample_max_inflight;
206   }
207 }
208 
AdaptLowerBounds(const Bbr2CongestionEvent & congestion_event)209 void Bbr2NetworkModel::AdaptLowerBounds(
210     const Bbr2CongestionEvent& congestion_event) {
211   if (Params().bw_lo_mode_ == Bbr2Params::DEFAULT) {
212     if (!congestion_event.end_of_round_trip ||
213         congestion_event.is_probing_for_bandwidth) {
214       return;
215     }
216 
217     if (bytes_lost_in_round_ > 0) {
218       if (bandwidth_lo_.IsInfinite()) {
219         bandwidth_lo_ = MaxBandwidth();
220       }
221       bandwidth_lo_ =
222           std::max(bandwidth_latest_, bandwidth_lo_ * (1.0 - Params().beta));
223       QUIC_DVLOG(3) << "bandwidth_lo_ updated to " << bandwidth_lo_
224                     << ", bandwidth_latest_ is " << bandwidth_latest_;
225 
226       if (Params().ignore_inflight_lo) {
227         return;
228       }
229       if (inflight_lo_ == inflight_lo_default()) {
230         inflight_lo_ = congestion_event.prior_cwnd;
231       }
232       inflight_lo_ = std::max<QuicByteCount>(
233           inflight_latest_, inflight_lo_ * (1.0 - Params().beta));
234     }
235     return;
236   }
237 
238   // Params().bw_lo_mode_ != Bbr2Params::DEFAULT
239   if (congestion_event.bytes_lost == 0) {
240     return;
241   }
242   // Ignore losses from packets sent when probing for more bandwidth in
243   // STARTUP or PROBE_UP when they're lost in DRAIN or PROBE_DOWN.
244   if (pacing_gain_ < 1) {
245     return;
246   }
247   // Decrease bandwidth_lo whenever there is loss.
248   // Set bandwidth_lo_ if it is not yet set.
249   if (bandwidth_lo_.IsInfinite()) {
250     bandwidth_lo_ = MaxBandwidth();
251   }
252   // Save bandwidth_lo_ if it hasn't already been saved.
253   if (prior_bandwidth_lo_.IsZero()) {
254     prior_bandwidth_lo_ = bandwidth_lo_;
255   }
256   switch (Params().bw_lo_mode_) {
257     case Bbr2Params::MIN_RTT_REDUCTION:
258       bandwidth_lo_ =
259           bandwidth_lo_ - QuicBandwidth::FromBytesAndTimeDelta(
260                               congestion_event.bytes_lost, MinRtt());
261       break;
262     case Bbr2Params::INFLIGHT_REDUCTION: {
263       // Use a max of BDP and inflight to avoid starving app-limited flows.
264       const QuicByteCount effective_inflight =
265           std::max(BDP(), congestion_event.prior_bytes_in_flight);
266       // This could use bytes_lost_in_round if the bandwidth_lo_ was saved
267       // when entering 'recovery', but this BBRv2 implementation doesn't have
268       // recovery defined.
269       bandwidth_lo_ =
270           bandwidth_lo_ * ((effective_inflight - congestion_event.bytes_lost) /
271                            static_cast<double>(effective_inflight));
272       break;
273     }
274     case Bbr2Params::CWND_REDUCTION:
275       bandwidth_lo_ =
276           bandwidth_lo_ *
277           ((congestion_event.prior_cwnd - congestion_event.bytes_lost) /
278            static_cast<double>(congestion_event.prior_cwnd));
279       break;
280     case Bbr2Params::DEFAULT:
281       QUIC_BUG(quic_bug_10466_1) << "Unreachable case DEFAULT.";
282   }
283   QuicBandwidth last_bandwidth = bandwidth_latest_;
284   // sample_max_bandwidth will be Zero() if the loss is triggered by a timer
285   // expiring.  Ideally we'd use the most recent bandwidth sample,
286   // but bandwidth_latest is safer than Zero().
287   if (!congestion_event.sample_max_bandwidth.IsZero()) {
288     // bandwidth_latest_ is the max bandwidth for the round, but to allow
289     // fast, conservation style response to loss, use the last sample.
290     last_bandwidth = congestion_event.sample_max_bandwidth;
291   }
292   if (pacing_gain_ > Params().full_bw_threshold) {
293     // In STARTUP, pacing_gain_ is applied to bandwidth_lo_ in
294     // UpdatePacingRate, so this backs that multiplication out to allow the
295     // pacing rate to decrease, but not below
296     // last_bandwidth * full_bw_threshold.
297     // TODO(ianswett): Consider altering pacing_gain_ when in STARTUP instead.
298     bandwidth_lo_ =
299         std::max(bandwidth_lo_,
300                  last_bandwidth * (Params().full_bw_threshold / pacing_gain_));
301   } else {
302     // Ensure bandwidth_lo isn't lower than last_bandwidth.
303     bandwidth_lo_ = std::max(bandwidth_lo_, last_bandwidth);
304   }
305   // If it's the end of the round, ensure bandwidth_lo doesn't decrease more
306   // than beta.
307   if (congestion_event.end_of_round_trip) {
308     bandwidth_lo_ =
309         std::max(bandwidth_lo_, prior_bandwidth_lo_ * (1.0 - Params().beta));
310     prior_bandwidth_lo_ = QuicBandwidth::Zero();
311   }
312   // These modes ignore inflight_lo as well.
313 }
314 
OnCongestionEventFinish(QuicPacketNumber least_unacked_packet,const Bbr2CongestionEvent & congestion_event)315 void Bbr2NetworkModel::OnCongestionEventFinish(
316     QuicPacketNumber least_unacked_packet,
317     const Bbr2CongestionEvent& congestion_event) {
318   if (congestion_event.end_of_round_trip) {
319     OnNewRound();
320   }
321 
322   bandwidth_sampler_.RemoveObsoletePackets(least_unacked_packet);
323 }
324 
UpdateNetworkParameters(QuicTime::Delta rtt)325 void Bbr2NetworkModel::UpdateNetworkParameters(QuicTime::Delta rtt) {
326   if (!rtt.IsZero()) {
327     min_rtt_filter_.Update(rtt, MinRttTimestamp());
328   }
329 }
330 
MaybeExpireMinRtt(const Bbr2CongestionEvent & congestion_event)331 bool Bbr2NetworkModel::MaybeExpireMinRtt(
332     const Bbr2CongestionEvent& congestion_event) {
333   if (congestion_event.event_time <
334       (MinRttTimestamp() + Params().probe_rtt_period)) {
335     return false;
336   }
337   if (congestion_event.sample_min_rtt.IsInfinite()) {
338     return false;
339   }
340   QUIC_DVLOG(3) << "Replacing expired min rtt of " << min_rtt_filter_.Get()
341                 << " by " << congestion_event.sample_min_rtt << "  @ "
342                 << congestion_event.event_time;
343   min_rtt_filter_.ForceUpdate(congestion_event.sample_min_rtt,
344                               congestion_event.event_time);
345   return true;
346 }
347 
IsInflightTooHigh(const Bbr2CongestionEvent & congestion_event,int64_t max_loss_events) const348 bool Bbr2NetworkModel::IsInflightTooHigh(
349     const Bbr2CongestionEvent& congestion_event,
350     int64_t max_loss_events) const {
351   const SendTimeState& send_state = congestion_event.last_packet_send_state;
352   if (!send_state.is_valid) {
353     // Not enough information.
354     return false;
355   }
356 
357   if (loss_events_in_round() < max_loss_events) {
358     return false;
359   }
360 
361   const QuicByteCount inflight_at_send = BytesInFlight(send_state);
362   // TODO(wub): Consider total_bytes_lost() - send_state.total_bytes_lost, which
363   // is the total bytes lost when the largest numbered packet was inflight.
364   // bytes_lost_in_round_, OTOH, is the total bytes lost in the "current" round.
365   const QuicByteCount bytes_lost_in_round = bytes_lost_in_round_;
366 
367   QUIC_DVLOG(3) << "IsInflightTooHigh: loss_events_in_round:"
368                 << loss_events_in_round()
369 
370                 << " bytes_lost_in_round:" << bytes_lost_in_round
371                 << ", lost_in_round_threshold:"
372                 << inflight_at_send * Params().loss_threshold;
373 
374   if (inflight_at_send > 0 && bytes_lost_in_round > 0) {
375     QuicByteCount lost_in_round_threshold =
376         inflight_at_send * Params().loss_threshold;
377     if (bytes_lost_in_round > lost_in_round_threshold) {
378       return true;
379     }
380   }
381 
382   return false;
383 }
384 
RestartRoundEarly()385 void Bbr2NetworkModel::RestartRoundEarly() {
386   OnNewRound();
387   round_trip_counter_.RestartRound();
388   rounds_with_queueing_ = 0;
389 }
390 
OnNewRound()391 void Bbr2NetworkModel::OnNewRound() {
392   bytes_lost_in_round_ = 0;
393   loss_events_in_round_ = 0;
394   max_bytes_delivered_in_round_ = 0;
395   min_bytes_in_flight_in_round_ = std::numeric_limits<uint64_t>::max();
396   inflight_hi_limited_in_round_ = false;
397 }
398 
cap_inflight_lo(QuicByteCount cap)399 void Bbr2NetworkModel::cap_inflight_lo(QuicByteCount cap) {
400   if (Params().ignore_inflight_lo) {
401     return;
402   }
403   if (inflight_lo_ != inflight_lo_default() && inflight_lo_ > cap) {
404     inflight_lo_ = cap;
405   }
406 }
407 
inflight_hi_with_headroom() const408 QuicByteCount Bbr2NetworkModel::inflight_hi_with_headroom() const {
409   QuicByteCount headroom = inflight_hi_ * Params().inflight_hi_headroom;
410 
411   return inflight_hi_ > headroom ? inflight_hi_ - headroom : 0;
412 }
413 
HasBandwidthGrowth(const Bbr2CongestionEvent & congestion_event)414 bool Bbr2NetworkModel::HasBandwidthGrowth(
415     const Bbr2CongestionEvent& congestion_event) {
416   QUICHE_DCHECK(!full_bandwidth_reached_);
417   QUICHE_DCHECK(congestion_event.end_of_round_trip);
418 
419   QuicBandwidth threshold =
420       full_bandwidth_baseline_ * Params().full_bw_threshold;
421 
422   if (MaxBandwidth() >= threshold) {
423     QUIC_DVLOG(3) << " CheckBandwidthGrowth at end of round. max_bandwidth:"
424                   << MaxBandwidth() << ", threshold:" << threshold
425                   << " (Still growing)  @ " << congestion_event.event_time;
426     full_bandwidth_baseline_ = MaxBandwidth();
427     rounds_without_bandwidth_growth_ = 0;
428     return true;
429   }
430   ++rounds_without_bandwidth_growth_;
431 
432   // full_bandwidth_reached is only set to true when not app-limited, except
433   // when exit_startup_on_persistent_queue is true.
434   if (rounds_without_bandwidth_growth_ >= Params().startup_full_bw_rounds &&
435       !congestion_event.last_packet_send_state.is_app_limited) {
436     full_bandwidth_reached_ = true;
437   }
438   QUIC_DVLOG(3) << " CheckBandwidthGrowth at end of round. max_bandwidth:"
439                 << MaxBandwidth() << ", threshold:" << threshold
440                 << " rounds_without_growth:" << rounds_without_bandwidth_growth_
441                 << " full_bw_reached:" << full_bandwidth_reached_ << "  @ "
442                 << congestion_event.event_time;
443 
444   return false;
445 }
446 
CheckPersistentQueue(const Bbr2CongestionEvent & congestion_event,float target_gain)447 void Bbr2NetworkModel::CheckPersistentQueue(
448     const Bbr2CongestionEvent& congestion_event, float target_gain) {
449   QUICHE_DCHECK(congestion_event.end_of_round_trip);
450   QUICHE_DCHECK_NE(min_bytes_in_flight_in_round_,
451                    std::numeric_limits<uint64_t>::max());
452   QUICHE_DCHECK_GE(target_gain, Params().full_bw_threshold);
453   QuicByteCount target =
454       std::max(static_cast<QuicByteCount>(target_gain * BDP()),
455                BDP() + QueueingThresholdExtraBytes());
456   if (min_bytes_in_flight_in_round_ < target) {
457     rounds_with_queueing_ = 0;
458     return;
459   }
460   rounds_with_queueing_++;
461   if (rounds_with_queueing_ >= Params().max_startup_queue_rounds) {
462     full_bandwidth_reached_ = true;
463   }
464 }
465 
466 }  // namespace quic
467