xref: /nrf52832-nimble/packages/NimBLE-latest/porting/nimble/src/mem.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 <stdlib.h>
21*042d53a7SEvalZero #include "os/os.h"
22*042d53a7SEvalZero #include "mem/mem.h"
23*042d53a7SEvalZero 
24*042d53a7SEvalZero /**
25*042d53a7SEvalZero  * Generic mempool allocation function.  Used with basic and extended mempools.
26*042d53a7SEvalZero  */
27*042d53a7SEvalZero static int
mem_malloc_mempool_gen(uint16_t num_blocks,uint32_t block_size,void ** out_buf)28*042d53a7SEvalZero mem_malloc_mempool_gen(uint16_t num_blocks, uint32_t block_size,
29*042d53a7SEvalZero                        void **out_buf)
30*042d53a7SEvalZero {
31*042d53a7SEvalZero     block_size = OS_ALIGN(block_size, OS_ALIGNMENT);
32*042d53a7SEvalZero 
33*042d53a7SEvalZero     if (num_blocks > 0) {
34*042d53a7SEvalZero         *out_buf = malloc(OS_MEMPOOL_BYTES(num_blocks, block_size));
35*042d53a7SEvalZero         if (*out_buf == NULL) {
36*042d53a7SEvalZero             return OS_ENOMEM;
37*042d53a7SEvalZero         }
38*042d53a7SEvalZero     } else {
39*042d53a7SEvalZero         *out_buf = NULL;
40*042d53a7SEvalZero     }
41*042d53a7SEvalZero 
42*042d53a7SEvalZero     return 0;
43*042d53a7SEvalZero }
44*042d53a7SEvalZero 
45*042d53a7SEvalZero /**
46*042d53a7SEvalZero  * Mallocs a block of memory and initializes a mempool to use it.
47*042d53a7SEvalZero  *
48*042d53a7SEvalZero  * @param mempool               The mempool to initialize.
49*042d53a7SEvalZero  * @param num_blocks            The total number of memory blocks in the
50*042d53a7SEvalZero  *                                  mempool.
51*042d53a7SEvalZero  * @param block_size            The size of each mempool entry.
52*042d53a7SEvalZero  * @param name                  The name to give the mempool.
53*042d53a7SEvalZero  * @param out_buf               On success, this points to the malloced memory.
54*042d53a7SEvalZero  *                                  Pass NULL if you don't need this
55*042d53a7SEvalZero  *                                  information.
56*042d53a7SEvalZero  *
57*042d53a7SEvalZero  * @return                      0 on success;
58*042d53a7SEvalZero  *                              OS_ENOMEM on malloc failure;
59*042d53a7SEvalZero  *                              Other OS code on unexpected error.
60*042d53a7SEvalZero  */
61*042d53a7SEvalZero int
mem_malloc_mempool(struct os_mempool * mempool,uint16_t num_blocks,uint32_t block_size,char * name,void ** out_buf)62*042d53a7SEvalZero mem_malloc_mempool(struct os_mempool *mempool, uint16_t num_blocks,
63*042d53a7SEvalZero                    uint32_t block_size, char *name, void **out_buf)
64*042d53a7SEvalZero {
65*042d53a7SEvalZero     void *buf;
66*042d53a7SEvalZero     int rc;
67*042d53a7SEvalZero 
68*042d53a7SEvalZero     rc = mem_malloc_mempool_gen(num_blocks, block_size, &buf);
69*042d53a7SEvalZero     if (rc != 0) {
70*042d53a7SEvalZero         return rc;
71*042d53a7SEvalZero     }
72*042d53a7SEvalZero 
73*042d53a7SEvalZero     rc = os_mempool_init(mempool, num_blocks, block_size, buf, name);
74*042d53a7SEvalZero     if (rc != 0) {
75*042d53a7SEvalZero         free(buf);
76*042d53a7SEvalZero         return rc;
77*042d53a7SEvalZero     }
78*042d53a7SEvalZero 
79*042d53a7SEvalZero     if (out_buf != NULL) {
80*042d53a7SEvalZero         *out_buf = buf;
81*042d53a7SEvalZero     }
82*042d53a7SEvalZero 
83*042d53a7SEvalZero     return 0;
84*042d53a7SEvalZero }
85*042d53a7SEvalZero 
86*042d53a7SEvalZero /**
87*042d53a7SEvalZero  * Mallocs a block of memory and initializes an extended mempool to use it.
88*042d53a7SEvalZero  *
89*042d53a7SEvalZero  * @param mpe                   The extended mempool to initialize.
90*042d53a7SEvalZero  * @param num_blocks            The total number of memory blocks in the
91*042d53a7SEvalZero  *                                  mempool.
92*042d53a7SEvalZero  * @param block_size            The size of each mempool entry.
93*042d53a7SEvalZero  * @param name                  The name to give the mempool.
94*042d53a7SEvalZero  * @param out_buf               On success, this points to the malloced memory.
95*042d53a7SEvalZero  *                                  Pass NULL if you don't need this
96*042d53a7SEvalZero  *                                  information.
97*042d53a7SEvalZero  *
98*042d53a7SEvalZero  * @return                      0 on success;
99*042d53a7SEvalZero  *                              OS_ENOMEM on malloc failure;
100*042d53a7SEvalZero  *                              Other OS code on unexpected error.
101*042d53a7SEvalZero  */
102*042d53a7SEvalZero int
mem_malloc_mempool_ext(struct os_mempool_ext * mpe,uint16_t num_blocks,uint32_t block_size,char * name,void ** out_buf)103*042d53a7SEvalZero mem_malloc_mempool_ext(struct os_mempool_ext *mpe, uint16_t num_blocks,
104*042d53a7SEvalZero                        uint32_t block_size, char *name, void **out_buf)
105*042d53a7SEvalZero {
106*042d53a7SEvalZero     void *buf;
107*042d53a7SEvalZero     int rc;
108*042d53a7SEvalZero 
109*042d53a7SEvalZero     rc = mem_malloc_mempool_gen(num_blocks, block_size, &buf);
110*042d53a7SEvalZero     if (rc != 0) {
111*042d53a7SEvalZero         return rc;
112*042d53a7SEvalZero     }
113*042d53a7SEvalZero 
114*042d53a7SEvalZero     rc = os_mempool_ext_init(mpe, num_blocks, block_size, buf, name);
115*042d53a7SEvalZero     if (rc != 0) {
116*042d53a7SEvalZero         free(buf);
117*042d53a7SEvalZero         return rc;
118*042d53a7SEvalZero     }
119*042d53a7SEvalZero 
120*042d53a7SEvalZero     if (out_buf != NULL) {
121*042d53a7SEvalZero         *out_buf = buf;
122*042d53a7SEvalZero     }
123*042d53a7SEvalZero 
124*042d53a7SEvalZero     return 0;
125*042d53a7SEvalZero }
126*042d53a7SEvalZero 
127*042d53a7SEvalZero /**
128*042d53a7SEvalZero  * Mallocs a block of memory and initializes an mbuf pool to use it.  The
129*042d53a7SEvalZero  * specified block_size indicates the size of an mbuf acquired from the pool if
130*042d53a7SEvalZero  * it does not contain a pkthdr.
131*042d53a7SEvalZero  *
132*042d53a7SEvalZero  * @param mempool               The mempool to initialize.
133*042d53a7SEvalZero  * @param mbuf_pool             The mbuf pool to initialize.
134*042d53a7SEvalZero  * @param num_blocks            The total number of mbufs in the pool.
135*042d53a7SEvalZero  * @param block_size            The size of each mbuf.
136*042d53a7SEvalZero  * @param name                  The name to give the mempool.
137*042d53a7SEvalZero  * @param out_buf               On success, this points to the malloced memory.
138*042d53a7SEvalZero  *                                  Pass NULL if you don't need this
139*042d53a7SEvalZero  *                                  information.
140*042d53a7SEvalZero  *
141*042d53a7SEvalZero  * @return                      0 on success;
142*042d53a7SEvalZero  *                              OS_ENOMEM on malloc failure;
143*042d53a7SEvalZero  *                              Other OS code on unexpected error.
144*042d53a7SEvalZero  */
145*042d53a7SEvalZero int
mem_malloc_mbuf_pool(struct os_mempool * mempool,struct os_mbuf_pool * mbuf_pool,uint16_t num_blocks,uint32_t block_size,char * name,void ** out_buf)146*042d53a7SEvalZero mem_malloc_mbuf_pool(struct os_mempool *mempool,
147*042d53a7SEvalZero                      struct os_mbuf_pool *mbuf_pool, uint16_t num_blocks,
148*042d53a7SEvalZero                      uint32_t block_size, char *name,
149*042d53a7SEvalZero                      void **out_buf)
150*042d53a7SEvalZero {
151*042d53a7SEvalZero     void *buf;
152*042d53a7SEvalZero     int rc;
153*042d53a7SEvalZero 
154*042d53a7SEvalZero     block_size = OS_ALIGN(block_size + sizeof (struct os_mbuf), OS_ALIGNMENT);
155*042d53a7SEvalZero 
156*042d53a7SEvalZero     rc = mem_malloc_mempool(mempool, num_blocks, block_size, name, &buf);
157*042d53a7SEvalZero     if (rc != 0) {
158*042d53a7SEvalZero         return rc;
159*042d53a7SEvalZero     }
160*042d53a7SEvalZero 
161*042d53a7SEvalZero     rc = os_mbuf_pool_init(mbuf_pool, mempool, block_size, num_blocks);
162*042d53a7SEvalZero     if (rc != 0) {
163*042d53a7SEvalZero         free(buf);
164*042d53a7SEvalZero         return rc;
165*042d53a7SEvalZero     }
166*042d53a7SEvalZero 
167*042d53a7SEvalZero     if (out_buf != NULL) {
168*042d53a7SEvalZero         *out_buf = buf;
169*042d53a7SEvalZero     }
170*042d53a7SEvalZero 
171*042d53a7SEvalZero     return 0;
172*042d53a7SEvalZero }
173*042d53a7SEvalZero 
174*042d53a7SEvalZero /**
175*042d53a7SEvalZero  * Mallocs a block of memory and initializes an mbuf pool to use it.  The
176*042d53a7SEvalZero  * specified block_size indicates the size of an mbuf acquired from the pool if
177*042d53a7SEvalZero  * it contains a pkthdr.
178*042d53a7SEvalZero  *
179*042d53a7SEvalZero  * @param mempool               The mempool to initialize.
180*042d53a7SEvalZero  * @param mbuf_pool             The mbuf pool to initialize.
181*042d53a7SEvalZero  * @param num_blocks            The total number of mbufs in the pool.
182*042d53a7SEvalZero  * @param block_size            The size of each mbuf.
183*042d53a7SEvalZero  * @param name                  The name to give the mempool.
184*042d53a7SEvalZero  * @param out_buf               On success, this points to the malloced memory.
185*042d53a7SEvalZero  *                                  Pass NULL if you don't need this
186*042d53a7SEvalZero  *                                  information.
187*042d53a7SEvalZero  *
188*042d53a7SEvalZero  * @return                      0 on success;
189*042d53a7SEvalZero  *                              OS_ENOMEM on malloc failure;
190*042d53a7SEvalZero  *                              Other OS code on unexpected error.
191*042d53a7SEvalZero  */
192*042d53a7SEvalZero int
mem_malloc_mbufpkt_pool(struct os_mempool * mempool,struct os_mbuf_pool * mbuf_pool,int num_blocks,int block_size,char * name,void ** out_buf)193*042d53a7SEvalZero mem_malloc_mbufpkt_pool(struct os_mempool *mempool,
194*042d53a7SEvalZero                         struct os_mbuf_pool *mbuf_pool, int num_blocks,
195*042d53a7SEvalZero                         int block_size, char *name,
196*042d53a7SEvalZero                         void **out_buf)
197*042d53a7SEvalZero {
198*042d53a7SEvalZero     int rc;
199*042d53a7SEvalZero 
200*042d53a7SEvalZero     rc = mem_malloc_mbuf_pool(mempool, mbuf_pool, num_blocks,
201*042d53a7SEvalZero                               block_size + sizeof (struct os_mbuf_pkthdr),
202*042d53a7SEvalZero                               name, out_buf);
203*042d53a7SEvalZero     return rc;
204*042d53a7SEvalZero }
205*042d53a7SEvalZero 
206*042d53a7SEvalZero int
mem_init_mbuf_pool(void * mem,struct os_mempool * mempool,struct os_mbuf_pool * mbuf_pool,int num_blocks,int block_size,char * name)207*042d53a7SEvalZero mem_init_mbuf_pool(void *mem, struct os_mempool *mempool,
208*042d53a7SEvalZero                    struct os_mbuf_pool *mbuf_pool, int num_blocks,
209*042d53a7SEvalZero                    int block_size, char *name)
210*042d53a7SEvalZero {
211*042d53a7SEvalZero     int rc;
212*042d53a7SEvalZero 
213*042d53a7SEvalZero     rc = os_mempool_init(mempool, num_blocks, block_size, mem, name);
214*042d53a7SEvalZero     if (rc != 0) {
215*042d53a7SEvalZero         return rc;
216*042d53a7SEvalZero     }
217*042d53a7SEvalZero 
218*042d53a7SEvalZero     rc = os_mbuf_pool_init(mbuf_pool, mempool, block_size, num_blocks);
219*042d53a7SEvalZero     if (rc != 0) {
220*042d53a7SEvalZero         return rc;
221*042d53a7SEvalZero     }
222*042d53a7SEvalZero 
223*042d53a7SEvalZero     return 0;
224*042d53a7SEvalZero }
225*042d53a7SEvalZero 
226*042d53a7SEvalZero /*
227*042d53a7SEvalZero  * Splits an appropriately-sized fragment from the front of an mbuf chain, as
228*042d53a7SEvalZero  * neeeded.  If the length of the mbuf chain greater than specified maximum
229*042d53a7SEvalZero  * fragment size, a new mbuf is allocated, and data is moved from the source
230*042d53a7SEvalZero  * mbuf to the new mbuf.  If the mbuf chain is small enough to fit in a single
231*042d53a7SEvalZero  * fragment, the source mbuf itself is returned unmodified, and the suplied
232*042d53a7SEvalZero  * pointer is set to NULL.
233*042d53a7SEvalZero  *
234*042d53a7SEvalZero  * This function is expected to be called in a loop until the entire mbuf chain
235*042d53a7SEvalZero  * has been consumed.  For example:
236*042d53a7SEvalZero  *
237*042d53a7SEvalZero  *     struct os_mbuf *frag;
238*042d53a7SEvalZero  *     struct os_mbuf *rsp;
239*042d53a7SEvalZero  *     // [...]
240*042d53a7SEvalZero  *     while (rsp != NULL) {
241*042d53a7SEvalZero  *         frag = mem_split_frag(&rsp, get_mtu(), frag_alloc, NULL);
242*042d53a7SEvalZero  *         if (frag == NULL) {
243*042d53a7SEvalZero  *             os_mbuf_free_chain(rsp);
244*042d53a7SEvalZero  *             return SYS_ENOMEM;
245*042d53a7SEvalZero  *         }
246*042d53a7SEvalZero  *         send_packet(frag)
247*042d53a7SEvalZero  *     }
248*042d53a7SEvalZero  *
249*042d53a7SEvalZero  * @param om                    The packet to fragment.  Upon fragmentation,
250*042d53a7SEvalZero  *                                  this mbuf is adjusted such that the
251*042d53a7SEvalZero  *                                  fragment data is removed.  If the packet
252*042d53a7SEvalZero  *                                  constitutes a single fragment, this gets
253*042d53a7SEvalZero  *                                  set to NULL on success.
254*042d53a7SEvalZero  * @param max_frag_sz           The maximum payload size of a fragment.
255*042d53a7SEvalZero  *                                  Typically this is the MTU of the
256*042d53a7SEvalZero  *                                  connection.
257*042d53a7SEvalZero  * @param alloc_cb              Points to a function that allocates an mbuf to
258*042d53a7SEvalZero  *                                  hold a fragment.  This function gets called
259*042d53a7SEvalZero  *                                  before the source mbuf chain is modified,
260*042d53a7SEvalZero  *                                  so it can safely inspect it.
261*042d53a7SEvalZero  * @param cb_arg                Generic parameter that gets passed to the
262*042d53a7SEvalZero  *                                  callback function.
263*042d53a7SEvalZero  *
264*042d53a7SEvalZero  * @return                      The next fragment to send on success;
265*042d53a7SEvalZero  *                              NULL on failure.
266*042d53a7SEvalZero  */
267*042d53a7SEvalZero struct os_mbuf *
mem_split_frag(struct os_mbuf ** om,uint16_t max_frag_sz,mem_frag_alloc_fn * alloc_cb,void * cb_arg)268*042d53a7SEvalZero mem_split_frag(struct os_mbuf **om, uint16_t max_frag_sz,
269*042d53a7SEvalZero                mem_frag_alloc_fn *alloc_cb, void *cb_arg)
270*042d53a7SEvalZero {
271*042d53a7SEvalZero     struct os_mbuf *frag;
272*042d53a7SEvalZero     int rc;
273*042d53a7SEvalZero 
274*042d53a7SEvalZero     if (OS_MBUF_PKTLEN(*om) <= max_frag_sz) {
275*042d53a7SEvalZero         /* Final fragment. */
276*042d53a7SEvalZero         frag = *om;
277*042d53a7SEvalZero         *om = NULL;
278*042d53a7SEvalZero         return frag;
279*042d53a7SEvalZero     }
280*042d53a7SEvalZero 
281*042d53a7SEvalZero     /* Packet needs to be split.  Allocate a new buffer for the fragment. */
282*042d53a7SEvalZero     frag = alloc_cb(max_frag_sz, cb_arg);
283*042d53a7SEvalZero     if (frag == NULL) {
284*042d53a7SEvalZero         goto err;
285*042d53a7SEvalZero     }
286*042d53a7SEvalZero 
287*042d53a7SEvalZero     /* Move data from the front of the packet into the fragment mbuf. */
288*042d53a7SEvalZero     rc = os_mbuf_appendfrom(frag, *om, 0, max_frag_sz);
289*042d53a7SEvalZero     if (rc != 0) {
290*042d53a7SEvalZero         goto err;
291*042d53a7SEvalZero     }
292*042d53a7SEvalZero     os_mbuf_adj(*om, max_frag_sz);
293*042d53a7SEvalZero 
294*042d53a7SEvalZero     /* Free unused portion of of source mbuf chain, if possible. */
295*042d53a7SEvalZero     *om = os_mbuf_trim_front(*om);
296*042d53a7SEvalZero 
297*042d53a7SEvalZero     return frag;
298*042d53a7SEvalZero 
299*042d53a7SEvalZero err:
300*042d53a7SEvalZero     os_mbuf_free_chain(frag);
301*042d53a7SEvalZero     return NULL;
302*042d53a7SEvalZero }
303