From f56bfdaeb7a3283f8129792b5d1ac35bd430b458 Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Fri, 8 May 2026 17:28:56 -0700 Subject: [PATCH] virtio-devices: vhost_user: skip pause for disconnected backends pause() returns DeviceDisconnected without calling into the backend when VhostUserCommon already knows the socket is gone. DeviceManager treats only that sentinel as log-and-continue, so one dead vhost-user device does not abort the whole pause iteration. Signed-off-by: Dylan Reid --- virtio-devices/src/vhost_user/mod.rs | 27 +++++++++++++++++++++------ vmm/src/device_manager.rs | 8 +++++++- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/virtio-devices/src/vhost_user/mod.rs b/virtio-devices/src/vhost_user/mod.rs index 1eee637d4..a8b3be672 100644 --- a/virtio-devices/src/vhost_user/mod.rs +++ b/virtio-devices/src/vhost_user/mod.rs @@ -657,13 +657,28 @@ impl VhostUserCommon { } pub fn pause(&mut self) -> std::result::Result<(), MigratableError> { - if let Some(vu) = &self.vu { - vu.lock().unwrap().pause_vhost_user().map_err(|e| { - MigratableError::Pause(anyhow!("Error pausing vhost-user backend: {e:?}")) - }) - } else { - Ok(()) + 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().pause_vhost_user() + { + if e.is_transport_lost() { + self.disconnected.store(true, Ordering::Relaxed); + return Err(MigratableError::DeviceDisconnected( + self.socket_path.clone(), + )); + } + + return Err(MigratableError::Pause(anyhow!( + "Error pausing vhost-user backend for socket {}: {e:?}", + self.socket_path + ))); + } + Ok(()) } pub fn resume(&mut self) -> std::result::Result<(), MigratableError> { diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 9b9cda598..69ac8ad99 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -5526,7 +5526,13 @@ impl Pausable for DeviceManager { fn pause(&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().pause()?; + match migratable.lock().unwrap().pause() { + Ok(()) => {} + Err(MigratableError::DeviceDisconnected(id)) => { + warn!("Skipping pause for disconnected device {id}"); + } + Err(e) => return Err(e), + } } } // On AArch64, the pause of device manager needs to trigger