tcp (fixes #1043): Fix BBR incorrect minRtt updates
This commit is contained in:
@@ -18,6 +18,8 @@ Changes from ns-3.42 to ns-3-dev
|
||||
|
||||
### New API
|
||||
|
||||
* (tcp) A new trace source `TcpSocketBase::LastRtt` has been added for tracing the last RTT sample observed. The existing trace source `TcpSocketBase::Rtt` is still providing the smoothed RTT, although it had been incorrectly documented as providing the last RTT.
|
||||
|
||||
### Changes to existing API
|
||||
|
||||
* (lr-wpan) Attribute `macBeaconPayload` in `MacPibAttributes` is now a std::vector<uint8_t> instead of a packet pointer.
|
||||
|
||||
@@ -728,7 +728,7 @@ TcpBbr::CongestionStateSet(Ptr<TcpSocketState> tcb, const TcpSocketState::TcpCon
|
||||
if (newState == TcpSocketState::CA_OPEN && !m_isInitialized)
|
||||
{
|
||||
NS_LOG_DEBUG("CongestionStateSet triggered to CA_OPEN :: " << newState);
|
||||
m_minRtt = tcb->m_lastRtt.Get() != Time::Max() ? tcb->m_lastRtt.Get() : Time::Max();
|
||||
m_minRtt = tcb->m_srtt.Get() != Time::Max() ? tcb->m_srtt.Get() : Time::Max();
|
||||
m_minRttStamp = Simulator::Now();
|
||||
m_priorCwnd = tcb->m_cWnd;
|
||||
tcb->m_ssThresh = tcb->m_initialSsThresh;
|
||||
|
||||
@@ -176,7 +176,11 @@ TcpSocketBase::GetTypeId()
|
||||
MakeTraceSourceAccessor(&TcpSocketBase::m_rto),
|
||||
"ns3::TracedValueCallback::Time")
|
||||
.AddTraceSource("RTT",
|
||||
"Last RTT sample",
|
||||
"Smoothed RTT",
|
||||
MakeTraceSourceAccessor(&TcpSocketBase::m_srttTrace),
|
||||
"ns3::TracedValueCallback::Time")
|
||||
.AddTraceSource("LastRTT",
|
||||
"RTT of the last (S)ACKed packet",
|
||||
MakeTraceSourceAccessor(&TcpSocketBase::m_lastRttTrace),
|
||||
"ns3::TracedValueCallback::Time")
|
||||
.AddTraceSource("NextTxSequence",
|
||||
@@ -322,6 +326,10 @@ TcpSocketBase::TcpSocketBase()
|
||||
|
||||
ok = m_tcb->TraceConnectWithoutContext("RTT", MakeCallback(&TcpSocketBase::UpdateRtt, this));
|
||||
NS_ASSERT(ok == true);
|
||||
|
||||
ok = m_tcb->TraceConnectWithoutContext("LastRTT",
|
||||
MakeCallback(&TcpSocketBase::UpdateLastRtt, this));
|
||||
NS_ASSERT(ok == true);
|
||||
}
|
||||
|
||||
TcpSocketBase::TcpSocketBase(const TcpSocketBase& sock)
|
||||
@@ -458,6 +466,10 @@ TcpSocketBase::TcpSocketBase(const TcpSocketBase& sock)
|
||||
|
||||
ok = m_tcb->TraceConnectWithoutContext("RTT", MakeCallback(&TcpSocketBase::UpdateRtt, this));
|
||||
NS_ASSERT(ok == true);
|
||||
|
||||
ok = m_tcb->TraceConnectWithoutContext("LastRTT",
|
||||
MakeCallback(&TcpSocketBase::UpdateLastRtt, this));
|
||||
NS_ASSERT(ok == true);
|
||||
}
|
||||
|
||||
TcpSocketBase::~TcpSocketBase()
|
||||
@@ -1989,7 +2001,7 @@ TcpSocketBase::ProcessAck(const SequenceNumber32& ackNumber,
|
||||
else if (ackNumber == oldHeadSequence)
|
||||
{
|
||||
// DupAck. Artificially call PktsAcked: after all, one segment has been ACKed.
|
||||
m_congestionControl->PktsAcked(m_tcb, 1, m_tcb->m_lastRtt);
|
||||
m_congestionControl->PktsAcked(m_tcb, 1, m_tcb->m_srtt);
|
||||
}
|
||||
else if (ackNumber > oldHeadSequence)
|
||||
{
|
||||
@@ -2057,7 +2069,7 @@ TcpSocketBase::ProcessAck(const SequenceNumber32& ackNumber,
|
||||
// This partial ACK acknowledge the fact that one segment has been
|
||||
// previously lost and now successfully received. All others have
|
||||
// been processed when they come under the form of dupACKs
|
||||
m_congestionControl->PktsAcked(m_tcb, 1, m_tcb->m_lastRtt);
|
||||
m_congestionControl->PktsAcked(m_tcb, 1, m_tcb->m_srtt);
|
||||
NewAck(ackNumber, m_isFirstPartialAck);
|
||||
|
||||
if (m_isFirstPartialAck)
|
||||
@@ -2085,7 +2097,7 @@ TcpSocketBase::ProcessAck(const SequenceNumber32& ackNumber,
|
||||
// of RecoveryPoint.
|
||||
else if (ackNumber < m_recover && m_tcb->m_congState == TcpSocketState::CA_LOSS)
|
||||
{
|
||||
m_congestionControl->PktsAcked(m_tcb, segsAcked, m_tcb->m_lastRtt);
|
||||
m_congestionControl->PktsAcked(m_tcb, segsAcked, m_tcb->m_srtt);
|
||||
m_congestionControl->IncreaseWindow(m_tcb, segsAcked);
|
||||
|
||||
NS_LOG_DEBUG(" Cong Control Called, cWnd=" << m_tcb->m_cWnd
|
||||
@@ -2100,7 +2112,7 @@ TcpSocketBase::ProcessAck(const SequenceNumber32& ackNumber,
|
||||
}
|
||||
else if (m_tcb->m_congState == TcpSocketState::CA_CWR)
|
||||
{
|
||||
m_congestionControl->PktsAcked(m_tcb, segsAcked, m_tcb->m_lastRtt);
|
||||
m_congestionControl->PktsAcked(m_tcb, segsAcked, m_tcb->m_srtt);
|
||||
// TODO: need to check behavior if marking is compounded by loss
|
||||
// and/or packet reordering
|
||||
if (!m_congestionControl->HasCongControl() && segsAcked >= 1)
|
||||
@@ -2113,7 +2125,7 @@ TcpSocketBase::ProcessAck(const SequenceNumber32& ackNumber,
|
||||
{
|
||||
if (m_tcb->m_congState == TcpSocketState::CA_OPEN)
|
||||
{
|
||||
m_congestionControl->PktsAcked(m_tcb, segsAcked, m_tcb->m_lastRtt);
|
||||
m_congestionControl->PktsAcked(m_tcb, segsAcked, m_tcb->m_srtt);
|
||||
}
|
||||
else if (m_tcb->m_congState == TcpSocketState::CA_DISORDER)
|
||||
{
|
||||
@@ -2121,7 +2133,7 @@ TcpSocketBase::ProcessAck(const SequenceNumber32& ackNumber,
|
||||
{
|
||||
m_congestionControl->PktsAcked(m_tcb,
|
||||
segsAcked - oldDupAckCount,
|
||||
m_tcb->m_lastRtt);
|
||||
m_tcb->m_srtt);
|
||||
}
|
||||
|
||||
if (!isDupack)
|
||||
@@ -2158,7 +2170,7 @@ TcpSocketBase::ProcessAck(const SequenceNumber32& ackNumber,
|
||||
// TODO: check consistency for dynamic segment size
|
||||
segsAcked =
|
||||
static_cast<uint32_t>(ackNumber - oldHeadSequence) / m_tcb->m_segmentSize;
|
||||
m_congestionControl->PktsAcked(m_tcb, segsAcked, m_tcb->m_lastRtt);
|
||||
m_congestionControl->PktsAcked(m_tcb, segsAcked, m_tcb->m_srtt);
|
||||
m_congestionControl->CwndEvent(m_tcb, TcpSocketState::CA_EVENT_COMPLETE_CWR);
|
||||
m_congestionControl->CongestionStateSet(m_tcb, TcpSocketState::CA_OPEN);
|
||||
m_tcb->m_congState = TcpSocketState::CA_OPEN;
|
||||
@@ -2177,7 +2189,7 @@ TcpSocketBase::ProcessAck(const SequenceNumber32& ackNumber,
|
||||
// can increase cWnd)
|
||||
segsAcked = (ackNumber - m_recover) / m_tcb->m_segmentSize;
|
||||
|
||||
m_congestionControl->PktsAcked(m_tcb, segsAcked, m_tcb->m_lastRtt);
|
||||
m_congestionControl->PktsAcked(m_tcb, segsAcked, m_tcb->m_srtt);
|
||||
|
||||
m_congestionControl->CongestionStateSet(m_tcb, TcpSocketState::CA_OPEN);
|
||||
m_tcb->m_congState = TcpSocketState::CA_OPEN;
|
||||
@@ -3622,59 +3634,88 @@ TcpSocketBase::ReceivedData(Ptr<Packet> p, const TcpHeader& tcpHeader)
|
||||
}
|
||||
}
|
||||
|
||||
Time
|
||||
TcpSocketBase::CalculateRttSample(const TcpHeader& tcpHeader, const RttHistory& rttHistory)
|
||||
{
|
||||
NS_LOG_FUNCTION(this);
|
||||
SequenceNumber32 ackSeq = tcpHeader.GetAckNumber();
|
||||
Time rtt;
|
||||
|
||||
if (!rttHistory.retx && ackSeq >= (rttHistory.seq + SequenceNumber32(rttHistory.count)))
|
||||
{ // Ok to use this sample
|
||||
if (m_timestampEnabled && tcpHeader.HasOption(TcpOption::TS))
|
||||
{
|
||||
Ptr<const TcpOptionTS> ts;
|
||||
ts = DynamicCast<const TcpOptionTS>(tcpHeader.GetOption(TcpOption::TS));
|
||||
rtt = TcpOptionTS::ElapsedTimeFromTsValue(ts->GetEcho());
|
||||
if (rtt.IsZero())
|
||||
{
|
||||
NS_LOG_LOGIC("TcpSocketBase::EstimateRtt - RTT calculated from TcpOption::TS "
|
||||
"is zero, approximating to 1us.");
|
||||
NS_LOG_DEBUG("RTT calculated from TcpOption::TS is zero, updating rtt to 1us.");
|
||||
rtt = MicroSeconds(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Elapsed time since the packet was transmitted
|
||||
rtt = Simulator::Now() - rttHistory.time;
|
||||
}
|
||||
}
|
||||
return rtt;
|
||||
}
|
||||
|
||||
void
|
||||
TcpSocketBase::EstimateRtt(const TcpHeader& tcpHeader)
|
||||
{
|
||||
NS_LOG_FUNCTION(this);
|
||||
SequenceNumber32 ackSeq = tcpHeader.GetAckNumber();
|
||||
Time m = Time(0.0);
|
||||
Time rtt;
|
||||
|
||||
// An ack has been received, calculate rtt and log this measurement
|
||||
// Note we use a linear search (O(n)) for this since for the common
|
||||
// case the ack'ed packet will be at the head of the list
|
||||
if (!m_history.empty())
|
||||
{
|
||||
RttHistory& h = m_history.front();
|
||||
if (!h.retx && ackSeq >= (h.seq + SequenceNumber32(h.count)))
|
||||
{ // Ok to use this sample
|
||||
if (m_timestampEnabled && tcpHeader.HasOption(TcpOption::TS))
|
||||
{
|
||||
Ptr<const TcpOptionTS> ts;
|
||||
ts = DynamicCast<const TcpOptionTS>(tcpHeader.GetOption(TcpOption::TS));
|
||||
m = TcpOptionTS::ElapsedTimeFromTsValue(ts->GetEcho());
|
||||
if (m.IsZero())
|
||||
{
|
||||
NS_LOG_LOGIC("TcpSocketBase::EstimateRtt - RTT calculated from TcpOption::TS "
|
||||
"is zero, approximating to 1us.");
|
||||
m = MicroSeconds(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m = Simulator::Now() - h.time; // Elapsed time
|
||||
}
|
||||
}
|
||||
}
|
||||
RttHistory& earliestTransmittedPktHistory = m_history.front();
|
||||
rtt = CalculateRttSample(tcpHeader, earliestTransmittedPktHistory);
|
||||
|
||||
// Now delete all ack history with seq <= ack
|
||||
// Store ACKed packet that has the latest transmission time to update `lastRtt`
|
||||
RttHistory latestTransmittedPktHistory = earliestTransmittedPktHistory;
|
||||
|
||||
// Delete all ACK history with seq <= ack
|
||||
while (!m_history.empty())
|
||||
{
|
||||
RttHistory& h = m_history.front();
|
||||
if ((h.seq + SequenceNumber32(h.count)) > ackSeq)
|
||||
RttHistory& rttHistory = m_history.front();
|
||||
if ((rttHistory.seq + SequenceNumber32(rttHistory.count)) > ackSeq)
|
||||
{
|
||||
break; // Done removing
|
||||
}
|
||||
|
||||
latestTransmittedPktHistory = rttHistory;
|
||||
m_history.pop_front(); // Remove
|
||||
}
|
||||
|
||||
if (!m.IsZero())
|
||||
// In case of multiple packets being ACKed in a single acknowledgement, `m_lastRtt` is
|
||||
// RTT of the last (S)ACKed packet calculated using the data packet with the latest
|
||||
// transmission time
|
||||
Time lastRtt = CalculateRttSample(tcpHeader, latestTransmittedPktHistory);
|
||||
if (!lastRtt.IsZero())
|
||||
{
|
||||
m_rtt->Measurement(m); // Log the measurement
|
||||
NS_LOG_DEBUG("Last RTT sample updated to: " << lastRtt);
|
||||
m_tcb->m_lastRtt = lastRtt;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rtt.IsZero())
|
||||
{
|
||||
m_rtt->Measurement(rtt); // Log the measurement
|
||||
// RFC 6298, clause 2.4
|
||||
m_rto = Max(m_rtt->GetEstimate() + Max(m_clockGranularity, m_rtt->GetVariation() * 4),
|
||||
m_minRto);
|
||||
m_tcb->m_lastRtt = m_rtt->GetEstimate();
|
||||
m_tcb->m_minRtt = std::min(m_tcb->m_lastRtt.Get(), m_tcb->m_minRtt);
|
||||
NS_LOG_INFO(this << m_tcb->m_lastRtt << m_tcb->m_minRtt);
|
||||
m_tcb->m_srtt = m_rtt->GetEstimate();
|
||||
m_tcb->m_minRtt = std::min(m_tcb->m_srtt.Get(), m_tcb->m_minRtt);
|
||||
NS_LOG_INFO(this << m_tcb->m_srtt << m_tcb->m_minRtt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4525,6 +4566,12 @@ TcpSocketBase::UpdateBytesInFlight(uint32_t oldValue, uint32_t newValue) const
|
||||
|
||||
void
|
||||
TcpSocketBase::UpdateRtt(Time oldValue, Time newValue) const
|
||||
{
|
||||
m_srttTrace(oldValue, newValue);
|
||||
}
|
||||
|
||||
void
|
||||
TcpSocketBase::UpdateLastRtt(Time oldValue, Time newValue) const
|
||||
{
|
||||
m_lastRttTrace(oldValue, newValue);
|
||||
}
|
||||
@@ -4626,12 +4673,12 @@ TcpSocketBase::UpdatePacingRate()
|
||||
<< m_tcb->m_ssThresh);
|
||||
factor = static_cast<double>(m_tcb->m_pacingCaRatio) / 100;
|
||||
}
|
||||
Time lastRtt = m_tcb->m_lastRtt.Get(); // Get underlying Time value
|
||||
NS_LOG_DEBUG("Last RTT is " << lastRtt.GetSeconds());
|
||||
Time srtt = m_tcb->m_srtt.Get(); // Get underlying Time value
|
||||
NS_LOG_DEBUG("Smoothed RTT is " << srtt.GetSeconds());
|
||||
|
||||
// Multiply by 8 to convert from bytes per second to bits per second
|
||||
DataRate pacingRate((std::max(m_tcb->m_cWnd, m_tcb->m_bytesInFlight) * 8 * factor) /
|
||||
lastRtt.GetSeconds());
|
||||
srtt.GetSeconds());
|
||||
if (pacingRate < m_tcb->m_maxPacingRate)
|
||||
{
|
||||
NS_LOG_DEBUG("Pacing rate updated to: " << pacingRate);
|
||||
|
||||
@@ -370,6 +370,11 @@ class TcpSocketBase : public TcpSocket
|
||||
/**
|
||||
* \brief Callback pointer for RTT trace chaining
|
||||
*/
|
||||
TracedCallback<Time, Time> m_srttTrace;
|
||||
|
||||
/**
|
||||
* \brief Callback pointer for Last RTT trace chaining
|
||||
*/
|
||||
TracedCallback<Time, Time> m_lastRttTrace;
|
||||
|
||||
/**
|
||||
@@ -444,6 +449,13 @@ class TcpSocketBase : public TcpSocket
|
||||
*/
|
||||
void UpdateRtt(Time oldValue, Time newValue) const;
|
||||
|
||||
/**
|
||||
* \brief Callback function to hook to TcpSocketState lastRtt
|
||||
* \param oldValue old lastRtt value
|
||||
* \param newValue new lastRtt value
|
||||
*/
|
||||
void UpdateLastRtt(Time oldValue, Time newValue) const;
|
||||
|
||||
/**
|
||||
* \brief Install a congestion control algorithm on this socket
|
||||
*
|
||||
@@ -1049,6 +1061,20 @@ class TcpSocketBase : public TcpSocket
|
||||
*/
|
||||
virtual void ReceivedData(Ptr<Packet> packet, const TcpHeader& tcpHeader);
|
||||
|
||||
/**
|
||||
* \brief Calculate RTT sample for the ACKed packet
|
||||
*
|
||||
* Per RFC 6298 (Section 3),
|
||||
* If `m_timestampsEnabled` is true, calculate RTT using timestamps option.
|
||||
* Otherwise, return RTT as the elapsed time since the packet was transmitted.
|
||||
* If ACKed packed was a retrasmitted packet, return zero time.
|
||||
*
|
||||
* \param tcpHeader the packet's TCP header
|
||||
* \param rttHistory the ACKed packet's RTT History
|
||||
* \returns the RTT sample
|
||||
*/
|
||||
virtual Time CalculateRttSample(const TcpHeader& tcpHeader, const RttHistory& rttHistory);
|
||||
|
||||
/**
|
||||
* \brief Take into account the packet for RTT estimation
|
||||
* \param tcpHeader the packet's TCP header
|
||||
|
||||
@@ -91,7 +91,11 @@ TcpSocketState::GetTypeId()
|
||||
MakeTraceSourceAccessor(&TcpSocketState::m_bytesInFlight),
|
||||
"ns3::TracedValueCallback::Uint32")
|
||||
.AddTraceSource("RTT",
|
||||
"Last RTT sample",
|
||||
"Smoothed RTT",
|
||||
MakeTraceSourceAccessor(&TcpSocketState::m_srtt),
|
||||
"ns3::TracedValueCallback::Time")
|
||||
.AddTraceSource("LastRTT",
|
||||
"RTT of the last (S)ACKed packet",
|
||||
MakeTraceSourceAccessor(&TcpSocketState::m_lastRtt),
|
||||
"ns3::TracedValueCallback::Time");
|
||||
return tid;
|
||||
@@ -120,6 +124,7 @@ TcpSocketState::TcpSocketState(const TcpSocketState& other)
|
||||
m_minRtt(other.m_minRtt),
|
||||
m_bytesInFlight(other.m_bytesInFlight),
|
||||
m_isCwndLimited(other.m_isCwndLimited),
|
||||
m_srtt(other.m_srtt),
|
||||
m_lastRtt(other.m_lastRtt),
|
||||
m_ecnMode(other.m_ecnMode),
|
||||
m_useEcn(other.m_useEcn),
|
||||
|
||||
@@ -208,7 +208,8 @@ class TcpSocketState : public Object
|
||||
|
||||
TracedValue<uint32_t> m_bytesInFlight{0}; //!< Bytes in flight
|
||||
bool m_isCwndLimited{false}; //!< Whether throughput is limited by cwnd
|
||||
TracedValue<Time> m_lastRtt{Seconds(0.0)}; //!< Last RTT sample collected
|
||||
TracedValue<Time> m_srtt; //!< Smoothed RTT
|
||||
TracedValue<Time> m_lastRtt; //!< RTT of the last (S)ACKed packet
|
||||
|
||||
Ptr<TcpRxBuffer> m_rxBuffer; //!< Rx buffer (reordering buffer)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user