From 7379853e0cc91fd760ef3247fc352f7610f16eda Mon Sep 17 00:00:00 2001 From: Oleksii Kurochko Date: Fri, 6 Mar 2026 17:33:21 +0100 Subject: [PATCH] xen/riscv: introduce tracking of pending vCPU interrupts, part 2 This patch is based on Linux kernel 6.16.0. Add the consumer side (vcpu_flush_interrupts()) of the lockless pending interrupt tracking introduced in part 1 (for producers). According, to the design only one consumer is possible, and it is vCPU itself. vcpu_flush_interrupts() is expected to be ran (as guests aren't ran now due to the lack of functionality) before the hypervisor returns control to the guest. Producers may set bits in irqs_pending_mask without a lock. Clearing bits in irqs_pending_mask is performed only by the consumer via xchg() (with aquire semantics). The consumer must not write to irqs_pending and must not act on bits that are not set in the mask. Otherwise, extra synchronization should be provided. The worst thing which could happen with such approach is that a new pending bit will be set to irqs_pending bitmap during update of hvip variable in vcpu_flush_interrupt() but it isn't problem as the new pending bit won't be lost and just be proceded during the next flush. As AIA specs introduced hviph register which would want to be updated when guest related AIA code vcpu_update_hvip() is introduced instead of just open-code it in vcpu_flush_interrupts(). Signed-off-by: Oleksii Kurochko Acked-by: Jan Beulich --- xen/arch/riscv/domain.c | 27 +++++++++++++++++++++++++++ xen/arch/riscv/include/asm/domain.h | 1 + xen/arch/riscv/traps.c | 2 ++ 3 files changed, 30 insertions(+) diff --git a/xen/arch/riscv/domain.c b/xen/arch/riscv/domain.c index 694a03ab74..9c86581ea2 100644 --- a/xen/arch/riscv/domain.c +++ b/xen/arch/riscv/domain.c @@ -231,6 +231,33 @@ void vcpu_sync_interrupts(struct vcpu *curr) } } +void vcpu_flush_interrupts(struct vcpu *curr) +{ + ASSERT(curr == current); + + if ( ACCESS_ONCE(curr->arch.irqs_pending_mask[0]) ) + { + unsigned long mask = xchg(&curr->arch.irqs_pending_mask[0], 0UL); + unsigned long val = ACCESS_ONCE(curr->arch.irqs_pending[0]) & mask; + register_t *hvip = &curr->arch.hvip; + + *hvip &= ~mask; + *hvip |= val; + + csr_write(CSR_HVIP, *hvip); + } + +#ifdef CONFIG_RISCV_32 + /* + * Flush AIA high interrupts. + * + * It is necessary to do only for CONFIG_RISCV_32 which isn't + * supported now. + */ +# error "Update v->arch.hviph" +#endif +} + static void __init __maybe_unused build_assertions(void) { /* diff --git a/xen/arch/riscv/include/asm/domain.h b/xen/arch/riscv/include/asm/domain.h index 1ecfe18c85..59d23e4f92 100644 --- a/xen/arch/riscv/include/asm/domain.h +++ b/xen/arch/riscv/include/asm/domain.h @@ -115,6 +115,7 @@ int vcpu_set_interrupt(struct vcpu *v, unsigned int irq); int vcpu_unset_interrupt(struct vcpu *v, unsigned int irq); void vcpu_sync_interrupts(struct vcpu *curr); +void vcpu_flush_interrupts(struct vcpu *curr); #endif /* ASM__RISCV__DOMAIN_H */ diff --git a/xen/arch/riscv/traps.c b/xen/arch/riscv/traps.c index 551f886e3a..244264c92a 100644 --- a/xen/arch/riscv/traps.c +++ b/xen/arch/riscv/traps.c @@ -175,6 +175,8 @@ static void check_for_pcpu_work(void) vcpu_sync_interrupts(curr); + vcpu_flush_interrupts(curr); + p2m_handle_vmenter(); }