From 68691db37bc6b133e0cf068137223daa75dad71a Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 31 Mar 2026 05:54:12 -0700 Subject: [PATCH] virtio-devices: vhost_user: Correctly shutdown epoll thread If the epoll thread is paused, which would be expected as a part of live migration/snapshot-restore unpause the thread so that it can receive the kill event. This mirrors the reset() behaviour of virtio devices. It is important here so as to close the connection with the vhost-user-backend to allow same host and --local migration and since after getting the device state the vhost-user backend should no longer be used. As a result of this change we can do --local and same-host migration with virtio-fs. Signed-off-by: Rob Bradford --- virtio-devices/src/vhost_user/mod.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/virtio-devices/src/vhost_user/mod.rs b/virtio-devices/src/vhost_user/mod.rs index 3ececc663..95e819e18 100644 --- a/virtio-devices/src/vhost_user/mod.rs +++ b/virtio-devices/src/vhost_user/mod.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use std::os::unix::io::AsRawFd; -use std::sync::atomic::AtomicBool; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Barrier, Mutex}; use std::{io, thread}; @@ -425,6 +425,22 @@ impl VhostUserCommon { } pub fn shutdown(&mut self) { + // Signal the epoll thread to exit, unpause it (it may be parked + // if the VM was paused for migration), then wait for it to finish. + // This ensures the thread drops its Arc, fully + // closing the vhost-user socket so the backend can accept a new + // connection from the destination. + if let Some(kill_evt) = self.virtio_common.kill_evt.take() { + let _ = kill_evt.write(1); + } + self.virtio_common.paused.store(false, Ordering::SeqCst); + if let Some(t) = self.epoll_thread.as_ref() { + t.thread().unpark(); + } + if let Some(t) = self.epoll_thread.take() { + let _ = t.join(); + } + // Remove socket path if needed if self.server { let _ = std::fs::remove_file(&self.socket_path);