diff --git a/buddy.c b/buddy.c index 3167149..011fdf6 100644 --- a/buddy.c +++ b/buddy.c @@ -166,16 +166,14 @@ #define MIN_BLOCK_SIZE BUDDY_ALLOC_MIN_BLOCK_SIZE #define MAX_BLOCK_ALIGN_MASK (MAX_BLOCK_SIZE - 1) -struct page { struct page *next; }; - /* * Gets the address of the i-th page of the memory pool. * In this context, a page is a block of size MAX_BLOCK_SIZE. */ -static struct page* +static struct buddy_page* page_index_to_ptr(char *base, int i) { - return (struct page*) (base + (i << MAX_BLOCK_LOG2)); + return (struct buddy_page*) (base + (i << MAX_BLOCK_LOG2)); } static struct buddy_alloc startup_empty() @@ -231,15 +229,23 @@ struct buddy_alloc buddy_startup(char *base, size_t size, /* * Make the linked list of pages */ - struct page *head = NULL; - struct page **tail = &head; + struct buddy_page *head = NULL; + struct buddy_page *tail = NULL; int num_pages = size >> MAX_BLOCK_LOG2; for (int i = 0; i < num_pages; i++) { - struct page *p = page_index_to_ptr(base, i); - *tail = p; - tail = &p->next; + + struct buddy_page *p = page_index_to_ptr(base, i); + + if (head) { + tail->next = p; + p->prev = tail; + } else { + head = p; + p->prev = NULL; + } + tail = p; + p->next = NULL; } - *tail = NULL; /* * Initialize the page info. The page_info bits are 0 when @@ -512,17 +518,15 @@ remove_sibling_from_list(struct buddy_alloc *alloc, int i, void *ptr) { size_t len = 1U << (i + MIN_BLOCK_LOG2); - struct page *sibling = (struct page*) sibling_of(ptr, len); - struct page *curs = (struct page*) alloc->lists[i]; - struct page **prev = (struct page**) &alloc->lists[i]; - while (curs != (struct page*) sibling) { - assert(curs); - prev = &curs->next; - curs = curs->next; - assert(curs); - } - assert(sibling == curs); - *prev = sibling->next; + struct buddy_page *sibling = (struct buddy_page*) sibling_of(ptr, len); + + if (sibling->prev) + sibling->prev->next = sibling->next; + else + alloc->lists[i] = sibling->next; + + if (sibling->next) + sibling->next->prev = sibling->prev; } /* @@ -537,21 +541,29 @@ static void append(struct buddy_alloc *alloc, { assert(i >= 0 && i < BUDDY_ALLOC_NUM_LISTS); - struct page *pag = ptr; + struct buddy_page *page = ptr; - pag->next = alloc->lists[i]; - alloc->lists[i] = pag; + if (alloc->lists[i]) + alloc->lists[i]->prev = page; + + page->prev = NULL; + page->next = alloc->lists[i]; + + alloc->lists[i] = page; } static char *pop(struct buddy_alloc *alloc, int i) { assert(i >= 0 && i < BUDDY_ALLOC_NUM_LISTS); - - char *ptr = alloc->lists[i]; - assert(ptr); - alloc->lists[i] = ((struct page*) ptr)->next; - return ptr; + struct buddy_page *page = alloc->lists[i]; + assert(page); + + alloc->lists[i] = page->next; + if (alloc->lists[i]) + alloc->lists[i]->prev = NULL; + + return (char*) page; } void *buddy_malloc(struct buddy_alloc *alloc, size_t len) diff --git a/buddy.h b/buddy.h index 3a6a949..c9b1c0b 100644 --- a/buddy.h +++ b/buddy.h @@ -29,20 +29,20 @@ /* - * This is the minimum and maximum block size. The allocator uses free - * lists to keep track of unused blocks, so a block must be at least - * the size of a pointer. We assume a pointer is 8 bytes long, so the - * minimum value must be greater or equal to 3 (log2(8) = 3). + * This is the minimum and maximum block size. The allocator uses doubly + * linked free lists to keep track of unused blocks, so a block must be + * at least the size of two pointers. We assume a pointer is 8 bytes long, + * so the minimum value must be greater or equal to 4 (log2(2*8) = 4). * * For the maximum value there is really no downside in making it big, * except for the fact that the pool provided by the user should at * least be that big. */ #define BUDDY_ALLOC_MAX_BLOCK_LOG2 13 -#define BUDDY_ALLOC_MIN_BLOCK_LOG2 3 +#define BUDDY_ALLOC_MIN_BLOCK_LOG2 4 _Static_assert(BUDDY_ALLOC_MIN_BLOCK_LOG2 <= BUDDY_ALLOC_MAX_BLOCK_LOG2); -_Static_assert(BUDDY_ALLOC_MIN_BLOCK_LOG2 > 2); +_Static_assert(BUDDY_ALLOC_MIN_BLOCK_LOG2 > 3); #define BUDDY_ALLOC_NUM_LISTS (BUDDY_ALLOC_MAX_BLOCK_LOG2 - BUDDY_ALLOC_MIN_BLOCK_LOG2 + 1) #define BUDDY_ALLOC_MAX_BLOCK_SIZE (1U << BUDDY_ALLOC_MAX_BLOCK_LOG2) @@ -67,10 +67,15 @@ struct page_info { uint32_t bits[BUDDY_ALLOC_WORDS_PER_PAGE]; }; +struct buddy_page { + struct buddy_page *prev; + struct buddy_page *next; +}; + struct buddy_alloc { void *base; size_t size; - void *lists[BUDDY_ALLOC_NUM_LISTS]; + struct buddy_page *lists[BUDDY_ALLOC_NUM_LISTS]; struct page_info *info; int num_info; };