-
Notifications
You must be signed in to change notification settings - Fork 551
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Connection Pool API #4832
base: main
Are you sure you want to change the base?
Conversation
src/inc/msquic.h
Outdated
typedef | ||
_IRQL_requires_max_(PASSIVE_LEVEL) | ||
_Check_return_ | ||
QUIC_STATUS | ||
(QUIC_API * QUIC_SIMPLE_CONN_POOL_CREATE_FN)( | ||
_In_ HQUIC* Registration, | ||
_In_ QUIC_CONNECTION_CALLBACK_HANDLER Handler, | ||
_In_opt_ void* Context, | ||
_In_opt_ const char* ServerName, | ||
_In_opt_ const QUIC_ADDR* ServerAddress, | ||
_In_ uint16_t ServerPort, | ||
_In_ uint32_t NumberOfConnections, | ||
_Out_writes_bytes_(NumberOfConnections * sizeof(HQUIC)) | ||
HQUIC** ConnectionPool | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the extra API table is unnecessary. Let's just add this function directly to the MsQuic table and eliminate all the rest.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But with the second level API table, we can extend the connection pool API to more than just an array of connections. I don't know how future-looking we should be with this design
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep it as simple as possible. As long as it's wrapped by preview flag we can change later if necessary.
src/inc/msquic.h
Outdated
_IRQL_requires_max_(PASSIVE_LEVEL) | ||
_Check_return_ | ||
QUIC_STATUS | ||
(QUIC_API * QUIC_SIMPLE_CONN_POOL_CREATE_FN)( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's call this just QUIC_CONNECTION_POOL_CREATE_FN
, and the usage below ConnectionPoolCreate
. I'd rather not include simple in the name. We can keep it wrapped in QUIC_API_ENABLE_PREVIEW_FEATURES
to allow for possible changes in the future if we want to make it more complicated.
src/inc/msquic.h
Outdated
@@ -1657,6 +1713,8 @@ typedef struct QUIC_API_TABLE { | |||
QUIC_STREAM_PROVIDE_RECEIVE_BUFFERS_FN | |||
StreamProvideReceiveBuffers; // Available from v2.5 | |||
#endif | |||
QUIC_CONNECTION_POOL_API_OPEN_FN ConnectionPoolApiOpen; // Available from v2.5 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please put it in QUIC_API_ENABLE_PREVIEW_FEATURES
, make sure it aligns with the other names and align the comment too.
if (RssConfig == NULL) { | ||
Status = QUIC_STATUS_INVALID_PARAMETER; | ||
goto Error; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is an internal function, we can probably assert this instead of checking it. SAL indicates this is a required output too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well it's part of the CxPlat interface, so if we make that library separate, it should validate this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we ever actually do that, then there will be plenty to clean up. For now, simpler is better IMO.
#include <stdio.h> | ||
|
||
#ifdef QUIC_CLOG | ||
#include "ToeplitzTest.cpp.clog.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a clog file you need to add this too I think, for this to work.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4832 +/- ##
==========================================
- Coverage 87.30% 86.19% -1.12%
==========================================
Files 56 57 +1
Lines 17634 17778 +144
==========================================
- Hits 15396 15324 -72
- Misses 2238 2454 +216 ☔ View full report in Codecov by Sentry. |
// | ||
|
||
// | ||
// Creates a simple connection pool with NumberOfConnections connections |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Creates a simple connection pool with NumberOfConnections connections | |
// Creates a simple pool of NumberOfConnections connections, |
// | ||
// Creates a simple connection pool with NumberOfConnections connections | ||
// all with the same Context and Handler, and puts them in the | ||
// caller-supplied array. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mention that the goal of this pool is to evenly spread these connections across RSS as much as possible.
_In_ HQUIC Registration, | ||
_In_ QUIC_CONNECTION_CALLBACK_HANDLER Handler, | ||
_In_opt_ void* Context, | ||
_In_opt_ const char* ServerName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since ServerName
can also be an IP address (as a string), let's remove the ServerAddress
variable. Again, we're trying to make this as simple as possible.
_In_ uint16_t ServerPort, | ||
_In_ uint16_t NumberOfConnections, | ||
_Out_writes_bytes_(NumberOfConnections * sizeof(HQUIC)) | ||
HQUIC** ConnectionPool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pointer to an array of pointers to handles is a lot of indirection. I'm wondering if we can just take a pointer to an array of handles...
#define QUIC_ADDR_EPHEMERAL_PORT_MIN 49152 | ||
#define QUIC_ADDR_EPHEMERAL_PORT_MAX 65535 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least on Windows, I think these ranges are customizable. Are they on Linux as well?
#define CXPLAT_RSS_HASH_TYPE_IPV4 0x001 | ||
#define CXPLAT_RSS_HASH_TYPE_TCP_IPV4 0x002 | ||
#define CXPLAT_RSS_HASH_TYPE_UDP_IPV4 0x004 | ||
#define CXPLAT_RSS_HASH_TYPE_IPV6 0x008 | ||
#define CXPLAT_RSS_HASH_TYPE_TCP_IPV6 0x010 | ||
#define CXPLAT_RSS_HASH_TYPE_UDP_IPV6 0x020 | ||
#define CXPLAT_RSS_HASH_TYPE_IPV6_EX 0x040 | ||
#define CXPLAT_RSS_HASH_TYPE_TCP_IPV6_EX 0x080 | ||
#define CXPLAT_RSS_HASH_TYPE_UDP_IPV6_EX 0x100 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are types set up as flags? Can you set all of them simultaneously? I'd assume they are mutually exclusive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And, should these be an enum?
RawRssConfig->HashSecretKeySize + | ||
RawRssConfig->IndirectionTableSize; | ||
CXPLAT_RSS_CONFIG* NewRssConfig = | ||
(CXPLAT_RSS_CONFIG*)CXPLAT_ALLOC_NONPAGED( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You used PAGED above and NONPAGED here. I know it doesn't really matter in user mode, but we might adapt this for kernel one day, so we should get it right. I'd say PAGED for both, as this likely wouldn't need to be called at DISPATCH.
inline | ||
void | ||
QuicAddrIncrementPort( | ||
_Inout_ QUIC_ADDR* Addr | ||
) | ||
{ | ||
uint32_t Port = QuicAddrGetPort(Addr); | ||
Port += 1; | ||
if (Port > QUIC_ADDR_EPHEMERAL_PORT_MAX) { | ||
Port = QUIC_ADDR_EPHEMERAL_PORT_MIN; | ||
} | ||
QuicAddrSetPort(Addr, (uint16_t)Port); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function already uses the platform abstraction layer to get/set the port and is duplicated 3 times. Finally, it's only used in one place (connection pool). I say we remove this helper and just do the logic directly in the connection pool to eliminate all the duplication.
} | ||
} | ||
// | ||
// This is safe because the RssProcessor array is the same size as the indirection table. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you add a debug assert above that they are the same, SAL should be happy.
} | ||
CreatedConnections++; | ||
|
||
Status = MsQuicSetParam(Connections[i], QUIC_PARAM_CONN_REMOTE_ADDRESS, sizeof(ResolvedRemoteAddress), &ResolvedRemoteAddress); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use QuicConnSetParam
instead. Also, you can debug assert the result, because it can only fail if you mess up the code here (i.e. invalid parameters). Same for local address.
(QUIC_API * QUIC_CONN_POOL_CREATE_FN)( | ||
_In_ HQUIC Registration, | ||
_In_ QUIC_CONNECTION_CALLBACK_HANDLER Handler, | ||
_In_opt_ void* Context, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They probably need to pass in an array of context pointers, a unique one for each.
|
||
} while (TRUE); | ||
|
||
Status = MsQuicConnectionOpen(Registration, Handler, Context, &Connections[i]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Call QuicConnAlloc
directly. Another thing: By default, creating a connection uses the "current partition" (i.e., current processor) to affinitize the connection, which isn't really what you want. You want it to be on the RSS partition. So, I would actually recommend modifying QuicConnAlloc
to expose a new input parameter for PartitionIndex
so you can set it explicitly.
// | ||
// The connection was created successfully, add it to the count for this processor. | ||
// | ||
ConnectionCounts[RssProcIndex]++; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're going to have to call QuicConnStart
otherwise the port you tried to find that was open might get taken. And, really, you're asking for trouble by using a model of 'try to find an open port, then stop using it, and hope you can get it again'. You should find an initial ephemeral port, and then start creating and starting connections on ports until you've got enough successfully started connections.
Description
Provide an API for pooling connections across multiple RSS cores
Testing
Manual testing
Documentation
Documentation will be updated with the new API