source: mainline/kernel/generic/src/interrupt/interrupt.c@ b2ec5cf

ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b2ec5cf was b2ec5cf, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 2 years ago

Implement atomic_time_stat_t for lockless timekeeping

We keep monotonically increasing temporal statistics in several places.
They are frequently written from the thread that owns them, and rarely
read from other threads in certain syscalls. This new code serves the
purpose of avoiding the need for synchronization on the writer side.
On 64b system, we can simply assume that 64b writes are indivisible,
and relaxed atomic read/writes simply serve to formally prevent C
undefined behavior from data races (they translate to regular memory
reads/writes in assembly).

On 32b systems, we use the same algorithm that's been used for userspace
clock access, using three fields and some memory barriers to maintain
consistency of reads when the upper half changes. Only readers always
synchronize though. For writers, barriers are avoided in the common case
when the upper half remains unchanged.

  • Property mode set to 100644
File size: 8.7 KB
Line 
1/*
2 * Copyright (c) 2005 Ondrej Palkovsky
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_interrupt
30 * @{
31 */
32/**
33 * @file
34 * @brief Interrupt redirector.
35 *
36 * This file provides means of registering interrupt handlers
37 * by kernel functions and calling the handlers when interrupts
38 * occur.
39 *
40 */
41
42#include <assert.h>
43#include <interrupt.h>
44#include <console/kconsole.h>
45#include <console/console.h>
46#include <console/cmd.h>
47#include <synch/mutex.h>
48#include <time/delay.h>
49#include <macros.h>
50#include <panic.h>
51#include <stdio.h>
52#include <stdarg.h>
53#include <symtab.h>
54#include <proc/thread.h>
55#include <arch/cycle.h>
56#include <arch/stack.h>
57#include <str.h>
58#include <trace.h>
59
60exc_table_t exc_table[IVT_ITEMS];
61IRQ_SPINLOCK_INITIALIZE(exctbl_lock);
62
63/** Register exception handler
64 *
65 * @param n Exception number.
66 * @param name Description.
67 * @param hot Whether the exception is actually handled
68 * in any meaningful way.
69 * @param handler New exception handler.
70 *
71 * @return Previously registered exception handler.
72 *
73 */
74iroutine_t exc_register(unsigned int n, const char *name, bool hot,
75 iroutine_t handler)
76{
77#if (IVT_ITEMS > 0)
78 assert(n < IVT_ITEMS);
79#endif
80
81 irq_spinlock_lock(&exctbl_lock, true);
82
83 iroutine_t old = exc_table[n].handler;
84 exc_table[n].handler = handler;
85 exc_table[n].name = name;
86 exc_table[n].hot = hot;
87 exc_table[n].cycles = 0;
88 exc_table[n].count = 0;
89
90 irq_spinlock_unlock(&exctbl_lock, true);
91
92 return old;
93}
94
95/** Dispatch exception according to exception table
96 *
97 * Called directly from the assembler code.
98 * CPU is interrupts_disable()'d.
99 *
100 */
101_NO_TRACE void exc_dispatch(unsigned int n, istate_t *istate)
102{
103#if (IVT_ITEMS > 0)
104 assert(n < IVT_ITEMS);
105#endif
106
107 /* Account user cycles */
108 if (THREAD) {
109 irq_spinlock_lock(&THREAD->lock, false);
110 thread_update_accounting(true);
111 irq_spinlock_unlock(&THREAD->lock, false);
112 }
113
114 /* Account CPU usage if it woke up from sleep */
115 if (CPU && CPU->idle) {
116 irq_spinlock_lock(&CPU->lock, false);
117 uint64_t now = get_cycle();
118 atomic_time_increment(&CPU->idle_cycles, now - CPU->last_cycle);
119 CPU->last_cycle = now;
120 CPU->idle = false;
121 irq_spinlock_unlock(&CPU->lock, false);
122 }
123
124 uint64_t begin_cycle = get_cycle();
125
126#ifdef CONFIG_UDEBUG
127 if (THREAD)
128 THREAD->udebug.uspace_state = istate;
129#endif
130
131 exc_table[n].handler(n + IVT_FIRST, istate);
132
133#ifdef CONFIG_UDEBUG
134 if (THREAD)
135 THREAD->udebug.uspace_state = NULL;
136#endif
137
138 /* This is a safe place to exit exiting thread */
139 if ((THREAD) && (THREAD->interrupted) && (istate_from_uspace(istate)))
140 thread_exit();
141
142 /* Account exception handling */
143 uint64_t end_cycle = get_cycle();
144
145 irq_spinlock_lock(&exctbl_lock, false);
146 exc_table[n].cycles += end_cycle - begin_cycle;
147 exc_table[n].count++;
148 irq_spinlock_unlock(&exctbl_lock, false);
149
150 /* Do not charge THREAD for exception cycles */
151 if (THREAD) {
152 irq_spinlock_lock(&THREAD->lock, false);
153 THREAD->last_cycle = end_cycle;
154 irq_spinlock_unlock(&THREAD->lock, false);
155 }
156}
157
158/** Default 'null' exception handler
159 *
160 */
161_NO_TRACE static void exc_undef(unsigned int n, istate_t *istate)
162{
163 fault_if_from_uspace(istate, "Unhandled exception %u.", n);
164 panic_badtrap(istate, n, "Unhandled exception %u.", n);
165}
166
167static _NO_TRACE void
168fault_from_uspace_core(istate_t *istate, const char *fmt, va_list args)
169{
170 printf("Task %s (%" PRIu64 ") killed due to an exception at "
171 "program counter %p.\n", TASK->name, TASK->taskid,
172 (void *) istate_get_pc(istate));
173
174 istate_decode(istate);
175 stack_trace_istate(istate);
176
177 printf("Kill message: ");
178 vprintf(fmt, args);
179 printf("\n");
180
181 task_kill_self(true);
182}
183
184/** Terminate thread and task after the exception came from userspace.
185 *
186 */
187_NO_TRACE void fault_from_uspace(istate_t *istate, const char *fmt, ...)
188{
189 va_list args;
190
191 va_start(args, fmt);
192 fault_from_uspace_core(istate, fmt, args);
193 va_end(args);
194}
195
196/** Terminate thread and task if exception came from userspace.
197 *
198 */
199_NO_TRACE void fault_if_from_uspace(istate_t *istate, const char *fmt, ...)
200{
201 if (!istate_from_uspace(istate))
202 return;
203
204 va_list args;
205 va_start(args, fmt);
206 fault_from_uspace_core(istate, fmt, args);
207 va_end(args);
208}
209
210/** Get istate structure of a thread.
211 *
212 * Get pointer to the istate structure at the bottom of the kernel stack.
213 *
214 * This function can be called in interrupt or user context. In interrupt
215 * context the istate structure is created by the low-level exception
216 * handler. In user context the istate structure is created by the
217 * low-level syscall handler.
218 */
219istate_t *istate_get(thread_t *thread)
220{
221 /*
222 * The istate structure should be right at the bottom of the kernel
223 * memory stack.
224 */
225 return (istate_t *) &thread->kstack[MEM_STACK_SIZE - sizeof(istate_t)];
226}
227
228#ifdef CONFIG_KCONSOLE
229
230static char flag_buf[MAX_CMDLINE + 1];
231
232/** Print all exceptions
233 *
234 */
235_NO_TRACE static int cmd_exc_print(cmd_arg_t *argv)
236{
237 bool excs_all;
238
239 if (str_cmp(flag_buf, "-a") == 0)
240 excs_all = true;
241 else if (str_cmp(flag_buf, "") == 0)
242 excs_all = false;
243 else {
244 printf("Unknown argument \"%s\".\n", flag_buf);
245 return 1;
246 }
247
248#if (IVT_ITEMS > 0)
249 unsigned int i;
250 unsigned int rows;
251
252 irq_spinlock_lock(&exctbl_lock, true);
253
254#ifdef __32_BITS__
255 printf("[exc ] [description ] [count ] [cycles ]"
256 " [handler ] [symbol\n");
257 rows = 1;
258#endif
259
260#ifdef __64_BITS__
261 printf("[exc ] [description ] [count ] [cycles ]"
262 " [handler ]\n");
263 printf(" [symbol\n");
264 rows = 2;
265#endif
266
267 for (i = 0; i < IVT_ITEMS; i++) {
268 if ((!excs_all) && (!exc_table[i].hot))
269 continue;
270
271 uint64_t count;
272 char count_suffix;
273
274 order_suffix(exc_table[i].count, &count, &count_suffix);
275
276 uint64_t cycles;
277 char cycles_suffix;
278
279 order_suffix(exc_table[i].cycles, &cycles, &cycles_suffix);
280
281 const char *symbol =
282 symtab_fmt_name_lookup((sysarg_t) exc_table[i].handler);
283
284#ifdef __32_BITS__
285 printf("%-8u %-20s %9" PRIu64 "%c %9" PRIu64 "%c %10p %s\n",
286 i + IVT_FIRST, exc_table[i].name, count, count_suffix,
287 cycles, cycles_suffix, exc_table[i].handler, symbol);
288
289 PAGING(rows, 1, irq_spinlock_unlock(&exctbl_lock, true),
290 irq_spinlock_lock(&exctbl_lock, true));
291#endif
292
293#ifdef __64_BITS__
294 printf("%-8u %-20s %9" PRIu64 "%c %9" PRIu64 "%c %18p\n",
295 i + IVT_FIRST, exc_table[i].name, count, count_suffix,
296 cycles, cycles_suffix, exc_table[i].handler);
297 printf(" %s\n", symbol);
298
299 PAGING(rows, 2, irq_spinlock_unlock(&exctbl_lock, true),
300 irq_spinlock_lock(&exctbl_lock, true));
301#endif
302 }
303
304 irq_spinlock_unlock(&exctbl_lock, true);
305#else /* (IVT_ITEMS > 0) */
306
307 printf("No exception table%s.\n", excs_all ? " (showing all exceptions)" : "");
308
309#endif /* (IVT_ITEMS > 0) */
310
311 return 1;
312}
313
314static cmd_arg_t exc_argv = {
315 .type = ARG_TYPE_STRING_OPTIONAL,
316 .buffer = flag_buf,
317 .len = sizeof(flag_buf)
318};
319
320static cmd_info_t exc_info = {
321 .name = "exc",
322 .description = "Print exception table (use -a for all exceptions).",
323 .func = cmd_exc_print,
324 .help = NULL,
325 .argc = 1,
326 .argv = &exc_argv
327};
328
329#endif /* CONFIG_KCONSOLE */
330
331/** Initialize generic exception handling support
332 *
333 */
334void exc_init(void)
335{
336 (void) exc_undef;
337
338#if (IVT_ITEMS > 0)
339 unsigned int i;
340
341 for (i = 0; i < IVT_ITEMS; i++)
342 exc_register(i, "undef", false, (iroutine_t) exc_undef);
343#endif
344
345#ifdef CONFIG_KCONSOLE
346 cmd_initialize(&exc_info);
347 if (!cmd_register(&exc_info))
348 printf("Cannot register command %s\n", exc_info.name);
349#endif
350}
351
352/** @}
353 */
Note: See TracBrowser for help on using the repository browser.