Buddy allocator
This commit is contained in:
parent
21d55031d9
commit
90c63ab41e
3 changed files with 162 additions and 0 deletions
1
Makefile
1
Makefile
|
@ -64,6 +64,7 @@ KERNEL_OBJ := \
|
|||
kern/libkern/spinlock.o \
|
||||
kern/libkern/mini-printf.o \
|
||||
kern/libkern/stdio.o \
|
||||
kern/libkern/buddy.o \
|
||||
kern/libkern/badrand.o
|
||||
|
||||
kern/kernel.elf: $(KERNEL_OBJ)
|
||||
|
|
139
kern/libkern/buddy.c
Normal file
139
kern/libkern/buddy.c
Normal file
|
@ -0,0 +1,139 @@
|
|||
#include <assert.h>
|
||||
#include <buddy.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX_ORDER 22 // 2^22 * min_block_size = max allocatable block
|
||||
#define MIN_BLOCK_SIZE 32 // Smallest allocatable block
|
||||
|
||||
typedef struct free_block {
|
||||
struct free_block *next;
|
||||
} free_block_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t order;
|
||||
} block_header_t;
|
||||
|
||||
static free_block_t *free_lists[MAX_ORDER + 1];
|
||||
|
||||
uintptr_t heap_start = (uintptr_t)&__heap_start;
|
||||
uintptr_t heap_end = (uintptr_t)&__heap_end;
|
||||
|
||||
// Utility: round up to next power of two
|
||||
static size_t next_pow2(size_t x) {
|
||||
if (x <= 1)
|
||||
return 1;
|
||||
x--;
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
x |= x >> 4;
|
||||
x |= x >> 8;
|
||||
x |= x >> 16;
|
||||
#if UINTPTR_MAX > 0xffffffff
|
||||
x |= x >> 32;
|
||||
#endif
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
// Find order for size
|
||||
static int size_to_order(size_t size) {
|
||||
size_t needed = (size < MIN_BLOCK_SIZE) ? MIN_BLOCK_SIZE : size;
|
||||
size_t block_size = next_pow2(needed);
|
||||
int order = 0;
|
||||
while ((MIN_BLOCK_SIZE << order) < block_size) {
|
||||
order++;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
// Initialize allocator
|
||||
void buddy_init(uintptr_t start, uintptr_t end) {
|
||||
heap_start = (start + MIN_BLOCK_SIZE - 1) & ~(MIN_BLOCK_SIZE - 1);
|
||||
heap_end = end & ~(MIN_BLOCK_SIZE - 1);
|
||||
|
||||
// Clear free lists
|
||||
for (int i = 0; i <= MAX_ORDER; i++) free_lists[i] = NULL;
|
||||
|
||||
// Put the whole region as one big block
|
||||
size_t total_size = heap_end - heap_start;
|
||||
int order = MAX_ORDER;
|
||||
while ((MIN_BLOCK_SIZE << order) > total_size && order > 0) order--;
|
||||
|
||||
free_block_t *block = (free_block_t *)heap_start;
|
||||
block->next = NULL;
|
||||
free_lists[order] = block;
|
||||
}
|
||||
|
||||
// Allocate memory
|
||||
void *buddy_alloc(size_t size) {
|
||||
int order = size_to_order(size);
|
||||
|
||||
for (int i = order; i <= MAX_ORDER; i++) {
|
||||
if (free_lists[i]) {
|
||||
// Remove block from this list
|
||||
free_block_t *block = free_lists[i];
|
||||
free_lists[i] = block->next;
|
||||
|
||||
// Split down to requested order
|
||||
while (i > order) {
|
||||
i--;
|
||||
uintptr_t buddy_addr = (uintptr_t)block + (MIN_BLOCK_SIZE << i);
|
||||
free_block_t *buddy = (free_block_t *)buddy_addr;
|
||||
buddy->next = free_lists[i];
|
||||
free_lists[i] = buddy;
|
||||
}
|
||||
|
||||
/*
|
||||
* Here, we install a header into the block. This will allow us to use free, without
|
||||
* specifying size, since it can read the order from one byte before the block start.
|
||||
*/
|
||||
{
|
||||
block_header_t *hdr = (block_header_t *)block;
|
||||
hdr->order = order;
|
||||
return (void *)(hdr + 1); // return pointer after header
|
||||
// return (void *)block; // Regular block return for reference
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL; // Out of memory
|
||||
}
|
||||
|
||||
// Free memory
|
||||
int buddy_free(void *ptr) {
|
||||
block_header_t *hdr = (block_header_t *)ptr - 1;
|
||||
int order = hdr->order;
|
||||
|
||||
assert_msg(order != MAX_ORDER, "The buddy freelist header seems to have been corrupted.");
|
||||
|
||||
uintptr_t addr = (uintptr_t)ptr;
|
||||
|
||||
while (order < MAX_ORDER) {
|
||||
uintptr_t buddy_addr = addr ^ (MIN_BLOCK_SIZE << order);
|
||||
|
||||
// Check if buddy is in free list
|
||||
free_block_t **prev = &free_lists[order];
|
||||
free_block_t *cur = free_lists[order];
|
||||
while (cur) {
|
||||
if ((uintptr_t)cur == buddy_addr) {
|
||||
// Remove buddy from list
|
||||
*prev = cur->next;
|
||||
// Merge
|
||||
if (buddy_addr < addr)
|
||||
addr = buddy_addr;
|
||||
order++;
|
||||
goto try_merge;
|
||||
}
|
||||
prev = &cur->next;
|
||||
cur = cur->next;
|
||||
}
|
||||
break; // Buddy not free, stop
|
||||
try_merge:;
|
||||
}
|
||||
|
||||
free_block_t *block = (free_block_t *)addr;
|
||||
block->next = free_lists[order];
|
||||
free_lists[order] = block;
|
||||
|
||||
return order;
|
||||
}
|
22
kern/libkern/buddy.h
Normal file
22
kern/libkern/buddy.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef BUDDY_H
|
||||
#define BUDDY_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern uintptr_t heap_start;
|
||||
extern uintptr_t heap_end;
|
||||
|
||||
extern char __heap_start;
|
||||
extern char __heap_end;
|
||||
|
||||
void buddy_init(uintptr_t start, uintptr_t end);
|
||||
void *buddy_alloc(size_t size);
|
||||
int buddy_free(void *ptr);
|
||||
|
||||
/* Returns total heap memory managed by buddy allocator in bytes */
|
||||
static inline size_t buddy_total_bytes(void) {
|
||||
return (uintptr_t)&__heap_end - (uintptr_t)&__heap_start;
|
||||
}
|
||||
|
||||
#endif // BUDDY_H
|
Loading…
Add table
Reference in a new issue