Merge LKCD 2.6 tree at :pserver:anonymous@cvs.sourceforge.net:/cvsroot/lkcd/2.6 as...
[linux-2.6.git] / kernel / printk.c
index 3b74688..32452b3 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/smp.h>
 #include <linux/security.h>
 #include <linux/bootmem.h>
+#include <linux/vs_base.h>
 
 #include <asm/uaccess.h>
 
@@ -247,7 +248,10 @@ 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 = -EPERM;
+
+       if (!vx_check(0, VX_ADMIN|VX_WATCH))
+               return error;
 
        error = security_syslog(type);
        if (error)
@@ -280,6 +284,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);
@@ -321,6 +326,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 +342,7 @@ int do_syslog(int type, char __user * buf, int len)
                                        error = -EFAULT;
                                        break;
                                }
+                               cond_resched();
                        }
                }
                break;
@@ -376,6 +383,20 @@ asmlinkage long sys_syslog(int type, char __user * buf, int len)
        return do_syslog(type, buf, len);
 }
 
+/*
+ * Netdump special routine. Don't print to global log_buf, just to the
+ * actual console device(s).
+ */
+static void netdump_call_console_drivers(const char *buf, unsigned long len)
+{
+       struct console *con;
+
+       for (con = console_drivers; con; con = con->next) {
+               if ((con->flags & CON_ENABLED) && con->write)
+                       con->write(con, buf, len);
+       }
+}
+
 /*
  * Call the console drivers on a range of log_buf
  */
@@ -471,6 +492,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.
  * 
@@ -493,12 +535,8 @@ asmlinkage int printk(const char *fmt, ...)
        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))
+               zap_locks();
 
        /* This stops the holder of console_sem just where we want him */
        spin_lock_irqsave(&logbuf_lock, flags);
@@ -508,6 +546,12 @@ asmlinkage int printk(const char *fmt, ...)
        printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args);
        va_end(args);
 
+       if (unlikely(netdump_mode)) {
+               netdump_call_console_drivers(printk_buf, printed_len);
+               spin_unlock_irqrestore(&logbuf_lock, flags);
+               goto out;
+       }
+
        /*
         * Copy the output into log_buf.  If the caller didn't provide
         * appropriate log level tags, we insert them here
@@ -666,6 +710,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