From 82d5b8dc2ac32f1cf5ca54d38f7edabca8dba31d Mon Sep 17 00:00:00 2001 From: Michael Bohan Date: Thu, 12 May 2011 17:22:58 -0700 Subject: [PATCH] printk: Don't take console semaphore in atomic context The CPU HOTPLUG take_cpu_down path is invokved with preemption disabled via stop_machine. This causes a "Scheduling while atomic" BUG when there is contention for the console semaphore. The solution is to defer the console flush until it's not in scheduling violation. Change-Id: I2d0d58576a4db308ee40850a18a6bb9784ca4e4b Signed-off-by: Michael Bohan (cherry picked from commit f6d11b2eb9c110d0801aa40b1bfdb8194a5e3e3a) --- kernel/printk.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) 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; }