diff options
author | Takao Indoh <indou.takao@jp.fujitsu.com> | 2011-03-29 12:35:04 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2011-06-17 04:17:12 -0400 |
commit | d8ad7d1123a960cc9f276bd499f9325c6f5e1bd1 (patch) | |
tree | 0260df1b5b5b1601f56229c7dde54594d2dfebcb | |
parent | eb96c925152fc289311e5d7e956b919e9b60ab53 (diff) |
generic-ipi: Fix kexec boot crash by initializing call_single_queue before enabling interrupts
There is a problem that kdump(2nd kernel) sometimes hangs up due
to a pending IPI from 1st kernel. Kernel panic occurs because IPI
comes before call_single_queue is initialized.
To fix the crash, rename init_call_single_data() to call_function_init()
and call it in start_kernel() so that call_single_queue can be
initialized before enabling interrupts.
The details of the crash are:
(1) 2nd kernel boots up
(2) A pending IPI from 1st kernel comes when irqs are first enabled
in start_kernel().
(3) Kernel tries to handle the interrupt, but call_single_queue
is not initialized yet at this point. As a result, in the
generic_smp_call_function_single_interrupt(), NULL pointer
dereference occurs when list_replace_init() tries to access
&q->list.next.
Therefore this patch changes the name of init_call_single_data()
to call_function_init() and calls it before local_irq_enable()
in start_kernel().
Signed-off-by: Takao Indoh <indou.takao@jp.fujitsu.com>
Reviewed-by: WANG Cong <xiyou.wangcong@gmail.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Milton Miller <miltonm@bga.com>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: kexec@lists.infradead.org
Link: http://lkml.kernel.org/r/D6CBEE2F420741indou.takao@jp.fujitsu.com
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | include/linux/smp.h | 5 | ||||
-rw-r--r-- | init/main.c | 1 | ||||
-rw-r--r-- | kernel/smp.c | 5 |
3 files changed, 6 insertions, 5 deletions
diff --git a/include/linux/smp.h b/include/linux/smp.h index 7ad824d510a2..8cc38d3bab0c 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h | |||
@@ -85,12 +85,15 @@ int smp_call_function_any(const struct cpumask *mask, | |||
85 | * Generic and arch helpers | 85 | * Generic and arch helpers |
86 | */ | 86 | */ |
87 | #ifdef CONFIG_USE_GENERIC_SMP_HELPERS | 87 | #ifdef CONFIG_USE_GENERIC_SMP_HELPERS |
88 | void __init call_function_init(void); | ||
88 | void generic_smp_call_function_single_interrupt(void); | 89 | void generic_smp_call_function_single_interrupt(void); |
89 | void generic_smp_call_function_interrupt(void); | 90 | void generic_smp_call_function_interrupt(void); |
90 | void ipi_call_lock(void); | 91 | void ipi_call_lock(void); |
91 | void ipi_call_unlock(void); | 92 | void ipi_call_unlock(void); |
92 | void ipi_call_lock_irq(void); | 93 | void ipi_call_lock_irq(void); |
93 | void ipi_call_unlock_irq(void); | 94 | void ipi_call_unlock_irq(void); |
95 | #else | ||
96 | static inline void call_function_init(void) { } | ||
94 | #endif | 97 | #endif |
95 | 98 | ||
96 | /* | 99 | /* |
@@ -134,7 +137,7 @@ static inline void smp_send_reschedule(int cpu) { } | |||
134 | #define smp_prepare_boot_cpu() do {} while (0) | 137 | #define smp_prepare_boot_cpu() do {} while (0) |
135 | #define smp_call_function_many(mask, func, info, wait) \ | 138 | #define smp_call_function_many(mask, func, info, wait) \ |
136 | (up_smp_call_function(func, info)) | 139 | (up_smp_call_function(func, info)) |
137 | static inline void init_call_single_data(void) { } | 140 | static inline void call_function_init(void) { } |
138 | 141 | ||
139 | static inline int | 142 | static inline int |
140 | smp_call_function_any(const struct cpumask *mask, smp_call_func_t func, | 143 | smp_call_function_any(const struct cpumask *mask, smp_call_func_t func, |
diff --git a/init/main.c b/init/main.c index cafba67c13bf..d7211faed2ad 100644 --- a/init/main.c +++ b/init/main.c | |||
@@ -542,6 +542,7 @@ asmlinkage void __init start_kernel(void) | |||
542 | timekeeping_init(); | 542 | timekeeping_init(); |
543 | time_init(); | 543 | time_init(); |
544 | profile_init(); | 544 | profile_init(); |
545 | call_function_init(); | ||
545 | if (!irqs_disabled()) | 546 | if (!irqs_disabled()) |
546 | printk(KERN_CRIT "start_kernel(): bug: interrupts were " | 547 | printk(KERN_CRIT "start_kernel(): bug: interrupts were " |
547 | "enabled early\n"); | 548 | "enabled early\n"); |
diff --git a/kernel/smp.c b/kernel/smp.c index 73a195193558..fb67dfa8394e 100644 --- a/kernel/smp.c +++ b/kernel/smp.c | |||
@@ -74,7 +74,7 @@ static struct notifier_block __cpuinitdata hotplug_cfd_notifier = { | |||
74 | .notifier_call = hotplug_cfd, | 74 | .notifier_call = hotplug_cfd, |
75 | }; | 75 | }; |
76 | 76 | ||
77 | static int __cpuinit init_call_single_data(void) | 77 | void __init call_function_init(void) |
78 | { | 78 | { |
79 | void *cpu = (void *)(long)smp_processor_id(); | 79 | void *cpu = (void *)(long)smp_processor_id(); |
80 | int i; | 80 | int i; |
@@ -88,10 +88,7 @@ static int __cpuinit init_call_single_data(void) | |||
88 | 88 | ||
89 | hotplug_cfd(&hotplug_cfd_notifier, CPU_UP_PREPARE, cpu); | 89 | hotplug_cfd(&hotplug_cfd_notifier, CPU_UP_PREPARE, cpu); |
90 | register_cpu_notifier(&hotplug_cfd_notifier); | 90 | register_cpu_notifier(&hotplug_cfd_notifier); |
91 | |||
92 | return 0; | ||
93 | } | 91 | } |
94 | early_initcall(init_call_single_data); | ||
95 | 92 | ||
96 | /* | 93 | /* |
97 | * csd_lock/csd_unlock used to serialize access to per-cpu csd resources | 94 | * csd_lock/csd_unlock used to serialize access to per-cpu csd resources |