From: Benjamin Herrenschmidt This patch removes the call to unblank() from printk, and avoids calling unblank at irq() time _unless_ oops_in_progress is 1. I also export oops_in_progress() so drivers who care like radeonfb can test it and know what to do. I audited call sites of unblank_screen(), console_unblank(), etc... and I _hope_ I got them all, the patch includes a small patch to the s390 bust_spinlocks code that sets oops_in_progress back to 0 _after_ unblanking for example. I added a few might_sleep() to help us catch possible remaining callers. I'll soon write a document explaining fbdev locking. The current situation after this patch is that: - All callbacks have console_semaphore held (fbdev's are fully serialised). - Everything is called in schedule'able context, except the cfb_* rendering operations and cursor operations, with the special case of unblank who can be called at any time when "oops_in_progress" is true. A driver that needs to sleep in it's unblank implementation is welcome to test that variable and use a fallback path (or just do nothing if it's not simple). Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Andrew Morton --- 25-akpm/arch/s390/mm/fault.c | 2 +- 25-akpm/drivers/char/vt.c | 18 +++++++++++++++--- 25-akpm/kernel/printk.c | 18 +++++++++++++----- 3 files changed, 29 insertions(+), 9 deletions(-) diff -puN arch/s390/mm/fault.c~vt-dont-call-unblank-at-irq-time arch/s390/mm/fault.c --- 25/arch/s390/mm/fault.c~vt-dont-call-unblank-at-irq-time 2005-03-10 22:06:02.000000000 -0800 +++ 25-akpm/arch/s390/mm/fault.c 2005-03-10 22:06:02.000000000 -0800 @@ -62,8 +62,8 @@ void bust_spinlocks(int yes) oops_in_progress = 1; } else { int loglevel_save = console_loglevel; - oops_in_progress = 0; console_unblank(); + oops_in_progress = 0; /* * OK, the message is on the console. Now we call printk() * without oops_in_progress set so that printk will give klogd diff -puN drivers/char/vt.c~vt-dont-call-unblank-at-irq-time drivers/char/vt.c --- 25/drivers/char/vt.c~vt-dont-call-unblank-at-irq-time 2005-03-10 22:06:02.000000000 -0800 +++ 25-akpm/drivers/char/vt.c 2005-03-10 22:06:02.000000000 -0800 @@ -2220,9 +2220,6 @@ void vt_console_print(struct console *co } set_cursor(vc); - if (!oops_in_progress) - poke_blanked_console(); - quit: clear_bit(0, &printing); } @@ -2823,6 +2820,13 @@ void do_unblank_screen(int leaving_gfx) { struct vc_data *vc; + /* This should now always be called from a "sane" (read: can schedule) + * context for the sake of the low level drivers, except in the special + * case of oops_in_progress + */ + if (!oops_in_progress) + might_sleep(); + WARN_CONSOLE_UNLOCKED(); ignore_poke = 0; @@ -2879,6 +2883,14 @@ void poke_blanked_console(void) { WARN_CONSOLE_UNLOCKED(); + /* Add this so we quickly catch whoever might call us in a non + * safe context. Nowadays, unblank_screen() isn't to be called in + * atomic contexts and is allowed to schedule (with the special case + * of oops_in_progress, but that isn't of any concern for this + * function. --BenH. + */ + might_sleep(); + /* This isn't perfectly race free, but a race here would be mostly harmless, * at worse, we'll do a spurrious blank and it's unlikely */ diff -puN kernel/printk.c~vt-dont-call-unblank-at-irq-time kernel/printk.c --- 25/kernel/printk.c~vt-dont-call-unblank-at-irq-time 2005-03-10 22:06:02.000000000 -0800 +++ 25-akpm/kernel/printk.c 2005-03-10 22:06:02.000000000 -0800 @@ -54,7 +54,12 @@ int console_printk[4] = { EXPORT_SYMBOL(console_printk); +/* + * Low lever drivers may need that to know if they can schedule in + * their unblank() callback or not. So let's export it. + */ int oops_in_progress; +EXPORT_SYMBOL(oops_in_progress); /* * console_sem protects the console_drivers list, and also @@ -749,12 +754,15 @@ void console_unblank(void) struct console *c; /* - * Try to get the console semaphore. If someone else owns it - * we have to return without unblanking because console_unblank - * may be called in interrupt context. + * console_unblank can no longer be called in interrupt context unless + * oops_in_progress is set to 1.. */ - if (down_trylock(&console_sem) != 0) - return; + if (oops_in_progress) { + if (down_trylock(&console_sem) != 0) + return; + } else + acquire_console_sem(); + console_locked = 1; console_may_schedule = 0; for (c = console_drivers; c != NULL; c = c->next) _