Re: [PATCH v3 02/18] x86/reboot: Harden virtualization hooks for emergency reboot

Huang, Kai
2023-05-22T12:47:06Z

On Fri, 2023-05-12 at 16:50 -0700, Sean Christopherson wrote:
> Provide dedicated helpers to (un)register virt hooks used during an
> emergency crash/reboot, and WARN if there is an attempt to overwrite
> the registered callback, or an attempt to do an unpaired unregister.
>
> Opportunsitically use rcu_assign_pointer() instead of RCU_INIT_POINTER(),
> mainly so that the set/unset paths are more symmetrical, but also because
> any performance gains from using RCU_INIT_POINTER() are meaningless for
> this code.
>
> Signed-off-by: Sean Christopherson <seanjc@google.com>

Reviewed-by: Kai Huang <kai.huang@intel.com>

> ---
> arch/x86/include/asm/reboot.h | 5 +++--
> arch/x86/kernel/reboot.c | 30 ++++++++++++++++++++++++------
> arch/x86/kvm/vmx/vmx.c | 6 ++----
> 3 files changed, 29 insertions(+), 12 deletions(-)
>
> diff --git a/arch/x86/include/asm/reboot.h b/arch/x86/include/asm/reboot.h
> index 2551baec927d..d9a38d379d18 100644
> --- a/arch/x86/include/asm/reboot.h
> +++ b/arch/x86/include/asm/reboot.h
> @@ -25,8 +25,9 @@ void __noreturn machine_real_restart(unsigned int type);
> #define MRR_BIOS 0
> #define MRR_APM 1
>
> -typedef void crash_vmclear_fn(void);
> -extern crash_vmclear_fn __rcu *crash_vmclear_loaded_vmcss;
> +typedef void (cpu_emergency_virt_cb)(void);
> +void cpu_emergency_register_virt_callback(cpu_emergency_virt_cb *callback);
> +void cpu_emergency_unregister_virt_callback(cpu_emergency_virt_cb *callback);
> void cpu_emergency_disable_virtualization(void);
>
> typedef void (*nmi_shootdown_cb)(int, struct pt_regs*);
> diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
> index 299b970e5f82..739e09527dbb 100644
> --- a/arch/x86/kernel/reboot.c
> +++ b/arch/x86/kernel/reboot.c
> @@ -794,17 +794,35 @@ void machine_crash_shutdown(struct pt_regs *regs)
> *
> * protected by rcu.
> */
> -crash_vmclear_fn __rcu *crash_vmclear_loaded_vmcss;
> -EXPORT_SYMBOL_GPL(crash_vmclear_loaded_vmcss);
> +static cpu_emergency_virt_cb __rcu *cpu_emergency_virt_callback;
> +
> +void cpu_emergency_register_virt_callback(cpu_emergency_virt_cb *callback)
> +{
> + if (WARN_ON_ONCE(rcu_access_pointer(cpu_emergency_virt_callback)))
> + return;
> +
> + rcu_assign_pointer(cpu_emergency_virt_callback, callback);
> +}
> +EXPORT_SYMBOL_GPL(cpu_emergency_register_virt_callback);
> +
> +void cpu_emergency_unregister_virt_callback(cpu_emergency_virt_cb *callback)
> +{
> + if (WARN_ON_ONCE(rcu_access_pointer(cpu_emergency_virt_callback) != callback))
> + return;
> +
> + rcu_assign_pointer(cpu_emergency_virt_callback, NULL);
> + synchronize_rcu();
> +}
> +EXPORT_SYMBOL_GPL(cpu_emergency_unregister_virt_callback);
>
> static inline void cpu_crash_vmclear_loaded_vmcss(void)
> {
> - crash_vmclear_fn *do_vmclear_operation = NULL;
> + cpu_emergency_virt_cb *callback;
>
> rcu_read_lock();
> - do_vmclear_operation = rcu_dereference(crash_vmclear_loaded_vmcss);
> - if (do_vmclear_operation)
> - do_vmclear_operation();
> + callback = rcu_dereference(cpu_emergency_virt_callback);
> + if (callback)
> + callback();
> rcu_read_unlock();
> }
>
> diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
> index 317f72baf0c3..fc9cdb4114cc 100644
> --- a/arch/x86/kvm/vmx/vmx.c
> +++ b/arch/x86/kvm/vmx/vmx.c
> @@ -8547,8 +8547,7 @@ static void __vmx_exit(void)
> {
> allow_smaller_maxphyaddr = false;
>
> - RCU_INIT_POINTER(crash_vmclear_loaded_vmcss, NULL);
> - synchronize_rcu();
> + cpu_emergency_unregister_virt_callback(crash_vmclear_local_loaded_vmcss);
>
> vmx_cleanup_l1d_flush();
> }
> @@ -8598,8 +8597,7 @@ static int __init vmx_init(void)
> pi_init_cpu(cpu);
> }
>
> - rcu_assign_pointer(crash_vmclear_loaded_vmcss,
> - crash_vmclear_local_loaded_vmcss);
> + cpu_emergency_register_virt_callback(crash_vmclear_local_loaded_vmcss);
>
> vmx_check_vmcs12_offsets();
>
> --
> 2.40.1.606.ga4b1b128d6-goog
>