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