Skip to content

Commit ca5c05f

Browse files
authored
Add strong partitioning to listeners, connections, and streams (#5376)
* WIP - listener affinity * WIP - listener events are onto partition thread * make connections explicitly partitioned, too * fix winuser build * update the sidecar, again * try to fix thread id abstraction * add array bounds assert * fix winkernel build (finally?) * update rust bindings * fix dotnet, too * fix more rust * fix a few bugs * WIP - test * add more tests, which all fail * sync submodules * WIP * add docs * WIP - tests passing w/o partition option * WIP - listener use-after-free on close notification * WIP - partition test (WIP) passes * WIP - more precise tests * test variable numbers of ecs * finalize basic test coverage * fix non-linux test build * more rust * fix release build * fix dotnet * updating sidecar * codecheck * fix winkernel build * try again to fix winkernel * STILL fix winkernel * fix winkernel yet again * fix win official * RE-fix * fix one listener bug * more listener fixes * more rust gen * more fixes * properly disable the feature on iouring/linux_xdp * ensure connections aren't implicitly partitioned * remove flaky/fragile/wrong stateless retry test * WIP * more fixes * improve comment * fix sidecar * initialize event for compiler * fix unref'd param * make connection async close wait opt-in * rename listener refcounts for clarity * add connection close async param * fixes * official build fix * dotnet * update spinquic for conn close async param * address PR feedback * do todo
1 parent 58e0f0a commit ca5c05f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2015
-242
lines changed

CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ else()
317317
set(QUIC_LOGGING_TYPE "lttng")
318318
message(STATUS "Choosing lttng as default logging type for platform")
319319
endif()
320-
320+
321321
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm|aarch64)$")
322322
set(QUIC_LINUX_XDP_ENABLED OFF CACHE BOOL "XDP not supported on ARM architectures" FORCE)
323323
endif()
@@ -775,6 +775,10 @@ if (QUIC_USE_SYSTEM_LIBCRYPTO)
775775
list(APPEND QUIC_COMMON_DEFINES CXPLAT_SYSTEM_CRYPTO)
776776
endif()
777777

778+
if (QUIC_LINUX_XDP_ENABLED)
779+
list(APPEND QUIC_COMMON_DEFINES CXPLAT_LINUX_XDP_ENABLED)
780+
endif()
781+
778782
if (QUIC_LINUX_IOURING_ENABLED)
779783
list(APPEND QUIC_COMMON_DEFINES CXPLAT_USE_IO_URING)
780784
endif()

docs/API.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ Please note, there is no explicit start/stop API for this library. Each API func
9090

9191
Generally, each app only needs a single registration. The registration represents the execution context where all logic for the app's connections run. The library will create a number of worker threads for each registration, shared for all the connections. This execution context is not shared between different registrations.
9292

93-
A registration is created by calling [RegistrationOpen](api/RegistrationOpen.md) and deleted by calling [RegistrationClose](api/RegistrationClose.md).
93+
A registration is created by calling [RegistrationOpen](api/RegistrationOpen.md) and deleted by calling [RegistrationClose](api/RegistrationClose.md) or (Preview) [RegistrationClose2](api/RegistrationClose2.md).
9494

9595
## Configuration
9696

docs/Diagnostics.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ QUIC_PERF_COUNTER_PATH_FAILURE | Total path challenges that fail ever
291291
QUIC_PERF_COUNTER_SEND_STATELESS_RESET | Total stateless reset packets sent ever
292292
QUIC_PERF_COUNTER_SEND_STATELESS_RETRY | Total stateless retry packets sent ever
293293
QUIC_PERF_COUNTER_CONN_LOAD_REJECT | Total connections rejected due to worker load.
294+
QUIC_PERF_COUNTER_LISTEN_QUEUE_DEPTH | Current listeners queued for processing.
294295
295296
## Windows Performance Monitor
296297

docs/Settings.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ These parameters are accessed by calling [GetParam](./api/GetParam.md) or [SetPa
171171
| `QUIC_PARAM_LISTENER_STATS`<br> 1 | QUIC_LISTENER_STATISTICS | Get-only | Get statistics specific to this Listener instance. |
172172
| `QUIC_PARAM_LISTENER_CIBIR_ID`<br> 2 | uint8_t[] | Both | The CIBIR well-known idenfitier. |
173173
| `QUIC_PARAM_DOS_MODE_EVENTS`<br> 2 | BOOLEAN | Both | The Listener opted in for DoS Mode event. |
174+
| `QUIC_PARAM_LISTENER_PARTITION_INDEX`<br> (preview) | uint16_t | Both | The partition to use for listener callback events and incoming connections. |
174175

175176
## Connection Parameters
176177

@@ -204,7 +205,8 @@ These parameters are accessed by calling [GetParam](./api/GetParam.md) or [SetPa
204205
| `QUIC_PARAM_CONN_STATISTICS_V2_PLAT`<br> 23 | QUIC_STATISTICS_V2 | Get-only | Connection-level statistics with platform-specific time format, version 2. |
205206
| `QUIC_PARAM_CONN_ORIG_DEST_CID` <br> 24 | uint8_t[] | Get-only | The original destination connection ID used by the client to connect to the server. |
206207
| `QUIC_PARAM_CONN_SEND_DSCP` <br> 25 | uint8_t | Both | The DiffServ Code Point put in the DiffServ field (formerly TypeOfService/TrafficClass) on packets sent from this connection. |
207-
| `QUIC_PARAM_CONN_NETWORK_STATISTICS` <br> 20 | QUIC_NETWORK_STATISTICS | Get-only | Returns Connection level network statistics |
208+
| `QUIC_PARAM_CONN_NETWORK_STATISTICS` <br> 32 | QUIC_NETWORK_STATISTICS | Get-only | Returns Connection level network statistics |
209+
| `QUIC_PARAM_CONN_CLOSE_ASYNC` <br> 26 | uint8_t (BOOLEAN) | Both | The desired connection close behavior. Defaults to false (synchronous). |
208210

209211
### QUIC_PARAM_CONN_STATISTICS_V2
210212

docs/api/QUIC_API_TABLE.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ See [RegistrationOpen](RegistrationOpen.md)
8080

8181
See [RegistrationClose](RegistrationClose.md)
8282

83+
`RegistrationClose2`
84+
85+
See (Preview) [RegistrationClose2](RegistrationClose2.md)
86+
8387
`RegistrationShutdown`
8488

8589
See [RegistrationShutdown](RegistrationShutdown.md)

docs/api/RegistrationClose.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ A registration handle from a previous call to [RegistrationOpen](RegistrationOpe
2323
2424
# Remarks
2525
26-
The application **must** close/delete all child configurations and connection objects before closing the registration. This call **will block** on those outstanding objects being cleaned up. Do no call it on any MsQuic event callback, or it will deadlock.
26+
The application **must** close/delete all child configurations and connection objects before closing the registration. This call **will block** on those outstanding objects being cleaned up. Do not call it on any MsQuic event callback or a thread that would otherwise be running an external execution context, or it will deadlock.
2727
2828
# See Also
2929
3030
[RegistrationOpen](RegistrationOpen.md)<br>
31+
(Preview) [RegistrationClose2](RegistrationClose2.md)<br>

docs/api/RegistrationClose2.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
RegistrationClose2 function
2+
======
3+
4+
> **Preview**
5+
> This routine is in preview and is subject to breaking changes.
6+
7+
Closes an existing registration asynchronously.
8+
9+
# Syntax
10+
11+
```C
12+
typedef
13+
_Function_class_(QUIC_REGISTRATION_CLOSE_CALLBACK)
14+
void
15+
(QUIC_API QUIC_REGISTRATION_CLOSE_CALLBACK)(
16+
_In_opt_ void* Context
17+
);
18+
typedef QUIC_REGISTRATION_CLOSE_CALLBACK *QUIC_REGISTRATION_CLOSE_CALLBACK_HANDLER;
19+
20+
typedef
21+
_IRQL_requires_max_(PASSIVE_LEVEL)
22+
void
23+
(QUIC_API * QUIC_REGISTRATION_CLOSE2_FN)(
24+
_In_ _Pre_defensive_ __drv_freesMem(Mem)
25+
HQUIC Registration,
26+
_In_ _Pre_defensive_ QUIC_REGISTRATION_CLOSE_CALLBACK_HANDLER Handler,
27+
_In_opt_ void* Context
28+
);
29+
```
30+
31+
# Parameters
32+
33+
`Registration`
34+
35+
A registration handle from a previous call to [RegistrationOpen](RegistrationOpen.md).
36+
37+
`Handler`
38+
39+
A registration close completion handler. It will be invoked exactly once upon completion of the registration close request.
40+
41+
`Context`
42+
43+
The context to provide to the close completion handler.
44+
45+
# Remarks
46+
47+
> **Preview**
48+
> This routine is in preview and is subject to breaking changes.
49+
50+
The application should close/delete all child configurations and connection objects before closing the registration. This request **will not complete** until those outstanding objects are cleaned up.
51+
52+
# See Also
53+
54+
[RegistrationOpen](RegistrationOpen.md)<br>

docs/api/RegistrationOpen.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ A caveat to this independence is that until a packet or connection can be determ
3939
# See Also
4040
4141
[RegistrationClose](RegistrationClose.md)<br>
42+
(Preview) [RegistrationClose2](RegistrationClose2.md)<br>

docs/api/RegistrationShutdown.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,4 @@ The 62-bit error code to indicate to the peer as the reason for the shutdown.
4444
[RegistrationOpen](RegistrationOpen.md)<br>
4545
[RegistrationClose](RegistrationClose.md)<br>
4646
[ConnectionShutdown](ConnectionShutdown.md)<br>
47+
(Preview) [RegistrationClose2](RegistrationClose2.md)<br>

src/core/api.c

Lines changed: 85 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -33,31 +33,12 @@
3333
_IRQL_requires_max_(DISPATCH_LEVEL)
3434
QUIC_STATUS
3535
QUIC_API
36-
MsQuicConnectionOpen(
37-
_In_ _Pre_defensive_ HQUIC RegistrationHandle,
38-
_In_ _Pre_defensive_ QUIC_CONNECTION_CALLBACK_HANDLER Handler,
39-
_In_opt_ void* Context,
40-
_Outptr_ _At_(*NewConnection, __drv_allocatesMem(Mem)) _Pre_defensive_
41-
HQUIC *NewConnection
42-
)
43-
{
44-
return
45-
MsQuicConnectionOpenInPartition(
46-
RegistrationHandle,
47-
QuicLibraryGetCurrentPartition()->Index,
48-
Handler,
49-
Context,
50-
NewConnection);
51-
}
52-
53-
_IRQL_requires_max_(DISPATCH_LEVEL)
54-
QUIC_STATUS
55-
QUIC_API
56-
MsQuicConnectionOpenInPartition(
36+
QuicConnectionOpenInPartition(
5737
_In_ _Pre_defensive_ HQUIC RegistrationHandle,
5838
_In_ uint16_t PartitionIndex,
5939
_In_ _Pre_defensive_ QUIC_CONNECTION_CALLBACK_HANDLER Handler,
6040
_In_opt_ void* Context,
41+
_In_ BOOLEAN Partitioned,
6142
_Outptr_ _At_(*NewConnection, __drv_allocatesMem(Mem)) _Pre_defensive_
6243
HQUIC *NewConnection
6344
)
@@ -98,6 +79,14 @@ MsQuicConnectionOpenInPartition(
9879
goto Error;
9980
}
10081

82+
//
83+
// Hard partitioning is only supported on a subset of platforms.
84+
//
85+
#if defined(__linux__) && !defined(CXPLAT_USE_IO_URING) && !defined(CXPLAT_LINUX_XDP_ENABLED)
86+
Connection->State.Partitioned = Partitioned;
87+
#else
88+
UNREFERENCED_PARAMETER(Partitioned);
89+
#endif
10190
Connection->ClientCallbackHandler = Handler;
10291
Connection->ClientContext = Context;
10392

@@ -114,6 +103,49 @@ MsQuicConnectionOpenInPartition(
114103
return Status;
115104
}
116105

106+
_IRQL_requires_max_(DISPATCH_LEVEL)
107+
QUIC_STATUS
108+
QUIC_API
109+
MsQuicConnectionOpen(
110+
_In_ _Pre_defensive_ HQUIC RegistrationHandle,
111+
_In_ _Pre_defensive_ QUIC_CONNECTION_CALLBACK_HANDLER Handler,
112+
_In_opt_ void* Context,
113+
_Outptr_ _At_(*NewConnection, __drv_allocatesMem(Mem)) _Pre_defensive_
114+
HQUIC *NewConnection
115+
)
116+
{
117+
return
118+
QuicConnectionOpenInPartition(
119+
RegistrationHandle,
120+
QuicLibraryGetCurrentPartition()->Index,
121+
Handler,
122+
Context,
123+
FALSE,
124+
NewConnection);
125+
}
126+
127+
_IRQL_requires_max_(DISPATCH_LEVEL)
128+
QUIC_STATUS
129+
QUIC_API
130+
MsQuicConnectionOpenInPartition(
131+
_In_ _Pre_defensive_ HQUIC RegistrationHandle,
132+
_In_ uint16_t PartitionIndex,
133+
_In_ _Pre_defensive_ QUIC_CONNECTION_CALLBACK_HANDLER Handler,
134+
_In_opt_ void* Context,
135+
_Outptr_ _At_(*NewConnection, __drv_allocatesMem(Mem)) _Pre_defensive_
136+
HQUIC *NewConnection
137+
)
138+
{
139+
return
140+
QuicConnectionOpenInPartition(
141+
RegistrationHandle,
142+
PartitionIndex,
143+
Handler,
144+
Context,
145+
TRUE,
146+
NewConnection);
147+
}
148+
117149
#pragma warning(push)
118150
#pragma warning(disable:6014) // SAL doesn't understand the free happens on the worker
119151
_IRQL_requires_max_(PASSIVE_LEVEL)
@@ -125,6 +157,7 @@ MsQuicConnectionClose(
125157
)
126158
{
127159
QUIC_CONNECTION* Connection;
160+
BOOLEAN WaitForCompletion = TRUE;
128161

129162
CXPLAT_PASSIVE_CODE();
130163

@@ -157,7 +190,7 @@ MsQuicConnectionClose(
157190

158191
CXPLAT_TEL_ASSERT(!Connection->State.HandleClosed);
159192

160-
if (MsQuicLib.CustomExecutions || IsWorkerThread) {
193+
if (IsWorkerThread) {
161194
//
162195
// Execute this blocking API call inline if called on the worker thread.
163196
//
@@ -172,40 +205,49 @@ MsQuicConnectionClose(
172205

173206
} else {
174207

175-
CXPLAT_EVENT CompletionEvent;
176-
QUIC_OPERATION Oper = { 0 };
177-
QUIC_API_CONTEXT ApiCtx;
208+
CXPLAT_EVENT CompletionEvent = {0};
178209

179-
Oper.Type = QUIC_OPER_TYPE_API_CALL;
180-
Oper.FreeAfterProcess = FALSE;
181-
Oper.API_CALL.Context = &ApiCtx;
210+
Connection->CloseOper.Type = QUIC_OPER_TYPE_API_CALL;
211+
Connection->CloseOper.FreeAfterProcess = FALSE;
212+
Connection->CloseOper.API_CALL.Context = &Connection->CloseApiContext;
182213

183-
ApiCtx.Type = QUIC_API_TYPE_CONN_CLOSE;
184-
CxPlatEventInitialize(&CompletionEvent, TRUE, FALSE);
185-
ApiCtx.Completed = &CompletionEvent;
186-
ApiCtx.Status = NULL;
214+
Connection->CloseApiContext.Type = QUIC_API_TYPE_CONN_CLOSE;
215+
Connection->CloseApiContext.Status = NULL;
216+
217+
if (Connection->State.CloseAsync) {
218+
Connection->CloseApiContext.Completed = NULL;
219+
WaitForCompletion = FALSE;
220+
} else {
221+
CxPlatEventInitialize(&CompletionEvent, TRUE, FALSE);
222+
Connection->CloseApiContext.Completed = &CompletionEvent;
223+
}
187224

188225
//
189226
// Queue the operation and wait for it to be processed.
190227
//
191-
QuicConnQueueOper(Connection, &Oper);
192-
QuicTraceEvent(
193-
ApiWaitOperation,
194-
"[ api] Waiting on operation");
195-
CxPlatEventWaitForever(CompletionEvent);
196-
CxPlatEventUninitialize(CompletionEvent);
228+
QuicConnQueueOper(Connection, &Connection->CloseOper);
229+
230+
if (WaitForCompletion) {
231+
QuicTraceEvent(
232+
ApiWaitOperation,
233+
"[ api] Waiting on operation");
234+
CxPlatEventWaitForever(CompletionEvent);
235+
CxPlatEventUninitialize(CompletionEvent);
236+
}
197237
}
198238

199239
//
200240
// Connection can only be released by the application after the released
201241
// flag was set, in response to the CONN_CLOSE operation was processed.
202242
//
203-
CXPLAT_TEL_ASSERT(Connection->State.HandleClosed);
243+
if (WaitForCompletion) {
244+
CXPLAT_TEL_ASSERT(Connection->State.HandleClosed);
204245

205-
//
206-
// Release the reference to the Connection.
207-
//
208-
QuicConnRelease(Connection, QUIC_CONN_REF_HANDLE_OWNER);
246+
//
247+
// Release the reference to the Connection.
248+
//
249+
QuicConnRelease(Connection, QUIC_CONN_REF_HANDLE_OWNER);
250+
}
209251

210252
Error:
211253

0 commit comments

Comments
 (0)