Refactor QUIC Bbr2's CheckBandwidthGrowth() into HasBandwidthGrowth() so when the excess queue criteria is used, we can stay in STARTUP if there was bandwidth growth that round.

Protected by quic_reloadable_flag_quic_bbr2_exit_startup_on_persistent_queue2.

PiperOrigin-RevId: 404873466
diff --git a/quic/core/congestion_control/bbr2_misc.cc b/quic/core/congestion_control/bbr2_misc.cc
index 6374e0b..9600f13 100644
--- a/quic/core/congestion_control/bbr2_misc.cc
+++ b/quic/core/congestion_control/bbr2_misc.cc
@@ -396,13 +396,10 @@
   return inflight_hi_ > headroom ? inflight_hi_ - headroom : 0;
 }
 
-Bbr2NetworkModel::BandwidthGrowth Bbr2NetworkModel::CheckBandwidthGrowth(
+bool Bbr2NetworkModel::HasBandwidthGrowth(
     const Bbr2CongestionEvent& congestion_event) {
   QUICHE_DCHECK(!full_bandwidth_reached_);
   QUICHE_DCHECK(congestion_event.end_of_round_trip);
-  if (congestion_event.last_packet_send_state.is_app_limited) {
-    return APP_LIMITED;
-  }
 
   QuicBandwidth threshold =
       full_bandwidth_baseline_ * Params().startup_full_bw_threshold;
@@ -413,14 +410,15 @@
                   << " (Still growing)  @ " << congestion_event.event_time;
     full_bandwidth_baseline_ = MaxBandwidth();
     rounds_without_bandwidth_growth_ = 0;
-    return GROWTH;
+    return true;
   }
-
   ++rounds_without_bandwidth_growth_;
-  BandwidthGrowth return_value = NO_GROWTH;
-  if (rounds_without_bandwidth_growth_ >= Params().startup_full_bw_rounds) {
+
+  // full_bandwidth_reached is only set to true when not app-limited, except
+  // when exit_startup_on_persistent_queue is true.
+  if (rounds_without_bandwidth_growth_ >= Params().startup_full_bw_rounds &&
+      !congestion_event.last_packet_send_state.is_app_limited) {
     full_bandwidth_reached_ = true;
-    return_value = EXIT;
   }
   QUIC_DVLOG(3) << " CheckBandwidthGrowth at end of round. max_bandwidth:"
                 << MaxBandwidth() << ", threshold:" << threshold
@@ -428,13 +426,12 @@
                 << " full_bw_reached:" << full_bandwidth_reached_ << "  @ "
                 << congestion_event.event_time;
 
-  return return_value;
+  return false;
 }
 
 bool Bbr2NetworkModel::CheckPersistentQueue(
     const Bbr2CongestionEvent& congestion_event, float bdp_gain) {
   QUICHE_DCHECK(congestion_event.end_of_round_trip);
-  QUICHE_DCHECK_NE(0u, min_bytes_in_flight_in_round_);
   if (min_bytes_in_flight_in_round_ >
       (bdp_gain * BDP() + QueueingThresholdExtraBytes())) {
     full_bandwidth_reached_ = true;
diff --git a/quic/core/congestion_control/bbr2_misc.h b/quic/core/congestion_control/bbr2_misc.h
index 6890534..8c8cfe1 100644
--- a/quic/core/congestion_control/bbr2_misc.h
+++ b/quic/core/congestion_control/bbr2_misc.h
@@ -456,22 +456,11 @@
   bool IsInflightTooHigh(const Bbr2CongestionEvent& congestion_event,
                          int64_t max_loss_events) const;
 
-  enum BandwidthGrowth {
-    APP_LIMITED = 0,
-    NO_GROWTH = 1,
-    GROWTH = 2,
-    EXIT = 3,  // Too many rounds without bandwidth growth.
-  };
-
   // Check bandwidth growth in the past round. Must be called at the end of a
-  // round.
-  // Return APP_LIMITED if the bandwidth sample was app-limited.
-  // Return GROWTH if the bandwidth grew as expected.
-  // Return NO_GROWTH if the bandwidth didn't increase enough.
-  // Return TOO_MANY_ROUNDS_WITH_NO_GROWTH if enough rounds have elapsed without
-  // growth, also sets |full_bandwidth_reached_| to true.
-  BandwidthGrowth CheckBandwidthGrowth(
-      const Bbr2CongestionEvent& congestion_event);
+  // round. Returns true if there was sufficient bandwidth growth and false
+  // otherwise.  If it's been too many rounds without growth, also sets
+  // |full_bandwidth_reached_| to true.
+  bool HasBandwidthGrowth(const Bbr2CongestionEvent& congestion_event);
 
   // Returns true if the minimum bytes in flight during the round is greater
   // than the BDP * |bdp_gain|.
diff --git a/quic/core/congestion_control/bbr2_sender.cc b/quic/core/congestion_control/bbr2_sender.cc
index b016ba1..3f1c449 100644
--- a/quic/core/congestion_control/bbr2_sender.cc
+++ b/quic/core/congestion_control/bbr2_sender.cc
@@ -194,7 +194,7 @@
     QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_startup_extra_acked, 2, 2);
     params_.startup_include_extra_acked = true;
   }
-  if (GetQuicReloadableFlag(quic_bbr2_exit_startup_on_persistent_queue) &&
+  if (GetQuicReloadableFlag(quic_bbr2_exit_startup_on_persistent_queue2) &&
       ContainsQuicTag(connection_options, kB207)) {
     params_.exit_startup_on_persistent_queue = true;
   }
diff --git a/quic/core/congestion_control/bbr2_simulator_test.cc b/quic/core/congestion_control/bbr2_simulator_test.cc
index 0968bb1..333267f 100644
--- a/quic/core/congestion_control/bbr2_simulator_test.cc
+++ b/quic/core/congestion_control/bbr2_simulator_test.cc
@@ -391,7 +391,7 @@
 }
 
 TEST_F(Bbr2DefaultTopologyTest, NormalStartupB207) {
-  SetQuicReloadableFlag(quic_bbr2_exit_startup_on_persistent_queue, true);
+  SetQuicReloadableFlag(quic_bbr2_exit_startup_on_persistent_queue2, true);
   SetConnectionOption(kB207);
   DefaultTopologyParams params;
   CreateNetwork(params);
@@ -411,9 +411,9 @@
       QuicTime::Delta::FromSeconds(5));
   ASSERT_TRUE(simulator_result);
   EXPECT_EQ(Bbr2Mode::DRAIN, sender_->ExportDebugState().mode);
-  EXPECT_EQ(0u, sender_->ExportDebugState().round_trip_count - max_bw_round);
+  EXPECT_EQ(3u, sender_->ExportDebugState().round_trip_count - max_bw_round);
   EXPECT_EQ(
-      0u,
+      3u,
       sender_->ExportDebugState().startup.round_trips_without_bandwidth_growth);
   EXPECT_EQ(0u, sender_connection_stats().packets_lost);
 }
@@ -512,7 +512,7 @@
 }
 
 TEST_F(Bbr2DefaultTopologyTest, SimpleTransferB207) {
-  SetQuicReloadableFlag(quic_bbr2_exit_startup_on_persistent_queue, true);
+  SetQuicReloadableFlag(quic_bbr2_exit_startup_on_persistent_queue2, true);
   SetConnectionOption(kB207);
   DefaultTopologyParams params;
   CreateNetwork(params);
diff --git a/quic/core/congestion_control/bbr2_startup.cc b/quic/core/congestion_control/bbr2_startup.cc
index 5821a0b..690bdb2 100644
--- a/quic/core/congestion_control/bbr2_startup.cc
+++ b/quic/core/congestion_control/bbr2_startup.cc
@@ -54,21 +54,18 @@
   if (!congestion_event.end_of_round_trip) {
     return Bbr2Mode::STARTUP;
   }
-  if (Params().exit_startup_on_persistent_queue) {
-    QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_exit_startup_on_persistent_queue);
+  bool has_bandwidth_growth = model_->HasBandwidthGrowth(congestion_event);
+  if (Params().exit_startup_on_persistent_queue && !has_bandwidth_growth) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_exit_startup_on_persistent_queue2);
     model_->CheckPersistentQueue(congestion_event, Params().startup_cwnd_gain);
   }
-  if (!model_->full_bandwidth_reached()) {
-    // TCP BBR always exits upon excessive losses. QUIC BBRv1 does not exit
-    // upon excessive losses, if enough bandwidth growth is observed.
-    Bbr2NetworkModel::BandwidthGrowth bw_growth =
-        model_->CheckBandwidthGrowth(congestion_event);
-
-    if (Params().always_exit_startup_on_excess_loss ||
-        (bw_growth == Bbr2NetworkModel::NO_GROWTH ||
-         bw_growth == Bbr2NetworkModel::EXIT)) {
-      CheckExcessiveLosses(congestion_event);
-    }
+  // TCP BBR always exits upon excessive losses. QUIC BBRv1 does not exit
+  // upon excessive losses, if enough bandwidth growth is observed or if the
+  // sample was app limited.
+  if (Params().always_exit_startup_on_excess_loss ||
+      (!congestion_event.last_packet_send_state.is_app_limited &&
+       !has_bandwidth_growth)) {
+    CheckExcessiveLosses(congestion_event);
   }
 
   if (Params().decrease_startup_pacing_at_end_of_round) {
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 1c70b08..a4884ab 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -140,7 +140,7 @@
 // When true, the B204 connection option enables extra acked in STARTUP, but also adds new logic to decrease it whenever max bandwidth increases.
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_startup_extra_acked, true)
 // When true, the B207 connection option causes BBR2 to exit STARTUP if a persistent queue of 2*BDP has existed for the entire round.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_exit_startup_on_persistent_queue, true)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_exit_startup_on_persistent_queue2, true)
 // When true, the BBQ0 connection option causes QUIC BBR2 to add bytes_acked to probe_up_acked if the connection hasn\'t been app-limited since inflight_hi was utilized.
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_add_bytes_acked_after_inflight_hi_limited, true)
 // When true, the BBR4 copt sets the extra_acked window to 20 RTTs and BBR5 sets it to 40 RTTs.