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