mempool.c 11 KB

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