diff mbox series

[v9,20/22] RISC-V: KVM: Fix race-condition in kvm_riscv_vcpu_sync_interrupts()

Message ID 20191016160649.24622-21-anup.patel@wdc.com (mailing list archive)
State New, archived
Headers show
Series KVM RISC-V Support | expand

Commit Message

Anup Patel Oct. 16, 2019, 4:12 p.m. UTC
Currently, we sync-up Guest VSIP and VSIE CSRs with HW state upon
VM-exit. This helps us track enable/disable state of interrupts
and VSIP.SSIP bit updates by Guest.

Unfortunately, the implementation of kvm_riscv_vcpu_sync_interrupts()
is racey when running SMP Guest on SMP Host because it can happen
that IRQ_S_SOFT is already queued from other Host CPU and we might
accidentally clear a valid pending IRQ_S_SOFT.

To fix this, we use test_and_set_bit() to update irqs_pending_mask
in kvm_riscv_vcpu_sync_interrupts() instead of directly calling
kvm_riscv_vcpu_set/unset_interrupt() functions.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
---
 arch/riscv/kvm/vcpu.c | 23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

Comments

Paolo Bonzini Oct. 21, 2019, 5:27 p.m. UTC | #1
On 16/10/19 18:12, Anup Patel wrote:
> +	/* Read current VSIP and VSIE CSRs */
> +	vsip = csr_read(CSR_VSIP);
> +	csr->vsie = csr_read(CSR_VSIE);
> +
> +	/* Sync-up VSIP.SSIP bit changes does by Guest */
> +	if ((csr->vsip ^ vsip) & (1UL << IRQ_S_SOFT)) {
> +		if (!test_and_set_bit(IRQ_S_SOFT, &v->irqs_pending_mask)) {
> +			if (vsip & (1UL << IRQ_S_SOFT))
> +				set_bit(IRQ_S_SOFT, &v->irqs_pending);
> +			else
> +				clear_bit(IRQ_S_SOFT, &v->irqs_pending);
> +		}

Looks good, but I wonder if this could just be "csr->vsip =
csr_read(CSR_VSIP)", which will be fixed up by flush_interrupts on the
next entry.

Paolo
Anup Patel Oct. 22, 2019, 5:07 a.m. UTC | #2
On Mon, Oct 21, 2019 at 10:57 PM Paolo Bonzini <pbonzini@redhat.com> wrote:
>
> On 16/10/19 18:12, Anup Patel wrote:
> > +     /* Read current VSIP and VSIE CSRs */
> > +     vsip = csr_read(CSR_VSIP);
> > +     csr->vsie = csr_read(CSR_VSIE);
> > +
> > +     /* Sync-up VSIP.SSIP bit changes does by Guest */
> > +     if ((csr->vsip ^ vsip) & (1UL << IRQ_S_SOFT)) {
> > +             if (!test_and_set_bit(IRQ_S_SOFT, &v->irqs_pending_mask)) {
> > +                     if (vsip & (1UL << IRQ_S_SOFT))
> > +                             set_bit(IRQ_S_SOFT, &v->irqs_pending);
> > +                     else
> > +                             clear_bit(IRQ_S_SOFT, &v->irqs_pending);
> > +             }
>
> Looks good, but I wonder if this could just be "csr->vsip =
> csr_read(CSR_VSIP)", which will be fixed up by flush_interrupts on the
> next entry.

It's not just "csr->vsip = csr_read(CSR_VSIP" because "irqs_pending"
bitmap has to be in-sync with Guest updates to VSIP because WFI
trap-n-emulate will check for pending interrupts which in-turn checks
"irqs_pending" bitmap.

Regards,
Anup
diff mbox series

Patch

diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c
index f1a218d3a8cf..844542dd13e4 100644
--- a/arch/riscv/kvm/vcpu.c
+++ b/arch/riscv/kvm/vcpu.c
@@ -662,15 +662,22 @@  void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu)
 
 void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu)
 {
-	vcpu->arch.guest_csr.vsip = csr_read(CSR_VSIP);
-	vcpu->arch.guest_csr.vsie = csr_read(CSR_VSIE);
+	unsigned long vsip;
+	struct kvm_vcpu_arch *v = &vcpu->arch;
+	struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
 
-	/* Guest can directly update VSIP software interrupt bits */
-	if (vcpu->arch.guest_csr.vsip ^ READ_ONCE(vcpu->arch.irqs_pending)) {
-		if (vcpu->arch.guest_csr.vsip & (1UL << IRQ_S_SOFT))
-			kvm_riscv_vcpu_set_interrupt(vcpu, IRQ_S_SOFT);
-		else
-			kvm_riscv_vcpu_unset_interrupt(vcpu, IRQ_S_SOFT);
+	/* Read current VSIP and VSIE CSRs */
+	vsip = csr_read(CSR_VSIP);
+	csr->vsie = csr_read(CSR_VSIE);
+
+	/* Sync-up VSIP.SSIP bit changes does by Guest */
+	if ((csr->vsip ^ vsip) & (1UL << IRQ_S_SOFT)) {
+		if (!test_and_set_bit(IRQ_S_SOFT, &v->irqs_pending_mask)) {
+			if (vsip & (1UL << IRQ_S_SOFT))
+				set_bit(IRQ_S_SOFT, &v->irqs_pending);
+			else
+				clear_bit(IRQ_S_SOFT, &v->irqs_pending);
+		}
 	}
 }