diff --git a/kernel/printk.c b/kernel/printk.c index b663c2c95d3..2146ab49fb7 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -1144,6 +1144,14 @@ void resume_console(void) console_unlock(); } +static void __cpuinit console_flush(struct work_struct *work) +{ + console_lock(); + console_unlock(); +} + +static __cpuinitdata DECLARE_WORK(console_cpu_notify_work, console_flush); + /** * console_cpu_notify - print deferred console messages after CPU hotplug * @self: notifier struct @@ -1154,6 +1162,9 @@ void resume_console(void) * will be spooled but will not show up on the console. This function is * called when a new CPU comes online (or fails to come up), and ensures * that any such output gets printed. + * + * Special handling must be done for cases invoked from an atomic context, + * as we can't be taking the console semaphore here. */ static int __cpuinit console_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) @@ -1161,11 +1172,16 @@ static int __cpuinit console_cpu_notify(struct notifier_block *self, switch (action) { case CPU_ONLINE: case CPU_DEAD: - case CPU_DYING: case CPU_DOWN_FAILED: case CPU_UP_CANCELED: console_lock(); console_unlock(); + /* invoked with preemption disabled, so defer */ + case CPU_DYING: + if (!console_trylock()) + schedule_work(&console_cpu_notify_work); + else + console_unlock(); } return NOTIFY_OK; }