diff --git a/net/tcp/Kconfig b/net/tcp/Kconfig index 325f7b79446..df88824b8f1 100644 --- a/net/tcp/Kconfig +++ b/net/tcp/Kconfig @@ -79,6 +79,33 @@ config NET_MAX_LISTENPORTS ---help--- Maximum number of listening TCP/IP ports (all tasks). Default: 20 +config NET_TCP_FAST_RETRANSMIT_WATERMARK + int "WaterMark to trigger Fast Retransmission" + default 3 + ---help--- + RFC2001: + 3. Fast Retransmit + Modifications to the congestion avoidance algorithm were proposed in + 1990 [3]. Before describing the change, realize that TCP may + generate an immediate acknowledgment (a duplicate ACK) when an out- + of-order segment is received (Section 4.2.2.21 of [1], with a note + that one reason for doing so was for the experimental fast- + retransmit algorithm). This duplicate ACK should not be delayed. + The purpose of this duplicate ACK is to let the other end know that a + segment was received out of order, and to tell it what sequence + number is expected. + + Since TCP does not know whether a duplicate ACK is caused by a lost + segment or just a reordering of segments, it waits for a small number + of duplicate ACKs to be received. It is assumed that if there is + just a reordering of the segments, there will be only one or two + duplicate ACKs before the reordered segment is processed, which will + then generate a new ACK. If three or more duplicate ACKs are + received in a row, it is a strong indication that a segment has been + lost. TCP then performs a retransmission of what appears to be the + missing segment, without waiting for a retransmission timer to + expire. + config NET_TCP_NOTIFIER bool "Support TCP notifications" default n diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h index 044869fa54f..864164d7bc6 100644 --- a/net/tcp/tcp.h +++ b/net/tcp/tcp.h @@ -79,6 +79,7 @@ # define TCP_WBPKTLEN(wrb) ((wrb)->wb_iob->io_pktlen) # define TCP_WBSENT(wrb) ((wrb)->wb_sent) # define TCP_WBNRTX(wrb) ((wrb)->wb_nrtx) +# define TCP_WBNACK(wrb) ((wrb)->wb_nack) # define TCP_WBIOB(wrb) ((wrb)->wb_iob) # define TCP_WBCOPYOUT(wrb,dest,n) (iob_copyout(dest,(wrb)->wb_iob,(n),0)) # define TCP_WBCOPYIN(wrb,src,n) \ @@ -299,6 +300,7 @@ struct tcp_wrbuffer_s uint16_t wb_sent; /* Number of bytes sent from the I/O buffer chain */ uint8_t wb_nrtx; /* The number of retransmissions for the last * segment sent */ + uint8_t wb_nack; /* The number of ack count */ struct iob_s *wb_iob; /* Head of the I/O buffer chain */ }; #endif diff --git a/net/tcp/tcp_send_buffered.c b/net/tcp/tcp_send_buffered.c index 040ad561831..3736b1beac3 100644 --- a/net/tcp/tcp_send_buffered.c +++ b/net/tcp/tcp_send_buffered.c @@ -326,6 +326,7 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev, { FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pvconn; FAR struct socket *psock = (FAR struct socket *)pvpriv; + bool rexmit = false; /* Check for a loss of connection */ @@ -482,6 +483,28 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev, wrb, TCP_WBSEQNO(wrb), TCP_WBPKTLEN(wrb)); } } + else if (ackno == TCP_WBSEQNO(wrb)) + { + /* Duplicate ACK? Retransmit data if need */ + + TCP_WBNACK(wrb)++; + + if (TCP_WBNACK(wrb) == + CONFIG_NET_TCP_FAST_RETRANSMIT_WATERMARK) + { + /* Do fast retransmit */ + + rexmit = true; + } + else if ((TCP_WBNACK(wrb) > + CONFIG_NET_TCP_FAST_RETRANSMIT_WATERMARK) && + TCP_WBNACK(wrb) == sq_count(&conn->unacked_q) - 1) + { + /* Reset the duplicate ack counter */ + + TCP_WBNACK(wrb) = 0; + } + } } /* A special case is the head of the write_q which may be partially @@ -521,6 +544,11 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev, /* Check if we are being asked to retransmit data */ else if ((flags & TCP_REXMIT) != 0) + { + rexmit = true; + } + + if (rexmit) { FAR struct tcp_wrbuffer_s *wrb; FAR sq_entry_t *entry; diff --git a/net/tcp/tcp_wrbuffer.c b/net/tcp/tcp_wrbuffer.c index 82df41dc4bf..03dff66d449 100644 --- a/net/tcp/tcp_wrbuffer.c +++ b/net/tcp/tcp_wrbuffer.c @@ -260,6 +260,10 @@ void tcp_wrbuffer_release(FAR struct tcp_wrbuffer_s *wrb) iob_free_chain(wrb->wb_iob, IOBUSER_NET_TCP_WRITEBUFFER); } + /* Reset the ack counter */ + + TCP_WBNACK(wrb) = 0; + /* Then free the write buffer structure */ sq_addlast(&wrb->wb_node, &g_wrbuffer.freebuffers);