virtio-devices: vhost-user: Use TimerFd connect_vhost_user

Replace the use of sleeps with a TimerFd. Initially this is functionally
equivalent but it can be extended to also handle other events.

Signed-off-by: Rob Bradford <rbradford@meta.com>
This commit is contained in:
Rob Bradford
2026-05-09 21:28:24 +01:00
parent f67d0569b4
commit 0f61655743
3 changed files with 62 additions and 13 deletions

View File

@@ -198,6 +198,8 @@ fn virtio_vhost_fs_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
(libc::SYS_sendmsg, vec![]),
(libc::SYS_sendto, vec![]),
(libc::SYS_socket, vec![]),
(libc::SYS_timerfd_create, vec![]),
(libc::SYS_timerfd_settime, vec![]),
]
}
@@ -212,6 +214,8 @@ fn virtio_generic_vhost_user_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
(libc::SYS_sendmsg, vec![]),
(libc::SYS_sendto, vec![]),
(libc::SYS_socket, vec![]),
(libc::SYS_timerfd_create, vec![]),
(libc::SYS_timerfd_settime, vec![]),
]
}
@@ -232,6 +236,8 @@ fn virtio_vhost_net_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
(libc::SYS_sendmsg, vec![]),
(libc::SYS_sendto, vec![]),
(libc::SYS_socket, vec![]),
(libc::SYS_timerfd_create, vec![]),
(libc::SYS_timerfd_settime, vec![]),
#[cfg(target_arch = "x86_64")]
(libc::SYS_unlink, vec![]),
#[cfg(target_arch = "aarch64")]
@@ -247,6 +253,8 @@ fn virtio_vhost_block_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
(libc::SYS_recvmsg, vec![]),
(libc::SYS_sendmsg, vec![]),
(libc::SYS_socket, vec![]),
(libc::SYS_timerfd_create, vec![]),
(libc::SYS_timerfd_settime, vec![]),
]
}

View File

@@ -72,7 +72,7 @@ pub enum Error {
#[error("Failed to open vhost device")]
VhostUserOpen(#[source] VhostError),
#[error("Connection to socket failed")]
VhostUserConnect,
VhostUserConnect(#[source] VhostError),
#[error("Get features failed")]
VhostUserGetFeatures(#[source] VhostError),
#[error("Get queue max number failed")]
@@ -159,6 +159,18 @@ pub enum Error {
VringBasesCountMismatch(usize, usize),
#[error("Backend state and vring bases must both be present or both be absent")]
InconsistentBackendState,
#[error("Failed to create timerfd")]
TimerFdCreate(#[source] io::Error),
#[error("Failed to arm timerfd")]
TimerFdArm(#[source] io::Error),
#[error("Failed waiting on timerfd")]
TimerFdWait(#[source] io::Error),
#[error("Failed to create epoll instance")]
EpollCreate(#[source] io::Error),
#[error("Failed to add fd to epoll")]
EpollCtl(#[source] io::Error),
#[error("Failed waiting on epoll")]
EpollWait(#[source] io::Error),
}
type Result<T> = std::result::Result<T, Error>;

View File

@@ -8,7 +8,6 @@ use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd};
use std::os::unix::net::{UnixListener, UnixStream};
use std::sync::Arc;
use std::sync::atomic::Ordering;
use std::thread::sleep;
use std::time::{Duration, Instant};
use log::{error, info};
@@ -26,7 +25,9 @@ use virtio_queue::{Queue, QueueT};
use vm_memory::guest_memory::Error as MmapError;
use vm_memory::{Address, FileOffset, GuestAddress, GuestMemory, GuestMemoryRegion};
use vm_migration::protocol::MemoryRangeTable;
use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet};
use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::timerfd::TimerFd;
use super::{Error, Result, VhostUserState};
use crate::vhost_user::Inflight;
@@ -402,10 +403,28 @@ impl VhostUserHandle {
queue_indexes: Vec::new(),
})
} else {
let now = Instant::now();
const RETRY_INTERVAL: Duration = Duration::from_millis(100);
const CONNECT_TIMEOUT: Duration = Duration::from_secs(60);
const TIMER_EVENT: u64 = 0;
// Retry connecting for a full minute
let err = loop {
let mut retry_timer = TimerFd::new().map_err(|e| Error::TimerFdCreate(e.into()))?;
retry_timer
.reset(RETRY_INTERVAL, Some(RETRY_INTERVAL))
.map_err(|e| Error::TimerFdArm(e.into()))?;
let epoll = Epoll::new().map_err(Error::EpollCreate)?;
epoll
.ctl(
ControlOperation::Add,
retry_timer.as_raw_fd(),
EpollEvent::new(EventSet::IN, TIMER_EVENT),
)
.map_err(Error::EpollCtl)?;
let start = Instant::now();
let mut events = [EpollEvent::default(); 1];
loop {
let err = match Frontend::connect(socket_path, num_queues) {
Ok(m) => {
return Ok(VhostUserHandle {
@@ -421,17 +440,27 @@ impl VhostUserHandle {
}
Err(e) => e,
};
sleep(Duration::from_millis(100));
if now.elapsed().as_secs() >= 60 {
break err;
if start.elapsed() >= CONNECT_TIMEOUT {
error!(
"Failed connecting the backend after trying for 1 minute for socket {socket_path}: {err:?}"
);
return Err(Error::VhostUserConnect(err));
}
};
error!(
"Failed connecting the backend after trying for 1 minute for socket {socket_path}: {err:?}"
);
Err(Error::VhostUserConnect)
loop {
match epoll.wait(-1, &mut events) {
Ok(_) => break,
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
Err(e) => return Err(Error::EpollWait(e)),
}
}
// Drain the timerfd so it stops signaling.
retry_timer
.wait()
.map_err(|e| Error::TimerFdWait(e.into()))?;
}
}
}