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

Message ID 20230512235026.808058-3-seanjc@google.com
State New
Headers
Series x86/reboot: KVM: Clean up "emergency" virt code |

Commit Message

Sean Christopherson May 12, 2023, 11:50 p.m. UTC
  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>
---
 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(-)
  

Comments

Kai Huang May 22, 2023, 12:46 p.m. UTC | #1
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
>
  

Patch

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();