mirror of
https://github.com/rdmsr/zag.git
synced 2026-11-02 08:08:21 +00:00
ke: add generic IPI mechanism
This commit is contained in:
@@ -113,14 +113,6 @@ pub inline fn restore_interrupts(state: bool) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn send_resched_ipi(target: u32) void {
|
|
||||||
pl.impl.send_resched_ipi(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn send_tlb_ipi(target: u32) void {
|
|
||||||
pl.impl.send_tlb_ipi(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn flush_full_tlb() void {
|
pub inline fn flush_full_tlb() void {
|
||||||
const prev_cr3 = amd64.read_cr(3);
|
const prev_cr3 = amd64.read_cr(3);
|
||||||
// Reload CR3 to flush the entire TLB.
|
// Reload CR3 to flush the entire TLB.
|
||||||
|
|||||||
@@ -85,8 +85,9 @@ export fn isr_handler_main(frame: *const amd64.IrqFrame) callconv(.{ .x86_64_sys
|
|||||||
ki.timer.clock();
|
ki.timer.clock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame.intno == 241) {
|
// Platform IPI number.
|
||||||
ki.shootdown.ipi_handler();
|
if (frame.intno == 240) {
|
||||||
|
ki.ipi.ipi_handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame.intno >= 32) {
|
if (frame.intno >= 32) {
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
//! Generic cross-core call mechanisms.
|
||||||
|
|
||||||
|
const r = @import("root");
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const ke = r.ke;
|
||||||
|
const ki = r.ki;
|
||||||
|
const pl = r.pl;
|
||||||
|
|
||||||
|
const State = enum(u8) {
|
||||||
|
/// Not currently processing IPIs.
|
||||||
|
Idle = 0,
|
||||||
|
/// Has IPIs pending.
|
||||||
|
Pending = 1,
|
||||||
|
/// Is currently processing IPIs.
|
||||||
|
Running = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
const PerCpu = struct {
|
||||||
|
/// Function to be called on other CPUs.
|
||||||
|
func: *const fn (u32, ?*anyopaque) void,
|
||||||
|
/// Argument passed to the function.
|
||||||
|
arg: ?*anyopaque,
|
||||||
|
/// Pending work from other CPUs.
|
||||||
|
pending: ke.AtomicCpuMask,
|
||||||
|
/// Pending CPUs.
|
||||||
|
counter: std.atomic.Value(u16),
|
||||||
|
/// IPI state.
|
||||||
|
state: std.atomic.Value(State),
|
||||||
|
};
|
||||||
|
|
||||||
|
const percpu = ke.CpuLocal(PerCpu, undefined);
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
/// and provided argument as arguments.
|
||||||
|
pub fn unicast(cpu: u32, func: *const fn (u32, ?*anyopaque) void, arg: ?*anyopaque) void {
|
||||||
|
const ipl = ke.ipl.raise(.Dispatch);
|
||||||
|
const curcpu = percpu.local();
|
||||||
|
const remote = percpu.remote(cpu);
|
||||||
|
|
||||||
|
curcpu.func = func;
|
||||||
|
curcpu.arg = arg;
|
||||||
|
curcpu.counter.store(1, .monotonic);
|
||||||
|
|
||||||
|
remote.pending.set(ke.cpu.current(), .monotonic);
|
||||||
|
|
||||||
|
const state = remote.state.swap(.Pending, .release);
|
||||||
|
|
||||||
|
if (state == .Idle) {
|
||||||
|
std.log.info("Sent ipi", .{});
|
||||||
|
pl.send_ipi(cpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (curcpu.counter.load(.acquire) != 0) {
|
||||||
|
std.atomic.spinLoopHint();
|
||||||
|
}
|
||||||
|
|
||||||
|
ke.ipl.lower(ipl);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn broadcast(func: *const fn (u32, ?*anyopaque) void, arg: ?*anyopaque) void {
|
||||||
|
const ipl = ke.ipl.raise(.High);
|
||||||
|
const curcpu = percpu.local();
|
||||||
|
|
||||||
|
curcpu.func = func;
|
||||||
|
curcpu.arg = arg;
|
||||||
|
curcpu.counter = .init(@truncate(ke.ncpus - 1));
|
||||||
|
|
||||||
|
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 (curcpu.counter.load(.acquire) != 0) {
|
||||||
|
std.atomic.spinLoopHint();
|
||||||
|
}
|
||||||
|
|
||||||
|
ke.ipl.lower(ipl);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ipi_handler() void {
|
||||||
|
const curcpu = percpu.local();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// Mark the current CPU as processing IPIs, if the CAS fails here,
|
||||||
|
// then it's probably some kind of IPI that was sent to force us to check
|
||||||
|
// software interrupts (like a rescheduling IPI).
|
||||||
|
const val = curcpu.state.cmpxchgStrong(
|
||||||
|
.Pending,
|
||||||
|
.Running,
|
||||||
|
.acquire,
|
||||||
|
.monotonic,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (val != null) {
|
||||||
|
// CAS failed.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const words = curcpu.pending.storage.len;
|
||||||
|
|
||||||
|
for (0..words) |i| {
|
||||||
|
var word = curcpu.pending.storage[i].swap(0, .monotonic);
|
||||||
|
|
||||||
|
// Go through every CPU in our pending mask.
|
||||||
|
while (word != 0) {
|
||||||
|
const bit = @ctz(word);
|
||||||
|
const cpu = @bitSizeOf(usize) * i + bit;
|
||||||
|
|
||||||
|
// Call its function and decrement the counter.
|
||||||
|
const remote = percpu.remote(@intCast(cpu));
|
||||||
|
remote.func(@intCast(cpu), remote.arg);
|
||||||
|
|
||||||
|
// Ensure all previous memory operations complete.
|
||||||
|
_ = remote.counter.fetchSub(1, .release);
|
||||||
|
|
||||||
|
word &= (word - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to make the CPU state go from running to idle, if this fails
|
||||||
|
// then we still have work to do.
|
||||||
|
_ = curcpu.state.cmpxchgStrong(
|
||||||
|
.Running,
|
||||||
|
.Idle,
|
||||||
|
.monotonic,
|
||||||
|
.monotonic,
|
||||||
|
) orelse return;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-11
@@ -28,17 +28,6 @@ const ImplSchema = struct {
|
|||||||
/// Read a per-cpu pointer on the current CPU.
|
/// Read a per-cpu pointer on the current CPU.
|
||||||
pub fn percpu_ptr(variable: anytype) @TypeOf(variable) {}
|
pub fn percpu_ptr(variable: anytype) @TypeOf(variable) {}
|
||||||
|
|
||||||
/// Send a rescheduling inter-processor interrupt (IPI)
|
|
||||||
/// on the target CPU.
|
|
||||||
pub fn send_resched_ipi(target: u32) void {
|
|
||||||
_ = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send a TLB shootdown IPI on the target CPU.
|
|
||||||
pub fn send_tlb_ipi(target: u32) void {
|
|
||||||
_ = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Flush the entire TLB on the current CPU.
|
/// Flush the entire TLB on the current CPU.
|
||||||
pub fn flush_full_tlb() void {}
|
pub fn flush_full_tlb() void {}
|
||||||
|
|
||||||
@@ -91,3 +80,4 @@ pub const queue = @import("queue.zig");
|
|||||||
pub const shootdown = @import("shootdown.zig");
|
pub const shootdown = @import("shootdown.zig");
|
||||||
pub const tunable = @import("tunable.zig");
|
pub const tunable = @import("tunable.zig");
|
||||||
pub const smr = @import("smr.zig");
|
pub const smr = @import("smr.zig");
|
||||||
|
pub const ipi = @import("ipi.zig");
|
||||||
|
|||||||
@@ -103,5 +103,10 @@ pub const smr = struct {
|
|||||||
pub const Sequence = p.smr.Sequence;
|
pub const Sequence = p.smr.Sequence;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const ipi = struct {
|
||||||
|
pub const unicast = p.ipi.unicast;
|
||||||
|
pub const broadcast = p.ipi.broadcast;
|
||||||
|
};
|
||||||
|
|
||||||
/// Number of CPUs on the system
|
/// Number of CPUs on the system
|
||||||
pub var ncpus: usize = 0;
|
pub var ncpus: usize = 0;
|
||||||
|
|||||||
+3
-2
@@ -95,6 +95,7 @@ const config = @import("config");
|
|||||||
const r = @import("root");
|
const r = @import("root");
|
||||||
const ke = r.ke;
|
const ke = r.ke;
|
||||||
const ki = ke.private;
|
const ki = ke.private;
|
||||||
|
const pl = r.pl;
|
||||||
|
|
||||||
const runqueues_n = 64;
|
const runqueues_n = 64;
|
||||||
const interactivity_threshold = 30;
|
const interactivity_threshold = 30;
|
||||||
@@ -624,7 +625,7 @@ pub fn update_priority_locked(td: *ke.Thread, new_prio: u8) void {
|
|||||||
ki.ipl.set_softint_pending(c, .Dispatch);
|
ki.ipl.set_softint_pending(c, .Dispatch);
|
||||||
|
|
||||||
if (c != ke.cpu.current()) {
|
if (c != ke.cpu.current()) {
|
||||||
ki.impl.send_resched_ipi(c);
|
pl.send_ipi(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1101,7 +1102,7 @@ fn enqueue_on_cpu(c: u32, td: *ke.Thread) void {
|
|||||||
|
|
||||||
if (c != ke.cpu.current()) {
|
if (c != ke.cpu.current()) {
|
||||||
// Send an IPI.
|
// Send an IPI.
|
||||||
ki.impl.send_resched_ipi(c);
|
pl.send_ipi(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// queues_lock dropped
|
// queues_lock dropped
|
||||||
|
|||||||
+11
-25
@@ -7,6 +7,7 @@ const r = @import("root");
|
|||||||
const ke = r.ke;
|
const ke = r.ke;
|
||||||
const ki = ke.private;
|
const ki = ke.private;
|
||||||
const mm = r.mm;
|
const mm = r.mm;
|
||||||
|
const pl = r.pl;
|
||||||
|
|
||||||
/// Structure representing a single shootdown request.
|
/// Structure representing a single shootdown request.
|
||||||
pub const ShootdownState = struct {
|
pub const ShootdownState = struct {
|
||||||
@@ -36,6 +37,9 @@ const PerCpu = struct {
|
|||||||
valid_states: [config.ncpus]std.atomic.Value(u64),
|
valid_states: [config.ncpus]std.atomic.Value(u64),
|
||||||
/// CPUs that have sent this CPU a shootdown.
|
/// CPUs that have sent this CPU a shootdown.
|
||||||
senders: ke.AtomicCpuMask,
|
senders: ke.AtomicCpuMask,
|
||||||
|
|
||||||
|
sync_addr: r.VAddr,
|
||||||
|
sync_count: usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
const slot_free: u16 = std.math.maxInt(u16);
|
const slot_free: u16 = std.math.maxInt(u16);
|
||||||
@@ -44,11 +48,6 @@ const percpu = ke.CpuLocal(PerCpu, undefined);
|
|||||||
|
|
||||||
pub var shootdowns: rtl.HandoffList = undefined;
|
pub var shootdowns: rtl.HandoffList = undefined;
|
||||||
|
|
||||||
var sync_counter: std.atomic.Value(usize) = .init(0);
|
|
||||||
var sync_addr: r.VAddr = 0;
|
|
||||||
var sync_count: usize = 0;
|
|
||||||
var shootdown_lock: ke.SpinLock = .init();
|
|
||||||
|
|
||||||
fn pcpu_init() linksection(r.init) void {
|
fn pcpu_init() linksection(r.init) void {
|
||||||
const local = percpu.local();
|
const local = percpu.local();
|
||||||
|
|
||||||
@@ -89,30 +88,17 @@ fn flush_range(va: r.VAddr, npages: usize) void {
|
|||||||
|
|
||||||
/// Synchronous shootdown path.
|
/// Synchronous shootdown path.
|
||||||
fn do_synchronous_shootdown(state: ShootdownState) void {
|
fn do_synchronous_shootdown(state: ShootdownState) void {
|
||||||
shootdown_lock.acquire_no_ipl();
|
const cpu = percpu.local();
|
||||||
|
|
||||||
sync_addr = state.base;
|
cpu.sync_addr = state.base;
|
||||||
sync_count = state.npages;
|
cpu.sync_count = state.npages;
|
||||||
sync_counter.store(ke.ncpus - 1, .release);
|
|
||||||
|
|
||||||
const cur = ke.cpu.current();
|
ke.ipi.broadcast(flush_tlb, null);
|
||||||
|
|
||||||
for (0..ke.ncpus) |i| {
|
|
||||||
if (i == cur) continue;
|
|
||||||
ki.impl.send_tlb_ipi(@truncate(i));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (sync_counter.load(.monotonic) != 0) {
|
fn flush_tlb(sender: u32, _: ?*anyopaque) void {
|
||||||
std.atomic.spinLoopHint();
|
const cpu = percpu.remote(sender);
|
||||||
}
|
flush_range(cpu.sync_addr, cpu.sync_count);
|
||||||
|
|
||||||
shootdown_lock.release_no_ipl();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called by the TLB interrupt handler.
|
|
||||||
pub fn ipi_handler() void {
|
|
||||||
flush_range(sync_addr, sync_count);
|
|
||||||
_ = sync_counter.fetchSub(1, .release);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called in a quiescent state to process the pending shootdowns.
|
/// Called in a quiescent state to process the pending shootdowns.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ pub const smp = @import("smp.zig");
|
|||||||
|
|
||||||
const ki = r.ke.private;
|
const ki = r.ke.private;
|
||||||
|
|
||||||
pub const name = "PC";
|
pub const name: []const u8 = "PC";
|
||||||
|
|
||||||
const com1 = 0x3F8;
|
const com1 = 0x3F8;
|
||||||
|
|
||||||
@@ -113,10 +113,6 @@ pub fn arm_timer(deadline: r.Nanoseconds) void {
|
|||||||
apic.arm_timer(deadline);
|
apic.arm_timer(deadline);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_resched_ipi(target_cpu: u32) void {
|
pub fn send_ipi(target_cpu: u32) void {
|
||||||
apic.send_ipi(smp.cpu_id_to_apic_id[target_cpu], 240, 0);
|
apic.send_ipi(smp.cpu_id_to_apic_id[target_cpu], 240, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_tlb_ipi(target_cpu: u32) void {
|
|
||||||
apic.send_ipi(smp.cpu_id_to_apic_id[target_cpu], 241, 0);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
const config = @import("config");
|
const config = @import("config");
|
||||||
const r = @import("root");
|
const r = @import("root");
|
||||||
|
const rtl = @import("rtl");
|
||||||
|
|
||||||
pub const impl = switch (config.arch) {
|
pub const impl = switch (config.arch) {
|
||||||
.amd64 => @import("pc/impl.zig"),
|
.amd64 => @import("pc/impl.zig"),
|
||||||
@@ -11,6 +12,28 @@ pub const impl = switch (config.arch) {
|
|||||||
|
|
||||||
pub const acpi = @import("acpi/root.zig");
|
pub const acpi = @import("acpi/root.zig");
|
||||||
|
|
||||||
|
const ImplSchema = struct {
|
||||||
|
pub const name: []const u8 = undefined;
|
||||||
|
pub fn early_init() void {}
|
||||||
|
pub fn late_init() void {}
|
||||||
|
pub fn debug_write(c: u8) void {
|
||||||
|
_ = c;
|
||||||
|
}
|
||||||
|
pub fn debug_read() u8 {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
pub fn send_ipi(cpu: u32) void {
|
||||||
|
_ = cpu;
|
||||||
|
}
|
||||||
|
pub fn arm_timer(ns: r.Nanoseconds) void {
|
||||||
|
_ = ns;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
rtl.assert_interface(impl, ImplSchema);
|
||||||
|
}
|
||||||
|
|
||||||
/// Early platform-specific initialization, called before anything else.
|
/// Early platform-specific initialization, called before anything else.
|
||||||
pub inline fn early_init() void {
|
pub inline fn early_init() void {
|
||||||
return impl.early_init();
|
return impl.early_init();
|
||||||
@@ -37,5 +60,10 @@ pub inline fn arm_timer(ns: r.Nanoseconds) void {
|
|||||||
return impl.arm_timer(ns);
|
return impl.arm_timer(ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send an inter-processor interrupt (IPI) to a target CPU.
|
||||||
|
pub inline fn send_ipi(cpu: u32) void {
|
||||||
|
return impl.send_ipi(cpu);
|
||||||
|
}
|
||||||
|
|
||||||
/// Pretty name for the platform.
|
/// Pretty name for the platform.
|
||||||
pub const name = impl.name;
|
pub const name = impl.name;
|
||||||
|
|||||||
Reference in New Issue
Block a user