gfe-relnote: Let GFE be able to use FIFO write scheduler in QUIC and enable it via a connection option FIFO. Protected by gfe2_reloadable_flag_quic_use_fifo_write_scheduler. PiperOrigin-RevId: 261676376 Change-Id: I74a4d92ad6d612c565eaa1e38186738988d02fe5
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h index 81f9730..0facf00 100644 --- a/quic/core/crypto/crypto_protocol.h +++ b/quic/core/crypto/crypto_protocol.h
@@ -199,6 +199,8 @@ // Enable Priority scheme experiment. const QuicTag kH2PR = TAG('H', '2', 'P', 'R'); // HTTP2 priorities. +const QuicTag kFIFO = TAG('F', 'I', 'F', 'O'); // Stream with the smallest ID + // has the highest priority. // Proof types (i.e. certificate types) // NOTE: although it would be silly to do so, specifying both kX509 and kX59R
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc index c61c347..2e79f78 100644 --- a/quic/core/http/end_to_end_test.cc +++ b/quic/core/http/end_to_end_test.cc
@@ -85,11 +85,13 @@ TestParams(const ParsedQuicVersionVector& client_supported_versions, const ParsedQuicVersionVector& server_supported_versions, ParsedQuicVersion negotiated_version, - QuicTag congestion_control_tag) + QuicTag congestion_control_tag, + QuicTag priority_tag) : client_supported_versions(client_supported_versions), server_supported_versions(server_supported_versions), negotiated_version(negotiated_version), - congestion_control_tag(congestion_control_tag) {} + congestion_control_tag(congestion_control_tag), + priority_tag(priority_tag) {} friend std::ostream& operator<<(std::ostream& os, const TestParams& p) { os << "{ server_supported_versions: " @@ -99,7 +101,8 @@ os << " negotiated_version: " << ParsedQuicVersionToString(p.negotiated_version); os << " congestion_control_tag: " - << QuicTagToString(p.congestion_control_tag) << " }"; + << QuicTagToString(p.congestion_control_tag); + os << " priority_tag: " << QuicTagToString(p.priority_tag) << " }"; return os; } @@ -107,6 +110,7 @@ ParsedQuicVersionVector server_supported_versions; ParsedQuicVersion negotiated_version; QuicTag congestion_control_tag; + QuicTag priority_tag; }; // Constructs various test permutations. @@ -153,28 +157,32 @@ if (FilterSupportedVersions(client_versions).empty()) { continue; } - // Add an entry for server and client supporting all versions. - params.push_back(TestParams(client_versions, all_supported_versions, - client_versions.front(), - congestion_control_tag)); + for (const QuicTag priority_tag : + {/*no tag*/ static_cast<QuicTag>(0), kH2PR, kFIFO}) { + // Add an entry for server and client supporting all versions. + params.push_back(TestParams(client_versions, all_supported_versions, + client_versions.front(), + congestion_control_tag, priority_tag)); - // Test client supporting all versions and server supporting - // 1 version. Simulate an old server and exercise version - // downgrade in the client. Protocol negotiation should - // occur. Skip the i = 0 case because it is essentially the - // same as the default case. - for (size_t i = 1; i < client_versions.size(); ++i) { - ParsedQuicVersionVector server_supported_versions; - server_supported_versions.push_back(client_versions[i]); - if (FilterSupportedVersions(server_supported_versions).empty()) { - continue; - } - params.push_back(TestParams(client_versions, server_supported_versions, - server_supported_versions.front(), - congestion_control_tag)); - } // End of inner version loop. - } // End of outer version loop. - } // End of congestion_control_tag loop. + // Test client supporting all versions and server supporting + // 1 version. Simulate an old server and exercise version + // downgrade in the client. Protocol negotiation should + // occur. Skip the i = 0 case because it is essentially the + // same as the default case. + for (size_t i = 1; i < client_versions.size(); ++i) { + ParsedQuicVersionVector server_supported_versions; + server_supported_versions.push_back(client_versions[i]); + if (FilterSupportedVersions(server_supported_versions).empty()) { + continue; + } + params.push_back(TestParams(client_versions, + server_supported_versions, + server_supported_versions.front(), + congestion_control_tag, priority_tag)); + } // End of inner version loop. + } // End of priority_tag loop. + } // End of outer version loop. + } // End of congestion_control_tag loop. return params; } @@ -343,9 +351,7 @@ GetQuicReloadableFlag(quic_enable_pcc3)) { copt.push_back(kTPCC); } - // TODO(fayang): Move this to GetTestParams when other priority connection - // opts are supported. - copt.push_back(kH2PR); + copt.push_back(GetParam().priority_tag); client_config_.SetConnectionOptionsToSend(copt);
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc index ba53d5e..e39fd41 100644 --- a/quic/core/quic_session.cc +++ b/quic/core/quic_session.cc
@@ -998,12 +998,22 @@ if (ContainsQuicTag(config_.ReceivedConnectionOptions(), kIFWA)) { AdjustInitialFlowControlWindows(1024 * 1024); } - // Enable HTTP2 (tree-style) priority write scheduler. if (GetQuicReloadableFlag(quic_use_http2_priority_write_scheduler) && ContainsQuicTag(config_.ReceivedConnectionOptions(), kH2PR) && !VersionHasIetfQuicFrames(connection_->transport_version())) { + // Enable HTTP2 (tree-style) priority write scheduler. use_http2_priority_write_scheduler_ = - write_blocked_streams_.UseHttp2PriorityScheduler(); + write_blocked_streams_.SwitchWriteScheduler( + spdy::WriteSchedulerType::HTTP2, + connection_->transport_version()); + } else if (GetQuicReloadableFlag(quic_enable_fifo_write_scheduler) && + ContainsQuicTag(config_.ReceivedConnectionOptions(), kFIFO)) { + // Enable FIFO write scheduler. + if (write_blocked_streams_.SwitchWriteScheduler( + spdy::WriteSchedulerType::FIFO, + connection_->transport_version())) { + QUIC_RELOADABLE_FLAG_COUNT(quic_enable_fifo_write_scheduler); + } } }
diff --git a/quic/core/quic_write_blocked_list.cc b/quic/core/quic_write_blocked_list.cc index c9b9c22..8e4bcf1 100644 --- a/quic/core/quic_write_blocked_list.cc +++ b/quic/core/quic_write_blocked_list.cc
@@ -6,7 +6,6 @@ #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" -#include "net/third_party/quiche/src/spdy/core/priority_write_scheduler.h" namespace quic {
diff --git a/quic/core/quic_write_blocked_list.h b/quic/core/quic_write_blocked_list.h index 17bc695..dd9ddb4 100644 --- a/quic/core/quic_write_blocked_list.h +++ b/quic/core/quic_write_blocked_list.h
@@ -14,7 +14,9 @@ #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" #include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/spdy/core/fifo_write_scheduler.h" #include "net/third_party/quiche/src/spdy/core/http2_priority_write_scheduler.h" +#include "net/third_party/quiche/src/spdy/core/priority_write_scheduler.h" namespace quic { @@ -63,20 +65,41 @@ return priority_write_scheduler_->ShouldYield(id); } - // Uses HTTP2 (tree-style) priority scheduler. This can only be called before - // any stream is registered. - bool UseHttp2PriorityScheduler() { - if (scheduler_type_ == spdy::WriteSchedulerType::HTTP2) { + // Switches write scheduler. This can only be called before any stream is + // registered. + bool SwitchWriteScheduler(spdy::WriteSchedulerType type, + QuicTransportVersion version) { + if (scheduler_type_ == type) { return true; } if (priority_write_scheduler_->NumRegisteredStreams() != 0) { QUIC_BUG << "Cannot switch scheduler with registered streams"; return false; } - QUIC_DVLOG(1) << "Using HTTP2 Priority Scheduler"; - priority_write_scheduler_ = - QuicMakeUnique<spdy::Http2PriorityWriteScheduler<QuicStreamId>>(); - scheduler_type_ = spdy::WriteSchedulerType::HTTP2; + QUIC_DVLOG(1) << "Switching to scheduler type: " + << spdy::WriteSchedulerTypeToString(type); + switch (type) { + case spdy::WriteSchedulerType::SPDY: + priority_write_scheduler_ = + QuicMakeUnique<spdy::PriorityWriteScheduler<QuicStreamId>>( + QuicVersionUsesCryptoFrames(version) + ? std::numeric_limits<QuicStreamId>::max() + : 0); + break; + case spdy::WriteSchedulerType::HTTP2: + priority_write_scheduler_ = + QuicMakeUnique<spdy::Http2PriorityWriteScheduler<QuicStreamId>>(); + break; + case spdy::WriteSchedulerType::FIFO: + priority_write_scheduler_ = + QuicMakeUnique<spdy::FifoWriteScheduler<QuicStreamId>>(); + break; + default: + QUIC_BUG << "Scheduler is not supported for type: " + << spdy::WriteSchedulerTypeToString(type); + return false; + } + scheduler_type_ = type; return true; } @@ -92,8 +115,8 @@ const auto id_and_precedence = priority_write_scheduler_->PopNextReadyStreamAndPrecedence(); const QuicStreamId id = std::get<0>(id_and_precedence); - if (scheduler_type_ == spdy::WriteSchedulerType::HTTP2) { - // No batch writing logic when using HTTP2 priorities. + if (scheduler_type_ != spdy::WriteSchedulerType::SPDY) { + // No batch writing logic for non-SPDY priority write scheduler. return id; } const spdy::SpdyPriority priority = @@ -144,7 +167,7 @@ } void UpdateBytesForStream(QuicStreamId stream_id, size_t bytes) { - if (scheduler_type_ == spdy::WriteSchedulerType::HTTP2) { + if (scheduler_type_ != spdy::WriteSchedulerType::SPDY) { return; } if (batch_write_stream_id_[last_priority_popped_] == stream_id) { @@ -185,10 +208,18 @@ private: bool PrecedenceMatchesSchedulerType( const spdy::SpdyStreamPrecedence& precedence) { - if (precedence.is_spdy3_priority()) { - return scheduler_type_ == spdy::WriteSchedulerType::SPDY; + switch (scheduler_type_) { + case spdy::WriteSchedulerType::SPDY: + return precedence.is_spdy3_priority(); + case spdy::WriteSchedulerType::HTTP2: + return !precedence.is_spdy3_priority(); + case spdy::WriteSchedulerType::FIFO: + break; + default: + DCHECK(false); + return false; } - return scheduler_type_ == spdy::WriteSchedulerType::HTTP2; + return true; } std::unique_ptr<QuicPriorityWriteScheduler> priority_write_scheduler_;
diff --git a/quic/core/quic_write_blocked_list_test.cc b/quic/core/quic_write_blocked_list_test.cc index ca3433b..5d4b639 100644 --- a/quic/core/quic_write_blocked_list_test.cc +++ b/quic/core/quic_write_blocked_list_test.cc
@@ -22,7 +22,9 @@ QuicWriteBlockedListTest() : write_blocked_list_(AllSupportedVersions()[0].transport_version) { if (GetParam()) { - write_blocked_list_.UseHttp2PriorityScheduler(); + write_blocked_list_.SwitchWriteScheduler( + spdy::WriteSchedulerType::HTTP2, + AllSupportedVersions()[0].transport_version); } }
diff --git a/spdy/core/spdy_protocol.cc b/spdy/core/spdy_protocol.cc index d9b642f..9094b47 100644 --- a/spdy/core/spdy_protocol.cc +++ b/spdy/core/spdy_protocol.cc
@@ -229,6 +229,20 @@ return "UNKNOWN_ERROR_CODE"; } +const char* WriteSchedulerTypeToString(WriteSchedulerType type) { + switch (type) { + case WriteSchedulerType::LIFO: + return "LIFO"; + case WriteSchedulerType::SPDY: + return "SPDY"; + case WriteSchedulerType::HTTP2: + return "HTTP2"; + case WriteSchedulerType::FIFO: + return "FIFO"; + } + return "UNKNOWN"; +} + size_t GetNumberRequiredContinuationFrames(size_t size) { DCHECK_GT(size, kHttp2MaxControlFrameSendSize); size_t overflow = size - kHttp2MaxControlFrameSendSize;
diff --git a/spdy/core/spdy_protocol.h b/spdy/core/spdy_protocol.h index 2d2ce96..2a521ab 100644 --- a/spdy/core/spdy_protocol.h +++ b/spdy/core/spdy_protocol.h
@@ -203,6 +203,7 @@ // https://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1#TOC-2.3.3-Stream-priority. HTTP2, // Uses HTTP2 (tree-style) priority described in // https://tools.ietf.org/html/rfc7540#section-5.3. + FIFO, // Stream with the smallest stream ID has the highest priority. }; // A SPDY priority is a number between 0 and 7 (inclusive). @@ -284,6 +285,9 @@ // for logging/debugging. const char* ErrorCodeToString(SpdyErrorCode error_code); +// Serialize |type| to string for logging/debugging. +const char* WriteSchedulerTypeToString(WriteSchedulerType type); + // Minimum size of a frame, in octets. const size_t kFrameMinimumSize = kFrameHeaderSize;