mempool.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. /*
  2. * Copyright (c) 2006-2022, 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. * 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to mempool.c
  18. * 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
  19. */
  20. #include <rthw.h>
  21. #include <rtthread.h>
  22. #ifdef RT_USING_MEMPOOL
  23. #ifndef __on_rt_mp_alloc_hook
  24. #define __on_rt_mp_alloc_hook(mp, block) __ON_HOOK_ARGS(rt_mp_alloc_hook, (mp, block))
  25. #endif
  26. #ifndef __on_rt_mp_free_hook
  27. #define __on_rt_mp_free_hook(mp, block) __ON_HOOK_ARGS(rt_mp_free_hook, (mp, block))
  28. #endif
  29. #if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
  30. static void (*rt_mp_alloc_hook)(struct rt_mempool *mp, void *block);
  31. static void (*rt_mp_free_hook)(struct rt_mempool *mp, void *block);
  32. /**
  33. * @addtogroup Hook
  34. */
  35. /**@{*/
  36. /**
  37. * @brief This function will set a hook function, which will be invoked when a memory
  38. * block is allocated from the memory pool.
  39. *
  40. * @param hook the hook function
  41. */
  42. void rt_mp_alloc_sethook(void (*hook)(struct rt_mempool *mp, void *block))
  43. {
  44. rt_mp_alloc_hook = hook;
  45. }
  46. /**
  47. * @brief This function will set a hook function, which will be invoked when a memory
  48. * block is released to the memory pool.
  49. *
  50. * @param hook the hook function
  51. */
  52. void rt_mp_free_sethook(void (*hook)(struct rt_mempool *mp, void *block))
  53. {
  54. rt_mp_free_hook = hook;
  55. }
  56. /**@}*/
  57. #endif /* RT_USING_HOOK */
  58. /**
  59. * @addtogroup MM
  60. */
  61. /**@{*/
  62. /**
  63. * @brief This function will initialize a memory pool object, normally which is used
  64. * for static object.
  65. *
  66. * @param mp is the memory pool object.
  67. *
  68. * @param name is the name of the memory pool.
  69. *
  70. * @param start is the start address of the memory pool.
  71. *
  72. * @param size is the total size of the memory pool.
  73. *
  74. * @param block_size is the size for each block..
  75. *
  76. * @return RT_EOK
  77. */
  78. rt_err_t rt_mp_init(struct rt_mempool *mp,
  79. const char *name,
  80. void *start,
  81. rt_size_t size,
  82. rt_size_t block_size)
  83. {
  84. rt_uint8_t *block_ptr;
  85. rt_size_t offset;
  86. /* parameter check */
  87. RT_ASSERT(mp != RT_NULL);
  88. RT_ASSERT(name != RT_NULL);
  89. RT_ASSERT(start != RT_NULL);
  90. RT_ASSERT(size > 0 && block_size > 0);
  91. /* initialize object */
  92. rt_object_init(&(mp->parent), RT_Object_Class_MemPool, name);
  93. /* initialize memory pool */
  94. mp->start_address = start;
  95. mp->size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
  96. /* align the block size */
  97. block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE);
  98. mp->block_size = block_size;
  99. /* align to align size byte */
  100. mp->block_total_count = mp->size / (mp->block_size + sizeof(rt_uint8_t *));
  101. mp->block_free_count = mp->block_total_count;
  102. /* initialize suspended thread list */
  103. rt_list_init(&(mp->suspend_thread));
  104. /* initialize free block list */
  105. block_ptr = (rt_uint8_t *)mp->start_address;
  106. for (offset = 0; offset < mp->block_total_count; offset ++)
  107. {
  108. *(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *))) =
  109. (rt_uint8_t *)(block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *)));
  110. }
  111. *(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *))) =
  112. RT_NULL;
  113. mp->block_list = block_ptr;
  114. rt_spin_lock_init(&(mp->spinlock));
  115. return RT_EOK;
  116. }
  117. RTM_EXPORT(rt_mp_init);
  118. /**
  119. * @brief This function will detach a memory pool from system object management.
  120. *
  121. * @param mp is the memory pool object.
  122. *
  123. * @return RT_EOK
  124. */
  125. rt_err_t rt_mp_detach(struct rt_mempool *mp)
  126. {
  127. struct rt_thread *thread;
  128. rt_base_t level;
  129. /* parameter check */
  130. RT_ASSERT(mp != RT_NULL);
  131. RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool);
  132. RT_ASSERT(rt_object_is_systemobject(&mp->parent));
  133. level = rt_spin_lock_irqsave(&(mp->spinlock));
  134. /* wake up all suspended threads */
  135. while (!rt_list_isempty(&(mp->suspend_thread)))
  136. {
  137. /* get next suspend thread */
  138. thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist);
  139. /* set error code to -RT_ERROR */
  140. thread->error = -RT_ERROR;
  141. /*
  142. * resume thread
  143. * In rt_thread_resume function, it will remove current thread from
  144. * suspend list
  145. */
  146. rt_thread_resume(thread);
  147. }
  148. /* detach object */
  149. rt_object_detach(&(mp->parent));
  150. rt_spin_unlock_irqrestore(&(mp->spinlock), level);
  151. return RT_EOK;
  152. }
  153. RTM_EXPORT(rt_mp_detach);
  154. #ifdef RT_USING_HEAP
  155. /**
  156. * @brief This function will create a mempool object and allocate the memory pool from
  157. * heap.
  158. *
  159. * @param name is the name of memory pool.
  160. *
  161. * @param block_count is the count of blocks in memory pool.
  162. *
  163. * @param block_size is the size for each block.
  164. *
  165. * @return the created mempool object
  166. */
  167. rt_mp_t rt_mp_create(const char *name,
  168. rt_size_t block_count,
  169. rt_size_t block_size)
  170. {
  171. rt_uint8_t *block_ptr;
  172. struct rt_mempool *mp;
  173. rt_size_t offset;
  174. RT_DEBUG_NOT_IN_INTERRUPT;
  175. /* parameter check */
  176. RT_ASSERT(name != RT_NULL);
  177. RT_ASSERT(block_count > 0 && block_size > 0);
  178. /* allocate object */
  179. mp = (struct rt_mempool *)rt_object_allocate(RT_Object_Class_MemPool, name);
  180. /* allocate object failed */
  181. if (mp == RT_NULL)
  182. return RT_NULL;
  183. /* initialize memory pool */
  184. block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE);
  185. mp->block_size = block_size;
  186. mp->size = (block_size + sizeof(rt_uint8_t *)) * block_count;
  187. /* allocate memory */
  188. mp->start_address = rt_malloc((block_size + sizeof(rt_uint8_t *)) *
  189. block_count);
  190. if (mp->start_address == RT_NULL)
  191. {
  192. /* no memory, delete memory pool object */
  193. rt_object_delete(&(mp->parent));
  194. return RT_NULL;
  195. }
  196. mp->block_total_count = block_count;
  197. mp->block_free_count = mp->block_total_count;
  198. /* initialize suspended thread list */
  199. rt_list_init(&(mp->suspend_thread));
  200. /* initialize free block list */
  201. block_ptr = (rt_uint8_t *)mp->start_address;
  202. for (offset = 0; offset < mp->block_total_count; offset ++)
  203. {
  204. *(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *)))
  205. = block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *));
  206. }
  207. *(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *)))
  208. = RT_NULL;
  209. mp->block_list = block_ptr;
  210. rt_spin_lock_init(&(mp->spinlock));
  211. return mp;
  212. }
  213. RTM_EXPORT(rt_mp_create);
  214. /**
  215. * @brief This function will delete a memory pool and release the object memory.
  216. *
  217. * @param mp is the memory pool object.
  218. *
  219. * @return RT_EOK
  220. */
  221. rt_err_t rt_mp_delete(rt_mp_t mp)
  222. {
  223. struct rt_thread *thread;
  224. rt_base_t level;
  225. RT_DEBUG_NOT_IN_INTERRUPT;
  226. /* parameter check */
  227. RT_ASSERT(mp != RT_NULL);
  228. RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool);
  229. RT_ASSERT(rt_object_is_systemobject(&mp->parent) == RT_FALSE);
  230. level = rt_spin_lock_irqsave(&(mp->spinlock));
  231. /* wake up all suspended threads */
  232. while (!rt_list_isempty(&(mp->suspend_thread)))
  233. {
  234. /* get next suspend thread */
  235. thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist);
  236. /* set error code to -RT_ERROR */
  237. thread->error = -RT_ERROR;
  238. /*
  239. * resume thread
  240. * In rt_thread_resume function, it will remove current thread from
  241. * suspend list
  242. */
  243. rt_thread_resume(thread);
  244. }
  245. /* release allocated room */
  246. rt_free(mp->start_address);
  247. /* detach object */
  248. rt_object_delete(&(mp->parent));
  249. rt_spin_unlock_irqrestore(&(mp->spinlock), level);
  250. return RT_EOK;
  251. }
  252. RTM_EXPORT(rt_mp_delete);
  253. #endif /* RT_USING_HEAP */
  254. /**
  255. * @brief This function will allocate a block from memory pool.
  256. *
  257. * @param mp is the memory pool object.
  258. *
  259. * @param time is the maximum waiting time for allocating memory.
  260. * - 0 for not waiting, allocating memory immediately.
  261. *
  262. * @return the allocated memory block or RT_NULL on allocated failed.
  263. */
  264. void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time)
  265. {
  266. rt_uint8_t *block_ptr;
  267. rt_base_t level;
  268. struct rt_thread *thread;
  269. rt_uint32_t before_sleep = 0;
  270. /* parameter check */
  271. RT_ASSERT(mp != RT_NULL);
  272. /* get current thread */
  273. thread = rt_thread_self();
  274. level = rt_spin_lock_irqsave(&(mp->spinlock));
  275. while (mp->block_free_count == 0)
  276. {
  277. /* memory block is unavailable. */
  278. if (time == 0)
  279. {
  280. rt_spin_unlock_irqrestore(&(mp->spinlock), level);
  281. rt_set_errno(-RT_ETIMEOUT);
  282. return RT_NULL;
  283. }
  284. RT_DEBUG_NOT_IN_INTERRUPT;
  285. thread->error = RT_EOK;
  286. /* need suspend thread */
  287. rt_thread_suspend(thread);
  288. rt_list_insert_after(&(mp->suspend_thread), &(thread->tlist));
  289. if (time > 0)
  290. {
  291. /* get the start tick of timer */
  292. before_sleep = rt_tick_get();
  293. /* init thread timer and start it */
  294. rt_timer_control(&(thread->thread_timer),
  295. RT_TIMER_CTRL_SET_TIME,
  296. &time);
  297. rt_timer_start(&(thread->thread_timer));
  298. }
  299. /* enable interrupt */
  300. rt_spin_unlock_irqrestore(&(mp->spinlock), level);
  301. /* do a schedule */
  302. rt_schedule();
  303. if (thread->error != RT_EOK)
  304. return RT_NULL;
  305. if (time > 0)
  306. {
  307. time -= rt_tick_get() - before_sleep;
  308. if (time < 0)
  309. time = 0;
  310. }
  311. level = rt_spin_lock_irqsave(&(mp->spinlock));
  312. }
  313. /* memory block is available. decrease the free block counter */
  314. mp->block_free_count--;
  315. /* get block from block list */
  316. block_ptr = mp->block_list;
  317. RT_ASSERT(block_ptr != RT_NULL);
  318. /* Setup the next free node. */
  319. mp->block_list = *(rt_uint8_t **)block_ptr;
  320. /* point to memory pool */
  321. *(rt_uint8_t **)block_ptr = (rt_uint8_t *)mp;
  322. rt_spin_unlock_irqrestore(&(mp->spinlock), level);
  323. RT_OBJECT_HOOK_CALL(rt_mp_alloc_hook,
  324. (mp, (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *))));
  325. return (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *));
  326. }
  327. RTM_EXPORT(rt_mp_alloc);
  328. /**
  329. * @brief This function will release a memory block.
  330. *
  331. * @param block the address of memory block to be released.
  332. */
  333. void rt_mp_free(void *block)
  334. {
  335. rt_uint8_t **block_ptr;
  336. struct rt_mempool *mp;
  337. struct rt_thread *thread;
  338. rt_base_t level;
  339. /* parameter check */
  340. if (block == RT_NULL) return;
  341. /* get the control block of pool which the block belongs to */
  342. block_ptr = (rt_uint8_t **)((rt_uint8_t *)block - sizeof(rt_uint8_t *));
  343. mp = (struct rt_mempool *)*block_ptr;
  344. RT_OBJECT_HOOK_CALL(rt_mp_free_hook, (mp, block));
  345. level = rt_spin_lock_irqsave(&(mp->spinlock));
  346. /* increase the free block count */
  347. mp->block_free_count ++;
  348. /* link the block into the block list */
  349. *block_ptr = mp->block_list;
  350. mp->block_list = (rt_uint8_t *)block_ptr;
  351. if (!rt_list_isempty(&(mp->suspend_thread)))
  352. {
  353. /* get the suspended thread */
  354. thread = rt_list_entry(mp->suspend_thread.next,
  355. struct rt_thread,
  356. tlist);
  357. /* set error */
  358. thread->error = RT_EOK;
  359. /* resume thread */
  360. rt_thread_resume(thread);
  361. rt_spin_unlock_irqrestore(&(mp->spinlock), level);
  362. /* do a schedule */
  363. rt_schedule();
  364. return;
  365. }
  366. rt_spin_unlock_irqrestore(&(mp->spinlock), level);
  367. }
  368. RTM_EXPORT(rt_mp_free);
  369. /**@}*/
  370. #endif /* RT_USING_MEMPOOL */