From: Sasha Levin Date: Mon, 27 Jul 2026 13:24:31 +0000 (-0400) Subject: Drop risc-v-kvm-serialize-virtual-interrupt-pending-state-updates.patch X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=9077fe3508d7fc19d4290046a1371b1b351eb1ae;p=thirdparty%2Fkernel%2Fstable-queue.git Drop risc-v-kvm-serialize-virtual-interrupt-pending-state-updates.patch Signed-off-by: Sasha Levin --- diff --git a/queue-7.1/risc-v-kvm-serialize-virtual-interrupt-pending-state-updates.patch b/queue-7.1/risc-v-kvm-serialize-virtual-interrupt-pending-state-updates.patch deleted file mode 100644 index 55810d9e17..0000000000 --- a/queue-7.1/risc-v-kvm-serialize-virtual-interrupt-pending-state-updates.patch +++ /dev/null @@ -1,382 +0,0 @@ -From d024a0a7879e6f37c0152aacf6d8e37b214a1738 Mon Sep 17 00:00:00 2001 -From: Xie Bo -Date: Wed, 15 Jul 2026 10:03:59 +0800 -Subject: RISC-V: KVM: Serialize virtual interrupt pending state updates - -From: Xie Bo - -commit d024a0a7879e6f37c0152aacf6d8e37b214a1738 upstream. - -KVM RISC-V tracks guest local interrupt state with two bitmaps: - - - irqs_pending: interrupts that should be visible to the guest - - irqs_pending_mask: interrupts whose pending state changed - -The current code updates those bitmaps with independent atomic bitops -and assumes a multiple-producer, single-consumer protocol. That model -does not actually hold. - -kvm_riscv_vcpu_sync_interrupts() is not a pure consumer. When the guest -changes guest-visible HVIP state, sync_interrupts() writes both -irqs_pending and irqs_pending_mask to reflect the new guest state back -into KVM state. As a result, irqs_pending and irqs_pending_mask form a -single logical state transition, but they are not updated atomically as -a pair. - -This allows a race where a newly injected interrupt is lost. For -example: - - CPU0 CPU1 - ---- ---- - kvm_riscv_vcpu_set_interrupt(VS_SOFT) - set_bit(VS_SOFT, irqs_pending) - kvm_riscv_vcpu_sync_interrupts() - sees guest-cleared HVIP.VSSIP - sets irqs_pending_mask - clear_bit(IRQ_VS_SOFT, irqs_pending) - set_bit(VS_SOFT, irqs_pending_mask) - kvm_vcpu_kick() - -After that interleaving, a later flush can update HVIP without VSSIP -even though a new virtual interrupt was injected. In practice, the -guest can remain blocked in WFI with work pending. - -The same pending/mask protocol is shared by VS soft interrupts, PMU -overflow delivery, and AIA high interrupt synchronization, so the race -is not limited to one interrupt source. - -Fix this by serializing all updates to irqs_pending and irqs_pending_mask -with a per-vCPU raw spinlock. This keeps the pending bit and the dirty -mask as one state transition across: - - - set/unset interrupt - - guest HVIP sync - - interrupt flush to guest CSR state - - vCPU reset - - AIA CSR writes that clear dirty state - -Use non-atomic bitmap operations while holding the lock. Hold the lock -across the AIA sync, flush, and pending checks as well, so both bitmap -words share the same serialization domain. - -This intentionally replaces the existing lockless protocol instead of -trying to repair it with additional barriers. The problem is not memory -ordering on a single field; it is that two separate bitmaps encode one -shared state machine while both producers and sync paths can modify -them. A per-vCPU raw spinlock keeps the fix small, local, and suitable -for backporting. - -Fixes: cce69aff689e ("RISC-V: KVM: Implement VCPU interrupts and requests handling") -Cc: stable@vger.kernel.org -Signed-off-by: Xie Bo -Reviewed-by: Anup Patel -Link: https://lore.kernel.org/r/20260715020359.1521354-2-xb@ultrarisc.com -Signed-off-by: Anup Patel -Signed-off-by: Greg Kroah-Hartman ---- - arch/riscv/include/asm/kvm_host.h | 10 ++--- - arch/riscv/kvm/aia.c | 35 +++++++++++++++---- - arch/riscv/kvm/vcpu.c | 68 ++++++++++++++++++++++++++------------ - arch/riscv/kvm/vcpu_onereg.c | 8 +++- - 4 files changed, 87 insertions(+), 34 deletions(-) - ---- a/arch/riscv/include/asm/kvm_host.h -+++ b/arch/riscv/include/asm/kvm_host.h -@@ -207,13 +207,13 @@ struct kvm_vcpu_arch { - /* - * VCPU interrupts - * -- * We have a lockless approach for tracking pending VCPU interrupts -- * implemented using atomic bitops. The irqs_pending bitmap represent -- * pending interrupts whereas irqs_pending_mask represent bits changed -- * in irqs_pending. Our approach is modeled around multiple producer -- * and single consumer problem where the consumer is the VCPU itself. -+ * The irqs_pending bitmap represents pending interrupts whereas -+ * irqs_pending_mask represents bits changed in irqs_pending. Updates -+ * to these bitmaps are serialized so vcpu interrupt sync/flush cannot -+ * drop a newly injected interrupt while syncing guest-visible HVIP. - */ - #define KVM_RISCV_VCPU_NR_IRQS 64 -+ raw_spinlock_t irqs_pending_lock; - DECLARE_BITMAP(irqs_pending, KVM_RISCV_VCPU_NR_IRQS); - DECLARE_BITMAP(irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS); - ---- a/arch/riscv/kvm/aia.c -+++ b/arch/riscv/kvm/aia.c -@@ -51,12 +51,15 @@ void kvm_riscv_vcpu_aia_flush_interrupts - struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; - unsigned long mask, val; - -+ lockdep_assert_held(&vcpu->arch.irqs_pending_lock); -+ - if (!kvm_riscv_aia_available()) - return; - -- if (READ_ONCE(vcpu->arch.irqs_pending_mask[1])) { -- mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[1], 0); -- val = READ_ONCE(vcpu->arch.irqs_pending[1]) & mask; -+ mask = vcpu->arch.irqs_pending_mask[1]; -+ if (mask) { -+ vcpu->arch.irqs_pending_mask[1] = 0; -+ val = vcpu->arch.irqs_pending[1] & mask; - - csr->hviph &= ~mask; - csr->hviph |= val; -@@ -67,6 +70,8 @@ void kvm_riscv_vcpu_aia_sync_interrupts( - { - struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; - -+ lockdep_assert_held(&vcpu->arch.irqs_pending_lock); -+ - if (kvm_riscv_aia_available()) - csr->vsieh = ncsr_read(CSR_VSIEH); - } -@@ -75,13 +80,22 @@ void kvm_riscv_vcpu_aia_sync_interrupts( - bool kvm_riscv_vcpu_aia_has_interrupts(struct kvm_vcpu *vcpu, u64 mask) - { - unsigned long seip; -+#ifdef CONFIG_32BIT -+ unsigned long flags; -+ bool pending; -+#endif - - if (!kvm_riscv_aia_available()) - return false; - - #ifdef CONFIG_32BIT -- if (READ_ONCE(vcpu->arch.irqs_pending[1]) & -- (vcpu->arch.aia_context.guest_csr.vsieh & upper_32_bits(mask))) -+ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); -+ pending = vcpu->arch.irqs_pending[1] & -+ (vcpu->arch.aia_context.guest_csr.vsieh & -+ upper_32_bits(mask)); -+ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); -+ -+ if (pending) - return true; - #endif - -@@ -205,6 +219,9 @@ int kvm_riscv_vcpu_aia_set_csr(struct kv - { - struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; - unsigned long regs_max = sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long); -+#ifdef CONFIG_32BIT -+ unsigned long flags; -+#endif - - if (!riscv_isa_extension_available(vcpu->arch.isa, SSAIA)) - return -ENOENT; -@@ -217,8 +234,12 @@ int kvm_riscv_vcpu_aia_set_csr(struct kv - ((unsigned long *)csr)[reg_num] = val; - - #ifdef CONFIG_32BIT -- if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph)) -- WRITE_ONCE(vcpu->arch.irqs_pending_mask[1], 0); -+ if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph)) { -+ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); -+ vcpu->arch.irqs_pending_mask[1] = 0; -+ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, -+ flags); -+ } - #endif - } - ---- a/arch/riscv/kvm/vcpu.c -+++ b/arch/riscv/kvm/vcpu.c -@@ -80,6 +80,7 @@ static void kvm_riscv_vcpu_context_reset - - static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset) - { -+ unsigned long flags; - bool loaded; - - /** -@@ -104,8 +105,10 @@ static void kvm_riscv_reset_vcpu(struct - - kvm_riscv_vcpu_aia_reset(vcpu); - -+ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); - bitmap_zero(vcpu->arch.irqs_pending, KVM_RISCV_VCPU_NR_IRQS); - bitmap_zero(vcpu->arch.irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS); -+ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); - - kvm_riscv_vcpu_pmu_reset(vcpu); - -@@ -151,6 +154,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu - - /* Setup VCPU hfence queue */ - spin_lock_init(&vcpu->arch.hfence_lock); -+ raw_spin_lock_init(&vcpu->arch.irqs_pending_lock); - - spin_lock_init(&vcpu->arch.reset_state.lock); - -@@ -352,10 +356,14 @@ void kvm_riscv_vcpu_flush_interrupts(str - { - struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; - unsigned long mask, val; -+ unsigned long flags; - -- if (READ_ONCE(vcpu->arch.irqs_pending_mask[0])) { -- mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[0], 0); -- val = READ_ONCE(vcpu->arch.irqs_pending[0]) & mask; -+ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); -+ -+ mask = vcpu->arch.irqs_pending_mask[0]; -+ if (mask) { -+ vcpu->arch.irqs_pending_mask[0] = 0; -+ val = vcpu->arch.irqs_pending[0] & mask; - - csr->hvip &= ~mask; - csr->hvip |= val; -@@ -363,11 +371,14 @@ void kvm_riscv_vcpu_flush_interrupts(str - - /* Flush AIA high interrupts */ - kvm_riscv_vcpu_aia_flush_interrupts(vcpu); -+ -+ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); - } - - void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu) - { - unsigned long hvip; -+ unsigned long flags; - struct kvm_vcpu_arch *v = &vcpu->arch; - struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; - -@@ -376,34 +387,41 @@ void kvm_riscv_vcpu_sync_interrupts(stru - - /* Sync-up HVIP.VSSIP bit changes does by Guest */ - hvip = ncsr_read(CSR_HVIP); -+ -+ raw_spin_lock_irqsave(&v->irqs_pending_lock, flags); -+ - if ((csr->hvip ^ hvip) & (1UL << IRQ_VS_SOFT)) { - if (hvip & (1UL << IRQ_VS_SOFT)) { -- if (!test_and_set_bit(IRQ_VS_SOFT, -- v->irqs_pending_mask)) -- set_bit(IRQ_VS_SOFT, v->irqs_pending); -+ if (!__test_and_set_bit(IRQ_VS_SOFT, -+ v->irqs_pending_mask)) -+ __set_bit(IRQ_VS_SOFT, v->irqs_pending); - } else { -- if (!test_and_set_bit(IRQ_VS_SOFT, -- v->irqs_pending_mask)) -- clear_bit(IRQ_VS_SOFT, v->irqs_pending); -+ if (!__test_and_set_bit(IRQ_VS_SOFT, -+ v->irqs_pending_mask)) -+ __clear_bit(IRQ_VS_SOFT, v->irqs_pending); - } - } - - /* Sync up the HVIP.LCOFIP bit changes (only clear) by the guest */ - if ((csr->hvip ^ hvip) & (1UL << IRQ_PMU_OVF)) { - if (!(hvip & (1UL << IRQ_PMU_OVF)) && -- !test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask)) -- clear_bit(IRQ_PMU_OVF, v->irqs_pending); -+ !__test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask)) -+ __clear_bit(IRQ_PMU_OVF, v->irqs_pending); - } - - /* Sync-up AIA high interrupts */ - kvm_riscv_vcpu_aia_sync_interrupts(vcpu); - -+ raw_spin_unlock_irqrestore(&v->irqs_pending_lock, flags); -+ - /* Sync-up timer CSRs */ - kvm_riscv_vcpu_timer_sync(vcpu); - } - - int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) - { -+ unsigned long flags; -+ - /* - * We only allow VS-mode software, timer, and external - * interrupts when irq is one of the local interrupts -@@ -416,9 +434,10 @@ int kvm_riscv_vcpu_set_interrupt(struct - irq != IRQ_PMU_OVF) - return -EINVAL; - -- set_bit(irq, vcpu->arch.irqs_pending); -- smp_mb__before_atomic(); -- set_bit(irq, vcpu->arch.irqs_pending_mask); -+ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); -+ __set_bit(irq, vcpu->arch.irqs_pending); -+ __set_bit(irq, vcpu->arch.irqs_pending_mask); -+ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); - - kvm_vcpu_kick(vcpu); - -@@ -427,6 +446,8 @@ int kvm_riscv_vcpu_set_interrupt(struct - - int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) - { -+ unsigned long flags; -+ - /* - * We only allow VS-mode software, timer, counter overflow and external - * interrupts when irq is one of the local interrupts -@@ -439,26 +460,33 @@ int kvm_riscv_vcpu_unset_interrupt(struc - irq != IRQ_PMU_OVF) - return -EINVAL; - -- clear_bit(irq, vcpu->arch.irqs_pending); -- smp_mb__before_atomic(); -- set_bit(irq, vcpu->arch.irqs_pending_mask); -+ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); -+ __clear_bit(irq, vcpu->arch.irqs_pending); -+ __set_bit(irq, vcpu->arch.irqs_pending_mask); -+ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); - - return 0; - } - - bool kvm_riscv_vcpu_has_interrupts(struct kvm_vcpu *vcpu, u64 mask) - { -+ unsigned long flags; - unsigned long ie; -+ bool ret; - -+ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); - ie = ((vcpu->arch.guest_csr.vsie & VSIP_VALID_MASK) - << VSIP_TO_HVIP_SHIFT) & (unsigned long)mask; - ie |= vcpu->arch.guest_csr.vsie & ~IRQ_LOCAL_MASK & - (unsigned long)mask; -- if (READ_ONCE(vcpu->arch.irqs_pending[0]) & ie) -- return true; -+ ret = vcpu->arch.irqs_pending[0] & ie; -+ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); - - /* Check AIA high interrupts */ -- return kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask); -+ if (!ret) -+ ret = kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask); -+ -+ return ret; - } - - void __kvm_riscv_vcpu_power_off(struct kvm_vcpu *vcpu) ---- a/arch/riscv/kvm/vcpu_onereg.c -+++ b/arch/riscv/kvm/vcpu_onereg.c -@@ -276,6 +276,7 @@ static int kvm_riscv_vcpu_general_get_cs - { - struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; - unsigned long regs_max = sizeof(struct kvm_riscv_csr) / sizeof(unsigned long); -+ unsigned long flags; - - if (reg_num >= regs_max) - return -ENOENT; -@@ -311,8 +312,11 @@ static int kvm_riscv_vcpu_general_set_cs - - ((unsigned long *)csr)[reg_num] = reg_val; - -- if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) -- WRITE_ONCE(vcpu->arch.irqs_pending_mask[0], 0); -+ if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) { -+ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); -+ vcpu->arch.irqs_pending_mask[0] = 0; -+ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); -+ } - - return 0; - } diff --git a/queue-7.1/series b/queue-7.1/series index 4cf7a6c4d5..a1786d4dc9 100644 --- a/queue-7.1/series +++ b/queue-7.1/series @@ -2,7 +2,6 @@ platform-x86-intel-uncore-freq-fix-current_freq_khz-.patch sched_ext-skip-ops.set_weight-for-disabled-tasks.patch drm-virtio-fix-deadlock-in-display_info_cb-by-removi.patch seqlock-allow-ubsan_alignment-to-fail-optimizing.patch -risc-v-kvm-serialize-virtual-interrupt-pending-state-updates.patch kvm-x86-check-for-invalid-obsolete-root-after-making-mmu-pages-available.patch kvm-x86-only-reset-tsc-deadline-timer-in-apic_timer_expired-on-kvm_run.patch kvm-nvmx-hide-shadow-vmcs-right-after-vmclear.patch