mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-09-18 22:14:16 +00:00 
			
		
		
		
	KVM: Disable CPU hotplug during hardware enabling/disabling
Disable CPU hotplug when enabling/disabling hardware to prevent the
corner case where if the following sequence occurs:
  1. A hotplugged CPU marks itself online in cpu_online_mask
  2. The hotplugged CPU enables interrupt before invoking KVM's ONLINE
     callback
  3  hardware_{en,dis}able_all() is invoked on another CPU
the hotplugged CPU will be included in on_each_cpu() and thus get sent
through hardware_{en,dis}able_nolock() before kvm_online_cpu() is called.
        start_secondary { ...
                set_cpu_online(smp_processor_id(), true); <- 1
                ...
                local_irq_enable();  <- 2
                ...
                cpu_startup_entry(CPUHP_AP_ONLINE_IDLE); <- 3
        }
KVM currently fudges around this race by keeping track of which CPUs have
done hardware enabling (see commit 1b6c016818 "KVM: Keep track of which
cpus have virtualization enabled"), but that's an inefficient, convoluted,
and hacky solution.
Signed-off-by: Chao Gao <chao.gao@intel.com>
[sean: split to separate patch, write changelog]
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20221130230934.1014142-43-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
			
			
This commit is contained in:
		
							parent
							
								
									aaf12a7b43
								
							
						
					
					
						commit
						e4aa7f88af
					
				
					 2 changed files with 22 additions and 1 deletions
				
			
		|  | @ -9296,7 +9296,16 @@ static inline void kvm_ops_update(struct kvm_x86_init_ops *ops) | |||
| 
 | ||||
| static int kvm_x86_check_processor_compatibility(void) | ||||
| { | ||||
| 	struct cpuinfo_x86 *c = &cpu_data(smp_processor_id()); | ||||
| 	int cpu = smp_processor_id(); | ||||
| 	struct cpuinfo_x86 *c = &cpu_data(cpu); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Compatibility checks are done when loading KVM and when enabling | ||||
| 	 * hardware, e.g. during CPU hotplug, to ensure all online CPUs are | ||||
| 	 * compatible, i.e. KVM should never perform a compatibility check on | ||||
| 	 * an offline CPU. | ||||
| 	 */ | ||||
| 	WARN_ON(!cpu_online(cpu)); | ||||
| 
 | ||||
| 	if (__cr4_reserved_bits(cpu_has, c) != | ||||
| 	    __cr4_reserved_bits(cpu_has, &boot_cpu_data)) | ||||
|  |  | |||
|  | @ -5167,15 +5167,26 @@ static void hardware_disable_all_nolock(void) | |||
| 
 | ||||
| static void hardware_disable_all(void) | ||||
| { | ||||
| 	cpus_read_lock(); | ||||
| 	raw_spin_lock(&kvm_count_lock); | ||||
| 	hardware_disable_all_nolock(); | ||||
| 	raw_spin_unlock(&kvm_count_lock); | ||||
| 	cpus_read_unlock(); | ||||
| } | ||||
| 
 | ||||
| static int hardware_enable_all(void) | ||||
| { | ||||
| 	int r = 0; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * 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(); | ||||
| 	raw_spin_lock(&kvm_count_lock); | ||||
| 
 | ||||
| 	kvm_usage_count++; | ||||
|  | @ -5190,6 +5201,7 @@ static int hardware_enable_all(void) | |||
| 	} | ||||
| 
 | ||||
| 	raw_spin_unlock(&kvm_count_lock); | ||||
| 	cpus_read_unlock(); | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Chao Gao
						Chao Gao