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