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 
13 /* Simple, fast heap implementation.
14  *
15  * A more or less conventional segregated fit allocator with
16  * power-of-two buckets.
17  *
18  * Excellent space efficiency.  Chunks can be split arbitrarily in 8
19  * byte units.  Overhead is only four bytes per allocated chunk (eight
20  * bytes for heaps >256kb or on 64 bit systems), plus a log2-sized
21  * array of 2-word bucket headers.  No coarse alignment restrictions
22  * on blocks, they can be split and merged (in units of 8 bytes)
23  * arbitrarily.
24  *
25  * Simple API.  Initialize at runtime with any blob of memory and not
26  * a macro-generated, carefully aligned static array.  Allocate and
27  * free by user pointer and not an opaque block handle.
28  *
29  * Good fragmentation resistance.  Freed blocks are always immediately
30  * merged with adjacent free blocks.  Allocations are attempted from a
31  * sample of the smallest bucket that might fit, falling back rapidly
32  * to the smallest block guaranteed to fit.  Split memory remaining in
33  * the chunk is always returned immediately to the heap for other
34  * allocation.
35  *
36  * Excellent performance with firmly bounded runtime.  All operations
37  * are constant time (though there is a search of the smallest bucket
38  * that has a compile-time-configurable upper bound, setting this to
39  * extreme values results in an effectively linear search of the
40  * list), objectively fast (~hundred instructions) and and amenable to
41  * locked operation.
42  */
43 
44 /* Note: the init_mem/bytes fields are for the static initializer to
45  * have somewhere to put the arguments.  The actual heap metadata at
46  * runtime lives in the heap memory itself and this struct simply
47  * functions as an opaque pointer.  Would be good to clean this up and
48  * put the two values somewhere else, though it would make
49  * SYS_HEAP_DEFINE a little hairy to write.
50  */
51 struct sys_heap {
52 	struct z_heap *heap;
53 	void *init_mem;
54 	size_t init_bytes;
55 };
56 
57 struct z_heap_stress_result {
58 	uint32_t total_allocs;
59 	uint32_t successful_allocs;
60 	uint32_t total_frees;
61 	uint64_t accumulated_in_use_bytes;
62 };
63 
64 /** @brief Initialize sys_heap
65  *
66  * Initializes a sys_heap struct to manage the specified memory.
67  *
68  * @param heap Heap to initialize
69  * @param mem Untyped pointer to unused memory
70  * @param bytes Size of region pointed to by @a mem
71  */
72 void sys_heap_init(struct sys_heap *heap, void *mem, size_t bytes);
73 
74 /** @brief Allocate memory from a sys_heap
75  *
76  * Returns a pointer to a block of unused memory in the heap.  This
77  * memory will not otherwise be used until it is freed with
78  * sys_heap_free().  If no memory can be allocated, NULL will be
79  * returned.  The allocated memory is guaranteed to have a starting
80  * address which is a multiple of sizeof(void *).  If a bigger alignment
81  * is necessary then sys_heap_aligned_alloc() should be used instead.
82  *
83  * @note The sys_heap implementation is not internally synchronized.
84  * No two sys_heap functions should operate on the same heap at the
85  * same time.  All locking must be provided by the user.
86  *
87  * @param heap Heap from which to allocate
88  * @param bytes Number of bytes requested
89  * @return Pointer to memory the caller can now use
90  */
91 void *sys_heap_alloc(struct sys_heap *heap, size_t bytes);
92 
93 /** @brief Allocate aligned memory from a sys_heap
94  *
95  * Behaves in all ways like sys_heap_alloc(), except that the returned
96  * memory (if available) will have a starting address in memory which
97  * is a multiple of the specified power-of-two alignment value in
98  * bytes.  With align=0 this behaves exactly like sys_heap_alloc().
99  * The resulting memory can be returned to the heap using sys_heap_free().
100  *
101  * @param heap Heap from which to allocate
102  * @param align Alignment in bytes, must be a power of two
103  * @param bytes Number of bytes requested
104  * @return Pointer to memory the caller can now use
105  */
106 void *sys_heap_aligned_alloc(struct sys_heap *heap, size_t align, size_t bytes);
107 
108 /** @brief Free memory into a sys_heap
109  *
110  * De-allocates a pointer to memory previously returned from
111  * sys_heap_alloc such that it can be used for other purposes.  The
112  * caller must not use the memory region after entry to this function.
113  *
114  * @note The sys_heap implementation is not internally synchronized.
115  * No two sys_heap functions should operate on the same heap at the
116  * same time.  All locking must be provided by the user.
117  *
118  * @param heap Heap to which to return the memory
119  * @param mem A pointer previously returned from sys_heap_alloc()
120  */
121 void sys_heap_free(struct sys_heap *heap, void *mem);
122 
123 /** @brief Expand the size of an existing allocation
124  *
125  * Returns a pointer to a new memory region with the same contents,
126  * but a different allocated size.  If the new allocation can be
127  * expanded in place, the pointer returned will be identical.
128  * Otherwise the data will be copies to a new block and the old one
129  * will be freed as per sys_heap_free().  If the specified size is
130  * smaller than the original, the block will be truncated in place and
131  * the remaining memory returned to the heap.  If the allocation of a
132  * new block fails, then NULL will be returned and the old block will
133  * not be freed or modified.
134  *
135  * @note The return of a NULL on failure is a different behavior than
136  * POSIX realloc(), which specifies that the original pointer will be
137  * returned (i.e. it is not possible to safely detect realloc()
138  * failure in POSIX, but it is here).
139  *
140  * @param heap Heap from which to allocate
141  * @param ptr Original pointer returned from a previous allocation
142  * @param align Alignment in bytes, must be a power of two
143  * @param bytes Number of bytes requested for the new block
144  * @return Pointer to memory the caller can now use, or NULL
145  */
146 void *sys_heap_aligned_realloc(struct sys_heap *heap, void *ptr,
147 			       size_t align, size_t bytes);
148 
149 #define sys_heap_realloc(heap, ptr, bytes) \
150 	sys_heap_aligned_realloc(heap, ptr, 0, bytes)
151 
152 /** @brief Validate heap integrity
153  *
154  * Validates the internal integrity of a sys_heap.  Intended for unit
155  * test and validation code, though potentially useful as a user API
156  * for applications with complicated runtime reliability requirements.
157  * Note: this cannot catch every possible error, but if it returns
158  * true then the heap is in a consistent state and can correctly
159  * handle any sys_heap_alloc() request and free any live pointer
160  * returned from a previou allocation.
161  *
162  * @param heap Heap to validate
163  * @return true, if the heap is valid, otherwise false
164  */
165 bool sys_heap_validate(struct sys_heap *heap);
166 
167 /** @brief sys_heap stress test rig
168  *
169  * Test rig for heap allocation validation.  This will loop for @a
170  * op_count cycles, in each iteration making a random choice to
171  * allocate or free a pointer of randomized (power law) size based on
172  * heuristics designed to keep the heap in a state where it is near @a
173  * target_percent full.  Allocation and free operations are provided
174  * by the caller as callbacks (i.e. this can in theory test any heap).
175  * Results, including counts of frees and successful/unsuccessful
176  * allocations, are returnewd via the @result struct.
177  *
178  * @param alloc_fn Callback to perform an allocation.  Passes back the @a
179  *              arg parameter as a context handle.
180  * @param free_fn Callback to perform a free of a pointer returned from
181  *             @a alloc.  Passes back the @a arg parameter as a
182  *             context handle.
183  * @param arg Context handle to pass back to the callbacks
184  * @param total_bytes Size of the byte array the heap was initialized in
185  * @param op_count How many iterations to test
186  * @param scratch_mem A pointer to scratch memory to be used by the
187  *                    test.  Should be about 1/2 the size of the heap
188  *                    for tests that need to stress fragmentation.
189  * @param scratch_bytes Size of the memory pointed to by @a scratch_mem
190  * @param target_percent Percentage fill value (1-100) to which the
191  *                       random allocation choices will seek.  High
192  *                       values will result in significant allocation
193  *                       failures and a very fragmented heap.
194  * @param result Struct into which to store test results.
195  */
196 void sys_heap_stress(void *(*alloc_fn)(void *arg, size_t bytes),
197 		     void (*free_fn)(void *arg, void *p),
198 		     void *arg, size_t total_bytes,
199 		     uint32_t op_count,
200 		     void *scratch_mem, size_t scratch_bytes,
201 		     int target_percent,
202 		     struct z_heap_stress_result *result);
203 
204 /** @brief Print heap internal structure information to the console
205  *
206  * Print information on the heap structure such as its size, chunk buckets,
207  * chunk list and some statistics for debugging purpose.
208  *
209  * @param heap Heap to print information about
210  * @param dump_chunks True to print the entire heap chunk list
211  */
212 void sys_heap_print_info(struct sys_heap *heap, bool dump_chunks);
213 
214 #endif /* ZEPHYR_INCLUDE_SYS_SYS_HEAP_H_ */
215