diff options
author | Peter Zijlstra <peterz@infradead.org> | 2010-06-28 10:51:01 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-06-28 12:21:30 -0400 |
commit | b433c3d4549ae74935b585115f076c6fb7bc48fe (patch) | |
tree | fa69951fe2dcdfed7482976d6d6fc0a0bd5d1d5d /init/main.c | |
parent | 0d98bb2656e9bd2dfda2d089db1fe1dbdab41504 (diff) |
init, sched: Fix race between init and kthreadd
Ilya reported that on a very slow machine he could reliably
reproduce a race between forking init and kthreadd. We first
fork init so that it obtains pid-1, however since the scheduler
is already fully running at this point it can preempt and run
the init thread before we spawn and set kthreadd_task.
The init thread can then attempt spawning kthreads without
kthreadd being present which results in an OOPS.
Reported-by: Ilya Loginov <isloginov@gmail.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
LKML-Reference: <1277736661.3561.110.camel@laptop>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'init/main.c')
-rw-r--r-- | init/main.c | 12 |
1 files changed, 12 insertions, 0 deletions
diff --git a/init/main.c b/init/main.c index 3bdb152f412f..633442f79e90 100644 --- a/init/main.c +++ b/init/main.c | |||
@@ -422,18 +422,26 @@ static void __init setup_command_line(char *command_line) | |||
422 | * gcc-3.4 accidentally inlines this function, so use noinline. | 422 | * gcc-3.4 accidentally inlines this function, so use noinline. |
423 | */ | 423 | */ |
424 | 424 | ||
425 | static __initdata DECLARE_COMPLETION(kthreadd_done); | ||
426 | |||
425 | static noinline void __init_refok rest_init(void) | 427 | static noinline void __init_refok rest_init(void) |
426 | __releases(kernel_lock) | 428 | __releases(kernel_lock) |
427 | { | 429 | { |
428 | int pid; | 430 | int pid; |
429 | 431 | ||
430 | rcu_scheduler_starting(); | 432 | rcu_scheduler_starting(); |
433 | /* | ||
434 | * We need to spawn init first so that it obtains pid-1, however | ||
435 | * the init task will end up wanting to create kthreads, which, if | ||
436 | * we schedule it before we create kthreadd, will OOPS. | ||
437 | */ | ||
431 | kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); | 438 | kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); |
432 | numa_default_policy(); | 439 | numa_default_policy(); |
433 | pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); | 440 | pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); |
434 | rcu_read_lock(); | 441 | rcu_read_lock(); |
435 | kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns); | 442 | kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns); |
436 | rcu_read_unlock(); | 443 | rcu_read_unlock(); |
444 | complete(&kthreadd_done); | ||
437 | unlock_kernel(); | 445 | unlock_kernel(); |
438 | 446 | ||
439 | /* | 447 | /* |
@@ -855,6 +863,10 @@ static noinline int init_post(void) | |||
855 | 863 | ||
856 | static int __init kernel_init(void * unused) | 864 | static int __init kernel_init(void * unused) |
857 | { | 865 | { |
866 | /* | ||
867 | * Wait until kthreadd is all set-up. | ||
868 | */ | ||
869 | wait_for_completion(&kthreadd_done); | ||
858 | lock_kernel(); | 870 | lock_kernel(); |
859 | 871 | ||
860 | /* | 872 | /* |