From nobody Sat Jul 25 22:03:25 2026 Received: from ultrarisc.com (unknown [218.76.62.146]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 32907365A00; Mon, 13 Jul 2026 06:49:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=218.76.62.146 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783925351; cv=none; b=rIrBo/+ICBtCDsHnxBC2sowbB5qiRK1ZmdtiqdV4XmD8GBKjMozu9OFkt+y1SFnn+PiQFYfEY16qHqjgYU3NrahN+xp7C1oE3j3XdJ6lI3bSrEGhaXojZfWeJXluxD83cPFOgTtyPc6AxQrvkCOa0rB+gqjRz2XsotgGGJGYhyg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783925351; c=relaxed/simple; bh=5Y4VkJrG24PiosnX8nGnwu9JcBTbmcHmwQ9uvQQLozE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=sg3jfS6np8qet1RPluNLfgrPXuaWbnObHyPxNTavXhOCWxV1gz6MvTs3u+5jRnczUKokCjZkmEgG0UUkmeIrZ3vifgkq2UzbVM2FeqKSKj+fSFK8E38t0Nk4a4HSd2vMYlRu7Bi4MqZaWzEAel8Pjzmuyo03Mh1uCtF7he+75P0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=ultrarisc.com; spf=pass smtp.mailfrom=ultrarisc.com; dkim=pass (1024-bit key) header.d=ultrarisc.com header.i=@ultrarisc.com header.b=gh0b/b81; arc=none smtp.client-ip=218.76.62.146 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=ultrarisc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ultrarisc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ultrarisc.com header.i=@ultrarisc.com header.b="gh0b/b81" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ultrarisc.com; s=dkim; h=Received:From:To:Cc:Subject:Date: Message-ID:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=pFCPAIT5wnM6AmHjwsAYttZ3JDK0jcAG08 rQhmOc01E=; b=gh0b/b81zhhdTMgdGjKzSwZkxD1zLqFjYJEqamQG3lYjqlgPqs phOaIyV1hLmojCAdiDieza3cV8L5xBcgD0XM8MnGFeGuZ6N3to1M5s9oHPRX8BOy Jq5vtVXzTckeBZwxrGorj9OiyqFhJU8ri1uH5E4LkZLtNJ6d4raFkJfHs= Received: from ur-dp1000 (unknown [192.168.100.1]) by localhost.localdomain (Coremail) with SMTP id AQAAfwA3cUJOilRqfnsQAA--.16812S3; Mon, 13 Jul 2026 14:49:10 +0800 (CST) From: Xie Bo To: Anup Patel Cc: Atish Patra , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Paolo Bonzini , Alexander Graf , kvm-riscv@lists.infradead.org, kvm@vger.kernel.org, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, stable@vger.kernel.org, Xie Bo Subject: [PATCH v3] RISC-V: KVM: Serialize virtual interrupt pending state updates Date: Mon, 13 Jul 2026 14:48:15 +0800 Message-ID: <20260713064815.1276212-2-xb@ultrarisc.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260713064815.1276212-1-xb@ultrarisc.com> References: <178159067899.108868.8176174463274678253@ultrarisc.com> <20260713064815.1276212-1-xb@ultrarisc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: AQAAfwA3cUJOilRqfnsQAA--.16812S3 X-Coremail-Antispam: 1UD129KBjvAXoW3KFyrJF4UXF45Wr45JFW3trb_yoW8Gw17uo WfGF4vvay8Gr1rGrZ8Z3y2ga48W34vgw4xXa1FyFWrZr1UX343XFyUKrsxXFy3JF45WF9F ya45Xa13GFZrJFyDn29KB7ZKAUJUUUU8529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UjIYCTnIWjp_UUUOW7AC8VAFwI0_Wr0E3s1l1xkIjI8I6I8E6xAIw20EY4v20xva j40_Wr0E3s1l1IIY67AEw4v_Jr0_Jr4l82xGYIkIc2x26280x7IE14v26r18M28IrcIa0x kI8VCY1x0267AKxVW8JVW5JwA2ocxC64kIII0Yj41l84x0c7CEw4AK67xGY2AK021l84AC jcxK6xIIjxv20xvE14v26r1j6r1xM28EF7xvwVC0I7IYx2IY6xkF7I0E14v26r4j6F4UM2 8EF7xvwVC2z280aVAFwI0_Gr0_Cr1l84ACjcxK6I8E87Iv6xkF7I0E14v26r4UJVWxJr1l e2I262IYc4CY6c8Ij28IcVAaY2xG8wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E2Ix0cI 8IcVAFwI0_Jrv_JF1lYx0Ex4A2jsIE14v26r1j6r4UMcvjeVCFs4IE7xkEbVWUJVW8JwAC jcxG0xvY0x0EwIxGrwACjI8F5VA0II8E6IAqYI8I648v4I1lFIxGxcIEc7CjxVA2Y2ka0x kIwI1lc7CjxVAaw2AFwI0_Jw0_GFylc2xSY4AK6svPMxAIw28IcxkI7VAKI48JMxAqzxv2 6xkF7I0En4kS14v26r1q6r43MxC20s026xCaFVCjc4AY6r1j6r4UMI8I3I0E5I8CrVAFwI 0_Jr0_Jr4lx2IqxVCjr7xvwVAFwI0_JrI_JrWlx4CE17CEb7AF67AKxVWUtVW8ZwCIc40Y 0x0EwIxGrwCI42IY6xIIjxv20xvE14v26r1j6r1xMIIF0xvE2Ix0cI8IcVCY1x0267AKxV W8JVWxJwCI42IY6xAIw20EY4v20xvaj40_Jr0_JF4lIxAIcVC2z280aVAFwI0_Jr0_Gr1l IxAIcVC2z280aVCY1x0267AKxVW8JVW8JrUvcSsGvfC2KfnxnUUI43ZEXa7VUjVMNUUUUU U== X-CM-SenderInfo: l0e63zxwud2x1vfou0bp/1tbiAQAHB2pUYNAAEwADsf Content-Type: text/plain; charset="utf-8" RISC-V KVM tracks guest 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 clears 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 h= andling") Cc: stable@vger.kernel.org Signed-off-by: Xie Bo --- Changes in v3: - Rebase onto Linux 7.2-rc3. - Use non-atomic bitmap operations under irqs_pending_lock. - Hold irqs_pending_lock across the AIA sync/flush helpers and add lockdep assertions for their locking contract. - Protect kvm_riscv_vcpu_has_interrupts() with irqs_pending_lock. Changes in v2: - Expand the race description and user-visible failure mode. arch/riscv/include/asm/kvm_host.h | 10 ++--- arch/riscv/kvm/aia.c | 33 ++++++++++++---- arch/riscv/kvm/vcpu.c | 63 +++++++++++++++++++++---------- arch/riscv/kvm/vcpu_onereg.c | 13 +++++-- 4 files changed, 82 insertions(+), 37 deletions(-) diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm= _host.h index 60017ceec9d..e2d5808169e 100644 --- a/arch/riscv/include/asm/kvm_host.h +++ b/arch/riscv/include/asm/kvm_host.h @@ -209,13 +209,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); =20 diff --git a/arch/riscv/kvm/aia.c b/arch/riscv/kvm/aia.c index bafb009c5ce..225e4aed81a 100644 --- a/arch/riscv/kvm/aia.c +++ b/arch/riscv/kvm/aia.c @@ -53,12 +53,15 @@ void kvm_riscv_vcpu_aia_flush_interrupts(struct kvm_vcp= u *vcpu) struct kvm_vcpu_aia_csr *csr =3D &vcpu->arch.aia_context.guest_csr; unsigned long mask, val; =20 + lockdep_assert_held(&vcpu->arch.irqs_pending_lock); + if (!kvm_riscv_aia_available()) return; =20 - if (READ_ONCE(vcpu->arch.irqs_pending_mask[1])) { - mask =3D xchg_acquire(&vcpu->arch.irqs_pending_mask[1], 0); - val =3D READ_ONCE(vcpu->arch.irqs_pending[1]) & mask; + mask =3D vcpu->arch.irqs_pending_mask[1]; + if (mask) { + vcpu->arch.irqs_pending_mask[1] =3D 0; + val =3D vcpu->arch.irqs_pending[1] & mask; =20 csr->hviph &=3D ~mask; csr->hviph |=3D val; @@ -69,6 +72,8 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *= vcpu) { struct kvm_vcpu_aia_csr *csr =3D &vcpu->arch.aia_context.guest_csr; =20 + lockdep_assert_held(&vcpu->arch.irqs_pending_lock); + if (kvm_riscv_aia_available()) csr->vsieh =3D ncsr_read(CSR_VSIEH); } @@ -78,11 +83,13 @@ bool kvm_riscv_vcpu_aia_has_interrupts(struct kvm_vcpu = *vcpu, u64 mask) { unsigned long seip; =20 + lockdep_assert_held(&vcpu->arch.irqs_pending_lock); + if (!kvm_riscv_aia_available()) return false; =20 #ifdef CONFIG_32BIT - if (READ_ONCE(vcpu->arch.irqs_pending[1]) & + if (vcpu->arch.irqs_pending[1] & (vcpu->arch.aia_context.guest_csr.vsieh & upper_32_bits(mask))) return true; #endif @@ -207,6 +214,9 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu, { struct kvm_vcpu_aia_csr *csr =3D &vcpu->arch.aia_context.guest_csr; unsigned long regs_max =3D sizeof(struct kvm_riscv_aia_csr) / sizeof(unsi= gned long); +#ifdef CONFIG_32BIT + unsigned long flags; +#endif =20 if (!riscv_isa_extension_available(vcpu->arch.isa, SSAIA)) return -ENOENT; @@ -216,11 +226,18 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu, reg_num =3D array_index_nospec(reg_num, regs_max); =20 if (kvm_riscv_aia_available()) { - ((unsigned long *)csr)[reg_num] =3D val; - #ifdef CONFIG_32BIT - if (reg_num =3D=3D KVM_REG_RISCV_CSR_AIA_REG(siph)) - WRITE_ONCE(vcpu->arch.irqs_pending_mask[1], 0); + if (reg_num =3D=3D KVM_REG_RISCV_CSR_AIA_REG(siph)) { + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + ((unsigned long *)csr)[reg_num] =3D val; + vcpu->arch.irqs_pending_mask[1] =3D 0; + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, + flags); + } else { + ((unsigned long *)csr)[reg_num] =3D val; + } +#else + ((unsigned long *)csr)[reg_num] =3D val; #endif } =20 diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c index cf6e231e76e..7d8d20839d4 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -80,6 +80,7 @@ static void kvm_riscv_vcpu_context_reset(struct kvm_vcpu = *vcpu, =20 static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset) { + unsigned long flags; bool loaded; =20 /** @@ -104,8 +105,10 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu= , bool kvm_sbi_reset) =20 kvm_riscv_vcpu_aia_reset(vcpu); =20 + 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); =20 kvm_riscv_vcpu_pmu_reset(vcpu); =20 @@ -151,6 +154,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) =20 /* Setup VCPU hfence queue */ spin_lock_init(&vcpu->arch.hfence_lock); + raw_spin_lock_init(&vcpu->arch.irqs_pending_lock); =20 spin_lock_init(&vcpu->arch.reset_state.lock); =20 @@ -352,10 +356,13 @@ void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu = *vcpu) { struct kvm_vcpu_csr *csr =3D &vcpu->arch.guest_csr; unsigned long mask, val; + unsigned long flags; =20 - if (READ_ONCE(vcpu->arch.irqs_pending_mask[0])) { - mask =3D xchg_acquire(&vcpu->arch.irqs_pending_mask[0], 0); - val =3D READ_ONCE(vcpu->arch.irqs_pending[0]) & mask; + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + mask =3D vcpu->arch.irqs_pending_mask[0]; + if (mask) { + vcpu->arch.irqs_pending_mask[0] =3D 0; + val =3D vcpu->arch.irqs_pending[0] & mask; =20 csr->hvip &=3D ~mask; csr->hvip |=3D val; @@ -363,11 +370,13 @@ void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu = *vcpu) =20 /* Flush AIA high interrupts */ kvm_riscv_vcpu_aia_flush_interrupts(vcpu); + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); } =20 void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu) { unsigned long hvip; + unsigned long flags; struct kvm_vcpu_arch *v =3D &vcpu->arch; struct kvm_vcpu_csr *csr =3D &vcpu->arch.guest_csr; =20 @@ -376,27 +385,29 @@ void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *= vcpu) =20 /* Sync-up HVIP.VSSIP bit changes does by Guest */ hvip =3D 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); } } =20 /* 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); } =20 /* Sync-up AIA high interrupts */ kvm_riscv_vcpu_aia_sync_interrupts(vcpu); + raw_spin_unlock_irqrestore(&v->irqs_pending_lock, flags); =20 /* Sync-up timer CSRs */ kvm_riscv_vcpu_timer_sync(vcpu); @@ -404,6 +415,8 @@ void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vc= pu) =20 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 +429,10 @@ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu= , unsigned int irq) irq !=3D IRQ_PMU_OVF) return -EINVAL; =20 - 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); =20 kvm_vcpu_kick(vcpu); =20 @@ -427,6 +441,8 @@ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu,= unsigned int irq) =20 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 +455,33 @@ int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *v= cpu, unsigned int irq) irq !=3D IRQ_PMU_OVF) return -EINVAL; =20 - 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); =20 return 0; } =20 bool kvm_riscv_vcpu_has_interrupts(struct kvm_vcpu *vcpu, u64 mask) { + unsigned long flags; unsigned long ie; + bool ret; =20 + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); ie =3D ((vcpu->arch.guest_csr.vsie & VSIP_VALID_MASK) << VSIP_TO_HVIP_SHIFT) & (unsigned long)mask; ie |=3D vcpu->arch.guest_csr.vsie & ~IRQ_LOCAL_MASK & (unsigned long)mask; - if (READ_ONCE(vcpu->arch.irqs_pending[0]) & ie) - return true; + ret =3D vcpu->arch.irqs_pending[0] & ie; =20 /* Check AIA high interrupts */ - return kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask); + if (!ret) + ret =3D kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask); + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); + + return ret; } =20 void __kvm_riscv_vcpu_power_off(struct kvm_vcpu *vcpu) diff --git a/arch/riscv/kvm/vcpu_onereg.c b/arch/riscv/kvm/vcpu_onereg.c index bb920e8923c..cba3682944b 100644 --- a/arch/riscv/kvm/vcpu_onereg.c +++ b/arch/riscv/kvm/vcpu_onereg.c @@ -298,6 +298,7 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vc= pu *vcpu, { struct kvm_vcpu_csr *csr =3D &vcpu->arch.guest_csr; unsigned long regs_max =3D sizeof(struct kvm_riscv_csr) / sizeof(unsigned= long); + unsigned long flags; =20 if (reg_num >=3D regs_max) return -ENOENT; @@ -309,10 +310,14 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_= vcpu *vcpu, reg_val <<=3D VSIP_TO_HVIP_SHIFT; } =20 - ((unsigned long *)csr)[reg_num] =3D reg_val; - - if (reg_num =3D=3D KVM_REG_RISCV_CSR_REG(sip)) - WRITE_ONCE(vcpu->arch.irqs_pending_mask[0], 0); + if (reg_num =3D=3D KVM_REG_RISCV_CSR_REG(sip)) { + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + ((unsigned long *)csr)[reg_num] =3D reg_val; + vcpu->arch.irqs_pending_mask[0] =3D 0; + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); + } else { + ((unsigned long *)csr)[reg_num] =3D reg_val; + } =20 return 0; } --=20 2.54.0