Skip to content

Commit

Permalink
Support setting flow control limits for individual stream types (#3948)
Browse files Browse the repository at this point in the history
  • Loading branch information
rzikm authored Oct 31, 2023
1 parent cacaa20 commit d33bc56
Show file tree
Hide file tree
Showing 13 changed files with 512 additions and 33 deletions.
5 changes: 4 additions & 1 deletion docs/Settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ The following settings are available via registry as well as via [QUIC_SETTINGS]
| Idle Timeout | uint64_t | IdleTimeoutMs | 30,000 | How long a connection can go idle before it is silently shut down. 0 to disable timeout |
| Max TLS Send Buffer (Client) | uint32_t | TlsClientMaxSendBuffer | 4,096 | How much client TLS data to buffer. |
| Max TLS Send Buffer (Server) | uint32_t | TlsServerMaxSendBuffer | 8,192 | How much server TLS data to buffer. |
| Stream Receive Window | uint32_t | StreamRecvWindowDefault | 32,768 | Initial stream receive window size. |
| Stream Receive Window | uint32_t | StreamRecvWindowDefault | 65,536 | Initial stream receive window size for all stream types. |
| Stream Receive Window (Bidirectional, locally created) | uint32_t | StreamRecvWindowBidiLocalDefault | - | If set, overrides stream receive window size for locally initiated bidirectional streams. |
| Stream Receive Window (Bidirectional, remotely created) | uint32_t | StreamRecvWindowBidiRemoteDefault | - | If set, overrides stream receive window size for remote initiated bidirectional streams. |
| Stream Receive Window (Unidirectional) | uint32_t | StreamRecvWindowUnidiDefault | - | If set, overrides stream receive window size for remote initiated unidirectional streams. |
| Stream Receive Buffer | uint32_t | StreamRecvBufferDefault | 4,096 | Stream initial buffer size. |
| Flow Control Window | uint32_t | ConnFlowControlWindow | 16,777,216 | Connection-wide flow control window. |
| Max Stateless Operations | uint32_t | MaxStatelessOperations | 16 | The maximum number of stateless operations that may be queued on a worker at any one time. |
Expand Down
52 changes: 49 additions & 3 deletions docs/api/QUIC_SETTINGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,18 @@ typedef struct QUIC_SETTINGS {
uint64_t DestCidUpdateIdleTimeoutMs : 1;
uint64_t GreaseQuicBitEnabled : 1;
uint64_t EcnEnabled : 1;
uint64_t RESERVED : 30;
uint64_t HyStartEnabled : 1;
uint64_t StreamRecvWindowBidiLocalDefault : 1;
uint64_t StreamRecvWindowBidiRemoteDefault : 1;
uint64_t StreamRecvWindowUnidiDefault : 1;
#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES
uint64_t EncryptionOffloadAllowed : 1;
uint64_t ReliableResetEnabled : 1;
uint64_t OneWayDelayEnabled : 1;
uint64_t RESERVED : 23;
#else
uint64_t RESERVED : 26;
#endif
} IsSet;
};

Expand Down Expand Up @@ -83,6 +94,23 @@ typedef struct QUIC_SETTINGS {
uint8_t MaxOperationsPerDrain;
uint8_t MtuDiscoveryMissingProbeCount;
uint32_t DestCidUpdateIdleTimeoutMs;
union {
uint64_t Flags;
struct {
uint64_t HyStartEnabled : 1;
#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES
uint64_t EncryptionOffloadAllowed : 1;
uint64_t ReliableResetEnabled : 1;
uint64_t OneWayDelayEnabled : 1;
uint64_t ReservedFlags : 60;
#else
uint64_t ReservedFlags : 63;
#endif
};
};
uint32_t StreamRecvWindowBidiLocalDefault;
uint32_t StreamRecvWindowBidiRemoteDefault;
uint32_t StreamRecvWindowUnidiDefault;

} QUIC_SETTINGS;
```
Expand Down Expand Up @@ -125,9 +153,9 @@ How much server TLS data to buffer. If the application expects very large serve

`StreamRecvWindowDefault`

Initial stream receive window size.
Initial stream receive flow control window size. This applies to all stream types. Limits for specific stream types can be set using `StreamRecvWindowBidirLocalDefault`, `StreamRecvWindowBidirRemoteDefault` and `StreamRecvWindowUnidirDefault`. The value must be a power of 2.

**Default value:** 32,768
**Default value:** 65,536

`StreamRecvBufferDefault`

Expand Down Expand Up @@ -303,6 +331,24 @@ Enable sender-side ECN support. The connection will validate and react to ECN fe

**Default value:** 0 (`FALSE`)

`StreamRecvWindowBidirLocalDefault`

Initial stream receive flow control window size for locally initiated bidirectional streams. If set, this value overwrites the `StreamRecvWindowDefault`.

**Default value:** 0 (no overwrite)

`StreamRecvWindowBidirRemoteDefault`

Initial stream receive flow control window size for remotely initiated bidirectional streams. If set, this value overwrites the `StreamRecvWindowDefault`.

**Default value:** 0 (no overwrite)

`StreamRecvWindowUnidiDefault`

Initial stream receive flow control window size for remotely initiated unidirectional streams. If set, this value overwrites the `StreamRecvWindowDefault`.

**Default value:** 0 (no overwrite)

# Remarks

When setting new values for the settings, the app must set the corresponding `.IsSet.*` parameter for each actual parameter that is being set or updated. For example:
Expand Down
12 changes: 6 additions & 6 deletions src/core/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -2198,9 +2198,9 @@ QuicConnRecvResumptionTicket(
//
if (ResumedTP.ActiveConnectionIdLimit > QUIC_ACTIVE_CONNECTION_ID_LIMIT ||
ResumedTP.InitialMaxData > Connection->Send.MaxData ||
ResumedTP.InitialMaxStreamDataBidiLocal > Connection->Settings.StreamRecvWindowDefault ||
ResumedTP.InitialMaxStreamDataBidiRemote > Connection->Settings.StreamRecvWindowDefault ||
ResumedTP.InitialMaxStreamDataUni > Connection->Settings.StreamRecvWindowDefault ||
ResumedTP.InitialMaxStreamDataBidiLocal > Connection->Settings.StreamRecvWindowBidiLocalDefault ||
ResumedTP.InitialMaxStreamDataBidiRemote > Connection->Settings.StreamRecvWindowBidiRemoteDefault ||
ResumedTP.InitialMaxStreamDataUni > Connection->Settings.StreamRecvWindowUnidiDefault ||
ResumedTP.InitialMaxUniStreams > Connection->Streams.Types[STREAM_ID_FLAG_IS_CLIENT | STREAM_ID_FLAG_IS_UNI_DIR].MaxTotalStreamCount ||
ResumedTP.InitialMaxBidiStreams > Connection->Streams.Types[STREAM_ID_FLAG_IS_CLIENT | STREAM_ID_FLAG_IS_BI_DIR].MaxTotalStreamCount) {
//
Expand Down Expand Up @@ -2344,9 +2344,9 @@ QuicConnGenerateLocalTransportParameters(
Link);

LocalTP->InitialMaxData = Connection->Send.MaxData;
LocalTP->InitialMaxStreamDataBidiLocal = Connection->Settings.StreamRecvWindowDefault;
LocalTP->InitialMaxStreamDataBidiRemote = Connection->Settings.StreamRecvWindowDefault;
LocalTP->InitialMaxStreamDataUni = Connection->Settings.StreamRecvWindowDefault;
LocalTP->InitialMaxStreamDataBidiLocal = Connection->Settings.StreamRecvWindowBidiLocalDefault;
LocalTP->InitialMaxStreamDataBidiRemote = Connection->Settings.StreamRecvWindowBidiRemoteDefault;
LocalTP->InitialMaxStreamDataUni = Connection->Settings.StreamRecvWindowUnidiDefault;
LocalTP->MaxUdpPayloadSize =
MaxUdpPayloadSizeFromMTU(
CxPlatSocketGetLocalMtu(
Expand Down
3 changes: 3 additions & 0 deletions src/core/quicdef.h
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,9 @@ CXPLAT_STATIC_ASSERT(
#define QUIC_SETTING_MAX_TLS_CLIENT_SEND_BUFFER "TlsClientMaxSendBuffer"
#define QUIC_SETTING_MAX_TLS_SERVER_SEND_BUFFER "TlsServerMaxSendBuffer"
#define QUIC_SETTING_STREAM_FC_WINDOW_SIZE "StreamRecvWindowDefault"
#define QUIC_SETTING_STREAM_FC_BIDI_LOCAL_WINDOW_SIZE "StreamRecvWindowBidiLocalDefault"
#define QUIC_SETTING_STREAM_FC_BIDI_REMOTE_WINDOW_SIZE "StreamRecvWindowBidiRemoteDefault"
#define QUIC_SETTING_STREAM_FC_UNIDI_WINDOW_SIZE "StreamRecvWindowUnidiDefault"
#define QUIC_SETTING_STREAM_RECV_BUFFER_SIZE "StreamRecvBufferDefault"
#define QUIC_SETTING_CONN_FLOW_CONTROL_WINDOW "ConnFlowControlWindow"

Expand Down
133 changes: 132 additions & 1 deletion src/core/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ QuicSettingsSetDefault(
if (!Settings->IsSet.StreamRecvWindowDefault) {
Settings->StreamRecvWindowDefault = QUIC_DEFAULT_STREAM_FC_WINDOW_SIZE;
}
if (!Settings->IsSet.StreamRecvWindowBidiLocalDefault) {
Settings->StreamRecvWindowBidiLocalDefault = QUIC_DEFAULT_STREAM_FC_WINDOW_SIZE;
}
if (!Settings->IsSet.StreamRecvWindowBidiRemoteDefault) {
Settings->StreamRecvWindowBidiRemoteDefault = QUIC_DEFAULT_STREAM_FC_WINDOW_SIZE;
}
if (!Settings->IsSet.StreamRecvWindowUnidiDefault) {
Settings->StreamRecvWindowUnidiDefault = QUIC_DEFAULT_STREAM_FC_WINDOW_SIZE;
}
if (!Settings->IsSet.StreamRecvBufferDefault) {
Settings->StreamRecvBufferDefault = QUIC_DEFAULT_STREAM_RECV_BUFFER_SIZE;
}
Expand Down Expand Up @@ -231,6 +240,15 @@ QuicSettingsCopy(
if (!Destination->IsSet.StreamRecvWindowDefault) {
Destination->StreamRecvWindowDefault = Source->StreamRecvWindowDefault;
}
if (!Destination->IsSet.StreamRecvWindowBidiLocalDefault) {
Destination->StreamRecvWindowBidiLocalDefault = Source->StreamRecvWindowBidiLocalDefault;
}
if (!Destination->IsSet.StreamRecvWindowBidiRemoteDefault) {
Destination->StreamRecvWindowBidiRemoteDefault = Source->StreamRecvWindowBidiRemoteDefault;
}
if (!Destination->IsSet.StreamRecvWindowUnidiDefault) {
Destination->StreamRecvWindowUnidiDefault = Source->StreamRecvWindowUnidiDefault;
}
if (!Destination->IsSet.StreamRecvBufferDefault) {
Destination->StreamRecvBufferDefault = Source->StreamRecvBufferDefault;
}
Expand Down Expand Up @@ -496,6 +514,40 @@ QuicSettingApply(
}
Destination->StreamRecvWindowDefault = Source->StreamRecvWindowDefault;
Destination->IsSet.StreamRecvWindowDefault = TRUE;

//
// Also set window size for individual stream types, they will be overwritten by a more specific settings if set
//
if (!Destination->IsSet.StreamRecvWindowBidiLocalDefault || OverWrite) {
Destination->StreamRecvWindowBidiLocalDefault = Source->StreamRecvWindowDefault;
}
if (!Destination->IsSet.StreamRecvWindowBidiRemoteDefault || OverWrite) {
Destination->StreamRecvWindowBidiRemoteDefault = Source->StreamRecvWindowDefault;
}
if (!Destination->IsSet.StreamRecvWindowUnidiDefault || OverWrite) {
Destination->StreamRecvWindowUnidiDefault = Source->StreamRecvWindowDefault;
}
}
if (Source->IsSet.StreamRecvWindowBidiLocalDefault && (!Destination->IsSet.StreamRecvWindowBidiLocalDefault || OverWrite)) {
if (Source->StreamRecvWindowBidiLocalDefault == 0 || (Source->StreamRecvWindowBidiLocalDefault & (Source->StreamRecvWindowBidiLocalDefault - 1)) != 0) {
return FALSE; // Must be power of 2
}
Destination->StreamRecvWindowBidiLocalDefault = Source->StreamRecvWindowBidiLocalDefault;
Destination->IsSet.StreamRecvWindowBidiLocalDefault = TRUE;
}
if (Source->IsSet.StreamRecvWindowBidiRemoteDefault && (!Destination->IsSet.StreamRecvWindowBidiRemoteDefault || OverWrite)) {
if (Source->StreamRecvWindowBidiRemoteDefault == 0 || (Source->StreamRecvWindowBidiRemoteDefault & (Source->StreamRecvWindowBidiRemoteDefault - 1)) != 0) {
return FALSE; // Must be power of 2
}
Destination->StreamRecvWindowBidiRemoteDefault = Source->StreamRecvWindowBidiRemoteDefault;
Destination->IsSet.StreamRecvWindowBidiRemoteDefault = TRUE;
}
if (Source->IsSet.StreamRecvWindowUnidiDefault && (!Destination->IsSet.StreamRecvWindowUnidiDefault || OverWrite)) {
if (Source->StreamRecvWindowUnidiDefault == 0 || (Source->StreamRecvWindowUnidiDefault & (Source->StreamRecvWindowUnidiDefault - 1)) != 0) {
return FALSE; // Must be power of 2
}
Destination->StreamRecvWindowUnidiDefault = Source->StreamRecvWindowUnidiDefault;
Destination->IsSet.StreamRecvWindowUnidiDefault = TRUE;
}
if (Source->IsSet.StreamRecvBufferDefault && (!Destination->IsSet.StreamRecvBufferDefault || OverWrite)) {
if (Source->StreamRecvBufferDefault < QUIC_DEFAULT_STREAM_RECV_BUFFER_SIZE) {
Expand Down Expand Up @@ -911,6 +963,33 @@ QuicSettingsLoad(
&ValueLen);
}

if (!Settings->IsSet.StreamRecvWindowBidiLocalDefault) {
ValueLen = sizeof(Settings->StreamRecvWindowBidiLocalDefault);
CxPlatStorageReadValue(
Storage,
QUIC_SETTING_STREAM_FC_BIDI_LOCAL_WINDOW_SIZE,
(uint8_t*)&Settings->StreamRecvWindowBidiLocalDefault,
&ValueLen);
}

if (!Settings->IsSet.StreamRecvWindowBidiRemoteDefault) {
ValueLen = sizeof(Settings->StreamRecvWindowBidiRemoteDefault);
CxPlatStorageReadValue(
Storage,
QUIC_SETTING_STREAM_FC_BIDI_REMOTE_WINDOW_SIZE,
(uint8_t*)&Settings->StreamRecvWindowBidiRemoteDefault,
&ValueLen);
}

if (!Settings->IsSet.StreamRecvWindowUnidiDefault) {
ValueLen = sizeof(Settings->StreamRecvWindowUnidiDefault);
CxPlatStorageReadValue(
Storage,
QUIC_SETTING_STREAM_FC_UNIDI_WINDOW_SIZE,
(uint8_t*)&Settings->StreamRecvWindowUnidiDefault,
&ValueLen);
}

if (!Settings->IsSet.StreamRecvBufferDefault) {
ValueLen = sizeof(Settings->StreamRecvBufferDefault);
CxPlatStorageReadValue(
Expand Down Expand Up @@ -1291,7 +1370,9 @@ QuicSettingsDump(
QuicTraceLogVerbose(SettingDumpTlsClientMaxSendBuffer, "[sett] TlsClientMaxSendBuffer = %u", Settings->TlsClientMaxSendBuffer);
QuicTraceLogVerbose(SettingDumpTlsServerMaxSendBuffer, "[sett] TlsServerMaxSendBuffer = %u", Settings->TlsServerMaxSendBuffer);
QuicTraceLogVerbose(SettingDumpStreamRecvWindowDefault, "[sett] StreamRecvWindowDefault= %u", Settings->StreamRecvWindowDefault);
QuicTraceLogVerbose(SettingDumpStreamRecvBufferDefault, "[sett] StreamRecvBufferDefault= %u", Settings->StreamRecvBufferDefault);
QuicTraceLogVerbose(SettingDumpStreamRecvWindowBidiLocalDefault, "[sett] StreamRecvWindowBidiLocalDefault = %u", Settings->StreamRecvWindowBidiLocalDefault);
QuicTraceLogVerbose(SettingDumpStreamRecvWindowBidiRemoteDefault, "[sett] StreamRecvWindowBidiRemoteDefault = %u", Settings->StreamRecvWindowBidiRemoteDefault);
QuicTraceLogVerbose(SettingDumpStreamRecvWindowUnidiDefault, "[sett] StreamRecvWindowUnidiDefault = %u", Settings->StreamRecvWindowUnidiDefault);
QuicTraceLogVerbose(SettingDumpConnFlowControlWindow, "[sett] ConnFlowControlWindow = %u", Settings->ConnFlowControlWindow);
QuicTraceLogVerbose(SettingDumpMaxBytesPerKey, "[sett] MaxBytesPerKey = %llu", Settings->MaxBytesPerKey);
QuicTraceLogVerbose(SettingDumpServerResumptionLevel, "[sett] ServerResumptionLevel = %hhu", Settings->ServerResumptionLevel);
Expand Down Expand Up @@ -1402,6 +1483,15 @@ QuicSettingsDumpNew(
if (Settings->IsSet.StreamRecvWindowDefault) {
QuicTraceLogVerbose(SettingDumpStreamRecvWindowDefault, "[sett] StreamRecvWindowDefault= %u", Settings->StreamRecvWindowDefault);
}
if (Settings->IsSet.StreamRecvWindowBidiLocalDefault) {
QuicTraceLogVerbose(SettingDumpStreamRecvWindowBidiLocalDefault, "[sett] StreamRecvWindowBidiLocalDefault = %u", Settings->StreamRecvWindowBidiLocalDefault);
}
if (Settings->IsSet.StreamRecvWindowBidiRemoteDefault) {
QuicTraceLogVerbose(SettingDumpStreamRecvWindowBidiRemoteDefault, "[sett] StreamRecvWindowBidiRemoteDefault = %u", Settings->StreamRecvWindowBidiRemoteDefault);
}
if (Settings->IsSet.StreamRecvWindowUnidiDefault) {
QuicTraceLogVerbose(SettingDumpStreamRecvWindowUnidiDefault, "[sett] StreamRecvWindowUnidiDefault = %u", Settings->StreamRecvWindowUnidiDefault);
}
if (Settings->IsSet.StreamRecvBufferDefault) {
QuicTraceLogVerbose(SettingDumpStreamRecvBufferDefault, "[sett] StreamRecvBufferDefault= %u", Settings->StreamRecvBufferDefault);
}
Expand Down Expand Up @@ -1700,6 +1790,27 @@ QuicSettingsSettingsToInternal(
SettingsSize,
InternalSettings);

SETTING_COPY_TO_INTERNAL_SIZED(
StreamRecvWindowBidiLocalDefault,
QUIC_SETTINGS,
Settings,
SettingsSize,
InternalSettings);

SETTING_COPY_TO_INTERNAL_SIZED(
StreamRecvWindowBidiRemoteDefault,
QUIC_SETTINGS,
Settings,
SettingsSize,
InternalSettings);

SETTING_COPY_TO_INTERNAL_SIZED(
StreamRecvWindowUnidiDefault,
QUIC_SETTINGS,
Settings,
SettingsSize,
InternalSettings);

return QUIC_STATUS_SUCCESS;
}

Expand Down Expand Up @@ -1832,6 +1943,26 @@ QuicSettingsGetSettings(
*SettingsLength,
InternalSettings);

SETTING_COPY_FROM_INTERNAL_SIZED(
StreamRecvWindowBidiLocalDefault,
QUIC_SETTINGS,
Settings,
*SettingsLength,
InternalSettings);

SETTING_COPY_FROM_INTERNAL_SIZED(
StreamRecvWindowBidiRemoteDefault,
QUIC_SETTINGS,
Settings,
*SettingsLength,
InternalSettings);

SETTING_COPY_FROM_INTERNAL_SIZED(StreamRecvWindowUnidiDefault,
QUIC_SETTINGS,
Settings,
*SettingsLength,
InternalSettings);

*SettingsLength = CXPLAT_MIN(*SettingsLength, sizeof(QUIC_SETTINGS));

return QUIC_STATUS_SUCCESS;
Expand Down
8 changes: 7 additions & 1 deletion src/core/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ typedef struct QUIC_SETTINGS_INTERNAL {
uint64_t TlsClientMaxSendBuffer : 1;
uint64_t TlsServerMaxSendBuffer : 1;
uint64_t StreamRecvWindowDefault : 1;
uint64_t StreamRecvWindowBidiLocalDefault : 1;
uint64_t StreamRecvWindowBidiRemoteDefault : 1;
uint64_t StreamRecvWindowUnidiDefault : 1;
uint64_t StreamRecvBufferDefault : 1;
uint64_t ConnFlowControlWindow : 1;
uint64_t MaxWorkerQueueDelayUs : 1;
Expand Down Expand Up @@ -57,7 +60,7 @@ typedef struct QUIC_SETTINGS_INTERNAL {
uint64_t EncryptionOffloadAllowed : 1;
uint64_t ReliableResetEnabled : 1;
uint64_t OneWayDelayEnabled : 1;
uint64_t RESERVED : 21;
uint64_t RESERVED : 18;
} IsSet;
};

Expand All @@ -69,6 +72,9 @@ typedef struct QUIC_SETTINGS_INTERNAL {
uint32_t TlsClientMaxSendBuffer;
uint32_t TlsServerMaxSendBuffer;
uint32_t StreamRecvWindowDefault;
uint32_t StreamRecvWindowBidiLocalDefault;
uint32_t StreamRecvWindowBidiRemoteDefault;
uint32_t StreamRecvWindowUnidiDefault;
uint32_t StreamRecvBufferDefault;
uint32_t ConnFlowControlWindow;
uint32_t MaxWorkerQueueDelayUs;
Expand Down
8 changes: 7 additions & 1 deletion src/core/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,17 @@ QuicStreamInitialize(
}
}

const uint32_t FlowControlWindowSize = Stream->Flags.Unidirectional
? Connection->Settings.StreamRecvWindowUnidiDefault
: OpenedRemotely
? Connection->Settings.StreamRecvWindowBidiRemoteDefault
: Connection->Settings.StreamRecvWindowBidiLocalDefault;

Status =
QuicRecvBufferInitialize(
&Stream->RecvBuffer,
InitialRecvBufferLength,
Connection->Settings.StreamRecvWindowDefault,
FlowControlWindowSize,
FALSE,
PreallocatedRecvBuffer);
if (QUIC_FAILED(Status)) {
Expand Down
Loading

0 comments on commit d33bc56

Please sign in to comment.