From c8f1edfaa4a9ac90670b71fdab83b1d8d5f133af Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 17 Jan 2019 10:29:56 +0100 Subject: [PATCH] alloc: fix and improve the block allocation algorithm The block allocation algorithm, as implemented in _balloc(), is first trying to find a single free memory block to satisfy the request. If that fails it is then trying to allocate several blocks in a sequence. That part is implemented wrongly. It can end up allocating multiple such block sequences, and not using but leaking them instead. This patch first simplifies the search for a single suitable buffer, then fixes the leakage. Signed-off-by: Guennadi Liakhovetski --- src/lib/alloc.c | 52 +++++++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/src/lib/alloc.c b/src/lib/alloc.c index 2ea885f14..fe372ce32 100644 --- a/src/lib/alloc.c +++ b/src/lib/alloc.c @@ -656,40 +656,32 @@ void *_balloc(int zone, uint32_t caps, size_t bytes) for (i = 0; i < heap->blocks; i++) { map = &heap->map[i]; - /* is block big enough */ - if (map->block_size < bytes) - continue; - - /* does block have free space */ - if (map->free_count == 0) - continue; - - /* allocate block */ - ptr = alloc_block(heap, i, caps); - goto out; - } - - /* request spans > 1 block */ - - /* only 1 choice for block size */ - if (heap->blocks == 1) { - ptr = alloc_cont_blocks(heap, 0, caps, bytes); - goto out; - } else { - - /* find best block size for request */ - for (i = 0; i < heap->blocks; i++) { - map = &heap->map[i]; - - /* allocate is block size smaller than request */ - if (map->block_size < bytes) - alloc_cont_blocks(heap, i, caps, bytes); + /* Check if blocks are big enough and at least one is free */ + if (map->block_size >= bytes && map->free_count) { + /* found: grab a block */ + ptr = alloc_block(heap, i, caps); + break; } } - ptr = alloc_cont_blocks(heap, heap->blocks - 1, caps, bytes); + /* request spans > 1 block */ + if (!ptr) { + /* + * Find the best block size for request. We know, that we failed + * to find a single large enough block, so, skip those. + */ + for (i = heap->blocks - 1; i >= 0; i--) { + map = &heap->map[i]; + + /* allocate if block size is smaller than request */ + if (heap->size >= bytes && map->block_size < bytes) { + ptr = alloc_cont_blocks(heap, i, caps, bytes); + if (ptr) + break; + } + } + } -out: if (ptr && ((zone & RZONE_FLAG_MASK) == RZONE_FLAG_UNCACHED)) ptr = cache_to_uncache(ptr);