From 99ec20ff976b09b4182048854e8abd9435f45ff6 Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Fri, 8 May 2026 17:29:08 -0700 Subject: [PATCH] virtio-devices: vhost_user: skip resume for disconnected backends resume() mirrors pause() for backend communication: it skips the vhost-user backend call when the device is already disconnected, and it marks newly failed resume_vhost_user() calls disconnected only when the classifier identifies transport loss. Signed-off-by: Dylan Reid --- virtio-devices/src/vhost_user/mod.rs | 39 +++++++++++++++++++++++----- vmm/src/device_manager.rs | 8 +++++- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/virtio-devices/src/vhost_user/mod.rs b/virtio-devices/src/vhost_user/mod.rs index a8b3be672..c5581a3f8 100644 --- a/virtio-devices/src/vhost_user/mod.rs +++ b/virtio-devices/src/vhost_user/mod.rs @@ -681,18 +681,45 @@ impl VhostUserCommon { Ok(()) } - pub fn resume(&mut self) -> std::result::Result<(), MigratableError> { - if let Some(vu) = &self.vu { - vu.lock().unwrap().resume_vhost_user().map_err(|e| { - MigratableError::Resume(anyhow!("Error resuming vhost-user backend: {e:?}")) - })?; + fn resume_internal(&mut self) -> std::result::Result<(), MigratableError> { + // Skip the resume_vhost_user call if the backend is disconnected. Process the queue + // interrupts to kick any paused workers. + if self.disconnected.load(Ordering::Relaxed) { + return Err(MigratableError::DeviceDisconnected( + self.socket_path.clone(), + )); } + + if let Some(vu) = &self.vu + && let Err(e) = vu.lock().unwrap().resume_vhost_user() + { + if e.is_transport_lost() { + self.disconnected.store(true, Ordering::Relaxed); + return Err(MigratableError::DeviceDisconnected( + self.socket_path.clone(), + )); + } + + return Err(MigratableError::Resume(anyhow!( + "Error resuming vhost-user backend for socket {}: {e:?}", + self.socket_path + ))); + } + + Ok(()) + } + + pub fn resume(&mut self) -> std::result::Result<(), MigratableError> { + let ret = self.resume_internal(); + + // Always run the interrupt loop so workers don't get stuck. for i in 0..self.vu_num_queues { self.virtio_common .trigger_interrupt(crate::VirtioInterruptType::Queue(i as u16)) .ok(); } - Ok(()) + + ret } pub fn state( diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 69ac8ad99..bca1f50c7 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -5553,7 +5553,13 @@ impl Pausable for DeviceManager { fn resume(&mut self) -> result::Result<(), MigratableError> { for (_, device_node) in self.device_tree.lock().unwrap().iter() { if let Some(migratable) = &device_node.migratable { - migratable.lock().unwrap().resume()?; + match migratable.lock().unwrap().resume() { + Ok(()) => {} + Err(MigratableError::DeviceDisconnected(id)) => { + warn!("Skipping resume for disconnected device {id}"); + } + Err(e) => return Err(e), + } } } Ok(())