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