* [PATCH v2 0/2] util/qemu-sockets: Introduce configurable TCP keep-alive idle period @ 2025-03-19 16:36 Juraj Marcin 2025-03-19 16:36 ` [PATCH v2 1/2] util/qemu-sockets: Add support for keep-alive flag to passive sockets Juraj Marcin 2025-03-19 16:36 ` [PATCH v2 2/2] utils/qemu-sockets: Introduce keep-alive-idle-period inet socket option Juraj Marcin 0 siblings, 2 replies; 9+ messages in thread From: Juraj Marcin @ 2025-03-19 16:36 UTC (permalink / raw) To: qemu-devel Cc: Juraj Marcin, vsementsov, Paolo Bonzini, Daniel P. Berrangé This series extends the work introduced by commit aec21d3175 ("qapi: Add InetSocketAddress member keep-alive") [1] First, it allows the use of the keep-alive flag for server-side sockets. Then it introduces a new keep-alive-idle-period setting, which changes the TCP_KEEPIDLE socket option on platforms that support it (this option is found in Linux, BSD, and Win32 documentations). By default, the value of keep-alive-idle-period is 0, which means no custom socket option value is set. This is useful, for example, for live migration. In case there is no traffic from the destination to the source machine during postcopy, the destination cannot detect a failed connection due to a lack of non-acknowledged packets and stays in the postcopy-active state until paused by the management of the QEMU instance. [1]: https://lore.kernel.org/all/20190725094937.32454-1-vsementsov@virtuozzo.com/ --- V2: - moved socket options setting into a common function for both server and client sockets (suggested by Vladimir) Juraj Marcin (2): util/qemu-sockets: Add support for keep-alive flag to passive sockets utils/qemu-sockets: Introduce keep-alive-idle-period inet socket option io/dns-resolver.c | 6 ++++ meson.build | 2 ++ qapi/sockets.json | 9 +++-- util/qemu-sockets.c | 81 ++++++++++++++++++++++++++++++++------------- 4 files changed, 73 insertions(+), 25 deletions(-) -- 2.48.1 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 1/2] util/qemu-sockets: Add support for keep-alive flag to passive sockets 2025-03-19 16:36 [PATCH v2 0/2] util/qemu-sockets: Introduce configurable TCP keep-alive idle period Juraj Marcin @ 2025-03-19 16:36 ` Juraj Marcin 2025-03-20 7:51 ` Vladimir Sementsov-Ogievskiy 2025-03-24 10:30 ` Daniel P. Berrangé 2025-03-19 16:36 ` [PATCH v2 2/2] utils/qemu-sockets: Introduce keep-alive-idle-period inet socket option Juraj Marcin 1 sibling, 2 replies; 9+ messages in thread From: Juraj Marcin @ 2025-03-19 16:36 UTC (permalink / raw) To: qemu-devel Cc: Juraj Marcin, vsementsov, Paolo Bonzini, Daniel P. Berrangé From: Juraj Marcin <jmarcin@redhat.com> Commit aec21d3175 (qapi: Add InetSocketAddress member keep-alive) introduces the keep-alive flag, which enables the SO_KEEPALIVE socket option, but only on client-side sockets. However, this option is also useful for server-side sockets, so they can check if a client is still reachable or drop the connection otherwise. This patch enables the SO_KEEPALIVE socket option on passive server-side sockets if the keep-alive flag is enabled. This socket option is then inherited by active server-side sockets communicating with connected clients. This patch also fixes an issue in 'qio_dns_resolver_lookup_sync_inet()' where the keep-alive flag is not copied together with other attributes. Signed-off-by: Juraj Marcin <jmarcin@redhat.com> --- io/dns-resolver.c | 2 ++ qapi/sockets.json | 4 ++-- util/qemu-sockets.c | 52 +++++++++++++++++++++++++-------------------- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/io/dns-resolver.c b/io/dns-resolver.c index 53b0e8407a..ee7403b65b 100644 --- a/io/dns-resolver.c +++ b/io/dns-resolver.c @@ -126,6 +126,8 @@ static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver, .has_mptcp = iaddr->has_mptcp, .mptcp = iaddr->mptcp, #endif + .has_keep_alive = iaddr->has_keep_alive, + .keep_alive = iaddr->keep_alive, }; (*addrs)[i] = newaddr; diff --git a/qapi/sockets.json b/qapi/sockets.json index 6a95023315..eb50f64e3a 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -56,8 +56,8 @@ # @ipv6: whether to accept IPv6 addresses, default try both IPv4 and # IPv6 # -# @keep-alive: enable keep-alive when connecting to this socket. Not -# supported for passive sockets. (Since 4.2) +# @keep-alive: enable keep-alive when connecting to/listening on this socket. +# (Since 4.2, not supported for listening sockets until 10.0) # # @mptcp: enable multi-path TCP. (Since 6.1) # diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 77477c1cd5..99357a4435 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -205,6 +205,22 @@ static int try_bind(int socket, InetSocketAddress *saddr, struct addrinfo *e) #endif } +static int inet_set_sockopts(int sock, InetSocketAddress *saddr, Error **errp) +{ + if (saddr->keep_alive) { + int keep_alive = 1; + int ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, + &keep_alive, sizeof(keep_alive)); + + if (ret < 0) { + error_setg_errno(errp, errno, + "Unable to set keep-alive option on socket"); + return -1; + } + } + return 0; +} + static int inet_listen_saddr(InetSocketAddress *saddr, int port_offset, int num, @@ -220,12 +236,6 @@ static int inet_listen_saddr(InetSocketAddress *saddr, int saved_errno = 0; bool socket_created = false; - if (saddr->keep_alive) { - error_setg(errp, "keep-alive option is not supported for passive " - "sockets"); - return -1; - } - memset(&ai,0, sizeof(ai)); ai.ai_flags = AI_PASSIVE; if (saddr->has_numeric && saddr->numeric) { @@ -313,13 +323,18 @@ static int inet_listen_saddr(InetSocketAddress *saddr, goto listen_failed; } } else { - if (!listen(slisten, num)) { + if (listen(slisten, num)) { + if (errno != EADDRINUSE) { + error_setg_errno(errp, errno, + "Failed to listen on socket"); + goto listen_failed; + } + } else { + if (inet_set_sockopts(slisten, saddr, errp)) { + goto listen_failed; + } goto listen_ok; } - if (errno != EADDRINUSE) { - error_setg_errno(errp, errno, "Failed to listen on socket"); - goto listen_failed; - } } /* Someone else managed to bind to the same port and beat us * to listen on it! Socket semantics does not allow us to @@ -474,19 +489,10 @@ int inet_connect_saddr(InetSocketAddress *saddr, Error **errp) error_propagate(errp, local_err); return sock; } - - if (saddr->keep_alive) { - int val = 1; - int ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, - &val, sizeof(val)); - - if (ret < 0) { - error_setg_errno(errp, errno, "Unable to set KEEPALIVE"); - close(sock); - return -1; - } + if (inet_set_sockopts(sock, saddr, errp)) { + close(sock); + return -1; } - return sock; } -- 2.48.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/2] util/qemu-sockets: Add support for keep-alive flag to passive sockets 2025-03-19 16:36 ` [PATCH v2 1/2] util/qemu-sockets: Add support for keep-alive flag to passive sockets Juraj Marcin @ 2025-03-20 7:51 ` Vladimir Sementsov-Ogievskiy 2025-03-24 10:30 ` Daniel P. Berrangé 1 sibling, 0 replies; 9+ messages in thread From: Vladimir Sementsov-Ogievskiy @ 2025-03-20 7:51 UTC (permalink / raw) To: Juraj Marcin, qemu-devel; +Cc: Paolo Bonzini, Daniel P. Berrangé On 19.03.25 19:36, Juraj Marcin wrote: > From: Juraj Marcin<jmarcin@redhat.com> > > Commit aec21d3175 (qapi: Add InetSocketAddress member keep-alive) > introduces the keep-alive flag, which enables the SO_KEEPALIVE socket > option, but only on client-side sockets. However, this option is also > useful for server-side sockets, so they can check if a client is still > reachable or drop the connection otherwise. > > This patch enables the SO_KEEPALIVE socket option on passive server-side > sockets if the keep-alive flag is enabled. This socket option is then > inherited by active server-side sockets communicating with connected > clients. > > This patch also fixes an issue in 'qio_dns_resolver_lookup_sync_inet()' > where the keep-alive flag is not copied together with other attributes. > > Signed-off-by: Juraj Marcin<jmarcin@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> -- Best regards, Vladimir ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/2] util/qemu-sockets: Add support for keep-alive flag to passive sockets 2025-03-19 16:36 ` [PATCH v2 1/2] util/qemu-sockets: Add support for keep-alive flag to passive sockets Juraj Marcin 2025-03-20 7:51 ` Vladimir Sementsov-Ogievskiy @ 2025-03-24 10:30 ` Daniel P. Berrangé 2025-03-26 12:40 ` Juraj Marcin 1 sibling, 1 reply; 9+ messages in thread From: Daniel P. Berrangé @ 2025-03-24 10:30 UTC (permalink / raw) To: Juraj Marcin; +Cc: qemu-devel, vsementsov, Paolo Bonzini On Wed, Mar 19, 2025 at 05:36:19PM +0100, Juraj Marcin wrote: > From: Juraj Marcin <jmarcin@redhat.com> > > Commit aec21d3175 (qapi: Add InetSocketAddress member keep-alive) > introduces the keep-alive flag, which enables the SO_KEEPALIVE socket > option, but only on client-side sockets. However, this option is also > useful for server-side sockets, so they can check if a client is still > reachable or drop the connection otherwise. > > This patch enables the SO_KEEPALIVE socket option on passive server-side > sockets if the keep-alive flag is enabled. This socket option is then > inherited by active server-side sockets communicating with connected > clients. > > This patch also fixes an issue in 'qio_dns_resolver_lookup_sync_inet()' > where the keep-alive flag is not copied together with other attributes. > > Signed-off-by: Juraj Marcin <jmarcin@redhat.com> > --- > io/dns-resolver.c | 2 ++ > qapi/sockets.json | 4 ++-- > util/qemu-sockets.c | 52 +++++++++++++++++++++++++-------------------- > 3 files changed, 33 insertions(+), 25 deletions(-) > > diff --git a/io/dns-resolver.c b/io/dns-resolver.c > index 53b0e8407a..ee7403b65b 100644 > --- a/io/dns-resolver.c > +++ b/io/dns-resolver.c > @@ -126,6 +126,8 @@ static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver, > .has_mptcp = iaddr->has_mptcp, > .mptcp = iaddr->mptcp, > #endif > + .has_keep_alive = iaddr->has_keep_alive, > + .keep_alive = iaddr->keep_alive, > }; > > (*addrs)[i] = newaddr; This is a bugfix, so should be a separate commit to facilitate cherry picking back to stable branches. > diff --git a/qapi/sockets.json b/qapi/sockets.json > index 6a95023315..eb50f64e3a 100644 > --- a/qapi/sockets.json > +++ b/qapi/sockets.json > @@ -56,8 +56,8 @@ > # @ipv6: whether to accept IPv6 addresses, default try both IPv4 and > # IPv6 > # > -# @keep-alive: enable keep-alive when connecting to this socket. Not > -# supported for passive sockets. (Since 4.2) > +# @keep-alive: enable keep-alive when connecting to/listening on this socket. > +# (Since 4.2, not supported for listening sockets until 10.0) This needs to be '10.1', since we're past feature freeze for '10.0' > # > # @mptcp: enable multi-path TCP. (Since 6.1) > # > diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c > index 77477c1cd5..99357a4435 100644 > --- a/util/qemu-sockets.c > +++ b/util/qemu-sockets.c > @@ -205,6 +205,22 @@ static int try_bind(int socket, InetSocketAddress *saddr, struct addrinfo *e) > #endif > } > > +static int inet_set_sockopts(int sock, InetSocketAddress *saddr, Error **errp) > +{ > + if (saddr->keep_alive) { > + int keep_alive = 1; > + int ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, > + &keep_alive, sizeof(keep_alive)); > + > + if (ret < 0) { > + error_setg_errno(errp, errno, > + "Unable to set keep-alive option on socket"); > + return -1; > + } > + } > + return 0; > +} > + > static int inet_listen_saddr(InetSocketAddress *saddr, > int port_offset, > int num, > @@ -220,12 +236,6 @@ static int inet_listen_saddr(InetSocketAddress *saddr, > int saved_errno = 0; > bool socket_created = false; > > - if (saddr->keep_alive) { > - error_setg(errp, "keep-alive option is not supported for passive " > - "sockets"); > - return -1; > - } > - > memset(&ai,0, sizeof(ai)); > ai.ai_flags = AI_PASSIVE; > if (saddr->has_numeric && saddr->numeric) { > @@ -313,13 +323,18 @@ static int inet_listen_saddr(InetSocketAddress *saddr, > goto listen_failed; > } > } else { > - if (!listen(slisten, num)) { > + if (listen(slisten, num)) { > + if (errno != EADDRINUSE) { > + error_setg_errno(errp, errno, > + "Failed to listen on socket"); > + goto listen_failed; > + } The implicit '} else {' fallthough is a success code path, but you're failing to set socket opts in that case. We accept that the socket might already be listening, but we must still ensure the keepalives are set. > + } else { > + if (inet_set_sockopts(slisten, saddr, errp)) { > + goto listen_failed; > + } > goto listen_ok; > } Stylewise, don't nest the success codepath - this also would fix the comment above. > - if (errno != EADDRINUSE) { > - error_setg_errno(errp, errno, "Failed to listen on socket"); > - goto listen_failed; > - } > } > /* Someone else managed to bind to the same port and beat us > * to listen on it! Socket semantics does not allow us to > @@ -474,19 +489,10 @@ int inet_connect_saddr(InetSocketAddress *saddr, Error **errp) > error_propagate(errp, local_err); > return sock; > } > - > - if (saddr->keep_alive) { > - int val = 1; > - int ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, > - &val, sizeof(val)); > - > - if (ret < 0) { > - error_setg_errno(errp, errno, "Unable to set KEEPALIVE"); > - close(sock); > - return -1; > - } > + if (inet_set_sockopts(sock, saddr, errp)) { > + close(sock); > + return -1; > } This is the refactoring of existing code into a separate function. This can be done in a separate commit, then the new feature of adding it to the listener socket codepath can be a followon commit. With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :| ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/2] util/qemu-sockets: Add support for keep-alive flag to passive sockets 2025-03-24 10:30 ` Daniel P. Berrangé @ 2025-03-26 12:40 ` Juraj Marcin 0 siblings, 0 replies; 9+ messages in thread From: Juraj Marcin @ 2025-03-26 12:40 UTC (permalink / raw) To: Daniel P. Berrangé; +Cc: qemu-devel, vsementsov, Paolo Bonzini Hi Daniel, On 2025-03-24 10:30, Daniel P. Berrangé wrote: > On Wed, Mar 19, 2025 at 05:36:19PM +0100, Juraj Marcin wrote: > > From: Juraj Marcin <jmarcin@redhat.com> > > > > Commit aec21d3175 (qapi: Add InetSocketAddress member keep-alive) > > introduces the keep-alive flag, which enables the SO_KEEPALIVE socket > > option, but only on client-side sockets. However, this option is also > > useful for server-side sockets, so they can check if a client is still > > reachable or drop the connection otherwise. > > > > This patch enables the SO_KEEPALIVE socket option on passive server-side > > sockets if the keep-alive flag is enabled. This socket option is then > > inherited by active server-side sockets communicating with connected > > clients. > > > > This patch also fixes an issue in 'qio_dns_resolver_lookup_sync_inet()' > > where the keep-alive flag is not copied together with other attributes. > > > > Signed-off-by: Juraj Marcin <jmarcin@redhat.com> > > --- > > io/dns-resolver.c | 2 ++ > > qapi/sockets.json | 4 ++-- > > util/qemu-sockets.c | 52 +++++++++++++++++++++++++-------------------- > > 3 files changed, 33 insertions(+), 25 deletions(-) > > > > diff --git a/io/dns-resolver.c b/io/dns-resolver.c > > index 53b0e8407a..ee7403b65b 100644 > > --- a/io/dns-resolver.c > > +++ b/io/dns-resolver.c > > @@ -126,6 +126,8 @@ static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver, > > .has_mptcp = iaddr->has_mptcp, > > .mptcp = iaddr->mptcp, > > #endif > > + .has_keep_alive = iaddr->has_keep_alive, > > + .keep_alive = iaddr->keep_alive, > > }; > > > > (*addrs)[i] = newaddr; > > This is a bugfix, so should be a separate commit to facilitate cherry > picking back to stable branches. > > > diff --git a/qapi/sockets.json b/qapi/sockets.json > > index 6a95023315..eb50f64e3a 100644 > > --- a/qapi/sockets.json > > +++ b/qapi/sockets.json > > @@ -56,8 +56,8 @@ > > # @ipv6: whether to accept IPv6 addresses, default try both IPv4 and > > # IPv6 > > # > > -# @keep-alive: enable keep-alive when connecting to this socket. Not > > -# supported for passive sockets. (Since 4.2) > > +# @keep-alive: enable keep-alive when connecting to/listening on this socket. > > +# (Since 4.2, not supported for listening sockets until 10.0) > > This needs to be '10.1', since we're past feature freeze for '10.0' > > > # > > # @mptcp: enable multi-path TCP. (Since 6.1) > > # > > diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c > > index 77477c1cd5..99357a4435 100644 > > --- a/util/qemu-sockets.c > > +++ b/util/qemu-sockets.c > > @@ -205,6 +205,22 @@ static int try_bind(int socket, InetSocketAddress *saddr, struct addrinfo *e) > > #endif > > } > > > > +static int inet_set_sockopts(int sock, InetSocketAddress *saddr, Error **errp) > > +{ > > + if (saddr->keep_alive) { > > + int keep_alive = 1; > > + int ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, > > + &keep_alive, sizeof(keep_alive)); > > + > > + if (ret < 0) { > > + error_setg_errno(errp, errno, > > + "Unable to set keep-alive option on socket"); > > + return -1; > > + } > > + } > > + return 0; > > +} > > + > > static int inet_listen_saddr(InetSocketAddress *saddr, > > int port_offset, > > int num, > > @@ -220,12 +236,6 @@ static int inet_listen_saddr(InetSocketAddress *saddr, > > int saved_errno = 0; > > bool socket_created = false; > > > > - if (saddr->keep_alive) { > > - error_setg(errp, "keep-alive option is not supported for passive " > > - "sockets"); > > - return -1; > > - } > > - > > memset(&ai,0, sizeof(ai)); > > ai.ai_flags = AI_PASSIVE; > > if (saddr->has_numeric && saddr->numeric) { > > @@ -313,13 +323,18 @@ static int inet_listen_saddr(InetSocketAddress *saddr, > > goto listen_failed; > > } > > } else { > > - if (!listen(slisten, num)) { > > + if (listen(slisten, num)) { > > + if (errno != EADDRINUSE) { > > + error_setg_errno(errp, errno, > > + "Failed to listen on socket"); > > + goto listen_failed; > > + } > > The implicit '} else {' fallthough is a success code path, > but you're failing to set socket opts in that case. We > accept that the socket might already be listening, but > we must still ensure the keepalives are set. Actually, it is not a success code path. Regardless of how 'listen()' fails, it is still a failure. The only difference is that with 'EADDRINUSE' it does not fail completely, but it continues with 'close()' at the end of the loop and tries the next port. This is the same behaviour as before: rc = try_bind(slisten, saddr, e); if (rc < 0) { if (errno != EADDRINUSE) { error_setg_errno(errp, errno, "Failed to bind socket"); /*** don't try next ports, just fail ***/ goto listen_failed; } /*** close this one and try the next port ***/ } else { if (!listen(slisten, num)) { /*** the only success path ***/ goto listen_ok; } if (errno != EADDRINUSE) { error_setg_errno(errp, errno, "Failed to listen on socket"); /*** don't try next ports, just fail ***/ goto listen_failed; } /*** close this one and try the next port ***/ } /* Someone else managed to bind to the same port and beat us * to listen on it! Socket semantics does not allow us to * recover from this situation, so we need to recreate the * socket to allow bind attempts for subsequent ports: */ close(slisten); slisten = -1; This loop probably deserves some more refactoring before modifying. Best regards, Juraj Marcin > > > + } else { > > + if (inet_set_sockopts(slisten, saddr, errp)) { > > + goto listen_failed; > > + } > > goto listen_ok; > > } > > Stylewise, don't nest the success codepath - this also would > fix the comment above. > > > - if (errno != EADDRINUSE) { > > - error_setg_errno(errp, errno, "Failed to listen on socket"); > > - goto listen_failed; > > - } > > } > > /* Someone else managed to bind to the same port and beat us > > * to listen on it! Socket semantics does not allow us to > > @@ -474,19 +489,10 @@ int inet_connect_saddr(InetSocketAddress *saddr, Error **errp) > > error_propagate(errp, local_err); > > return sock; > > } > > - > > - if (saddr->keep_alive) { > > - int val = 1; > > - int ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, > > - &val, sizeof(val)); > > - > > - if (ret < 0) { > > - error_setg_errno(errp, errno, "Unable to set KEEPALIVE"); > > - close(sock); > > - return -1; > > - } > > + if (inet_set_sockopts(sock, saddr, errp)) { > > + close(sock); > > + return -1; > > } > > This is the refactoring of existing code into a separate function. This > can be done in a separate commit, then the new feature of adding it to > the listener socket codepath can be a followon commit. > > > With regards, > Daniel > -- > |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| > |: https://libvirt.org -o- https://fstop138.berrange.com :| > |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :| > ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 2/2] utils/qemu-sockets: Introduce keep-alive-idle-period inet socket option 2025-03-19 16:36 [PATCH v2 0/2] util/qemu-sockets: Introduce configurable TCP keep-alive idle period Juraj Marcin 2025-03-19 16:36 ` [PATCH v2 1/2] util/qemu-sockets: Add support for keep-alive flag to passive sockets Juraj Marcin @ 2025-03-19 16:36 ` Juraj Marcin 2025-03-20 8:00 ` Vladimir Sementsov-Ogievskiy 2025-03-24 11:12 ` Daniel P. Berrangé 1 sibling, 2 replies; 9+ messages in thread From: Juraj Marcin @ 2025-03-19 16:36 UTC (permalink / raw) To: qemu-devel Cc: Juraj Marcin, vsementsov, Paolo Bonzini, Daniel P. Berrangé From: Juraj Marcin <jmarcin@redhat.com> The default idle period for TCP connection could be even 2 hours. However, in some cases, the application needs to be aware of a connection issue much sooner. This is the case, for example, for postcopy live migration. If there is no traffic from the migration destination guest (server-side) to the migration source guest (client-side), the destination keeps waiting for pages indefinitely and does not switch to the postcopy-paused state. This can happen, for example, if the destination QEMU instance is started with '-S' command line option and the machine is not started yet or if the machine is idle and produces no new page faults for not-yet-migrated pages. This patch introduces a new inet socket parameter on platforms where TCP_KEEPIDLE is defined and passes the configured value to the TCP_KEEPIDLE socket option when a client-side or server-side socket is created. The default value is 0, which means system configuration is used, and no custom value is set. Signed-off-by: Juraj Marcin <jmarcin@redhat.com> --- io/dns-resolver.c | 4 ++++ meson.build | 2 ++ qapi/sockets.json | 5 +++++ util/qemu-sockets.c | 29 +++++++++++++++++++++++++++++ 4 files changed, 40 insertions(+) diff --git a/io/dns-resolver.c b/io/dns-resolver.c index ee7403b65b..03c59673f0 100644 --- a/io/dns-resolver.c +++ b/io/dns-resolver.c @@ -128,6 +128,10 @@ static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver, #endif .has_keep_alive = iaddr->has_keep_alive, .keep_alive = iaddr->keep_alive, +#ifdef HAVE_TCP_KEEPIDLE + .has_keep_alive_idle_period = iaddr->has_keep_alive_idle_period, + .keep_alive_idle_period = iaddr->keep_alive_idle_period, +#endif }; (*addrs)[i] = newaddr; diff --git a/meson.build b/meson.build index 41f68d3806..e3440b09c8 100644 --- a/meson.build +++ b/meson.build @@ -2734,6 +2734,8 @@ if linux_io_uring.found() config_host_data.set('HAVE_IO_URING_PREP_WRITEV2', cc.has_header_symbol('liburing.h', 'io_uring_prep_writev2')) endif +config_host_data.set('HAVE_TCP_KEEPIDLE', + cc.has_header_symbol('netinet/tcp.h', 'TCP_KEEPIDLE')) # has_member config_host_data.set('HAVE_SIGEV_NOTIFY_THREAD_ID', diff --git a/qapi/sockets.json b/qapi/sockets.json index eb50f64e3a..ddd82b1e66 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -59,6 +59,10 @@ # @keep-alive: enable keep-alive when connecting to/listening on this socket. # (Since 4.2, not supported for listening sockets until 10.0) # +# @keep-alive-idle-period: time in seconds the connection needs to be idle +# before sending a keepalive packet. Only supported for TCP sockets on +# systems where TCP_KEEPIDLE socket option is defined. (Since 10.0) +# # @mptcp: enable multi-path TCP. (Since 6.1) # # Since: 1.3 @@ -71,6 +75,7 @@ '*ipv4': 'bool', '*ipv6': 'bool', '*keep-alive': 'bool', + '*keep-alive-idle-period': { 'type': 'uint32', 'if': 'HAVE_TCP_KEEPIDLE' }, '*mptcp': { 'type': 'bool', 'if': 'HAVE_IPPROTO_MPTCP' } } } ## diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 99357a4435..23c8a6cc2b 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -217,6 +217,19 @@ static int inet_set_sockopts(int sock, InetSocketAddress *saddr, Error **errp) "Unable to set keep-alive option on socket"); return -1; } +#ifdef HAVE_TCP_KEEPIDLE + if (saddr->has_keep_alive_idle_period && + saddr->keep_alive_idle_period) { + int keep_idle = saddr->has_keep_alive_idle_period; + ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keep_idle, + sizeof(keep_idle)); + if (ret < 0) { + error_setg_errno(errp, errno, + "Unable to set TCP keep-alive idle option on socket"); + return -1; + } + } +#endif } return 0; } @@ -697,6 +710,22 @@ int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) } addr->has_keep_alive = true; } +#ifdef HAVE_TCP_KEEPIDLE + begin = strstr(optstr, ",keep-alive-idle-period="); + if (begin) { + begin += strlen(",keep-alive-idle-period="); + if (sscanf(begin, "%" PRIu32 "%n", &addr->keep_alive_idle_period, &pos) != 1 || + (begin[pos] != '\0' && begin[pos] != ',')) { + error_setg(errp, "error parsing keep-alive-idle-period argument"); + return -1; + } + if (addr->keep_alive_idle_period > INT_MAX) { + error_setg(errp, "keep-alive-idle-period value is too large"); + return -1; + } + addr->has_keep_alive_idle_period = true; + } +#endif #ifdef HAVE_IPPROTO_MPTCP begin = strstr(optstr, ",mptcp"); if (begin) { -- 2.48.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/2] utils/qemu-sockets: Introduce keep-alive-idle-period inet socket option 2025-03-19 16:36 ` [PATCH v2 2/2] utils/qemu-sockets: Introduce keep-alive-idle-period inet socket option Juraj Marcin @ 2025-03-20 8:00 ` Vladimir Sementsov-Ogievskiy 2025-03-24 11:12 ` Daniel P. Berrangé 1 sibling, 0 replies; 9+ messages in thread From: Vladimir Sementsov-Ogievskiy @ 2025-03-20 8:00 UTC (permalink / raw) To: Juraj Marcin, qemu-devel; +Cc: Paolo Bonzini, Daniel P. Berrangé On 19.03.25 19:36, Juraj Marcin wrote: > From: Juraj Marcin<jmarcin@redhat.com> > > The default idle period for TCP connection could be even 2 hours. > However, in some cases, the application needs to be aware of a > connection issue much sooner. > > This is the case, for example, for postcopy live migration. If there is > no traffic from the migration destination guest (server-side) to the > migration source guest (client-side), the destination keeps waiting for > pages indefinitely and does not switch to the postcopy-paused state. > This can happen, for example, if the destination QEMU instance is > started with '-S' command line option and the machine is not started yet > or if the machine is idle and produces no new page faults for > not-yet-migrated pages. > > This patch introduces a new inet socket parameter on platforms where > TCP_KEEPIDLE is defined and passes the configured value to the > TCP_KEEPIDLE socket option when a client-side or server-side socket is > created. > > The default value is 0, which means system configuration is used, and no > custom value is set. > > Signed-off-by: Juraj Marcin<jmarcin@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> -- Best regards, Vladimir ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/2] utils/qemu-sockets: Introduce keep-alive-idle-period inet socket option 2025-03-19 16:36 ` [PATCH v2 2/2] utils/qemu-sockets: Introduce keep-alive-idle-period inet socket option Juraj Marcin 2025-03-20 8:00 ` Vladimir Sementsov-Ogievskiy @ 2025-03-24 11:12 ` Daniel P. Berrangé 2025-03-24 14:08 ` Juraj Marcin 1 sibling, 1 reply; 9+ messages in thread From: Daniel P. Berrangé @ 2025-03-24 11:12 UTC (permalink / raw) To: Juraj Marcin; +Cc: qemu-devel, vsementsov, Paolo Bonzini On Wed, Mar 19, 2025 at 05:36:20PM +0100, Juraj Marcin wrote: > From: Juraj Marcin <jmarcin@redhat.com> > > The default idle period for TCP connection could be even 2 hours. > However, in some cases, the application needs to be aware of a > connection issue much sooner. > > This is the case, for example, for postcopy live migration. If there is > no traffic from the migration destination guest (server-side) to the > migration source guest (client-side), the destination keeps waiting for > pages indefinitely and does not switch to the postcopy-paused state. > This can happen, for example, if the destination QEMU instance is > started with '-S' command line option and the machine is not started yet > or if the machine is idle and produces no new page faults for > not-yet-migrated pages. > > This patch introduces a new inet socket parameter on platforms where > TCP_KEEPIDLE is defined and passes the configured value to the > TCP_KEEPIDLE socket option when a client-side or server-side socket is > created. > > The default value is 0, which means system configuration is used, and no > custom value is set. > > Signed-off-by: Juraj Marcin <jmarcin@redhat.com> > --- > io/dns-resolver.c | 4 ++++ > meson.build | 2 ++ > qapi/sockets.json | 5 +++++ > util/qemu-sockets.c | 29 +++++++++++++++++++++++++++++ > 4 files changed, 40 insertions(+) > > diff --git a/io/dns-resolver.c b/io/dns-resolver.c > index ee7403b65b..03c59673f0 100644 > --- a/io/dns-resolver.c > +++ b/io/dns-resolver.c > @@ -128,6 +128,10 @@ static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver, > #endif > .has_keep_alive = iaddr->has_keep_alive, > .keep_alive = iaddr->keep_alive, > +#ifdef HAVE_TCP_KEEPIDLE > + .has_keep_alive_idle_period = iaddr->has_keep_alive_idle_period, > + .keep_alive_idle_period = iaddr->keep_alive_idle_period, > +#endif > }; > > (*addrs)[i] = newaddr; I feel like this code is somewhat fragile. It is probably best to add a commit that refactors it to do a struct copy, and then override the few fields that need to be different. newaddr->u.inet = iaddr; newaddr->u.inet.host = g_strdup(uaddr); ... that way we don't risk forgetting to copy fields as fixed in the previous commit > diff --git a/meson.build b/meson.build > index 41f68d3806..e3440b09c8 100644 > --- a/meson.build > +++ b/meson.build > @@ -2734,6 +2734,8 @@ if linux_io_uring.found() > config_host_data.set('HAVE_IO_URING_PREP_WRITEV2', > cc.has_header_symbol('liburing.h', 'io_uring_prep_writev2')) > endif > +config_host_data.set('HAVE_TCP_KEEPIDLE', > + cc.has_header_symbol('netinet/tcp.h', 'TCP_KEEPIDLE')) > > # has_member > config_host_data.set('HAVE_SIGEV_NOTIFY_THREAD_ID', > diff --git a/qapi/sockets.json b/qapi/sockets.json > index eb50f64e3a..ddd82b1e66 100644 > --- a/qapi/sockets.json > +++ b/qapi/sockets.json > @@ -59,6 +59,10 @@ > # @keep-alive: enable keep-alive when connecting to/listening on this socket. > # (Since 4.2, not supported for listening sockets until 10.0) > # > +# @keep-alive-idle-period: time in seconds the connection needs to be idle > +# before sending a keepalive packet. Only supported for TCP sockets on > +# systems where TCP_KEEPIDLE socket option is defined. (Since 10.0) There are three tunables for keepalive on TCP sockets: TCP_KEEPCNT (since Linux 2.4) The maximum number of keepalive probes TCP should send before dropping the connection. This option should not be used in code intended to be portable. TCP_KEEPIDLE (since Linux 2.4) The time (in seconds) the connection needs to remain idle before TCP starts sending keepalive probes, if the socket option SO_KEEPALIVE has been set on this socket. This option should not be used in code intended to be portable. TCP_KEEPINTVL (since Linux 2.4) The time (in seconds) between individual keepalive probes. This option should not be used in code intended to be portable. Shouldn't we be supporting all of these, rather than just a subset ? > +# > # @mptcp: enable multi-path TCP. (Since 6.1) > # > # Since: 1.3 > @@ -71,6 +75,7 @@ > '*ipv4': 'bool', > '*ipv6': 'bool', > '*keep-alive': 'bool', > + '*keep-alive-idle-period': { 'type': 'uint32', 'if': 'HAVE_TCP_KEEPIDLE' }, > '*mptcp': { 'type': 'bool', 'if': 'HAVE_IPPROTO_MPTCP' } } } > > ## > @@ -697,6 +710,22 @@ int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) > } > addr->has_keep_alive = true; > } > +#ifdef HAVE_TCP_KEEPIDLE > + begin = strstr(optstr, ",keep-alive-idle-period="); A bit too verbose - make it just 'keep-alive-idle=' > + if (begin) { > + begin += strlen(",keep-alive-idle-period="); > + if (sscanf(begin, "%" PRIu32 "%n", &addr->keep_alive_idle_period, &pos) != 1 || > + (begin[pos] != '\0' && begin[pos] != ',')) { > + error_setg(errp, "error parsing keep-alive-idle-period argument"); > + return -1; > + } > + if (addr->keep_alive_idle_period > INT_MAX) { > + error_setg(errp, "keep-alive-idle-period value is too large"); > + return -1; > + } > + addr->has_keep_alive_idle_period = true; > + } > +#endif > #ifdef HAVE_IPPROTO_MPTCP > begin = strstr(optstr, ",mptcp"); > if (begin) { > -- > 2.48.1 > With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :| ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/2] utils/qemu-sockets: Introduce keep-alive-idle-period inet socket option 2025-03-24 11:12 ` Daniel P. Berrangé @ 2025-03-24 14:08 ` Juraj Marcin 0 siblings, 0 replies; 9+ messages in thread From: Juraj Marcin @ 2025-03-24 14:08 UTC (permalink / raw) To: Daniel P. Berrangé; +Cc: qemu-devel, vsementsov, Paolo Bonzini On 2025-03-24 11:12, Daniel P. Berrangé wrote: > On Wed, Mar 19, 2025 at 05:36:20PM +0100, Juraj Marcin wrote: > > From: Juraj Marcin <jmarcin@redhat.com> > > > > The default idle period for TCP connection could be even 2 hours. > > However, in some cases, the application needs to be aware of a > > connection issue much sooner. > > > > This is the case, for example, for postcopy live migration. If there is > > no traffic from the migration destination guest (server-side) to the > > migration source guest (client-side), the destination keeps waiting for > > pages indefinitely and does not switch to the postcopy-paused state. > > This can happen, for example, if the destination QEMU instance is > > started with '-S' command line option and the machine is not started yet > > or if the machine is idle and produces no new page faults for > > not-yet-migrated pages. > > > > This patch introduces a new inet socket parameter on platforms where > > TCP_KEEPIDLE is defined and passes the configured value to the > > TCP_KEEPIDLE socket option when a client-side or server-side socket is > > created. > > > > The default value is 0, which means system configuration is used, and no > > custom value is set. > > > > Signed-off-by: Juraj Marcin <jmarcin@redhat.com> > > --- > > io/dns-resolver.c | 4 ++++ > > meson.build | 2 ++ > > qapi/sockets.json | 5 +++++ > > util/qemu-sockets.c | 29 +++++++++++++++++++++++++++++ > > 4 files changed, 40 insertions(+) > > > > diff --git a/io/dns-resolver.c b/io/dns-resolver.c > > index ee7403b65b..03c59673f0 100644 > > --- a/io/dns-resolver.c > > +++ b/io/dns-resolver.c > > @@ -128,6 +128,10 @@ static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver, > > #endif > > .has_keep_alive = iaddr->has_keep_alive, > > .keep_alive = iaddr->keep_alive, > > +#ifdef HAVE_TCP_KEEPIDLE > > + .has_keep_alive_idle_period = iaddr->has_keep_alive_idle_period, > > + .keep_alive_idle_period = iaddr->keep_alive_idle_period, > > +#endif > > }; > > > > (*addrs)[i] = newaddr; > > I feel like this code is somewhat fragile. It is probably best to add a > commit that refactors it to do a struct copy, and then override the few > fields that need to be different. > > newaddr->u.inet = iaddr; > newaddr->u.inet.host = g_strdup(uaddr); > ... > > that way we don't risk forgetting to copy fields as fixed in the previous > commit > > > diff --git a/meson.build b/meson.build > > index 41f68d3806..e3440b09c8 100644 > > --- a/meson.build > > +++ b/meson.build > > @@ -2734,6 +2734,8 @@ if linux_io_uring.found() > > config_host_data.set('HAVE_IO_URING_PREP_WRITEV2', > > cc.has_header_symbol('liburing.h', 'io_uring_prep_writev2')) > > endif > > +config_host_data.set('HAVE_TCP_KEEPIDLE', > > + cc.has_header_symbol('netinet/tcp.h', 'TCP_KEEPIDLE')) > > > > # has_member > > config_host_data.set('HAVE_SIGEV_NOTIFY_THREAD_ID', > > diff --git a/qapi/sockets.json b/qapi/sockets.json > > index eb50f64e3a..ddd82b1e66 100644 > > --- a/qapi/sockets.json > > +++ b/qapi/sockets.json > > @@ -59,6 +59,10 @@ > > # @keep-alive: enable keep-alive when connecting to/listening on this socket. > > # (Since 4.2, not supported for listening sockets until 10.0) > > # > > +# @keep-alive-idle-period: time in seconds the connection needs to be idle > > +# before sending a keepalive packet. Only supported for TCP sockets on > > +# systems where TCP_KEEPIDLE socket option is defined. (Since 10.0) > > There are three tunables for keepalive on TCP sockets: > > TCP_KEEPCNT (since Linux 2.4) > The maximum number of keepalive probes TCP should send before > dropping the connection. This option should not be used in > code intended to be portable. > > TCP_KEEPIDLE (since Linux 2.4) > The time (in seconds) the connection needs to remain idle > before TCP starts sending keepalive probes, if the socket > option SO_KEEPALIVE has been set on this socket. This > option should not be used in code intended to be portable. > > TCP_KEEPINTVL (since Linux 2.4) > The time (in seconds) between individual keepalive probes. > This option should not be used in code intended to be portable. > > Shouldn't we be supporting all of these, rather than just a subset ? They were not strictly necessary for the Live Migration use case, but I can also include them in the next version. Thank you for your feedback, also for the first patch. Best regards, Juraj Marcin > > > +# > > # @mptcp: enable multi-path TCP. (Since 6.1) > > # > > # Since: 1.3 > > @@ -71,6 +75,7 @@ > > '*ipv4': 'bool', > > '*ipv6': 'bool', > > '*keep-alive': 'bool', > > + '*keep-alive-idle-period': { 'type': 'uint32', 'if': 'HAVE_TCP_KEEPIDLE' }, > > '*mptcp': { 'type': 'bool', 'if': 'HAVE_IPPROTO_MPTCP' } } } > > > > ## > > > @@ -697,6 +710,22 @@ int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) > > } > > addr->has_keep_alive = true; > > } > > +#ifdef HAVE_TCP_KEEPIDLE > > + begin = strstr(optstr, ",keep-alive-idle-period="); > > A bit too verbose - make it just 'keep-alive-idle=' > > > + if (begin) { > > + begin += strlen(",keep-alive-idle-period="); > > + if (sscanf(begin, "%" PRIu32 "%n", &addr->keep_alive_idle_period, &pos) != 1 || > > + (begin[pos] != '\0' && begin[pos] != ',')) { > > + error_setg(errp, "error parsing keep-alive-idle-period argument"); > > + return -1; > > + } > > + if (addr->keep_alive_idle_period > INT_MAX) { > > + error_setg(errp, "keep-alive-idle-period value is too large"); > > + return -1; > > + } > > + addr->has_keep_alive_idle_period = true; > > + } > > +#endif > > #ifdef HAVE_IPPROTO_MPTCP > > begin = strstr(optstr, ",mptcp"); > > if (begin) { > > -- > > 2.48.1 > > > > With regards, > Daniel > -- > |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| > |: https://libvirt.org -o- https://fstop138.berrange.com :| > |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :| > ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-03-26 12:41 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-03-19 16:36 [PATCH v2 0/2] util/qemu-sockets: Introduce configurable TCP keep-alive idle period Juraj Marcin 2025-03-19 16:36 ` [PATCH v2 1/2] util/qemu-sockets: Add support for keep-alive flag to passive sockets Juraj Marcin 2025-03-20 7:51 ` Vladimir Sementsov-Ogievskiy 2025-03-24 10:30 ` Daniel P. Berrangé 2025-03-26 12:40 ` Juraj Marcin 2025-03-19 16:36 ` [PATCH v2 2/2] utils/qemu-sockets: Introduce keep-alive-idle-period inet socket option Juraj Marcin 2025-03-20 8:00 ` Vladimir Sementsov-Ogievskiy 2025-03-24 11:12 ` Daniel P. Berrangé 2025-03-24 14:08 ` Juraj Marcin
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).