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