Cleans up vcore/uth/pth init code
[akaros.git] / user / c3po / threads / vcore.c
1 #include <vcore.h>
2 #include <mcs.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <assert.h>
6 #include <stdio.h>
7 #include <errno.h>
8 #include <parlib.h>
9 #include <ros/event.h>
10 #include <arch/atomic.h>
11 #include <arch/arch.h>
12 #include <sys/queue.h>
13 #include <sys/mman.h>
14 #include <event.h>
15 #include <threadlib_internal.h>
16 #include <threadlib.h>
17
18 // Comment out, to enable debugging in this file
19 #ifndef DEBUG_threadlib_c
20 #undef debug
21 #define debug(...)
22 #undef tdebug
23 #define tdebug(...)
24 #endif
25
26 // Thread local pointer to the user thread currently running on a given core. 
27 // This variable is only meaningful to the context running
28 // the scheduler code (i.e. vcore context in ROS, user thread or scheduler
29 // context on linux depending on whether we use a scheduler thread or not)
30 __thread thread_t* current_thread=NULL;
31
32 /**
33  * Bootup constructors.  Make sure things are initialized in the proper order.
34  * Ideally we would be able to use the contsructor priorities instead of having
35  * to call them all back to back like this, but for some reason I couldn't get
36  * them to work properly. I.e. the priorities weren't being honored.
37  **/
38 extern void read_config();
39 extern void main_thread_init();
40 extern void vcore_startup();
41 void __attribute__ ((constructor)) ctors()
42 {
43         
44 //      init_cycle_clock();
45         read_config();
46         main_thread_init();
47         vcore_startup();
48 }
49
50         
51         //uint32_t vc = vcore_id();
52         //uint32_t kvc = ros_syscall(SYS_getvcoreid, 0, 0, 0, 0, 0, 0);
53         //set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
54         ///* Verify that the currently running thread is the one that the vcore thought
55         // * was running */
56         //if(current_thread != t)
57         //      printf("variable:\tthread:vcore\n"
58         //             "current_thread:\t%p:%p\n"
59         //             "vcore_id:\t%p:%p\n"
60         //             "SYS_getvcoreid:\t%p:%p\n",
61         //             t, current_thread, vc, vcore_id(), kvc, ros_syscall(SYS_getvcoreid, 0, 0, 0, 0, 0, 0)
62         //            );
63         //assert(current_thread == t);
64
65 /**
66  * Initialize the vcores, including jumping into muticore mode.
67  **/
68 void vcore_startup()
69 {
70         /* Initilize the bootstrap code for using the vcores */
71         vcore_init();
72         assert(vcore_id() == 0);
73
74         /* Tell the kernel where and how we want to receive events.  This is just an
75          * example of what to do to have a notification turned on.  We're turning on
76          * USER_IPIs, posting events to vcore 0's vcpd, and telling the kernel to
77          * send to vcore 0.  Note sys_self_notify will ignore the vcoreid and
78          * private preference.  Also note that enable_kevent() is just an example,
79          * and you probably want to use parts of event.c to do what you want. */
80         enable_kevent(EV_USER_IPI, 0, EVENT_IPI | EVENT_VCORE_PRIVATE);
81
82         /* Grab a reference to the main_thread on the current stack (i.e.
83          * current_thread, since we know this has been set up for us properly by
84          * the fact that the constructor calls main_thread_init() before this
85          * function.  We will need this reference below. */
86         thread_t *t = current_thread;
87
88     /* Change temporarily to vcore0s tls region so we can save the main_thread
89          * into its thread local current_thread variable.  One minor issue is that
90          * vcore0's transition-TLS isn't TLS_INITed yet.  Until it is (right before
91          * vcore_entry(), don't try and take the address of any of its TLS vars. */
92         extern void** vcore_thread_control_blocks;
93         set_tls_desc(vcore_thread_control_blocks[0], 0);
94         current_thread = t;
95         set_tls_desc(t->context->tls_desc, 0);
96
97         /* Jump into multi-core mode! */
98         /* The next line of code that will run is inside vcore_entry().  When this
99          * thread is resumed, it will continue directly after this call to
100          * vcore_change_to_m() */
101         vcore_change_to_m();
102 }
103
104 /**
105  * Switch into vcore mode to run the scheduler code. 
106  **/
107 void switch_to_vcore() {
108
109         uint32_t vcoreid = vcore_id();
110
111         /* Disable notifications.  Once we do this, we might miss a notif_pending,
112          * so we need to enter vcore entry later.  Need to disable notifs so we
113          * don't get in weird loops */
114         struct preempt_data *vcpd = vcpd_of(vcoreid);
115         vcpd->notif_disabled = TRUE;
116
117         /* Grab a reference to the currently running thread on this vcore */
118         thread_t *t = current_thread; 
119
120         /* Switch to the vcore's tls region */
121         extern void** vcore_thread_control_blocks;
122         set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
123         
124         /* Verify that the thread the vcore thinks was running is the same as the thread
125          * that was actually running */
126         assert(current_thread == t);
127
128         /* Set the stack pointer to the stack of the vcore. 
129          * We know this function is always inlined because of the attribute we set
130          * on it, so there will be no stack unwinding when this function "returns".
131          * After this call, make sure you don't use local variables. */
132         set_stack_pointer((void*)vcpd->transition_stack);
133         assert(in_vcore_context());
134
135         /* Leave the current vcore completely */
136         current_thread = NULL; 
137         
138         /* Restart the vcore and run the scheduler code */
139         vcore_entry();
140         assert(0);
141 }
142
143 /**
144  * Entry point for the vcore.  Basic job is to either resume the thread that
145  * was interrupted in the case of a notification coming in, or to find a new
146  * thread from the user level threading library and launch it.
147  **/
148 void __attribute__((noreturn)) vcore_entry()
149 {
150         /* Grab references to the current vcoreid vcore preemption data, and the
151      * vcoremap */
152         assert(in_vcore_context());
153         uint32_t vcoreid = vcore_id();
154         struct preempt_data *vcpd = vcpd_of(vcoreid);
155         struct vcore *vc = &__procinfo.vcoremap[vcoreid];
156
157         tdebug("current=%s, vcore=%d\n",
158                 current_thread?current_thread->name : "NULL", vcoreid);
159
160         /* Assert that notifications are disabled. Should always have notifications
161          * disabled when coming in here. */
162         assert(vcpd->notif_disabled == TRUE);
163
164         /* Put this in the loop that deals with notifications.  It will return if
165          * there is no preempt pending. */ 
166         if (vc->preempt_pending)
167                 sys_yield(TRUE);
168
169         /* When running vcore_entry(), we are using the TLS of the vcore, not any
170          * particular thread.  If current_thread is set in the vcore's TLS, then 
171          * that means the thread did not yield voluntarily, and was, instead, 
172          * interrupted by a notification.  We therefore need to restore the thread
173          * context from the notification trapframe, not the one stored in the 
174          * thread struct itself. */
175     if (unlikely(current_thread)) {
176         vcpd->notif_pending = 0;
177         /* Do one last check for notifs after clearing pending */
178         // TODO: call the handle_notif() here (first)
179
180                 /* Copy the notification trapframe into the current 
181                  * threads trapframe */
182                 memcpy(&current_thread->context->utf, &vcpd->notif_tf, 
183                        sizeof(struct user_trapframe));
184
185         /* Restore the context from the current_thread's trapframe */
186         restore_context(current_thread->context);
187         assert(0);
188     }
189
190         /* Otherwise either a vcore is coming up for the first time, or a thread
191          * has just yielded and vcore_entry() was called directly. In this case we 
192          * need to figure out which thread to schedule next on the vcore */
193         run_next_thread();
194         assert(0);
195 }
196