mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-30 14:58:21 +00:00
all: tidy
This commit is contained in:
+3
-4
@@ -35,10 +35,10 @@ fn reap_stacks() void {
|
||||
// Reap all the excess stacks.
|
||||
var entry: ?*ke.Stack = lst;
|
||||
|
||||
while (entry != null) {
|
||||
const next = entry.?.next;
|
||||
while (entry) |e| {
|
||||
const next = e.next;
|
||||
mm.heap.free(
|
||||
(@intFromPtr(entry) + @sizeOf(ke.Stack)) -
|
||||
(@intFromPtr(e) + @sizeOf(ke.Stack)) -
|
||||
ps.thread.kernel_thread_stack_size,
|
||||
ps.thread.kernel_thread_stack_size,
|
||||
);
|
||||
@@ -86,7 +86,6 @@ fn balance_manager(_: ?*anyopaque) void {
|
||||
|
||||
if (stack_reap_time == 0) {
|
||||
stack_reap_time = stack_reap_interval;
|
||||
|
||||
reap_stacks();
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -1,3 +1,5 @@
|
||||
//! Kernel heap.
|
||||
|
||||
const r = @import("root");
|
||||
const ke = r.ke;
|
||||
const mm = r.mm;
|
||||
@@ -43,7 +45,11 @@ pub fn alloc(size: usize, policy: mm.WaitPolicy) mm.Error!*anyopaque {
|
||||
|
||||
mmp.kernel_space.lock.acquire();
|
||||
|
||||
pte.* = mmp.impl.make_leaf_pte(page, .{ .read = true, .write = true }, 0);
|
||||
pte.* = mmp.impl.make_leaf_pte(
|
||||
page,
|
||||
.{ .read = true, .write = true },
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
mmp.kernel_space.lock.release();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! Memory manager initialization.
|
||||
|
||||
const r = @import("root");
|
||||
const pl = r.pl;
|
||||
const mm = r.mm;
|
||||
|
||||
+9
-7
@@ -1,17 +1,19 @@
|
||||
const r = @import("root");
|
||||
const std = @import("std");
|
||||
const rtl = @import("rtl");
|
||||
const pl = r.pl;
|
||||
const mm = r.mm;
|
||||
const mmp = mm.private;
|
||||
const ke = r.ke;
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const log = std.log.scoped(.@"mm/phys");
|
||||
|
||||
// How aggressive is memory reclamation.
|
||||
const threshold_scale_factor = 10;
|
||||
|
||||
/// Threshold at which most memory allocations block, only high priority allocations are allowed.
|
||||
/// Threshold at which most memory allocations block, only high priority
|
||||
/// allocations are allowed.
|
||||
pub var min_memory_threshold: usize = 0;
|
||||
/// Threshold at which reclamation starts.
|
||||
pub var low_memory_threshold: usize = 0;
|
||||
@@ -29,14 +31,14 @@ var free_page_event: ke.Event = undefined;
|
||||
|
||||
var free_pages: usize = 0;
|
||||
|
||||
pub var usable_memory: std.atomic.Value(usize) = .init(0);
|
||||
pub var usable_memory = std.atomic.Value(usize).init(0);
|
||||
|
||||
// Wait for pages to be available.
|
||||
// List lock is held.
|
||||
fn wait_for_pages(old_ipl: ke.Ipl) void {
|
||||
var ipl = old_ipl;
|
||||
|
||||
std.debug.assert(@intFromEnum(ipl) < @intFromEnum(ke.Ipl.Dispatch));
|
||||
assert(@backingInt(ipl) < @backingInt(ke.Ipl.Dispatch));
|
||||
|
||||
if (free_pages >= min_memory_threshold) {
|
||||
return;
|
||||
@@ -65,7 +67,7 @@ pub fn alloc() r.PAddr {
|
||||
|
||||
wait_for_pages(ipl);
|
||||
|
||||
std.debug.assert(!free_list.is_empty());
|
||||
assert(!free_list.is_empty());
|
||||
|
||||
const head = free_list.first();
|
||||
head.remove();
|
||||
@@ -94,8 +96,8 @@ pub fn alloc_opts(opts: struct { policy: mm.WaitPolicy }) ?r.PAddr {
|
||||
wait_for_pages(ipl);
|
||||
}
|
||||
|
||||
std.debug.assert(free_pages > 0);
|
||||
std.debug.assert(!free_list.is_empty());
|
||||
assert(free_pages > 0);
|
||||
assert(!free_list.is_empty());
|
||||
|
||||
const head = free_list.first();
|
||||
head.remove();
|
||||
|
||||
+29
-18
@@ -2,10 +2,12 @@
|
||||
|
||||
const r = @import("root");
|
||||
const rtl = @import("rtl");
|
||||
const std = @import("std");
|
||||
const mm = r.mm;
|
||||
const mmp = mm.private;
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
/// Describes a contiguous region of physical memory to be mapped
|
||||
/// at a given virtual address.
|
||||
const MapItem = struct {
|
||||
@@ -45,8 +47,6 @@ fn index_for_level(va: usize, level: usize) usize {
|
||||
}
|
||||
|
||||
pub const PMap = struct {
|
||||
const Self = @This();
|
||||
|
||||
const num_levels = mmp.impl.levels.len;
|
||||
const entries_per_table = 512;
|
||||
const index_mask = entries_per_table - 1;
|
||||
@@ -61,7 +61,7 @@ pub const PMap = struct {
|
||||
return is_leaf_level(level) and mmp.impl.is_leaf_level_enabled(level);
|
||||
}
|
||||
|
||||
fn cursor(self: *Self, va: r.VAddr) Cursor {
|
||||
fn cursor(self: *PMap, va: r.VAddr) Cursor {
|
||||
var c = Cursor{
|
||||
.pmap = self,
|
||||
.va = va,
|
||||
@@ -73,11 +73,16 @@ pub const PMap = struct {
|
||||
return c;
|
||||
}
|
||||
|
||||
pub fn activate(self: *Self) void {
|
||||
pub fn activate(self: *PMap) void {
|
||||
mmp.impl.activate(self.root_pa);
|
||||
}
|
||||
|
||||
pub fn map_from(self: *Self, va: r.VAddr, size: usize, source: anytype) void {
|
||||
pub fn map_from(
|
||||
self: *PMap,
|
||||
va: r.VAddr,
|
||||
size: usize,
|
||||
source: anytype,
|
||||
) void {
|
||||
var c = self.cursor(va);
|
||||
var remain = size;
|
||||
|
||||
@@ -91,7 +96,7 @@ pub const PMap = struct {
|
||||
/// Map a contiguous virtual address range to a
|
||||
/// contiguous physical address range.
|
||||
pub fn map_contiguous_range(
|
||||
self: *Self,
|
||||
self: *PMap,
|
||||
va: r.VAddr,
|
||||
pa: r.PAddr,
|
||||
size: usize,
|
||||
@@ -107,7 +112,7 @@ pub const PMap = struct {
|
||||
self.map_from(va, size, src);
|
||||
}
|
||||
|
||||
pub fn query(self: *Self, va: r.VAddr) ?r.PAddr {
|
||||
pub fn query(self: *PMap, va: r.VAddr) ?r.PAddr {
|
||||
var c = self.cursor(va);
|
||||
|
||||
var level: usize = num_levels - 1;
|
||||
@@ -129,8 +134,8 @@ pub const PMap = struct {
|
||||
/// Unmap a contiguous range of virtual pages.
|
||||
/// Only small pages are supported.
|
||||
/// Return a pfn that points to the physical pages that were unmapped.
|
||||
pub fn unmap(self: *Self, va: r.VAddr, size: usize) ?mmp.PfnList {
|
||||
std.debug.assert(std.mem.isAligned(va, mm.page_size));
|
||||
pub fn unmap(self: *PMap, va: r.VAddr, size: usize) ?mmp.PfnList {
|
||||
assert(std.mem.isAligned(va, mm.page_size));
|
||||
|
||||
var c = self.cursor(va);
|
||||
|
||||
@@ -178,7 +183,7 @@ pub const PMap = struct {
|
||||
}
|
||||
|
||||
pub const Cursor = struct {
|
||||
pmap: *Self,
|
||||
pmap: *PMap,
|
||||
va: r.VAddr,
|
||||
/// Cached pointers to each level's page table, filled as the cursor
|
||||
/// descends. Only indices in `[0, top_level]` are valid.
|
||||
@@ -283,9 +288,12 @@ pub fn wire_pte(
|
||||
va: r.VAddr,
|
||||
policy: mm.WaitPolicy,
|
||||
) mm.Error!*mmp.impl.Pte {
|
||||
std.debug.assert(space.lock.is_locked());
|
||||
assert(space.lock.is_locked());
|
||||
|
||||
var table: [*]mmp.impl.Pte = @ptrFromInt(
|
||||
mm.p2v(mmp.kernel_space.pmap.root_pa),
|
||||
);
|
||||
|
||||
var table: [*]mmp.impl.Pte = @ptrFromInt(mm.p2v(mmp.kernel_space.pmap.root_pa));
|
||||
var level = mmp.impl.levels.len - 1;
|
||||
|
||||
while (true) {
|
||||
@@ -293,7 +301,7 @@ pub fn wire_pte(
|
||||
var entry = table[idx];
|
||||
|
||||
if (level == 0) {
|
||||
std.debug.assert(!entry.present);
|
||||
assert(!entry.present);
|
||||
return &table[idx];
|
||||
}
|
||||
|
||||
@@ -302,10 +310,13 @@ pub fn wire_pte(
|
||||
// since that may block.
|
||||
space.lock.release();
|
||||
|
||||
const new_table_pa = mmp.phys.alloc_opts(.{ .policy = policy }) orelse {
|
||||
space.lock.acquire();
|
||||
return mm.Error.OutOfMemory;
|
||||
};
|
||||
const new_table_pa = mmp.phys.alloc_opts(
|
||||
.{ .policy = policy },
|
||||
) orelse
|
||||
{
|
||||
space.lock.acquire();
|
||||
return mm.Error.OutOfMemory;
|
||||
};
|
||||
|
||||
// Re-acquire the lock and re-validate the entry,
|
||||
// since another thread may have raced to allocate it.
|
||||
|
||||
@@ -88,7 +88,11 @@ pub inline fn make_table_pte(pa: r.PAddr) Pte {
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn make_leaf_pte(pa: r.PAddr, flags: mm.MapFlags, level: usize) Pte {
|
||||
pub inline fn make_leaf_pte(
|
||||
pa: r.PAddr,
|
||||
flags: mm.MapFlags,
|
||||
level: usize,
|
||||
) Pte {
|
||||
_ = level;
|
||||
return Pte{
|
||||
.present = true,
|
||||
@@ -118,5 +122,6 @@ pub fn is_leaf_level_enabled(level: usize) bool {
|
||||
}
|
||||
|
||||
pub fn init_kernel() void {
|
||||
mmp.kernel_space.pmap.root_pa = (rv64.read_csr("satp") & ((@as(u64, 1) << 44) - 1)) << 12;
|
||||
mmp.kernel_space.pmap.root_pa = (rv64.read_csr("satp") &
|
||||
((@as(u64, 1) << 44) - 1)) << 12;
|
||||
}
|
||||
|
||||
+5
-3
@@ -35,7 +35,7 @@ comptime {
|
||||
if (@sizeOf(Page) != r.BootInfo.page_struct_size) {
|
||||
@compileError(
|
||||
std.fmt.comptimePrint(
|
||||
"'Page' size mismatch between loader and kernel: kernel={} loader={}",
|
||||
"'Page' size mismatch between loader and kernel: k={} l={}",
|
||||
.{ @sizeOf(Page), r.BootInfo.page_struct_size },
|
||||
),
|
||||
);
|
||||
@@ -68,11 +68,13 @@ pub inline fn pfn_to_page(pfn: Pfn) usize {
|
||||
}
|
||||
|
||||
pub inline fn struct_page_to_pfn(page: *Page) Pfn {
|
||||
return @intCast((@intFromPtr(page) - private.impl.pfndb_base) / @sizeOf(Page));
|
||||
return @intCast((@intFromPtr(page) - private.impl.pfndb_base) /
|
||||
@sizeOf(Page));
|
||||
}
|
||||
|
||||
pub inline fn pfn_to_struct_page(pfn: Pfn) *Page {
|
||||
return @ptrFromInt(private.impl.pfndb_base + (@as(usize, @intCast(pfn)) * @sizeOf(Page)));
|
||||
return @ptrFromInt(private.impl.pfndb_base +
|
||||
(@as(usize, pfn) * @sizeOf(Page)));
|
||||
}
|
||||
|
||||
pub var pfndb: [*]Page = @ptrFromInt(private.impl.pfndb_base);
|
||||
|
||||
+10
-8
@@ -1,7 +1,6 @@
|
||||
//! MM-level TLB shootdown support
|
||||
|
||||
const r = @import("root");
|
||||
const std = @import("std");
|
||||
const rtl = @import("rtl");
|
||||
|
||||
const ke = r.ke;
|
||||
@@ -10,9 +9,12 @@ 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);
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
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;
|
||||
|
||||
@@ -54,7 +56,7 @@ fn reap_pages(_: ?*anyopaque) void {
|
||||
/// 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));
|
||||
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 {
|
||||
@@ -67,7 +69,7 @@ pub fn reclaim_range(space: *mm.Space, va: r.VAddr, size: usize) void {
|
||||
.base = va,
|
||||
.npages = @truncate(size / mm.page_size),
|
||||
.link = undefined,
|
||||
.state = .init(0),
|
||||
.state = std.atomic.Value(u16).init(0),
|
||||
.payload = .{
|
||||
@intFromPtr(space),
|
||||
@as(*const usize, @ptrCast(@alignCast(&list))).*,
|
||||
@@ -77,7 +79,7 @@ pub fn reclaim_range(space: *mm.Space, va: r.VAddr, size: usize) void {
|
||||
space.lock.release();
|
||||
|
||||
const ipl = ke.ipl.raise(.Dispatch);
|
||||
var mask: ke.CpuMask = .init(false);
|
||||
var mask = ke.CpuMask.init(false);
|
||||
|
||||
for (0..ke.ncpus) |i| {
|
||||
if (i != ke.cpu.current()) {
|
||||
@@ -110,6 +112,6 @@ fn activation(_: *rtl.HandoffList) void {
|
||||
}
|
||||
|
||||
pub fn init() void {
|
||||
ke.shootdown.shootdowns.* = .init(activation);
|
||||
ke.shootdown.shootdowns.* = rtl.HandoffList.init(activation);
|
||||
work_item.init(.High, reap_pages, null);
|
||||
}
|
||||
|
||||
+121
-85
@@ -114,16 +114,18 @@
|
||||
//! an object leaves the slab layer and destructors run when it returns to it,
|
||||
//! slabs hold raw objects while magazines hold constructed ones.
|
||||
|
||||
const rtl = @import("rtl");
|
||||
const r = @import("root");
|
||||
const std = @import("std");
|
||||
const config = @import("config");
|
||||
const rtl = @import("rtl");
|
||||
|
||||
const r = @import("root");
|
||||
const ke = r.ke;
|
||||
const mm = r.mm;
|
||||
const ex = r.ex;
|
||||
const mmp = mm.private;
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
/// Interval at which we do housekeeping (working set update, reaping, etc.)
|
||||
pub const update_interval_s = 15;
|
||||
|
||||
@@ -276,7 +278,7 @@ const Slab = struct {
|
||||
const bm = self.bitmap();
|
||||
|
||||
const bit = @as(u64, 1) << @intCast(idx % 64);
|
||||
std.debug.assert(bm[idx / 64] & (bit) == 0);
|
||||
assert(bm[idx / 64] & (bit) == 0);
|
||||
|
||||
bm[idx / 64] |= bit;
|
||||
}
|
||||
@@ -558,8 +560,6 @@ pub const Zone = struct {
|
||||
|
||||
arg: ?*anyopaque,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
const InitOptions = struct {
|
||||
alignment: u16 = slab_align,
|
||||
ctor: ?*const fn (*anyopaque) void = null,
|
||||
@@ -586,7 +586,7 @@ pub const Zone = struct {
|
||||
|
||||
/// Initialize a zone.
|
||||
pub fn init(
|
||||
self: *Self,
|
||||
self: *Zone,
|
||||
name: []const u8,
|
||||
size: usize,
|
||||
options: InitOptions,
|
||||
@@ -634,9 +634,9 @@ pub const Zone = struct {
|
||||
self.smr_reclaim = options.smr_reclaim;
|
||||
|
||||
if (options.import) |i| {
|
||||
std.debug.assert(options.release != null);
|
||||
assert(options.release != null);
|
||||
self.import = i;
|
||||
self.release = options.release.?;
|
||||
self.release = options.release orelse unreachable;
|
||||
self.arg = options.arg;
|
||||
} else {
|
||||
self.import = zone_import;
|
||||
@@ -669,7 +669,7 @@ pub const Zone = struct {
|
||||
}
|
||||
|
||||
/// Allocate an object from the zone.
|
||||
pub fn alloc(self: *Self, opts: AllocOpts) mm.Error!*anyopaque {
|
||||
pub fn alloc(self: *Zone, opts: AllocOpts) mm.Error!*anyopaque {
|
||||
// Try grabbing an object from the magazine layer.
|
||||
if (magazines_initialized and self.use_magazines) {
|
||||
@branchHint(.likely);
|
||||
@@ -682,12 +682,13 @@ pub const Zone = struct {
|
||||
while (true) {
|
||||
if (cpu.alloc_rounds > 0) {
|
||||
// Fast path: just pop from the alloc magazine.
|
||||
const alloc_mag = cpu.alloc orelse unreachable;
|
||||
cpu.alloc_rounds -= 1;
|
||||
buf = cpu.alloc.?.rounds_ptr()[cpu.alloc_rounds];
|
||||
buf = alloc_mag.rounds_ptr()[cpu.alloc_rounds];
|
||||
|
||||
if (is_poison_enabled and !self.dont_touch) {
|
||||
if (!check_poison(buf, self.obj_size, free_poison)) {
|
||||
@panic("slab poison mismatch: object corrupted after free");
|
||||
@panic("poison mismatch: possible use-after-free");
|
||||
}
|
||||
|
||||
fill_with_poison(buf, self.obj_size, alloc_poison);
|
||||
@@ -702,7 +703,7 @@ pub const Zone = struct {
|
||||
}
|
||||
|
||||
if (cpu.free_rounds > 0 and self.smr == null) {
|
||||
std.debug.assert(cpu.free != null);
|
||||
assert(cpu.free != null);
|
||||
|
||||
// Alloc magazine is empty. If the free magazine has items,
|
||||
// swap them. The free magazine now becomes our alloc
|
||||
@@ -715,7 +716,8 @@ pub const Zone = struct {
|
||||
// Both magazines are empty.
|
||||
// Try to get a full magazine from the CPU-local depot.
|
||||
if (self.full_mags_ready(&cpu.depot.full_mags)) {
|
||||
const full_mag = Depot.alloc(&cpu.depot.full_mags).?;
|
||||
const full_mag = Depot.alloc(&cpu.depot.full_mags) orelse
|
||||
unreachable;
|
||||
|
||||
// Discard our empty alloc magazine to the depot.
|
||||
if (cpu.alloc) |empty_mag| {
|
||||
@@ -741,7 +743,8 @@ pub const Zone = struct {
|
||||
|
||||
// Try getting a full magazine from the CPU-local depot again.
|
||||
if (self.full_mags_ready(&cpu.depot.full_mags)) {
|
||||
const full_mag = Depot.alloc(&cpu.depot.full_mags).?;
|
||||
const full_mag = Depot.alloc(&cpu.depot.full_mags) orelse
|
||||
unreachable;
|
||||
|
||||
// Discard our empty alloc magazine to the depot.
|
||||
if (cpu.alloc) |empty_mag| {
|
||||
@@ -758,14 +761,15 @@ pub const Zone = struct {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Nothing was found. Allocate a new magazine and fill it from the
|
||||
// import layer, then replace the CPU's alloc magazine with it.
|
||||
// It is preferable (maybe even required?) to drop the CPU lock
|
||||
// while we do this work; when we do re-acquire the lock we then
|
||||
// might notice that someone has already done the work for us and
|
||||
// put a magazine in the CPU's alloc storage. In that case, it would
|
||||
// be better to take our fresh magazine and insert it into the depot
|
||||
// instead, since there is a higher chance the impostor magazine
|
||||
// Nothing was found. Allocate a new magazine and fill it from
|
||||
// the import layer, then replace the CPU's alloc magazine with
|
||||
// it. It is preferable (maybe even required?) to drop the CPU
|
||||
// lock while we do this work; when we do re-acquire the lock
|
||||
// we then might notice that someone has already done the work
|
||||
// for us and put a magazine in the CPU's alloc storage.
|
||||
// In that case, it would be better to take our fresh magazine
|
||||
// and insert it into the depot instead,
|
||||
// since there is a higher chance the impostor magazine
|
||||
// contains objects that are hotter in cache.
|
||||
cpu.lock.release(ipl);
|
||||
|
||||
@@ -783,7 +787,8 @@ pub const Zone = struct {
|
||||
opts.policy,
|
||||
);
|
||||
|
||||
// We dropped the lock and IPL, so we might've been migrated.
|
||||
// We dropped the lock and IPL, so we might've been
|
||||
// migrated.
|
||||
ipl = ke.ipl.raise(.Dispatch);
|
||||
cpu = &self.cpus[ke.cpu.current()].value;
|
||||
cpu.lock.acquire_no_ipl();
|
||||
@@ -793,14 +798,19 @@ pub const Zone = struct {
|
||||
cpu.alloc_rounds = cnt;
|
||||
continue;
|
||||
} else {
|
||||
// We lost the race. If the magazine we got was fully filled,
|
||||
// then put it in the depot. Otherwise, dispose of it.
|
||||
// We lost the race. If the magazine we got was fully
|
||||
// filled, then put it in the depot.
|
||||
// Otherwise, dispose of it.
|
||||
if (cnt == magazine_size.load()) {
|
||||
// Ensure this magazine isn't counted as a freed one,
|
||||
// it is instantly reclaimable.
|
||||
// Ensure this magazine isn't counted as a freed
|
||||
// one, it is instantly reclaimable.
|
||||
nm.seq = ke.smr.seq_invalid;
|
||||
|
||||
Depot.free(nm, &cpu.depot.full_mags, self.reuse_policy);
|
||||
Depot.free(
|
||||
nm,
|
||||
&cpu.depot.full_mags,
|
||||
self.reuse_policy,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -828,8 +838,8 @@ pub const Zone = struct {
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback in case this is a zone with magazines disabled or we got into trouble above,
|
||||
// just fetch an object from the import layer.
|
||||
// Fallback in case this is a zone with magazines disabled or we got
|
||||
// into trouble above, just fetch an object from the import layer.
|
||||
var elems: [1]*anyopaque = undefined;
|
||||
|
||||
_ = try self.import(
|
||||
@@ -843,7 +853,7 @@ pub const Zone = struct {
|
||||
}
|
||||
|
||||
/// Free an object back to the zone.
|
||||
pub fn free(self: *Self, obj: *anyopaque) void {
|
||||
pub fn free(self: *Zone, obj: *anyopaque) void {
|
||||
if (magazines_initialized and self.use_magazines) {
|
||||
@branchHint(.likely);
|
||||
|
||||
@@ -857,8 +867,12 @@ pub const Zone = struct {
|
||||
{
|
||||
// Fast path: push directly to free magazine.
|
||||
// For SMR zones the dtor and poisoning are deferred until
|
||||
// the magazine is reused, as readers may still hold the object.
|
||||
if (is_poison_enabled and self.smr == null and !self.dont_touch) {
|
||||
// the magazine is reused, as readers may still hold
|
||||
// the object.
|
||||
const free_mag = cpu.free orelse unreachable;
|
||||
if (is_poison_enabled and self.smr == null and
|
||||
!self.dont_touch)
|
||||
{
|
||||
if (self.dtor) |dtor| {
|
||||
dtor(obj);
|
||||
}
|
||||
@@ -866,12 +880,16 @@ pub const Zone = struct {
|
||||
fill_with_poison(obj, self.obj_size, free_poison);
|
||||
}
|
||||
|
||||
cpu.free.?.rounds_ptr()[cpu.free_rounds] = obj;
|
||||
free_mag.rounds_ptr()[cpu.free_rounds] = obj;
|
||||
cpu.free_rounds += 1;
|
||||
|
||||
if (self.smr != null and cpu.free_rounds == magazine_size.load()) {
|
||||
if (self.smr != null and
|
||||
cpu.free_rounds == magazine_size.load())
|
||||
{
|
||||
const smr = self.smr orelse unreachable;
|
||||
|
||||
// Stamp the now-full magazine.
|
||||
cpu.free.?.seq = ke.smr.deferred_advance(self.smr.?);
|
||||
free_mag.seq = ke.smr.deferred_advance(smr);
|
||||
}
|
||||
|
||||
break true;
|
||||
@@ -929,8 +947,8 @@ pub const Zone = struct {
|
||||
}
|
||||
|
||||
// No empty magazine in the depot, try to allocate a new one.
|
||||
// We need to drop IPL and the CPU lock here because we need to acquire
|
||||
// the magazine's allocator's blocking lock.
|
||||
// We need to drop IPL and the CPU lock here because we need to
|
||||
// acquire the magazine's allocator's blocking lock.
|
||||
cpu.lock.release(ipl);
|
||||
|
||||
const raw_mag = magazine_zone.alloc(
|
||||
@@ -970,7 +988,7 @@ pub const Zone = struct {
|
||||
self.release(self.arg, &slice, self.chunk_size);
|
||||
}
|
||||
|
||||
fn slab_free(self: *Self, obj: *anyopaque, poison: bool) void {
|
||||
fn slab_free(self: *Zone, obj: *anyopaque, poison: bool) void {
|
||||
if (!is_poison_enabled or poison and !self.dont_touch) {
|
||||
if (self.dtor) |dtor| {
|
||||
dtor(obj);
|
||||
@@ -988,7 +1006,8 @@ pub const Zone = struct {
|
||||
@intFromPtr(obj),
|
||||
mm.page_size,
|
||||
);
|
||||
const phys = mmp.kernel_space.pmap.query(page_va).?;
|
||||
const phys = mmp.kernel_space.pmap.query(page_va) orelse
|
||||
unreachable;
|
||||
const page = mm.pfn_to_struct_page(mm.page_to_pfn(phys));
|
||||
break :blk page.alloced.slab_data.slab;
|
||||
} else blk: {
|
||||
@@ -1023,7 +1042,7 @@ pub const Zone = struct {
|
||||
elem_size: usize,
|
||||
policy: mm.WaitPolicy,
|
||||
) mm.Error!usize {
|
||||
const self: *Self = @ptrCast(@alignCast(arg));
|
||||
const self: *Zone = @ptrCast(@alignCast(arg));
|
||||
|
||||
_ = elem_size;
|
||||
|
||||
@@ -1046,18 +1065,21 @@ pub const Zone = struct {
|
||||
|
||||
self.lock.acquire();
|
||||
|
||||
// Note: this is racy and could lead to two threads creating a slab
|
||||
// at the same time. Worst case scenario, we have one slab too many,
|
||||
// so we don't care; we already did the work required for slab
|
||||
// construction and this ideally should not happen very often.
|
||||
// Note: this is racy and could lead to two threads creating a
|
||||
// slab at the same time. Worst case scenario, we have one slab
|
||||
// too many, so we don't care; we already did the work
|
||||
// required for slab construction and this ideally should not
|
||||
// happen very often.
|
||||
self.partial_slabs.insert_tail(&slab.link);
|
||||
} else {
|
||||
slab = @fieldParentPtr("link", self.partial_slabs.first());
|
||||
}
|
||||
|
||||
while (slab.refcount != slab.capacity and i < requested) {
|
||||
const idx = slab.bitmap_alloc().?;
|
||||
const buf: *anyopaque = @ptrFromInt(slab.base + idx * self.chunk_size);
|
||||
const idx = slab.bitmap_alloc() orelse unreachable;
|
||||
const buf: *anyopaque = @ptrFromInt(
|
||||
slab.base + idx * self.chunk_size,
|
||||
);
|
||||
|
||||
slab.refcount += 1;
|
||||
|
||||
@@ -1068,7 +1090,7 @@ pub const Zone = struct {
|
||||
|
||||
if (is_poison_enabled) {
|
||||
if (!check_poison(buf, self.obj_size, free_poison)) {
|
||||
@panic("slab poison mismatch: object corrupted after free");
|
||||
@panic("poison mismatch: possible use-after-free");
|
||||
}
|
||||
|
||||
fill_with_poison(buf, self.obj_size, alloc_poison);
|
||||
@@ -1087,8 +1109,12 @@ pub const Zone = struct {
|
||||
return i;
|
||||
}
|
||||
|
||||
fn zone_release(arg: ?*anyopaque, elems: []*anyopaque, elem_size: usize) void {
|
||||
const self: *Self = @ptrCast(@alignCast(arg));
|
||||
fn zone_release(
|
||||
arg: ?*anyopaque,
|
||||
elems: []*anyopaque,
|
||||
elem_size: usize,
|
||||
) void {
|
||||
const self: *Zone = @ptrCast(@alignCast(arg));
|
||||
|
||||
_ = elem_size;
|
||||
|
||||
@@ -1112,7 +1138,7 @@ pub const Zone = struct {
|
||||
|
||||
/// Periodic updates on the zone.
|
||||
/// Updates the magazine working set and trims or grows them if needed.
|
||||
fn update(self: *Self) void {
|
||||
fn update(self: *Zone) void {
|
||||
const ipl = self.depot_lock.acquire();
|
||||
|
||||
// Update the working set size.
|
||||
@@ -1187,7 +1213,7 @@ pub const Zone = struct {
|
||||
}
|
||||
|
||||
/// Returns whether or not we should trim excess magazines.
|
||||
fn trim_needed(self: *Self) bool {
|
||||
fn trim_needed(self: *Zone) bool {
|
||||
if (!self.use_magazines) return false;
|
||||
if (self.trim_depot) return true;
|
||||
|
||||
@@ -1221,8 +1247,9 @@ pub const Zone = struct {
|
||||
|
||||
/// Called periodically to trim our magazines.
|
||||
/// This frees the excess magazines.
|
||||
fn trim(self: *Self, trim_depot: bool) void {
|
||||
// Detach magazines under the locks, then release their objects unlocked.
|
||||
fn trim(self: *Zone, trim_depot: bool) void {
|
||||
// Detach magazines under the locks, then release their objects
|
||||
// unlocked.
|
||||
self.lock.acquire();
|
||||
|
||||
const empty_mags = self.trim_maglist(&self.depot.empty_mags);
|
||||
@@ -1262,7 +1289,7 @@ pub const Zone = struct {
|
||||
|
||||
/// Called when memory is low and we need to make memory ASAP.
|
||||
/// Drains all magazines from the depot.
|
||||
fn drain(self: *Self) void {
|
||||
fn drain(self: *Zone) void {
|
||||
if (!self.use_magazines) return;
|
||||
|
||||
// Detach magazines before invoking release callbacks.
|
||||
@@ -1304,7 +1331,7 @@ pub const Zone = struct {
|
||||
}
|
||||
|
||||
/// Detach excess magazines for destruction.
|
||||
fn trim_maglist(self: *Self, maglist: *MagazineList) ?*Magazine {
|
||||
fn trim_maglist(self: *Zone, maglist: *MagazineList) ?*Magazine {
|
||||
const ipl = self.depot_lock.acquire();
|
||||
|
||||
// Grab the magazines and WSS under the depot lock.
|
||||
@@ -1345,7 +1372,7 @@ pub const Zone = struct {
|
||||
}
|
||||
|
||||
/// Trim a CPU's depot to `target`.
|
||||
fn trim_cpu(self: *Self, cpu: *Cpu, depot: *Depot, target: usize) void {
|
||||
fn trim_cpu(self: *Zone, cpu: *Cpu, depot: *Depot, target: usize) void {
|
||||
const ipl = cpu.lock.acquire();
|
||||
|
||||
// Split the target (cpu_depot_size) in 2 for each magazine type.
|
||||
@@ -1361,7 +1388,7 @@ pub const Zone = struct {
|
||||
// Move them to our depot.
|
||||
for (0..n) |_| {
|
||||
Depot.free(
|
||||
Depot.alloc(&cpu.depot.full_mags).?,
|
||||
Depot.alloc(&cpu.depot.full_mags) orelse unreachable,
|
||||
&depot.full_mags,
|
||||
self.reuse_policy,
|
||||
);
|
||||
@@ -1375,7 +1402,7 @@ pub const Zone = struct {
|
||||
// Move them to our depot.
|
||||
for (0..n) |_| {
|
||||
Depot.free(
|
||||
Depot.alloc(&cpu.depot.empty_mags).?,
|
||||
Depot.alloc(&cpu.depot.empty_mags) orelse unreachable,
|
||||
&depot.empty_mags,
|
||||
self.reuse_policy,
|
||||
);
|
||||
@@ -1385,7 +1412,7 @@ pub const Zone = struct {
|
||||
cpu.lock.release(ipl);
|
||||
}
|
||||
|
||||
fn maglist_destroy(self: *Self, head: ?*Magazine, n: usize) void {
|
||||
fn maglist_destroy(self: *Zone, head: ?*Magazine, n: usize) void {
|
||||
var elems = head;
|
||||
while (elems) |mag| {
|
||||
elems = mag.next;
|
||||
@@ -1394,7 +1421,7 @@ pub const Zone = struct {
|
||||
}
|
||||
|
||||
/// Destroy a magazine.
|
||||
fn magazine_destroy(self: *Self, magazine: *Magazine, rounds: usize) void {
|
||||
fn magazine_destroy(self: *Zone, magazine: *Magazine, rounds: usize) void {
|
||||
if (rounds != 0) {
|
||||
if (self.smr) |smr| {
|
||||
// The stamp may still be uncommitted if the magazine never
|
||||
@@ -1439,7 +1466,7 @@ pub const Zone = struct {
|
||||
mmp.phys.free(phys);
|
||||
}
|
||||
|
||||
fn slab_create_small(self: *Self, policy: mm.WaitPolicy) mm.Error!*Slab {
|
||||
fn slab_create_small(self: *Zone, policy: mm.WaitPolicy) mm.Error!*Slab {
|
||||
const buf = try alloc_page(policy);
|
||||
|
||||
const capacity = Slab.calc_capacity(self.chunk_size, self.alignment);
|
||||
@@ -1463,7 +1490,9 @@ pub const Zone = struct {
|
||||
}
|
||||
|
||||
for (0..capacity) |i| {
|
||||
const obj: *anyopaque = @ptrFromInt(slab.base + i * self.chunk_size);
|
||||
const obj: *anyopaque = @ptrFromInt(
|
||||
slab.base + i * self.chunk_size,
|
||||
);
|
||||
if (is_poison_enabled) {
|
||||
fill_with_poison(obj, self.obj_size, free_poison);
|
||||
}
|
||||
@@ -1472,7 +1501,7 @@ pub const Zone = struct {
|
||||
return slab;
|
||||
}
|
||||
|
||||
fn slab_create_large(self: *Self, policy: mm.WaitPolicy) mm.Error!*Slab {
|
||||
fn slab_create_large(self: *Zone, policy: mm.WaitPolicy) mm.Error!*Slab {
|
||||
const capacity = (self.slab_size) / self.chunk_size;
|
||||
const buf = try mmp.heap.alloc(self.slab_size, policy);
|
||||
|
||||
@@ -1515,7 +1544,7 @@ pub const Zone = struct {
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn slab_create(self: *Self, policy: mm.WaitPolicy) mm.Error!*Slab {
|
||||
fn slab_create(self: *Zone, policy: mm.WaitPolicy) mm.Error!*Slab {
|
||||
if (self.obj_size <= small_slab_size) {
|
||||
return self.slab_create_small(policy);
|
||||
} else {
|
||||
@@ -1523,7 +1552,7 @@ pub const Zone = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn slab_destroy(self: *Self, slab: *Slab) void {
|
||||
fn slab_destroy(self: *Zone, slab: *Slab) void {
|
||||
if (self.obj_size > small_slab_size) {
|
||||
const base = slab.base;
|
||||
const alloc_size = @sizeOf(Slab) + Slab.bitmap_bytes(slab.capacity);
|
||||
@@ -1538,7 +1567,7 @@ pub const Zone = struct {
|
||||
}
|
||||
|
||||
/// Check that a full magazine can be taken from the list head.
|
||||
fn full_mags_ready(self: *Self, list: *MagazineList) bool {
|
||||
fn full_mags_ready(self: *Zone, list: *MagazineList) bool {
|
||||
const head = list.head orelse return false;
|
||||
const smr = self.smr orelse return true;
|
||||
|
||||
@@ -1547,7 +1576,7 @@ pub const Zone = struct {
|
||||
|
||||
/// Deferred free-time work for SMR zones, ran when a full magazine
|
||||
/// is reused after its grace period expired.
|
||||
fn magazine_reuse(self: *Self, mag: *Magazine) void {
|
||||
fn magazine_reuse(self: *Zone, mag: *Magazine) void {
|
||||
if (self.smr == null) return;
|
||||
|
||||
for (0..magazine_size.load()) |i| {
|
||||
@@ -1564,7 +1593,7 @@ pub const Zone = struct {
|
||||
|
||||
/// Move all local full magazines to the zone depot so their sequences
|
||||
/// can age, committing their deferred advances. Depot lock is held.
|
||||
fn smr_rotate_full(self: *Self, smr: *ke.smr.Domain, cpu: *Cpu) void {
|
||||
fn smr_rotate_full(self: *Zone, smr: *ke.smr.Domain, cpu: *Cpu) void {
|
||||
const src = &cpu.depot.full_mags;
|
||||
const dst = &self.depot.full_mags;
|
||||
const tail = src.tail orelse return;
|
||||
@@ -1584,7 +1613,8 @@ pub const Zone = struct {
|
||||
src.num = 0;
|
||||
src.min = 0;
|
||||
|
||||
// The last magazine has the newest stamp, committing it covers them all.
|
||||
// The last magazine has the newest stamp, committing it covers them
|
||||
// all.
|
||||
ke.smr.deferred_advance_commit(smr, tail.seq);
|
||||
}
|
||||
|
||||
@@ -1592,7 +1622,7 @@ pub const Zone = struct {
|
||||
/// after an allocation. The CPU depot moves its extra full magazines to the
|
||||
/// zone depot, and tries to get full magazines from the zone depot up to
|
||||
/// `target`. CPU lock is held.
|
||||
fn alloc_depot_rebalance(self: *Self, target: usize, cpu: *Cpu) void {
|
||||
fn alloc_depot_rebalance(self: *Zone, target: usize, cpu: *Cpu) void {
|
||||
if (!self.depot_lock.try_acquire_no_ipl()) {
|
||||
// Register the contention.
|
||||
self.depot_lock.acquire_no_ipl();
|
||||
@@ -1605,7 +1635,7 @@ pub const Zone = struct {
|
||||
|
||||
for (0..n) |_| {
|
||||
Depot.free(
|
||||
Depot.alloc(&cpu.depot.empty_mags).?,
|
||||
Depot.alloc(&cpu.depot.empty_mags) orelse unreachable,
|
||||
&self.depot.empty_mags,
|
||||
self.reuse_policy,
|
||||
);
|
||||
@@ -1631,7 +1661,7 @@ pub const Zone = struct {
|
||||
if (n != 0 and self.full_mags_ready(&self.depot.full_mags)) {
|
||||
for (0..n) |_| {
|
||||
Depot.free(
|
||||
Depot.alloc(&self.depot.full_mags).?,
|
||||
Depot.alloc(&self.depot.full_mags) orelse unreachable,
|
||||
&cpu.depot.full_mags,
|
||||
self.reuse_policy,
|
||||
);
|
||||
@@ -1645,7 +1675,7 @@ pub const Zone = struct {
|
||||
/// after a free.The CPU depot moves its extra full magazines to the
|
||||
/// zone depot, and tries to get empty magazines from the zone depot up to
|
||||
/// `target`. CPU lock is held.
|
||||
fn free_depot_rebalance(self: *Self, target: usize, cpu: *Cpu) void {
|
||||
fn free_depot_rebalance(self: *Zone, target: usize, cpu: *Cpu) void {
|
||||
if (!self.depot_lock.try_acquire_no_ipl()) {
|
||||
// Register the contention.
|
||||
self.depot_lock.acquire_no_ipl();
|
||||
@@ -1663,7 +1693,8 @@ pub const Zone = struct {
|
||||
if (n != 0 and self.full_mags_ready(&self.depot.full_mags)) {
|
||||
for (0..n) |_| {
|
||||
Depot.free(
|
||||
Depot.alloc(&self.depot.full_mags).?,
|
||||
Depot.alloc(&self.depot.full_mags) orelse
|
||||
unreachable,
|
||||
&cpu.depot.full_mags,
|
||||
self.reuse_policy,
|
||||
);
|
||||
@@ -1675,7 +1706,7 @@ pub const Zone = struct {
|
||||
|
||||
for (0..n) |_| {
|
||||
Depot.free(
|
||||
Depot.alloc(&cpu.depot.full_mags).?,
|
||||
Depot.alloc(&cpu.depot.full_mags) orelse unreachable,
|
||||
&self.depot.full_mags,
|
||||
self.reuse_policy,
|
||||
);
|
||||
@@ -1691,7 +1722,7 @@ pub const Zone = struct {
|
||||
);
|
||||
for (0..n) |_| {
|
||||
Depot.free(
|
||||
Depot.alloc(&self.depot.empty_mags).?,
|
||||
Depot.alloc(&self.depot.empty_mags) orelse unreachable,
|
||||
&cpu.depot.empty_mags,
|
||||
self.reuse_policy,
|
||||
);
|
||||
@@ -1719,7 +1750,7 @@ pub fn TypedZone(comptime T: type) type {
|
||||
return struct {
|
||||
zone: Zone,
|
||||
|
||||
const Self = @This();
|
||||
const TZone = @This();
|
||||
|
||||
pub const InitOptions = struct {
|
||||
ctor: ?*const fn (*T) void = null,
|
||||
@@ -1728,7 +1759,11 @@ pub fn TypedZone(comptime T: type) type {
|
||||
smr_reclaim: ?*const fn (*T) void = null,
|
||||
};
|
||||
|
||||
pub fn init(self: *@This(), name: []const u8, options: InitOptions) void {
|
||||
pub fn init(
|
||||
self: *TZone,
|
||||
name: []const u8,
|
||||
options: InitOptions,
|
||||
) void {
|
||||
self.zone.init(name, @sizeOf(T), .{
|
||||
.alignment = @alignOf(T),
|
||||
.ctor = @ptrCast(options.ctor),
|
||||
@@ -1738,17 +1773,17 @@ pub fn TypedZone(comptime T: type) type {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn create(self: *Self) mm.Error!*T {
|
||||
pub fn create(self: *TZone) mm.Error!*T {
|
||||
const ret = try self.zone.alloc(.{});
|
||||
return @ptrCast(@alignCast(ret));
|
||||
}
|
||||
|
||||
pub fn create_opts(self: *Self, opts: AllocOpts) mm.Error!*T {
|
||||
pub fn create_opts(self: *TZone, opts: AllocOpts) mm.Error!*T {
|
||||
const ret = try self.zone.alloc(opts);
|
||||
return @ptrCast(@alignCast(ret));
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Self, obj: *T) void {
|
||||
pub fn destroy(self: *TZone, obj: *T) void {
|
||||
self.zone.free(obj);
|
||||
}
|
||||
};
|
||||
@@ -1787,7 +1822,7 @@ pub fn late_init() linksection(r.init) void {
|
||||
z.cpus = cpus_alloc();
|
||||
|
||||
for (0..ke.ncpus) |i| {
|
||||
z.cpus[i] = .init(.{
|
||||
z.cpus[i] = rtl.CachePadded(Cpu).init(.{
|
||||
.lock = ke.SpinLock.init("Magazine"),
|
||||
.alloc = null,
|
||||
.free = null,
|
||||
@@ -1889,7 +1924,8 @@ fn gpa_remap(
|
||||
new_len: usize,
|
||||
ret_addr: usize,
|
||||
) ?[*]u8 {
|
||||
if (gpa_resize(undefined, buf, alignment, new_len, ret_addr)) return buf.ptr;
|
||||
if (gpa_resize(undefined, buf, alignment, new_len, ret_addr))
|
||||
return buf.ptr;
|
||||
|
||||
const new_ptr = gpa_alloc(undefined, new_len, alignment, ret_addr) orelse
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user