feat: use per-cpu sections

This commit is contained in:
Loïc Grégoire
2026-03-23 20:29:34 -04:00
parent 33c796c958
commit 020e81f34b
14 changed files with 235 additions and 117 deletions
+37 -34
View File
@@ -8,18 +8,16 @@ comptime {
if (!@hasDecl(ki.impl, "Cpu")) @compileError("impl must provide Cpu");
}
pub const ThreadStatus = packed struct(u8) {
interactive: bool,
priority: u7,
};
pub const PreemptionReason = enum(u8) {
None,
/// A higher priority thread was readied.
HigherPriority,
};
/// Per-CPU state
extern var __init_array_percpu_start: u8;
extern var __init_array_percpu_end: u8;
/// Global Per-CPU state.
pub const Cpu = struct {
/// Implementation-dependent CPU data.
impl: ki.impl.Cpu,
@@ -27,13 +25,8 @@ pub const Cpu = struct {
id: u32,
/// Current IPL on this CPU.
ipl: ke.Ipl,
/// Bitmask of pending software interrupts on this CPU.
pending_softints: std.atomic.Value(u8),
/// Currently running thread.
current_thread: ?*ke.Thread,
/// Atomic byte for getting the current thread's status locklessly.
/// 1 bit for thread interactivity, 7 for priority.
current_thread_status: std.atomic.Value(ThreadStatus),
/// Per-CPU idle thread.
idle_thread: ?*ke.Thread,
/// Thread selected for preemption.
@@ -42,47 +35,57 @@ pub const Cpu = struct {
preemption_reason: PreemptionReason,
/// Flag indicating the start of the scheduling timer.
start_timer: bool,
/// Queue of DPCs on this CPU.
dpc_queue: rtl.List,
/// Lock over this CPU's DPC queue.
dpc_lock: ke.SpinLock,
/// Heap of pending timers on this CPU.
timers_heap: rtl.PairingHeap(.min, ki.timer.cmp_timer),
/// Lock over the timer heap.
timers_lock: ke.SpinLock,
/// Per-CPU scheduler state.
sched_data: ki.sched.PerCpu,
/// DPCs
resched_dpc: ke.Dpc,
timer_dpc: ke.Dpc,
resched_timer: ke.Timer,
/// Initialize `cpu` for usage with its idle thread being `thread`.
pub fn init(cpu: *Cpu, thread: *ke.Thread) void {
cpu.* = .{
.id = cpu.id,
.ipl = .Zero,
.pending_softints = .init(0),
.ipl = .Passive,
.idle_thread = thread,
.current_thread = thread,
.timer_dpc = .init(ki.timer.handle_expiry),
.resched_dpc = .init(ki.sched.clock),
.timers_heap = .init(),
.current_thread_status = .init(.{ .interactive = false, .priority = 0 }),
.next_thread = null,
.dpc_lock = .init(),
.preemption_reason = .None,
.start_timer = false,
.dpc_queue = undefined,
.timers_lock = .init(),
.sched_data = undefined,
.resched_timer = undefined,
.impl = cpu.impl,
};
cpu.dpc_queue.init();
cpu.resched_timer.init();
// Call per-cpu init functions.
const start = @intFromPtr(&__init_array_percpu_start);
const end = @intFromPtr(&__init_array_percpu_end);
const count = (end - start) / @sizeOf(*const fn () void);
const funcs: [*]const *const fn () callconv(.c) void = @ptrFromInt(start);
ki.sched.init_cpu(&cpu.sched_data);
for (0..count) |i| {
funcs[i]();
}
cpu.resched_timer.init();
}
};
comptime {
if (!@hasDecl(ki.impl, "percpu_ptr")) @compileError("impl must provide percpu_ptr()");
if (!@hasDecl(ki.impl, "percpu_ptr_other")) @compileError("impl must provide percpu_ptr_other()");
}
/// Wraps around CPU-local data.
pub fn CpuLocal(comptime T: type, comptime init: T) type {
return struct {
var storage: T linksection(".data.percpu") = init;
/// Return a pointer to local CPU data.
pub fn local() @TypeOf(ki.impl.percpu_ptr(&storage)) {
return ki.impl.percpu_ptr(&storage);
}
/// Return a pointer to remote CPU data.
pub fn remote(cpu: *ke.Cpu) *T {
return ki.impl.percpu_ptr_other(&storage, cpu.id);
}
};
}