48524f5067416a6ebb6c111a5dd420fe9ad6ec3b
[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         if (vcore_init())
72                 printf("vcore_init() failed, we're fucked!\n");
73         assert(vcore_id() == 0);
74
75         /* Tell the kernel where and how we want to receive events.  This is just an
76          * example of what to do to have a notification turned on.  We're turning on
77          * USER_IPIs, posting events to vcore 0's vcpd, and telling the kernel to
78          * send to vcore 0.  Note sys_self_notify will ignore the vcoreid and
79          * private preference.  Also note that enable_kevent() is just an example,
80          * and you probably want to use parts of event.c to do what you want. */
81         enable_kevent(EV_USER_IPI, 0, EVENT_IPI | EVENT_VCORE_PRIVATE);
82
83         /* Grab a reference to the main_thread on the current stack (i.e.
84          * current_thread, since we know this has been set up for us properly by
85          * the fact that the constructor calls main_thread_init() before this
86          * function.  We will need this reference below. */
87         thread_t *t = current_thread;
88
89     /* Change temporarily to vcore0s tls region so we can save the main_thread
90          * into its thread local current_thread variable.  One minor issue is that
91          * vcore0's transition-TLS isn't TLS_INITed yet.  Until it is (right before
92          * vcore_entry(), don't try and take the address of any of its TLS vars. */
93         extern void** vcore_thread_control_blocks;
94         set_tls_desc(vcore_thread_control_blocks[0], 0);
95         current_thread = t;
96         set_tls_desc(t->context->tls_desc, 0);
97
98         /* Jump into multi-core mode! */
99         /* The next line of code that will run is inside vcore_entry().  When this
100          * thread is resumed, it will continue directly after this call to
101          * vcore_change_to_m() */
102         vcore_change_to_m();
103 }
104
105 /**
106  * Switch into vcore mode to run the scheduler code. 
107  **/
108 void switch_to_vcore() {
109
110         uint32_t vcoreid = vcore_id();
111
112         /* Disable notifications.  Once we do this, we might miss a notif_pending,
113          * so we need to enter vcore entry later.  Need to disable notifs so we
114          * don't get in weird loops */
115         struct preempt_data *vcpd = vcpd_of(vcoreid);
116         vcpd->notif_disabled = TRUE;
117
118         /* Grab a reference to the currently running thread on this vcore */
119         thread_t *t = current_thread; 
120
121         /* Switch to the vcore's tls region */
122         extern void** vcore_thread_control_blocks;
123         set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
124         
125         /* Verify that the thread the vcore thinks was running is the same as the thread
126          * that was actually running */
127         assert(current_thread == t);
128
129         /* Set the stack pointer to the stack of the vcore. 
130          * We know this function is always inlined because of the attribute we set
131          * on it, so there will be no stack unwinding when this function "returns".
132          * After this call, make sure you don't use local variables. */
133         set_stack_pointer((void*)vcpd->transition_stack);
134         assert(in_vcore_context());
135
136         /* Leave the current vcore completely */
137         current_thread = NULL; 
138         
139         /* Restart the vcore and run the scheduler code */
140         vcore_entry();
141         assert(0);
142 }
143
144 /**
145  * Entry point for the vcore.  Basic job is to either resume the thread that
146  * was interrupted in the case of a notification coming in, or to find a new
147  * thread from the user level threading library and launch it.
148  **/
149 void __attribute__((noreturn)) vcore_entry()
150 {
151         /* Grab references to the current vcoreid vcore preemption data, and the
152      * vcoremap */
153         assert(in_vcore_context());
154         uint32_t vcoreid = vcore_id();
155         struct preempt_data *vcpd = vcpd_of(vcoreid);
156         struct vcore *vc = &__procinfo.vcoremap[vcoreid];
157
158         tdebug("current=%s, vcore=%d\n",
159                 current_thread?current_thread->name : "NULL", vcoreid);
160
161         /* Assert that notifications are disabled. Should always have notifications
162          * disabled when coming in here. */
163         assert(vcpd->notif_disabled == TRUE);
164
165         /* Put this in the loop that deals with notifications.  It will return if
166          * there is no preempt pending. */ 
167         if (vc->preempt_pending)
168                 sys_yield(TRUE);
169
170         /* When running vcore_entry(), we are using the TLS of the vcore, not any
171          * particular thread.  If current_thread is set in the vcore's TLS, then 
172          * that means the thread did not yield voluntarily, and was, instead, 
173          * interrupted by a notification.  We therefore need to restore the thread
174          * context from the notification trapframe, not the one stored in the 
175          * thread struct itself. */
176     if (unlikely(current_thread)) {
177         vcpd->notif_pending = 0;
178         /* Do one last check for notifs after clearing pending */
179         // TODO: call the handle_notif() here (first)
180
181                 /* Copy the notification trapframe into the current 
182                  * threads trapframe */
183                 memcpy(&current_thread->context->utf, &vcpd->notif_tf, 
184                        sizeof(struct user_trapframe));
185
186         /* Restore the context from the current_thread's trapframe */
187         restore_context(current_thread->context);
188         assert(0);
189     }
190
191         /* Otherwise either a vcore is coming up for the first time, or a thread
192          * has just yielded and vcore_entry() was called directly. In this case we 
193          * need to figure out which thread to schedule next on the vcore */
194         run_next_thread();
195         assert(0);
196 }
197