X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flib%2Fkernel%2Fconsole.c;h=0d031b5b4bdd6a2257f416bdf1fc1ffdbbb4398e;hb=53a7f5d0952a4595f252247f5ee3d017468eb57e;hp=149f29c7eb46f07e12c6af8744d8669e25b0340c;hpb=76f6e5dbcd612bbf79e0243bb8a0247b52043571;p=pintos-anon diff --git a/src/lib/kernel/console.c b/src/lib/kernel/console.c index 149f29c..0d031b5 100644 --- a/src/lib/kernel/console.c +++ b/src/lib/kernel/console.c @@ -3,11 +3,12 @@ #include #include "devices/serial.h" #include "devices/vga.h" +#include "threads/init.h" #include "threads/interrupt.h" #include "threads/synch.h" static void vprintf_helper (char, void *); -static void putchar_unlocked (uint8_t c); +static void putchar_have_lock (uint8_t c); /* The console lock. Both the vga and serial layers do their own locking, so it's @@ -16,10 +17,106 @@ static void putchar_unlocked (uint8_t c); from mixing their output, which looks confusing. */ static struct lock console_lock; +/* True in ordinary circumstances: we want to use the console + lock to avoid mixing output between threads, as explained + above. + + False in early boot before the point that locks are functional + or the console lock has been initialized, or after a kernel + panics. In the former case, taking the lock would cause an + assertion failure, which in turn would cause a panic, turning + it into the latter case. In the latter case, if it is a buggy + lock_acquire() implementation that caused the panic, we'll + likely just recurse. */ +static bool use_console_lock; + +/* It's possible, if you add enough debug output to Pintos, to + try to recursively grab console_lock from a single thread. As + a real example, I added a printf() call to palloc_free(). + Here's a real backtrace that resulted: + + lock_console() + vprintf() + printf() - palloc() tries to grab the lock again + palloc_free() + schedule_tail() - another thread dying as we switch threads + schedule() + thread_yield() + intr_handler() - timer interrupt + intr_set_level() + serial_putc() + putchar_have_lock() + putbuf() + sys_write() - one process writing to the console + syscall_handler() + intr_handler() + + This kind of thing is very difficult to debug, so we avoid the + problem by simulating a recursive lock with a depth + counter. */ +static int console_lock_depth; + +/* Number of characters written to console. */ +static int64_t write_cnt; + +/* Enable console locking. */ void console_init (void) { - lock_init (&console_lock, "console"); + lock_init (&console_lock); + use_console_lock = true; +} + +/* Notifies the console that a kernel panic is underway, + which warns it to avoid trying to take the console lock from + now on. */ +void +console_panic (void) +{ + use_console_lock = false; +} + +/* Prints console statistics. */ +void +console_print_stats (void) +{ + printf ("Console: %lld characters output\n", write_cnt); +} + +/* Acquires the console lock. */ +static void +acquire_console (void) +{ + if (!intr_context () && use_console_lock) + { + if (lock_held_by_current_thread (&console_lock)) + console_lock_depth++; + else + lock_acquire (&console_lock); + } +} + +/* Releases the console lock. */ +static void +release_console (void) +{ + if (!intr_context () && use_console_lock) + { + if (console_lock_depth > 0) + console_lock_depth--; + else + lock_release (&console_lock); + } +} + +/* Returns true if the current thread has the console lock, + false otherwise. */ +static bool +console_locked_by_current_thread (void) +{ + return (intr_context () + || !use_console_lock + || lock_held_by_current_thread (&console_lock)); } /* The standard vprintf() function, @@ -30,13 +127,9 @@ vprintf (const char *format, va_list args) { int char_cnt = 0; - if (!intr_context ()) - lock_acquire (&console_lock); - + acquire_console (); __vprintf (format, args, vprintf_helper, &char_cnt); - - if (!intr_context ()) - lock_release (&console_lock); + release_console (); return char_cnt; } @@ -46,15 +139,11 @@ vprintf (const char *format, va_list args) int puts (const char *s) { - if (!intr_context ()) - lock_acquire (&console_lock); - + acquire_console (); while (*s != '\0') - putchar_unlocked (*s++); - putchar_unlocked ('\n'); - - if (!intr_context ()) - lock_release (&console_lock); + putchar_have_lock (*s++); + putchar_have_lock ('\n'); + release_console (); return 0; } @@ -63,28 +152,20 @@ puts (const char *s) void putbuf (const char *buffer, size_t n) { - if (!intr_context ()) - lock_acquire (&console_lock); - + acquire_console (); while (n-- > 0) - putchar_unlocked (*buffer++); - - if (!intr_context ()) - lock_release (&console_lock); + putchar_have_lock (*buffer++); + release_console (); } /* Writes C to the vga display and serial port. */ int putchar (int c) { - if (!intr_context ()) - lock_acquire (&console_lock); - - putchar_unlocked (c); - - if (!intr_context ()) - lock_release (&console_lock); - + acquire_console (); + putchar_have_lock (c); + release_console (); + return c; } @@ -94,15 +175,17 @@ vprintf_helper (char c, void *char_cnt_) { int *char_cnt = char_cnt_; (*char_cnt)++; - putchar_unlocked (c); + putchar_have_lock (c); } /* Writes C to the vga display and serial port. The caller has already acquired the console lock if appropriate. */ static void -putchar_unlocked (uint8_t c) +putchar_have_lock (uint8_t c) { + ASSERT (console_locked_by_current_thread ()); + write_cnt++; serial_putc (c); vga_putc (c); }