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