2 changed files with 348 additions and 19 deletions
@ -1,37 +1,359 @@ |
|||
#include <stdint.h> |
|||
#include <stddef.h> |
|||
|
|||
#include "../klib/sprintf.h" |
|||
#include "kalloc.h" |
|||
#include "../klib/sprintf.h" |
|||
#include "../memory/vmap.h" |
|||
#include "../memory/paging.h" |
|||
#include "../debug/logging.h" |
|||
#include "../debug/assert.h" |
|||
#include "../common.h" |
|||
|
|||
|
|||
#define HEAP_SIZE_KB 8 * 1024 |
|||
#define HEAP_BEGIN heap |
|||
#define HEAP_SIZE 1024 * HEAP_SIZE_KB |
|||
#define MIN_EXPAND_SIZE 1024 |
|||
#define MIN_SEGMENT_SIZE 32 |
|||
|
|||
//(0x00EFFFFF - HEAP_BEGIN)
|
|||
#define MAX(X,Y) (X >= Y ? X : Y) |
|||
|
|||
//static const uint8_t* heap [HEAP_SIZE] __attribute__((section(".bss")));
|
|||
typedef struct seg_header { |
|||
struct seg_header *next; |
|||
uint32_t size; |
|||
|
|||
//static void* brk = (void *) HEAP_BEGIN;
|
|||
// boolean variable
|
|||
uint32_t free; |
|||
} seg_header; |
|||
|
|||
/*
|
|||
void* kmalloc(size_t size) { |
|||
void* ptr = brk; |
|||
brk = mallign16(brk+size); |
|||
|
|||
kprintf("kmalloc(%lu); heap use: %u ko / %u ko\n", |
|||
size, ((size_t)brk - (size_t)HEAP_BEGIN) / 1024, HEAP_SIZE / 1024); |
|||
// assert that the header is 8-byte alligned
|
|||
// this is important so that every allocation
|
|||
// is 8-byte alligned too
|
|||
static_assert(sizeof(seg_header) % 8 == 0); |
|||
|
|||
// segment headers represent a linked list
|
|||
// going downward in address space
|
|||
|
|||
/**
|
|||
* 0 | node0 |
|||
* | ////
|
|||
* | ////
|
|||
* | ////
|
|||
* | node1 |
|||
* | ////
|
|||
* | ////
|
|||
* | ////
|
|||
* ... |
|||
* | nodeN |
|||
* | free |
|||
* | free |
|||
* | free |
|||
* BRK ---- |
|||
* |
|||
* node n -> node n-1 |
|||
* |
|||
* |
|||
* split: |
|||
* node n+1 -> node n |
|||
* |
|||
* node n+1 -> new_node |
|||
* new_node -> node n |
|||
* new_node.size = node n.size - SIZE - sizeof(node) |
|||
* |
|||
*/ |
|||
|
|||
static void *kheap_begin = (void *)KERNEL_HEAP_BEGIN; |
|||
static size_t kheap_size = 0; |
|||
|
|||
// sum of the available heap ranges,
|
|||
// without the last free segment:
|
|||
// indice of how fragmented
|
|||
// the heap is
|
|||
// TODO: use this to unfragment the whole
|
|||
// heap when this number is too big
|
|||
|
|||
//static size_t fragmented_available_size = 0;
|
|||
|
|||
static seg_header* current_segment = NULL; |
|||
|
|||
/**
|
|||
* expand the heap by size bytes |
|||
*/ |
|||
static void expand_heap(size_t size) { |
|||
size_t new_kheap_pages_size = (kheap_size + size + 0xfff) >> 12; |
|||
size_t old_kheap_pages_size = (kheap_size + 0xfff) >> 12; |
|||
|
|||
// alloc extra pages if needed
|
|||
if(new_kheap_pages_size != old_kheap_pages_size) { |
|||
alloc_pages( |
|||
kheap_begin + (old_kheap_pages_size << 12), |
|||
new_kheap_pages_size - old_kheap_pages_size, |
|||
PRESENT_ENTRY// | PL_XD // execute disable pages
|
|||
); |
|||
} |
|||
|
|||
// create a new segment in the extra space
|
|||
seg_header* new_segment = kheap_begin + kheap_size; |
|||
|
|||
|
|||
new_segment->next = current_segment; |
|||
new_segment->size = size - sizeof(seg_header); |
|||
new_segment->free = 1; |
|||
|
|||
current_segment = new_segment; |
|||
|
|||
|
|||
kheap_size += size; |
|||
|
|||
klog_debug("kernel heap extended to %lu KB", kheap_size / 1024); |
|||
} |
|||
|
|||
/**
|
|||
* current_segment shouldn't be NULL |
|||
* |
|||
*/ |
|||
static void defragment(void) { |
|||
assert(current_segment != NULL); |
|||
|
|||
assert((size_t)brk - (size_t)HEAP_BEGIN < HEAP_SIZE); |
|||
seg_header* pred2 = NULL; |
|||
seg_header* pred = current_segment; |
|||
|
|||
for(seg_header* seg = current_segment->next; |
|||
seg != NULL; |
|||
) { |
|||
|
|||
/**
|
|||
* || seg || pred | |
|||
* || |
|||
* \/ |
|||
* || SEG | |
|||
* |
|||
* |
|||
*/ |
|||
// we can merge these two nodes
|
|||
if(seg->free && pred->free) { |
|||
if(!pred2) |
|||
// pred is the head
|
|||
current_segment = seg; |
|||
else |
|||
pred2->next = seg; |
|||
|
|||
seg->size += sizeof(seg_header) + pred->size; |
|||
|
|||
// pred2 doesn't change!
|
|||
pred = seg; |
|||
seg = seg->next; |
|||
continue; |
|||
} |
|||
else { |
|||
pred2 = pred; |
|||
pred = seg; |
|||
seg = seg->next; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// try to merge the node with the next one
|
|||
// we suppose that the argument is free
|
|||
static void try_merge(seg_header* free_node) { |
|||
assert(free_node->free); |
|||
|
|||
seg_header* next = free_node->next; |
|||
|
|||
if(!next) |
|||
return; |
|||
|
|||
/*
|
|||
* insert a new segment between to_split and pred |
|||
* return the new segment |
|||
* |
|||
* | next | free_node | |
|||
* | F R E E | F R E E | |
|||
* | size1 | size0 | |
|||
* |
|||
* || |
|||
* \/ |
|||
* |
|||
* | next | |
|||
* | F R E E | |
|||
* | size0 + size1 | |
|||
* |
|||
**/ |
|||
// we can merge!
|
|||
if(next->free) { |
|||
next->size += free_node->size; |
|||
|
|||
// if the argument is the head
|
|||
// of the list, the head should
|
|||
// become the
|
|||
if(free_node == current_segment) { |
|||
current_segment = next; |
|||
return; |
|||
} |
|||
|
|||
// if not, we must find the preceding
|
|||
// node in the linked list
|
|||
|
|||
for(seg_header* seg = current_segment; |
|||
seg != NULL; |
|||
seg = seg->next) { |
|||
if(seg->next == free_node) { |
|||
// we found it!
|
|||
|
|||
// now make it point on the
|
|||
// new merged node which is
|
|||
// the 'next' node
|
|||
seg->next = next; |
|||
|
|||
// we are done merging!
|
|||
return; |
|||
} |
|||
} |
|||
// oh no! wtf
|
|||
// the argument wasn't in the list
|
|||
assert(0); |
|||
} |
|||
} |
|||
|
|||
/*
|
|||
* insert a new segment between to_split and pred |
|||
* return the new segment |
|||
* | to_split | |
|||
* | F R E E | |
|||
* | size0 | |
|||
* || |
|||
* \/ |
|||
* |
|||
* | to_split | new | |
|||
* | F R E E | F R E E | |
|||
* | size |size0-size| |
|||
**/ |
|||
static seg_header* split_segment(seg_header* pred, seg_header* tosplit, size_t size) { |
|||
seg_header* new_segment = (void*)tosplit + sizeof(seg_header) + size; |
|||
|
|||
if(pred != NULL) |
|||
pred->next = new_segment; |
|||
|
|||
new_segment->next = tosplit; |
|||
new_segment->free = 1; |
|||
new_segment->size = tosplit->size // size to split
|
|||
- size // size reserved
|
|||
- sizeof(seg_header); // new segment header
|
|||
|
|||
|
|||
tosplit->size = size; |
|||
|
|||
return ptr; |
|||
return new_segment; |
|||
} |
|||
|
|||
void kfree(void* ptr) { |
|||
(void) ptr; |
|||
|
|||
void kheap_init(void) { |
|||
klog_info("init kernel heap..."); |
|||
expand_heap(MIN_EXPAND_SIZE); |
|||
} |
|||
|
|||
*/ |
|||
|
|||
void* __attribute__((noinline)) kmalloc(size_t size) { |
|||
// align the size to assure that
|
|||
// the whole structure is alligned
|
|||
size = ((size + 7 ) / 8) * 8; |
|||
if(size < MIN_SEGMENT_SIZE) |
|||
size = MIN_SEGMENT_SIZE; |
|||
|
|||
// search for a big enough pool
|
|||
seg_header* seg = current_segment; |
|||
seg_header* pred = NULL; |
|||
|
|||
while(1) { |
|||
|
|||
if(seg == NULL) { |
|||
break; |
|||
} |
|||
|
|||
else if(!seg->free || seg->size < size) { |
|||
// this segment is not right, check the next one
|
|||
pred = seg; |
|||
|
|||
// keep the preceding node in memory
|
|||
// we need it when spliting the current
|
|||
// segment
|
|||
seg = seg->next; |
|||
|
|||
continue; |
|||
} |
|||
|
|||
// we found a satisfying segment!
|
|||
|
|||
if(seg->size >= size + sizeof(seg_header) + MIN_SEGMENT_SIZE) { |
|||
// we don't take the whole space
|
|||
|
|||
// get the inserted segment
|
|||
seg_header* new_seg = split_segment(pred, seg, size); |
|||
|
|||
if(pred == NULL) { |
|||
// seg == current_segment
|
|||
|
|||
// the 'current' segment should
|
|||
// be the last in the list.
|
|||
// => advance the current one
|
|||
current_segment = new_seg; |
|||
} |
|||
else { |
|||
// put the new node in the linked list
|
|||
pred->next = new_seg; |
|||
} |
|||
|
|||
// mark seg as allocated,
|
|||
// leaving the new node free
|
|||
} |
|||
// else, the segment is not big enough to
|
|||
// be splitted, we mark it allocated as is,
|
|||
// wasting a bit of memory
|
|||
|
|||
seg->free = 0; |
|||
|
|||
return (void *)seg + sizeof(seg_header); |
|||
} |
|||
|
|||
// no available segment in the heap
|
|||
// let's expand
|
|||
|
|||
expand_heap(MAX(size+sizeof(seg_header), MIN_EXPAND_SIZE)); |
|||
|
|||
// retrty now that we are sure that the memory is avaiable
|
|||
return kmalloc(size); |
|||
} |
|||
|
|||
|
|||
// O(1) free
|
|||
void kfree(void *ptr) { |
|||
seg_header* header = ptr - sizeof(seg_header); |
|||
|
|||
assert(header->free == 0); |
|||
|
|||
header->free = 1; |
|||
|
|||
static int i = 0; |
|||
|
|||
// defragment the heap every N frees
|
|||
if((i++ % 32) == 0) |
|||
defragment(); |
|||
} |
|||
|
|||
|
|||
#ifndef NDEBUG |
|||
void kmalloc_test(void) { |
|||
void* arr[128]; |
|||
|
|||
uint64_t size = 5; |
|||
|
|||
for(int j = 0; j < 100; j++) { |
|||
for(int i = 0; i < 128; i++) { |
|||
arr[i] = kmalloc(size % 1024); |
|||
size = (16807 * size) % (1 << 31 - 1); |
|||
} |
|||
for(int i = 0; i < 128; i++) |
|||
kfree(arr[i]); |
|||
} |
|||
|
|||
//print();
|
|||
|
|||
} |
|||
#endif |
|||
|
Loading…
Reference in new issue