mirror of
https://github.com/torvalds/linux.git
synced 2026-09-25 17:42:03 +02:00
KVM: x86: Don't treat interrupts as allowed just because a nested run is pending
When querying whether or not interrupts (IRQs) are allowed, check for a pending nested run _after_ checking whether or not interrupts are blocked. If L1 is running L2 _without_ nested_exit_on_intr(), i.e. if L1 IRQs can be blocked while running L2, and interrupts will indeed be blocked once the nested VM-Enter to L2 is completed, then KVM should treat interrupts as not being allowed. For injection, this avoids an unnecessary (forced) VM-Exit, as KVM can immediately request an IRQ window, instead of forcing an exit and _then_ requesting an IRQ window (because after the forced exit, KVM will see that interrupts are blocked). For non-injection usage, only kvm_vcpu_ready_for_interrupt_injection() is affected in practice. Barring KVM bugs or misbehaving userspace (at which point all architectural guarantees are off), kvm_vcpu_has_events() is unreachable when a nested run is pending. To reach kvm_vcpu_has_events(), kvm_vcpu_running() needs to return false, i.e. vcpu->arch.mp_state needs to be something other than RUNNABLE. If nested_run_pending is true, then mp_state *must* be RUNNABLE (again barring bugs or stupid userspace), because KVM shouldn't emulate VMRUN/VMLAUNCH/VMRESUME while the vCPU is !RUNNABLE. The one "near miss" is VMX's GUEST_ACTIVITY_STATE field, which allows L1 to put the vCPU into HLT or WFS as part of nested VMLAUNCH/VMRESUME. However, KVM clears nested_run_pending prior to calling kvm_emulate_halt_noskip() when putting L2 into HLT via GUEST_ACTIVITY_HLT, and also clears the flag before setting mp_state to INIT_RECEIVED. SVM has no equivalent to GUEST_ACTIVITY_STATE. I.e. the vCPU will always be runnable if a nested run is pending, and thus kvm_arch_vcpu_runnable() => kvm_vcpu_has_events() is effectively dead code, as is __kvm_emulate_halt() => kvm_vcpu_has_events(). Oh, and TDX doesn't support nested VMX. Similarly, kvm_can_do_async_pf() is unreachable as KVM shouldn't be faulting in memory with a pending nested VM-Enter. As for kvm_vcpu_ready_for_interrupt_injection(), KVM's current behavior of incorrectly treating interrupts as being allowed could result in KVM prematurely exiting to userspace to accept an ExtINT. But, KVM will still hold/block the ExtINT and request its own IRQ window. I.e. the net effect is more or less the same as the for-injection case, the unnecessary exit just happens at a different boundary. Signed-off-by: Sean Christopherson <seanjc@google.com> Message-ID: <20260613000329.732085-27-seanjc@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
ee67344af1
commit
4f1f1ffbdd
|
|
@ -4055,12 +4055,12 @@ static int svm_interrupt_allowed(struct kvm_vcpu *vcpu, bool for_injection)
|
|||
{
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
|
||||
if (vcpu->arch.nested_run_pending)
|
||||
return -EBUSY;
|
||||
|
||||
if (svm_interrupt_blocked(vcpu))
|
||||
return 0;
|
||||
|
||||
if (vcpu->arch.nested_run_pending)
|
||||
return -EBUSY;
|
||||
|
||||
/*
|
||||
* An IRQ must not be injected into L2 if it's supposed to VM-Exit,
|
||||
* e.g. if the IRQ arrived asynchronously after checking nested events.
|
||||
|
|
|
|||
|
|
@ -5250,6 +5250,9 @@ bool vmx_interrupt_blocked(struct kvm_vcpu *vcpu)
|
|||
|
||||
int vmx_interrupt_allowed(struct kvm_vcpu *vcpu, bool for_injection)
|
||||
{
|
||||
if (vmx_interrupt_blocked(vcpu))
|
||||
return 0;
|
||||
|
||||
if (vcpu->arch.nested_run_pending)
|
||||
return -EBUSY;
|
||||
|
||||
|
|
@ -5260,7 +5263,7 @@ int vmx_interrupt_allowed(struct kvm_vcpu *vcpu, bool for_injection)
|
|||
if (for_injection && is_guest_mode(vcpu) && nested_exit_on_intr(vcpu))
|
||||
return -EBUSY;
|
||||
|
||||
return !vmx_interrupt_blocked(vcpu);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vmx_set_tss_addr(struct kvm *kvm, unsigned int addr)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user