When a user chooses to force shutdown QEMU by pressing ^C or sending a SIGINT otherwise, we want to shutdown as soon as possible, and entering a blocking read which happens in the main thread seems like the opposite of that.
This may seem like a rare case, but it is actually not when using vhost-user devices, which usually have the control plane working via UNIX sockets. The way the code is currently written, all vhost-user devices are serviced in the main thread and thus block each other, as well as other things that happen in the QEMU's main thread, including QMP, and even network devices that are not vhost-net. In case the vhost-user backend servicing a device decides to hang for whatever reason, any control plane request in QEMU will also hang the main loop until the backend either dies or ends up replying. Ideally the vhost-user handling code should be rewritten to work asynchronously, or to support io-threads or similar, but that seems like a giant undertaking and we would like to at least be able to make QEMU shutdown no matter if a vhost-user backend is currently able to service the control plane or not. Luckily for us, SIGINT or similar causes the kernel to cancel (almost) all blocking syscalls with EINTR, which we can utilize to check whether a shutdown was requested while we were blocked in the syscall, which is what this commit does. The check is performed even on the first attempt, not only retries after EINTR. This is intentional to avoid race conditions where QEMU may decide to perform a control plane request before the shutdown event is checked for thus forcing the user to send SIGINT at least 2 times. Signed-off-by: Daniil Tatianin <d-tatia...@yandex-team.ru> --- io/channel-socket.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/io/channel-socket.c b/io/channel-socket.c index 3b7ca924ff..5be01029a5 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -26,10 +26,20 @@ #include "io/channel-watch.h" #include "trace.h" #include "qapi/clone-visitor.h" +#include "sysemu/runstate.h" #ifdef CONFIG_LINUX #include <linux/errqueue.h> #include <sys/socket.h> +/* + * This function is not available when io links against qemu-img etc., + * in this case just pretend it always returns false. + */ +__attribute__((weak)) bool qemu_force_shutdown_requested(void) +{ + return false; +} + #if (defined(MSG_ZEROCOPY) && defined(SO_ZEROCOPY)) #define QEMU_MSG_ZEROCOPY #endif @@ -541,6 +551,12 @@ static ssize_t qio_channel_socket_readv(QIOChannel *ioc, } retry: + if (qemu_force_shutdown_requested()) { + error_setg_errno(errp, ECANCELED, + "Socket read aborted due to force shutdown"); + return -1; + } + ret = recvmsg(sioc->fd, &msg, sflags); if (ret < 0) { if (errno == EAGAIN) { -- 2.34.1