From db5d2b8f5249bad43d607b550365f80bc7cad831 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:39 +0800 Subject: [PATCH 01/16] LoongArch: KVM: Add separate KVM_REQ_LBT_LOAD bit There are different structures with FPU and LBT register restore, with FPU the structure is vcpu::arch::fpu, with LBT the structure is vcpu:: arch::lbt. Moreover, FPU/LSX/LASX saving and restoring share the common structure vcpu::arch::fpu. New request bit KVM_REQ_LBT_LOAD is used for LBT register restore, and rename KVM_REQ_AUX_LOAD with KVM_REQ_FPU_LOAD for FPU register restore. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/include/asm/kvm_host.h | 3 ++- arch/loongarch/kvm/exit.c | 12 +++++------- arch/loongarch/kvm/vcpu.c | 8 ++++---- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h index 776bc487a705..e76a49d4233b 100644 --- a/arch/loongarch/include/asm/kvm_host.h +++ b/arch/loongarch/include/asm/kvm_host.h @@ -38,7 +38,8 @@ #define KVM_REQ_TLB_FLUSH_GPA KVM_ARCH_REQ(0) #define KVM_REQ_STEAL_UPDATE KVM_ARCH_REQ(1) #define KVM_REQ_PMU KVM_ARCH_REQ(2) -#define KVM_REQ_AUX_LOAD KVM_ARCH_REQ(3) +#define KVM_REQ_FPU_LOAD KVM_ARCH_REQ(3) +#define KVM_REQ_LBT_LOAD KVM_ARCH_REQ(4) #define KVM_GUESTDBG_SW_BP_MASK \ (KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP) diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c index 3b95cd0f989b..b839c989d3dc 100644 --- a/arch/loongarch/kvm/exit.c +++ b/arch/loongarch/kvm/exit.c @@ -756,7 +756,7 @@ static int kvm_handle_fpu_disabled(struct kvm_vcpu *vcpu, int ecode) } vcpu->arch.aux_ldtype = KVM_LARCH_FPU; - kvm_make_request(KVM_REQ_AUX_LOAD, vcpu); + kvm_make_request(KVM_REQ_FPU_LOAD, vcpu); return RESUME_GUEST; } @@ -798,7 +798,7 @@ static int kvm_handle_lsx_disabled(struct kvm_vcpu *vcpu, int ecode) kvm_queue_exception(vcpu, EXCCODE_INE, 0); else { vcpu->arch.aux_ldtype = KVM_LARCH_LSX; - kvm_make_request(KVM_REQ_AUX_LOAD, vcpu); + kvm_make_request(KVM_REQ_FPU_LOAD, vcpu); } return RESUME_GUEST; @@ -818,7 +818,7 @@ static int kvm_handle_lasx_disabled(struct kvm_vcpu *vcpu, int ecode) kvm_queue_exception(vcpu, EXCCODE_INE, 0); else { vcpu->arch.aux_ldtype = KVM_LARCH_LASX; - kvm_make_request(KVM_REQ_AUX_LOAD, vcpu); + kvm_make_request(KVM_REQ_FPU_LOAD, vcpu); } return RESUME_GUEST; @@ -828,10 +828,8 @@ static int kvm_handle_lbt_disabled(struct kvm_vcpu *vcpu, int ecode) { if (!kvm_guest_has_lbt(&vcpu->arch)) kvm_queue_exception(vcpu, EXCCODE_INE, 0); - else { - vcpu->arch.aux_ldtype = KVM_LARCH_LBT; - kvm_make_request(KVM_REQ_AUX_LOAD, vcpu); - } + else + kvm_make_request(KVM_REQ_LBT_LOAD, vcpu); return RESUME_GUEST; } diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index e28084c49e68..c28aa37f37a9 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -239,7 +239,7 @@ static void kvm_late_check_requests(struct kvm_vcpu *vcpu) vcpu->arch.flush_gpa = INVALID_GPA; } - if (kvm_check_request(KVM_REQ_AUX_LOAD, vcpu)) { + if (kvm_check_request(KVM_REQ_FPU_LOAD, vcpu)) { switch (vcpu->arch.aux_ldtype) { case KVM_LARCH_FPU: kvm_own_fpu(vcpu); @@ -250,15 +250,15 @@ static void kvm_late_check_requests(struct kvm_vcpu *vcpu) case KVM_LARCH_LASX: kvm_own_lasx(vcpu); break; - case KVM_LARCH_LBT: - kvm_own_lbt(vcpu); - break; default: break; } vcpu->arch.aux_ldtype = 0; } + + if (kvm_check_request(KVM_REQ_LBT_LOAD, vcpu)) + kvm_own_lbt(vcpu); } /* From f1d9dda01aacfae22044c5cb07b50556cfa75eb1 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:40 +0800 Subject: [PATCH 02/16] LoongArch: KVM: Enable FPU with max supported FPU type There are three FPU types FPU/LSX/LASX, which represents FPU64, FPU128 and FPU256, and now lazy FPU method is used with FPU enabling. There are three different HW FPU exception code with different FPU type. The exising method is to enable specified FPU type with responding FPU exeception. Supposing application uses FPU64 and FPU256, there will be FPU256 exception when FPU256 type is used. Here enable FPU with the max VM supported type directly, so it can avoid unnecessary FPU exception in future if further FPU type is used. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/include/asm/kvm_host.h | 1 - arch/loongarch/kvm/exit.c | 9 ++------- arch/loongarch/kvm/vcpu.c | 19 +++++-------------- 3 files changed, 7 insertions(+), 22 deletions(-) diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h index e76a49d4233b..bad16c5f963c 100644 --- a/arch/loongarch/include/asm/kvm_host.h +++ b/arch/loongarch/include/asm/kvm_host.h @@ -204,7 +204,6 @@ struct kvm_vcpu_arch { /* Which auxiliary state is loaded (KVM_LARCH_*) */ unsigned int aux_inuse; - unsigned int aux_ldtype; /* FPU state */ struct loongarch_fpu fpu FPU_ALIGN; diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c index b839c989d3dc..c8492670f169 100644 --- a/arch/loongarch/kvm/exit.c +++ b/arch/loongarch/kvm/exit.c @@ -755,7 +755,6 @@ static int kvm_handle_fpu_disabled(struct kvm_vcpu *vcpu, int ecode) return RESUME_HOST; } - vcpu->arch.aux_ldtype = KVM_LARCH_FPU; kvm_make_request(KVM_REQ_FPU_LOAD, vcpu); return RESUME_GUEST; @@ -796,10 +795,8 @@ static int kvm_handle_lsx_disabled(struct kvm_vcpu *vcpu, int ecode) { if (!kvm_guest_has_lsx(&vcpu->arch)) kvm_queue_exception(vcpu, EXCCODE_INE, 0); - else { - vcpu->arch.aux_ldtype = KVM_LARCH_LSX; + else kvm_make_request(KVM_REQ_FPU_LOAD, vcpu); - } return RESUME_GUEST; } @@ -816,10 +813,8 @@ static int kvm_handle_lasx_disabled(struct kvm_vcpu *vcpu, int ecode) { if (!kvm_guest_has_lasx(&vcpu->arch)) kvm_queue_exception(vcpu, EXCCODE_INE, 0); - else { - vcpu->arch.aux_ldtype = KVM_LARCH_LASX; + else kvm_make_request(KVM_REQ_FPU_LOAD, vcpu); - } return RESUME_GUEST; } diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index c28aa37f37a9..c65518b4e18e 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -240,21 +240,12 @@ static void kvm_late_check_requests(struct kvm_vcpu *vcpu) } if (kvm_check_request(KVM_REQ_FPU_LOAD, vcpu)) { - switch (vcpu->arch.aux_ldtype) { - case KVM_LARCH_FPU: - kvm_own_fpu(vcpu); - break; - case KVM_LARCH_LSX: - kvm_own_lsx(vcpu); - break; - case KVM_LARCH_LASX: + if (kvm_guest_has_lasx(&vcpu->arch)) kvm_own_lasx(vcpu); - break; - default: - break; - } - - vcpu->arch.aux_ldtype = 0; + else if (kvm_guest_has_lsx(&vcpu->arch)) + kvm_own_lsx(vcpu); + else if (kvm_guest_has_fpu(&vcpu->arch)) + kvm_own_fpu(vcpu); } if (kvm_check_request(KVM_REQ_LBT_LOAD, vcpu)) From 22e36941b54c33a3c45e14b1d016866c801832ed Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:40 +0800 Subject: [PATCH 03/16] LoongArch: KVM: Remove some middle FPU states With max VM supported FPU type enabled, if VM supports LASX, there is only NONE --> LASX, no middle FPU state such as NONE --> FPU --> LASX or NONE --> FPU --> LSX --> LASX. Here remove the middle FPU states in function kvm_own_lsx() and kvm_own_lasx(). And it becomes simpler than before. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/vcpu.c | 37 ++++--------------------------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index c65518b4e18e..e875efb26cb9 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1389,24 +1389,10 @@ int kvm_own_lsx(struct kvm_vcpu *vcpu) /* Enable LSX for guest */ kvm_check_fcsr(vcpu, vcpu->arch.fpu.fcsr); set_csr_euen(CSR_EUEN_LSXEN | CSR_EUEN_FPEN); - switch (vcpu->arch.aux_inuse & KVM_LARCH_FPU) { - case KVM_LARCH_FPU: - /* - * Guest FPU state already loaded, - * only restore upper LSX state - */ - _restore_lsx_upper(&vcpu->arch.fpu); - break; - default: - /* Neither FP or LSX already active, - * restore full LSX state - */ - kvm_restore_lsx(&vcpu->arch.fpu); - break; - } - trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_LSX); + kvm_restore_lsx(&vcpu->arch.fpu); vcpu->arch.aux_inuse |= KVM_LARCH_LSX | KVM_LARCH_FPU; + trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_LSX); return 0; } @@ -1418,25 +1404,10 @@ int kvm_own_lasx(struct kvm_vcpu *vcpu) { kvm_check_fcsr(vcpu, vcpu->arch.fpu.fcsr); set_csr_euen(CSR_EUEN_FPEN | CSR_EUEN_LSXEN | CSR_EUEN_LASXEN); - switch (vcpu->arch.aux_inuse & (KVM_LARCH_FPU | KVM_LARCH_LSX)) { - case KVM_LARCH_LSX: - case KVM_LARCH_LSX | KVM_LARCH_FPU: - /* Guest LSX state already loaded, only restore upper LASX state */ - _restore_lasx_upper(&vcpu->arch.fpu); - break; - case KVM_LARCH_FPU: - /* Guest FP state already loaded, only restore upper LSX & LASX state */ - _restore_lsx_upper(&vcpu->arch.fpu); - _restore_lasx_upper(&vcpu->arch.fpu); - break; - default: - /* Neither FP or LSX already active, restore full LASX state */ - kvm_restore_lasx(&vcpu->arch.fpu); - break; - } - trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_LASX); + kvm_restore_lasx(&vcpu->arch.fpu); vcpu->arch.aux_inuse |= KVM_LARCH_LASX | KVM_LARCH_LSX | KVM_LARCH_FPU; + trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_LASX); return 0; } From 848c55a2c9703fc477bc65a2acc5d680ea174c4b Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:40 +0800 Subject: [PATCH 04/16] LoongArch: KVM: Remove KVM_LARCH_LSX and KVM_LARCH_LASX In kvm_lose_fpu() FPU state is save in vcpu::arch::fpu, its FPU status comes from vcpu->arch.aux_inuse. Instead existing API vm_guest_has_xxx() can be used also, moreover, the bits KVM_LARCH_LSX and KVM_LARCH_LASX in arch.aux_inuse are removed. It makes the logic simpler than ever. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/include/asm/kvm_host.h | 10 ++++------ arch/loongarch/kvm/vcpu.c | 17 ++++++++++------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h index bad16c5f963c..23cfbecebbd7 100644 --- a/arch/loongarch/include/asm/kvm_host.h +++ b/arch/loongarch/include/asm/kvm_host.h @@ -158,12 +158,10 @@ enum emulation_result { }; #define KVM_LARCH_FPU (0x1 << 0) -#define KVM_LARCH_LSX (0x1 << 1) -#define KVM_LARCH_LASX (0x1 << 2) -#define KVM_LARCH_LBT (0x1 << 3) -#define KVM_LARCH_PMU (0x1 << 4) -#define KVM_LARCH_SWCSR_LATEST (0x1 << 5) -#define KVM_LARCH_HWCSR_USABLE (0x1 << 6) +#define KVM_LARCH_LBT (0x1 << 1) +#define KVM_LARCH_PMU (0x1 << 2) +#define KVM_LARCH_SWCSR_LATEST (0x1 << 3) +#define KVM_LARCH_HWCSR_USABLE (0x1 << 4) #define LOONGARCH_PV_FEAT_UPDATED BIT_ULL(63) #define LOONGARCH_PV_FEAT_MASK (BIT(KVM_FEATURE_IPI) | \ diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index e875efb26cb9..27a738cb53f2 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1391,7 +1391,7 @@ int kvm_own_lsx(struct kvm_vcpu *vcpu) set_csr_euen(CSR_EUEN_LSXEN | CSR_EUEN_FPEN); kvm_restore_lsx(&vcpu->arch.fpu); - vcpu->arch.aux_inuse |= KVM_LARCH_LSX | KVM_LARCH_FPU; + vcpu->arch.aux_inuse |= KVM_LARCH_FPU; trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_LSX); return 0; @@ -1406,7 +1406,7 @@ int kvm_own_lasx(struct kvm_vcpu *vcpu) set_csr_euen(CSR_EUEN_FPEN | CSR_EUEN_LSXEN | CSR_EUEN_LASXEN); kvm_restore_lasx(&vcpu->arch.fpu); - vcpu->arch.aux_inuse |= KVM_LARCH_LASX | KVM_LARCH_LSX | KVM_LARCH_FPU; + vcpu->arch.aux_inuse |= KVM_LARCH_FPU; trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_LASX); return 0; @@ -1418,29 +1418,32 @@ void kvm_lose_fpu(struct kvm_vcpu *vcpu) { preempt_disable(); + if (!(vcpu->arch.aux_inuse & KVM_LARCH_FPU)) + goto done; + kvm_check_fcsr_alive(vcpu); - if (vcpu->arch.aux_inuse & KVM_LARCH_LASX) { + if (kvm_guest_has_lasx(&vcpu->arch)) { kvm_save_lasx(&vcpu->arch.fpu); - vcpu->arch.aux_inuse &= ~(KVM_LARCH_LSX | KVM_LARCH_FPU | KVM_LARCH_LASX); trace_kvm_aux(vcpu, KVM_TRACE_AUX_SAVE, KVM_TRACE_AUX_LASX); /* Disable LASX & LSX & FPU */ clear_csr_euen(CSR_EUEN_FPEN | CSR_EUEN_LSXEN | CSR_EUEN_LASXEN); - } else if (vcpu->arch.aux_inuse & KVM_LARCH_LSX) { + } else if (kvm_guest_has_lsx(&vcpu->arch)) { kvm_save_lsx(&vcpu->arch.fpu); - vcpu->arch.aux_inuse &= ~(KVM_LARCH_LSX | KVM_LARCH_FPU); trace_kvm_aux(vcpu, KVM_TRACE_AUX_SAVE, KVM_TRACE_AUX_LSX); /* Disable LSX & FPU */ clear_csr_euen(CSR_EUEN_FPEN | CSR_EUEN_LSXEN); } else if (vcpu->arch.aux_inuse & KVM_LARCH_FPU) { kvm_save_fpu(&vcpu->arch.fpu); - vcpu->arch.aux_inuse &= ~KVM_LARCH_FPU; trace_kvm_aux(vcpu, KVM_TRACE_AUX_SAVE, KVM_TRACE_AUX_FPU); /* Disable FPU */ clear_csr_euen(CSR_EUEN_FPEN); } + vcpu->arch.aux_inuse &= ~KVM_LARCH_FPU; + +done: kvm_lose_lbt(vcpu); preempt_enable(); From f4caaac76379daf4a617d9134b6087fb2636fb31 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:40 +0800 Subject: [PATCH 05/16] LoongArch: KVM: Fix FPU register width with user access API At the beginning, only 64 bit FPU is supported. With FPU register get interface, 64 bit FPU data is copied to user space, the same with FPU register set API. However with LSX and LASX supported in later, there should be FPU data copied with bigger width. So here fixes this issue, copy the whole 256 bit FPU data from/to user space. Cc: stable@vger.kernel.org Fixes: db1ecca22edf ("LoongArch: KVM: Add LSX (128bit SIMD) support") Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/vcpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 27a738cb53f2..ab0b0da270ca 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1303,7 +1303,7 @@ int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) fpu->fcc = vcpu->arch.fpu.fcc; fpu->fcsr = vcpu->arch.fpu.fcsr; for (i = 0; i < NUM_FPU_REGS; i++) - memcpy(&fpu->fpr[i], &vcpu->arch.fpu.fpr[i], FPU_REG_WIDTH / 64); + memcpy(&fpu->fpr[i], &vcpu->arch.fpu.fpr[i], sizeof(union fpureg)); return 0; } @@ -1315,7 +1315,7 @@ int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) vcpu->arch.fpu.fcc = fpu->fcc; vcpu->arch.fpu.fcsr = fpu->fcsr; for (i = 0; i < NUM_FPU_REGS; i++) - memcpy(&vcpu->arch.fpu.fpr[i], &fpu->fpr[i], FPU_REG_WIDTH / 64); + memcpy(&vcpu->arch.fpu.fpr[i], &fpu->fpr[i], sizeof(union fpureg)); return 0; } From 1ef045effba8b85953fb9eccdfaf80e6f300b612 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:40 +0800 Subject: [PATCH 06/16] LoongArch: KVM: Use existing macro about interrupt bit mask With interrupt post, register CSR.GINTC and CSR.ESTAT is used, and CSR.ESTAT is used for percpu interrupt injection and CSR.GINTC is for external hardware interrupt injection. Here use existing macro about interrupt bit of register CSR.GINTC and CSR.ESTAT, rather than hard coded constant value. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/include/asm/kvm_vcpu.h | 43 ++++++++++++++++++--------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/arch/loongarch/include/asm/kvm_vcpu.h b/arch/loongarch/include/asm/kvm_vcpu.h index 3784ab4ccdb5..a70a32103523 100644 --- a/arch/loongarch/include/asm/kvm_vcpu.h +++ b/arch/loongarch/include/asm/kvm_vcpu.h @@ -10,22 +10,37 @@ #include /* Controlled by 0x5 guest estat */ -#define CPU_SIP0 (_ULCAST_(1)) -#define CPU_SIP1 (_ULCAST_(1) << 1) -#define CPU_PMU (_ULCAST_(1) << 10) -#define CPU_TIMER (_ULCAST_(1) << 11) -#define CPU_IPI (_ULCAST_(1) << 12) -#define CPU_AVEC (_ULCAST_(1) << 14) +#define CPU_SIP0 BIT(INT_SWI0) +#define CPU_SIP1 BIT(INT_SWI1) +#define CPU_HWI0 BIT(INT_HWI0) +#define CPU_HWI1 BIT(INT_HWI1) +#define CPU_HWI2 BIT(INT_HWI2) +#define CPU_HWI3 BIT(INT_HWI3) +#define CPU_HWI4 BIT(INT_HWI4) +#define CPU_HWI5 BIT(INT_HWI5) +#define CPU_HWI6 BIT(INT_HWI6) +#define CPU_HWI7 BIT(INT_HWI7) +#define CPU_PMU BIT(INT_PCOV) +#define CPU_TIMER BIT(INT_TI) +#define CPU_IPI BIT(INT_IPI) +#define CPU_AVEC BIT(INT_AVEC) +#define KVM_ESTAT_INTI_MASK (CPU_SIP0 | CPU_SIP1 | CPU_PMU | CPU_TIMER \ + | CPU_IPI | CPU_AVEC) +#define KVM_ESTAT_EXTI_MASK (CPU_HWI0 | CPU_HWI1 | CPU_HWI2 | CPU_HWI3 \ + | CPU_HWI4 | CPU_HWI5 | CPU_HWI6 | CPU_HWI7) /* Controlled by 0x52 guest exception VIP aligned to estat bit 5~12 */ -#define CPU_IP0 (_ULCAST_(1)) -#define CPU_IP1 (_ULCAST_(1) << 1) -#define CPU_IP2 (_ULCAST_(1) << 2) -#define CPU_IP3 (_ULCAST_(1) << 3) -#define CPU_IP4 (_ULCAST_(1) << 4) -#define CPU_IP5 (_ULCAST_(1) << 5) -#define CPU_IP6 (_ULCAST_(1) << 6) -#define CPU_IP7 (_ULCAST_(1) << 7) +#define VIP_DELTA (INT_HWI0 - CSR_GINTC_VIP_SHIFT) +#define CPU_IP0 BIT(INT_HWI0 - VIP_DELTA) +#define CPU_IP1 BIT(INT_HWI1 - VIP_DELTA) +#define CPU_IP2 BIT(INT_HWI2 - VIP_DELTA) +#define CPU_IP3 BIT(INT_HWI3 - VIP_DELTA) +#define CPU_IP4 BIT(INT_HWI4 - VIP_DELTA) +#define CPU_IP5 BIT(INT_HWI5 - VIP_DELTA) +#define CPU_IP6 BIT(INT_HWI6 - VIP_DELTA) +#define CPU_IP7 BIT(INT_HWI7 - VIP_DELTA) +#define KVM_GINTC_IRQ_MASK (CPU_IP0 | CPU_IP1 | CPU_IP2 | CPU_IP3 \ + | CPU_IP4 | CPU_IP5 | CPU_IP6 | CPU_IP7) #define MNSEC_PER_SEC (NSEC_PER_SEC >> 20) From 09b318ab77b7a4fc9987fd98d1525fc55ddc2617 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:40 +0800 Subject: [PATCH 07/16] LoongArch: KVM: Check irq validity in kvm_vcpu_ioctl_interrupt() Function kvm_vcpu_ioctl_interrupt() can be called from userspace, here add irq validility cheking in kvm_vcpu_ioctl_interrupt(). Cc: stable@vger.kernel.org Fixes: f45ad5b8aa93 ("LoongArch: KVM: Implement vcpu interrupt operations") Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/vcpu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index ab0b0da270ca..4235bf8e455e 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1452,6 +1452,10 @@ void kvm_lose_fpu(struct kvm_vcpu *vcpu) int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq) { int intr = (int)irq->irq; + unsigned int vector = abs(intr); + + if (vector >= EXCCODE_INT_NUM) + return -EINVAL; if (intr > 0) kvm_queue_irq(vcpu, intr); From 33c6da26d8a7f831f6d714ae43ba5a427572ed8f Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:41 +0800 Subject: [PATCH 08/16] LoongArch: KVM: Check msgint feature in interrupt post Interrupt AVEC is valid only if VM has msgint feature, and this feature is checked in interrupt handling. Since interrupt handling is executing in VM context switch, and it is hot path, here move the feature checking in interrupt post rather than interrupt handling. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/interrupt.c | 5 ----- arch/loongarch/kvm/vcpu.c | 3 +++ 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/loongarch/kvm/interrupt.c b/arch/loongarch/kvm/interrupt.c index a18c60dffbba..48dd56aa4dc5 100644 --- a/arch/loongarch/kvm/interrupt.c +++ b/arch/loongarch/kvm/interrupt.c @@ -36,8 +36,6 @@ static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority) switch (priority) { case INT_AVEC: - if (!kvm_guest_has_msgint(&vcpu->arch)) - break; dmsintc_inject_irq(vcpu); fallthrough; case INT_TI: @@ -75,9 +73,6 @@ static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority) switch (priority) { case INT_AVEC: - if (!kvm_guest_has_msgint(&vcpu->arch)) - break; - fallthrough; case INT_TI: case INT_IPI: case INT_SWI0: diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 4235bf8e455e..cb42f43afcc8 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1457,6 +1457,9 @@ int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq) if (vector >= EXCCODE_INT_NUM) return -EINVAL; + if (!kvm_guest_has_msgint(&vcpu->arch) && (vector == INT_AVEC)) + return -EINVAL; + if (intr > 0) kvm_queue_irq(vcpu, intr); else if (intr < 0) From 3179253ad52f9d0e1c44873f0a2a4f0792e7103d Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:42 +0800 Subject: [PATCH 09/16] LoongArch: KVM: Inject interrupts with batch method With bitmask method, interrupts can be injected with batch mode rather than one by one. Also remove unused array priority_to_irq[] here. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/interrupt.c | 93 ++++++++++------------------------ 1 file changed, 27 insertions(+), 66 deletions(-) diff --git a/arch/loongarch/kvm/interrupt.c b/arch/loongarch/kvm/interrupt.c index 48dd56aa4dc5..055ac9235b03 100644 --- a/arch/loongarch/kvm/interrupt.c +++ b/arch/loongarch/kvm/interrupt.c @@ -9,39 +9,16 @@ #include #include -static unsigned int priority_to_irq[EXCCODE_INT_NUM] = { - [INT_TI] = CPU_TIMER, - [INT_IPI] = CPU_IPI, - [INT_SWI0] = CPU_SIP0, - [INT_SWI1] = CPU_SIP1, - [INT_HWI0] = CPU_IP0, - [INT_HWI1] = CPU_IP1, - [INT_HWI2] = CPU_IP2, - [INT_HWI3] = CPU_IP3, - [INT_HWI4] = CPU_IP4, - [INT_HWI5] = CPU_IP5, - [INT_HWI6] = CPU_IP6, - [INT_HWI7] = CPU_IP7, - [INT_AVEC] = CPU_AVEC, -}; - -static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority) +static void kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned long mask) { - unsigned int irq = 0; + unsigned long irq; unsigned long old, new; - clear_bit(priority, &vcpu->arch.irq_pending); - if (priority < EXCCODE_INT_NUM) - irq = priority_to_irq[priority]; - - switch (priority) { - case INT_AVEC: + if (mask & CPU_AVEC) dmsintc_inject_irq(vcpu); - fallthrough; - case INT_TI: - case INT_IPI: - case INT_SWI0: - case INT_SWI1: + + irq = mask & KVM_ESTAT_INTI_MASK; + if (irq) { old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL); set_gcsr_estat(irq); new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL); @@ -49,34 +26,20 @@ static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority) /* Inject TI if TVAL inverted */ if (new > old) set_gcsr_estat(CPU_TIMER); - break; - - case INT_HWI0 ... INT_HWI7: - set_csr_gintc(irq); - break; - - default: - break; } - return 1; + irq = (mask >> VIP_DELTA) & KVM_GINTC_IRQ_MASK; + if (irq) + set_csr_gintc(irq); } -static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority) +static void kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned long mask) { - unsigned int irq = 0; + unsigned long irq; unsigned long old, new; - clear_bit(priority, &vcpu->arch.irq_clear); - if (priority < EXCCODE_INT_NUM) - irq = priority_to_irq[priority]; - - switch (priority) { - case INT_AVEC: - case INT_TI: - case INT_IPI: - case INT_SWI0: - case INT_SWI1: + irq = mask & KVM_ESTAT_INTI_MASK; + if (irq) { old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL); clear_gcsr_estat(irq); new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL); @@ -84,30 +47,28 @@ static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority) /* Inject TI if TVAL inverted */ if (new > old) set_gcsr_estat(CPU_TIMER); - break; - - case INT_HWI0 ... INT_HWI7: - clear_csr_gintc(irq); - break; - - default: - break; } - return 1; + irq = (mask >> VIP_DELTA) & KVM_GINTC_IRQ_MASK; + if (irq) + clear_csr_gintc(irq); } void kvm_deliver_intr(struct kvm_vcpu *vcpu) { - unsigned int priority; - unsigned long *pending = &vcpu->arch.irq_pending; - unsigned long *pending_clr = &vcpu->arch.irq_clear; + unsigned long mask; - for_each_set_bit(priority, pending_clr, EXCCODE_INT_NUM) - kvm_irq_clear(vcpu, priority); + mask = READ_ONCE(vcpu->arch.irq_clear); + if (mask) { + mask = xchg_relaxed(&vcpu->arch.irq_clear, 0); + kvm_irq_clear(vcpu, mask); + } - for_each_set_bit(priority, pending, EXCCODE_INT_NUM) - kvm_irq_deliver(vcpu, priority); + mask = READ_ONCE(vcpu->arch.irq_pending); + if (mask) { + mask = xchg_relaxed(&vcpu->arch.irq_pending, 0); + kvm_irq_deliver(vcpu, mask); + } } int kvm_pending_timer(struct kvm_vcpu *vcpu) From 42985883613cb316d9a1520365ff6c1e07ef5c11 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:42 +0800 Subject: [PATCH 10/16] LoongArch: KVM: Add valid bit check when set CSR.ESTAT register When set CSR.ESTAT register in function _kvm_setcsr(), valid bit check is added here. Also interrupt CPU_AVEC is checked by msgint feature. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/vcpu.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index cb42f43afcc8..89aa49df276a 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -593,7 +593,7 @@ struct kvm_vcpu *kvm_get_vcpu_by_cpuid(struct kvm *kvm, int cpuid) static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val) { - unsigned long gintc; + unsigned long estat, gintc; struct loongarch_csrs *csr = vcpu->arch.csr; if (get_gcsr_flag(id) & INVALID_GCSR) @@ -612,8 +612,9 @@ static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val) preempt_enable(); /* ESTAT IP0~IP7 get from GINTC */ - gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & 0xff; - *val = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT) | (gintc << 2); + gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & KVM_GINTC_IRQ_MASK; + estat = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT) & ~KVM_ESTAT_EXTI_MASK; + *val = estat | (gintc << VIP_DELTA); return 0; } @@ -628,7 +629,8 @@ static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val) static int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 val) { - int ret = 0, gintc; + int ret = 0; + unsigned long estat, gintc; struct loongarch_csrs *csr = vcpu->arch.csr; if (get_gcsr_flag(id) & INVALID_GCSR) @@ -639,11 +641,16 @@ static int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 val) if (id == LOONGARCH_CSR_ESTAT) { /* ESTAT IP0~IP7 inject through GINTC */ - gintc = (val >> 2) & 0xff; - kvm_set_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc); + gintc = (val >> VIP_DELTA) & KVM_GINTC_IRQ_MASK; + gintc |= kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & ~KVM_GINTC_IRQ_MASK; + kvm_write_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc); - gintc = val & ~(0xffUL << 2); - kvm_set_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, gintc); + /* Only set valid ESTAT bits */ + estat = val & ~KVM_ESTAT_EXTI_MASK; + estat &= CSR_ESTAT_IS | CSR_ESTAT_EXC | CSR_ESTAT_ESUBCODE; + if (!kvm_guest_has_msgint(&vcpu->arch)) + estat &= ~CPU_AVEC; + kvm_write_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, estat); return ret; } From 8c5cd2b9d6a724998c1ab9af06ac897b6152d568 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:43 +0800 Subject: [PATCH 11/16] LoongArch: KVM: Deliver interrupt after IN_GUEST_MODE is set Interrupt delivery should be called after IN_GUEST_MODE is set. Other threads may be posting interrupt however does not send IPI to the vCPU, since the vCPU is not in IN_GUEST_MODE yet. Here move function call with kvm_deliver_intr() after IN_GUEST_MODE is set, and set mode with OUTSIDE_GUEST_MODE with atomic method. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/vcpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 89aa49df276a..33aefd90d228 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -299,10 +299,10 @@ static int kvm_pre_enter_guest(struct kvm_vcpu *vcpu) * check vmid before vcpu enter guest */ local_irq_disable(); - kvm_deliver_intr(vcpu); kvm_deliver_exception(vcpu); /* Make sure the vcpu mode has been written */ smp_store_mb(vcpu->mode, IN_GUEST_MODE); + kvm_deliver_intr(vcpu); kvm_check_vpid(vcpu); /* @@ -339,7 +339,7 @@ static int kvm_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu) u32 intr = estat & CSR_ESTAT_IS; u32 ecode = (estat & CSR_ESTAT_EXC) >> CSR_ESTAT_EXC_SHIFT; - vcpu->mode = OUTSIDE_GUEST_MODE; + smp_store_mb(vcpu->mode, OUTSIDE_GUEST_MODE); /* Set a default exit reason */ run->exit_reason = KVM_EXIT_UNKNOWN; From 83551ccedffda28b1fa7aeb8fcf5e00489443816 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:43 +0800 Subject: [PATCH 12/16] LoongArch: KVM: Remove timer interrupt injection when SW timer expired The software timer emulation is to wake up vCPU when the vCPU executes idle instruction and gives up host CPU, the vCPU timer tick value and interrupt is set when vCPU is scheduled in. It is not necessary to inject timer interrupt when SW timer is expired. Here remove it, also use common API kvm_vcpu_wake_up() to wake up vCPU. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/timer.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/loongarch/kvm/timer.c b/arch/loongarch/kvm/timer.c index 8356fce0043f..3829f35a4070 100644 --- a/arch/loongarch/kvm/timer.c +++ b/arch/loongarch/kvm/timer.c @@ -30,8 +30,7 @@ enum hrtimer_restart kvm_swtimer_wakeup(struct hrtimer *timer) struct kvm_vcpu *vcpu; vcpu = container_of(timer, struct kvm_vcpu, arch.swtimer); - kvm_queue_irq(vcpu, INT_TI); - rcuwait_wake_up(&vcpu->wait); + kvm_vcpu_wake_up(vcpu); return HRTIMER_NORESTART; } From fb89e0fe2dc4246c86ad0eb0fa2b7cb2f8ba9728 Mon Sep 17 00:00:00 2001 From: Qiang Ma Date: Thu, 11 Jun 2026 20:46:43 +0800 Subject: [PATCH 13/16] LoongArch: KVM: Check the return values for put_user() put_user() may return -EFAULT, so, when the user space address is invalid, the caller should return -EFAULT. Cc: stable@vger.kernel.org Reviewed-by: Bibo Mao Signed-off-by: Qiang Ma Signed-off-by: Huacai Chen --- arch/loongarch/kvm/vcpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 33aefd90d228..20c207d80e31 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1106,7 +1106,8 @@ static int kvm_loongarch_cpucfg_get_attr(struct kvm_vcpu *vcpu, return -ENXIO; } - put_user(val, uaddr); + if (put_user(val, uaddr)) + return -EFAULT; return ret; } From ebd50de14f1a06b7e0206083904bcc62b9ba65be Mon Sep 17 00:00:00 2001 From: Qiang Ma Date: Thu, 11 Jun 2026 20:46:43 +0800 Subject: [PATCH 14/16] LoongArch: KVM: Return full old CSR value from kvm_emu_xchg_csr() The LoongArch CSRXCHG instruction returns the full old CSR value in rd after applying the masked update. kvm_emu_xchg_csr() currently masks the saved value before returning it to the guest, so rd receives only the bits selected by the write mask. That breaks the architectural behavior and makes a zero mask return 0 instead of the previous CSR value. So, keep the masked CSR update, but return the unmodified old CSR value. Cc: stable@vger.kernel.org Fixes: da50f5a693ff ("LoongArch: KVM: Implement handle csr exception") Reviewed-by: Bibo Mao Signed-off-by: Qiang Ma Signed-off-by: Huacai Chen --- arch/loongarch/kvm/exit.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c index c8492670f169..8572b63478bb 100644 --- a/arch/loongarch/kvm/exit.c +++ b/arch/loongarch/kvm/exit.c @@ -103,7 +103,6 @@ static unsigned long kvm_emu_xchg_csr(struct kvm_vcpu *vcpu, int csrid, old = kvm_read_sw_gcsr(csr, csrid); val = (old & ~csr_mask) | (val & csr_mask); kvm_write_sw_gcsr(csr, csrid, val); - old = old & csr_mask; } else pr_warn_once("Unsupported csrxchg 0x%x with pc %lx\n", csrid, vcpu->arch.pc); From 3474037904c20ff915e3ebab0ab5c1e41bbe549e Mon Sep 17 00:00:00 2001 From: Yanfei Xu Date: Thu, 11 Jun 2026 20:46:43 +0800 Subject: [PATCH 15/16] LoongArch: KVM: Validate irqchip index in irqfd routing Sashiko reported that the irqchip index is not validated for LoongArch. Add validation and reject out-of-range irqchip indexes to avoid indexing past the routing table's chip array. Cc: stable@vger.kernel.org Fixes: 1928254c5ccb ("LoongArch: KVM: Add irqfd support") Closes: https://lore.kernel.org/kvm/20260525051714.485D51F000E9@smtp.kernel.org/ Reported-by: Sashiko Reviewed-by: Bibo Mao Signed-off-by: Yanfei Xu Signed-off-by: Huacai Chen --- arch/loongarch/kvm/irqfd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/kvm/irqfd.c b/arch/loongarch/kvm/irqfd.c index f4f953b22419..40ed1081c4b6 100644 --- a/arch/loongarch/kvm/irqfd.c +++ b/arch/loongarch/kvm/irqfd.c @@ -51,7 +51,8 @@ int kvm_set_routing_entry(struct kvm *kvm, e->irqchip.irqchip = ue->u.irqchip.irqchip; e->irqchip.pin = ue->u.irqchip.pin; - if (e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS) + if (e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS || + e->irqchip.irqchip >= KVM_NR_IRQCHIPS) return -EINVAL; return 0; From aeded601d6aceb57cdda4b2701d2ee00c43a8b69 Mon Sep 17 00:00:00 2001 From: Zeng Chi Date: Thu, 11 Jun 2026 20:46:44 +0800 Subject: [PATCH 16/16] LoongArch: KVM: Add missing slots_lock for device register/unregister kvm_io_bus_register_dev() and kvm_io_bus_unregister_dev() should be called under kvm->slots_lock. The unregister calls in ipi.c, eiointc.c and pch_pic.c were also missing this protection. Add it to match the register side. Cc: stable@vger.kernel.org Reviewed-by: Bibo Mao Signed-off-by: Zeng Chi Signed-off-by: Huacai Chen --- arch/loongarch/kvm/intc/eiointc.c | 6 ++++++ arch/loongarch/kvm/intc/ipi.c | 2 ++ arch/loongarch/kvm/intc/pch_pic.c | 2 ++ 3 files changed, 10 insertions(+) diff --git a/arch/loongarch/kvm/intc/eiointc.c b/arch/loongarch/kvm/intc/eiointc.c index 2ab7fafa86d5..2b14485d14a7 100644 --- a/arch/loongarch/kvm/intc/eiointc.c +++ b/arch/loongarch/kvm/intc/eiointc.c @@ -645,10 +645,14 @@ static int kvm_eiointc_create(struct kvm_device *dev, u32 type) device = &s->device_vext; kvm_iodevice_init(device, &kvm_eiointc_virt_ops); + mutex_lock(&kvm->slots_lock); ret = kvm_io_bus_register_dev(kvm, KVM_IOCSR_BUS, EIOINTC_VIRT_BASE, EIOINTC_VIRT_SIZE, device); + mutex_unlock(&kvm->slots_lock); if (ret < 0) { + mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &s->device); + mutex_unlock(&kvm->slots_lock); kfree(s); return ret; } @@ -667,8 +671,10 @@ static void kvm_eiointc_destroy(struct kvm_device *dev) kvm = dev->kvm; eiointc = kvm->arch.eiointc; + mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device); kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device_vext); + mutex_unlock(&kvm->slots_lock); kfree(eiointc); kfree(dev); } diff --git a/arch/loongarch/kvm/intc/ipi.c b/arch/loongarch/kvm/intc/ipi.c index 1f6ebbd0af5c..4fa0897d7bdb 100644 --- a/arch/loongarch/kvm/intc/ipi.c +++ b/arch/loongarch/kvm/intc/ipi.c @@ -447,7 +447,9 @@ static void kvm_ipi_destroy(struct kvm_device *dev) kvm = dev->kvm; ipi = kvm->arch.ipi; + mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &ipi->device); + mutex_unlock(&kvm->slots_lock); kfree(ipi); kfree(dev); } diff --git a/arch/loongarch/kvm/intc/pch_pic.c b/arch/loongarch/kvm/intc/pch_pic.c index aa0ed59ae8cf..175a630aceb4 100644 --- a/arch/loongarch/kvm/intc/pch_pic.c +++ b/arch/loongarch/kvm/intc/pch_pic.c @@ -481,7 +481,9 @@ static void kvm_pch_pic_destroy(struct kvm_device *dev) kvm = dev->kvm; s = kvm->arch.pch_pic; /* unregister pch pic device and free it's memory */ + mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &s->device); + mutex_unlock(&kvm->slots_lock); kfree(s); kfree(dev); }