Use bitfields to handle free blocks. Add check when creating new VM area.
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
#define TASK_NAME_MAX_LENGTH 100
|
||||
|
||||
/// The default dimension of the stack of a process (1 MByte).
|
||||
#define DEFAULT_STACK_SIZE 0x100000
|
||||
#define DEFAULT_STACK_SIZE (1 * M)
|
||||
|
||||
/// @brief This structure is used to track the statistics of a process.
|
||||
/// @details
|
||||
|
||||
+88
-72
@@ -4,13 +4,16 @@
|
||||
/// See LICENSE.md for details.
|
||||
|
||||
// Setup the logging for this file (do this before any other include).
|
||||
#include "sys/kernel_levels.h" // Include kernel log levels.
|
||||
#include "system/syscall.h"
|
||||
#include "time.h"
|
||||
#define __DEBUG_HEADER__ "[KHEAP ]" ///< Change header.
|
||||
#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level.
|
||||
#include "io/debug.h" // Include debugging functions.
|
||||
|
||||
#include "kernel.h"
|
||||
#include "stddef.h"
|
||||
#include "sys/kernel_levels.h" // Include kernel log levels.
|
||||
#define __DEBUG_HEADER__ "[KHEAP ]" ///< Change header.
|
||||
#define __DEBUG_LEVEL__ LOGLEVEL_INFO ///< Set log level.
|
||||
#include "io/debug.h" // Include debugging functions.
|
||||
|
||||
#include "stdlib.h"
|
||||
#include "mem/kheap.h"
|
||||
#include "mem/paging.h"
|
||||
#include "sys/list_head.h"
|
||||
@@ -22,69 +25,64 @@
|
||||
#include "stdio.h"
|
||||
#include "math.h"
|
||||
|
||||
/// The heap size.
|
||||
#define HEAP_SIZE (4 * M)
|
||||
/// The lower bound address, when randomly placing the virtual memory area.
|
||||
#define HEAP_VM_LB 0x40000000
|
||||
/// The upper bound address, when randomly placing the virtual memory area.
|
||||
#define HEAP_VM_UB 0x50000000
|
||||
|
||||
/// Overhead given by the block_t itself.
|
||||
#define OVERHEAD sizeof(block_t)
|
||||
|
||||
/// Align the given address.
|
||||
#define ADDR_ALIGN(addr) ((((uint32_t)(addr)) & 0xFFFFF000) + 0x1000)
|
||||
|
||||
/// Checks if the given address is aligned.
|
||||
#define IS_ALIGN(addr) ((((uint32_t)(addr)) & 0x00000FFF) == 0)
|
||||
|
||||
/// @brief The size is encoded in the lowest 31 bits, while the 32nd is used to
|
||||
/// determine if the block is free/used.
|
||||
/// @param size the size of the block.
|
||||
/// @return the real size of the block.
|
||||
#define BLOCK_REAL_SIZE(size) ((size)&0x7FFFFFFF)
|
||||
|
||||
/// @brief Checks if the given block is actually free.
|
||||
#define BLOCK_IS_FREE(block) ((block->size) & 0x80000000)
|
||||
|
||||
/// @brief Sets the block as free, by altering the bit of the size field.
|
||||
#define BLOCK_SET_FREE(block) ((block->size) |= 0x80000000)
|
||||
|
||||
/// @brief Sets the block as used, by altering the bit of the size field.
|
||||
#define BLOCK_SET_USED(block) ((block->size) &= 0x7FFFFFFF)
|
||||
|
||||
/// @brief Identifies a block of memory.
|
||||
typedef struct block_t {
|
||||
/// @brief Identifies the size of the block and also if it is free or
|
||||
/// allocated.
|
||||
/// @details
|
||||
/// | 1 bit | 31 bit |
|
||||
/// | free/alloc | bits of real size |
|
||||
/// To calculate the real size, set to zero the last bit.
|
||||
unsigned int size;
|
||||
/// Entry in the list of blocks.
|
||||
/// @brief Single bit that identifies if the block is free.
|
||||
unsigned int is_free : 1;
|
||||
/// @brief Size of the block.
|
||||
unsigned int size : 31;
|
||||
/// @brief Entry in the list of blocks.
|
||||
list_head list;
|
||||
/// Entry in the list of free blocks.
|
||||
/// @brief 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 {
|
||||
/// List of blocks.
|
||||
/// @brief List of blocks.
|
||||
list_head list;
|
||||
/// List of free blocks.
|
||||
/// @brief List of free blocks.
|
||||
list_head free;
|
||||
} heap_header_t;
|
||||
|
||||
/// @brief Randomizes the starting address of the
|
||||
///
|
||||
/// @return uint32_t
|
||||
static inline uint32_t __vm_get_random_address(uint32_t lb, uint32_t ub)
|
||||
{
|
||||
return lb + (rand() % (ub - lb + 1));
|
||||
}
|
||||
|
||||
/// @brief Returns the given size, rounded in multiples of 16.
|
||||
/// @param size the given size.
|
||||
/// @return the size rounded to the nearest multiple of 16.
|
||||
static inline uint32_t __blkmngr_get_rounded_size(uint32_t size)
|
||||
{
|
||||
return round_up(BLOCK_REAL_SIZE(size), 16);
|
||||
return round_up(size, 16);
|
||||
}
|
||||
|
||||
/// @brief Checks if the given size fits inside the block.
|
||||
/// @brief Checks if the given size fits inside the block.
|
||||
/// @param block The given block.
|
||||
/// @param size The size to check
|
||||
/// @return
|
||||
/// @return 1 if it fits, 0 otherwise.
|
||||
static inline int __blkmngr_does_it_fit(block_t *block, uint32_t size)
|
||||
{
|
||||
assert(block && "Received null block.");
|
||||
return BLOCK_REAL_SIZE(block->size) >= BLOCK_REAL_SIZE(size);
|
||||
return block->size >= size;
|
||||
}
|
||||
|
||||
/// @brief Prepares a string that represents the block.
|
||||
@@ -96,8 +94,8 @@ static inline const char *__block_to_string(block_t *block)
|
||||
if (block) {
|
||||
sprintf(buffer, "0x%p [%9s](%d)",
|
||||
block,
|
||||
to_human_size(BLOCK_REAL_SIZE(block->size)),
|
||||
BLOCK_IS_FREE(block));
|
||||
to_human_size(block->size),
|
||||
block->is_free);
|
||||
} else {
|
||||
sprintf(buffer, "NULL");
|
||||
}
|
||||
@@ -160,11 +158,11 @@ static inline block_t *__blkmngr_find_best_fitting(heap_header_t *header, uint32
|
||||
list_for_each_decl(it, &header->free)
|
||||
{
|
||||
block = list_entry(it, block_t, free);
|
||||
if (!BLOCK_IS_FREE(block))
|
||||
if (!block->is_free)
|
||||
continue;
|
||||
if (!__blkmngr_does_it_fit(block, size))
|
||||
continue;
|
||||
if (!best_fitting || (BLOCK_REAL_SIZE(block->size) < BLOCK_REAL_SIZE(best_fitting->size)))
|
||||
if (!best_fitting || (block->size < best_fitting->size))
|
||||
best_fitting = block;
|
||||
}
|
||||
return best_fitting;
|
||||
@@ -192,27 +190,48 @@ static inline block_t *__blkmngr_get_next_block(heap_header_t *header, block_t *
|
||||
return list_entry(block->list.next, block_t, list);
|
||||
}
|
||||
|
||||
/// @brief Checks if the given `previous` block, is before `block`.
|
||||
/// @param block the block from which we check the other block.
|
||||
/// @param previous the supposedly previous block.
|
||||
/// @return 1 if it is the previous block, 0 otherwise.
|
||||
static inline int __blkmngr_is_previous_block(block_t *block, block_t *previous)
|
||||
{
|
||||
assert(block && "Received null block.");
|
||||
assert(previous && "Received null previous block.");
|
||||
return block->list.prev == &previous->list;
|
||||
}
|
||||
|
||||
/// @brief Checks if the given `next` block, is after `block`.
|
||||
/// @param block the block from which we check the other block.
|
||||
/// @param next the supposedly next block.
|
||||
/// @return 1 if it is the next block, 0 otherwise.
|
||||
static inline int __blkmngr_is_next_block(block_t *block, block_t *next)
|
||||
{
|
||||
assert(block && "Received null block.");
|
||||
assert(next && "Received null next block.");
|
||||
return block->list.next == &next->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.");
|
||||
assert(block->is_free && "The block is not free.");
|
||||
|
||||
pr_debug("Splitting %s\n", __block_to_string(block));
|
||||
|
||||
// Create the new block.
|
||||
block_t *split = (block_t *)((char *)block + OVERHEAD + BLOCK_REAL_SIZE(size));
|
||||
|
||||
block_t *split = (block_t *)((char *)block + OVERHEAD + size);
|
||||
// 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;
|
||||
split->size = block->size - OVERHEAD - size;
|
||||
// Update the size of the base block.
|
||||
block->size = BLOCK_REAL_SIZE(size);
|
||||
block->size = size;
|
||||
// Set the blocks as free.
|
||||
BLOCK_SET_FREE(block);
|
||||
BLOCK_SET_FREE(split);
|
||||
block->is_free = 1;
|
||||
split->is_free = 1;
|
||||
|
||||
pr_debug("Into %s\n", __block_to_string(block));
|
||||
pr_debug("And %s\n", __block_to_string(split));
|
||||
@@ -222,25 +241,21 @@ static inline void __blkmngr_merge_blocks(heap_header_t *header, block_t *block,
|
||||
{
|
||||
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.");
|
||||
if (block->list.next != &other->list) {
|
||||
__blkmngr_dump(header);
|
||||
pr_err("Cannot merge %s", __block_to_string(block));
|
||||
pr_err(" and %s, they are not adjacent!\n", __block_to_string(other));
|
||||
assert(block->list.next == &other->list && "The blocks are not adjacent.");
|
||||
}
|
||||
assert(block->is_free && "The first block is not free.");
|
||||
assert(other->is_free && "The second block is not free.");
|
||||
assert(__blkmngr_is_next_block(block, other) && "The blocks are not adjacent.");
|
||||
|
||||
pr_debug("Merging %s\n", __block_to_string(block));
|
||||
pr_debug("And %s\n", __block_to_string(other));
|
||||
|
||||
// Remove the block from the free list.
|
||||
// Remove the other block from the free list.
|
||||
list_head_remove(&other->free);
|
||||
// Remove the block from the list.
|
||||
// Remove the other block from the list.
|
||||
list_head_remove(&other->list);
|
||||
// Update the size.
|
||||
block->size = BLOCK_REAL_SIZE(block->size) + BLOCK_REAL_SIZE(other->size) + OVERHEAD;
|
||||
block->size = block->size + other->size + OVERHEAD;
|
||||
// Set the splitted block as free.
|
||||
BLOCK_SET_FREE(block);
|
||||
block->is_free = 1;
|
||||
|
||||
pr_debug("Into %s\n", __block_to_string(block));
|
||||
}
|
||||
@@ -292,7 +307,7 @@ static void *__do_malloc(vm_area_struct_t *heap, size_t size)
|
||||
block_t *block = __blkmngr_find_best_fitting(header, rounded_size);
|
||||
// If we were able to find a suitable block, either split it, or return it.
|
||||
if (block) {
|
||||
if (BLOCK_REAL_SIZE(block->size) > actual_size) {
|
||||
if (block->size > actual_size) {
|
||||
// Split the block, provide the rounded size to the function.
|
||||
__blkmngr_split_block(header, block, rounded_size);
|
||||
} else {
|
||||
@@ -315,7 +330,7 @@ static void *__do_malloc(vm_area_struct_t *heap, size_t size)
|
||||
list_head_insert_before(&block->list, &header->list);
|
||||
}
|
||||
// Set the new block as used.
|
||||
BLOCK_SET_USED(block);
|
||||
block->is_free = 0;
|
||||
__blkmngr_dump(header);
|
||||
return (void *)((char *)block + OVERHEAD);
|
||||
}
|
||||
@@ -335,16 +350,16 @@ static void __do_free(vm_area_struct_t *heap, void *ptr)
|
||||
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);
|
||||
block->is_free = 1;
|
||||
// Merge adjacent blocks.
|
||||
if (prev && BLOCK_IS_FREE(prev) && next && BLOCK_IS_FREE(next)) {
|
||||
if (prev && prev->is_free && next && next->is_free) {
|
||||
pr_debug("Merging with previous and next.\n");
|
||||
__blkmngr_merge_blocks(header, prev, block);
|
||||
__blkmngr_merge_blocks(header, prev, next);
|
||||
} else if (prev && BLOCK_IS_FREE(prev)) {
|
||||
} else if (prev && prev->is_free) {
|
||||
pr_debug("Merging with previous.\n");
|
||||
__blkmngr_merge_blocks(header, prev, block);
|
||||
} else if (next && BLOCK_IS_FREE(next)) {
|
||||
} else if (next && next->is_free) {
|
||||
pr_debug("Merging with next.\n");
|
||||
// Merge the blocks.
|
||||
__blkmngr_merge_blocks(header, block, next);
|
||||
@@ -372,14 +387,15 @@ void *sys_brk(void *addr)
|
||||
if (heap == NULL) {
|
||||
pr_debug("Allocating heap!\n");
|
||||
// Set the heap to 4 Mb.
|
||||
size_t heap_size = 4 * M;
|
||||
size_t heap_size = HEAP_SIZE;
|
||||
// Add to that the space required to store the header, and the first block.
|
||||
size_t segment_size = heap_size + sizeof(heap_header_t) + sizeof(block_t);
|
||||
// TODO: Randomize VM start, and check why it is 0x40000000 and not
|
||||
// 0x400000.
|
||||
// Create the virtual memory area, we are goin to place the area between
|
||||
// 0x40000000 and 0x50000000, which surely is below the stack. The VM
|
||||
// code will check if it is a valid area anyway.
|
||||
heap = create_vm_area(
|
||||
task->mm,
|
||||
0x40000000,
|
||||
__vm_get_random_address(HEAP_VM_LB, HEAP_VM_UB),
|
||||
segment_size,
|
||||
MM_RW | MM_PRESENT | MM_USER | MM_UPDADDR,
|
||||
GFP_HIGHUSER);
|
||||
@@ -404,9 +420,9 @@ void *sys_brk(void *addr)
|
||||
list_head_insert_before(&block->list, &header->list);
|
||||
list_head_insert_before(&block->free, &header->free);
|
||||
// Save where the heap actually start.
|
||||
task->mm->brk = (uint32_t)((char *)block + OVERHEAD + BLOCK_REAL_SIZE(block->size));
|
||||
task->mm->brk = (uint32_t)((char *)block + OVERHEAD + block->size);
|
||||
// Set the block as free.
|
||||
BLOCK_SET_FREE(block);
|
||||
block->is_free = 1;
|
||||
__blkmngr_dump(header);
|
||||
}
|
||||
void *_ret = NULL;
|
||||
|
||||
+20
-17
@@ -83,20 +83,19 @@ void paging_flush_tlb_single(unsigned long addr)
|
||||
static inline int __valid_vm_area(mm_struct_t *mm, uintptr_t vm_start, uintptr_t vm_end)
|
||||
{
|
||||
if (vm_end <= vm_start)
|
||||
return 1;
|
||||
return -1;
|
||||
// Get the stack.
|
||||
vm_area_struct_t *area, *prev_area;
|
||||
vm_area_struct_t *area;
|
||||
list_for_each_prev_decl(it, &mm->mmap_list)
|
||||
{
|
||||
area = list_entry(it, vm_area_struct_t, vm_list);
|
||||
assert(area && "There is a NULL area in the list.");
|
||||
// Check the previous segment.
|
||||
if (area->vm_list.prev != &mm->mmap_list) {
|
||||
prev_area = list_entry(area->vm_list.prev, vm_area_struct_t, vm_list);
|
||||
assert(prev_area && "There is a NULL area in the list.");
|
||||
if ((vm_start > prev_area->vm_end) && (vm_end < area->vm_start))
|
||||
return 0;
|
||||
}
|
||||
if ((vm_start >= area->vm_start) && (vm_start <= area->vm_end))
|
||||
return 0;
|
||||
if ((vm_end >= area->vm_start) && (vm_end <= area->vm_end))
|
||||
return 0;
|
||||
if ((vm_start <= area->vm_start) && (vm_end >= area->vm_end))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -131,16 +130,22 @@ static inline int __find_vm_free_area(mm_struct_t *mm, size_t length, uintptr_t
|
||||
}
|
||||
|
||||
vm_area_struct_t *create_vm_area(mm_struct_t *mm,
|
||||
uint32_t virt_start,
|
||||
uint32_t vm_start,
|
||||
size_t size,
|
||||
uint32_t pgflags,
|
||||
uint32_t gfpflags)
|
||||
{
|
||||
// Compute the end of the virtual memory area.
|
||||
uint32_t vm_end = vm_start + size;
|
||||
// Check if the range is already occupied.
|
||||
if (__valid_vm_area(mm, vm_start, vm_end) <= 0) {
|
||||
pr_crit("The virtual memory area range [%p, %p] is already in use.\n", vm_start, vm_end);
|
||||
kernel_panic("Wrong virtual memory area range.");
|
||||
}
|
||||
// Allocate on kernel space the structure for the segment.
|
||||
vm_area_struct_t *new_segment = kmem_cache_alloc(vm_area_cache, GFP_KERNEL);
|
||||
|
||||
uint32_t order = find_nearest_order_greater(virt_start, size);
|
||||
|
||||
// Find the nearest order for the given memory size.
|
||||
uint32_t order = find_nearest_order_greater(vm_start, size);
|
||||
uint32_t phy_vm_start;
|
||||
|
||||
if (pgflags & MM_COW) {
|
||||
@@ -152,13 +157,11 @@ vm_area_struct_t *create_vm_area(mm_struct_t *mm,
|
||||
phy_vm_start = get_physical_address_from_page(page);
|
||||
}
|
||||
|
||||
mem_upd_vm_area(mm->pgd, virt_start, phy_vm_start, size, pgflags);
|
||||
|
||||
uint32_t vm_start = virt_start;
|
||||
mem_upd_vm_area(mm->pgd, vm_start, phy_vm_start, size, pgflags);
|
||||
|
||||
// Update vm_area_struct info.
|
||||
new_segment->vm_start = vm_start;
|
||||
new_segment->vm_end = vm_start + size;
|
||||
new_segment->vm_end = vm_end;
|
||||
new_segment->vm_mm = mm;
|
||||
|
||||
// Update memory descriptor list of vm_area_struct.
|
||||
|
||||
Reference in New Issue
Block a user