1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3 * Copyright (c) 2018 Natale Patriciello <natale.patriciello@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation;
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 */
19 #include "tcp-rate-ops.h"
20 #include "ns3/log.h"
21 #include "ns3/simulator.h"
22
23 namespace ns3 {
24
25 NS_LOG_COMPONENT_DEFINE ("TcpRateOps");
26 NS_OBJECT_ENSURE_REGISTERED (TcpRateOps);
27
28 TypeId
GetTypeId(void)29 TcpRateOps::GetTypeId (void)
30 {
31 static TypeId tid = TypeId ("ns3::TcpRateOps")
32 .SetParent<Object> ()
33 .SetGroupName ("Internet")
34 ;
35 return tid;
36 }
37
38 NS_OBJECT_ENSURE_REGISTERED (TcpRateLinux);
39
40 TypeId
GetTypeId(void)41 TcpRateLinux::GetTypeId (void)
42 {
43 static TypeId tid = TypeId ("ns3::TcpRateLinux")
44 .SetParent<TcpRateOps> ()
45 .SetGroupName ("Internet")
46 .AddTraceSource ("TcpRateUpdated",
47 "Tcp rate information has been updated",
48 MakeTraceSourceAccessor (&TcpRateLinux::m_rateTrace),
49 "ns3::TcpRateLinux::TcpRateUpdated")
50 .AddTraceSource ("TcpRateSampleUpdated",
51 "Tcp rate sample has been updated",
52 MakeTraceSourceAccessor (&TcpRateLinux::m_rateSampleTrace),
53 "ns3::TcpRateLinux::TcpRateSampleUpdated")
54 ;
55 return tid;
56 }
57
58 const TcpRateOps::TcpRateSample &
GenerateSample(uint32_t delivered,uint32_t lost,bool is_sack_reneg,uint32_t priorInFlight,const Time & minRtt)59 TcpRateLinux::GenerateSample (uint32_t delivered, uint32_t lost, bool is_sack_reneg,
60 uint32_t priorInFlight, const Time &minRtt)
61 {
62 NS_LOG_FUNCTION (this << delivered << lost << is_sack_reneg);
63
64 /* Clear app limited if bubble is acked and gone. */
65 if (m_rate.m_appLimited != 0 && m_rate.m_delivered > m_rate.m_appLimited)
66 {
67 NS_LOG_INFO ("Updating Rate m_appLimited to zero");
68 m_rate.m_appLimited = 0;
69 }
70
71 NS_LOG_INFO ("Updating RateSample m_ackedSacked=" << delivered <<
72 ", m_bytesLoss=" << lost << " and m_priorInFlight" << priorInFlight);
73 m_rateSample.m_ackedSacked = delivered; /* freshly ACKed or SACKed */
74 m_rateSample.m_bytesLoss = lost; /* freshly marked lost */
75 m_rateSample.m_priorInFlight = priorInFlight;
76
77 /* Return an invalid sample if no timing information is available or
78 * in recovery from loss with SACK reneging. Rate samples taken during
79 * a SACK reneging event may overestimate bw by including packets that
80 * were SACKed before the reneg.
81 */
82 if (m_rateSample.m_priorTime == Seconds (0) || is_sack_reneg)
83 {
84 NS_LOG_INFO ("PriorTime is zero, invalidating sample");
85 m_rateSample.m_delivered = -1;
86 m_rateSample.m_interval = Seconds (0);
87 m_rateSampleTrace (m_rateSample);
88 return m_rateSample;
89 }
90
91 // LINUX:
92 // /* Model sending data and receiving ACKs as separate pipeline phases
93 // * for a window. Usually the ACK phase is longer, but with ACK
94 // * compression the send phase can be longer. To be safe we use the
95 // * longer phase.
96 // */
97 // auto snd_us = m_rateSample.m_interval; /* send phase */
98 // auto ack_us = Simulator::Now () - m_rateSample.m_prior_mstamp;
99 // m_rateSample.m_interval = std::max (snd_us, ack_us);
100
101 m_rateSample.m_interval = std::max (m_rateSample.m_sendElapsed, m_rateSample.m_ackElapsed);
102 m_rateSample.m_delivered = m_rate.m_delivered - m_rateSample.m_priorDelivered;
103 NS_LOG_INFO ("Updating sample interval=" << m_rateSample.m_interval << " and delivered data" << m_rateSample.m_delivered);
104
105 /* Normally we expect m_interval >= minRtt.
106 * Note that rate may still be over-estimated when a spuriously
107 * retransmistted skb was first (s)acked because "interval_us"
108 * is under-estimated (up to an RTT). However continuously
109 * measuring the delivery rate during loss recovery is crucial
110 * for connections suffer heavy or prolonged losses.
111 */
112 if (m_rateSample.m_interval < minRtt)
113 {
114 NS_LOG_INFO ("Sampling interval is invalid");
115 m_rateSample.m_interval = Seconds (0);
116 m_rateSample.m_priorTime = Seconds (0); // To make rate sample invalid
117 m_rateSampleTrace (m_rateSample);
118 return m_rateSample;
119 }
120
121 /* Record the last non-app-limited or the highest app-limited bw */
122 if (!m_rateSample.m_isAppLimited
123 || (m_rateSample.m_delivered * m_rate.m_rateInterval >=
124 m_rate.m_rateDelivered * m_rateSample.m_interval))
125 {
126 m_rate.m_rateDelivered = m_rateSample.m_delivered;
127 m_rate.m_rateInterval = m_rateSample.m_interval;
128 m_rate.m_rateAppLimited = m_rateSample.m_isAppLimited;
129 m_rateSample.m_deliveryRate = DataRate (m_rateSample.m_delivered * 8.0 / m_rateSample.m_interval.GetSeconds ());
130 NS_LOG_INFO ("Updating delivery rate=" << m_rateSample.m_deliveryRate);
131 }
132
133 m_rateSampleTrace (m_rateSample);
134 return m_rateSample;
135 }
136
137 void
CalculateAppLimited(uint32_t cWnd,uint32_t in_flight,uint32_t segmentSize,const SequenceNumber32 & tailSeq,const SequenceNumber32 & nextTx,const uint32_t lostOut,const uint32_t retransOut)138 TcpRateLinux::CalculateAppLimited (uint32_t cWnd, uint32_t in_flight,
139 uint32_t segmentSize, const SequenceNumber32 &tailSeq,
140 const SequenceNumber32 &nextTx, const uint32_t lostOut,
141 const uint32_t retransOut)
142 {
143 NS_LOG_FUNCTION (this);
144
145 /* Missing checks from Linux:
146 * - Nothing in sending host's qdisc queues or NIC tx queue. NOT IMPLEMENTED
147 */
148 if (tailSeq - nextTx < static_cast<int32_t> (segmentSize) // We have less than one packet to send.
149 && in_flight < cWnd // We are not limited by CWND.
150 && lostOut <= retransOut) // All lost packets have been retransmitted.
151 {
152 m_rate.m_appLimited = std::max<uint32_t> (m_rate.m_delivered + in_flight, 1);
153 m_rateTrace (m_rate);
154 }
155
156 // m_appLimited will be reset once in GenerateSample, if it has to be.
157 // else
158 // {
159 // m_rate.m_appLimited = 0;
160 // }
161 }
162
163 void
SkbDelivered(TcpTxItem * skb)164 TcpRateLinux::SkbDelivered (TcpTxItem * skb)
165 {
166 NS_LOG_FUNCTION (this << skb);
167
168 TcpTxItem::RateInformation & skbInfo = skb->GetRateInformation ();
169
170 if (skbInfo.m_deliveredTime == Time::Max ())
171 {
172 return;
173 }
174
175 m_rate.m_delivered += skb->GetSeqSize ();
176 m_rate.m_deliveredTime = Simulator::Now ();
177
178 if (m_rateSample.m_priorDelivered == 0
179 || skbInfo.m_delivered > m_rateSample.m_priorDelivered)
180 {
181 m_rateSample.m_ackElapsed = Simulator::Now () - m_rateSample.m_priorTime;
182 m_rateSample.m_priorDelivered = skbInfo.m_delivered;
183 m_rateSample.m_priorTime = skbInfo.m_deliveredTime;
184 m_rateSample.m_isAppLimited = skbInfo.m_isAppLimited;
185 m_rateSample.m_sendElapsed = skb->GetLastSent () - skbInfo.m_firstSent;
186
187 m_rateSampleTrace (m_rateSample);
188
189 m_rate.m_firstSentTime = skb->GetLastSent ();
190 }
191
192 /* Mark off the skb delivered once it's taken into account to avoid being
193 * used again when it's cumulatively acked, in case it was SACKed.
194 */
195 skbInfo.m_deliveredTime = Time::Max ();
196 m_rate.m_txItemDelivered = skbInfo.m_delivered;
197 m_rateTrace (m_rate);
198 }
199
200 void
SkbSent(TcpTxItem * skb,bool isStartOfTransmission)201 TcpRateLinux::SkbSent (TcpTxItem *skb, bool isStartOfTransmission)
202 {
203 NS_LOG_FUNCTION (this << skb << isStartOfTransmission);
204
205 TcpTxItem::RateInformation & skbInfo = skb->GetRateInformation ();
206
207 /* In general we need to start delivery rate samples from the
208 * time we received the most recent ACK, to ensure we include
209 * the full time the network needs to deliver all in-flight
210 * packets. If there are no packets in flight yet, then we
211 * know that any ACKs after now indicate that the network was
212 * able to deliver those packets completely in the sampling
213 * interval between now and the next ACK.
214 *
215 * Note that we use the entire window size instead of bytes_in_flight
216 * because the latter is a guess based on RTO and loss-marking
217 * heuristics. We don't want spurious RTOs or loss markings to cause
218 * a spuriously small time interval, causing a spuriously high
219 * bandwidth estimate.
220 */
221 if (isStartOfTransmission)
222 {
223 NS_LOG_INFO ("Starting of a transmission at time " << Simulator::Now ().GetSeconds ());
224 m_rate.m_firstSentTime = Simulator::Now ();
225 m_rate.m_deliveredTime = Simulator::Now ();
226 m_rateTrace (m_rate);
227 }
228
229 skbInfo.m_firstSent = m_rate.m_firstSentTime;
230 skbInfo.m_deliveredTime = m_rate.m_deliveredTime;
231 skbInfo.m_isAppLimited = (m_rate.m_appLimited != 0);
232 skbInfo.m_delivered = m_rate.m_delivered;
233 }
234
235 std::ostream &
operator <<(std::ostream & os,TcpRateLinux::TcpRateConnection const & rate)236 operator<< (std::ostream & os, TcpRateLinux::TcpRateConnection const & rate)
237 {
238 os << "m_delivered = " << rate.m_delivered << std::endl;
239 os << "m_deliveredTime = " << rate.m_deliveredTime << std::endl;
240 os << "m_firstSentTime = " << rate.m_firstSentTime << std::endl;
241 os << "m_appLimited = " << rate.m_appLimited << std::endl;
242 os << "m_rateDelivered = " << rate.m_rateDelivered << std::endl;
243 os << "m_rateInterval = " << rate.m_rateInterval << std::endl;
244 os << "m_rateAppLimited = " << rate.m_rateAppLimited << std::endl;
245 os << "m_txItemDelivered = " << rate.m_txItemDelivered << std::endl;
246 return os;
247 }
248
249 std::ostream &
operator <<(std::ostream & os,TcpRateLinux::TcpRateSample const & sample)250 operator<< (std::ostream & os, TcpRateLinux::TcpRateSample const & sample)
251 {
252 os << "m_deliveryRate = " << sample.m_deliveryRate << std::endl;
253 os << " m_isAppLimited = " << sample.m_isAppLimited << std::endl;
254 os << " m_interval = " << sample.m_interval << std::endl;
255 os << " m_delivered = " << sample.m_delivered << std::endl;
256 os << " m_priorDelivered = " << sample.m_priorDelivered << std::endl;
257 os << " m_priorTime = " << sample.m_priorTime << std::endl;
258 os << " m_sendElapsed = " << sample.m_sendElapsed << std::endl;
259 os << " m_ackElapsed = " << sample.m_ackElapsed << std::endl;
260 os << " m_bytesLoss = " << sample.m_bytesLoss << std::endl;
261 os << " m_priorInFlight= " << sample.m_priorInFlight << std::endl;
262 os << " m_ackedSacked = " << sample.m_ackedSacked << std::endl;
263 return os;
264 }
265
266 bool
operator ==(TcpRateLinux::TcpRateSample const & lhs,TcpRateLinux::TcpRateSample const & rhs)267 operator== (TcpRateLinux::TcpRateSample const & lhs, TcpRateLinux::TcpRateSample const & rhs)
268 {
269 return (lhs.m_deliveryRate == rhs.m_deliveryRate
270 && lhs.m_isAppLimited == rhs.m_isAppLimited
271 && lhs.m_interval == rhs.m_interval
272 && lhs.m_delivered == rhs.m_delivered
273 && lhs.m_priorDelivered == rhs.m_priorDelivered
274 && lhs.m_priorTime == rhs.m_priorTime
275 && lhs.m_sendElapsed == rhs.m_sendElapsed
276 && lhs.m_ackElapsed == rhs.m_ackElapsed
277 );
278 }
279
280 bool
operator ==(TcpRateLinux::TcpRateConnection const & lhs,TcpRateLinux::TcpRateConnection const & rhs)281 operator== (TcpRateLinux::TcpRateConnection const & lhs,
282 TcpRateLinux::TcpRateConnection const & rhs)
283 {
284 return (lhs.m_delivered == rhs.m_delivered
285 && lhs.m_deliveredTime == rhs.m_deliveredTime
286 && lhs.m_firstSentTime == rhs.m_firstSentTime
287 && lhs.m_appLimited == rhs.m_appLimited
288 );
289 }
290
291
292 } // namespace ns3
293