From 7b662987f87219421e3065e038c9b2d83255e07c Mon Sep 17 00:00:00 2001 From: Lephe Date: Wed, 9 Nov 2022 21:35:32 +0100 Subject: [PATCH] kmalloc: add kmalloc_max() function --- include/gint/kmalloc.h | 16 +++++++++++++ src/kmalloc/arena_gint.c | 49 ++++++++++++++++++++++++++++++++++++++++ src/kmalloc/kmalloc.c | 30 ++++++++++++++++++++++++ 3 files changed, 95 insertions(+) diff --git a/include/gint/kmalloc.h b/include/gint/kmalloc.h index 6483ce5..de2496e 100644 --- a/include/gint/kmalloc.h +++ b/include/gint/kmalloc.h @@ -39,6 +39,20 @@ void *krealloc(void *ptr, size_t size); /* kfree(): Free memory allocated with kalloc() */ void kfree(void *ptr); +/* kmalloc_max(): Allocate the largest block available in an arena + + This function is like kmalloc(), but it find the largest block in the arena + and returns it whole after setting its size in *size. This is useful if you + need memory to manage with another allocator, or you don't yet know the size + of the data to be generated. The block can later be shrunk with realloc(). + + Currently only gint-managed arenas support this operation. + + @size Will be set to size of returned block (if there is one) + @arean_name Name of arena to allocate in (*cannot* be NULL) + Returns the address of the largest block available, NULL on error. */ +void *kmalloc_max(size_t *size, char const *arena_name); + //--- // Extension API for new areas and statistics //--- @@ -53,6 +67,8 @@ typedef struct { void * (*realloc)(void *ptr, size_t newsize, void *data); /* kfree() handles ptr == NULL*/ void (*free)(void *ptr, void *data); + /* kmalloc_max() backend */ + void * (*malloc_max)(size_t *size, void *data); /* Name, should be unique; gint reserves names starting with "_" */ char const *name; diff --git a/src/kmalloc/arena_gint.c b/src/kmalloc/arena_gint.c index 1888bc5..b037b0a 100644 --- a/src/kmalloc/arena_gint.c +++ b/src/kmalloc/arena_gint.c @@ -178,6 +178,25 @@ static block_t *best_fit(block_t *list, size_t size) return best_match; } +/* Find a worst fit in the list */ +static block_t *worst_fit(block_t *list) +{ + block_t *best_match = NULL; + size_t best_size = 0; + + while(list) + { + if(list->size >= best_size) + { + best_match = list; + best_size = list->size; + } + list = next_link(list); + } + + return best_match; +} + //--- // Index-level operations //--- @@ -366,6 +385,35 @@ static void *gint_realloc(void *ptr, size_t size, void *data) return new_ptr; } +static void *gint_malloc_max(size_t *size, void *data) +{ + index_t *index = data; + stats_t *s = index->stats; + + /* Find the largest available block in the largest possible class */ + block_t *alloc; + for(int c = 15; c >= 0; c--) + { + block_t *list = index->classes[c]; + alloc = (c < 14) ? list : worst_fit(list); + if(alloc) break; + } + if(!alloc) return NULL; + + remove_link(alloc, index); + + /* Mark the block as allocated and return it */ + block_t *next = next_block(alloc); + alloc->used = true; + if(next) next->previous_used = true; + + if(s) s->used_memory += alloc->size; + if(s) s->peak_used_memory = max(s->peak_used_memory, s->used_memory); + + *size = alloc->size; + return (void *)alloc + sizeof(block_t); +} + /* kmalloc_init_arena(): Initialize an arena with gint's allocator */ void kmalloc_init_arena(kmalloc_arena_t *a, bool enable_statistics) { @@ -375,6 +423,7 @@ void kmalloc_init_arena(kmalloc_arena_t *a, bool enable_statistics) a->malloc = gint_malloc; a->free = gint_free; a->realloc = gint_realloc; + a->malloc_max = gint_malloc_max; /* The index is located at the very start of the arena */ index_t *index = a->start; diff --git a/src/kmalloc/kmalloc.c b/src/kmalloc/kmalloc.c index 9d8e179..bb2d2ca 100644 --- a/src/kmalloc/kmalloc.c +++ b/src/kmalloc/kmalloc.c @@ -131,6 +131,36 @@ void kfree(void *ptr) a->stats.live_blocks--; } +/* kmalloc_max(): Allocate the largest block available in an arena */ +void *kmalloc_max(size_t *size, char const *name) +{ + for(int i = 0; i < KMALLOC_ARENA_MAX; i++) if(arenas[i]) + { + kmalloc_arena_t *a = arenas[i]; + if(strcmp(a->name, name)) continue; + + void *rc = a->malloc_max ? a->malloc_max(size, a->data) : NULL; + + /* Maintain statistics */ + struct kmalloc_stats *s = &a->stats; + if(rc) + { + s->live_blocks++; + s->peak_live_blocks = max(s->live_blocks, + s->peak_live_blocks); + s->total_volume += *size; + s->total_blocks++; + return rc; + } + else + { + s->total_failures++; + } + } + + return NULL; +} + /* kmalloc_add_arena(): Add a new arena to the heap source */ bool kmalloc_add_arena(kmalloc_arena_t *arena) {