1 /*
2 * FreeRTOS+TCP <DEVELOPMENT BRANCH>
3 * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy of
8 * this software and associated documentation files (the "Software"), to deal in
9 * the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11 * the Software, and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * http://aws.amazon.com/freertos
25 * http://www.FreeRTOS.org
26 */
27
28 /******************************************************************************
29 *
30 * See the following web page for essential buffer allocation scheme usage and
31 * configuration details:
32 * http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Buffer_Management.html
33 *
34 ******************************************************************************/
35
36 /* Standard includes. */
37 #include <stdint.h>
38
39 /* FreeRTOS includes. */
40 #include "FreeRTOS.h"
41 #include "task.h"
42 #include "queue.h"
43 #include "semphr.h"
44
45 /* FreeRTOS+TCP includes. */
46 #include "FreeRTOS_IP.h"
47 #include "FreeRTOS_IP_Private.h"
48 #include "NetworkInterface.h"
49 #include "NetworkBufferManagement.h"
50
51 /* For an Ethernet interrupt to be able to obtain a network buffer there must
52 * be at least this number of buffers available. */
53 #define baINTERRUPT_BUFFER_GET_THRESHOLD ( 3 )
54
55 /* A list of free (available) NetworkBufferDescriptor_t structures. */
56 static List_t xFreeBuffersList;
57
58 /* Some statistics about the use of buffers. */
59 static UBaseType_t uxMinimumFreeNetworkBuffers = 0U;
60
61 /* Declares the pool of NetworkBufferDescriptor_t structures that are available
62 * to the system. All the network buffers referenced from xFreeBuffersList exist
63 * in this array. The array is not accessed directly except during initialisation,
64 * when the xFreeBuffersList is filled (as all the buffers are free when the system
65 * is booted). */
66 static NetworkBufferDescriptor_t xNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ];
67
68 /* This constant is defined as true to let FreeRTOS_TCP_IP.c know that the
69 * network buffers have constant size, large enough to hold the biggest Ethernet
70 * packet. No resizing will be done. */
71 const BaseType_t xBufferAllocFixedSize = pdTRUE;
72
73 /* The semaphore used to obtain network buffers. */
74 static SemaphoreHandle_t xNetworkBufferSemaphore = NULL;
75
76 #if ( ipconfigTCP_IP_SANITY != 0 )
77 static char cIsLow = pdFALSE;
78 UBaseType_t bIsValidNetworkDescriptor( const NetworkBufferDescriptor_t * pxDesc );
79 #else
80 static UBaseType_t bIsValidNetworkDescriptor( const NetworkBufferDescriptor_t * pxDesc );
81 #endif /* ipconfigTCP_IP_SANITY */
82
83 static void prvShowWarnings( void );
84
85 /* The user can define their own ipconfigBUFFER_ALLOC_LOCK() and
86 * ipconfigBUFFER_ALLOC_UNLOCK() macros, especially for use form an ISR. If these
87 * are not defined then default them to call the normal enter/exit critical
88 * section macros. */
89 #if !defined( ipconfigBUFFER_ALLOC_LOCK )
90
91 #define ipconfigBUFFER_ALLOC_INIT() do {} while( ipFALSE_BOOL )
92 #define ipconfigBUFFER_ALLOC_LOCK_FROM_ISR() \
93 UBaseType_t uxSavedInterruptStatus = ( UBaseType_t ) portSET_INTERRUPT_MASK_FROM_ISR(); \
94 {
95 #define ipconfigBUFFER_ALLOC_UNLOCK_FROM_ISR() \
96 portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); \
97 }
98
99 #define ipconfigBUFFER_ALLOC_LOCK() taskENTER_CRITICAL()
100 #define ipconfigBUFFER_ALLOC_UNLOCK() taskEXIT_CRITICAL()
101
102 #endif /* ipconfigBUFFER_ALLOC_LOCK */
103
104 /*-----------------------------------------------------------*/
105
106 #if ( ipconfigTCP_IP_SANITY != 0 )
107
108 /* HT: SANITY code will be removed as soon as the library is stable
109 * and and ready to become public
110 * Function below gives information about the use of buffers */
111 #define WARN_LOW ( 2 )
112 #define WARN_HIGH ( ( 5 * ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ) / 10 )
113
114 #endif /* ipconfigTCP_IP_SANITY */
115
116 /*-----------------------------------------------------------*/
117
118 #if ( ipconfigTCP_IP_SANITY != 0 )
119
prvIsFreeBuffer(const NetworkBufferDescriptor_t * pxDescr)120 BaseType_t prvIsFreeBuffer( const NetworkBufferDescriptor_t * pxDescr )
121 {
122 return ( bIsValidNetworkDescriptor( pxDescr ) != 0 ) &&
123 ( listIS_CONTAINED_WITHIN( &xFreeBuffersList, &( pxDescr->xBufferListItem ) ) != 0 );
124 }
125 /*-----------------------------------------------------------*/
126
prvShowWarnings(void)127 static void prvShowWarnings( void )
128 {
129 UBaseType_t uxCount = uxGetNumberOfFreeNetworkBuffers();
130
131 if( ( ( cIsLow == 0 ) && ( uxCount <= WARN_LOW ) ) || ( ( cIsLow != 0 ) && ( uxCount >= WARN_HIGH ) ) )
132 {
133 cIsLow = !cIsLow;
134 FreeRTOS_debug_printf( ( "*** Warning *** %s %lu buffers left\n", cIsLow ? "only" : "now", uxCount ) );
135 }
136 }
137 /*-----------------------------------------------------------*/
138
bIsValidNetworkDescriptor(const NetworkBufferDescriptor_t * pxDesc)139 UBaseType_t bIsValidNetworkDescriptor( const NetworkBufferDescriptor_t * pxDesc )
140 {
141 uint32_t offset = ( uint32_t ) ( ( ( const char * ) pxDesc ) - ( ( const char * ) xNetworkBuffers ) );
142
143 if( ( offset >= sizeof( xNetworkBuffers ) ) ||
144 ( ( offset % sizeof( xNetworkBuffers[ 0 ] ) ) != 0 ) )
145 {
146 return pdFALSE;
147 }
148
149 return ( UBaseType_t ) ( pxDesc - xNetworkBuffers ) + 1;
150 }
151 /*-----------------------------------------------------------*/
152
153 #else /* if ( ipconfigTCP_IP_SANITY != 0 ) */
bIsValidNetworkDescriptor(const NetworkBufferDescriptor_t * pxDesc)154 static UBaseType_t bIsValidNetworkDescriptor( const NetworkBufferDescriptor_t * pxDesc )
155 {
156 ( void ) pxDesc;
157 return ( UBaseType_t ) pdTRUE;
158 }
159 /*-----------------------------------------------------------*/
160
prvShowWarnings(void)161 static void prvShowWarnings( void )
162 {
163 }
164 /*-----------------------------------------------------------*/
165
166 #endif /* ipconfigTCP_IP_SANITY */
167
xNetworkBuffersInitialise(void)168 BaseType_t xNetworkBuffersInitialise( void )
169 {
170 BaseType_t xReturn;
171 uint32_t x;
172
173 /* Only initialise the buffers and their associated kernel objects if they
174 * have not been initialised before. */
175 if( xNetworkBufferSemaphore == NULL )
176 {
177 /* In case alternative locking is used, the mutexes can be initialised
178 * here */
179 ipconfigBUFFER_ALLOC_INIT();
180
181 #if ( configSUPPORT_STATIC_ALLOCATION == 1 )
182 {
183 static StaticSemaphore_t xNetworkBufferSemaphoreBuffer;
184 xNetworkBufferSemaphore = xSemaphoreCreateCountingStatic(
185 ( UBaseType_t ) ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS,
186 ( UBaseType_t ) ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS,
187 &xNetworkBufferSemaphoreBuffer );
188 }
189 #else
190 {
191 xNetworkBufferSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, ( UBaseType_t ) ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS );
192 }
193 #endif /* configSUPPORT_STATIC_ALLOCATION */
194
195 configASSERT( xNetworkBufferSemaphore != NULL );
196
197 if( xNetworkBufferSemaphore != NULL )
198 {
199 vListInitialise( &xFreeBuffersList );
200
201 /* Initialise all the network buffers. The buffer storage comes
202 * from the network interface, and different hardware has different
203 * requirements. */
204 vNetworkInterfaceAllocateRAMToBuffers( xNetworkBuffers );
205
206 for( x = 0U; x < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; x++ )
207 {
208 /* Initialise and set the owner of the buffer list items. */
209 vListInitialiseItem( &( xNetworkBuffers[ x ].xBufferListItem ) );
210 listSET_LIST_ITEM_OWNER( &( xNetworkBuffers[ x ].xBufferListItem ), &xNetworkBuffers[ x ] );
211
212 /* Currently, all buffers are available for use. */
213 vListInsert( &xFreeBuffersList, &( xNetworkBuffers[ x ].xBufferListItem ) );
214 }
215
216 uxMinimumFreeNetworkBuffers = ( UBaseType_t ) ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS;
217 }
218 }
219
220 if( xNetworkBufferSemaphore == NULL )
221 {
222 xReturn = pdFAIL;
223 }
224 else
225 {
226 xReturn = pdPASS;
227 }
228
229 return xReturn;
230 }
231 /*-----------------------------------------------------------*/
232
pxGetNetworkBufferWithDescriptor(size_t xRequestedSizeBytes,TickType_t xBlockTimeTicks)233 NetworkBufferDescriptor_t * pxGetNetworkBufferWithDescriptor( size_t xRequestedSizeBytes,
234 TickType_t xBlockTimeTicks )
235 {
236 NetworkBufferDescriptor_t * pxReturn = NULL;
237 BaseType_t xInvalid = pdFALSE;
238 UBaseType_t uxCount;
239
240 /* The current implementation only has a single size memory block, so
241 * the requested size parameter is not used (yet). */
242 ( void ) xRequestedSizeBytes;
243
244 if( xNetworkBufferSemaphore != NULL )
245 {
246 /* If there is a semaphore available, there is a network buffer
247 * available. */
248 if( xSemaphoreTake( xNetworkBufferSemaphore, xBlockTimeTicks ) == pdPASS )
249 {
250 /* Protect the structure as it is accessed from tasks and
251 * interrupts. */
252 ipconfigBUFFER_ALLOC_LOCK();
253 {
254 pxReturn = ( NetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &xFreeBuffersList );
255
256 if( ( bIsValidNetworkDescriptor( pxReturn ) != pdFALSE_UNSIGNED ) &&
257 listIS_CONTAINED_WITHIN( &xFreeBuffersList, &( pxReturn->xBufferListItem ) ) )
258 {
259 ( void ) uxListRemove( &( pxReturn->xBufferListItem ) );
260 }
261 else
262 {
263 xInvalid = pdTRUE;
264 }
265 }
266 ipconfigBUFFER_ALLOC_UNLOCK();
267
268 if( xInvalid == pdTRUE )
269 {
270 /* _RB_ Can printf() be called from an interrupt? (comment
271 * above says this can be called from an interrupt too) */
272
273 /* _HT_ The function shall not be called from an ISR. Comment
274 * was indeed misleading. Hopefully clear now?
275 * So the printf()is OK here. */
276 FreeRTOS_debug_printf( ( "pxGetNetworkBufferWithDescriptor: INVALID BUFFER: %p (valid %lu)\n",
277 pxReturn, bIsValidNetworkDescriptor( pxReturn ) ) );
278 pxReturn = NULL;
279 }
280 else
281 {
282 /* Reading UBaseType_t, no critical section needed. */
283 uxCount = listCURRENT_LIST_LENGTH( &xFreeBuffersList );
284
285 /* For stats, latch the lowest number of network buffers since
286 * booting. */
287 if( uxMinimumFreeNetworkBuffers > uxCount )
288 {
289 uxMinimumFreeNetworkBuffers = uxCount;
290 }
291
292 pxReturn->xDataLength = xRequestedSizeBytes;
293 pxReturn->pxInterface = NULL;
294 pxReturn->pxEndPoint = NULL;
295
296 #if ( ipconfigTCP_IP_SANITY != 0 )
297 {
298 prvShowWarnings();
299 }
300 #endif /* ipconfigTCP_IP_SANITY */
301
302 #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
303 {
304 /* make sure the buffer is not linked */
305 pxReturn->pxNextBuffer = NULL;
306 }
307 #endif /* ipconfigUSE_LINKED_RX_MESSAGES */
308 }
309
310 iptraceNETWORK_BUFFER_OBTAINED( pxReturn );
311 }
312 else
313 {
314 /* lint wants to see at least a comment. */
315 iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER();
316 }
317 }
318
319 return pxReturn;
320 }
321 /*-----------------------------------------------------------*/
322
pxNetworkBufferGetFromISR(size_t xRequestedSizeBytes)323 NetworkBufferDescriptor_t * pxNetworkBufferGetFromISR( size_t xRequestedSizeBytes )
324 {
325 NetworkBufferDescriptor_t * pxReturn = NULL;
326
327 /* The current implementation only has a single size memory block, so
328 * the requested size parameter is not used (yet). */
329 ( void ) xRequestedSizeBytes;
330
331 /* If there is a semaphore available then there is a buffer available, but,
332 * as this is called from an interrupt, only take a buffer if there are at
333 * least baINTERRUPT_BUFFER_GET_THRESHOLD buffers remaining. This prevents,
334 * to a certain degree at least, a rapidly executing interrupt exhausting
335 * buffer and in so doing preventing tasks from continuing. */
336 if( uxQueueMessagesWaitingFromISR( ( QueueHandle_t ) xNetworkBufferSemaphore ) > ( UBaseType_t ) baINTERRUPT_BUFFER_GET_THRESHOLD )
337 {
338 if( xSemaphoreTakeFromISR( xNetworkBufferSemaphore, NULL ) == pdPASS )
339 {
340 /* Protect the structure as it is accessed from tasks and interrupts. */
341 ipconfigBUFFER_ALLOC_LOCK_FROM_ISR();
342 {
343 pxReturn = ( NetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &xFreeBuffersList );
344 uxListRemove( &( pxReturn->xBufferListItem ) );
345 }
346 ipconfigBUFFER_ALLOC_UNLOCK_FROM_ISR();
347
348 iptraceNETWORK_BUFFER_OBTAINED_FROM_ISR( pxReturn );
349 }
350 }
351
352 if( pxReturn == NULL )
353 {
354 iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER_FROM_ISR();
355 }
356
357 return pxReturn;
358 }
359 /*-----------------------------------------------------------*/
360
vNetworkBufferReleaseFromISR(NetworkBufferDescriptor_t * const pxNetworkBuffer)361 BaseType_t vNetworkBufferReleaseFromISR( NetworkBufferDescriptor_t * const pxNetworkBuffer )
362 {
363 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
364
365 /* Ensure the buffer is returned to the list of free buffers before the
366 * counting semaphore is 'given' to say a buffer is available. */
367 ipconfigBUFFER_ALLOC_LOCK_FROM_ISR();
368 {
369 vListInsertEnd( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) );
370 }
371 ipconfigBUFFER_ALLOC_UNLOCK_FROM_ISR();
372
373 ( void ) xSemaphoreGiveFromISR( xNetworkBufferSemaphore, &xHigherPriorityTaskWoken );
374 iptraceNETWORK_BUFFER_RELEASED( pxNetworkBuffer );
375
376 return xHigherPriorityTaskWoken;
377 }
378 /*-----------------------------------------------------------*/
379
vReleaseNetworkBufferAndDescriptor(NetworkBufferDescriptor_t * const pxNetworkBuffer)380 void vReleaseNetworkBufferAndDescriptor( NetworkBufferDescriptor_t * const pxNetworkBuffer )
381 {
382 BaseType_t xListItemAlreadyInFreeList;
383
384 if( bIsValidNetworkDescriptor( pxNetworkBuffer ) == pdFALSE_UNSIGNED )
385 {
386 FreeRTOS_debug_printf( ( "vReleaseNetworkBufferAndDescriptor: Invalid buffer %p\n", pxNetworkBuffer ) );
387 }
388 else
389 {
390 /* Ensure the buffer is returned to the list of free buffers before the
391 * counting semaphore is 'given' to say a buffer is available. */
392 ipconfigBUFFER_ALLOC_LOCK();
393 {
394 {
395 xListItemAlreadyInFreeList = listIS_CONTAINED_WITHIN( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) );
396
397 if( xListItemAlreadyInFreeList == pdFALSE )
398 {
399 vListInsertEnd( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) );
400 }
401 }
402 }
403 ipconfigBUFFER_ALLOC_UNLOCK();
404
405 if( xListItemAlreadyInFreeList )
406 {
407 FreeRTOS_debug_printf( ( "vReleaseNetworkBufferAndDescriptor: %p ALREADY RELEASED (now %lu)\n",
408 pxNetworkBuffer, uxGetNumberOfFreeNetworkBuffers() ) );
409 }
410 else
411 {
412 ( void ) xSemaphoreGive( xNetworkBufferSemaphore );
413 prvShowWarnings();
414 }
415
416 iptraceNETWORK_BUFFER_RELEASED( pxNetworkBuffer );
417 }
418 }
419 /*-----------------------------------------------------------*/
420
uxGetMinimumFreeNetworkBuffers(void)421 UBaseType_t uxGetMinimumFreeNetworkBuffers( void )
422 {
423 return uxMinimumFreeNetworkBuffers;
424 }
425 /*-----------------------------------------------------------*/
426
uxGetNumberOfFreeNetworkBuffers(void)427 UBaseType_t uxGetNumberOfFreeNetworkBuffers( void )
428 {
429 return listCURRENT_LIST_LENGTH( &xFreeBuffersList );
430 }
431
pxResizeNetworkBufferWithDescriptor(NetworkBufferDescriptor_t * pxNetworkBuffer,size_t xNewSizeBytes)432 NetworkBufferDescriptor_t * pxResizeNetworkBufferWithDescriptor( NetworkBufferDescriptor_t * pxNetworkBuffer,
433 size_t xNewSizeBytes )
434 {
435 /* In BufferAllocation_1.c all network buffer are allocated with a
436 * maximum size of 'ipTOTAL_ETHERNET_FRAME_SIZE'.No need to resize the
437 * network buffer. */
438 pxNetworkBuffer->xDataLength = xNewSizeBytes;
439 return pxNetworkBuffer;
440 }
441
442 /*#endif */ /* ipconfigINCLUDE_TEST_CODE */
443