From 3f2c142b64005a92bfaeb9ca66519ab6aba95325 Mon Sep 17 00:00:00 2001 From: vicLin8712 Date: Wed, 17 Sep 2025 15:30:25 +0800 Subject: [PATCH 1/3] Add sched_t to kcb for O(1) scheduler support Introduce sched_t structure to encapsulate O(1) scheduler state. sched_t maintains ready_queues separated by priority, enabling task selection in O(1) complexity. kcb now contains a sched_t instance, allowing per-hart scheduler control and future SMP extensions. --- include/sys/task.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/include/sys/task.h b/include/sys/task.h index 33d0b60..8b6843d 100644 --- a/include/sys/task.h +++ b/include/sys/task.h @@ -84,6 +84,28 @@ typedef struct tcb { void *rt_prio; /* Opaque pointer for custom real-time scheduler hook */ } tcb_t; +/* Scheduler attribution */ +typedef struct sched { + volatile uint32_t ready_bitmap; /* 8-bit priority bitmap */ + list_t + *ready_queues[TASK_PRIORITY_LEVELS]; /* Separate queue per priority */ + uint16_t queue_counts[TASK_PRIORITY_LEVELS]; /* O(1) size tracking */ + + /* Weighted Round-Robin State per Priority Level */ + list_node_t *rr_cursors[TASK_PRIORITY_LEVELS]; /* Round-robin position */ + uint32_t + quantum_cycles[TASK_PRIORITY_LEVELS]; /* Scheduling cycles per level */ + + /* Performance Optimization */ + uint8_t last_selected_prio; /* Cache hint for next selection */ + uint32_t local_switches; /* Context switch count */ + + /* Hart-Specific Data */ + tcb_t *current_task; /* Currently running task */ + uint8_t hart_id; /* RISC-V hart identifier */ + +} sched_t; + /* Kernel Control Block (KCB) * * Singleton structure holding global kernel state, including task lists, @@ -104,6 +126,9 @@ typedef struct { /* Timer Management */ list_t *timer_list; /* List of active software timers */ volatile uint32_t ticks; /* Global system tick, incremented by timer */ + + /* per-hart scheduler management */ + sched_t scheduler; } kcb_t; /* Global pointer to the singleton Kernel Control Block */ From 6f5e52841d428731d71a3c6f24a4fb5135dd5143 Mon Sep 17 00:00:00 2001 From: vicLin8712 Date: Thu, 18 Sep 2025 20:18:53 +0800 Subject: [PATCH 2/3] Implement task selection in O(1) time complexity New tasks created by `mo_task_spawn()` are enqueued into their priority ready queue. The very first task is special-cased: it becomes `task_current` and its state is set to `TASK_RUNNING` (not enqueued). `sched_select_next_task()` first returns the running task to its ready queue, then finds the highest non-empty priority by scanning the `ready_bitmap`, pops one node from that queue, and assigns it to `task_current`. The corresponding priority bit position in bitmap will be cleared when the queue becomes empty. This reduces selection from O(n) to O(1) and remains existing function calls. --- include/sys/task.h | 2 +- kernel/task.c | 80 ++++++++++++++++++++++++++++++---------------- 2 files changed, 54 insertions(+), 28 deletions(-) diff --git a/include/sys/task.h b/include/sys/task.h index 8b6843d..974a39e 100644 --- a/include/sys/task.h +++ b/include/sys/task.h @@ -128,7 +128,7 @@ typedef struct { volatile uint32_t ticks; /* Global system tick, incremented by timer */ /* per-hart scheduler management */ - sched_t scheduler; + sched_t *harts; } kcb_t; /* Global pointer to the singleton Kernel Control Block */ diff --git a/kernel/task.c b/kernel/task.c index 59ffdae..736e06b 100644 --- a/kernel/task.c +++ b/kernel/task.c @@ -15,6 +15,18 @@ static int32_t noop_rtsched(void); void _timer_tick_handler(void); +/* hart 0 scheduler */ +static sched_t hart0_sched = { + .ready_bitmap = 0, + .ready_queues = {NULL}, + .rr_cursors = {NULL}, + .quantum_cycles = {0}, + .last_selected_prio = 0, + .local_switches = 0, + .current_task = NULL, + .hart_id = 0, +}; + /* Kernel-wide control block (KCB) */ static kcb_t kernel_state = { .tasks = NULL, @@ -25,6 +37,7 @@ static kcb_t kernel_state = { .task_count = 0, .ticks = 0, .preemptive = true, /* Default to preemptive mode */ + .harts = &hart0_sched, }; kcb_t *kcb = &kernel_state; @@ -344,7 +357,6 @@ static void sched_enqueue_task(tcb_t *task) /* Ensure task has appropriate time slice for its priority */ task->time_slice = get_priority_timeslice(task->prio_level); task->state = TASK_READY; - /* Task selection is handled directly through the master task list */ } @@ -418,34 +430,32 @@ uint16_t sched_select_next_task(void) /* Mark current task as ready if it was running */ if (current_task->state == TASK_RUNNING) + { current_task->state = TASK_READY; + list_pushback(kcb->harts->ready_queues[current_task->prio_level], current_task); + kcb->harts->ready_bitmap |= (1U << current_task->prio_level); + } /* Round-robin search: find next ready task in the master task list */ - list_node_t *start_node = kcb->task_current; - list_node_t *node = start_node; - int iterations = 0; /* Safety counter to prevent infinite loops */ - - do { - /* Move to next task (circular) */ - node = list_cnext(kcb->tasks, node); - if (!node || !node->data) - continue; - - tcb_t *task = node->data; - - /* Skip non-ready tasks */ - if (task->state != TASK_READY) - continue; - - /* Found a ready task */ - kcb->task_current = node; - task->state = TASK_RUNNING; - task->time_slice = get_priority_timeslice(task->prio_level); - - return task->id; - - } while (node != start_node && ++iterations < SCHED_IMAX); + /* Find highest priority task queue */ + uint32_t bitmap = kcb->harts->ready_bitmap; + int highest_prio_level = 0; + for (;!(bitmap & 1U); highest_prio_level++, bitmap >>= 1); + + /* Pop out from corresponding queue and mark it as TASK_RUNNING */ + list_node_t *node = kcb->harts->ready_queues[highest_prio_level]->head->next; + list_pop(kcb->harts->ready_queues[highest_prio_level]); + ((tcb_t *)node->data)->state = TASK_RUNNING; + kcb->task_current = node; + + /* Check popped queue is empty or not */ + if (kcb->harts->ready_queues[highest_prio_level]->length == 0) + kcb->harts->ready_bitmap &= ~(1U << highest_prio_level); + + if (node) + return 1; + /* No ready tasks found - this should not happen in normal operation */ panic(ERR_NO_TASKS); return 0; @@ -603,6 +613,12 @@ int32_t mo_task_spawn(void *task_entry, uint16_t stack_size_req) } } + /* Create corresponding ready queue */ + if (!kcb->harts->ready_queues[tcb->prio_level]) { + kcb->harts->ready_queues[tcb->prio_level] = list_create(); + kcb->tasks = list_create(); + } + list_node_t *node = list_pushback(kcb->tasks, tcb); if (!node) { CRITICAL_LEAVE(); @@ -615,8 +631,16 @@ int32_t mo_task_spawn(void *task_entry, uint16_t stack_size_req) tcb->id = kcb->next_tid++; kcb->task_count++; /* Cached count of active tasks for quick access */ - if (!kcb->task_current) + /* If tcb is the first task, turn it into TASK_RUNNING state and does not put into ready queue */ + if (!kcb->task_current) { kcb->task_current = node; + tcb->state = TASK_RUNNING; + } + else { + /* Setup bitmap and put into corresponding ready queue */ + kcb->harts->ready_bitmap |= (1U << tcb->prio_level); + list_pushback(kcb->harts->ready_queues[tcb->prio_level], tcb); + } CRITICAL_LEAVE(); @@ -630,8 +654,10 @@ int32_t mo_task_spawn(void *task_entry, uint16_t stack_size_req) /* Add to cache and mark ready */ cache_task(tcb->id, tcb); - sched_enqueue_task(tcb); + /* Active task from TASK_STOPPED state */ + if (tcb->state == TASK_STOPPED) + sched_enqueue_task(tcb); return tcb->id; } From e43cc5a2e0a8ad81f32d3589357e401029f6627c Mon Sep 17 00:00:00 2001 From: vicLin8712 Date: Fri, 19 Sep 2025 17:12:57 +0800 Subject: [PATCH 3/3] Enable enqueue/dequeue from ready queues `sched_enqueue_task`/`sched_dequeue_task` also put/remove task from corresponding prior queue and check the length of queue after put/remove task to setup bitmap. bitmap marco used here for simple operation purpose, it can be modified for the further development. The enqueue and dequeue process in `mo_task_spawn`/`sched_select_next_task` are all replaced by `sched_enqueue_task` and `sched_dequeue_task`. --- include/sys/task.h | 8 +++++- kernel/task.c | 61 ++++++++++++++++++++++++++-------------------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/include/sys/task.h b/include/sys/task.h index 974a39e..b6e2af9 100644 --- a/include/sys/task.h +++ b/include/sys/task.h @@ -59,6 +59,12 @@ enum task_states { #define TASK_TIMESLICE_LOW 10 /* Low priority: longer slice */ #define TASK_TIMESLICE_IDLE 15 /* Idle tasks: longest slice */ +/* Bitmap operations */ +#define bitmap_check(prio) (kcb->harts->ready_bitmap & 1U << prio) +#define bitmap_set(prio) (kcb->harts->ready_bitmap |= 1U << prio) +#define bitmap_clean(prio) (kcb->harts->ready_bitmap &= ~(1U << prio)) + + /* Task Control Block (TCB) * * Contains all essential information about a single task, including saved @@ -86,7 +92,7 @@ typedef struct tcb { /* Scheduler attribution */ typedef struct sched { - volatile uint32_t ready_bitmap; /* 8-bit priority bitmap */ + uint32_t ready_bitmap; /* 8-bit priority bitmap */ list_t *ready_queues[TASK_PRIORITY_LEVELS]; /* Separate queue per priority */ uint16_t queue_counts[TASK_PRIORITY_LEVELS]; /* O(1) size tracking */ diff --git a/kernel/task.c b/kernel/task.c index 736e06b..1e8531a 100644 --- a/kernel/task.c +++ b/kernel/task.c @@ -357,7 +357,15 @@ static void sched_enqueue_task(tcb_t *task) /* Ensure task has appropriate time slice for its priority */ task->time_slice = get_priority_timeslice(task->prio_level); task->state = TASK_READY; - /* Task selection is handled directly through the master task list */ + + /* Push task into corresponding ready queue and setup bitmap */ + CRITICAL_ENTER(); + if (!kcb->harts->ready_queues[task->prio_level]) + kcb->harts->ready_queues[task->prio_level] = list_create(); + + list_pushback(kcb->harts->ready_queues[task->prio_level], task); + bitmap_set(task->prio_level); + CRITICAL_LEAVE(); } /* Remove task from ready queues - state-based approach for compatibility */ @@ -367,9 +375,20 @@ void sched_dequeue_task(tcb_t *task) return; /* For tasks that need to be removed from ready state (suspended/cancelled), - * we rely on the state change. The scheduler will skip non-ready tasks - * when it encounters them during the round-robin traversal. + * we rely on the state change. The scheduler will remove it from + * corresponding priority ready queue and setup bitmap by checking remaining + * task count in this queue. + * + * The state of task will be modified by `mo_task_suspended` or + * `mo_task_cancel`. */ + CRITICAL_ENTER(); + list_node_t *node = find_task_node_by_id(task->id); + list_remove(kcb->harts->ready_queues[task->prio_level], node); + + if (!kcb->harts->ready_queues[task->prio_level]->length) + bitmap_clean(task->prio_level); + CRITICAL_LEAVE(); } /* Handle time slice expiration for current task */ @@ -430,32 +449,30 @@ uint16_t sched_select_next_task(void) /* Mark current task as ready if it was running */ if (current_task->state == TASK_RUNNING) - { - current_task->state = TASK_READY; - list_pushback(kcb->harts->ready_queues[current_task->prio_level], current_task); - kcb->harts->ready_bitmap |= (1U << current_task->prio_level); - } + sched_enqueue_task(current_task); /* Round-robin search: find next ready task in the master task list */ /* Find highest priority task queue */ uint32_t bitmap = kcb->harts->ready_bitmap; int highest_prio_level = 0; - for (;!(bitmap & 1U); highest_prio_level++, bitmap >>= 1); + for (; !(bitmap & 1U); highest_prio_level++, bitmap >>= 1) + ; /* Pop out from corresponding queue and mark it as TASK_RUNNING */ - list_node_t *node = kcb->harts->ready_queues[highest_prio_level]->head->next; + list_node_t *node = + kcb->harts->ready_queues[highest_prio_level]->head->next; list_pop(kcb->harts->ready_queues[highest_prio_level]); - ((tcb_t *)node->data)->state = TASK_RUNNING; + ((tcb_t *) node->data)->state = TASK_RUNNING; kcb->task_current = node; /* Check popped queue is empty or not */ if (kcb->harts->ready_queues[highest_prio_level]->length == 0) - kcb->harts->ready_bitmap &= ~(1U << highest_prio_level); - + bitmap_clean(highest_prio_level); + if (node) - return 1; - + return ((tcb_t *) node->data)->id; + /* No ready tasks found - this should not happen in normal operation */ panic(ERR_NO_TASKS); return 0; @@ -613,11 +630,6 @@ int32_t mo_task_spawn(void *task_entry, uint16_t stack_size_req) } } - /* Create corresponding ready queue */ - if (!kcb->harts->ready_queues[tcb->prio_level]) { - kcb->harts->ready_queues[tcb->prio_level] = list_create(); - kcb->tasks = list_create(); - } list_node_t *node = list_pushback(kcb->tasks, tcb); if (!node) { @@ -631,16 +643,12 @@ int32_t mo_task_spawn(void *task_entry, uint16_t stack_size_req) tcb->id = kcb->next_tid++; kcb->task_count++; /* Cached count of active tasks for quick access */ - /* If tcb is the first task, turn it into TASK_RUNNING state and does not put into ready queue */ + /* If tcb is the first task, turn it into TASK_RUNNING state and does not + * put into ready queue */ if (!kcb->task_current) { kcb->task_current = node; tcb->state = TASK_RUNNING; } - else { - /* Setup bitmap and put into corresponding ready queue */ - kcb->harts->ready_bitmap |= (1U << tcb->prio_level); - list_pushback(kcb->harts->ready_queues[tcb->prio_level], tcb); - } CRITICAL_LEAVE(); @@ -747,7 +755,6 @@ int32_t mo_task_suspend(uint16_t id) CRITICAL_LEAVE(); return ERR_TASK_CANT_SUSPEND; } - task->state = TASK_SUSPENDED; bool is_current = (kcb->task_current == node);