This commit adds an initial implemented of TCP delayed ACKs as specified in RFC 1122.

Squashed commit of the following:

    net/tmp:  Rename the unacked field of the tcp connection structure to tx_unacked.  Too confusing with the implementation of delayed RX ACKs.

    net/tcp:  Initial implementation of TCP delayed ACKs.

    net/tcp:  Add delayed ACK configuration selection.  Rename tcp_ack() to tcp_synack().  It may or may not send a ACK.  It will always send SYN or SYN/ACK.
This commit is contained in:
Gregory Nutt 2019-12-08 13:13:51 -06:00
parent 594734e0ae
commit 66ef6d143a
12 changed files with 174 additions and 71 deletions

View File

@ -164,7 +164,7 @@ static uint16_t tcp_close_eventhandler(FAR struct net_driver_s *dev,
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
/* Check if all outstanding bytes have been ACKed */
if (conn->unacked != 0 || !sq_empty(&conn->write_q))
if (conn->tx_unacked != 0 || !sq_empty(&conn->write_q))
{
/* No... we are still waiting for ACKs. Drop any received data, but
* do not yet report TCP_CLOSE in the response.

View File

@ -557,7 +557,7 @@ static uint16_t tcp_send_eventhandler(FAR struct net_driver_s *dev,
/* Increment the count of bytes sent, the number of unacked bytes,
* and the total count of TCP packets sent.
*
* NOTE: tcp_appsend() normally increments conn->unacked based on
* NOTE: tcp_appsend() normally increments conn->tx_unacked based on
* the value of dev->d_sndlen. However, dev->d_len is always
* zero for 6LoWPAN since it does not send via the dev->d_buf
* but, rather, uses a backdoor frame interface with the IEEE
@ -565,21 +565,21 @@ static uint16_t tcp_send_eventhandler(FAR struct net_driver_s *dev,
*/
sinfo->s_sent += sndlen;
conn->unacked += sndlen;
conn->tx_unacked += sndlen;
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
/* For compability with buffered send logic */
conn->sndseq_max = tcp_addsequence(conn->sndseq, conn->unacked);
conn->sndseq_max = tcp_addsequence(conn->sndseq, conn->tx_unacked);
#endif
#ifdef CONFIG_NET_STATISTICS
g_netstats.tcp.sent++;
#endif
ninfo("Sent: acked=%d sent=%d buflen=%d unacked=%d\n",
ninfo("Sent: acked=%d sent=%d buflen=%d tx_unacked=%d\n",
sinfo->s_acked, sinfo->s_sent, sinfo->s_buflen,
conn->unacked);
conn->tx_unacked);
}
}
@ -612,7 +612,7 @@ end_wait:
/* There are no outstanding, unacknowledged bytes */
conn->unacked = 0;
conn->tx_unacked = 0;
/* Wake up the waiting thread */
@ -710,7 +710,7 @@ static int sixlowpan_send_packet(FAR struct socket *psock,
* initial sequence number.
*/
conn->unacked = 0;
conn->tx_unacked = 0;
/* Notify the IEEE802.15.4 MAC that we have data to send. */

View File

@ -20,6 +20,20 @@ config NET_TCP_NO_STACK
if NET_TCP && !NET_TCP_NO_STACK
config NET_TCP_DELAYED_ACK
bool "TCP/IP Delayed ACK"
default n
---help---
RFC 1122: A host that is receiving a stream of TCP data segments
can increase efficiency in both the Internet and the hosts
by sending fewer than one ACK (acknowledgment) segment per data
segment received; this is known as a "delayed ACK".
TCP should implement a delayed ACK, but an ACK should not be
excessively delayed; in particular, the delay MUST be less than
0.5 seconds, and in a stream of full-sized segments there should
be an ACK for at least every second segments.
config NET_TCP_KEEPALIVE
bool "TCP/IP Keep-alive support"
default n

View File

@ -175,15 +175,19 @@ struct tcp_conn_s
uint8_t timer; /* The retransmission timer (units: half-seconds) */
uint8_t nrtx; /* The number of retransmissions for the last
* segment sent */
#ifdef CONFIG_NET_TCP_DELAYED_ACK
uint8_t rx_unackseg; /* Number of un-ACKed received segments */
uint8_t rx_acktimer; /* Time since last ACK sent (units: half-seconds) */
#endif
uint16_t lport; /* The local TCP port, in network byte order */
uint16_t rport; /* The remoteTCP port, in network byte order */
uint16_t mss; /* Current maximum segment size for the
* connection */
uint16_t winsize; /* Current window size of the connection */
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
uint32_t unacked; /* Number bytes sent but not yet ACKed */
uint32_t tx_unacked; /* Number bytes sent but not yet ACKed */
#else
uint16_t unacked; /* Number bytes sent but not yet ACKed */
uint16_t tx_unacked; /* Number bytes sent but not yet ACKed */
#endif
/* If the TCP socket is bound to a local address, then this is
@ -941,7 +945,7 @@ ssize_t tcp_sendfile(FAR struct socket *psock, FAR struct file *infile,
void tcp_reset(FAR struct net_driver_s *dev);
/****************************************************************************
* Name: tcp_ack
* Name: tcp_synack
*
* Description:
* Send the SYN or SYNACK response.
@ -959,7 +963,7 @@ void tcp_reset(FAR struct net_driver_s *dev);
*
****************************************************************************/
void tcp_ack(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
void tcp_synack(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
uint8_t ack);
/****************************************************************************

View File

@ -85,10 +85,49 @@ void tcp_appsend(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
{
uint8_t hdrlen;
/* Handle the result based on the application response */
ninfo("result: %04x d_sndlen: %d conn->tx_unacked: %d\n",
result, dev->d_sndlen, conn->tx_unacked);
ninfo("result: %04x d_sndlen: %d conn->unacked: %d\n",
result, dev->d_sndlen, conn->unacked);
#ifdef CONFIG_NET_TCP_DELAYED_ACK
/* Did the caller request that an ACK be sent? */
if ((result & TCP_SNDACK) != 0)
{
/* Yes.. Handle delayed acknowledgments */
/* Reset the ACK timer in any event. */
conn->rx_acktimer = 0;
/* Per RFC 1122: "...there SHOULD be an ACK for at least every second
* segment."
*/
if (conn->rx_unackseg > 0)
{
/* Reset the delayed ACK state and send the ACK with this packet. */
conn->rx_unackseg = 0;
}
else
{
/* Indicate that there is one un-ACKed segment and don't send the
* ACK on this pack.
*/
result &= ~TCP_SNDACK;
conn->rx_unackseg = 1;
/* A special case is if there are not other flags and no TCP TX
* data payload. In this case, don't send anything.*/
if (result == 0 && dev->d_sndlen == 0)
{
return;
}
}
}
#endif
/* Get the IP header length associated with the IP domain configured for
* this TCP connection.
@ -143,7 +182,7 @@ void tcp_appsend(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
else if ((result & TCP_CLOSE) != 0)
{
conn->tcpstateflags = TCP_FIN_WAIT_1;
conn->unacked = 1;
conn->tx_unacked = 1;
conn->nrtx = 0;
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
conn->sndseq_max = tcp_getsequence(conn->sndseq) + 1;
@ -172,10 +211,10 @@ void tcp_appsend(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
* do not go through this path.
*/
conn->unacked += dev->d_sndlen;
conn->tx_unacked += dev->d_sndlen;
/* The application cannot send more than what is allowed by the
* MSS (the minumum of the MSS and the available window).
* MSS (the minimum of the MSS and the available window).
*/
DEBUGASSERT(dev->d_sndlen <= conn->mss);
@ -183,6 +222,7 @@ void tcp_appsend(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
conn->nrtx = 0;
#endif
/* Then handle the rest of the operation just as for the rexmit case */
tcp_rexmit(dev, conn, result);
@ -213,8 +253,8 @@ void tcp_rexmit(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
{
uint8_t hdrlen;
ninfo("result: %04x d_sndlen: %d conn->unacked: %d\n",
result, dev->d_sndlen, conn->unacked);
ninfo("result: %04x d_sndlen: %d conn->tx_unacked: %d\n",
result, dev->d_sndlen, conn->tx_unacked);
/* Get the IP header length associated with the IP domain configured for
* this TCP connection.
@ -247,7 +287,7 @@ void tcp_rexmit(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
if (dev->d_sndlen > 0)
#else
if (dev->d_sndlen > 0 && conn->unacked > 0)
if (dev->d_sndlen > 0 && conn->tx_unacked > 0)
#endif
{
/* We always set the ACK flag in response packets adding the length of

View File

@ -1008,7 +1008,7 @@ FAR struct tcp_conn_s *tcp_alloc_accept(FAR struct net_driver_s *dev,
conn->tcpstateflags = TCP_SYN_RCVD;
tcp_initsequence(conn->sndseq);
conn->unacked = 1;
conn->tx_unacked = 1;
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
conn->expired = 0;
conn->isn = 0;
@ -1243,7 +1243,7 @@ int tcp_connect(FAR struct tcp_conn_s *conn, FAR const struct sockaddr *addr)
conn->tcpstateflags = TCP_SYN_SENT;
tcp_initsequence(conn->sndseq);
conn->unacked = 1; /* TCP length of the SYN is one. */
conn->tx_unacked = 1; /* TCP length of the SYN is one. */
conn->nrtx = 0;
conn->timer = 1; /* Send the SYN next time around. */
conn->rto = TCP_RTO;

View File

@ -295,7 +295,7 @@ static void tcp_input(FAR struct net_driver_s *dev, uint8_t domain,
/* Our response will be a SYNACK. */
tcp_ack(dev, conn, TCP_ACK | TCP_SYN);
tcp_synack(dev, conn, TCP_ACK | TCP_SYN);
return;
}
}
@ -416,7 +416,7 @@ found:
(tcp->flags & (TCP_SYN | TCP_FIN | TCP_RST)) == 0 &&
(conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED &&
(dev->d_len == 0 || dev->d_len == 1) &&
conn->unacked <= 0)
conn->tx_unacked <= 0)
{
uint32_t ackseq;
uint32_t rcvseq;
@ -471,20 +471,20 @@ found:
* data, calculate RTT estimations, and reset the retransmission timer.
*/
if ((tcp->flags & TCP_ACK) != 0 && conn->unacked > 0)
if ((tcp->flags & TCP_ACK) != 0 && conn->tx_unacked > 0)
{
uint32_t unackseq;
uint32_t ackseq;
/* The next sequence number is equal to the current sequence
* number (sndseq) plus the size of the outstanding, unacknowledged
* data (unacked).
* data (tx_unacked).
*/
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
unackseq = conn->sndseq_max;
#else
unackseq = tcp_addsequence(conn->sndseq, conn->unacked);
unackseq = tcp_addsequence(conn->sndseq, conn->tx_unacked);
#endif
/* Get the sequence number of that has just been acknowledged by this
@ -504,7 +504,7 @@ found:
{
/* Calculate the new number of outstanding, unacknowledged bytes */
conn->unacked = unackseq - ackseq;
conn->tx_unacked = unackseq - ackseq;
}
else
{
@ -517,11 +517,11 @@ found:
if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED)
{
nwarn("WARNING: ackseq > unackseq\n");
nwarn(" sndseq=%u unacked=%u unackseq=%u ackseq=%u\n",
tcp_getsequence(conn->sndseq), conn->unacked, unackseq,
nwarn(" sndseq=%u tx_unacked=%u unackseq=%u ackseq=%u\n",
tcp_getsequence(conn->sndseq), conn->tx_unacked, unackseq,
ackseq);
conn->unacked = 0;
conn->tx_unacked = 0;
}
}
@ -530,8 +530,8 @@ found:
* be beyond ackseq.
*/
ninfo("sndseq: %08x->%08x unackseq: %08x new unacked: %d\n",
tcp_getsequence(conn->sndseq), ackseq, unackseq, conn->unacked);
ninfo("sndseq: %08x->%08x unackseq: %08x new tx_unacked: %d\n",
tcp_getsequence(conn->sndseq), ackseq, unackseq, conn->tx_unacked);
tcp_setsequence(conn->sndseq, ackseq);
/* Do RTT estimation, unless we have done retransmissions. */
@ -619,7 +619,7 @@ found:
conn->sent = 0;
conn->sndseq_max = 0;
#endif
conn->unacked = 0;
conn->tx_unacked = 0;
flags = TCP_CONNECTED;
ninfo("TCP state: TCP_ESTABLISHED\n");
@ -639,7 +639,7 @@ found:
if ((tcp->flags & TCP_CTL) == TCP_SYN)
{
tcp_ack(dev, conn, TCP_ACK | TCP_SYN);
tcp_synack(dev, conn, TCP_ACK | TCP_SYN);
return;
}
@ -714,7 +714,7 @@ found:
memcpy(conn->rcvseq, tcp->seqno, 4);
net_incr32(conn->rcvseq, 1);
conn->unacked = 0;
conn->tx_unacked = 0;
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
conn->isn = tcp_getsequence(tcp->ackno);
@ -770,7 +770,7 @@ found:
* if the connection is going to be closed.
*/
#if 0
if (conn->unacked > 0)
if (conn->tx_unacked > 0)
{
goto drop;
}
@ -791,7 +791,7 @@ found:
(void)tcp_callback(dev, conn, flags);
conn->tcpstateflags = TCP_LAST_ACK;
conn->unacked = 1;
conn->tx_unacked = 1;
conn->nrtx = 0;
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
conn->sndseq_max = tcp_getsequence(conn->sndseq) + 1;
@ -943,7 +943,7 @@ found:
if ((tcp->flags & TCP_FIN) != 0)
{
if ((flags & TCP_ACKDATA) != 0 && conn->unacked == 0)
if ((flags & TCP_ACKDATA) != 0 && conn->tx_unacked == 0)
{
conn->tcpstateflags = TCP_TIME_WAIT;
conn->timer = 0;
@ -960,7 +960,7 @@ found:
tcp_send(dev, conn, TCP_ACK, tcpiplen);
return;
}
else if ((flags & TCP_ACKDATA) != 0 && conn->unacked == 0)
else if ((flags & TCP_ACKDATA) != 0 && conn->tx_unacked == 0)
{
conn->tcpstateflags = TCP_FIN_WAIT_2;
ninfo("TCP state: TCP_FIN_WAIT_2\n");

View File

@ -547,10 +547,10 @@ void tcp_reset(FAR struct net_driver_s *dev)
}
/****************************************************************************
* Name: tcp_ack
* Name: tcp_synack
*
* Description:
* Send the SYN or SYNACK response.
* Send the SYN, ACK, or SYNACK response.
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
@ -565,7 +565,7 @@ void tcp_reset(FAR struct net_driver_s *dev)
*
****************************************************************************/
void tcp_ack(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
void tcp_synack(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
uint8_t ack)
{
struct tcp_hdr_s *tcp;
@ -609,7 +609,7 @@ void tcp_ack(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
tcp->flags = ack;
/* We send out the TCP Maximum Segment Size option with our ack. */
/* We send out the TCP Maximum Segment Size option with our ACK. */
tcp->optdata[0] = TCP_OPT_MSS;
tcp->optdata[1] = TCP_OPT_MSS_LEN;

View File

@ -531,13 +531,13 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev,
/* Yes.. Reset the number of bytes sent sent from the write buffer */
sent = TCP_WBSENT(wrb);
if (conn->unacked > sent)
if (conn->tx_unacked > sent)
{
conn->unacked -= sent;
conn->tx_unacked -= sent;
}
else
{
conn->unacked = 0;
conn->tx_unacked = 0;
}
if (conn->sent > sent)
@ -550,8 +550,8 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev,
}
TCP_WBSENT(wrb) = 0;
ninfo("REXMIT: wrb=%p sent=%u, conn unacked=%d sent=%d\n",
wrb, TCP_WBSENT(wrb), conn->unacked, conn->sent);
ninfo("REXMIT: wrb=%p sent=%u, conn tx_unacked=%d sent=%d\n",
wrb, TCP_WBSENT(wrb), conn->tx_unacked, conn->sent);
/* Increment the retransmit count on this write buffer. */
@ -603,13 +603,13 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev,
/* Reset the number of bytes sent sent from the write buffer */
sent = TCP_WBSENT(wrb);
if (conn->unacked > sent)
if (conn->tx_unacked > sent)
{
conn->unacked -= sent;
conn->tx_unacked -= sent;
}
else
{
conn->unacked = 0;
conn->tx_unacked = 0;
}
if (conn->sent > sent)
@ -622,8 +622,8 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev,
}
TCP_WBSENT(wrb) = 0;
ninfo("REXMIT: wrb=%p sent=%u, conn unacked=%d sent=%d\n",
wrb, TCP_WBSENT(wrb), conn->unacked, conn->sent);
ninfo("REXMIT: wrb=%p sent=%u, conn tx_unacked=%d sent=%d\n",
wrb, TCP_WBSENT(wrb), conn->tx_unacked, conn->sent);
/* Free any write buffers that have exceed the retry count */
@ -767,7 +767,7 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev,
* number calculations.
*/
conn->unacked += sndlen;
conn->tx_unacked += sndlen;
conn->sent += sndlen;
/* Below prediction will become true, unless retransmission occurrence */
@ -780,8 +780,8 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev,
conn->sndseq_max = predicted_seqno;
}
ninfo("SEND: wrb=%p nrtx=%u unacked=%u sent=%u\n",
wrb, TCP_WBNRTX(wrb), conn->unacked, conn->sent);
ninfo("SEND: wrb=%p nrtx=%u tx_unacked=%u sent=%u\n",
wrb, TCP_WBNRTX(wrb), conn->tx_unacked, conn->sent);
/* Increment the count of bytes sent from this write buffer */

View File

@ -530,7 +530,7 @@ end_wait:
/* There are no outstanding, unacknowledged bytes */
conn->unacked = 0;
conn->tx_unacked = 0;
/* Wake up the waiting thread */
@ -747,7 +747,7 @@ ssize_t psock_tcp_send(FAR struct socket *psock,
* initial sequence number.
*/
conn->unacked = 0;
conn->tx_unacked = 0;
/* Set the initial time for calculating timeouts */

View File

@ -614,7 +614,7 @@ ssize_t tcp_sendfile(FAR struct socket *psock, FAR struct file *infile,
* initial sequence number.
*/
conn->unacked = 0;
conn->tx_unacked = 0;
#ifdef CONFIG_NET_SOCKOPTS
/* Set the initial time for calculating timeouts */

View File

@ -60,6 +60,20 @@
#include "socket/socket.h"
#include "tcp/tcp.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Per RFC 1122: "... an ACK should not be excessively delayed; in
* particular, the delay MUST be less than 0.5 seconds ..."
*
* NOTE: We only have 0.5 timing resolution here so the delay will be
* between 0.5 and 1.0 seconds, and may be delayed further, depending on the
* polling rate of the the driver (often 1 second).
*/
#define ACK_DELAY (1)
/****************************************************************************
* Public Functions
****************************************************************************/
@ -187,7 +201,7 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
* retransmit.
*/
if (conn->unacked > 0)
if (conn->tx_unacked > 0)
{
/* The connection has outstanding data */
@ -315,14 +329,14 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
* SYNACK.
*/
tcp_ack(dev, conn, TCP_ACK | TCP_SYN);
tcp_synack(dev, conn, TCP_ACK | TCP_SYN);
goto done;
case TCP_SYN_SENT:
/* In the SYN_SENT state, we retransmit out SYN. */
tcp_ack(dev, conn, TCP_SYN);
tcp_synack(dev, conn, TCP_SYN);
goto done;
case TCP_ESTABLISHED:
@ -454,7 +468,7 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
* dummy byte that we just sent.
*/
conn->unacked++;
conn->tx_unacked++;
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
/* Increment the un-ACKed sequence number */
@ -472,9 +486,40 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
}
#endif
#ifdef CONFIG_NET_TCP_DELAYED_ACK
/* Handle delayed acknowledgments. Is there a segment with a
* delayed acknowledgment?
*/
if (conn->rx_unackseg > 0)
{
/* Increment the ACK delay. */
conn->rx_acktimer += hsec;
/* Per RFC 1122: "...an ACK should not be excessively
* delayed; in particular, the delay must be less than
* 0.5 seconds..."
*/
if (conn->rx_acktimer >= ACK_DELAY)
{
/* Reset the delayed ACK state and send the ACK
* packet.
*/
conn->rx_unackseg = 0;
conn->rx_acktimer = 0;
tcp_synack(dev, conn, TCP_ACK);
goto done;
}
}
#endif
/* There was no need for a retransmission and there was no
* need to probe the remote peer. We poll the application for
* new outgoing data.
* need to probe the remote peer and there was no need to
* send a delayed ACK. We poll the application for new
* outgoing data.
*/
result = tcp_callback(dev, conn, TCP_POLL);