[PATCH] RISC-V: KVM: Change imsic->vsfile_lock from rwlock_t to raw_spinlock_t

Anup Patel anup at brainfault.org
Sun Mar 1 22:44:32 PST 2026


On Sat, Jan 31, 2026 at 8:28 AM Jiakai Xu <xujiakai2025 at iscas.ac.cn> wrote:
>
> The per-vCPU IMSIC context uses a vsfile_lock to protect access
> to the VS-file. Currently, this lock is an rwlock_t, and is used
> with read_lock_irqsave/write_lock_irqsave in multiple places
> inside arch/riscv/kvm/aia_imsic.c.
>
> During fuzz testing of KVM ioctl sequences, an
> "[BUG: Invalid wait context]" crash was observed when holding
> vsfile_lock in certain VCPU scheduling paths, for example during
> kvm_riscv_vcpu_aia_imsic_put(). Log shows that at this point
> the task may hold vcpu->mutex and scheduler runqueue locks,
> and thus is in a context where acquiring a read/write rwlock
> with irqsave is illegal.
>
> The crash manifests as:
>   [ BUG: Invalid wait context ]
>   (&imsic->vsfile_lock){....}-{3:3}, at:
>   kvm_riscv_vcpu_aia_imsic_put arch/riscv/kvm/aia_imsic.c:728
>   ...
>   2 locks held by syz.4.4541/8252:
>    #0: (&vcpu->mutex), at: kvm_vcpu_ioctl virt/kvm/kvm_main.c:4460
>    #1: (&rq->__lock), at: raw_spin_rq_lock_nested kernel/sched/core.c:639
>    #1: (&rq->__lock), at: raw_spin_rq_lock kernel/sched/sched.h:1580
>    #1: (&rq->__lock), at: rq_lock kernel/sched/sched.h:1907
>    #1: (&rq->__lock), at: __schedule kernel/sched/core.c:6772
>   ...
>   Call Trace:
>    _raw_read_lock_irqsave kernel/locking/spinlock.c:236
>    kvm_riscv_vcpu_aia_imsic_put arch/riscv/kvm/aia_imsic.c:716
>    kvm_riscv_vcpu_aia_put arch/riscv/kvm/aia.c:154
>    kvm_arch_vcpu_put arch/riscv/kvm/vcpu.c:650
>    kvm_sched_out virt/kvm/kvm_main.c:6421
>    __fire_sched_out_preempt_notifiers kernel/sched/core.c:4835
>    fire_sched_out_preempt_notifiers kernel/sched/core.c:4843
>    prepare_task_switch kernel/sched/core.c:5050
>    context_switch kernel/sched/core.c:5205
>    __schedule kernel/sched/core.c:6867
>    __schedule_loop kernel/sched/core.c:6949
>    schedule kernel/sched/core.c:6964
>    kvm_riscv_check_vcpu_requests arch/riscv/kvm/vcpu.c:699
>    kvm_arch_vcpu_ioctl_run arch/riscv/kvm/vcpu.c:920
>
> Therefore, replace vsfile_lock with raw_spinlock_t, and update
> all acquire/release calls to
> raw_spin_lock_irqsave()/raw_spin_unlock_irqrestore().
>
> Fixes: db8b7e97d6137a ("RISC-V: KVM: Add in-kernel virtualization of AIA IMSIC")
> Signed-off-by: Jiakai Xu <xujiakai2025 at iscas.ac.cn>
> Signed-off-by: Jiakai Xu <jiakaiPeanut at gmail.com>

This patch breaks KVM guest booting when the underlying
KVM host does not have any guest files.
E.g. "qemu-system-riscv64 -M virt,aia=aplic-imsic ..."

Regards,
Anup

> ---
>  arch/riscv/kvm/aia_imsic.c | 36 ++++++++++++++++++------------------
>  1 file changed, 18 insertions(+), 18 deletions(-)
>
> diff --git a/arch/riscv/kvm/aia_imsic.c b/arch/riscv/kvm/aia_imsic.c
> index fda0346f0ea1f..8730229442a26 100644
> --- a/arch/riscv/kvm/aia_imsic.c
> +++ b/arch/riscv/kvm/aia_imsic.c
> @@ -47,7 +47,7 @@ struct imsic {
>          */
>
>         /* IMSIC VS-file */
> -       rwlock_t vsfile_lock;
> +       raw_spinlock_t vsfile_lock;
>         int vsfile_cpu;
>         int vsfile_hgei;
>         void __iomem *vsfile_va;
> @@ -597,13 +597,13 @@ static void imsic_vsfile_cleanup(struct imsic *imsic)
>          * VCPU is being destroyed.
>          */
>
> -       write_lock_irqsave(&imsic->vsfile_lock, flags);
> +       raw_spin_lock_irqsave(&imsic->vsfile_lock, flags);
>         old_vsfile_hgei = imsic->vsfile_hgei;
>         old_vsfile_cpu = imsic->vsfile_cpu;
>         imsic->vsfile_cpu = imsic->vsfile_hgei = -1;
>         imsic->vsfile_va = NULL;
>         imsic->vsfile_pa = 0;
> -       write_unlock_irqrestore(&imsic->vsfile_lock, flags);
> +       raw_spin_unlock_irqrestore(&imsic->vsfile_lock, flags);
>
>         memset(imsic->swfile, 0, sizeof(*imsic->swfile));
>
> @@ -688,10 +688,10 @@ bool kvm_riscv_vcpu_aia_imsic_has_interrupt(struct kvm_vcpu *vcpu)
>          * only check for interrupt when IMSIC VS-file is being used.
>          */
>
> -       read_lock_irqsave(&imsic->vsfile_lock, flags);
> +       raw_spin_lock_irqsave(&imsic->vsfile_lock, flags);
>         if (imsic->vsfile_cpu > -1)
>                 ret = !!(csr_read(CSR_HGEIP) & BIT(imsic->vsfile_hgei));
> -       read_unlock_irqrestore(&imsic->vsfile_lock, flags);
> +       raw_spin_unlock_irqrestore(&imsic->vsfile_lock, flags);
>
>         return ret;
>  }
> @@ -713,10 +713,10 @@ void kvm_riscv_vcpu_aia_imsic_put(struct kvm_vcpu *vcpu)
>         if (!kvm_vcpu_is_blocking(vcpu))
>                 return;
>
> -       read_lock_irqsave(&imsic->vsfile_lock, flags);
> +       raw_spin_lock_irqsave(&imsic->vsfile_lock, flags);
>         if (imsic->vsfile_cpu > -1)
>                 csr_set(CSR_HGEIE, BIT(imsic->vsfile_hgei));
> -       read_unlock_irqrestore(&imsic->vsfile_lock, flags);
> +       raw_spin_unlock_irqrestore(&imsic->vsfile_lock, flags);
>  }
>
>  void kvm_riscv_vcpu_aia_imsic_release(struct kvm_vcpu *vcpu)
> @@ -727,13 +727,13 @@ void kvm_riscv_vcpu_aia_imsic_release(struct kvm_vcpu *vcpu)
>         struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
>
>         /* Read and clear IMSIC VS-file details */
> -       write_lock_irqsave(&imsic->vsfile_lock, flags);
> +       raw_spin_lock_irqsave(&imsic->vsfile_lock, flags);
>         old_vsfile_hgei = imsic->vsfile_hgei;
>         old_vsfile_cpu = imsic->vsfile_cpu;
>         imsic->vsfile_cpu = imsic->vsfile_hgei = -1;
>         imsic->vsfile_va = NULL;
>         imsic->vsfile_pa = 0;
> -       write_unlock_irqrestore(&imsic->vsfile_lock, flags);
> +       raw_spin_unlock_irqrestore(&imsic->vsfile_lock, flags);
>
>         /* Do nothing, if no IMSIC VS-file to release */
>         if (old_vsfile_cpu < 0)
> @@ -786,10 +786,10 @@ int kvm_riscv_vcpu_aia_imsic_update(struct kvm_vcpu *vcpu)
>                 return 1;
>
>         /* Read old IMSIC VS-file details */
> -       read_lock_irqsave(&imsic->vsfile_lock, flags);
> +       raw_spin_lock_irqsave(&imsic->vsfile_lock, flags);
>         old_vsfile_hgei = imsic->vsfile_hgei;
>         old_vsfile_cpu = imsic->vsfile_cpu;
> -       read_unlock_irqrestore(&imsic->vsfile_lock, flags);
> +       raw_spin_unlock_irqrestore(&imsic->vsfile_lock, flags);
>
>         /* Do nothing if we are continuing on same CPU */
>         if (old_vsfile_cpu == vcpu->cpu)
> @@ -839,12 +839,12 @@ int kvm_riscv_vcpu_aia_imsic_update(struct kvm_vcpu *vcpu)
>         /* TODO: Update the IOMMU mapping ??? */
>
>         /* Update new IMSIC VS-file details in IMSIC context */
> -       write_lock_irqsave(&imsic->vsfile_lock, flags);
> +       raw_spin_lock_irqsave(&imsic->vsfile_lock, flags);
>         imsic->vsfile_hgei = new_vsfile_hgei;
>         imsic->vsfile_cpu = vcpu->cpu;
>         imsic->vsfile_va = new_vsfile_va;
>         imsic->vsfile_pa = new_vsfile_pa;
> -       write_unlock_irqrestore(&imsic->vsfile_lock, flags);
> +       raw_spin_unlock_irqrestore(&imsic->vsfile_lock, flags);
>
>         /*
>          * At this point, all interrupt producers have been moved
> @@ -943,7 +943,7 @@ int kvm_riscv_aia_imsic_rw_attr(struct kvm *kvm, unsigned long type,
>         isel = KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(type);
>         imsic = vcpu->arch.aia_context.imsic_state;
>
> -       read_lock_irqsave(&imsic->vsfile_lock, flags);
> +       raw_spin_lock_irqsave(&imsic->vsfile_lock, flags);
>
>         rc = 0;
>         vsfile_hgei = imsic->vsfile_hgei;
> @@ -958,7 +958,7 @@ int kvm_riscv_aia_imsic_rw_attr(struct kvm *kvm, unsigned long type,
>                                             isel, val, 0, 0);
>         }
>
> -       read_unlock_irqrestore(&imsic->vsfile_lock, flags);
> +       raw_spin_unlock_irqrestore(&imsic->vsfile_lock, flags);
>
>         if (!rc && vsfile_cpu >= 0)
>                 rc = imsic_vsfile_rw(vsfile_hgei, vsfile_cpu, imsic->nr_eix,
> @@ -1015,7 +1015,7 @@ int kvm_riscv_vcpu_aia_imsic_inject(struct kvm_vcpu *vcpu,
>         if (imsic->nr_msis <= iid)
>                 return -EINVAL;
>
> -       read_lock_irqsave(&imsic->vsfile_lock, flags);
> +       raw_spin_lock_irqsave(&imsic->vsfile_lock, flags);
>
>         if (imsic->vsfile_cpu >= 0) {
>                 writel(iid, imsic->vsfile_va + IMSIC_MMIO_SETIPNUM_LE);
> @@ -1025,7 +1025,7 @@ int kvm_riscv_vcpu_aia_imsic_inject(struct kvm_vcpu *vcpu,
>                 imsic_swfile_extirq_update(vcpu);
>         }
>
> -       read_unlock_irqrestore(&imsic->vsfile_lock, flags);
> +       raw_spin_unlock_irqrestore(&imsic->vsfile_lock, flags);
>
>         return 0;
>  }
> @@ -1081,7 +1081,7 @@ int kvm_riscv_vcpu_aia_imsic_init(struct kvm_vcpu *vcpu)
>
>         /* Setup IMSIC context  */
>         imsic->nr_msis = kvm->arch.aia.nr_ids + 1;
> -       rwlock_init(&imsic->vsfile_lock);
> +       raw_spin_lock_init(&imsic->vsfile_lock);
>         imsic->nr_eix = BITS_TO_U64(imsic->nr_msis);
>         imsic->nr_hw_eix = BITS_TO_U64(kvm_riscv_aia_max_ids);
>         imsic->vsfile_hgei = imsic->vsfile_cpu = -1;
> --
> 2.34.1
>



More information about the linux-riscv mailing list