ke: make getting the current thread atomic

This commit is contained in:
Loïc Grégoire
2026-10-23 23:47:23 -04:00
parent 0a843157c2
commit 76147556a3
10 changed files with 38 additions and 24 deletions
+7
View File
@@ -246,3 +246,10 @@ pub inline fn lower_software_ipl(new: ke.Ipl, old: *ke.Ipl) bool {
[old_ptr] "r" (old), [old_ptr] "r" (old),
: .{ .rcx = true, .memory = true }); : .{ .rcx = true, .memory = true });
} }
pub inline fn current_thread() ?*ke.Thread {
return @ptrFromInt(asm volatile (
\\mov %%gs:current_thread, %[ret]
: [ret] "=r" (-> u64),
));
}
+2 -2
View File
@@ -95,8 +95,8 @@ pub fn ap_entry(cpu_id: u32, booted: *std.atomic.Value(usize)) noreturn {
const sched = kep.sched.percpu.local(); const sched = kep.sched.percpu.local();
sched.current_thread = pl.impl.smp.start_thread.local().*; kep.sched.current_thread.local().* = pl.impl.smp.start_thread.local().*;
sched.idle_thread = sched.current_thread; sched.idle_thread = kep.thread.current();
amd64.sti(); amd64.sti();
+1 -1
View File
@@ -141,7 +141,7 @@ fn dispatch_queue(cpu: u32) void {
kep.shootdown.process_shootdowns(); kep.shootdown.process_shootdowns();
if (sched_cpu.next_thread != null) { if (sched_cpu.next_thread != null) {
const curtd = sched_cpu.current_thread orelse unreachable; const curtd = ke.thread.current();
if (!curtd.smr_sections.is_empty()) { if (!curtd.smr_sections.is_empty()) {
kep.smr.mark_thread_stalled(curtd); kep.smr.mark_thread_stalled(curtd);
+1 -1
View File
@@ -54,7 +54,7 @@ fn init() linksection(r.init) void {
kep.cpu.init_cpu(0); kep.cpu.init_cpu(0);
kep.turnstile.init_turnstiles(); kep.turnstile.init_turnstiles();
kep.sched.percpu.local().current_thread = &thread0; kep.sched.current_thread.local().* = &thread0;
kep.sched.percpu.local().idle_thread = &thread0; kep.sched.percpu.local().idle_thread = &thread0;
std.log.info("Zag for {s} ({s}), cmdline is \"{?s}\"", .{ std.log.info("Zag for {s} ({s}), cmdline is \"{?s}\"", .{
+3 -5
View File
@@ -32,9 +32,7 @@ pub const Mutex = struct {
} }
pub fn acquire(m: *Mutex) void { pub fn acquire(m: *Mutex) void {
const ipl = ke.ipl.raise(.Dispatch); const curtd = ke.thread.current();
const curtd = kep.sched.percpu.local().current_thread orelse
unreachable;
if (config.warden) { if (config.warden) {
kep.warden.check(m.wd, .Lock); kep.warden.check(m.wd, .Lock);
@@ -54,7 +52,6 @@ pub const Mutex = struct {
); );
if (cur_owner == null) { if (cur_owner == null) {
ke.ipl.lower(ipl);
return; return;
} }
@@ -74,10 +71,11 @@ pub const Mutex = struct {
continue; continue;
} }
ke.ipl.lower(ipl);
return; return;
} }
const ipl = ke.ipl.raise(.Dispatch);
// Slow path: contended, block on a turnstile. // Slow path: contended, block on a turnstile.
while (true) { while (true) {
const ts = kep.turnstile.lookup(m); const ts = kep.turnstile.lookup(m);
+7
View File
@@ -74,6 +74,13 @@ const ImplSchema = struct {
_ = old; _ = old;
unreachable; unreachable;
} }
/// Return a pointer to the current thread.
/// This must be atomic with regards to migration (i.e this should be a
/// consistent pointer no matter what).
pub fn current_thread() ?*ke.Thread {
unreachable;
}
}; };
comptime { comptime {
+1 -1
View File
@@ -60,7 +60,7 @@ pub const Queue = struct {
/// This blocks until an item is actually popped. /// This blocks until an item is actually popped.
pub fn remove(self: *Queue, timeout: ?rtl.Duration) !*rtl.List.Entry { pub fn remove(self: *Queue, timeout: ?rtl.Duration) !*rtl.List.Entry {
const ipl = self.hdr.lock.acquire(); const ipl = self.hdr.lock.acquire();
const td = kep.sched.percpu.local().current_thread orelse unreachable; const td = ke.thread.current();
if (td.queue) |q| { if (td.queue) |q| {
assert(q == self); assert(q == self);
+14 -12
View File
@@ -208,8 +208,6 @@ pub const PerCpu = struct {
start_timer: bool, start_timer: bool,
/// The reason why the current thread was preempted. /// The reason why the current thread was preempted.
preemption_reason: PreemptionReason, preemption_reason: PreemptionReason,
/// Currently running thread.
current_thread: ?*ke.Thread,
/// Per-CPU idle thread. /// Per-CPU idle thread.
idle_thread: ?*ke.Thread, idle_thread: ?*ke.Thread,
/// Thread selected for preemption. /// Thread selected for preemption.
@@ -242,6 +240,11 @@ pub const Accounting = struct {
}; };
pub const percpu = ke.CpuLocalType(PerCpu, undefined); pub const percpu = ke.CpuLocalType(PerCpu, undefined);
pub const current_thread = ke.ExportedCpuLocalType(
?*ke.Thread,
null,
"current_thread",
);
var balance_timer: ke.Timer = undefined; var balance_timer: ke.Timer = undefined;
var balance_dpc = ke.Dpc.init(balance); var balance_dpc = ke.Dpc.init(balance);
@@ -276,7 +279,6 @@ fn init_cpu() linksection(r.init) void {
.resched_timer = undefined, .resched_timer = undefined,
.start_timer = false, .start_timer = false,
.preemption_reason = .None, .preemption_reason = .None,
.current_thread = null,
.idle_thread = null, .idle_thread = null,
.next_thread = null, .next_thread = null,
.pick_offset = 0, .pick_offset = 0,
@@ -1022,7 +1024,7 @@ fn enqueue_on_cpu(c: u32, td: *ke.Thread) void {
attach_load_avg(cpu, td); attach_load_avg(cpu, td);
if (cpu.current_thread != null and if (current_thread.local().* != null and
should_preempt_cpu(td, cpu, c != ke.cpu.current())) should_preempt_cpu(td, cpu, c != ke.cpu.current()))
{ {
// We can preempt the current thread, first check whether or not there // We can preempt the current thread, first check whether or not there
@@ -1088,8 +1090,8 @@ fn allocate_stack(td: *ke.Thread) ?usize {
} }
// Execute a context switch on `cpu`. // Execute a context switch on `cpu`.
fn do_switch(cpu: *PerCpu, cur: *ke.Thread, next: *ke.Thread) void { fn do_switch(cur: *ke.Thread, next: *ke.Thread) void {
cpu.current_thread = next; current_thread.local().* = next;
next.last_cpu = ke.cpu.current(); next.last_cpu = ke.cpu.current();
const time = ke.time.read_time(); const time = ke.time.read_time();
@@ -1153,7 +1155,7 @@ fn do_switch(cpu: *PerCpu, cur: *ke.Thread, next: *ke.Thread) void {
/// Handle preemption. /// Handle preemption.
/// This is called in DPC dispatch when it notices `next_thread` is set. /// This is called in DPC dispatch when it notices `next_thread` is set.
pub fn handle_preemption(cpu: *PerCpu) void { pub fn handle_preemption(cpu: *PerCpu) void {
const cur = cpu.current_thread orelse unreachable; const cur = ke.thread.current();
var next: *ke.Thread = undefined; var next: *ke.Thread = undefined;
while (true) { while (true) {
@@ -1220,7 +1222,7 @@ pub fn handle_preemption(cpu: *PerCpu) void {
std.atomic.spinLoopHint(); std.atomic.spinLoopHint();
} }
do_switch(cpu, cur, next); do_switch(cur, next);
// cur.lock dropped // cur.lock dropped
} }
@@ -1230,7 +1232,7 @@ pub fn clock(_: *ke.Dpc, _: ?*anyopaque) void {
const cpu = percpu.local(); const cpu = percpu.local();
assert(kep.ipl.current() == .Dispatch); assert(kep.ipl.current() == .Dispatch);
const curtd = cpu.current_thread orelse return; const curtd = current_thread.local().* orelse return;
const now = ke.time.read_time(); const now = ke.time.read_time();
const now_us = now.to_us(); const now_us = now.to_us();
@@ -1292,7 +1294,7 @@ pub fn enqueue(td: *ke.Thread) void {
/// Yield on the current CPU. /// Yield on the current CPU.
pub fn yield(continuation: ?ke.Continuation) void { pub fn yield(continuation: ?ke.Continuation) void {
const ipl = ke.ipl.raise(.Dispatch); const ipl = ke.ipl.raise(.Dispatch);
const td = percpu.local().current_thread orelse unreachable; const td = ke.thread.current();
td.lock.acquire_no_ipl(); td.lock.acquire_no_ipl();
td.switching.store(true, .monotonic); td.switching.store(true, .monotonic);
@@ -1317,7 +1319,7 @@ pub fn yield_locked(continuation: ?ke.Continuation) void {
sched_cpu.queues_lock.acquire_no_ipl(); sched_cpu.queues_lock.acquire_no_ipl();
const cur = sched_cpu.current_thread orelse unreachable; const cur = ke.thread.current();
var next = sched_cpu.next_thread; var next = sched_cpu.next_thread;
cur.continuation = continuation; cur.continuation = continuation;
@@ -1386,7 +1388,7 @@ pub fn yield_locked(continuation: ?ke.Continuation) void {
while (n.switching.load(.monotonic) == true) { while (n.switching.load(.monotonic) == true) {
std.atomic.spinLoopHint(); std.atomic.spinLoopHint();
} }
do_switch(sched_cpu, cur, n); do_switch(cur, n);
} else { } else {
// Same thread. // Same thread.
cur.state.store(.Running, .monotonic); cur.state.store(.Running, .monotonic);
+1 -1
View File
@@ -316,7 +316,7 @@ pub fn exit() void {
/// Return the currently running thread. /// Return the currently running thread.
pub fn current() *ke.Thread { pub fn current() *ke.Thread {
return kep.sched.percpu.local().current_thread orelse unreachable; return kep.impl.current_thread() orelse unreachable;
} }
// -- Stack allocation and continuations --------------------------------------- // -- Stack allocation and continuations ---------------------------------------
+1 -1
View File
@@ -443,7 +443,7 @@ pub fn block(
queue: Queue, queue: Queue,
) void { ) void {
const chain = chain_for(obj); const chain = chain_for(obj);
const curtd = kep.sched.percpu.local().current_thread.?; const curtd = ke.thread.current();
const ipl = ke.ipl.current(); const ipl = ke.ipl.current();
var ts = turnstile; var ts = turnstile;