Use that beautiful list_head to implement the heap.

This commit is contained in:
Enrico Fraccaroli (Galfurian)
2023-06-19 19:32:10 -04:00
parent 96ee8198f8
commit 90487afffc
+83 -125
View File
@@ -48,24 +48,18 @@ typedef struct block_t {
/// | first bits of real size | free/alloc |
/// To calculate the real size, set to zero the last bit
uint32_t size;
/// Pointer to the next free block.
struct block_t *nextfree;
/// Pointer to the previous free block.
struct block_t *prevfree;
/// Pointer to the previous block.
struct block_t *prev;
/// Pointer to the next block.
struct block_t *next;
/// Entry in the list of blocks.
list_head list;
/// Entry in the list of free blocks.
list_head free;
} block_t;
/// @brief Maps the heap memory to this three easily accessible values.
typedef struct {
/// @brief Pointer to the head block.
block_t *head;
/// @brief Pointer to the tail block.
block_t *tail;
/// @brief Pointer to the free block list.
block_t *free;
/// List of blocks.
list_head list;
/// List of free blocks.
list_head free;
} heap_header_t;
/// @brief Returns the given size, rounded in multiples of 16.
@@ -107,47 +101,33 @@ static inline void __blkmngr_dump(heap_header_t *header)
{
assert(header && "Received a NULL heap header.");
pr_debug("\n");
if (header->head) {
block_t *block;
if (!list_head_empty(&header->list)) {
pr_debug("# LIST:\n");
for (block_t *it = header->head; it; it = it->next)
pr_debug("# %s{%p,%p}\n", __block_to_string(it), it->prev, it->next);
list_for_each_decl(it, &header->list)
{
block = list_entry(it, block_t, list);
pr_debug("# %s{0x%p,0x%p}\n",
__block_to_string(block),
list_entry(it->prev, block_t, list),
list_entry(it->next, block_t, list));
}
pr_debug("\n");
}
if (header->free) {
if (!list_head_empty(&header->free)) {
pr_debug("# FREE:\n");
for (block_t *it = header->free; it; it = it->nextfree)
pr_debug("# %s{%p,%p}\n", __block_to_string(it), it->prevfree, it->nextfree);
list_for_each_decl(it, &header->free)
{
block = list_entry(it, block_t, free);
pr_debug("# %s{0x%p,0x%p}\n",
__block_to_string(block),
list_entry(it->prev, block_t, free),
list_entry(it->next, block_t, free));
}
}
pr_debug("\n");
}
/// @brief Removes the block from freelist.
static inline void __blkmngr_remove_from_freelist(heap_header_t *header, block_t *block)
{
assert(header && "Received a NULL heap header.");
assert(block && "Received null block.");
if (block == header->free)
header->free = block->nextfree;
else if (block->prevfree)
block->prevfree->nextfree = block->nextfree;
if (block->nextfree)
block->nextfree->prevfree = block->prevfree;
block->prevfree = NULL;
block->nextfree = NULL;
}
/// @brief Add the block to the free list.
static inline void __blkmngr_add_to_freelist(heap_header_t *header, block_t *block)
{
assert(header && "Received a NULL heap header.");
assert(block && "Received null block.");
if (header->free)
header->free->prevfree = block;
block->prevfree = NULL;
block->nextfree = header->free;
header->free = block;
}
/// @brief Find the best fitting block in the memory pool.
/// @param header header describing the heap.
/// @param size the size we want.
@@ -155,12 +135,14 @@ static inline void __blkmngr_add_to_freelist(heap_header_t *header, block_t *blo
static inline block_t *__blkmngr_find_best_fitting(heap_header_t *header, uint32_t size)
{
assert(header && "Received a NULL heap header.");
block_t *best_fitting = NULL, *it;
for (it = header->free; it; it = it->nextfree) {
if (!__blkmngr_does_it_fit(it, size))
block_t *best_fitting = NULL, *block;
list_for_each_decl(it, &header->free)
{
block = list_entry(it, block_t, free);
if (!__blkmngr_does_it_fit(block, size))
continue;
if (!best_fitting || (BLOCK_REAL_SIZE(it->size) < BLOCK_REAL_SIZE(best_fitting->size)))
best_fitting = it;
if (!best_fitting || (BLOCK_REAL_SIZE(block->size) < BLOCK_REAL_SIZE(best_fitting->size)))
best_fitting = block;
}
return best_fitting;
}
@@ -171,9 +153,9 @@ static inline block_t *__blkmngr_get_previous_block(heap_header_t *header, block
assert(header && "Received a NULL heap header.");
assert(block && "Received null block.");
// If the block is actually the head of the list, return NULL.
if (block == header->head)
if (block->list.prev == &header->list)
return NULL;
return block->prev;
return list_entry(block->list.prev, block_t, list);
}
/// @brief Given a block, finds its next block.
@@ -182,76 +164,57 @@ static inline block_t *__blkmngr_get_next_block(heap_header_t *header, block_t *
assert(header && "Received a NULL heap header.");
assert(block && "Received null block.");
// If the block is actually the tail of the list, return NULL.
if (block == header->tail)
if (block->list.next == &header->list)
return NULL;
return block->next;
return list_entry(block->list.next, block_t, list);
}
static inline void __blkmngr_split_block(heap_header_t *header, block_t *block, uint32_t size)
{
assert(block && "Received NULL block.");
assert(BLOCK_IS_FREE(block) && "The block is not free.");
pr_debug("Splitting %s", __block_to_string(block));
pr_debug("{next: %s,", __block_to_string(block->next));
pr_debug(" nextfree: %s}\n", __block_to_string(block->nextfree));
// Create the new block.
block_t *split = (block_t *)((char *)block + OVERHEAD + size);
// Update the pointer of the new block.
split->prev = block;
split->prevfree = block;
split->next = block->next;
split->nextfree = block->nextfree;
// Update the pointer of the base block.
block->next = split;
block->nextfree = split;
// Insert the block in the list.
list_head_insert_after(&split->list, &block->list);
// Insert the block in the free list.
list_head_insert_after(&split->free, &block->free);
// Update the size of the new block.
split->size = BLOCK_REAL_SIZE(block->size) - OVERHEAD - size;
// Update the size of the base block.
block->size = BLOCK_REAL_SIZE(size);
// Set the splitted block as free.
BLOCK_SET_FREE(split);
// If the block was the tail of the list, replace it with the new one.
if (header->tail == block)
header->tail = split;
pr_debug("Into %s", __block_to_string(block));
pr_debug("{next: %s,", __block_to_string(block->next));
pr_debug(" nextfree: %s}\n", __block_to_string(block->nextfree));
pr_debug("And %s", __block_to_string(split));
pr_debug("{next: %s,", __block_to_string(split->next));
pr_debug(" nextfree: %s}\n", __block_to_string(split->nextfree));
pr_debug("into %s", __block_to_string(block));
pr_debug("and %s\n", __block_to_string(split));
}
static inline void __blkmngr_merge_blocks(heap_header_t *header, block_t *block1, block_t *block2)
static inline void __blkmngr_merge_blocks(heap_header_t *header, block_t *block, block_t *other)
{
assert(block1 && "Received NULL first block.");
assert(block2 && "Received NULL second block.");
assert(BLOCK_IS_FREE(block1) && "The first block is not free.");
assert(BLOCK_IS_FREE(block2) && "The second block is not free.");
assert(block1->next == block2 && "The blocks are not adjacent.");
assert(block && "Received NULL first block.");
assert(other && "Received NULL second block.");
assert(BLOCK_IS_FREE(block) && "The first block is not free.");
assert(BLOCK_IS_FREE(other) && "The second block is not free.");
assert(block->list.next == &other->list && "The blocks are not adjacent.");
pr_debug("Merging %s", __block_to_string(block1));
pr_debug("{next: %s,", __block_to_string(block1->next));
pr_debug(" nextfree: %s}\n", __block_to_string(block1->nextfree));
pr_debug("And %s", __block_to_string(block2));
pr_debug("{next: %s,", __block_to_string(block2->next));
pr_debug(" nextfree: %s}\n", __block_to_string(block2->nextfree));
pr_debug("Merging %s", __block_to_string(block));
pr_debug(" and %s", __block_to_string(other));
// Remove the block from the free list.
__blkmngr_remove_from_freelist(header, block2);
// Merge the block.
block1->next = block2->next;
list_head_remove(&other->free);
// Remove the block from the list.
list_head_remove(&other->list);
// Update the size.
block1->size = BLOCK_REAL_SIZE(block1->size) +
BLOCK_REAL_SIZE(block2->size) + OVERHEAD;
block->size = BLOCK_REAL_SIZE(block->size) + BLOCK_REAL_SIZE(other->size) + OVERHEAD;
// Set the splitted block as free.
BLOCK_SET_FREE(block1);
// If the second block was the tail of the list, replace it with the first one.
if (header->tail == block2)
header->tail = block1;
BLOCK_SET_FREE(block);
pr_debug("Into %s", __block_to_string(block1));
pr_debug("{next: %s,", __block_to_string(block1->next));
pr_debug(" nextfree: %s}\n", __block_to_string(block1->nextfree));
pr_debug("into %s", __block_to_string(block));
}
/// @brief Extends the provided heap of the given increment.
@@ -270,7 +233,7 @@ static void *__do_brk(vm_area_struct_t *heap, uint32_t increment)
// Compute the new heap top.
uint32_t new_heap_top = mm->brk + increment;
// Debugging message.
pr_notice("Expanding heap from %p to %p.\n", mm->brk, new_heap_top);
pr_notice("Expanding heap from 0x%p to 0x%p.\n", mm->brk, new_heap_top);
// If new boundary is larger than the end, we fail.
if (new_heap_top > heap->vm_end) {
pr_err("The new boundary is larger than the end!");
@@ -302,10 +265,10 @@ static void *__do_malloc(vm_area_struct_t *heap, size_t size)
// Split the block.
__blkmngr_split_block(header, block, rounded_size);
} else {
pr_debug("Found perferct block: %s\n", __block_to_string(block));
pr_debug("Found perfect block: %s\n", __block_to_string(block));
}
// Remove the block from the free list.
__blkmngr_remove_from_freelist(header, block);
list_head_remove(&block->free);
} else {
pr_warning("Failed to find suitable block, we need to create a new one.\n");
// We need more space, specifically the size of the block plus the size
@@ -313,16 +276,10 @@ static void *__do_malloc(vm_area_struct_t *heap, size_t size)
block = __do_brk(heap, rounded_size + OVERHEAD);
// Check the block.
assert(block && "Failed to create a new block!");
// Check if the tail is properly set.
assert(header->tail && "The tail is not set!");
// Add the new block to the list.
header->tail->next = block;
// Setup the new block.
block->size = rounded_size;
block->prev = header->tail;
block->next = NULL;
block->prevfree = NULL;
block->nextfree = NULL;
list_head_init(&block->list);
list_head_init(&block->free);
list_head_insert_before(&block->list, &header->list);
}
// Set the new block as used.
BLOCK_SET_USED(block);
@@ -343,9 +300,9 @@ static void __do_free(vm_area_struct_t *heap, void *ptr)
block_t *prev = __blkmngr_get_previous_block(header, block);
// Get the next block.
block_t *next = __blkmngr_get_next_block(header, block);
pr_debug("Freeing block %s\n", __block_to_string(block));
// Set the block free.
BLOCK_SET_FREE(block);
pr_debug("Freeing block %s\n", __block_to_string(block));
// Merge adjacent blocks.
if (prev && next && BLOCK_IS_FREE(prev) && BLOCK_IS_FREE(next)) {
__blkmngr_merge_blocks(header, prev, block);
@@ -356,10 +313,10 @@ static void __do_free(vm_area_struct_t *heap, void *ptr)
// Merge the blocks.
__blkmngr_merge_blocks(header, block, next);
// Add the block to the free list.
__blkmngr_add_to_freelist(header, block);
list_head_insert_before(&block->free, &header->free);
} else {
// Add the block to the free list.
__blkmngr_add_to_freelist(header, block);
list_head_insert_before(&block->free, &header->free);
}
__blkmngr_dump(header);
}
@@ -390,8 +347,8 @@ void *sys_brk(void *addr)
MM_RW | MM_PRESENT | MM_USER | MM_UPDADDR,
GFP_HIGHUSER);
pr_debug("Heap size : %s.\n", to_human_size(heap_size));
pr_debug("Heap start : %p.\n", heap->vm_start);
pr_debug("Heap end : %p.\n", heap->vm_end);
pr_debug("Heap start : 0x%p.\n", heap->vm_start);
pr_debug("Heap end : 0x%p.\n", heap->vm_end);
// Initialize the memory.
memset((char *)heap->vm_start, 0, segment_size);
// Save where the original heap starts.
@@ -400,17 +357,18 @@ void *sys_brk(void *addr)
task->mm->brk = heap->vm_start + sizeof(heap_header_t);
// Initialize the header.
heap_header_t *header = (heap_header_t *)heap->vm_start;
header->head = (block_t *)(header + sizeof(heap_header_t));
header->tail = (block_t *)(header + sizeof(heap_header_t));
header->free = header->head;
list_head_init(&header->list);
list_head_init(&header->free);
// Preare the first block.
block_t *first = header->head;
first->size = heap_size;
first->prev = NULL;
first->next = NULL;
first->nextfree = NULL;
block_t *block = (block_t *)(header + sizeof(heap_header_t));
block->size = heap_size;
list_head_init(&block->list);
list_head_init(&block->free);
// Insert the block inside the heap.
list_head_insert_before(&block->list, &header->list);
list_head_insert_before(&block->free, &header->free);
// Set the block as free.
BLOCK_SET_FREE(first);
BLOCK_SET_FREE(block);
__blkmngr_dump(header);
}
void *_ret = NULL;