and removed two tcp_send_txnotify() calls from tcp_sendfile (they are not needed anymore).
As a result, the TX throughput of both the tcp_send_buffered and tcp_send_unbuffered
is significantly boosted in case of TUN/TAP network device.
According to RFC 5681 (3.2) the TCP Fast Retransmit algorithm should start
if the threshold of 3 duplicate ACKs is reached.
Thus the threshold should be a constant, not an integer option.
(conn->sndseq was updated in multiple places that was unreasonable and complicated).
This optimization is the same as it was done for tcp_send_unbuffered.
Wrong unackseq calculation locked conn->tx_unacked at non-zero values
even if all ACKs were received.
This issue is the same as it was with tcp_send_unbuffered.
Do not use pvconn argument to get the TCP connection pointer because pvconn is
normally NULL for some events like NETDEV_DOWN. Instead, the TCP connection pointer
can be reliably obtained from the corresponding TCP socket.
Both the snd_ackcb and snd_datacb callbacks were created and destroyed right after sending every packet.
Whenever TCP_REXMIT event occurred due to TCP send timeout, TCP_REXMIT was ignored because
snd_ackcb callback had been destroyed by the time.
The issue is fixed as follows:
- both the snd_ackcb and snd_datacb callbacks are combined into one snd_cb callback
(the same way as in tcp_send_unbuffered.c).
- the snd_cb callback lives until all requested data (via sendfile) is sent,
including all ACKs and possible retransmissions.
As a positive side effect of the code optimization / fix, sendfile TCP payload throughput is increased.
tcp_sendfile() reads data directly from a file and does not use NET_TCP_WRITE_BUFFERS data flow
even if CONFIG_NET_TCP_WRITE_BUFFERS option is enabled.
Despite this, tcp_sendfile relied on NET_TCP_WRITE_BUFFERS specific flow control variables that
were idle during sendfile operation. Thus it was a total inconsistency.
E.g. because of the issue, TCP socket used by sendfile() operation never issued
FIN packet on close() command, and the TCP connection hung up.
As a result of the fix, simultaneously enabled CONFIG_NET_TCP_WRITE_BUFFERS and
CONFIG_NET_SENDFILE options can coexist.
Wrong unackseq calculation locked conn->tx_unacked at non-zero values
even if all ACKs were received. Thus unbuffered psock_tcp_send() never completed.
If the remote TCP receiver advertised TCP window size greater than 64 KB
and TCP ACK packets returned to the NuttX TCP sender with a significant delay,
tx_unacked variable overflowed and further TCP send stalled forever
(until TCP re-connection).
While it's a neat idea, it doesn't work well in reality.
* Many of modern tcp stacks don't obey the "ack every other packet"
rule these days. (Linux, macOS, ...)
* Even if a traditional TCP implementation is assumed, we can't
predict/control which packets are acked reliably. For example,
window updates can easily mess up our strategy.
tcp_timer: eliminated false decrements of conn->timer in case of multiple network adapters.
The false timer decrements sometimes provoked TCP spurious retransmissions due to premature timeouts.
In case of enabled packet forwarding mode, packets were forwarded in a reverse order
because of LIFO behavior of the connection event list.
The issue exposed only during high network traffic. Thus the event list started to grow
that resulted in changing the order of packets inside of groups of several packets
like the following: 3, 2, 1, 6, 5, 4, 8, 7 etc.
Remarks concerning the connection event list implementation:
* Now the queue (list) is FIFO as it should be.
* The list is singly linked.
* The list has a head pointer (inside of outer net_driver_s structure),
and a tail pointer is added into outer net_driver_s structure.
* The list item is devif_callback_s structure.
It still has two pointers to two different list chains (*nxtconn and *nxtdev).
* As before the first argument (*dev) of the list functions can be NULL,
while the other argument (*list) is effective (not NULL).
* An extra (*tail) argument is added to devif_callback_alloc()
and devif_conn_callback_free() functions.
* devif_callback_alloc() time complexity is O(1) (i.e. O(n) to fill the whole list).
* devif_callback_free() time complexity is O(n) (i.e. O(n^2) to empty the whole list).
* devif_conn_event() time complexity is O(n).
Gregory Nutt has submitted the SGA
UVC Ingenieure has submitted the SGA
Max Holtzberg has submitted the ICLA
as a result we can migrate the licenses to Apache.
Signed-off-by: Alin Jerpelea <alin.jerpelea@sony.com>
* Do not accept the window in old segments.
Implement SND.WL1/WL2 things in the RFC.
* Do not accept the window in the segment w/o ACK bit set.
The window is an offset from the ack seq.
(maybe it's simpler to just drop segments w/o ACK though)
* Subtract snd_wnd by the amount of the ack advancement.
Fix a wrong assertion in:
```
commit 98ec46d726
Author: YAMAMOTO Takashi <yamamoto@midokura.com>
Date: Tue Jul 20 09:10:43 2021 +0900
tcp_send_buffered.c: fix iob allocation deadlock
Ensure to put the wrb back onto the write_q when blocking
on iob allocation. Otherwise, it can deadlock with other
threads doing the same thing.
```
I forget to submit this with https://github.com/apache/incubator-nuttx/pull/4257
With an applictation using mbedtls, I observed retransmitted segments
with corrupted user data, detected by the peer tls during mac processing.
Looking at the packet dump, I suspect that a wrb which has been put back
onto the write_q for retransmission was partially sent but fully acked.
Note: it's normal for a retransmission to be acked before sent.
In that case, the bug fixed in this commit would cause the wrb have
a wrong sequence number, possibly the same as the next wrb. It matches
what I saw in the packet dump. That is, the broken segments contain the
payload identical to one of the previous segment.
Consider a bi-directional TCP connection:
1. we use all IOBs for tx queue
2. we advertize zero recv window because we have no free IOBs
3. if the peer tcp does the same thing,
both sides advertize zero window and can not drain the tx queue.
For a similar stall to happen, the peer doesn't need to be
a naive tcp implementation like nuttx. A naive application blocking
on send() without draining its read buffer is enough.
(Probably such an application should be fixed to drain rx even
when tx is full. However, it's another story.)
This commit avoids the situation by prevent tx from grabbing
the all IOBs in the first place. (assuming CONFIG_IOB_THROTTLE > 0)
Since we do not have the Nagle's algorithm,
the TCP_NODELAY socket option is enabled by default.
Change-Id: I0c8619bb06cf418f7eded5bd72ac512b349cacc5
Signed-off-by: chao.an <anchao@xiaomi.com>
Since a SOL option IP_TTL exist, we should rename this IP_TTL
in netconfig.h to avoid confusion.
Signed-off-by: Huang Qi <huangqi3@xiaomi.com>
Change-Id: Ib04c36553f23bce8d362e97294a8b83eaa050cf3
quote from https://man7.org/linux/man-pages/man2/sendfile.2.html:
If offset is not NULL, then it points to a variable holding the
file offset from which sendfile() will start reading data from
in_fd. When sendfile() returns, this variable will be set to the
offset of the byte following the last byte that was read. If
offset is not NULL, then sendfile() does not modify the file
offset of in_fd; otherwise the file offset is adjusted to reflect
the number of bytes read from in_fd.
If offset is NULL, then data will be read from in_fd starting at
the file offset, and the file offset will be updated by the call.
The change also align with the implementation at:
libs/libc/misc/lib_sendfile.c
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
Change-Id: I607944f40b04f76731af7b205dcd319b0637fa04
1. change all window relative value type to uint32_t
2. move window range validity check(UINT16_MAX) before assembling TCP header
Signed-off-by: chao.an <anchao@xiaomi.com>
tcp_close disposes the connection immediately if it's called in
TCP_LAST_ACK. If it happens, we will end up with responding the
last ACK with a RST.
This commit fixes it by making tcp_close wait for the completion
of the passive close.
This fixes connection closing issues with CONFIG_NET_TCP_WRITE_BUFFERS.
Because TCP_CLOSE is used for both of input and output for tcp_callback,
the close callback and the send callback confuses each other as
the following. As it effectively disposes the connection immediately,
we end up with responding to the consequent ACK and FIN/ACK from the peer
with RSTs.
tcp_timer
-> tcp_close_eventhandler
returns TCP_CLOSE (meaning an active close)
-> psock_send_eventhandler
called with TCP_CLOSE from tcp_close_eventhandler, misinterpet as
a passive close.
-> tcp_lost_connection
-> tcp_shutdown_monitor
-> tcp_callback
-> tcp_close_eventhandler
misinterpret TCP_CLOSE from itself as
a passive close
The current code just leave the window value from the segment
from the peer. It doesn't make sense.
Instead, always use 0.
This matches what NetBSD and Linux do.
(As far as I read their code correctly.)
* It doesn't make sense to have this conditional on our own
SO_KEEPALIVE support. (CONFIG_NET_TCP_KEEPALIVE)
Actually we don't have a control on the peer tcp stack,
who decides to send us keep-alive probes.
* We should respond them for non ESTABLISHED states. eg. FIN_WAIT_2
See also:
https://github.com/apache/incubator-nuttx/pull/3919#issuecomment-868248576
Do not bother to preserve segment boundaries in the tcp
readahead queues.
* Avoid wasting the tail IOB space for each segments.
Instead, pack the newly received data into the tail space
of the last IOB. Also, advertise the tail space as
a part of the window.
* Use IOB chain directly. Eliminate IOB queue overhead.
* Allow to accept only a part of a segment.
* This change improves the memory efficiency.
And probably more importantly, allows less-confusing
recv window advertisement behavior.
Previously, even when we advertise N bytes window,
we often couldn't actually accept N bytes. Depending on
the segment sizes and IOB configurations, it was causing
segment drops.
Also, the previous code was moving the right edge of the
window back and forth too often, even when nothing in
the system was competing on the IOBs. Shrinking the
window that way is a kinda well known recipe to confuse
the peer stack.
* Move the code to advance rcvseq for user data from tcp_input
to receive handlers.
Motivation: allow partial ack.
* If we drop a segment, ignore FIN as well. Note than tcp FIN bit is
logically after the user data in the same segment.
I assume this was just an oversight because I couldn't
find any obvious reason to special-case only the first IOB.
The commit message of the original commit is cited below.
```
commit bf21056001
Author: chao.an <anchao@xiaomi.com>
Date: Fri Nov 27 09:50:38 2020 +0800
net/tcp: fallback to unthrottle pool to avoid deadlock
Add a fallback mechanism to ensure that there are still available
iobs for an free connection, Guarantees all connections will have
a minimum threshold iob to keep the connection not be hanged.
Change-Id: I59bed98d135ccd1f16264b9ccacdd1b0d91261de
Signed-off-by: chao.an <anchao@xiaomi.com>
```
* 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
Gregory Nutt is the copyright holder for those files and he has submitted the
SGA as a result we can migrate the licenses to Apache.
Signed-off-by: Alin Jerpelea <alin.jerpelea@sony.com>
My recent changes to buffered tcp send broke this. [1]
One of my local apps using non-blocking tcp is working
again with this fix.
[1]
```
commit 837e1a72a4
Author: YAMAMOTO Takashi <yamamoto@midokura.com>
Date: Mon Mar 15 16:19:42 2021 +0900
tcp_send_buffered.c: improve tcp write buffering
```
reset the connection refcount if SYN retry count has elapsed
Assertion:
up_assert: Assertion failed at file:tcp/tcp_conn.c line: 764 task: netdev_wq
N/A
Signed-off-by: chao.an <anchao@xiaomi.com>
Summary:
- Based on the discussion (PR#2772), let me revert the commit
Impact:
- None
Testing:
- N/A
This reverts commit ec8bf5c8c1.
Suggested-by: YAMAMOTO Takashi <yamamoto@midokura.com>
Signed-off-by: Masayuki Ishikawa <Masayuki.Ishikawa@jp.sony.com>
Summary:
- This commit adds DEBUGASSERT() to check the IOB size
Impact:
- None
Testing:
- Tested with sabre-6quad:netnsh with QEMU
Signed-off-by: Masayuki Ishikawa <Masayuki.Ishikawa@jp.sony.com>
Request the TCP ACK to estimate the receive window after handle
any data already buffered in a read-ahead buffer.
Change-Id: Id998a1125dd2991d73ba4bef081ddcb7adea4f0d
Signed-off-by: chao.an <anchao@xiaomi.com>
Urgent data preceded "normal" data in the TCP payload. If the urgent data is larger than the size of the TCP payload, this indicates that the entire payload is urgent data and that urgent data continues in the next packet.
This case was handled correctly for the case where urgent data was present but was not being handled correctly in the case where the urgent data was NOT present.
RFC2001: TCP Slow Start, Congestion Avoidance, Fast Retransmit,
and Fast Recovery Algorithms
...
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.
Change-Id: Ie2cbcecab507c3d831f74390a6a85e0c5c8e0652
Signed-off-by: chao.an <anchao@xiaomi.com>
The number of available iobs is already sub in iob_navail(true) on line 114
net/tcp/tcp_recvwindow.c:
...
73 uint16_t tcp_get_recvwindow(FAR struct net_driver_s *dev)
...
114 niob_avail = iob_navail(true);
Change-Id: I230927904d8db08ed8d95d7fa18c5c5fce08aa5e
Signed-off-by: chao.an <anchao@xiaomi.com>
Add a fallback mechanism to ensure that there are still available
iobs for an free connection, Guarantees all connections will have
a minimum threshold iob to keep the connection not be hanged.
Change-Id: I59bed98d135ccd1f16264b9ccacdd1b0d91261de
Signed-off-by: chao.an <anchao@xiaomi.com>
remove the connection assertion since the instance will be invalid
if the network device has been taken down.
net/netdev/netdev_ioctl.c:
1847 void netdev_ifdown(FAR struct net_driver_s *dev)
1848 {
...
1871 /* Notify clients that the network has been taken down */
1872
1873 devif_dev_event(dev, NULL, NETDEV_DOWN);
...
1883 }
Change-Id: I492b97b5ebe035ea67bbdd7ed635cb13d085e89c
Signed-off-by: chao.an <anchao@xiaomi.com>
In the current net stack implementation, there is no mechanism
for notifying the loss of the wireless connection, if the network
is disconnected then application sends data packets through tcp,
the tcp_timer will keep retrying fetch the ack for awhile, the
connection status will not be able to be switched timely.
Change-Id: I84d1121527edafc6ee6ad56ba164838694e7e11c
Signed-off-by: chao.an <anchao@xiaomi.com>
Fixes an issue where tcp sockets with activated keepalives stalled and
were not properly closed. Poll would not indicate a POLLHUP and therefore
locks down the application.
* tcp_conn_s.tcp_conn_s & tcp_conn_s.keepintvl changed to uint32_t
According RFC1122 keepidle MUST have a default of 2 hours.
Found by clang-check:
tcp/tcp_timer.c:185:15: warning: Value stored to 'result' is never read
result = tcp_callback(dev, conn, TCP_TIMEDOUT);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tcp/tcp_timer.c:264:23: warning: Value stored to 'result' is never read
result = tcp_callback(dev, listener, TCP_TIMEDOUT);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tcp/tcp_timer.c:300:19: warning: Value stored to 'result' is never read
result = tcp_callback(dev, conn, TCP_TIMEDOUT);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 warnings generated.
In some extreme scenarios(eg. crash, reboot, reset, etc...),
an established connection cannot guarantee that the port can be
closed properly, if we try to reconnect quickly after reset, the
connection will fail since the current port is same as the
previous one, the previous port connection has been hold on server side.
dynamically apply for the port base to avoid duplication.
Change-Id: I0089244b2707ea61f553a4dae09c7af3649c70bd
Signed-off-by: chao.an <anchao@xiaomi.com>
It's enough to check the buffer available in the net event handler
Change-Id: I2d7c7a03675cf6eff6ffb42a81b7c7245253e92c
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
Since the request address was not properly resolved before the handshake,
every time of connection, the handshake data will be overwitten into
arp packet and retransmitted until the next tcp timer.
Request the arp address before the handshake to avoid the retransmission.
Change-Id: I80118b9a8096c126c8e16cdf2f7b3d98fca92437
Signed-off-by: chao.an <anchao@xiaomi.com>