FreeRTOS+TCP data loss

rwt33 wrote on Thursday, June 11, 2015:

Hi guys,
I’ve recently finished developing a STM32F zero-copy driver port and have been doing some performance tests but are hitting an issue with TCP transfers. I’ve been using the iperf (tested ver 2 & 3) for first loop-back bandwidth tests. In UDP loop-back tests it comfortably achieves 100mb/s full duplex with zero packet loss running for minutes on end.
I’m having problems with TCP transfers though. In basic text (eg CLI) TCP connections it all works fine, but when I try an iperf loop-back Wireshark shows it connects ok, but after several PSH transfers it stops. After a few seconds iperf (or more likely Windows’ IP stack) retries again but after a few SYN & RST ping-pongs it stops again for a few seconds before the next retry. Given the lack of packet loss with UDP transfers it suggests a TCP handshaking sensitivity.
That above explanation may not be enough for you to go by, but would it be possible for you to test a very simple iperf TCP loopback on your known working ports? I ask mainly because it’s actually quite a good standard example that you could use.

Also, if people are interested in the driver port I could package it up and post it.

FYI, on a side note, a heads up on a few tiny website API documentation discrepancies:

  • FreeRTOS_createsocketset is now FreeRTOS_CreateSocketSet in the code
  • FreeRTOS_shutdown links to “FreeRTOS_revc()” (sic) typo. The href itself is correct though.
  • The code example in FreeRTOS_recvfrom doesn’t pass pxSourceAddressLength as a pointer.

Thanks for the support guys.
Rob

heinbali01 wrote on Thursday, June 11, 2015:

Hi Rob, I am preparing a small iperf module that should work for both TCP and UDP.

Does your IPerf code echo the data? If so, that might cause troubles.

The example will soon be ready.

Regards.

rwt33 wrote on Thursday, June 11, 2015:

I’ve tried both. UDP echo is straight forward:

int lReturned = FreeRTOS_recvfrom(xSocket, (void *)&pucUDPPayloadBuffer, 0, FREERTOS_ZERO_COPY, &xEchoServerAddress, &xAddressLength);
if (lReturned > 0)
{
	int lSent = FreeRTOS_sendto(xSocket, (void *)pucUDPPayloadBuffer, lReturned, FREERTOS_ZERO_COPY, &xEchoServerAddress, sizeof(xEchoServerAddress));
	if (lSent <= 0)
	{
		FreeRTOS_ReleaseUDPPayloadBuffer(pucUDPPayloadBuffer);
	}
}

Both TCP sink-only and TCP echo (separate server & client connections) are causing the iperf client to stop.

Echo (server & client connections) example:

for (;;)
{
	int rxLen = FreeRTOS_recv(xServerSocket, tcpEchoBuff, sizeof(tcpEchoBuff), 0);
	if (rxLen >= 0)
	{
		if (rxLen > 0)
		{
			int num = 0;
			do
			{
				int numSent = FreeRTOS_send(xConnectionBackSocket, tcpEchoBuff, rxLen, 0);
				if (numSent <= 0)
					break;
				num += numSent;
			} while (num < rxLen);
		}
	}
	else
		break;
}
FreeRTOS_shutdown(xConnectionBackSocket, FREERTOS_SHUT_RDWR);
while (FreeRTOS_recv(xConnectionBackSocket, tcpEchoBuff, sizeof(tcpEchoBuff), 0) >= 0) { }
FreeRTOS_closesocket(xConnectionBackSocket);

FreeRTOS_shutdown(xServerSocket, FREERTOS_SHUT_RDWR);
while (FreeRTOS_recv(xServerSocket, tcpEchoBuff, sizeof(tcpEchoBuff), 0) >= 0) { }
FreeRTOS_closesocket(xServerSocket);

Regards

rtel wrote on Saturday, June 13, 2015:

Thanks for the documentation corrections - they have been made and will get uploaded to the website shortly.

Regards.

heinbali01 wrote on Monday, June 15, 2015:

Hi Robert,

Thanks for testing the iperf module that I sent you last week. I attach it here (see “iperf_task_v1_0.c”), so that other users can also have a look at it.

“iperf_task_v1_0.c” is a module that can be used on any platform with +TCP. It implements a simple iperf server for both TCP and UDP. When using TCP, it is also able to actively connect to its iperf client and echo back all data received.
When using this double connection, there is no flow control yet. The iperf client may be sending aggressively and thus preventing +TCP from forwarding all data back to the remote client.
Why is that possible? That happens because the reception of data always has priority above transmission. Now suppose that the RX stream already uses 60% of the bandwidth and also a high percentage of the CPU time, the TX stream buffer may run full and data will get dropped. With a bit of extra code this can be prevented.

The loss of data can be seen in the following logging. 32MB was sent and 21.8 MB was received back:

C:\temp>iperf -c 192.168.2.106 --dualtest --port 5001 --num 32M

Server listening on TCP port 5001
TCP window size: 64.0 KByte (default)


Client connecting to 192.168.2.106, TCP port 5001
TCP window size: 64.0 KByte (default)

[ 4] local 192.168.2.3 port 2411 connected with 192.168.2.106 port 5001
[ 5] local 192.168.2.3 port 5001 connected with 192.168.2.106 port 55328
[ ID] Interval Transfer Bandwidth
[ 4] 0.0- 4.6 sec 32.0 MBytes 58.8 Mbits/sec
[ 5] 0.0- 4.6 sec 21.8 MBytes 40.1 Mbits/sec

Note that TCP flow control is fully automatic: if you stop to call FreeRTOS_recv() on a receiving socket, the TCP sliding window will run full, and the remote instance of iperf will temporarily stop sending data. As soon as RX data are popped (by calling FreeRTOS_recv()), a window-change message is sent and the communication will resume.

Regards.

iperf_task_v1_0.c (16.6 KB)