xref: /nrf52832-nimble/packages/NimBLE-latest/porting/nimble/src/os_mempool.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
1*042d53a7SEvalZero /*
2*042d53a7SEvalZero  * Licensed to the Apache Software Foundation (ASF) under one
3*042d53a7SEvalZero  * or more contributor license agreements.  See the NOTICE file
4*042d53a7SEvalZero  * distributed with this work for additional information
5*042d53a7SEvalZero  * regarding copyright ownership.  The ASF licenses this file
6*042d53a7SEvalZero  * to you under the Apache License, Version 2.0 (the
7*042d53a7SEvalZero  * "License"); you may not use this file except in compliance
8*042d53a7SEvalZero  * with the License.  You may obtain a copy of the License at
9*042d53a7SEvalZero  *
10*042d53a7SEvalZero  *  http://www.apache.org/licenses/LICENSE-2.0
11*042d53a7SEvalZero  *
12*042d53a7SEvalZero  * Unless required by applicable law or agreed to in writing,
13*042d53a7SEvalZero  * software distributed under the License is distributed on an
14*042d53a7SEvalZero  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15*042d53a7SEvalZero  * KIND, either express or implied.  See the License for the
16*042d53a7SEvalZero  * specific language governing permissions and limitations
17*042d53a7SEvalZero  * under the License.
18*042d53a7SEvalZero  */
19*042d53a7SEvalZero 
20*042d53a7SEvalZero #include "os/os.h"
21*042d53a7SEvalZero 
22*042d53a7SEvalZero #include <string.h>
23*042d53a7SEvalZero #include <assert.h>
24*042d53a7SEvalZero #include <stdbool.h>
25*042d53a7SEvalZero 
26*042d53a7SEvalZero #define OS_MEM_TRUE_BLOCK_SIZE(bsize)   OS_ALIGN(bsize, OS_ALIGNMENT)
27*042d53a7SEvalZero #define OS_MEMPOOL_TRUE_BLOCK_SIZE(mp) OS_MEM_TRUE_BLOCK_SIZE(mp->mp_block_size)
28*042d53a7SEvalZero 
29*042d53a7SEvalZero STAILQ_HEAD(, os_mempool) g_os_mempool_list =
30*042d53a7SEvalZero     STAILQ_HEAD_INITIALIZER(g_os_mempool_list);
31*042d53a7SEvalZero 
32*042d53a7SEvalZero #if MYNEWT_VAL(OS_MEMPOOL_POISON)
33*042d53a7SEvalZero static uint32_t os_mem_poison = 0xde7ec7ed;
34*042d53a7SEvalZero 
35*042d53a7SEvalZero static void
os_mempool_poison(void * start,int sz)36*042d53a7SEvalZero os_mempool_poison(void *start, int sz)
37*042d53a7SEvalZero {
38*042d53a7SEvalZero     int i;
39*042d53a7SEvalZero     char *p = start;
40*042d53a7SEvalZero 
41*042d53a7SEvalZero     for (i = sizeof(struct os_memblock); i < sz;
42*042d53a7SEvalZero          i = i + sizeof(os_mem_poison)) {
43*042d53a7SEvalZero         memcpy(p + i, &os_mem_poison, min(sizeof(os_mem_poison), sz - i));
44*042d53a7SEvalZero     }
45*042d53a7SEvalZero }
46*042d53a7SEvalZero 
47*042d53a7SEvalZero static void
os_mempool_poison_check(void * start,int sz)48*042d53a7SEvalZero os_mempool_poison_check(void *start, int sz)
49*042d53a7SEvalZero {
50*042d53a7SEvalZero     int i;
51*042d53a7SEvalZero     char *p = start;
52*042d53a7SEvalZero 
53*042d53a7SEvalZero     for (i = sizeof(struct os_memblock); i < sz;
54*042d53a7SEvalZero          i = i + sizeof(os_mem_poison)) {
55*042d53a7SEvalZero         assert(!memcmp(p + i, &os_mem_poison,
56*042d53a7SEvalZero                min(sizeof(os_mem_poison), sz - i)));
57*042d53a7SEvalZero     }
58*042d53a7SEvalZero }
59*042d53a7SEvalZero #else
60*042d53a7SEvalZero #define os_mempool_poison(start, sz)
61*042d53a7SEvalZero #define os_mempool_poison_check(start, sz)
62*042d53a7SEvalZero #endif
63*042d53a7SEvalZero 
64*042d53a7SEvalZero os_error_t
os_mempool_init(struct os_mempool * mp,uint16_t blocks,uint32_t block_size,void * membuf,char * name)65*042d53a7SEvalZero os_mempool_init(struct os_mempool *mp, uint16_t blocks, uint32_t block_size,
66*042d53a7SEvalZero                 void *membuf, char *name)
67*042d53a7SEvalZero {
68*042d53a7SEvalZero     int true_block_size;
69*042d53a7SEvalZero     uint8_t *block_addr;
70*042d53a7SEvalZero     struct os_memblock *block_ptr;
71*042d53a7SEvalZero 
72*042d53a7SEvalZero     /* Check for valid parameters */
73*042d53a7SEvalZero     if (!mp || (blocks < 0) || (block_size <= 0)) {
74*042d53a7SEvalZero         return OS_INVALID_PARM;
75*042d53a7SEvalZero     }
76*042d53a7SEvalZero 
77*042d53a7SEvalZero     if ((!membuf) && (blocks != 0)) {
78*042d53a7SEvalZero         return OS_INVALID_PARM;
79*042d53a7SEvalZero     }
80*042d53a7SEvalZero 
81*042d53a7SEvalZero     if (membuf != NULL) {
82*042d53a7SEvalZero         /* Blocks need to be sized properly and memory buffer should be
83*042d53a7SEvalZero          * aligned
84*042d53a7SEvalZero          */
85*042d53a7SEvalZero         if (((uintptr_t)membuf & (OS_ALIGNMENT - 1)) != 0) {
86*042d53a7SEvalZero             return OS_MEM_NOT_ALIGNED;
87*042d53a7SEvalZero         }
88*042d53a7SEvalZero     }
89*042d53a7SEvalZero     true_block_size = OS_MEM_TRUE_BLOCK_SIZE(block_size);
90*042d53a7SEvalZero 
91*042d53a7SEvalZero     /* Initialize the memory pool structure */
92*042d53a7SEvalZero     mp->mp_block_size = block_size;
93*042d53a7SEvalZero     mp->mp_num_free = blocks;
94*042d53a7SEvalZero     mp->mp_min_free = blocks;
95*042d53a7SEvalZero     mp->mp_flags = 0;
96*042d53a7SEvalZero     mp->mp_num_blocks = blocks;
97*042d53a7SEvalZero     mp->mp_membuf_addr = (uintptr_t)membuf;
98*042d53a7SEvalZero     mp->name = name;
99*042d53a7SEvalZero     os_mempool_poison(membuf, true_block_size);
100*042d53a7SEvalZero     SLIST_FIRST(mp) = membuf;
101*042d53a7SEvalZero 
102*042d53a7SEvalZero     /* Chain the memory blocks to the free list */
103*042d53a7SEvalZero     block_addr = (uint8_t *)membuf;
104*042d53a7SEvalZero     block_ptr = (struct os_memblock *)block_addr;
105*042d53a7SEvalZero     while (blocks > 1) {
106*042d53a7SEvalZero         block_addr += true_block_size;
107*042d53a7SEvalZero         os_mempool_poison(block_addr, true_block_size);
108*042d53a7SEvalZero         SLIST_NEXT(block_ptr, mb_next) = (struct os_memblock *)block_addr;
109*042d53a7SEvalZero         block_ptr = (struct os_memblock *)block_addr;
110*042d53a7SEvalZero         --blocks;
111*042d53a7SEvalZero     }
112*042d53a7SEvalZero 
113*042d53a7SEvalZero     /* Last one in the list should be NULL */
114*042d53a7SEvalZero     SLIST_NEXT(block_ptr, mb_next) = NULL;
115*042d53a7SEvalZero 
116*042d53a7SEvalZero     STAILQ_INSERT_TAIL(&g_os_mempool_list, mp, mp_list);
117*042d53a7SEvalZero 
118*042d53a7SEvalZero     return OS_OK;
119*042d53a7SEvalZero }
120*042d53a7SEvalZero 
121*042d53a7SEvalZero os_error_t
os_mempool_ext_init(struct os_mempool_ext * mpe,uint16_t blocks,uint32_t block_size,void * membuf,char * name)122*042d53a7SEvalZero os_mempool_ext_init(struct os_mempool_ext *mpe, uint16_t blocks,
123*042d53a7SEvalZero                     uint32_t block_size, void *membuf, char *name)
124*042d53a7SEvalZero {
125*042d53a7SEvalZero     int rc;
126*042d53a7SEvalZero 
127*042d53a7SEvalZero     rc = os_mempool_init(&mpe->mpe_mp, blocks, block_size, membuf, name);
128*042d53a7SEvalZero     if (rc != 0) {
129*042d53a7SEvalZero         return rc;
130*042d53a7SEvalZero     }
131*042d53a7SEvalZero 
132*042d53a7SEvalZero     mpe->mpe_mp.mp_flags = OS_MEMPOOL_F_EXT;
133*042d53a7SEvalZero     mpe->mpe_put_cb = NULL;
134*042d53a7SEvalZero     mpe->mpe_put_arg = NULL;
135*042d53a7SEvalZero 
136*042d53a7SEvalZero     return 0;
137*042d53a7SEvalZero }
138*042d53a7SEvalZero 
139*042d53a7SEvalZero os_error_t
os_mempool_clear(struct os_mempool * mp)140*042d53a7SEvalZero os_mempool_clear(struct os_mempool *mp)
141*042d53a7SEvalZero {
142*042d53a7SEvalZero     struct os_memblock *block_ptr;
143*042d53a7SEvalZero     int true_block_size;
144*042d53a7SEvalZero     uint8_t *block_addr;
145*042d53a7SEvalZero     uint16_t blocks;
146*042d53a7SEvalZero 
147*042d53a7SEvalZero     if (!mp) {
148*042d53a7SEvalZero         return OS_INVALID_PARM;
149*042d53a7SEvalZero     }
150*042d53a7SEvalZero 
151*042d53a7SEvalZero     true_block_size = OS_MEM_TRUE_BLOCK_SIZE(mp->mp_block_size);
152*042d53a7SEvalZero 
153*042d53a7SEvalZero     /* cleanup the memory pool structure */
154*042d53a7SEvalZero     mp->mp_num_free = mp->mp_num_blocks;
155*042d53a7SEvalZero     mp->mp_min_free = mp->mp_num_blocks;
156*042d53a7SEvalZero     os_mempool_poison((void *)mp->mp_membuf_addr, true_block_size);
157*042d53a7SEvalZero     SLIST_FIRST(mp) = (void *)mp->mp_membuf_addr;
158*042d53a7SEvalZero 
159*042d53a7SEvalZero     /* Chain the memory blocks to the free list */
160*042d53a7SEvalZero     block_addr = (uint8_t *)mp->mp_membuf_addr;
161*042d53a7SEvalZero     block_ptr = (struct os_memblock *)block_addr;
162*042d53a7SEvalZero     blocks = mp->mp_num_blocks;
163*042d53a7SEvalZero 
164*042d53a7SEvalZero     while (blocks > 1) {
165*042d53a7SEvalZero         block_addr += true_block_size;
166*042d53a7SEvalZero         os_mempool_poison(block_addr, true_block_size);
167*042d53a7SEvalZero         SLIST_NEXT(block_ptr, mb_next) = (struct os_memblock *)block_addr;
168*042d53a7SEvalZero         block_ptr = (struct os_memblock *)block_addr;
169*042d53a7SEvalZero         --blocks;
170*042d53a7SEvalZero     }
171*042d53a7SEvalZero 
172*042d53a7SEvalZero     /* Last one in the list should be NULL */
173*042d53a7SEvalZero     SLIST_NEXT(block_ptr, mb_next) = NULL;
174*042d53a7SEvalZero 
175*042d53a7SEvalZero     return OS_OK;
176*042d53a7SEvalZero }
177*042d53a7SEvalZero 
178*042d53a7SEvalZero bool
os_mempool_is_sane(const struct os_mempool * mp)179*042d53a7SEvalZero os_mempool_is_sane(const struct os_mempool *mp)
180*042d53a7SEvalZero {
181*042d53a7SEvalZero     struct os_memblock *block;
182*042d53a7SEvalZero 
183*042d53a7SEvalZero     /* Verify that each block in the free list belongs to the mempool. */
184*042d53a7SEvalZero     SLIST_FOREACH(block, mp, mb_next) {
185*042d53a7SEvalZero         if (!os_memblock_from(mp, block)) {
186*042d53a7SEvalZero             return false;
187*042d53a7SEvalZero         }
188*042d53a7SEvalZero         os_mempool_poison_check(block, OS_MEMPOOL_TRUE_BLOCK_SIZE(mp));
189*042d53a7SEvalZero     }
190*042d53a7SEvalZero 
191*042d53a7SEvalZero     return true;
192*042d53a7SEvalZero }
193*042d53a7SEvalZero 
194*042d53a7SEvalZero int
os_memblock_from(const struct os_mempool * mp,const void * block_addr)195*042d53a7SEvalZero os_memblock_from(const struct os_mempool *mp, const void *block_addr)
196*042d53a7SEvalZero {
197*042d53a7SEvalZero     uintptr_t true_block_size;
198*042d53a7SEvalZero     uintptr_t baddr_ptr;
199*042d53a7SEvalZero     uintptr_t end;
200*042d53a7SEvalZero 
201*042d53a7SEvalZero      _Static_assert(sizeof block_addr == sizeof baddr_ptr,
202*042d53a7SEvalZero                     "Pointer to void must be native word size.");
203*042d53a7SEvalZero 
204*042d53a7SEvalZero     baddr_ptr = (uintptr_t)block_addr;
205*042d53a7SEvalZero     true_block_size = OS_MEMPOOL_TRUE_BLOCK_SIZE(mp);
206*042d53a7SEvalZero     end = mp->mp_membuf_addr + (mp->mp_num_blocks * true_block_size);
207*042d53a7SEvalZero 
208*042d53a7SEvalZero     /* Check that the block is in the memory buffer range. */
209*042d53a7SEvalZero     if ((baddr_ptr < mp->mp_membuf_addr) || (baddr_ptr >= end)) {
210*042d53a7SEvalZero         return 0;
211*042d53a7SEvalZero     }
212*042d53a7SEvalZero 
213*042d53a7SEvalZero     /* All freed blocks should be on true block size boundaries! */
214*042d53a7SEvalZero     if (((baddr_ptr - mp->mp_membuf_addr) % true_block_size) != 0) {
215*042d53a7SEvalZero         return 0;
216*042d53a7SEvalZero     }
217*042d53a7SEvalZero 
218*042d53a7SEvalZero     return 1;
219*042d53a7SEvalZero }
220*042d53a7SEvalZero 
221*042d53a7SEvalZero void *
os_memblock_get(struct os_mempool * mp)222*042d53a7SEvalZero os_memblock_get(struct os_mempool *mp)
223*042d53a7SEvalZero {
224*042d53a7SEvalZero     os_sr_t sr;
225*042d53a7SEvalZero     struct os_memblock *block;
226*042d53a7SEvalZero 
227*042d53a7SEvalZero     /* Check to make sure they passed in a memory pool (or something) */
228*042d53a7SEvalZero     block = NULL;
229*042d53a7SEvalZero     if (mp) {
230*042d53a7SEvalZero         OS_ENTER_CRITICAL(sr);
231*042d53a7SEvalZero         /* Check for any free */
232*042d53a7SEvalZero         if (mp->mp_num_free) {
233*042d53a7SEvalZero             /* Get a free block */
234*042d53a7SEvalZero             block = SLIST_FIRST(mp);
235*042d53a7SEvalZero 
236*042d53a7SEvalZero             /* Set new free list head */
237*042d53a7SEvalZero             SLIST_FIRST(mp) = SLIST_NEXT(block, mb_next);
238*042d53a7SEvalZero 
239*042d53a7SEvalZero             /* Decrement number free by 1 */
240*042d53a7SEvalZero             mp->mp_num_free--;
241*042d53a7SEvalZero             if (mp->mp_min_free > mp->mp_num_free) {
242*042d53a7SEvalZero                 mp->mp_min_free = mp->mp_num_free;
243*042d53a7SEvalZero             }
244*042d53a7SEvalZero         }
245*042d53a7SEvalZero         OS_EXIT_CRITICAL(sr);
246*042d53a7SEvalZero 
247*042d53a7SEvalZero         if (block) {
248*042d53a7SEvalZero             os_mempool_poison_check(block, OS_MEMPOOL_TRUE_BLOCK_SIZE(mp));
249*042d53a7SEvalZero         }
250*042d53a7SEvalZero     }
251*042d53a7SEvalZero 
252*042d53a7SEvalZero     return (void *)block;
253*042d53a7SEvalZero }
254*042d53a7SEvalZero 
255*042d53a7SEvalZero os_error_t
os_memblock_put_from_cb(struct os_mempool * mp,void * block_addr)256*042d53a7SEvalZero os_memblock_put_from_cb(struct os_mempool *mp, void *block_addr)
257*042d53a7SEvalZero {
258*042d53a7SEvalZero     os_sr_t sr;
259*042d53a7SEvalZero     struct os_memblock *block;
260*042d53a7SEvalZero 
261*042d53a7SEvalZero     os_mempool_poison(block_addr, OS_MEMPOOL_TRUE_BLOCK_SIZE(mp));
262*042d53a7SEvalZero 
263*042d53a7SEvalZero     block = (struct os_memblock *)block_addr;
264*042d53a7SEvalZero     OS_ENTER_CRITICAL(sr);
265*042d53a7SEvalZero 
266*042d53a7SEvalZero     /* Chain current free list pointer to this block; make this block head */
267*042d53a7SEvalZero     SLIST_NEXT(block, mb_next) = SLIST_FIRST(mp);
268*042d53a7SEvalZero     SLIST_FIRST(mp) = block;
269*042d53a7SEvalZero 
270*042d53a7SEvalZero     /* XXX: Should we check that the number free <= number blocks? */
271*042d53a7SEvalZero     /* Increment number free */
272*042d53a7SEvalZero     mp->mp_num_free++;
273*042d53a7SEvalZero 
274*042d53a7SEvalZero     OS_EXIT_CRITICAL(sr);
275*042d53a7SEvalZero 
276*042d53a7SEvalZero     return OS_OK;
277*042d53a7SEvalZero }
278*042d53a7SEvalZero 
279*042d53a7SEvalZero os_error_t
os_memblock_put(struct os_mempool * mp,void * block_addr)280*042d53a7SEvalZero os_memblock_put(struct os_mempool *mp, void *block_addr)
281*042d53a7SEvalZero {
282*042d53a7SEvalZero     struct os_mempool_ext *mpe;
283*042d53a7SEvalZero     int rc;
284*042d53a7SEvalZero #if MYNEWT_VAL(OS_MEMPOOL_CHECK)
285*042d53a7SEvalZero     struct os_memblock *block;
286*042d53a7SEvalZero #endif
287*042d53a7SEvalZero 
288*042d53a7SEvalZero     /* Make sure parameters are valid */
289*042d53a7SEvalZero     if ((mp == NULL) || (block_addr == NULL)) {
290*042d53a7SEvalZero         return OS_INVALID_PARM;
291*042d53a7SEvalZero     }
292*042d53a7SEvalZero 
293*042d53a7SEvalZero #if MYNEWT_VAL(OS_MEMPOOL_CHECK)
294*042d53a7SEvalZero     /* Check that the block we are freeing is a valid block! */
295*042d53a7SEvalZero     assert(os_memblock_from(mp, block_addr));
296*042d53a7SEvalZero 
297*042d53a7SEvalZero     /*
298*042d53a7SEvalZero      * Check for duplicate free.
299*042d53a7SEvalZero      */
300*042d53a7SEvalZero     SLIST_FOREACH(block, mp, mb_next) {
301*042d53a7SEvalZero         assert(block != (struct os_memblock *)block_addr);
302*042d53a7SEvalZero     }
303*042d53a7SEvalZero #endif
304*042d53a7SEvalZero 
305*042d53a7SEvalZero     /* If this is an extended mempool with a put callback, call the callback
306*042d53a7SEvalZero      * instead of freeing the block directly.
307*042d53a7SEvalZero      */
308*042d53a7SEvalZero     if (mp->mp_flags & OS_MEMPOOL_F_EXT) {
309*042d53a7SEvalZero         mpe = (struct os_mempool_ext *)mp;
310*042d53a7SEvalZero         if (mpe->mpe_put_cb != NULL) {
311*042d53a7SEvalZero             rc = mpe->mpe_put_cb(mpe, block_addr, mpe->mpe_put_arg);
312*042d53a7SEvalZero             return rc;
313*042d53a7SEvalZero         }
314*042d53a7SEvalZero     }
315*042d53a7SEvalZero 
316*042d53a7SEvalZero     /* No callback; free the block. */
317*042d53a7SEvalZero     return os_memblock_put_from_cb(mp, block_addr);
318*042d53a7SEvalZero }
319*042d53a7SEvalZero 
320*042d53a7SEvalZero struct os_mempool *
os_mempool_info_get_next(struct os_mempool * mp,struct os_mempool_info * omi)321*042d53a7SEvalZero os_mempool_info_get_next(struct os_mempool *mp, struct os_mempool_info *omi)
322*042d53a7SEvalZero {
323*042d53a7SEvalZero     struct os_mempool *cur;
324*042d53a7SEvalZero 
325*042d53a7SEvalZero     if (mp == NULL) {
326*042d53a7SEvalZero         cur = STAILQ_FIRST(&g_os_mempool_list);
327*042d53a7SEvalZero     } else {
328*042d53a7SEvalZero         cur = STAILQ_NEXT(mp, mp_list);
329*042d53a7SEvalZero     }
330*042d53a7SEvalZero 
331*042d53a7SEvalZero     if (cur == NULL) {
332*042d53a7SEvalZero         return (NULL);
333*042d53a7SEvalZero     }
334*042d53a7SEvalZero 
335*042d53a7SEvalZero     omi->omi_block_size = cur->mp_block_size;
336*042d53a7SEvalZero     omi->omi_num_blocks = cur->mp_num_blocks;
337*042d53a7SEvalZero     omi->omi_num_free = cur->mp_num_free;
338*042d53a7SEvalZero     omi->omi_min_free = cur->mp_min_free;
339*042d53a7SEvalZero     strncpy(omi->omi_name, cur->name, sizeof(omi->omi_name));
340*042d53a7SEvalZero 
341*042d53a7SEvalZero     return (cur);
342*042d53a7SEvalZero }
343*042d53a7SEvalZero 
344*042d53a7SEvalZero 
345