Skip to content

Commit f1ff1ac

Browse files
committed
Hide some task-switching internals inside task_yield.
Signed-off-by: Amy Ringo <me@remexre.com>
1 parent 95c7069 commit f1ff1ac

7 files changed

Lines changed: 89 additions & 17 deletions

File tree

src/kernel/arch/riscv64/task.S

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ task_switch:
4848
li t0, 1
4949
bne a2, t0, 1f
5050

51+
// Save the new task to the hart locals.
52+
sd a0, 8(a1)
53+
5154
// Get the f_off and f_len. Jump to the appropriate place based on the f_len,
5255
// to swap the callee-save floating-point registers.
5356
ld a5, 16(a4) // a5 = f_off
@@ -134,8 +137,10 @@ task_switch:
134137
la t0, task_resume
135138
sd t0, 40(a5)
136139

137-
// Save SSTATUS to the old task.
140+
// Save SSTATUS to the old task, setting its SPP bit so that we return to
141+
// supervisor mode.
138142
csrr t0, sstatus
143+
ori t0, t0, 1<<8
139144
sd t0, 296(a5)
140145

141146
// Load SEPC and SSTATUS from the new task.
@@ -207,4 +212,4 @@ task_switch:
207212
.p2align 2
208213
.type task_resume, @function
209214
task_resume:
210-
ebreak
215+
ret

src/kernel/drivers/riscv_cpu.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ constexpr u64 TASK_MAGIC = 0x636967614d6f6b75;
8585
static void task_main(void *main_arg, void (*main)(void *), u64 magic) {
8686
assert(TASK_MAGIC == magic);
8787
main(main_arg);
88-
TODO("task exit");
88+
task_exit();
8989
}
9090

9191
static void riscv_cpu_set_task_regs(struct hart *hart, struct task *task,

src/kernel/include/scheduler.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,12 @@
1414
*
1515
* Ensures:
1616
*
17-
* - The task is in the list of running tasks.
17+
* - If the task is non-`nullptr`, the task is in the list of running tasks.
1818
*/
19-
struct task *scheduler_get(void);
19+
struct task *scheduler_get();
2020

2121
/**
22-
* Puts a task back in the scheduler, allowing other harts to run it later. The
23-
* task must not be accessed again after calling this function.
22+
* Puts a task back in the scheduler, allowing other harts to run it later.
2423
*
2524
* Requires:
2625
*
@@ -29,8 +28,7 @@ struct task *scheduler_get(void);
2928
void scheduler_put(struct task *task);
3029

3130
/**
32-
* Puts a new task into the scheduler's list of runnable tasks. The task must
33-
* not be accessed again afer calling this function.
31+
* Puts a new task into the scheduler's list of runnable tasks.
3432
*
3533
* Requires:
3634
*

src/kernel/include/task.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,17 @@ static_assert(offsetof(struct task, register_save) == 40);
106106
struct task *task_new(struct hart *hart, void (*main)(void *), void *main_arg);
107107

108108
/**
109-
* Switches to the given task.
109+
* Considers whether to switch to another task.
110110
*
111-
* TODO: This should require that a hartlock be held.
111+
* There is no guarantee that a switch will occur; in particular, this does
112+
* _not_ necessarily give up the current task's quantum.
112113
*/
113-
void task_switch(struct task *task);
114+
void task_yield();
115+
116+
/**
117+
* Exits from the current task.
118+
*/
119+
[[noreturn]]
120+
void task_exit();
114121

115122
#endif // UKO_OS_KERNEL__TASK_H

src/kernel/main.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,16 +71,19 @@ void main(u64 hart_id, paddr devicetree_start, paddr kernel_start,
7171
run_selftests();
7272

7373
// Create another task and switch to it.
74-
struct task *task = task_new(get_hart_locals()->hart, gm, (void *)0x12345);
75-
scheduler_put_new(task);
76-
get_hart_locals()->task->flags = TASK_STATE_RUNNABLE;
77-
task->flags = TASK_STATE_RUNNING;
78-
task_switch(task);
74+
scheduler_put_new(task_new(get_hart_locals()->hart, gm, (void *)0x12345));
75+
task_yield();
76+
print("back in main");
77+
task_yield();
78+
print("back in main again");
79+
task_yield();
80+
print("back in main a last time -- nothing should've switched this time");
7981

8082
TODO();
8183
}
8284

8385
static void gm(void *ptr) {
8486
print("GM {uptr}", ptr);
87+
task_yield();
8588
print("GN {uptr}", ptr);
8689
}

src/kernel/scheduler.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* SPDX-License-Identifier: GPL-3.0-or-later
55
*/
66

7+
#include <print.h>
78
#include <scheduler.h>
89

910
/**
@@ -24,6 +25,24 @@ static struct list_head scheduler_runnable = LIST_INIT(scheduler_runnable);
2425
*/
2526
static struct list_head scheduler_blocked = LIST_INIT(scheduler_blocked);
2627

28+
struct task *scheduler_get() {
29+
struct task *task =
30+
container_of(list_shift(&scheduler_runnable), struct task, list);
31+
assert(task->flags == TASK_STATE_RUNNABLE);
32+
task->flags = TASK_STATE_RUNNING;
33+
list_push(&scheduler_running, &task->list);
34+
return task;
35+
}
36+
37+
void scheduler_put(struct task *task) {
38+
assert(!list_is_empty(&task->list));
39+
assert(task->flags == TASK_STATE_RUNNING);
40+
41+
list_remove(&task->list);
42+
task->flags = TASK_STATE_RUNNABLE;
43+
list_push(&scheduler_runnable, &task->list);
44+
}
45+
2746
void scheduler_put_new(struct task *task) {
2847
assert(list_is_empty(&task->list));
2948
assert(task->flags == TASK_STATE_CONSTRUCTING);

src/kernel/task.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,17 @@
77
#include <mm/alloc.h>
88
#include <mm/paging.h>
99
#include <mm/physical_alloc.h>
10+
#include <scheduler.h>
1011
#include <stdatomic.h>
1112
#include <task.h>
1213

14+
/**
15+
* Switches to the given task.
16+
*
17+
* TODO: This should require that a hartlock be held.
18+
*/
19+
void task_switch(struct task *task);
20+
1321
struct task *task_new(struct hart *hart, void (*main)(void *), void *main_arg) {
1422
assert(hart);
1523
assert(hart->hart_group);
@@ -54,3 +62,35 @@ struct task *task_new(struct hart *hart, void (*main)(void *), void *main_arg) {
5462

5563
return task;
5664
}
65+
66+
void task_yield() {
67+
// Ask the scheduler for another task to run. If there was nothing to run,
68+
// don't try to switch.
69+
struct task *new_task = scheduler_get();
70+
if (!new_task)
71+
return;
72+
assert(new_task->flags == TASK_STATE_RUNNING);
73+
74+
// Get the current task.
75+
struct task *old_task = get_hart_locals()->task;
76+
77+
// Hand the current task back to the scheduler.
78+
//
79+
// TODO: This _really_ ought to be under some kind of lock that gets released
80+
// during the context-switch.
81+
assert(old_task->flags == TASK_STATE_RUNNING);
82+
scheduler_put(old_task);
83+
assert(old_task->flags == TASK_STATE_RUNNABLE);
84+
85+
// Switch to the new task.
86+
task_switch(new_task);
87+
88+
// Make sure we came back all right.
89+
assert(get_hart_locals()->task == old_task);
90+
assert(old_task->flags == TASK_STATE_RUNNING);
91+
}
92+
93+
[[noreturn]]
94+
void task_exit() {
95+
TODO();
96+
}

0 commit comments

Comments
 (0)