[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251229111708.59402-2-khushit.shah@nutanix.com>
Date: Mon, 29 Dec 2025 11:17:06 +0000
From: Khushit Shah <khushit.shah@...anix.com>
To: seanjc@...gle.com, pbonzini@...hat.com, kai.huang@...el.com,
dwmw2@...radead.org
Cc: mingo@...hat.com, x86@...nel.org, bp@...en8.de, hpa@...or.com,
linux-kernel@...r.kernel.org, kvm@...r.kernel.org,
dave.hansen@...ux.intel.com, tglx@...utronix.de, jon@...anix.com,
shaju.abraham@...anix.com, Khushit Shah <khushit.shah@...anix.com>
Subject: [PATCH v5 1/3] KVM: x86: Refactor suppress EOI broadcast logic
Extract the suppress EOI broadcast (Directed EOI) logic into helper
functions and move the check from kvm_ioapic_update_eoi_one() to
kvm_ioapic_update_eoi() (required for a later patch). Prepare
kvm_ioapic_send_eoi() to honor Suppress EOI Broadcast in split IRQCHIP
mode.
Introduce two helper functions:
- kvm_lapic_advertise_suppress_eoi_broadcast(): determines whether KVM
should advertise Suppress EOI Broadcast support to the guest
- kvm_lapic_respect_suppress_eoi_broadcast(): determines whether KVM should
honor the guest's request to suppress EOI broadcasts
This refactoring prepares for I/O APIC version 0x20 support and userspace
control of suppress EOI broadcast behavior.
Signed-off-by: Khushit Shah <khushit.shah@...anix.com>
---
arch/x86/kvm/ioapic.c | 12 +++++++---
arch/x86/kvm/lapic.c | 53 ++++++++++++++++++++++++++++++++++++-------
arch/x86/kvm/lapic.h | 3 +++
3 files changed, 57 insertions(+), 11 deletions(-)
diff --git a/arch/x86/kvm/ioapic.c b/arch/x86/kvm/ioapic.c
index 2c2783296aed..6bf8d110aece 100644
--- a/arch/x86/kvm/ioapic.c
+++ b/arch/x86/kvm/ioapic.c
@@ -545,7 +545,6 @@ static void kvm_ioapic_update_eoi_one(struct kvm_vcpu *vcpu,
int trigger_mode,
int pin)
{
- struct kvm_lapic *apic = vcpu->arch.apic;
union kvm_ioapic_redirect_entry *ent = &ioapic->redirtbl[pin];
/*
@@ -560,8 +559,7 @@ static void kvm_ioapic_update_eoi_one(struct kvm_vcpu *vcpu,
kvm_notify_acked_irq(ioapic->kvm, KVM_IRQCHIP_IOAPIC, pin);
spin_lock(&ioapic->lock);
- if (trigger_mode != IOAPIC_LEVEL_TRIG ||
- kvm_lapic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI)
+ if (trigger_mode != IOAPIC_LEVEL_TRIG)
return;
ASSERT(ent->fields.trig_mode == IOAPIC_LEVEL_TRIG);
@@ -591,10 +589,16 @@ static void kvm_ioapic_update_eoi_one(struct kvm_vcpu *vcpu,
void kvm_ioapic_update_eoi(struct kvm_vcpu *vcpu, int vector, int trigger_mode)
{
int i;
+ struct kvm_lapic *apic = vcpu->arch.apic;
struct kvm_ioapic *ioapic = vcpu->kvm->arch.vioapic;
spin_lock(&ioapic->lock);
rtc_irq_eoi(ioapic, vcpu, vector);
+
+ if ((kvm_lapic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI) &&
+ kvm_lapic_respect_suppress_eoi_broadcast(ioapic->kvm))
+ goto out;
+
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
union kvm_ioapic_redirect_entry *ent = &ioapic->redirtbl[i];
@@ -602,6 +606,8 @@ void kvm_ioapic_update_eoi(struct kvm_vcpu *vcpu, int vector, int trigger_mode)
continue;
kvm_ioapic_update_eoi_one(vcpu, ioapic, trigger_mode, i);
}
+
+out:
spin_unlock(&ioapic->lock);
}
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 0ae7f913d782..2c24fd8d815f 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -105,6 +105,39 @@ bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector)
apic_test_vector(vector, apic->regs + APIC_IRR);
}
+bool kvm_lapic_advertise_suppress_eoi_broadcast(struct kvm *kvm)
+{
+ /*
+ * The default in-kernel I/O APIC emulates the 82093AA and does not
+ * implement an EOI register. Some guests (e.g. Windows with the
+ * Hyper-V role enabled) disable LAPIC EOI broadcast without checking
+ * the I/O APIC version, which can cause level-triggered interrupts to
+ * never be EOI'd.
+ *
+ * To avoid this, KVM must not advertise Suppress EOI Broadcast support
+ * when using the default in-kernel I/O APIC.
+ *
+ * Historically, in split IRQCHIP mode, KVM always advertised Suppress
+ * EOI Broadcast support but did not actually suppress EOIs, resulting
+ * in quirky behavior.
+ */
+ return !ioapic_in_kernel(kvm);
+}
+
+bool kvm_lapic_respect_suppress_eoi_broadcast(struct kvm *kvm)
+{
+ /*
+ * Returns true if KVM should honor the guest's request to suppress EOI
+ * broadcasts, i.e. actually implement Suppress EOI Broadcast.
+ *
+ * Historically, in split IRQCHIP mode, KVM ignored the suppress EOI
+ * broadcast bit set by the guest and broadcasts EOIs to the userspace
+ * I/O APIC. For In-kernel I/O APIC, the support itself is not
+ * advertised, but if bit was set by the guest, it was respected.
+ */
+ return ioapic_in_kernel(kvm);
+}
+
__read_mostly DEFINE_STATIC_KEY_FALSE(kvm_has_noapic_vcpu);
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_has_noapic_vcpu);
@@ -554,15 +587,9 @@ void kvm_apic_set_version(struct kvm_vcpu *vcpu)
v = APIC_VERSION | ((apic->nr_lvt_entries - 1) << 16);
- /*
- * KVM emulates 82093AA datasheet (with in-kernel IOAPIC implementation)
- * which doesn't have EOI register; Some buggy OSes (e.g. Windows with
- * Hyper-V role) disable EOI broadcast in lapic not checking for IOAPIC
- * version first and level-triggered interrupts never get EOIed in
- * IOAPIC.
- */
+
if (guest_cpu_cap_has(vcpu, X86_FEATURE_X2APIC) &&
- !ioapic_in_kernel(vcpu->kvm))
+ kvm_lapic_advertise_suppress_eoi_broadcast(vcpu->kvm))
v |= APIC_LVR_DIRECTED_EOI;
kvm_lapic_set_reg(apic, APIC_LVR, v);
}
@@ -1517,6 +1544,16 @@ static void kvm_ioapic_send_eoi(struct kvm_lapic *apic, int vector)
/* Request a KVM exit to inform the userspace IOAPIC. */
if (irqchip_split(apic->vcpu->kvm)) {
+ /*
+ * Don't exit to userspace if the guest has enabled Directed
+ * EOI, a.k.a. Suppress EOI Broadcasts, in which case the local
+ * APIC doesn't broadcast EOIs (the guest must EOI the target
+ * I/O APIC(s) directly).
+ */
+ if ((kvm_lapic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI) &&
+ kvm_lapic_respect_suppress_eoi_broadcast(apic->vcpu->kvm))
+ return;
+
apic->vcpu->arch.pending_ioapic_eoi = vector;
kvm_make_request(KVM_REQ_IOAPIC_EOI_EXIT, apic->vcpu);
return;
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index 282b9b7da98c..fe2db0f1d190 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -231,6 +231,9 @@ static inline int kvm_lapic_latched_init(struct kvm_vcpu *vcpu)
bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector);
+bool kvm_lapic_advertise_suppress_eoi_broadcast(struct kvm *kvm);
+bool kvm_lapic_respect_suppress_eoi_broadcast(struct kvm *kvm);
+
void kvm_wait_lapic_expire(struct kvm_vcpu *vcpu);
void kvm_bitmap_or_dest_vcpus(struct kvm *kvm, struct kvm_lapic_irq *irq,
--
2.39.3
Powered by blists - more mailing lists