patch-2.3.15 linux/kernel/printk.c
Next file: linux/kernel/resource.c
Previous file: linux/kernel/panic.c
Back to the patch index
Back to the overall index
- Lines: 324
- Date:
Mon Aug 23 11:15:53 1999
- Orig file:
v2.3.14/linux/kernel/printk.c
- Orig date:
Mon Aug 16 23:58:59 1999
diff -u --recursive --new-file v2.3.14/linux/kernel/printk.c linux/kernel/printk.c
@@ -10,6 +10,8 @@
* elsewhere, in preparation for a serial line console (someday).
* Ted Ts'o, 2/11/93.
* Modified for sysctl support, 1/8/97, Chris Horn.
+ * Fixed SMP synchronization, 08/08/99, Manfred Spraul
+ * manfreds@colorfullife.com
*/
#include <linux/mm.h>
@@ -21,6 +23,7 @@
#include <asm/uaccess.h>
#define LOG_BUF_LEN (16384)
+#define LOG_BUF_MASK (LOG_BUF_LEN-1)
static char buf[1024];
@@ -40,13 +43,14 @@
int minimum_console_loglevel = MINIMUM_CONSOLE_LOGLEVEL;
int default_console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
+spinlock_t console_lock = SPIN_LOCK_UNLOCKED;
+
struct console *console_drivers = NULL;
static char log_buf[LOG_BUF_LEN];
static unsigned long log_start = 0;
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
@@ -118,7 +122,7 @@
*/
int do_syslog(int type, char * buf, int len)
{
- unsigned long i, j, count;
+ unsigned long i, j, limit, count;
int do_clear = 0;
char c;
int error = -EPERM;
@@ -145,10 +149,9 @@
i = 0;
spin_lock_irq(&console_lock);
while (log_size && i < len) {
- c = *((char *) log_buf+log_start);
+ c = log_buf[log_start & LOG_BUF_MASK];
log_start++;
log_size--;
- log_start &= LOG_BUF_LEN-1;
spin_unlock_irq(&console_lock);
__put_user(c,buf);
buf++;
@@ -171,34 +174,41 @@
error = verify_area(VERIFY_WRITE,buf,len);
if (error)
goto out;
+ spin_lock_irq(&console_lock);
count = len;
if (count > LOG_BUF_LEN)
count = LOG_BUF_LEN;
- /* 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)
count = logged_chars;
- j = log_start + log_size - count;
- spin_unlock_irq(&console_lock);
- /* While writing data to the userspace buffer printk may
- trash our information but the _only_ thing we care is to
- have a coherent `j' value. */
- for (i = 0; i < count; i++) {
- c = *((char *) log_buf+(j++ & (LOG_BUF_LEN-1)));
- __put_user(c, buf++);
- }
if (do_clear)
- {
- /* the increment done in printk may undo our
- not atomic assigment if we do it without the
- console lock held. */
- spin_lock_irq(&console_lock);
logged_chars = 0;
+ limit = log_start + log_size;
+ /*
+ * __put_user() could sleep, and while we sleep
+ * printk() could overwrite the messages
+ * we try to copy to user space. Therefore
+ * the messages are copied in reverse. <manfreds>
+ */
+ for(i=0;i < count;i++) {
+ j = limit-1-i;
+ if (j+LOG_BUF_LEN < log_start+log_size)
+ break;
+ c = log_buf[ j & LOG_BUF_MASK ];
spin_unlock_irq(&console_lock);
+ __put_user(c,&buf[count-1-i]);
+ spin_lock_irq(&console_lock);
}
+ spin_unlock_irq(&console_lock);
error = i;
+ if(i != count) {
+ int offset = count-error;
+ /* buffer overflow during copy, correct user buffer. */
+ for(i=0;i<error;i++) {
+ __get_user(c,&buf[i+offset]);
+ __put_user(c,&buf[i]);
+ }
+ }
+
break;
case 5: /* Clear ring buffer */
spin_lock_irq(&console_lock);
@@ -219,9 +229,9 @@
error = -EINVAL;
if (len < 1 || len > 8)
goto out;
- spin_lock_irq(&console_lock);
if (len < minimum_console_loglevel)
len = minimum_console_loglevel;
+ spin_lock_irq(&console_lock);
console_loglevel = len;
spin_unlock_irq(&console_lock);
error = 0;
@@ -234,14 +244,13 @@
return error;
}
-asmlinkage int sys_syslog(int type, char * buf, int len)
+asmlinkage long sys_syslog(int type, char * buf, int len)
{
if ((type != 3) && !capable(CAP_SYS_ADMIN))
return -EPERM;
return do_syslog(type, buf, len);
}
-
asmlinkage int printk(const char *fmt, ...)
{
va_list args;
@@ -275,13 +284,12 @@
}
line_feed = 0;
for (; p < buf_end; p++) {
- log_buf[(log_start+log_size) & (LOG_BUF_LEN-1)] = *p;
+ log_buf[(log_start+log_size) & LOG_BUF_MASK] = *p;
if (log_size < LOG_BUF_LEN)
log_size++;
- else {
+ else
log_start++;
- log_start &= LOG_BUF_LEN-1;
- }
+
logged_chars++;
if (*p == '\n') {
line_feed = 1;
@@ -306,29 +314,33 @@
void console_print(const char *s)
{
- struct console *c = console_drivers;
+ struct console *c;
+ unsigned long flags;
int len = strlen(s);
- spin_lock_irq(&console_lock);
+ 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_irq(&console_lock);
+ spin_unlock_irqrestore(&console_lock,flags);
}
void unblank_console(void)
{
- struct console *c = console_drivers;
-
- spin_lock_irq(&console_lock);
+ 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_irq(&console_lock);
+ spin_unlock_irqrestore(&console_lock,flags);
}
/*
@@ -339,13 +351,13 @@
*/
void register_console(struct console * console)
{
- int i,j,len;
+ int i, j,len;
int p;
char buf[16];
signed char msg_level = -1;
char *q;
+ unsigned long flags;
- spin_lock_irq(&console_lock);
/*
* See if we want to use this console driver. If we
* didn't select a console we take the first one
@@ -384,12 +396,13 @@
}
if (!(console->flags & CON_ENABLED))
- goto out;
+ return;
/*
* 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;
@@ -397,23 +410,33 @@
console->next = console_drivers->next;
console_drivers->next = console;
}
- if ((console->flags & CON_PRINTBUFFER) == 0) goto out;
-
+ if ((console->flags & CON_PRINTBUFFER) == 0)
+ goto done;
/*
* Print out buffered log messages.
*/
- for (p=log_start,i=0,j=0; i < log_size; i++) {
+ p = log_start & LOG_BUF_MASK;
+
+ for (i=0,j=0; i < log_size; i++) {
buf[j++] = log_buf[p];
- p++; p &= LOG_BUF_LEN-1;
+ p = (p+1) & LOG_BUF_MASK;
if (buf[j-1] != '\n' && i < log_size - 1 && j < sizeof(buf)-1)
continue;
buf[j] = 0;
q = buf;
len = j;
if (msg_level < 0) {
- msg_level = buf[1] - '0';
- q = buf + 3;
- len -= 3;
+ if(buf[0] == '<' &&
+ buf[1] >= '0' &&
+ buf[1] <= '7' &&
+ buf[2] == '>') {
+ msg_level = buf[1] - '0';
+ q = buf + 3;
+ len -= 3;
+ } else
+ {
+ msg_level = default_message_loglevel;
+ }
}
if (msg_level < console_loglevel)
console->write(console, q, len);
@@ -421,32 +444,35 @@
msg_level = -1;
j = 0;
}
- out:
- spin_unlock_irq(&console_lock);
+done:
+ spin_unlock_irqrestore(&console_lock,flags);
}
int unregister_console(struct console * console)
{
struct console *a,*b;
- int ret = 0;
-
- spin_lock_irq(&console_lock);
+ unsigned long flags;
+ int res = 1;
+
+ spin_lock_irqsave(&console_lock,flags);
if (console_drivers == console) {
console_drivers=console->next;
- goto out;
- }
- for (a=console_drivers->next, b=console_drivers ;
- a; b=a, a=b->next) {
- if (a == console) {
- b->next = a->next;
- goto out;
- }
+ res = 0;
+ } else
+ {
+ for (a=console_drivers->next, b=console_drivers ;
+ a; b=a, a=b->next) {
+ if (a == console) {
+ b->next = a->next;
+ res = 0;
+ break;
+ }
+ }
}
- ret = 1;
- out:
- spin_unlock_irq(&console_lock);
- return ret;
+
+ spin_unlock_irqrestore(&console_lock,flags);
+ return res;
}
/*
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)