diff --git a/Documentation/components/net/delay_act_and_tcp_perf.rst b/Documentation/components/net/delay_act_and_tcp_perf.rst index 50247b9a57f7a..1d3a0154cc6b0 100644 --- a/Documentation/components/net/delay_act_and_tcp_perf.rst +++ b/Documentation/components/net/delay_act_and_tcp_perf.rst @@ -131,9 +131,30 @@ ACKing behavior and you have MSS sizes that are larger that the average size of the user buffers, then your throughput can probably be greatly improved by enabling ``CONFIG_NET_TCP_SPLIT=y`` -NOTE: NuttX is `not` an RFC 1122 recipient; NuttX will ACK +NOTE: NuttX is `not` an RFC 1122 recipient; NuttX will ACK every TCP/IP packet that it receives. +The Delayed ACK Threshold Configuration +======================================= + +The delayed ACK logic is enabled with ``CONFIG_NET_TCP_DELAYED_ACK``. +When enabled, the number of received data segments that trigger one +ACK can be configured with ``CONFIG_NET_TCP_ACK_FREQUENCY``: + +* Value 1 degenerates to an immediate ACK per received segment + (delayed ACK effectively disabled). +* Value 2 (default) is the RFC 1122 compliant behavior: an ACK for + at least every second segment in a stream of full-sized segments. +* Values above 2 reduce ACK traffic and can improve throughput when + bulk data is received, at the cost of a coarser ACK clock and + slower peer congestion window growth. + +NOTE: RFC 1122 Section 4.2.3.2 states that in a stream of full-sized +segments there SHOULD be an ACK for at least every second segment. +Setting ``CONFIG_NET_TCP_ACK_FREQUENCY`` above 2 no longer meets +that requirement. The delayed ACK timer still forces an ACK after +at most 0.5 seconds as required by RFC 1122. + Write Buffering =============== diff --git a/net/tcp/Kconfig b/net/tcp/Kconfig index 09c1dda7cd542..4475ca3d3e3a2 100644 --- a/net/tcp/Kconfig +++ b/net/tcp/Kconfig @@ -35,6 +35,28 @@ config NET_TCP_DELAYED_ACK 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_ACK_FREQUENCY + int "Segments received per ACK (delayed ACK threshold)" + depends on NET_TCP_DELAYED_ACK + range 1 255 + default 2 + ---help--- + Number of received data segments that trigger one ACK (the + delayed ACK threshold). The delayed ACK timer still forces + an ACK after at most 0.5 seconds per RFC 1122. + + Value 1 degenerates to an immediate ACK per received segment + (delayed ACK effectively disabled). Value 2 (default) is the + RFC 1122 compliant behavior (an ACK for at least every second + segment). Values above 2 reduce the ACK traffic at the cost of + a coarser ACK clock and slower peer congestion window growth. + + NOTE: RFC 1122 Section 4.2.3.2 states that in a stream of + full-sized segments there SHOULD be an ACK for at least every + second segment. Setting this value above 2 no longer meets + that requirement, but the delayed ACK timer still limits the + ACK delay to 0.5 seconds as required by RFC 1122. + config NET_TCP_KEEPALIVE bool "TCP/IP Keep-alive support" default n diff --git a/net/tcp/tcp_appsend.c b/net/tcp/tcp_appsend.c index 98fa10d6313d1..497c8daa5e652 100644 --- a/net/tcp/tcp_appsend.c +++ b/net/tcp/tcp_appsend.c @@ -118,19 +118,22 @@ void tcp_appsend(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn, * NOTES: * 1. If there is a data payload or other flags to be sent with the * outgoing packet, then we may as well include the ACK too. - * 2. The RFC refers to full-size segments. It is not clear what + * 2. The ACK threshold is CONFIG_NET_TCP_ACK_FREQUENCY segments. + * The default value of 2 preserves the RFC 1122 behavior (an + * ACK for at least every second segment). + * 3. The RFC refers to full-size segments. It is not clear what * "full-size" means. Does that mean that the payload is the size * of the MSS? Payload size is not considered other there being * a payload or or not. Should there be some special action for * small payloads of size < MSS? - * 3. Experimentation shows that Windows and Linux behave somewhat + * 4. Experimentation shows that Windows and Linux behave somewhat * differently; they delay the ACKs for many more segments (6 or * more). Delaying for more segments would provide less network * traffic and better performance but seems non-compliant. */ - if (conn->rx_unackseg > 0 || dev->d_sndlen > 0 || - result != TCP_SNDACK) + if (conn->rx_unackseg >= CONFIG_NET_TCP_ACK_FREQUENCY - 1 || + dev->d_sndlen > 0 || result != TCP_SNDACK) { /* Reset the delayed ACK state and send the ACK with this packet. */ @@ -138,12 +141,11 @@ void tcp_appsend(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn, } else { - /* This is only an ACK and there is no pending delayed ACK and - * no TX data is being sent. Indicate that there is one un-ACKed - * segment and don't send anything now. + /* This is only an ACK and there is no TX data being sent. + * Count one more un-ACKed segment and don't send anything now. */ - conn->rx_unackseg = 1; + conn->rx_unackseg++; return; } } @@ -224,21 +226,21 @@ void tcp_appsend(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn, { #endif - /* If d_sndlen > 0, the application has data to be sent. */ + /* If d_sndlen > 0, the application has data to be sent. */ - if (dev->d_sndlen > 0) - { - /* Remember how much data we send out now so that we know - * when everything has been acknowledged. Just increment the - * amount of data sent. This will be needed in sequence number - * calculations and we know that this is not a re-transmission. - * Retransmissions do not go through this path. - */ + if (dev->d_sndlen > 0) + { + /* Remember how much data we send out now so that we know + * when everything has been acknowledged. Just increment the + * amount of data sent. This will be needed in sequence number + * calculations and we know that this is not a re-transmission. + * Retransmissions do not go through this path. + */ - conn->tx_unacked += dev->d_sndlen; - } + conn->tx_unacked += dev->d_sndlen; + } - conn->nrtx = 0; + conn->nrtx = 0; #ifdef CONFIG_NET_TCP_WRITE_BUFFERS }