Skip to content

Commit

Permalink
KVM: x86/xen: inject vCPU upcall vector when local APIC is enabled
Browse files Browse the repository at this point in the history
Linux guests since commit b1c3497 ("x86/xen: Add support for
HVMOP_set_evtchn_upcall_vector") in v6.0 onwards will use the per-vCPU
upcall vector when it's advertised in the Xen CPUID leaves.

This upcall is injected through the guest's local APIC as an MSI, unlike
the older system vector which was merely injected by the hypervisor any
time the CPU was able to receive an interrupt and the upcall_pending
flags is set in its vcpu_info.

Effectively, that makes the per-CPU upcall edge triggered instead of
level triggered, which results in the upcall being lost if the MSI is
delivered when the local APIC is *disabled*.

Xen checks the vcpu_info->evtchn_upcall_pending flag when the local APIC
for a vCPU is software enabled (in fact, on any write to the SPIV
register which doesn't disable the APIC). Do the same in KVM since KVM
doesn't provide a way for userspace to intervene and trap accesses to
the SPIV register of a local APIC emulated by KVM.

Fixes: fde0451 ("KVM: x86/xen: Support per-vCPU event channel upcall via local APIC")
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Reviewed-by: Paul Durrant <paul@xen.org>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20240227115648.3104-3-dwmw2@infradead.org
Signed-off-by: Sean Christopherson <seanjc@google.com>
  • Loading branch information
dwmw2 authored and sean-jc committed Mar 5, 2024
1 parent 451a707 commit 8e62bf2
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 2 deletions.
5 changes: 4 additions & 1 deletion arch/x86/kvm/lapic.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "ioapic.h"
#include "trace.h"
#include "x86.h"
#include "xen.h"
#include "cpuid.h"
#include "hyperv.h"
#include "smm.h"
Expand Down Expand Up @@ -499,8 +500,10 @@ static inline void apic_set_spiv(struct kvm_lapic *apic, u32 val)
}

/* Check if there are APF page ready requests pending */
if (enabled)
if (enabled) {
kvm_make_request(KVM_REQ_APF_READY, apic->vcpu);
kvm_xen_sw_enable_lapic(apic->vcpu);
}
}

static inline void kvm_apic_set_xapic_id(struct kvm_lapic *apic, u8 id)
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/kvm/xen.c
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ void kvm_xen_update_runstate(struct kvm_vcpu *v, int state)
kvm_xen_update_runstate_guest(v, state == RUNSTATE_runnable);
}

static void kvm_xen_inject_vcpu_vector(struct kvm_vcpu *v)
void kvm_xen_inject_vcpu_vector(struct kvm_vcpu *v)
{
struct kvm_lapic_irq irq = { };
int r;
Expand Down
18 changes: 18 additions & 0 deletions arch/x86/kvm/xen.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ extern struct static_key_false_deferred kvm_xen_enabled;

int __kvm_xen_has_interrupt(struct kvm_vcpu *vcpu);
void kvm_xen_inject_pending_events(struct kvm_vcpu *vcpu);
void kvm_xen_inject_vcpu_vector(struct kvm_vcpu *vcpu);
int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data);
int kvm_xen_vcpu_get_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data);
int kvm_xen_hvm_set_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data);
Expand All @@ -36,6 +37,19 @@ int kvm_xen_setup_evtchn(struct kvm *kvm,
const struct kvm_irq_routing_entry *ue);
void kvm_xen_update_tsc_info(struct kvm_vcpu *vcpu);

static inline void kvm_xen_sw_enable_lapic(struct kvm_vcpu *vcpu)
{
/*
* The local APIC is being enabled. If the per-vCPU upcall vector is
* set and the vCPU's evtchn_upcall_pending flag is set, inject the
* interrupt.
*/
if (static_branch_unlikely(&kvm_xen_enabled.key) &&
vcpu->arch.xen.vcpu_info_cache.active &&
vcpu->arch.xen.upcall_vector && __kvm_xen_has_interrupt(vcpu))
kvm_xen_inject_vcpu_vector(vcpu);
}

static inline bool kvm_xen_msr_enabled(struct kvm *kvm)
{
return static_branch_unlikely(&kvm_xen_enabled.key) &&
Expand Down Expand Up @@ -101,6 +115,10 @@ static inline void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu)
{
}

static inline void kvm_xen_sw_enable_lapic(struct kvm_vcpu *vcpu)
{
}

static inline bool kvm_xen_msr_enabled(struct kvm *kvm)
{
return false;
Expand Down

0 comments on commit 8e62bf2

Please sign in to comment.