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