sched: refactor and implement sleep

io-wait
anna 3 years ago
parent 0e6d0057a8
commit 6e269e0217
Signed by: fef
GPG Key ID: EC22E476DC2D3D84

@ -15,7 +15,7 @@ void atomic_leave(void)
atom_put(&atomic_context); atom_put(&atomic_context);
} }
int is_atomic_context(void) int is_atomic(void)
{ {
return atom_count(&atomic_context); return atom_count(&atomic_context);
} }

@ -3,8 +3,9 @@
#include <arch-generic/entry.h> #include <arch-generic/entry.h>
#include <arch/hardware.h> #include <arch/hardware.h>
#include <ardix/syscall.h>
#include <ardix/types.h> #include <ardix/types.h>
#include <ardix/sched.h>
#include <ardix/syscall.h>
#include <errno.h> #include <errno.h>
#include <stddef.h> #include <stddef.h>
@ -16,7 +17,7 @@
extern uint16_t __syscall_return_point; extern uint16_t __syscall_return_point;
#endif #endif
void arch_enter(void *sp) int arch_enter(void *sp)
{ {
struct reg_snapshot *regs = sp; struct reg_snapshot *regs = sp;
enum syscall sc_num = arch_syscall_num(regs); enum syscall sc_num = arch_syscall_num(regs);
@ -39,13 +40,13 @@ void arch_enter(void *sp)
if (sc_num > NSYSCALLS) { if (sc_num > NSYSCALLS) {
arch_syscall_set_rval(regs, -ENOSYS); arch_syscall_set_rval(regs, -ENOSYS);
return; return 0;
} }
handler = sys_table[sc_num]; handler = sys_table[sc_num];
if (handler == NULL) { if (handler == NULL) {
arch_syscall_set_rval(regs, -ENOSYS); arch_syscall_set_rval(regs, -ENOSYS);
return; return 0;
} }
/* TODO: not every syscall uses the max amount of parameters (duh) */ /* TODO: not every syscall uses the max amount of parameters (duh) */
@ -53,6 +54,9 @@ void arch_enter(void *sp)
arch_syscall_arg4(regs), arch_syscall_arg5(regs), arch_syscall_arg6(regs)); arch_syscall_arg4(regs), arch_syscall_arg5(regs), arch_syscall_arg6(regs));
arch_syscall_set_rval(regs, sc_ret); arch_syscall_set_rval(regs, sc_ret);
int ret = need_resched;
need_resched = 0;
return ret;
} }
/* /*

@ -4,7 +4,8 @@
.text .text
.extern sched_process_switch /* void *sched_switch(void *curr_sp); */
.extern sched_switch
/* void handle_pend_sv(void); */ /* void handle_pend_sv(void); */
func_begin handle_pend_sv func_begin handle_pend_sv
@ -27,7 +28,7 @@ func_begin handle_pend_sv
*/ */
/* TODO: Implement banked stack pointer */ /* TODO: Implement banked stack pointer */
mov r0, sp mov r0, sp
bl sched_process_switch /* sp = sched_process_switch(sp); */ bl sched_switch /* sp = sched_switch(sp); */
mov sp, r0 mov sp, r0
/* /*

@ -4,8 +4,12 @@
.text .text
/* int arch_enter(void *sp); */
.extern arch_enter .extern arch_enter
/* void *sched_switch(void *sp); */
.extern sched_switch
/* void handle_svc(void); */ /* void handle_svc(void); */
func_begin handle_svc func_begin handle_svc
/* /*
@ -28,8 +32,18 @@ func_begin handle_svc
push {r4-r11,lr} push {r4-r11,lr}
mov r0, sp mov r0, sp
bl arch_enter /* arch_enter(sp); */ bl arch_enter /* int need_resched = arch_enter(sp); */
cmp r0, #0
beq svc_out
mov r0, sp
bl sched_switch /* sp = sched_switch(sp); */
mov sp, r0
clrex
svc_out:
pop {r4-r11,lr} pop {r4-r11,lr}
bx lr bx lr

@ -11,13 +11,18 @@
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
volatile unsigned long int tick = 0;
unsigned int systick_reload;
void handle_sys_tick(void) void handle_sys_tick(void)
{ {
tick++;
/* /*
* fire a PendSV exception and do the actual context switching there * fire a PendSV exception and do the actual context switching there
* because the docs say you're supposed to do it that way * because the docs say you're supposed to do it that way
*/ */
if (!is_atomic_context()) if (!is_atomic())
arch_irq_invoke(IRQNO_PEND_SV); arch_irq_invoke(IRQNO_PEND_SV);
} }
@ -41,14 +46,14 @@ static inline void sched_nvic_set_prio_group(uint32_t prio_group)
int arch_sched_hwtimer_init(unsigned int freq) int arch_sched_hwtimer_init(unsigned int freq)
{ {
uint32_t ticks = sys_core_clock / freq; systick_reload = sys_core_clock / freq;
if (ticks > REG_SYSTICK_LOAD_RELOAD_MASK) if (systick_reload > REG_SYSTICK_LOAD_RELOAD_MASK)
return 1; return 1;
/* Ensure SysTick and PendSV are preemptive */ /* Ensure SysTick and PendSV are preemptive */
sched_nvic_set_prio_group(0b011); sched_nvic_set_prio_group(0b011);
REG_SYSTICK_LOAD = (ticks & REG_SYSTICK_LOAD_RELOAD_MASK) - 1; REG_SYSTICK_LOAD = (systick_reload & REG_SYSTICK_LOAD_RELOAD_MASK) - 1;
REG_SYSTICK_VAL = 0U; REG_SYSTICK_VAL = 0U;
REG_SYSTICK_CTRL = REG_SYSTICK_CTRL_CLKSOURCE_BIT /* MCK */ REG_SYSTICK_CTRL = REG_SYSTICK_CTRL_CLKSOURCE_BIT /* MCK */
| REG_SYSTICK_CTRL_TICKINT_BIT /* trigger exception */ | REG_SYSTICK_CTRL_TICKINT_BIT /* trigger exception */
@ -70,7 +75,6 @@ void arch_task_init(struct task *task, void (*entry)(void))
void yield(enum task_state state) void yield(enum task_state state)
{ {
REG_SYSTICK_VAL = 0U; /* Reset timer (TODO: don't do this lmao) */
current->state = state; current->state = state;
arch_irq_invoke(IRQNO_PEND_SV); arch_irq_invoke(IRQNO_PEND_SV);
} }
@ -91,13 +95,18 @@ int arch_idle_task_init(struct task *task)
task->stack_bottom = sp + sizeof(struct reg_snapshot); task->stack_bottom = sp + sizeof(struct reg_snapshot);
arch_task_init(task, idle_task_entry); arch_task_init(task, idle_task_entry);
task->lastexec = 0; task->sleep = 0;
task->sleep_usecs = 0; task->last_tick = 0;
task->state = TASK_READY; task->state = TASK_READY;
task->pid = -1; task->pid = -1;
return 0; return 0;
} }
unsigned long int ms_to_ticks(unsigned long int ms)
{
return (unsigned long int)systick_reload * ms / sys_core_clock;
}
/* /*
* This file is part of Ardix. * This file is part of Ardix.
* Copyright (c) 2020, 2021 Felix Kopp <owo@fef.moe>. * Copyright (c) 2020, 2021 Felix Kopp <owo@fef.moe>.

@ -27,6 +27,14 @@ void arch_task_init(struct task *task, void (*entry)(void));
int arch_idle_task_init(struct task *task); int arch_idle_task_init(struct task *task);
/**
* @brief Convert milliseconds to system ticks, rounding to zero.
*
* @param ms Amount of milliseconds
* @returns Equivalent time in system ticks
*/
unsigned long int ms_to_ticks(unsigned long ms);
/* /*
* This file is part of Ardix. * This file is part of Ardix.
* Copyright (c) 2020, 2021 Felix Kopp <owo@fef.moe>. * Copyright (c) 2020, 2021 Felix Kopp <owo@fef.moe>.

@ -15,7 +15,7 @@ void atomic_enter(void);
void atomic_leave(void); void atomic_leave(void);
/** Return a nonzero value if the current process is in atomic context. */ /** Return a nonzero value if the current process is in atomic context. */
int is_atomic_context(void); int is_atomic(void);
/* /*
* This file is part of Ardix. * This file is part of Ardix.

@ -12,11 +12,6 @@
#warning "CONFIG_SCHED_MAXTASK is > 64, this could have a significant performance impact" #warning "CONFIG_SCHED_MAXTASK is > 64, this could have a significant performance impact"
#endif #endif
#ifndef CONFIG_STACKSZ
/** Per-task stack size in bytes */
#define CONFIG_STACKSZ 4096U
#endif
enum task_state { enum task_state {
/** Task is dead / doesn't exist */ /** Task is dead / doesn't exist */
TASK_DEAD, TASK_DEAD,
@ -24,60 +19,84 @@ enum task_state {
TASK_READY, TASK_READY,
/** Task is waiting for its next time share. */ /** Task is waiting for its next time share. */
TASK_QUEUE, TASK_QUEUE,
/** Task is sleeping, `sleep_until` specifies when to wake it up. */ /** Task is sleeping, `task::sleep` specifies for how many ticks. */
TASK_SLEEP, TASK_SLEEP,
/** Task is waiting for I/O to flush buffers. */ /** Task is waiting for I/O to flush buffers. */
TASK_IOWAIT, TASK_IOWAIT,
}; };
/** Stores an entire process image. */ /** @brief Core structure holding information about a task. */
struct task { struct task {
struct kent kent; struct kent kent;
/** current stack pointer (only gets updated for task switching) */ /** current stack pointer (only gets updated for task switching) */
void *sp; void *sp;
/** first address of the stack (highest if the stack grows downwards) */ /** first address of the stack (highest if the stack grows downwards) */
void *stack_bottom; void *stack_bottom;
/** if `state` is `TASK_SLEEP`, the last execution time */ /** @brief If state is `TASK_SLEEP`, the total amount of ticks to sleep */
unsigned long int lastexec; unsigned long int sleep;
/** if `state` is `TASK_SLEEP`, the amount of us to sleep in total */ /** @brief Last execution in ticks */
unsigned long int sleep_usecs; unsigned long int last_tick;
enum task_state state; enum task_state state;
pid_t pid; pid_t pid;
}; };
/** @brief Current task (access from syscall context only) */
extern struct task *current; extern struct task *current;
/** @brief Global system tick counter (may overflow) */
extern volatile unsigned long int tick;
/**
* @brief If nonzero, the scheduler is invoked after the current syscall.
* This is checked and then cleared after every syscall. If it has a nonzero
* value, `sched_switch()` is called after `arch_enter()`.
*/
extern int need_resched;
/** /**
* Initialize the scheduler subsystem. * @brief Initialize the scheduler subsystem.
* This sets up a hardware interrupt timer (SysTick for Cortex-M3). * This sets up a hardware interrupt timer (SysTick for Cortex-M3).
*/ */
int sched_init(void); int sched_init(void);
/** /**
* Switch to the next task (interrupt context only). * @brief Switch to the next task (scheduler context only).
* Must be called directly from within an interrupt routine. * Must be called directly from within an interrupt routine.
* This selects a new task to be run and updates the old and new task's `state` * This selects a new task to be run and updates the old and new task's `state`
* field to the appropriate value. * field to the appropriate value. Called from the scheduler exception handler.
* *
* @param curr_sp: stack pointer of the current task * @param curr_sp Stack pointer of the current task
* @returns stack pointer of the new task * @returns Stack pointer of the new task
*/ */
void *sched_switch(void *curr_sp); void *sched_switch(void *curr_sp);
/** /**
* Create a copy of the current process image and return it. * @brief Create a copy of the `current` task and return it.
* The new task becomes a child of the `current` task and is inserted into the
* process table so that it can be executed by the scheduler after its state
* is set to `TASK_QUEUE`. When the task is returned, its initial state is
* `TASK_UNKNOWN` so that the caller has time to do any additional required
* setup work.
*
* @param task Task to make a copy of
* @returns The new (child) task copy, or `NULL` on failure
*/
struct task *task_clone(struct task *task);
/**
* @brief Sleep for an approximate amount of milliseconds.
* Must not be invoked from atomic or irq context.
* *
* @param task: the task to make a copy of * @param ms Amount of milliseconds
* @returns the new (child) task copy, or `NULL` on failure
*/ */
struct task *sched_task_clone(struct task *task); void msleep(unsigned long int ms);
/** /**
* Request the scheduler be invoked early, resulting in the current task to * @brief Suspend the `current` task and invoke the scheduler early.
* be suspended. * May only be called from syscall context.
* *
* @param state: State the task should enter. * @param state State the task should enter.
* Allowed values are `TASK_SLEEP` and `TASK_IOWAIT`. * Allowed values are `TASK_SLEEP` and `TASK_IOWAIT`.
*/ */
void yield(enum task_state state); void yield(enum task_state state);

@ -53,6 +53,10 @@
/** Function attribute for hinting this function has malloc-like behavior. */ /** Function attribute for hinting this function has malloc-like behavior. */
#define __malloc(deallocator, argn) __attribute__(( malloc )) #define __malloc(deallocator, argn) __attribute__(( malloc ))
#define __preinit_call(fn) __section(.preinit_array) void (*fn##_ptr)(void) = fn
#define __init_call(fn) __section(.init_array) void (*fn##_ptr)(void) = fn
/* /*
* This file is part of Ardix. * This file is part of Ardix.
* Copyright (c) 2020, 2021 Felix Kopp <owo@fef.moe>. * Copyright (c) 2020, 2021 Felix Kopp <owo@fef.moe>.

@ -21,6 +21,8 @@ struct task *current;
static struct task idle_task; static struct task idle_task;
int need_resched = 0;
static void task_destroy(struct kent *kent) static void task_destroy(struct kent *kent)
{ {
struct task *task = container_of(kent, struct task, kent); struct task *task = container_of(kent, struct task, kent);
@ -71,31 +73,46 @@ out:
return i; return i;
} }
#include <arch/debug.h>
/** /**
* Determine whether the specified task is a candidate for execution. * @brief Determine whether the specified task is a candidate for execution.
* *
* @param task: the task * @param task The task
* @returns whether `task` could be run next * @returns whether `task` could be run next
*/ */
static inline bool can_run(const struct task *task) static inline bool can_run(const struct task *task)
{ {
enum task_state state = task->state; switch (task->state) {
return state == TASK_QUEUE || state == TASK_READY; case TASK_SLEEP:
return tick - task->last_tick > task->sleep;
case TASK_QUEUE:
case TASK_READY:
return true;
case TASK_DEAD:
case TASK_IOWAIT:
return false;
}
return false; /* this shouldn't be reached */
} }
void *sched_process_switch(void *curr_sp) void *sched_switch(void *curr_sp)
{ {
struct task *tmp; struct task *tmp;
int i; int i;
pid_t nextpid = current->pid; pid_t nextpid = current->pid;
current->sp = curr_sp; current->sp = curr_sp;
//__breakpoint;
kevents_process(); kevents_process();
if (current->state != TASK_SLEEP && current->state != TASK_IOWAIT) if (current->state != TASK_SLEEP && current->state != TASK_IOWAIT)
current->state = TASK_QUEUE; current->state = TASK_QUEUE;
for (i = 0; i < CONFIG_SCHED_MAXTASK; i++) { for (i = 0; i < CONFIG_SCHED_MAXTASK; i++) {
//__breakpoint;
nextpid++; nextpid++;
nextpid %= CONFIG_SCHED_MAXTASK; nextpid %= CONFIG_SCHED_MAXTASK;
@ -110,6 +127,8 @@ void *sched_process_switch(void *curr_sp)
current = &idle_task; current = &idle_task;
current->state = TASK_READY; current->state = TASK_READY;
current->last_tick = tick;
//__breakpoint;
return current->sp; return current->sp;
} }
@ -143,6 +162,14 @@ err_alloc:
return NULL; return NULL;
} }
void msleep(unsigned long int ms)
{
//__breakpoint;
current->sleep = ms_to_ticks(ms);
yield(TASK_SLEEP);
//__breakpoint;
}
/* /*
* This file is part of Ardix. * This file is part of Ardix.
* Copyright (c) 2020, 2021 Felix Kopp <owo@fef.moe>. * Copyright (c) 2020, 2021 Felix Kopp <owo@fef.moe>.

Loading…
Cancel
Save