Skip to content

Commit 85e96ff

Browse files
Andy Rossnashif
authored andcommitted
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>
1 parent 3ad85f4 commit 85e96ff

File tree

3 files changed

+220
-0
lines changed

3 files changed

+220
-0
lines changed

include/sys/multi_heap.h

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/* Copyright (c) 2021 Intel Corporation
2+
* SPDX-License-Identifier: Apache-2.0
3+
*/
4+
5+
#ifndef ZEPHYR_INCLUDE_SYS_MULTI_HEAP_H_
6+
#define ZEPHYR_INCLUDE_SYS_MULTI_HEAP_H_
7+
8+
#include <zephyr/types.h>
9+
10+
#define MAX_MULTI_HEAPS 8
11+
12+
/**
13+
* @brief Multi-heap allocator
14+
*
15+
* A sys_multi_heap represents a single allocator made from multiple,
16+
* separately managed pools of memory that must be accessed via a
17+
* unified API. They can be discontiguous, and in many cases will be
18+
* expected to have different capabilities (for example: latency,
19+
* cacheability, cpu affinity, etc...)
20+
*
21+
* Allocation from the multiheap provides an opaque "configuration"
22+
* value to specify requirements and heuristics to assist the choice
23+
* in backend, which is then provided to a user-specified "choice"
24+
* function whose job it is to select a heap based on information in
25+
* the config specifier and runtime state (heap full state, etc...)
26+
*/
27+
struct sys_multi_heap;
28+
29+
/**
30+
* @brief Multi-heap choice function
31+
*
32+
* This is a user-provided functions whose responsibility is selecting
33+
* a specific sys_heap backend based on the opaque cfg value, which is
34+
* specified by the user as an argument to sys_multi_heap_alloc(), and
35+
* performing the allocation on behalf of the caller. The callback is
36+
* free to choose any registered heap backend to perform the
37+
* allocation, and may choose to pad the user-provided values as
38+
* needed, and to use an aligned allocation where required by the
39+
* specified configuration.
40+
*
41+
* NULL may be returned, which will cause the
42+
* allocation to fail and a NULL reported to the calling code.
43+
*
44+
* @param cfg An opaque user-provided value. It may be interpreted in
45+
* any way by the application
46+
* @param align Alignment of requested memory (or zero for no alignment)
47+
* @param size The user-specified allocation size in bytes
48+
* @return A pointer to the allocated memory
49+
*/
50+
typedef void *(*sys_multi_heap_fn_t)(struct sys_multi_heap *mheap, void *cfg,
51+
size_t align, size_t size);
52+
53+
struct sys_multi_heap {
54+
int nheaps;
55+
sys_multi_heap_fn_t choice;
56+
struct sys_heap *heaps[MAX_MULTI_HEAPS];
57+
};
58+
59+
/**
60+
* @brief Initialize multi-heap
61+
*
62+
* Initialize a sys_multi_heap struct with the specified choice
63+
* function. Note that individual heaps must be added later with
64+
* sys_multi_heap_add_heap so that the heap bounds can be tracked by
65+
* the multi heap code.
66+
*
67+
* @note In general a multiheap is likely to be instantiated
68+
* semi-statically from system configuration (for example, via
69+
* linker-provided bounds on available memory in different regions, or
70+
* from devicetree definitions of hardware-provided addressible
71+
* memory, etc...). The general expectation is that a soc- or
72+
* board-level platform device will be initialized at system boot from
73+
* these upstream configuration sources and not that an application
74+
* will assemble a multi-heap on its own.
75+
*
76+
* @param heap A sys_multi_heap to initialize
77+
* @param choice_fn A sys_multi_heap_fn_t callback used to select
78+
* heaps at allocation time
79+
*/
80+
void sys_multi_heap_init(struct sys_multi_heap *heap,
81+
sys_multi_heap_fn_t choice_fn);
82+
83+
/**
84+
* @brief Add sys_heap to multi heap
85+
*
86+
* This adds a known sys_heap backend to an existing multi heap,
87+
* allowing the multi heap internals to track the bounds of the heap
88+
* and determine which heap (if any) from which a freed block was
89+
* allocated.
90+
*
91+
* @param mheap A sys_multi_heap to which to add a heap
92+
* @param heap The heap to add
93+
*/
94+
void sys_multi_heap_add_heap(struct sys_multi_heap *mheap, struct sys_heap *heap);
95+
96+
/**
97+
* @brief Allocate memory from multi heap
98+
*
99+
* Just as for sys_heap_alloc(), allocates a block of memory of the
100+
* specified size in bytes. Takes an opaque configuration pointer
101+
* passed to the multi heap choice function, which is used by
102+
* integration code to choose a heap backend.
103+
*
104+
* @param mheap Multi heap pointer
105+
* @param cfg Opaque configuration parameter, as for sys_multi_heap_fn_t
106+
* @param bytes Requested size of the allocation, in bytes
107+
* @return A valid pointer to heap memory, or NULL if no memory is available
108+
*/
109+
void *sys_multi_heap_alloc(struct sys_multi_heap *mheap, void *cfg, size_t bytes);
110+
111+
/**
112+
* @brief Allocate aligned memory from multi heap
113+
*
114+
* Just as for sys_multi_heap_alloc(), allocates a block of memory of
115+
* the specified size in bytes. Takes an additional parameter
116+
* specifying a power of two alignment, in bytes.
117+
*
118+
* @param mheap Multi heap pointer
119+
* @param cfg Opaque configuration parameter, as for sys_multi_heap_fn_t
120+
* @param align Power of two alignment for the returned pointer, in bytes
121+
* @param bytes Requested size of the allocation, in bytes
122+
* @return A valid pointer to heap memory, or NULL if no memory is available
123+
*/
124+
void *sys_multi_heap_aligned_alloc(struct sys_multi_heap *mheap,
125+
void *cfg, size_t align, size_t bytes);
126+
127+
/**
128+
* @brief Free memory allocated from multi heap
129+
*
130+
* Returns the specified block, which must be the return value of a
131+
* previously successful sys_multi_heap_alloc() or
132+
* sys_multi_heap_aligned_alloc() call, to the heap backend from which
133+
* it was allocated.
134+
*
135+
* Accepts NULL as a block parameter, which is specified to have no
136+
* effect.
137+
*
138+
* @param mheap Multi heap pointer
139+
* @param block Block to free
140+
*/
141+
void sys_multi_heap_free(struct sys_multi_heap *mheap, void *block);
142+
143+
#endif /* ZEPHYR_INCLUDE_SYS_MULTI_HEAP_H_ */

lib/os/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ zephyr_sources(
2323
heap.c
2424
heap-validate.c
2525
bitarray.c
26+
multi_heap.c
2627
)
2728

2829
zephyr_sources_ifdef(CONFIG_CBPRINTF_COMPLETE cbprintf_complete.c)

lib/os/multi_heap.c

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/* Copyright (c) 2021 Intel Corporation
2+
* SPDX-License-Identifier: Apache-2.0
3+
*/
4+
#include <sys/__assert.h>
5+
#include <sys/util.h>
6+
#include <sys/sys_heap.h>
7+
#include <sys/multi_heap.h>
8+
9+
void sys_multi_heap_init(struct sys_multi_heap *heap, sys_multi_heap_fn_t choice_fn)
10+
{
11+
heap->nheaps = 0;
12+
heap->choice = choice_fn;
13+
}
14+
15+
void sys_multi_heap_add_heap(struct sys_multi_heap *mheap, struct sys_heap *heap)
16+
{
17+
__ASSERT_NO_MSG(mheap->nheaps < ARRAY_SIZE(mheap->heaps));
18+
19+
mheap->heaps[mheap->nheaps++] = heap;
20+
21+
/* Now sort them in memory order, simple extraction sort */
22+
for (int i = 0; i < mheap->nheaps; i++) {
23+
void *swap;
24+
int lowest = -1;
25+
uintptr_t lowest_addr = UINTPTR_MAX;
26+
27+
for (int j = i; j < mheap->nheaps; j++) {
28+
uintptr_t haddr = (uintptr_t)mheap->heaps[j]->heap;
29+
30+
if (haddr < lowest_addr) {
31+
lowest = j;
32+
lowest_addr = haddr;
33+
}
34+
}
35+
swap = mheap->heaps[i];
36+
mheap->heaps[i] = mheap->heaps[lowest];
37+
mheap->heaps[lowest] = swap;
38+
}
39+
}
40+
41+
void *sys_multi_heap_alloc(struct sys_multi_heap *mheap, void *cfg, size_t bytes)
42+
{
43+
return mheap->choice(mheap, cfg, 0, bytes);
44+
}
45+
46+
void *sys_multi_heap_aligned_alloc(struct sys_multi_heap *mheap,
47+
void *cfg, size_t align, size_t bytes)
48+
{
49+
return mheap->choice(mheap, cfg, align, bytes);
50+
}
51+
52+
void sys_multi_heap_free(struct sys_multi_heap *mheap, void *block)
53+
{
54+
uintptr_t haddr, baddr = (uintptr_t) block;
55+
int i;
56+
57+
/* Search the heaps array to find the correct heap
58+
*
59+
* FIXME: just a linear search currently, as the list is
60+
* always short for reasonable apps and this code is very
61+
* quick. The array is stored in sorted order though, so a
62+
* binary search based on the block address is the design
63+
* goal.
64+
*/
65+
for (i = 0; i < mheap->nheaps; i++) {
66+
haddr = (uintptr_t)mheap->heaps[i]->heap;
67+
if (baddr < haddr) {
68+
break;
69+
}
70+
}
71+
72+
/* Now i stores the index of the heap after our target (even
73+
* if it's invalid and our target is the last!)
74+
*/
75+
sys_heap_free(mheap->heaps[i-1], block);
76+
}

0 commit comments

Comments
 (0)