refactor: re-structure the kernel address space for a more integral layout.
[lunaix-os.git] / lunaix-os / kernel / sched.c
1 #include <arch/x86/interrupts.h>
2 #include <arch/x86/tss.h>
3
4 #include <hal/apic.h>
5 #include <hal/cpu.h>
6
7 #include <lunaix/mm/kalloc.h>
8 #include <lunaix/mm/pmm.h>
9 #include <lunaix/mm/vmm.h>
10 #include <lunaix/process.h>
11 #include <lunaix/sched.h>
12 #include <lunaix/signal.h>
13 #include <lunaix/spike.h>
14 #include <lunaix/status.h>
15 #include <lunaix/syscall.h>
16 #include <lunaix/syslog.h>
17
18 #define MAX_PROCESS 512
19
20 volatile struct proc_info* __current;
21
22 struct proc_info dummy;
23
24 struct scheduler sched_ctx;
25
26 LOG_MODULE("SCHED")
27
28 void
29 sched_init()
30 {
31     size_t pg_size = ROUNDUP(sizeof(struct proc_info) * MAX_PROCESS, 0x1000);
32
33     for (size_t i = 0; i <= pg_size; i += 4096) {
34         uintptr_t pa = pmm_alloc_page(KERNEL_PID, PP_FGPERSIST);
35         vmm_set_mapping(
36           PD_REFERENCED, PROC_START + i, pa, PG_PREM_RW, VMAP_NULL);
37     }
38
39     sched_ctx = (struct scheduler){ ._procs = (struct proc_info*)PROC_START,
40                                     .ptable_len = 0,
41                                     .procs_index = 0 };
42 }
43
44 void
45 run(struct proc_info* proc)
46 {
47     if (!(__current->state & ~PROC_RUNNING)) {
48         __current->state = PROC_STOPPED;
49     }
50     proc->state = PROC_RUNNING;
51
52     // FIXME: 这里还是得再考虑一下。
53     // tss_update_esp(__current->intr_ctx.esp);
54     apic_done_servicing();
55
56     asm volatile("pushl %0\n"
57                  "jmp switch_to\n" ::"r"(proc)); // kernel/asm/x86/interrupt.S
58 }
59
60 void
61 schedule()
62 {
63     if (!sched_ctx.ptable_len) {
64         return;
65     }
66
67     // 上下文切换相当的敏感!我们不希望任何的中断打乱栈的顺序……
68     cpu_disable_interrupt();
69     struct proc_info* next;
70     int prev_ptr = sched_ctx.procs_index;
71     int ptr = prev_ptr;
72     // round-robin scheduler
73     do {
74         ptr = (ptr + 1) % sched_ctx.ptable_len;
75         next = &sched_ctx._procs[ptr];
76     } while (next->state != PROC_STOPPED && ptr != prev_ptr);
77
78     sched_ctx.procs_index = ptr;
79
80     run(next);
81 }
82
83 static void
84 proc_timer_callback(struct proc_info* proc)
85 {
86     proc->timer = NULL;
87     proc->state = PROC_STOPPED;
88 }
89
90 __DEFINE_LXSYSCALL1(unsigned int, sleep, unsigned int, seconds)
91 {
92     // FIXME: sleep的实现或许需要改一下。专门绑一个计时器好像没有必要……
93     if (!seconds) {
94         return 0;
95     }
96
97     if (__current->timer) {
98         return __current->timer->counter / timer_context()->running_frequency;
99     }
100
101     struct lx_timer* timer =
102       timer_run_second(seconds, proc_timer_callback, __current, 0);
103     __current->timer = timer;
104     __current->intr_ctx.registers.eax = seconds;
105     __current->state = PROC_BLOCKED;
106     schedule();
107 }
108
109 __DEFINE_LXSYSCALL1(void, exit, int, status)
110 {
111     terminate_proc(status);
112 }
113
114 __DEFINE_LXSYSCALL(void, yield)
115 {
116     schedule();
117 }
118
119 pid_t
120 _wait(pid_t wpid, int* status, int options);
121
122 __DEFINE_LXSYSCALL1(pid_t, wait, int*, status)
123 {
124     return _wait(-1, status, 0);
125 }
126
127 __DEFINE_LXSYSCALL3(pid_t, waitpid, pid_t, pid, int*, status, int, options)
128 {
129     return _wait(pid, status, options);
130 }
131
132 pid_t
133 _wait(pid_t wpid, int* status, int options)
134 {
135     pid_t cur = __current->pid;
136     int status_flags = 0;
137     struct proc_info *proc, *n;
138     if (llist_empty(&__current->children)) {
139         return -1;
140     }
141
142     wpid = wpid ? wpid : -__current->pgid;
143     cpu_enable_interrupt();
144 repeat:
145     llist_for_each(proc, n, &__current->children, siblings)
146     {
147         if (!~wpid || proc->pid == wpid || proc->pgid == -wpid) {
148             if (proc->state == PROC_TERMNAT && !options) {
149                 status_flags |= PROCTERM;
150                 goto done;
151             }
152             if (proc->state == PROC_STOPPED && (options & WUNTRACED)) {
153                 status_flags |= PROCSTOP;
154                 goto done;
155             }
156         }
157     }
158     if ((options & WNOHANG)) {
159         return 0;
160     }
161     // 放弃当前的运行机会
162     sched_yield();
163     goto repeat;
164
165 done:
166     cpu_disable_interrupt();
167     *status = (proc->exit_code & 0xffff) | status_flags;
168     return destroy_process(proc->pid);
169 }
170
171 pid_t
172 alloc_pid()
173 {
174     pid_t i = 0;
175     for (;
176          i < sched_ctx.ptable_len && sched_ctx._procs[i].state != PROC_DESTROY;
177          i++)
178         ;
179
180     if (i == MAX_PROCESS) {
181         panick("Panic in Ponyville shimmer!");
182     }
183     return i;
184 }
185
186 void
187 push_process(struct proc_info* process)
188 {
189     int index = process->pid;
190     if (index < 0 || index > sched_ctx.ptable_len) {
191         __current->k_status = LXINVLDPID;
192         return;
193     }
194
195     if (index == sched_ctx.ptable_len) {
196         sched_ctx.ptable_len++;
197     }
198
199     sched_ctx._procs[index] = *process;
200
201     process = &sched_ctx._procs[index];
202
203     // make sure the reference is relative to process table
204     llist_init_head(&process->children);
205     llist_init_head(&process->grp_member);
206
207     // every process is the child of first process (pid=1)
208     if (process->parent) {
209         llist_append(&process->parent->children, &process->siblings);
210     } else {
211         process->parent = &sched_ctx._procs[0];
212     }
213
214     process->state = PROC_STOPPED;
215 }
216
217 // from <kernel/process.c>
218 extern void
219 __del_pagetable(pid_t pid, uintptr_t mount_point);
220
221 pid_t
222 destroy_process(pid_t pid)
223 {
224     int index = pid;
225     if (index <= 0 || index > sched_ctx.ptable_len) {
226         __current->k_status = LXINVLDPID;
227         return;
228     }
229     struct proc_info* proc = &sched_ctx._procs[index];
230     proc->state = PROC_DESTROY;
231     llist_delete(&proc->siblings);
232
233     if (proc->mm.regions) {
234         struct mm_region *pos, *n;
235         llist_for_each(pos, n, &proc->mm.regions->head, head)
236         {
237             lxfree(pos);
238         }
239     }
240
241     vmm_mount_pd(PD_MOUNT_1, proc->page_table);
242
243     __del_pagetable(pid, PD_MOUNT_1);
244
245     vmm_unmount_pd(PD_MOUNT_1);
246
247     return pid;
248 }
249
250 void
251 terminate_proc(int exit_code)
252 {
253     __current->state = PROC_TERMNAT;
254     __current->exit_code = exit_code;
255
256     schedule();
257 }
258
259 struct proc_info*
260 get_process(pid_t pid)
261 {
262     int index = pid;
263     if (index < 0 || index > sched_ctx.ptable_len) {
264         return NULL;
265     }
266     return &sched_ctx._procs[index];
267 }
268
269 int
270 orphaned_proc(pid_t pid)
271 {
272     if (!pid)
273         return 0;
274     if (pid >= sched_ctx.ptable_len)
275         return 0;
276     struct proc_info* proc = &sched_ctx._procs[pid];
277     struct proc_info* parent = proc->parent;
278
279     // 如果其父进程的状态是terminated 或 destroy中的一种
280     // 或者其父进程是在该进程之后创建的,那么该进程为孤儿进程
281     return (parent->state & PROC_TERMMASK) || parent->created > proc->created;
282 }