dedicated kthread interface and enablement of lrud auto-recycler
[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 <asm/abi.h>
13 #include <asm/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 static int
87 __thread_putsleep(int seconds)
88 {
89     if (!seconds) {
90         return 0;
91     }
92
93     struct scheduler* sched;
94     time_t systime;
95     struct haybed* bed;
96
97     sched = scheduler();
98     systime = clock_systime() / 1000;
99     bed = &current_thread->sleep;
100
101     if (bed->wakeup_time) {
102         return (bed->wakeup_time - systime);
103     }
104
105     bed->wakeup_time = systime + seconds;
106
107     if (llist_empty(&bed->sleepers)) {
108         llist_append(&sched->sleepers, &bed->sleepers);
109     }
110
111     block_current_thread();
112     return seconds;
113 }
114
115 void
116 thread_release_mem(struct thread* thread)
117 {
118     struct leaflet* leaflet;
119     struct proc_mm* mm = vmspace(thread->process);
120     ptr_t vm_mnt = mm->vm_mnt;
121
122     // Ensure we have mounted
123     assert(vm_mnt);
124
125     pte_t* ptep = mkptep_va(vm_mnt, thread->kstack);
126     leaflet = pte_leaflet(*ptep);
127     
128     ptep -= KSTACK_PAGES;
129     set_pte(ptep, null_pte);
130     ptep_unmap_leaflet(ptep + 1, leaflet);
131
132     leaflet_return(leaflet);
133     
134     if (thread->ustack) {
135         if ((thread->ustack->start & 0xfff)) {
136             fail("invalid ustack struct");
137         }
138         mem_unmap_region(vm_mnt, thread->ustack);
139     }
140 }
141
142 struct thread*
143 create_thread(struct proc_info* proc, bool with_ustack)
144 {
145     struct proc_mm* mm = vmspace(proc);
146     assert(mm->vm_mnt);
147
148     ptr_t vm_mnt = mm->vm_mnt;
149     struct mm_region* ustack_region = NULL;
150     if (with_ustack && 
151         !(__alloc_user_thread_stack(proc, &ustack_region, vm_mnt))) 
152     {
153         return NULL;
154     }
155
156     ptr_t kstack = __alloc_kernel_thread_stack(proc, vm_mnt);
157     if (!kstack) {
158         mem_unmap_region(vm_mnt, ustack_region);
159         return NULL;
160     }
161
162     struct thread* th = alloc_thread(proc);
163     if (!th) {
164         return NULL;
165     }
166     
167     th->kstack = kstack;
168     th->ustack = ustack_region;
169     
170     if (ustack_region) {
171         th->ustack_top = align_stack(ustack_region->end - 1);
172     }
173
174     return th;
175 }
176
177 void
178 start_thread(struct thread* th, ptr_t entry)
179 {
180     assert(th && entry);
181     struct proc_mm* mm = vmspace(th->process);
182
183     assert(mm->vm_mnt);
184     
185     struct hart_transition transition;
186     if (!kernel_addr(entry)) {
187         assert(th->ustack);
188
189         hart_user_transfer(&transition, th->kstack, th->ustack_top, entry);
190     } 
191     else {
192         hart_kernel_transfer(&transition, th->kstack, entry);
193     }
194
195     install_hart_transition(mm->vm_mnt, &transition);
196     th->hstate = (struct hart_state*)transition.inject;
197
198     commit_thread(th);
199 }
200
201 void 
202 exit_thread(void* val) {
203     terminate_current_thread((ptr_t)val);
204     schedule();
205 }
206
207 struct thread*
208 thread_find(struct proc_info* proc, tid_t tid)
209 {
210     struct thread *pos, *n;
211     llist_for_each(pos, n, &proc->threads, proc_sibs) {
212         if (pos->tid == tid) {
213             return pos;
214         }
215     }
216
217     return NULL;
218 }
219
220 void
221 thread_stats_update(bool inbound, bool voluntary)
222 {
223     struct thread_stats* stats;
224     time_t now;
225
226     now   = clock_systime();
227     stats = &current_thread->stats;
228
229     stats->at_user = !kernel_context(current_thread->hstate);
230
231     if (!inbound) {
232         if (kernel_process(current_thread->process) || 
233             stats->at_user)
234         {
235             // exiting to user or kernel (kernel thread only), how graceful
236             stats->last_leave = now;
237         }
238         else {
239             // exiting to kernel, effectively reentry
240             stats->last_reentry = now;
241         }
242
243         stats->last_resume = now;
244         return;
245     }
246
247     stats->last_reentry = now;
248
249     if (!stats->at_user)
250     {
251         // entering from kernel, it is a kernel preempt
252         thread_stats_update_kpreempt();
253         return;
254     }
255
256     // entering from user space, a clean entrance.
257
258     if (!voluntary) {
259         stats->entry_count_invol++;
260     }
261     else {
262         stats->entry_count_vol++;
263     }
264
265     thread_stats_reset_kpreempt();
266     stats->last_entry = now;
267 }
268
269 void
270 kthread_spawn(ptr_t entry)
271 {
272     assert(kernel_process(__current));
273
274     struct thread* th = create_thread(__current, false);
275     
276     assert(th);
277     start_thread(th, entry);
278     detach_thread(th);
279 }
280
281 void
282 kthread_sleep(int seconds)
283 {
284     if (__thread_putsleep(seconds))
285         yield_current();
286 }
287
288 __DEFINE_LXSYSCALL3(int, th_create, tid_t*, tid, 
289                         struct uthread_param*, thparam, void*, entry)
290 {
291     no_preemption();
292
293     struct thread* th = create_thread(__current, true);
294     if (!th) {
295         return EAGAIN;
296     }
297
298     ptr_t ustack_top;
299
300     ustack_top = th->ustack_top;
301     ustack_top = align_stack(ustack_top - sizeof(*thparam));
302
303     memcpy((void*)ustack_top, thparam, sizeof(*thparam));
304
305     th->ustack_top = ustack_top;
306     start_thread(th, (ptr_t)entry);
307     
308     if (tid) {
309         *tid = th->tid;
310     }
311
312     return 0;
313 }
314
315 __DEFINE_LXSYSCALL(tid_t, th_self)
316 {
317     return current_thread->tid;
318 }
319
320 __DEFINE_LXSYSCALL1(void, th_exit, void*, val)
321 {
322     exit_thread(val);
323 }
324
325 __DEFINE_LXSYSCALL2(int, th_join, tid_t, tid, void**, val_ptr)
326 {
327     struct thread* th = thread_find(__current, tid);
328     if (!th) {
329         return EINVAL;
330     }
331
332     if (th == current_thread) {
333         return EDEADLK;
334     }
335
336     while (!proc_terminated(th)) {
337         yield_current();
338     }
339
340     if (val_ptr) {
341         *val_ptr = (void*)th->exit_val;
342     }
343
344     no_preemption();
345     destory_thread(th);
346
347     return 0;
348 }
349
350 __DEFINE_LXSYSCALL1(int, th_detach, tid_t, tid)
351 {
352     // can not detach the only thread
353     if (__current->thread_count == 1) {
354         return EINVAL;
355     }
356
357     struct thread* th = thread_find(__current, tid);
358     if (!th) {
359         return EINVAL;
360     }
361
362     detach_thread(th);
363     return 0;
364 }
365
366 __DEFINE_LXSYSCALL2(int, th_kill, tid_t, tid, int, signum)
367 {
368     struct thread* target = thread_find(__current, tid);
369     if (!target) {
370         return EINVAL;
371     }
372
373     if (signum > _SIG_NUM) {
374         return EINVAL;
375     }
376
377     if (signum) {
378         thread_setsignal(target, signum);
379     }
380     
381     return 0;
382 }
383
384 __DEFINE_LXSYSCALL1(unsigned int, sleep, unsigned int, seconds)
385 {
386     int sec;
387
388     sec = __thread_putsleep(seconds);
389     store_retval(seconds);
390     
391     if (sec)
392         schedule();
393
394     return 0;
395 }