xref: /FreeRTOS-Plus-TCP-v3.1.0/source/portable/BufferManagement/BufferAllocation_1.c (revision 37bdfe577f3b728058de714e2e747d3c78803f26)
1 /*
2  * FreeRTOS+TCP V3.1.0
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 
294                 #if ( ipconfigTCP_IP_SANITY != 0 )
295                     {
296                         prvShowWarnings();
297                     }
298                 #endif /* ipconfigTCP_IP_SANITY */
299 
300                 #if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
301                     {
302                         /* make sure the buffer is not linked */
303                         pxReturn->pxNextBuffer = NULL;
304                     }
305                 #endif /* ipconfigUSE_LINKED_RX_MESSAGES */
306             }
307 
308             iptraceNETWORK_BUFFER_OBTAINED( pxReturn );
309         }
310         else
311         {
312             /* lint wants to see at least a comment. */
313             iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER();
314         }
315     }
316 
317     return pxReturn;
318 }
319 /*-----------------------------------------------------------*/
320 
pxNetworkBufferGetFromISR(size_t xRequestedSizeBytes)321 NetworkBufferDescriptor_t * pxNetworkBufferGetFromISR( size_t xRequestedSizeBytes )
322 {
323     NetworkBufferDescriptor_t * pxReturn = NULL;
324 
325     /* The current implementation only has a single size memory block, so
326      * the requested size parameter is not used (yet). */
327     ( void ) xRequestedSizeBytes;
328 
329     /* If there is a semaphore available then there is a buffer available, but,
330      * as this is called from an interrupt, only take a buffer if there are at
331      * least baINTERRUPT_BUFFER_GET_THRESHOLD buffers remaining.  This prevents,
332      * to a certain degree at least, a rapidly executing interrupt exhausting
333      * buffer and in so doing preventing tasks from continuing. */
334     if( uxQueueMessagesWaitingFromISR( ( QueueHandle_t ) xNetworkBufferSemaphore ) > ( UBaseType_t ) baINTERRUPT_BUFFER_GET_THRESHOLD )
335     {
336         if( xSemaphoreTakeFromISR( xNetworkBufferSemaphore, NULL ) == pdPASS )
337         {
338             /* Protect the structure as it is accessed from tasks and interrupts. */
339             ipconfigBUFFER_ALLOC_LOCK_FROM_ISR();
340             {
341                 pxReturn = ( NetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &xFreeBuffersList );
342                 uxListRemove( &( pxReturn->xBufferListItem ) );
343             }
344             ipconfigBUFFER_ALLOC_UNLOCK_FROM_ISR();
345 
346             iptraceNETWORK_BUFFER_OBTAINED_FROM_ISR( pxReturn );
347         }
348     }
349 
350     if( pxReturn == NULL )
351     {
352         iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER_FROM_ISR();
353     }
354 
355     return pxReturn;
356 }
357 /*-----------------------------------------------------------*/
358 
vNetworkBufferReleaseFromISR(NetworkBufferDescriptor_t * const pxNetworkBuffer)359 BaseType_t vNetworkBufferReleaseFromISR( NetworkBufferDescriptor_t * const pxNetworkBuffer )
360 {
361     BaseType_t xHigherPriorityTaskWoken = pdFALSE;
362 
363     /* Ensure the buffer is returned to the list of free buffers before the
364      * counting semaphore is 'given' to say a buffer is available. */
365     ipconfigBUFFER_ALLOC_LOCK_FROM_ISR();
366     {
367         vListInsertEnd( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) );
368     }
369     ipconfigBUFFER_ALLOC_UNLOCK_FROM_ISR();
370 
371     ( void ) xSemaphoreGiveFromISR( xNetworkBufferSemaphore, &xHigherPriorityTaskWoken );
372     iptraceNETWORK_BUFFER_RELEASED( pxNetworkBuffer );
373 
374     return xHigherPriorityTaskWoken;
375 }
376 /*-----------------------------------------------------------*/
377 
vReleaseNetworkBufferAndDescriptor(NetworkBufferDescriptor_t * const pxNetworkBuffer)378 void vReleaseNetworkBufferAndDescriptor( NetworkBufferDescriptor_t * const pxNetworkBuffer )
379 {
380     BaseType_t xListItemAlreadyInFreeList;
381 
382     if( bIsValidNetworkDescriptor( pxNetworkBuffer ) == pdFALSE_UNSIGNED )
383     {
384         FreeRTOS_debug_printf( ( "vReleaseNetworkBufferAndDescriptor: Invalid buffer %p\n", pxNetworkBuffer ) );
385     }
386     else
387     {
388         /* Ensure the buffer is returned to the list of free buffers before the
389          * counting semaphore is 'given' to say a buffer is available. */
390         ipconfigBUFFER_ALLOC_LOCK();
391         {
392             {
393                 xListItemAlreadyInFreeList = listIS_CONTAINED_WITHIN( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) );
394 
395                 if( xListItemAlreadyInFreeList == pdFALSE )
396                 {
397                     vListInsertEnd( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) );
398                 }
399             }
400         }
401         ipconfigBUFFER_ALLOC_UNLOCK();
402 
403         if( xListItemAlreadyInFreeList )
404         {
405             FreeRTOS_debug_printf( ( "vReleaseNetworkBufferAndDescriptor: %p ALREADY RELEASED (now %lu)\n",
406                                      pxNetworkBuffer, uxGetNumberOfFreeNetworkBuffers() ) );
407         }
408         else
409         {
410             ( void ) xSemaphoreGive( xNetworkBufferSemaphore );
411             prvShowWarnings();
412         }
413 
414         iptraceNETWORK_BUFFER_RELEASED( pxNetworkBuffer );
415     }
416 }
417 /*-----------------------------------------------------------*/
418 
uxGetMinimumFreeNetworkBuffers(void)419 UBaseType_t uxGetMinimumFreeNetworkBuffers( void )
420 {
421     return uxMinimumFreeNetworkBuffers;
422 }
423 /*-----------------------------------------------------------*/
424 
uxGetNumberOfFreeNetworkBuffers(void)425 UBaseType_t uxGetNumberOfFreeNetworkBuffers( void )
426 {
427     return listCURRENT_LIST_LENGTH( &xFreeBuffersList );
428 }
429 
pxResizeNetworkBufferWithDescriptor(NetworkBufferDescriptor_t * pxNetworkBuffer,size_t xNewSizeBytes)430 NetworkBufferDescriptor_t * pxResizeNetworkBufferWithDescriptor( NetworkBufferDescriptor_t * pxNetworkBuffer,
431                                                                  size_t xNewSizeBytes )
432 {
433     /* In BufferAllocation_1.c all network buffer are allocated with a
434      * maximum size of 'ipTOTAL_ETHERNET_FRAME_SIZE'.No need to resize the
435      * network buffer. */
436     pxNetworkBuffer->xDataLength = xNewSizeBytes;
437     return pxNetworkBuffer;
438 }
439 
440 /*#endif */ /* ipconfigINCLUDE_TEST_CODE */
441