From: Paolo Bonzini <pbonzini@redhat.com>
To: Sean Christopherson <seanjc@google.com>
Cc: kvm@vger.kernel.org, linux-kernel@vger.kernel.org,
Chao Gao <chao.gao@intel.com>, Kai Huang <kai.huang@intel.com>
Subject: Re: [PATCH v3 2/8] KVM: Register cpuhp and syscore callbacks when enabling hardware
Date: Wed, 14 Aug 2024 20:12:07 +0200 [thread overview]
Message-ID: <76605c6e-37f8-4abc-ade3-3ba381d6c9c4@redhat.com> (raw)
In-Reply-To: <20240608000639.3295768-3-seanjc@google.com>
On 6/8/24 02:06, Sean Christopherson wrote:
> Register KVM's cpuhp and syscore callback when enabling virtualization
> in hardware instead of registering the callbacks during initialization,
> and let the CPU up/down framework invoke the inner enable/disable
> functions. Registering the callbacks during initialization makes things
> more complex than they need to be, as KVM needs to be very careful about
> handling races between enabling CPUs being onlined/offlined and hardware
> being enabled/disabled.
>
> Intel TDX support will require KVM to enable virtualization during KVM
> initialization, i.e. will add another wrinkle to things, at which point
> sorting out the potential races with kvm_usage_count would become even
> more complex.
>
> Note, using the cpuhp framework has a subtle behavioral change: enabling
> will be done serially across all CPUs, whereas KVM currently sends an IPI
> to all CPUs in parallel. While serializing virtualization enabling could
> create undesirable latency, the issue is limited to creation of KVM's
> first VM,
Isn't that "limited to when kvm_usage_count goes from 0 to 1", so every
time a VM is started if you never run two?
You're fixing this later though, so this is just an issue with the
commit message.
Paolo
and even that can be mitigated, e.g. by letting userspace force
> virtualization to be enabled when KVM is initialized.
>
> Cc: Chao Gao <chao.gao@intel.com>
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
> virt/kvm/kvm_main.c | 174 ++++++++++++++++----------------------------
> 1 file changed, 61 insertions(+), 113 deletions(-)
>
> diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
> index d9b0579d3eea..f6b114f42433 100644
> --- a/virt/kvm/kvm_main.c
> +++ b/virt/kvm/kvm_main.c
> @@ -5502,7 +5502,7 @@ static DEFINE_PER_CPU(bool, hardware_enabled);
> static DEFINE_MUTEX(kvm_usage_lock);
> static int kvm_usage_count;
>
> -static int __hardware_enable_nolock(void)
> +static int hardware_enable_nolock(void)
> {
> if (__this_cpu_read(hardware_enabled))
> return 0;
> @@ -5517,34 +5517,18 @@ static int __hardware_enable_nolock(void)
> return 0;
> }
>
> -static void hardware_enable_nolock(void *failed)
> -{
> - if (__hardware_enable_nolock())
> - atomic_inc(failed);
> -}
> -
> static int kvm_online_cpu(unsigned int cpu)
> {
> - int ret = 0;
> -
> /*
> * Abort the CPU online process if hardware virtualization cannot
> * be enabled. Otherwise running VMs would encounter unrecoverable
> * errors when scheduled to this CPU.
> */
> - mutex_lock(&kvm_usage_lock);
> - if (kvm_usage_count)
> - ret = __hardware_enable_nolock();
> - mutex_unlock(&kvm_usage_lock);
> - return ret;
> + return hardware_enable_nolock();
> }
>
> static void hardware_disable_nolock(void *junk)
> {
> - /*
> - * Note, hardware_disable_all_nolock() tells all online CPUs to disable
> - * hardware, not just CPUs that successfully enabled hardware!
> - */
> if (!__this_cpu_read(hardware_enabled))
> return;
>
> @@ -5555,78 +5539,10 @@ static void hardware_disable_nolock(void *junk)
>
> static int kvm_offline_cpu(unsigned int cpu)
> {
> - mutex_lock(&kvm_usage_lock);
> - if (kvm_usage_count)
> - hardware_disable_nolock(NULL);
> - mutex_unlock(&kvm_usage_lock);
> + hardware_disable_nolock(NULL);
> return 0;
> }
>
> -static void hardware_disable_all_nolock(void)
> -{
> - BUG_ON(!kvm_usage_count);
> -
> - kvm_usage_count--;
> - if (!kvm_usage_count)
> - on_each_cpu(hardware_disable_nolock, NULL, 1);
> -}
> -
> -static void hardware_disable_all(void)
> -{
> - cpus_read_lock();
> - mutex_lock(&kvm_usage_lock);
> - hardware_disable_all_nolock();
> - mutex_unlock(&kvm_usage_lock);
> - cpus_read_unlock();
> -}
> -
> -static int hardware_enable_all(void)
> -{
> - atomic_t failed = ATOMIC_INIT(0);
> - int r;
> -
> - /*
> - * Do not enable hardware virtualization if the system is going down.
> - * If userspace initiated a forced reboot, e.g. reboot -f, then it's
> - * possible for an in-flight KVM_CREATE_VM to trigger hardware enabling
> - * after kvm_reboot() is called. Note, this relies on system_state
> - * being set _before_ kvm_reboot(), which is why KVM uses a syscore ops
> - * hook instead of registering a dedicated reboot notifier (the latter
> - * runs before system_state is updated).
> - */
> - if (system_state == SYSTEM_HALT || system_state == SYSTEM_POWER_OFF ||
> - system_state == SYSTEM_RESTART)
> - return -EBUSY;
> -
> - /*
> - * When onlining a CPU, cpu_online_mask is set before kvm_online_cpu()
> - * is called, and so on_each_cpu() between them includes the CPU that
> - * is being onlined. As a result, hardware_enable_nolock() may get
> - * invoked before kvm_online_cpu(), which also enables hardware if the
> - * usage count is non-zero. Disable CPU hotplug to avoid attempting to
> - * enable hardware multiple times.
> - */
> - cpus_read_lock();
> - mutex_lock(&kvm_usage_lock);
> -
> - r = 0;
> -
> - kvm_usage_count++;
> - if (kvm_usage_count == 1) {
> - on_each_cpu(hardware_enable_nolock, &failed, 1);
> -
> - if (atomic_read(&failed)) {
> - hardware_disable_all_nolock();
> - r = -EBUSY;
> - }
> - }
> -
> - mutex_unlock(&kvm_usage_lock);
> - cpus_read_unlock();
> -
> - return r;
> -}
> -
> static void kvm_shutdown(void)
> {
> /*
> @@ -5658,8 +5574,7 @@ static int kvm_suspend(void)
> lockdep_assert_not_held(&kvm_usage_lock);
> lockdep_assert_irqs_disabled();
>
> - if (kvm_usage_count)
> - hardware_disable_nolock(NULL);
> + hardware_disable_nolock(NULL);
> return 0;
> }
>
> @@ -5668,8 +5583,7 @@ static void kvm_resume(void)
> lockdep_assert_not_held(&kvm_usage_lock);
> lockdep_assert_irqs_disabled();
>
> - if (kvm_usage_count)
> - WARN_ON_ONCE(__hardware_enable_nolock());
> + WARN_ON_ONCE(hardware_enable_nolock());
> }
>
> static struct syscore_ops kvm_syscore_ops = {
> @@ -5677,6 +5591,60 @@ static struct syscore_ops kvm_syscore_ops = {
> .resume = kvm_resume,
> .shutdown = kvm_shutdown,
> };
> +
> +static int hardware_enable_all(void)
> +{
> + int r;
> +
> + guard(mutex)(&kvm_usage_lock);
> +
> + if (kvm_usage_count++)
> + return 0;
> +
> + r = cpuhp_setup_state(CPUHP_AP_KVM_ONLINE, "kvm/cpu:online",
> + kvm_online_cpu, kvm_offline_cpu);
> + if (r)
> + goto err_cpuhp;
> +
> + register_syscore_ops(&kvm_syscore_ops);
> +
> + /*
> + * Undo virtualization enabling and bail if the system is going down.
> + * If userspace initiated a forced reboot, e.g. reboot -f, then it's
> + * possible for an in-flight operation to enable virtualization after
> + * syscore_shutdown() is called, i.e. without kvm_shutdown() being
> + * invoked. Note, this relies on system_state being set _before_
> + * kvm_shutdown(), e.g. to ensure either kvm_shutdown() is invoked
> + * or this CPU observes the impending shutdown. Which is why KVM uses
> + * a syscore ops hook instead of registering a dedicated reboot
> + * notifier (the latter runs before system_state is updated).
> + */
> + if (system_state == SYSTEM_HALT || system_state == SYSTEM_POWER_OFF ||
> + system_state == SYSTEM_RESTART) {
> + r = -EBUSY;
> + goto err_rebooting;
> + }
> +
> + return 0;
> +
> +err_rebooting:
> + unregister_syscore_ops(&kvm_syscore_ops);
> + cpuhp_remove_state(CPUHP_AP_KVM_ONLINE);
> +err_cpuhp:
> + --kvm_usage_count;
> + return r;
> +}
> +
> +static void hardware_disable_all(void)
> +{
> + guard(mutex)(&kvm_usage_lock);
> +
> + if (--kvm_usage_count)
> + return;
> +
> + unregister_syscore_ops(&kvm_syscore_ops);
> + cpuhp_remove_state(CPUHP_AP_KVM_ONLINE);
> +}
> #else /* CONFIG_KVM_GENERIC_HARDWARE_ENABLING */
> static int hardware_enable_all(void)
> {
> @@ -6382,15 +6350,6 @@ int kvm_init(unsigned vcpu_size, unsigned vcpu_align, struct module *module)
> int r;
> int cpu;
>
> -#ifdef CONFIG_KVM_GENERIC_HARDWARE_ENABLING
> - r = cpuhp_setup_state_nocalls(CPUHP_AP_KVM_ONLINE, "kvm/cpu:online",
> - kvm_online_cpu, kvm_offline_cpu);
> - if (r)
> - return r;
> -
> - register_syscore_ops(&kvm_syscore_ops);
> -#endif
> -
> /* A kmem cache lets us meet the alignment requirements of fx_save. */
> if (!vcpu_align)
> vcpu_align = __alignof__(struct kvm_vcpu);
> @@ -6401,10 +6360,8 @@ int kvm_init(unsigned vcpu_size, unsigned vcpu_align, struct module *module)
> offsetofend(struct kvm_vcpu, stats_id)
> - offsetof(struct kvm_vcpu, arch),
> NULL);
> - if (!kvm_vcpu_cache) {
> - r = -ENOMEM;
> - goto err_vcpu_cache;
> - }
> + if (!kvm_vcpu_cache)
> + return -ENOMEM;
>
> for_each_possible_cpu(cpu) {
> if (!alloc_cpumask_var_node(&per_cpu(cpu_kick_mask, cpu),
> @@ -6461,11 +6418,6 @@ int kvm_init(unsigned vcpu_size, unsigned vcpu_align, struct module *module)
> for_each_possible_cpu(cpu)
> free_cpumask_var(per_cpu(cpu_kick_mask, cpu));
> kmem_cache_destroy(kvm_vcpu_cache);
> -err_vcpu_cache:
> -#ifdef CONFIG_KVM_GENERIC_HARDWARE_ENABLING
> - unregister_syscore_ops(&kvm_syscore_ops);
> - cpuhp_remove_state_nocalls(CPUHP_AP_KVM_ONLINE);
> -#endif
> return r;
> }
> EXPORT_SYMBOL_GPL(kvm_init);
> @@ -6487,10 +6439,6 @@ void kvm_exit(void)
> kmem_cache_destroy(kvm_vcpu_cache);
> kvm_vfio_ops_exit();
> kvm_async_pf_deinit();
> -#ifdef CONFIG_KVM_GENERIC_HARDWARE_ENABLING
> - unregister_syscore_ops(&kvm_syscore_ops);
> - cpuhp_remove_state_nocalls(CPUHP_AP_KVM_ONLINE);
> -#endif
> kvm_irqfd_exit();
> }
> EXPORT_SYMBOL_GPL(kvm_exit);
next prev parent reply other threads:[~2024-08-14 18:12 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-06-08 0:06 [PATCH v3 0/8] KVM: Register cpuhp/syscore callbacks when enabling virt Sean Christopherson
2024-06-08 0:06 ` [PATCH v3 1/8] KVM: Use dedicated mutex to protect kvm_usage_count to avoid deadlock Sean Christopherson
2024-06-10 0:26 ` Huang, Kai
2024-08-14 18:06 ` Paolo Bonzini
2024-08-15 14:39 ` Sean Christopherson
2024-08-15 16:10 ` Paolo Bonzini
2024-08-30 23:45 ` Sean Christopherson
2024-09-02 13:03 ` Paolo Bonzini
2024-06-08 0:06 ` [PATCH v3 2/8] KVM: Register cpuhp and syscore callbacks when enabling hardware Sean Christopherson
2024-06-10 0:55 ` Huang, Kai
2024-08-14 18:12 ` Paolo Bonzini [this message]
2024-08-14 20:55 ` Sean Christopherson
2024-06-08 0:06 ` [PATCH v3 3/8] KVM: Rename functions related to enabling virtualization hardware Sean Christopherson
2024-06-08 0:06 ` [PATCH v3 4/8] KVM: Add a module param to allow enabling virtualization when KVM is loaded Sean Christopherson
2024-08-02 12:02 ` Huang, Kai
2024-08-02 12:06 ` Huang, Kai
2024-08-13 2:31 ` Sean Christopherson
2024-08-13 5:22 ` Huang, Kai
2024-08-13 23:47 ` Huang, Kai
2024-08-14 18:14 ` Paolo Bonzini
2024-06-08 0:06 ` [PATCH v3 5/8] KVM: Add arch hooks for enabling/disabling virtualization Sean Christopherson
2024-08-14 18:15 ` Paolo Bonzini
2024-06-08 0:06 ` [PATCH v3 6/8] x86/reboot: Unconditionally define cpu_emergency_virt_cb typedef Sean Christopherson
2024-06-08 0:06 ` [PATCH v3 7/8] KVM: x86: Register "emergency disable" callbacks when virt is enabled Sean Christopherson
2024-06-08 0:06 ` [PATCH v3 8/8] KVM: Enable virtualization at load/initialization by default Sean Christopherson
2024-08-14 18:20 ` Paolo Bonzini
2024-06-10 0:59 ` [PATCH v3 0/8] KVM: Register cpuhp/syscore callbacks when enabling virt Huang, Kai
2024-08-14 18:23 ` Paolo Bonzini
2024-08-14 22:17 ` Huang, Kai
2024-08-15 14:41 ` Sean Christopherson
2024-08-20 8:57 ` Paolo Bonzini
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=76605c6e-37f8-4abc-ade3-3ba381d6c9c4@redhat.com \
--to=pbonzini@redhat.com \
--cc=chao.gao@intel.com \
--cc=kai.huang@intel.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=seanjc@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®