mirror of
https://github.com/rdmsr/zag.git
synced 2026-11-01 23:58:21 +00:00
rtl: add LinkerSet
This commit is contained in:
@@ -52,12 +52,6 @@ SECTIONS
|
|||||||
} :rodata
|
} :rodata
|
||||||
|
|
||||||
|
|
||||||
.percpu_init_array : {
|
|
||||||
__init_array_percpu_start = .;
|
|
||||||
KEEP(*(.percpu_init_array))
|
|
||||||
__init_array_percpu_end = .;
|
|
||||||
} :rodata
|
|
||||||
|
|
||||||
/* Add a .note.gnu.build-id output section in case a build ID flag is added to the */
|
/* Add a .note.gnu.build-id output section in case a build ID flag is added to the */
|
||||||
/* linker command. */
|
/* linker command. */
|
||||||
.note.gnu.build-id : {
|
.note.gnu.build-id : {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const mm = r.mm;
|
|||||||
const ex = r.ex;
|
const ex = r.ex;
|
||||||
const ps = r.ps;
|
const ps = r.ps;
|
||||||
const exp = ex.private;
|
const exp = ex.private;
|
||||||
|
const rtl = @import("rtl");
|
||||||
|
|
||||||
// XXX
|
// XXX
|
||||||
const fbconsole = @import("../dev/fbconsole.zig");
|
const fbconsole = @import("../dev/fbconsole.zig");
|
||||||
|
|||||||
+3
-9
@@ -5,22 +5,16 @@ const r = @import("root");
|
|||||||
const ke = r.ke;
|
const ke = r.ke;
|
||||||
const ki = r.ke.private;
|
const ki = r.ke.private;
|
||||||
|
|
||||||
extern var __init_array_percpu_start: u8;
|
|
||||||
extern var __init_array_percpu_end: u8;
|
|
||||||
|
|
||||||
const id = CpuLocal(u32, 0);
|
const id = CpuLocal(u32, 0);
|
||||||
|
|
||||||
/// Initialize a CPU. Must be called on all CPUs.
|
/// Initialize a CPU. Must be called on all CPUs.
|
||||||
pub fn init_cpu(cpu_id: u32) void {
|
pub fn init_cpu(cpu_id: u32) void {
|
||||||
const start = @intFromPtr(&__init_array_percpu_start);
|
const elems = r.percpu_init_set.elems();
|
||||||
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);
|
|
||||||
|
|
||||||
id.local().* = cpu_id;
|
id.local().* = cpu_id;
|
||||||
|
|
||||||
for (0..count) |i| {
|
for (elems) |func| {
|
||||||
funcs[i]();
|
func();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -40,12 +40,14 @@ const pcpu = ke.CpuLocal(PerCpu, .{
|
|||||||
.queue = undefined,
|
.queue = undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const dpc_percpu_init linksection(r.percpu_init) = &pcpu_init;
|
fn pcpu_init() linksection(r.init) void {
|
||||||
|
|
||||||
fn pcpu_init() linksection(r.init) callconv(.c) void {
|
|
||||||
pcpu.local().queue.init();
|
pcpu.local().queue.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
_ = r.percpu_init_set.insert(&pcpu_init);
|
||||||
|
}
|
||||||
|
|
||||||
/// Enqueue a DPC.
|
/// Enqueue a DPC.
|
||||||
/// If the DPC is already enqueued, this function is a no-op.
|
/// If the DPC is already enqueued, this function is a no-op.
|
||||||
pub fn enqueue(dpc: *Dpc, arg: ?*anyopaque) void {
|
pub fn enqueue(dpc: *Dpc, arg: ?*anyopaque) void {
|
||||||
|
|||||||
+5
-3
@@ -384,10 +384,8 @@ pub fn update_priority_locked(td: *ke.Thread, new_prio: u8) void {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sched_percpu_init linksection(r.percpu_init) = &init_cpu;
|
|
||||||
|
|
||||||
/// Initialize a CPU for use by the scheduler.
|
/// Initialize a CPU for use by the scheduler.
|
||||||
fn init_cpu() linksection(r.init) callconv(.c) void {
|
fn init_cpu() linksection(r.init) void {
|
||||||
var cpu = percpu.local();
|
var cpu = percpu.local();
|
||||||
|
|
||||||
cpu.* = .{
|
cpu.* = .{
|
||||||
@@ -421,6 +419,10 @@ fn init_cpu() linksection(r.init) callconv(.c) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
_ = r.percpu_init_set.insert(&init_cpu);
|
||||||
|
}
|
||||||
|
|
||||||
/// Called on CPU 0 to initialize load balancing mechanisms.
|
/// Called on CPU 0 to initialize load balancing mechanisms.
|
||||||
pub fn late_init() linksection(r.init) void {
|
pub fn late_init() linksection(r.init) void {
|
||||||
balance_timer.init();
|
balance_timer.init();
|
||||||
|
|||||||
@@ -49,9 +49,7 @@ var sync_addr: r.VAddr = 0;
|
|||||||
var sync_count: usize = 0;
|
var sync_count: usize = 0;
|
||||||
var shootdown_lock: ke.SpinLock = .init();
|
var shootdown_lock: ke.SpinLock = .init();
|
||||||
|
|
||||||
export const tlb_percpu_init linksection(r.percpu_init) = &pcpu_init;
|
fn pcpu_init() linksection(r.init) void {
|
||||||
|
|
||||||
fn pcpu_init() linksection(r.init) callconv(.c) void {
|
|
||||||
const local = percpu.local();
|
const local = percpu.local();
|
||||||
|
|
||||||
local.valid_states = @splat(.init(0));
|
local.valid_states = @splat(.init(0));
|
||||||
@@ -59,6 +57,10 @@ fn pcpu_init() linksection(r.init) callconv(.c) void {
|
|||||||
local.states = @splat(.{ .base = 0, .npages = 0, .state = .init(slot_free), .payload = undefined, .link = undefined });
|
local.states = @splat(.{ .base = 0, .npages = 0, .state = .init(slot_free), .payload = undefined, .link = undefined });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
_ = r.percpu_init_set.insert(&pcpu_init);
|
||||||
|
}
|
||||||
|
|
||||||
/// Allocate an invalidation state slot on this CPU.
|
/// Allocate an invalidation state slot on this CPU.
|
||||||
/// O(N) algorithm but N is tiny.
|
/// O(N) algorithm but N is tiny.
|
||||||
fn allocate_slot(cpu: *PerCpu) ?usize {
|
fn allocate_slot(cpu: *PerCpu) ?usize {
|
||||||
|
|||||||
@@ -124,10 +124,8 @@ const PerCpu = struct {
|
|||||||
|
|
||||||
const pcpu = ke.CpuLocal(PerCpu, undefined);
|
const pcpu = ke.CpuLocal(PerCpu, undefined);
|
||||||
|
|
||||||
export const qspinlock_percpu_init linksection(r.percpu_init) = &init_cpu;
|
|
||||||
|
|
||||||
/// Initialize a CPU's MCS nodes.
|
/// Initialize a CPU's MCS nodes.
|
||||||
fn init_cpu() linksection(r.init) callconv(.c) void {
|
fn init_cpu() linksection(r.init) void {
|
||||||
var cpu = pcpu.local();
|
var cpu = pcpu.local();
|
||||||
cpu.curr_idx = 0;
|
cpu.curr_idx = 0;
|
||||||
|
|
||||||
@@ -137,6 +135,10 @@ fn init_cpu() linksection(r.init) callconv(.c) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
_ = r.percpu_init_set.insert(&init_cpu);
|
||||||
|
}
|
||||||
|
|
||||||
/// Queued spin lock implementation.
|
/// Queued spin lock implementation.
|
||||||
/// See https://rdmsr.github.io/writing/qspinlocks
|
/// See https://rdmsr.github.io/writing/qspinlocks
|
||||||
pub const QSpinLock = LockTemplate(struct {
|
pub const QSpinLock = LockTemplate(struct {
|
||||||
|
|||||||
+23
-19
@@ -5,6 +5,8 @@ const rtl = @import("rtl");
|
|||||||
const r = @import("root");
|
const r = @import("root");
|
||||||
const pl = r.pl;
|
const pl = r.pl;
|
||||||
|
|
||||||
|
const set = rtl.LinkerSet("tunables", *const Entry);
|
||||||
|
|
||||||
const Entry = struct {
|
const Entry = struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
address: *anyopaque,
|
address: *anyopaque,
|
||||||
@@ -18,7 +20,7 @@ pub fn Tunable(comptime T: type, comptime default: T, comptime name: []const u8)
|
|||||||
return struct {
|
return struct {
|
||||||
var storage: T = default;
|
var storage: T = default;
|
||||||
|
|
||||||
const metadata: Entry linksection(".data.tunable") =
|
const metadata: Entry =
|
||||||
.{
|
.{
|
||||||
.name = name,
|
.name = name,
|
||||||
.address = &storage,
|
.address = &storage,
|
||||||
@@ -30,33 +32,32 @@ pub fn Tunable(comptime T: type, comptime default: T, comptime name: []const u8)
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
_ = set.insert(&metadata);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load() T {
|
pub fn load() T {
|
||||||
_ = metadata;
|
|
||||||
return storage;
|
return storage;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
extern var __tunables_start: u8;
|
|
||||||
extern var __tunables_end: u8;
|
|
||||||
|
|
||||||
pub fn init(boot_info: *pl.BootInfo) void {
|
pub fn init(boot_info: *pl.BootInfo) void {
|
||||||
const cmdline = boot_info.cmdline orelse return;
|
const cmdline = boot_info.cmdline orelse return;
|
||||||
|
|
||||||
const start = @intFromPtr(&__tunables_start);
|
const elems = set.elems();
|
||||||
const end = @intFromPtr(&__tunables_end);
|
|
||||||
const count = (end - start) / @sizeOf(Entry);
|
|
||||||
const entries: [*]Entry = @ptrFromInt(start);
|
|
||||||
|
|
||||||
for (0..count) |i| {
|
for (elems) |entry| {
|
||||||
switch (entries[i].type) {
|
switch (entry.type) {
|
||||||
.num => |info| {
|
.num => |info| {
|
||||||
const a = entries[i].address;
|
const a = entry.address;
|
||||||
|
|
||||||
switch (info.signedness) {
|
switch (info.signedness) {
|
||||||
.unsigned => {
|
.unsigned => {
|
||||||
const value = rtl.cmdline.get_number(u64, cmdline, entries[i].name) orelse {
|
const value = rtl.cmdline.get_number(u64, cmdline, entry.name) catch |e| {
|
||||||
std.log.warn("Invalid number value for '{s}', falling back.", .{entries[i].name});
|
if (e == error.Format) {
|
||||||
|
std.log.warn("Invalid number value for '{s}', falling back.", .{entry.name});
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -70,8 +71,11 @@ pub fn init(boot_info: *pl.BootInfo) void {
|
|||||||
},
|
},
|
||||||
|
|
||||||
.signed => {
|
.signed => {
|
||||||
const value = rtl.cmdline.get_number(i64, cmdline, entries[i].name) orelse {
|
const value = rtl.cmdline.get_number(i64, cmdline, entry.name) catch |e| {
|
||||||
std.log.warn("Invalid number value for '{s}', falling back.", .{entries[i].name});
|
if (e == error.Format) {
|
||||||
|
std.log.warn("Invalid number value for '{s}', falling back.", .{entry.name});
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -87,15 +91,15 @@ pub fn init(boot_info: *pl.BootInfo) void {
|
|||||||
},
|
},
|
||||||
|
|
||||||
.bool => {
|
.bool => {
|
||||||
const value = rtl.cmdline.get_string(cmdline, entries[i].name) orelse continue;
|
const value = rtl.cmdline.get_string(cmdline, entry.name) orelse continue;
|
||||||
const ptr: *bool = @ptrCast(@alignCast(entries[i].address));
|
const ptr: *bool = @ptrCast(@alignCast(entry.address));
|
||||||
|
|
||||||
if (std.mem.eql(u8, value, "true")) {
|
if (std.mem.eql(u8, value, "true")) {
|
||||||
ptr.* = true;
|
ptr.* = true;
|
||||||
} else if (std.mem.eql(u8, value, "false")) {
|
} else if (std.mem.eql(u8, value, "false")) {
|
||||||
ptr.* = false;
|
ptr.* = false;
|
||||||
} else {
|
} else {
|
||||||
std.log.warn("Invalid cmdline boolean value for tunable '{s}'", .{entries[i].name});
|
std.log.warn("Invalid cmdline boolean value for tunable '{s}'", .{entry.name});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -11,7 +11,8 @@ pub const ps = @import("proc/root.zig");
|
|||||||
const config = @import("config");
|
const config = @import("config");
|
||||||
|
|
||||||
pub const init = ".text.init";
|
pub const init = ".text.init";
|
||||||
pub const percpu_init = ".percpu_init_array";
|
|
||||||
|
pub const percpu_init_set = rtl.LinkerSet("percpu_init", *const fn () void);
|
||||||
pub const percpu = ".data.percpu";
|
pub const percpu = ".data.percpu";
|
||||||
|
|
||||||
pub const VAddr = usize;
|
pub const VAddr = usize;
|
||||||
|
|||||||
+3
-3
@@ -17,13 +17,13 @@ pub fn get_string(cmdline: []const u8, key: []const u8) ?[]const u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the number value of `key` in `cmdline`.
|
/// Get the number value of `key` in `cmdline`.
|
||||||
pub fn get_number(comptime T: type, cmdline: []const u8, key: []const u8) ?T {
|
pub fn get_number(comptime T: type, cmdline: []const u8, key: []const u8) !T {
|
||||||
const str = get_string(cmdline, key);
|
const str = get_string(cmdline, key);
|
||||||
if (str) |s| {
|
if (str) |s| {
|
||||||
const res = std.fmt.parseInt(T, s, 10) catch return null;
|
const res = std.fmt.parseInt(T, s, 10) catch return error.Format;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
return null;
|
return error.NotPresent;
|
||||||
}
|
}
|
||||||
|
|
||||||
test "get_string() should return the value of a key" {
|
test "get_string() should return the value of a key" {
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
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
|
||||||
|
/// 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 {
|
||||||
|
if (@typeInfo(T) != .pointer) {
|
||||||
|
@compileError("LinkerSet type must be a pointer type");
|
||||||
|
}
|
||||||
|
|
||||||
|
return struct {
|
||||||
|
const start = @extern(*u8, .{ .name = "__start_set_" ++ name });
|
||||||
|
const end = @extern(*u8, .{ .name = "__stop_set_" ++ name });
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn count() usize {
|
||||||
|
return (@intFromPtr(end) - @intFromPtr(start)) / @sizeOf(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn elems() []T {
|
||||||
|
const n = count();
|
||||||
|
const arr: [*]T = @ptrCast(@alignCast(start));
|
||||||
|
|
||||||
|
return arr[0..n];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(elem: T) type {
|
||||||
|
const U = struct {
|
||||||
|
var storage: T linksection("set_" ++ name) = elem;
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
_ = &storage;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return U;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ pub const AVLTree = @import("avl.zig").AVLTree;
|
|||||||
pub const BitMap = @import("bitmap.zig").BitMap;
|
pub const BitMap = @import("bitmap.zig").BitMap;
|
||||||
pub const AtomicBitMap = @import("bitmap.zig").AtomicBitMap;
|
pub const AtomicBitMap = @import("bitmap.zig").AtomicBitMap;
|
||||||
pub const HandoffList = @import("handoff.zig").HandoffList;
|
pub const HandoffList = @import("handoff.zig").HandoffList;
|
||||||
|
pub const LinkerSet = @import("linker_set.zig").LinkerSet;
|
||||||
|
|
||||||
/// Asserts that a given type `T` matches the schema declared by `I`.
|
/// Asserts that a given type `T` matches the schema declared by `I`.
|
||||||
/// This includes public methods and fields.
|
/// This includes public methods and fields.
|
||||||
|
|||||||
Reference in New Issue
Block a user