The initial KVM_ARM_VCPU_INIT path holds config_lock while resetting the vCPU, but repeated initialization of an existing vCPU does not. As a result, reset_mdcr() can race with VM-wide PMU configuration and overwrite MDCR_EL2 with a stale counter count. Take config_lock around the repeated-init checks and reset. The vCPU ioctl already holds vcpu->mutex, matching the established lock ordering. Fixes: c8823e51b534 ("KVM: arm64: Fix MDCR_EL2.HPMN reset value") Closes: https://sashiko.dev/#/patchset/20260710-hybrid-v8-0-621409f3a592@rsg.ci.i.u-tokyo.ac.jp?part=1 Assisted-by: Codex:gpt-5.6-sol Signed-off-by: Akihiko Odaki Reviewed-by: Fuad Tabba --- arch/arm64/kvm/arm.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index ccae82c1242b..8eff03c32418 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -1670,29 +1670,26 @@ static int __kvm_vcpu_set_target(struct kvm_vcpu *vcpu, { unsigned long features = init->features[0]; struct kvm *kvm = vcpu->kvm; - int ret = -EINVAL; + int ret; - mutex_lock(&kvm->arch.config_lock); + lockdep_assert_held(&kvm->arch.config_lock); if (test_bit(KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED, &kvm->arch.flags) && kvm_vcpu_init_changed(vcpu, init)) - goto out_unlock; + return -EINVAL; bitmap_copy(kvm->arch.vcpu_features, &features, KVM_VCPU_MAX_FEATURES); ret = kvm_setup_vcpu(vcpu); if (ret) - goto out_unlock; + return ret; /* Now we know what it is, we can reset it. */ kvm_reset_vcpu(vcpu); set_bit(KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED, &kvm->arch.flags); vcpu_set_flag(vcpu, VCPU_INITIALIZED); - ret = 0; -out_unlock: - mutex_unlock(&kvm->arch.config_lock); - return ret; + return 0; } static int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, @@ -1708,6 +1705,8 @@ static int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, if (ret) return ret; + guard(mutex)(&vcpu->kvm->arch.config_lock); + if (!kvm_vcpu_initialized(vcpu)) return __kvm_vcpu_set_target(vcpu, init); -- 2.55.0