Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 39 additions & 5 deletions src/kernel/heap.zig
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ pub const FreeListAllocator = struct {
};
}

pub fn bytesFree(self: *Self) usize {
var bytes: usize = 0;
var header_opt = self.first_free;
while (header_opt) |header| : (header_opt = header.next_free) {
bytes += header.size;
}
return bytes;
}

pub fn allocator(self: *Self) Allocator {
return Allocator.init(self, alloc, resize, free);
}
Expand Down Expand Up @@ -426,6 +435,7 @@ pub const FreeListAllocator = struct {
try testing.expectEqual(header, free_list.first_free.?);
try testing.expectEqual(header.next_free, null);
try testing.expectEqual(header.size, size - @sizeOf(Header));
try testing.expectEqual(free_list.bytesFree(), size - @sizeOf(Header));

try testing.expectError(Error.TooSmall, FreeListAllocator.init(0, @sizeOf(Header) - 1));
}
Expand All @@ -436,10 +446,10 @@ pub const FreeListAllocator = struct {
defer testing.allocator.free(region);
const start = @ptrToInt(region.ptr);
var free_list = &(try FreeListAllocator.init(start, size));

std.debug.print("", .{});
var usage = @sizeOf(Header);

const alloc0 = try free_list.alloc(64, 0, 0, @returnAddress());
usage += 64 + @sizeOf(Header);
const alloc0_addr = @ptrToInt(alloc0.ptr);
// Should be at the start of the heap
try testing.expectEqual(alloc0_addr, start);
Expand All @@ -448,11 +458,11 @@ pub const FreeListAllocator = struct {
try testing.expectEqual(header.size, size - 64 - @sizeOf(Header));
try testing.expectEqual(header.next_free, null);
try testing.expectEqual(free_list.first_free, header);

std.debug.print("", .{});
try testing.expectEqual(free_list.bytesFree(), size - usage);

// 64 bytes aligned to 4 bytes
const alloc1 = try free_list.alloc(64, 4, 0, @returnAddress());
usage += 64 + @sizeOf(Header);
const alloc1_addr = @ptrToInt(alloc1.ptr);
const alloc1_end = alloc1_addr + alloc1.len;
// Should be to the right of the first allocation, with some alignment padding in between
Expand All @@ -464,8 +474,10 @@ pub const FreeListAllocator = struct {
try testing.expectEqual(header.size, size - (alloc1_end - start) - @sizeOf(Header));
try testing.expectEqual(header.next_free, null);
try testing.expectEqual(free_list.first_free, header);
try testing.expectEqual(free_list.bytesFree(), size - usage);

const alloc2 = try free_list.alloc(64, 256, 0, @returnAddress());
usage += 64 + @sizeOf(Header);
const alloc2_addr = @ptrToInt(alloc2.ptr);
const alloc2_end = alloc2_addr + alloc2.len;
try testing.expect(alloc1_end < alloc2_addr);
Expand All @@ -479,26 +491,32 @@ pub const FreeListAllocator = struct {
try testing.expectEqual(free_list.first_free, header);
try testing.expectEqual(header.next_free, second_header);
}
try testing.expectEqual(free_list.bytesFree(), size - usage);

// Try allocating something smaller than @sizeOf(Header). This should scale up to @sizeOf(Header)
var alloc3 = try free_list.alloc(1, 0, 0, @returnAddress());
usage += @sizeOf(Header) * 2;
const alloc3_addr = @ptrToInt(alloc3.ptr);
const alloc3_end = alloc3_addr + @sizeOf(Header);
const header2 = @intToPtr(*Header, alloc3_end);
// The new free node on the right should be the first one free
try testing.expectEqual(free_list.first_free, header2);
// And it should point to the free node on the right of alloc2
try testing.expectEqual(header2.next_free, second_header);
try testing.expectEqual(free_list.bytesFree(), size - usage);

// Attempting to allocate more than the size of the largest free node should fail
const remaining_size = second_header.size + @sizeOf(Header);
try testing.expectError(Allocator.Error.OutOfMemory, free_list.alloc(remaining_size + 1, 0, 0, @returnAddress()));
try testing.expectEqual(free_list.bytesFree(), size - usage);

// Alloc a non aligned to header
var alloc4 = try free_list.alloc(13, 1, 0, @returnAddress());
usage += 13 + @sizeOf(Header);
const alloc4_addr = @ptrToInt(alloc4.ptr);
const alloc4_end = alloc4_addr + std.mem.alignForward(13, @alignOf(Header));
const header3 = @intToPtr(*Header, alloc4_end);
try testing.expectEqual(free_list.bytesFree(), size - usage);

// We should still have a length of 13
try testing.expectEqual(alloc4.len, 13);
Expand All @@ -515,10 +533,17 @@ pub const FreeListAllocator = struct {
defer testing.allocator.free(region);
const start = @ptrToInt(region.ptr);
var free_list = &(try FreeListAllocator.init(start, size));
var usage = @sizeOf(Header);

var alloc0 = try free_list.alloc(128, 0, 0, @returnAddress());
usage += @sizeOf(Header) + 128;
try testing.expectEqual(free_list.bytesFree(), size - usage);
var alloc1 = try free_list.alloc(256, 0, 0, @returnAddress());
usage += @sizeOf(Header) + 256;
try testing.expectEqual(free_list.bytesFree(), size - usage);
var alloc2 = try free_list.alloc(64, 0, 0, @returnAddress());
usage += @sizeOf(Header) + 64;
try testing.expectEqual(free_list.bytesFree(), size - usage);

// There should be a single free node after alloc2
const free_node3 = @intToPtr(*Header, @ptrToInt(alloc2.ptr) + alloc2.len);
Expand All @@ -527,27 +552,32 @@ pub const FreeListAllocator = struct {
try testing.expectEqual(free_node3.next_free, null);

free_list.free(alloc0, 0, 0);
usage -= 128 + @sizeOf(Header);
// There should now be two free nodes. One where alloc0 was and another after alloc2
const free_node0 = @intToPtr(*Header, start);
try testing.expectEqual(free_list.first_free, free_node0);
try testing.expectEqual(free_node0.size, alloc0.len - @sizeOf(Header));
try testing.expectEqual(free_node0.next_free, free_node3);
try testing.expectEqual(free_list.bytesFree(), size - usage);

// Freeing alloc1 should join it with free_node0
free_list.free(alloc1, 0, 0);
usage -= 256 + @sizeOf(Header);
try testing.expectEqual(free_list.first_free, free_node0);
try testing.expectEqual(free_node0.size, alloc0.len - @sizeOf(Header) + alloc1.len);
try testing.expectEqual(free_node0.next_free, free_node3);
try testing.expectEqual(free_list.bytesFree(), size - usage);

// Freeing alloc2 should then join them all together into one big free node
free_list.free(alloc2, 0, 0);
usage -= 64 + @sizeOf(Header);
try testing.expectEqual(free_list.first_free, free_node0);
try testing.expectEqual(free_node0.size, size - @sizeOf(Header));
try testing.expectEqual(free_node0.next_free, null);
try testing.expectEqual(free_list.bytesFree(), size - usage);
}

test "resize" {
std.debug.print("", .{});
const size = 1024;
var region = try testing.allocator.alloc(u8, size);
defer testing.allocator.free(region);
Expand Down Expand Up @@ -608,3 +638,7 @@ pub fn init(comptime vmm_payload: type, heap_vmm: *vmm.VirtualMemoryManager(vmm_
errdefer heap_vmm.free(heap_start) catch unreachable;
return try FreeListAllocator.init(heap_start, heap_size);
}

test "" {
_ = FreeListAllocator;
}
2 changes: 2 additions & 0 deletions src/kernel/kmain.zig
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ export fn kmain(boot_payload: arch.BootPayload) void {
panic_root.panic(@errorReturnTrace(), "Failed to schedule init stage 2 task: {}\n", .{e});
};

kmain_log.info("Kernel heap free: {} KB / {} KB\n", .{ kernel_heap.bytesFree() / 1024, heap_size / 1024 });

// Can't return for now, later this can return maybe
// TODO: Maybe make this the idle task
arch.spinWait();
Expand Down