Merge branch 'master' of github.com:Minep/lunaix-os
[lunaix-os.git] / lunaix-os / kernel / sched.c
1 #include <arch/x86/interrupts.h>
2 #include <arch/x86/tss.h>
3 #include <hal/apic.h>
4 #include <hal/cpu.h>
5 #include <lunaix/mm/kalloc.h>
6 #include <lunaix/mm/vmm.h>
7 #include <lunaix/process.h>
8 #include <lunaix/sched.h>
9
10 #include <lunaix/spike.h>
11 #include <lunaix/status.h>
12 #include <lunaix/syscall.h>
13 #include <lunaix/syslog.h>
14
15 #define MAX_PROCESS 512
16
17 volatile struct proc_info* __current;
18
19 struct proc_info dummy;
20
21 extern void __proc_table;
22
23 struct scheduler sched_ctx;
24
25 LOG_MODULE("SCHED")
26
27 void
28 sched_init()
29 {
30     size_t pg_size = ROUNDUP(sizeof(struct proc_info) * MAX_PROCESS, 0x1000);
31     assert_msg(vmm_alloc_pages(
32                  KERNEL_PID, &__proc_table, pg_size, PG_PREM_RW, PP_FGPERSIST),
33                "Fail to allocate proc table");
34
35     sched_ctx = (struct scheduler){ ._procs = (struct proc_info*)&__proc_table,
36                                     .ptable_len = 0,
37                                     .procs_index = 0 };
38 }
39
40 void
41 run(struct proc_info* proc)
42 {
43     if (!(__current->state & ~PROC_RUNNING)) {
44         __current->state = PROC_STOPPED;
45     }
46     proc->state = PROC_RUNNING;
47
48     // FIXME: 这里还是得再考虑一下。
49     // tss_update_esp(__current->intr_ctx.esp);
50
51     if (__current->page_table != proc->page_table) {
52         __current = proc;
53         cpu_lcr3(__current->page_table);
54         // from now on, the we are in the kstack of another process
55     } else {
56         __current = proc;
57     }
58
59     apic_done_servicing();
60
61     asm volatile("pushl %0\n"
62                  "jmp soft_iret\n" ::"r"(&__current->intr_ctx)
63                  : "memory");
64 }
65
66 void
67 schedule()
68 {
69     if (!sched_ctx.ptable_len) {
70         return;
71     }
72
73     struct proc_info* next;
74     int prev_ptr = sched_ctx.procs_index;
75     int ptr = prev_ptr;
76     // round-robin scheduler
77     do {
78         ptr = (ptr + 1) % sched_ctx.ptable_len;
79         next = &sched_ctx._procs[ptr];
80     } while (next->state != PROC_STOPPED && ptr != prev_ptr);
81
82     sched_ctx.procs_index = ptr;
83
84     run(next);
85 }
86
87 static void
88 proc_timer_callback(struct proc_info* proc)
89 {
90     proc->timer = NULL;
91     proc->state = PROC_STOPPED;
92 }
93
94 __DEFINE_LXSYSCALL1(unsigned int, sleep, unsigned int, seconds)
95 {
96     // FIXME: sleep的实现或许需要改一下。专门绑一个计时器好像没有必要……
97     if (!seconds) {
98         return 0;
99     }
100     if (__current->timer) {
101         return __current->timer->counter / timer_context()->running_frequency;
102     }
103
104     struct lx_timer* timer =
105       timer_run_second(seconds, proc_timer_callback, __current, 0);
106     __current->timer = timer;
107     __current->intr_ctx.registers.eax = seconds;
108     __current->state = PROC_BLOCKED;
109     schedule();
110 }
111
112 __DEFINE_LXSYSCALL1(void, exit, int, status)
113 {
114     terminate_proc(status);
115 }
116
117 __DEFINE_LXSYSCALL(void, yield)
118 {
119     schedule();
120 }
121
122 __DEFINE_LXSYSCALL1(pid_t, wait, int*, status)
123 {
124     pid_t cur = __current->pid;
125     struct proc_info *proc, *n;
126     if (llist_empty(&__current->children)) {
127         return -1;
128     }
129 repeat:
130     llist_for_each(proc, n, &__current->children, siblings)
131     {
132         if (proc->state == PROC_TERMNAT) {
133             goto done;
134         }
135     }
136     // FIXME: 除了循环,也许有更高效的办法……
137     // (在这里进行schedule,需要重写context switch!)
138     goto repeat;
139
140 done:
141     *status = proc->exit_code;
142     return destroy_process(proc->pid);
143 }
144
145 pid_t
146 alloc_pid()
147 {
148     pid_t i = 0;
149     for (;
150          i < sched_ctx.ptable_len && sched_ctx._procs[i].state != PROC_DESTROY;
151          i++)
152         ;
153
154     if (i == MAX_PROCESS) {
155         panick("Panic in Ponyville shimmer!");
156     }
157     return i;
158 }
159
160 void
161 push_process(struct proc_info* process)
162 {
163     int index = process->pid;
164     if (index < 0 || index > sched_ctx.ptable_len) {
165         __current->k_status = LXINVLDPID;
166         return;
167     }
168
169     if (index == sched_ctx.ptable_len) {
170         sched_ctx.ptable_len++;
171     }
172
173     sched_ctx._procs[index] = *process;
174
175     process = &sched_ctx._procs[index];
176
177     // make sure the address is in the range of process table
178     llist_init_head(&process->children);
179     // every process is the child of first process (pid=1)
180     if (process->parent) {
181         llist_append(&process->parent->children, &process->siblings);
182     } else {
183         process->parent = &sched_ctx._procs[0];
184     }
185
186     process->state = PROC_STOPPED;
187 }
188
189 // from <kernel/process.c>
190 extern void
191 __del_pagetable(pid_t pid, uintptr_t mount_point);
192
193 pid_t
194 destroy_process(pid_t pid)
195 {
196     int index = pid;
197     if (index <= 0 || index > sched_ctx.ptable_len) {
198         __current->k_status = LXINVLDPID;
199         return;
200     }
201     struct proc_info* proc = &sched_ctx._procs[index];
202     proc->state = PROC_DESTROY;
203     llist_delete(&proc->siblings);
204
205     if (proc->mm.regions) {
206         struct mm_region *pos, *n;
207         llist_for_each(pos, n, &proc->mm.regions->head, head)
208         {
209             lxfree(pos);
210         }
211     }
212
213     vmm_mount_pd(PD_MOUNT_2, proc->page_table);
214
215     __del_pagetable(pid, PD_MOUNT_2);
216
217     vmm_unmount_pd(PD_MOUNT_2);
218
219     return pid;
220 }
221
222 void
223 terminate_proc(int exit_code)
224 {
225     __current->state = PROC_TERMNAT;
226     __current->exit_code = exit_code;
227
228     schedule();
229 }
230
231 struct proc_info*
232 get_process(pid_t pid)
233 {
234     int index = pid;
235     if (index < 0 || index > sched_ctx.ptable_len) {
236         return NULL;
237     }
238     return &sched_ctx._procs[index];
239 }
240
241 int
242 orphaned_proc(pid_t pid)
243 {
244     if (!pid)
245         return 0;
246     if (pid >= sched_ctx.ptable_len)
247         return 0;
248     struct proc_info* proc = &sched_ctx._procs[pid];
249     struct proc_info* parent = proc->parent;
250
251     // 如果其父进程的状态是terminated 或 destroy中的一种
252     // 或者其父进程是在该进程之后创建的,那么该进程为孤儿进程
253     return (parent->state & PROC_TERMMASK) || parent->created > proc->created;
254 }