1 /*
2  * Copyright (c) 2019 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #ifndef ZEPHYR_INCLUDE_SYS_SYS_HEAP_H_
7 #define ZEPHYR_INCLUDE_SYS_SYS_HEAP_H_
8 
9 #include <stddef.h>
10 #include <stdbool.h>
11 #include <zephyr/types.h>
12 #include <zephyr/sys/mem_stats.h>
13 #include <zephyr/toolchain.h>
14 
15 #ifdef __cplusplus
16 extern "C" {
17 #endif
18 
19 /* Simple, fast heap implementation.
20  *
21  * A more or less conventional segregated fit allocator with
22  * power-of-two buckets.
23  *
24  * Excellent space efficiency.  Chunks can be split arbitrarily in 8
25  * byte units.  Overhead is only four bytes per allocated chunk (eight
26  * bytes for heaps >256kb or on 64 bit systems), plus a log2-sized
27  * array of 2-word bucket headers.  No coarse alignment restrictions
28  * on blocks, they can be split and merged (in units of 8 bytes)
29  * arbitrarily.
30  *
31  * Simple API.  Initialize at runtime with any blob of memory and not
32  * a macro-generated, carefully aligned static array.  Allocate and
33  * free by user pointer and not an opaque block handle.
34  *
35  * Good fragmentation resistance.  Freed blocks are always immediately
36  * merged with adjacent free blocks.  Allocations are attempted from a
37  * sample of the smallest bucket that might fit, falling back rapidly
38  * to the smallest block guaranteed to fit.  Split memory remaining in
39  * the chunk is always returned immediately to the heap for other
40  * allocation.
41  *
42  * Excellent performance with firmly bounded runtime.  All operations
43  * are constant time (though there is a search of the smallest bucket
44  * that has a compile-time-configurable upper bound, setting this to
45  * extreme values results in an effectively linear search of the
46  * list), objectively fast (~hundred instructions) and amenable to
47  * locked operation.
48  */
49 
50 /* Note: the init_mem/bytes fields are for the static initializer to
51  * have somewhere to put the arguments.  The actual heap metadata at
52  * runtime lives in the heap memory itself and this struct simply
53  * functions as an opaque pointer.  Would be good to clean this up and
54  * put the two values somewhere else, though it would make
55  * SYS_HEAP_DEFINE a little hairy to write.
56  */
57 struct sys_heap {
58 	struct z_heap *heap;
59 	void *init_mem;
60 	size_t init_bytes;
61 };
62 
63 struct z_heap_stress_result {
64 	uint32_t total_allocs;
65 	uint32_t successful_allocs;
66 	uint32_t total_frees;
67 	uint64_t accumulated_in_use_bytes;
68 };
69 
70 /**
71  * @defgroup low_level_heap_allocator Low Level Heap Allocator
72  * @ingroup heaps
73  * @{
74  */
75 
76 /**
77  * @brief Get the runtime statistics of a sys_heap
78  *
79  * @param heap Pointer to specified sys_heap
80  * @param stats Pointer to struct to copy statistics into
81  * @return -EINVAL if null pointers, otherwise 0
82  */
83 int sys_heap_runtime_stats_get(struct sys_heap *heap,
84 		struct sys_memory_stats *stats);
85 
86 /**
87  * @brief Reset the maximum heap usage.
88  *
89  * Set the statistic measuring the maximum number of allocated bytes to the
90  * current number of allocated bytes.
91  *
92  * @param heap Pointer to sys_heap
93  * @return -EINVAL if null pointer was passed, otherwise 0
94  */
95 int sys_heap_runtime_stats_reset_max(struct sys_heap *heap);
96 
97 /** @brief Initialize sys_heap
98  *
99  * Initializes a sys_heap struct to manage the specified memory.
100  *
101  * @param heap Heap to initialize
102  * @param mem Untyped pointer to unused memory
103  * @param bytes Size of region pointed to by @a mem
104  */
105 void sys_heap_init(struct sys_heap *heap, void *mem, size_t bytes);
106 
107 /** @brief Allocate memory from a sys_heap
108  *
109  * Returns a pointer to a block of unused memory in the heap.  This
110  * memory will not otherwise be used until it is freed with
111  * sys_heap_free().  If no memory can be allocated, NULL will be
112  * returned.  The allocated memory is guaranteed to have a starting
113  * address which is a multiple of sizeof(void *).  If a bigger alignment
114  * is necessary then sys_heap_aligned_alloc() should be used instead.
115  *
116  * @note The sys_heap implementation is not internally synchronized.
117  * No two sys_heap functions should operate on the same heap at the
118  * same time.  All locking must be provided by the user.
119  *
120  * @param heap Heap from which to allocate
121  * @param bytes Number of bytes requested
122  * @return Pointer to memory the caller can now use
123  */
124 void *sys_heap_alloc(struct sys_heap *heap, size_t bytes);
125 
126 /** @brief Allocate aligned memory from a sys_heap
127  *
128  * Behaves in all ways like sys_heap_alloc(), except that the returned
129  * memory (if available) will have a starting address in memory which
130  * is a multiple of the specified power-of-two alignment value in
131  * bytes.  With align=0 this behaves exactly like sys_heap_alloc().
132  * The resulting memory can be returned to the heap using sys_heap_free().
133  *
134  * @param heap Heap from which to allocate
135  * @param align Alignment in bytes, must be a power of two
136  * @param bytes Number of bytes requested
137  * @return Pointer to memory the caller can now use
138  */
139 void *sys_heap_aligned_alloc(struct sys_heap *heap, size_t align, size_t bytes);
140 
141 /** @brief Free memory into a sys_heap
142  *
143  * De-allocates a pointer to memory previously returned from
144  * sys_heap_alloc such that it can be used for other purposes.  The
145  * caller must not use the memory region after entry to this function.
146  *
147  * @note The sys_heap implementation is not internally synchronized.
148  * No two sys_heap functions should operate on the same heap at the
149  * same time.  All locking must be provided by the user.
150  *
151  * @param heap Heap to which to return the memory
152  * @param mem A pointer previously returned from sys_heap_alloc()
153  */
154 void sys_heap_free(struct sys_heap *heap, void *mem);
155 
156 /** @brief Expand the size of an existing allocation
157  *
158  * Returns a pointer to a new memory region with the same contents,
159  * but a different allocated size.  If the new allocation can be
160  * expanded in place, the pointer returned will be identical.
161  * Otherwise the data will be copies to a new block and the old one
162  * will be freed as per sys_heap_free().  If the specified size is
163  * smaller than the original, the block will be truncated in place and
164  * the remaining memory returned to the heap.  If the allocation of a
165  * new block fails, then NULL will be returned and the old block will
166  * not be freed or modified.
167  *
168  * @param heap Heap from which to allocate
169  * @param ptr Original pointer returned from a previous allocation
170  * @param align Alignment in bytes, must be a power of two
171  * @param bytes Number of bytes requested for the new block
172  * @return Pointer to memory the caller can now use, or NULL
173  */
174 void *sys_heap_aligned_realloc(struct sys_heap *heap, void *ptr,
175 			       size_t align, size_t bytes);
176 
177 #define sys_heap_realloc(heap, ptr, bytes) \
178 	sys_heap_aligned_realloc(heap, ptr, 0, bytes)
179 
180 /** @brief Return allocated memory size
181  *
182  * Returns the size, in bytes, of a block returned from a successful
183  * sys_heap_alloc() or sys_heap_alloc_aligned() call.  The value
184  * returned is the size of the heap-managed memory, which may be
185  * larger than the number of bytes requested due to allocation
186  * granularity.  The heap code is guaranteed to make no access to this
187  * region of memory until a subsequent sys_heap_free() on the same
188  * pointer.
189  *
190  * @param heap Heap containing the block
191  * @param mem Pointer to memory allocated from this heap
192  * @return Size in bytes of the memory region
193  */
194 size_t sys_heap_usable_size(struct sys_heap *heap, void *mem);
195 
196 /** @brief Validate heap integrity
197  *
198  * Validates the internal integrity of a sys_heap.  Intended for unit
199  * test and validation code, though potentially useful as a user API
200  * for applications with complicated runtime reliability requirements.
201  * Note: this cannot catch every possible error, but if it returns
202  * true then the heap is in a consistent state and can correctly
203  * handle any sys_heap_alloc() request and free any live pointer
204  * returned from a previous allocation.
205  *
206  * @param heap Heap to validate
207  * @return true, if the heap is valid, otherwise false
208  */
209 #ifdef CONFIG_SYS_HEAP_VALIDATE
210 bool sys_heap_validate(struct sys_heap *heap);
211 #else
sys_heap_validate(struct sys_heap * heap)212 static inline bool sys_heap_validate(struct sys_heap *heap)
213 {
214 	ARG_UNUSED(heap);
215 	return true;
216 }
217 #endif
218 
219 /** @brief sys_heap stress test rig
220  *
221  * Test rig for heap allocation validation.  This will loop for @a
222  * op_count cycles, in each iteration making a random choice to
223  * allocate or free a pointer of randomized (power law) size based on
224  * heuristics designed to keep the heap in a state where it is near @a
225  * target_percent full.  Allocation and free operations are provided
226  * by the caller as callbacks (i.e. this can in theory test any heap).
227  * Results, including counts of frees and successful/unsuccessful
228  * allocations, are returned via the @a result struct.
229  *
230  * @param alloc_fn Callback to perform an allocation.  Passes back the @a
231  *              arg parameter as a context handle.
232  * @param free_fn Callback to perform a free of a pointer returned from
233  *             @a alloc.  Passes back the @a arg parameter as a
234  *             context handle.
235  * @param arg Context handle to pass back to the callbacks
236  * @param total_bytes Size of the byte array the heap was initialized in
237  * @param op_count How many iterations to test
238  * @param scratch_mem A pointer to scratch memory to be used by the
239  *                    test.  Should be about 1/2 the size of the heap
240  *                    for tests that need to stress fragmentation.
241  * @param scratch_bytes Size of the memory pointed to by @a scratch_mem
242  * @param target_percent Percentage fill value (1-100) to which the
243  *                       random allocation choices will seek.  High
244  *                       values will result in significant allocation
245  *                       failures and a very fragmented heap.
246  * @param result Struct into which to store test results.
247  */
248 void sys_heap_stress(void *(*alloc_fn)(void *arg, size_t bytes),
249 		     void (*free_fn)(void *arg, void *p),
250 		     void *arg, size_t total_bytes,
251 		     uint32_t op_count,
252 		     void *scratch_mem, size_t scratch_bytes,
253 		     int target_percent,
254 		     struct z_heap_stress_result *result);
255 
256 /** @brief Print heap internal structure information to the console
257  *
258  * Print information on the heap structure such as its size, chunk buckets,
259  * chunk list and some statistics for debugging purpose.
260  *
261  * @param heap Heap to print information about
262  * @param dump_chunks True to print the entire heap chunk list
263  */
264 void sys_heap_print_info(struct sys_heap *heap, bool dump_chunks);
265 
266 /** @brief Save the heap pointer
267  *
268  * The heap pointer is saved into an internal array, if there is space.
269  *
270  * @param heap Heap to save
271  * @return -EINVAL if null pointer or array is full, otherwise 0
272  */
273 int sys_heap_array_save(struct sys_heap *heap);
274 
275 /** @brief Get the array of saved heap pointers
276  *
277  * Returns the pointer to the array of heap pointers.
278  *
279  * @param heap Heap array
280  * @return -EINVAL if null pointer, otherwise number of saved pointers
281  */
282 int sys_heap_array_get(struct sys_heap ***heap);
283 
284 /**
285  * @}
286  */
287 
288 #ifdef __cplusplus
289 }
290 #endif
291 
292 #endif /* ZEPHYR_INCLUDE_SYS_SYS_HEAP_H_ */
293