Skip to content

Commit f439af0

Browse files
committed
GPT-5 improving congestion control
1 parent 0e2deda commit f439af0

1 file changed

Lines changed: 60 additions & 47 deletions

File tree

src/net/SCTP/SctpDataSender.cs

Lines changed: 60 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -205,17 +205,19 @@ public void SetReceiverWindow(uint remoteARwnd)
205205
/// <param name="sack">The SACK chunk from the remote peer.</param>
206206
public void GotSack(SctpChunkView sack)
207207
{
208-
{
209-
if (_inRetransmitMode.TryTurnOff())
210-
{
211-
logger.LogDebug("SCTP sender exiting retransmit mode.");
212-
}
213-
214-
unchecked
215-
{
216-
uint maxTSNDistance = SctpDataReceiver.GetDistance(_cumulativeAckTSN, TSN);
217-
bool processGapReports = true;
218-
uint cumAckTSNBeforeSackProcessing = _cumulativeAckTSN;
208+
{
209+
if (_inRetransmitMode.TryTurnOff())
210+
{
211+
logger.LogDebug("SCTP sender exiting retransmit mode, cwnd {Cwnd} ssthresh {Ssthresh} outstanding {Outstanding}.",
212+
_congestionWindow, _slowStartThreshold, _outstandingBytes);
213+
}
214+
215+
unchecked
216+
{
217+
uint outstandingBytesBeforeSackProcessing = (uint)_outstandingBytes;
218+
uint maxTSNDistance = SctpDataReceiver.GetDistance(_cumulativeAckTSN, TSN);
219+
bool processGapReports = true;
220+
uint cumAckTSNBeforeSackProcessing = _cumulativeAckTSN;
219221

220222
if (_unconfirmedChunks.TryGetValue(sack.CumulativeTsnAck, out var result))
221223
{
@@ -292,20 +294,26 @@ public void GotSack(SctpChunkView sack)
292294
ProcessGapReports(sack.GapAckBlocks, maxTSNDistance, didIncrementCumAckTSN);
293295
}
294296

295-
// rfc4960 6.2.1 D iv
296-
// If the Cumulative TSN Ack matches or exceeds the Fast Recovery exitpoint(Section 7.2.4), Fast Recovery is exited.
297-
if (SctpDataReceiver.IsNewerOrEqual(_fastRecoveryExitPoint, _cumulativeAckTSN) && _inFastRecoveryMode.TryTurnOff())
298-
{
299-
logger.LogTrace("SCTP sender exiting fast recovery at TSN {TSN}", _fastRecoveryExitPoint);
300-
}
301-
}
302-
303-
var outstandingBytes = _outstandingBytes;
304-
_receiverWindow = CalculateReceiverWindow(sack.ARwnd, outstandingBytes: (uint)outstandingBytes);
305-
_congestionWindow = CalculateCongestionWindow(InterlockedEx.Read(ref _lastAckedDataChunkSize), outstandingBytes: (uint)outstandingBytes);
306-
307-
// SACK's will normally allow more data to be sent.
308-
_senderMre.Set();
297+
// rfc4960 6.2.1 D iv
298+
// If the Cumulative TSN Ack matches or exceeds the Fast Recovery exitpoint(Section 7.2.4), Fast Recovery is exited.
299+
if (SctpDataReceiver.IsNewerOrEqual(_cumulativeAckTSN, _fastRecoveryExitPoint) && _inFastRecoveryMode.TryTurnOff())
300+
{
301+
logger.LogTrace("SCTP sender exiting fast recovery at TSN {TSN} cwnd {Cwnd} ssthresh {Ssthresh} outstanding {Outstanding}.",
302+
_fastRecoveryExitPoint, _congestionWindow, _slowStartThreshold, _outstandingBytes);
303+
}
304+
305+
var outstandingBytesAfterSackProcessing = (uint)_outstandingBytes;
306+
_receiverWindow = CalculateReceiverWindow(sack.ARwnd, outstandingBytes: outstandingBytesAfterSackProcessing);
307+
_congestionWindow = CalculateCongestionWindow(InterlockedEx.Read(ref _lastAckedDataChunkSize), outstandingBytes: outstandingBytesBeforeSackProcessing);
308+
309+
logger.LogTrace(
310+
"SCTP sender SACK updated cwnd {Cwnd} rwnd {Rwnd} outstandingBefore {OutstandingBefore} outstandingAfter {OutstandingAfter} retransmitMode {Retransmit} fastRecoveryMode {FastRecovery}.",
311+
_congestionWindow, _receiverWindow, outstandingBytesBeforeSackProcessing, outstandingBytesAfterSackProcessing,
312+
_inRetransmitMode.IsOn(), _inFastRecoveryMode.IsOn());
313+
}
314+
315+
// SACK's will normally allow more data to be sent.
316+
_senderMre.Set();
309317
}
310318
}
311319

@@ -511,7 +519,8 @@ private void ProcessGapReports(ReadOnlySpan<byte> sackGapBlocks, uint maxTSNDist
511519
var last = SctpTsnGapBlock.Read(sackGapBlocks.Slice(sackGapBlocks.Length - SctpSackChunk.GAP_REPORT_LENGTH));
512520
_fastRecoveryExitPoint = _cumulativeAckTSN + last.End;
513521

514-
logger.LogDebug($"SCTP sender entering fast recovery mode due to missing TSN {missingTSN}. Fast recovery exit point {_fastRecoveryExitPoint}.");
522+
logger.LogDebug("SCTP sender entering fast recovery mode due to missing TSN {MissingTSN}. ExitPoint {ExitPoint} cwnd {Cwnd} ssthresh {Ssthresh} outstanding {Outstanding}.",
523+
missingTSN, _fastRecoveryExitPoint, _congestionWindow, _slowStartThreshold, _outstandingBytes);
515524
// RFC4960 7.2.3
516525
_slowStartThreshold = (uint)Math.Max(_congestionWindow / 2, 4 * _defaultMTU);
517526
_congestionWindow = _slowStartThreshold;
@@ -574,14 +583,15 @@ private void DoSend(object state)
574583
{
575584
logger.LogDebug("SCTP association data send thread started for association {ID}.", _associationID);
576585

577-
while (!_closed.HasOccurred)
578-
{
579-
var outstandingBytes = (uint)_outstandingBytes;
586+
while (!_closed.HasOccurred)
587+
{
588+
var outstandingBytes = (uint)_outstandingBytes;
589+
var allowedWindow = Math.Min(_congestionWindow, _receiverWindow);
580590
// DateTime.Now calls have been a tiny bit expensive in the past so get a small saving by only
581591
// calling once per loop.
582592
var now = SctpDataChunk.Timestamp.Now;
583593

584-
int burstSize = (_inRetransmitMode.IsOn() || _inFastRecoveryMode.IsOn() || _congestionWindow < outstandingBytes || _receiverWindow == 0) ? 1 : MAX_BURST;
594+
int burstSize = (_inRetransmitMode.IsOn() || _inFastRecoveryMode.IsOn() || allowedWindow <= outstandingBytes || _receiverWindow == 0) ? 1 : MAX_BURST;
585595
int chunksSent = 0;
586596

587597
//logger.LogTrace($"SCTP sender burst size {burstSize}, in retransmit mode {_inRetransmitMode}, cwnd {_congestionWindow}, arwnd {_receiverWindow}.");
@@ -638,12 +648,13 @@ private void DoSend(object state)
638648
"flags {Flags:X2}, send count {Count}.",
639649
chunk.TSN, chunk.UserDataLength, chunk.ChunkFlags, chunk.SendCount);
640650

641-
_sendDataChunk(chunk);
642-
chunksSent++;
643-
644-
if (_inRetransmitMode.TryTurnOn())
645-
{
646-
logger.LogDebug("SCTP sender entering retransmit mode.");
651+
_sendDataChunk(chunk);
652+
chunksSent++;
653+
654+
if (_inRetransmitMode.TryTurnOn())
655+
{
656+
logger.LogDebug("SCTP sender entering retransmit mode, cwnd {Cwnd} ssthresh {Ssthresh} outstanding {Outstanding}.",
657+
_congestionWindow, _slowStartThreshold, _outstandingBytes);
647658

648659
// When the T3-rtx timer expires on an address, SCTP should perform slow start.
649660
// RFC4960 7.2.3
@@ -664,9 +675,9 @@ private void DoSend(object state)
664675
// rfc4960 6.1: At any given time, the sender MUST NOT transmit new data to a given transport address
665676
// if it has cwnd or more bytes of data outstanding to that transport address.
666677

667-
// Send any new data chunks that have not yet been sent.
668-
if (chunksSent < burstSize && !_sendQueue.IsEmpty && _congestionWindow > outstandingBytes)
669-
{
678+
// Send any new data chunks that have not yet been sent.
679+
if (chunksSent < burstSize && !_sendQueue.IsEmpty && allowedWindow > outstandingBytes)
680+
{
670681
while (chunksSent < burstSize && _sendQueue.TryDequeue(out var dataChunk))
671682
{
672683
dataChunk.LastSentAt = SctpDataChunk.Timestamp.Now;
@@ -708,14 +719,16 @@ private void DoSend(object state)
708719
/// <summary>
709720
/// Determines how many milliseconds the send thread should wait before the next send attempt.
710721
/// </summary>
711-
private int GetSendWaitMilliseconds()
712-
{
713-
if (!_sendQueue.IsEmpty || !_missingChunks.IsEmpty)
714-
{
715-
if (_receiverWindow > 0 && _congestionWindow > (uint)_outstandingBytes)
716-
{
717-
return _burstPeriodMilliseconds;
718-
}
722+
private int GetSendWaitMilliseconds()
723+
{
724+
if (!_sendQueue.IsEmpty || !_missingChunks.IsEmpty)
725+
{
726+
var allowedWindow = Math.Min(_congestionWindow, _receiverWindow);
727+
728+
if (_receiverWindow > 0 && allowedWindow > (uint)_outstandingBytes)
729+
{
730+
return _burstPeriodMilliseconds;
731+
}
719732
else
720733
{
721734
return _rtoMinimumMilliseconds;

0 commit comments

Comments
 (0)