xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/congestion_control/bbr2_probe_bw.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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_probe_bw.h"
6 
7 #include "quiche/quic/core/congestion_control/bbr2_misc.h"
8 #include "quiche/quic/core/congestion_control/bbr2_sender.h"
9 #include "quiche/quic/core/quic_bandwidth.h"
10 #include "quiche/quic/core/quic_time.h"
11 #include "quiche/quic/core/quic_types.h"
12 #include "quiche/quic/platform/api/quic_flag_utils.h"
13 #include "quiche/quic/platform/api/quic_logging.h"
14 
15 namespace quic {
16 
Enter(QuicTime now,const Bbr2CongestionEvent *)17 void Bbr2ProbeBwMode::Enter(QuicTime now,
18                             const Bbr2CongestionEvent* /*congestion_event*/) {
19   if (cycle_.phase == CyclePhase::PROBE_NOT_STARTED) {
20     // First time entering PROBE_BW. Start a new probing cycle.
21     EnterProbeDown(/*probed_too_high=*/false, /*stopped_risky_probe=*/false,
22                    now);
23   } else {
24     // Transitioning from PROBE_RTT to PROBE_BW. Re-enter the last phase before
25     // PROBE_RTT.
26     QUICHE_DCHECK(cycle_.phase == CyclePhase::PROBE_CRUISE ||
27                   cycle_.phase == CyclePhase::PROBE_REFILL);
28     cycle_.cycle_start_time = now;
29     if (cycle_.phase == CyclePhase::PROBE_CRUISE) {
30       EnterProbeCruise(now);
31     } else if (cycle_.phase == CyclePhase::PROBE_REFILL) {
32       EnterProbeRefill(cycle_.probe_up_rounds, now);
33     }
34   }
35 }
36 
OnCongestionEvent(QuicByteCount prior_in_flight,QuicTime event_time,const AckedPacketVector &,const LostPacketVector &,const Bbr2CongestionEvent & congestion_event)37 Bbr2Mode Bbr2ProbeBwMode::OnCongestionEvent(
38     QuicByteCount prior_in_flight, QuicTime event_time,
39     const AckedPacketVector& /*acked_packets*/,
40     const LostPacketVector& /*lost_packets*/,
41     const Bbr2CongestionEvent& congestion_event) {
42   QUICHE_DCHECK_NE(cycle_.phase, CyclePhase::PROBE_NOT_STARTED);
43 
44   if (congestion_event.end_of_round_trip) {
45     if (cycle_.cycle_start_time != event_time) {
46       ++cycle_.rounds_since_probe;
47     }
48     if (cycle_.phase_start_time != event_time) {
49       ++cycle_.rounds_in_phase;
50     }
51   }
52 
53   bool switch_to_probe_rtt = false;
54 
55   if (cycle_.phase == CyclePhase::PROBE_UP) {
56     UpdateProbeUp(prior_in_flight, congestion_event);
57   } else if (cycle_.phase == CyclePhase::PROBE_DOWN) {
58     UpdateProbeDown(prior_in_flight, congestion_event);
59     // Maybe transition to PROBE_RTT at the end of this cycle.
60     if (cycle_.phase != CyclePhase::PROBE_DOWN &&
61         model_->MaybeExpireMinRtt(congestion_event)) {
62       switch_to_probe_rtt = true;
63     }
64   } else if (cycle_.phase == CyclePhase::PROBE_CRUISE) {
65     UpdateProbeCruise(congestion_event);
66   } else if (cycle_.phase == CyclePhase::PROBE_REFILL) {
67     UpdateProbeRefill(congestion_event);
68   }
69 
70   // Do not need to set the gains if switching to PROBE_RTT, they will be set
71   // when Bbr2ProbeRttMode::Enter is called.
72   if (!switch_to_probe_rtt) {
73     model_->set_pacing_gain(PacingGainForPhase(cycle_.phase));
74     model_->set_cwnd_gain(Params().probe_bw_cwnd_gain);
75   }
76 
77   return switch_to_probe_rtt ? Bbr2Mode::PROBE_RTT : Bbr2Mode::PROBE_BW;
78 }
79 
GetCwndLimits() const80 Limits<QuicByteCount> Bbr2ProbeBwMode::GetCwndLimits() const {
81   if (cycle_.phase == CyclePhase::PROBE_CRUISE) {
82     return NoGreaterThan(
83         std::min(model_->inflight_lo(), model_->inflight_hi_with_headroom()));
84   }
85   if (Params().probe_up_ignore_inflight_hi &&
86       cycle_.phase == CyclePhase::PROBE_UP) {
87     // Similar to STARTUP.
88     return NoGreaterThan(model_->inflight_lo());
89   }
90 
91   return NoGreaterThan(std::min(model_->inflight_lo(), model_->inflight_hi()));
92 }
93 
IsProbingForBandwidth() const94 bool Bbr2ProbeBwMode::IsProbingForBandwidth() const {
95   return cycle_.phase == CyclePhase::PROBE_REFILL ||
96          cycle_.phase == CyclePhase::PROBE_UP;
97 }
98 
OnExitQuiescence(QuicTime now,QuicTime quiescence_start_time)99 Bbr2Mode Bbr2ProbeBwMode::OnExitQuiescence(QuicTime now,
100                                            QuicTime quiescence_start_time) {
101   QUIC_DVLOG(3) << sender_ << " Postponing min_rtt_timestamp("
102                 << model_->MinRttTimestamp() << ") by "
103                 << now - quiescence_start_time;
104   model_->PostponeMinRttTimestamp(now - quiescence_start_time);
105   return Bbr2Mode::PROBE_BW;
106 }
107 
108 // TODO(ianswett): Remove prior_in_flight from UpdateProbeDown.
UpdateProbeDown(QuicByteCount prior_in_flight,const Bbr2CongestionEvent & congestion_event)109 void Bbr2ProbeBwMode::UpdateProbeDown(
110     QuicByteCount prior_in_flight,
111     const Bbr2CongestionEvent& congestion_event) {
112   QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_DOWN);
113 
114   if (cycle_.rounds_in_phase == 1 && congestion_event.end_of_round_trip) {
115     cycle_.is_sample_from_probing = false;
116 
117     if (!congestion_event.last_packet_send_state.is_app_limited) {
118       QUIC_DVLOG(2)
119           << sender_
120           << " Advancing max bw filter after one round in PROBE_DOWN.";
121       model_->AdvanceMaxBandwidthFilter();
122       cycle_.has_advanced_max_bw = true;
123     }
124 
125     if (last_cycle_stopped_risky_probe_ && !last_cycle_probed_too_high_) {
126       EnterProbeRefill(/*probe_up_rounds=*/0, congestion_event.event_time);
127       return;
128     }
129   }
130 
131   MaybeAdaptUpperBounds(congestion_event);
132 
133   if (IsTimeToProbeBandwidth(congestion_event)) {
134     EnterProbeRefill(/*probe_up_rounds=*/0, congestion_event.event_time);
135     return;
136   }
137 
138   if (HasStayedLongEnoughInProbeDown(congestion_event)) {
139     QUIC_DVLOG(3) << sender_ << " Proportional time based PROBE_DOWN exit";
140     EnterProbeCruise(congestion_event.event_time);
141     return;
142   }
143 
144   const QuicByteCount inflight_with_headroom =
145       model_->inflight_hi_with_headroom();
146   QUIC_DVLOG(3)
147       << sender_
148       << " Checking if have enough inflight headroom. prior_in_flight:"
149       << prior_in_flight << " congestion_event.bytes_in_flight:"
150       << congestion_event.bytes_in_flight
151       << ", inflight_with_headroom:" << inflight_with_headroom;
152   QuicByteCount bytes_in_flight = congestion_event.bytes_in_flight;
153 
154   if (bytes_in_flight > inflight_with_headroom) {
155     // Stay in PROBE_DOWN.
156     return;
157   }
158 
159   // Transition to PROBE_CRUISE iff we've drained to target.
160   QuicByteCount bdp = model_->BDP();
161   QUIC_DVLOG(3) << sender_ << " Checking if drained to target. bytes_in_flight:"
162                 << bytes_in_flight << ", bdp:" << bdp;
163   if (bytes_in_flight < bdp) {
164     EnterProbeCruise(congestion_event.event_time);
165   }
166 }
167 
MaybeAdaptUpperBounds(const Bbr2CongestionEvent & congestion_event)168 Bbr2ProbeBwMode::AdaptUpperBoundsResult Bbr2ProbeBwMode::MaybeAdaptUpperBounds(
169     const Bbr2CongestionEvent& congestion_event) {
170   const SendTimeState& send_state = congestion_event.last_packet_send_state;
171   if (!send_state.is_valid) {
172     QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
173                   << ": NOT_ADAPTED_INVALID_SAMPLE";
174     return NOT_ADAPTED_INVALID_SAMPLE;
175   }
176 
177   // TODO(ianswett): Rename to bytes_delivered if
178   // use_bytes_delivered_for_inflight_hi is default enabled.
179   QuicByteCount inflight_at_send = BytesInFlight(send_state);
180   if (Params().use_bytes_delivered_for_inflight_hi) {
181     if (congestion_event.last_packet_send_state.total_bytes_acked <=
182         model_->total_bytes_acked()) {
183       inflight_at_send =
184           model_->total_bytes_acked() -
185           congestion_event.last_packet_send_state.total_bytes_acked;
186     } else {
187       QUIC_BUG(quic_bug_10436_1)
188           << "Total_bytes_acked(" << model_->total_bytes_acked()
189           << ") < send_state.total_bytes_acked("
190           << congestion_event.last_packet_send_state.total_bytes_acked << ")";
191     }
192   }
193   // TODO(ianswett): Inflight too high is really checking for loss, not
194   // inflight.
195   if (model_->IsInflightTooHigh(congestion_event,
196                                 Params().probe_bw_full_loss_count)) {
197     if (cycle_.is_sample_from_probing) {
198       cycle_.is_sample_from_probing = false;
199       if (!send_state.is_app_limited ||
200           Params().max_probe_up_queue_rounds > 0) {
201         const QuicByteCount inflight_target =
202             sender_->GetTargetBytesInflight() * (1.0 - Params().beta);
203         if (inflight_at_send >= inflight_target) {
204           // The new code does not change behavior.
205           QUIC_CODE_COUNT(quic_bbr2_cut_inflight_hi_gradually_noop);
206         } else {
207           // The new code actually cuts inflight_hi slower than before.
208           QUIC_CODE_COUNT(quic_bbr2_cut_inflight_hi_gradually_in_effect);
209         }
210         if (Params().limit_inflight_hi_by_max_delivered) {
211           QuicByteCount new_inflight_hi =
212               std::max(inflight_at_send, inflight_target);
213           if (new_inflight_hi >= model_->max_bytes_delivered_in_round()) {
214             QUIC_CODE_COUNT(quic_bbr2_cut_inflight_hi_max_delivered_noop);
215           } else {
216             QUIC_CODE_COUNT(quic_bbr2_cut_inflight_hi_max_delivered_in_effect);
217             new_inflight_hi = model_->max_bytes_delivered_in_round();
218           }
219           QUIC_DVLOG(3) << sender_
220                         << " Setting inflight_hi due to loss. new_inflight_hi:"
221                         << new_inflight_hi
222                         << ", inflight_at_send:" << inflight_at_send
223                         << ", inflight_target:" << inflight_target
224                         << ", max_bytes_delivered_in_round:"
225                         << model_->max_bytes_delivered_in_round() << "  @ "
226                         << congestion_event.event_time;
227           model_->set_inflight_hi(new_inflight_hi);
228         } else {
229           model_->set_inflight_hi(std::max(inflight_at_send, inflight_target));
230         }
231       }
232 
233       QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
234                     << ": ADAPTED_PROBED_TOO_HIGH";
235       return ADAPTED_PROBED_TOO_HIGH;
236     }
237     return ADAPTED_OK;
238   }
239 
240   if (model_->inflight_hi() == model_->inflight_hi_default()) {
241     QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
242                   << ": NOT_ADAPTED_INFLIGHT_HIGH_NOT_SET";
243     return NOT_ADAPTED_INFLIGHT_HIGH_NOT_SET;
244   }
245 
246   // Raise the upper bound for inflight.
247   if (inflight_at_send > model_->inflight_hi()) {
248     QUIC_DVLOG(3)
249         << sender_ << " " << cycle_.phase
250         << ": Adapting inflight_hi from inflight_at_send. inflight_at_send:"
251         << inflight_at_send << ", old inflight_hi:" << model_->inflight_hi();
252     model_->set_inflight_hi(inflight_at_send);
253   }
254 
255   return ADAPTED_OK;
256 }
257 
IsTimeToProbeBandwidth(const Bbr2CongestionEvent & congestion_event) const258 bool Bbr2ProbeBwMode::IsTimeToProbeBandwidth(
259     const Bbr2CongestionEvent& congestion_event) const {
260   if (HasCycleLasted(cycle_.probe_wait_time, congestion_event)) {
261     return true;
262   }
263 
264   if (IsTimeToProbeForRenoCoexistence(1.0, congestion_event)) {
265     ++sender_->connection_stats_->bbr_num_short_cycles_for_reno_coexistence;
266     return true;
267   }
268   return false;
269 }
270 
271 // QUIC only. Used to prevent a Bbr2 flow from staying in PROBE_DOWN for too
272 // long, as seen in some multi-sender simulator tests.
HasStayedLongEnoughInProbeDown(const Bbr2CongestionEvent & congestion_event) const273 bool Bbr2ProbeBwMode::HasStayedLongEnoughInProbeDown(
274     const Bbr2CongestionEvent& congestion_event) const {
275   // Stay in PROBE_DOWN for at most the time of a min rtt, as it is done in
276   // BBRv1.
277   // TODO(wub): Consider exit after a full round instead, which typically
278   // indicates most(if not all) packets sent during PROBE_UP have been acked.
279   return HasPhaseLasted(model_->MinRtt(), congestion_event);
280 }
281 
HasCycleLasted(QuicTime::Delta duration,const Bbr2CongestionEvent & congestion_event) const282 bool Bbr2ProbeBwMode::HasCycleLasted(
283     QuicTime::Delta duration,
284     const Bbr2CongestionEvent& congestion_event) const {
285   bool result =
286       (congestion_event.event_time - cycle_.cycle_start_time) > duration;
287   QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
288                 << ": HasCycleLasted=" << result << ". elapsed:"
289                 << (congestion_event.event_time - cycle_.cycle_start_time)
290                 << ", duration:" << duration;
291   return result;
292 }
293 
HasPhaseLasted(QuicTime::Delta duration,const Bbr2CongestionEvent & congestion_event) const294 bool Bbr2ProbeBwMode::HasPhaseLasted(
295     QuicTime::Delta duration,
296     const Bbr2CongestionEvent& congestion_event) const {
297   bool result =
298       (congestion_event.event_time - cycle_.phase_start_time) > duration;
299   QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
300                 << ": HasPhaseLasted=" << result << ". elapsed:"
301                 << (congestion_event.event_time - cycle_.phase_start_time)
302                 << ", duration:" << duration;
303   return result;
304 }
305 
IsTimeToProbeForRenoCoexistence(double probe_wait_fraction,const Bbr2CongestionEvent &) const306 bool Bbr2ProbeBwMode::IsTimeToProbeForRenoCoexistence(
307     double probe_wait_fraction,
308     const Bbr2CongestionEvent& /*congestion_event*/) const {
309   if (!Params().enable_reno_coexistence) {
310     return false;
311   }
312 
313   uint64_t rounds = Params().probe_bw_probe_max_rounds;
314   if (Params().probe_bw_probe_reno_gain > 0.0) {
315     QuicByteCount target_bytes_inflight = sender_->GetTargetBytesInflight();
316     uint64_t reno_rounds = Params().probe_bw_probe_reno_gain *
317                            target_bytes_inflight / kDefaultTCPMSS;
318     rounds = std::min(rounds, reno_rounds);
319   }
320   bool result = cycle_.rounds_since_probe >= (rounds * probe_wait_fraction);
321   QUIC_DVLOG(3) << sender_ << " " << cycle_.phase
322                 << ": IsTimeToProbeForRenoCoexistence=" << result
323                 << ". rounds_since_probe:" << cycle_.rounds_since_probe
324                 << ", rounds:" << rounds
325                 << ", probe_wait_fraction:" << probe_wait_fraction;
326   return result;
327 }
328 
RaiseInflightHighSlope()329 void Bbr2ProbeBwMode::RaiseInflightHighSlope() {
330   QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_UP);
331   uint64_t growth_this_round = 1 << cycle_.probe_up_rounds;
332   // The number 30 below means |growth_this_round| is capped at 1G and the lower
333   // bound of |probe_up_bytes| is (practically) 1 mss, at this speed inflight_hi
334   // grows by approximately 1 packet per packet acked.
335   cycle_.probe_up_rounds = std::min<uint64_t>(cycle_.probe_up_rounds + 1, 30);
336   uint64_t probe_up_bytes = sender_->GetCongestionWindow() / growth_this_round;
337   cycle_.probe_up_bytes =
338       std::max<QuicByteCount>(probe_up_bytes, kDefaultTCPMSS);
339   QUIC_DVLOG(3) << sender_ << " Rasing inflight_hi slope. probe_up_rounds:"
340                 << cycle_.probe_up_rounds
341                 << ", probe_up_bytes:" << cycle_.probe_up_bytes;
342 }
343 
ProbeInflightHighUpward(const Bbr2CongestionEvent & congestion_event)344 void Bbr2ProbeBwMode::ProbeInflightHighUpward(
345     const Bbr2CongestionEvent& congestion_event) {
346   QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_UP);
347   if (Params().probe_up_ignore_inflight_hi) {
348     // When inflight_hi is disabled in PROBE_UP, it increases when
349     // the number of bytes delivered in a round is larger inflight_hi.
350     return;
351   }
352   if (Params().probe_up_simplify_inflight_hi) {
353     // Raise inflight_hi exponentially if it was utilized this round.
354     cycle_.probe_up_acked += congestion_event.bytes_acked;
355     if (!congestion_event.end_of_round_trip) {
356       return;
357     }
358     if (!model_->inflight_hi_limited_in_round() ||
359         model_->loss_events_in_round() > 0) {
360       cycle_.probe_up_acked = 0;
361       return;
362     }
363   } else {
364     if (congestion_event.prior_bytes_in_flight < congestion_event.prior_cwnd) {
365       QUIC_DVLOG(3) << sender_
366                     << " Raising inflight_hi early return: Not cwnd limited.";
367       // Not fully utilizing cwnd, so can't safely grow.
368       return;
369     }
370 
371     if (congestion_event.prior_cwnd < model_->inflight_hi()) {
372       QUIC_DVLOG(3)
373           << sender_
374           << " Raising inflight_hi early return: inflight_hi not fully used.";
375       // Not fully using inflight_hi, so don't grow it.
376       return;
377     }
378 
379     // Increase inflight_hi by the number of probe_up_bytes within
380     // probe_up_acked.
381     cycle_.probe_up_acked += congestion_event.bytes_acked;
382   }
383 
384   if (cycle_.probe_up_acked >= cycle_.probe_up_bytes) {
385     uint64_t delta = cycle_.probe_up_acked / cycle_.probe_up_bytes;
386     cycle_.probe_up_acked -= delta * cycle_.probe_up_bytes;
387     QuicByteCount new_inflight_hi =
388         model_->inflight_hi() + delta * kDefaultTCPMSS;
389     if (new_inflight_hi > model_->inflight_hi()) {
390       QUIC_DVLOG(3) << sender_ << " Raising inflight_hi from "
391                     << model_->inflight_hi() << " to " << new_inflight_hi
392                     << ". probe_up_bytes:" << cycle_.probe_up_bytes
393                     << ", delta:" << delta
394                     << ", (new)probe_up_acked:" << cycle_.probe_up_acked;
395 
396       model_->set_inflight_hi(new_inflight_hi);
397     } else {
398       QUIC_BUG(quic_bug_10436_2)
399           << "Not growing inflight_hi due to wrap around. Old value:"
400           << model_->inflight_hi() << ", new value:" << new_inflight_hi;
401     }
402   }
403 
404   if (congestion_event.end_of_round_trip) {
405     RaiseInflightHighSlope();
406   }
407 }
408 
UpdateProbeCruise(const Bbr2CongestionEvent & congestion_event)409 void Bbr2ProbeBwMode::UpdateProbeCruise(
410     const Bbr2CongestionEvent& congestion_event) {
411   QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_CRUISE);
412   MaybeAdaptUpperBounds(congestion_event);
413   QUICHE_DCHECK(!cycle_.is_sample_from_probing);
414 
415   if (IsTimeToProbeBandwidth(congestion_event)) {
416     EnterProbeRefill(/*probe_up_rounds=*/0, congestion_event.event_time);
417     return;
418   }
419 }
420 
UpdateProbeRefill(const Bbr2CongestionEvent & congestion_event)421 void Bbr2ProbeBwMode::UpdateProbeRefill(
422     const Bbr2CongestionEvent& congestion_event) {
423   QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_REFILL);
424   MaybeAdaptUpperBounds(congestion_event);
425   QUICHE_DCHECK(!cycle_.is_sample_from_probing);
426 
427   if (cycle_.rounds_in_phase > 0 && congestion_event.end_of_round_trip) {
428     EnterProbeUp(congestion_event.event_time);
429     return;
430   }
431 }
432 
UpdateProbeUp(QuicByteCount prior_in_flight,const Bbr2CongestionEvent & congestion_event)433 void Bbr2ProbeBwMode::UpdateProbeUp(
434     QuicByteCount prior_in_flight,
435     const Bbr2CongestionEvent& congestion_event) {
436   QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_UP);
437   if (MaybeAdaptUpperBounds(congestion_event) == ADAPTED_PROBED_TOO_HIGH) {
438     EnterProbeDown(/*probed_too_high=*/true, /*stopped_risky_probe=*/false,
439                    congestion_event.event_time);
440     return;
441   }
442 
443   // TODO(wub): Consider exit PROBE_UP after a certain number(e.g. 64) of RTTs.
444 
445   ProbeInflightHighUpward(congestion_event);
446 
447   bool is_risky = false;
448   bool is_queuing = false;
449   if (last_cycle_probed_too_high_ && prior_in_flight >= model_->inflight_hi()) {
450     is_risky = true;
451     QUIC_DVLOG(3) << sender_
452                   << " Probe is too risky. last_cycle_probed_too_high_:"
453                   << last_cycle_probed_too_high_
454                   << ", prior_in_flight:" << prior_in_flight
455                   << ", inflight_hi:" << model_->inflight_hi();
456     // TCP uses min_rtt instead of a full round:
457     //   HasPhaseLasted(model_->MinRtt(), congestion_event)
458   } else if (cycle_.rounds_in_phase > 0) {
459     if (Params().max_probe_up_queue_rounds > 0) {
460       if (congestion_event.end_of_round_trip) {
461         model_->CheckPersistentQueue(congestion_event,
462                                      Params().full_bw_threshold);
463         if (model_->rounds_with_queueing() >=
464             Params().max_probe_up_queue_rounds) {
465           QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_probe_two_rounds, 3, 3);
466           is_queuing = true;
467         }
468       }
469     } else {
470       QuicByteCount queuing_threshold_extra_bytes =
471           model_->QueueingThresholdExtraBytes();
472       if (Params().add_ack_height_to_queueing_threshold) {
473         queuing_threshold_extra_bytes += model_->MaxAckHeight();
474       }
475       QuicByteCount queuing_threshold =
476           (Params().full_bw_threshold * model_->BDP()) +
477           queuing_threshold_extra_bytes;
478 
479       is_queuing = congestion_event.bytes_in_flight >= queuing_threshold;
480 
481       QUIC_DVLOG(3) << sender_
482                     << " Checking if building up a queue. prior_in_flight:"
483                     << prior_in_flight
484                     << ", post_in_flight:" << congestion_event.bytes_in_flight
485                     << ", threshold:" << queuing_threshold
486                     << ", is_queuing:" << is_queuing
487                     << ", max_bw:" << model_->MaxBandwidth()
488                     << ", min_rtt:" << model_->MinRtt();
489     }
490   }
491 
492   if (is_risky || is_queuing) {
493     EnterProbeDown(/*probed_too_high=*/false, /*stopped_risky_probe=*/is_risky,
494                    congestion_event.event_time);
495   }
496 }
497 
EnterProbeDown(bool probed_too_high,bool stopped_risky_probe,QuicTime now)498 void Bbr2ProbeBwMode::EnterProbeDown(bool probed_too_high,
499                                      bool stopped_risky_probe, QuicTime now) {
500   QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> "
501                 << CyclePhase::PROBE_DOWN << " after "
502                 << now - cycle_.phase_start_time << ", or "
503                 << cycle_.rounds_in_phase
504                 << " rounds. probed_too_high:" << probed_too_high
505                 << ", stopped_risky_probe:" << stopped_risky_probe << "  @ "
506                 << now;
507   last_cycle_probed_too_high_ = probed_too_high;
508   last_cycle_stopped_risky_probe_ = stopped_risky_probe;
509 
510   cycle_.cycle_start_time = now;
511   cycle_.phase = CyclePhase::PROBE_DOWN;
512   cycle_.rounds_in_phase = 0;
513   cycle_.phase_start_time = now;
514   ++sender_->connection_stats_->bbr_num_cycles;
515   if (Params().bw_lo_mode_ != Bbr2Params::QuicBandwidthLoMode::DEFAULT) {
516     // Clear bandwidth lo if it was set in PROBE_UP, because losses in PROBE_UP
517     // should not permanently change bandwidth_lo.
518     // It's possible for bandwidth_lo to be set during REFILL, but if that was
519     // a valid value, it'll quickly be rediscovered.
520     model_->clear_bandwidth_lo();
521   }
522 
523   // Pick probe wait time.
524   cycle_.rounds_since_probe =
525       sender_->RandomUint64(Params().probe_bw_max_probe_rand_rounds);
526   cycle_.probe_wait_time =
527       Params().probe_bw_probe_base_duration +
528       QuicTime::Delta::FromMicroseconds(sender_->RandomUint64(
529           Params().probe_bw_probe_max_rand_duration.ToMicroseconds()));
530 
531   cycle_.probe_up_bytes = std::numeric_limits<QuicByteCount>::max();
532   cycle_.probe_up_app_limited_since_inflight_hi_limited_ = false;
533   cycle_.has_advanced_max_bw = false;
534   model_->RestartRoundEarly();
535 }
536 
EnterProbeCruise(QuicTime now)537 void Bbr2ProbeBwMode::EnterProbeCruise(QuicTime now) {
538   if (cycle_.phase == CyclePhase::PROBE_DOWN) {
539     ExitProbeDown();
540   }
541   QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> "
542                 << CyclePhase::PROBE_CRUISE << " after "
543                 << now - cycle_.phase_start_time << ", or "
544                 << cycle_.rounds_in_phase << " rounds.  @ " << now;
545 
546   model_->cap_inflight_lo(model_->inflight_hi());
547   cycle_.phase = CyclePhase::PROBE_CRUISE;
548   cycle_.rounds_in_phase = 0;
549   cycle_.phase_start_time = now;
550   cycle_.is_sample_from_probing = false;
551 }
552 
EnterProbeRefill(uint64_t probe_up_rounds,QuicTime now)553 void Bbr2ProbeBwMode::EnterProbeRefill(uint64_t probe_up_rounds, QuicTime now) {
554   if (cycle_.phase == CyclePhase::PROBE_DOWN) {
555     ExitProbeDown();
556   }
557   QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> "
558                 << CyclePhase::PROBE_REFILL << " after "
559                 << now - cycle_.phase_start_time << ", or "
560                 << cycle_.rounds_in_phase
561                 << " rounds. probe_up_rounds:" << probe_up_rounds << "  @ "
562                 << now;
563   cycle_.phase = CyclePhase::PROBE_REFILL;
564   cycle_.rounds_in_phase = 0;
565   cycle_.phase_start_time = now;
566   cycle_.is_sample_from_probing = false;
567   last_cycle_stopped_risky_probe_ = false;
568 
569   model_->clear_bandwidth_lo();
570   model_->clear_inflight_lo();
571   cycle_.probe_up_rounds = probe_up_rounds;
572   cycle_.probe_up_acked = 0;
573   model_->RestartRoundEarly();
574 }
575 
EnterProbeUp(QuicTime now)576 void Bbr2ProbeBwMode::EnterProbeUp(QuicTime now) {
577   QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_REFILL);
578   QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> "
579                 << CyclePhase::PROBE_UP << " after "
580                 << now - cycle_.phase_start_time << ", or "
581                 << cycle_.rounds_in_phase << " rounds.  @ " << now;
582   cycle_.phase = CyclePhase::PROBE_UP;
583   cycle_.rounds_in_phase = 0;
584   cycle_.phase_start_time = now;
585   cycle_.is_sample_from_probing = true;
586   RaiseInflightHighSlope();
587 
588   model_->RestartRoundEarly();
589 }
590 
ExitProbeDown()591 void Bbr2ProbeBwMode::ExitProbeDown() {
592   QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_DOWN);
593   if (!cycle_.has_advanced_max_bw) {
594     QUIC_DVLOG(2) << sender_ << " Advancing max bw filter at end of cycle.";
595     model_->AdvanceMaxBandwidthFilter();
596     cycle_.has_advanced_max_bw = true;
597   }
598 }
599 
600 // static
CyclePhaseToString(CyclePhase phase)601 const char* Bbr2ProbeBwMode::CyclePhaseToString(CyclePhase phase) {
602   switch (phase) {
603     case CyclePhase::PROBE_NOT_STARTED:
604       return "PROBE_NOT_STARTED";
605     case CyclePhase::PROBE_UP:
606       return "PROBE_UP";
607     case CyclePhase::PROBE_DOWN:
608       return "PROBE_DOWN";
609     case CyclePhase::PROBE_CRUISE:
610       return "PROBE_CRUISE";
611     case CyclePhase::PROBE_REFILL:
612       return "PROBE_REFILL";
613     default:
614       break;
615   }
616   return "<Invalid CyclePhase>";
617 }
618 
operator <<(std::ostream & os,const Bbr2ProbeBwMode::CyclePhase phase)619 std::ostream& operator<<(std::ostream& os,
620                          const Bbr2ProbeBwMode::CyclePhase phase) {
621   return os << Bbr2ProbeBwMode::CyclePhaseToString(phase);
622 }
623 
ExportDebugState() const624 Bbr2ProbeBwMode::DebugState Bbr2ProbeBwMode::ExportDebugState() const {
625   DebugState s;
626   s.phase = cycle_.phase;
627   s.cycle_start_time = cycle_.cycle_start_time;
628   s.phase_start_time = cycle_.phase_start_time;
629   return s;
630 }
631 
operator <<(std::ostream & os,const Bbr2ProbeBwMode::DebugState & state)632 std::ostream& operator<<(std::ostream& os,
633                          const Bbr2ProbeBwMode::DebugState& state) {
634   os << "[PROBE_BW] phase: " << state.phase << "\n";
635   os << "[PROBE_BW] cycle_start_time: " << state.cycle_start_time << "\n";
636   os << "[PROBE_BW] phase_start_time: " << state.phase_start_time << "\n";
637   return os;
638 }
639 
Params() const640 const Bbr2Params& Bbr2ProbeBwMode::Params() const { return sender_->Params(); }
641 
PacingGainForPhase(Bbr2ProbeBwMode::CyclePhase phase) const642 float Bbr2ProbeBwMode::PacingGainForPhase(
643     Bbr2ProbeBwMode::CyclePhase phase) const {
644   if (phase == Bbr2ProbeBwMode::CyclePhase::PROBE_UP) {
645     return Params().probe_bw_probe_up_pacing_gain;
646   }
647   if (phase == Bbr2ProbeBwMode::CyclePhase::PROBE_DOWN) {
648     return Params().probe_bw_probe_down_pacing_gain;
649   }
650   return Params().probe_bw_default_pacing_gain;
651 }
652 
653 }  // namespace quic
654