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

Last change on this file was 690ad20, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 3 months ago

Convert kio buffer to bytes (part 1)

  • Property mode set to 100644
File size: 8.7 KB
RevLine 
[91db0280]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
[174156fd]29/** @addtogroup kernel_generic
[91db0280]30 * @{
31 */
32/** @file
33 */
34
[163e34c]35#include <abi/log.h>
36#include <arch.h>
37#include <atomic.h>
38#include <console/console.h>
[91db0280]39#include <ddi/ddi.h>
[163e34c]40#include <ddi/irq.h>
41#include <errno.h>
[91db0280]42#include <ipc/event.h>
43#include <ipc/irq.h>
[163e34c]44#include <log.h>
[91db0280]45#include <panic.h>
46#include <print.h>
[163e34c]47#include <printf_core.h>
[91db0280]48#include <stdarg.h>
[aafed15]49#include <stdlib.h>
[163e34c]50#include <str.h>
51#include <synch/spinlock.h>
52#include <syscall/copy.h>
53#include <sysinfo/sysinfo.h>
54#include <typedefs.h>
[91db0280]55
56#define LOG_PAGES 8
57#define LOG_LENGTH (LOG_PAGES * PAGE_SIZE)
58#define LOG_ENTRY_HEADER_LENGTH (sizeof(size_t) + sizeof(uint32_t))
59
60/** Cyclic buffer holding the data for kernel log */
[571cc2d]61static uint8_t log_buffer[LOG_LENGTH] __attribute__((aligned(PAGE_SIZE)));
[91db0280]62
63/** Kernel log initialized */
[e90cfa6]64static atomic_bool log_inited = false;
[91db0280]65
66/** Position in the cyclic buffer where the first log entry starts */
[571cc2d]67static size_t log_start = 0;
[91db0280]68
69/** Sum of length of all log entries currently stored in the cyclic buffer */
[571cc2d]70static size_t log_used = 0;
[91db0280]71
72/** Log spinlock */
[571cc2d]73static IRQ_SPINLOCK_INITIALIZE(log_lock);
[91db0280]74
75/** Overall count of logged messages, which may overflow as needed */
76static uint32_t log_counter = 0;
77
78/** Starting position of the entry currently being written to the log */
79static size_t log_current_start = 0;
80
81/** Length (including header) of the entry currently being written to the log */
82static size_t log_current_len = 0;
83
84/** Start of the next entry to be handed to uspace starting from log_start */
85static size_t next_for_uspace = 0;
86
87static void log_update(void *);
88
89/** Initialize kernel logging facility
90 *
91 */
92void log_init(void)
[1b20da0]93{
[91db0280]94 event_set_unmask_callback(EVENT_KLOG, log_update);
[e3306d04]95 atomic_store(&log_inited, true);
[91db0280]96}
97
[ae7d03c]98static size_t log_copy_from(uint8_t *data, size_t pos, size_t len)
99{
[91db0280]100 for (size_t i = 0; i < len; i++, pos = (pos + 1) % LOG_LENGTH) {
101 data[i] = log_buffer[pos];
102 }
103 return pos;
104}
105
[ae7d03c]106static size_t log_copy_to(const uint8_t *data, size_t pos, size_t len)
107{
[91db0280]108 for (size_t i = 0; i < len; i++, pos = (pos + 1) % LOG_LENGTH) {
109 log_buffer[pos] = data[i];
110 }
111 return pos;
112}
113
114/** Append data to the currently open log entry.
[1b20da0]115 *
[91db0280]116 * This function requires that the log_lock is acquired by the caller.
117 */
118static void log_append(const uint8_t *data, size_t len)
119{
120 /* Cap the length so that the entry entirely fits into the buffer */
121 if (len > LOG_LENGTH - log_current_len) {
122 len = LOG_LENGTH - log_current_len;
123 }
[a35b458]124
[91db0280]125 if (len == 0)
126 return;
[a35b458]127
[91db0280]128 size_t log_free = LOG_LENGTH - log_used - log_current_len;
[a35b458]129
[91db0280]130 /* Discard older entries to make space, if necessary */
131 while (len > log_free) {
132 size_t entry_len;
133 log_copy_from((uint8_t *) &entry_len, log_start, sizeof(size_t));
134 log_start = (log_start + entry_len) % LOG_LENGTH;
135 log_used -= entry_len;
136 log_free += entry_len;
137 next_for_uspace -= entry_len;
138 }
[a35b458]139
[91db0280]140 size_t pos = (log_current_start + log_current_len) % LOG_LENGTH;
141 log_copy_to(data, pos, len);
142 log_current_len += len;
143}
144
145/** Begin writing an entry to the log.
[1b20da0]146 *
[91db0280]147 * This acquires the log and output buffer locks, so only calls to log_* functions should
148 * be used until calling log_end.
149 */
150void log_begin(log_facility_t fac, log_level_t level)
151{
[90dd8aee]152 console_lock();
[571cc2d]153 irq_spinlock_lock(&log_lock, true);
154 irq_spinlock_lock(&kio_lock, true);
[a35b458]155
[91db0280]156 log_current_start = (log_start + log_used) % LOG_LENGTH;
157 log_current_len = 0;
[a35b458]158
[91db0280]159 /* Write header of the log entry, the length will be written in log_end() */
160 log_append((uint8_t *) &log_current_len, sizeof(size_t));
161 log_append((uint8_t *) &log_counter, sizeof(uint32_t));
162 uint32_t fac32 = fac;
163 uint32_t lvl32 = level;
164 log_append((uint8_t *) &fac32, sizeof(uint32_t));
165 log_append((uint8_t *) &lvl32, sizeof(uint32_t));
[a35b458]166
[91db0280]167 log_counter++;
168}
169
170/** Finish writing an entry to the log.
[1b20da0]171 *
[91db0280]172 * This releases the log and output buffer locks.
173 */
[ae7d03c]174void log_end(void)
175{
[91db0280]176 /* Set the length in the header to correct value */
177 log_copy_to((uint8_t *) &log_current_len, log_current_start, sizeof(size_t));
178 log_used += log_current_len;
[a35b458]179
[690ad20]180 kio_push_bytes("\n", 1);
[571cc2d]181 irq_spinlock_unlock(&kio_lock, true);
182 irq_spinlock_unlock(&log_lock, true);
[a35b458]183
[91db0280]184 /* This has to be called after we released the locks above */
185 kio_flush();
186 kio_update(NULL);
187 log_update(NULL);
[90dd8aee]188 console_unlock();
[91db0280]189}
190
191static void log_update(void *event)
192{
[036e97c]193 if (!atomic_load(&log_inited))
[91db0280]194 return;
[a35b458]195
[571cc2d]196 irq_spinlock_lock(&log_lock, true);
[91db0280]197 if (next_for_uspace < log_used)
198 event_notify_0(EVENT_KLOG, true);
[571cc2d]199 irq_spinlock_unlock(&log_lock, true);
[91db0280]200}
201
202static int log_printf_str_write(const char *str, size_t size, void *data)
203{
[690ad20]204 kio_push_bytes(str, size);
[91db0280]205 log_append((const uint8_t *)str, size);
[163e34c]206 return EOK;
[91db0280]207}
208
209/** Append a message to the currently being written entry.
[1b20da0]210 *
[91db0280]211 * Requires that an entry has been started using log_begin()
212 */
213int log_vprintf(const char *fmt, va_list args)
214{
215 printf_spec_t ps = {
216 log_printf_str_write,
217 NULL
218 };
[a35b458]219
[163e34c]220 return printf_core(fmt, &ps, args);
[91db0280]221}
222
223/** Append a message to the currently being written entry.
[1b20da0]224 *
[91db0280]225 * Requires that an entry has been started using log_begin()
226 */
227int log_printf(const char *fmt, ...)
228{
229 int ret;
230 va_list args;
[a35b458]231
[91db0280]232 va_start(args, fmt);
233 ret = log_vprintf(fmt, args);
234 va_end(args);
[a35b458]235
[91db0280]236 return ret;
237}
238
239/** Log a message to the kernel log.
[1b20da0]240 *
[91db0280]241 * This atomically appends a log entry.
242 * The resulting message should not contain a trailing newline, as the log
243 * entries are explicitly delimited when stored in the log.
244 */
245int log(log_facility_t fac, log_level_t level, const char *fmt, ...)
246{
247 int ret;
248 va_list args;
[a35b458]249
[91db0280]250 log_begin(fac, level);
[a35b458]251
[91db0280]252 va_start(args, fmt);
253 ret = log_vprintf(fmt, args);
254 va_end(args);
[a35b458]255
[91db0280]256 log_end();
[a35b458]257
[91db0280]258 return ret;
259}
260
261/** Control of the log from uspace
262 *
263 */
[5a5269d]264sys_errno_t sys_klog(sysarg_t operation, uspace_addr_t buf, size_t size,
265 sysarg_t level, uspace_ptr_size_t uspace_nread)
[91db0280]266{
267 char *data;
[b7fd2a0]268 errno_t rc;
[a35b458]269
[91db0280]270 if (size > PAGE_SIZE)
[b7fd2a0]271 return (sys_errno_t) ELIMIT;
[a35b458]272
[91db0280]273 switch (operation) {
[ae7d03c]274 case KLOG_WRITE:
[11b285d]275 data = (char *) malloc(size + 1);
[ae7d03c]276 if (!data)
277 return (sys_errno_t) ENOMEM;
278
279 rc = copy_from_uspace(data, buf, size);
280 if (rc) {
281 free(data);
282 return (sys_errno_t) rc;
283 }
284 data[size] = 0;
[a35b458]285
[ae7d03c]286 if (level >= LVL_LIMIT)
287 level = LVL_NOTE;
[a35b458]288
[ae7d03c]289 log(LF_USPACE, level, "%s", data);
[a35b458]290
[ae7d03c]291 free(data);
292 return EOK;
293 case KLOG_READ:
[11b285d]294 data = (char *) malloc(size);
[ae7d03c]295 if (!data)
296 return (sys_errno_t) ENOMEM;
297
298 size_t entry_len = 0;
299 size_t copied = 0;
300
301 rc = EOK;
302
[571cc2d]303 irq_spinlock_lock(&log_lock, true);
[ae7d03c]304
305 while (next_for_uspace < log_used) {
306 size_t pos = (log_start + next_for_uspace) % LOG_LENGTH;
307 log_copy_from((uint8_t *) &entry_len, pos, sizeof(size_t));
308
309 if (entry_len > PAGE_SIZE) {
310 /*
311 * Since we limit data transfer
312 * to uspace to a maximum of PAGE_SIZE
313 * bytes, skip any entries larger
314 * than this limit to prevent
315 * userspace being stuck trying to
316 * read them.
317 */
[91db0280]318 next_for_uspace += entry_len;
[ae7d03c]319 continue;
[91db0280]320 }
[a35b458]321
[ae7d03c]322 if (size < copied + entry_len) {
323 if (copied == 0)
324 rc = EOVERFLOW;
325 break;
[91db0280]326 }
[a35b458]327
[ae7d03c]328 log_copy_from((uint8_t *) (data + copied), pos, entry_len);
329 copied += entry_len;
330 next_for_uspace += entry_len;
331 }
332
[571cc2d]333 irq_spinlock_unlock(&log_lock, true);
[a35b458]334
[ae7d03c]335 if (rc != EOK) {
[91db0280]336 free(data);
[ae7d03c]337 return (sys_errno_t) rc;
338 }
339
340 rc = copy_to_uspace(buf, data, size);
341
342 free(data);
[a35b458]343
[ae7d03c]344 if (rc != EOK)
345 return (sys_errno_t) rc;
[a35b458]346
[ae7d03c]347 return copy_to_uspace(uspace_nread, &copied, sizeof(copied));
348 return EOK;
349 default:
350 return (sys_errno_t) ENOTSUP;
[91db0280]351 }
352}
353
354/** @}
355 */
Note: See TracBrowser for help on using the repository browser.