lib/os: Add sys_multi_heap utility
This is a simple wrapper allowing multiple sys_heap regions to be unified under a single allocation API. Sometimes apps need the ability to share multiple discontiguous regions in a single "heap", or to have memory of different "types" be allocated heuristically based on usage (e.g. cacheability, latency, power...). This allows a user-specified function to select the underlying memory to use for each application. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
This commit is contained in:
parent
3ad85f41b5
commit
85e96ff3ca
3 changed files with 220 additions and 0 deletions
143
include/sys/multi_heap.h
Normal file
143
include/sys/multi_heap.h
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
/* Copyright (c) 2021 Intel Corporation
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_INCLUDE_SYS_MULTI_HEAP_H_
|
||||||
|
#define ZEPHYR_INCLUDE_SYS_MULTI_HEAP_H_
|
||||||
|
|
||||||
|
#include <zephyr/types.h>
|
||||||
|
|
||||||
|
#define MAX_MULTI_HEAPS 8
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Multi-heap allocator
|
||||||
|
*
|
||||||
|
* A sys_multi_heap represents a single allocator made from multiple,
|
||||||
|
* separately managed pools of memory that must be accessed via a
|
||||||
|
* unified API. They can be discontiguous, and in many cases will be
|
||||||
|
* expected to have different capabilities (for example: latency,
|
||||||
|
* cacheability, cpu affinity, etc...)
|
||||||
|
*
|
||||||
|
* Allocation from the multiheap provides an opaque "configuration"
|
||||||
|
* value to specify requirements and heuristics to assist the choice
|
||||||
|
* in backend, which is then provided to a user-specified "choice"
|
||||||
|
* function whose job it is to select a heap based on information in
|
||||||
|
* the config specifier and runtime state (heap full state, etc...)
|
||||||
|
*/
|
||||||
|
struct sys_multi_heap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Multi-heap choice function
|
||||||
|
*
|
||||||
|
* This is a user-provided functions whose responsibility is selecting
|
||||||
|
* a specific sys_heap backend based on the opaque cfg value, which is
|
||||||
|
* specified by the user as an argument to sys_multi_heap_alloc(), and
|
||||||
|
* performing the allocation on behalf of the caller. The callback is
|
||||||
|
* free to choose any registered heap backend to perform the
|
||||||
|
* allocation, and may choose to pad the user-provided values as
|
||||||
|
* needed, and to use an aligned allocation where required by the
|
||||||
|
* specified configuration.
|
||||||
|
*
|
||||||
|
* NULL may be returned, which will cause the
|
||||||
|
* allocation to fail and a NULL reported to the calling code.
|
||||||
|
*
|
||||||
|
* @param cfg An opaque user-provided value. It may be interpreted in
|
||||||
|
* any way by the application
|
||||||
|
* @param align Alignment of requested memory (or zero for no alignment)
|
||||||
|
* @param size The user-specified allocation size in bytes
|
||||||
|
* @return A pointer to the allocated memory
|
||||||
|
*/
|
||||||
|
typedef void *(*sys_multi_heap_fn_t)(struct sys_multi_heap *mheap, void *cfg,
|
||||||
|
size_t align, size_t size);
|
||||||
|
|
||||||
|
struct sys_multi_heap {
|
||||||
|
int nheaps;
|
||||||
|
sys_multi_heap_fn_t choice;
|
||||||
|
struct sys_heap *heaps[MAX_MULTI_HEAPS];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize multi-heap
|
||||||
|
*
|
||||||
|
* Initialize a sys_multi_heap struct with the specified choice
|
||||||
|
* function. Note that individual heaps must be added later with
|
||||||
|
* sys_multi_heap_add_heap so that the heap bounds can be tracked by
|
||||||
|
* the multi heap code.
|
||||||
|
*
|
||||||
|
* @note In general a multiheap is likely to be instantiated
|
||||||
|
* semi-statically from system configuration (for example, via
|
||||||
|
* linker-provided bounds on available memory in different regions, or
|
||||||
|
* from devicetree definitions of hardware-provided addressible
|
||||||
|
* memory, etc...). The general expectation is that a soc- or
|
||||||
|
* board-level platform device will be initialized at system boot from
|
||||||
|
* these upstream configuration sources and not that an application
|
||||||
|
* will assemble a multi-heap on its own.
|
||||||
|
*
|
||||||
|
* @param heap A sys_multi_heap to initialize
|
||||||
|
* @param choice_fn A sys_multi_heap_fn_t callback used to select
|
||||||
|
* heaps at allocation time
|
||||||
|
*/
|
||||||
|
void sys_multi_heap_init(struct sys_multi_heap *heap,
|
||||||
|
sys_multi_heap_fn_t choice_fn);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add sys_heap to multi heap
|
||||||
|
*
|
||||||
|
* This adds a known sys_heap backend to an existing multi heap,
|
||||||
|
* allowing the multi heap internals to track the bounds of the heap
|
||||||
|
* and determine which heap (if any) from which a freed block was
|
||||||
|
* allocated.
|
||||||
|
*
|
||||||
|
* @param mheap A sys_multi_heap to which to add a heap
|
||||||
|
* @param heap The heap to add
|
||||||
|
*/
|
||||||
|
void sys_multi_heap_add_heap(struct sys_multi_heap *mheap, struct sys_heap *heap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocate memory from multi heap
|
||||||
|
*
|
||||||
|
* Just as for sys_heap_alloc(), allocates a block of memory of the
|
||||||
|
* specified size in bytes. Takes an opaque configuration pointer
|
||||||
|
* passed to the multi heap choice function, which is used by
|
||||||
|
* integration code to choose a heap backend.
|
||||||
|
*
|
||||||
|
* @param mheap Multi heap pointer
|
||||||
|
* @param cfg Opaque configuration parameter, as for sys_multi_heap_fn_t
|
||||||
|
* @param bytes Requested size of the allocation, in bytes
|
||||||
|
* @return A valid pointer to heap memory, or NULL if no memory is available
|
||||||
|
*/
|
||||||
|
void *sys_multi_heap_alloc(struct sys_multi_heap *mheap, void *cfg, size_t bytes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocate aligned memory from multi heap
|
||||||
|
*
|
||||||
|
* Just as for sys_multi_heap_alloc(), allocates a block of memory of
|
||||||
|
* the specified size in bytes. Takes an additional parameter
|
||||||
|
* specifying a power of two alignment, in bytes.
|
||||||
|
*
|
||||||
|
* @param mheap Multi heap pointer
|
||||||
|
* @param cfg Opaque configuration parameter, as for sys_multi_heap_fn_t
|
||||||
|
* @param align Power of two alignment for the returned pointer, in bytes
|
||||||
|
* @param bytes Requested size of the allocation, in bytes
|
||||||
|
* @return A valid pointer to heap memory, or NULL if no memory is available
|
||||||
|
*/
|
||||||
|
void *sys_multi_heap_aligned_alloc(struct sys_multi_heap *mheap,
|
||||||
|
void *cfg, size_t align, size_t bytes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free memory allocated from multi heap
|
||||||
|
*
|
||||||
|
* Returns the specified block, which must be the return value of a
|
||||||
|
* previously successful sys_multi_heap_alloc() or
|
||||||
|
* sys_multi_heap_aligned_alloc() call, to the heap backend from which
|
||||||
|
* it was allocated.
|
||||||
|
*
|
||||||
|
* Accepts NULL as a block parameter, which is specified to have no
|
||||||
|
* effect.
|
||||||
|
*
|
||||||
|
* @param mheap Multi heap pointer
|
||||||
|
* @param block Block to free
|
||||||
|
*/
|
||||||
|
void sys_multi_heap_free(struct sys_multi_heap *mheap, void *block);
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_INCLUDE_SYS_MULTI_HEAP_H_ */
|
|
@ -23,6 +23,7 @@ zephyr_sources(
|
||||||
heap.c
|
heap.c
|
||||||
heap-validate.c
|
heap-validate.c
|
||||||
bitarray.c
|
bitarray.c
|
||||||
|
multi_heap.c
|
||||||
)
|
)
|
||||||
|
|
||||||
zephyr_sources_ifdef(CONFIG_CBPRINTF_COMPLETE cbprintf_complete.c)
|
zephyr_sources_ifdef(CONFIG_CBPRINTF_COMPLETE cbprintf_complete.c)
|
||||||
|
|
76
lib/os/multi_heap.c
Normal file
76
lib/os/multi_heap.c
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
/* Copyright (c) 2021 Intel Corporation
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <sys/__assert.h>
|
||||||
|
#include <sys/util.h>
|
||||||
|
#include <sys/sys_heap.h>
|
||||||
|
#include <sys/multi_heap.h>
|
||||||
|
|
||||||
|
void sys_multi_heap_init(struct sys_multi_heap *heap, sys_multi_heap_fn_t choice_fn)
|
||||||
|
{
|
||||||
|
heap->nheaps = 0;
|
||||||
|
heap->choice = choice_fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sys_multi_heap_add_heap(struct sys_multi_heap *mheap, struct sys_heap *heap)
|
||||||
|
{
|
||||||
|
__ASSERT_NO_MSG(mheap->nheaps < ARRAY_SIZE(mheap->heaps));
|
||||||
|
|
||||||
|
mheap->heaps[mheap->nheaps++] = heap;
|
||||||
|
|
||||||
|
/* Now sort them in memory order, simple extraction sort */
|
||||||
|
for (int i = 0; i < mheap->nheaps; i++) {
|
||||||
|
void *swap;
|
||||||
|
int lowest = -1;
|
||||||
|
uintptr_t lowest_addr = UINTPTR_MAX;
|
||||||
|
|
||||||
|
for (int j = i; j < mheap->nheaps; j++) {
|
||||||
|
uintptr_t haddr = (uintptr_t)mheap->heaps[j]->heap;
|
||||||
|
|
||||||
|
if (haddr < lowest_addr) {
|
||||||
|
lowest = j;
|
||||||
|
lowest_addr = haddr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
swap = mheap->heaps[i];
|
||||||
|
mheap->heaps[i] = mheap->heaps[lowest];
|
||||||
|
mheap->heaps[lowest] = swap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *sys_multi_heap_alloc(struct sys_multi_heap *mheap, void *cfg, size_t bytes)
|
||||||
|
{
|
||||||
|
return mheap->choice(mheap, cfg, 0, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *sys_multi_heap_aligned_alloc(struct sys_multi_heap *mheap,
|
||||||
|
void *cfg, size_t align, size_t bytes)
|
||||||
|
{
|
||||||
|
return mheap->choice(mheap, cfg, align, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sys_multi_heap_free(struct sys_multi_heap *mheap, void *block)
|
||||||
|
{
|
||||||
|
uintptr_t haddr, baddr = (uintptr_t) block;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Search the heaps array to find the correct heap
|
||||||
|
*
|
||||||
|
* FIXME: just a linear search currently, as the list is
|
||||||
|
* always short for reasonable apps and this code is very
|
||||||
|
* quick. The array is stored in sorted order though, so a
|
||||||
|
* binary search based on the block address is the design
|
||||||
|
* goal.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < mheap->nheaps; i++) {
|
||||||
|
haddr = (uintptr_t)mheap->heaps[i]->heap;
|
||||||
|
if (baddr < haddr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now i stores the index of the heap after our target (even
|
||||||
|
* if it's invalid and our target is the last!)
|
||||||
|
*/
|
||||||
|
sys_heap_free(mheap->heaps[i-1], block);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue