From 107c5ea6a7b44706413b846f80008c0cfd71249c Mon Sep 17 00:00:00 2001 From: kilroy Date: Fri, 9 Jan 2026 09:53:49 +0000 Subject: [PATCH 1/3] Add fix for KVM clock control on resume --- src/vmm/src/logger/metrics.rs | 3 +++ src/vmm/src/vstate/vcpu/x86_64.rs | 12 ++++++++++++ tests/host_tools/fcmetrics.py | 1 + 3 files changed, 16 insertions(+) diff --git a/src/vmm/src/logger/metrics.rs b/src/vmm/src/logger/metrics.rs index 26a755c8f4e..ec264cd1036 100644 --- a/src/vmm/src/logger/metrics.rs +++ b/src/vmm/src/logger/metrics.rs @@ -780,6 +780,8 @@ pub struct VcpuMetrics { pub exit_mmio_write: SharedIncMetric, /// Number of errors during this VCPU's run. pub failures: SharedIncMetric, + /// Number of times that the `KVM_KVMCLOCK_CTRL` ioctl failed. + pub kvmclock_ctrl_fails: SharedIncMetric, /// Provides Min/max/sum for KVM exits handling input IO. pub exit_io_in_agg: LatencyAggregateMetrics, /// Provides Min/max/sum for KVM exits handling output IO. @@ -798,6 +800,7 @@ impl VcpuMetrics { exit_mmio_read: SharedIncMetric::new(), exit_mmio_write: SharedIncMetric::new(), failures: SharedIncMetric::new(), + kvmclock_ctrl_fails: SharedIncMetric::new(), exit_io_in_agg: LatencyAggregateMetrics::new(), exit_io_out_agg: LatencyAggregateMetrics::new(), exit_mmio_read_agg: LatencyAggregateMetrics::new(), diff --git a/src/vmm/src/vstate/vcpu/x86_64.rs b/src/vmm/src/vstate/vcpu/x86_64.rs index d91bfd1cea2..5ea974ef88b 100644 --- a/src/vmm/src/vstate/vcpu/x86_64.rs +++ b/src/vmm/src/vstate/vcpu/x86_64.rs @@ -565,6 +565,15 @@ impl KvmVcpu { self.fd.set_tsc_khz(tsc_freq).map_err(SetTscError) } + /// Calls KVM_KVMCLOCK_CTRL to avoid guest soft lockup watchdog panics on resume. + /// See https://docs.kernel.org/virt/kvm/api.html . + pub fn kvmclock_ctrl(&self) { + if let Err(err) = self.fd.kvmclock_ctrl() { + METRICS.vcpu.kvmclock_ctrl_fails.inc(); + warn!("KVM_KVMCLOCK_CTRL call failed {}", err); + } + } + /// Use provided state to populate KVM internal state. pub fn restore_state(&self, state: &VcpuState) -> Result<(), KvmVcpuError> { // Ordering requirements: @@ -621,6 +630,9 @@ impl KvmVcpu { self.fd .set_vcpu_events(&state.vcpu_events) .map_err(KvmVcpuError::VcpuSetVcpuEvents)?; + + self.kvmclock_ctrl(); + Ok(()) } } diff --git a/tests/host_tools/fcmetrics.py b/tests/host_tools/fcmetrics.py index 53caf4d4adc..5a5fba48f37 100644 --- a/tests/host_tools/fcmetrics.py +++ b/tests/host_tools/fcmetrics.py @@ -233,6 +233,7 @@ def validate_fc_metrics(metrics): "exit_mmio_read", "exit_mmio_write", "failures", + "kvmclock_ctrl_fails", {"exit_io_in_agg": latency_agg_metrics_fields}, {"exit_io_out_agg": latency_agg_metrics_fields}, {"exit_mmio_read_agg": latency_agg_metrics_fields}, From 6a9f30fd386c301b767ea5b61adacfb4bea1f19d Mon Sep 17 00:00:00 2001 From: kilroy Date: Mon, 12 Jan 2026 08:28:27 +0000 Subject: [PATCH 2/3] Exit on partial read/write for iovec --- src/vmm/src/devices/virtio/iovec.rs | 54 ++++++++++++++++------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/src/vmm/src/devices/virtio/iovec.rs b/src/vmm/src/devices/virtio/iovec.rs index 78e4e26aeb5..78a506af5a5 100644 --- a/src/vmm/src/devices/virtio/iovec.rs +++ b/src/vmm/src/devices/virtio/iovec.rs @@ -159,23 +159,26 @@ impl IoVecBuffer { slice = slice.subslice(0, len)?; } - let bytes_read = loop { + match loop { match dst.write_volatile(&slice) { Err(VolatileMemoryError::IOError(err)) - if err.kind() == ErrorKind::Interrupted => - { - continue - } - Ok(bytes_read) => break bytes_read, - Err(volatile_memory_error) => return Err(volatile_memory_error), + if err.kind() == ErrorKind::Interrupted => {} + result => break result, } - }; - total_bytes_read += bytes_read; + } { + Ok(bytes_read) => { + total_bytes_read += bytes_read; - if bytes_read < slice.len() { - break; + if bytes_read < slice.len() { + break; + } + len -= bytes_read; + } + // exit successfully if we previously managed to write some bytes + Err(_) if total_bytes_read > 0 => break, + // this captures the `volatile_memory_error` from the above loop + Err(err) => return Err(err), } - len -= bytes_read; } Ok(total_bytes_read) @@ -299,23 +302,26 @@ impl IoVecBufferMut { slice = slice.subslice(0, len)?; } - let bytes_read = loop { + match loop { match src.read_volatile(&mut slice) { Err(VolatileMemoryError::IOError(err)) - if err.kind() == ErrorKind::Interrupted => - { - continue - } - Ok(bytes_read) => break bytes_read, - Err(volatile_memory_error) => return Err(volatile_memory_error), + if err.kind() == ErrorKind::Interrupted => {} + result => break result, } - }; - total_bytes_read += bytes_read; + } { + Ok(bytes_read) => { + total_bytes_read += bytes_read; - if bytes_read < slice.len() { - break; + if bytes_read < slice.len() { + break; + } + len -= bytes_read; + } + // exit successfully if we previously managed to write some bytes + Err(_) if total_bytes_read > 0 => break, + // this captures the `volatile_memory_error` from the above loop + Err(err) => return Err(err), } - len -= bytes_read; } Ok(total_bytes_read) From 0d86af5c133b1daef182074c7d6f88c5bba748b1 Mon Sep 17 00:00:00 2001 From: kilroy Date: Fri, 16 Jan 2026 08:37:14 +0000 Subject: [PATCH 3/3] Resolve related TODO issue --- src/vmm/src/vstate/vcpu/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vmm/src/vstate/vcpu/mod.rs b/src/vmm/src/vstate/vcpu/mod.rs index 7e5eb50dac2..1cb88623b48 100644 --- a/src/vmm/src/vstate/vcpu/mod.rs +++ b/src/vmm/src/vstate/vcpu/mod.rs @@ -288,8 +288,8 @@ impl Vcpu { .send(VcpuResponse::Paused) .expect("vcpu channel unexpectedly closed"); - // TODO: we should call `KVM_KVMCLOCK_CTRL` here to make sure - // TODO continued: the guest soft lockup watchdog does not panic on Resume. + #[cfg(target_arch = "x86_64")] + self.kvm_vcpu.kvmclock_ctrl(); // Move to 'paused' state. state = StateMachine::next(Self::paused);