mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-27 13:58:20 +00:00
all: more tidying
This commit is contained in:
@@ -39,7 +39,7 @@ const Msr = enum(u32) {
|
||||
};
|
||||
|
||||
fn write_msr(msr: Msr, value: u64) void {
|
||||
amd64.wrmsr(@intFromEnum(msr), value);
|
||||
amd64.wrmsr(@backingInt(msr), value);
|
||||
}
|
||||
|
||||
const PVClock = extern struct {
|
||||
|
||||
@@ -138,11 +138,11 @@ pub inline fn wrmsr(msr: u32, value: u64) void {
|
||||
}
|
||||
|
||||
pub inline fn read_msr(comptime msr: Msr) u64 {
|
||||
return rdmsr(@intFromEnum(msr));
|
||||
return rdmsr(@backingInt(msr));
|
||||
}
|
||||
|
||||
pub inline fn write_msr(comptime msr: Msr, value: u64) void {
|
||||
wrmsr(@intFromEnum(msr), value);
|
||||
wrmsr(@backingInt(msr), value);
|
||||
}
|
||||
|
||||
pub inline fn rdgsbase() usize {
|
||||
@@ -348,8 +348,8 @@ pub const CpuidRequest = union(enum) {
|
||||
.HighestExtendedFunction => .{ 0x80000000, 0 },
|
||||
.ExtendedInfo => .{ 0x80000001, 0 },
|
||||
.PowerManagementInfo => .{ 0x80000007, 0 },
|
||||
.ExtendedFeatures => |s| .{ 0x7, @intFromEnum(s) },
|
||||
.BrandString => |p| .{ @intFromEnum(p), 0 },
|
||||
.ExtendedFeatures => |s| .{ 0x7, @backingInt(s) },
|
||||
.BrandString => |p| .{ @backingInt(p), 0 },
|
||||
.HypervisorId => .{ 0x40000000, 0 },
|
||||
};
|
||||
return cpuid(leaf, subleaf);
|
||||
@@ -709,7 +709,7 @@ pub const IdtEntry = extern struct {
|
||||
.offset_low = @truncate(offset),
|
||||
.selector = cs,
|
||||
.ist = ist,
|
||||
.type_attr = @intFromEnum(attr),
|
||||
.type_attr = @backingInt(attr),
|
||||
.offset_mid = @truncate(offset >> 16),
|
||||
.offset_high = @truncate(offset >> 32),
|
||||
.reserved = 0,
|
||||
|
||||
+2
-2
@@ -81,8 +81,8 @@ pub const InternedString = packed struct(usize) {
|
||||
}
|
||||
};
|
||||
|
||||
const Table = rcu.HashTable(Entry, "link", Context);
|
||||
const EntryZone = mm.zone.TypedZone(Entry);
|
||||
const Table = rcu.HashTableType(Entry, "link", Context);
|
||||
const EntryZone = mm.zone.ZoneType(Entry);
|
||||
|
||||
var domain: *ke.smr.Domain = undefined;
|
||||
var table: Table = undefined;
|
||||
|
||||
+2
-2
@@ -82,9 +82,9 @@ const Segment = struct {
|
||||
const freelist_count = @bitSizeOf(usize);
|
||||
const max_qcaches = 16;
|
||||
|
||||
var seg_zone: mmp.zone.TypedZone(Segment) = undefined;
|
||||
var seg_zone: mmp.zone.ZoneType(Segment) = undefined;
|
||||
|
||||
const SegmentTree = rtl.RBTree(Segment.cmp);
|
||||
const SegmentTree = rtl.RbTreeType(Segment.cmp);
|
||||
|
||||
pub const Arena = struct {
|
||||
/// Name of the arena, for debugging purposes.
|
||||
|
||||
+8
-8
@@ -22,7 +22,7 @@ const ps = r.ps;
|
||||
const mm = r.mm;
|
||||
|
||||
/// Maximum number of additional threads created per pool.
|
||||
const max_dynamic_threads = ke.Tunable(u8, 4, "ex.work.max_dynamic");
|
||||
const max_dynamic_threads = ke.TunableType(u8, 4, "ex.work.max_dynamic");
|
||||
|
||||
var pool_manager_event: ke.Event = undefined;
|
||||
|
||||
@@ -98,7 +98,7 @@ pub const DelayedWorkItem = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const Context = rtl.TaggedPtr(Pool);
|
||||
const Context = rtl.TaggedPtrType(Pool);
|
||||
|
||||
/// Represents a thread worker pool and its queue.
|
||||
const Pool = struct {
|
||||
@@ -204,7 +204,7 @@ const PerCpu = struct {
|
||||
normal: Pool,
|
||||
};
|
||||
|
||||
const percpu = ke.CpuLocal(PerCpu, undefined);
|
||||
const percpu = ke.CpuLocalType(PerCpu, undefined);
|
||||
|
||||
/// Low-priority work queue.
|
||||
var low: Pool = undefined;
|
||||
@@ -214,8 +214,8 @@ fn work_loop(p: ?*anyopaque) void {
|
||||
const dynamic = ctx.tag() == 1;
|
||||
|
||||
while (true) {
|
||||
const timeout: ?r.Nanoseconds = if (dynamic)
|
||||
.from(r.Seconds.init(5))
|
||||
const timeout: ?rtl.Duration = if (dynamic)
|
||||
rtl.Duration.seconds(5)
|
||||
else
|
||||
null;
|
||||
const item = ctx.ptr().queue.remove(timeout) catch {
|
||||
@@ -239,9 +239,9 @@ fn work_dpc(dpc: *ke.Dpc, _: ?*anyopaque) void {
|
||||
enqueue(&item.item);
|
||||
}
|
||||
|
||||
/// Enqueue a work item in `time`.
|
||||
pub fn enqueue_in(item: *DelayedWorkItem, time: r.Nanoseconds) void {
|
||||
ke.timer.set(&item.timer, time, .{ .dpc = &item.dpc });
|
||||
/// Enqueue a work item after `duration`.
|
||||
pub fn enqueue_in(item: *DelayedWorkItem, duration: rtl.Duration) void {
|
||||
ke.timer.set(&item.timer, duration, .{ .dpc = &item.dpc });
|
||||
}
|
||||
|
||||
/// Enqueue a work item to be executed eventually.
|
||||
|
||||
@@ -124,7 +124,7 @@ export fn isr_handler_main(frame: *const amd64.IrqFrame) callconv(.c) void {
|
||||
|
||||
_ = ke.ipl.set_hardware(ipl);
|
||||
|
||||
if (@intFromEnum(ipl) < @intFromEnum(ke.Ipl.get_max_software()) and
|
||||
if (@backingInt(ipl) < @backingInt(ke.Ipl.get_max_software()) and
|
||||
kep.ipl.is_softint_pending(.Dispatch))
|
||||
{
|
||||
kep.dpc.dispatch();
|
||||
|
||||
+5
-5
@@ -7,7 +7,7 @@ const r = @import("root");
|
||||
const ke = r.ke;
|
||||
const kep = r.ke.private;
|
||||
|
||||
const id = CpuLocal(u32, 0);
|
||||
const id = CpuLocalType(u32, 0);
|
||||
|
||||
/// Initialize a CPU. Must be called on all CPUs.
|
||||
pub fn init_cpu(cpu_id: u32) void {
|
||||
@@ -25,7 +25,7 @@ pub fn current() u32 {
|
||||
}
|
||||
|
||||
/// Wraps around CPU-local data.
|
||||
pub fn CpuLocal(comptime T: type, comptime init: T) type {
|
||||
pub fn CpuLocalType(comptime T: type, comptime init: T) type {
|
||||
return struct {
|
||||
var storage: T linksection(".data.percpu") = init;
|
||||
|
||||
@@ -42,7 +42,7 @@ pub fn CpuLocal(comptime T: type, comptime init: T) type {
|
||||
}
|
||||
|
||||
/// Wraps around CPU-local data with a designated symbol name.
|
||||
pub fn ExportedCpuLocal(
|
||||
pub fn ExportedCpuLocalTypeType(
|
||||
comptime T: type,
|
||||
comptime init: T,
|
||||
comptime name: []const u8,
|
||||
@@ -62,5 +62,5 @@ pub fn ExportedCpuLocal(
|
||||
}
|
||||
|
||||
/// Bitmask of CPUs.
|
||||
pub const CpuMask = rtl.BitMap(config.ncpus);
|
||||
pub const AtomicCpuMask = rtl.AtomicBitMap(config.ncpus);
|
||||
pub const CpuMask = rtl.BitmapType(config.ncpus);
|
||||
pub const AtomicCpuMask = rtl.AtomicBitmapType(config.ncpus);
|
||||
|
||||
+3
-3
@@ -39,7 +39,7 @@ const PerCpu = struct {
|
||||
lock: ke.SpinLock,
|
||||
};
|
||||
|
||||
const pcpu = ke.CpuLocal(PerCpu, .{
|
||||
const pcpu = ke.CpuLocalType(PerCpu, .{
|
||||
.lock = undefined,
|
||||
.queue = undefined,
|
||||
});
|
||||
@@ -120,7 +120,7 @@ fn dispatch_queue(cpu: u32) void {
|
||||
if (sched_cpu.start_timer) {
|
||||
ke.timer.set(
|
||||
&sched_cpu.resched_timer,
|
||||
.from(r.Milliseconds.init(config.sched_timeslice)),
|
||||
rtl.Duration.ms(config.sched_timeslice),
|
||||
.{ .dpc = &sched_cpu.resched_dpc },
|
||||
);
|
||||
}
|
||||
@@ -130,7 +130,7 @@ fn dispatch_queue(cpu: u32) void {
|
||||
ke.timer.cancel(&sched_cpu.resched_timer);
|
||||
ke.timer.set(
|
||||
&sched_cpu.resched_timer,
|
||||
.from(r.Milliseconds.init(config.sched_timeslice)),
|
||||
rtl.Duration.ms(config.sched_timeslice),
|
||||
.{ .dpc = &sched_cpu.resched_dpc },
|
||||
);
|
||||
}
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ const PerCpu = struct {
|
||||
state: std.atomic.Value(State),
|
||||
};
|
||||
|
||||
const percpu = ke.CpuLocal(PerCpu, undefined);
|
||||
const percpu = ke.CpuLocalType(PerCpu, undefined);
|
||||
|
||||
var frozen_cpus = std.atomic.Value(u32).init(0);
|
||||
|
||||
|
||||
+2
-2
@@ -33,13 +33,13 @@ const PerCpu = struct {
|
||||
};
|
||||
|
||||
/// Current IPL on this CPU.
|
||||
pub const cpu_ipl = ke.ExportedCpuLocal(
|
||||
pub const cpu_ipl = ke.ExportedCpuLocalTypeType(
|
||||
Ipl,
|
||||
.Passive,
|
||||
"cpu_ipl",
|
||||
);
|
||||
|
||||
pub const percpu = ke.CpuLocal(PerCpu, .{
|
||||
pub const percpu = ke.CpuLocalType(PerCpu, .{
|
||||
.pending_softints = std.atomic.Value(u8).init(0),
|
||||
});
|
||||
|
||||
|
||||
+2
-2
@@ -45,7 +45,7 @@ const DebugWriter = struct {
|
||||
// Messages are on average 2^5 = 32 bytes.
|
||||
const avg_msg_size_bits = 5;
|
||||
|
||||
pub var ringbuffer = kep.log_ring.RingBuffer(
|
||||
pub var ringbuffer = kep.log_ring.RingBufferType(
|
||||
config.log_buffer_shift,
|
||||
avg_msg_size_bits,
|
||||
).init();
|
||||
@@ -87,7 +87,7 @@ pub fn log(
|
||||
ringbuffer.publish(res);
|
||||
|
||||
// Signal whomever is waiting on logs to get published.
|
||||
if (@intFromEnum(ke.ipl.current()) <= @intFromEnum(ke.Ipl.Dispatch)) {
|
||||
if (@backingInt(ke.ipl.current()) <= @backingInt(ke.Ipl.Dispatch)) {
|
||||
event.signal();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! `RingBuffer` is a concurrent ring buffer that allows multiple writers
|
||||
//! `RingBufferType` is a concurrent ring buffer that allows multiple writers
|
||||
//! and readers to operate concurrently without locks, and also supports re-
|
||||
//! entrancy, making it safe to use from interrupt context.
|
||||
//! It is modelled after Linux's `printk_ringbuffer`, but is simplified,
|
||||
@@ -68,8 +68,9 @@
|
||||
//! during eviction.
|
||||
|
||||
const std = @import("std");
|
||||
const r = @import("root");
|
||||
const rtl = @import("rtl");
|
||||
|
||||
const r = @import("root");
|
||||
const ke = r.ke;
|
||||
const kep = ke.private;
|
||||
|
||||
@@ -101,7 +102,7 @@ const Desc = struct {
|
||||
|
||||
pub const Info = struct {
|
||||
sequence: u64,
|
||||
timestamp: r.Nanoseconds,
|
||||
timestamp: rtl.Timestamp,
|
||||
length: u16,
|
||||
};
|
||||
|
||||
@@ -131,7 +132,7 @@ pub const ReadError = error{NotYetAvailable};
|
||||
// block position.
|
||||
const lpos_no_data = 1;
|
||||
|
||||
pub fn RingBuffer(data_size_bits: usize, avg_msg_bits: usize) type {
|
||||
pub fn RingBufferType(data_size_bits: usize, avg_msg_bits: usize) type {
|
||||
return struct {
|
||||
const data_size = 1 << data_size_bits;
|
||||
const desc_bits = data_size_bits - avg_msg_bits;
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ pub const Queue = struct {
|
||||
|
||||
/// Remove the item at the head.
|
||||
/// This blocks until an item is actually popped.
|
||||
pub fn remove(self: *Queue, timeout: ?r.Nanoseconds) !*rtl.List.Entry {
|
||||
pub fn remove(self: *Queue, timeout: ?rtl.Duration) !*rtl.List.Entry {
|
||||
const ipl = self.hdr.lock.acquire();
|
||||
const td = kep.sched.percpu.local().current_thread orelse unreachable;
|
||||
|
||||
|
||||
+3
-3
@@ -14,15 +14,15 @@ pub const Dpc = p.dpc.Dpc;
|
||||
pub const TimeCounter = p.time.TimeCounter;
|
||||
pub const Timer = p.timer.Timer;
|
||||
pub const Console = p.log.Console;
|
||||
pub const CpuLocal = p.cpu.CpuLocal;
|
||||
pub const ExportedCpuLocal = p.cpu.ExportedCpuLocal;
|
||||
pub const CpuLocalType = p.cpu.CpuLocalType;
|
||||
pub const ExportedCpuLocalTypeType = p.cpu.ExportedCpuLocalTypeType;
|
||||
pub const CpuMask = p.cpu.CpuMask;
|
||||
pub const AtomicCpuMask = p.cpu.AtomicCpuMask;
|
||||
pub const Event = p.event.Event;
|
||||
pub const Queue = p.queue.Queue;
|
||||
pub const Mutex = p.mutex.Mutex;
|
||||
pub const ShootdownState = p.shootdown.ShootdownState;
|
||||
pub const Tunable = p.tunable.Tunable;
|
||||
pub const TunableType = p.tunable.TunableType;
|
||||
pub const Priority = p.thread.Priority;
|
||||
pub const Stack = p.thread.Stack;
|
||||
pub const Continuation = p.thread.Continuation;
|
||||
|
||||
+12
-12
@@ -109,7 +109,7 @@ const interactivity_threshold = 30;
|
||||
const scaling_factor = 50;
|
||||
const preempt_threshold = @backingInt(ke.Priority.LowRealtime);
|
||||
const balance_interval = @as(u64, config.sched_balance_interval);
|
||||
const sched_migration_cost = ke.Tunable(
|
||||
const sched_migration_cost = ke.TunableType(
|
||||
u64,
|
||||
500,
|
||||
"sched_migration_cost_us",
|
||||
@@ -226,8 +226,8 @@ pub const Average = struct {
|
||||
est: usize = pelt_load_avg_max,
|
||||
// Amount of the period that has been accounted for
|
||||
period_contrib: usize = 0,
|
||||
/// Timestamp (us) up to which history has been accounted.
|
||||
last_update: r.Microseconds = r.Microseconds.init(0),
|
||||
/// Timestamp up to which history has been accounted.
|
||||
last_update: rtl.Timestamp = .{ .value = 0 },
|
||||
};
|
||||
|
||||
pub const Accounting = struct {
|
||||
@@ -241,7 +241,7 @@ pub const Accounting = struct {
|
||||
pct_window_start: u64 = 0,
|
||||
};
|
||||
|
||||
pub const percpu = ke.CpuLocal(PerCpu, undefined);
|
||||
pub const percpu = ke.CpuLocalType(PerCpu, undefined);
|
||||
|
||||
var balance_timer: ke.Timer = undefined;
|
||||
var balance_dpc = ke.Dpc.init(balance);
|
||||
@@ -303,7 +303,7 @@ pub fn late_init() linksection(r.init) void {
|
||||
balance_timer.init();
|
||||
ke.timer.set(
|
||||
&balance_timer,
|
||||
.from(r.Milliseconds.init(balance_interval)),
|
||||
rtl.Duration.ms(balance_interval),
|
||||
.{ .dpc = &balance_dpc },
|
||||
);
|
||||
}
|
||||
@@ -331,8 +331,8 @@ fn pelt_do_decay(val: u64, n: u64) u64 {
|
||||
}
|
||||
|
||||
fn pelt_update(avg: *Average, runnable: bool) void {
|
||||
const now = ke.time.read_time().to(r.Microseconds);
|
||||
const new_delta = now.value - avg.last_update.value;
|
||||
const now = ke.time.read_time();
|
||||
const new_delta = now.to_us() - avg.last_update.to_us();
|
||||
|
||||
if (new_delta == 0) return;
|
||||
|
||||
@@ -411,7 +411,7 @@ fn attach_load_avg(cpu: *PerCpu, td: *ke.Thread) void {
|
||||
if (!tracks_load_avg(td)) return;
|
||||
|
||||
if (td.avg.last_update.value == 0) {
|
||||
td.avg.last_update = ke.time.read_time().to(r.Microseconds);
|
||||
td.avg.last_update = ke.time.read_time();
|
||||
} else {
|
||||
pelt_update_td(td, null, false);
|
||||
}
|
||||
@@ -1093,7 +1093,7 @@ fn do_switch(cpu: *PerCpu, cur: *ke.Thread, next: *ke.Thread) void {
|
||||
next.last_cpu = ke.cpu.current();
|
||||
|
||||
const time = ke.time.read_time();
|
||||
const now_us = time.to(r.Microseconds).value;
|
||||
const now_us = time.to_us();
|
||||
|
||||
if (tracks_load_avg(cur)) {
|
||||
account(cur, now_us, .Running);
|
||||
@@ -1233,7 +1233,7 @@ pub fn clock(_: *ke.Dpc, _: ?*anyopaque) void {
|
||||
const curtd = cpu.current_thread orelse return;
|
||||
|
||||
const now = ke.time.read_time();
|
||||
const now_us = now.to(r.Microseconds).value;
|
||||
const now_us = now.to_us();
|
||||
|
||||
curtd.lock.acquire_no_ipl();
|
||||
defer curtd.lock.release_no_ipl();
|
||||
@@ -1448,7 +1448,7 @@ pub fn unblock_locked(td: *ke.Thread) void {
|
||||
|
||||
const now = ke.time.read_time();
|
||||
|
||||
const now_us = now.to(r.Microseconds).value;
|
||||
const now_us = now.to_us();
|
||||
const delta = (now_us - td.acct.stamp);
|
||||
|
||||
if (tracks_load_avg(td)) {
|
||||
@@ -1765,7 +1765,7 @@ fn balance(_: *ke.Dpc, _: ?*anyopaque) void {
|
||||
|
||||
ke.timer.set(
|
||||
&balance_timer,
|
||||
.from(r.Milliseconds.init(ms)),
|
||||
rtl.Duration.ms(ms),
|
||||
.{
|
||||
.dpc = &balance_dpc,
|
||||
},
|
||||
|
||||
@@ -47,7 +47,7 @@ const PerCpu = struct {
|
||||
|
||||
const slot_free: u16 = std.math.maxInt(u16);
|
||||
const slot_reserved: u16 = std.math.maxInt(u16) - 1;
|
||||
const percpu = ke.CpuLocal(PerCpu, undefined);
|
||||
const percpu = ke.CpuLocalType(PerCpu, undefined);
|
||||
|
||||
pub var shootdowns: rtl.HandoffList = undefined;
|
||||
|
||||
|
||||
+66
-53
@@ -132,59 +132,7 @@ fn scan(
|
||||
}
|
||||
|
||||
if (dom.preempt) {
|
||||
rtl.barrier.fence(.seq_cst);
|
||||
|
||||
// Preemption is enabled, check stalled readers.
|
||||
for (0..ke.ncpus) |i| {
|
||||
const cpu = &dom.cpus[i];
|
||||
|
||||
var seq = cpu.stall_seq.load(.monotonic);
|
||||
|
||||
while (seq != seq_invalid) {
|
||||
if (seq < clk_read) {
|
||||
seq = clk_read;
|
||||
}
|
||||
|
||||
if (goal <= seq) {
|
||||
// The goal has been reached.
|
||||
break;
|
||||
}
|
||||
|
||||
if (!should_wait) break;
|
||||
|
||||
const ipl = ke.ipl.raise(.Dispatch);
|
||||
|
||||
cpu.stall_lock.acquire_no_ipl();
|
||||
cpu.stall_goal = goal;
|
||||
cpu.stall_lock.release_no_ipl();
|
||||
|
||||
const ts = kep.turnstile.lookup(cpu);
|
||||
|
||||
seq = cpu.stall_seq.load(.monotonic);
|
||||
|
||||
// Re-check under the chain lock.
|
||||
if (seq == seq_invalid or goal <= seq) {
|
||||
kep.turnstile.exit(cpu, ts);
|
||||
ke.ipl.lower(ipl);
|
||||
break;
|
||||
}
|
||||
|
||||
kep.turnstile.block(
|
||||
ts,
|
||||
cpu,
|
||||
.{ .shared = &cpu.stalled },
|
||||
.Exclusive,
|
||||
);
|
||||
|
||||
ke.ipl.lower(ipl);
|
||||
seq = cpu.stall_seq.load(.monotonic);
|
||||
}
|
||||
|
||||
if (seq != seq_invalid) {
|
||||
// Update the minimum read sequence.
|
||||
read_seq = @min(read_seq, seq);
|
||||
}
|
||||
}
|
||||
read_seq = scan_preempt(dom, clk_read, goal, should_wait, read_seq);
|
||||
}
|
||||
|
||||
// The orderings here serve two purposes:
|
||||
@@ -215,6 +163,71 @@ fn scan(
|
||||
return dom_rd_seq;
|
||||
}
|
||||
|
||||
fn scan_preempt(
|
||||
dom: *Domain,
|
||||
goal: Sequence,
|
||||
clk_read: Sequence,
|
||||
should_wait: bool,
|
||||
last_read_seq: Sequence,
|
||||
) Sequence {
|
||||
rtl.barrier.fence(.seq_cst);
|
||||
var read_seq = last_read_seq;
|
||||
|
||||
// Preemption is enabled, check stalled readers.
|
||||
for (0..ke.ncpus) |i| {
|
||||
const cpu = &dom.cpus[i];
|
||||
|
||||
var seq = cpu.stall_seq.load(.monotonic);
|
||||
|
||||
while (seq != seq_invalid) {
|
||||
if (seq < clk_read) {
|
||||
seq = clk_read;
|
||||
}
|
||||
|
||||
if (goal <= seq) {
|
||||
// The goal has been reached.
|
||||
break;
|
||||
}
|
||||
|
||||
if (!should_wait) break;
|
||||
|
||||
const ipl = ke.ipl.raise(.Dispatch);
|
||||
|
||||
cpu.stall_lock.acquire_no_ipl();
|
||||
cpu.stall_goal = goal;
|
||||
cpu.stall_lock.release_no_ipl();
|
||||
|
||||
const ts = kep.turnstile.lookup(cpu);
|
||||
|
||||
seq = cpu.stall_seq.load(.monotonic);
|
||||
|
||||
// Re-check under the chain lock.
|
||||
if (seq == seq_invalid or goal <= seq) {
|
||||
kep.turnstile.exit(cpu, ts);
|
||||
ke.ipl.lower(ipl);
|
||||
break;
|
||||
}
|
||||
|
||||
kep.turnstile.block(
|
||||
ts,
|
||||
cpu,
|
||||
.{ .shared = &cpu.stalled },
|
||||
.Exclusive,
|
||||
);
|
||||
|
||||
ke.ipl.lower(ipl);
|
||||
seq = cpu.stall_seq.load(.monotonic);
|
||||
}
|
||||
|
||||
if (seq != seq_invalid) {
|
||||
// Update the minimum read sequence.
|
||||
read_seq = @min(read_seq, seq);
|
||||
}
|
||||
}
|
||||
|
||||
return read_seq;
|
||||
}
|
||||
|
||||
/// Poll to determine whether all CPUs have reached `goal`.
|
||||
/// If wait is true then this will spinloop until the goal is met.
|
||||
/// This will advance the domain read sequence if applicable.
|
||||
|
||||
@@ -8,7 +8,7 @@ const r = @import("root");
|
||||
const ke = r.ke;
|
||||
const kep = ke.private;
|
||||
|
||||
fn LockTemplate(comptime T: type) type {
|
||||
fn LockType(comptime T: type) type {
|
||||
comptime {
|
||||
if (!@hasDecl(T, "acquire_no_ipl"))
|
||||
@compileError("lock backend must implement acquire_no_ipl");
|
||||
@@ -115,7 +115,7 @@ fn LockTemplate(comptime T: type) type {
|
||||
}
|
||||
|
||||
/// Simple Spin lock implementation.
|
||||
pub const SpinLock = LockTemplate(struct {
|
||||
pub const SpinLock = LockType(struct {
|
||||
locked: std.atomic.Value(u8),
|
||||
|
||||
const Lock = @This();
|
||||
@@ -163,7 +163,7 @@ const PerCpu = struct {
|
||||
curr_idx: u32,
|
||||
};
|
||||
|
||||
const pcpu = ke.CpuLocal(PerCpu, undefined);
|
||||
const pcpu = ke.CpuLocalType(PerCpu, undefined);
|
||||
|
||||
/// Initialize a CPU's MCS nodes.
|
||||
fn init_cpu() linksection(r.init) void {
|
||||
@@ -182,7 +182,7 @@ comptime {
|
||||
|
||||
/// Queued spin lock implementation.
|
||||
/// See https://rdmsr.github.io/writing/qspinlocks
|
||||
pub const QSpinLock = LockTemplate(struct {
|
||||
pub const QSpinLock = LockType(struct {
|
||||
const Tail = packed struct(u16) {
|
||||
idx: u2,
|
||||
cpu: u14,
|
||||
|
||||
+3
-3
@@ -348,12 +348,12 @@ const Cpu = struct {
|
||||
/// Note that this is in addition to the global minimum of `ncpus` stacks.
|
||||
/// Keep this low for minimal memory overhead, but potentially
|
||||
/// increased latency.
|
||||
const excess_stacks = ke.Tunable(u32, 1, "ke.thread.excess_stacks");
|
||||
const excess_stacks = ke.TunableType(u32, 1, "ke.thread.excess_stacks");
|
||||
|
||||
var global_depot_lock: ke.SpinLock = undefined;
|
||||
var global_depot: Depot = .{};
|
||||
|
||||
const percpu = ke.CpuLocal(Cpu, undefined);
|
||||
const percpu = ke.CpuLocalType(Cpu, undefined);
|
||||
const percpu_count_max = 2;
|
||||
|
||||
const wma_unit = 256;
|
||||
@@ -368,7 +368,7 @@ inline fn wma_mix(old: u32, new: u32) u32 {
|
||||
fn init_cpu() linksection(r.init) void {
|
||||
const cpu = percpu.local();
|
||||
cpu.* = .{
|
||||
.stack_alloc_dpc = .init(stack_alloc_handler),
|
||||
.stack_alloc_dpc = ke.Dpc.init(stack_alloc_handler),
|
||||
.depot = .{},
|
||||
};
|
||||
}
|
||||
|
||||
+6
-6
@@ -37,7 +37,7 @@ const State = struct {
|
||||
|
||||
// SeqLock-protected time state.
|
||||
// Avoids a race on `initial_count` and `offset` with `update_overflow`.
|
||||
var state = rtl.SeqLock(State).init(.{
|
||||
var state = rtl.SeqLockType(State).init(.{
|
||||
.initial_count = 0,
|
||||
.offset = 0,
|
||||
});
|
||||
@@ -84,14 +84,14 @@ pub fn register_source(tc: *TimeCounter) void {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the time elapsed since boot in nanoseconds.
|
||||
pub fn read_time() r.Nanoseconds {
|
||||
const tc = best_tc orelse return .init(0);
|
||||
/// Return the time elapsed since boot.
|
||||
pub fn read_time() rtl.Timestamp {
|
||||
const tc = best_tc orelse return .{ .value = 0 };
|
||||
|
||||
const s = state.load();
|
||||
const curr_count = tc.read_count() & tc.mask;
|
||||
const elapsed = (curr_count - s.initial_count) & tc.mask;
|
||||
return .init(s.offset + ticks_to_ns(tc, elapsed));
|
||||
return .{ .value = (s.offset + ticks_to_ns(tc, elapsed)) };
|
||||
}
|
||||
|
||||
/// Return the best TimeCounter.
|
||||
@@ -100,7 +100,7 @@ pub fn best() ?*TimeCounter {
|
||||
}
|
||||
|
||||
/// Do a busy sleep of `ns` nanoseconds using TimeCounter.
|
||||
pub fn sleep(ns: r.Nanoseconds) void {
|
||||
pub fn sleep(ns: rtl.Duration) void {
|
||||
const start = read_time();
|
||||
|
||||
while (true) {
|
||||
|
||||
+9
-9
@@ -10,7 +10,7 @@ const pl = r.pl;
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const TimerHeap = rtl.PairingHeap(.Min, kep.timer.cmp_timer);
|
||||
const TimerHeap = rtl.PairingHeapType(.Min, kep.timer.cmp_timer);
|
||||
|
||||
const PerCpu = struct {
|
||||
/// Heap of pending timers on this CPU.
|
||||
@@ -35,7 +35,7 @@ pub const Timer = struct {
|
||||
/// Timer state.
|
||||
state: std.atomic.Value(State),
|
||||
/// When the timer is bound to expire.
|
||||
deadline: r.Nanoseconds,
|
||||
deadline: rtl.Timestamp,
|
||||
/// Attached DPC.
|
||||
dpc: ?*ke.Dpc,
|
||||
/// Intrusive pairing heap node.
|
||||
@@ -47,7 +47,7 @@ pub const Timer = struct {
|
||||
self.* = .{
|
||||
.hdr = undefined,
|
||||
.state = std.atomic.Value(State).init(.Stopped),
|
||||
.deadline = r.Nanoseconds.init(0),
|
||||
.deadline = .{ .value = 0 },
|
||||
.dpc = null,
|
||||
.node = .{},
|
||||
.cpu = null,
|
||||
@@ -57,10 +57,10 @@ pub const Timer = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const percpu = ke.CpuLocal(PerCpu, .{
|
||||
const percpu = ke.CpuLocalType(PerCpu, .{
|
||||
.timers = TimerHeap.init(),
|
||||
.lock = undefined,
|
||||
.dpc = .init(handle_expiry),
|
||||
.dpc = ke.Dpc.init(handle_expiry),
|
||||
});
|
||||
|
||||
fn pcpu_init() linksection(r.init) void {
|
||||
@@ -77,7 +77,7 @@ const Options = struct {
|
||||
|
||||
/// Start a timer with an expiration time.
|
||||
/// A DPC that will be enqueued upon expiration can be passed.
|
||||
pub fn set(timer: *Timer, time: r.Nanoseconds, opts: Options) void {
|
||||
pub fn set(timer: *Timer, duration: rtl.Duration, opts: Options) void {
|
||||
const ipl = timer.hdr.lock.acquire();
|
||||
defer timer.hdr.lock.release(ipl);
|
||||
|
||||
@@ -92,7 +92,7 @@ pub fn set(timer: *Timer, time: r.Nanoseconds, opts: Options) void {
|
||||
defer cpu.lock.release_no_ipl();
|
||||
|
||||
// Initialize the timer.
|
||||
timer.deadline = .init(ke.time.read_time().value + time.value);
|
||||
timer.deadline = ke.time.read_time().add(duration);
|
||||
|
||||
timer.cpu = cpu;
|
||||
timer.dpc = opts.dpc;
|
||||
@@ -102,7 +102,7 @@ pub fn set(timer: *Timer, time: r.Nanoseconds, opts: Options) void {
|
||||
|
||||
if (cpu.timers.root == &timer.node) {
|
||||
// This is the earliest timer to expire, arm the hardware timer.
|
||||
pl.arm_timer(time);
|
||||
pl.arm_timer(duration);
|
||||
}
|
||||
|
||||
// Locks dropped
|
||||
@@ -198,7 +198,7 @@ fn handle_expiry(_: *ke.Dpc, _: ?*anyopaque) void {
|
||||
if (timer.deadline.value > now.value and
|
||||
timer.deadline.value - now.value > std.time.ns_per_ms)
|
||||
{
|
||||
pl.arm_timer(.init(timer.deadline.value - now.value));
|
||||
pl.arm_timer(rtl.Duration.ns(timer.deadline.value - now.value));
|
||||
cpu.lock.release_no_ipl();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
//! Tunable kernel parameters.
|
||||
//! TunableType kernel parameters.
|
||||
//! These are variables set in the cmdline at boot and never changed.
|
||||
const std = @import("std");
|
||||
const rtl = @import("rtl");
|
||||
const r = @import("root");
|
||||
const pl = r.pl;
|
||||
|
||||
const set = rtl.LinkerSet("tunables", *const Entry);
|
||||
const set = rtl.LinkerSetType("tunables", *const Entry);
|
||||
|
||||
const Entry = struct {
|
||||
name: []const u8,
|
||||
@@ -16,7 +16,7 @@ const Entry = struct {
|
||||
},
|
||||
};
|
||||
|
||||
pub fn Tunable(
|
||||
pub fn TunableType(
|
||||
comptime T: type,
|
||||
comptime default: T,
|
||||
comptime name: []const u8,
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ pub const Status = enum(u8) {
|
||||
};
|
||||
|
||||
const Options = struct {
|
||||
timeout: ?r.Nanoseconds = null,
|
||||
timeout: ?rtl.Duration = null,
|
||||
waitblocks: ?[]WaitBlock = null,
|
||||
continuation: ?ke.Continuation = null,
|
||||
};
|
||||
|
||||
+3
-3
@@ -68,14 +68,14 @@ pub const HolderData = if (config.warden) HeldLocks else struct {};
|
||||
const num_classes = 128;
|
||||
const num_buckets = 128;
|
||||
|
||||
const cpu_held_spinlocks = ke.CpuLocal(HeldLocks, .{});
|
||||
const cpu_held_spinlocks = ke.CpuLocalType(HeldLocks, .{});
|
||||
|
||||
/// Matrix of reachability between classes
|
||||
var reachable: [num_classes]Relation = @splat(.{});
|
||||
var graph_lock: ke.SpinLock = undefined;
|
||||
|
||||
var classes: [num_classes]Class = @splat(undefined);
|
||||
var class_buckets: [num_classes]rtl.AVLTree(nodes_cmp) = @splat(.init());
|
||||
var class_buckets: [num_classes]rtl.AvlTreeType(nodes_cmp) = @splat(.init());
|
||||
var started = false;
|
||||
var num_active_classes: u32 = 0;
|
||||
|
||||
@@ -89,7 +89,7 @@ fn nodes_cmp(a: *const rtl.bst.Node, b: *const rtl.bst.Node) std.math.Order {
|
||||
/// Find a lock class by its name.
|
||||
fn find_lock_class(name: []const u8) struct {
|
||||
?*Class,
|
||||
?*rtl.AVLTree(nodes_cmp),
|
||||
?*rtl.AvlTreeType(nodes_cmp),
|
||||
} {
|
||||
if (name.len == 0) {
|
||||
// This lock shouldn't be checked.
|
||||
|
||||
+5
-1
@@ -125,7 +125,7 @@ extern var rodata_end_addr: u8;
|
||||
extern var data_start_addr: u8;
|
||||
extern var data_end_addr: u8;
|
||||
|
||||
fn map_self() void {
|
||||
fn map_executable() void {
|
||||
const layout = r.impl.get_image_layout();
|
||||
|
||||
const text_start = @intFromPtr(&text_start_addr);
|
||||
@@ -180,6 +180,10 @@ fn map_self() void {
|
||||
.global = true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn map_self() void {
|
||||
map_executable();
|
||||
|
||||
const max_hhdm_address = 4 * 1024 * 1024 * 1024;
|
||||
|
||||
|
||||
+3
-2
@@ -5,6 +5,7 @@
|
||||
//! 2. Zone trimming
|
||||
//! 3. Working-set trimming (eventually...)
|
||||
|
||||
const rtl = @import("rtl");
|
||||
const r = @import("root");
|
||||
const std = @import("std");
|
||||
const ps = r.ps;
|
||||
@@ -12,7 +13,7 @@ const ke = r.ke;
|
||||
const mm = r.mm;
|
||||
const mmp = mm.private;
|
||||
|
||||
const balance_interval = ke.Tunable(u32, 1000, "mm.balance.interval_ms");
|
||||
const balance_interval = ke.TunableType(u32, 1000, "mm.balance.interval_ms");
|
||||
|
||||
const stack_reap_interval = 5;
|
||||
const zone_update_interval = mm.zone.update_interval_s;
|
||||
@@ -60,7 +61,7 @@ fn balance_manager(_: ?*anyopaque) void {
|
||||
if (which == timeout) {
|
||||
ke.timer.set(
|
||||
&timer,
|
||||
.from(r.Milliseconds.init(balance_interval.load())),
|
||||
rtl.Duration.ms(balance_interval.load()),
|
||||
.{},
|
||||
);
|
||||
}
|
||||
|
||||
+13
-13
@@ -139,34 +139,34 @@ const slab_align = 8;
|
||||
const zones_num = 32;
|
||||
|
||||
/// Number of rounds in a magazine.
|
||||
const magazine_size = ke.Tunable(u8, 8, "mm.zone.mag_size");
|
||||
const magazine_size = ke.TunableType(u8, 8, "mm.zone.mag_size");
|
||||
|
||||
/// How much extra memory CPUs are allowed to keep around.
|
||||
const max_local_memory = ke.Tunable(
|
||||
const max_local_memory = ke.TunableType(
|
||||
u32,
|
||||
@intCast(r.kib(128)),
|
||||
"mm.zone.max_local_mem",
|
||||
);
|
||||
|
||||
/// Number of contentions allowed per second before the depot grows.
|
||||
const depot_grow_level = ke.Tunable(
|
||||
const depot_grow_level = ke.TunableType(
|
||||
u32,
|
||||
5 * wma_unit,
|
||||
"mm.zone.depot_grow_level",
|
||||
);
|
||||
|
||||
/// Number of contentions allowed per second before the depot shrinks.
|
||||
const depot_shrink_level = ke.Tunable(
|
||||
const depot_shrink_level = ke.TunableType(
|
||||
u32,
|
||||
wma_unit / 2,
|
||||
"mm.zone.depot_shrink_level",
|
||||
);
|
||||
|
||||
/// Number of excess magazines in a zone before they are trimmed.
|
||||
const excess_magazines = ke.Tunable(u32, 8, "mm.zone.excess_mags");
|
||||
const excess_magazines = ke.TunableType(u32, 8, "mm.zone.excess_mags");
|
||||
|
||||
/// Excess memory in a zone before magazines are trimmed.
|
||||
const excess_memory = ke.Tunable(
|
||||
const excess_memory = ke.TunableType(
|
||||
u32,
|
||||
@intCast(r.kib(16)),
|
||||
"mm.zone.excess_mem",
|
||||
@@ -200,7 +200,7 @@ var zone_list_lock: ke.Mutex = undefined;
|
||||
var magazine_zone: Zone = undefined;
|
||||
var cpu_zone: Zone = undefined;
|
||||
|
||||
var smr_zone: TypedZone(ke.smr.Domain) = undefined;
|
||||
var smr_zone: ZoneType(ke.smr.Domain) = undefined;
|
||||
var smr_cpu_zone: Zone = undefined;
|
||||
|
||||
pub var smr_sys_domain: *ke.smr.Domain = undefined;
|
||||
@@ -491,10 +491,10 @@ inline fn wma_mix(old: usize, new: usize) usize {
|
||||
return (3 * old + new * wma_unit) / 4;
|
||||
}
|
||||
|
||||
fn cpus_alloc() []rtl.CachePadded(Cpu) {
|
||||
fn cpus_alloc() []rtl.CachePaddedType(Cpu) {
|
||||
const ptr = cpu_zone.alloc(.{}) catch
|
||||
@panic("failed to allocate zone CPU state");
|
||||
const raw: [*]rtl.CachePadded(Cpu) = @ptrCast(@alignCast(ptr));
|
||||
const raw: [*]rtl.CachePaddedType(Cpu) = @ptrCast(@alignCast(ptr));
|
||||
return raw[0..ke.ncpus];
|
||||
}
|
||||
|
||||
@@ -533,7 +533,7 @@ pub const Zone = struct {
|
||||
dont_touch: bool,
|
||||
|
||||
/// Per-CPU state.
|
||||
cpus: []rtl.CachePadded(Cpu),
|
||||
cpus: []rtl.CachePaddedType(Cpu),
|
||||
|
||||
ctor: ?*const fn (obj: *anyopaque) void,
|
||||
dtor: ?*const fn (obj: *anyopaque) void,
|
||||
@@ -1746,7 +1746,7 @@ pub fn update() void {
|
||||
}
|
||||
|
||||
/// Parameterized version of Zone, useful for object caches.
|
||||
pub fn TypedZone(comptime T: type) type {
|
||||
pub fn ZoneType(comptime T: type) type {
|
||||
return struct {
|
||||
zone: Zone,
|
||||
|
||||
@@ -1807,7 +1807,7 @@ pub fn early_init() linksection(r.init) void {
|
||||
|
||||
/// Post-SMP initialization.
|
||||
pub fn late_init() linksection(r.init) void {
|
||||
cpu_zone.init("cpus", @sizeOf(rtl.CachePadded(Cpu)) * ke.ncpus, .{
|
||||
cpu_zone.init("cpus", @sizeOf(rtl.CachePaddedType(Cpu)) * ke.ncpus, .{
|
||||
.magazines = false,
|
||||
.alignment = std.atomic.cache_line,
|
||||
});
|
||||
@@ -1822,7 +1822,7 @@ pub fn late_init() linksection(r.init) void {
|
||||
z.cpus = cpus_alloc();
|
||||
|
||||
for (0..ke.ncpus) |i| {
|
||||
z.cpus[i] = rtl.CachePadded(Cpu).init(.{
|
||||
z.cpus[i] = rtl.CachePaddedType(Cpu).init(.{
|
||||
.lock = ke.SpinLock.init("Magazine"),
|
||||
.alloc = null,
|
||||
.free = null,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const std = @import("std");
|
||||
const amd64 = @import("arch");
|
||||
const rtl = @import("rtl");
|
||||
const r = @import("root");
|
||||
const tsc = @import("tsc.zig");
|
||||
const acpi = r.pl.acpi;
|
||||
@@ -74,31 +75,31 @@ pub const ApicRegisters = enum(u32) {
|
||||
pub var apics: std.ArrayList(u32) = .empty;
|
||||
var xapic_address: ?usize = null;
|
||||
pub var xapic_base_physical: usize = 0;
|
||||
const in_x2apic_mode = ke.CpuLocal(bool, false);
|
||||
const timer_ticks_per_us = ke.CpuLocal(u64, 0);
|
||||
const in_x2apic_mode = ke.CpuLocalType(bool, false);
|
||||
const timer_ticks_per_us = ke.CpuLocalType(u64, 0);
|
||||
|
||||
const log = std.log.scoped(.apic);
|
||||
|
||||
fn xapic_write(register: ApicRegisters, value: u32) void {
|
||||
const lapic_base = xapic_address orelse return;
|
||||
r.mmio_write(u32, lapic_base + @intFromEnum(register), value);
|
||||
r.mmio_write(u32, lapic_base + @backingInt(register), value);
|
||||
}
|
||||
|
||||
fn xapic_read(register: ApicRegisters) u32 {
|
||||
const lapic_base = xapic_address orelse return 0;
|
||||
return r.mmio_read(u32, lapic_base + @intFromEnum(register));
|
||||
return r.mmio_read(u32, lapic_base + @backingInt(register));
|
||||
}
|
||||
|
||||
fn x2apic_write(register: ApicRegisters, value: u64) void {
|
||||
amd64.wrmsr(
|
||||
@intFromEnum(amd64.Msr.X2ApicBase) + (@intFromEnum(register) >> 4),
|
||||
@backingInt(amd64.Msr.X2ApicBase) + (@backingInt(register) >> 4),
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
fn x2apic_read(register: ApicRegisters) u64 {
|
||||
return amd64.rdmsr(
|
||||
@intFromEnum(amd64.Msr.X2ApicBase) + (@intFromEnum(register) >> 4),
|
||||
@backingInt(amd64.Msr.X2ApicBase) + (@backingInt(register) >> 4),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -170,7 +171,7 @@ fn lapic_calibrate(ms: u64) u64 {
|
||||
write(.LvtTimer, (1 << 16));
|
||||
write(.TimerInitialCount, std.math.maxInt(u32));
|
||||
|
||||
ke.time.sleep(.from(r.Milliseconds.init(ms)));
|
||||
ke.time.sleep(rtl.Duration.ms(ms));
|
||||
|
||||
const ticks = std.math.maxInt(u32) - read(.TimerCurrentCount);
|
||||
|
||||
@@ -205,7 +206,7 @@ fn timer_init() linksection(r.init) void {
|
||||
timer_ticks_per_us.local().* = (avg_ticks / calib_us);
|
||||
}
|
||||
|
||||
pub fn arm_timer(ns: r.Nanoseconds) void {
|
||||
pub fn arm_timer(ns: rtl.Duration) void {
|
||||
if (amd64.cpu_features.tsc_deadline) {
|
||||
const now = amd64.rdtsc();
|
||||
|
||||
@@ -222,7 +223,7 @@ pub fn arm_timer(ns: r.Nanoseconds) void {
|
||||
write(.LvtTimer, 1 << 16);
|
||||
write(.TimerInitialCount, 0);
|
||||
|
||||
const us = ns.to(r.Microseconds).value;
|
||||
const us = ns.to_us();
|
||||
const ticks: u32 = @truncate(@max(us * timer_ticks_per_us.local().*, 1));
|
||||
|
||||
// Setup IRQ.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const rtl = @import("rtl");
|
||||
const r = @import("root");
|
||||
const pl = r.pl;
|
||||
const amd64 = r.arch;
|
||||
@@ -109,8 +110,8 @@ pub fn debug_read() u8 {
|
||||
return amd64.inb(com1);
|
||||
}
|
||||
|
||||
pub fn arm_timer(deadline: r.Nanoseconds) void {
|
||||
apic.arm_timer(deadline);
|
||||
pub fn arm_timer(duration: rtl.Duration) void {
|
||||
apic.arm_timer(duration);
|
||||
}
|
||||
|
||||
pub fn send_ipi(target_cpu: u32) void {
|
||||
|
||||
+33
-38
@@ -19,9 +19,9 @@ extern var __percpu_end: u8;
|
||||
|
||||
pub export var cpu_id_to_apic_id: [config.ncpus]u32 = undefined;
|
||||
|
||||
const start_stack = ke.ExportedCpuLocal(usize, 0, "ap_start_stack");
|
||||
const start_stack = ke.ExportedCpuLocalTypeType(usize, 0, "ap_start_stack");
|
||||
|
||||
pub const start_thread = ke.CpuLocal(*ke.Thread, undefined);
|
||||
pub const start_thread = ke.CpuLocalType(*ke.Thread, undefined);
|
||||
|
||||
var aps_booted = std.atomic.Value(usize).init(0);
|
||||
|
||||
@@ -58,6 +58,23 @@ fn make_thread(
|
||||
return td;
|
||||
}
|
||||
|
||||
fn delay_for_cpu() struct { rtl.Duration, rtl.Duration } {
|
||||
// From Linux, on modern CPUs we can skip the long delay after INIT.
|
||||
const skip_delay = switch (amd64.cpu_features.vendor) {
|
||||
.Intel => amd64.cpu_features.family >= 0x06,
|
||||
.Amd => amd64.cpu_features.family >= 0x0f,
|
||||
.Hygon => amd64.cpu_features.family >= 0x18,
|
||||
.Unknown => false,
|
||||
};
|
||||
|
||||
const init_delay = rtl.Duration.ms(if (skip_delay) 0 else 10);
|
||||
const sipi_delay = rtl.Duration.us(if (skip_delay) 10 else 300);
|
||||
|
||||
return .{ init_delay, sipi_delay };
|
||||
}
|
||||
|
||||
const ap_stack_size = r.kib(16);
|
||||
|
||||
pub fn init() linksection(r.init) void {
|
||||
const trampoline_start = @intFromPtr(&AP_TRAMPOLINE_START);
|
||||
const trampoline_size = @intFromPtr(&AP_TRAMPOLINE_END) - trampoline_start;
|
||||
@@ -69,22 +86,10 @@ pub fn init() linksection(r.init) void {
|
||||
.execute = true,
|
||||
});
|
||||
|
||||
// From Linux, on modern CPUs we can skip the long delay after INIT.
|
||||
const skip_delay = switch (amd64.cpu_features.vendor) {
|
||||
.Intel => amd64.cpu_features.family >= 0x06,
|
||||
.Amd => amd64.cpu_features.family >= 0x0f,
|
||||
.Hygon => amd64.cpu_features.family >= 0x18,
|
||||
.Unknown => false,
|
||||
};
|
||||
|
||||
const init_delay = r.Nanoseconds.init(
|
||||
if (skip_delay) 0 else std.time.ns_per_ms * 10,
|
||||
);
|
||||
const sipi_delay = r.Nanoseconds.init(
|
||||
if (skip_delay) std.time.ns_per_us * 10 else std.time.ns_per_us * 300,
|
||||
);
|
||||
|
||||
const init_delay, const sipi_delay = delay_for_cpu();
|
||||
const page: [*]u8 = @ptrFromInt(mm.p2v(0x8000));
|
||||
const pcpu_start = @intFromPtr(&__percpu_start);
|
||||
|
||||
@memcpy(
|
||||
page[0..trampoline_size],
|
||||
@as([*]u8, @ptrFromInt(trampoline_start)),
|
||||
@@ -94,19 +99,16 @@ pub fn init() linksection(r.init) void {
|
||||
const data_phys: *ApData = @ptrFromInt(mm.p2v(0x8000 + data_offset));
|
||||
|
||||
const idtr = amd64.sidtr();
|
||||
|
||||
const offsets = mm.zone.gpa.alloc(usize, apic.apics.items.len + 1) catch
|
||||
@panic("Failed to allocate AP local data offsets");
|
||||
|
||||
// Allocate per-cpu offsets for CPU-local data.
|
||||
kep.impl.cpu_offsets = @ptrCast(offsets);
|
||||
const percpu_size = @intFromPtr(&__percpu_end) -
|
||||
@intFromPtr(&__percpu_start);
|
||||
const percpu_size = @intFromPtr(&__percpu_end) - pcpu_start;
|
||||
|
||||
log.info("per-CPU data size: {} bytes", .{percpu_size});
|
||||
|
||||
// Allocate per-cpu offsets for CPU-local data.
|
||||
kep.impl.cpu_offsets = @ptrCast(offsets);
|
||||
kep.impl.cpu_offsets[0] = 0;
|
||||
|
||||
cpu_id_to_apic_id[0] = apic.get_id();
|
||||
|
||||
// Set up the AP data block.
|
||||
@@ -117,7 +119,7 @@ pub fn init() linksection(r.init) void {
|
||||
data_phys.xapic_virt_base = mm.p2v(apic.xapic_base_physical);
|
||||
|
||||
for (0..apic.apics.items.len, apic.apics.items) |i, apic_id| {
|
||||
const cpu_id = i + 1;
|
||||
const cpu_id: u32 = @as(u32, @intCast(i)) + 1;
|
||||
cpu_id_to_apic_id[cpu_id] = apic_id;
|
||||
|
||||
// Allocate per-cpu data.
|
||||
@@ -126,39 +128,32 @@ pub fn init() linksection(r.init) void {
|
||||
|
||||
@memcpy(
|
||||
cpu_data,
|
||||
@as([*]u8, @ptrCast(&__percpu_start))[0..percpu_size],
|
||||
@as([*]u8, @ptrFromInt(pcpu_start))[0..percpu_size],
|
||||
);
|
||||
|
||||
const self_offset_offset = @intFromPtr(&kep.impl.cpu_self_offset) -
|
||||
@intFromPtr(&__percpu_start);
|
||||
pcpu_start;
|
||||
|
||||
kep.impl.cpu_offsets[cpu_id] = @intFromPtr(cpu_data.ptr) -%
|
||||
@intFromPtr(&__percpu_start);
|
||||
kep.impl.cpu_offsets[cpu_id] = @intFromPtr(cpu_data.ptr) -% pcpu_start;
|
||||
|
||||
const off: *usize = @ptrCast(@alignCast(&cpu_data[self_offset_offset]));
|
||||
|
||||
// copy the offset into the AP's self_offset variable
|
||||
off.* = kep.impl.cpu_offsets[cpu_id];
|
||||
|
||||
const stack_size = r.kib(16);
|
||||
|
||||
const stack_top = @intFromPtr(mm.heap.alloc(
|
||||
stack_size,
|
||||
ap_stack_size,
|
||||
.DontWaitForMemory,
|
||||
) catch
|
||||
@panic("Failed to allocate AP stack")) + stack_size;
|
||||
@panic("Failed to allocate AP stack")) + ap_stack_size;
|
||||
|
||||
start_stack.remote(@intCast(cpu_id)).* = stack_top & ~@as(usize, 15);
|
||||
start_thread.remote(@intCast(cpu_id)).* = make_thread(
|
||||
stack_top,
|
||||
@intCast(cpu_id),
|
||||
);
|
||||
start_stack.remote(cpu_id).* = stack_top & ~@as(usize, 15);
|
||||
start_thread.remote(cpu_id).* = make_thread(stack_top, cpu_id);
|
||||
|
||||
rtl.barrier.fence(.release);
|
||||
|
||||
// Send the INIT-SIPI-SIPI sequence to start the AP.
|
||||
apic.send_init(apic_id);
|
||||
|
||||
ke.time.sleep(init_delay);
|
||||
apic.send_sipi(apic_id, 0x08);
|
||||
ke.time.sleep(sipi_delay);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Not sure if this should be in platform/pc or in kern/
|
||||
const std = @import("std");
|
||||
const amd64 = @import("arch");
|
||||
const rtl = @import("rtl");
|
||||
const r = @import("root");
|
||||
const ke = r.ke;
|
||||
|
||||
@@ -54,7 +55,7 @@ pub fn init() linksection(r.init) void {
|
||||
// Calculate how many ticks it costs to call tc_sleep.
|
||||
for (0..calib_cost_runs) |_| {
|
||||
const start = amd64.rdtsc();
|
||||
ke.time.sleep(.init(0));
|
||||
ke.time.sleep(rtl.Duration.ns(0));
|
||||
const end = amd64.rdtsc();
|
||||
calib_cost += (end - start);
|
||||
}
|
||||
@@ -62,7 +63,7 @@ pub fn init() linksection(r.init) void {
|
||||
calib_cost /= calib_cost_runs;
|
||||
|
||||
const runs = 5;
|
||||
const calib_time = 10 * std.time.ns_per_ms;
|
||||
const calib_time = rtl.Duration.ms(10);
|
||||
|
||||
// Sleep for 10ms `runs` times and measure the TSC frequency,
|
||||
// then average the middle values to get a stable estimate.
|
||||
@@ -70,9 +71,10 @@ pub fn init() linksection(r.init) void {
|
||||
|
||||
for (0..runs) |i| {
|
||||
const start = amd64.rdtsc();
|
||||
ke.time.sleep(.init(calib_time));
|
||||
ke.time.sleep(calib_time);
|
||||
const end = amd64.rdtsc();
|
||||
freqs[i] = (end - start - calib_cost) * (std.time.ns_per_s / calib_time);
|
||||
freqs[i] = (end - start - calib_cost) *
|
||||
(std.time.ns_per_s / calib_time.value);
|
||||
}
|
||||
|
||||
// Sort and average middle values, discarding min and max.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const rtl = @import("rtl");
|
||||
const r = @import("root");
|
||||
const rv64 = r.arch;
|
||||
const ke = r.ke;
|
||||
@@ -63,7 +64,10 @@ pub fn debug_read() u8 {
|
||||
}
|
||||
|
||||
pub fn send_ipi(_: u32) void {}
|
||||
pub fn arm_timer(ns: r.Nanoseconds) void {
|
||||
const ticks = @max(1, @as(u64, @intCast((@as(u128, ns.value) * timebase_hz) / std.time.ns_per_s)));
|
||||
pub fn arm_timer(ns: rtl.Duration) void {
|
||||
const ticks = @max(1, @as(
|
||||
u64,
|
||||
@intCast((@as(u128, ns.value) * timebase_hz) / std.time.ns_per_s),
|
||||
));
|
||||
set_timer(read_time() +% ticks);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
//! functions.
|
||||
|
||||
const config = @import("config");
|
||||
const r = @import("root");
|
||||
const rtl = @import("rtl");
|
||||
|
||||
pub const impl = switch (config.arch) {
|
||||
@@ -27,7 +26,7 @@ const ImplSchema = struct {
|
||||
pub fn send_ipi(cpu: u32) void {
|
||||
_ = cpu;
|
||||
}
|
||||
pub fn arm_timer(ns: r.Nanoseconds) void {
|
||||
pub fn arm_timer(ns: rtl.Duration) void {
|
||||
_ = ns;
|
||||
}
|
||||
};
|
||||
@@ -58,7 +57,7 @@ pub inline fn debug_read() u8 {
|
||||
}
|
||||
|
||||
/// Arm a one-shot timer to fire in `ns` nanoseconds.
|
||||
pub inline fn arm_timer(ns: r.Nanoseconds) void {
|
||||
pub inline fn arm_timer(ns: rtl.Duration) void {
|
||||
return impl.arm_timer(ns);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ const mm = r.mm;
|
||||
|
||||
pub const thread = @import("thread.zig");
|
||||
|
||||
pub var turnstile_zone: mm.zone.TypedZone(kep.turnstile.Turnstile) = undefined;
|
||||
pub var turnstile_zone: mm.zone.ZoneType(kep.turnstile.Turnstile) = undefined;
|
||||
|
||||
fn turnstile_ctor(ts: *kep.turnstile.Turnstile) void {
|
||||
ts.* = .{
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ pub const kernel_thread_stack_size = r.kib(16);
|
||||
|
||||
var reaper_item: ex.WorkItem = undefined;
|
||||
var stack_item: ex.WorkItem = undefined;
|
||||
var thread_zone: mm.zone.TypedZone(Thread) = undefined;
|
||||
var thread_zone: mm.zone.ZoneType(Thread) = undefined;
|
||||
|
||||
/// Higher-level wrapper over a thread.
|
||||
pub const Thread = struct {
|
||||
|
||||
@@ -443,7 +443,7 @@ const Core = struct {
|
||||
};
|
||||
|
||||
/// Scalable, lock-free hashtable backed by SMR.
|
||||
pub fn Table(
|
||||
pub fn TableType(
|
||||
comptime T: type,
|
||||
comptime link_field: []const u8,
|
||||
comptime Context: type,
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
pub const HashTable = @import("hashtable.zig").Table;
|
||||
pub const HashTableType = @import("hashtable.zig").TableType;
|
||||
|
||||
/// Singly-linked list
|
||||
pub const SList = struct {
|
||||
|
||||
+1
-6
@@ -16,17 +16,12 @@ const config = @import("config");
|
||||
|
||||
pub const init = ".text.init";
|
||||
|
||||
pub const percpu_init_set = rtl.LinkerSet("percpu_init", *const fn () void);
|
||||
pub const percpu_init_set = rtl.LinkerSetType("percpu_init", *const fn () void);
|
||||
pub const percpu = ".data.percpu";
|
||||
|
||||
pub const VAddr = usize;
|
||||
pub const PAddr = usize;
|
||||
|
||||
pub const Nanoseconds = rtl.units.Nanoseconds;
|
||||
pub const Microseconds = rtl.units.Microseconds;
|
||||
pub const Milliseconds = rtl.units.Milliseconds;
|
||||
pub const Seconds = rtl.units.Seconds;
|
||||
|
||||
pub var kernel_heap_base: usize = 0;
|
||||
pub var kernel_pfndb_base: usize = 0;
|
||||
|
||||
|
||||
+3
-3
@@ -19,17 +19,17 @@ fn set_bf(node: *bst.Node, bf: u2) void {
|
||||
node.parent.set_tag(bf);
|
||||
}
|
||||
|
||||
pub fn AVLTree(
|
||||
pub fn AvlTreeType(
|
||||
comptime cmp: fn (*const bst.Node, *const bst.Node) std.math.Order,
|
||||
) type {
|
||||
return struct {
|
||||
tree: bst.BST(cmp),
|
||||
tree: bst.BstType(cmp),
|
||||
|
||||
const Tree = @This();
|
||||
|
||||
pub fn init() Tree {
|
||||
return Tree{
|
||||
.tree = bst.BST(cmp).init(),
|
||||
.tree = bst.BstType(cmp).init(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+27
-24
@@ -16,7 +16,7 @@ fn bit_mask(comptime Word: type, bit_index: usize) Word {
|
||||
}
|
||||
|
||||
/// Static bitmap that uses the most efficient underlying word size for N < 64
|
||||
pub fn BitMap(comptime N: usize) type {
|
||||
pub fn BitmapType(comptime N: usize) type {
|
||||
return struct {
|
||||
pub const Word = if (N > 32) u64 else ClosestType(N);
|
||||
const Bitmap = @This();
|
||||
@@ -107,7 +107,8 @@ pub fn BitMap(comptime N: usize) type {
|
||||
|
||||
/// Check whether all bits in the bitmap are equal to a given value.
|
||||
pub fn is_all(self: *const Bitmap, value: bool) bool {
|
||||
const v: Bitmap.Word = if (value) std.math.maxInt(Bitmap.Word)
|
||||
const v: Bitmap.Word = if (value)
|
||||
std.math.maxInt(Bitmap.Word)
|
||||
else
|
||||
0;
|
||||
for (self.storage) |word| {
|
||||
@@ -118,9 +119,9 @@ pub fn BitMap(comptime N: usize) type {
|
||||
};
|
||||
}
|
||||
|
||||
/// Atomic static bitmap with the same API as `BitMap`, but each operation
|
||||
/// Atomic static bitmap with the same API as `BitmapType`, but each operation
|
||||
/// accepts a memory ordering.
|
||||
pub fn AtomicBitMap(comptime N: usize) type {
|
||||
pub fn AtomicBitmapType(comptime N: usize) type {
|
||||
return struct {
|
||||
pub const Word = if (N > 32) u64 else ClosestType(N);
|
||||
const Bitmap = @This();
|
||||
@@ -250,7 +251,9 @@ pub fn AtomicBitMap(comptime N: usize) type {
|
||||
value: bool,
|
||||
comptime order: AtomicOrder,
|
||||
) bool {
|
||||
const v: Bitmap.Word = if (value) std.math.maxInt(Bitmap.Word) else
|
||||
const v: Bitmap.Word = if (value)
|
||||
std.math.maxInt(Bitmap.Word)
|
||||
else
|
||||
0;
|
||||
for (self.storage) |word| {
|
||||
if (word.load(order) != v) return false;
|
||||
@@ -260,19 +263,19 @@ pub fn AtomicBitMap(comptime N: usize) type {
|
||||
};
|
||||
}
|
||||
|
||||
test "BitMap has proper underlying type" {
|
||||
assert(BitMap(8).Word == u8);
|
||||
assert(BitMap(10).Word == u16);
|
||||
assert(BitMap(16).Word == u16);
|
||||
assert(BitMap(21).Word == u32);
|
||||
assert(BitMap(32).Word == u32);
|
||||
assert(BitMap(64).Word == u64);
|
||||
assert(BitMap(128).Word == u64);
|
||||
assert(BitMap(1024).Word == u64);
|
||||
test "BitmapType has proper underlying type" {
|
||||
assert(BitmapType(8).Word == u8);
|
||||
assert(BitmapType(10).Word == u16);
|
||||
assert(BitmapType(16).Word == u16);
|
||||
assert(BitmapType(21).Word == u32);
|
||||
assert(BitmapType(32).Word == u32);
|
||||
assert(BitmapType(64).Word == u64);
|
||||
assert(BitmapType(128).Word == u64);
|
||||
assert(BitmapType(1024).Word == u64);
|
||||
}
|
||||
|
||||
test BitMap {
|
||||
var bitmap = BitMap(10).init(false);
|
||||
test BitmapType {
|
||||
var bitmap = BitmapType(10).init(false);
|
||||
|
||||
assert(@TypeOf(bitmap).Word == u16);
|
||||
assert(bitmap.get(0) == false);
|
||||
@@ -285,8 +288,8 @@ test BitMap {
|
||||
assert(bitmap.get(0) == false);
|
||||
}
|
||||
|
||||
test "BitMap count and iter" {
|
||||
var bitmap = BitMap(70).init(false);
|
||||
test "BitmapType count and iter" {
|
||||
var bitmap = BitmapType(70).init(false);
|
||||
|
||||
assert(bitmap.count() == 0);
|
||||
{
|
||||
@@ -314,7 +317,7 @@ test "BitMap count and iter" {
|
||||
|
||||
assert(bitmap.count() == 4);
|
||||
|
||||
var all = BitMap(70).init(true);
|
||||
var all = BitmapType(70).init(true);
|
||||
assert(all.count() == 70);
|
||||
|
||||
var all_it = all.iter();
|
||||
@@ -325,8 +328,8 @@ test "BitMap count and iter" {
|
||||
assert(expected == 70);
|
||||
}
|
||||
|
||||
test AtomicBitMap {
|
||||
var bitmap = AtomicBitMap(10).init(false);
|
||||
test AtomicBitmapType {
|
||||
var bitmap = AtomicBitmapType(10).init(false);
|
||||
|
||||
assert(@TypeOf(bitmap).Word == u16);
|
||||
assert(bitmap.get(0, .seq_cst) == false);
|
||||
@@ -341,8 +344,8 @@ test AtomicBitMap {
|
||||
assert(bitmap.is_all(false, .seq_cst) == true);
|
||||
}
|
||||
|
||||
test "AtomicBitMap count and iter" {
|
||||
var bitmap = AtomicBitMap(70).init(false);
|
||||
test "AtomicBitmapType count and iter" {
|
||||
var bitmap = AtomicBitmapType(70).init(false);
|
||||
|
||||
assert(bitmap.count(.seq_cst) == 0);
|
||||
{
|
||||
@@ -370,7 +373,7 @@ test "AtomicBitMap count and iter" {
|
||||
|
||||
assert(bitmap.count(.seq_cst) == 4);
|
||||
|
||||
var all = AtomicBitMap(70).init(true);
|
||||
var all = AtomicBitmapType(70).init(true);
|
||||
assert(all.count(.seq_cst) == 70);
|
||||
|
||||
var all_it = all.iter(.seq_cst);
|
||||
|
||||
+7
-5
@@ -1,10 +1,10 @@
|
||||
const TaggedPtr = @import("tagged_ptr.zig").TaggedPtr;
|
||||
const TaggedPtrType = @import("tagged_ptr.zig").TaggedPtrType;
|
||||
const std = @import("std");
|
||||
|
||||
pub const Node = struct {
|
||||
left: *Node,
|
||||
right: *Node,
|
||||
parent: TaggedPtr(Node),
|
||||
parent: TaggedPtrType(Node),
|
||||
};
|
||||
|
||||
pub var nil: Node = .{
|
||||
@@ -16,7 +16,9 @@ pub var nil: Node = .{
|
||||
/// Base struct for binary search trees.
|
||||
/// This should be used as a field in other types that are based on
|
||||
/// binary search trees, such as red-black trees or AVL trees.
|
||||
pub fn BST(comptime cmp: fn (*const Node, *const Node) std.math.Order) type {
|
||||
pub fn BstType(
|
||||
comptime cmp: fn (*const Node, *const Node) std.math.Order,
|
||||
) type {
|
||||
return struct {
|
||||
const Tree = @This();
|
||||
|
||||
@@ -166,8 +168,8 @@ fn node_cmp(a: *const Node, b: *const Node) std.math.Order {
|
||||
return .eq;
|
||||
}
|
||||
|
||||
test BST {
|
||||
var tree = BST(node_cmp).init();
|
||||
test BstType {
|
||||
var tree = BstType(node_cmp).init();
|
||||
|
||||
var nodes = [_]MyNode{
|
||||
.{ .value = 5, .node = undefined },
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const std = @import("std");
|
||||
|
||||
/// Type padded to a cache line.
|
||||
pub fn CachePadded(comptime T: type) type {
|
||||
pub fn CachePaddedType(comptime T: type) type {
|
||||
return struct {
|
||||
const cache_line = std.atomic.cache_line;
|
||||
const value_size = @sizeOf(T);
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
const std = @import("std");
|
||||
|
||||
/// Used for declaring global objects that get collected by the linker
|
||||
/// into a `LinkerSet`. This works by declaring each set its own section
|
||||
/// into a `LinkerSetType`. This works by declaring each set its own section
|
||||
/// and abusing linker-generated `__start_<name>` and `__stop_<name>` symbols.
|
||||
/// Each set stores pointers to objects, so `T` must be a pointer type.
|
||||
pub fn LinkerSet(name: []const u8, comptime T: type) type {
|
||||
pub fn LinkerSetType(name: []const u8, comptime T: type) type {
|
||||
if (@typeInfo(T) != .pointer) {
|
||||
@compileError("LinkerSet type must be a pointer type");
|
||||
@compileError("LinkerSetType type must be a pointer type");
|
||||
}
|
||||
|
||||
return struct {
|
||||
|
||||
@@ -8,7 +8,7 @@ pub const Node = struct {
|
||||
prev: ?*Node = null,
|
||||
};
|
||||
|
||||
pub fn PairingHeap(
|
||||
pub fn PairingHeapType(
|
||||
comptime order: enum { Min, Max },
|
||||
comptime cmp: fn (*Node, *Node) std.math.Order,
|
||||
) type {
|
||||
@@ -178,8 +178,8 @@ fn my_cmp(a: *Node, b: *Node) std.math.Order {
|
||||
return std.math.order(ta.value, tb.value);
|
||||
}
|
||||
|
||||
const MinHeap = PairingHeap(.Min, my_cmp);
|
||||
const MaxHeap = PairingHeap(.Max, my_cmp);
|
||||
const MinHeap = PairingHeapType(.Min, my_cmp);
|
||||
const MaxHeap = PairingHeapType(.Max, my_cmp);
|
||||
|
||||
test "empty heap" {
|
||||
var heap = MinHeap.init();
|
||||
|
||||
+7
-7
@@ -15,11 +15,11 @@ fn set_color(node: *bst.Node, color: u2) void {
|
||||
node.parent.set_tag(color);
|
||||
}
|
||||
|
||||
pub fn RBTree(
|
||||
pub fn RbTreeType(
|
||||
comptime cmp: fn (*const bst.Node, *const bst.Node) std.math.Order,
|
||||
) type {
|
||||
return struct {
|
||||
tree: bst.BST(cmp),
|
||||
tree: bst.BstType(cmp),
|
||||
|
||||
const Tree = @This();
|
||||
|
||||
@@ -73,7 +73,7 @@ pub fn RBTree(
|
||||
|
||||
pub fn init() Tree {
|
||||
return Tree{
|
||||
.tree = bst.BST(cmp).init(),
|
||||
.tree = bst.BstType(cmp).init(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -291,7 +291,7 @@ fn cmp_test_node(a: *const bst.Node, b: *const bst.Node) std.math.Order {
|
||||
return .eq;
|
||||
}
|
||||
|
||||
fn get_black_height(tree: *RBTree(cmp_test_node), node: *bst.Node) !usize {
|
||||
fn get_black_height(tree: *RbTreeType(cmp_test_node), node: *bst.Node) !usize {
|
||||
if (tree.tree.is_nil(node)) return 1;
|
||||
|
||||
const left_bh = try get_black_height(tree, node.left);
|
||||
@@ -309,17 +309,17 @@ fn get_black_height(tree: *RBTree(cmp_test_node), node: *bst.Node) !usize {
|
||||
return left_bh + if (get_color(node) == black) @as(usize, 1) else 0;
|
||||
}
|
||||
|
||||
fn check_invariants(tree: *RBTree(cmp_test_node)) !void {
|
||||
fn check_invariants(tree: *RbTreeType(cmp_test_node)) !void {
|
||||
if (tree.tree.is_empty()) return;
|
||||
try std.testing.expect(get_color(tree.tree.root) == black);
|
||||
_ = try get_black_height(tree, tree.tree.root);
|
||||
}
|
||||
|
||||
fn make_tree() RBTree(cmp_test_node) {
|
||||
fn make_tree() RbTreeType(cmp_test_node) {
|
||||
return .init();
|
||||
}
|
||||
|
||||
fn insert_all(tree: *RBTree(cmp_test_node), nodes: []TestNode) !void {
|
||||
fn insert_all(tree: *RbTreeType(cmp_test_node), nodes: []TestNode) !void {
|
||||
for (nodes) |*n| try tree.insert(&n.node);
|
||||
}
|
||||
|
||||
|
||||
+12
-11
@@ -1,22 +1,23 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub const units = @import("units.zig");
|
||||
pub const Duration = @import("units.zig").Duration;
|
||||
pub const Timestamp = @import("units.zig").Timestamp;
|
||||
pub const List = @import("list.zig").List;
|
||||
pub const SeqLock = @import("seqlock.zig").SeqLock;
|
||||
pub const SeqLockType = @import("seqlock.zig").SeqLockType;
|
||||
pub const barrier = @import("barrier.zig");
|
||||
pub const cmdline = @import("cmdline.zig");
|
||||
pub const pairing_heap = @import("pairing_heap.zig");
|
||||
pub const PairingHeap = pairing_heap.PairingHeap;
|
||||
pub const TaggedPtr = @import("tagged_ptr.zig").TaggedPtr;
|
||||
pub const PairingHeapType = pairing_heap.PairingHeapType;
|
||||
pub const TaggedPtrType = @import("tagged_ptr.zig").TaggedPtrType;
|
||||
pub const bst = @import("bst.zig");
|
||||
pub const BST = bst.BST;
|
||||
pub const RBTree = @import("rbtree.zig").RBTree;
|
||||
pub const AVLTree = @import("avl.zig").AVLTree;
|
||||
pub const BitMap = @import("bitmap.zig").BitMap;
|
||||
pub const AtomicBitMap = @import("bitmap.zig").AtomicBitMap;
|
||||
pub const BstType = bst.BstType;
|
||||
pub const RbTreeType = @import("rbtree.zig").RbTreeType;
|
||||
pub const AvlTreeType = @import("avl.zig").AvlTreeType;
|
||||
pub const BitmapType = @import("bitmap.zig").BitmapType;
|
||||
pub const AtomicBitmapType = @import("bitmap.zig").AtomicBitmapType;
|
||||
pub const HandoffList = @import("handoff.zig").HandoffList;
|
||||
pub const LinkerSet = @import("linker_set.zig").LinkerSet;
|
||||
pub const CachePadded = @import("cache_padded.zig").CachePadded;
|
||||
pub const LinkerSetType = @import("linker_set.zig").LinkerSetType;
|
||||
pub const CachePaddedType = @import("cache_padded.zig").CachePaddedType;
|
||||
|
||||
pub fn comptime_error(comptime msg: []const u8, args: anytype) void {
|
||||
@compileError(std.fmt.comptimePrint(msg, args));
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
const std = @import("std");
|
||||
const rtl = @import("rtl");
|
||||
|
||||
pub fn SeqLock(comptime T: type) type {
|
||||
pub fn SeqLockType(comptime T: type) type {
|
||||
return struct {
|
||||
const Lock = @This();
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/// Struct used to wrap a pointer with a 2-bit tag in the lower bits.
|
||||
/// This assumes that the pointer is at least 4-byte aligned.
|
||||
pub fn TaggedPtr(comptime T: type) type {
|
||||
pub fn TaggedPtrType(comptime T: type) type {
|
||||
return struct {
|
||||
value: usize,
|
||||
|
||||
@@ -39,9 +39,9 @@ pub fn TaggedPtr(comptime T: type) type {
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
test TaggedPtr {
|
||||
test TaggedPtrType {
|
||||
var x: u32 = 0;
|
||||
var tagged = TaggedPtr(u32).init(&x, 2);
|
||||
var tagged = TaggedPtrType(u32).init(&x, 2);
|
||||
|
||||
try std.testing.expectEqual(&x, tagged.ptr());
|
||||
try std.testing.expectEqual(2, tagged.tag());
|
||||
|
||||
+43
-54
@@ -1,67 +1,56 @@
|
||||
/// Type-safe measurement units.
|
||||
const std = @import("std");
|
||||
|
||||
const Dimension = enum { Time };
|
||||
|
||||
fn scale_value(
|
||||
comptime from_scale: u64,
|
||||
comptime to_scale: u64,
|
||||
pub const Duration = struct {
|
||||
/// Value, in nanoseconds.
|
||||
value: u64,
|
||||
) u64 {
|
||||
if (comptime from_scale >= to_scale) {
|
||||
return value * comptime (from_scale / to_scale);
|
||||
} else {
|
||||
return value / comptime (to_scale / from_scale);
|
||||
|
||||
pub fn ns(count: u64) Duration {
|
||||
return .{ .value = count };
|
||||
}
|
||||
}
|
||||
|
||||
pub fn Unit(
|
||||
comptime dim: Dimension,
|
||||
comptime scale: u64,
|
||||
comptime unit_suffix: []const u8,
|
||||
) type {
|
||||
return struct {
|
||||
const UnitType = @This();
|
||||
pub const dimension = dim;
|
||||
pub const base_per_unit = scale;
|
||||
pub fn us(count: u64) Duration {
|
||||
return .{ .value = std.time.ns_per_us * count };
|
||||
}
|
||||
|
||||
value: u64,
|
||||
pub fn ms(count: u64) Duration {
|
||||
return .{ .value = std.time.ns_per_ms * count };
|
||||
}
|
||||
|
||||
fn check(comptime T: type) void {
|
||||
comptime {
|
||||
if (!(@typeInfo(T) == .@"struct" and @hasDecl(T, "dimension")))
|
||||
@compileError(@typeName(T) ++ " is not a unit");
|
||||
if (T.dimension != dim)
|
||||
@compileError("cannot convert between " ++
|
||||
@tagName(T.dimension) ++
|
||||
" and " ++ @tagName(dim));
|
||||
}
|
||||
}
|
||||
pub fn seconds(count: u64) Duration {
|
||||
return .{ .value = std.time.ns_per_s * count };
|
||||
}
|
||||
|
||||
pub fn init(value: u64) UnitType {
|
||||
return .{ .value = value };
|
||||
}
|
||||
pub fn to_us(duration: Duration) u64 {
|
||||
return duration.value / std.time.ns_per_us;
|
||||
}
|
||||
|
||||
pub fn to(self: UnitType, comptime T: type) T {
|
||||
check(T);
|
||||
return T.init(scale_value(scale, T.base_per_unit, self.value));
|
||||
}
|
||||
pub fn to_ms(duration: Duration) u64 {
|
||||
return duration.value / std.time.ns_per_ms;
|
||||
}
|
||||
|
||||
pub fn from(other: anytype) UnitType {
|
||||
const From = @TypeOf(other);
|
||||
check(From);
|
||||
return UnitType.init(
|
||||
scale_value(From.base_per_unit, scale, other.value),
|
||||
);
|
||||
}
|
||||
pub fn to_seconds(duration: Duration) u64 {
|
||||
return duration.value / std.time.ns_per_s;
|
||||
}
|
||||
};
|
||||
|
||||
pub inline fn format(self: UnitType, writer: *std.Io.Writer) !void {
|
||||
try writer.print("{} {s}", .{ self.value, unit_suffix });
|
||||
}
|
||||
};
|
||||
}
|
||||
pub const Timestamp = struct {
|
||||
/// Value, in nanoseconds.
|
||||
value: u64,
|
||||
|
||||
pub const Nanoseconds = Unit(.Time, 1, "ns");
|
||||
pub const Microseconds = Unit(.Time, std.time.ns_per_us, "us");
|
||||
pub const Milliseconds = Unit(.Time, std.time.ns_per_ms, "ms");
|
||||
pub const Seconds = Unit(.Time, std.time.ns_per_s, "s");
|
||||
pub fn add(now: Timestamp, duration: Duration) Timestamp {
|
||||
return .{ .value = now.value + duration.value };
|
||||
}
|
||||
|
||||
pub fn to_us(timestamp: Timestamp) u64 {
|
||||
return timestamp.value / std.time.ns_per_us;
|
||||
}
|
||||
|
||||
pub fn to_ms(timestamp: Timestamp) u64 {
|
||||
return timestamp.value / std.time.ns_per_ms;
|
||||
}
|
||||
|
||||
pub fn to_seconds(timestamp: Timestamp) u64 {
|
||||
return timestamp.value / std.time.ns_per_s;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Fireworks test ported from the Boron operating system.
|
||||
const std = @import("std");
|
||||
const rtl = @import("rtl");
|
||||
const r = @import("root");
|
||||
const ke = r.ke;
|
||||
const mm = r.mm;
|
||||
@@ -110,7 +111,7 @@ fn get_random_color() u32 {
|
||||
|
||||
fn sleep(ms: usize, continuation: ?ke.Continuation) void {
|
||||
_ = ke.wait.wait_any(&.{}, "sleep", .{
|
||||
.timeout = .from(r.Milliseconds.init(ms)),
|
||||
.timeout = rtl.Duration.ms(ms),
|
||||
.continuation = continuation,
|
||||
}) catch {
|
||||
return;
|
||||
|
||||
+5
-4
@@ -88,7 +88,7 @@ const Errors = struct {
|
||||
line_num: usize,
|
||||
) void {
|
||||
errors.emit(
|
||||
"{s}:{d}: error: type name '{s}' should be PascalCase\n",
|
||||
"{s}:{d}: error: type name '{s}' should be PascalCase + Type\n",
|
||||
.{ file.path, line_num, name },
|
||||
);
|
||||
}
|
||||
@@ -298,8 +298,10 @@ fn tidy_function(
|
||||
errors.add_function_not_documented(name, file, lineno + 1);
|
||||
}
|
||||
|
||||
if (returns_type and std.ascii.isLower(name[0])) {
|
||||
errors.add_wrong_fn_type_name(name, file, lineno + 1);
|
||||
if (returns_type) {
|
||||
if (std.ascii.isLower(name[0]) or !std.mem.endsWith(u8, name, "Type")) {
|
||||
errors.add_wrong_fn_type_name(name, file, lineno + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!returns_type) {
|
||||
@@ -359,7 +361,6 @@ fn tidy_banned(file: SourceFile, errors: *Errors) void {
|
||||
.{ "catch unreachable", "proper error handling or documentation" },
|
||||
.{ "catch {}", "proper error handling or documentation" },
|
||||
.{ "= .init(", "full type name" },
|
||||
.{ ".?", "orelse unreachable" },
|
||||
};
|
||||
|
||||
for (ban_list) |ban_item| {
|
||||
|
||||
Reference in New Issue
Block a user