Boot framework rework (#45)
[lunaix-os.git] / lunaix-os / kernel / process / thread.c
1 #include <lunaix/process.h>
2 #include <lunaix/sched.h>
3 #include <lunaix/syscall.h>
4 #include <lunaix/syscall_utils.h>
5 #include <lunaix/mm/mmap.h>
6 #include <lunaix/mm/page.h>
7 #include <lunaix/syslog.h>
8 #include <lunaix/kpreempt.h>
9
10 #include <usr/lunaix/threads.h>
11
12 #include <sys/abi.h>
13 #include <sys/mm/mm_defs.h>
14
15 LOG_MODULE("THREAD")
16
17 static ptr_t
18 __alloc_user_thread_stack(struct proc_info* proc, 
19                           struct mm_region** stack_region, ptr_t vm_mnt)
20 {
21     ptr_t th_stack_top = (proc->thread_count + 1) * USR_STACK_SIZE_THREAD;
22     th_stack_top = ROUNDUP(USR_STACK_END - th_stack_top, PAGE_SIZE);
23
24     struct mm_region* vmr;
25     struct proc_mm* mm = vmspace(proc);
26     struct mmap_param param = { .vms_mnt = vm_mnt,
27                                 .pvms = mm,
28                                 .mlen = USR_STACK_SIZE_THREAD,
29                                 .proct = PROT_READ | PROT_WRITE,
30                                 .flags = MAP_ANON | MAP_PRIVATE,
31                                 .type = REGION_TYPE_STACK };
32
33     int errno;
34     
35     errno = mmap_user((void**)&th_stack_top, &vmr, th_stack_top, NULL, &param);
36     if (errno) {
37         WARN("failed to create user thread stack: %d", errno);
38         return 0;
39     }
40
41     pte_t* guardp = mkptep_va(vm_mnt, vmr->start);
42     set_pte(guardp, guard_pte);
43
44     *stack_region = vmr;
45
46     ptr_t stack_top = align_stack(th_stack_top + USR_STACK_SIZE_THREAD - 1);
47     return stack_top;
48 }
49
50 static ptr_t
51 __alloc_kernel_thread_stack(struct proc_info* proc, ptr_t vm_mnt)
52 {
53     pfn_t kstack_top = pfn(KSTACK_AREA_END);
54     pfn_t kstack_end = pfn(KSTACK_AREA);
55     pte_t* ptep      = mkptep_pn(vm_mnt, kstack_top);
56     while (ptep_pfn(ptep) > kstack_end) {
57         ptep -= KSTACK_PAGES;
58
59         pte_t pte = pte_at(ptep);
60         if (pte_isnull(pte)) {
61             goto found;
62         }
63
64         ptep--;
65     }
66
67     WARN("failed to create kernel stack: max stack num reach\n");
68     return 0;
69
70 found:;
71     unsigned int po = count_order(KSTACK_PAGES);
72     struct leaflet* leaflet = alloc_leaflet(po);
73
74     if (!leaflet) {
75         WARN("failed to create kernel stack: nomem\n");
76         return 0;
77     }
78
79     set_pte(ptep++, guard_pte);
80     ptep_map_leaflet(ptep, mkpte_prot(KERNEL_DATA), leaflet);
81
82     ptep += KSTACK_PAGES;
83     return align_stack(ptep_va(ptep, LFT_SIZE) - 1);
84 }
85
86 void
87 thread_release_mem(struct thread* thread)
88 {
89     struct leaflet* leaflet;
90     struct proc_mm* mm = vmspace(thread->process);
91     ptr_t vm_mnt = mm->vm_mnt;
92
93     // Ensure we have mounted
94     assert(vm_mnt);
95
96     pte_t* ptep = mkptep_va(vm_mnt, thread->kstack);
97     leaflet = pte_leaflet(*ptep);
98     
99     ptep -= KSTACK_PAGES;
100     set_pte(ptep, null_pte);
101     ptep_unmap_leaflet(ptep + 1, leaflet);
102
103     leaflet_return(leaflet);
104     
105     if (thread->ustack) {
106         if ((thread->ustack->start & 0xfff)) {
107             fail("invalid ustack struct");
108         }
109         mem_unmap_region(vm_mnt, thread->ustack);
110     }
111 }
112
113 struct thread*
114 create_thread(struct proc_info* proc, bool with_ustack)
115 {
116     struct proc_mm* mm = vmspace(proc);
117     assert(mm->vm_mnt);
118
119     ptr_t vm_mnt = mm->vm_mnt;
120     struct mm_region* ustack_region = NULL;
121     if (with_ustack && 
122         !(__alloc_user_thread_stack(proc, &ustack_region, vm_mnt))) 
123     {
124         return NULL;
125     }
126
127     ptr_t kstack = __alloc_kernel_thread_stack(proc, vm_mnt);
128     if (!kstack) {
129         mem_unmap_region(vm_mnt, ustack_region);
130         return NULL;
131     }
132
133     struct thread* th = alloc_thread(proc);
134     if (!th) {
135         return NULL;
136     }
137     
138     th->kstack = kstack;
139     th->ustack = ustack_region;
140     
141     if (ustack_region) {
142         th->ustack_top = align_stack(ustack_region->end - 1);
143     }
144
145     return th;
146 }
147
148 void
149 start_thread(struct thread* th, ptr_t entry)
150 {
151     assert(th && entry);
152     struct proc_mm* mm = vmspace(th->process);
153
154     assert(mm->vm_mnt);
155     
156     struct hart_transition transition;
157     if (!kernel_addr(entry)) {
158         assert(th->ustack);
159
160         hart_user_transfer(&transition, th->kstack, th->ustack_top, entry);
161     } 
162     else {
163         hart_kernel_transfer(&transition, th->kstack, entry);
164     }
165
166     install_hart_transition(mm->vm_mnt, &transition);
167     th->hstate = (struct hart_state*)transition.inject;
168
169     commit_thread(th);
170 }
171
172 void 
173 exit_thread(void* val) {
174     terminate_current_thread((ptr_t)val);
175     schedule();
176 }
177
178 struct thread*
179 thread_find(struct proc_info* proc, tid_t tid)
180 {
181     struct thread *pos, *n;
182     llist_for_each(pos, n, &proc->threads, proc_sibs) {
183         if (pos->tid == tid) {
184             return pos;
185         }
186     }
187
188     return NULL;
189 }
190
191 void
192 thread_stats_update(bool inbound, bool voluntary)
193 {
194     struct thread_stats* stats;
195     time_t now;
196
197     now   = clock_systime();
198     stats = &current_thread->stats;
199
200     stats->at_user = !kernel_context(current_thread->hstate);
201
202     if (!inbound) {
203         if (kernel_process(current_thread->process) || 
204             stats->at_user)
205         {
206             // exiting to user or kernel (kernel thread only), how graceful
207             stats->last_leave = now;
208         }
209         else {
210             // exiting to kernel, effectively reentry
211             stats->last_reentry = now;
212         }
213
214         stats->last_resume = now;
215         return;
216     }
217
218     stats->last_reentry = now;
219
220     if (!stats->at_user)
221     {
222         // entering from kernel, it is a kernel preempt
223         thread_stats_update_kpreempt();
224         return;
225     }
226
227     // entering from user space, a clean entrance.
228
229     if (!voluntary) {
230         stats->entry_count_invol++;
231     }
232     else {
233         stats->entry_count_vol++;
234     }
235
236     thread_stats_reset_kpreempt();
237     stats->last_entry = now;
238 }
239
240 __DEFINE_LXSYSCALL3(int, th_create, tid_t*, tid, 
241                         struct uthread_param*, thparam, void*, entry)
242 {
243     no_preemption();
244
245     struct thread* th = create_thread(__current, true);
246     if (!th) {
247         return EAGAIN;
248     }
249
250     ptr_t ustack_top;
251
252     ustack_top = th->ustack_top;
253     ustack_top = align_stack(ustack_top - sizeof(*thparam));
254
255     memcpy((void*)ustack_top, thparam, sizeof(*thparam));
256
257     th->ustack_top = ustack_top;
258     start_thread(th, (ptr_t)entry);
259     
260     if (tid) {
261         *tid = th->tid;
262     }
263
264     return 0;
265 }
266
267 __DEFINE_LXSYSCALL(tid_t, th_self)
268 {
269     return current_thread->tid;
270 }
271
272 __DEFINE_LXSYSCALL1(void, th_exit, void*, val)
273 {
274     exit_thread(val);
275 }
276
277 __DEFINE_LXSYSCALL2(int, th_join, tid_t, tid, void**, val_ptr)
278 {
279     struct thread* th = thread_find(__current, tid);
280     if (!th) {
281         return EINVAL;
282     }
283
284     if (th == current_thread) {
285         return EDEADLK;
286     }
287
288     while (!proc_terminated(th)) {
289         yield_current();
290     }
291
292     if (val_ptr) {
293         *val_ptr = (void*)th->exit_val;
294     }
295
296     no_preemption();
297     destory_thread(th);
298
299     return 0;
300 }
301
302 __DEFINE_LXSYSCALL1(int, th_detach, tid_t, tid)
303 {
304     // can not detach the only thread
305     if (__current->thread_count == 1) {
306         return EINVAL;
307     }
308
309     struct thread* th = thread_find(__current, tid);
310     if (!th) {
311         return EINVAL;
312     }
313
314     detach_thread(th);
315     return 0;
316 }
317
318 __DEFINE_LXSYSCALL2(int, th_kill, tid_t, tid, int, signum)
319 {
320     struct thread* target = thread_find(__current, tid);
321     if (!target) {
322         return EINVAL;
323     }
324
325     if (signum > _SIG_NUM) {
326         return EINVAL;
327     }
328
329     if (signum) {
330         thread_setsignal(target, signum);
331     }
332     
333     return 0;
334 }