Fixes TLS for statically linked apps in _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
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 extern void** vcore_thread_control_blocks;
19
20 static void free_transition_tls(int id)
21 {
22         extern void _dl_deallocate_tls (void *tcb, bool dealloc_tcb) internal_function;
23         if(vcore_thread_control_blocks[id])
24         {
25                 _dl_deallocate_tls(vcore_thread_control_blocks[id],true);
26                 vcore_thread_control_blocks[id] = NULL;
27         }
28 }
29
30 static int allocate_transition_tls(int id)
31 {
32         extern void *_dl_allocate_tls(void *mem) internal_function;
33         /* We want to free and then reallocate the tls rather than simply 
34          * reinitializing it because its size may have changed */
35         free_transition_tls(id);
36
37         void *tcb = _dl_allocate_tls(NULL);
38
39         if ((vcore_thread_control_blocks[id] = tcb) == NULL) {
40                 errno = ENOMEM;
41                 return -1;
42         }
43         /* Make sure the TLS is set up properly - its tcb pointer points to itself.
44          * Keep this in sync with sysdeps/ros/XXX/tls.h.  For whatever reason,
45          * dynamically linked programs do not need this to be redone, but statics
46          * do. */
47         tcbhead_t *head = (tcbhead_t*)tcb;
48         head->tcb = tcb;
49         head->self = tcb;
50         return 0;
51 }
52
53 static void free_transition_stack(int id)
54 {
55         // don't actually free stacks
56 }
57
58 static int allocate_transition_stack(int id)
59 {
60         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[id];
61         if (vcpd->transition_stack)
62                 return 0; // reuse old stack
63
64         void* stackbot = mmap(0, TRANSITION_STACK_SIZE,
65                               PROT_READ|PROT_WRITE|PROT_EXEC,
66                               MAP_POPULATE|MAP_ANONYMOUS, -1, 0);
67
68         if(stackbot == MAP_FAILED)
69                 return -1; // errno set by mmap
70
71         vcpd->transition_stack = (uintptr_t)stackbot + TRANSITION_STACK_SIZE;
72
73         return 0;
74 }
75
76 int vcore_init()
77 {
78         static int initialized = 0;
79         if(initialized)
80                 return 0;
81
82         vcore_thread_control_blocks = (void**)calloc(max_vcores(),sizeof(void*));
83
84         if(!vcore_thread_control_blocks)
85                 goto vcore_init_fail;
86
87         /* Need to alloc vcore0's transition stuff here (technically, just the TLS)
88          * so that schedulers can use vcore0's transition TLS before it comes up in
89          * vcore_entry() */
90         if(allocate_transition_stack(0) || allocate_transition_tls(0))
91                 goto vcore_init_tls_fail;
92
93         initialized = 1;
94         return 0;
95
96 vcore_init_tls_fail:
97         free(vcore_thread_control_blocks);
98 vcore_init_fail:
99         errno = ENOMEM;
100         return -1;
101 }
102
103 int vcore_request(size_t k)
104 {
105         int ret = -1;
106         size_t i,j;
107
108         if(vcore_init() < 0)
109                 return -1;
110
111         // TODO: could do this function without a lock once we 
112         // have atomic fetch and add in user space
113         mcs_lock_lock(&_vcore_lock);
114
115         size_t vcores_wanted = num_vcores() + k;
116         if(k < 0 || vcores_wanted > max_vcores())
117         {
118                 errno = EAGAIN;
119                 goto fail;
120         }
121
122         for(i = _max_vcores_ever_wanted; i < vcores_wanted; i++)
123         {
124                 if(allocate_transition_stack(i) || allocate_transition_tls(i))
125                         goto fail; // errno set by the call that failed
126                 _max_vcores_ever_wanted++;
127         }
128         ret = sys_resource_req(RES_CORES, vcores_wanted, 1, 0);
129
130 fail:
131         mcs_lock_unlock(&_vcore_lock);
132         return ret;
133 }
134
135 void vcore_yield()
136 {
137         sys_yield(0);
138 }
139
140 size_t max_vcores()
141 {
142         return MIN(__procinfo.max_vcores, MAX_VCORES);
143 }
144
145 size_t num_vcores()
146 {
147         return __procinfo.num_vcores;
148 }
149
150 int vcore_id()
151 {
152         return __vcoreid;
153 }
154