On Fri, Jul 31, 2026 at 06:23:26PM +0200, Laurent Vivier wrote:
The pre-opened socket pools init_sock_pool4/6 are consumed by tcp_conn_pool_sock() when creating new connections from any worker thread, and refilled by tcp_sock_refill_pool() from tcp_timer() in post_handler(). These can run concurrently on different threads.
Add a mutex protecting both operations in tcp_conn_sock() and tcp_sock_refill_init(), where init namespace pools are accessed.
I'm guessing you're going with a mutex rather than a per-thread socket pool for simplicity? That might be the right choice, but I do wonder a bit about it. The pool exists to avoid the latency of creating a new socket for a new connection. If the latency of taking the lock exceeds that of creating a socket, there's no longer any point to the pool. In the unconstest case, that's almost certainly not the case - a happy path futex() lock should be be much faster than a syscall. If the lock _is_ contested, I suspect opening a socket directly might win - at least for host ns sockets. For guest ns sockets, the latency is higher because we need vfork()/setns()/etc. Then again... taking a socket from the pool also involves writing the pool, which potentially means a cacheline pingpong. That might incur a pretty substantial latency. In theory we could trylock() and open a socket directly if we don't get the lock immediately, but at that point it would probably be simpler to have per-thread socket pools anyway. I guess, since there is no meaningful shared state in the socket pool, a per-thread pool seems like the more natural approach to me.
Signed-off-by: Laurent Vivier
--- tcp.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tcp.c b/tcp.c index ab7cbfa5de83..aef689faf031 100644 --- a/tcp.c +++ b/tcp.c @@ -293,6 +293,7 @@ #include
#include #include +#include #include
#include @@ -439,6 +440,7 @@ static socklen_t tcp_info_size; /* Pools for pre-opened sockets (in init) */ int init_sock_pool4 [TCP_SOCK_POOL_SIZE]; int init_sock_pool6 [TCP_SOCK_POOL_SIZE]; +static pthread_mutex_t sock_pool_lock = PTHREAD_MUTEX_INITIALIZER; /** * conn_at_sidx() - Get TCP connection specific flow at given sidx @@ -1581,7 +1583,11 @@ int tcp_conn_sock(sa_family_t af) int *pool = af == AF_INET6 ? init_sock_pool6 : init_sock_pool4; int s;
- if ((s = tcp_conn_pool_sock(pool)) >= 0) + pthread_mutex_lock(&sock_pool_lock); + s = tcp_conn_pool_sock(pool); + pthread_mutex_unlock(&sock_pool_lock); + + if (s >= 0) return s;
/* If the pool is empty we just open a new one without refilling the @@ -2858,6 +2864,7 @@ int tcp_sock_refill_pool(int pool[], sa_family_t af) */ static void tcp_sock_refill_init(const struct ctx *c) { + pthread_mutex_lock(&sock_pool_lock); if (c->ifi4) { int rc = tcp_sock_refill_pool(init_sock_pool4, AF_INET); if (rc < 0) @@ -2870,6 +2877,7 @@ static void tcp_sock_refill_init(const struct ctx *c) warn("TCP: Error refilling IPv6 host socket pool: %s", strerror_(-rc)); } + pthread_mutex_unlock(&sock_pool_lock); }
/** -- 2.54.0
-- David Gibson (he or they) | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you, not the other way | around. http://www.ozlabs.org/~dgibson