[PATCH 2/3] LoongArch: KVM: Fix HW timer interrupt lost when inject interrupt from software
From: Bibo Mao
Date: Tue Apr 14 2026 - 03:28:59 EST
With passthrough HW timer, timer interrupt is injected by HW. When
inject emulated CPU interrupt by software such CPU_SIP0/CPU_IPI, HW
timer interrupt may be lost.
Here check whether there is timer tick value reversion before and after
injecting emulated CPU interrupt by software, timer enabling by reading
timer cfg register is skipped here. If timer tick value is detected
with value changed, timer should be enabled. And inject timer interrupt
by software if there is.
Cc: <stable@xxxxxxxxxxxxxxx>
Fixes: f45ad5b8aa93 ("LoongArch: KVM: Implement vcpu interrupt operations").
Signed-off-by: Bibo Mao <maobibo@xxxxxxxxxxx>
---
arch/loongarch/kvm/interrupt.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/arch/loongarch/kvm/interrupt.c b/arch/loongarch/kvm/interrupt.c
index f67dddfec7d0..422515744dc0 100644
--- a/arch/loongarch/kvm/interrupt.c
+++ b/arch/loongarch/kvm/interrupt.c
@@ -27,6 +27,7 @@ static unsigned int priority_to_irq[EXCCODE_INT_NUM] = {
static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
{
unsigned int irq = 0;
+ unsigned long old, new;
clear_bit(priority, &vcpu->arch.irq_pending);
if (priority < EXCCODE_INT_NUM)
@@ -41,7 +42,20 @@ static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
case INT_IPI:
case INT_SWI0:
case INT_SWI1:
+ old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
set_gcsr_estat(irq);
+ new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
+ /*
+ * On some platforms, the passthrought HW timer interrupt
+ * is lost when inject CPU interrupt by software if HW
+ * timer interrupt is arriving.
+ *
+ * Here check whether there is inversion with timer tick
+ * value, inject timer interrupt by SW if so.
+ */
+
+ if (new > old)
+ set_gcsr_estat(CPU_TIMER);
break;
case INT_HWI0 ... INT_HWI7:
@@ -58,6 +72,7 @@ static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority)
{
unsigned int irq = 0;
+ unsigned long old, new;
clear_bit(priority, &vcpu->arch.irq_clear);
if (priority < EXCCODE_INT_NUM)
@@ -72,7 +87,11 @@ static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority)
case INT_IPI:
case INT_SWI0:
case INT_SWI1:
+ old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
clear_gcsr_estat(irq);
+ new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL);
+ if (new > old)
+ set_gcsr_estat(CPU_TIMER);
break;
case INT_HWI0 ... INT_HWI7:
--
2.39.3