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:
committed by
Augustin Cavalier
parent
5d9eb1eb1b
commit
272e1a2f97
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user