Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/core/address.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,7 @@ evpl_address_get_address(
sin6 = (struct sockaddr_in6 *) sa;
inet_ntop(AF_INET6, &sin6->sin6_addr, addr_str, sizeof(addr_str));
snprintf(str, len, "[%s]:%d", addr_str, ntohs(sin6->sin6_port));
} else {
snprintf(str, len, "(unspecified)");
}
} /* evpl_bind_get_local_address */
14 changes: 14 additions & 0 deletions src/core/xlio/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,15 @@ struct evpl_xlio {
struct xlio_api_t *extra;
struct evpl_xlio_api *api;
struct evpl_poll *poll;
struct evpl_timer idle_timer;
xlio_poll_group_t poll_group;
struct evpl_xlio_socket **active_sockets;
struct evpl_xlio_buffer *free_xlio_buffers;
struct evpl_xlio_zc *free_zc;
int num_active_sockets;
int max_active_sockets;
int num_connections;
int idle_timer_active;
};

struct evpl_xlio_accepted_socket {
Expand Down Expand Up @@ -216,6 +219,17 @@ evpl_xlio_pending_close(
#endif // if 0

s->socket = 0;

if (!s->listen) {
--xlio->num_connections;
if (xlio->num_connections == 0) {
/* Last active connection gone — release the event loop so
* it can drain spin_ns of idleness and park in
* evpl_core_wait until the idle_timer (or some other
* source) wakes it. */
evpl->force_poll_mode = 0;
}
}
}
} /* evpl_xlio_pending_close */

Expand Down
13 changes: 13 additions & 0 deletions src/core/xlio/tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,19 @@ evpl_xlio_tcp_attach(
bind->local->addrlen = sslen;
memcpy(bind->local->addr, &ss, sslen);

/* Now that the socket is attached to this thread's poll group, resolve
* the peer address here. evpl_xlio_socket_accept (the listener-thread
* callback) deliberately leaves remote NULL because calling
* getpeername() in that context races XLIO's connection state machine
* for short-lived connections. */
sslen = sizeof(ss);
rc = xlio->extra->xlio_socket_getpeername(sock, (struct sockaddr *) &ss, &sslen);
evpl_xlio_abort_if(rc < 0, "xlio_socket_getpeername failed");

bind->remote = evpl_address_alloc();
bind->remote->addrlen = sslen;
memcpy(bind->remote->addr, &ss, sslen);

rc = xlio->extra->xlio_socket_update(s->socket, 0, (uintptr_t) s);

evpl_xlio_abort_if(rc, "Failed to update socket");
Expand Down
56 changes: 48 additions & 8 deletions src/core/xlio/xlio.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,6 @@ evpl_xlio_socket_accept(
struct evpl_xlio *xlio;
struct evpl_bind *listen_bind;
struct evpl_xlio_socket *ls;
struct evpl_address *srcaddr;
struct evpl_xlio_accepted_socket *accepted_socket;
int rc;

Expand All @@ -223,15 +222,16 @@ evpl_xlio_socket_accept(

xlio = evpl_framework_private(evpl, EVPL_FRAMEWORK_XLIO);

srcaddr = evpl_address_alloc();

xlio->extra->xlio_socket_getpeername(sock, srcaddr->addr, &srcaddr->addrlen);

rc = xlio->extra->xlio_socket_detach_group(sock);

evpl_xlio_abort_if(rc, "Failed to detach socket from group");

listen_bind->accept_callback(evpl, listen_bind, srcaddr, accepted_socket, listen_bind->private_data);
/* The peer address is resolved on the worker thread inside
* evpl_xlio_tcp_attach, after the socket has been re-attached to that
* thread's poll group. Calling getpeername() from this listener-thread
* context races XLIO's internal connection state machine and can return
* stale or empty data when sockets churn quickly. */
listen_bind->accept_callback(evpl, listen_bind, NULL, accepted_socket, listen_bind->private_data);

} /* evpl_xlio_socket_accept */

Expand Down Expand Up @@ -265,6 +265,17 @@ evpl_xlio_socket_rx(
evpl_xlio_socket_check_active(xlio, s);
} /* evpl_xlio_socket_rx */

static void
evpl_xlio_idle_timer(
struct evpl *evpl,
struct evpl_timer *timer)
{
struct evpl_xlio *xlio = container_of(timer, struct evpl_xlio, idle_timer);

xlio->extra->xlio_poll_group_poll(xlio->poll_group);
xlio->extra->xlio_poll_group_flush(xlio->poll_group);
} /* evpl_xlio_idle_timer */

static void
evpl_xlio_poll(
struct evpl *evpl,
Expand All @@ -275,6 +286,14 @@ evpl_xlio_poll(
struct evpl_bind *bind;
int i, res;

/* When this thread has no established XLIO connections, skip the entire
* poll. The idle_timer wakes us every 10ms to service listen sockets and
* detect new incoming work; once any connection exists, the regular poll
* path takes over again. */
if (xlio->num_connections == 0) {
return;
}

xlio->extra->xlio_poll_group_poll(xlio->poll_group);

if (xlio->num_active_sockets) {
Expand Down Expand Up @@ -376,6 +395,9 @@ evpl_xlio_destroy(

if (xlio->poll) {
evpl_remove_poll(evpl, xlio->poll);
if (xlio->idle_timer_active) {
evpl_remove_timer(evpl, &xlio->idle_timer);
}
evpl->force_poll_mode = 0;
}

Expand Down Expand Up @@ -461,8 +483,26 @@ evpl_xlio_socket_init(
}

if (!xlio->poll) {
xlio->poll = evpl_add_poll(evpl, NULL, NULL, evpl_xlio_poll, xlio);
evpl->force_poll_mode = 1;
xlio->poll = evpl_add_poll(evpl, NULL, NULL, evpl_xlio_poll, xlio);
}

if (listen) {
/* Listen sockets need a periodic poll to catch incoming accepts
* even when num_connections == 0 (poll callback short-circuits).
* Worker threads never hit this branch and therefore never pay
* for the timer: when a worker has connections the regular poll
* runs, and when it doesn't there's nothing for XLIO to service. */
if (!xlio->idle_timer_active) {
evpl_add_timer(evpl, &xlio->idle_timer, evpl_xlio_idle_timer, 10000UL);
xlio->idle_timer_active = 1;
}
} else {
if (xlio->num_connections == 0) {
/* First active connection on this thread — pin the event loop
* into poll mode so we get per-iteration polling latency. */
evpl->force_poll_mode = 1;
}
++xlio->num_connections;
}

s->readable = 0;
Expand Down
Loading