miniOS
x86_64 hobby kernel with SMP, VFS, and POSIX process model
Loading...
Searching...
No Matches
sched.c File Reference
Include dependency graph for sched.c:

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)

Function Documentation

◆ __attribute__()

void __attribute__ ( (noreturn) )
extern

◆ sched_activate_task()

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.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ sched_create_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.

  • : Argument passed to @func in RDI (first parameter register).

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.

Returns
: Thread pointer, or NULL on OOM (kmalloc failed or pool exhausted).

sched_create_thread() - Create a new kernel thread. @func: Entry function; called with

  • as its sole argument.
  • : Opaque argument forwarded to @func.

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.

Returns
: Pointer to the new thread on success, NULL on OOM.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ sched_create_user_task()

struct thread * sched_create_user_task ( uint64_t entry_rip)

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.

Returns
: Pointer to the new user thread, or NULL on OOM.

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).

Returns
: Pointer to the new thread on success, NULL on OOM.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ sched_current()

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.

Returns
: Pointer to the current thread (never NULL after sched_init).

sched_current() - Return the currently running thread.

Returns
: Pointer to the thread currently executing on the CPU.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ sched_has_user_threads()

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.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ sched_init()

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().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ sched_next()

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.

Returns
: Next thread to run (never NULL; falls back to idle).

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.

Returns
: Pointer to the next thread to run (never NULL).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ sched_ptrace_stop()

void sched_ptrace_stop ( int reason)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ sched_rdtsc()

uint64_t sched_rdtsc ( void )
inlinestatic
Here is the call graph for this function:
Here is the caller graph for this function:

◆ sched_schedule()

void sched_schedule ( void )
static
Here is the call graph for this function:
Here is the caller graph for this function:

◆ sched_signal_thread()

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.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ sched_stop_current()

void sched_stop_current ( int stop_signal)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ sched_tick()

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.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ sched_wake_tid()

void sched_wake_tid ( uint32_t tid)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ sched_yield()

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.

Here is the call graph for this function:
Here is the caller graph for this function: