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 <rbradford@meta.com>
This commit is contained in:
Rob Bradford
2026-03-15 06:35:43 -07:00
parent ad3179fe11
commit ef91fc64e5

View File

@@ -302,6 +302,7 @@ pub struct VhostUserCommon {
pub vu_num_queues: usize,
pub migration_started: bool,
pub server: bool,
pub interrupt_cb: Option<Arc<dyn VirtioInterrupt>>,
}
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<Snapshot, MigratableError>