Mbed OS Reference
Loading...
Searching...
No Matches
NetStackMemoryManager.h
1/*
2 * Copyright (c) 2018 ARM Limited
3 * SPDX-License-Identifier: Apache-2.0
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#ifndef NET_STACK_MEMORY_MANAGER_H
19#define NET_STACK_MEMORY_MANAGER_H
20
21#include <optional>
22
23/**
24 * Network Stack interface memory manager
25 *
26 * \par
27 * This interface provides abstraction for memory modules used in different IP stacks (often to accommodate zero
28 * copy). NetStack interface is required to accept output packets and provide received data using this stack-
29 * independent API. This header should be implemented for each IP stack, so that we keep EMAC module independent.
30 *
31 * \par
32 * NetStack memory interface uses memory buffer chains to store data. Data passed in either direction
33 * may either be contiguous (a single-buffer chain), or may consist of multiple buffers.
34 * Chaining of the buffers is made using singly-linked list. The NetStack data-passing APIs do not specify
35 * alignment or structure of the chain in either direction.
36 *
37 * \par
38 * Memory buffers can be allocated either from heap or from memory pools. Heap buffers are always contiguous.
39 * Memory pool buffers may be either contiguous or chained depending on allocation size. By LwIP convention,
40 * the pool should only be used for Rx packets -- the EMAC may opt to keep buffers pre-allocated from the pool
41 * for receiving packets into. This is done because LwIP will do special stuff when the pool runs out of space,
42 * e.g. flushing TCP out-of-sequence segment buffers to free up memory.
43 *
44 * On NetStack interface buffer chain ownership is transferred. EMAC must free buffer chain that it is given for
45 * link output and the stack must free the buffer chain that it is given for link input.
46 *
47 */
48
49#include "nsapi.h"
50#include "Callback.h"
51
52// Opaque struct representing a memory buffer or a chain of memory buffers.
53typedef void net_stack_mem_buf_t;
54
56protected:
57 /// Callback which shall be called (if set) by the implementation after one or more buffer spaces
58 /// become free in the pool. This is used by zero-copy Ethernet MACs as a hint that
59 /// now is a good time to allocate fresh buffers off the pool into Ethernet descriptors.
60 /// It *is* legal to call this function if you aren't totally sure new memory is available --
61 /// the mac will try to allocate more buffers, and if it can't, oh well.
62 /// However, it is not legal for memory to become available without a call to this function.
63 /// Such a situation might lead to a lockup of the MAC due to not having memory allocated for Rx.
64 // TODO this eventually needs to get converted to a list once we support boards with more than 1 EMAC
66
67public:
68
69 /**
70 * Allocates memory buffer from the heap
71 *
72 * Memory buffer allocated from heap is always contiguous and can be arbitrary size.
73 *
74 * @param size Size of the memory to allocate in bytes
75 * @param align Memory alignment requirement in bytes
76 * @return Allocated memory buffer, or NULL in case of error
77 */
78 virtual net_stack_mem_buf_t *alloc_heap(uint32_t size, uint32_t align) = 0;
79
80 /**
81 * Allocates memory buffer chain from a pool
82 *
83 * Memory allocated from pool is contiguous if size is equal or less than
84 * (aligned) allocation unit, otherwise may be chained. Will typically come from
85 * fixed-size packet pool memory.
86 *
87 * @param size Total size of the memory to allocate in bytes
88 * @param align Memory alignment requirement for each buffer in bytes
89 * @return Allocated memory buffer chain, or NULL in case of error
90 */
91 virtual net_stack_mem_buf_t *alloc_pool(uint32_t size, uint32_t align) = 0;
92
93 /**
94 * @brief Reallocates a buffer or buffer chain as a contiguous (non-chained) heap buffer, freeing the original.
95 *
96 * Only the visible data in the source buffer is copied, not any skipped headers. Data from chained
97 * buffers *will* be copied.
98 *
99 * @param orig_buf Original buffer. Will be freed whether or not the new buffer is allocated.
100 * @param new_align Alignment to allocate the new buffer with
101 * @param new_len If set, this length will be used instead of the buffer's original length
102 * @param new_header_skip_size If set, this header skip size will be set on the new buffer. If unset, no header skip will be set
103 *
104 * @return Pointer to new buffer, or nullptr if allocation failed.
105 */
106 net_stack_mem_buf_t *realloc_heap(net_stack_mem_buf_t *orig_buf, uint32_t new_align, std::optional<uint32_t> new_len = std::nullopt, std::optional<uint16_t> new_header_skip_size = std::nullopt);
107
108 /**
109 * Get memory buffer pool allocation unit
110 *
111 * Returns the maximum size of contiguous memory that can be allocated from a pool.
112 *
113 * @param align Memory alignment requirement in bytes
114 * @return Contiguous memory size
115 */
116 virtual uint32_t get_pool_alloc_unit(uint32_t align) const = 0;
117
118 /**
119 * Get memory buffer pool size.
120 *
121 * @return The number of pool buffers that can be allocated at any one time
122 */
123 uint32_t get_pool_size()
124 {
125#if UNITTEST
126 return 0;
127#else
128 return MBED_CONF_NSAPI_EMAC_RX_POOL_NUM_BUFS;
129#endif
130 }
131
132 /**
133 * Free memory buffer chain
134 *
135 * Frees all buffers from the chained list.
136 *
137 * @param buf Memory buffer chain to be freed.
138 */
139 virtual void free(net_stack_mem_buf_t *buf) = 0;
140
141 /**
142 * Return total length of a memory buffer chain
143 *
144 * Returns a total length of this buffer and any following buffers in the chain.
145 *
146 * @param buf Memory buffer chain
147 * @return Total length in bytes
148 */
149 virtual uint32_t get_total_len(const net_stack_mem_buf_t *buf) const = 0;
150
151 /**
152 * @brief Copy from a raw buffer in memory to a memory buffer chain
153 *
154 * Copies data to a buffer chain. Copy operation does not adjust the lengths
155 * of the copied-to memory buffer chain, so chain total length must match the
156 * copied length.
157 *
158 * @param to_buf Memory buffer chain to copy to
159 * @param ptr Pointer to data
160 * @param len Data length
161 */
162 virtual void copy_to_buf(net_stack_mem_buf_t *to_buf, const void *ptr, uint32_t len);
163
164 /**
165 * @brief Copy from a memory buffer chain to a raw buffer in memory.
166 *
167 * Header skip bytes are processed, so the copy will begin AFTER the header bytes of
168 * \c from_buf
169 *
170 * @param len Data length
171 * @param ptr Pointer to data
172 * @param from_buf Memory buffer chain to copy from
173 * @return Length of the data that was copied
174 */
175 virtual uint32_t copy_from_buf(void *ptr, uint32_t len, const net_stack_mem_buf_t *from_buf) const;
176
177 /**
178 * Concatenate two memory buffer chains
179 *
180 * Concatenates buffer chain to end of the other buffer chain. Concatenated-to buffer total length
181 * is adjusted accordingly. cat_buf must point to the start of a the chain. After concatenation
182 * to_buf's chain now owns those buffers, and they will be freed when the to_buf chain is freed.
183 *
184 * @warning It is forbidden for \c cat_buf to have skipped header bytes.
185 *
186 * @param to_buf Memory buffer chain to concatenate to
187 * @param cat_buf Memory buffer chain to concatenate
188 */
189 virtual void cat(net_stack_mem_buf_t *to_buf, net_stack_mem_buf_t *cat_buf) = 0;
190
191 /**
192 * Returns the next buffer
193 *
194 * Returns the next buffer from the memory buffer chain.
195 *
196 * @param buf Memory buffer
197 * @return The next memory buffer, or NULL if last
198 */
199 virtual net_stack_mem_buf_t *get_next(const net_stack_mem_buf_t *buf) const = 0;
200
201 /**
202 * @brief Count the number of buffers in a buffer chain
203 *
204 * @param buf Memory buffer
205 * @return The number of buffers in the chain
206 */
207 size_t count_buffers(const net_stack_mem_buf_t *buf)
208 {
209 size_t count = 0;
210 while (buf != nullptr) {
211 count += 1;
212 buf = get_next(buf);
213 }
214 return count;
215 }
216
217 /**
218 * Return pointer to the payload of the buffer
219 *
220 * Note that this is affected by the current header skip size (see below)
221 *
222 * @param buf Memory buffer
223 * @return Pointer to the payload
224 */
225 virtual void *get_ptr(const net_stack_mem_buf_t *buf) const = 0;
226
227 /**
228 * Return payload size of this individual buffer (NOT including any chained buffers)
229 *
230 * Note that this is affected by the current header skip size (see below)
231 *
232 * @param buf Memory buffer
233 * @return Size in bytes
234 */
235 virtual uint32_t get_len(const net_stack_mem_buf_t *buf) const = 0;
236
237 /**
238 * Sets the payload size of the buffer
239 *
240 * The allocated payload size will not change. It is not permitted
241 * to change the length of a buffer that is not the first (or only) in a chain.
242 *
243 * Note that this is affected by the current header skip size (see below)
244 *
245 * *Note as of Dec 2024: Different implementations (Nanostack vs LwIP) disagree about
246 * how to implement this operation. Specifically, if called on the head of a buffer
247 * chain, the LwIP implementation allows changing the length of the chain as a whole.
248 * However, the Nanostack implementation does not and only can change the length of the head buffer.
249 * For fear of breaking existing code, I do not want to change this behavior.
250 * So, if constructing a buffer chain, it is safest to set the buffer lengths first before
251 * building the chain.
252 *
253 * @param buf Memory buffer
254 * @param len Payload size, must be less or equal to the allocated size
255 */
256 virtual void set_len(net_stack_mem_buf_t *buf, uint32_t len) = 0;
257
258 /**
259 * @brief Skips (or un-skips) header space from the buffer.
260 *
261 * Skipping n bytes of header space causes the buffer's payload pointer to refer to a location n bytes after
262 * the base address of the packet buffer, and the length of the buffer to be decreased by n.
263 *
264 * This is commonly used to skip protocol headers in network
265 * packets. For example, if you have an Ethernet frame, skipping 14 bytes of header space will cause
266 * the "start" of the packet buffer to point to the IP header instead.
267 *
268 * Multiple calls to this function add together, so for example if you first skip 14 bytes, then -4, then
269 * 10, the result will be 20 total bytes of skipped header.
270 *
271 * @param buf Buffer to operate on.
272 * @param amount Amount of header space to skip. Negative values are allowed and cause
273 * previously skipped header space to be removed.
274 *
275 * @warning Skipping a larger total header space than the size of the first buffer in the chain, or skipping a negative
276 * total header space, results in undefined behavior.
277 */
278 virtual void skip_header_space(net_stack_mem_buf_t *buf, int32_t amount) = 0;
279
280 /**
281 * @brief Restores previously skipped header space to the buffer.
282 *
283 * This function is the inverse of \c skip_header_space().
284 *
285 * @param buf Buffer to operate on.
286 * @param amount Amount of header space to skip. Negative values are allowed and cause
287 * previously skipped header space to be removed.
288 */
289 inline void restore_header_space(net_stack_mem_buf_t *buf, const int32_t amount)
290 {
291 skip_header_space(buf, -1 * amount);
292 }
293
294 /**
295 * @brief Get the total number of header bytes that are currently being skipped.
296 *
297 * This is the aggregate result of all \c skip_header_space() / \c restore_header_space() calls.
298 *
299 * @param buf Buffer to operate on.
300 */
301 virtual int32_t get_header_skip_size(net_stack_mem_buf_t *buf) = 0;
302
303 enum class Lifetime {
304 POOL_ALLOCATED, ///< Allocated from the memory manager's pool
305 HEAP_ALLOCATED, ///< Allocated from the memory manager's heap
306 CONSTANT, ///< Buffer points to constant data (e.g. in ROM) that will live forever
307 VOLATILE ///< Buffer points to data from the application that will not live past the current network stack call.
308 };
309
310 /**
311 * Gets the lifetime of the buffer
312 *
313 * @param buf Memory buffer
314 */
315 virtual Lifetime get_lifetime(net_stack_mem_buf_t const *buf) const = 0;
316
317 /**
318 * @brief Set callback which will be called when pool space becomes available
319 *
320 * \warning The callback could be called from any thread, and should make no assumptions about
321 * being in the same thread as anything else.
322 *
323 * @param cb Callback to call
324 */
329
330protected:
331 ~NetStackMemoryManager() = default;
332};
333
334#endif /* NET_STACK_MEMORY_MANAGER_H */
virtual uint32_t get_pool_alloc_unit(uint32_t align) const =0
Get memory buffer pool allocation unit.
virtual int32_t get_header_skip_size(net_stack_mem_buf_t *buf)=0
Get the total number of header bytes that are currently being skipped.
virtual void copy_to_buf(net_stack_mem_buf_t *to_buf, const void *ptr, uint32_t len)
Copy from a raw buffer in memory to a memory buffer chain.
virtual net_stack_mem_buf_t * alloc_pool(uint32_t size, uint32_t align)=0
Allocates memory buffer chain from a pool.
virtual void cat(net_stack_mem_buf_t *to_buf, net_stack_mem_buf_t *cat_buf)=0
Concatenate two memory buffer chains.
uint32_t get_pool_size()
Get memory buffer pool size.
virtual uint32_t copy_from_buf(void *ptr, uint32_t len, const net_stack_mem_buf_t *from_buf) const
Copy from a memory buffer chain to a raw buffer in memory.
virtual net_stack_mem_buf_t * alloc_heap(uint32_t size, uint32_t align)=0
Allocates memory buffer from the heap.
net_stack_mem_buf_t * realloc_heap(net_stack_mem_buf_t *orig_buf, uint32_t new_align, std::optional< uint32_t > new_len=std::nullopt, std::optional< uint16_t > new_header_skip_size=std::nullopt)
Reallocates a buffer or buffer chain as a contiguous (non-chained) heap buffer, freeing the original.
virtual void * get_ptr(const net_stack_mem_buf_t *buf) const =0
Return pointer to the payload of the buffer.
virtual uint32_t get_len(const net_stack_mem_buf_t *buf) const =0
Return payload size of this individual buffer (NOT including any chained buffers)
virtual void free(net_stack_mem_buf_t *buf)=0
Free memory buffer chain.
@ HEAP_ALLOCATED
Allocated from the memory manager's heap.
@ VOLATILE
Buffer points to data from the application that will not live past the current network stack call.
@ CONSTANT
Buffer points to constant data (e.g. in ROM) that will live forever.
@ POOL_ALLOCATED
Allocated from the memory manager's pool.
virtual void skip_header_space(net_stack_mem_buf_t *buf, int32_t amount)=0
Skips (or un-skips) header space from the buffer.
void restore_header_space(net_stack_mem_buf_t *buf, const int32_t amount)
Restores previously skipped header space to the buffer.
virtual net_stack_mem_buf_t * get_next(const net_stack_mem_buf_t *buf) const =0
Returns the next buffer.
virtual Lifetime get_lifetime(net_stack_mem_buf_t const *buf) const =0
Gets the lifetime of the buffer.
mbed::Callback< void()> onPoolSpaceAvailCallback
Callback which shall be called (if set) by the implementation after one or more buffer spaces become ...
size_t count_buffers(const net_stack_mem_buf_t *buf)
Count the number of buffers in a buffer chain.
void set_on_pool_space_avail_cb(mbed::Callback< void()> cb)
Set callback which will be called when pool space becomes available.
virtual void set_len(net_stack_mem_buf_t *buf, uint32_t len)=0
Sets the payload size of the buffer.
virtual uint32_t get_total_len(const net_stack_mem_buf_t *buf) const =0
Return total length of a memory buffer chain.
Callback class based on template specialization.
Definition Callback.h:53