Receive latest UDP Packet from IP-Task

Background

Specs:

  • FreeRTOS Version: 10.3.1
  • Hardware: CPU ARM Cortex A9
  • Communication Protocol: Ethernet UDP (high traffic)

Expectations

Our application calls FreeRTOS_recvfrom() while there are messages waiting using the field xWaitingPacketsList.
At the end of the while loop we expect to handle the latest UDP packet that recevied in our channel.

Reality

Our application receives the latest package in our socket but not in the IPTask (channel) queue.

Investigation & Conclusions

  • We have Windows PC that sends UDP packets in high traffic to our target board.
  • Each packet has an ID Number that we can track them in our application
  • Our application receives the packets and handle them
  • At some point we stop the PC from sending data and we compare between the ID in our application to the latest ID packet that the PC sent
  • We are able to see that the PC holds a higher ID then our application
    Note:
    If we fake the filed WaitingMessages in the IPTask.NetworkQueue.WaitingMessages, the application handles the latest ID packet. Is it possible the the IPTask received multiple packets in one RxEvent and sends the first packet (the oldest) to our socket?

Questions

  1. We want to be able to get the latest UDP packet that was sent to our application. The previous packets are irrelevant to us therefore it is acceptable to ignore them.
    How can we achieve such behavior?
  2. How can we configure IPTask queue to sent the latest packet to the socket?

Thanks.

Hey @hpaskal,

As this is a very custom requirement I don’t think there is a way to do this in the FreeRTOS+TCP without modifying the stack itself.

If I understand your question correctly the following logic should work. Though this can be added in multiple places in the stack, I would try to do it in the sockets recvfrom implementation (FreeRTOS_recvfrom) if I want to easily preserve the usual behavior of the UDP sockets as well:

diff --git a/source/FreeRTOS_Sockets.c b/source/FreeRTOS_Sockets.c
index af1248a..8804c95 100644
--- a/source/FreeRTOS_Sockets.c
+++ b/source/FreeRTOS_Sockets.c
@@ -958,6 +958,24 @@ int32_t FreeRTOS_recvfrom( const ConstSocket_t xSocket,
         {
             taskENTER_CRITICAL();
             {
+
+                if( ( ( UBaseType_t ) xFlags & ( UBaseType_t ) FREERTOS_DISCARD_OLD ) != 0U )
+                {
+                    while (lPacketCount > 1)
+                    {
+                        /* The owner of the list item is the network buffer. */
+                        pxNetworkBuffer = ( ( NetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &( pxSocket->u.xUDP.xWaitingPacketsList ) ) );      
+                        /* Remove the network buffer from the list of buffers waiting to
+                        * be processed by the socket. */
+                        ( void ) uxListRemove( &( pxNetworkBuffer->xBufferListItem ) );
+
+                        /* Release the buffer */
+                        vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
+
+                        lPacketCount = ( BaseType_t ) listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xWaitingPacketsList ) );
+                    }
+                }
+
                 /* The owner of the list item is the network buffer. */
                 pxNetworkBuffer = ( ( NetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &( pxSocket->u.xUDP.xWaitingPacketsList

Note: Non tested code.

FREERTOS_DISCARD_OLD is a custom flag to enable the behavior.

UDP packets are NOT guaranteed to be delivered in order and there are multiple factors that can impact delivery (such as network congestion). As a result, you cannot guarantee that the latest packet received by the stack was the latest packet sent by the other party. You should, therefore, re-think if you want your application to rely on this behavior.

Is it possible for you to determine whether the packet you get from FreeRTOS_recvfrom is stale, then discard if it is stale and call FreeRTOS_recvfrom again?

Tony Josi’s sample code helped me to understand what you actually want.
Mind that vReleaseNetworkBufferAndDescriptor() may not be handled from a critical section.

You can also use FreeRTOS_udp_rx_size().
It returns the number of packets queued in xUDP.xWaitingPacketsList.

It is disabled with an #if 0 because we didn’t have the time to document it. But it works.

int32_t lCount;
do
{
    lCount = FreeRTOS_recvfrom( xSocket,
                                pcBuffer,
                                sizeof pcBuffer,
                                0U,
                                &xFromAddress,
                                &uxLength );
    if( lCount <= 0 )
    {
        break;
    }
    if( FreeRTOS_udp_rx_size() == 0 )
    {
        /* This was the latest in the queue. */
        break;
    }
    /* Loop to read the next packet. */
}

I do find it strange that packets get out-of-order, or that that get dropped along the way.
I have never seen that a UDP packet got dropped or swapped on a LAN. I would also recommend to run WireShark and make a long PCAP, using a “udp.port=xx” filter.

If you have implemented the logging macro FreeRTOS_printf(), you may want to call vPrintResourceStats() in every loop of the NetworkInterface EMAC task like here.

It will print warnings when the IP-stack runs out of resources.

1 Like

Unfortunately UDP packets can get reordered (nowadays) with smarter network cards with multiple internal parallel packet processing queues. Since there is no guarantee about the order it’s done and it happens in reality especially with high traffic load.

1 Like

It is also possible for udp packets to simply get dropped under high load.

1 Like