From 74bf0b4a55de50468d40482ee86d05d57d3e1fc1 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Sat, 9 May 2026 21:50:12 +0100 Subject: [PATCH] virtio-devices: vhost-user: Abandon reconnection if kill event sent Abandon the reconnection to the vhost-user socket if the kill_evt is fired because e.g. a device removal request has come in during the reconnection. Signed-off-by: Rob Bradford --- virtio-devices/src/vhost_user/blk.rs | 9 ++++- virtio-devices/src/vhost_user/fs.rs | 3 +- .../src/vhost_user/generic_vhost_user.rs | 3 +- virtio-devices/src/vhost_user/mod.rs | 26 +++++++++---- virtio-devices/src/vhost_user/net.rs | 9 ++++- .../src/vhost_user/vu_common_ctrl.rs | 39 ++++++++++++++++--- 6 files changed, 69 insertions(+), 20 deletions(-) diff --git a/virtio-devices/src/vhost_user/blk.rs b/virtio-devices/src/vhost_user/blk.rs index d2ae3ca5b..dbaab8b01 100644 --- a/virtio-devices/src/vhost_user/blk.rs +++ b/virtio-devices/src/vhost_user/blk.rs @@ -59,8 +59,13 @@ impl Blk { ) -> Result { let num_queues = vu_cfg.num_queues; - let mut vu = - VhostUserHandle::connect_vhost_user(false, &vu_cfg.socket, num_queues as u64, false)?; + let mut vu = VhostUserHandle::connect_vhost_user( + false, + &vu_cfg.socket, + num_queues as u64, + false, + None, + )?; let ( avail_features, diff --git a/virtio-devices/src/vhost_user/fs.rs b/virtio-devices/src/vhost_user/fs.rs index 660cbb480..31738a1bb 100644 --- a/virtio-devices/src/vhost_user/fs.rs +++ b/virtio-devices/src/vhost_user/fs.rs @@ -90,7 +90,8 @@ impl Fs { let num_queues = NUM_QUEUE_OFFSET + req_num_queues; // Connect to the vhost-user socket. - let mut vu = VhostUserHandle::connect_vhost_user(false, path, num_queues as u64, false)?; + let mut vu = + VhostUserHandle::connect_vhost_user(false, path, num_queues as u64, false, None)?; let ( avail_features, diff --git a/virtio-devices/src/vhost_user/generic_vhost_user.rs b/virtio-devices/src/vhost_user/generic_vhost_user.rs index 3024b1976..cd53f9269 100644 --- a/virtio-devices/src/vhost_user/generic_vhost_user.rs +++ b/virtio-devices/src/vhost_user/generic_vhost_user.rs @@ -78,7 +78,8 @@ impl GenericVhostUser { let num_queues = request_queue_sizes.len(); // Connect to the vhost-user socket. - let mut vu = VhostUserHandle::connect_vhost_user(false, path, num_queues as u64, false)?; + let mut vu = + VhostUserHandle::connect_vhost_user(false, path, num_queues as u64, false, None)?; let ( avail_features, diff --git a/virtio-devices/src/vhost_user/mod.rs b/virtio-devices/src/vhost_user/mod.rs index d06a0ad9f..7759782fd 100644 --- a/virtio-devices/src/vhost_user/mod.rs +++ b/virtio-devices/src/vhost_user/mod.rs @@ -171,6 +171,8 @@ pub enum Error { EpollCtl(#[source] io::Error), #[error("Failed waiting on epoll")] EpollWait(#[source] io::Error), + #[error("Aborted vhost-user connect: kill event received")] + ConnectKilled, } type Result = std::result::Result; @@ -236,18 +238,25 @@ impl VhostUserEpollHandler { epoll::Events::EPOLLHUP, )?; - let mut vhost_user = VhostUserHandle::connect_vhost_user( + let mut vhost_user = match VhostUserHandle::connect_vhost_user( self.server, &self.socket_path, self.queues.len() as u64, true, - ) - .map_err(|e| { - EpollHelperError::IoError(std::io::Error::other(format!( - "failed connecting vhost-user backend for socket {}: {e:?}", - self.socket_path - ))) - })?; + Some(&self.kill_evt), + ) { + Ok(vu) => vu, + // Kill event fired during the connect retry loop; abandon the + // reconnect attempt. The EpollHelper observes the same kill + // event and will tear down on its next iteration. + Err(Error::ConnectKilled) => return Ok(()), + Err(e) => { + return Err(EpollHelperError::IoError(std::io::Error::other(format!( + "failed connecting vhost-user backend for socket {}: {e:?}", + self.socket_path + )))); + } + }; let queues = self .queues @@ -428,6 +437,7 @@ impl VhostUserCommon { &self.socket_path, self.vu_num_queues as u64, false, + None, )?; vu.set_protocol_features_vhost_user(acked_features, self.acked_protocol_features)?; diff --git a/virtio-devices/src/vhost_user/net.rs b/virtio-devices/src/vhost_user/net.rs index e6e01f8f0..eed7c8284 100644 --- a/virtio-devices/src/vhost_user/net.rs +++ b/virtio-devices/src/vhost_user/net.rs @@ -69,8 +69,13 @@ impl Net { ) -> Result { let mut num_queues = vu_cfg.num_queues; - let mut vu = - VhostUserHandle::connect_vhost_user(server, &vu_cfg.socket, num_queues as u64, false)?; + let mut vu = VhostUserHandle::connect_vhost_user( + server, + &vu_cfg.socket, + num_queues as u64, + false, + None, + )?; let ( avail_features, diff --git a/virtio-devices/src/vhost_user/vu_common_ctrl.rs b/virtio-devices/src/vhost_user/vu_common_ctrl.rs index e8cc050ef..23a37c335 100644 --- a/virtio-devices/src/vhost_user/vu_common_ctrl.rs +++ b/virtio-devices/src/vhost_user/vu_common_ctrl.rs @@ -381,6 +381,7 @@ impl VhostUserHandle { socket_path: &str, num_queues: u64, unlink_socket: bool, + kill_evt: Option<&EventFd>, ) -> Result { if server { if unlink_socket { @@ -405,7 +406,12 @@ impl VhostUserHandle { } else { const RETRY_INTERVAL: Duration = Duration::from_millis(100); const CONNECT_TIMEOUT: Duration = Duration::from_secs(60); - const TIMER_EVENT: u64 = 0; + + #[repr(u64)] + enum ConnectEvent { + Timer = 0, + Kill = 1, + } let mut retry_timer = TimerFd::new().map_err(|e| Error::TimerFdCreate(e.into()))?; retry_timer @@ -417,10 +423,20 @@ impl VhostUserHandle { .ctl( ControlOperation::Add, retry_timer.as_raw_fd(), - EpollEvent::new(EventSet::IN, TIMER_EVENT), + EpollEvent::new(EventSet::IN, ConnectEvent::Timer as u64), ) .map_err(Error::EpollCtl)?; + if let Some(kill_evt) = kill_evt { + epoll + .ctl( + ControlOperation::Add, + kill_evt.as_raw_fd(), + EpollEvent::new(EventSet::IN, ConnectEvent::Kill as u64), + ) + .map_err(Error::EpollCtl)?; + } + let start = Instant::now(); let mut events = [EpollEvent::default(); 1]; @@ -456,10 +472,21 @@ impl VhostUserHandle { } } - // Drain the timerfd so it stops signaling. - retry_timer - .wait() - .map_err(|e| Error::TimerFdWait(e.into()))?; + match events[0].data() { + x if x == ConnectEvent::Kill as u64 => { + info!( + "Aborting vhost-user connect for socket {socket_path}: kill event received" + ); + return Err(Error::ConnectKilled); + } + x if x == ConnectEvent::Timer as u64 => { + // Drain the timerfd to clear the event. + retry_timer + .wait() + .map_err(|e| Error::TimerFdWait(e.into()))?; + } + _ => unreachable!(), + } } } }