1 /* 2 * Copyright (c) 2006-2018, RT-Thread Development Team 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 * 6 * Change Logs: 7 * Date Author Notes 8 * 2006-05-27 Bernard implement memory pool 9 * 2006-06-03 Bernard fix the thread timer init bug 10 * 2006-06-30 Bernard fix the allocate/free block bug 11 * 2006-08-04 Bernard add hook support 12 * 2006-08-10 Bernard fix interrupt bug in rt_mp_alloc 13 * 2010-07-13 Bernard fix RT_ALIGN issue found by kuronca 14 * 2010-10-26 yi.qiu add module support in rt_mp_delete 15 * 2011-01-24 Bernard add object allocation check. 16 * 2012-03-22 Bernard fix align issue in rt_mp_init and rt_mp_create. 17 */ 18 19 #include <rthw.h> 20 #include <rtthread.h> 21 22 #ifdef RT_USING_MEMPOOL 23 24 #ifdef RT_USING_HOOK 25 static void (*rt_mp_alloc_hook)(struct rt_mempool *mp, void *block); 26 static void (*rt_mp_free_hook)(struct rt_mempool *mp, void *block); 27 28 /** 29 * @addtogroup Hook 30 */ 31 32 /**@{*/ 33 34 /** 35 * This function will set a hook function, which will be invoked when a memory 36 * block is allocated from memory pool. 37 * 38 * @param hook the hook function 39 */ 40 void rt_mp_alloc_sethook(void (*hook)(struct rt_mempool *mp, void *block)) 41 { 42 rt_mp_alloc_hook = hook; 43 } 44 45 /** 46 * This function will set a hook function, which will be invoked when a memory 47 * block is released to memory pool. 48 * 49 * @param hook the hook function 50 */ 51 void rt_mp_free_sethook(void (*hook)(struct rt_mempool *mp, void *block)) 52 { 53 rt_mp_free_hook = hook; 54 } 55 56 /**@}*/ 57 #endif 58 59 /** 60 * @addtogroup MM 61 */ 62 63 /**@{*/ 64 65 /** 66 * This function will initialize a memory pool object, normally which is used 67 * for static object. 68 * 69 * @param mp the memory pool object 70 * @param name the name of memory pool 71 * @param start the star address of memory pool 72 * @param size the total size of memory pool 73 * @param block_size the size for each block 74 * 75 * @return RT_EOK 76 */ 77 rt_err_t rt_mp_init(struct rt_mempool *mp, 78 const char *name, 79 void *start, 80 rt_size_t size, 81 rt_size_t block_size) 82 { 83 rt_uint8_t *block_ptr; 84 register rt_size_t offset; 85 86 /* parameter check */ 87 RT_ASSERT(mp != RT_NULL); 88 89 /* initialize object */ 90 rt_object_init(&(mp->parent), RT_Object_Class_MemPool, name); 91 92 /* initialize memory pool */ 93 mp->start_address = start; 94 mp->size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE); 95 96 /* align the block size */ 97 block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE); 98 mp->block_size = block_size; 99 100 /* align to align size byte */ 101 mp->block_total_count = mp->size / (mp->block_size + sizeof(rt_uint8_t *)); 102 mp->block_free_count = mp->block_total_count; 103 104 /* initialize suspended thread list */ 105 rt_list_init(&(mp->suspend_thread)); 106 mp->suspend_thread_count = 0; 107 108 /* initialize free block list */ 109 block_ptr = (rt_uint8_t *)mp->start_address; 110 for (offset = 0; offset < mp->block_total_count; offset ++) 111 { 112 *(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *))) = 113 (rt_uint8_t *)(block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *))); 114 } 115 116 *(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *))) = 117 RT_NULL; 118 119 mp->block_list = block_ptr; 120 121 return RT_EOK; 122 } 123 RTM_EXPORT(rt_mp_init); 124 125 /** 126 * This function will detach a memory pool from system object management. 127 * 128 * @param mp the memory pool object 129 * 130 * @return RT_EOK 131 */ 132 rt_err_t rt_mp_detach(struct rt_mempool *mp) 133 { 134 struct rt_thread *thread; 135 register rt_ubase_t temp; 136 137 /* parameter check */ 138 RT_ASSERT(mp != RT_NULL); 139 RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool); 140 RT_ASSERT(rt_object_is_systemobject(&mp->parent)); 141 142 /* wake up all suspended threads */ 143 while (!rt_list_isempty(&(mp->suspend_thread))) 144 { 145 /* disable interrupt */ 146 temp = rt_hw_interrupt_disable(); 147 148 /* get next suspend thread */ 149 thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist); 150 /* set error code to RT_ERROR */ 151 thread->error = -RT_ERROR; 152 153 /* 154 * resume thread 155 * In rt_thread_resume function, it will remove current thread from 156 * suspend list 157 */ 158 rt_thread_resume(thread); 159 160 /* decrease suspended thread count */ 161 mp->suspend_thread_count --; 162 163 /* enable interrupt */ 164 rt_hw_interrupt_enable(temp); 165 } 166 167 /* detach object */ 168 rt_object_detach(&(mp->parent)); 169 170 return RT_EOK; 171 } 172 RTM_EXPORT(rt_mp_detach); 173 174 #ifdef RT_USING_HEAP 175 /** 176 * This function will create a mempool object and allocate the memory pool from 177 * heap. 178 * 179 * @param name the name of memory pool 180 * @param block_count the count of blocks in memory pool 181 * @param block_size the size for each block 182 * 183 * @return the created mempool object 184 */ 185 rt_mp_t rt_mp_create(const char *name, 186 rt_size_t block_count, 187 rt_size_t block_size) 188 { 189 rt_uint8_t *block_ptr; 190 struct rt_mempool *mp; 191 register rt_size_t offset; 192 193 RT_DEBUG_NOT_IN_INTERRUPT; 194 195 /* allocate object */ 196 mp = (struct rt_mempool *)rt_object_allocate(RT_Object_Class_MemPool, name); 197 /* allocate object failed */ 198 if (mp == RT_NULL) 199 return RT_NULL; 200 201 /* initialize memory pool */ 202 block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE); 203 mp->block_size = block_size; 204 mp->size = (block_size + sizeof(rt_uint8_t *)) * block_count; 205 206 /* allocate memory */ 207 mp->start_address = rt_malloc((block_size + sizeof(rt_uint8_t *)) * 208 block_count); 209 if (mp->start_address == RT_NULL) 210 { 211 /* no memory, delete memory pool object */ 212 rt_object_delete(&(mp->parent)); 213 214 return RT_NULL; 215 } 216 217 mp->block_total_count = block_count; 218 mp->block_free_count = mp->block_total_count; 219 220 /* initialize suspended thread list */ 221 rt_list_init(&(mp->suspend_thread)); 222 mp->suspend_thread_count = 0; 223 224 /* initialize free block list */ 225 block_ptr = (rt_uint8_t *)mp->start_address; 226 for (offset = 0; offset < mp->block_total_count; offset ++) 227 { 228 *(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *))) 229 = block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *)); 230 } 231 232 *(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *))) 233 = RT_NULL; 234 235 mp->block_list = block_ptr; 236 237 return mp; 238 } 239 RTM_EXPORT(rt_mp_create); 240 241 /** 242 * This function will delete a memory pool and release the object memory. 243 * 244 * @param mp the memory pool object 245 * 246 * @return RT_EOK 247 */ 248 rt_err_t rt_mp_delete(rt_mp_t mp) 249 { 250 struct rt_thread *thread; 251 register rt_ubase_t temp; 252 253 RT_DEBUG_NOT_IN_INTERRUPT; 254 255 /* parameter check */ 256 RT_ASSERT(mp != RT_NULL); 257 RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool); 258 RT_ASSERT(rt_object_is_systemobject(&mp->parent) == RT_FALSE); 259 260 /* wake up all suspended threads */ 261 while (!rt_list_isempty(&(mp->suspend_thread))) 262 { 263 /* disable interrupt */ 264 temp = rt_hw_interrupt_disable(); 265 266 /* get next suspend thread */ 267 thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist); 268 /* set error code to RT_ERROR */ 269 thread->error = -RT_ERROR; 270 271 /* 272 * resume thread 273 * In rt_thread_resume function, it will remove current thread from 274 * suspend list 275 */ 276 rt_thread_resume(thread); 277 278 /* decrease suspended thread count */ 279 mp->suspend_thread_count --; 280 281 /* enable interrupt */ 282 rt_hw_interrupt_enable(temp); 283 } 284 285 /* release allocated room */ 286 rt_free(mp->start_address); 287 288 /* detach object */ 289 rt_object_delete(&(mp->parent)); 290 291 return RT_EOK; 292 } 293 RTM_EXPORT(rt_mp_delete); 294 #endif 295 296 /** 297 * This function will allocate a block from memory pool 298 * 299 * @param mp the memory pool object 300 * @param time the waiting time 301 * 302 * @return the allocated memory block or RT_NULL on allocated failed 303 */ 304 void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time) 305 { 306 rt_uint8_t *block_ptr; 307 register rt_base_t level; 308 struct rt_thread *thread; 309 rt_uint32_t before_sleep = 0; 310 311 /* get current thread */ 312 thread = rt_thread_self(); 313 314 /* disable interrupt */ 315 level = rt_hw_interrupt_disable(); 316 317 while (mp->block_free_count == 0) 318 { 319 /* memory block is unavailable. */ 320 if (time == 0) 321 { 322 /* enable interrupt */ 323 rt_hw_interrupt_enable(level); 324 325 rt_set_errno(-RT_ETIMEOUT); 326 327 return RT_NULL; 328 } 329 330 RT_DEBUG_NOT_IN_INTERRUPT; 331 332 thread->error = RT_EOK; 333 334 /* need suspend thread */ 335 rt_thread_suspend(thread); 336 rt_list_insert_after(&(mp->suspend_thread), &(thread->tlist)); 337 mp->suspend_thread_count++; 338 339 if (time > 0) 340 { 341 /* get the start tick of timer */ 342 before_sleep = rt_tick_get(); 343 344 /* init thread timer and start it */ 345 rt_timer_control(&(thread->thread_timer), 346 RT_TIMER_CTRL_SET_TIME, 347 &time); 348 rt_timer_start(&(thread->thread_timer)); 349 } 350 351 /* enable interrupt */ 352 rt_hw_interrupt_enable(level); 353 354 /* do a schedule */ 355 rt_schedule(); 356 357 if (thread->error != RT_EOK) 358 return RT_NULL; 359 360 if (time > 0) 361 { 362 time -= rt_tick_get() - before_sleep; 363 if (time < 0) 364 time = 0; 365 } 366 /* disable interrupt */ 367 level = rt_hw_interrupt_disable(); 368 } 369 370 /* memory block is available. decrease the free block counter */ 371 mp->block_free_count--; 372 373 /* get block from block list */ 374 block_ptr = mp->block_list; 375 RT_ASSERT(block_ptr != RT_NULL); 376 377 /* Setup the next free node. */ 378 mp->block_list = *(rt_uint8_t **)block_ptr; 379 380 /* point to memory pool */ 381 *(rt_uint8_t **)block_ptr = (rt_uint8_t *)mp; 382 383 /* enable interrupt */ 384 rt_hw_interrupt_enable(level); 385 386 RT_OBJECT_HOOK_CALL(rt_mp_alloc_hook, 387 (mp, (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *)))); 388 389 return (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *)); 390 } 391 RTM_EXPORT(rt_mp_alloc); 392 393 /** 394 * This function will release a memory block 395 * 396 * @param block the address of memory block to be released 397 */ 398 void rt_mp_free(void *block) 399 { 400 rt_uint8_t **block_ptr; 401 struct rt_mempool *mp; 402 struct rt_thread *thread; 403 register rt_base_t level; 404 405 /* get the control block of pool which the block belongs to */ 406 block_ptr = (rt_uint8_t **)((rt_uint8_t *)block - sizeof(rt_uint8_t *)); 407 mp = (struct rt_mempool *)*block_ptr; 408 409 RT_OBJECT_HOOK_CALL(rt_mp_free_hook, (mp, block)); 410 411 /* disable interrupt */ 412 level = rt_hw_interrupt_disable(); 413 414 /* increase the free block count */ 415 mp->block_free_count ++; 416 417 /* link the block into the block list */ 418 *block_ptr = mp->block_list; 419 mp->block_list = (rt_uint8_t *)block_ptr; 420 421 if (mp->suspend_thread_count > 0) 422 { 423 /* get the suspended thread */ 424 thread = rt_list_entry(mp->suspend_thread.next, 425 struct rt_thread, 426 tlist); 427 428 /* set error */ 429 thread->error = RT_EOK; 430 431 /* resume thread */ 432 rt_thread_resume(thread); 433 434 /* decrease suspended thread count */ 435 mp->suspend_thread_count --; 436 437 /* enable interrupt */ 438 rt_hw_interrupt_enable(level); 439 440 /* do a schedule */ 441 rt_schedule(); 442 443 return; 444 } 445 446 /* enable interrupt */ 447 rt_hw_interrupt_enable(level); 448 } 449 RTM_EXPORT(rt_mp_free); 450 451 /**@}*/ 452 453 #endif 454 455