Pthread code makes sure it goes into _M mode
[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 /* Returns -1 with errno set on error, or 0 on success.  This does not return
125  * the number of cores actually granted (though some parts of the kernel do
126  * internally).
127  *
128  * Note the doesn't block or anything (despite the min number requested is
129  * 1), since the kernel won't block the call. */
130 int vcore_request(size_t k)
131 {
132         int ret = -1;
133         size_t i,j;
134
135         if(vcore_init() < 0)
136                 return -1;
137
138         // TODO: could do this function without a lock once we 
139         // have atomic fetch and add in user space
140         mcs_lock_lock(&_vcore_lock);
141
142         size_t vcores_wanted = num_vcores() + k;
143         if(k < 0 || vcores_wanted > max_vcores())
144         {
145                 errno = EAGAIN;
146                 goto fail;
147         }
148
149         for(i = _max_vcores_ever_wanted; i < vcores_wanted; i++)
150         {
151                 if(allocate_transition_stack(i) || allocate_transition_tls(i))
152                         goto fail; // errno set by the call that failed
153                 _max_vcores_ever_wanted++;
154         }
155         ret = sys_resource_req(RES_CORES, vcores_wanted, 1, 0);
156
157 fail:
158         mcs_lock_unlock(&_vcore_lock);
159         return ret;
160 }
161
162 void vcore_yield()
163 {
164         sys_yield(0);
165 }
166
167
168 /* Deals with a pending preemption (checks, responds).  If the 2LS registered a
169  * function, it will get run.  Returns true if you got preempted.  Called
170  * 'check' instead of 'handle', since this isn't an event handler.  It's the "Oh
171  * shit a preempt is on its way ASAP". */
172 bool check_preempt_pending(uint32_t vcoreid)
173 {
174         bool retval = FALSE;
175         if (__procinfo.vcoremap[vcoreid].preempt_pending) {
176                 retval = TRUE;
177                 if (sched_ops->preempt_pending)
178                         sched_ops->preempt_pending();
179                 /* this tries to yield, but will pop back up if this was a spurious
180                  * preempt_pending. */
181                 sys_yield(TRUE);
182         }
183         return retval;
184 }
185
186 /* Clear pending, and try to handle events that came in between a previous call
187  * to handle_events() and the clearing of pending.  While it's not a big deal,
188  * we'll loop in case we catch any.  Will break out of this once there are no
189  * events, and we will have send pending to 0. 
190  *
191  * Note that this won't catch every race/case of an incoming event.  Future
192  * events will get caught in pop_ros_tf() */
193 void clear_notif_pending(uint32_t vcoreid)
194 {
195         do {
196                 cmb();
197                 __procdata.vcore_preempt_data[vcoreid].notif_pending = 0;
198         } while (handle_events(vcoreid));
199 }