gfe-relnote: Do not add peer_max_ack_delay if an immediate ACK is expected when calculating PTO timeout. Protected by existing gfe2_reloadable_flag_quic_enable_pto.

PiperOrigin-RevId: 277922554
Change-Id: I228b6bd1a299b8cb9a3f71eef089b680af1a80c0
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index 4d5a890..2d0f6d3 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -194,6 +194,10 @@
                                                  // consecutive PTOs.
 const QuicTag kPTOS = TAG('P', 'T', 'O', 'S');   // Skip packet number before
                                                  // sending the last PTO.
+const QuicTag kPTOA = TAG('P', 'T', 'O', 'A');   // Do not add max ack delay
+                                                 // when computing PTO timeout
+                                                 // if an immediate ACK is
+                                                 // expected.
 
 // 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 aa4ee06..d2ec4c8 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -326,7 +326,6 @@
       bytes_received_before_address_validation_(0),
       bytes_sent_before_address_validation_(0),
       address_validated_(false),
-      skip_packet_number_for_pto_(false),
       treat_queued_packets_as_sent_(
           GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)),
       mtu_discovery_v2_(GetQuicReloadableFlag(quic_mtu_discovery_v2)) {
@@ -445,16 +444,11 @@
     }
     if (config.HasClientSentConnectionOption(k7PTO, perspective_)) {
       max_consecutive_ptos_ = 6;
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 3, 4);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 3, 5);
     }
     if (config.HasClientSentConnectionOption(k8PTO, perspective_)) {
       max_consecutive_ptos_ = 7;
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 4, 4);
-    }
-    if (GetQuicReloadableFlag(quic_skip_packet_number_for_pto) &&
-        config.HasClientSentConnectionOption(kPTOS, perspective_)) {
-      QUIC_RELOADABLE_FLAG_COUNT(quic_skip_packet_number_for_pto);
-      skip_packet_number_for_pto_ = true;
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 4, 5);
     }
   }
   if (config.HasClientSentConnectionOption(kNSTP, perspective_)) {
@@ -2649,7 +2643,7 @@
 
   const auto retransmission_mode =
       sent_packet_manager_.OnRetransmissionTimeout();
-  if (skip_packet_number_for_pto_ &&
+  if (sent_packet_manager_.skip_packet_number_for_pto() &&
       retransmission_mode == QuicSentPacketManager::PTO_MODE &&
       sent_packet_manager_.pending_timer_transmission_count() == 1) {
     // Skip a packet number when a single PTO packet is sent to elicit an
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index f618ddf..7098cc8 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -1496,9 +1496,6 @@
   // EnforceAntiAmplificationLimit returns true.
   bool address_validated_;
 
-  // If true, skip packet number before sending the last PTO retransmission.
-  bool skip_packet_number_for_pto_;
-
   // Used to store content of packets which cannot be sent because of write
   // blocked. Packets' encrypted buffers are copied and owned by
   // buffered_packets_. From unacked_packet_map (and congestion control)'s
diff --git a/quic/core/quic_constants.h b/quic/core/quic_constants.h
index 3dc462c..6b22bbf 100644
--- a/quic/core/quic_constants.h
+++ b/quic/core/quic_constants.h
@@ -245,6 +245,15 @@
 // packet is lost due to early retransmission by time based loss detection.
 static const int kDefaultLossDelayShift = 2;
 
+// Maximum number of retransmittable packets received before sending an ack.
+const QuicPacketCount kDefaultRetransmittablePacketsBeforeAck = 2;
+// Wait for up to 10 retransmittable packets before sending an ack.
+const QuicPacketCount kMaxRetransmittablePacketsBeforeAck = 10;
+// Minimum number of packets received before ack decimation is enabled.
+// This intends to avoid the beginning of slow start, when CWNDs may be
+// rapidly increasing.
+const QuicPacketCount kMinReceivedBeforeAckDecimation = 100;
+
 // Packet number of first sending packet of a connection. Please note, this
 // cannot be used as first received packet because peer can choose its starting
 // packet number.
diff --git a/quic/core/quic_received_packet_manager.cc b/quic/core/quic_received_packet_manager.cc
index e21f4c4..908d04f 100644
--- a/quic/core/quic_received_packet_manager.cc
+++ b/quic/core/quic_received_packet_manager.cc
@@ -25,14 +25,6 @@
 // against an ack loss
 const size_t kMaxPacketsAfterNewMissing = 4;
 
-// Maximum number of retransmittable packets received before sending an ack.
-const QuicPacketCount kDefaultRetransmittablePacketsBeforeAck = 2;
-// Minimum number of packets received before ack decimation is enabled.
-// This intends to avoid the beginning of slow start, when CWNDs may be
-// rapidly increasing.
-const QuicPacketCount kMinReceivedBeforeAckDecimation = 100;
-// Wait for up to 10 retransmittable packets before sending an ack.
-const QuicPacketCount kMaxRetransmittablePacketsBeforeAck = 10;
 // One quarter RTT delay when doing ack decimation.
 const float kAckDecimationDelay = 0.25;
 // One eighth RTT delay when doing ack decimation.
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index 2b52dd2..48a11c8 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -102,6 +102,8 @@
       consecutive_pto_count_(0),
       handshake_mode_disabled_(false),
       forward_secure_packet_acked_(false),
+      skip_packet_number_for_pto_(false),
+      always_include_max_ack_delay_for_pto_timeout_(true),
       neuter_handshake_packets_once_(
           GetQuicReloadableFlag(quic_neuter_handshake_packets_once)) {
   SetSendAlgorithm(congestion_control_type);
@@ -148,15 +150,33 @@
   if (GetQuicReloadableFlag(quic_enable_pto)) {
     if (config.HasClientSentConnectionOption(k2PTO, perspective)) {
       pto_enabled_ = true;
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 2, 4);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 2, 5);
     }
     if (config.HasClientSentConnectionOption(k1PTO, perspective)) {
       pto_enabled_ = true;
       max_probe_packets_per_pto_ = 1;
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 1, 4);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 1, 5);
     }
   }
 
+  if (GetQuicReloadableFlag(quic_skip_packet_number_for_pto) &&
+      config.HasClientSentConnectionOption(kPTOS, perspective)) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_skip_packet_number_for_pto);
+    if (!pto_enabled_) {
+      QUIC_PEER_BUG
+          << "PTO is not enabled when receiving PTOS connection option.";
+      pto_enabled_ = true;
+      max_probe_packets_per_pto_ = 1;
+    }
+    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;
+  }
+
   // Configure congestion control.
   if (config.HasClientRequestedIndependentOption(kTBBR, perspective)) {
     SetSendAlgorithm(kBBR);
@@ -430,6 +450,37 @@
   }
 }
 
+bool QuicSentPacketManager::ShouldAddMaxAckDelay() const {
+  DCHECK(pto_enabled_);
+  if (always_include_max_ack_delay_for_pto_timeout_) {
+    return true;
+  }
+  if (!unacked_packets_
+           .GetLargestSentRetransmittableOfPacketNumberSpace(APPLICATION_DATA)
+           .IsInitialized() ||
+      unacked_packets_.GetLargestSentRetransmittableOfPacketNumberSpace(
+          APPLICATION_DATA) <
+          FirstSendingPacketNumber() + kMinReceivedBeforeAckDecimation - 1) {
+    // Peer is doing TCP style acking. Expect an immediate ACK if more than 1
+    // packet are outstanding.
+    if (unacked_packets_.packets_in_flight() >=
+        kDefaultRetransmittablePacketsBeforeAck) {
+      return false;
+    }
+  } else if (unacked_packets_.packets_in_flight() >=
+             kMaxRetransmittablePacketsBeforeAck) {
+    // Peer is doing ack decimation. Expect an immediate ACK if >= 10
+    // packets are outstanding.
+    return false;
+  }
+  if (skip_packet_number_for_pto_ && consecutive_pto_count_ > 0) {
+    // An immediate ACK is expected when doing PTOS. Please note, this will miss
+    // cases when PTO fires and turns out to be spurious.
+    return false;
+  }
+  return true;
+}
+
 void QuicSentPacketManager::MarkForRetransmission(
     QuicPacketNumber packet_number,
     TransmissionType transmission_type) {
@@ -974,7 +1025,7 @@
       rtt_stats_.smoothed_rtt() +
       std::max(4 * rtt_stats_.mean_deviation(),
                QuicTime::Delta::FromMilliseconds(1)) +
-      peer_max_ack_delay_;
+      (ShouldAddMaxAckDelay() ? peer_max_ack_delay_ : QuicTime::Delta::Zero());
   return pto_delay * (1 << consecutive_pto_count_);
 }
 
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index b56b999..f15b3d5 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -395,6 +395,10 @@
     return forward_secure_packet_acked_;
   }
 
+  bool skip_packet_number_for_pto() const {
+    return skip_packet_number_for_pto_;
+  }
+
  private:
   friend class test::QuicConnectionPeer;
   friend class test::QuicSentPacketManagerPeer;
@@ -504,6 +508,10 @@
   // switches to IETF QUIC with QUIC TLS.
   void NeuterHandshakePackets();
 
+  // Indicates whether including peer_max_ack_delay_ when calculating PTO
+  // timeout.
+  bool ShouldAddMaxAckDelay() const;
+
   // Newly serialized retransmittable packets are added to this map, which
   // contains owning pointers to any contained frames.  If a packet is
   // retransmitted, this map will contain entries for both the old and the new
@@ -617,6 +625,12 @@
   // ENCRYPTION_FORWARD_SECURE packet.
   bool forward_secure_packet_acked_;
 
+  // If true, skip packet number before sending the last PTO retransmission.
+  bool skip_packet_number_for_pto_;
+
+  // If true, always include peer_max_ack_delay_ when calculating PTO timeout.
+  bool always_include_max_ack_delay_for_pto_timeout_;
+
   // 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 9567fc4..f2a72e0 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -2760,6 +2760,128 @@
   EXPECT_TRUE(manager_.forward_secure_packet_acked());
 }
 
+TEST_F(QuicSentPacketManagerTest, PtoTimeoutIncludesMaxAckDelay) {
+  EnablePto(k1PTO);
+  // Use PTOS and PTOA.
+  QuicConfig config;
+  QuicTagVector options;
+  options.push_back(kPTOS);
+  options.push_back(kPTOA);
+  QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+  EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+  EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+  manager_.SetFromConfig(config);
+  EXPECT_TRUE(manager_.skip_packet_number_for_pto());
+  EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true));
+
+  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 and ack delay is included.
+  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 but ack delay is
+  // not included as an immediate ACK is expected.
+  expected_pto_delay = expected_pto_delay - QuicTime::Delta::FromMilliseconds(
+                                                kDefaultDelayedAckTimeMs);
+  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 1 probe packets get sent and packet number gets skipped.
+  EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+      .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+        RetransmitDataPacket(4, type, ENCRYPTION_FORWARD_SECURE);
+      })));
+  manager_.MaybeSendProbePackets();
+  // Verify PTO period gets set to twice the current value. Also, ack delay is
+  // not included.
+  QuicTime sent_time = clock_.Now();
+  EXPECT_EQ(sent_time + expected_pto_delay * 2,
+            manager_.GetRetransmissionTime());
+
+  // Received ACK for packets 1 and 2.
+  uint64_t acked[] = {1, 2};
+  ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
+  manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(3));
+  EXPECT_EQ(PACKETS_NEWLY_ACKED,
+            manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(1),
+                                   ENCRYPTION_FORWARD_SECURE));
+  expected_pto_delay =
+      rtt_stats->SmoothedOrInitialRtt() +
+      std::max(4 * rtt_stats->mean_deviation(),
+               QuicTime::Delta::FromMilliseconds(1)) +
+      QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs);
+
+  // Verify PTO is correctly re-armed based on sent time of packet 4. Because of
+  // PTOS turns out to be spurious, ACK delay is included.
+  EXPECT_EQ(sent_time + expected_pto_delay, manager_.GetRetransmissionTime());
+
+  // Received ACK for packets 4.
+  ExpectAck(4);
+  manager_.OnAckFrameStart(QuicPacketNumber(4), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(4), QuicPacketNumber(5));
+  EXPECT_EQ(PACKETS_NEWLY_ACKED,
+            manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(4),
+                                   ENCRYPTION_FORWARD_SECURE));
+  EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
+  // Send more packets, such that peer will do ack decimation.
+  std::vector<uint64_t> acked2;
+  for (size_t i = 5; i <= 100; ++i) {
+    SendDataPacket(i, ENCRYPTION_FORWARD_SECURE);
+    acked2.push_back(i);
+  }
+  // Received ACK for all sent packets.
+  ExpectAcksAndLosses(true, &acked2[0], acked2.size(), nullptr, 0);
+  manager_.OnAckFrameStart(QuicPacketNumber(100), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(5), QuicPacketNumber(101));
+  EXPECT_EQ(PACKETS_NEWLY_ACKED,
+            manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(100),
+                                   ENCRYPTION_FORWARD_SECURE));
+
+  expected_pto_delay =
+      rtt_stats->SmoothedOrInitialRtt() +
+      std::max(4 * rtt_stats->mean_deviation(),
+               QuicTime::Delta::FromMilliseconds(1)) +
+      QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs);
+  for (size_t i = 101; i < 110; i++) {
+    SendDataPacket(i, ENCRYPTION_FORWARD_SECURE);
+    // Verify PTO timeout includes ACK delay as there are less than 10 packets
+    // outstanding.
+    EXPECT_EQ(clock_.Now() + expected_pto_delay,
+              manager_.GetRetransmissionTime());
+  }
+  expected_pto_delay = expected_pto_delay - QuicTime::Delta::FromMilliseconds(
+                                                kDefaultDelayedAckTimeMs);
+  SendDataPacket(110, ENCRYPTION_FORWARD_SECURE);
+  // Verify ACK delay is excluded.
+  EXPECT_EQ(clock_.Now() + expected_pto_delay,
+            manager_.GetRetransmissionTime());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/quic_unacked_packet_map.cc b/quic/core/quic_unacked_packet_map.cc
index a4497c3..d8beb43 100644
--- a/quic/core/quic_unacked_packet_map.cc
+++ b/quic/core/quic_unacked_packet_map.cc
@@ -28,6 +28,7 @@
     : perspective_(perspective),
       least_unacked_(FirstSendingPacketNumber()),
       bytes_in_flight_(0),
+      packets_in_flight_(0),
       last_inflight_packet_sent_time_(QuicTime::Zero()),
       last_crypto_packet_sent_time_(QuicTime::Zero()),
       session_notifier_(nullptr),
@@ -70,6 +71,7 @@
   }
   if (set_in_flight) {
     bytes_in_flight_ += bytes_sent;
+    ++packets_in_flight_;
     info.in_flight = true;
     largest_sent_retransmittable_packets_[GetPacketNumberSpace(
         info.encryption_level)] = packet_number;
@@ -191,7 +193,9 @@
 void QuicUnackedPacketMap::RemoveFromInFlight(QuicTransmissionInfo* info) {
   if (info->in_flight) {
     QUIC_BUG_IF(bytes_in_flight_ < info->bytes_sent);
+    QUIC_BUG_IF(packets_in_flight_ == 0);
     bytes_in_flight_ -= info->bytes_sent;
+    --packets_in_flight_;
     info->in_flight = false;
   }
 }
diff --git a/quic/core/quic_unacked_packet_map.h b/quic/core/quic_unacked_packet_map.h
index 6b390b7..fd6510a 100644
--- a/quic/core/quic_unacked_packet_map.h
+++ b/quic/core/quic_unacked_packet_map.h
@@ -94,6 +94,7 @@
 
   // Returns the sum of bytes from all packets in flight.
   QuicByteCount bytes_in_flight() const { return bytes_in_flight_; }
+  QuicPacketCount packets_in_flight() const { return packets_in_flight_; }
 
   // Returns the smallest packet number of a serialized packet which has not
   // been acked by the peer.  If there are no unacked packets, returns 0.
@@ -134,6 +135,7 @@
   size_t GetNumUnackedPacketsDebugOnly() const;
 
   // Returns true if there are multiple packets in flight.
+  // TODO(fayang): Remove this method and use packets_in_flight_ instead.
   bool HasMultipleInFlightPackets() const;
 
   // Returns true if there are any pending crypto packets.
@@ -260,6 +262,7 @@
   QuicPacketNumber least_unacked_;
 
   QuicByteCount bytes_in_flight_;
+  QuicPacketCount packets_in_flight_;
 
   // Time that the last inflight packet was sent.
   QuicTime last_inflight_packet_sent_time_;