aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2015-07-05 13:12:32 -0400
committerThomas Gleixner <tglx@linutronix.de>2015-07-07 05:54:04 -0400
commit5a3f75e3f02836518ce49536e9c460ca8e1fa290 (patch)
treed789e3eea0d8e334c6b702e415450ea75b5d4e1a
parent827a82ff399523a954253dfea401af748640f0f4 (diff)
x86/irq: Plug irq vector hotplug race
Jin debugged a nasty cpu hotplug race which results in leaking a irq vector on the newly hotplugged cpu. cpu N cpu M native_cpu_up device_shutdown do_boot_cpu free_msi_irqs start_secondary arch_teardown_msi_irqs smp_callin default_teardown_msi_irqs setup_vector_irq arch_teardown_msi_irq __setup_vector_irq native_teardown_msi_irq lock(vector_lock) destroy_irq install vectors unlock(vector_lock) lock(vector_lock) ---> __clear_irq_vector unlock(vector_lock) lock(vector_lock) set_cpu_online unlock(vector_lock) This leaves the irq vector(s) which are torn down on CPU M stale in the vector array of CPU N, because CPU M does not see CPU N online yet. There is a similar issue with concurrent newly setup interrupts. The alloc/free protection of irq descriptors does not prevent the above race, because it merily prevents interrupt descriptors from going away or changing concurrently. Prevent this by moving the call to setup_vector_irq() into the vector_lock held region which protects set_cpu_online(): cpu N cpu M native_cpu_up device_shutdown do_boot_cpu free_msi_irqs start_secondary arch_teardown_msi_irqs smp_callin default_teardown_msi_irqs lock(vector_lock) arch_teardown_msi_irq setup_vector_irq() __setup_vector_irq native_teardown_msi_irq install vectors destroy_irq set_cpu_online unlock(vector_lock) lock(vector_lock) __clear_irq_vector unlock(vector_lock) So cpu M either sees the cpu N online before clearing the vector or cpu N installs the vectors after cpu M has cleared it. Reported-by: xiao jin <jin.xiao@intel.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Joerg Roedel <jroedel@suse.de> Cc: Borislav Petkov <bp@suse.de> Cc: Yanmin Zhang <yanmin_zhang@linux.intel.com> Link: http://lkml.kernel.org/r/20150705171102.141898931@linutronix.de
-rw-r--r--arch/x86/kernel/apic/vector.c10
-rw-r--r--arch/x86/kernel/smpboot.c13
2 files changed, 7 insertions, 16 deletions
diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c
index 28eba2d38b15..f813261d9740 100644
--- a/arch/x86/kernel/apic/vector.c
+++ b/arch/x86/kernel/apic/vector.c
@@ -409,12 +409,6 @@ static void __setup_vector_irq(int cpu)
409 int irq, vector; 409 int irq, vector;
410 struct apic_chip_data *data; 410 struct apic_chip_data *data;
411 411
412 /*
413 * vector_lock will make sure that we don't run into irq vector
414 * assignments that might be happening on another cpu in parallel,
415 * while we setup our initial vector to irq mappings.
416 */
417 raw_spin_lock(&vector_lock);
418 /* Mark the inuse vectors */ 412 /* Mark the inuse vectors */
419 for_each_active_irq(irq) { 413 for_each_active_irq(irq) {
420 data = apic_chip_data(irq_get_irq_data(irq)); 414 data = apic_chip_data(irq_get_irq_data(irq));
@@ -436,16 +430,16 @@ static void __setup_vector_irq(int cpu)
436 if (!cpumask_test_cpu(cpu, data->domain)) 430 if (!cpumask_test_cpu(cpu, data->domain))
437 per_cpu(vector_irq, cpu)[vector] = VECTOR_UNDEFINED; 431 per_cpu(vector_irq, cpu)[vector] = VECTOR_UNDEFINED;
438 } 432 }
439 raw_spin_unlock(&vector_lock);
440} 433}
441 434
442/* 435/*
443 * Setup the vector to irq mappings. 436 * Setup the vector to irq mappings. Must be called with vector_lock held.
444 */ 437 */
445void setup_vector_irq(int cpu) 438void setup_vector_irq(int cpu)
446{ 439{
447 int irq; 440 int irq;
448 441
442 lockdep_assert_held(&vector_lock);
449 /* 443 /*
450 * On most of the platforms, legacy PIC delivers the interrupts on the 444 * On most of the platforms, legacy PIC delivers the interrupts on the
451 * boot cpu. But there are certain platforms where PIC interrupts are 445 * boot cpu. But there are certain platforms where PIC interrupts are
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 0bd8c1d507b3..d3010aa79daf 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -171,11 +171,6 @@ static void smp_callin(void)
171 apic_ap_setup(); 171 apic_ap_setup();
172 172
173 /* 173 /*
174 * Need to setup vector mappings before we enable interrupts.
175 */
176 setup_vector_irq(smp_processor_id());
177
178 /*
179 * Save our processor parameters. Note: this information 174 * Save our processor parameters. Note: this information
180 * is needed for clock calibration. 175 * is needed for clock calibration.
181 */ 176 */
@@ -239,11 +234,13 @@ static void notrace start_secondary(void *unused)
239 check_tsc_sync_target(); 234 check_tsc_sync_target();
240 235
241 /* 236 /*
242 * We need to hold vector_lock so there the set of online cpus 237 * Lock vector_lock and initialize the vectors on this cpu
243 * does not change while we are assigning vectors to cpus. Holding 238 * before setting the cpu online. We must set it online with
244 * this lock ensures we don't half assign or remove an irq from a cpu. 239 * vector_lock held to prevent a concurrent setup/teardown
240 * from seeing a half valid vector space.
245 */ 241 */
246 lock_vector_lock(); 242 lock_vector_lock();
243 setup_vector_irq(smp_processor_id());
247 set_cpu_online(smp_processor_id(), true); 244 set_cpu_online(smp_processor_id(), true);
248 unlock_vector_lock(); 245 unlock_vector_lock();
249 cpu_set_state_online(smp_processor_id()); 246 cpu_set_state_online(smp_processor_id());