ke: freeze CPUs on panic

This commit is contained in:
Loïc Grégoire
2026-09-02 22:17:54 -04:00
parent d640053477
commit cf58ad369e
4 changed files with 76 additions and 6 deletions
+4
View File
@@ -158,6 +158,10 @@ pub inline fn percpu_ptr_other(variable: anytype, id: u32) @TypeOf(variable) {
return percpu_ptr_for(variable, id);
}
pub inline fn halt() void {
asm volatile ("hlt");
}
pub inline fn percpu_ptr(variable: anytype) @TypeOf(variable) {
const offset = asm volatile ("mov %%gs:(%[self]), %[out]"
: [out] "=r" (-> u64),
+38 -1
View File
@@ -4,7 +4,7 @@ const r = @import("root");
const std = @import("std");
const ke = r.ke;
const ki = r.ki;
const ki = ke.private;
const pl = r.pl;
const State = enum(u8) {
@@ -31,6 +31,8 @@ const PerCpu = struct {
const percpu = ke.CpuLocal(PerCpu, undefined);
var frozen_cpus: std.atomic.Value(u32) = .init(0);
/// Call a function on another CPU.
/// The function will be executed synchronously, i.e this will block until
/// the function finishes running. The callback is called with the sender cpu
@@ -63,6 +65,10 @@ pub fn unicast(
ke.ipl.lower(ipl);
}
/// Call a function on all other CPUs.
/// The function will be executed synchronously, i.e this will block until
/// the function finishes running. The callback is called with the sender cpu
/// and provided argument as arguments.
pub fn broadcast(func: *const fn (u32, ?*anyopaque) void, arg: ?*anyopaque) void {
const ipl = ke.ipl.raise(.Dispatch);
const curcpu = percpu.local();
@@ -91,6 +97,28 @@ pub fn broadcast(func: *const fn (u32, ?*anyopaque) void, arg: ?*anyopaque) void
ke.ipl.lower(ipl);
}
/// Freeze all other CPUs.
/// Called at IPL >= Dispatch.
pub fn freeze_cpus() void {
frozen_cpus.store(1, .monotonic);
for (0..ke.ncpus) |i| {
if (i == ke.cpu.current()) continue;
const remote = percpu.remote(@intCast(i));
remote.pending.set(ke.cpu.current(), .monotonic);
const state = remote.state.swap(.Pending, .release);
if (state == .Idle) {
pl.send_ipi(@intCast(i));
}
}
while (frozen_cpus.load(.monotonic) != ke.ncpus) {
std.atomic.spinLoopHint();
}
}
pub fn ipi_handler() void {
const curcpu = percpu.local();
@@ -105,6 +133,15 @@ pub fn ipi_handler() void {
.monotonic,
);
if (frozen_cpus.load(.monotonic) != 0) {
// We have been requested to freeze, ack and do it now.
_ = frozen_cpus.fetchAdd(1, .monotonic);
while (true) {
ki.impl.halt();
}
}
if (val != null) {
// CAS failed.
return;
+31 -5
View File
@@ -71,24 +71,50 @@ fn walk_stack_frame(base: usize) void {
}
pub var panic_lock: ke.SpinLock = .init();
var crash_count: std.atomic.Value(u8) = .init(0);
pub fn panic_with_frame(
msg: []const u8,
frame: usize,
) noreturn {
std.log.err("KERNEL PANIC: {s} on CPU {}, curthread is {*}", .{ msg, ke.cpu.current(), ki.sched.percpu.local().current_thread.? });
_ = ki.impl.disable_interrupts();
std.log.info("Crash count is {} on cpu {}", .{ crash_count.load(.monotonic), ke.cpu.current() });
if (crash_count.fetchAdd(1, .monotonic) != 0) {
std.log.err("Recursive panic from CPU {}", .{ke.cpu.current()});
// Another CPU has already crashed, just freeze manually right now.
while (true) {
ki.impl.halt();
}
}
// We're the first one who actually crashed, that means we get to handle
// things like dumping the system info and freezing other CPUs. Start by
// doing the freeze.
ki.ipi.freeze_cpus();
_ = ke.ipl.raise(.High);
std.log.err("KERNEL PANIC: {s} on CPU {}, curthread is {*}", .{
msg,
ke.cpu.current(),
ki.sched.percpu.local().current_thread.?,
});
std.log.err("Stack trace:", .{});
walk_stack_frame(frame);
panic_lock.release_no_ipl();
while (true) {}
while (true) {
ki.impl.halt();
}
}
pub fn panic(
msg: []const u8,
_: ?usize,
) noreturn {
panic_lock.acquire_no_ipl();
panic_with_frame(msg, @frameAddress());
}
+3
View File
@@ -46,6 +46,9 @@ const ImplSchema = struct {
return false;
}
/// Halt the CPU. This is called on crashes.
pub fn halt() void {}
/// Restore the state of interrupts.
pub fn restore_interrupts(state: bool) void {
_ = state;