From ef91fc64e577cb7621ebeef9e8695ad3279c5e38 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Sun, 15 Mar 2026 06:35:43 -0700 Subject: [PATCH] virtio-devices: vhost_user: Trigger interrupts in guest on resume Trigger the interrupts in the guest for the virtio device queues behind the vhost-user devices when resuming. This avoids a situation where interrupts from the backend get lost when they are dispatched from the backend when then guest is paused leading to the guest/backend effectively waiting for each other to move forward. This is more reproducible with longer durations between pause and resume as there is more opportunity for the backend to completely process it's queue and fire all the interrupts. It's perfectly safe and allowed by the virtio spec to generate these interrupts and the performance impact is negligible and is a safe way to ensure forward progress after a resume. See: #7850 Signed-off-by: Rob Bradford --- virtio-devices/src/vhost_user/mod.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/virtio-devices/src/vhost_user/mod.rs b/virtio-devices/src/vhost_user/mod.rs index 158da3d80..0dad19ace 100644 --- a/virtio-devices/src/vhost_user/mod.rs +++ b/virtio-devices/src/vhost_user/mod.rs @@ -302,6 +302,7 @@ pub struct VhostUserCommon { pub vu_num_queues: usize, pub migration_started: bool, pub server: bool, + pub interrupt_cb: Option>, } impl VhostUserCommon { @@ -345,6 +346,8 @@ impl VhostUserCommon { ) .map_err(ActivateError::VhostUserSetup)?; + self.interrupt_cb = Some(interrupt_cb.clone()); + Ok(VhostUserEpollHandler { vu: vu.clone(), mem, @@ -425,10 +428,16 @@ impl VhostUserCommon { if let Some(vu) = &self.vu { vu.lock().unwrap().resume_vhost_user().map_err(|e| { MigratableError::Resume(anyhow!("Error resuming vhost-user backend: {e:?}")) - }) - } else { - Ok(()) + })?; } + if let Some(interrupt_cb) = &self.interrupt_cb { + for i in 0..self.vu_num_queues { + interrupt_cb + .trigger(crate::VirtioInterruptType::Queue(i as u16)) + .ok(); + } + } + Ok(()) } pub fn snapshot<'a, T>(&mut self, state: &T) -> std::result::Result