Expand description
Scheduler implementation
Implements a per-CPU round-robin scheduler for Strat9-OS with support for cooperative and preemptive multitasking.
§Preemption design
The timer interrupt (100Hz) calls maybe_preempt() which picks the next
task and performs a context switch. Interrupts are disabled while the
scheduler lock is held to prevent deadlock on single-core systems:
yield_task(): CLI → lock → pick next → TSS/CR3 → unlock → switch_context → restore IF- Timer handler: CPU already cleared IF → lock → pick next → TSS/CR3 → unlock → switch_context
Each task has its own 16KB kernel stack. Callee-saved registers are
pushed/popped by switch_context(). CpuContext only stores saved_rsp.
TODO(v3 scheduler):
- API stabilization before adding more features:
- freeze scheduler command syntax.
- add a small machine-friendly output format (key=value) for scripts/debug.
- observability v2:
- per-class latency/wait histograms.
- one structured dump format (instead of free-form text logs) for top/debug.
- targeted scheduler tests (high priority):
- config validation/reject paths (class/policy map).
- ready-task migration on class-table updates.
- SMP steal/preempt non-regression.
- only then: CPU affinity (first truly useful advanced scheduler feature).
Legacy backlog:
- class registry v2:
- dynamic add/remove/reorder with validation and safe reject path.
- policy->class mapping as runtime registry (not only static enum mapping).
- atomic class-table migration:
- RCU/STW swap + migration of queued tasks across classes.
- preserve per-task accounting (vruntime, rt budget, wake deadlines).
- balancing v2:
- dedicated balancer module, per-class steal policy, CPU affinity masks.
- NUMA-aware placement (future) and stronger anti-thrashing controls.
- SMP hardening:
- explicit lock hierarchy doc + assertions.
- improved resched IPI batching/coalescing policy tuning.
- observability v2:
- latency/wait-time histograms per class + structured trace dump.
- shell/top integration over stable snapshot API.
- tests:
- deterministic migration/policy-remap/SMP-steal suites.
- fairness/starvation long-run regression in test ISO.
Optimization roadmap (stability-first, incremental):
- Lock contention reduction (highest ROI, low risk)
- keep scheduler critical sections minimal: compute decisions under lock, execute expensive side effects (IPI, signal delivery, cleanup) after unlock.
- split hot paths into tiny helpers with explicit “lock held / lock free” contract.
- add/track contention counters in every try_lock fallback path.
- Wakeup path scalability (only after strong guards)
- re-introduce deadline index behind a runtime feature flag (default OFF).
- enforce single writer API for wake deadlines (no direct field stores in syscalls).
- add strict invariants:
- if task has deadline != 0, index contains task exactly once.
- on wake/kill/exit/resume, deadline is removed from index and field cleared.
- keep safe fallback scan path available and switchable at runtime.
- Scheduler observability for regressions
- keep stable key=value output for scripts (
scheduler metrics kv,scheduler dump kv). - expose blocked-task ids and per-cpu preempt causes to diagnose stalls quickly.
- include boot-phase and lock-miss counters in all dump modes.
- keep stable key=value output for scripts (
- Balancing/pick optimizations
- tune steal hysteresis/cooldown with metrics, avoid ping-pong migration.
- avoid counting idle task as runnable load for CPU selection.
- add bounded per-tick work budgets to prevent long interrupt latency tails.
- Safety rails before each optimization lands
- ship each optimization in one isolated patchset with rollback switch.
- validate with targeted scenarios:
- boot + shell responsiveness,
- timeout-heavy workload (poll/futex/nanosleep),
- SMP preempt/steal stress.
- if any regression appears, disable feature first, debug second.
Modules§
- core_
impl 🔒 - perf_
counters - Lightweight TSC-based performance counters for critical kernel paths.
- runtime_
ops 🔒 - task_
ops 🔒 - timer_
ops 🔒
Structs§
- CpuUsage
Snapshot - Global
Sched State - Global task registry : cold path: fork, exit, all_tasks scan.
- PerCpu
Class 🔒RqSet - Sched
Identity - Identity maps for the scheduler: PID/TID routing, process groups, session membership, and parent/child relationships.
- Scheduler
Cpu 🔒 - Per-CPU scheduler state
- Scheduler
Metrics Snapshot - Scheduler
State Snapshot - Switch
Target 🔒 - Information needed to perform a context switch after releasing the lock.
Enums§
- Wait
Child Result - Result of a non-blocking wait on child exit.
Constants§
Statics§
- BLOCKED_
TASKS 🔒 - Blocked tasks registry : hot path: block/wake.
- CPU_
FAIR_ 🔒RUNTIME_ TICKS - CPU_
IDLE_ 🔒TICKS - CPU_
PREEMPT_ 🔒COUNT - CPU_
RT_ 🔒RUNTIME_ TICKS - CPU_
STEAL_ 🔒IN_ COUNT - CPU_
STEAL_ 🔒OUT_ COUNT - CPU_
SWITCH_ 🔒COUNT - CPU_
TOTAL_ 🔒TICKS - Per-CPU scheduler tick counters used for CPU usage estimation.
- CPU_
TRY_ 🔒LOCK_ FAIL_ COUNT - FIRST_
PREEMPT_ 🔒LOGGED - One-shot flag per CPU: set to true after the first preemption is logged. Prevents flooding the serial port with a preempt trace on every tick.
- FORCE_
RESCHED_ 🔒HINT - Lock-free per-CPU hint: request a local preemption as soon as maybe_preempt
can observe scheduler state. Written from IRQ paths without touching
GLOBAL_SCHED_STATE, consumed under scheduler lock inmaybe_preempt. - GLOBAL_
SCHED_ 🔒STATE - Global scheduler state : cold path: fork, exit, wake, block.
- IPI_
SEND_ 🔒TRACE_ BUDGET - LAST_
STEAL_ 🔒TICK - LOCAL_
SCHEDULERS 🔒 - Per-CPU local scheduler locks : hot path: timer tick, preemption, yield.
- RESCHED_
IPI_ 🔒PENDING - SCHED_
IDENTITY 🔒 - Identity maps : cold path: PID/TID lookups, process groups, sessions, parent/child.
- SCHED_
VERBOSE 🔒 - Verbose scheduler trace switch.
- TICK_
COUNT 🔒 - Global tick counter (safe to increment from interrupt context)
Functions§
- active_
cpu_ 🔒count - Performs the active cpu count operation.
- add_
task - Add a task to the scheduler
- add_
task_ with_ parent - Add a task and register a parent/child relation.
- block_
current_ task - Block the current task and yield to the scheduler.
- class_
table - Return the scheduler class-table currently in use.
- clear_
task_ wake_ deadline - Performs the clear task wake deadline operation.
- configure_
class_ table - Configure scheduler class pick/steal order at runtime.
- cpu_
is_ 🔒valid - Performs the cpu is valid operation.
- cpu_
usage_ snapshot - Performs the cpu usage snapshot operation.
- create_
session - Create a new session for the calling task.
- current_
cpu_ 🔒index - Performs the current cpu index operation.
- current_
pgid - Get the current process group id.
- current_
pid - Get the current process ID (POSIX pid).
- current_
sid - Get the current session id.
- current_
task_ clone - Get the current task (cloned Arc), if any.
- current_
task_ clone_ spin_ debug - Debug-only blocking variant used to diagnose early ring3 entry stalls.
- current_
task_ clone_ try - Best-effort, non-blocking variant of
current_task_clone. - current_
task_ id - Get the current task’s ID (if any task is running).
- current_
task_ id_ try - Get the current task’s ID without blocking (safe for exceptions).
- current_
tid - Get the current thread ID (POSIX tid).
- debug_
scheduler_ lock_ addr - Returns the scheduler lock address for deadlock tracing.
- exit_
current_ task - Mark the current task as Dead and yield to the scheduler.
- finish_
interrupt_ switch - Finalize a preemption-driven switch once the raw timer stub has already moved onto the next task’s kernel stack.
- finish_
switch - Called immediately after a context switch completes (in the new task’s context). This safely re-queues the previously running task now that its state is fully saved.
- flush_
deferred_ silo_ cleanups - get_
all_ tasks - Get a list of all tasks in the system (for timer checking). Returns None if scheduler is not initialized or currently locked.
- get_
child_ task_ id_ by_ pid - Resolve a direct child of
parentby POSIX pid. - get_
child_ task_ id_ by_ tid - Resolve a direct child of
parentby POSIX tid. - get_
parent_ id - Get parent task ID for a child task.
- get_
parent_ pid - Get parent process ID for a child task.
- get_
pgid_ by_ pid - Resolve a PID to the current process group id.
- get_
sid_ by_ pid - Resolve a PID to the current session id.
- get_
task_ by_ id - Get a task by its TaskId (if still registered).
- get_
task_ by_ pid - Resolve a POSIX pid to the corresponding task.
- get_
task_ id_ by_ pid - Resolve a POSIX pid to internal TaskId.
- get_
task_ id_ by_ tid - Resolve a POSIX tid to the corresponding internal task id.
- get_
task_ ids_ in_ pgid - Collect task IDs that currently belong to process group
pgid. - get_
task_ ids_ in_ tgid - Collect task IDs that currently belong to thread group
tgid. - init_
scheduler - Initialize the scheduler
- kill_
task - Kill a task by ID (best-effort).
- log_
state - Dump per-cpu scheduler queues for tracing/debug.
- maybe_
preempt - Called from the timer interrupt handler (or a resched IPI) to potentially preempt the current task.
- maybe_
preempt_ from_ interrupt - Interrupt-aware preemption path.
- note_
try_ lock_ fail - Performs the note try lock fail operation.
- note_
try_ 🔒lock_ fail_ on_ cpu - Performs the note try lock fail on cpu operation.
- request_
force_ 🔒resched_ hint - Request a local force-reschedule hint for
cpu. - reset_
scheduler_ metrics - Performs the reset scheduler metrics operation.
- resume_
task - Resume a previously suspended task by ID.
- sched_
trace 🔒 - Performs the sched trace operation.
- schedule
- Start the scheduler (called from kernel_main)
- schedule_
on_ cpu - Performs the schedule on cpu operation.
- scheduler_
metrics_ snapshot - Performs the scheduler metrics snapshot operation.
- send_
resched_ 🔒ipi_ to_ cpu - Send a reschedule IPI to
cpu_index. No-op if APIC is not initialized, or ifcpu_indexis the current CPU (the caller already handles the local-CPU case viayield_cpu). - set_
process_ group - Set process group id for
target_pid(or current ifNone). - set_
task_ sched_ policy - Update a task scheduling policy and requeue if needed.
- set_
task_ wake_ deadline - Sets task wake deadline.
- set_
verbose - Enable or disable verbose scheduler tracing.
- state_
snapshot - Structured scheduler state snapshot for shell/top/debug tooling.
- suspend_
task - Suspend a task by ID (best-effort).
- take_
force_ 🔒resched_ hint - Consume and clear the local force-reschedule hint for
cpu. - ticks
- Get the current tick count
- timer_
tick - Timer interrupt handler - called from interrupt context.
- try_
wait_ child - Try to reap a zombie child.
- validate_
task_ 🔒context - Performs the validate task context operation.
- verbose_
enabled - Return current verbose tracing state.
- wake_
task - Wake a blocked task by its ID.
- yield_
dead_ task - Force a context switch away from the current task, unconditionally.
- yield_
task - Yield the current task to allow other tasks to run (cooperative).