xref: /FreeRTOS-Plus-TCP-v4.0.0/source/portable/BufferManagement/BufferAllocation_1.c (revision 245d5879903cfc6e52a466fc1067fe54f869740c)
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