X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=kernel%2Fprintk.c;h=f38996f4c20026b7c5d8215e62e93281ebd163a8;hb=a6d8dea2993ef90fb69b81372daa0b63f8aa940e;hp=3b74688184a8d4c7d423a730baeef2804e892937;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/kernel/printk.c b/kernel/printk.c index 3b7468818..f38996f4c 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include @@ -77,7 +79,7 @@ static int console_locked; * It is also used in interesting ways to provide interlocking in * release_console_sem(). */ -static spinlock_t logbuf_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(logbuf_lock); static char __log_buf[__LOG_BUF_LEN]; static char *log_buf = __log_buf; @@ -108,6 +110,7 @@ struct console_cmdline #define MAX_CMDLINECONSOLES 8 static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES]; +static int selected_console = -1; static int preferred_console = -1; /* Flag: console code may call schedule() */ @@ -140,7 +143,7 @@ static int __init console_setup(char *str) strcpy(name, "ttyS1"); #endif for(s = name; *s; s++) - if (*s >= '0' && *s <= '9') + if ((*s >= '0' && *s <= '9') || *s == ',') break; idx = simple_strtoul(s, NULL, 10); *s = 0; @@ -173,12 +176,12 @@ int __init add_preferred_console(char *name, int idx, char *options) for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++) if (strcmp(console_cmdline[i].name, name) == 0 && console_cmdline[i].index == idx) { - preferred_console = i; + selected_console = i; return 0; } if (i == MAX_CMDLINECONSOLES) return -E2BIG; - preferred_console = i; + selected_console = i; c = &console_cmdline[i]; memcpy(c->name, name, sizeof(c->name)); c->name[sizeof(c->name) - 1] = 0; @@ -192,6 +195,8 @@ static int __init log_buf_len_setup(char *str) unsigned long size = memparse(str, &str); unsigned long flags; + if (size) + size = roundup_pow_of_two(size); if (size > log_buf_len) { unsigned long start, dest_idx, offset; char * new_log_buf; @@ -247,18 +252,13 @@ int do_syslog(int type, char __user * buf, int len) unsigned long i, j, limit, count; int do_clear = 0; char c; - int error = 0; + int error; error = security_syslog(type); if (error) return error; - switch (type) { - case 0: /* Close log */ - break; - case 1: /* Open log */ - break; - case 2: /* Read from log */ + if ((type >= 2) && (type <= 4)) { error = -EINVAL; if (!buf || len < 0) goto out; @@ -268,6 +268,16 @@ int do_syslog(int type, char __user * buf, int len) error = verify_area(VERIFY_WRITE,buf,len); if (error) goto out; + } + if (!vx_check(0, VX_ADMIN|VX_WATCH)) + return vx_do_syslog(type, buf, len); + + switch (type) { + case 0: /* Close log */ + break; + case 1: /* Open log */ + break; + case 2: /* Read from log */ error = wait_event_interruptible(log_wait, (log_start - log_end)); if (error) goto out; @@ -280,6 +290,7 @@ int do_syslog(int type, char __user * buf, int len) error = __put_user(c,buf); buf++; i++; + cond_resched(); spin_lock_irq(&logbuf_lock); } spin_unlock_irq(&logbuf_lock); @@ -290,15 +301,6 @@ int do_syslog(int type, char __user * buf, int len) do_clear = 1; /* FALL THRU */ case 3: /* Read last kernel messages */ - error = -EINVAL; - if (!buf || len < 0) - goto out; - error = 0; - if (!len) - goto out; - error = verify_area(VERIFY_WRITE,buf,len); - if (error) - goto out; count = len; if (count > log_buf_len) count = log_buf_len; @@ -321,6 +323,7 @@ int do_syslog(int type, char __user * buf, int len) c = LOG_BUF(j); spin_unlock_irq(&logbuf_lock); error = __put_user(c,&buf[count-1-i]); + cond_resched(); spin_lock_irq(&logbuf_lock); } spin_unlock_irq(&logbuf_lock); @@ -336,6 +339,7 @@ int do_syslog(int type, char __user * buf, int len) error = -EFAULT; break; } + cond_resched(); } } break; @@ -471,6 +475,27 @@ static void emit_log_char(char c) logged_chars++; } +/* + * Zap console related locks when oopsing. Only zap at most once + * every 10 seconds, to leave time for slow consoles to print a + * full oops. + */ +static void zap_locks(void) +{ + static unsigned long oops_timestamp; + + if (time_after_eq(jiffies, oops_timestamp) && + !time_after(jiffies, oops_timestamp + 30*HZ)) + return; + + oops_timestamp = jiffies; + + /* If a crash is occurring, make sure we can't deadlock */ + spin_lock_init(&logbuf_lock); + /* And make sure that we print immediately */ + init_MUTEX(&console_sem); +} + /* * This is printk. It can be called from any context. We want it to work. * @@ -487,26 +512,34 @@ static void emit_log_char(char c) asmlinkage int printk(const char *fmt, ...) { va_list args; + int r; + + va_start(args, fmt); + r = vprintk(fmt, args); + va_end(args); + + return r; +} + +static volatile int printk_cpu = -1; + +asmlinkage int vprintk(const char *fmt, va_list args) +{ unsigned long flags; int printed_len; char *p; static char printk_buf[1024]; static int log_level_unknown = 1; - if (oops_in_progress) { - /* If a crash is occurring, make sure we can't deadlock */ - spin_lock_init(&logbuf_lock); - /* And make sure that we print immediately */ - init_MUTEX(&console_sem); - } + if (unlikely(oops_in_progress && printk_cpu == smp_processor_id())) + zap_locks(); /* This stops the holder of console_sem just where we want him */ spin_lock_irqsave(&logbuf_lock, flags); + printk_cpu = smp_processor_id(); /* Emit the output into the temporary buffer */ - va_start(args, fmt); printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args); - va_end(args); /* * Copy the output into log_buf. If the caller didn't provide @@ -558,6 +591,7 @@ out: return printed_len; } EXPORT_SYMBOL(printk); +EXPORT_SYMBOL(vprintk); /** * acquire_console_sem - lock the console system for exclusive use. @@ -577,6 +611,16 @@ void acquire_console_sem(void) } EXPORT_SYMBOL(acquire_console_sem); +int try_acquire_console_sem(void) +{ + if (down_trylock(&console_sem)) + return -1; + console_locked = 1; + console_may_schedule = 0; + return 0; +} +EXPORT_SYMBOL(try_acquire_console_sem); + int is_console_locked(void) { return console_locked; @@ -611,8 +655,9 @@ void release_console_sem(void) _con_start = con_start; _log_end = log_end; con_start = log_end; /* Flush */ - spin_unlock_irqrestore(&logbuf_lock, flags); + spin_unlock(&logbuf_lock); call_console_drivers(_con_start, _log_end); + local_irq_restore(flags); } console_locked = 0; console_may_schedule = 0; @@ -631,12 +676,10 @@ EXPORT_SYMBOL(release_console_sem); * * Must be called within acquire_console_sem(). */ -void console_conditional_schedule(void) +void __sched console_conditional_schedule(void) { - if (console_may_schedule && need_resched()) { - set_current_state(TASK_RUNNING); - schedule(); - } + if (console_may_schedule) + cond_resched(); } EXPORT_SYMBOL(console_conditional_schedule); @@ -666,6 +709,47 @@ void console_unblank(void) } EXPORT_SYMBOL(console_unblank); +/* + * Return the console tty driver structure and its associated index + */ +struct tty_driver *console_device(int *index) +{ + struct console *c; + struct tty_driver *driver = NULL; + + acquire_console_sem(); + for (c = console_drivers; c != NULL; c = c->next) { + if (!c->device) + continue; + driver = c->device(c, index); + if (driver) + break; + } + release_console_sem(); + return driver; +} + +/* + * Prevent further output on the passed console device so that (for example) + * serial drivers can disable console output before suspending a port, and can + * re-enable output afterwards. + */ +void console_stop(struct console *console) +{ + acquire_console_sem(); + console->flags &= ~CON_ENABLED; + release_console_sem(); +} +EXPORT_SYMBOL(console_stop); + +void console_start(struct console *console) +{ + acquire_console_sem(); + console->flags |= CON_ENABLED; + release_console_sem(); +} +EXPORT_SYMBOL(console_start); + /* * The console driver calls this routine during kernel initialization * to register the console printing procedure with printk() and to @@ -677,6 +761,9 @@ void register_console(struct console * console) int i; unsigned long flags; + if (preferred_console < 0) + preferred_console = selected_console; + /* * See if we want to use this console driver. If we * didn't select a console we take the first one @@ -767,7 +854,7 @@ int unregister_console(struct console * console) * would prevent fbcon from taking over. */ if (console_drivers == NULL) - preferred_console = -1; + preferred_console = selected_console; release_console_sem(); @@ -785,7 +872,7 @@ EXPORT_SYMBOL(unregister_console); void tty_write_message(struct tty_struct *tty, char *msg) { if (tty && tty->driver->write) - tty->driver->write(tty, 0, msg, strlen(msg)); + tty->driver->write(tty, msg, strlen(msg)); return; } @@ -798,7 +885,7 @@ void tty_write_message(struct tty_struct *tty, char *msg) */ int __printk_ratelimit(int ratelimit_jiffies, int ratelimit_burst) { - static spinlock_t ratelimit_lock = SPIN_LOCK_UNLOCKED; + static DEFINE_SPINLOCK(ratelimit_lock); static unsigned long toks = 10*5*HZ; static unsigned long last_msg; static int missed;