The software assumes that when mbigen active is set to 1, the mbigen has already sent the write request interrupt to the ITS. However, the hardware actually goes through two stages. In the first stage, the MBIX output is set to 1, indicating that there is a write request that needs to be signaled, and at this point, the mbigen active is set to 1. In the second stage, the mbix_wr signals the write request. If the software clears the active bit by writing to MBIX_INT_CLR between the first and second stages, the write request will also be cleared. However, the software has already saved the active=1 state, which results in an incorrect state. Fixes: 78feb450c1fb ("KVM: arm64: GICv4.1: Add support for MBIGEN save/restore") Signed-off-by: Jinqian Yang <yangjinqian1@huawei.com> --- arch/arm64/kvm/arch_timer.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index 55c800c095c0..ed7957e4466c 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -771,8 +771,19 @@ static void kvm_vtimer_mbigen_restore_stat(struct kvm_vcpu *vcpu) vtimer_mbigen_set_vector(vcpu->cpu, vpeid); - if (mbigen_ctx->active) - vtimer_mbigen_set_active(vcpu->cpu, true); + /* + * There exists a corner case in kvm_timer_vcpu_load/put. The + * vtimer has expired, mbigen is active, but the message has + * not been sent to the ITS. Therefore, if we restore the mbigen + * active state, it cannot be cleared permanently and all subsequent + * vtimer interrupts will be blocked. To avoid this, we choose not + * to restore it. If the guest kernel is Linux, there is no problem + * doing this. + * This solution may cause an extra interrupt to be sent. This extra + * interrupt may be merged with the previous pending interrupt, or + * the extra interrupt may be cleared when the Linux guest is processing + * the previous interrupt. + */ mbigen_ctx->loaded = true; out: @@ -1047,10 +1058,6 @@ static void kvm_vtimer_mbigen_save_stat(struct kvm_vcpu *vcpu) mbigen_ctx->active = vtimer_mbigen_get_active(vcpu->cpu); - /* Clear active state in MBIGEN now that we've saved everything. */ - if (mbigen_ctx->active) - vtimer_mbigen_set_active(vcpu->cpu, false); - mbigen_ctx->loaded = false; out: local_irq_restore(flags); @@ -1086,6 +1093,10 @@ void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu) #ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS if (vtimer_is_irqbypass()) { kvm_vtimer_mbigen_save_stat(vcpu); + /* + * After enabling the automatic clearing of mbigen active state, + * hardware ensures that the active state is cleared here. + */ kvm_vtimer_mbigen_auto_clr_set(vcpu, true); } #endif -- 2.33.0