+TCP UDP processing using FreeRTOS_recvfrom and Zero-Copy

joehinkle wrote on Wednesday, August 31, 2016:

I’m using FreeRTOS_recvfrom with the FREERTOS_ZERO_COPY flag.

Please confirm that the message received via this call is directly from the network buffer with no mid-stream coping.

Also, I am expecting a “group” of these messages being received back-to-back with little time between them. Since the stack is running at a hight priority than my task calling FreeRTOS_recvfrom, will the stack drop or “queue” another incoming message if I have not finished initial processing of the previous message and gotten back to issue another FreeRTOS_recvfrom call?

Thanks.

Joe

rtel wrote on Wednesday, August 31, 2016:

Please confirm that the message received via this call is directly from
the network buffer with no mid-stream coping.

Assuming your network driver does not itself copy the data - then yes -
at least for UDP, its more complicated for TCP.

Also, I am expecting a “group” of these messages being received
back-to-back with little time between them. Since the stack is running
at a hight priority than my task calling FreeRTOS_recvfrom, will the
stack drop or “queue” another incoming message if I have not finished
initial processing of the previous message and gotten back to issue
another FreeRTOS_recvfrom call?

Again - assuming your driver does not drop messages - then it will queue
messages until you run out of network buffers. If you run out of
network buffers then it will drop packets. If dropped packets form part
of a TCP stream then the TCP protocol will ensure the packets are
re-transmitted.

heinbali01 wrote on Thursday, September 01, 2016:

Joe,

The name “+TCP stack” can be misleading, because it includes both +UDP ( the former version of FreeRTOS’ IP-stack ) and +TCP.

A UDP packet travels on its own from end-point to end-point. And as +TCP doesn’t implement Ethernet fragmentation, there will be single Ethernet message per UDP packet ( Fragmentation would make it possible to transmit larger datagrams ).

Only TCP sockets have a stream buffer.

UDP sockets are much simpler. A UDP socket puts the received Network Buffers into a List. As Richard wrote, UDP packets can travel zero-copy, all the way from DMA-reception in the EMAC until the user application.

In FreeRTOS_DHCP.c you find an example of zero-copy reception:

uint8_t *pucUDPPayload;
int32_t lBytes;

lBytes = FreeRTOS_recvfrom( xDHCPData.xDHCPSocket, ( void * ) &pucUDPPayload,
    0ul, FREERTOS_ZERO_COPY, &xClient, &xClientLength );
if( lBytes > 0 )
{
    /* Use the 'lBytes' UDP payload data. */
    FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayload );
}

Note that pucUDPPayload is a bit of a special pointer. It points somewhere halfway in the pucEthernetBuffer of a NetworkBufferDescriptor_t.
The Ethernet Header, the IP-header and UDP-header are skipped. pucUDPPayload points to the first data-byte of the UDP-payload.

FreeRTOS_ReleaseUDPPayloadBuffer() will find the pucEthernetBuffer and it will release the original NetworkBufferDescriptor_t.

Does that make sense?

I think that the FREERTOS_ZERO_COPY flag is exactly what you are looking for.

I am expecting a “group” of these messages being received back-to-back
with little time between them.

Normally when you receive N packets, the network driver will queue N messages for the IP-task. When packets are received in a burst, the driver can link them together in a chain and put a single message into the queue. You can reach quite a performance gain with this technique:

#define ipconfigUSE_LINKED_RX_MESSAGES    1

Find an example at the bottom of this post.

Since the stack is running at a higher priority than my task calling
FreeRTOS_recvfrom, will the stack drop or “queue” another incoming
message if I have not finished initial processing of the previous
message and gotten back to issue another FreeRTOS_recvfrom call?

As Richard wrote: the driver will queue-up packets as long as there are Network Buffers available.

Example of using linked RX messages:

#if( ipconfigUSE_LINKED_RX_MESSAGES != 0 )

    void linked_rx_receive()
    {
    NetworkBufferDescriptor_t *pxHead = NULL;
    NetworkBufferDescriptor_t *pxTail = NULL;

        for( ;; )
        {
            /* driver_receive() returns the next available packet.
            It may not block. When nothing has arrived, it returns
            NULL. */

            pxNetworkBuffer = driver_receive();

            if( pxNetworkBuffer == NULL )
            {
                /* That's it for now. */
                break;
            }
            if( pxHead == NULL )
            {
                /* Remember the first packet. */
                pxHead = pxNetworkBuffer;
            }
            if( pxTail != NULL )
            {
                /* Make the link */
                pxTail->pxNextBuffer = pxNetworkBuffer;
            }
            /* Remember the last packet. */
            pxTail = pxNetworkBuffer;
        }
        if( pxHead != NULL )
        {
            IPStackEvent_t xRxEvent;

            /* Just make sure that the last item has no next buffer: */
            pxTail->pxNextBuffer = NULL;

            xRxEvent.eEventType = eNetworkRxEvent;
            xRxEvent.pvData = ( void * )pxHead;

            /* Send a linked list of packets. */
            if( xSendEventStructToIPTask( &xRxEvent, ( portTickType ) 1000 ) != pdPASS )
            {
                /* Sending failed, make sure to release every linked Network Buffer! */
            }
        }
    }
#endif /* ipconfigUSE_LINKED_RX_MESSAGES != 0 */