If B2SL option is set in QUIC BBR2, when STARTUP exits due to loss, it will set the inflight_hi to the max of bdp and max_bytes_delivered_in_round. Protected by FLAGS_quic_reloadable_flag_quic_bbr2_startup_loss_exit_use_max_delivered. PiperOrigin-RevId: 338737724 Change-Id: I2e7923606b6210af7f198e10687d99132dc23b07
diff --git a/quic/core/congestion_control/bbr2_misc.cc b/quic/core/congestion_control/bbr2_misc.cc index 9f6744e..3e3937a 100644 --- a/quic/core/congestion_control/bbr2_misc.cc +++ b/quic/core/congestion_control/bbr2_misc.cc
@@ -148,6 +148,18 @@ loss_events_in_round_++; } + if (GetQuicReloadableFlag(quic_bbr2_startup_loss_exit_use_max_delivered) && + congestion_event->bytes_acked > 0 && + congestion_event->last_packet_send_state.is_valid && + total_bytes_acked() > + congestion_event->last_packet_send_state.total_bytes_acked) { + QuicByteCount bytes_delivered = + total_bytes_acked() - + congestion_event->last_packet_send_state.total_bytes_acked; + max_bytes_delivered_in_round_ = + std::max(max_bytes_delivered_in_round_, bytes_delivered); + } + // |bandwidth_latest_| and |inflight_latest_| only increased within a round. if (sample.sample_max_bandwidth > bandwidth_latest_) { bandwidth_latest_ = sample.sample_max_bandwidth; @@ -282,6 +294,9 @@ void Bbr2NetworkModel::RestartRound() { bytes_lost_in_round_ = 0; loss_events_in_round_ = 0; + if (GetQuicReloadableFlag(quic_bbr2_startup_loss_exit_use_max_delivered)) { + max_bytes_delivered_in_round_ = 0; + } round_trip_counter_.RestartRound(); }
diff --git a/quic/core/congestion_control/bbr2_misc.h b/quic/core/congestion_control/bbr2_misc.h index 80a7423..f544273 100644 --- a/quic/core/congestion_control/bbr2_misc.h +++ b/quic/core/congestion_control/bbr2_misc.h
@@ -192,6 +192,9 @@ // Can be enabled by connection optoin 'B2HI'. bool limit_inflight_hi_by_cwnd = false; + + // Can be enabled by connection option 'B2SL'. + bool startup_loss_exit_use_max_delivered_for_inflight_hi = false; }; class QUIC_EXPORT_PRIVATE RoundTripCounter { @@ -421,6 +424,10 @@ int64_t loss_events_in_round() const { return loss_events_in_round_; } + QuicByteCount max_bytes_delivered_in_round() const { + return max_bytes_delivered_in_round_; + } + QuicPacketNumber end_of_app_limited_phase() const { return bandwidth_sampler_.end_of_app_limited_phase(); } @@ -473,6 +480,12 @@ // Number of loss marking events in the current round. int64_t loss_events_in_round_ = 0; + // A max of bytes delivered among all congestion events in the current round. + // A congestions event's bytes delivered is the total bytes acked between time + // Ts and Ta, which is the time when the largest acked packet(within the + // congestion event) was sent and acked, respectively. + QuicByteCount max_bytes_delivered_in_round_ = 0; + // Max bandwidth in the current round. Updated once per congestion event. QuicBandwidth bandwidth_latest_ = QuicBandwidth::Zero(); // Max bandwidth of recent rounds. Updated once per round.
diff --git a/quic/core/congestion_control/bbr2_sender.cc b/quic/core/congestion_control/bbr2_sender.cc index 4e57a9a..1c53323 100644 --- a/quic/core/congestion_control/bbr2_sender.cc +++ b/quic/core/congestion_control/bbr2_sender.cc
@@ -163,6 +163,10 @@ quic_bbr2_no_exit_startup_on_loss_with_bw_growth); params_.always_exit_startup_on_excess_loss = false; } + if (GetQuicReloadableFlag(quic_bbr2_startup_loss_exit_use_max_delivered) && + ContainsQuicTag(connection_options, kB2SL)) { + params_.startup_loss_exit_use_max_delivered_for_inflight_hi = true; + } if (ContainsQuicTag(connection_options, kBSAO)) { model_.EnableOverestimateAvoidance(); }
diff --git a/quic/core/congestion_control/bbr2_simulator_test.cc b/quic/core/congestion_control/bbr2_simulator_test.cc index 5e5ffee..3485c11 100644 --- a/quic/core/congestion_control/bbr2_simulator_test.cc +++ b/quic/core/congestion_control/bbr2_simulator_test.cc
@@ -734,6 +734,44 @@ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); } +// Test exiting STARTUP earlier upon loss due to loss when connection option +// B2SL is used. +TEST_F(Bbr2DefaultTopologyTest, ExitStartupDueToLossB2SL) { + SetConnectionOption(kB2SL); + DefaultTopologyParams params; + params.switch_queue_capacity_in_bdp = 0.5; + CreateNetwork(params); + + // Run until the full bandwidth is reached and check how many rounds it was. + sender_endpoint_.AddBytesToTransfer(12 * 1024 * 1024); + QuicRoundTripCount max_bw_round = 0; + QuicBandwidth max_bw(QuicBandwidth::Zero()); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this, &max_bw, &max_bw_round]() { + if (max_bw < sender_->ExportDebugState().bandwidth_hi) { + max_bw = sender_->ExportDebugState().bandwidth_hi; + max_bw_round = sender_->ExportDebugState().round_trip_count; + } + return sender_->ExportDebugState().startup.full_bandwidth_reached; + }, + QuicTime::Delta::FromSeconds(5)); + ASSERT_TRUE(simulator_result); + EXPECT_EQ(Bbr2Mode::DRAIN, sender_->ExportDebugState().mode); + EXPECT_GE(2u, sender_->ExportDebugState().round_trip_count - max_bw_round); + EXPECT_EQ( + 1u, + sender_->ExportDebugState().startup.round_trips_without_bandwidth_growth); + EXPECT_NE(0u, sender_connection_stats().packets_lost); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); + + if (GetQuicReloadableFlag(quic_bbr2_startup_loss_exit_use_max_delivered)) { + EXPECT_GT(sender_->ExportDebugState().inflight_hi, 1.2f * params.BDP()); + } else { + EXPECT_APPROX_EQ(sender_->ExportDebugState().inflight_hi, params.BDP(), + 0.1f); + } +} + TEST_F(Bbr2DefaultTopologyTest, SenderPoliced) { DefaultTopologyParams params; params.sender_policer_params = TrafficPolicerParams();
diff --git a/quic/core/congestion_control/bbr2_startup.cc b/quic/core/congestion_control/bbr2_startup.cc index 8947766..5c47adb 100644 --- a/quic/core/congestion_control/bbr2_startup.cc +++ b/quic/core/congestion_control/bbr2_startup.cc
@@ -107,11 +107,21 @@ // At the end of a round trip. Check if loss is too high in this round. if (model_->IsInflightTooHigh(congestion_event, Params().startup_full_loss_count)) { - const QuicByteCount bdp = model_->BDP(model_->MaxBandwidth()); - QUIC_DVLOG(3) << sender_ - << " Exiting STARTUP due to loss. inflight_hi:" << bdp; + QuicByteCount new_inflight_hi = model_->BDP(model_->MaxBandwidth()); + if (Params().startup_loss_exit_use_max_delivered_for_inflight_hi) { + if (new_inflight_hi < model_->max_bytes_delivered_in_round()) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_bbr2_startup_loss_exit_use_max_delivered, 1, 2); + new_inflight_hi = model_->max_bytes_delivered_in_round(); + } else { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_bbr2_startup_loss_exit_use_max_delivered, 2, 2); + } + } + QUIC_DVLOG(3) << sender_ << " Exiting STARTUP due to loss. inflight_hi:" + << new_inflight_hi; // TODO(ianswett): Add a shared method to set inflight_hi in the model. - model_->set_inflight_hi(bdp); + model_->set_inflight_hi(new_inflight_hi); full_bandwidth_reached_ = true; sender_->connection_stats_->bbr_exit_startup_due_to_loss = true;
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h index 50fa103..248d37c 100644 --- a/quic/core/crypto/crypto_protocol.h +++ b/quic/core/crypto/crypto_protocol.h
@@ -131,6 +131,10 @@ const QuicTag kB2HI = TAG('B', '2', 'H', 'I'); // Limit inflight_hi reduction // based on CWND. const QuicTag kB2HR = TAG('B', '2', 'H', 'R'); // 15% inflight_hi headroom. +const QuicTag kB2SL = TAG('B', '2', 'S', 'L'); // When exiting STARTUP due to + // loss, set inflight_hi to the + // max of bdp and max bytes + // delivered in round. const QuicTag kBSAO = TAG('B', 'S', 'A', 'O'); // Avoid Overestimation in // Bandwidth Sampler with ack // aggregation