Skip to content

Commit 4ad9843

Browse files
yong-xuanavpatel
authored andcommitted
RISCV: KVM: update external interrupt atomically for IMSIC swfile
The emulated IMSIC update the external interrupt pending depending on the value of eidelivery and topei. It might lose an interrupt when it is interrupted before setting the new value to the pending status. For example, when VCPU0 sends an IPI to VCPU1 via IMSIC: VCPU0 VCPU1 CSRSWAP topei = 0 The VCPU1 has claimed all the external interrupt in its interrupt handler. topei of VCPU1's IMSIC = 0 set pending in VCPU1's IMSIC topei of VCPU1' IMSIC = 1 set the external interrupt pending of VCPU1 clear the external interrupt pending of VCPU1 When the VCPU1 switches back to VS mode, it exits the interrupt handler because the result of CSRSWAP topei is 0. If there are no other external interrupts injected into the VCPU1's IMSIC, VCPU1 will never know this pending interrupt unless it initiative read the topei. If the interruption occurs between updating interrupt pending in IMSIC and updating external interrupt pending of VCPU, it will not cause a problem. Suppose that the VCPU1 clears the IPI pending in IMSIC right after VCPU0 sets the pending, the external interrupt pending of VCPU1 will not be set because the topei is 0. But when the VCPU1 goes back to VS mode, the pending IPI will be reported by the CSRSWAP topei, it will not lose this interrupt. So we only need to make the external interrupt updating procedure as a critical section to avoid the problem. Fixes: db8b7e9 ("RISC-V: KVM: Add in-kernel virtualization of AIA IMSIC") Tested-by: Roy Lin <roy.lin@sifive.com> Tested-by: Wayling Chen <wayling.chen@sifive.com> Co-developed-by: Vincent Chen <vincent.chen@sifive.com> Signed-off-by: Vincent Chen <vincent.chen@sifive.com> Signed-off-by: Yong-Xuan Wang <yongxuan.wang@sifive.com> Signed-off-by: Anup Patel <anup@brainfault.org>
1 parent 3279f52 commit 4ad9843

File tree

1 file changed

+13
-0
lines changed

1 file changed

+13
-0
lines changed

arch/riscv/kvm/aia_imsic.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ struct imsic {
5555
/* IMSIC SW-file */
5656
struct imsic_mrif *swfile;
5757
phys_addr_t swfile_pa;
58+
spinlock_t swfile_extirq_lock;
5859
};
5960

6061
#define imsic_vs_csr_read(__c) \
@@ -613,12 +614,23 @@ static void imsic_swfile_extirq_update(struct kvm_vcpu *vcpu)
613614
{
614615
struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
615616
struct imsic_mrif *mrif = imsic->swfile;
617+
unsigned long flags;
618+
619+
/*
620+
* The critical section is necessary during external interrupt
621+
* updates to avoid the risk of losing interrupts due to potential
622+
* interruptions between reading topei and updating pending status.
623+
*/
624+
625+
spin_lock_irqsave(&imsic->swfile_extirq_lock, flags);
616626

617627
if (imsic_mrif_atomic_read(mrif, &mrif->eidelivery) &&
618628
imsic_mrif_topei(mrif, imsic->nr_eix, imsic->nr_msis))
619629
kvm_riscv_vcpu_set_interrupt(vcpu, IRQ_VS_EXT);
620630
else
621631
kvm_riscv_vcpu_unset_interrupt(vcpu, IRQ_VS_EXT);
632+
633+
spin_unlock_irqrestore(&imsic->swfile_extirq_lock, flags);
622634
}
623635

624636
static void imsic_swfile_read(struct kvm_vcpu *vcpu, bool clear,
@@ -1039,6 +1051,7 @@ int kvm_riscv_vcpu_aia_imsic_init(struct kvm_vcpu *vcpu)
10391051
}
10401052
imsic->swfile = page_to_virt(swfile_page);
10411053
imsic->swfile_pa = page_to_phys(swfile_page);
1054+
spin_lock_init(&imsic->swfile_extirq_lock);
10421055

10431056
/* Setup IO device */
10441057
kvm_iodevice_init(&imsic->iodev, &imsic_iodoev_ops);

0 commit comments

Comments
 (0)