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