--- 2.2.11/kernel/printk.c Tue Mar 9 01:55:51 1999 +++ 2.2.11-oom-printk/kernel/printk.c Fri Aug 13 15:45:27 1999 @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -46,6 +47,7 @@ static unsigned long logged_chars = 0; struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES]; static int preferred_console = -1; +spinlock_t console_lock = SPIN_LOCK_UNLOCKED; /* * Setup a list of consoles. Called from init/main.c @@ -115,12 +117,12 @@ */ int do_syslog(int type, char * buf, int len) { - unsigned long i, j, count, flags; + unsigned long i, j, count; int do_clear = 0; char c; int error = -EPERM; + char * tmp_buf; - lock_kernel(); error = 0; switch (type) { case 0: /* Close log */ @@ -141,18 +143,20 @@ if (error) goto out; i = 0; + spin_lock_irq(&console_lock); while (log_size && i < len) { c = *((char *) log_buf+log_start); log_start++; log_size--; log_start &= LOG_BUF_LEN-1; - sti(); - __put_user(c,buf); + spin_unlock_irq(&console_lock); + if ((error = __put_user(c,buf)) == -EFAULT) + goto out; buf++; i++; - cli(); + spin_lock_irq(&console_lock); } - sti(); + spin_unlock_irq(&console_lock); error = i; break; case 4: /* Read/clear last kernel messages */ @@ -168,43 +172,66 @@ error = verify_area(VERIFY_WRITE,buf,len); if (error) goto out; - /* - * The logged_chars, log_start, and log_size values may - * change from an interrupt, so we disable interrupts. - */ - __save_flags(flags); - __cli(); + realloc_buf: count = len; if (count > LOG_BUF_LEN) count = LOG_BUF_LEN; + spin_lock_irq(&console_lock); if (count > logged_chars) count = logged_chars; + spin_unlock_irq(&console_lock); + tmp_buf = kmalloc(count, GFP_KERNEL); + if (!tmp_buf) + goto out; + /* The logged_chars, log_start, and log_size are serialized + by the console_lock (the console_lock can be acquired also + from irqs by printk). */ + spin_lock_irq(&console_lock); + if (count > logged_chars) + { + spin_unlock_irq(&console_lock); + kfree(tmp_buf); + goto realloc_buf; + } j = log_start + log_size - count; - __restore_flags(flags); for (i = 0; i < count; i++) { c = *((char *) log_buf+(j++ & (LOG_BUF_LEN-1))); - __put_user(c, buf++); + *(tmp_buf+i) = c; } if (do_clear) + /* the increment done in printk may undo our + not atomic assigment if we do it without the + console lock held. */ logged_chars = 0; + spin_unlock_irq(&console_lock); + if ((error = __copy_to_user(buf, tmp_buf, count)) == -EFAULT) + goto out; error = i; break; case 5: /* Clear ring buffer */ + spin_lock_irq(&console_lock); logged_chars = 0; + spin_unlock_irq(&console_lock); break; case 6: /* Disable logging to console */ + spin_lock_irq(&console_lock); console_loglevel = minimum_console_loglevel; + spin_unlock_irq(&console_lock); break; case 7: /* Enable logging to console */ + spin_lock_irq(&console_lock); console_loglevel = default_console_loglevel; + spin_unlock_irq(&console_lock); break; case 8: error = -EINVAL; if (len < 1 || len > 8) goto out; + spin_lock_irq(&console_lock); if (len < minimum_console_loglevel) len = minimum_console_loglevel; console_loglevel = len; + spin_unlock_irq(&console_lock); error = 0; break; default: @@ -212,7 +239,6 @@ break; } out: - unlock_kernel(); return error; } @@ -224,8 +250,6 @@ } -spinlock_t console_lock; - asmlinkage int printk(const char *fmt, ...) { va_list args; @@ -290,24 +314,33 @@ void console_print(const char *s) { - struct console *c = console_drivers; + struct console *c; int len = strlen(s); + unsigned long flags; + spin_lock_irqsave(&console_lock, flags); + c = console_drivers; while(c) { if ((c->flags & CON_ENABLED) && c->write) c->write(c, s, len); c = c->next; } + spin_unlock_irqrestore(&console_lock, flags); } void unblank_console(void) { - struct console *c = console_drivers; + struct console *c; + unsigned long flags; + + spin_lock_irqsave(&console_lock, flags); + c = console_drivers; while(c) { if ((c->flags & CON_ENABLED) && c->unblank) c->unblank(); c = c->next; } + spin_unlock_irqrestore(&console_lock, flags); } /* @@ -319,10 +352,11 @@ void register_console(struct console * console) { int i,j,len; - int p = log_start; + int p; char buf[16]; signed char msg_level = -1; char *q; + unsigned long flags; /* * See if we want to use this console driver. If we @@ -368,6 +402,7 @@ * Put this console in the list - keep the * preferred driver at the head of the list. */ + spin_lock_irqsave(&console_lock, flags); if ((console->flags & CON_CONSDEV) || console_drivers == NULL) { console->next = console_drivers; console_drivers = console; @@ -375,12 +410,15 @@ console->next = console_drivers->next; console_drivers->next = console; } + spin_unlock_irqrestore(&console_lock, flags); + if ((console->flags & CON_PRINTBUFFER) == 0) return; /* * Print out buffered log messages. */ - for (i=0,j=0; i < log_size; i++) { + spin_lock_irqsave(&console_lock, flags); + for (p=log_start,i=0,j=0; i < log_size; i++) { buf[j++] = log_buf[p]; p++; p &= LOG_BUF_LEN-1; if (buf[j-1] != '\n' && i < log_size - 1 && j < sizeof(buf)-1) @@ -389,9 +427,16 @@ q = buf; len = j; if (msg_level < 0) { - msg_level = buf[1] - '0'; - q = buf + 3; - len -= 3; + msg_level = default_message_loglevel; + if (buf[0] == '<' && + buf[1] >= '0' && + buf[1] <= '7' && + buf[2] == '>') + { + msg_level = buf[1] - '0'; + q = buf + 3; + len -= 3; + } } if (msg_level < console_loglevel) console->write(console, q, len); @@ -399,26 +444,32 @@ msg_level = -1; j = 0; } + spin_unlock_irqrestore(&console_lock, flags); } int unregister_console(struct console * console) { struct console *a,*b; + int ret = 0; + unsigned long flags; + spin_lock_irqsave(&console_lock, flags); if (console_drivers == console) { console_drivers=console->next; - return (0); + goto out; } for (a=console_drivers->next, b=console_drivers ; a; b=a, a=b->next) { if (a == console) { b->next = a->next; - return 0; + goto out; } } - - return (1); + ret = 1; + out: + spin_unlock_irqrestore(&console_lock, flags); + return ret; } /*