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 <mbohan@codeaurora.org>
(cherry picked from commit f6d11b2eb9c110d0801aa40b1bfdb8194a5e3e3a)
This commit is contained in:
Michael Bohan
2011-05-12 17:22:58 -07:00
committed by Rohit Vaswani
parent 8041e1099b
commit 82d5b8dc2a

View File

@@ -1144,6 +1144,14 @@ void resume_console(void)
console_unlock(); 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 * console_cpu_notify - print deferred console messages after CPU hotplug
* @self: notifier struct * @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 * 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 * called when a new CPU comes online (or fails to come up), and ensures
* that any such output gets printed. * 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, static int __cpuinit console_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu) unsigned long action, void *hcpu)
@@ -1161,11 +1172,16 @@ static int __cpuinit console_cpu_notify(struct notifier_block *self,
switch (action) { switch (action) {
case CPU_ONLINE: case CPU_ONLINE:
case CPU_DEAD: case CPU_DEAD:
case CPU_DYING:
case CPU_DOWN_FAILED: case CPU_DOWN_FAILED:
case CPU_UP_CANCELED: case CPU_UP_CANCELED:
console_lock(); console_lock();
console_unlock(); 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; return NOTIFY_OK;
} }