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 <rbradford@meta.com>
This commit is contained in:
Rob Bradford
2026-05-09 21:50:12 +01:00
parent 0f61655743
commit 74bf0b4a55
6 changed files with 69 additions and 20 deletions

View File

@@ -59,8 +59,13 @@ impl Blk {
) -> Result<Blk> {
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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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<T> = std::result::Result<T, Error>;
@@ -236,18 +238,25 @@ impl<S: VhostUserFrontendReqHandler> VhostUserEpollHandler<S> {
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)?;

View File

@@ -69,8 +69,13 @@ impl Net {
) -> Result<Net> {
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,

View File

@@ -381,6 +381,7 @@ impl VhostUserHandle {
socket_path: &str,
num_queues: u64,
unlink_socket: bool,
kill_evt: Option<&EventFd>,
) -> Result<Self> {
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!(),
}
}
}
}