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

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)