Skip to content

Commit

Permalink
Implement task priority (#16)
Browse files Browse the repository at this point in the history
Finally, Resea Kernel become *policy-free* kernel. Currently this feature
is not yet used in the userspace, but it'll be a good example for educational
purposes.
  • Loading branch information
nuta committed Sep 10, 2020
1 parent a0a4acb commit d416c78
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 10 deletions.
17 changes: 17 additions & 0 deletions kernel/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,20 @@ static task_t sys_task_self(void) {
return CURRENT->tid;
}

/// Updates the scheduling policy for the task.
static error_t sys_task_schedule(task_t tid, int priority) {
if (!CAPABLE(CURRENT, CAP_TASK)) {
return ERR_NOT_PERMITTED;
}

struct task *task = task_lookup_unchecked(tid);
if (!task || task == CURRENT) {
return ERR_INVALID_TASK;
}

return task_schedule(task, priority);
}

/// Send/receive IPC messages.
static error_t sys_ipc(task_t dst, task_t src, __user struct message *m,
unsigned flags) {
Expand Down Expand Up @@ -307,6 +321,9 @@ long handle_syscall(int n, long a1, long a2, long a3, long a4, long a5) {
case SYS_TASK_SELF:
ret = sys_task_self();
break;
case SYS_TASK_SCHEDULE:
ret = sys_task_schedule(a1, a2);
break;
case SYS_VM_MAP:
ret = sys_vm_map(a1, a2, a3, a4, a5);
break;
Expand Down
51 changes: 42 additions & 9 deletions kernel/task.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@

/// All tasks.
static struct task tasks[CONFIG_NUM_TASKS];
/// A queue of runnable tasks excluding currently running tasks.
static list_t runqueue;
/// A queue of runnable tasks excluding currently running tasks. Lower index
/// means higher priority.
static list_t runqueues[TASK_PRIORITY_MAX];
/// IRQ owners.
static struct task *irq_owners[IRQ_MAX];

Expand All @@ -26,6 +27,10 @@ static struct task *irq_owners[IRQ_MAX];
static int page_usages[CONFIG_MAX_MAPPABLE_ADDR / PAGE_SIZE];
#endif

static void enqueue_task(struct task *task) {
list_push_back(&runqueues[task->priority], &task->runqueue_next);
}

/// Returns the task struct for the task ID. It returns NULL if the ID is
/// invalid.
struct task *task_lookup_unchecked(task_t tid) {
Expand Down Expand Up @@ -83,6 +88,7 @@ error_t task_create(struct task *task, const char *name, vaddr_t ip,
task->src = IPC_DENY;
task->timeout = 0;
task->quantum = 0;
task->priority = TASK_PRIORITY_MAX - 1;
task->ref_count = 0;
bitmap_fill(task->caps, sizeof(task->caps), (flags & TASK_ALL_CAPS) != 0);
strncpy(task->name, name, sizeof(task->name));
Expand All @@ -95,7 +101,7 @@ error_t task_create(struct task *task, const char *name, vaddr_t ip,
}

// Append the newly created task into the runqueue.
if (task != IDLE_TASK) {
if (task != IDLE_TASK && ((flags & TASK_SCHED) == 0)) {
task_resume(task);
}

Expand Down Expand Up @@ -183,19 +189,43 @@ void task_block(struct task *task) {
void task_resume(struct task *task) {
DEBUG_ASSERT(task->state == TASK_BLOCKED);
task->state = TASK_RUNNABLE;
list_push_back(&runqueue, &task->runqueue_next);
enqueue_task(task);
mp_reschedule();
}

/// Updates the scheduling policy for the task.
error_t task_schedule(struct task *task, int priority) {
if (priority >= TASK_PRIORITY_MAX) {
return ERR_INVALID_ARG;
}

task->priority = priority;
if (task->state == TASK_RUNNABLE) {
list_remove(&task->runqueue_next);
enqueue_task(task);
}

return OK;
}

/// Picks the next task to run.
static struct task *scheduler(struct task *current) {
if (current != IDLE_TASK && current->state == TASK_RUNNABLE) {
// The current task is still runnable. Enqueue into the runqueue.
list_push_back(&runqueue, &current->runqueue_next);
enqueue_task(current);
}

struct task *next = LIST_POP_FRONT(&runqueue, struct task, runqueue_next);
return (next) ? next : IDLE_TASK;
// Look for the task with the highest priority. Tasks with the same priority
// is scheduled in round-robin fashion.
for (int i = 0; i < TASK_PRIORITY_MAX; i++) {
struct task *next =
LIST_POP_FRONT(&runqueues[i], struct task, runqueue_next);
if (next) {
return next;
}
}

return IDLE_TASK;
}

/// Do a context switch: save the current register state on the stack and
Expand Down Expand Up @@ -352,7 +382,7 @@ void handle_timer_irq(void) {
// Switch task if the current task has spend its time slice.
DEBUG_ASSERT(CURRENT == IDLE_TASK || CURRENT->quantum > 0);
CURRENT->quantum--;
if (!CURRENT->quantum || (CURRENT == IDLE_TASK && !list_is_empty(&runqueue))) {
if (!CURRENT->quantum || CURRENT == IDLE_TASK) {
task_switch();
}
}
Expand Down Expand Up @@ -409,7 +439,10 @@ void task_dump(void) {

/// Initializes the task subsystem.
void task_init(void) {
list_init(&runqueue);
for (int i = 0; i < TASK_PRIORITY_MAX; i++) {
list_init(&runqueues[i]);
}

for (int i = 0; i < CONFIG_NUM_TASKS; i++) {
tasks[i].state = TASK_UNUSED;
tasks[i].tid = i + 1;
Expand Down
9 changes: 9 additions & 0 deletions kernel/task.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ STATIC_ASSERT(TASK_TIME_SLICE > 0);
/// The task is waiting for a receiver/sender task in IPC.
#define TASK_BLOCKED 2

#define TASK_PRIORITY_MAX 8
STATIC_ASSERT(TASK_PRIORITY_MAX > 0);

// struct arch_cpuvar *
#define ARCH_CPUVAR (&get_cpuvar()->arch)

Expand Down Expand Up @@ -62,6 +65,11 @@ struct task {
/// The remaining time slice in ticks. If this value reaches 0, the kernel
/// switches into the next task (so-called preemptive context switching).
unsigned quantum;
/// The task priority. The lower value means higher priority. The scheduler
/// always picks the runnable task with the highest priority. If there're
/// multiple runnable tasks with the same highest priority, the kernel
/// schedules in round-robin fashion.
int priority;
/// The message buffer.
struct message m;
/// The acceptable sender task ID. If it's IPC_ANY, the task accepts
Expand Down Expand Up @@ -98,6 +106,7 @@ __mustuse error_t task_destroy(struct task *task);
__noreturn void task_exit(enum exception_type exp);
void task_block(struct task *task);
void task_resume(struct task *task);
error_t task_schedule(struct task *task, int priority);
struct task *task_lookup(task_t tid);
struct task *task_lookup_unchecked(task_t tid);
void task_switch(void);
Expand Down
3 changes: 2 additions & 1 deletion libs/common/include/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ typedef int error_t;
#define SYS_TASK_DESTROY 9
#define SYS_TASK_EXIT 10
#define SYS_TASK_SELF 11
// 12 is reserved for SYS_TASK_SCHEDULE
#define SYS_TASK_SCHEDULE 12
#define SYS_VM_MAP 13
#define SYS_VM_UNMAP 14
#define SYS_IRQ_ACQUIRE 15
Expand All @@ -103,6 +103,7 @@ typedef int error_t;
// Task flags.
#define TASK_ALL_CAPS (1 << 0)
#define TASK_ABI_EMU (1 << 1)
#define TASK_SCHED (1 << 2)

// Map flags.
#define MAP_W (1 << 1)
Expand Down
4 changes: 4 additions & 0 deletions libs/resea/include/resea/syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ static inline task_t sys_task_self(void) {
return syscall(SYS_TASK_SELF, 0, 0, 0, 0, 0);
}

static inline error_t sys_task_schedule(task_t task, int priority) {
return syscall(SYS_TASK_SCHEDULE, task, priority, 0, 0, 0);
}

static inline error_t sys_vm_map(task_t task, vaddr_t vaddr, vaddr_t src,
vaddr_t kpage, unsigned flags) {
return syscall(SYS_VM_MAP, task, vaddr, src, kpage, flags);
Expand Down
1 change: 1 addition & 0 deletions libs/resea/include/resea/task.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ task_t task_self(void);
error_t task_map(task_t task, vaddr_t vaddr, vaddr_t src, vaddr_t kpage,
unsigned flags);
error_t task_unmap(task_t task, vaddr_t vaddr);
error_t task_schedule(task_t task, int priority);

#endif
4 changes: 4 additions & 0 deletions libs/resea/task.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ error_t task_map(task_t task, vaddr_t vaddr, vaddr_t src, vaddr_t kpage,
error_t task_unmap(task_t task, vaddr_t vaddr) {
return sys_vm_unmap(task, vaddr);
}

error_t task_schedule(task_t task, int priority) {
return sys_task_schedule(task, priority);
}

0 comments on commit d416c78

Please sign in to comment.