mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] KVM: x86: Reevaluate pending interrupts when enabling ExtINT through LVT0
@ 2026-10-08 12:49 Matthew Leach
  2026-11-04 20:09 ` Sean Christopherson
  0 siblings, 1 reply; 2+ messages in thread
From: Matthew Leach @ 2026-10-08 12:49 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin
  Cc: kvm, linux-kernel, kernel, Matthew Leach

If the PIC output becomes asserted while LVT0 is masked, unmasking LVT0
in ExtINT mode makes that interrupt injectable without setting
KVM_REQ_EVENT. Without another event triggering reevaluation, the
interrupt remains pending and a guest waiting for timer ticks can hang.

Fix by setting KVM_REQ_EVENT on an LVT0 write where pic interrupts
become acceptable.

Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
---
During calibrate_delay_converge() we spin waiting for jiffies to increment on a
tick interrupt. However, if KVM fails to deliver the interrupt this causes the
guest to hang, spinning on the while loop.

Reproduce with:

qemu-system-x86_64 \
  -cpu host \
  -accel kvm \
  -smp 1 \
  -machine pc,hpet=off \
  -kernel <bzimage> \
  -nographic \
  -append "console=ttyS0,115200 noapic apic=debug no-kvmclock clearcpuid=tsc"

Resulting in a hang at:

printk: legacy console [ttyS0] enabled
printk: legacy console [ttyS0] enabled
printk: legacy bootconsole [uart8250] disabled
printk: legacy bootconsole [uart8250] disabled
ACPI: Core revision 20260408
ACPI: setting ELCR to 0200 (from 0c00)
APIC: Switch to symmetric I/O mode setup
Not enabling interrupt remapping due to skipped IO-APIC setup
Enabled ExtINT on CPU#0

Note that the bug manifests itself only if a PIC interrupt is asserting during
the mask window, so it is timing based. Using nested virt helps, or apply the
following patch to the guest to provide enough time for a tick after masking:

--8<---------------cut here---------------start------------->8---
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index aa1e19979aa8..ddee09360dd0 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -1509,7 +1509,7 @@ static void apic_clear_isr(void)
 static void setup_local_APIC(void)
 {
 	int cpu = smp_processor_id();
-	unsigned int value;
+	unsigned int value, i;
 
 	if (apic_is_disabled) {
 		disable_ioapic_support();
@@ -1527,6 +1527,9 @@ static void setup_local_APIC(void)
 	value &= ~APIC_SPIV_APIC_ENABLED;
 	apic_write(APIC_SPIV, value);
 
+	for (i = 0; i < 10000000UL; i++)
+		cpu_relax();
+
 #ifdef CONFIG_X86_32
 	/* Pound the ESR really hard over the head with a big hammer - mbligh */
 	if (lapic_is_integrated() && apic->disable_esr) {
--8<---------------cut here---------------end--------------->8---
---
 arch/x86/kvm/lapic.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index e1f3cea14765..ce7fbd6c45c2 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -2520,6 +2520,14 @@ static int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
 			val |= APIC_LVT_MASKED;
 		val &= apic_lvt_mask[index];
 		kvm_lapic_set_reg(apic, reg, val);
+
+		/*
+		 * Enabling ExtINT can make an already-asserted PIC interrupt
+		 * deliverable without another transition on the PIC output.
+		 */
+		if (reg == APIC_LVT0 && kvm_apic_accept_pic_intr(apic->vcpu))
+			kvm_make_request(KVM_REQ_EVENT, apic->vcpu);
+
 		break;
 	}
 

---
base-commit: a90ee4305c4a5df72c11b31dacfdc76e00fcf78a
change-id: 20261008-lapic-extint-lvt0-unmask-fix-52d2328af682

Best regards,
--  
Matt


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] KVM: x86: Reevaluate pending interrupts when enabling ExtINT through LVT0
  2026-10-08 12:49 [PATCH] KVM: x86: Reevaluate pending interrupts when enabling ExtINT through LVT0 Matthew Leach
@ 2026-11-04 20:09 ` Sean Christopherson
  0 siblings, 0 replies; 2+ messages in thread
From: Sean Christopherson @ 2026-11-04 20:09 UTC (permalink / raw)
  To: Matthew Leach
  Cc: Paolo Bonzini, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
	Dave Hansen, x86, H. Peter Anvin, kvm, linux-kernel, kernel

On Thu, Oct 08, 2026, Matthew Leach wrote:
> If the PIC output becomes asserted while LVT0 is masked, unmasking LVT0
> in ExtINT mode makes that interrupt injectable without setting
> KVM_REQ_EVENT. Without another event triggering reevaluation, the
> interrupt remains pending and a guest waiting for timer ticks can hang.
> 
> Fix by setting KVM_REQ_EVENT on an LVT0 write where pic interrupts
> become acceptable.

Why just for LVT0?  Isn't this a bug in general?  I.e. can't we simply do:

diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index d6e5fbc3c58c..ffdc67ca8fa3 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -2554,6 +2554,8 @@ static int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
 			val |= APIC_LVT_MASKED;
 		val &= apic_lvt_mask[index];
 		kvm_lapic_set_reg(apic, reg, val);
+		if (!(val & APIC_LVT_MASKED))
+			kvm_make_request(KVM_REQ_EVENT, apic->vcpu);
 		break;
 	}
 
Ahh, but there's nowhere for edge-triggered events to be latched, so this only
affects level-triggered events.  Which means, this?

diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index d6e5fbc3c58c..b72930a8516d 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -2554,6 +2554,8 @@ static int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
 			val |= APIC_LVT_MASKED;
 		val &= apic_lvt_mask[index];
 		kvm_lapic_set_reg(apic, reg, val);
+		if (!(val & APIC_LVT_MASKED) && (val & APIC_LVT_LEVEL_TRIGGER))
+			kvm_make_request(KVM_REQ_EVENT, apic->vcpu);
 		break;
 	}
 
> Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
> ---
> During calibrate_delay_converge() we spin waiting for jiffies to increment on a
> tick interrupt. However, if KVM fails to deliver the interrupt this causes the
> guest to hang, spinning on the while loop.
> 
> Reproduce with:
> 
> qemu-system-x86_64 \
>   -cpu host \
>   -accel kvm \
>   -smp 1 \
>   -machine pc,hpet=off \
>   -kernel <bzimage> \
>   -nographic \
>   -append "console=ttyS0,115200 noapic apic=debug no-kvmclock clearcpuid=tsc"
> 
> Resulting in a hang at:
> 
> printk: legacy console [ttyS0] enabled
> printk: legacy console [ttyS0] enabled
> printk: legacy bootconsole [uart8250] disabled
> printk: legacy bootconsole [uart8250] disabled
> ACPI: Core revision 20260408
> ACPI: setting ELCR to 0200 (from 0c00)
> APIC: Switch to symmetric I/O mode setup
> Not enabling interrupt remapping due to skipped IO-APIC setup
> Enabled ExtINT on CPU#0
> 
> Note that the bug manifests itself only if a PIC interrupt is asserting during
> the mask window, so it is timing based. Using nested virt helps, or apply the
> following patch to the guest to provide enough time for a tick after masking:
> 
> --8<---------------cut here---------------start------------->8---
> diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
> index aa1e19979aa8..ddee09360dd0 100644
> --- a/arch/x86/kernel/apic/apic.c
> +++ b/arch/x86/kernel/apic/apic.c
> @@ -1509,7 +1509,7 @@ static void apic_clear_isr(void)
>  static void setup_local_APIC(void)
>  {
>  	int cpu = smp_processor_id();
> -	unsigned int value;
> +	unsigned int value, i;
>  
>  	if (apic_is_disabled) {
>  		disable_ioapic_support();
> @@ -1527,6 +1527,9 @@ static void setup_local_APIC(void)
>  	value &= ~APIC_SPIV_APIC_ENABLED;
>  	apic_write(APIC_SPIV, value);
>  
> +	for (i = 0; i < 10000000UL; i++)
> +		cpu_relax();
> +
>  #ifdef CONFIG_X86_32
>  	/* Pound the ESR really hard over the head with a big hammer - mbligh */
>  	if (lapic_is_integrated() && apic->disable_esr) {
> --8<---------------cut here---------------end--------------->8---
> ---
>  arch/x86/kvm/lapic.c | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
> index e1f3cea14765..ce7fbd6c45c2 100644
> --- a/arch/x86/kvm/lapic.c
> +++ b/arch/x86/kvm/lapic.c
> @@ -2520,6 +2520,14 @@ static int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
>  			val |= APIC_LVT_MASKED;
>  		val &= apic_lvt_mask[index];
>  		kvm_lapic_set_reg(apic, reg, val);
> +
> +		/*
> +		 * Enabling ExtINT can make an already-asserted PIC interrupt
> +		 * deliverable without another transition on the PIC output.
> +		 */
> +		if (reg == APIC_LVT0 && kvm_apic_accept_pic_intr(apic->vcpu))
> +			kvm_make_request(KVM_REQ_EVENT, apic->vcpu);
> +
>  		break;
>  	}
>  
> 
> ---
> base-commit: a90ee4305c4a5df72c11b31dacfdc76e00fcf78a
> change-id: 20261008-lapic-extint-lvt0-unmask-fix-52d2328af682
> 
> Best regards,
> --  
> Matt
> 
> 

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-11-04 20:09 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-08 12:49 [PATCH] KVM: x86: Reevaluate pending interrupts when enabling ExtINT through LVT0 Matthew Leach
2026-11-04 20:09 ` Sean Christopherson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®