User code can tell if it is in vcore context (XCC)
[akaros.git] / user / parlib / vcore.c
1 #include <arch/arch.h>
2 #include <stdbool.h>
3 #include <errno.h>
4 #include <vcore.h>
5 #include <mcs.h>
6 #include <sys/param.h>
7 #include <parlib.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <sys/mman.h>
11 #include <rstdio.h>
12 #include <glibc-tls.h>
13 #include <event.h>
14 #include <ros/arch/membar.h>
15
16 /* starting with 1 since we alloc vcore0's stacks and TLS in vcore_init(). */
17 static size_t _max_vcores_ever_wanted = 1;
18 static mcs_lock_t _vcore_lock = MCS_LOCK_INIT;
19
20 /* Which operations we'll call for the 2LS.  Will change a bit with Lithe.  For
21  * now, there are no defaults. */
22 struct schedule_ops default_2ls_ops = {0};
23 struct schedule_ops *sched_ops = &default_2ls_ops;
24
25 extern void** vcore_thread_control_blocks;
26
27 /* Get a TLS, returns 0 on failure.  Vcores have their own TLS, and any thread
28  * created by a user-level scheduler needs to create a TLS as well. */
29 void *allocate_tls(void)
30 {
31         extern void *_dl_allocate_tls(void *mem) internal_function;
32         void *tcb = _dl_allocate_tls(NULL);
33         if (!tcb)
34                 return 0;
35         /* Make sure the TLS is set up properly - its tcb pointer points to itself.
36          * Keep this in sync with sysdeps/ros/XXX/tls.h.  For whatever reason,
37          * dynamically linked programs do not need this to be redone, but statics
38          * do. */
39         tcbhead_t *head = (tcbhead_t*)tcb;
40         head->tcb = tcb;
41         head->self = tcb;
42         return tcb;
43 }
44
45 /* TODO: probably don't want to dealloc.  Considering caching */
46 static void free_transition_tls(int id)
47 {
48         extern void _dl_deallocate_tls (void *tcb, bool dealloc_tcb) internal_function;
49         if(vcore_thread_control_blocks[id])
50         {
51                 _dl_deallocate_tls(vcore_thread_control_blocks[id],true);
52                 vcore_thread_control_blocks[id] = NULL;
53         }
54 }
55
56 static int allocate_transition_tls(int id)
57 {
58         /* We want to free and then reallocate the tls rather than simply 
59          * reinitializing it because its size may have changed.  TODO: not sure if
60          * this is right.  0-ing is one thing, but freeing and reallocating can be
61          * expensive, esp if syscalls are involved.  Check out glibc's
62          * allocatestack.c for what might work. */
63         free_transition_tls(id);
64
65         void *tcb = allocate_tls();
66
67         if ((vcore_thread_control_blocks[id] = tcb) == NULL) {
68                 errno = ENOMEM;
69                 return -1;
70         }
71         return 0;
72 }
73
74 static void free_transition_stack(int id)
75 {
76         // don't actually free stacks
77 }
78
79 static int allocate_transition_stack(int id)
80 {
81         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[id];
82         if (vcpd->transition_stack)
83                 return 0; // reuse old stack
84
85         void* stackbot = mmap(0, TRANSITION_STACK_SIZE,
86                               PROT_READ|PROT_WRITE|PROT_EXEC,
87                               MAP_POPULATE|MAP_ANONYMOUS, -1, 0);
88
89         if(stackbot == MAP_FAILED)
90                 return -1; // errno set by mmap
91
92         vcpd->transition_stack = (uintptr_t)stackbot + TRANSITION_STACK_SIZE;
93
94         return 0;
95 }
96
97 int vcore_init()
98 {
99         static int initialized = 0;
100         if(initialized)
101                 return 0;
102
103         vcore_thread_control_blocks = (void**)calloc(max_vcores(),sizeof(void*));
104
105         if(!vcore_thread_control_blocks)
106                 goto vcore_init_fail;
107
108         /* Need to alloc vcore0's transition stuff here (technically, just the TLS)
109          * so that schedulers can use vcore0's transition TLS before it comes up in
110          * vcore_entry() */
111         if(allocate_transition_stack(0) || allocate_transition_tls(0))
112                 goto vcore_init_tls_fail;
113
114         initialized = 1;
115         return 0;
116
117 vcore_init_tls_fail:
118         free(vcore_thread_control_blocks);
119 vcore_init_fail:
120         errno = ENOMEM;
121         return -1;
122 }
123
124 int vcore_request(size_t k)
125 {
126         int ret = -1;
127         size_t i,j;
128
129         if(vcore_init() < 0)
130                 return -1;
131
132         // TODO: could do this function without a lock once we 
133         // have atomic fetch and add in user space
134         mcs_lock_lock(&_vcore_lock);
135
136         size_t vcores_wanted = num_vcores() + k;
137         if(k < 0 || vcores_wanted > max_vcores())
138         {
139                 errno = EAGAIN;
140                 goto fail;
141         }
142
143         for(i = _max_vcores_ever_wanted; i < vcores_wanted; i++)
144         {
145                 if(allocate_transition_stack(i) || allocate_transition_tls(i))
146                         goto fail; // errno set by the call that failed
147                 _max_vcores_ever_wanted++;
148         }
149         ret = sys_resource_req(RES_CORES, vcores_wanted, 1, 0);
150
151 fail:
152         mcs_lock_unlock(&_vcore_lock);
153         return ret;
154 }
155
156 void vcore_yield()
157 {
158         sys_yield(0);
159 }
160
161
162 /* Deals with a pending preemption (checks, responds).  If the 2LS registered a
163  * function, it will get run.  Returns true if you got preempted.  Called
164  * 'check' instead of 'handle', since this isn't an event handler.  It's the "Oh
165  * shit a preempt is on its way ASAP". */
166 bool check_preempt_pending(uint32_t vcoreid)
167 {
168         bool retval = FALSE;
169         if (__procinfo.vcoremap[vcoreid].preempt_pending) {
170                 retval = TRUE;
171                 if (sched_ops->preempt_pending)
172                         sched_ops->preempt_pending();
173                 /* this tries to yield, but will pop back up if this was a spurious
174                  * preempt_pending. */
175                 sys_yield(TRUE);
176         }
177         return retval;
178 }
179
180 /* Clear pending, and try to handle events that came in between a previous call
181  * to handle_events() and the clearing of pending.  While it's not a big deal,
182  * we'll loop in case we catch any.  Will break out of this once there are no
183  * events, and we will have send pending to 0. 
184  *
185  * Note that this won't catch every race/case of an incoming event.  Future
186  * events will get caught in pop_ros_tf() */
187 void clear_notif_pending(uint32_t vcoreid)
188 {
189         do {
190                 cmb();
191                 __procdata.vcore_preempt_data[vcoreid].notif_pending = 0;
192         } while (handle_events(vcoreid));
193 }