diff --git a/.azure/templates/build-config-winkernel.yml b/.azure/templates/build-config-winkernel.yml index d56c04fbc..42dd978c5 100644 --- a/.azure/templates/build-config-winkernel.yml +++ b/.azure/templates/build-config-winkernel.yml @@ -13,14 +13,12 @@ jobs: submodules: recursive - task: VSBuild@1 - continueOnError: true inputs: solution: msquic.kernel.sln platform: ${{ parameters.arch }} configuration: debug - task: VSBuild@1 - continueOnError: true inputs: solution: msquic.kernel.sln platform: ${{ parameters.arch }} diff --git a/src/core/connection.c b/src/core/connection.c index f56299512..f600669e8 100644 --- a/src/core/connection.c +++ b/src/core/connection.c @@ -4868,6 +4868,34 @@ QuicConnParamSet( Status = QUIC_STATUS_SUCCESS; break; + case QUIC_PARAM_CONN_STREAM_SCHEDULING_SCHEME: { + + if (BufferLength != sizeof(QUIC_STREAM_SCHEDULING_SCHEME)) { + Status = QUIC_STATUS_INVALID_PARAMETER; + break; + } + + QUIC_STREAM_SCHEDULING_SCHEME Scheme = + *(QUIC_STREAM_SCHEDULING_SCHEME*)Buffer; + + if (Scheme >= QUIC_STREAM_SCHEDULING_SCHEME_COUNT) { + Status = QUIC_STATUS_INVALID_PARAMETER; + break; + } + + Connection->State.UseRoundRobinStreamScheduling = + Scheme == QUIC_STREAM_SCHEDULING_SCHEME_ROUND_ROBIN; + + QuicTraceLogConnInfo( + UpdateStreamSchedulingScheme, + Connection, + "Updated Stream Scheduling Scheme = %u", + Scheme); + + Status = QUIC_STATUS_SUCCESS; + break; + } + case QUIC_PARAM_CONN_FORCE_KEY_UPDATE: if (!Connection->State.Connected || @@ -5395,6 +5423,27 @@ QuicConnParamGet( Status = QUIC_STATUS_SUCCESS; break; + case QUIC_PARAM_CONN_STREAM_SCHEDULING_SCHEME: + + if (*BufferLength < sizeof(QUIC_STREAM_SCHEDULING_SCHEME)) { + *BufferLength = sizeof(QUIC_STREAM_SCHEDULING_SCHEME); + Status = QUIC_STATUS_BUFFER_TOO_SMALL; + break; + } + + if (Buffer == NULL) { + Status = QUIC_STATUS_INVALID_PARAMETER; + break; + } + + *BufferLength = sizeof(QUIC_STREAM_SCHEDULING_SCHEME); + *(QUIC_STREAM_SCHEDULING_SCHEME*)Buffer = + Connection->State.UseRoundRobinStreamScheduling ? + QUIC_STREAM_SCHEDULING_SCHEME_ROUND_ROBIN : QUIC_STREAM_SCHEDULING_SCHEME_FIFO; + + Status = QUIC_STATUS_SUCCESS; + break; + default: Status = QUIC_STATUS_INVALID_PARAMETER; break; diff --git a/src/core/connection.h b/src/core/connection.h index 53175bb6d..5819294ba 100644 --- a/src/core/connection.h +++ b/src/core/connection.h @@ -131,10 +131,16 @@ typedef union QUIC_CONNECTION_STATE { BOOLEAN ShareBinding : 1; // - // Indicate the TestTransportParameter variable has been set by the app. + // Indicates the TestTransportParameter variable has been set by the app. // BOOLEAN TestTransportParameterSet : 1; + // + // Indicates the connection is using the round robin stream scheduling + // scheme. + // + BOOLEAN UseRoundRobinStreamScheduling : 1; + #ifdef QuicVerifierEnabledByAddr // // The calling app is being verified (app or driver verifier). diff --git a/src/core/quicdef.h b/src/core/quicdef.h index 51d7544a9..172b448a1 100644 --- a/src/core/quicdef.h +++ b/src/core/quicdef.h @@ -159,7 +159,7 @@ typedef struct QUIC_PATH QUIC_PATH; // The number of packets we write for a single stream before going to the next // one in the round robin. // -#define QUIC_STREAM_SEND_BATCH_COUNT 4 +#define QUIC_STREAM_SEND_BATCH_COUNT 8 // // The maximum number of received packets to batch process at a time. diff --git a/src/core/send.c b/src/core/send.c index 828a35364..a22e8d44e 100644 --- a/src/core/send.c +++ b/src/core/send.c @@ -803,7 +803,8 @@ QuicSendGetNextStream( _Out_ uint32_t* PacketCount ) { - QUIC_DBG_ASSERT(!QuicConnIsClosed(QuicSendGetConnection(Send)) || QuicListIsEmpty(&Send->SendStreams)); + QUIC_CONNECTION* Connection = QuicSendGetConnection(Send); + QUIC_DBG_ASSERT(!QuicConnIsClosed(Connection) || QuicListIsEmpty(&Send->SendStreams)); QUIC_LIST_ENTRY* Entry = Send->SendStreams.Flink; while (Entry != &Send->SendStreams) { @@ -821,13 +822,19 @@ QuicSendGetNextStream( // if (QuicSendCanSendStreamNow(Stream)) { - // - // Move the stream to the end of the queue. - // - QuicListEntryRemove(&Stream->SendLink); - QuicListInsertTail(&Send->SendStreams, &Stream->SendLink); + if (Connection->State.UseRoundRobinStreamScheduling) { + // + // Move the stream to the end of the queue. + // + QuicListEntryRemove(&Stream->SendLink); + QuicListInsertTail(&Send->SendStreams, &Stream->SendLink); + + *PacketCount = QUIC_STREAM_SEND_BATCH_COUNT; + + } else { // FIFO prioritization scheme + *PacketCount = UINT32_MAX; + } - *PacketCount = QUIC_STREAM_SEND_BATCH_COUNT; return Stream; } diff --git a/src/inc/msquic.h b/src/inc/msquic.h index f1748d907..94c8c6933 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -106,6 +106,12 @@ typedef enum QUIC_CONNECTION_SHUTDOWN_FLAGS { DEFINE_ENUM_FLAG_OPERATORS(QUIC_CONNECTION_SHUTDOWN_FLAGS); +typedef enum QUIC_STREAM_SCHEDULING_SCHEME { + QUIC_STREAM_SCHEDULING_SCHEME_FIFO = 0x0000, // Sends stream data first come, first served. (Default) + QUIC_STREAM_SCHEDULING_SCHEME_ROUND_ROBIN = 0x0001, // Sends stream data evenly multiplexed. + QUIC_STREAM_SCHEDULING_SCHEME_COUNT // The number of stream scheduling schemes. +} QUIC_STREAM_SCHEDULING_SCHEME; + typedef enum QUIC_STREAM_OPEN_FLAGS { QUIC_STREAM_OPEN_FLAG_NONE = 0x0000, QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL = 0x0001, // Indicates the stream is unidirectional. @@ -358,6 +364,7 @@ typedef enum QUIC_PARAM_LEVEL { #define QUIC_PARAM_CONN_SHARE_UDP_BINDING 17 // uint8_t (BOOLEAN) #define QUIC_PARAM_CONN_IDEAL_PROCESSOR 18 // uint8_t #define QUIC_PARAM_CONN_MAX_STREAM_IDS 19 // uint64_t[4] +#define QUIC_PARAM_CONN_STREAM_SCHEDULING_SCHEME 20 // QUIC_STREAM_SCHEDULING_SCHEME #ifdef WIN32 // Windows certificate validation ignore flags. #define QUIC_CERTIFICATE_FLAG_IGNORE_REVOCATION 0x00000080 diff --git a/src/test/MsQuicTests.h b/src/test/MsQuicTests.h index d28fa3c30..9dc8a3da9 100644 --- a/src/test/MsQuicTests.h +++ b/src/test/MsQuicTests.h @@ -122,7 +122,8 @@ QuicTestConnectAndPing( _In_ bool ServerRejectZeroRtt, _In_ bool UseSendBuffer, _In_ bool UnidirectionalStreams, - _In_ bool ServerInitiatedStreams + _In_ bool ServerInitiatedStreams, + _In_ bool FifoScheduling ); // @@ -379,6 +380,7 @@ typedef struct { uint8_t UseSendBuffer; uint8_t UnidirectionalStreams; uint8_t ServerInitiatedStreams; + uint8_t FifoScheduling; } QUIC_RUN_CONNECT_AND_PING_PARAMS; #pragma pack(pop) diff --git a/src/test/bin/quic_gtest.cpp b/src/test/bin/quic_gtest.cpp index 709bcc334..035bfba8b 100644 --- a/src/test/bin/quic_gtest.cpp +++ b/src/test/bin/quic_gtest.cpp @@ -529,11 +529,12 @@ TEST_P(WithSendArgs1, Send) { 0, // StreamBurstDelayMs 0, // ServerStatelessRetry 0, // ClientRebind - 0, // ClientZeroRtt, + 0, // ClientZeroRtt 0, // ServerRejectZeroRtt (uint8_t)GetParam().UseSendBuffer, (uint8_t)GetParam().UnidirectionalStreams, - (uint8_t)GetParam().ServerInitiatedStreams + (uint8_t)GetParam().ServerInitiatedStreams, + 0 // FifoScheduling }; ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_CONNECT_AND_PING, Params)); } else { @@ -550,7 +551,8 @@ TEST_P(WithSendArgs1, Send) { false, // ServerRejectZeroRtt GetParam().UseSendBuffer, GetParam().UnidirectionalStreams, - GetParam().ServerInitiatedStreams); + GetParam().ServerInitiatedStreams, + false); // FifoScheduling } } @@ -570,7 +572,8 @@ TEST_P(WithSendArgs2, SendLarge) { 0, // ServerRejectZeroRtt (uint8_t)GetParam().UseSendBuffer, 0, // UnidirectionalStreams - 0 // ServerInitiatedStreams + 0, // ServerInitiatedStreams + 1 // FifoScheduling }; ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_CONNECT_AND_PING, Params)); } else { @@ -587,7 +590,8 @@ TEST_P(WithSendArgs2, SendLarge) { false, // ServerRejectZeroRtt GetParam().UseSendBuffer, false, // UnidirectionalStreams - false); // ServerInitiatedStreams + false, // ServerInitiatedStreams + true); // FifoScheduling } } @@ -607,7 +611,8 @@ TEST_P(WithSendArgs3, SendIntermittently) { 0, // ServerRejectZeroRtt (uint8_t)GetParam().UseSendBuffer, 0, // UnidirectionalStreams - 0 // ServerInitiatedStreams + 0, // ServerInitiatedStreams + 0 // FifoScheduling }; ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_CONNECT_AND_PING, Params)); } else { @@ -624,7 +629,8 @@ TEST_P(WithSendArgs3, SendIntermittently) { false, // ServerRejectZeroRtt GetParam().UseSendBuffer, false, // UnidirectionalStreams - false); // ServerInitiatedStreams + false, // ServerInitiatedStreams + false); // FifoScheduling } } @@ -646,7 +652,8 @@ TEST_P(WithSend0RttArgs1, Send0Rtt) { 0, // ServerRejectZeroRtt (uint8_t)GetParam().UseSendBuffer, (uint8_t)GetParam().UnidirectionalStreams, - 0 // ServerInitiatedStreams + 0, // ServerInitiatedStreams + 0 // FifoScheduling }; ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_CONNECT_AND_PING, Params)); } else { @@ -663,7 +670,8 @@ TEST_P(WithSend0RttArgs1, Send0Rtt) { false, // ServerRejectZeroRtt GetParam().UseSendBuffer, GetParam().UnidirectionalStreams, - false); // ServerInitiatedStreams + false, // ServerInitiatedStreams + false); // FifoScheduling } } @@ -683,7 +691,8 @@ TEST_P(WithSend0RttArgs2, Reject0Rtt) { 1, // ServerRejectZeroRtt 0, // UseSendBuffer 0, // UnidirectionalStreams - 0 // ServerInitiatedStreams + 0, // ServerInitiatedStreams + 0 // FifoScheduling }; ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_CONNECT_AND_PING, Params)); } else { @@ -700,7 +709,8 @@ TEST_P(WithSend0RttArgs2, Reject0Rtt) { true, // ServerRejectZeroRtt false, // UseSendBuffer false, // UnidirectionalStreams - false); // ServerInitiatedStreams + false, // ServerInitiatedStreams + false); // FifoScheduling } } diff --git a/src/test/bin/winkernel/control.cpp b/src/test/bin/winkernel/control.cpp index be22e7f4a..983fe4379 100644 --- a/src/test/bin/winkernel/control.cpp +++ b/src/test/bin/winkernel/control.cpp @@ -703,7 +703,8 @@ QuicTestCtlEvtIoDeviceControl( Params->Params2.ServerRejectZeroRtt != 0, Params->Params2.UseSendBuffer != 0, Params->Params2.UnidirectionalStreams != 0, - Params->Params2.ServerInitiatedStreams != 0 + Params->Params2.ServerInitiatedStreams != 0, + Params->Params2.FifoScheduling != 0 )); break; diff --git a/src/test/lib/QuicTest.cpp b/src/test/lib/QuicTest.cpp index 8aa47c2bd..a23a3897e 100644 --- a/src/test/lib/QuicTest.cpp +++ b/src/test/lib/QuicTest.cpp @@ -607,6 +607,7 @@ struct PingStats const uint64_t PayloadLength; const uint32_t ConnectionCount; const uint32_t StreamCount; + const bool FifoScheduling; const bool UnidirectionalStreams; const bool ServerInitiatedStreams; const bool ZeroRtt; @@ -622,6 +623,7 @@ struct PingStats uint64_t _PayloadLength, uint32_t _ConnectionCount, uint32_t _StreamCount, + bool _FifoScheduling, bool _UnidirectionalStreams, bool _ServerInitiatedStreams, bool _ZeroRtt, @@ -632,6 +634,7 @@ struct PingStats PayloadLength(_PayloadLength), ConnectionCount(_ConnectionCount), StreamCount(_StreamCount), + FifoScheduling(_FifoScheduling), UnidirectionalStreams(_UnidirectionalStreams), ServerInitiatedStreams(_ServerInitiatedStreams), ZeroRtt(_ZeroRtt), @@ -701,6 +704,7 @@ PingStreamShutdown( #if !QUIC_SEND_FAKE_LOSS if (!ConnState->GetPingStats()->ServerInitiatedStreams && + !ConnState->GetPingStats()->FifoScheduling && ConnState->GetPingStats()->ZeroRtt) { if (Stream->GetBytesReceived() != 0 && // TODO - Support 0-RTT indication for Stream Open callback. !Stream->GetUsedZeroRtt()) { @@ -811,6 +815,11 @@ ListenerAcceptPingConnection( } } + Connection->SetPriorityScheme( + Stats->FifoScheduling ? + QUIC_STREAM_SCHEDULING_SCHEME_FIFO : + QUIC_STREAM_SCHEDULING_SCHEME_ROUND_ROBIN); + if (Stats->ServerInitiatedStreams) { SendPingBurst( Connection, @@ -849,6 +858,11 @@ NewPingConnection( Connection->SetShutdownCompleteCallback(PingConnectionShutdown); Connection->SetExpectedResumed(ClientStats->ZeroRtt); + Connection->SetPriorityScheme( + ClientStats->FifoScheduling ? + QUIC_STREAM_SCHEDULING_SCHEME_FIFO : + QUIC_STREAM_SCHEDULING_SCHEME_ROUND_ROBIN); + if (ClientStats->ServerInitiatedStreams) { Connection->SetPeerUnidiStreamCount((uint16_t)ClientStats->StreamCount); Connection->SetPeerBidiStreamCount((uint16_t)ClientStats->StreamCount); @@ -875,14 +889,15 @@ QuicTestConnectAndPing( _In_ bool ServerRejectZeroRtt, _In_ bool UseSendBuffer, _In_ bool UnidirectionalStreams, - _In_ bool ServerInitiatedStreams + _In_ bool ServerInitiatedStreams, + _In_ bool FifoScheduling ) { const uint32_t TimeoutMs = EstimateTimeoutMs(Length) * StreamBurstCount; const uint16_t TotalStreamCount = (uint16_t)(StreamCount * StreamBurstCount); - PingStats ServerStats(Length, ConnectionCount, TotalStreamCount, UnidirectionalStreams, ServerInitiatedStreams, ClientZeroRtt && !ServerRejectZeroRtt, false, QUIC_STATUS_SUCCESS); - PingStats ClientStats(Length, ConnectionCount, TotalStreamCount, UnidirectionalStreams, ServerInitiatedStreams, ClientZeroRtt && !ServerRejectZeroRtt); + PingStats ServerStats(Length, ConnectionCount, TotalStreamCount, FifoScheduling, UnidirectionalStreams, ServerInitiatedStreams, ClientZeroRtt && !ServerRejectZeroRtt, false, QUIC_STATUS_SUCCESS); + PingStats ClientStats(Length, ConnectionCount, TotalStreamCount, FifoScheduling, UnidirectionalStreams, ServerInitiatedStreams, ClientZeroRtt && !ServerRejectZeroRtt); MsQuicSession Session; TEST_TRUE(Session.IsValid()); @@ -1098,8 +1113,8 @@ QuicTestServerDisconnect( void ) { - PingStats ServerStats(UINT64_MAX - 1, 1, 1, TRUE, TRUE, FALSE, TRUE, QUIC_STATUS_CONNECTION_TIMEOUT); - PingStats ClientStats(UINT64_MAX - 1, 1, 1, TRUE, TRUE, FALSE, TRUE); + PingStats ServerStats(UINT64_MAX - 1, 1, 1, TRUE, TRUE, TRUE, FALSE, TRUE, QUIC_STATUS_CONNECTION_TIMEOUT); + PingStats ClientStats(UINT64_MAX - 1, 1, 1, TRUE, TRUE, TRUE, FALSE, TRUE); { MsQuicSession Session; @@ -1206,7 +1221,7 @@ QuicTestClientDisconnect( // back to the client as it continues to receive the client's UDP packets. // - PingStats ClientStats(UINT64_MAX - 1, 1, 1, TRUE, FALSE, FALSE, TRUE, + PingStats ClientStats(UINT64_MAX - 1, 1, 1, TRUE, TRUE, FALSE, FALSE, TRUE, StopListenerFirst ? QUIC_STATUS_CONNECTION_TIMEOUT : QUIC_STATUS_ABORTED); { diff --git a/src/test/lib/TestConnection.cpp b/src/test/lib/TestConnection.cpp index d0b15b965..b31709ee5 100644 --- a/src/test/lib/TestConnection.cpp +++ b/src/test/lib/TestConnection.cpp @@ -643,6 +643,39 @@ TestConnection::SetShareUdpBinding( &bValue); } +QUIC_STREAM_SCHEDULING_SCHEME +TestConnection::GetPriorityScheme() +{ + QUIC_STREAM_SCHEDULING_SCHEME value; + uint32_t valueSize = sizeof(value); + QUIC_STATUS Status = + MsQuic->GetParam( + QuicConnection, + QUIC_PARAM_LEVEL_CONNECTION, + QUIC_PARAM_CONN_STREAM_SCHEDULING_SCHEME, + &valueSize, + &value); + if (QUIC_FAILED(Status)) { + value = QUIC_STREAM_SCHEDULING_SCHEME_FIFO; + TEST_FAILURE("MsQuic->GetParam(CONN_PRIORITY_SCHEME) failed, 0x%x.", Status); + } + return value; +} + +QUIC_STATUS +TestConnection::SetPriorityScheme( + QUIC_STREAM_SCHEDULING_SCHEME value + ) +{ + return + MsQuic->SetParam( + QuicConnection, + QUIC_PARAM_LEVEL_CONNECTION, + QUIC_PARAM_CONN_STREAM_SCHEDULING_SCHEME, + sizeof(value), + &value); +} + QUIC_STATUS TestConnection::SetSecurityConfig( QUIC_SEC_CONFIG* value diff --git a/src/test/lib/TestConnection.h b/src/test/lib/TestConnection.h index f85a2f4a2..98f867af1 100644 --- a/src/test/lib/TestConnection.h +++ b/src/test/lib/TestConnection.h @@ -210,6 +210,9 @@ public: bool GetShareUdpBinding(); QUIC_STATUS SetShareUdpBinding(bool value); + QUIC_STREAM_SCHEDULING_SCHEME GetPriorityScheme(); + QUIC_STATUS SetPriorityScheme(QUIC_STREAM_SCHEDULING_SCHEME value); + QUIC_STATUS SetSecurityConfig(QUIC_SEC_CONFIG* value); bool HasNewZeroRttTicket();