diff --git a/docs/api/DatagramSend.md b/docs/api/DatagramSend.md index ba37151ff1..d0f380f18d 100644 --- a/docs/api/DatagramSend.md +++ b/docs/api/DatagramSend.md @@ -21,7 +21,36 @@ QUIC_STATUS # Parameters -**TODO** +`Connection` + +The current established connection. + +`Buffers` + +An array of `QUIC_BUFFER` structs that each contain a pointer and length to app data to send on the stream. This may be `NULL` **only** if `BufferCount` is zero. + +`BufferCount` + +The number of `QUIC_BUFFER` structs in the `Buffers` array. This may be zero. + +`Flags` + +The set of flags that controls the behavior of `DatagramSend`: + +Value | Meaning +--- | --- +**QUIC_SEND_FLAG_NONE**
0 | No special behavior. Data is not allowed in 0-RTT by default. +**QUIC_SEND_FLAG_ALLOW_0_RTT**
1 | Indicates that the data is allowed to be sent in 0-RTT (if available). Makes no guarantee the data will be sent in 0-RTT. Additionally, even if 0-RTT keys are available the data may end up being sent in 1-RTT for multiple reasons. +**QUIC_SEND_FLAG_START**
2 | **Unused and ignored** for `DatagramSend` +**QUIC_SEND_FLAG_FIN**
4 | **Unused and ignored** for `DatagramSend` +**QUIC_SEND_FLAG_DGRAM_PRIORITY**
8 | Sets a priority to ensure a datagram is sent before others. +**QUIC_SEND_FLAG_DELAY_SEND**
16 | **Unused and ignored** for `DatagramSend` +**QUIC_SEND_FLAG_CANCEL_ON_LOSS**
32 | **Unused and ignored** for `DatagramSend` +**QUIC_SEND_FLAG_CANCEL_ON_BLOCKED**
64 | Allows MsQuic to drop frames when all the data that could be sent has been flushed out, but there are still some frames remaining in the queue. + +`ClientSendContext` + +The app context pointer (possibly null) to be associated with the send. # Return Value diff --git a/docs/api/StreamSend.md b/docs/api/StreamSend.md index c8a5b03fc4..bf662c8d3f 100644 --- a/docs/api/StreamSend.md +++ b/docs/api/StreamSend.md @@ -46,6 +46,7 @@ Value | Meaning **QUIC_SEND_FLAG_DGRAM_PRIORITY**
8 | **Unused and ignored** for `StreamSend` **QUIC_SEND_FLAG_DELAY_SEND**
16 | Provides a hint to MsQuic to indicate the data does not need to be sent immediately, likely because more is soon to follow. **QUIC_SEND_FLAG_CANCEL_ON_LOSS**
32 | Informs MsQuic to irreversibly mark the associated stream to be canceled when packet loss has been detected on it. I.e., all sends on a given stream are subject to this behavior from the moment the flag has been supplied for the first time. +**QUIC_SEND_FLAG_CANCEL_ON_BLOCKED**
64 | **Unused and ignored** for `StreamSend` for now `ClientSendContext` diff --git a/src/core/datagram.c b/src/core/datagram.c index 0d794d85a5..4786fa1280 100644 --- a/src/core/datagram.c +++ b/src/core/datagram.c @@ -588,3 +588,40 @@ QuicDatagramProcessFrame( return TRUE; } + +_IRQL_requires_max_(PASSIVE_LEVEL) +void +QuicDatagramCancelBlocked( + _In_ QUIC_CONNECTION* Connection + ) +{ + QUIC_DATAGRAM* Datagram = &Connection->Datagram; + QUIC_SEND_REQUEST** SendQueue = &Datagram->SendQueue; + + if (*SendQueue == NULL) { + return; + } + + do { + if ((*SendQueue)->Flags & QUIC_SEND_FLAG_CANCEL_ON_BLOCKED) { + QUIC_SEND_REQUEST* SendRequest = *SendQueue; + if (Datagram->PrioritySendQueueTail == &SendRequest->Next) { + Datagram->PrioritySendQueueTail = SendQueue; + } + *SendQueue = SendRequest->Next; + QuicDatagramCancelSend(Connection, SendRequest); + } else { + SendQueue = &((*SendQueue)->Next); + } + } while (*SendQueue != NULL); + + Datagram->SendQueueTail = SendQueue; + + if (Datagram->SendQueue != NULL) { + QuicSendSetSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_DATAGRAM); + } else { + QuicSendClearSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_DATAGRAM); + } + + QuicDatagramValidate(Datagram); +} diff --git a/src/core/datagram.h b/src/core/datagram.h index 86f3772ff2..95caf08ceb 100644 --- a/src/core/datagram.h +++ b/src/core/datagram.h @@ -103,3 +103,9 @@ QuicDatagramProcessFrame( const uint8_t * const Buffer, _Inout_ uint16_t* Offset ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +void +QuicDatagramCancelBlocked( + _In_ QUIC_CONNECTION* Connection + ); diff --git a/src/core/send.c b/src/core/send.c index 306801d4bf..a0f155b8f3 100644 --- a/src/core/send.c +++ b/src/core/send.c @@ -1497,6 +1497,11 @@ QuicSendFlush( //QuicConnUpdatePeerPacketTolerance(Connection, Builder.TotalCountDatagrams); } + // + // Clears the SendQueue list of not sent packets if the flag is applied + // + QuicDatagramCancelBlocked(Connection); + return Result != QUIC_SEND_INCOMPLETE; } #pragma warning(pop) diff --git a/src/cs/lib/msquic_generated.cs b/src/cs/lib/msquic_generated.cs index 375e6897b0..3e6f9d96b7 100644 --- a/src/cs/lib/msquic_generated.cs +++ b/src/cs/lib/msquic_generated.cs @@ -197,6 +197,7 @@ internal enum QUIC_SEND_FLAGS DELAY_SEND = 0x0010, CANCEL_ON_LOSS = 0x0020, PRIORITY_WORK = 0x0040, + CANCEL_ON_BLOCKED = 0x0080, } internal enum QUIC_DATAGRAM_SEND_STATE diff --git a/src/ffi/linux_bindings.rs b/src/ffi/linux_bindings.rs index 9b4f449f4a..0d50d32a73 100644 --- a/src/ffi/linux_bindings.rs +++ b/src/ffi/linux_bindings.rs @@ -390,6 +390,7 @@ pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_DGRAM_PRIORITY: QUIC_SEND_FLAGS = 8; pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_DELAY_SEND: QUIC_SEND_FLAGS = 16; pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_CANCEL_ON_LOSS: QUIC_SEND_FLAGS = 32; pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_PRIORITY_WORK: QUIC_SEND_FLAGS = 64; +pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_CANCEL_ON_BLOCKED: QUIC_SEND_FLAGS = 128; pub type QUIC_SEND_FLAGS = ::std::os::raw::c_uint; pub const QUIC_DATAGRAM_SEND_STATE_QUIC_DATAGRAM_SEND_UNKNOWN: QUIC_DATAGRAM_SEND_STATE = 0; pub const QUIC_DATAGRAM_SEND_STATE_QUIC_DATAGRAM_SEND_SENT: QUIC_DATAGRAM_SEND_STATE = 1; diff --git a/src/ffi/win_bindings.rs b/src/ffi/win_bindings.rs index 0b564fdcaf..d6ac54efcb 100644 --- a/src/ffi/win_bindings.rs +++ b/src/ffi/win_bindings.rs @@ -389,6 +389,7 @@ pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_DGRAM_PRIORITY: QUIC_SEND_FLAGS = 8; pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_DELAY_SEND: QUIC_SEND_FLAGS = 16; pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_CANCEL_ON_LOSS: QUIC_SEND_FLAGS = 32; pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_PRIORITY_WORK: QUIC_SEND_FLAGS = 64; +pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_CANCEL_ON_BLOCKED: QUIC_SEND_FLAGS = 128; pub type QUIC_SEND_FLAGS = ::std::os::raw::c_int; pub const QUIC_DATAGRAM_SEND_STATE_QUIC_DATAGRAM_SEND_UNKNOWN: QUIC_DATAGRAM_SEND_STATE = 0; pub const QUIC_DATAGRAM_SEND_STATE_QUIC_DATAGRAM_SEND_SENT: QUIC_DATAGRAM_SEND_STATE = 1; diff --git a/src/inc/msquic.h b/src/inc/msquic.h index 825bfb8a6a..b8dc5471db 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -244,6 +244,7 @@ typedef enum QUIC_SEND_FLAGS { QUIC_SEND_FLAG_DELAY_SEND = 0x0010, // Indicates the send should be delayed because more will be queued soon. QUIC_SEND_FLAG_CANCEL_ON_LOSS = 0x0020, // Indicates that a stream is to be cancelled when packet loss is detected. QUIC_SEND_FLAG_PRIORITY_WORK = 0x0040, // Higher priority than other connection work. + QUIC_SEND_FLAG_CANCEL_ON_BLOCKED = 0x0080, // Indicates that a frame should be dropped when it can't be sent immediately. } QUIC_SEND_FLAGS; DEFINE_ENUM_FLAG_OPERATORS(QUIC_SEND_FLAGS) diff --git a/src/plugins/trace/README.md b/src/plugins/trace/README.md index c97b4825eb..5966c07df0 100644 --- a/src/plugins/trace/README.md +++ b/src/plugins/trace/README.md @@ -51,7 +51,9 @@ One of the built-in capabilities of WPA is the ability to analyze CPU trace info ### Linux Linux perf command is one of the way to collect such information. ```sh -# on Linux +# on Linux (kernel > 5.10) +sudo apt-get install -y linux-perf +# on Linux (kernel <= 5.10) sudo apt-get install -y linux-tools-`uname -r` # use your own options perf record -a -g -F 10 -o out.perf.data diff --git a/src/test/MsQuicTests.h b/src/test/MsQuicTests.h index aaae75274e..77de8d913f 100644 --- a/src/test/MsQuicTests.h +++ b/src/test/MsQuicTests.h @@ -642,6 +642,11 @@ QuicTestDatagramSend( _In_ int Family ); +void +QuicTestDatagramDrop( + _In_ int Family + ); + // // Storage tests // @@ -1331,4 +1336,8 @@ typedef struct { QUIC_CTL_CODE(125, METHOD_BUFFERED, FILE_WRITE_DATA) // BOOLEAN - EnableResumption -#define QUIC_MAX_IOCTL_FUNC_CODE 125 +#define IOCTL_QUIC_RUN_DATAGRAM_DROP \ + QUIC_CTL_CODE(126, METHOD_BUFFERED, FILE_WRITE_DATA) + // int - Family + +#define QUIC_MAX_IOCTL_FUNC_CODE 126 diff --git a/src/test/bin/quic_gtest.cpp b/src/test/bin/quic_gtest.cpp index 2010fb93bd..5b01ffb796 100644 --- a/src/test/bin/quic_gtest.cpp +++ b/src/test/bin/quic_gtest.cpp @@ -2352,6 +2352,15 @@ TEST_P(WithFamilyArgs, DatagramSend) { } } +TEST_P(WithFamilyArgs, DatagramDrop) { + TestLoggerT Logger("QuicTestDatagramDrop", GetParam()); + if (TestingKernelMode) { + ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_DATAGRAM_DROP, GetParam().Family)); + } else { + QuicTestDatagramDrop(GetParam().Family); + } +} + #ifdef _WIN32 // Storage tests only supported on Windows static BOOLEAN CanRunStorageTests = FALSE; diff --git a/src/test/bin/winkernel/control.cpp b/src/test/bin/winkernel/control.cpp index bdeab35fd3..1558f81396 100644 --- a/src/test/bin/winkernel/control.cpp +++ b/src/test/bin/winkernel/control.cpp @@ -524,6 +524,7 @@ size_t QUIC_IOCTL_BUFFER_SIZES[] = 0, 0, sizeof(BOOLEAN), + sizeof(INT32), }; CXPLAT_STATIC_ASSERT( @@ -955,6 +956,13 @@ QuicTestCtlEvtIoDeviceControl( Params->Family)); break; + case IOCTL_QUIC_RUN_DATAGRAM_DROP: + CXPLAT_FRE_ASSERT(Params != nullptr); + QuicTestCtlRun( + QuicTestDatagramDrop( + Params->Family)); + break; + case IOCTL_QUIC_RUN_NAT_PORT_REBIND: CXPLAT_FRE_ASSERT(Params != nullptr); QuicTestCtlRun( diff --git a/src/test/lib/DatagramTest.cpp b/src/test/lib/DatagramTest.cpp index 5c19fd731e..421e022378 100644 --- a/src/test/lib/DatagramTest.cpp +++ b/src/test/lib/DatagramTest.cpp @@ -267,3 +267,102 @@ QuicTestDatagramSend( } } } + +void +QuicTestDatagramDrop( + _In_ int Family + ) +{ + MsQuicRegistration Registration; + TEST_TRUE(Registration.IsValid()); + + MsQuicAlpn Alpn("MsQuicTest"); + + MsQuicSettings Settings; + Settings.SetDatagramReceiveEnabled(true); + + MsQuicCredentialConfig ClientCredConfig; + MsQuicConfiguration ClientConfiguration(Registration, Alpn, Settings, ClientCredConfig); + TEST_TRUE(ClientConfiguration.IsValid()); + + MsQuicConfiguration ServerConfiguration(Registration, Alpn, Settings, ServerSelfSignedCredConfig); + TEST_TRUE(ServerConfiguration.IsValid()); + + uint8_t RawBuffer[1100] = {0}; + QUIC_BUFFER DatagramBuffer = { sizeof(RawBuffer), RawBuffer }; + + SelectiveLossHelper LossHelper; + + { + TestListener Listener(Registration, ListenerAcceptConnection, ServerConfiguration); + TEST_TRUE(Listener.IsValid()); + + QUIC_ADDRESS_FAMILY QuicAddrFamily = (Family == 4) ? QUIC_ADDRESS_FAMILY_INET : QUIC_ADDRESS_FAMILY_INET6; + QuicAddr ServerLocalAddr(QuicAddrFamily); + TEST_QUIC_SUCCEEDED(Listener.Start(Alpn, &ServerLocalAddr.SockAddr)); + TEST_QUIC_SUCCEEDED(Listener.GetLocalAddr(ServerLocalAddr)); + + { + UniquePtr Server; + ServerAcceptContext ServerAcceptCtx((TestConnection**)&Server); + Listener.Context = &ServerAcceptCtx; + + { + TestConnection Client(Registration); + TEST_TRUE(Client.IsValid()); + + TEST_TRUE(Client.GetDatagramSendEnabled()); + + for (int i = 0; i < 20; i++) { + TEST_QUIC_SUCCEEDED( + MsQuic->DatagramSend( + Client.GetConnection(), + &DatagramBuffer, + 1, + (i%2 == 0) ? QUIC_SEND_FLAG_CANCEL_ON_BLOCKED : QUIC_SEND_FLAG_NONE, + nullptr)); + } + + TEST_QUIC_SUCCEEDED( + Client.Start( + ClientConfiguration, + QuicAddrFamily, + QUIC_TEST_LOOPBACK_FOR_AF(QuicAddrFamily), + ServerLocalAddr.GetPort())); + + if (!Client.WaitForConnectionComplete()) { + return; + } + TEST_TRUE(Client.GetIsConnected()); + + TEST_TRUE(Client.GetDatagramSendEnabled()); + + TEST_NOT_EQUAL(nullptr, Server); + if (!Server->WaitForConnectionComplete()) { + return; + } + TEST_TRUE(Server->GetIsConnected()); + + TEST_TRUE(Server->GetDatagramSendEnabled()); + + CxPlatSleep(100); + + uint32_t Tries = 0; + while (Client.GetDatagramsSent() != 10 && Client.GetDatagramsCanceled() != 10 && ++Tries < 10) { + CxPlatSleep(100); + } + + TEST_EQUAL(10, Client.GetDatagramsCanceled()); + TEST_EQUAL(10, Client.GetDatagramsSent()); + + Client.Shutdown(QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, QUIC_TEST_NO_ERROR); + if (!Client.WaitForShutdownComplete()) { + return; + } + + TEST_FALSE(Client.GetPeerClosed()); + TEST_FALSE(Client.GetTransportClosed()); + } + } + } +}