Update BBR3 to more closely align with the BBRv3 draft.

BBR3 now uses a PROBE_UP cwnd gain of 2.25 and the Drain state is limited to 3 round trips. Removes the B201 connection option from tests, because it's been deprecated.

PiperOrigin-RevId: 909174463
diff --git a/quiche/quic/core/congestion_control/bbr2_misc.h b/quiche/quic/core/congestion_control/bbr2_misc.h
index 83ee77b..e9ee27b 100644
--- a/quiche/quic/core/congestion_control/bbr2_misc.h
+++ b/quiche/quic/core/congestion_control/bbr2_misc.h
@@ -154,6 +154,7 @@
   float probe_bw_default_pacing_gain = 1.0;
 
   float probe_bw_cwnd_gain = kDerivedDefaultCwndGain;
+  float probe_up_cwnd_gain = 2.25;  // For BBR3.
 
   /*
    * PROBE_UP parameters.
diff --git a/quiche/quic/core/congestion_control/bbr3_sender.cc b/quiche/quic/core/congestion_control/bbr3_sender.cc
index 7067b28..1f248f6 100644
--- a/quiche/quic/core/congestion_control/bbr3_sender.cc
+++ b/quiche/quic/core/congestion_control/bbr3_sender.cc
@@ -59,6 +59,8 @@
   }
   // The BBR IETF draft uses 0.5 as the drain pacing gain.
   params_.drain_pacing_gain = 0.5f;
+  // The BBR IETF draft uses 0.9 as the PROBE_DOWN pacing gain.
+  params_.probe_bw_probe_down_pacing_gain = 0.9f;
   // STARTUP has a shorter MaxAckHeightTracker window of 1 round.
   params_.startup_include_extra_acked = true;
   model_.SetMaxAckHeightTrackerWindowLength(1);
@@ -520,6 +522,7 @@
   model_.clear_bandwidth_lo();
   // Increase the max_ack_height_tracker window when exiting STARTUP from 1.
   model_.SetMaxAckHeightTrackerWindowLength(max_ack_height_window_length_);
+  drain_rounds_ = 0;
 }
 
 void Bbr3Sender::OnExitQuiescence(QuicTime now) {
@@ -689,12 +692,17 @@
   QUICHE_DCHECK_EQ(model_.cwnd_gain(), params_.drain_cwnd_gain);
   model_.set_cwnd_gain(params_.drain_cwnd_gain);
 
+  if (congestion_event.end_of_round_trip) {
+    ++drain_rounds_;
+  }
+
   QuicByteCount drain_target = DrainTarget();
-  if (congestion_event.bytes_in_flight <= drain_target) {
+  if (congestion_event.bytes_in_flight <= drain_target || drain_rounds_ > 3) {
     QUIC_DVLOG(3) << this << " Exiting DRAIN. bytes_in_flight:"
                   << congestion_event.bytes_in_flight
                   << ", bdp:" << model_.BDP()
-                  << ", drain_target:" << drain_target << "  @ "
+                  << ", drain_target:" << drain_target
+                  << ", drain_rounds:" << drain_rounds_ << "  @ "
                   << congestion_event.event_time;
     return Bbr2Mode::PROBE_BW;
   }
@@ -744,12 +752,17 @@
 
   // Do not need to set the gains if switching to PROBE_RTT, they will be set
   // when Bbr2ProbeRttMode::Enter is called.
-  if (!switch_to_probe_rtt) {
-    model_.set_pacing_gain(PacingGainForPhase(probe_bw_.phase));
+  if (switch_to_probe_rtt) {
+    return Bbr2Mode::PROBE_RTT;
+  }
+  model_.set_pacing_gain(PacingGainForPhase(probe_bw_.phase));
+  if (probe_bw_.phase == ProbePhase::PROBE_UP) {
+    model_.set_cwnd_gain(params_.probe_up_cwnd_gain);
+  } else {
     model_.set_cwnd_gain(params_.probe_bw_cwnd_gain);
   }
 
-  return switch_to_probe_rtt ? Bbr2Mode::PROBE_RTT : Bbr2Mode::PROBE_BW;
+  return Bbr2Mode::PROBE_BW;
 }
 
 void Bbr3Sender::UpdateProbeDown(QuicByteCount prior_in_flight,
diff --git a/quiche/quic/core/congestion_control/bbr3_sender.h b/quiche/quic/core/congestion_control/bbr3_sender.h
index 88b0855..a3aea6a 100644
--- a/quiche/quic/core/congestion_control/bbr3_sender.h
+++ b/quiche/quic/core/congestion_control/bbr3_sender.h
@@ -214,6 +214,9 @@
   // Startup state.
   QuicBandwidth startup_max_bw_at_round_beginning_ = QuicBandwidth::Zero();
 
+  // Drain state.
+  uint64_t drain_rounds_ = 0;
+
   // Probe BW state.
   struct ProbeBWState {
     QuicTime cycle_start_time = QuicTime::Zero();
diff --git a/quiche/quic/core/congestion_control/bbr3_simulator_test.cc b/quiche/quic/core/congestion_control/bbr3_simulator_test.cc
index d220de5..9818b12 100644
--- a/quiche/quic/core/congestion_control/bbr3_simulator_test.cc
+++ b/quiche/quic/core/congestion_control/bbr3_simulator_test.cc
@@ -1350,14 +1350,13 @@
   // Get the queue at the bottleneck, which is the outgoing queue at the port to
   // which the receiver is connected.
   const simulator::Queue* queue = switch_->port_queue(2);
-  bool simulator_result;
 
   // We have no intention of ever finishing this transfer.
   sender_endpoint_.AddBytesToTransfer(100 * 1024 * 1024);
 
   // Run the startup, and verify that it fills up the queue.
   ASSERT_EQ(Bbr2Mode::STARTUP, sender_->ExportDebugState().mode);
-  simulator_result = simulator_.RunUntilOrTimeout(
+  bool simulator_result = simulator_.RunUntilOrTimeout(
       [this]() {
         return sender_->ExportDebugState().mode != Bbr2Mode::STARTUP;
       },
@@ -1405,6 +1404,62 @@
   EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->smoothed_rtt(), 0.1f);
 }
 
+// Verify the flow exits DRAIN after 3 rounds no matter what.
+TEST_F(Bbr3DefaultTopologyTest, DrainExitDueTo3RoundLimit) {
+  DefaultTopologyParams params;
+  CreateNetwork(params);
+
+  const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(10);
+  // Get the queue at the bottleneck, which is the outgoing queue at the port to
+  // which the receiver is connected.
+  const simulator::Queue* queue = switch_->port_queue(2);
+
+  // We have no intention of ever finishing this transfer.
+  sender_endpoint_.AddBytesToTransfer(100 * 1024 * 1024);
+
+  // Run the startup, and verify that it fills up the queue.
+  ASSERT_EQ(Bbr2Mode::STARTUP, sender_->ExportDebugState().mode);
+  bool simulator_result = simulator_.RunUntilOrTimeout(
+      [this]() {
+        return sender_->ExportDebugState().mode != Bbr2Mode::STARTUP;
+      },
+      timeout);
+  ASSERT_TRUE(simulator_result);
+  ASSERT_EQ(Bbr2Mode::DRAIN, sender_->ExportDebugState().mode);
+  EXPECT_APPROX_EQ(sender_->BandwidthEstimate() * 0.5f, sender_->PacingRate(0),
+                   0.01f);
+
+  // BBR uses CWND gain of 2 during STARTUP, hence it will fill the buffer with
+  // approximately 1 BDP.  Here, we use 0.95 to give some margin for error.
+  EXPECT_GE(queue->bytes_queued(), 0.95 * params.BDP());
+
+  // Observe increased RTT due to bufferbloat.
+  const QuicTime::Delta queueing_delay =
+      params.test_link.bandwidth.TransferTime(queue->bytes_queued());
+  EXPECT_APPROX_EQ(params.RTT() + queueing_delay, rtt_stats()->latest_rtt(),
+                   0.1f);
+
+  // Transition to the drain phase and verify that it makes the queue
+  // have at most a BDP worth of packets.
+  const QuicRoundTripCount round_count_at_drain_entry =
+      sender_->ExportDebugState().round_trip_count;
+  // Half the bottleneck bandwidth so we never drain the queue.
+  params.test_link.bandwidth = params.BottleneckBandwidth() * 0.5f;
+  TestLink()->set_bandwidth(params.test_link.bandwidth);
+
+  simulator_result = simulator_.RunUntilOrTimeout(
+      [this]() { return sender_->ExportDebugState().mode != Bbr2Mode::DRAIN; },
+      timeout);
+  ASSERT_TRUE(simulator_result);
+  ASSERT_EQ(Bbr2Mode::PROBE_BW, sender_->ExportDebugState().mode);
+  // Verify we are exiting DRAIN with bytes_in_flight > DrainTarget(), which
+  // means we exited due to drain_rounds > 3.
+  EXPECT_GT(sender_unacked_map()->bytes_in_flight(),
+            sender_->ExportDebugState().drain.drain_target);
+  EXPECT_EQ(sender_->ExportDebugState().round_trip_count,
+            round_count_at_drain_entry + 3);
+}
+
 // Ensure that a connection that is app-limited and is at sufficiently low
 // bandwidth will not exit high gain phase, and similarly ensure that the
 // connection will exit low gain early if the number of bytes in flight is low.