threads(M10): per-thread fs.base — the TLS thread-pointer mechanism
Each thread gets its own x86_64 thread pointer (FS base) for user-space TLS. Task.fs_base is restored on every context switch only when it changes (same conditional-load discipline as CR3; architecture.setFsBase -> wrmsr IA32_FS_BASE). New set_thread_pointer=44 syscall sets the caller's fs_base and loads it now. The kernel never touches FS, so no swapgs complication. The runtime lays a small per-thread TLS block at the top of each thread's stack (self-pointer at %fs:0 + scratch) and the thread trampoline calls set_thread_pointer before any user code — so every spawned thread has a private, switch-stable thread pointer, reclaimed with the stack. thread-test tls mode: two threads write unique markers to their own %fs:8 and, after both wrote, read back — a shared fs.base would clobber one (cross-talk). Deferred: the Zig threadlocal *compiler* layer (ELF variant-II PT_TLS + linker sections + template copy) — high-uncertainty, no consumer today; this lands the load-bearing per-thread fs.base it builds on. See docs/threading-plan.md M10. Gate thread-tls PASS (3x); full guardrail 25/25; build + host tests clean.
This commit is contained in:
@@ -89,6 +89,10 @@ pub const Task = struct {
|
||||
// The task id this task is blocked in `thread_join` on (0 = not joining). Woken by
|
||||
// `wakeJoinersLocked` when that task exits (docs/threading-plan.md M9).
|
||||
join_target: u32 = 0,
|
||||
// The FS-base (x86_64 thread pointer) for this task's user-space TLS — 0 until the
|
||||
// task sets it via `set_thread_pointer`. Restored on every context switch to this task
|
||||
// (docs/threading-plan.md M10).
|
||||
fs_base: u64 = 0,
|
||||
// The mmap / MMIO grant-arena cursors moved from Task to the per-address-space object
|
||||
// (`AspaceRef`, below) so threads sharing one address space hand out disjoint grants
|
||||
// — see aspaceMmapNextPtr / aspaceDeviceMapNextPtr (docs/threading-plan.md M7).
|
||||
@@ -284,6 +288,7 @@ pub const PerCpu = struct {
|
||||
index: u32 = 0, // dense 0-based core index
|
||||
online: bool = false, // has this core finished bring-up?
|
||||
loaded_aspace: u64 = 0, // the address-space root currently loaded on this core
|
||||
loaded_fs_base: u64 = 0, // the FS base currently loaded on this core (docs/threading-plan.md M10)
|
||||
// Tasks pinned to this core (affinity == index), per priority level + bitmap.
|
||||
pinned_head: [number_priorities]?*Task = .{null} ** number_priorities,
|
||||
pinned_tail: [number_priorities]?*Task = .{null} ** number_priorities,
|
||||
@@ -568,6 +573,12 @@ fn switchTo(pc: *PerCpu, save_sp: *usize, next: *Task) void {
|
||||
architecture.loadPageTable(want);
|
||||
pc.loaded_aspace = want;
|
||||
}
|
||||
// Restore the next task's user TLS thread pointer (FS base) — only on change, the same
|
||||
// conditional-load discipline as CR3 above (docs/threading-plan.md M10).
|
||||
if (next.fs_base != pc.loaded_fs_base) {
|
||||
architecture.setFsBase(next.fs_base);
|
||||
pc.loaded_fs_base = next.fs_base;
|
||||
}
|
||||
architecture.switchContext(save_sp, next.sp);
|
||||
// Resumed now (switchContext returned into our own switchTo frame). Re-fetch the core
|
||||
// via thisCpu(): the `pc` parameter is from *our* earlier switchTo call, so it names
|
||||
@@ -667,6 +678,16 @@ pub fn joinThreadLocked(tid: u32) void {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the calling task's user TLS thread pointer (FS base) and load it now. Persisted on
|
||||
/// the Task so context switches restore it (docs/threading-plan.md M10). Caller holds the
|
||||
/// kernel lock.
|
||||
pub fn setThreadPointerLocked(addr: u64) void {
|
||||
const pc = thisCpu();
|
||||
pc.current.fs_base = addr;
|
||||
architecture.setFsBase(addr);
|
||||
pc.loaded_fs_base = addr;
|
||||
}
|
||||
|
||||
/// Wake every task blocked in `thread_join` on `tid` — called from the exit paths once the
|
||||
/// exiting task's state is `.free`. Caller holds the lock.
|
||||
fn wakeJoinersLocked(tid: u32) void {
|
||||
|
||||
Reference in New Issue
Block a user