QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 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 "net/third_party/quiche/src/quic/core/congestion_control/bbr2_probe_bw.h" |
| 6 | |
| 7 | #include "net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.h" |
| 8 | #include "net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.h" |
| 9 | #include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" |
| 10 | #include "net/third_party/quiche/src/quic/core/quic_time.h" |
| 11 | #include "net/third_party/quiche/src/quic/core/quic_types.h" |
wub | 32dce0f | 2019-12-18 15:25:24 -0800 | [diff] [blame] | 12 | #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" |
dmcardle | 16df40e | 2020-01-03 14:50:41 -0800 | [diff] [blame] | 13 | #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" |
QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 14 | |
| 15 | namespace quic { |
| 16 | |
| 17 | void Bbr2ProbeBwMode::Enter(const Bbr2CongestionEvent& congestion_event) { |
| 18 | if (cycle_.phase == CyclePhase::PROBE_NOT_STARTED) { |
| 19 | // First time entering PROBE_BW. Start a new probing cycle. |
| 20 | EnterProbeDown(/*probed_too_high=*/false, /*stopped_risky_probe=*/false, |
| 21 | congestion_event); |
| 22 | } else { |
| 23 | // Transitioning from PROBE_RTT to PROBE_BW. Re-enter the last phase before |
| 24 | // PROBE_RTT. |
| 25 | DCHECK(cycle_.phase == CyclePhase::PROBE_CRUISE || |
| 26 | cycle_.phase == CyclePhase::PROBE_REFILL); |
| 27 | cycle_.cycle_start_time = congestion_event.event_time; |
| 28 | if (cycle_.phase == CyclePhase::PROBE_CRUISE) { |
| 29 | EnterProbeCruise(congestion_event); |
| 30 | } else if (cycle_.phase == CyclePhase::PROBE_REFILL) { |
| 31 | EnterProbeRefill(cycle_.probe_up_rounds, congestion_event); |
| 32 | } |
| 33 | } |
| 34 | } |
| 35 | |
| 36 | Bbr2Mode Bbr2ProbeBwMode::OnCongestionEvent( |
| 37 | QuicByteCount prior_in_flight, |
| 38 | QuicTime event_time, |
| 39 | const AckedPacketVector& /*acked_packets*/, |
| 40 | const LostPacketVector& /*lost_packets*/, |
| 41 | const Bbr2CongestionEvent& congestion_event) { |
| 42 | 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 | |
wub | 2fd0c6c | 2019-11-25 07:09:16 -0800 | [diff] [blame] | 53 | bool switch_to_probe_rtt = false; |
| 54 | |
QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 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)) { |
wub | 2fd0c6c | 2019-11-25 07:09:16 -0800 | [diff] [blame] | 62 | switch_to_probe_rtt = true; |
QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 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 | |
wub | 2fd0c6c | 2019-11-25 07:09:16 -0800 | [diff] [blame] | 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 | } |
QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 76 | |
wub | 2fd0c6c | 2019-11-25 07:09:16 -0800 | [diff] [blame] | 77 | return switch_to_probe_rtt ? Bbr2Mode::PROBE_RTT : Bbr2Mode::PROBE_BW; |
QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 78 | } |
| 79 | |
| 80 | 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 | |
| 86 | return NoGreaterThan(std::min(model_->inflight_lo(), model_->inflight_hi())); |
| 87 | } |
| 88 | |
| 89 | bool Bbr2ProbeBwMode::IsProbingForBandwidth() const { |
| 90 | return cycle_.phase == CyclePhase::PROBE_REFILL || |
| 91 | cycle_.phase == CyclePhase::PROBE_UP; |
| 92 | } |
| 93 | |
| 94 | void Bbr2ProbeBwMode::UpdateProbeDown( |
| 95 | QuicByteCount prior_in_flight, |
| 96 | const Bbr2CongestionEvent& congestion_event) { |
| 97 | DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_DOWN); |
| 98 | |
| 99 | if (cycle_.rounds_in_phase == 1 && congestion_event.end_of_round_trip) { |
| 100 | cycle_.is_sample_from_probing = false; |
| 101 | |
| 102 | if (!congestion_event.last_sample_is_app_limited) { |
| 103 | QUIC_DVLOG(2) |
| 104 | << sender_ |
| 105 | << " Advancing max bw filter after one round in PROBE_DOWN."; |
| 106 | model_->AdvanceMaxBandwidthFilter(); |
| 107 | cycle_.has_advanced_max_bw = true; |
| 108 | } |
| 109 | |
| 110 | if (last_cycle_stopped_risky_probe_ && !last_cycle_probed_too_high_) { |
| 111 | EnterProbeRefill(/*probe_up_rounds=*/0, congestion_event); |
| 112 | return; |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | MaybeAdaptUpperBounds(congestion_event); |
| 117 | |
| 118 | if (IsTimeToProbeBandwidth(congestion_event)) { |
| 119 | EnterProbeRefill(/*probe_up_rounds=*/0, congestion_event); |
| 120 | return; |
| 121 | } |
| 122 | |
| 123 | if (HasStayedLongEnoughInProbeDown(congestion_event)) { |
| 124 | QUIC_DVLOG(3) << sender_ << " Proportional time based PROBE_DOWN exit"; |
| 125 | EnterProbeCruise(congestion_event); |
| 126 | return; |
| 127 | } |
| 128 | |
| 129 | const QuicByteCount inflight_with_headroom = |
| 130 | model_->inflight_hi_with_headroom(); |
| 131 | QUIC_DVLOG(3) |
| 132 | << sender_ |
| 133 | << " Checking if have enough inflight headroom. prior_in_flight:" |
| 134 | << prior_in_flight |
| 135 | << ", inflight_with_headroom:" << inflight_with_headroom; |
| 136 | if (prior_in_flight > inflight_with_headroom) { |
| 137 | // Stay in PROBE_DOWN. |
| 138 | return; |
| 139 | } |
| 140 | |
| 141 | // Transition to PROBE_CRUISE iff we've drained to target. |
| 142 | QuicByteCount bdp = model_->BDP(model_->MaxBandwidth()); |
| 143 | QUIC_DVLOG(3) << sender_ << " Checking if drained to target. prior_in_flight:" |
| 144 | << prior_in_flight << ", bdp:" << bdp; |
| 145 | if (prior_in_flight < bdp) { |
| 146 | EnterProbeCruise(congestion_event); |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | Bbr2ProbeBwMode::AdaptUpperBoundsResult Bbr2ProbeBwMode::MaybeAdaptUpperBounds( |
| 151 | const Bbr2CongestionEvent& congestion_event) { |
wub | c690c9c | 2019-12-23 08:27:12 -0800 | [diff] [blame] | 152 | const SendTimeState& send_state = |
| 153 | model_->one_bw_sample_per_ack_event() |
| 154 | ? congestion_event.last_packet_send_state |
| 155 | : SendStateOfLargestPacket(congestion_event); |
QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 156 | if (!send_state.is_valid) { |
| 157 | QUIC_DVLOG(3) << sender_ << " " << cycle_.phase |
| 158 | << ": NOT_ADAPTED_INVALID_SAMPLE"; |
| 159 | return NOT_ADAPTED_INVALID_SAMPLE; |
| 160 | } |
| 161 | |
wub | 28e3468 | 2020-01-15 13:27:13 -0800 | [diff] [blame] | 162 | bool has_enough_loss_events = true; |
| 163 | if (model_->always_count_loss_events()) { |
| 164 | QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_always_count_loss_events, 2, 2); |
| 165 | has_enough_loss_events = |
| 166 | model_->loss_events_in_round() >= Params().probe_bw_full_loss_count; |
| 167 | } |
| 168 | |
| 169 | if (has_enough_loss_events && model_->IsInflightTooHigh(congestion_event)) { |
QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 170 | if (cycle_.is_sample_from_probing) { |
| 171 | cycle_.is_sample_from_probing = false; |
| 172 | |
| 173 | if (!send_state.is_app_limited) { |
| 174 | QuicByteCount inflight_at_send = BytesInFlight(send_state); |
wub | 32dce0f | 2019-12-18 15:25:24 -0800 | [diff] [blame] | 175 | if (GetQuicReloadableFlag(quic_bbr2_cut_inflight_hi_gradually)) { |
| 176 | QuicByteCount inflight_target = |
| 177 | sender_->GetTargetBytesInflight() * (1.0 - Params().beta); |
| 178 | if (inflight_at_send >= inflight_target) { |
| 179 | // The new code does not change behavior. |
| 180 | QUIC_CODE_COUNT(quic_bbr2_cut_inflight_hi_gradually_noop); |
| 181 | } else { |
| 182 | // The new code actually cuts inflight_hi slower than before. |
| 183 | QUIC_CODE_COUNT(quic_bbr2_cut_inflight_hi_gradually_in_effect); |
| 184 | } |
| 185 | model_->set_inflight_hi(std::max(inflight_at_send, inflight_target)); |
| 186 | } else { |
| 187 | model_->set_inflight_hi(inflight_at_send); |
| 188 | } |
QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 189 | } |
| 190 | |
| 191 | QUIC_DVLOG(3) << sender_ << " " << cycle_.phase |
| 192 | << ": ADAPTED_PROBED_TOO_HIGH"; |
| 193 | return ADAPTED_PROBED_TOO_HIGH; |
| 194 | } |
| 195 | return ADAPTED_OK; |
| 196 | } |
| 197 | |
| 198 | if (model_->inflight_hi() == model_->inflight_hi_default()) { |
| 199 | QUIC_DVLOG(3) << sender_ << " " << cycle_.phase |
| 200 | << ": NOT_ADAPTED_INFLIGHT_HIGH_NOT_SET"; |
| 201 | return NOT_ADAPTED_INFLIGHT_HIGH_NOT_SET; |
| 202 | } |
| 203 | |
| 204 | const QuicByteCount inflight_at_send = BytesInFlight(send_state); |
| 205 | |
| 206 | // Raise the upper bound for inflight. |
| 207 | if (inflight_at_send > model_->inflight_hi()) { |
| 208 | QUIC_DVLOG(3) |
| 209 | << sender_ << " " << cycle_.phase |
| 210 | << ": Adapting inflight_hi from inflight_at_send. inflight_at_send:" |
| 211 | << inflight_at_send << ", old inflight_hi:" << model_->inflight_hi(); |
| 212 | model_->set_inflight_hi(inflight_at_send); |
| 213 | } |
| 214 | |
| 215 | return ADAPTED_OK; |
| 216 | } |
| 217 | |
| 218 | bool Bbr2ProbeBwMode::IsTimeToProbeBandwidth( |
| 219 | const Bbr2CongestionEvent& congestion_event) const { |
wub | 9528ecf | 2020-01-16 13:16:48 -0800 | [diff] [blame] | 220 | if (HasCycleLasted(cycle_.probe_wait_time, congestion_event)) { |
| 221 | return true; |
| 222 | } |
| 223 | |
| 224 | if (IsTimeToProbeForRenoCoexistence(1.0, congestion_event)) { |
| 225 | ++sender_->connection_stats_->bbr_num_short_cycles_for_reno_coexistence; |
| 226 | return true; |
| 227 | } |
| 228 | return false; |
QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 229 | } |
| 230 | |
| 231 | // QUIC only. Used to prevent a Bbr2 flow from staying in PROBE_DOWN for too |
| 232 | // long, as seen in some multi-sender simulator tests. |
| 233 | bool Bbr2ProbeBwMode::HasStayedLongEnoughInProbeDown( |
| 234 | const Bbr2CongestionEvent& congestion_event) const { |
wub | 720c8e6 | 2019-12-02 16:01:56 -0800 | [diff] [blame] | 235 | if (exit_probe_down_after_one_rtt_) { |
| 236 | QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_exit_probe_bw_down_after_one_rtt); |
| 237 | // Stay in PROBE_DOWN for at most the time of a min rtt, as it is done in |
| 238 | // BBRv1. The intention here is to figure out whether the performance |
| 239 | // regression in BBRv2 is because it stays in PROBE_DOWN for too long. |
| 240 | // TODO(wub): Consider exit after a full round instead, which typically |
| 241 | // indicates most(if not all) packets sent during PROBE_UP have been acked. |
| 242 | return HasPhaseLasted(model_->MinRtt(), congestion_event); |
| 243 | } |
QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 244 | // The amount of time to stay in PROBE_DOWN, as a fraction of probe wait time. |
| 245 | const double kProbeWaitFraction = 0.2; |
| 246 | return HasCycleLasted(cycle_.probe_wait_time * kProbeWaitFraction, |
| 247 | congestion_event) || |
| 248 | IsTimeToProbeForRenoCoexistence(kProbeWaitFraction, congestion_event); |
| 249 | } |
| 250 | |
| 251 | bool Bbr2ProbeBwMode::HasCycleLasted( |
| 252 | QuicTime::Delta duration, |
| 253 | const Bbr2CongestionEvent& congestion_event) const { |
| 254 | bool result = |
| 255 | (congestion_event.event_time - cycle_.cycle_start_time) > duration; |
| 256 | QUIC_DVLOG(3) << sender_ << " " << cycle_.phase |
| 257 | << ": HasCycleLasted=" << result << ". elapsed:" |
| 258 | << (congestion_event.event_time - cycle_.cycle_start_time) |
| 259 | << ", duration:" << duration; |
| 260 | return result; |
| 261 | } |
| 262 | |
| 263 | bool Bbr2ProbeBwMode::HasPhaseLasted( |
| 264 | QuicTime::Delta duration, |
| 265 | const Bbr2CongestionEvent& congestion_event) const { |
| 266 | bool result = |
| 267 | (congestion_event.event_time - cycle_.phase_start_time) > duration; |
| 268 | QUIC_DVLOG(3) << sender_ << " " << cycle_.phase |
| 269 | << ": HasPhaseLasted=" << result << ". elapsed:" |
| 270 | << (congestion_event.event_time - cycle_.phase_start_time) |
| 271 | << ", duration:" << duration; |
| 272 | return result; |
| 273 | } |
| 274 | |
| 275 | bool Bbr2ProbeBwMode::IsTimeToProbeForRenoCoexistence( |
| 276 | double probe_wait_fraction, |
| 277 | const Bbr2CongestionEvent& /*congestion_event*/) const { |
| 278 | uint64_t rounds = Params().probe_bw_probe_max_rounds; |
| 279 | if (Params().probe_bw_probe_reno_gain > 0.0) { |
wub | 2957990 | 2019-12-18 10:16:17 -0800 | [diff] [blame] | 280 | QuicByteCount target_bytes_inflight = sender_->GetTargetBytesInflight(); |
| 281 | uint64_t reno_rounds = Params().probe_bw_probe_reno_gain * |
| 282 | target_bytes_inflight / kDefaultTCPMSS; |
QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 283 | rounds = std::min(rounds, reno_rounds); |
| 284 | } |
| 285 | bool result = cycle_.rounds_since_probe >= (rounds * probe_wait_fraction); |
| 286 | QUIC_DVLOG(3) << sender_ << " " << cycle_.phase |
| 287 | << ": IsTimeToProbeForRenoCoexistence=" << result |
| 288 | << ". rounds_since_probe:" << cycle_.rounds_since_probe |
| 289 | << ", rounds:" << rounds |
| 290 | << ", probe_wait_fraction:" << probe_wait_fraction; |
| 291 | return result; |
| 292 | } |
| 293 | |
| 294 | void Bbr2ProbeBwMode::RaiseInflightHighSlope() { |
| 295 | DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_UP); |
| 296 | uint64_t growth_this_round = 1 << cycle_.probe_up_rounds; |
| 297 | // The number 30 below means |growth_this_round| is capped at 1G and the lower |
| 298 | // bound of |probe_up_bytes| is (practically) 1 mss, at this speed inflight_hi |
| 299 | // grows by approximately 1 packet per packet acked. |
| 300 | cycle_.probe_up_rounds = std::min<uint64_t>(cycle_.probe_up_rounds + 1, 30); |
| 301 | uint64_t probe_up_bytes = sender_->GetCongestionWindow() / growth_this_round; |
| 302 | cycle_.probe_up_bytes = |
| 303 | std::max<QuicByteCount>(probe_up_bytes, kDefaultTCPMSS); |
| 304 | QUIC_DVLOG(3) << sender_ << " Rasing inflight_hi slope. probe_up_rounds:" |
| 305 | << cycle_.probe_up_rounds |
| 306 | << ", probe_up_bytes:" << cycle_.probe_up_bytes; |
| 307 | } |
| 308 | |
| 309 | void Bbr2ProbeBwMode::ProbeInflightHighUpward( |
| 310 | const Bbr2CongestionEvent& congestion_event) { |
| 311 | DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_UP); |
| 312 | if (!model_->IsCongestionWindowLimited(congestion_event)) { |
| 313 | QUIC_DVLOG(3) << sender_ |
wub | 3034a7d | 2019-09-27 13:33:14 -0700 | [diff] [blame] | 314 | << " Raising inflight_hi early return: Not cwnd limited."; |
QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 315 | // Not fully utilizing cwnd, so can't safely grow. |
| 316 | return; |
| 317 | } |
| 318 | |
wub | 680d563 | 2019-11-25 08:49:25 -0800 | [diff] [blame] | 319 | if (congestion_event.prior_cwnd < model_->inflight_hi()) { |
wub | 3034a7d | 2019-09-27 13:33:14 -0700 | [diff] [blame] | 320 | QUIC_DVLOG(3) |
| 321 | << sender_ |
| 322 | << " Raising inflight_hi early return: inflight_hi not fully used."; |
| 323 | // Not fully using inflight_hi, so don't grow it. |
| 324 | return; |
| 325 | } |
| 326 | |
QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 327 | // Increase inflight_hi by the number of probe_up_bytes within probe_up_acked. |
| 328 | cycle_.probe_up_acked += congestion_event.bytes_acked; |
| 329 | if (cycle_.probe_up_acked >= cycle_.probe_up_bytes) { |
| 330 | uint64_t delta = cycle_.probe_up_acked / cycle_.probe_up_bytes; |
| 331 | cycle_.probe_up_acked -= delta * cycle_.probe_up_bytes; |
wub | 3034a7d | 2019-09-27 13:33:14 -0700 | [diff] [blame] | 332 | QuicByteCount new_inflight_hi = |
| 333 | model_->inflight_hi() + delta * kDefaultTCPMSS; |
| 334 | if (new_inflight_hi > model_->inflight_hi()) { |
| 335 | QUIC_DVLOG(3) << sender_ << " Raising inflight_hi from " |
| 336 | << model_->inflight_hi() << " to " << new_inflight_hi |
| 337 | << ". probe_up_bytes:" << cycle_.probe_up_bytes |
| 338 | << ", delta:" << delta |
| 339 | << ", (new)probe_up_acked:" << cycle_.probe_up_acked; |
| 340 | |
| 341 | model_->set_inflight_hi(new_inflight_hi); |
wub | 680d563 | 2019-11-25 08:49:25 -0800 | [diff] [blame] | 342 | } else { |
wub | 3034a7d | 2019-09-27 13:33:14 -0700 | [diff] [blame] | 343 | QUIC_BUG << "Not growing inflight_hi due to wrap around. Old value:" |
| 344 | << model_->inflight_hi() << ", new value:" << new_inflight_hi; |
| 345 | } |
QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 346 | } |
| 347 | |
| 348 | if (congestion_event.end_of_round_trip) { |
| 349 | RaiseInflightHighSlope(); |
| 350 | } |
| 351 | } |
| 352 | |
| 353 | void Bbr2ProbeBwMode::UpdateProbeCruise( |
| 354 | const Bbr2CongestionEvent& congestion_event) { |
| 355 | DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_CRUISE); |
| 356 | MaybeAdaptUpperBounds(congestion_event); |
| 357 | DCHECK(!cycle_.is_sample_from_probing); |
| 358 | |
| 359 | if (IsTimeToProbeBandwidth(congestion_event)) { |
| 360 | EnterProbeRefill(/*probe_up_rounds=*/0, congestion_event); |
| 361 | return; |
| 362 | } |
| 363 | } |
| 364 | |
| 365 | void Bbr2ProbeBwMode::UpdateProbeRefill( |
| 366 | const Bbr2CongestionEvent& congestion_event) { |
| 367 | DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_REFILL); |
| 368 | MaybeAdaptUpperBounds(congestion_event); |
| 369 | DCHECK(!cycle_.is_sample_from_probing); |
| 370 | |
| 371 | if (cycle_.rounds_in_phase > 0 && congestion_event.end_of_round_trip) { |
| 372 | EnterProbeUp(congestion_event); |
| 373 | return; |
| 374 | } |
| 375 | } |
| 376 | |
| 377 | void Bbr2ProbeBwMode::UpdateProbeUp( |
| 378 | QuicByteCount prior_in_flight, |
| 379 | const Bbr2CongestionEvent& congestion_event) { |
| 380 | DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_UP); |
| 381 | if (MaybeAdaptUpperBounds(congestion_event) == ADAPTED_PROBED_TOO_HIGH) { |
| 382 | EnterProbeDown(/*probed_too_high=*/true, /*stopped_risky_probe=*/false, |
| 383 | congestion_event); |
| 384 | return; |
| 385 | } |
| 386 | |
| 387 | // TODO(wub): Consider exit PROBE_UP after a certain number(e.g. 64) of RTTs. |
| 388 | |
| 389 | ProbeInflightHighUpward(congestion_event); |
| 390 | |
| 391 | bool is_risky = false; |
| 392 | bool is_queuing = false; |
| 393 | if (last_cycle_probed_too_high_ && prior_in_flight >= model_->inflight_hi()) { |
| 394 | is_risky = true; |
| 395 | QUIC_DVLOG(3) << sender_ |
| 396 | << " Probe is too risky. last_cycle_probed_too_high_:" |
| 397 | << last_cycle_probed_too_high_ |
| 398 | << ", prior_in_flight:" << prior_in_flight |
| 399 | << ", inflight_hi:" << model_->inflight_hi(); |
| 400 | // TCP uses min_rtt instead of a full round: |
| 401 | // HasPhaseLasted(model_->MinRtt(), congestion_event) |
| 402 | } else if (cycle_.rounds_in_phase > 0) { |
| 403 | QuicByteCount bdp = model_->BDP(model_->MaxBandwidth()); |
| 404 | QuicByteCount queuing_threshold = |
| 405 | (Params().probe_bw_probe_inflight_gain * bdp) + 2 * kDefaultTCPMSS; |
| 406 | is_queuing = prior_in_flight >= queuing_threshold; |
| 407 | QUIC_DVLOG(3) << sender_ |
| 408 | << " Checking if building up a queue. prior_in_flight:" |
| 409 | << prior_in_flight << ", threshold:" << queuing_threshold |
| 410 | << ", is_queuing:" << is_queuing |
| 411 | << ", max_bw:" << model_->MaxBandwidth() |
| 412 | << ", min_rtt:" << model_->MinRtt(); |
| 413 | } |
| 414 | |
| 415 | if (is_risky || is_queuing) { |
| 416 | EnterProbeDown(/*probed_too_high=*/false, /*stopped_risky_probe=*/is_risky, |
| 417 | congestion_event); |
| 418 | } |
| 419 | } |
| 420 | |
| 421 | void Bbr2ProbeBwMode::EnterProbeDown( |
| 422 | bool probed_too_high, |
| 423 | bool stopped_risky_probe, |
| 424 | const Bbr2CongestionEvent& congestion_event) { |
| 425 | QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> " |
| 426 | << CyclePhase::PROBE_DOWN << " after " |
| 427 | << congestion_event.event_time - cycle_.phase_start_time |
| 428 | << ", or " << cycle_.rounds_in_phase |
| 429 | << " rounds. probed_too_high:" << probed_too_high |
| 430 | << ", stopped_risky_probe:" << stopped_risky_probe << " @ " |
| 431 | << congestion_event.event_time; |
| 432 | last_cycle_probed_too_high_ = probed_too_high; |
| 433 | last_cycle_stopped_risky_probe_ = stopped_risky_probe; |
| 434 | |
| 435 | cycle_.cycle_start_time = congestion_event.event_time; |
| 436 | cycle_.phase = CyclePhase::PROBE_DOWN; |
| 437 | cycle_.rounds_in_phase = 0; |
| 438 | cycle_.phase_start_time = congestion_event.event_time; |
wub | 9528ecf | 2020-01-16 13:16:48 -0800 | [diff] [blame] | 439 | ++sender_->connection_stats_->bbr_num_cycles; |
QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 440 | |
| 441 | // Pick probe wait time. |
| 442 | cycle_.rounds_since_probe = |
| 443 | sender_->RandomUint64(Params().probe_bw_max_probe_rand_rounds); |
| 444 | cycle_.probe_wait_time = |
| 445 | Params().probe_bw_probe_base_duration + |
| 446 | QuicTime::Delta::FromMicroseconds(sender_->RandomUint64( |
| 447 | Params().probe_bw_probe_max_rand_duration.ToMicroseconds())); |
| 448 | |
| 449 | cycle_.probe_up_bytes = std::numeric_limits<QuicByteCount>::max(); |
| 450 | cycle_.has_advanced_max_bw = false; |
| 451 | model_->RestartRound(); |
| 452 | } |
| 453 | |
| 454 | void Bbr2ProbeBwMode::EnterProbeCruise( |
| 455 | const Bbr2CongestionEvent& congestion_event) { |
| 456 | if (cycle_.phase == CyclePhase::PROBE_DOWN) { |
| 457 | ExitProbeDown(congestion_event); |
| 458 | } |
| 459 | QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> " |
| 460 | << CyclePhase::PROBE_CRUISE << " after " |
| 461 | << congestion_event.event_time - cycle_.phase_start_time |
| 462 | << ", or " << cycle_.rounds_in_phase << " rounds. @ " |
| 463 | << congestion_event.event_time; |
wub | 680d563 | 2019-11-25 08:49:25 -0800 | [diff] [blame] | 464 | |
| 465 | model_->cap_inflight_lo(model_->inflight_hi()); |
QUICHE team | 7872c77 | 2019-07-23 10:19:37 -0400 | [diff] [blame] | 466 | cycle_.phase = CyclePhase::PROBE_CRUISE; |
| 467 | cycle_.rounds_in_phase = 0; |
| 468 | cycle_.phase_start_time = congestion_event.event_time; |
| 469 | cycle_.is_sample_from_probing = false; |
| 470 | } |
| 471 | |
| 472 | void Bbr2ProbeBwMode::EnterProbeRefill( |
| 473 | uint64_t probe_up_rounds, |
| 474 | const Bbr2CongestionEvent& congestion_event) { |
| 475 | if (cycle_.phase == CyclePhase::PROBE_DOWN) { |
| 476 | ExitProbeDown(congestion_event); |
| 477 | } |
| 478 | QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> " |
| 479 | << CyclePhase::PROBE_REFILL << " after " |
| 480 | << congestion_event.event_time - cycle_.phase_start_time |
| 481 | << ", or " << cycle_.rounds_in_phase |
| 482 | << " rounds. probe_up_rounds:" << probe_up_rounds << " @ " |
| 483 | << congestion_event.event_time; |
| 484 | cycle_.phase = CyclePhase::PROBE_REFILL; |
| 485 | cycle_.rounds_in_phase = 0; |
| 486 | cycle_.phase_start_time = congestion_event.event_time; |
| 487 | cycle_.is_sample_from_probing = false; |
| 488 | last_cycle_stopped_risky_probe_ = false; |
| 489 | |
| 490 | model_->clear_bandwidth_lo(); |
| 491 | model_->clear_inflight_lo(); |
| 492 | cycle_.probe_up_rounds = probe_up_rounds; |
| 493 | cycle_.probe_up_acked = 0; |
| 494 | model_->RestartRound(); |
| 495 | } |
| 496 | |
| 497 | void Bbr2ProbeBwMode::EnterProbeUp( |
| 498 | const Bbr2CongestionEvent& congestion_event) { |
| 499 | DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_REFILL); |
| 500 | QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> " |
| 501 | << CyclePhase::PROBE_UP << " after " |
| 502 | << congestion_event.event_time - cycle_.phase_start_time |
| 503 | << ", or " << cycle_.rounds_in_phase << " rounds. @ " |
| 504 | << congestion_event.event_time; |
| 505 | cycle_.phase = CyclePhase::PROBE_UP; |
| 506 | cycle_.rounds_in_phase = 0; |
| 507 | cycle_.phase_start_time = congestion_event.event_time; |
| 508 | cycle_.is_sample_from_probing = true; |
| 509 | RaiseInflightHighSlope(); |
| 510 | |
| 511 | model_->RestartRound(); |
| 512 | } |
| 513 | |
| 514 | void Bbr2ProbeBwMode::ExitProbeDown( |
| 515 | const Bbr2CongestionEvent& /*congestion_event*/) { |
| 516 | DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_DOWN); |
| 517 | if (!cycle_.has_advanced_max_bw) { |
| 518 | QUIC_DVLOG(2) << sender_ << " Advancing max bw filter at end of cycle."; |
| 519 | model_->AdvanceMaxBandwidthFilter(); |
| 520 | cycle_.has_advanced_max_bw = true; |
| 521 | } |
| 522 | } |
| 523 | |
| 524 | // static |
| 525 | const char* Bbr2ProbeBwMode::CyclePhaseToString(CyclePhase phase) { |
| 526 | switch (phase) { |
| 527 | case CyclePhase::PROBE_NOT_STARTED: |
| 528 | return "PROBE_NOT_STARTED"; |
| 529 | case CyclePhase::PROBE_UP: |
| 530 | return "PROBE_UP"; |
| 531 | case CyclePhase::PROBE_DOWN: |
| 532 | return "PROBE_DOWN"; |
| 533 | case CyclePhase::PROBE_CRUISE: |
| 534 | return "PROBE_CRUISE"; |
| 535 | case CyclePhase::PROBE_REFILL: |
| 536 | return "PROBE_REFILL"; |
| 537 | default: |
| 538 | break; |
| 539 | } |
| 540 | return "<Invalid CyclePhase>"; |
| 541 | } |
| 542 | |
| 543 | std::ostream& operator<<(std::ostream& os, |
| 544 | const Bbr2ProbeBwMode::CyclePhase phase) { |
| 545 | return os << Bbr2ProbeBwMode::CyclePhaseToString(phase); |
| 546 | } |
| 547 | |
| 548 | Bbr2ProbeBwMode::DebugState Bbr2ProbeBwMode::ExportDebugState() const { |
| 549 | DebugState s; |
| 550 | s.phase = cycle_.phase; |
| 551 | s.cycle_start_time = cycle_.cycle_start_time; |
| 552 | s.phase_start_time = cycle_.phase_start_time; |
| 553 | return s; |
| 554 | } |
| 555 | |
| 556 | std::ostream& operator<<(std::ostream& os, |
| 557 | const Bbr2ProbeBwMode::DebugState& state) { |
| 558 | os << "[PROBE_BW] phase: " << state.phase << "\n"; |
| 559 | os << "[PROBE_BW] cycle_start_time: " << state.cycle_start_time << "\n"; |
| 560 | os << "[PROBE_BW] phase_start_time: " << state.phase_start_time << "\n"; |
| 561 | return os; |
| 562 | } |
| 563 | |
| 564 | const Bbr2Params& Bbr2ProbeBwMode::Params() const { |
| 565 | return sender_->Params(); |
| 566 | } |
| 567 | |
| 568 | float Bbr2ProbeBwMode::PacingGainForPhase( |
| 569 | Bbr2ProbeBwMode::CyclePhase phase) const { |
| 570 | if (phase == Bbr2ProbeBwMode::CyclePhase::PROBE_UP) { |
| 571 | return Params().probe_bw_probe_up_pacing_gain; |
| 572 | } |
| 573 | if (phase == Bbr2ProbeBwMode::CyclePhase::PROBE_DOWN) { |
| 574 | return Params().probe_bw_probe_down_pacing_gain; |
| 575 | } |
| 576 | return Params().probe_bw_default_pacing_gain; |
| 577 | } |
| 578 | |
| 579 | } // namespace quic |