AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
sched.c
Go to the documentation of this file.
1
10
11#include <core/system.h>
12#include <core/panic.h>
13#include <core/arch.h>
14#include <sys/proc.h>
15#include <sys/sched.h>
16#include <sys/signal.h>
17#include <ds/list.h>
18#include <dev/random.h>
19#include <sys/callout.h>
20#include <fs/vfs.h>
21
22#include <aquila/stat.h>
23
24/* The threads ready to run, in the order they will run (their run_link). */
25static struct list ready;
26
27/* The processors; cpus[0] is the one that booted. */
30
31/* The threads waiting with a deadline (thread->sleep_until), whichever
32 * wait queue they sleep on (their timer_link). */
33static struct list timed;
34
35/* nanosleep() sleeps here; only the timer wakes its threads up. */
36static struct waitq naps;
37
38/* The processes with a real time timer armed (proc->alarm_at, their
39 * alarm_link). */
40static struct list alarms;
41
54void sched_cpu_tick(uint32_t period, int user)
55{
56 /* a killed thread's process may be gone already (thread->killed) */
57 if (!curcpu()->idle && curthread && !curthread->killed) {
58 if (user)
59 curproc->utime += period;
60 else
61 curproc->stime += period;
62 }
63}
64
74void sched_timer_tick(uint32_t period, int user)
75{
76 uint64_t now = arch_rtime_ns();
77
78 /* when an interrupt comes, counted in cycles, isn't predictable */
80
81 callout_run(now);
82
83 sched_cpu_tick(period, user);
84
85 list_for (node, &timed) {
86 struct thread *thread = list_entry(node, struct thread, timer_link);
87
88 if (now >= thread->sleep_until) {
91 }
92 }
93
94 list_for (node, &alarms) {
95 struct proc *proc = list_entry(node, struct proc, alarm_link);
96
97 if (now >= proc->alarm_at) {
98 if (proc->alarm_interval) {
99 /* the next expiry, rather than every one that was missed */
101 if (proc->alarm_at <= now)
103 } else {
104 list_remove(&proc->alarm_link);
105 }
106
107 signal_proc_send(proc, SIGALRM);
108 }
109 }
110}
111
122{
123 uint64_t now = arch_rtime_ns();
124
125 int armed = list_linked(&proc->alarm_link);
126
127 *value = !armed ? 0 : proc->alarm_at > now ? proc->alarm_at - now : 1;
128 *interval = armed ? proc->alarm_interval : 0;
129}
130
145 uint64_t *old_value, uint64_t *old_interval)
146{
147 uint64_t now = arch_rtime_ns(), v, i;
148
149 sched_alarm_get(proc, &v, &i);
150
151 if (old_value)
152 *old_value = v;
153
154 if (old_interval)
155 *old_interval = i;
156
157 list_remove(&proc->alarm_link);
158
159 if (!value)
160 return;
161
162 proc->alarm_at = now + value;
164 list_append(&alarms, &proc->alarm_link);
165}
166
174{
175 list_remove(&thread->timer_link);
176}
177
187int sched_wait(struct waitq *waitq, uint64_t until)
188{
189 if (until) {
190 if (arch_rtime_ns() >= until)
191 return 1;
192
193 curthread->sleep_until = until;
194 list_append(&timed, &curthread->timer_link);
195 }
196
197 int err = waitq_sleep(waitq);
199
200 if (err)
201 return err;
202
203 return until && arch_rtime_ns() >= until;
204}
205
213{
214 int r;
215
216 while (!(r = sched_wait(&naps, until)))
217 ;
218
219 return r < 0 ? r : 0;
220}
221
230{
231 for (int i = 0; i < cpus_count; ++i)
232 if (cpus[i].thread == thread && &cpus[i] != curcpu())
233 return &cpus[i];
234
235 return NULL;
236}
237
245{
246 struct cpu *cpu = sched_thread_cpu(thread);
247
248 if (cpu)
250}
251
259{
260 struct thread *thread = curthread;
261
262 if (thread->owner) {
263 queue_remove(&thread->owner->threads, thread);
265 thread->owner = NULL;
266 }
267
269}
270
276static void sched_cpu_wake(void)
277{
278 if (!sched_running || curcpu()->idle)
279 return;
280
281 for (int i = 0; i < cpus_count; ++i) {
282 struct cpu *cpu = &cpus[i];
283
284 /* a CPU already woken up runs another thread: try the next one */
285 if (cpu != curcpu() && cpu->idle && !cpu->kicked) {
286 cpu->kicked = 1;
288 return;
289 }
290 }
291}
292
299{
300 list_append(&ready, &thread->run_link);
302}
303
304/* An urgent thread was woken up: switch to it when the interrupt returns
305 * to user mode (an idle CPU switches anyway). */
307
316{
317 if (!thread->urgent) {
319 return;
320 }
321
322 list_prepend(&ready, &thread->run_link);
323 sched_urgent = 1;
325}
326
332void kernel_idle(void)
333{
334 curcpu()->idle = 1;
335
336 if (!list_empty(&ready))
337 schedule();
338
339 arch_idle();
340}
341
348{
349 /* another CPU may wake this one up again */
350 curcpu()->kicked = 0;
351
352 if (curcpu()->idle && !list_empty(&ready))
353 schedule();
354}
355
362{
363 thread->spawned = 1;
365}
366
368
375void sched_init_spawn(struct proc *init)
376{
377 proc_init(init);
378
379 /* init's defaults */
380 init->mask = 022; /* umask */
381 init->cwd = vfs_root;
382 vfs_root->ref++;
383
385
386 session_new(init);
387
388 curthread = (struct thread *) init->threads.head->value;
389 curthread->state = RUNNABLE;
390 sched_running = 1;
392}
393
400void schedule(void)
401{
402 sched_urgent = 0;
403
404 if (!curcpu()->idle)
406
407 curcpu()->idle = 0;
408
409 if (list_empty(&ready))
410 kernel_idle();
411
412 curthread = list_entry(list_pop(&ready), struct thread, run_link);
413
414 if (curthread->spawned) {
416 } else {
418 }
419}
void arch_cpu_kick(struct cpu *cpu)
Makes another CPU enter the kernel now, with an inter-processor interrupt: to run a thread that just ...
Definition smp.c:14
void arch_idle(void)
Makes this CPU idle, on its idle stack, until an interrupt makes a thread ready.
Definition sched.c:62
void arch_cur_thread_kill(void)
Ends the current thread.
Definition sched.c:87
void arch_sched_init(void)
Starts the timer tick on every CPU, and the clock where it needs to be started.
Definition sched.c:43
void arch_thread_switch(struct thread *thread)
Switches to a thread that has run before: its address space, its kernel stack, then where it stopped.
Definition thread.c:76
void arch_thread_spawn(struct thread *thread)
Runs a thread for the first time: switches to its address space and kernel stack, and starts it.
Definition thread.c:63
void callout_run(uint64_t now)
Calls the timer's callouts that are due, from the timer interrupt.
Definition callout.c:131
struct cpu cpus[CPUS_MAX]
Definition sched.c:28
int cpus_count
Definition sched.c:29
#define CPUS_MAX
Definition cpu.h:21
#define list_entry(node, type, member)
Definition list.h:43
#define list_for(n, l)
Definition list.h:48
int proc_init(struct proc *proc)
Gives a new process (init) its ID and its descriptor table.
Definition proc.c:197
int session_new(struct proc *proc)
Makes a new session, with a new process group, both led by a process and with its ID,...
Definition proc.c:596
void random_add(uint32_t value)
Stirs a value into the pool, which goes into the next key.
Definition random.c:68
void sched_init_spawn(struct proc *init)
Starts the scheduler with the first process, init, and runs it.
Definition sched.c:375
void sched_idle_wakeup(void)
Runs a ready thread now if an interrupt made one ready while this CPU was idle, rather than at the ne...
Definition sched.c:347
int sched_urgent
Definition sched.c:306
#define curproc
Definition sched.h:13
void sched_cpu_tick(uint32_t period, int user)
Charges a timer tick to the process that was running on this CPU.
Definition sched.c:54
void sched_alarm_get(struct proc *proc, uint64_t *value, uint64_t *interval)
Reads what is left of a process's real time timer.
Definition sched.c:121
void sched_thread_killed(void)
Ends the current thread, which was killed while it ran on this CPU (thread->killed).
Definition sched.c:258
int sched_wait(struct waitq *waitq, uint64_t until)
Puts the current thread to sleep on a wait queue until it is woken up, a signal ends the sleep,...
Definition sched.c:187
void sched_timer_cancel(struct thread *thread)
Takes a thread off the list of threads waiting with a deadline, when it no longer waits (it was woken...
Definition sched.c:173
void sched_alarm(struct proc *proc, uint64_t value, uint64_t interval, uint64_t *old_value, uint64_t *old_interval)
Sets a process's real time timer, which setitimer() and alarm() use: the process gets SIGALRM once va...
Definition sched.c:144
int sched_running
Definition sched.c:367
int sched_sleep(uint64_t until)
Puts the current thread to sleep until a time comes, for nanosleep().
Definition sched.c:212
void kernel_idle(void)
Gives up the CPU when the current thread can't run anymore (it sleeps, or it exited): the next ready ...
Definition sched.c:332
void schedule(void)
Switches to the next ready thread.
Definition sched.c:400
#define curthread
Definition sched.h:12
void sched_timer_tick(uint32_t period, int user)
Runs the boot CPU's timer tick.
Definition sched.c:74
struct cpu * sched_thread_cpu(struct thread *thread)
Finds the CPU that is running a thread, if it's another CPU than this one.
Definition sched.c:229
void sched_thread_kick(struct thread *thread)
Makes a thread that runs on another CPU enter the kernel now, rather than at its next tick: a signal ...
Definition sched.c:244
void sched_thread_ready(struct thread *thread)
Puts a thread at the end of the ready list, to run in its turn.
Definition sched.c:298
void sched_thread_spawn(struct thread *thread)
Starts running a thread for the first time.
Definition sched.c:361
void sched_thread_wake(struct thread *thread)
Makes a thread that was sleeping ready to run.
Definition sched.c:315
int signal_proc_send(struct proc *proc, int signal)
Sends a signal to a process.
Definition signal.c:310
#define NULL
Definition stddef.h:27
unsigned int uint32_t
Definition stddef.h:22
unsigned long long int uint64_t
Definition stddef.h:23
A processor, and what runs on it.
Definition cpu.h:11
int idle
Definition cpu.h:15
int kicked
Definition cpu.h:16
A list: its first and last nodes, and how many there are.
Definition list.h:37
A process.
Definition proc.h:75
struct waitq thread_join
threads waiting in thread_join() sleep here
Definition proc.h:143
struct vnode * cwd
its current directory (a reference), or NULL for the root
Definition proc.h:93
struct list_node alarm_link
Definition proc.h:115
uint64_t alarm_at
its real time timer (ITIMER_REAL): SIGALRM when arch_rtime_ns() reaches alarm_at, then every alarm_in...
Definition proc.h:114
mode_t mask
its file mode creation mask (umask)
Definition proc.h:96
uint64_t alarm_interval
Definition proc.h:114
struct queue threads
its threads
Definition proc.h:136
void * value
Definition queue.h:20
struct qnode * head
Definition queue.h:31
A thread.
Definition thread.h:23
struct list_node run_link
it's on the ready list (sched.c) while it's ready to run, or on the wait queue it sleeps on (waitq); ...
Definition thread.h:45
struct list_node timer_link
it's on the timer's list while it waits with a deadline
Definition thread.h:75
uint64_t sleep_until
when it waits with a deadline (sched_wait()): the deadline, in arch_rtime_ns() time
Definition thread.h:68
int urgent
it runs before the others once woken, as soon as the interrupt that woke it returns (sched_thread_wak...
Definition thread.h:91
int spawned
whether it has run yet
Definition thread.h:87
struct proc * owner
its process
Definition thread.h:31
size_t ref
how many references there are to it
Definition vfs.h:133
Definition waitq.h:19
static struct list ready
Definition sched.c:25
static struct list timed
Definition sched.c:33
static struct list alarms
Definition sched.c:40
static struct waitq naps
Definition sched.c:36
static void sched_cpu_wake(void)
Wakes up an idle CPU to run a thread that just became ready.
Definition sched.c:276
@ RUNNABLE
Definition thread.h:16
uint64_t arch_rtime_ns(void)
Reads the monotonic clock, which starts at boot.
Definition timer.c:56
static uint64_t interval
Definition timer.c:16
uint64_t arch_cycles(void)
Reads a counter that changes as fast as the CPU runs, which the kernel uses as a source of randomness...
Definition timer.c:82
struct vnode * vfs_root
Definition vfs.c:29
void thread_wakeup(struct thread *thread)
Wakes up one thread, if it is sleeping on a wait queue.
Definition waitq.c:216
void waitq_wakeup(struct waitq *waitq)
Wakes up every thread sleeping on a wait queue, and every thread polling it.
Definition waitq.c:178
int waitq_sleep(struct waitq *waitq)
Puts the current thread to sleep on a wait queue until it is woken up.
Definition waitq.c:50