diff --git a/src/mm/balance.zig b/src/mm/balance.zig new file mode 100644 index 0000000..30ee25e --- /dev/null +++ b/src/mm/balance.zig @@ -0,0 +1,71 @@ +//! Balance manager. +//! The balance manager is called periodically (or when memory is low) to do +//! the following actions: +//! 1. Reap unused kernel stacks +//! 2. Zone trimming +//! 3. Working-set trimming (eventually...) + +const r = @import("root"); +const std = @import("std"); +const ps = r.ps; +const ke = r.ke; +const mm = r.mm; +const mi = mm.private; + +const balance_interval = ke.Tunable(u32, 1000, "mm.balance.interval_ms"); + +const stack_reap_interval = 5; +const zone_update_interval = mm.zone.update_interval_s; + +var stack_reap_time: u8 = stack_reap_interval; +var zone_update_time: u8 = zone_update_interval; + +pub fn init() void { + const td = ps.thread.create_kernel(.LowRealtime, .{ + .func = balance_manager, + .arg = null, + }, true) catch + @panic("Could not create balance manager thread"); + + ke.sched.enqueue(&td.kern); +} + +fn balance_manager(_: ?*anyopaque) void { + while (true) { + var timer: ke.Timer = undefined; + timer.init(); + ke.timer.set( + &timer, + .from(r.Milliseconds.init(balance_interval.load())), + .{}, + ); + + _ = ke.wait.wait_one(&timer.hdr, "balmgr", .{}) catch unreachable; + + stack_reap_time -= 1; + zone_update_time -= 1; + + if (zone_update_time == 0) { + zone_update_time = zone_update_interval; + mm.zone.update(); + } + + if (stack_reap_time == 0) { + stack_reap_time = stack_reap_interval; + + if (ke.thread.stack_cache_update()) |lst| { + // Reap all the excess stacks. + var entry: ?*ke.Stack = lst; + + while (entry != null) { + const next = entry.?.next; + mm.heap.free( + (@intFromPtr(entry) + @sizeOf(ke.Stack)) - ps.thread.kernel_thread_stack_size, + ps.thread.kernel_thread_stack_size, + ); + entry = next; + } + } + } + } +} diff --git a/src/mm/init.zig b/src/mm/init.zig index e9e94c3..b9a6695 100644 --- a/src/mm/init.zig +++ b/src/mm/init.zig @@ -14,4 +14,5 @@ pub fn init() linksection(r.init) void { pub fn late_init() linksection(r.init) void { mi.zone.late_init(); mi.tlb.init(); + mi.balance.init(); } diff --git a/src/mm/private.zig b/src/mm/private.zig index 5d6f5f7..04a5b8d 100644 --- a/src/mm/private.zig +++ b/src/mm/private.zig @@ -7,6 +7,7 @@ pub const heap = @import("heap.zig"); pub const zone = @import("zone.zig"); pub const PMap = pmap.PMap; pub const tlb = @import("tlb.zig"); +pub const balance = @import("balance.zig"); const ke = @import("root").ke; diff --git a/src/mm/zone.zig b/src/mm/zone.zig index df7f14c..26ec5bc 100644 --- a/src/mm/zone.zig +++ b/src/mm/zone.zig @@ -118,9 +118,7 @@ const ex = r.ex; const mi = mm.private; /// Interval at which we do housekeeping (working set update, reaping, etc.) -const update_interval_s = 15; -const update_interval: r.Nanoseconds = .from(r.Seconds.init(update_interval_s)); -var update_work_item: ex.DelayedWorkItem = undefined; +pub const update_interval_s = 15; /// Fixed point conversion for WMA computation. const wma_unit = 256; @@ -1076,11 +1074,10 @@ pub const Zone = struct { self.lock.release(); - while (true) { + while (entry != head) { const slab: *Slab = @fieldParentPtr("link", entry); const next = entry.next; self.slab_destroy(slab); - if (next == head) break; entry = next; } } @@ -1332,6 +1329,7 @@ pub const Zone = struct { mm.heap.free(slab.base, self.slab_size); } else { + std.log.info("slab is {*}", .{slab}); free_page(@ptrFromInt( std.mem.alignBackward(usize, @intFromPtr(slab), mm.page_size), )); @@ -1503,7 +1501,7 @@ pub const Zone = struct { }; /// Called periodically to do housekeeping tasks on zones. -fn update(_: ?*anyopaque) void { +pub fn update() void { zone_list_lock.acquire(); var zone = all_zones; @@ -1513,9 +1511,6 @@ fn update(_: ?*anyopaque) void { } zone_list_lock.release(); - - // Do it again. - ex.work.enqueue_in(&update_work_item, update_interval); } /// Parameterized version of Zone, useful for object caches. @@ -1605,8 +1600,6 @@ pub fn late_init() linksection(r.init) void { zone_list_lock.release(); magazines_initialized = true; - update_work_item.init(.Normal, update, null); - ex.work.enqueue_in(&update_work_item, update_interval); smr_zone.init("SMR", .{}); smr_cpu_zone.init("SMR CPU", @sizeOf(ke.smr.Cpu) * ke.ncpus, .{});