|
miniOS
x86_64 hobby kernel with SMP, VFS, and POSIX process model
|
#include <miniOS/sched/sched.h>#include <miniOS/syscall.h>#include <miniOS/signal.h>#include <miniOS/fs/vfs.h>#include <miniOS/fs/pipe.h>#include <miniOS/net/unix_sock.h>#include <miniOS/mm/heap.h>#include <miniOS/mm/pmm.h>#include <miniOS/mm/vmm.h>#include <miniOS/arch/x86_64/tss.h>#include <miniOS/arch/x86_64/segment.h>#include <miniOS/arch/x86_64/smp.h>#include <miniOS/arch/x86_64/spinlock.h>#include <miniOS/io.h>#include <string.h>
Functions | |
| static uint64_t | sched_rdtsc (void) |
| void | per_cpu_update_rsp0 (uint64_t rsp0) |
| Update this CPU's TSS RSP0 and cpu_t.kstack_top. | |
| void | __attribute__ ((noreturn)) |
| void | sched_init (void) |
| struct thread * | sched_create_thread (thread_func_t func, void *arg) |
| struct thread * | sched_current (void) |
| struct thread * | sched_next (void) |
| bool | sched_has_user_threads (void) |
| void | sched_signal_thread (struct thread *t, int sig) |
| void | sched_stop_current (int stop_signal) |
| void | sched_ptrace_stop (int reason) |
| void | sched_wake_tid (uint32_t tid) |
| static void | sched_schedule (void) |
| void | sched_tick (void) |
| void | sched_yield (void) |
| struct thread * | sched_create_user_task (uint64_t entry_rip) |
| void | sched_activate_task (struct thread *t) |
|
extern |
| void sched_activate_task | ( | struct thread * | t | ) |
sched_activate_task() - Transfer "current" ownership to @t without context_switch_asm. @t: Thread to activate. Must currently be in THREAD_BLOCKED state.
Sets g_current->state = THREAD_RUNNABLE, then sets g_current = t and t->state = THREAD_RUNNING. Used by kernel_main before enter_ring3() so the user task is correctly identified as "current" when it issues its first syscall or triggers a preemptive switch back to the idle thread.


| struct thread * sched_create_thread | ( | thread_func_t | func, |
| void * | arg ) |
sched_create_thread() - Allocate and initialise a kernel thread. @func: Entry function pointer.
Scans thread_pool[] for the first slot with state == THREAD_DEAD (or unused). Allocates a 16 KiB kernel stack via kmalloc. Sets initial context: RIP=func, RSP=stack_top-8, RDI=arg, CS=0x08, SS=0x10, RFLAGS=0x202. Appends thread to run queue at tail (before idle) for round-robin fairness. Thread starts THREAD_BLOCKED; caller sets THREAD_RUNNABLE when ready.
sched_create_thread() - Create a new kernel thread. @func: Entry function; called with
Allocates a thread slot from thread_pool and a 16 KiB kernel stack via kmalloc. The thread is enqueued as THREAD_BLOCKED; the caller must set state = THREAD_RUNNABLE once the context is fully initialised to avoid a race with the LAPIC timer ISR.


sched_create_user_task() - Create a ring-3 user thread. @entry_rip: User-mode entry point virtual address.
Calls sched_create_thread() for the kernel scaffolding, then maps USER_STACK_PAGES (64) 4 KiB pages into the user virtual address space just below USER_STACK_TOP=0x7FFFFFFFE000 using vmm_map_page with PAGE_PRESENT|PAGE_WRITE|PAGE_USER flags. Stores the physical base of the first page in user_stack_phys_base for cleanup by sched_exit_current. Sets up TSS RSP0 via tss_set_rsp0() so syscall entries use the thread's kernel stack.
sched_create_user_task() - Create a new ring-3 user thread. @entry_rip: Virtual address of the user-mode entry point.
Allocates USER_STACK_PAGES (64) pages of user stack just below USER_STACK_TOP (0x7FFFFFFFE000), with an implicit guard page below. The thread starts at ring-3 with RIP=@entry_rip, RSP=USER_STACK_TOP-8, RFLAGS=0x202 (interrupts enabled).


| struct thread * sched_current | ( | void | ) |
sched_current() - Return pointer to the currently running thread.
Returns the global g_current pointer. Called from syscall handlers and context switch paths to identify the active task.
sched_current() - Return the currently running thread.


| bool sched_has_user_threads | ( | void | ) |
sched_has_user_threads() - Check if any non-idle thread is still alive.
Returns true if at least one thread slot with index > 0 (i.e., not the idle thread) has a state other than THREAD_DEAD. Used by bsp_idle_resume to decide whether to call qemu_exit or continue halting.


| void sched_init | ( | void | ) |
sched_init() - Initialise the scheduler.
Initialises thread_pool, sets up the run queue (circular singly-linked list with tail pointer), and captures the current boot context as the idle thread. Idle thread uses static storage (not kmalloc'd stack) and the boot stack. Sets g_current = &idle, state = THREAD_RUNNING.
sched_init() - Initialise the scheduler and create the idle thread.
Captures the current execution context (boot stack, boot code) as the idle thread. Must be called with interrupts disabled, before the first call to sched_tick().


| struct thread * sched_next | ( | void | ) |
sched_next() - Select the next RUNNABLE thread via round-robin.
Starts from current_thread->next and walks the circular run queue, returning the first thread in THREAD_RUNNABLE state. Starting after the current thread (not from run_queue_head) ensures fair round-robin: threads that arrive later in the queue are not starved by earlier threads that are frequently RUNNABLE (e.g. idle, net_poll busy-loop).
If no runnable thread is found after a full traversal, returns the idle thread. Does not modify current_thread.
sched_next() - Pick the next RUNNABLE thread (round-robin).
Walks the run queue to find the next thread in THREAD_RUNNABLE state. Falls back to the idle thread if no runnable thread is found. Does NOT perform the actual CPU context switch — that is done by context_switch_asm.


| void sched_ptrace_stop | ( | int | reason | ) |


|
inlinestatic |


|
static |


| void sched_signal_thread | ( | struct thread * | t, |
| int | sig ) |
sched_signal_thread() - Deliver a signal to a thread, waking it if blocked. @t: Target thread. @sig: Signal number (1-31).
Sets the pending bit and, if the thread is THREAD_WAITING, transitions it to THREAD_RUNNABLE so the scheduler will pick it up. This enables waitpid and other blocking waits to return -EINTR when signalled.


| void sched_stop_current | ( | int | stop_signal | ) |


| void sched_tick | ( | void | ) |
sched_tick() - LAPIC timer ISR callback; advance the scheduler.
Calls sched_schedule() to perform a context switch when the current thread's quantum expires (or immediately when a higher-priority thread becomes runnable). EOI is sent by the LAPIC timer ISR before calling this function; the context switch is safe.
Only the BSP (cpu_id 0) advances sched_tick_count and scans ITIMER_REAL deadlines — every CPU's LAPIC timer is independently calibrated to the same ~100Hz (confirmed at boot: "AP CPU N: LAPIC timer started, ICR=... (~100 Hz)"), so if every CPU incremented the same global counter it would advance N times faster than real time under N-CPU SMP, making any ITIMER_REAL-derived deadline (alarm(2)/setitimer(2) — e.g. busybox ping's -i interval pacing) fire N times too fast. The BSP's own tick alone is a correct ~100Hz wall-clock source regardless of core count; every other CPU still runs its own local preemption decision below unconditionally.
Context: Must only be called from the LAPIC timer ISR with interrupts disabled via the interrupt gate mechanism. Must not allocate.
sched_tick() - Advance the scheduler from the LAPIC timer ISR.
Increments the tick counter and calls the internal scheduler to switch to the next runnable thread when the quantum expires.
Context: Must only be called from the LAPIC timer ISR with interrupts disabled. Must not sleep or allocate memory.


| void sched_wake_tid | ( | uint32_t | tid | ) |


| void sched_yield | ( | void | ) |
sched_yield() - Voluntarily yield to the next runnable thread.
Disables interrupts around sched_schedule() to prevent the LAPIC timer from firing inside context_switch_asm during a cooperative yield.
Without this guard, the cooperative path (sched_yield from SYS_fork / sched_wait) runs with IF=1 (sti was issued in syscall_entry before syscall_dispatch). If a timer fires after sched_schedule sets current_thread=next but before context_switch_asm finishes restoring the new thread's registers, the nested sched_schedule saves wrong register values into next->ctx, corrupting the thread on re-schedule.
The preemptive path (sched_tick from the timer ISR) is always called with IF=0 (interrupt gates clear IF on entry), so it is not affected.
sched_yield() - Voluntarily yield the CPU to the next runnable thread.
Marks the calling thread as THREAD_RUNNABLE and invokes the internal scheduler to switch to the next thread immediately.

