Skip to content
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

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft

Conversation

anrossi
Copy link
Contributor

@anrossi anrossi commented Feb 20, 2025

Description

Provide an API for pooling connections across multiple RSS cores

Testing

Manual testing

Documentation

Documentation will be updated with the new API

src/inc/msquic.h Outdated
Comment on lines 1631 to 1645
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
);
Copy link
Member

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.

Copy link
Contributor Author

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

Copy link
Member

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)(
Copy link
Member

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
Copy link
Member

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.

Comment on lines +1898 to +1901
if (RssConfig == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
goto Error;
}
Copy link
Member

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.

Copy link
Contributor Author

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

Copy link
Member

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"
Copy link
Member

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.

Copy link

codecov bot commented Feb 21, 2025

Codecov Report

Attention: Patch coverage is 1.38889% with 142 lines in your changes missing coverage. Please review.

Project coverage is 86.19%. Comparing base (2c81e07) to head (54be6ca).
Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/core/connection_pool.c 0.00% 142 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

//

//
// Creates a simple connection pool with NumberOfConnections connections
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 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.
Copy link
Member

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,
Copy link
Member

@nibanks nibanks Feb 21, 2025

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
Copy link
Member

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...

Comment on lines +222 to +223
#define QUIC_ADDR_EPHEMERAL_PORT_MIN 49152
#define QUIC_ADDR_EPHEMERAL_PORT_MAX 65535
Copy link
Member

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?

Comment on lines +864 to +872
#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
Copy link
Member

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.

Copy link
Member

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(
Copy link
Member

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.

Comment on lines +325 to +338
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);
}

Copy link
Member

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.
Copy link
Member

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);
Copy link
Member

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,
Copy link
Member

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]);
Copy link
Member

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]++;
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants