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