mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-28 22:18:22 +00:00
116 lines
3.1 KiB
Zig
116 lines
3.1 KiB
Zig
//! MM-level TLB shootdown support
|
|
|
|
const r = @import("root");
|
|
const std = @import("std");
|
|
const rtl = @import("rtl");
|
|
|
|
const ke = r.ke;
|
|
const mm = r.mm;
|
|
const mmp = mm.private;
|
|
const ps = r.ps;
|
|
const ex = r.ex;
|
|
|
|
pub var sync_shootdowns: std.atomic.Value(usize) = .init(0);
|
|
pub var async_shootdowns: std.atomic.Value(usize) = .init(0);
|
|
pub var sent_shootdowns: std.atomic.Value(usize) = .init(0);
|
|
|
|
var work_item: ex.WorkItem = undefined;
|
|
|
|
/// Reclaim the memory associated with a state.
|
|
/// This can only be done when all CPUs have flushed their TLB.
|
|
fn reclaim_state(obj: *anyopaque, _: ?*anyopaque) void {
|
|
const link: *?*anyopaque = @ptrCast(@alignCast(obj));
|
|
const state: *ke.ShootdownState = @fieldParentPtr("link", link);
|
|
|
|
const space: *mm.Space = @ptrFromInt(state.payload[0]);
|
|
const pfn_list: mmp.PfnList = @bitCast(state.payload[1]);
|
|
const base = state.base;
|
|
const npages = state.npages;
|
|
|
|
// Reclaim the virtual address space.
|
|
space.lock.acquire();
|
|
|
|
space.arena.free(base, @as(usize, npages) * mm.page_size);
|
|
|
|
space.lock.release();
|
|
|
|
// Free the physical pages
|
|
mm.phys.free_batch(
|
|
mm.pfn_to_struct_page(pfn_list.head),
|
|
mm.pfn_to_struct_page(pfn_list.tail),
|
|
npages,
|
|
);
|
|
|
|
_ = async_shootdowns.fetchAdd(1, .monotonic);
|
|
|
|
// Release the slot after all copied state has been consumed.
|
|
state.release();
|
|
}
|
|
|
|
fn reap_pages(_: ?*anyopaque) void {
|
|
ke.shootdown.shootdowns.process(reclaim_state, null);
|
|
}
|
|
|
|
/// Unmap and flush a virtual range on the given space.
|
|
/// Space lock is held on entry and IPL is raised to Dispatch.
|
|
pub fn reclaim_range(space: *mm.Space, va: r.VAddr, size: usize) void {
|
|
std.debug.assert(std.mem.isAligned(va, mm.page_size));
|
|
|
|
// Unmap the virtual addresses and get the backing physical pages.
|
|
const list = space.pmap.unmap(va, size) orelse {
|
|
// If no physical pages, free the VA directly.
|
|
space.arena.free(va, size);
|
|
return;
|
|
};
|
|
|
|
const state: ke.ShootdownState = .{
|
|
.base = va,
|
|
.npages = @truncate(size / mm.page_size),
|
|
.link = undefined,
|
|
.state = .init(0),
|
|
.payload = .{
|
|
@intFromPtr(space),
|
|
@bitCast(list),
|
|
},
|
|
};
|
|
|
|
space.lock.release();
|
|
|
|
const ipl = ke.ipl.raise(.Dispatch);
|
|
var mask: ke.CpuMask = .init(false);
|
|
|
|
for (0..ke.ncpus) |i| {
|
|
if (i != ke.cpu.current()) {
|
|
mask.set(i);
|
|
}
|
|
}
|
|
|
|
ke.shootdown.submit(state, mask) catch {
|
|
_ = sync_shootdowns.fetchAdd(1, .monotonic);
|
|
|
|
ke.ipl.lower(ipl);
|
|
space.lock.acquire();
|
|
space.arena.free(va, size);
|
|
|
|
// Free the physical pages
|
|
mm.phys.free_batch(
|
|
mm.pfn_to_struct_page(list.head),
|
|
mm.pfn_to_struct_page(list.tail),
|
|
size / mm.page_size,
|
|
);
|
|
return;
|
|
};
|
|
|
|
ke.ipl.lower(ipl);
|
|
space.lock.acquire();
|
|
}
|
|
|
|
fn activation(_: *rtl.HandoffList) void {
|
|
ex.work.enqueue(&work_item);
|
|
}
|
|
|
|
pub fn init() void {
|
|
ke.shootdown.shootdowns.* = .init(activation);
|
|
work_item.init(.High, reap_pages, null);
|
|
}
|