unified: Support heap memory pool

Fleshes out the prototype heap memory pool support
to make it fully operational. Noteworthy changes are
listed below:

Tweaks arguments to k_malloc() and k_free() to be more like
malloc() and free(). Similarly, modifies k_free() to take
no action when passed a NULL pointer.

Now stores the complete block descriptor at the start
of any block allocated from the heap memory pool. This
increases memory overhead by 4 bytes per block, but
streamlines the allocation and freeing algorithms. It also
ensures that the routines will work if the block descriptor
internals are changed in the future.

Now allows the heap memory pool to be defined using the
HEAP_MEM_POOL_SIZE configuration option. This will be the
official configuration approach in the unified kernel.

Also allows the heap memory pool to be defined using the
(undocumented) HEAP_SIZE entry in the MDEF. This is provided
for legacy reasons only.

Co-locates memory pool initialization code to keep the line
that causes memory pool initialization to be done during booting
right next to the routine that does the initialization.

Change-Id: Ifea9d88142fb434d4bea38bb1fcc4856a3853d8d
Signed-off-by: Allan Stephens <allan.stephens@windriver.com>
This commit is contained in:
Allan Stephens 2016-10-13 15:44:48 -05:00 committed by Anas Nashif
commit 480a131ad9
4 changed files with 87 additions and 61 deletions

View file

@ -1814,10 +1814,10 @@ extern void k_mem_pool_free(struct k_mem_block *block);
extern void k_mem_pool_defrag(struct k_mem_pool *pool);
/**
* @brief Allocate memory from heap pool
* @brief Allocate memory from heap
*
* This routine provides traditional malloc semantics; internally it uses
* the memory pool APIs on a dedicated HEAP pool
* This routine provides traditional malloc() semantics. The memory is
* allocated from the heap memory pool.
*
* @param size Size of memory requested by the caller (in bytes)
*
@ -1826,7 +1826,10 @@ extern void k_mem_pool_defrag(struct k_mem_pool *pool);
extern void *k_malloc(size_t size);
/**
* @brief Free memory allocated through k_malloc()
* @brief Free memory allocated from heap
*
* This routine provides traditional free() semantics. The memory being
* returned must have been allocated from the heap memory pool.
*
* @param ptr Pointer to previously allocated memory
*

View file

@ -319,4 +319,14 @@ config MEM_POOL_AD_AFTER_SEARCH_FOR_BIGGERBLOCK
endchoice
config HEAP_MEM_POOL_SIZE
int
prompt "Heap memory pool size (in bytes)"
default 0
help
This option specifies the size of the heap memory pool used when
dynamically allocating memory using k_malloc(). Supported values
are: 256, 1024, 4096, and 16384. A size of zero means that no
heap memory pool is defined.
endmenu

View file

@ -24,17 +24,14 @@
#include <ksched.h>
#include <wait_q.h>
#include <init.h>
#include <stdlib.h>
#include <string.h>
#define _QUAD_BLOCK_AVAILABLE 0x0F
#define _QUAD_BLOCK_ALLOCATED 0x0
extern struct k_mem_pool _k_mem_pool_start[];
extern struct k_mem_pool _k_mem_pool_end[];
#if defined _HEAP_MEM_POOL
static struct k_mem_pool *heap_mem_pool = _HEAP_MEM_POOL;
#else
static struct k_mem_pool *heap_mem_pool;
#endif
static void init_one_memory_pool(struct k_mem_pool *pool);
@ -61,6 +58,8 @@ static int init_static_pools(struct device *unused)
return 0;
}
SYS_INIT(init_static_pools, PRIMARY, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);
/**
*
* @brief Initialize the memory pool
@ -523,45 +522,6 @@ int k_mem_pool_alloc(struct k_mem_pool *pool, struct k_mem_block *block,
return -ENOMEM;
}
#define MALLOC_ALIGN (sizeof(uint32_t))
void *k_malloc(size_t size)
{
uint32_t new_size;
uint32_t *aligned_addr;
struct k_mem_block mem_block;
__ASSERT(heap_mem_pool != NULL,
"Try to allocate a block in undefined heap\n");
/* The address pool returns, may not be aligned. Also
* pool_free requires both start address and size. So
* we end up needing 2 slots to save the size and
* start address in addition to padding space
*/
new_size = size + (sizeof(uint32_t) << 1) + MALLOC_ALIGN - 1;
if (k_mem_pool_alloc(heap_mem_pool, &mem_block, new_size,
K_NO_WAIT) != 0) {
return NULL;
}
/* Get the next aligned address following the address returned by pool*/
aligned_addr = (uint32_t *) ROUND_UP(mem_block.addr_in_pool,
MALLOC_ALIGN);
/* Save the size requested to the pool API, to be used while freeing */
*aligned_addr = new_size;
/* Save the original unaligned_addr pointer too */
aligned_addr++;
*((void **) aligned_addr) = mem_block.addr_in_pool;
/* return the subsequent address */
return ++aligned_addr;
}
void k_mem_pool_free(struct k_mem_block *block)
{
int offset;
@ -579,22 +539,70 @@ void k_mem_pool_free(struct k_mem_block *block)
k_sched_unlock();
}
void k_free(void *ptr)
/*
* Heap memory pool support
*/
#if (CONFIG_HEAP_MEM_POOL_SIZE > 0)
/*
* Case 1: Heap is defined using HEAP_MEM_POOL_SIZE configuration option.
*
* This module defines the heap memory pool and the _HEAP_MEM_POOL symbol
* that has the address of the associated memory pool struct.
*/
K_MEM_POOL_DEFINE(_heap_mem_pool, 64, CONFIG_HEAP_MEM_POOL_SIZE, 1, 4);
#define _HEAP_MEM_POOL (&_heap_mem_pool)
#else
/*
* Case 2: Heap is defined using HEAP_SIZE item type in MDEF.
*
* Sysgen defines the heap memory pool and the _heap_mem_pool_ptr variable
* that has the address of the associated memory pool struct. This module
* defines the _HEAP_MEM_POOL symbol as an alias for _heap_mem_pool_ptr.
*
* Note: If the MDEF does not define the heap memory pool k_malloc() will
* compile successfully, but will trigger a link error if it is used.
*/
extern struct k_mem_pool * const _heap_mem_pool_ptr;
#define _HEAP_MEM_POOL _heap_mem_pool_ptr
#endif /* CONFIG_HEAP_MEM_POOL_SIZE */
void *k_malloc(size_t size)
{
struct k_mem_block mem_block;
struct k_mem_block block;
__ASSERT(heap_mem_pool != NULL,
"Try to free a block in undefined heap\n");
/*
* get a block large enough to hold an initial (hidden) block
* descriptor, as well as the space the caller requested
*/
size += sizeof(struct k_mem_block);
if (k_mem_pool_alloc(_HEAP_MEM_POOL, &block, size, K_NO_WAIT) != 0) {
return NULL;
}
mem_block.pool_id = heap_mem_pool;
/* save the block descriptor info at the start of the actual block */
memcpy(block.data, &block, sizeof(struct k_mem_block));
/* Fetch the pointer returned by the pool API */
mem_block.addr_in_pool = *((void **) ((uint32_t *)ptr - 1));
mem_block.data = *((void **) ((uint32_t *)ptr - 1));
/* Further fetch the size asked from pool */
mem_block.req_size = *((uint32_t *)ptr - 2);
k_mem_pool_free(&mem_block);
/* return address of the user area part of the block to the caller */
return (char *)block.data + sizeof(struct k_mem_block);
}
SYS_INIT(init_static_pools, PRIMARY, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);
void k_free(void *ptr)
{
if (ptr != NULL) {
/* point to hidden block descriptor at start of block */
ptr = (char *)ptr - sizeof(struct k_mem_block);
/* return block to the heap memory pool */
k_mem_pool_free(ptr);
}
}

View file

@ -898,6 +898,11 @@ def kernel_main_c_pools():
pool_descriptors += "K_MEM_POOL_DEFINE(_k_mem_pool_obj_%s, %d, %d, %d, 4);\n" % \
(pool[0], min_block_size, max_block_size,
num_maximal_blocks)
if (heap_pos_in_pool_list != -1):
kernel_main_c_out(
"\nkmemory_pool_t _heap_mem_pool_ptr = " +
"&_k_mem_pool_obj__HEAP_MEM_POOL;\n")
kernel_main_c_out(pool_descriptors)