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(())