source: mainline/kernel/generic/src/log/log.c@ f167c851

Last change on this file since f167c851 was 90dd8aee, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 9 months ago

Introduce a console lock to prevent line splitting in kernel output

It is a common occurrence to see broken lines on screen or in serial
output due to bad timing. This is an attempt to prevent that without
breaking anything.

It could be considered a stopgap solution, since the whole console
stack deserves a better/faster implementation.

  • Property mode set to 100644
File size: 9.3 KB
Line 
1/*
2 * Copyright (c) 2013 Martin Sucha
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup kernel_generic
30 * @{
31 */
32/** @file
33 */
34
35#include <sysinfo/sysinfo.h>
36#include <synch/spinlock.h>
37#include <typedefs.h>
38#include <ddi/irq.h>
39#include <ddi/ddi.h>
40#include <ipc/event.h>
41#include <ipc/irq.h>
42#include <arch.h>
43#include <panic.h>
44#include <putchar.h>
45#include <atomic.h>
46#include <syscall/copy.h>
47#include <errno.h>
48#include <str.h>
49#include <print.h>
50#include <printf/printf_core.h>
51#include <stdarg.h>
52#include <log.h>
53#include <console/console.h>
54#include <abi/log.h>
55#include <stdlib.h>
56
57#define LOG_PAGES 8
58#define LOG_LENGTH (LOG_PAGES * PAGE_SIZE)
59#define LOG_ENTRY_HEADER_LENGTH (sizeof(size_t) + sizeof(uint32_t))
60
61/** Cyclic buffer holding the data for kernel log */
62uint8_t log_buffer[LOG_LENGTH] __attribute__((aligned(PAGE_SIZE)));
63
64/** Kernel log initialized */
65static atomic_bool log_inited = false;
66
67/** Position in the cyclic buffer where the first log entry starts */
68size_t log_start = 0;
69
70/** Sum of length of all log entries currently stored in the cyclic buffer */
71size_t log_used = 0;
72
73/** Log spinlock */
74SPINLOCK_STATIC_INITIALIZE_NAME(log_lock, "log_lock");
75
76/** Overall count of logged messages, which may overflow as needed */
77static uint32_t log_counter = 0;
78
79/** Starting position of the entry currently being written to the log */
80static size_t log_current_start = 0;
81
82/** Length (including header) of the entry currently being written to the log */
83static size_t log_current_len = 0;
84
85/** Start of the next entry to be handed to uspace starting from log_start */
86static size_t next_for_uspace = 0;
87
88static void log_update(void *);
89
90/** Initialize kernel logging facility
91 *
92 */
93void log_init(void)
94{
95 event_set_unmask_callback(EVENT_KLOG, log_update);
96 atomic_store(&log_inited, true);
97}
98
99static size_t log_copy_from(uint8_t *data, size_t pos, size_t len)
100{
101 for (size_t i = 0; i < len; i++, pos = (pos + 1) % LOG_LENGTH) {
102 data[i] = log_buffer[pos];
103 }
104 return pos;
105}
106
107static size_t log_copy_to(const uint8_t *data, size_t pos, size_t len)
108{
109 for (size_t i = 0; i < len; i++, pos = (pos + 1) % LOG_LENGTH) {
110 log_buffer[pos] = data[i];
111 }
112 return pos;
113}
114
115/** Append data to the currently open log entry.
116 *
117 * This function requires that the log_lock is acquired by the caller.
118 */
119static void log_append(const uint8_t *data, size_t len)
120{
121 /* Cap the length so that the entry entirely fits into the buffer */
122 if (len > LOG_LENGTH - log_current_len) {
123 len = LOG_LENGTH - log_current_len;
124 }
125
126 if (len == 0)
127 return;
128
129 size_t log_free = LOG_LENGTH - log_used - log_current_len;
130
131 /* Discard older entries to make space, if necessary */
132 while (len > log_free) {
133 size_t entry_len;
134 log_copy_from((uint8_t *) &entry_len, log_start, sizeof(size_t));
135 log_start = (log_start + entry_len) % LOG_LENGTH;
136 log_used -= entry_len;
137 log_free += entry_len;
138 next_for_uspace -= entry_len;
139 }
140
141 size_t pos = (log_current_start + log_current_len) % LOG_LENGTH;
142 log_copy_to(data, pos, len);
143 log_current_len += len;
144}
145
146/** Begin writing an entry to the log.
147 *
148 * This acquires the log and output buffer locks, so only calls to log_* functions should
149 * be used until calling log_end.
150 */
151void log_begin(log_facility_t fac, log_level_t level)
152{
153 console_lock();
154 spinlock_lock(&log_lock);
155 spinlock_lock(&kio_lock);
156
157 log_current_start = (log_start + log_used) % LOG_LENGTH;
158 log_current_len = 0;
159
160 /* Write header of the log entry, the length will be written in log_end() */
161 log_append((uint8_t *) &log_current_len, sizeof(size_t));
162 log_append((uint8_t *) &log_counter, sizeof(uint32_t));
163 uint32_t fac32 = fac;
164 uint32_t lvl32 = level;
165 log_append((uint8_t *) &fac32, sizeof(uint32_t));
166 log_append((uint8_t *) &lvl32, sizeof(uint32_t));
167
168 log_counter++;
169}
170
171/** Finish writing an entry to the log.
172 *
173 * This releases the log and output buffer locks.
174 */
175void log_end(void)
176{
177 /* Set the length in the header to correct value */
178 log_copy_to((uint8_t *) &log_current_len, log_current_start, sizeof(size_t));
179 log_used += log_current_len;
180
181 kio_push_char('\n');
182 spinlock_unlock(&kio_lock);
183 spinlock_unlock(&log_lock);
184
185 /* This has to be called after we released the locks above */
186 kio_flush();
187 kio_update(NULL);
188 log_update(NULL);
189 console_unlock();
190}
191
192static void log_update(void *event)
193{
194 if (!atomic_load(&log_inited))
195 return;
196
197 spinlock_lock(&log_lock);
198 if (next_for_uspace < log_used)
199 event_notify_0(EVENT_KLOG, true);
200 spinlock_unlock(&log_lock);
201}
202
203static int log_printf_str_write(const char *str, size_t size, void *data)
204{
205 size_t offset = 0;
206 size_t chars = 0;
207
208 while (offset < size) {
209 kio_push_char(str_decode(str, &offset, size));
210 chars++;
211 }
212
213 log_append((const uint8_t *)str, size);
214
215 return chars;
216}
217
218static int log_printf_wstr_write(const char32_t *wstr, size_t size, void *data)
219{
220 char buffer[16];
221 size_t offset = 0;
222 size_t chars = 0;
223
224 for (offset = 0; offset < size; offset += sizeof(char32_t), chars++) {
225 kio_push_char(wstr[chars]);
226
227 size_t buffer_offset = 0;
228 errno_t rc = chr_encode(wstr[chars], buffer, &buffer_offset, 16);
229 if (rc != EOK) {
230 return EOF;
231 }
232
233 log_append((const uint8_t *)buffer, buffer_offset);
234 }
235
236 return chars;
237}
238
239/** Append a message to the currently being written entry.
240 *
241 * Requires that an entry has been started using log_begin()
242 */
243int log_vprintf(const char *fmt, va_list args)
244{
245 int ret;
246
247 printf_spec_t ps = {
248 log_printf_str_write,
249 log_printf_wstr_write,
250 NULL
251 };
252
253 ret = printf_core(fmt, &ps, args);
254
255 return ret;
256}
257
258/** Append a message to the currently being written entry.
259 *
260 * Requires that an entry has been started using log_begin()
261 */
262int log_printf(const char *fmt, ...)
263{
264 int ret;
265 va_list args;
266
267 va_start(args, fmt);
268 ret = log_vprintf(fmt, args);
269 va_end(args);
270
271 return ret;
272}
273
274/** Log a message to the kernel log.
275 *
276 * This atomically appends a log entry.
277 * The resulting message should not contain a trailing newline, as the log
278 * entries are explicitly delimited when stored in the log.
279 */
280int log(log_facility_t fac, log_level_t level, const char *fmt, ...)
281{
282 int ret;
283 va_list args;
284
285 log_begin(fac, level);
286
287 va_start(args, fmt);
288 ret = log_vprintf(fmt, args);
289 va_end(args);
290
291 log_end();
292
293 return ret;
294}
295
296/** Control of the log from uspace
297 *
298 */
299sys_errno_t sys_klog(sysarg_t operation, uspace_addr_t buf, size_t size,
300 sysarg_t level, uspace_ptr_size_t uspace_nread)
301{
302 char *data;
303 errno_t rc;
304
305 if (size > PAGE_SIZE)
306 return (sys_errno_t) ELIMIT;
307
308 switch (operation) {
309 case KLOG_WRITE:
310 data = (char *) malloc(size + 1);
311 if (!data)
312 return (sys_errno_t) ENOMEM;
313
314 rc = copy_from_uspace(data, buf, size);
315 if (rc) {
316 free(data);
317 return (sys_errno_t) rc;
318 }
319 data[size] = 0;
320
321 if (level >= LVL_LIMIT)
322 level = LVL_NOTE;
323
324 log(LF_USPACE, level, "%s", data);
325
326 free(data);
327 return EOK;
328 case KLOG_READ:
329 data = (char *) malloc(size);
330 if (!data)
331 return (sys_errno_t) ENOMEM;
332
333 size_t entry_len = 0;
334 size_t copied = 0;
335
336 rc = EOK;
337
338 spinlock_lock(&log_lock);
339
340 while (next_for_uspace < log_used) {
341 size_t pos = (log_start + next_for_uspace) % LOG_LENGTH;
342 log_copy_from((uint8_t *) &entry_len, pos, sizeof(size_t));
343
344 if (entry_len > PAGE_SIZE) {
345 /*
346 * Since we limit data transfer
347 * to uspace to a maximum of PAGE_SIZE
348 * bytes, skip any entries larger
349 * than this limit to prevent
350 * userspace being stuck trying to
351 * read them.
352 */
353 next_for_uspace += entry_len;
354 continue;
355 }
356
357 if (size < copied + entry_len) {
358 if (copied == 0)
359 rc = EOVERFLOW;
360 break;
361 }
362
363 log_copy_from((uint8_t *) (data + copied), pos, entry_len);
364 copied += entry_len;
365 next_for_uspace += entry_len;
366 }
367
368 spinlock_unlock(&log_lock);
369
370 if (rc != EOK) {
371 free(data);
372 return (sys_errno_t) rc;
373 }
374
375 rc = copy_to_uspace(buf, data, size);
376
377 free(data);
378
379 if (rc != EOK)
380 return (sys_errno_t) rc;
381
382 return copy_to_uspace(uspace_nread, &copied, sizeof(copied));
383 return EOK;
384 default:
385 return (sys_errno_t) ENOTSUP;
386 }
387}
388
389/** @}
390 */
Note: See TracBrowser for help on using the repository browser.