Skip to content

openvmm: on KVM, a halted VP with a PV kick waiting restores as running #1386

Open

Description

@tangruize

On KVM, set_activity of a halted activity on a vCPU whose guest left a PV kick waiting (a KVM_HC_KICK_CPU that the target has not consumed) leaves the VP runnable. A release-build restore of a snapshot onto such a VP starts it running where the snapshot had it halted, as after a spurious wakeup; a debug build panics in restore_all's read-back check ("restore state mismatch"). Reaching it takes a KVM partition without a time ABI whose guest issues KVM_HC_KICK_CPU, and a restore onto a VP that is not fresh. MicroVMs are not affected: every microVM has a time ABI, which enables KVM_CAP_ENFORCE_PV_FEATURE_CPUID, and KVM's 0x4000_0000 leaves are filtered from every guest's CPUID, so KVM refuses the kick.

Reproduction

On a KVM partition without a time ABI, let a real-mode guest kick its own vCPU with vmcall (KVM_HC_KICK_CPU of APIC ID 0) and leave the kick unconsumed. Stop the VP and call set_activity with mp_state: Halted. Expected: activity() reads Halted. Actual: it reads Running, in debug and release builds.

Cause

KvmVpStateAccess::set_activity (vmm_core/virt_kvm/src/arch/x86_64/vp_state.rs) issues a single KVM_SET_MP_STATE with the target state:

// vmm_core/virt_kvm/src/arch/x86_64/vp_state.rs
let state = match mp_state {
    vp::MpState::Running => kvm::KVM_MP_STATE_RUNNABLE,
    vp::MpState::WaitForSipi => kvm::KVM_MP_STATE_INIT_RECEIVED,
    vp::MpState::Halted => kvm::KVM_MP_STATE_HALTED,
    vp::MpState::Idle => {
        return Err(KvmError::InvalidState("Hyper-V idle state not supported"));
    }
};
self.kvm().set_mp_state(state)?;

KVM's kvm_set_mp_state clears pv_unhalted only when the new state is RUNNABLE, and KVM_GET_MP_STATE reports a HALTED vCPU with pv_unhalted set as RUNNABLE (arch/x86/kvm/x86.h and x86.c).

Fix

fix.patch makes set_activity set RUNNABLE first whenever the target state is not RUNNABLE. That clears any waiting kick, and the target state follows, so the VP holds the activity set and no kick that the saved state does not hold. On a VP without a waiting kick the extra ioctl changes nothing observable; a VP set to WaitForSipi or Halted loses a kick its guest left waiting. The patch has no test: a regression test needs /dev/kvm and a running guest, and the fix cannot be split into a function that runs without KVM.

Activity

  1. tangruize commented on Nov 8, 2026

    @tangruize
    ContributorAuthor

    Proposed patch

    Patch against OpenVMM 3cf58a5234ed824435825c9f87187f5129219724. Save the contents below as fix.patch and run git apply fix.patch.

    fix.patch
    diff --git a/vmm_core/virt_kvm/src/arch/x86_64/vp_state.rs b/vmm_core/virt_kvm/src/arch/x86_64/vp_state.rs
    index 59dd45b..bb14efc 100644
    --- a/vmm_core/virt_kvm/src/arch/x86_64/vp_state.rs
    +++ b/vmm_core/virt_kvm/src/arch/x86_64/vp_state.rs
    @@ -351,6 +351,13 @@ fn set_activity(&mut self, value: &vp::Activity) -> Result<(), Self::Error> {
                     return Err(KvmError::InvalidState("Hyper-V idle state not supported"));
                 }
             };
    +        // A PV kick waiting in KVM (another vCPU's KVM_HC_KICK_CPU) survives
    +        // KVM_SET_MP_STATE of any state but RUNNABLE, and KVM reports a halted
    +        // vCPU with one as runnable. Clear it first, so the VP holds the
    +        // activity set and no kick the saved state does not hold.
    +        if state != kvm::KVM_MP_STATE_RUNNABLE {
    +            self.kvm().set_mp_state(kvm::KVM_MP_STATE_RUNNABLE)?;
    +        }
             self.kvm().set_mp_state(state)?;
     
             let mut event_flags = 0;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions