b4f95e77e2f02aef570efd6e7edf8b2b862b4d9b
[akaros.git] / kern / src / smp.c
1 /*
2  * Copyright (c) 2009 The Regents of the University of California
3  * Barret Rhoden <brho@cs.berkeley.edu>
4  * See LICENSE for details.
5  */
6
7 #ifdef __SHARC__
8 #pragma nosharc
9 #endif
10
11 #include <arch/arch.h>
12 #include <atomic.h>
13 #include <smp.h>
14 #include <error.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <assert.h>
18 #include <pmap.h>
19 #include <process.h>
20 #include <manager.h>
21 #include <trap.h>
22
23 struct per_cpu_info per_cpu_info[MAX_NUM_CPUS];
24
25 // tracks number of global waits on smp_calls, must be <= NUM_HANDLER_WRAPPERS
26 atomic_t outstanding_calls = 0;
27
28 /* Helper for running a proc (if we should).  Lots of repetition with
29  * proc_restartcore */
30 static void try_run_proc(void)
31 {
32         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
33
34         disable_irq();
35         /* There was a process running here, and we should return to it */
36         if (pcpui->cur_tf) {                    /* aka, current_tf */
37                 assert(pcpui->cur_proc);        /* aka, current */
38                 proc_restartcore();
39                 assert(0);
40         } else {
41                 assert(!pcpui->cur_proc);
42         }
43 }
44
45 /* All cores end up calling this whenever there is nothing left to do.  Non-zero
46  * cores call it when they are done booting.  Other cases include after getting
47  * a DEATH IPI.
48  * - Management cores (core 0 for now) call manager, which should never return.
49  * - Worker cores halt and wake up when interrupted, do any work on their work
50  *   queue, then halt again.
51  * TODO: think about unifying the manager into a workqueue function, so we don't
52  * need to check mgmt_core in here.  it gets a little ugly, since there are
53  * other places where we check for mgmt and might not smp_idle / call manager.
54  */
55 static void __smp_idle(void)
56 {
57         int8_t state = 0;
58
59         /* TODO: idle, abandon_core(), and proc_restartcore() need cleaned up */
60         enable_irq();   /* get any IRQs before we halt later */
61         try_run_proc();
62         /* if we made it here, we truly want to idle */
63         /* in the future, we may need to proactively leave process context here.
64          * for now, it is possible to have a current loaded, even if we are idle
65          * (and presumably about to execute a kmsg or fire up a vcore). */
66         if (!management_core()) {
67                 while (1) {
68                         disable_irq();
69                         process_routine_kmsg(0);
70                         try_run_proc();
71                         /* cpu_halt() atomically turns on interrupts and halts the core.
72                          * Important to do this, since we could have a RKM come in via an
73                          * interrupt right while PRKM is returning, and we wouldn't catch
74                          * it. */
75                         cpu_halt();
76                         /* interrupts are back on now (given our current semantics) */
77                 }
78         } else {
79                 enable_irqsave(&state);
80                 /* this makes us wait to enter the manager til any IO is done (totally
81                  * arbitrary 10ms), so we can handle the routine message that we
82                  * currently use to do the completion.  Note this also causes us to wait
83                  * 10ms regardless of how long the IO takes.  This all needs work. */
84                 //udelay(10000); /* done in the manager for now */
85                 process_routine_kmsg(0);
86                 try_run_proc();
87                 disable_irqsave(&state);
88                 manager();
89         }
90         assert(0);
91 }
92
93 void smp_idle(void)
94 {
95         #ifdef __CONFIG_RESET_STACKS__
96         set_stack_pointer(get_stack_top());
97         #endif /* __CONFIG_RESET_STACKS__ */
98         __smp_idle();
99         assert(0);
100 }
101
102 /* Arch-independent per-cpu initialization.  This will call the arch dependent
103  * init first. */
104 void smp_percpu_init(void)
105 {
106         uint32_t coreid = core_id();
107         /* Do this first */
108         __arch_pcpu_init(coreid);
109         per_cpu_info[coreid].spare = 0;
110         /* Init relevant lists */
111         spinlock_init(&per_cpu_info[coreid].immed_amsg_lock);
112         STAILQ_INIT(&per_cpu_info[coreid].immed_amsgs);
113         spinlock_init(&per_cpu_info[coreid].routine_amsg_lock);
114         STAILQ_INIT(&per_cpu_info[coreid].routine_amsgs);
115         /* Initialize the per-core timer chain */
116         init_timer_chain(&per_cpu_info[coreid].tchain, set_pcpu_alarm_interrupt);
117
118 #ifdef __CONFIG_KTHREAD_POISON__
119 /* TODO: KTHR-STACK */
120 uintptr_t *poison = (uintptr_t*)ROUNDDOWN(get_stack_top() - 1, PGSIZE);
121 *poison = 0xdeadbeef;
122 #endif /* __CONFIG_KTHREAD_POISON__ */
123
124 }