virtio-devices: Abort vhost-user client connect on kill_evt

Client connect retried for a full minute on every failure and did not
watch kill_evt for early exit, unlike reconnect, so teardown and
permanent connect errors both stalled for CONNECT_TIMEOUT.

Update the client path to watch kill_evt for early abort, fail
non-retryable errors immediately, and surface timeout with a dedicated
error.

Partially Fixes #8052

Signed-off-by: Chinmoy <daschinmoyy21@gmail.com>
Assisted-by: GLM 5.2
This commit is contained in:
Chinmoy
2026-07-05 00:18:56 +05:30
committed by Rob Bradford
parent 9474c07526
commit c070d5dfa4
6 changed files with 40 additions and 19 deletions
+1 -1
View File
@@ -63,7 +63,7 @@ impl Blk {
&vu_cfg.socket, &vu_cfg.socket,
num_queues as u64, num_queues as u64,
false, false,
None, &exit_evt,
)?; )?;
let ( let (
+1 -1
View File
@@ -90,7 +90,7 @@ impl Fs {
// Connect to the vhost-user socket. // Connect to the vhost-user socket.
let mut vu = let mut vu =
VhostUserHandle::connect_vhost_user(false, path, num_queues as u64, false, None)?; VhostUserHandle::connect_vhost_user(false, path, num_queues as u64, false, &exit_evt)?;
let ( let (
avail_features, avail_features,
@@ -77,7 +77,7 @@ impl GenericVhostUser {
// Connect to the vhost-user socket. // Connect to the vhost-user socket.
let mut vu = let mut vu =
VhostUserHandle::connect_vhost_user(false, path, num_queues as u64, false, None)?; VhostUserHandle::connect_vhost_user(false, path, num_queues as u64, false, &exit_evt)?;
let ( let (
avail_features, avail_features,
+3 -1
View File
@@ -179,6 +179,8 @@ pub enum Error {
EpollWait(#[source] io::Error), EpollWait(#[source] io::Error),
#[error("Aborted vhost-user connect: kill event received")] #[error("Aborted vhost-user connect: kill event received")]
ConnectKilled, ConnectKilled,
#[error("Timed out waiting for vhost-user connection")]
VhostUserConnectTimeout,
} }
type Result<T> = result::Result<T, Error>; type Result<T> = result::Result<T, Error>;
@@ -329,7 +331,7 @@ impl<S: VhostUserFrontendReqHandler> VhostUserEpollHandler<S> {
&self.socket_path, &self.socket_path,
self.queues.len() as u64, self.queues.len() as u64,
true, true,
Some(&self.kill_evt), &self.kill_evt,
) { ) {
Ok(vu) => vu, Ok(vu) => vu,
// Kill event fired during the connect retry loop; abandon the // Kill event fired during the connect retry loop; abandon the
+1 -1
View File
@@ -93,7 +93,7 @@ impl Net {
&vu_cfg.socket, &vu_cfg.socket,
num_queues as u64, num_queues as u64,
false, false,
None, &exit_evt,
)?; )?;
let ( let (
+33 -14
View File
@@ -17,9 +17,13 @@ use vhost::vhost_user::message::{
VhostUserProtocolFeatures, VhostUserVirtioFeatures, VhostUserProtocolFeatures, VhostUserVirtioFeatures,
}; };
use vhost::vhost_user::{ use vhost::vhost_user::{
Frontend, FrontendReqHandler, VhostUserFrontend, VhostUserFrontendReqHandler, Error as VhostUserError, Frontend, FrontendReqHandler, VhostUserFrontend,
VhostUserFrontendReqHandler,
};
use vhost::{
Error as VhostError, VhostBackend, VhostUserDirtyLogRegion, VhostUserMemoryRegionInfo,
VringConfigData,
}; };
use vhost::{VhostBackend, VhostUserDirtyLogRegion, VhostUserMemoryRegionInfo, VringConfigData};
use virtio_queue::desc::RawDescriptor; use virtio_queue::desc::RawDescriptor;
use virtio_queue::{Queue, QueueT}; use virtio_queue::{Queue, QueueT};
use vm_memory::guest_memory::Error as MmapError; use vm_memory::guest_memory::Error as MmapError;
@@ -381,7 +385,7 @@ impl VhostUserHandle {
socket_path: &str, socket_path: &str,
num_queues: u64, num_queues: u64,
unlink_socket: bool, unlink_socket: bool,
kill_evt: Option<&EventFd>, kill_evt: &EventFd,
) -> Result<Self> { ) -> Result<Self> {
if server { if server {
if unlink_socket { if unlink_socket {
@@ -427,15 +431,13 @@ impl VhostUserHandle {
) )
.map_err(Error::EpollCtl)?; .map_err(Error::EpollCtl)?;
if let Some(kill_evt) = kill_evt { epoll
epoll .ctl(
.ctl( ControlOperation::Add,
ControlOperation::Add, kill_evt.as_raw_fd(),
kill_evt.as_raw_fd(), EpollEvent::new(EventSet::IN, ConnectEvent::Kill as u64),
EpollEvent::new(EventSet::IN, ConnectEvent::Kill as u64), )
) .map_err(Error::EpollCtl)?;
.map_err(Error::EpollCtl)?;
}
let start = Instant::now(); let start = Instant::now();
let mut events = [EpollEvent::default(); 1]; let mut events = [EpollEvent::default(); 1];
@@ -457,13 +459,30 @@ impl VhostUserHandle {
Err(e) => e, Err(e) => e,
}; };
if start.elapsed() >= CONNECT_TIMEOUT { let retryable = match &err {
VhostError::VhostUserProtocol(VhostUserError::SocketConnect(io_err)) => {
matches!(
io_err.kind(),
io::ErrorKind::NotFound
| io::ErrorKind::Interrupted
| io::ErrorKind::ConnectionRefused
)
}
_ => false,
};
if !retryable {
error!( error!(
"Failed connecting the backend after trying for 1 minute for socket {socket_path}: {err:?}" "Failed connecting to vhost-user backend for socket {socket_path}: {err:?}"
); );
return Err(Error::VhostUserConnect(err)); return Err(Error::VhostUserConnect(err));
} }
if start.elapsed() >= CONNECT_TIMEOUT {
error!("Timed out waiting for vhost-user connection on socket {socket_path}");
return Err(Error::VhostUserConnectTimeout);
}
loop { loop {
match epoll.wait(-1, &mut events) { match epoll.wait(-1, &mut events) {
Ok(_) => break, Ok(_) => break,