Parse command-line options much earlier, so that thread_mlfqs is
[pintos-anon] / src / lib / kernel / console.c
1 #include <console.h>
2 #include <stdarg.h>
3 #include <stdio.h>
4 #include "devices/serial.h"
5 #include "devices/vga.h"
6 #include "threads/init.h"
7 #include "threads/interrupt.h"
8 #include "threads/synch.h"
9
10 static void vprintf_helper (char, void *);
11 static void putchar_have_lock (uint8_t c);
12
13 /* The console lock.
14    Both the vga and serial layers do their own locking, so it's
15    safe to call them at any time.
16    But this lock is useful to prevent simultaneous printf() calls
17    from mixing their output, which looks confusing. */
18 static struct lock console_lock;
19
20 /* True in ordinary circumstances: we want to use the console
21    lock to avoid mixing output between threads, as explained
22    above.
23
24    False in early boot before the point that locks are functional
25    or the console lock has been initialized, or after a kernel
26    panics.  In the former case, taking the lock would cause an
27    assertion failure, which in turn would cause a panic, turning
28    it into the latter case.  In the latter case, if it is a buggy
29    lock_acquire() implementation that caused the panic, we'll
30    likely just recurse. */
31 static bool use_console_lock;
32
33 /* It's possible, if you add enough debug output to Pintos, to
34    try to recursively grab console_lock from a single thread.  As
35    a real example, I added a printf() call to palloc_free().
36    Here's a real backtrace that resulted:
37
38    lock_console()
39    vprintf()
40    printf()             - palloc() tries to grab the lock again
41    palloc_free()        
42    schedule_tail()      - another thread dying as we switch threads
43    schedule()
44    thread_yield()
45    intr_handler()       - timer interrupt
46    intr_set_level()
47    serial_putc()
48    putchar_have_lock()
49    putbuf()
50    sys_write()          - one process writing to the console
51    syscall_handler()
52    intr_handler()
53
54    This kind of thing is very difficult to debug, so we avoid the
55    problem by simulating a recursive lock with a depth
56    counter. */
57 static int console_lock_depth;
58
59 /* Number of characters written to console. */
60 static int64_t write_cnt;
61
62 /* Enable console locking. */
63 void
64 console_init (void) 
65 {
66   lock_init (&console_lock);
67   use_console_lock = true;
68 }
69
70 /* Notifies the console that a kernel panic is underway,
71    which warns it to avoid trying to take the console lock from
72    now on. */
73 void
74 console_panic (void) 
75 {
76   use_console_lock = false;
77 }
78
79 /* Prints console statistics. */
80 void
81 console_print_stats (void) 
82 {
83   printf ("Console: %lld characters output\n", write_cnt);
84 }
85
86 /* Acquires the console lock. */
87 static void
88 acquire_console (void) 
89 {
90   if (!intr_context () && use_console_lock) 
91     {
92       if (lock_held_by_current_thread (&console_lock)) 
93         console_lock_depth++; 
94       else
95         lock_acquire (&console_lock); 
96     }
97 }
98
99 /* Releases the console lock. */
100 static void
101 release_console (void) 
102 {
103   if (!intr_context () && use_console_lock) 
104     {
105       if (console_lock_depth > 0)
106         console_lock_depth--;
107       else
108         lock_release (&console_lock); 
109     }
110 }
111
112 /* Returns true if the current thread has the console lock,
113    false otherwise. */
114 static bool
115 console_locked_by_current_thread (void) 
116 {
117   return (intr_context ()
118           || !use_console_lock
119           || lock_held_by_current_thread (&console_lock));
120 }
121
122 /* The standard vprintf() function,
123    which is like printf() but uses a va_list.
124    Writes its output to both vga display and serial port. */
125 int
126 vprintf (const char *format, va_list args) 
127 {
128   int char_cnt = 0;
129
130   acquire_console ();
131   __vprintf (format, args, vprintf_helper, &char_cnt);
132   release_console ();
133
134   return char_cnt;
135 }
136
137 /* Writes string S to the console, followed by a new-line
138    character. */
139 int
140 puts (const char *s) 
141 {
142   acquire_console ();
143   while (*s != '\0')
144     putchar_have_lock (*s++);
145   putchar_have_lock ('\n');
146   release_console ();
147
148   return 0;
149 }
150
151 /* Writes the N characters in BUFFER to the console. */
152 void
153 putbuf (const char *buffer, size_t n) 
154 {
155   acquire_console ();
156   while (n-- > 0)
157     putchar_have_lock (*buffer++);
158   release_console ();
159 }
160
161 /* Writes C to the vga display and serial port. */
162 int
163 putchar (int c) 
164 {
165   acquire_console ();
166   putchar_have_lock (c);
167   release_console ();
168   
169   return c;
170 }
171 \f
172 /* Helper function for vprintf(). */
173 static void
174 vprintf_helper (char c, void *char_cnt_) 
175 {
176   int *char_cnt = char_cnt_;
177   (*char_cnt)++;
178   putchar_have_lock (c);
179 }
180
181 /* Writes C to the vga display and serial port.
182    The caller has already acquired the console lock if
183    appropriate. */
184 static void
185 putchar_have_lock (uint8_t c) 
186 {
187   ASSERT (console_locked_by_current_thread ());
188   write_cnt++;
189   serial_putc (c);
190   vga_putc (c);
191 }