Kernel KVM virtualization development
 help / color / mirror / Atom feed
From: Sean Christopherson <seanjc@google.com>
To: Chao Gao <chao.gao@intel.com>
Cc: Keqiang Duan <duankeqiangcym@gmail.com>,
	kvm@vger.kernel.org, pbonzini@redhat.com,
	 linux-kernel@vger.kernel.org, stable@vger.kernel.org
Subject: Re: [PATCH] KVM: VMX: Clear GUEST_ACTIVITY_STATE when userspace makes a vCPU RUNNABLE
Date: Wed, 19 Aug 2026 08:20:04 -0700	[thread overview]
Message-ID: <aoXJpPDqwu4IlmdX@google.com> (raw)
In-Reply-To: <aoW3fOhPuFY7Whia@intel.com>

On Wed, Aug 19, 2026, Chao Gao wrote:
> On Wed, Aug 19, 2026 at 11:46:52AM +0800, Keqiang Duan wrote:
> >Force a vCPU out of its hardware-tracked halted state when userspace
> >explicitly declares the vCPU RUNNABLE via KVM_SET_MP_STATE, i.e. clear
> >VMCS.GUEST_ACTIVITY_STATE if it says the vCPU is halted.  Add an optional
> >kvm_x86_ops hook to do the clearing, as SVM has no equivalent VMCB field.
> >
> >When HLT-exiting is disabled for a VM (KVM_CAP_X86_DISABLE_EXITS with
> >KVM_X86_DISABLE_EXITS_HLT, e.g. QEMU's "-overcommit cpu-pm=on"), a guest
> >HLT halts the physical CPU instead of exiting to KVM, and hardware saves
> >GUEST_ACTIVITY_STATE=HLT into the VMCS on the next VM-Exit.  That field is
> >sticky: it survives VM-Exit/VM-Enter and is only cleared by vmx_clear_hlt()
> >on event injection, or by vmx_vcpu_reset() on INIT / vCPU creation.
> >
> >Nothing clears it on a userspace-driven state change.  KVM_SET_REGS only
> >writes the software register cache and KVM_SET_MP_STATE only writes
> >vcpu->arch.mp_state; kvm_vcpu_running() likewise consults software state
> >only.  A VMM that emulates a machine reset therefore ends up with a vCPU
> >that KVM happily VM-Enters while hardware refuses to fetch instructions.
> >
> >Reproduce with a Linux guest by triggering a panic/kdump on a non-boot
> >vCPU: nmi_shootdown_cpus() parks the other vCPUs -- including vCPU0 -- in
> >crash_nmi_callback(), which does local_irq_disable() followed by a bare
> >HLT.  The capture kernel then resets the machine via port 0xCF9.  QEMU
> >rewrites RIP to 0xfff0 and sets mp_state to RUNNABLE, but vCPU0's
> >GUEST_ACTIVITY_STATE is still HLT, so the BSP never executes the reset
> >vector, never sends SIPIs, and the entire VM hangs at "reboot: machine
> >restart" forever.  Only destroying and recreating the VM recovers it.
> >
> >Clearing the state is always safe: waking from HLT is architecturally
> >permitted to be spurious, and every HLT in the kernel is inside a loop.
> >Hook KVM_SET_MP_STATE rather than the VM-Enter path so that the clearing
> >is driven by an explicit userspace declaration, and so that no work is
> >added to vmx_vcpu_run().
> >
> >Note, vmx_clear_hlt() loses its "static" qualifier as the kvm_x86_ops table
> >now lives in vmx/main.c.  TDX cannot disable HLT-exiting and KVM cannot
> >access a TD's VMCS, so vt_clear_hlt() short-circuits for TD vCPUs,
> >following the existing vt_*() wrapper pattern.
> >
> >Fixes: caa057a2cad6 ("KVM: X86: Provide a capability to disable HLT intercepts")
> >Cc: stable@vger.kernel.org
> >Signed-off-by: Keqiang Duan <duankeqiangcym@gmail.com>
> 
> There was an earlier attempt to fix this issue:
> 
> https://lore.kernel.org/kvm/20230630072612.1106705-1-aiqi.i7@bytedance.com
> 
> Sean suggested doing exactly this there, i.e. clearing the activity state in
> kvm_arch_vcpu_ioctl_set_mpstate() via a new kvm_x86_ops hook:
> 
> https://lore.kernel.org/kvm/ZMgIQ5m1jMSAogT4@google.com

Thanks Chao, I knew this sounded familiar!

As suggested in the above link, the call into vendor code should be gated on
kvm_hlt_in_guest().  Yes, vmx_clear_hlt() already does that, but it's a cheap
check and makes the common x86 code easier to understand.

The other difference between my suggestion and this patch is clearing HLT if and
only if the vCPU is being made RUNNABLE, versus clearing HLT if the vCPU is being
put into any state other than HALTED.  In practice it probably doesn't matter?

AP_RESET_HOLD is unsupported for VMX, SIPI_RECEIVED is deprecated and is now just
a variant off INIT_RECEIVED, and getting out of INIT_RECIEVED and UNINITIALIZED
requires stuffing RUNNABLE or going through vmx_vcpu_reset(), which explicitly
sets the activity state to GUEST_ACTIVITY_ACTIVE.

But I think my vote would still be to do:

	mp_state->mp_state != KVM_MP_STATE_HALTED

because I can't think of any reason not to?

Side topic, I think we should officially take an erratum in
Documentation/virt/kvm/x86/errata.rst to state that KVM doesn't support save/restore
of HLT state when HLT-exiting is disabled.  AFAIK, there's simply no sane solution
for SVM, and that would justify why the kvm_x86_ops would be clear_hlt() as opposed
to set_mp_state(), i.e. why KVM only handles taking vCPUs out of HLT, not putting
them into HLT.

      reply	other threads:[~2026-08-19 15:20 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-19  3:46 [PATCH] KVM: VMX: Clear GUEST_ACTIVITY_STATE when userspace makes a vCPU RUNNABLE Keqiang Duan
2026-08-19 14:02 ` Chao Gao
2026-08-19 15:20   ` Sean Christopherson [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aoXJpPDqwu4IlmdX@google.com \
    --to=seanjc@google.com \
    --cc=chao.gao@intel.com \
    --cc=duankeqiangcym@gmail.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=stable@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox