KVM: add kvm_lock_all_vcpus and kvm_trylock_all_vcpus
authorMaxim Levitsky <mlevitsk@redhat.com>
Mon, 12 May 2025 18:04:04 +0000 (14:04 -0400)
committerPaolo Bonzini <pbonzini@redhat.com>
Tue, 27 May 2025 16:16:41 +0000 (12:16 -0400)
In a few cases, usually in the initialization code, KVM locks all vCPUs
of a VM to ensure that userspace doesn't do funny things while KVM performs
an operation that affects the whole VM.

Until now, all these operations were implemented using custom code,
and all of them share the same problem:

Lockdep can't cope with simultaneous locking of a large number of locks of
the same class.

However if these locks are taken while another lock is already held,
which is luckily the case, it is possible to take advantage of little known
_nest_lock feature of lockdep which allows in this case to have an
unlimited number of locks of same class to be taken.

To implement this, create two functions:
kvm_lock_all_vcpus() and kvm_trylock_all_vcpus()

Both functions are needed because some code that will be replaced in
the subsequent patches, uses mutex_trylock, instead of regular mutex_lock.

Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
Acked-by: Marc Zyngier <maz@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Message-ID: <20250512180407.659015-4-mlevitsk@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
include/linux/kvm_host.h
virt/kvm/kvm_main.c

index 1dedc421b3e33b57ddffd8646e3b3ec638fb7cba..a6140415c693b7e40ab22346fbbf64571b24b662 100644 (file)
@@ -1015,6 +1015,10 @@ static inline struct kvm_vcpu *kvm_get_vcpu_by_id(struct kvm *kvm, int id)
 
 void kvm_destroy_vcpus(struct kvm *kvm);
 
+int kvm_trylock_all_vcpus(struct kvm *kvm);
+int kvm_lock_all_vcpus(struct kvm *kvm);
+void kvm_unlock_all_vcpus(struct kvm *kvm);
+
 void vcpu_load(struct kvm_vcpu *vcpu);
 void vcpu_put(struct kvm_vcpu *vcpu);
 
index 69782df3617fdebc9c561ec0107a6971e4be5f9a..d660a7da3baa848aba96f55091b873a1e2e6c19a 100644 (file)
@@ -1368,6 +1368,65 @@ static int kvm_vm_release(struct inode *inode, struct file *filp)
        return 0;
 }
 
+int kvm_trylock_all_vcpus(struct kvm *kvm)
+{
+       struct kvm_vcpu *vcpu;
+       unsigned long i, j;
+
+       lockdep_assert_held(&kvm->lock);
+
+       kvm_for_each_vcpu(i, vcpu, kvm)
+               if (!mutex_trylock_nest_lock(&vcpu->mutex, &kvm->lock))
+                       goto out_unlock;
+       return 0;
+
+out_unlock:
+       kvm_for_each_vcpu(j, vcpu, kvm) {
+               if (i == j)
+                       break;
+               mutex_unlock(&vcpu->mutex);
+       }
+       return -EINTR;
+}
+EXPORT_SYMBOL_GPL(kvm_trylock_all_vcpus);
+
+int kvm_lock_all_vcpus(struct kvm *kvm)
+{
+       struct kvm_vcpu *vcpu;
+       unsigned long i, j;
+       int r;
+
+       lockdep_assert_held(&kvm->lock);
+
+       kvm_for_each_vcpu(i, vcpu, kvm) {
+               r = mutex_lock_killable_nest_lock(&vcpu->mutex, &kvm->lock);
+               if (r)
+                       goto out_unlock;
+       }
+       return 0;
+
+out_unlock:
+       kvm_for_each_vcpu(j, vcpu, kvm) {
+               if (i == j)
+                       break;
+               mutex_unlock(&vcpu->mutex);
+       }
+       return r;
+}
+EXPORT_SYMBOL_GPL(kvm_lock_all_vcpus);
+
+void kvm_unlock_all_vcpus(struct kvm *kvm)
+{
+       struct kvm_vcpu *vcpu;
+       unsigned long i;
+
+       lockdep_assert_held(&kvm->lock);
+
+       kvm_for_each_vcpu(i, vcpu, kvm)
+               mutex_unlock(&vcpu->mutex);
+}
+EXPORT_SYMBOL_GPL(kvm_unlock_all_vcpus);
+
 /*
  * Allocation size is twice as large as the actual dirty bitmap size.
  * See kvm_vm_ioctl_get_dirty_log() why this is needed.