[En-Nut-Discussion] Request for testers: Improved ethernet RTTO algorithm

Uwe Bonnes bon at elektron.ikp.physik.tu-darmstadt.de
Tue Mar 28 16:11:06 CEST 2017


Hello,

appended patch uses RFC6298 instead of RFC793 for RTTO calculation. It
improves throughput much for bad connections for Allan. The patch seems
sensible to me and I kept the old algo as configuration item. As I don't
have a possibility to test, I would appreciate feedback on this patch before
application to trunk.

Please test and report!
-- 
Uwe Bonnes                bon at elektron.ikp.physik.tu-darmstadt.de

Institut fuer Kernphysik  Schlossgartenstrasse 9  64289 Darmstadt
--------- Tel. 06151 1623569 ------- Fax. 06151 1623305 ---------
>From 8b782d64e34388826dc9b8cfaf5e77bc4844f793 Mon Sep 17 00:00:00 2001
From: Uwe Bonnes <bon at elektron.ikp.physik.tu-darmstadt.de>
Date: Wed, 22 Mar 2017 15:57:53 +0100
Subject: tcputil: Use RFC6298 for RTTO calculation.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Keep old algo as configuration option TCP_RFC793.
Thanks to Allan Bazinet <allan.bazinet at gmail.com>.

Problem Statement:

ENut TCP stack exhibits instability in the case of retransmits, as in the
case of a lossy WiFi network. Find cause and  correct.

Test Methodology Used:

Download style.css (284 kB) from an ENut device, using the command:

curl http://192.168.3.131/style.css -o /dev/null

Baseline with functional network, device connected over WiFi.  Repeat with
lossy network simulator configured as follows:

Downlink max 1Mbps, 10% packets dropped, 500ms delay.
Uplink, max 1Mbps, 10% packets dropped, 500ms delay.

This should fairly closely approximate a Hayes 2400 having a bad day; it
should be an absolutely horrible network.

N.B., Increased HTTPD socket timeouts to 24 seconds to remove that as a
variable, given the savage nature of the lossy setup.

Observations:

Baseline, 150 - 170 kBps, 1-2 s total transfer time typical.
Lossy, DNF. Best attempt was no more than 50% completion before abort.

Analysis:

Socket RTTO becomes very pessimistic very quickly in the presence of
retransmits and recovers to optimistic values only very slowly, if at
all. RTTO quickly becomes 20 seconds; throughput degrades until socket
aborts due to timeout.

Investigation revealed RTTO calculations to be using RFC793 (1981)
smoothed average methodology. This methodology is documented, in Van
Jacobson’s paper of 1988, "Congestion Avoidance and Control”, to exhibit
the behavior observed. In short, the smoothed average methodology is
only suitable for dealing with congestion (frankly, it also does a
relatively poor job at dealing with congestion), and will exhibit
pathological behavior in the case of loss, as recent loss events tend
to dominate the calculation, while recent successful transfers do not
provide adequate weight to reduce the RTTO quickly enough. VJ88 describes
this behavior as ‘pouring gasoline on a fire’.

VJ88: http://ee.lbl.gov/papers/congavoid.pdf

Solution:

Reimplemented RTTO calculation algorithm using Van Jacobson statistical
historical weighting approach as outlined in RFC6298 (2011), with the
exception of section 2.4 — rather than a minimum 1 second RTTO, the
minimum is bounded by the clock granularity.

Post-Solution Observations:

Baseline, unchanged.
Lossy,  0 - 2000 bytes/sec, 4 - 5 minutes  to download typical. Zero
failures in 20 minutes of testing.

Conclusion:

Issue appears to be resolved.

Comments:

Decision to not implement 1 second minimum RTTO as described in
RFC6298 section 2.4 may contribute to spurious retransmissions. If
these are observed, then the decision to treat section 2.4 as an
historical anachronism was a poor choice, and the 1 second minimum
should be implemented.  If no spurious retransmissions are observed,
then section 2.4 is overly conservative given present equipment
and practice, and the decision is a correct one.
---
 nut/conf/net/net.nut       |  8 ++++++
 nut/include/sys/sock_var.h |  5 ++++
 nut/net/tcpsock.c          |  3 +++
 nut/net/tcputil.c          | 61 +++++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 73 insertions(+), 4 deletions(-)

diff --git a/nut/conf/net/net.nut b/nut/conf/net/net.nut
index f06051b..4d28387 100644
--- a/nut/conf/net/net.nut
+++ b/nut/conf/net/net.nut
@@ -147,6 +147,14 @@ nutnet =
                 file = "include/cfg/tcp.h"
             },
             {
+                macro = "TCP_RFC793",
+                brief = "Use old congestion algorithm",
+                description = "Use RFC793 as congestion algorithm. Used in NutOs before March 2017."..
+                              "Default is to use algorithm according to RFC6298.",
+                flavor = "boolean",
+                file = "include/cfg/tcp.h"
+            },
+            {
                 macro = "TCP_COLLECT_INADV",
                 brief = "Segment Collection",
                 description = "TCP segments received in advance are stored in a special queue "..
diff --git a/nut/include/sys/sock_var.h b/nut/include/sys/sock_var.h
index fae84f5..d5bca80 100644
--- a/nut/include/sys/sock_var.h
+++ b/nut/include/sys/sock_var.h
@@ -132,6 +132,7 @@
  *
  */
 
+#include <cfg/tcp.h>
 #include <compiler.h>
 #include <stdint.h>
 #include <sys/types.h>
@@ -259,6 +260,10 @@ struct tcp_socket {
 
     uint32_t  so_rtt_seq;     /*!< \brief Sequence number for RTT calculation. */
     uint16_t so_rtto;        /*!< \brief Current retransmission timeout. */
+#if !defined(TCP_RFC793)
+    uint16_t  so_rtsv;      /*!< \brief Statistical variance estimator.*/
+    uint32_t  so_rtsa;      /*!< \brief Statistical average estimator*/
+#endif
     uint16_t so_retransmits; /*!< \brief Number of retransmits. */
     uint16_t so_time_wait;   /*!< \brief Time wait counter. */
     uint16_t so_retran_time; /*!< \brief Retransmit time counter. */
diff --git a/nut/net/tcpsock.c b/nut/net/tcpsock.c
index ba24ee9..7d2dab5 100644
--- a/nut/net/tcpsock.c
+++ b/nut/net/tcpsock.c
@@ -225,6 +225,9 @@ TCPSOCKET *NutTcpCreateSocket(void)
 
         sock->so_mss = TCP_MSS;
         sock->so_rtto = 1000; /* Initial retransmission time out */
+#if !defined(RFC793)
+        sock->so_rtsv = 1000;
+#endif
 
         sock->so_next = tcpSocketList;
 
diff --git a/nut/net/tcputil.c b/nut/net/tcputil.c
index b04af7a..e8442c0 100644
--- a/nut/net/tcputil.c
+++ b/nut/net/tcputil.c
@@ -48,7 +48,11 @@
 #include <sys/timer.h>
 #include <stdio.h>
 
-/*!
+#ifdef NUTDEBUG
+# include <net/netdebug.h>
+#endif
+
+ /*!
  * \addtogroup xgTCP
  */
 /*@{*/
@@ -69,6 +73,7 @@
  */
 void NutTcpCalcRtt(TCPSOCKET * sock)
 {
+#if defined(TCP_RFC793)
     uint16_t delta;
 
     if (sock->so_retran_time == 0)
@@ -76,9 +81,57 @@ void NutTcpCalcRtt(TCPSOCKET * sock)
 
     delta = (uint16_t) NutGetMillis() - (sock->so_retran_time & ~1);
 
-    /* According to RFC793 (or STD007), page 41, we use 0.8 for ALPHA and 2.0 for BETA. */
-    sock->so_rtto = min (TCP_RTTO_MAX, max(TCP_RTTO_MIN, (delta * 4 + sock->so_rtto * 8) / 10));
-    //@@@printf ("[%04X] new retran timeout: %u, delta: %u\n", (u_short) sock, sock->so_rtto, delta);
+    /* According to RFC793 (or STD007), page 41, we use 0.8 for ALPHA and
+     * 2.0 for BETA. */
+    sock->so_rtto = min(TCP_RTTO_MAX,
+                        max(TCP_RTTO_MIN,(delta * 4 + sock->so_rtto * 8) / 10));
+# ifdef NUTDEBUG
+   printf ("[%04X] new retran timeout: %u, delta: %u\n",
+            (u_short) sock, sock->so_rtto, delta);
+# endif
+#else
+    /*
+     * Mod alb - Van Jacobson Round Trip Timing
+     *
+     * The original implementation above is RFC793, September 1981. It
+     * does not handle issues well. This solution uses Van Jacobson's
+     * approach instead, from his November 1988 paper, "Congestion
+     * Avoidance and Control".  See RFC6298 for details of the approach;
+     * the implementation here is exactly as in VJ88, Appendix A.
+     *
+     */
+
+    int16_t m;
+
+    if (sock->so_retran_time == 0) return;
+
+    /* Compute m, round trip time measurement.*/
+
+    m  = (int16_t)(NutGetMillis() - sock->so_retran_time);
+
+    /* Update average estimator.*/
+
+    m -= sock->so_rtsa >> 3;
+    sock->so_rtsa += m;
+
+    /* Update variance estimator.*/
+
+    if (m < 0) m = -m;
+
+    m -= sock->so_rtsv >> 2;
+    sock->so_rtsv += m;
+
+    /* Update round trip timer.*/
+
+    sock->so_rtto = (sock->so_rtsa >> 3) + sock->so_rtsv;
+
+# ifdef NUTDEBUG
+    fprintf(stdout,
+            "%lu [%x]NutTcpCalcRTT() so_rtto: %u, so_rtsa: %lu so_rtsv: %lu\n",
+            NutGetMillis(), (int)sock, sock->so_rtto, sock->so_rtsa,
+            sock->so_rtsv);
+# endif
+#endif
 }
 
 /*@}*/
-- 
2.10.2



More information about the En-Nut-Discussion mailing list