Message ID | 20221102231911.3107438-40-seanjc@google.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | KVM: Rework kvm_init() and hardware enabling | expand |
On 11/3/22 00:19, Sean Christopherson wrote: > +- kvm_lock is taken outside kvm->mmu_lock Not surprising since one is a mutex and one is an rwlock. :) You can drop this hunk as well as the "Opportunistically update KVM's locking documentation" sentence in the commit message. > - vcpu->mutex is taken outside kvm->arch.hyperv.hv_lock > > - kvm->arch.mmu_lock is an rwlock. kvm->arch.tdp_mmu_pages_lock and > @@ -216,15 +220,11 @@ time it will be set using the Dirty tracking mechanism described above. > :Type: mutex > :Arch: any > :Protects: - vm_list > - > -``kvm_count_lock`` > -^^^^^^^^^^^^^^^^^^ > - > -:Type: raw_spinlock_t > -:Arch: any > -:Protects: - hardware virtualization enable/disable > -:Comment: 'raw' because hardware enabling/disabling must be atomic /wrt > - migration. > + - kvm_usage_count > + - hardware virtualization enable/disable > + - module probing (x86 only) What do you mean exactly by "module probing"? Is it anything else than what is serialized by vendor_module_lock? Paolo > +:Comment: KVM also disables CPU hotplug via cpus_read_lock() during > + enable/disable. > > ``kvm->mn_invalidate_lock`` > ^^^^^^^^^^^^^^^^^^^^^^^^^^^ > diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c > index 4e765ef9f4bd..c8d92e6c3922 100644 > --- a/virt/kvm/kvm_main.c > +++ b/virt/kvm/kvm_main.c > @@ -100,7 +100,6 @@ EXPORT_SYMBOL_GPL(halt_poll_ns_shrink); > */ > > DEFINE_MUTEX(kvm_lock); > -static DEFINE_RAW_SPINLOCK(kvm_count_lock); > LIST_HEAD(vm_list); > > static cpumask_var_t cpus_hardware_enabled; > @@ -5028,9 +5027,10 @@ static void hardware_enable_nolock(void *junk) > > static int kvm_online_cpu(unsigned int cpu) > { > + unsigned long flags; > int ret = 0; > > - raw_spin_lock(&kvm_count_lock); > + mutex_lock(&kvm_lock); > /* > * Abort the CPU online process if hardware virtualization cannot > * be enabled. Otherwise running VMs would encounter unrecoverable > @@ -5039,13 +5039,16 @@ static int kvm_online_cpu(unsigned int cpu) > if (kvm_usage_count) { > WARN_ON_ONCE(atomic_read(&hardware_enable_failed)); > > + local_irq_save(flags); > hardware_enable_nolock(NULL); > + local_irq_restore(flags); > + > if (atomic_read(&hardware_enable_failed)) { > atomic_set(&hardware_enable_failed, 0); > ret = -EIO; > } > } > - raw_spin_unlock(&kvm_count_lock); > + mutex_unlock(&kvm_lock); > return ret; > } > > @@ -5061,10 +5064,13 @@ static void hardware_disable_nolock(void *junk) > > static int kvm_offline_cpu(unsigned int cpu) > { > - raw_spin_lock(&kvm_count_lock); > - if (kvm_usage_count) > + mutex_lock(&kvm_lock); > + if (kvm_usage_count) { > + preempt_disable(); > hardware_disable_nolock(NULL); > - raw_spin_unlock(&kvm_count_lock); > + preempt_enable(); > + } > + mutex_unlock(&kvm_lock); > return 0; > } > > @@ -5079,9 +5085,11 @@ static void hardware_disable_all_nolock(void) > > static void hardware_disable_all(void) > { > - raw_spin_lock(&kvm_count_lock); > + cpus_read_lock(); > + mutex_lock(&kvm_lock); > hardware_disable_all_nolock(); > - raw_spin_unlock(&kvm_count_lock); > + mutex_unlock(&kvm_lock); > + cpus_read_unlock(); > } > > static int hardware_enable_all(void) > @@ -5097,7 +5105,7 @@ static int hardware_enable_all(void) > * Disable CPU hotplug to prevent scenarios where KVM sees > */ > cpus_read_lock(); > - raw_spin_lock(&kvm_count_lock); > + mutex_lock(&kvm_lock); > > kvm_usage_count++; > if (kvm_usage_count == 1) { > @@ -5110,7 +5118,7 @@ static int hardware_enable_all(void) > } > } > > - raw_spin_unlock(&kvm_count_lock); > + mutex_unlock(&kvm_lock); > cpus_read_unlock(); > > return r; > @@ -5716,6 +5724,15 @@ static void kvm_init_debug(void) > > static int kvm_suspend(void) > { > + /* > + * Secondary CPUs and CPU hotplug are disabled across the suspend/resume > + * callbacks, i.e. no need to acquire kvm_lock to ensure the usage count > + * is stable. Assert that kvm_lock is not held as a paranoid sanity > + * check that the system isn't suspended when KVM is enabling hardware. > + */ > + lockdep_assert_not_held(&kvm_lock); > + lockdep_assert_irqs_disabled(); > + > if (kvm_usage_count) > hardware_disable_nolock(NULL); > return 0; > @@ -5723,10 +5740,11 @@ static int kvm_suspend(void) > > static void kvm_resume(void) > { > - if (kvm_usage_count) { > - lockdep_assert_not_held(&kvm_count_lock); > + lockdep_assert_not_held(&kvm_lock); > + lockdep_assert_irqs_disabled(); > + > + if (kvm_usage_count) > hardware_enable_nolock(NULL); > - } > } > > static struct syscore_ops kvm_syscore_ops = {
On Thu, Nov 03, 2022, Paolo Bonzini wrote: > On 11/3/22 00:19, Sean Christopherson wrote: > > +- kvm_lock is taken outside kvm->mmu_lock > > Not surprising since one is a mutex and one is an rwlock. :) Heh, Signed-off-by: Captain Obvious <seanjc@google.com> > You can drop this hunk as well as the "Opportunistically update KVM's locking > documentation" sentence in the commit message. Will do. > > - vcpu->mutex is taken outside kvm->arch.hyperv.hv_lock > > - kvm->arch.mmu_lock is an rwlock. kvm->arch.tdp_mmu_pages_lock and > > @@ -216,15 +220,11 @@ time it will be set using the Dirty tracking mechanism described above. > > :Type: mutex > > :Arch: any > > :Protects: - vm_list > > - > > -``kvm_count_lock`` > > -^^^^^^^^^^^^^^^^^^ > > - > > -:Type: raw_spinlock_t > > -:Arch: any > > -:Protects: - hardware virtualization enable/disable > > -:Comment: 'raw' because hardware enabling/disabling must be atomic /wrt > > - migration. > > + - kvm_usage_count > > + - hardware virtualization enable/disable > > + - module probing (x86 only) > > What do you mean exactly by "module probing"? Is it anything else than what > is serialized by vendor_module_lock? Ooh, I forgot to update this patch after switching to vendor_module_lock. I added the above after fixing the first deadlock between kvm_lock and cpu_hotplug_lock, but later gave up on trying to use kvm_lock after deadlock #2, which is when I when I realized piggybacking kvm_lock was going to be a maintainance nightmare due.
diff --git a/Documentation/virt/kvm/locking.rst b/Documentation/virt/kvm/locking.rst index 845a561629f1..4feaf527575b 100644 --- a/Documentation/virt/kvm/locking.rst +++ b/Documentation/virt/kvm/locking.rst @@ -9,6 +9,8 @@ KVM Lock Overview The acquisition orders for mutexes are as follows: +- cpus_read_lock() is taken outside kvm_lock + - kvm->lock is taken outside vcpu->mutex - kvm->lock is taken outside kvm->slots_lock and kvm->irq_lock @@ -29,6 +31,8 @@ The acquisition orders for mutexes are as follows: On x86: +- kvm_lock is taken outside kvm->mmu_lock + - vcpu->mutex is taken outside kvm->arch.hyperv.hv_lock - kvm->arch.mmu_lock is an rwlock. kvm->arch.tdp_mmu_pages_lock and @@ -216,15 +220,11 @@ time it will be set using the Dirty tracking mechanism described above. :Type: mutex :Arch: any :Protects: - vm_list - -``kvm_count_lock`` -^^^^^^^^^^^^^^^^^^ - -:Type: raw_spinlock_t -:Arch: any -:Protects: - hardware virtualization enable/disable -:Comment: 'raw' because hardware enabling/disabling must be atomic /wrt - migration. + - kvm_usage_count + - hardware virtualization enable/disable + - module probing (x86 only) +:Comment: KVM also disables CPU hotplug via cpus_read_lock() during + enable/disable. ``kvm->mn_invalidate_lock`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 4e765ef9f4bd..c8d92e6c3922 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -100,7 +100,6 @@ EXPORT_SYMBOL_GPL(halt_poll_ns_shrink); */ DEFINE_MUTEX(kvm_lock); -static DEFINE_RAW_SPINLOCK(kvm_count_lock); LIST_HEAD(vm_list); static cpumask_var_t cpus_hardware_enabled; @@ -5028,9 +5027,10 @@ static void hardware_enable_nolock(void *junk) static int kvm_online_cpu(unsigned int cpu) { + unsigned long flags; int ret = 0; - raw_spin_lock(&kvm_count_lock); + mutex_lock(&kvm_lock); /* * Abort the CPU online process if hardware virtualization cannot * be enabled. Otherwise running VMs would encounter unrecoverable @@ -5039,13 +5039,16 @@ static int kvm_online_cpu(unsigned int cpu) if (kvm_usage_count) { WARN_ON_ONCE(atomic_read(&hardware_enable_failed)); + local_irq_save(flags); hardware_enable_nolock(NULL); + local_irq_restore(flags); + if (atomic_read(&hardware_enable_failed)) { atomic_set(&hardware_enable_failed, 0); ret = -EIO; } } - raw_spin_unlock(&kvm_count_lock); + mutex_unlock(&kvm_lock); return ret; } @@ -5061,10 +5064,13 @@ static void hardware_disable_nolock(void *junk) static int kvm_offline_cpu(unsigned int cpu) { - raw_spin_lock(&kvm_count_lock); - if (kvm_usage_count) + mutex_lock(&kvm_lock); + if (kvm_usage_count) { + preempt_disable(); hardware_disable_nolock(NULL); - raw_spin_unlock(&kvm_count_lock); + preempt_enable(); + } + mutex_unlock(&kvm_lock); return 0; } @@ -5079,9 +5085,11 @@ static void hardware_disable_all_nolock(void) static void hardware_disable_all(void) { - raw_spin_lock(&kvm_count_lock); + cpus_read_lock(); + mutex_lock(&kvm_lock); hardware_disable_all_nolock(); - raw_spin_unlock(&kvm_count_lock); + mutex_unlock(&kvm_lock); + cpus_read_unlock(); } static int hardware_enable_all(void) @@ -5097,7 +5105,7 @@ static int hardware_enable_all(void) * Disable CPU hotplug to prevent scenarios where KVM sees */ cpus_read_lock(); - raw_spin_lock(&kvm_count_lock); + mutex_lock(&kvm_lock); kvm_usage_count++; if (kvm_usage_count == 1) { @@ -5110,7 +5118,7 @@ static int hardware_enable_all(void) } } - raw_spin_unlock(&kvm_count_lock); + mutex_unlock(&kvm_lock); cpus_read_unlock(); return r; @@ -5716,6 +5724,15 @@ static void kvm_init_debug(void) static int kvm_suspend(void) { + /* + * Secondary CPUs and CPU hotplug are disabled across the suspend/resume + * callbacks, i.e. no need to acquire kvm_lock to ensure the usage count + * is stable. Assert that kvm_lock is not held as a paranoid sanity + * check that the system isn't suspended when KVM is enabling hardware. + */ + lockdep_assert_not_held(&kvm_lock); + lockdep_assert_irqs_disabled(); + if (kvm_usage_count) hardware_disable_nolock(NULL); return 0; @@ -5723,10 +5740,11 @@ static int kvm_suspend(void) static void kvm_resume(void) { - if (kvm_usage_count) { - lockdep_assert_not_held(&kvm_count_lock); + lockdep_assert_not_held(&kvm_lock); + lockdep_assert_irqs_disabled(); + + if (kvm_usage_count) hardware_enable_nolock(NULL); - } } static struct syscore_ops kvm_syscore_ops = {