Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 11 Apr 2013 21:45:50 +0200
From:      Andre Oppermann <andre@freebsd.org>
To:        Matt Miller <matt@matthewjmiller.net>
Cc:        FreeBSD Net <freebsd-net@freebsd.org>
Subject:   Re: RFC 3042 Implementation
Message-ID:  <516712EE.8010708@freebsd.org>
In-Reply-To: <CAFc6gu9ynWjeF0J%2Bv_vYeHQNmArJdteH3AaTrHwa1bRVHYtQgg@mail.gmail.com>
References:  <CAFc6gu9ynWjeF0J%2Bv_vYeHQNmArJdteH3AaTrHwa1bRVHYtQgg@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help
On 11.04.2013 20:59, Matt Miller wrote:
> In some of our tests, we noticed some duplicate pure ACKs (not window
> updates), most of which the duplicates were coming from this tcp_output()
> call in tcp_do_segment() (line 2534):
>
> 2508                                 } else if (V_tcp_do_rfc3042) {
> 2509                                         cc_ack_received(tp, th,
> CC_DUPACK);
> 2510                                         u_long oldcwnd = tp->snd_cwnd;
> 2511                                         tcp_seq oldsndmax =
> tp->snd_max;
> 2512                                         u_int sent;
> 2513
> 2514                                         KASSERT(tp->t_dupacks == 1 ||
> 2515                                             tp->t_dupacks == 2,
> 2516                                             ("%s: dupacks not 1 or 2",
> 2517                                             __func__));
> 2518                                         if (tp->t_dupacks == 1)
> 2519                                                 tp->snd_limited = 0;
> 2520                                         tp->snd_cwnd =
> 2521                                             (tp->snd_nxt -
> tp->snd_una) +
> 2522                                             (tp->t_dupacks -
> tp->snd_limited) *
> 2523                                             tp->t_maxseg;
> 2524                                         if ((thflags & TH_FIN) &&
> 2525
> (TCPS_HAVERCVDFIN(tp->t_state) == 0)) {
> 2526                                                 /*
> 2527                                                  * If its a fin we
> need to process
> 2528                                                  * it to avoid a race
> where both
> 2529                                                  * sides enter
> FIN-WAIT and send FIN|ACK
> 2530                                                  * at the same time.
> 2531                                                  */
> 2532                                                 break;
> 2533                                         }
> 2534                                         (void) tcp_output(tp);
>
> I added some instrumentation here to count how many time the following is
> zero prior to calling tcp_output():
>
> so->so_snd.sb_cc - ((tp->snd_nxt - tp->snd_una)
>
> And, in our tests, it was like 97% of the time.
>
> It looks like the intent of the RFC is to only send one or two unsent data
> segments here, not pure ACKs.  And this subsequent standard seems to
> clarify that new data should be available for transmission:
>
> http://tools.ietf.org/html/rfc5681
>
> <quote>
>         On the first and second duplicate ACKs received at a sender, a
>         TCP SHOULD send a segment of previously unsent data per [RFC3042]
>         provided that the receiver's advertised window allows, the total
>         FlightSize would remain less than or equal to cwnd plus 2*SMSS,
>         and that new data is available for transmission.
> </quote>
>
> So, this is a detailed way of asking: do we need a check here to make sure
> there is new data to send prior to calling tcp_output()?

Yes.  Based on your input the attached patch should fix it (untested).

-- 
Andre

Index: tcp_input.c
===================================================================
--- tcp_input.c (revision 249375)
+++ tcp_input.c (working copy)
@@ -2564,6 +2564,7 @@
                                         u_long oldcwnd = tp->snd_cwnd;
                                         tcp_seq oldsndmax = tp->snd_max;
                                         u_int sent;
+                                       int avail;

                                         KASSERT(tp->t_dupacks == 1 ||
                                             tp->t_dupacks == 2,
@@ -2585,7 +2586,17 @@
                                                  */
                                                 break;
                                         }
-                                       (void) tcp_output(tp);
+                                       /*
+                                        * Only call tcp_output when there
+                                        * is new data available to be sent.
+                                        * Otherwise we send pure ACKs.
+                                        */
+                                       SOCKBUF_LOCK(&so->so_snd);
+                                       avail = so->so_snd.sb_cc -
+                                           (tp->snd_nxt - tp->snd_una);
+                                       SOCKBUF_UNLOCK(&so->so_snd);
+                                       if (avail > 0)
+                                               (void) tcp_output(tp);
                                         sent = tp->snd_max - oldsndmax;
                                         if (sent > tp->t_maxseg) {
                                                 KASSERT((tp->t_dupacks == 2 &&



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?516712EE.8010708>