From: David Woodhouse <[email protected]> With APICv live on plane-0 vCPUs while their plane-1 twins use software injection, the twins trip the APICv coherency checks: stale vcpu-vs-VM activation state at vcpu_enter_guest(), and timer interrupt injection against a vCPU that is not the running one. Cross-plane event delivery (forced VTL entry, twin kicks, NMI folding) depends on software-injection bookkeeping that hardware acceleration bypasses.
Add APICV_INHIBIT_REASON_PLANES, set it when a plane above 0 is created, and include it in the VMX and AVIC required-inhibit masks; __kvm_set_or_clear_apicv_inhibit() silently ignores a reason the vendor mask does not carry. The reference VSM hypervisor likewise force-disables APICv when VTLs are enabled. Signed-off-by: David Woodhouse <[email protected]> Assisted-by: LLM --- arch/x86/include/asm/kvm_host.h | 10 +++++++++- arch/x86/kvm/svm/svm.h | 3 ++- arch/x86/kvm/vmx/main.c | 3 ++- arch/x86/kvm/x86.c | 10 ++++++++++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 1ba77d9375cd..5945b57c091d 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1436,6 +1436,13 @@ enum kvm_apicv_inhibit { */ APICV_INHIBIT_REASON_PHYSICAL_ID_TOO_BIG, + /* + * APICv is inhibited when the VM has multiple planes (Hyper-V VTLs). + * Cross-plane event delivery relies on software injection; hardware + * acceleration on one twin would bypass it. + */ + APICV_INHIBIT_REASON_PLANES, + NR_APICV_INHIBIT_REASONS, }; @@ -1455,7 +1462,8 @@ enum kvm_apicv_inhibit { __APICV_INHIBIT_REASON(PIT_REINJ), \ __APICV_INHIBIT_REASON(SEV), \ __APICV_INHIBIT_REASON(LOGICAL_ID_ALIASED), \ - __APICV_INHIBIT_REASON(PHYSICAL_ID_TOO_BIG) + __APICV_INHIBIT_REASON(PHYSICAL_ID_TOO_BIG), \ + __APICV_INHIBIT_REASON(PLANES) struct kvm_possible_nx_huge_pages { /* diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 7a1bf4f5b8e0..64438317d148 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -925,7 +925,8 @@ extern struct kvm_x86_nested_ops svm_nested_ops; BIT(APICV_INHIBIT_REASON_APIC_ID_MODIFIED) | \ BIT(APICV_INHIBIT_REASON_APIC_BASE_MODIFIED) | \ BIT(APICV_INHIBIT_REASON_LOGICAL_ID_ALIASED) | \ - BIT(APICV_INHIBIT_REASON_PHYSICAL_ID_TOO_BIG) \ + BIT(APICV_INHIBIT_REASON_PHYSICAL_ID_TOO_BIG) | \ + BIT(APICV_INHIBIT_REASON_PLANES) \ ) bool __init avic_hardware_setup(void); diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c index 572921bdfb32..81a8d398068b 100644 --- a/arch/x86/kvm/vmx/main.c +++ b/arch/x86/kvm/vmx/main.c @@ -876,7 +876,8 @@ static int vt_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, BIT(APICV_INHIBIT_REASON_BLOCKIRQ) | \ BIT(APICV_INHIBIT_REASON_PHYSICAL_ID_ALIASED) | \ BIT(APICV_INHIBIT_REASON_APIC_ID_MODIFIED) | \ - BIT(APICV_INHIBIT_REASON_APIC_BASE_MODIFIED)) + BIT(APICV_INHIBIT_REASON_APIC_BASE_MODIFIED) | \ + BIT(APICV_INHIBIT_REASON_PLANES)) struct kvm_x86_ops vt_x86_ops __initdata = { .name = KBUILD_MODNAME, diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 3e2465b2fa2b..052821799120 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -13979,6 +13979,16 @@ int kvm_arch_plane_init(struct kvm *kvm, struct kvm_plane *plane, { mutex_init(&plane->arch.apic_map_lock); + /* + * Cross-plane event delivery (forced VTL entry, twin kicks, NMI + * folding) relies on software injection via KVM_REQ_EVENT; hardware + * APIC virtualization on one twin would bypass the bookkeeping the + * other twin's plane depends on. Mirrors the reference VSM + * hypervisor, which force-disables APICv when VTLs are enabled. + */ + if (plane_level > 0) + kvm_set_apicv_inhibit(kvm, APICV_INHIBIT_REASON_PLANES); + return 0; } -- 2.50.1

