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