Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cpu: Defer smpboot kthread unparking until CPU known to scheduler

Currently, smpboot_unpark_threads() is invoked before the incoming CPU
has been added to the scheduler's runqueue structures. This might
potentially cause the unparked kthread to run on the wrong CPU, since the
correct CPU isn't fully set up yet.

That causes a sporadic, hard to debug boot crash triggering on some
systems, reported by Borislav Petkov, and bisected down to:

2a442c9c6453 ("x86: Use common outgoing-CPU-notification code")

This patch places smpboot_unpark_threads() in a CPU hotplug
notifier with priority set so that these kthreads are unparked just after
the CPU has been added to the runqueues.

Reported-and-tested-by: Borislav Petkov <bp@suse.de>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Paul E. McKenney and committed by
Ingo Molnar
00df35f9 4bfe186d

+34 -3
+2
include/linux/cpu.h
··· 73 73 /* migration should happen before other stuff but after perf */ 74 74 CPU_PRI_PERF = 20, 75 75 CPU_PRI_MIGRATION = 10, 76 + CPU_PRI_SMPBOOT = 9, 76 77 /* bring up workqueues before normal notifiers and down after */ 77 78 CPU_PRI_WORKQUEUE_UP = 5, 78 79 CPU_PRI_WORKQUEUE_DOWN = -5, ··· 166 165 } 167 166 #endif 168 167 168 + void smpboot_thread_init(void); 169 169 int cpu_up(unsigned int cpu); 170 170 void notify_cpu_starting(unsigned int cpu); 171 171 extern void cpu_maps_update_begin(void);
+1
init/main.c
··· 384 384 int pid; 385 385 386 386 rcu_scheduler_starting(); 387 + smpboot_thread_init(); 387 388 /* 388 389 * We need to spawn init first so that it obtains pid 1, however 389 390 * the init task will end up wanting to create kthreads, which, if
+31 -3
kernel/cpu.c
··· 448 448 EXPORT_SYMBOL(cpu_down); 449 449 #endif /*CONFIG_HOTPLUG_CPU*/ 450 450 451 + /* 452 + * Unpark per-CPU smpboot kthreads at CPU-online time. 453 + */ 454 + static int smpboot_thread_call(struct notifier_block *nfb, 455 + unsigned long action, void *hcpu) 456 + { 457 + int cpu = (long)hcpu; 458 + 459 + switch (action & ~CPU_TASKS_FROZEN) { 460 + 461 + case CPU_ONLINE: 462 + smpboot_unpark_threads(cpu); 463 + break; 464 + 465 + default: 466 + break; 467 + } 468 + 469 + return NOTIFY_OK; 470 + } 471 + 472 + static struct notifier_block smpboot_thread_notifier = { 473 + .notifier_call = smpboot_thread_call, 474 + .priority = CPU_PRI_SMPBOOT, 475 + }; 476 + 477 + void __cpuinit smpboot_thread_init(void) 478 + { 479 + register_cpu_notifier(&smpboot_thread_notifier); 480 + } 481 + 451 482 /* Requires cpu_add_remove_lock to be held */ 452 483 static int _cpu_up(unsigned int cpu, int tasks_frozen) 453 484 { ··· 517 486 if (ret != 0) 518 487 goto out_notify; 519 488 BUG_ON(!cpu_online(cpu)); 520 - 521 - /* Wake the per cpu threads */ 522 - smpboot_unpark_threads(cpu); 523 489 524 490 /* Now call notifier in preparation. */ 525 491 cpu_notify(CPU_ONLINE | mod, hcpu);