gfe-relnote: In QUIC, add 2 connection options to set the start point of exponential backoff when calculating PTO timeout. Protected by exisiting gfe2_reloadable_flag_quic_enable_pto.

PiperOrigin-RevId: 278370348
Change-Id: I3bbafdc9789b4a38f2fa7175febf86cb8f419a29
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index 0e99be9..0aeb1f9 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -198,6 +198,10 @@
                                                  // when computing PTO timeout
                                                  // if an immediate ACK is
                                                  // expected.
+const QuicTag kPEB1 = TAG('P', 'E', 'B', '1');   // Start exponential backoff
+                                                 // since 1st PTO.
+const QuicTag kPEB2 = TAG('P', 'E', 'B', '2');   // Start exponential backoff
+                                                 // since 2nd PTO.
 
 // Optional support of truncated Connection IDs.  If sent by a peer, the value
 // is the minimum number of bytes allowed for the connection ID sent to the
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index d2ec4c8..bf100c5 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -444,11 +444,11 @@
     }
     if (config.HasClientSentConnectionOption(k7PTO, perspective_)) {
       max_consecutive_ptos_ = 6;
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 3, 5);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 3, 7);
     }
     if (config.HasClientSentConnectionOption(k8PTO, perspective_)) {
       max_consecutive_ptos_ = 7;
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 4, 5);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 4, 7);
     }
   }
   if (config.HasClientSentConnectionOption(kNSTP, perspective_)) {
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index 48a11c8..5880ea9 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -104,6 +104,7 @@
       forward_secure_packet_acked_(false),
       skip_packet_number_for_pto_(false),
       always_include_max_ack_delay_for_pto_timeout_(true),
+      pto_exponential_backoff_start_point_(0),
       neuter_handshake_packets_once_(
           GetQuicReloadableFlag(quic_neuter_handshake_packets_once)) {
   SetSendAlgorithm(congestion_control_type);
@@ -150,12 +151,12 @@
   if (GetQuicReloadableFlag(quic_enable_pto)) {
     if (config.HasClientSentConnectionOption(k2PTO, perspective)) {
       pto_enabled_ = true;
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 2, 5);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 2, 7);
     }
     if (config.HasClientSentConnectionOption(k1PTO, perspective)) {
       pto_enabled_ = true;
       max_probe_packets_per_pto_ = 1;
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 1, 5);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 1, 7);
     }
   }
 
@@ -171,10 +172,19 @@
     skip_packet_number_for_pto_ = true;
   }
 
-  if (pto_enabled_ &&
-      config.HasClientSentConnectionOption(kPTOA, perspective)) {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 5, 5);
-    always_include_max_ack_delay_for_pto_timeout_ = false;
+  if (pto_enabled_) {
+    if (config.HasClientSentConnectionOption(kPTOA, perspective)) {
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 5, 7);
+      always_include_max_ack_delay_for_pto_timeout_ = false;
+    }
+    if (config.HasClientSentConnectionOption(kPEB1, perspective)) {
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 6, 7);
+      StartExponentialBackoffAfterNthPto(1);
+    }
+    if (config.HasClientSentConnectionOption(kPEB2, perspective)) {
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 7, 7);
+      StartExponentialBackoffAfterNthPto(2);
+    }
   }
 
   // Configure congestion control.
@@ -800,6 +810,11 @@
   uber_loss_algorithm_.SetLossDetectionType(kIetfLossDetection);
 }
 
+void QuicSentPacketManager::StartExponentialBackoffAfterNthPto(
+    size_t exponential_backoff_start_point) {
+  pto_exponential_backoff_start_point_ = exponential_backoff_start_point;
+}
+
 QuicSentPacketManager::RetransmissionTimeoutMode
 QuicSentPacketManager::GetRetransmissionMode() const {
   DCHECK(unacked_packets_.HasInFlightPackets() ||
@@ -1026,7 +1041,9 @@
       std::max(4 * rtt_stats_.mean_deviation(),
                QuicTime::Delta::FromMilliseconds(1)) +
       (ShouldAddMaxAckDelay() ? peer_max_ack_delay_ : QuicTime::Delta::Zero());
-  return pto_delay * (1 << consecutive_pto_count_);
+  return pto_delay * (1 << (consecutive_pto_count_ -
+                            std::min(consecutive_pto_count_,
+                                     pto_exponential_backoff_start_point_)));
 }
 
 QuicTime::Delta QuicSentPacketManager::GetSlowStartDuration() const {
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index f15b3d5..5d362f4 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -383,6 +383,11 @@
   // Also enable IETF loss detection.
   void EnableIetfPtoAndLossDetection();
 
+  // Called to set the start point of doing exponential backoff when calculating
+  // PTO timeout.
+  void StartExponentialBackoffAfterNthPto(
+      size_t exponential_backoff_start_point);
+
   bool supports_multiple_packet_number_spaces() const {
     return unacked_packets_.supports_multiple_packet_number_spaces();
   }
@@ -631,6 +636,11 @@
   // If true, always include peer_max_ack_delay_ when calculating PTO timeout.
   bool always_include_max_ack_delay_for_pto_timeout_;
 
+  // When calculating PTO timeout, the start point of doing exponential backoff.
+  // For example, 0 : always do exponential backoff. n : do exponential backoff
+  // since nth PTO.
+  size_t pto_exponential_backoff_start_point_;
+
   // Latched value of quic_neuter_handshake_packets_once.
   const bool neuter_handshake_packets_once_;
 };
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index f2a72e0..c9d70b9 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -2882,6 +2882,121 @@
             manager_.GetRetransmissionTime());
 }
 
+TEST_F(QuicSentPacketManagerTest, StartExponentialBackoffSince2ndPto) {
+  EnablePto(k2PTO);
+  QuicConfig config;
+  QuicTagVector options;
+  options.push_back(kPEB2);
+  QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+  EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+  EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+  manager_.SetFromConfig(config);
+
+  EXPECT_CALL(*send_algorithm_, PacingRate(_))
+      .WillRepeatedly(Return(QuicBandwidth::Zero()));
+  EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+      .WillRepeatedly(Return(10 * kDefaultTCPMSS));
+  RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+  rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+                       QuicTime::Delta::Zero(), QuicTime::Zero());
+  QuicTime::Delta srtt = rtt_stats->smoothed_rtt();
+
+  SendDataPacket(1, ENCRYPTION_FORWARD_SECURE);
+  // Verify PTO is correctly set.
+  QuicTime::Delta expected_pto_delay =
+      srtt + 4 * rtt_stats->mean_deviation() +
+      QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs);
+  EXPECT_EQ(clock_.Now() + expected_pto_delay,
+            manager_.GetRetransmissionTime());
+
+  clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+  SendDataPacket(2, ENCRYPTION_FORWARD_SECURE);
+  // Verify PTO is correctly set based on sent time of packet 2.
+  EXPECT_EQ(clock_.Now() + expected_pto_delay,
+            manager_.GetRetransmissionTime());
+  EXPECT_EQ(0u, stats_.pto_count);
+
+  // Invoke PTO.
+  clock_.AdvanceTime(expected_pto_delay);
+  manager_.OnRetransmissionTimeout();
+  EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now()));
+  EXPECT_EQ(1u, stats_.pto_count);
+
+  // Verify two probe packets get sent.
+  EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+      .Times(2)
+      .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+        RetransmitDataPacket(3, type, ENCRYPTION_FORWARD_SECURE);
+      })))
+      .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+        RetransmitDataPacket(4, type, ENCRYPTION_FORWARD_SECURE);
+      })));
+  manager_.MaybeSendProbePackets();
+  // Verify no exponential backoff.
+  EXPECT_EQ(clock_.Now() + expected_pto_delay,
+            manager_.GetRetransmissionTime());
+
+  // Invoke 2nd PTO.
+  clock_.AdvanceTime(expected_pto_delay);
+  manager_.OnRetransmissionTimeout();
+  EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now()));
+  EXPECT_EQ(2u, stats_.pto_count);
+
+  // Verify two probe packets get sent.
+  EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+      .Times(2)
+      .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+        RetransmitDataPacket(5, type, ENCRYPTION_FORWARD_SECURE);
+      })))
+      .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+        RetransmitDataPacket(6, type, ENCRYPTION_FORWARD_SECURE);
+      })));
+  manager_.MaybeSendProbePackets();
+  // Verify still no exponential backoff.
+  EXPECT_EQ(clock_.Now() + expected_pto_delay,
+            manager_.GetRetransmissionTime());
+
+  // Invoke 3rd PTO.
+  clock_.AdvanceTime(expected_pto_delay);
+  manager_.OnRetransmissionTimeout();
+  EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now()));
+  EXPECT_EQ(3u, stats_.pto_count);
+
+  // Verify two probe packets get sent.
+  EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+      .Times(2)
+      .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+        RetransmitDataPacket(7, type, ENCRYPTION_FORWARD_SECURE);
+      })))
+      .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+        RetransmitDataPacket(8, type, ENCRYPTION_FORWARD_SECURE);
+      })));
+  manager_.MaybeSendProbePackets();
+  // Verify exponential backoff starts.
+  EXPECT_EQ(clock_.Now() + expected_pto_delay * 2,
+            manager_.GetRetransmissionTime());
+
+  // Invoke 4th PTO.
+  clock_.AdvanceTime(expected_pto_delay * 2);
+  manager_.OnRetransmissionTimeout();
+  EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now()));
+  EXPECT_EQ(4u, stats_.pto_count);
+
+  // Verify two probe packets get sent.
+  EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+      .Times(2)
+      .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+        RetransmitDataPacket(9, type, ENCRYPTION_FORWARD_SECURE);
+      })))
+      .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+        RetransmitDataPacket(10, type, ENCRYPTION_FORWARD_SECURE);
+      })));
+  manager_.MaybeSendProbePackets();
+  // Verify exponential backoff continues.
+  EXPECT_EQ(clock_.Now() + expected_pto_delay * 4,
+            manager_.GetRetransmissionTime());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic