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