From 85e96ff3ca362aa824ef7944fc667734feeeb78a Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Wed, 22 Sep 2021 06:53:42 -0700 Subject: [PATCH] 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 --- include/sys/multi_heap.h | 143 +++++++++++++++++++++++++++++++++++++++ lib/os/CMakeLists.txt | 1 + lib/os/multi_heap.c | 76 +++++++++++++++++++++ 3 files changed, 220 insertions(+) create mode 100644 include/sys/multi_heap.h create mode 100644 lib/os/multi_heap.c diff --git a/include/sys/multi_heap.h b/include/sys/multi_heap.h new file mode 100644 index 00000000000..5617ada6b1b --- /dev/null +++ b/include/sys/multi_heap.h @@ -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 + +#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_ */ diff --git a/lib/os/CMakeLists.txt b/lib/os/CMakeLists.txt index 144ac183ef0..bcf238b802c 100644 --- a/lib/os/CMakeLists.txt +++ b/lib/os/CMakeLists.txt @@ -23,6 +23,7 @@ zephyr_sources( heap.c heap-validate.c bitarray.c + multi_heap.c ) zephyr_sources_ifdef(CONFIG_CBPRINTF_COMPLETE cbprintf_complete.c) diff --git a/lib/os/multi_heap.c b/lib/os/multi_heap.c new file mode 100644 index 00000000000..0789a4c83ff --- /dev/null +++ b/lib/os/multi_heap.c @@ -0,0 +1,76 @@ +/* Copyright (c) 2021 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include + +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); +}