diff --git a/arch/at91sam3x8e/sched.c b/arch/at91sam3x8e/sched.c index 5fe72ba..053770d 100644 --- a/arch/at91sam3x8e/sched.c +++ b/arch/at91sam3x8e/sched.c @@ -17,6 +17,7 @@ void irq_sys_tick(void) * fire a PendSV interrupt and do the actual context switching there * because it is faster that way (according to the docs, at least) */ + if (!_is_atomic_context) arch_irq_invoke(IRQNO_PEND_SV); } diff --git a/include/arch/at91sam3x8e/sched.h b/include/arch/at91sam3x8e/sched.h index df45310..32761b4 100644 --- a/include/arch/at91sam3x8e/sched.h +++ b/include/arch/at91sam3x8e/sched.h @@ -3,19 +3,22 @@ #pragma once +#include #include + +#include #include /** Enter atomic context, i.e. disable preemption */ __always_inline void sched_atomic_enter(void) { - arch_irq_disable(IRQNO_PEND_SV); + _is_atomic_context = true; } /** Leave atomic context, i.e. re-enable preemption */ __always_inline void sched_atomic_leave(void) { - arch_irq_enable(IRQNO_PEND_SV); + _is_atomic_context = false; } /* diff --git a/include/ardix/sched.h b/include/ardix/sched.h index aa1d4a4..5bd5654 100644 --- a/include/ardix/sched.h +++ b/include/ardix/sched.h @@ -3,9 +3,12 @@ #pragma once +#include #include #include +#include + #ifndef CONFIG_SCHED_MAXPROC /** The maximum number of processes. */ #define CONFIG_SCHED_MAXPROC 8 @@ -40,8 +43,6 @@ enum proc_state { /** Stores an entire process image. */ struct process { - /** Next process in the (circular) list. */ - struct process *next; /** Stack pointer. */ void *sp; /** Bottom of the stack (i.e. the highest address). */ @@ -59,6 +60,8 @@ struct process { /** The currently executing process. */ extern struct process *_current_process; +extern bool _is_atomic_context; + /** * Initialize the scheduler subsystem. * This sets up a hardware interrupt timer (SysTick for Cortex-M3). @@ -102,38 +105,6 @@ struct process *sched_process_create(void (*entry)(void)); */ void sched_switch_early(enum proc_state state); -/** - * Suspend the current process for the specified amount of milliseconds. - * Note that there are slight deviations from this time interval because of the - * round-robin scheduling algorithm. - * If the sleep time is required to be exactly accurate, use `atomic_udelay()`. - * Note, however, that this will block *all* other processes, even including - * I/O, for the entire time period. - * - * @param msecs: The amount of milliseconds to (approximately) sleep for. - */ -void msleep(unsigned long int msecs); - -/** - * Block the entire CPU from execution for the specified amount of microseconds. - * Note that this will temporarily disable the scheduler, meaning that *nothing* - * (not even I/O) will be executed. The only reason you would ever want to use - * this is for mission-critical, very short (<= 100 us) periods of time. - * - * @param usecs: The amount of microseconds to halt the CPU for. - */ -void atomic_udelay(unsigned long int usecs); - -/** - * Attempt to acquire an atomic lock. - * - * @param mutex: The pointer to the mutex. - * @returns `0` if the lock could be acquired, and `-EAGAIN` if not. - */ -int atomic_lock(atomic_t *mutex); - -void atomic_unlock(atomic_t *mutex); - /* * Copyright (c) 2020 Felix Kopp * diff --git a/kernel/sched.c b/kernel/sched.c index d7e35bd..60d6778 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -3,45 +3,42 @@ #include #include + +#include +#include #include #include #include + +#include #include extern uint32_t _sstack; extern uint32_t _estack; +struct process *proc_table[CONFIG_SCHED_MAXPROC]; struct process *_current_process; -/** - * An array of all processes. - * The `pid` not only identifies each process, it is also the index of the - * struct in this array. Unused slots have a `pid` of `-1`, however. - */ -static struct process procs[CONFIG_SCHED_MAXPROC]; +bool _is_atomic_context = false; int sched_init(void) { int i; - _current_process = &procs[0]; - _current_process->next = _current_process; + _current_process = malloc(sizeof(*_current_process)); + if (_current_process == NULL) + return -ENOMEM; + _current_process->sp = &_sstack; _current_process->stack_bottom = &_estack; _current_process->pid = 0; _current_process->state = PROC_READY; + proc_table[0] = _current_process; - for (i = 1; i < CONFIG_SCHED_MAXPROC; i++) { - procs[i].next = NULL; - procs[i].sp = NULL; - procs[i].stack_bottom = &_estack - (CONFIG_STACKSZ * (unsigned int)i); - procs[i].pid = -1; - procs[i].state = PROC_DEAD; - } + for (i = 1; i < CONFIG_SCHED_MAXPROC; i++) + proc_table[i] = NULL; - i = arch_sched_hwtimer_init(CONFIG_SCHED_INTR_FREQ); - - return i; + return arch_sched_hwtimer_init(CONFIG_SCHED_INTR_FREQ); } /** @@ -62,67 +59,58 @@ static inline bool sched_proc_should_run(const struct process *proc) void *sched_process_switch(void *curr_sp) { - struct process *nextproc = _current_process; + pid_t nextpid = _current_process->pid; _current_process->sp = curr_sp; - if (_current_process->state != PROC_SLEEP) + if (_current_process->state != PROC_SLEEP && _current_process->state != PROC_IOWAIT) _current_process->state = PROC_QUEUE; - while (true) { - nextproc = nextproc->next; - if (sched_proc_should_run(nextproc)) { - nextproc->state = PROC_READY; - _current_process = nextproc; + while (1) { + nextpid++; + nextpid %= CONFIG_SCHED_MAXPROC; + if (proc_table[nextpid] != NULL && proc_table[nextpid]->state == PROC_QUEUE) { + _current_process = proc_table[nextpid]; break; } - - /* TODO: Let the CPU sleep if there is nothing to do */ + /* TODO: Add idle thread */ } + _current_process = proc_table[nextpid]; + _current_process->state = PROC_READY; return _current_process->sp; } -/** - * Find an unused process slot in the `procs` array, insert that process into - * the scheduler's ring queue and return it. Must run in atomic context. - * - * @returns A pointer to the new process slot, or `NULL` if none are available. - */ -static struct process *proclist_find_free_slot_and_link(void) -{ - pid_t i; - struct process *newproc = NULL; - - /* PID 0 is always reserved for the Kernel process, so start counting from 1 */ - for (i = 1; i < CONFIG_SCHED_MAXPROC; i++) { - if (procs[i].pid == -1 && procs[i].state == PROC_DEAD) { - newproc = &procs[i]; - newproc->next = procs[i - 1].next; - procs[i - 1].next = newproc; - newproc->pid = i; - break; - } - } - - return newproc; -} - struct process *sched_process_create(void (*entry)(void)) { - struct process *proc; + pid_t pid; + struct process *proc = malloc(sizeof(*proc)); + if (proc == NULL) + return NULL; sched_atomic_enter(); - proc = proclist_find_free_slot_and_link(); - if (proc != NULL) { - proc->sp = proc->stack_bottom; - proc->lastexec = 0; - proc->sleep_usecs = 0; - proc->state = PROC_QUEUE; - - arch_sched_process_init(proc, entry); + for (pid = 1; pid < CONFIG_SCHED_MAXPROC; pid++) { + if (proc_table[pid] == NULL) + break; } + if (pid == CONFIG_SCHED_MAXPROC) { + /* max number of processess exceeded */ + free(proc); + sched_atomic_leave(); + return NULL; + } + + proc->pid = pid; + proc->stack_bottom = &_estack - (pid * CONFIG_STACKSZ); + proc->lastexec = 0; + proc->sleep_usecs = 0; + proc->state = PROC_QUEUE; + + arch_sched_process_init(proc, entry); + + proc_table[pid] = proc; + sched_atomic_leave(); return proc;