forked from mit-pdos/xv6-riscv
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Completely add buddy allocator sources with linked list implementation. Co-authored-by: DecafMango
- Loading branch information
1 parent
a751244
commit 577119a
Showing
5 changed files
with
417 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,333 @@ | ||
#include "types.h" | ||
#include "param.h" | ||
#include "memlayout.h" | ||
#include "spinlock.h" | ||
#include "riscv.h" | ||
#include "defs.h" | ||
#include "list.h" | ||
|
||
// Buddy allocator | ||
|
||
static int nsizes; // the number of entries in bd_sizes array | ||
|
||
#define LEAF_SIZE 16 // The smallest block size | ||
#define MAXSIZE (nsizes - 1) // Largest index in bd_sizes array | ||
#define BLK_SIZE(k) ((1L << (k)) * LEAF_SIZE) // Size of block at size k | ||
#define HEAP_SIZE BLK_SIZE(MAXSIZE) | ||
#define NBLK(k) (1 << (MAXSIZE - k)) // Number of block at size k | ||
#define ROUNDUP(n, sz) \ | ||
(((((n)-1) / (sz)) + 1) * (sz)) // Round up to the next multiple of sz | ||
|
||
typedef struct list Bd_list; | ||
|
||
// The allocator has sz_info for each size k. Each sz_info has a free | ||
// list, an array alloc to keep track which blocks have been | ||
// allocated, and an split array to to keep track which blocks have | ||
// been split. The arrays are of type char (which is 1 byte), but the | ||
// allocator uses 1 bit per block (thus, one char records the info of | ||
// 8 blocks). | ||
struct sz_info { | ||
Bd_list free; | ||
char *alloc; | ||
char *split; | ||
}; | ||
typedef struct sz_info Sz_info; | ||
|
||
static Sz_info *bd_sizes; | ||
static void *bd_base; // start address of memory managed by the buddy allocator | ||
static struct spinlock lock; | ||
|
||
// Return 1 if bit at position index in array is set to 1 | ||
int bit_isset(char *array, int index) { | ||
char b = array[index / 8]; | ||
char m = (1 << (index % 8)); | ||
return (b & m) == m; | ||
} | ||
|
||
// Set bit at position index in array to 1 | ||
void bit_set(char *array, int index) { | ||
char b = array[index / 8]; | ||
char m = (1 << (index % 8)); | ||
array[index / 8] = (b | m); | ||
} | ||
|
||
// Clear bit at position index in array | ||
void bit_clear(char *array, int index) { | ||
char b = array[index / 8]; | ||
char m = (1 << (index % 8)); | ||
array[index / 8] = (b & ~m); | ||
} | ||
|
||
// Print a bit vector as a list of ranges of 1 bits | ||
void bd_print_vector(char *vector, int len) { | ||
int last, lb; | ||
|
||
last = 1; | ||
lb = 0; | ||
for (int b = 0; b < len; b++) { | ||
if (last == bit_isset(vector, b)) continue; | ||
if (last == 1) printf(" [%d, %d)", lb, b); | ||
lb = b; | ||
last = bit_isset(vector, b); | ||
} | ||
if (lb == 0 || last == 1) { | ||
printf(" [%d, %d)", lb, len); | ||
} | ||
printf("\n"); | ||
} | ||
|
||
// Print buddy's data structures | ||
void bd_print() { | ||
for (int k = 0; k < nsizes; k++) { | ||
printf("size %d (blksz %ld nblk %d): free list: ", k, BLK_SIZE(k), NBLK(k)); | ||
lst_print(&bd_sizes[k].free); | ||
printf(" alloc:"); | ||
bd_print_vector(bd_sizes[k].alloc, NBLK(k)); | ||
if (k > 0) { | ||
printf(" split:"); | ||
bd_print_vector(bd_sizes[k].split, NBLK(k)); | ||
} | ||
} | ||
} | ||
|
||
// What is the first k such that 2^k >= n? | ||
int firstk(uint64 n) { | ||
int k = 0; | ||
uint64 size = LEAF_SIZE; | ||
|
||
while (size < n) { | ||
k++; | ||
size *= 2; | ||
} | ||
return k; | ||
} | ||
|
||
// Compute the block index for address p at size k | ||
int blk_index(int k, char *p) { | ||
int n = p - (char *)bd_base; | ||
return n / BLK_SIZE(k); | ||
} | ||
|
||
// Convert a block index at size k back into an address | ||
void *addr(int k, int bi) { | ||
int n = bi * BLK_SIZE(k); | ||
return (char *)bd_base + n; | ||
} | ||
|
||
// allocate nbytes, but malloc won't return anything smaller than LEAF_SIZE | ||
void *bd_malloc(uint64 nbytes) { | ||
int fk, k; | ||
|
||
acquire(&lock); | ||
|
||
// Find a free block >= nbytes, starting with smallest k possible | ||
fk = firstk(nbytes); | ||
for (k = fk; k < nsizes; k++) { | ||
if (!lst_empty(&bd_sizes[k].free)) break; | ||
} | ||
if (k >= nsizes) { // No free blocks? | ||
release(&lock); | ||
return 0; | ||
} | ||
|
||
// Found a block; pop it and potentially split it. | ||
char *p = lst_pop(&bd_sizes[k].free); | ||
bit_set(bd_sizes[k].alloc, blk_index(k, p)); | ||
for (; k > fk; k--) { | ||
// split a block at size k and mark one half allocated at size k-1 | ||
// and put the buddy on the free list at size k-1 | ||
char *q = p + BLK_SIZE(k - 1); // p's buddy | ||
bit_set(bd_sizes[k].split, blk_index(k, p)); | ||
bit_set(bd_sizes[k - 1].alloc, blk_index(k - 1, p)); | ||
lst_push(&bd_sizes[k - 1].free, q); | ||
} | ||
release(&lock); | ||
|
||
return p; | ||
} | ||
|
||
// Find the size of the block that p points to. | ||
int size(char *p) { | ||
for (int k = 0; k < nsizes; k++) { | ||
if (bit_isset(bd_sizes[k + 1].split, blk_index(k + 1, p))) { | ||
return k; | ||
} | ||
} | ||
return 0; | ||
} | ||
|
||
// Free memory pointed to by p, which was earlier allocated using | ||
// bd_malloc. | ||
void bd_free(void *p) { | ||
void *q; | ||
int k; | ||
|
||
acquire(&lock); | ||
for (k = size(p); k < MAXSIZE; k++) { | ||
int bi = blk_index(k, p); | ||
int buddy = (bi % 2 == 0) ? bi + 1 : bi - 1; | ||
bit_clear(bd_sizes[k].alloc, bi); // free p at size k | ||
if (bit_isset(bd_sizes[k].alloc, buddy)) { // is buddy allocated? | ||
break; // break out of loop | ||
} | ||
// budy is free; merge with buddy | ||
q = addr(k, buddy); | ||
lst_remove(q); // remove buddy from free list | ||
if (buddy % 2 == 0) { | ||
p = q; | ||
} | ||
// at size k+1, mark that the merged buddy pair isn't split | ||
// anymore | ||
bit_clear(bd_sizes[k + 1].split, blk_index(k + 1, p)); | ||
} | ||
lst_push(&bd_sizes[k].free, p); | ||
release(&lock); | ||
} | ||
|
||
// Compute the first block at size k that doesn't contain p | ||
int blk_index_next(int k, char *p) { | ||
int n = (p - (char *)bd_base) / BLK_SIZE(k); | ||
if ((p - (char *)bd_base) % BLK_SIZE(k) != 0) n++; | ||
return n; | ||
} | ||
|
||
int _log2(uint64 n) { | ||
int k = 0; | ||
while (n > 1) { | ||
k++; | ||
n = n >> 1; | ||
} | ||
return k; | ||
} | ||
|
||
// Mark memory from [start, stop), starting at size 0, as allocated. | ||
void bd_mark(void *start, void *stop) { | ||
int bi, bj; | ||
|
||
if (((uint64)start % LEAF_SIZE != 0) || ((uint64)stop % LEAF_SIZE != 0)) | ||
panic("bd_mark"); | ||
|
||
for (int k = 0; k < nsizes; k++) { | ||
bi = blk_index(k, start); | ||
bj = blk_index_next(k, stop); | ||
for (; bi < bj; bi++) { | ||
if (k > 0) { | ||
// if a block is allocated at size k, mark it as split too. | ||
bit_set(bd_sizes[k].split, bi); | ||
} | ||
bit_set(bd_sizes[k].alloc, bi); | ||
} | ||
} | ||
} | ||
|
||
// If a block is marked as allocated and the buddy is free, put the | ||
// buddy on the free list at size k. | ||
int bd_initfree_pair(int k, int bi) { | ||
int buddy = (bi % 2 == 0) ? bi + 1 : bi - 1; | ||
int free = 0; | ||
if (bit_isset(bd_sizes[k].alloc, bi) != bit_isset(bd_sizes[k].alloc, buddy)) { | ||
// one of the pair is free | ||
free = BLK_SIZE(k); | ||
if (bit_isset(bd_sizes[k].alloc, bi)) | ||
lst_push(&bd_sizes[k].free, addr(k, buddy)); // put buddy on free list | ||
else | ||
lst_push(&bd_sizes[k].free, addr(k, bi)); // put bi on free list | ||
} | ||
return free; | ||
} | ||
|
||
// Initialize the free lists for each size k. For each size k, there | ||
// are only two pairs that may have a buddy that should be on free list: | ||
// bd_left and bd_right. | ||
int bd_initfree(void *bd_left, void *bd_right) { | ||
int free = 0; | ||
|
||
for (int k = 0; k < MAXSIZE; k++) { // skip max size | ||
int left = blk_index_next(k, bd_left); | ||
int right = blk_index(k, bd_right); | ||
free += bd_initfree_pair(k, left); | ||
if (right <= left) continue; | ||
free += bd_initfree_pair(k, right); | ||
} | ||
return free; | ||
} | ||
|
||
// Mark the range [bd_base,p) as allocated | ||
int bd_mark_data_structures(char *p) { | ||
int meta = p - (char *)bd_base; | ||
printf("bd: %d meta bytes for managing %ld bytes of memory\n", meta, | ||
BLK_SIZE(MAXSIZE)); | ||
bd_mark(bd_base, p); | ||
return meta; | ||
} | ||
|
||
// Mark the range [end, HEAPSIZE) as allocated | ||
int bd_mark_unavailable(void *end, void *left) { | ||
int unavailable = BLK_SIZE(MAXSIZE) - (end - bd_base); | ||
if (unavailable > 0) unavailable = ROUNDUP(unavailable, LEAF_SIZE); | ||
printf("bd: 0x%x bytes unavailable\n", unavailable); | ||
|
||
void *bd_end = bd_base + BLK_SIZE(MAXSIZE) - unavailable; | ||
bd_mark(bd_end, bd_base + BLK_SIZE(MAXSIZE)); | ||
return unavailable; | ||
} | ||
|
||
// Initialize the buddy allocator: it manages memory from [base, end). | ||
void bd_init(void *base, void *end) { | ||
char *p = (char *)ROUNDUP((uint64)base, LEAF_SIZE); | ||
int sz; | ||
|
||
initlock(&lock, "buddy"); | ||
bd_base = (void *)p; | ||
|
||
// compute the number of sizes we need to manage [base, end) | ||
nsizes = _log2(((char *)end - p) / LEAF_SIZE) + 1; | ||
if ((char *)end - p > BLK_SIZE(MAXSIZE)) { | ||
nsizes++; // round up to the next power of 2 | ||
} | ||
|
||
printf("bd: memory sz is %ld bytes; allocate an size array of length %d\n", | ||
(char *)end - p, nsizes); | ||
|
||
// allocate bd_sizes array | ||
bd_sizes = (Sz_info *)p; | ||
p += sizeof(Sz_info) * nsizes; | ||
memset(bd_sizes, 0, sizeof(Sz_info) * nsizes); | ||
|
||
// initialize free list and allocate the alloc array for each size k | ||
for (int k = 0; k < nsizes; k++) { | ||
lst_init(&bd_sizes[k].free); | ||
sz = sizeof(char) * ROUNDUP(NBLK(k), 8) / 8; | ||
bd_sizes[k].alloc = p; | ||
memset(bd_sizes[k].alloc, 0, sz); | ||
p += sz; | ||
} | ||
|
||
// allocate the split array for each size k, except for k = 0, since | ||
// we will not split blocks of size k = 0, the smallest size. | ||
for (int k = 1; k < nsizes; k++) { | ||
sz = sizeof(char) * (ROUNDUP(NBLK(k), 8)) / 8; | ||
bd_sizes[k].split = p; | ||
memset(bd_sizes[k].split, 0, sz); | ||
p += sz; | ||
} | ||
p = (char *)ROUNDUP((uint64)p, LEAF_SIZE); | ||
|
||
// done allocating; mark the memory range [base, p) as allocated, so | ||
// that buddy will not hand out that memory. | ||
int meta = bd_mark_data_structures(p); | ||
|
||
// mark the unavailable memory range [end, HEAP_SIZE) as allocated, | ||
// so that buddy will not hand out that memory. | ||
int unavailable = bd_mark_unavailable(end, p); | ||
void *bd_end = bd_base + BLK_SIZE(MAXSIZE) - unavailable; | ||
|
||
// initialize free lists for each size k | ||
int free = bd_initfree(p, bd_end); | ||
|
||
// check if the amount that is free is what we expect | ||
if (free != BLK_SIZE(MAXSIZE) - meta - unavailable) { | ||
printf("free %d %ld\n", free, BLK_SIZE(MAXSIZE) - meta - unavailable); | ||
panic("bd_init: free mem"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.