|
From: James S. <jsi...@us...> - 2001-10-08 04:30:41
|
Update of /cvsroot/linuxconsole/ruby/linux/kernel
In directory usw-pr-cvs1:/tmp/cvs-serv25361
Modified Files:
printk.c
Log Message:
After looking at Andrew Morton's patch and my work I merged them more together. The design I have is far more fine grain locking. I also did alot of proper inlining of code to minimize the diff. I replaced the console_sem with a console_lock again. But this time I don't do IRQ blocking with it (only spin_lock and spin_unlock) and I have the proper locking down for the console driver list now.
Index: printk.c
===================================================================
RCS file: /cvsroot/linuxconsole/ruby/linux/kernel/printk.c,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -d -r1.16 -r1.17
--- printk.c 2001/08/05 22:58:18 1.16
+++ printk.c 2001/10/08 04:30:38 1.17
@@ -13,7 +13,7 @@
* Fixed SMP synchronization, 08/08/99, Manfred Spraul
* man...@co...
* Rewrote bits to get rid of console_lock
- * 01Mar01 Andrew Morton <an...@uo...>
+ * 01Mar01 Andrew Morton <an...@uo...>
*/
#include <linux/mm.h>
@@ -27,7 +27,7 @@
#include <asm/uaccess.h>
-#define LOG_BUF_LEN (16384) /* This must be a power of two */
+#define LOG_BUF_LEN (16384) /* This must be a power of two */
#define LOG_BUF_MASK (LOG_BUF_LEN-1)
/* printk's without a loglevel use this.. */
@@ -44,14 +44,13 @@
int default_message_loglevel = DEFAULT_MESSAGE_LOGLEVEL;
int minimum_console_loglevel = MINIMUM_CONSOLE_LOGLEVEL;
int default_console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
+
int oops_in_progress;
/*
- * console_sem protects the console_drivers list, and also
- * provides serialisation for access to the entire console
- * driver system.
+ * console_lock protects the console_drivers list
*/
-static DECLARE_MUTEX(console_sem);
+static spinlock_t console_lock = SPIN_LOCK_UNLOCKED;
struct console *console_drivers;
/*
@@ -68,14 +67,10 @@
* The indices into log_buf are not constrained to LOG_BUF_LEN - they
* must be masked before subscripting
*/
-static unsigned long log_start; /* Index into log_buf: next char to be read
- syslog() */
-static unsigned long con_start; /* Index into log_buf: next char to be sent
- to consoles */
-static unsigned long log_end; /* Index into log_buf: most recently written
- char + 1 */
-static unsigned long logged_chars; /* Number of chars produced since last
- read+clear operation */
+static unsigned long log_start; /* Index into log_buf: next char to be read by syslog() */
+static unsigned long con_start; /* Index into log_buf: next char to be sent to consoles */
+static unsigned long log_end; /* Index into log_buf: most-recently-written-char + 1 */
+static unsigned long logged_chars; /* Number of chars produced since last read+clear operation */
struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];
static int preferred_console = -1;
@@ -147,7 +142,7 @@
* 6 -- Disable printk's to console
* 7 -- Enable printk's to console
* 8 -- Set level of messages printed to console
- * 9 -- Return the number of unread characters in the log buffer
+ * 9 -- Return number of unread characters in the log buffer
*/
int do_syslog(int type, char * buf, int len)
{
@@ -171,7 +166,7 @@
error = verify_area(VERIFY_WRITE,buf,len);
if (error)
goto out;
- error = wait_event_interruptible(log_wait,(log_start-log_end));
+ error = wait_event_interruptible(log_wait, (log_start - log_end));
if (error)
goto out;
i = 0;
@@ -267,7 +262,7 @@
spin_lock_irq(&logbuf_lock);
error = log_end - log_start;
spin_unlock_irq(&logbuf_lock);
- break;
+ break;
default:
error = -EINVAL;
break;
@@ -302,18 +297,17 @@
/*
* Write out chars from start to end - 1 inclusive
*/
-static void _call_console_drivers(struct console *con, unsigned long start,
- unsigned long end, int msg_log_level)
+static void _call_console_drivers(struct console *con, unsigned long start, unsigned long end, int msg_log_level)
{
- if (msg_log_level<console_loglevel && con && start != end){
+ if (msg_log_level < console_loglevel && con && start != end) {
if ((start & LOG_BUF_MASK) > (end & LOG_BUF_MASK)) {
- /* wrapped write */
- __call_console_drivers(con, start & LOG_BUF_MASK, LOG_BUF_LEN);
- __call_console_drivers(con, 0, end & LOG_BUF_MASK);
- } else {
- __call_console_drivers(con, start, end);
- }
- }
+ /* wrapped write */
+ __call_console_drivers(con, start & LOG_BUF_MASK, LOG_BUF_LEN);
+ __call_console_drivers(con, 0, end & LOG_BUF_MASK);
+ } else {
+ __call_console_drivers(con, start, end);
+ }
+ }
}
/*
@@ -321,50 +315,47 @@
* log_buf[start] to log_buf[end - 1].
* The console_sem must be held.
*/
-static void call_console_drivers(struct console *con, unsigned long start,
- unsigned long end)
+static void call_console_drivers(struct console *con, unsigned long start, unsigned long end)
{
unsigned long cur_index, start_print;
- static int msg_level = -1;
+ static int msg_level = -1;
- if (((long)(start - end)) > 0)
- BUG();
+ if (((long)(start - end)) > 0)
+ BUG();
cur_index = start;
- start_print = start;
- while (cur_index != end) {
- if (msg_level < 0 &&
- ((end - cur_index) >= 3) &&
- LOG_BUF(cur_index + 0) == '<' &&
- LOG_BUF(cur_index + 1) >= '0' &&
- LOG_BUF(cur_index + 1) <= '7' &&
- LOG_BUF(cur_index + 2) == '>') {
- msg_level = LOG_BUF(cur_index + 1) - '0';
- cur_index += 3;
- start_print = cur_index;
- }
- while (cur_index != end) {
- char c = LOG_BUF(cur_index);
- cur_index++;
+ start_print = start;
+ while (cur_index != end) {
+ if ( msg_level < 0 &&
+ ((end - cur_index) > 2) &&
+ LOG_BUF(cur_index + 0) == '<' &&
+ LOG_BUF(cur_index + 1) >= '0' &&
+ LOG_BUF(cur_index + 1) <= '7' &&
+ LOG_BUF(cur_index + 2) == '>')
+ {
+ msg_level = LOG_BUF(cur_index + 1) - '0';
+ cur_index += 3;
+ start_print = cur_index;
+ }
+ while (cur_index != end) {
+ char c = LOG_BUF(cur_index);
+ cur_index++;
- if (c == '\n') {
+ if (c == '\n') {
if (msg_level < 0) {
- /*
- * printk() has already given us
- * loglevel tages in the buffer.
- * This code is here in case the
- * log buffer has wrapped right
- * around and scribbled on those
- * tags.
- */
- msg_level = default_message_loglevel;
- }
- _call_console_drivers(con, start_print,
- cur_index, msg_level);
- msg_level = -1;
- start_print = cur_index;
- break;
- }
+ /*
+ * printk() has already given us loglevel tags in
+ * the buffer. This code is here in case the
+ * log buffer has wrapped right round and scribbled
+ * on those tags
+ */
+ msg_level = default_message_loglevel;
+ }
+ _call_console_drivers(con, start_print, cur_index, msg_level);
+ msg_level = -1;
+ start_print = cur_index;
+ break;
+ }
}
}
_call_console_drivers(con, start_print, end, msg_level);
@@ -373,100 +364,116 @@
static void emit_log_char(char c)
{
LOG_BUF(log_end) = c;
- log_end++;
- if (log_end - log_start > LOG_BUF_LEN)
- log_start = log_end - LOG_BUF_LEN;
+ log_end++;
+ if (log_end - log_start > LOG_BUF_LEN)
+ log_start = log_end - LOG_BUF_LEN;
if (log_end - con_start > LOG_BUF_LEN)
- con_start = log_end - LOG_BUF_LEN;
- if (logged_chars < LOG_BUF_LEN)
- logged_chars++;
+ con_start = log_end - LOG_BUF_LEN;
+ if (logged_chars < LOG_BUF_LEN)
+ logged_chars++;
}
+/*
+ * This is printk. It can be called from any context. We want it to work.
+ *
+ * We try to grab the console_sem. If we succeed, it's easy - we log the output and
+ * call the console drivers. If we fail to get the semaphore we place the output
+ * into the log buffer and return. The current holder of the console_sem will
+ * notice the new output in release_console_sem() and will send it to the
+ * consoles before releasing the semaphore.
+ *
+ * One effect of this deferred printing is that code which calls printk() and
+ * then changes console_loglevel may break. This is because console_loglevel
+ * is inspected when the actual printing occurs.
+ */
asmlinkage int printk(const char *fmt, ...)
{
static struct {
- char buf[1024];
- unsigned long semi_random;
+ char buf[1024];
+ unsigned long semi_random;
} printk_buf;
- static int log_level_unknown = 1;
- struct tty_driver *driver;
- unsigned long sr_copy;
- unsigned long flags;
- struct console *con;
- int printed_len;
- va_list args;
+ static int log_level_unknown = 1;
+ struct tty_driver *driver;
+ unsigned long sr_copy;
+ unsigned long flags;
+ struct console *con;
+ int printed_len;
+ va_list args;
char *p;
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 */
+ spin_lock_init(&console_lock);
}
/* This stops the holder of console_sem just where we want him */
spin_lock_irqsave(&logbuf_lock, flags);
+
/* Emit the output into the temporary buffer */
printk_buf.semi_random += jiffies;
sr_copy = printk_buf.semi_random;
va_start(args, fmt);
- printed_len = vsprintf(printk_buf.buf, fmt, args);
+ printed_len = vsnprintf(printk_buf.buf, sizeof(printk_buf.buf), fmt, args);
va_end(args);
-
- if (sr_copy != printk_buf.semi_random)
+
+ if (sr_copy != printk_buf.semi_random)
panic("buffer overrun in printk()");
- /*
- * Copy the output into log_buf. If the caller didn't provide
- * appropriate log level tags, we insert them here
- */
+ /*
+ * Copy the output into log_buf. If the caller didn't provide
+ * appropriate log level tags, we insert them here
+ */
for (p = printk_buf.buf; *p; p++) {
- if (log_level_unknown) {
- if (p[0] != '<' || p[1] < '0' || p[1] > '7' || p[2] != '>') {
+ if (log_level_unknown) {
+ if (p[0] != '<' || p[1] < '0' || p[1] > '7' || p[2] != '>') {
emit_log_char('<');
- emit_log_char(default_message_loglevel + '0');
- emit_log_char('>');
- }
- log_level_unknown = 0;
- }
- emit_log_char(*p);
- if (*p == '\n')
+ emit_log_char(default_message_loglevel + '0');
+ emit_log_char('>');
+ }
+ log_level_unknown = 0;
+ }
+ emit_log_char(*p);
+ if (*p == '\n')
log_level_unknown = 1;
}
spin_unlock_irqrestore(&logbuf_lock, flags);
- for (con = console_drivers; con; con = con->next) {
- if ((con->flags & CON_ENABLED) && con->write) {
+ spin_lock(&console_lock);
+ for (con = console_drivers; con; con = con->next) {
+ /*
+ * We own the drivers list. We can drop the lock and
+ * let release_console_sem() print the text
+ */
+ if ((con->flags & CON_ENABLED) && con->write) {
driver = get_tty_driver(con->device(con));
- if (driver && !down_trylock(&driver->tty_lock))
+ if (driver && !down_trylock(&driver->tty_lock)) {
+ driver->may_schedule = 0;
release_console_sem(con->device(con));
+ }
}
}
+ spin_unlock(&console_lock);
return printed_len;
}
EXPORT_SYMBOL(printk);
-void console_print(const char *s)
-{
- printk(KERN_EMERG "%s", s);
-}
-EXPORT_SYMBOL(console_print);
-
/**
* acquire_console_sem - lock the console system for exclusive use.
*
* Acquires a semaphore which guarantees that the caller has
- * exclusive access to the console display being drawn to.
+ * exclusive access to a console system.
*
* Can sleep, returns nothing.
*/
void acquire_console_sem(kdev_t device)
{
struct tty_driver *driver = get_tty_driver(device);
-
+
if (in_interrupt())
- BUG();
- down(&driver->tty_lock);
- //console_may_schedule = 1;
+ BUG();
+ down(&driver->tty_lock);
+ driver->may_schedule = 1;
}
EXPORT_SYMBOL(acquire_console_sem);
@@ -488,42 +495,67 @@
{
struct tty_driver *driver = get_tty_driver(device);
unsigned long _con_start, _log_end;
- unsigned long must_wake_klogd = 0;
+ unsigned long must_wake_klogd = 0;
unsigned long flags;
struct console *con;
if (driver->flags & TTY_DRIVER_CONSOLE) {
- /* Look for new messages */
+ spin_lock(&console_lock);
+ /* Look for new messages */
for (con = console_drivers; con; con = con->next) {
if (con->device(con) == device)
break;
- }
-
+ }
+ spin_unlock(&console_lock);
+
for ( ; ; ) {
- spin_lock_irqsave(&logbuf_lock, flags);
- must_wake_klogd |= log_start - log_end;
- if (con_start == log_end)
- break; /* Nothing to print */
- _con_start = con_start;
- _log_end = log_end;
- con_start = log_end; /* Flush */
- spin_unlock_irqrestore(&logbuf_lock, flags);
- call_console_drivers(con, _con_start, _log_end);
+ spin_lock_irqsave(&logbuf_lock, flags);
+ must_wake_klogd |= log_start - log_end;
+ if (con_start == log_end)
+ break; /* Nothing to print */
+ _con_start = con_start;
+ _log_end = log_end;
+ con_start = log_end; /* Flush */
+ spin_unlock_irqrestore(&logbuf_lock, flags);
+ call_console_drivers(con, _con_start, _log_end);
}
- spin_unlock_irqrestore(&logbuf_lock, flags);
- if (must_wake_klogd && !oops_in_progress)
- wake_up_interruptible(&log_wait);
+ spin_unlock_irqrestore(&logbuf_lock, flags);
+ if (must_wake_klogd && !oops_in_progress)
+ wake_up_interruptible(&log_wait);
}
- up(&driver->tty_lock);
+ driver->may_schedule = 0;
+ up(&driver->tty_lock);
}
+/** console_conditional_schedule - yield the CPU if required
+ *
+ * If the console code is currently allowed to sleep, and
+ * if this CPU should yield the CPU to another task, do
+ * so here.
+ *
+ * Must be called within acquire_console_sem().
+ */
+void console_conditional_schedule(struct tty_driver *driver)
+{
+ if (driver->may_schedule && current->need_resched) {
+ set_current_state(TASK_RUNNING);
+ schedule();
+ }
+}
+
+void console_print(const char *s)
+{
+ printk(KERN_EMERG "%s", s);
+}
+EXPORT_SYMBOL(console_print);
+
/*
* The console driver calls this routine during kernel initialization
* to register the console printing procedure with printk() and to
* print any messages that were printed by the kernel before the
* console driver was initialized.
*/
-void register_console(struct console *console)
+void register_console(struct console * console)
{
struct tty_driver *driver;
unsigned long flags;
@@ -573,25 +605,26 @@
* Put this console in the list - keep the
* preferred driver at the head of the list.
*/
- down(&console_sem);
+ spin_lock(&console_lock);
if ((console->flags & CON_CONSDEV) || console_drivers == NULL) {
console->next = console_drivers;
console_drivers = console;
} else {
console->next = console_drivers->next;
console_drivers->next = console;
- }
- driver = get_tty_driver(console->device(console));
- if (driver) {
- init_MUTEX(&driver->tty_lock);
- driver->flags |= TTY_DRIVER_CONSOLE;
}
- up(&console_sem);
- if (console->flags & CON_PRINTBUFFER) {
+ spin_unlock(&console_lock);
+
+ driver = get_tty_driver(console->device(console));
+ if (driver) {
+ init_MUTEX(&driver->tty_lock);
+ driver->flags |= TTY_DRIVER_CONSOLE;
+ }
+
+ if (console->flags & CON_PRINTBUFFER) {
/*
- * release_cosole_sem() will print out the buffered
- * messages for us.
- */
+ * release_console_sem() will print out the buffered messages for us.
+ */
spin_lock_irqsave(&logbuf_lock, flags);
con_start = log_start;
spin_unlock_irqrestore(&logbuf_lock, flags);
@@ -600,15 +633,16 @@
}
EXPORT_SYMBOL(register_console);
-int unregister_console(struct console *console)
+int unregister_console(struct console * console)
{
struct tty_driver *driver = get_tty_driver(console->device(console));
struct console *a,*b;
int res = 1;
- down(&console_sem);
if (driver)
release_console_sem(console->device(console));
+
+ spin_lock(&console_lock);
if (console_drivers == console) {
console_drivers=console->next;
res = 0;
@@ -629,14 +663,16 @@
*/
if (console_drivers == NULL)
preferred_console = -1;
- up(&console_sem);
+
+ spin_unlock(&console_lock);
return res;
}
EXPORT_SYMBOL(unregister_console);
-/*
- * Write a message to a certain tty, not just the console. This is used for
- * messages that need to be redirected to a specific tty.
+/**
+ * tty_write_message - write a message to a certain tty, not just the console.
+ *
+ * This is used for messages that need to be redirected to a specific tty.
* We don't put it into the syslog queue right now maybe in the future if
* really needed.
*/
|