You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2021/06/14 02:20:35 UTC
[incubator-nuttx] 02/03: tcp: window update improvements
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit 14ec75e7fcb43db72a70c0b8eeefe7b9843a5596
Author: YAMAMOTO Takashi <ya...@midokura.com>
AuthorDate: Tue Jun 1 16:57:00 2021 +0900
tcp: window update improvements
* Fixes the case where the window was small but not zero.
* tcp_recvfrom: Remove tcp_ackhandler. Instead, simply schedule TX for
a possible window update and make tcp_appsend decide.
* Replace rcv_wnd (the last advertized window size value) with
rcv_adv. (the window edge sequence number advertized to the peer)
rcv_wnd was complicated to deal with because its base (rcvseq) is
also moving.
* tcp_appsend: Send a window update even if there are no other reasons
to send an ack.
Namely, send an update if it increases the window by
* 2 * mss
* or the half of the max possible window size
---
net/sixlowpan/sixlowpan_tcpsend.c | 3 +-
net/tcp/tcp.h | 24 ++++++---
net/tcp/tcp_appsend.c | 12 ++++-
net/tcp/tcp_conn.c | 1 +
net/tcp/tcp_input.c | 1 +
net/tcp/tcp_recvfrom.c | 57 +--------------------
net/tcp/tcp_recvwindow.c | 104 ++++++++++++++++++++++++++++++++++++++
net/tcp/tcp_send.c | 3 +-
8 files changed, 140 insertions(+), 65 deletions(-)
diff --git a/net/sixlowpan/sixlowpan_tcpsend.c b/net/sixlowpan/sixlowpan_tcpsend.c
index 593bbc4..47f9c5a 100644
--- a/net/sixlowpan/sixlowpan_tcpsend.c
+++ b/net/sixlowpan/sixlowpan_tcpsend.c
@@ -257,6 +257,7 @@ static int sixlowpan_tcp_header(FAR struct tcp_conn_s *conn,
{
/* Update the TCP received window based on I/O buffer availability */
+ uint32_t rcvseq = tcp_getsequence(conn->rcvseq);
uint16_t recvwndo = tcp_get_recvwindow(dev, conn);
/* Set the TCP Window */
@@ -266,7 +267,7 @@ static int sixlowpan_tcp_header(FAR struct tcp_conn_s *conn,
/* Update the Receiver Window */
- conn->rcv_wnd = recvwndo;
+ conn->rcv_adv = rcvseq + recvwndo;
}
/* Calculate TCP checksum. */
diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h
index b71c012..5cbf8f4 100644
--- a/net/tcp/tcp.h
+++ b/net/tcp/tcp.h
@@ -188,7 +188,7 @@ struct tcp_conn_s
* connection */
uint16_t snd_wnd; /* Sequence and acknowledgement numbers of last
* window update */
- uint16_t rcv_wnd; /* Receiver window available */
+ uint32_t rcv_adv; /* The right edge of the recv window advertized */
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
uint32_t tx_unacked; /* Number bytes sent but not yet ACKed */
#else
@@ -267,12 +267,6 @@ struct tcp_conn_s
FAR struct devif_callback_s *connevents;
- /* Receiver callback to indicate that the data has been consumed and that
- * an ACK should be send.
- */
-
- FAR struct devif_callback_s *rcv_ackcb;
-
/* accept() is called when the TCP logic has created a connection
*
* accept_private: This is private data that will be available to the
@@ -1469,6 +1463,22 @@ uint16_t tcp_get_recvwindow(FAR struct net_driver_s *dev,
FAR struct tcp_conn_s *conn);
/****************************************************************************
+ * Name: tcp_should_send_recvwindow
+ *
+ * Description:
+ * Determine if we should advertize the new recv window to the peer.
+ *
+ * Input Parameters:
+ * conn - The TCP connection structure holding connection information.
+ *
+ * Returned Value:
+ * If we should send an update.
+ *
+ ****************************************************************************/
+
+bool tcp_should_send_recvwindow(FAR struct tcp_conn_s *conn);
+
+/****************************************************************************
* Name: psock_tcp_cansend
*
* Description:
diff --git a/net/tcp/tcp_appsend.c b/net/tcp/tcp_appsend.c
index cff0740..d2a05bd 100644
--- a/net/tcp/tcp_appsend.c
+++ b/net/tcp/tcp_appsend.c
@@ -89,10 +89,20 @@ void tcp_appsend(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
ninfo("result: %04x d_sndlen: %d conn->tx_unacked: %" PRId32 "\n",
result, dev->d_sndlen, (uint32_t)conn->tx_unacked);
+ /* Need to update the recv window? */
+
+ if (tcp_should_send_recvwindow(conn))
+ {
+ result |= TCP_SNDACK;
+#ifdef CONFIG_NET_TCP_DELAYED_ACK
+ conn->rx_unackseg = 0;
+#endif
+ }
+
#ifdef CONFIG_NET_TCP_DELAYED_ACK
/* Did the caller request that an ACK be sent? */
- if ((result & TCP_SNDACK) != 0)
+ else if ((result & TCP_SNDACK) != 0)
{
/* Yes.. Handle delayed acknowledgments */
diff --git a/net/tcp/tcp_conn.c b/net/tcp/tcp_conn.c
index af1515f..d2b3c34 100644
--- a/net/tcp/tcp_conn.c
+++ b/net/tcp/tcp_conn.c
@@ -966,6 +966,7 @@ FAR struct tcp_conn_s *tcp_alloc_accept(FAR struct net_driver_s *dev,
/* rcvseq should be the seqno from the incoming packet + 1. */
memcpy(conn->rcvseq, tcp->seqno, 4);
+ conn->rcv_adv = tcp_getsequence(conn->rcvseq);
/* Initialize the list of TCP read-ahead buffers */
diff --git a/net/tcp/tcp_input.c b/net/tcp/tcp_input.c
index 5823f24..2d8735f 100644
--- a/net/tcp/tcp_input.c
+++ b/net/tcp/tcp_input.c
@@ -704,6 +704,7 @@ found:
conn->tcpstateflags = TCP_ESTABLISHED;
memcpy(conn->rcvseq, tcp->seqno, 4);
+ conn->rcv_adv = tcp_getsequence(conn->rcvseq);
net_incr32(conn->rcvseq, 1);
conn->tx_unacked = 0;
diff --git a/net/tcp/tcp_recvfrom.c b/net/tcp/tcp_recvfrom.c
index 973a5c5..c6b79d7 100644
--- a/net/tcp/tcp_recvfrom.c
+++ b/net/tcp/tcp_recvfrom.c
@@ -511,53 +511,6 @@ static uint16_t tcp_recvhandler(FAR struct net_driver_s *dev,
}
/****************************************************************************
- * Name: tcp_ackhandler
- *
- * Description:
- * This function is called with the network locked to send the ACK in
- * response by the lower, device interfacing layer.
- *
- * Input Parameters:
- * dev The structure of the network driver that generated the event.
- * pvconn The connection structure associated with the socket
- * flags Set of events describing why the callback was invoked
- *
- * Returned Value:
- * ACK should be send in the response.
- *
- * Assumptions:
- * The network is locked.
- *
- ****************************************************************************/
-
-static uint16_t tcp_ackhandler(FAR struct net_driver_s *dev,
- FAR void *pvconn, FAR void *pvpriv,
- uint16_t flags)
-{
- FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pvconn;
-
- ninfo("flags: %04x\n", flags);
-
- if (conn != NULL && (flags & TCP_POLL) != 0)
- {
- /* Indicate that the data has been consumed and that an ACK
- * should be send.
- */
-
- if (tcp_get_recvwindow(dev, conn) != 0 &&
- conn->rcv_wnd == 0)
- {
- flags |= TCP_SNDACK;
- }
-
- tcp_callback_free(conn, conn->rcv_ackcb);
- conn->rcv_ackcb = NULL;
- }
-
- return flags;
-}
-
-/****************************************************************************
* Name: tcp_recvfrom_initialize
*
* Description:
@@ -816,15 +769,9 @@ ssize_t psock_tcp_recvfrom(FAR struct socket *psock, FAR void *buf,
* not only this particular connection.
*/
- if (conn->rcv_wnd == 0 && conn->rcv_ackcb == NULL)
+ if (tcp_should_send_recvwindow(conn))
{
- conn->rcv_ackcb = tcp_callback_alloc(conn);
- if (conn->rcv_ackcb)
- {
- conn->rcv_ackcb->flags = TCP_POLL;
- conn->rcv_ackcb->event = tcp_ackhandler;
- netdev_txnotify_dev(conn->dev);
- }
+ netdev_txnotify_dev(conn->dev);
}
net_unlock();
diff --git a/net/tcp/tcp_recvwindow.c b/net/tcp/tcp_recvwindow.c
index b8afa3b..0008382 100644
--- a/net/tcp/tcp_recvwindow.c
+++ b/net/tcp/tcp_recvwindow.c
@@ -38,6 +38,45 @@
#include "tcp/tcp.h"
/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: tcp_maxrcvwin
+ *
+ * Description:
+ * Calculate the possible max TCP receive window for the connection.
+ *
+ * Input Parameters:
+ * conn - The TCP connection.
+ *
+ * Returned Value:
+ * The value of the TCP receive window.
+ ****************************************************************************/
+
+static uint16_t tcp_maxrcvwin(FAR struct tcp_conn_s *conn)
+{
+ size_t maxiob;
+ uint16_t maxwin;
+
+ /* Calculate the max possible window size for the connection.
+ * This needs to be in sync with tcp_get_recvwindow().
+ */
+
+ maxiob = (CONFIG_IOB_NBUFFERS - CONFIG_IOB_THROTTLE) * CONFIG_IOB_BUFSIZE;
+ if (maxiob >= UINT16_MAX)
+ {
+ maxwin = UINT16_MAX;
+ }
+ else
+ {
+ maxwin = maxiob;
+ }
+
+ return maxwin;
+}
+
+/****************************************************************************
* Public Functions
****************************************************************************/
@@ -163,3 +202,68 @@ uint16_t tcp_get_recvwindow(FAR struct net_driver_s *dev,
return recvwndo;
}
+
+bool tcp_should_send_recvwindow(FAR struct tcp_conn_s *conn)
+{
+ FAR struct net_driver_s *dev = conn->dev;
+ uint16_t win;
+ uint16_t maxwin;
+ uint16_t oldwin;
+ uint32_t rcvseq;
+ uint16_t adv;
+ uint16_t mss;
+
+ /* Note: rcv_adv can be smaller than rcvseq.
+ * For examples, when:
+ *
+ * - we shrunk the window
+ * - zero window probes advanced rcvseq
+ */
+
+ rcvseq = tcp_getsequence(conn->rcvseq);
+ if (TCP_SEQ_GT(conn->rcv_adv, rcvseq))
+ {
+ oldwin = TCP_SEQ_SUB(conn->rcv_adv, rcvseq);
+ }
+ else
+ {
+ oldwin = 0;
+ }
+
+ win = tcp_get_recvwindow(dev, conn);
+
+ /* If the window doesn't extend, don't send. */
+
+ if (win <= oldwin)
+ {
+ return false;
+ }
+
+ adv = win - oldwin;
+
+ /* The following conditions are inspired from NetBSD TCP stack.
+ *
+ * - If we can extend the window by the half of the max possible size,
+ * send it.
+ *
+ * - If we can extend the window by 2 * mss, send it.
+ */
+
+ maxwin = tcp_maxrcvwin(conn);
+ if (2 * adv >= maxwin)
+ {
+ return true;
+ }
+
+ /* Revisit: the real expected size should be used instead.
+ * E.g. consider the path MTU
+ */
+
+ mss = tcp_rx_mss(dev);
+ if (adv >= 2 * mss)
+ {
+ return true;
+ }
+
+ return false;
+}
diff --git a/net/tcp/tcp_send.c b/net/tcp/tcp_send.c
index 1079b13..a17fe97 100644
--- a/net/tcp/tcp_send.c
+++ b/net/tcp/tcp_send.c
@@ -362,6 +362,7 @@ static void tcp_sendcommon(FAR struct net_driver_s *dev,
{
/* Update the TCP received window based on I/O buffer availability */
+ uint32_t rcvseq = tcp_getsequence(conn->rcvseq);
uint16_t recvwndo = tcp_get_recvwindow(dev, conn);
/* Set the TCP Window */
@@ -371,7 +372,7 @@ static void tcp_sendcommon(FAR struct net_driver_s *dev,
/* Update the Receiver Window */
- conn->rcv_wnd = recvwndo;
+ conn->rcv_adv = rcvseq + recvwndo;
}
/* Finish the IP portion of the message and calculate checksums */