Introducing LunaBuild to the build flow (#36)
[lunaix-os.git] / lunaix-os / kernel / process / sched.c
1 #include <sys/abi.h>
2 #include <sys/mm/mempart.h>
3
4 #include <sys/cpu.h>
5
6 #include <lunaix/fs/taskfs.h>
7 #include <lunaix/mm/cake.h>
8 #include <lunaix/mm/mmap.h>
9 #include <lunaix/mm/pmm.h>
10 #include <lunaix/mm/valloc.h>
11 #include <lunaix/mm/vmm.h>
12 #include <lunaix/mm/procvm.h>
13 #include <lunaix/process.h>
14 #include <lunaix/sched.h>
15 #include <lunaix/signal.h>
16 #include <lunaix/spike.h>
17 #include <lunaix/status.h>
18 #include <lunaix/syscall.h>
19 #include <lunaix/syslog.h>
20 #include <lunaix/hart_state.h>
21 #include <lunaix/kpreempt.h>
22
23 #include <lunaix/generic/isrm.h>
24
25 #include <klibc/string.h>
26
27 struct thread empty_thread_obj;
28
29 volatile struct proc_info* __current;
30 volatile struct thread* current_thread = &empty_thread_obj;
31
32 struct scheduler sched_ctx;
33
34 struct cake_pile *proc_pile ,*thread_pile;
35
36 LOG_MODULE("SCHED")
37
38 void
39 sched_init()
40 {
41     proc_pile = cake_new_pile("proc", sizeof(struct proc_info), 1, 0);
42     thread_pile = cake_new_pile("thread", sizeof(struct thread), 1, 0);
43     cake_set_constructor(proc_pile, cake_ctor_zeroing);
44     cake_set_constructor(thread_pile, cake_ctor_zeroing);
45
46     sched_ctx = (struct scheduler){
47         .procs = vzalloc(PROC_TABLE_SIZE), .ptable_len = 0, .procs_index = 0};
48     
49     llist_init_head(&sched_ctx.sleepers);
50 }
51
52 void
53 run(struct thread* thread)
54 {
55     thread->state = PS_RUNNING;
56     thread->process->state = PS_RUNNING;
57     thread->process->th_active = thread;
58
59     procvm_mount_self(vmspace(thread->process));
60     set_current_executing(thread);
61
62     switch_context();
63     fail("unexpected return from switching");
64 }
65
66 /*
67     Currently, we do not allow self-destorying thread, doing
68     so will eliminate current kernel stack which is disaster.
69     A compromise solution is to perform a regular scan and 
70     clean-up on these thread, in the preemptible kernel thread.
71 */
72
73 void _preemptible
74 cleanup_detached_threads() {
75     ensure_preempt_caller();
76
77     // XXX may be a lock on sched_context will ben the most appropriate?
78     cpu_disable_interrupt();
79
80     int i = 0;
81     struct thread *pos, *n;
82     llist_for_each(pos, n, sched_ctx.threads, sched_sibs) {
83         if (likely(!proc_terminated(pos) || !thread_detached(pos))) {
84             continue;
85         }
86
87         struct proc_mm* mm = vmspace(pos->process);
88
89         procvm_mount(mm);
90         destory_thread(pos);
91         procvm_unmount(mm);
92         
93         i++;
94     }
95
96     if (i) {
97         INFO("cleaned %d terminated detached thread(s)", i);
98     }
99
100     cpu_enable_interrupt();
101 }
102
103 int
104 can_schedule(struct thread* thread)
105 {
106     if (!thread) {
107         return 0;
108     }
109
110     if (unlikely(kernel_process(thread->process))) {
111         // a kernel process is always runnable
112         return thread->state == PS_READY;
113     }
114
115     struct sigctx* sh = &thread->sigctx;
116
117     if ((thread->state & PS_PAUSED)) {
118         return !!(sh->sig_pending & ~1);
119     }
120     if ((thread->state & PS_BLOCKED)) {
121         return sigset_test(sh->sig_pending, _SIGINT);
122     }
123
124     if (sigset_test(sh->sig_pending, _SIGSTOP)) {
125         // If one thread is experiencing SIGSTOP, then we know
126         // all other threads are also SIGSTOP (as per POSIX-2008.1)
127         // In which case, the entire process is stopped.
128         thread->state = PS_STOPPED;
129         return 0;
130     }
131     if (sigset_test(sh->sig_pending, _SIGCONT)) {
132         thread->state = PS_READY;
133     }
134
135     return (thread->state == PS_READY) \
136             && proc_runnable(thread->process);
137 }
138
139 void
140 check_sleepers()
141 {
142     struct thread *pos, *n;
143     time_t now = clock_systime() / 1000;
144
145     llist_for_each(pos, n, &sched_ctx.sleepers, sleep.sleepers)
146     {
147         if (proc_terminated(pos)) {
148             goto del;
149         }
150
151         time_t wtime = pos->sleep.wakeup_time;
152         time_t atime = pos->sleep.alarm_time;
153
154         if (wtime && now >= wtime) {
155             pos->sleep.wakeup_time = 0;
156             pos->state = PS_READY;
157         }
158
159         if (atime && now >= atime) {
160             pos->sleep.alarm_time = 0;
161             thread_setsignal(pos, _SIGALRM);
162         }
163
164         if (!wtime && !atime) {
165         del:
166             llist_delete(&pos->sleep.sleepers);
167         }
168     }
169 }
170
171 void
172 schedule()
173 {
174     assert(sched_ctx.ptable_len && sched_ctx.ttable_len);
175
176     // 上下文切换相当的敏感!我们不希望任何的中断打乱栈的顺序……
177     cpu_disable_interrupt();
178
179     if (!(current_thread->state & ~PS_RUNNING)) {
180         current_thread->state = PS_READY;
181         __current->state = PS_READY;
182
183     }
184
185     procvm_unmount_self(vmspace(__current));
186     check_sleepers();
187
188     // round-robin scheduler
189     
190     struct thread* current = current_thread;
191     struct thread* to_check = current;
192     
193     do {
194         to_check = list_next(to_check, struct thread, sched_sibs);
195
196         if (can_schedule(to_check)) {
197             break;
198         }
199
200         if (to_check == current) {
201             // FIXME do something less leathal here
202             fail("Ran out of threads!")
203             goto done;  
204         }
205
206     } while (1);
207
208     sched_ctx.procs_index = to_check->process->pid;
209
210 done:
211     isrm_notify_eos(0);
212     run(to_check);
213
214     fail("unexpected return from scheduler");
215 }
216
217 void
218 sched_pass()
219 {
220     cpu_enable_interrupt();
221     cpu_trap_sched();
222 }
223
224 __DEFINE_LXSYSCALL1(unsigned int, sleep, unsigned int, seconds)
225 {
226     if (!seconds) {
227         return 0;
228     }
229
230     time_t systime = clock_systime() / 1000;
231     struct haybed* bed = &current_thread->sleep;
232
233     if (bed->wakeup_time) {
234         return (bed->wakeup_time - systime);
235     }
236
237     bed->wakeup_time = systime + seconds;
238
239     if (llist_empty(&bed->sleepers)) {
240         llist_append(&sched_ctx.sleepers, &bed->sleepers);
241     }
242
243     store_retval(seconds);
244
245     block_current_thread();
246     schedule();
247
248     return 0;
249 }
250
251 __DEFINE_LXSYSCALL1(unsigned int, alarm, unsigned int, seconds)
252 {
253     struct haybed* bed = &current_thread->sleep;
254     time_t prev_ddl = bed->alarm_time;
255     time_t now = clock_systime() / 1000;
256
257     bed->alarm_time = seconds ? now + seconds : 0;
258
259     struct proc_info* root_proc = sched_ctx.procs[0];
260     if (llist_empty(&bed->sleepers)) {
261         llist_append(&sched_ctx.sleepers, &bed->sleepers);
262     }
263
264     return prev_ddl ? (prev_ddl - now) : 0;
265 }
266
267 __DEFINE_LXSYSCALL1(void, exit, int, status)
268 {
269     terminate_current(status);
270     schedule();
271 }
272
273 __DEFINE_LXSYSCALL(void, yield)
274 {
275     schedule();
276 }
277
278 pid_t
279 _wait(pid_t wpid, int* status, int options);
280
281 __DEFINE_LXSYSCALL1(pid_t, wait, int*, status)
282 {
283     return _wait(-1, status, 0);
284 }
285
286 __DEFINE_LXSYSCALL3(pid_t, waitpid, pid_t, pid, int*, status, int, options)
287 {
288     return _wait(pid, status, options);
289 }
290
291 __DEFINE_LXSYSCALL(int, geterrno)
292 {
293     return current_thread->syscall_ret;
294 }
295
296 pid_t
297 _wait(pid_t wpid, int* status, int options)
298 {
299     pid_t cur = __current->pid;
300     int status_flags = 0;
301     struct proc_info *proc, *n;
302     if (llist_empty(&__current->children)) {
303         return -1;
304     }
305
306     wpid = wpid ? wpid : -__current->pgid;
307 repeat:
308     llist_for_each(proc, n, &__current->children, siblings)
309     {
310         if (!~wpid || proc->pid == wpid || proc->pgid == -wpid) {
311             if (proc->state == PS_TERMNAT && !options) {
312                 status_flags |= PEXITTERM;
313                 goto done;
314             }
315             if (proc->state == PS_READY && (options & WUNTRACED)) {
316                 status_flags |= PEXITSTOP;
317                 goto done;
318             }
319         }
320     }
321     if ((options & WNOHANG)) {
322         return 0;
323     }
324     // 放弃当前的运行机会
325     sched_pass();
326     goto repeat;
327
328 done:
329     if (status) {
330         *status = proc->exit_code | status_flags;
331     }
332     return destroy_process(proc->pid);
333 }
334
335 static inline pid_t
336 get_free_pid() {
337     pid_t i = 0;
338     
339     for (; i < sched_ctx.ptable_len && sched_ctx.procs[i]; i++)
340         ;
341     
342     if (unlikely(i == MAX_PROCESS)) {
343         panick("Panic in Ponyville shimmer!");
344     }
345
346     return i;
347 }
348
349 struct thread*
350 alloc_thread(struct proc_info* process) {
351     if (process->thread_count >= MAX_THREAD_PP) {
352         return NULL;
353     }
354     
355     struct thread* th = cake_grab(thread_pile);
356
357     th->process = process;
358     th->created = clock_systime();
359
360     // FIXME we need a better tid allocation method!
361     th->tid = th->created;
362     th->tid = (th->created ^ ((ptr_t)th)) % MAX_THREAD_PP;
363
364     th->state = PS_CREATED;
365     
366     llist_init_head(&th->sleep.sleepers);
367     llist_init_head(&th->sched_sibs);
368     llist_init_head(&th->proc_sibs);
369     waitq_init(&th->waitqueue);
370
371     return th;
372 }
373
374 struct proc_info*
375 alloc_process()
376 {
377     pid_t i = get_free_pid();
378
379     if (i == sched_ctx.ptable_len) {
380         sched_ctx.ptable_len++;
381     }
382
383     struct proc_info* proc = cake_grab(proc_pile);
384     if (!proc) {
385         return NULL;
386     }
387
388     proc->state = PS_CREATED;
389     proc->pid = i;
390     proc->created = clock_systime();
391     proc->pgid = proc->pid;
392
393     proc->sigreg = vzalloc(sizeof(struct sigregistry));
394     proc->fdtable = vzalloc(sizeof(struct v_fdtable));
395
396     proc->mm = procvm_create(proc);
397     
398     llist_init_head(&proc->tasks);
399     llist_init_head(&proc->children);
400     llist_init_head(&proc->grp_member);
401     llist_init_head(&proc->threads);
402
403     iopoll_init(&proc->pollctx);
404
405     sched_ctx.procs[i] = proc;
406
407     return proc;
408 }
409
410 void
411 commit_thread(struct thread* thread) {
412     struct proc_info* process = thread->process;
413
414     assert(process && !proc_terminated(process));
415
416     llist_append(&process->threads, &thread->proc_sibs);
417     
418     if (sched_ctx.threads) {
419         llist_append(sched_ctx.threads, &thread->sched_sibs);
420     } else {
421         sched_ctx.threads = &thread->sched_sibs;
422     }
423
424     sched_ctx.ttable_len++;
425     process->thread_count++;
426     thread->state = PS_READY;
427 }
428
429 void
430 commit_process(struct proc_info* process)
431 {
432     assert(process == sched_ctx.procs[process->pid]);
433     assert(process->state == PS_CREATED);
434
435     // every process is the child of first process (pid=1)
436     if (!process->parent) {
437         if (likely(!kernel_process(process))) {
438             process->parent = sched_ctx.procs[1];
439         } else {
440             process->parent = process;
441         }
442     } else {
443         assert(!proc_terminated(process->parent));
444     }
445
446     if (sched_ctx.proc_list) {
447         llist_append(sched_ctx.proc_list, &process->tasks);
448     } else {
449         sched_ctx.proc_list = &process->tasks;
450     }
451
452     llist_append(&process->parent->children, &process->siblings);
453
454     process->state = PS_READY;
455 }
456
457 void
458 destory_thread(struct thread* thread) 
459 {
460     cake_ensure_valid(thread);
461     
462     struct proc_info* proc = thread->process;
463
464     llist_delete(&thread->sched_sibs);
465     llist_delete(&thread->proc_sibs);
466     llist_delete(&thread->sleep.sleepers);
467     waitq_cancel_wait(&thread->waitqueue);
468
469     thread_release_mem(thread);
470
471     proc->thread_count--;
472     sched_ctx.ttable_len--;
473
474     cake_release(thread_pile, thread);
475 }
476
477 void 
478 delete_process(struct proc_info* proc)
479 {
480     pid_t pid = proc->pid;
481     struct proc_mm* mm = vmspace(proc);
482
483     assert(pid);    // long live the pid0 !!
484
485     sched_ctx.procs[pid] = NULL;
486
487     llist_delete(&proc->siblings);
488     llist_delete(&proc->grp_member);
489     llist_delete(&proc->tasks);
490
491     iopoll_free(proc);
492
493     taskfs_invalidate(pid);
494
495     if (proc->cwd) {
496         vfs_unref_dnode(proc->cwd);
497     }
498
499     if (proc->cmd) {
500         vfree(proc->cmd);
501     }
502
503     for (size_t i = 0; i < VFS_MAX_FD; i++) {
504         struct v_fd* fd = proc->fdtable->fds[i];
505         if (fd) {
506             vfs_pclose(fd->file, pid);
507             vfs_free_fd(fd);
508         }
509     }
510
511     vfree(proc->fdtable);
512
513     signal_free_registry(proc->sigreg);
514
515     procvm_mount(mm);
516     
517     struct thread *pos, *n;
518     llist_for_each(pos, n, &proc->threads, proc_sibs) {
519         // terminate and destory all thread unconditionally
520         destory_thread(pos);
521     }
522
523     procvm_unmount_release(mm);
524
525     cake_release(proc_pile, proc);
526 }
527
528 pid_t
529 destroy_process(pid_t pid)
530 {
531     int index = pid;
532     if (index <= 0 || index > sched_ctx.ptable_len) {
533         syscall_result(EINVAL);
534         return -1;
535     }
536
537     struct proc_info* proc = sched_ctx.procs[index];
538     delete_process(proc);
539
540     return pid;
541 }
542
543 static void 
544 terminate_proc_only(struct proc_info* proc, int exit_code) {
545     proc->state = PS_TERMNAT;
546     proc->exit_code = exit_code;
547
548     proc_setsignal(proc->parent, _SIGCHLD);
549 }
550
551 void
552 terminate_thread(struct thread* thread, ptr_t val) {
553     thread->exit_val = val;
554     thread->state = PS_TERMNAT;
555
556     struct proc_info* proc = thread->process;
557     if (proc->thread_count == 1) {
558         terminate_proc_only(thread->process, 0);
559     }
560 }
561
562 void
563 terminate_current_thread(ptr_t val) {
564     terminate_thread(current_thread, val);
565 }
566
567 void 
568 terminate_proccess(struct proc_info* proc, int exit_code) {
569     assert(!kernel_process(proc));
570
571     if (proc->pid == 1) {
572         panick("Attempt to kill init");
573     }
574
575     terminate_proc_only(proc, exit_code);
576
577     struct thread *pos, *n;
578     llist_for_each(pos, n, &__current->threads, proc_sibs) {
579         pos->state = PS_TERMNAT;
580     }
581 }
582
583 void
584 terminate_current(int exit_code)
585 {
586     terminate_proccess(__current, exit_code);
587 }
588
589 struct proc_info*
590 get_process(pid_t pid)
591 {
592     int index = pid;
593     if (index < 0 || index > sched_ctx.ptable_len) {
594         return NULL;
595     }
596     return sched_ctx.procs[index];
597 }
598
599 int
600 orphaned_proc(pid_t pid)
601 {
602     if (!pid)
603         return 0;
604     if (pid >= sched_ctx.ptable_len)
605         return 0;
606     struct proc_info* proc = sched_ctx.procs[pid];
607     struct proc_info* parent = proc->parent;
608
609     // 如果其父进程的状态是terminated 或 destroy中的一种
610     // 或者其父进程是在该进程之后创建的,那么该进程为孤儿进程
611     return proc_terminated(parent) || parent->created > proc->created;
612 }