1 /*
2  * Copyright (c) 2021 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  *
10  * @brief Memory Blocks Allocator
11  */
12 
13 #ifndef ZEPHYR_INCLUDE_SYS_MEM_BLOCKS_H_
14 #define ZEPHYR_INCLUDE_SYS_MEM_BLOCKS_H_
15 
16 #ifdef __cplusplus
17 extern "C" {
18 #endif
19 
20 #include <stddef.h>
21 #include <stdint.h>
22 
23 #include <zephyr/kernel.h>
24 #include <zephyr/math/ilog2.h>
25 #include <zephyr/sys/bitarray.h>
26 #include <zephyr/sys/mem_stats.h>
27 
28 #define MAX_MULTI_ALLOCATORS 8
29 
30 /**
31  * @defgroup mem_blocks_apis Memory Blocks APIs
32  * @ingroup memory_management
33  * @{
34  */
35 
36 /**
37  * @brief Memory Blocks Allocator
38  */
39 struct sys_mem_blocks;
40 
41 /**
42  * @brief Multi Memory Blocks Allocator
43  */
44 struct sys_multi_mem_blocks;
45 
46 /**
47  * @typedef sys_mem_blocks_t
48  *
49  * @brief Memory Blocks Allocator
50  */
51 typedef struct sys_mem_blocks sys_mem_blocks_t;
52 
53 /**
54  * @typedef sys_multi_mem_blocks_t
55  *
56  * @brief Multi Memory Blocks Allocator
57  */
58 typedef struct sys_multi_mem_blocks sys_multi_mem_blocks_t;
59 
60 /**
61  * @brief Multi memory blocks allocator choice function
62  *
63  * This is a user-provided functions whose responsibility is selecting
64  * a specific memory blocks allocator based on the opaque cfg value,
65  * which is specified by the user as an argument to
66  * sys_multi_mem_blocks_alloc(). The callback returns a pointer to
67  * the chosen allocator where the allocation is performed.
68  *
69  * NULL may be returned, which will cause the
70  * allocation to fail and a -EINVAL reported to the calling code.
71  *
72  * @param group Multi memory blocks allocator structure.
73  * @param cfg   An opaque user-provided value. It may be interpreted in
74  *              any way by the application.
75  *
76  * @return A pointer to the chosen allocator, or NULL if none is chosen.
77  */
78 typedef sys_mem_blocks_t *(*sys_multi_mem_blocks_choice_fn_t)
79 	(struct sys_multi_mem_blocks *group, void *cfg);
80 
81 /**
82  * @cond INTERNAL_HIDDEN
83  */
84 
85 struct sys_mem_blocks_info {
86 	uint32_t num_blocks;       /* Total number of blocks */
87 	uint8_t  blk_sz_shift;     /* Bit shift for block size */
88 #ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS
89 	uint32_t used_blocks;      /* Current number of blocks in use */
90 	uint32_t max_used_blocks;  /* Maximum number of blocks in use */
91 #endif
92 };
93 
94 struct sys_mem_blocks {
95 	struct sys_mem_blocks_info  info;
96 
97 	/* Memory block buffer */
98 	uint8_t *buffer;
99 
100 	/* Bitmap of allocated blocks */
101 	sys_bitarray_t *bitmap;
102 
103 #ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS
104 	/* Spinlock guarding access to memory block internals */
105 	struct k_spinlock  lock;
106 #endif
107 #ifdef CONFIG_OBJ_CORE_SYS_MEM_BLOCKS
108 	struct k_obj_core obj_core;
109 #endif
110 };
111 
112 struct sys_multi_mem_blocks {
113 	/* Number of allocators in this group */
114 	int num_allocators;
115 	sys_multi_mem_blocks_choice_fn_t choice_fn;
116 	sys_mem_blocks_t *allocators[MAX_MULTI_ALLOCATORS];
117 };
118 
119 /**
120  * @brief Create a memory block object with a providing backing buffer.
121  *
122  * @param name     Name of the memory block object.
123  * @param blk_sz   Size of each memory block (in bytes, power of 2).
124  * @param num_blks Total number of memory blocks.
125  * @param buf      Backing buffer of type uint8_t.
126  * @param mbmod    Modifier to the memory block struct
127  */
128 #define _SYS_MEM_BLOCKS_DEFINE_WITH_EXT_BUF(name, blk_sz, num_blks, buf, mbmod) \
129 	_SYS_BITARRAY_DEFINE(_sys_mem_blocks_bitmap_##name,		\
130 			     num_blks, mbmod);				\
131 	mbmod struct sys_mem_blocks name = {                            \
132 		.info = {num_blks, ilog2(blk_sz)},                      \
133 		.buffer = buf,						\
134 		.bitmap = &_sys_mem_blocks_bitmap_##name,		\
135 	};                                                              \
136 	STRUCT_SECTION_ITERABLE_ALTERNATE(sys_mem_blocks_ptr,           \
137 					  sys_mem_blocks *,             \
138 					  __##name##_ptr) = &name;      \
139 	LINKER_KEEP(__##name##_ptr);
140 
141 /**
142  * @brief Create a memory block object with a new backing buffer.
143  *
144  * @param name     Name of the memory block object.
145  * @param blk_sz   Size of each memory block (in bytes, power of 2).
146  * @param num_blks Total number of memory blocks.
147  * @param balign   Alignment of the memory block buffer (power of 2).
148  * @param mbmod    Modifier to the memory block struct
149  */
150 #define _SYS_MEM_BLOCKS_DEFINE(name, blk_sz, num_blks, balign, mbmod)	\
151 	mbmod uint8_t __noinit_named(sys_mem_blocks_buf_##name)		\
152 		__aligned(WB_UP(balign))				\
153 		_sys_mem_blocks_buf_##name[num_blks * WB_UP(blk_sz)];	\
154 	_SYS_MEM_BLOCKS_DEFINE_WITH_EXT_BUF(name, blk_sz, num_blks,	\
155 					   _sys_mem_blocks_buf_##name,	\
156 					   mbmod);
157 
158 /**
159  * INTERNAL_HIDDEN @endcond
160  */
161 
162 /**
163  * @brief Create a memory block object with a new backing buffer.
164  *
165  * @param name      Name of the memory block object.
166  * @param blk_sz    Size of each memory block (in bytes).
167  * @param num_blks  Total number of memory blocks.
168  * @param buf_align Alignment of the memory block buffer (power of 2).
169  */
170 #define SYS_MEM_BLOCKS_DEFINE(name, blk_sz, num_blks, buf_align) \
171 	_SYS_MEM_BLOCKS_DEFINE(name, blk_sz, num_blks, buf_align,)
172 
173 /**
174  * @brief Create a static memory block object with a new backing buffer.
175  *
176  * @param name      Name of the memory block object.
177  * @param blk_sz    Size of each memory block (in bytes).
178  * @param num_blks  Total number of memory blocks.
179  * @param buf_align Alignment of the memory block buffer (power of 2).
180  */
181 #define SYS_MEM_BLOCKS_DEFINE_STATIC(name, blk_sz, num_blks, buf_align) \
182 	_SYS_MEM_BLOCKS_DEFINE(name, blk_sz, num_blks, buf_align, static)
183 
184 
185 /**
186  * @brief Create a memory block object with a providing backing buffer.
187  *
188  * @param name     Name of the memory block object.
189  * @param blk_sz   Size of each memory block (in bytes).
190  * @param num_blks Total number of memory blocks.
191  * @param buf      Backing buffer of type uint8_t.
192  */
193 #define SYS_MEM_BLOCKS_DEFINE_WITH_EXT_BUF(name, blk_sz, num_blks, buf) \
194 	_SYS_MEM_BLOCKS_DEFINE_WITH_EXT_BUF(name, blk_sz, num_blks, buf,)
195 
196 /**
197  * @brief Create a static memory block object with a providing backing buffer.
198  *
199  * @param name     Name of the memory block object.
200  * @param blk_sz   Size of each memory block (in bytes).
201  * @param num_blks Total number of memory blocks.
202  * @param buf      Backing buffer of type uint8_t.
203  */
204 #define SYS_MEM_BLOCKS_DEFINE_STATIC_WITH_EXT_BUF(name, blk_sz, num_blks, buf) \
205 	_SYS_MEM_BLOCKS_DEFINE_WITH_EXT_BUF(name, blk_sz, num_blks, buf, static)
206 
207 /**
208  * @brief Allocate multiple memory blocks
209  *
210  * Allocate multiple memory blocks, and place their pointers into
211  * the output array.
212  *
213  * @param[in]  mem_block  Pointer to memory block object.
214  * @param[in]  count      Number of blocks to allocate.
215  * @param[out] out_blocks Output array to be populated by pointers to
216  *                        the memory blocks. It must have at least
217  *                        @p count elements.
218  *
219  * @retval 0       Successful
220  * @retval -EINVAL Invalid argument supplied.
221  * @retval -ENOMEM Not enough blocks for allocation.
222  */
223 int sys_mem_blocks_alloc(sys_mem_blocks_t *mem_block, size_t count,
224 			 void **out_blocks);
225 
226 /**
227  * @brief Allocate a contiguous set of memory blocks
228  *
229  * Allocate multiple memory blocks, and place their pointers into
230  * the output array.
231  *
232  * @param[in]  mem_block Pointer to memory block object.
233  * @param[in]  count     Number of blocks to allocate.
234  * @param[out] out_block Output pointer to the start of the allocated block set
235  *
236  * @retval 0       Successful
237  * @retval -EINVAL Invalid argument supplied.
238  * @retval -ENOMEM Not enough contiguous blocks for allocation.
239  */
240 int sys_mem_blocks_alloc_contiguous(sys_mem_blocks_t *mem_block, size_t count,
241 				   void **out_block);
242 
243 /**
244  * @brief Force allocation of a specified blocks in a memory block object
245  *
246  * Allocate a specified blocks in a memory block object.
247  * Note: use caution when mixing sys_mem_blocks_get and sys_mem_blocks_alloc,
248  * allocation may take any of the free memory space
249  *
250  *
251  * @param[in]  mem_block  Pointer to memory block object.
252  * @param[in]  in_block   Address of the first required block to allocate
253  * @param[in]  count      Number of blocks to allocate.
254  *
255  * @retval 0       Successful
256  * @retval -EINVAL Invalid argument supplied.
257  * @retval -ENOMEM Some of blocks are taken and cannot be allocated
258  */
259 int sys_mem_blocks_get(sys_mem_blocks_t *mem_block, void *in_block, size_t count);
260 
261 /**
262  * @brief check if the region is free
263  *
264  * @param[in]  mem_block  Pointer to memory block object.
265  * @param[in]  in_block   Address of the first block to check
266  * @param[in]  count      Number of blocks to check.
267  *
268  * @retval 1 All memory blocks are free
269  * @retval 0 At least one of the memory blocks is taken
270  */
271 int sys_mem_blocks_is_region_free(sys_mem_blocks_t *mem_block, void *in_block, size_t count);
272 
273 /**
274  * @brief Free multiple memory blocks
275  *
276  * Free multiple memory blocks according to the array of memory
277  * block pointers.
278  *
279  * @param[in] mem_block Pointer to memory block object.
280  * @param[in] count     Number of blocks to free.
281  * @param[in] in_blocks Input array of pointers to the memory blocks.
282  *
283  * @retval 0       Successful
284  * @retval -EINVAL Invalid argument supplied.
285  * @retval -EFAULT Invalid pointers supplied.
286  */
287 int sys_mem_blocks_free(sys_mem_blocks_t *mem_block, size_t count,
288 			void **in_blocks);
289 
290 /**
291  * @brief Free contiguous multiple memory blocks
292  *
293  * Free contiguous multiple memory blocks
294  *
295  * @param[in] mem_block Pointer to memory block object.
296  * @param[in] block     Pointer to the first memory block
297  * @param[in] count     Number of blocks to free.
298  *
299  * @retval 0       Successful
300  * @retval -EINVAL Invalid argument supplied.
301  * @retval -EFAULT Invalid pointer supplied.
302  */
303 int sys_mem_blocks_free_contiguous(sys_mem_blocks_t *mem_block, void *block, size_t count);
304 
305 #ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS
306 /**
307  * @brief Get the runtime statistics of a memory block
308  *
309  * This function retrieves the runtime stats for the specified memory block
310  * @a mem_block and copies it into the memory pointed to by @a stats.
311  *
312  * @param mem_block Pointer to system memory block
313  * @param stats Pointer to struct to copy statistics into
314  *
315  * @return -EINVAL if NULL pointer was passed, otherwise 0
316  */
317 int sys_mem_blocks_runtime_stats_get(sys_mem_blocks_t *mem_block,
318 				     struct sys_memory_stats *stats);
319 
320 /**
321  * @brief Reset the maximum memory block usage
322  *
323  * This routine resets the maximum memory usage in the specified memory
324  * block @a mem_block to match that block's current memory usage.
325  *
326  * @param mem_block Pointer to system memory block
327  *
328  * @return -EINVAL if NULL pointer was passed, otherwise 0
329  */
330 int sys_mem_blocks_runtime_stats_reset_max(sys_mem_blocks_t *mem_block);
331 #endif
332 
333 /**
334  * @brief Initialize multi memory blocks allocator group
335  *
336  * Initialize a sys_multi_mem_block struct with the specified choice
337  * function. Note that individual allocator must be added later with
338  * sys_multi_mem_blocks_add_allocator.
339  *
340  * @param group     Multi memory blocks allocator structure.
341  * @param choice_fn A sys_multi_mem_blocks_choice_fn_t callback used to
342  *                  select the allocator to be used at allocation time
343  */
344 void sys_multi_mem_blocks_init(sys_multi_mem_blocks_t *group,
345 			       sys_multi_mem_blocks_choice_fn_t choice_fn);
346 
347 /**
348  * @brief Add an allocator to an allocator group
349  *
350  * This adds a known allocator to an existing multi memory blocks
351  * allocator group.
352  *
353  * @param group Multi memory blocks allocator structure.
354  * @param alloc Allocator to add
355  */
356 void sys_multi_mem_blocks_add_allocator(sys_multi_mem_blocks_t *group,
357 					sys_mem_blocks_t *alloc);
358 
359 /**
360  * @brief Allocate memory from multi memory blocks allocator group
361  *
362  * Just as for sys_mem_blocks_alloc(), allocates multiple blocks of
363  * memory. Takes an opaque configuration pointer passed to the choice
364  * function, which is used by integration code to choose an allocator.
365  *
366  * @param[in]  group      Multi memory blocks allocator structure.
367  * @param[in]  cfg        Opaque configuration parameter,
368  *                        as for sys_multi_mem_blocks_choice_fn_t
369  * @param[in]  count      Number of blocks to allocate
370  * @param[out] out_blocks Output array to be populated by pointers to
371  *                        the memory blocks. It must have at least
372  *                        @p count elements.
373  * @param[out] blk_size   If not NULL, output the block size of
374  *                        the chosen allocator.
375  *
376  * @retval 0       Successful
377  * @retval -EINVAL Invalid argument supplied, or no allocator chosen.
378  * @retval -ENOMEM Not enough blocks for allocation.
379  */
380 int sys_multi_mem_blocks_alloc(sys_multi_mem_blocks_t *group,
381 			       void *cfg, size_t count,
382 			       void **out_blocks,
383 			       size_t *blk_size);
384 
385 /**
386  * @brief Free memory allocated from multi memory blocks allocator group
387  *
388  * Free previous allocated memory blocks from sys_multi_mem_blocks_alloc().
389  *
390  * Note that all blocks in @p in_blocks must be from the same allocator.
391  *
392  * @param[in] group     Multi memory blocks allocator structure.
393  * @param[in] count     Number of blocks to free.
394  * @param[in] in_blocks Input array of pointers to the memory blocks.
395  *
396  * @retval 0       Successful
397  * @retval -EINVAL Invalid argument supplied, or no allocator chosen.
398  * @retval -EFAULT Invalid pointer(s) supplied.
399  */
400 int sys_multi_mem_blocks_free(sys_multi_mem_blocks_t *group,
401 			      size_t count, void **in_blocks);
402 
403 /** @} */
404 
405 #ifdef __cplusplus
406 }
407 #endif
408 
409 #endif /* ZEPHYR_INCLUDE_SYS_MEM_BLOCKS_H_ */
410