Parse command-line options much earlier, so that thread_mlfqs is
[pintos-anon] / src / lib / kernel / console.c
index 803b46b393dca80c9773e165bdcf8715c2a67de5..0d031b5b4bdd6a2257f416bdf1fc1ffdbbb4398e 100644 (file)
@@ -3,11 +3,12 @@
 #include <stdio.h>
 #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,31 +139,33 @@ 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;
 }
 
+/* Writes the N characters in BUFFER to the console. */
+void
+putbuf (const char *buffer, size_t n) 
+{
+  acquire_console ();
+  while (n-- > 0)
+    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;
 }
 \f
@@ -80,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);
 }