gfe-relnote: In QUIC, add probe timeout mode, which unifies TLP and RTO. Protected by gfe2_reloadable_flag_quic_enable_pto.
PTO mode is enabled on client side if gfe2_reloadable_flag_quic_enable_pto is true and client sends 1PTO or 2PTO. PTO mode is enabled on server side if gfe2_reloadable_flag_quic_enable_pto is true and server receives 1PTO or 2PTO from client.
Connection is closed after 7 or 8 PTO depending on connection option 7PTO or 8PTO, respectively.
PiperOrigin-RevId: 263574963
Change-Id: Id952a3e4640146c3fe72e3d6745cbac5ee16dcdc
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index 54fbdfa..c1da218 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -115,6 +115,9 @@
QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs)),
rtt_updated_(false),
acked_packets_iter_(last_ack_frame_.packets.rbegin()),
+ enable_pto_(false),
+ max_probe_packets_per_pto_(2),
+ consecutive_pto_count_(0),
loss_removes_from_inflight_(
GetQuicReloadableFlag(quic_loss_removes_from_inflight)),
ignore_tlpr_if_no_pending_stream_data_(
@@ -180,6 +183,18 @@
}
}
+ if (GetQuicReloadableFlag(quic_enable_pto) && fix_rto_retransmission_) {
+ if (config.HasClientSentConnectionOption(k2PTO, perspective)) {
+ enable_pto_ = true;
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 2, 4);
+ }
+ if (config.HasClientSentConnectionOption(k1PTO, perspective)) {
+ enable_pto_ = true;
+ max_probe_packets_per_pto_ = 1;
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 1, 4);
+ }
+ }
+
// Configure congestion control.
if (config.HasClientRequestedIndependentOption(kTBBR, perspective)) {
SetSendAlgorithm(kBBR);
@@ -340,6 +355,7 @@
// Reset all retransmit counters any time a new packet is acked.
consecutive_rto_count_ = 0;
consecutive_tlp_count_ = 0;
+ consecutive_pto_count_ = 0;
consecutive_crypto_retransmission_count_ = 0;
}
@@ -470,7 +486,7 @@
<< "transmission_type: "
<< QuicUtils::TransmissionTypeToString(transmission_type);
// Handshake packets should never be sent as probing retransmissions.
- DCHECK(!transmission_info->has_crypto_handshake ||
+ DCHECK(enable_pto_ || !transmission_info->has_crypto_handshake ||
transmission_type != PROBING_RETRANSMISSION);
if (!loss_removes_from_inflight_ &&
!RetransmissionLeavesBytesInFlight(transmission_type)) {
@@ -741,6 +757,12 @@
++stats_->rto_count;
RetransmitRtoPackets();
return RTO_MODE;
+ case PTO_MODE:
+ QUIC_DVLOG(1) << ENDPOINT << "PTO mode";
+ ++stats_->pto_count;
+ ++consecutive_pto_count_;
+ pending_timer_transmission_count_ = max_probe_packets_per_pto_;
+ return PTO_MODE;
}
}
@@ -776,6 +798,7 @@
}
bool QuicSentPacketManager::MaybeRetransmitTailLossProbe() {
+ DCHECK(!enable_pto_);
if (pending_timer_transmission_count_ == 0) {
return false;
}
@@ -804,6 +827,7 @@
}
void QuicSentPacketManager::RetransmitRtoPackets() {
+ DCHECK(!enable_pto_);
QUIC_BUG_IF(pending_timer_transmission_count_ > 0)
<< "Retransmissions already queued:" << pending_timer_transmission_count_;
// Mark two packets for retransmission.
@@ -859,6 +883,41 @@
}
}
+void QuicSentPacketManager::MaybeSendProbePackets() {
+ if (pending_timer_transmission_count_ == 0) {
+ return;
+ }
+ QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked();
+ std::vector<QuicPacketNumber> probing_packets;
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++packet_number) {
+ if (it->state == OUTSTANDING &&
+ unacked_packets_.HasRetransmittableFrames(*it)) {
+ probing_packets.push_back(packet_number);
+ if (probing_packets.size() == pending_timer_transmission_count_) {
+ break;
+ }
+ }
+ }
+
+ for (QuicPacketNumber retransmission : probing_packets) {
+ QUIC_DVLOG(1) << ENDPOINT << "Marking " << retransmission
+ << " for probing retransmission";
+ MarkForRetransmission(retransmission, PROBING_RETRANSMISSION);
+ }
+ // It is possible that there is not enough outstanding data for probing.
+}
+
+void QuicSentPacketManager::AdjustPendingTimerTransmissions() {
+ if (pending_timer_transmission_count_ < max_probe_packets_per_pto_) {
+ // There are packets sent already, clear credit.
+ pending_timer_transmission_count_ = 0;
+ return;
+ }
+ // No packet gets sent, leave 1 credit to allow data to be write eventually.
+ pending_timer_transmission_count_ = 1;
+}
+
QuicSentPacketManager::RetransmissionTimeoutMode
QuicSentPacketManager::GetRetransmissionMode() const {
DCHECK(unacked_packets_.HasInFlightPackets());
@@ -868,6 +927,9 @@
if (loss_algorithm_->GetLossTimeout() != QuicTime::Zero()) {
return LOSS_MODE;
}
+ if (enable_pto_) {
+ return PTO_MODE;
+ }
if (consecutive_tlp_count_ < max_tail_loss_probes_) {
if (unacked_packets_.HasUnackedRetransmittableFrames()) {
return TLP_MODE;
@@ -962,6 +1024,7 @@
case LOSS_MODE:
return loss_algorithm_->GetLossTimeout();
case TLP_MODE: {
+ DCHECK(!enable_pto_);
// TODO(ianswett): When CWND is available, it would be preferable to
// set the timer based on the earliest retransmittable packet.
// Base the updated timer on the send time of the last packet.
@@ -971,6 +1034,7 @@
return std::max(clock_->ApproximateNow(), tlp_time);
}
case RTO_MODE: {
+ DCHECK(!enable_pto_);
// The RTO is based on the first outstanding packet.
const QuicTime sent_time = unacked_packets_.GetLastPacketSentTime();
QuicTime rto_time = sent_time + GetRetransmissionDelay();
@@ -979,6 +1043,12 @@
unacked_packets_.GetLastPacketSentTime() + GetTailLossProbeDelay();
return std::max(tlp_time, rto_time);
}
+ case PTO_MODE: {
+ // Ensure PTO never gets set to a time in the past.
+ return std::max(
+ clock_->ApproximateNow(),
+ unacked_packets_.GetLastPacketSentTime() + GetProbeTimeoutDelay());
+ }
}
DCHECK(false);
return QuicTime::Zero();
@@ -1070,6 +1140,22 @@
return retransmission_delay;
}
+const QuicTime::Delta QuicSentPacketManager::GetProbeTimeoutDelay() const {
+ DCHECK(enable_pto_);
+ if (rtt_stats_.smoothed_rtt().IsZero()) {
+ if (rtt_stats_.initial_rtt().IsZero()) {
+ return QuicTime::Delta::FromSeconds(1);
+ }
+ return 2 * rtt_stats_.initial_rtt();
+ }
+ const QuicTime::Delta pto_delay =
+ rtt_stats_.smoothed_rtt() +
+ std::max(4 * rtt_stats_.mean_deviation(),
+ QuicTime::Delta::FromMilliseconds(1)) +
+ peer_max_ack_delay_;
+ return pto_delay * (1 << consecutive_pto_count_);
+}
+
QuicTime::Delta QuicSentPacketManager::GetSlowStartDuration() const {
if (send_algorithm_->GetCongestionControlType() != kBBR) {
return QuicTime::Delta::Infinite();
@@ -1124,6 +1210,7 @@
}
consecutive_rto_count_ = 0;
consecutive_tlp_count_ = 0;
+ consecutive_pto_count_ = 0;
rtt_stats_.OnConnectionMigration();
send_algorithm_->OnConnectionMigration();
}