Heap: Use LinkedList instead of doing things manually

This commit is contained in:
apio 2022-12-06 18:28:04 +01:00
parent d8f75f1d3c
commit 09e447d9d2
Signed by: apio
GPG Key ID: B8A7D06E42258954

View File

@ -5,6 +5,7 @@
#include "memory/KernelVM.h" #include "memory/KernelVM.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include <luna/Alignment.h> #include <luna/Alignment.h>
#include <luna/LinkedList.h>
#include <luna/SafeArithmetic.h> #include <luna/SafeArithmetic.h>
#include <luna/String.h> #include <luna/String.h>
#include <luna/SystemError.h> #include <luna/SystemError.h>
@ -18,20 +19,17 @@ static constexpr usize BLOCK_DEAD = 0xdeaddeaddeaddead;
static constexpr usize MINIMUM_PAGES_PER_ALLOCATION = 4; static constexpr usize MINIMUM_PAGES_PER_ALLOCATION = 4;
struct HeapBlock struct HeapBlock : DoublyLinkedListNode<HeapBlock>
{ {
usize req_size; usize req_size;
usize full_size; usize full_size;
int status; int status;
HeapBlock* next;
HeapBlock* last;
usize magic; usize magic;
}; };
static_assert(sizeof(HeapBlock) == 48UL); static_assert(sizeof(HeapBlock) == 48UL);
static HeapBlock* heap_start = nullptr; static DoublyLinkedList<HeapBlock> heap;
static HeapBlock* heap_end = nullptr;
static Result<HeapBlock*> allocate_pages(usize count) static Result<HeapBlock*> allocate_pages(usize count)
{ {
@ -96,7 +94,8 @@ static Result<HeapBlock*> split(HeapBlock* block, usize size)
const usize old_size = const usize old_size =
block->full_size; // Save the old value of this variable since we are going to use it after modifying it block->full_size; // Save the old value of this variable since we are going to use it after modifying it
if (available < (size + sizeof(HeapBlock))) return err(0); // This error is not propagated. if (available < (size + sizeof(HeapBlock)))
return err(ENONE); // This block hasn't got enough free space to hold the requested size.
const usize offset = get_fair_offset_to_split_at(block, size + sizeof(HeapBlock)); const usize offset = get_fair_offset_to_split_at(block, size + sizeof(HeapBlock));
block->full_size = offset; // shrink the old block to fit this offset block->full_size = offset; // shrink the old block to fit this offset
@ -106,24 +105,20 @@ static Result<HeapBlock*> split(HeapBlock* block, usize size)
new_block->magic = BLOCK_MAGIC; new_block->magic = BLOCK_MAGIC;
new_block->status = (block->status & BLOCK_END_MEM) ? BLOCK_END_MEM : 0; new_block->status = (block->status & BLOCK_END_MEM) ? BLOCK_END_MEM : 0;
new_block->full_size = old_size - (offset + sizeof(HeapBlock)); new_block->full_size = old_size - (offset + sizeof(HeapBlock));
new_block->next = block->next; heap.append_after(block, new_block);
new_block->last = block;
block->status &= ~BLOCK_END_MEM; // this block is no longer the last block in this memory range block->status &= ~BLOCK_END_MEM; // this block is no longer the last block in its memory range
block->next = new_block;
return new_block; return new_block;
} }
static Result<void> combine_forward(HeapBlock* block) static Result<void> combine_forward(HeapBlock* block)
{ {
HeapBlock* const next = block->next; // The caller needs to ensure there is a next block.
if (next == heap_end) heap_end = block; HeapBlock* const next = heap.next(block).value();
heap.remove(next);
next->magic = BLOCK_DEAD; next->magic = BLOCK_DEAD;
block->next = block->next->next;
if (block->next) block->next->last = block;
if (next->status & BLOCK_END_MEM) if (next->status & BLOCK_END_MEM)
{ {
if (next->status & BLOCK_START_MEM) if (next->status & BLOCK_START_MEM)
@ -142,13 +137,11 @@ static Result<void> combine_forward(HeapBlock* block)
static Result<HeapBlock*> combine_backward(HeapBlock* block) static Result<HeapBlock*> combine_backward(HeapBlock* block)
{ {
HeapBlock* const last = block->last; // The caller needs to ensure there is a last block.
if (block == heap_end) heap_end = last; HeapBlock* const last = heap.previous(block).value();
heap.remove(block);
block->magic = BLOCK_DEAD; block->magic = BLOCK_DEAD;
last->next = block->next;
if (last->next) last->next->last = last;
if (block->status & BLOCK_END_MEM) if (block->status & BLOCK_END_MEM)
{ {
if (block->status & BLOCK_START_MEM) if (block->status & BLOCK_START_MEM)
@ -171,7 +164,7 @@ Result<void*> kmalloc(usize size)
size = align_up(size, 16UL); size = align_up(size, 16UL);
if (!heap_start) if (!heap.first().has_value())
{ {
const usize pages = get_pages_for_allocation(size + sizeof(HeapBlock)); const usize pages = get_pages_for_allocation(size + sizeof(HeapBlock));
HeapBlock* const block = TRY(allocate_pages(pages)); HeapBlock* const block = TRY(allocate_pages(pages));
@ -179,15 +172,10 @@ Result<void*> kmalloc(usize size)
block->full_size = (pages * ARCH_PAGE_SIZE) - sizeof(HeapBlock); block->full_size = (pages * ARCH_PAGE_SIZE) - sizeof(HeapBlock);
block->magic = BLOCK_MAGIC; block->magic = BLOCK_MAGIC;
block->status = BLOCK_START_MEM | BLOCK_END_MEM; block->status = BLOCK_START_MEM | BLOCK_END_MEM;
block->next = block->last = nullptr; heap.append(block);
heap_start = block;
check(!heap_end);
heap_end = heap_start;
} }
HeapBlock* block = heap_start; HeapBlock* block = heap.first().value();
while (block) while (block)
{ {
// Trying to find a free block... // Trying to find a free block...
@ -195,7 +183,7 @@ Result<void*> kmalloc(usize size)
{ {
if (block->full_size < size) if (block->full_size < size)
{ {
block = block->next; // Let's not try to split this block, it's not big enough block = heap.next(block).value_or(nullptr);
continue; continue;
} }
break; // We found a free block that's big enough!! break; // We found a free block that's big enough!!
@ -203,10 +191,10 @@ Result<void*> kmalloc(usize size)
auto rc = split(block, size); auto rc = split(block, size);
if (rc.has_value()) if (rc.has_value())
{ {
block = rc.release_value(); // We managed to get a free block from a larger used block!! block = rc.value(); // We managed to get a free block from a larger used block!!
break; break;
} }
block = block->next; block = heap.next(block).value_or(nullptr);
} }
if (!block) // No free blocks, let's allocate a new one if (!block) // No free blocks, let's allocate a new one
@ -217,11 +205,7 @@ Result<void*> kmalloc(usize size)
block->full_size = (pages * ARCH_PAGE_SIZE) - sizeof(HeapBlock); block->full_size = (pages * ARCH_PAGE_SIZE) - sizeof(HeapBlock);
block->magic = BLOCK_MAGIC; block->magic = BLOCK_MAGIC;
block->status = BLOCK_START_MEM | BLOCK_END_MEM; block->status = BLOCK_START_MEM | BLOCK_END_MEM;
block->next = nullptr; heap.append(block);
block->last = heap_end;
heap_end->next = block;
heap_end = block;
} }
block->req_size = size; block->req_size = size;
@ -257,13 +241,15 @@ Result<void> kfree(void* ptr)
else else
block->status &= ~BLOCK_USED; block->status &= ~BLOCK_USED;
if (block->next && is_block_free(block->next)) auto maybe_next = heap.next(block);
if (maybe_next.has_value() && is_block_free(maybe_next.value()))
{ {
// The next block is also free, thus we can merge! // The next block is also free, thus we can merge!
TRY(combine_forward(block)); TRY(combine_forward(block));
} }
if (block->last && is_block_free(block->last)) auto maybe_last = heap.previous(block);
if (maybe_last.has_value() && is_block_free(maybe_last.value()))
{ {
// The last block is also free, thus we can merge! // The last block is also free, thus we can merge!
block = TRY(combine_backward(block)); block = TRY(combine_backward(block));
@ -271,10 +257,7 @@ Result<void> kfree(void* ptr)
if ((block->status & BLOCK_START_MEM) && (block->status & BLOCK_END_MEM)) if ((block->status & BLOCK_START_MEM) && (block->status & BLOCK_END_MEM))
{ {
if (block == heap_start) heap_start = block->next; heap.remove(block);
if (block == heap_end) heap_end = block->last;
if (block->last) block->last->next = block->next;
if (block->next) block->next->last = block->last;
TRY(release_pages(block, get_blocks_from_size(block->full_size + sizeof(HeapBlock), ARCH_PAGE_SIZE))); TRY(release_pages(block, get_blocks_from_size(block->full_size + sizeof(HeapBlock), ARCH_PAGE_SIZE)));
} }
@ -337,14 +320,14 @@ Result<void*> kcalloc(usize nmemb, usize size)
void dump_heap_usage() void dump_heap_usage()
{ {
kdbgln("-- Dumping usage stats for kernel heap:"); kdbgln("-- Dumping usage stats for kernel heap:");
if (!heap_start) if (!heap.count())
{ {
kdbgln("- Heap is not currently being used"); kdbgln("- Heap is not currently being used");
return; return;
} }
usize alloc_total = 0; usize alloc_total = 0;
usize alloc_used = 0; usize alloc_used = 0;
HeapBlock* block = heap_start; HeapBlock* block = heap.first().value();
while (block) while (block)
{ {
if (is_block_free(block)) if (is_block_free(block))
@ -358,7 +341,7 @@ void dump_heap_usage()
alloc_total += block->full_size + sizeof(HeapBlock); alloc_total += block->full_size + sizeof(HeapBlock);
alloc_used += block->req_size; alloc_used += block->req_size;
} }
block = block->next; block = heap.next(block).value_or(nullptr);
} }
kdbgln("-- Total memory allocated for heap: %zu bytes", alloc_total); kdbgln("-- Total memory allocated for heap: %zu bytes", alloc_total);