tcp: fixed no response from window update, removed ideal timer

The reason for the erratic behavior was that the tcp implementation
silently drops window update messages after noting the update but without
triggering any data send event. Before the new TCP patches were applied,
the implementation relied on a retransmission timeout to trigger a send event
after a window update. One of the new patches dealing with the ideal timer
changed the semantic of the restransmit function call and caused the behavior
witnessed.

But a retransmission timeout is not the correct solution to window update. In
fact a retransmission is not a desired effect of window update. So in the patch
attached, I have changed the behavior of the implementation to immediately
acknowledge the window update (along with data from SendQueue) and thus solving
the problem of complete halt in data transmission.

The patch also has the changes re-implemented that were reverted back but had
nothing to do with the issue at hand. For the time being, I have also removed
the "ideal timer" part from the patch (although it wasn't creating any
conflict). I initially decided to implement the ideal timer using the same
timer used for retransmission to avoid adding an additional timer. But as I
have seen, it can be problematic. So I will be re-implementing the ideal timer
and thus it was not included in this patch.

Signed-off-by: Augustin Cavalier <[email protected]>
Fixes #13704.
This commit is contained in:
A-star-ayush
2017-11-11 11:56:51 +01:00
committed by Augustin Cavalier
parent 5d9eb1eb1b
commit 272e1a2f97
3 changed files with 76 additions and 33 deletions
@@ -18,6 +18,7 @@
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <KernelExport.h>
#include <Select.h>
@@ -428,6 +429,7 @@ TCPEndpoint::TCPEndpoint(net_socket* socket)
fSendWindow(0),
fSendMaxWindow(0),
fSendMaxSegmentSize(TCP_DEFAULT_MAX_SEGMENT_SIZE),
fSendMaxSegments(0),
fSendQueue(socket->send.buffer_size),
fInitialSendSequence(0),
fPreviousHighestAcknowledge(0),
@@ -1384,13 +1386,6 @@ TCPEndpoint::_AddData(tcp_segment_header& segment, net_buffer* buffer)
if ((segment.flags & TCP_FLAG_PUSH) != 0)
fReceiveQueue.SetPushPointer();
if (fSendUnacknowledged == fSendMax) {
TRACE("data received, resetting ideal timer to: %"
B_PRIdBIGTIME, fRetransmitTimeout);
gStackModule->set_timer(&fRetransmitTimer, fRetransmitTimeout);
T(TimerSet(this, "ideal", fRetransmitTimeout));
}
return fReceiveQueue.Available() > 0;
}
@@ -1426,7 +1421,14 @@ TCPEndpoint::_PrepareReceivePath(tcp_segment_header& segment)
fFlags &= ~FLAG_OPTION_TIMESTAMP;
}
fCongestionWindow = 2 * fSendMaxSegmentSize;
if (fSendMaxSegmentSize > 2190)
fCongestionWindow = 2 * fSendMaxSegmentSize;
else if (fSendMaxSegmentSize > 1095)
fCongestionWindow = 3 * fSendMaxSegmentSize;
else
fCongestionWindow = 4 * fSendMaxSegmentSize;
fSendMaxSegments = fCongestionWindow / fSendMaxSegmentSize;
fSlowStartThreshold = (uint32)segment.advertised_window << fSendWindowShift;
}
@@ -1709,6 +1711,9 @@ TCPEndpoint::_Receive(tcp_segment_header& segment, net_buffer* buffer)
}
#endif
if (advertisedWindow > fSendWindow)
action |= IMMEDIATE_ACKNOWLEDGE;
fSendWindow = advertisedWindow;
if (advertisedWindow > fSendMaxWindow)
fSendMaxWindow = advertisedWindow;
@@ -1946,6 +1951,9 @@ inline bool
TCPEndpoint::_ShouldSendSegment(tcp_segment_header& segment, uint32 length,
uint32 segmentMaxSize, uint32 flightSize)
{
if (fState == ESTABLISHED && fSendMaxSegments == 0)
return false;
if (length > 0) {
// Avoid the silly window syndrome - we only send a segment in case:
// - we have a full segment to send, or
@@ -2168,6 +2176,9 @@ TCPEndpoint::_SendQueued(bool force, uint32 sendWindow)
fReceiveMaxAdvertised = fReceiveNext
+ ((uint32)segment.advertised_window << fReceiveWindowShift);
if (segmentLength != 0 && fState == ESTABLISHED)
--fSendMaxSegments;
status = next->module->send_routed_data(next, fRoute, buffer);
if (status < B_OK) {
gBufferModule->free(buffer);
@@ -2265,7 +2276,46 @@ TCPEndpoint::_Acknowledged(tcp_segment_header& segment)
if (fSendUnacknowledged < segment.acknowledge) {
fSendQueue.RemoveUntil(segment.acknowledge);
uint32 bytesAcknowledged = segment.acknowledge - fSendUnacknowledged.Number();
fPreviousHighestAcknowledge = fSendUnacknowledged;
fSendUnacknowledged = segment.acknowledge;
if (fPreviousHighestAcknowledge > fSendUnacknowledged) {
// need to update the recover variable upon a sequence wraparound
fRecover = segment.acknowledge - 1;
}
// the acknowledgment of the SYN/ACK MUST NOT increase the size of the congestion window
if (fSendUnacknowledged != fInitialSendSequence) {
if (fCongestionWindow < fSlowStartThreshold)
fCongestionWindow += min_c(bytesAcknowledged, fSendMaxSegmentSize);
else {
uint32 increment = fSendMaxSegmentSize * fSendMaxSegmentSize;
if (increment < fCongestionWindow)
increment = 1;
else
increment /= fCongestionWindow;
fCongestionWindow += increment;
}
fSendMaxSegments = UINT32_MAX;
}
if ((fFlags & FLAG_RECOVERY) != 0) {
fSendNext = fSendUnacknowledged;
_SendQueued();
fCongestionWindow -= bytesAcknowledged;
if (bytesAcknowledged > fSendMaxSegmentSize)
fCongestionWindow += fSendMaxSegmentSize;
fSendNext = fSendMax;
} else
fDuplicateAcknowledgeCount = 0;
if (fSendNext < fSendUnacknowledged)
fSendNext = fSendUnacknowledged;
@@ -2282,10 +2332,9 @@ TCPEndpoint::_Acknowledged(tcp_segment_header& segment)
}
if (fSendUnacknowledged == fSendMax) {
TRACE("all acknowledged, cancelling retransmission timer. Using it as ideal timer for: %"
B_PRIdBIGTIME, fRetransmitTimeout);
gStackModule->set_timer(&fRetransmitTimer, fRetransmitTimeout);
T(TimerSet(this, "ideal", fRetransmitTimeout));
TRACE("all acknowledged, cancelling retransmission timer.");
gStackModule->cancel_timer(&fRetransmitTimer);
T(TimerSet(this, "retransmit", -1));
fSendTime = 0;
} else {
TRACE("data acknowledged, resetting retransmission timer to: %"
@@ -2299,20 +2348,6 @@ TCPEndpoint::_Acknowledged(tcp_segment_header& segment)
fSendCondition.NotifyAll();
gSocketModule->notify(socket, B_SELECT_WRITE, fSendQueue.Free());
}
if (fCongestionWindow < fSlowStartThreshold)
fCongestionWindow += fSendMaxSegmentSize;
}
if (fCongestionWindow >= fSlowStartThreshold) {
uint32 increment = fSendMaxSegmentSize * fSendMaxSegmentSize;
if (increment < fCongestionWindow)
increment = 1;
else
increment /= fCongestionWindow;
fCongestionWindow += increment;
}
// if there is data left to be sent, send it now
@@ -2326,14 +2361,19 @@ TCPEndpoint::_Retransmit()
{
TRACE("Retransmit()");
_ResetSlowStart();
if (fState < ESTABLISHED) {
fRetransmitTimeout = TCP_SYN_RETRANSMIT_TIMEOUT;
fCongestionWindow = fSendMaxSegmentSize;
} else {
_ResetSlowStart();
fDuplicateAcknowledgeCount = 0;
// Do exponential back off of the retransmit timeout
fRetransmitTimeout *= 2;
if (fRetransmitTimeout > TCP_MAX_RETRANSMIT_TIMEOUT)
fRetransmitTimeout = TCP_MAX_RETRANSMIT_TIMEOUT;
}
fSendNext = fSendUnacknowledged;
// Do exponential back off of the retransmit timeout
fRetransmitTimeout *= 2;
if (fRetransmitTimeout > TCP_MAX_RETRANSMIT_TIMEOUT)
fRetransmitTimeout = TCP_MAX_RETRANSMIT_TIMEOUT;
_SendQueued();
fRecover = fSendNext.Number() - 1;
@@ -145,6 +145,7 @@ private:
uint32 fSendWindow;
uint32 fSendMaxWindow;
uint32 fSendMaxSegmentSize;
uint32 fSendMaxSegments;
BufferQueue fSendQueue;
tcp_sequence fLastAcknowledgeSent;
tcp_sequence fInitialSendSequence;
@@ -193,6 +193,8 @@ operator==(tcp_sequence a, tcp_sequence b)
#define TCP_MIN_RETRANSMIT_TIMEOUT 200000 // 200 msecs
// Maximum retransmit timeout (per RFC6298)
#define TCP_MAX_RETRANSMIT_TIMEOUT 60000000 // 60 secs
// New value for timeout in case of lost SYN (RFC 6298)
#define TCP_SYN_RETRANSMIT_TIMEOUT 3000000 // 3 secs
struct tcp_sack {
uint32 left_edge;