From d573f1bf9619419feceef4bee3a4a18b735b71af Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Sun, 26 Apr 2026 13:11:47 +0100 Subject: [PATCH] virtio-devices: Make reset() best-effort on backend failures The virtio specification treats reset as the recovery operation and so must take the device back to a fresh state, and the driver waits for the status read-back to converge before continuing. There is no defined way for the device to report a reset failure to the driver. Previously the implementations of reset() would return early and not complete all their cleanup leaving them in an inconsistent state. Now log errors and continue through the execution. Signed-off-by: Rob Bradford Assisted-by: Claude:claude-opus-4-7 --- virtio-devices/src/device.rs | 10 +++++++--- virtio-devices/src/vdpa.rs | 3 ++- virtio-devices/src/vhost_user/mod.rs | 11 +++++++---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/virtio-devices/src/device.rs b/virtio-devices/src/device.rs index 89e1ee2ea..84f064259 100644 --- a/virtio-devices/src/device.rs +++ b/virtio-devices/src/device.rs @@ -293,9 +293,13 @@ impl VirtioCommon { pub fn reset(&mut self) -> Option> { self.queue_evts.clear(); - // We first must resume the virtio thread if it was paused. - if self.pause_evt.take().is_some() { - self.resume().ok()?; + // Resume the virtio thread if it was paused. Reset must always + // converge to fresh state, so a resume failure is logged but doesn't + // skip the rest of the teardown. + if self.pause_evt.take().is_some() + && let Err(e) = self.resume() + { + error!("Failed to resume paused device during reset: {e:?}"); } if let Some(kill_evt) = self.kill_evt.take() { diff --git a/virtio-devices/src/vdpa.rs b/virtio-devices/src/vdpa.rs index f9bf7a39d..e1cf48d9a 100644 --- a/virtio-devices/src/vdpa.rs +++ b/virtio-devices/src/vdpa.rs @@ -451,9 +451,10 @@ impl VirtioDevice for Vdpa { } fn reset(&mut self) -> Option> { + // Backend reset failures are logged but don't skip local cleanup: + // reset must converge to fresh state regardless of backend state. if let Err(e) = self.reset_vdpa() { error!("Failed to reset vhost-vdpa: {e:?}"); - return None; } event!("vdpa", "reset", "id", &self.id); diff --git a/virtio-devices/src/vhost_user/mod.rs b/virtio-devices/src/vhost_user/mod.rs index 9f2eea423..07c8d4555 100644 --- a/virtio-devices/src/vhost_user/mod.rs +++ b/virtio-devices/src/vhost_user/mod.rs @@ -429,9 +429,13 @@ impl VhostUserCommon { } pub fn reset(&mut self, id: &str) -> Option> { - // We first must resume the virtio thread if it was paused. - if self.virtio_common.pause_evt.take().is_some() { - self.virtio_common.resume().ok()?; + // Resume the virtio thread if it was paused. Reset must always + // converge to fresh state, so backend resume / reset failures are + // logged but don't skip the rest of the teardown. + if self.virtio_common.pause_evt.take().is_some() + && let Err(e) = self.virtio_common.resume() + { + error!("Failed to resume paused device during reset: {e:?}"); } if let Some(vu) = &self.vu @@ -441,7 +445,6 @@ impl VhostUserCommon { "Failed to reset vhost-user daemon for socket {}: {e:?}", self.socket_path ); - return None; } if let Some(kill_evt) = self.virtio_common.kill_evt.take() {