A Discrete-Event Network Simulator
API
cobalt-queue-disc.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2019 NITK Surathkal
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * Cobalt, the CoDel - BLUE - Alternate Queueing discipline
18  * Based on linux code.
19  *
20  * Ported to ns-3 by: Vignesh Kannan <vignesh2496@gmail.com>
21  * Harsh Lara <harshapplefan@gmail.com>
22  * Jendaipou Palmei <jendaipoupalmei@gmail.com>
23  * Shefali Gupta <shefaligups11@gmail.com>
24  * Mohit P. Tahiliani <tahiliani@nitk.edu.in>
25  */
26 
27 #include "cobalt-queue-disc.h"
28 
29 #include "ns3/abort.h"
30 #include "ns3/drop-tail-queue.h"
31 #include "ns3/enum.h"
32 #include "ns3/log.h"
33 #include "ns3/net-device-queue-interface.h"
34 #include "ns3/object-factory.h"
35 #include "ns3/uinteger.h"
36 
37 #include <climits>
38 
39 namespace ns3
40 {
41 
42 NS_LOG_COMPONENT_DEFINE("CobaltQueueDisc");
43 
44 NS_OBJECT_ENSURE_REGISTERED(CobaltQueueDisc);
45 
46 TypeId
48 {
49  static TypeId tid =
50  TypeId("ns3::CobaltQueueDisc")
52  .SetGroupName("TrafficControl")
53  .AddConstructor<CobaltQueueDisc>()
54  .AddAttribute(
55  "MaxSize",
56  "The maximum number of packets/bytes accepted by this queue disc.",
57  QueueSizeValue(QueueSize(QueueSizeUnit::BYTES, 1500 * DEFAULT_COBALT_LIMIT)),
58  MakeQueueSizeAccessor(&QueueDisc::SetMaxSize, &QueueDisc::GetMaxSize),
59  MakeQueueSizeChecker())
60  .AddAttribute("Interval",
61  "The Cobalt algorithm interval",
62  StringValue("100ms"),
65  .AddAttribute("Target",
66  "The Cobalt algorithm target queue delay",
67  StringValue("5ms"),
70  .AddAttribute("UseEcn",
71  "True to use ECN (packets are marked instead of being dropped)",
72  BooleanValue(false),
75  .AddAttribute("Pdrop",
76  "Marking Probability",
77  DoubleValue(0),
79  MakeDoubleChecker<double>())
80  .AddAttribute("Increment",
81  "Pdrop increment value",
82  DoubleValue(1. / 256),
84  MakeDoubleChecker<double>())
85  .AddAttribute("Decrement",
86  "Pdrop decrement Value",
87  DoubleValue(1. / 4096),
89  MakeDoubleChecker<double>())
90  .AddAttribute("CeThreshold",
91  "The CoDel CE threshold for marking packets",
95  .AddAttribute("UseL4s",
96  "True to use L4S (only ECT1 packets are marked at CE threshold)",
97  BooleanValue(false),
100  .AddAttribute("BlueThreshold",
101  "The Threshold after which Blue is enabled",
102  TimeValue(MilliSeconds(400)),
104  MakeTimeChecker())
105  .AddTraceSource("Count",
106  "Cobalt count",
108  "ns3::TracedValueCallback::Uint32")
109  .AddTraceSource("DropState",
110  "Dropping state",
112  "ns3::TracedValueCallback::Bool")
113  .AddTraceSource("DropNext",
114  "Time until next packet drop",
116  "ns3::TracedValueCallback::Uint32");
117 
118  return tid;
119 }
120 
128 /* borrowed from the linux kernel */
129 static inline uint32_t
130 ReciprocalDivide(uint32_t A, uint32_t R)
131 {
132  return (uint32_t)(((uint64_t)A * R) >> 32);
133 }
134 
139 static int64_t
141 {
142  Time time = Simulator::Now();
143  int64_t ns = time.GetNanoSeconds();
144 
145  return ns;
146 }
147 
149  : QueueDisc()
150 {
151  NS_LOG_FUNCTION(this);
153  m_uv = CreateObject<UniformRandomVariable>();
154 }
155 
156 double
158 {
159  return m_pDrop;
160 }
161 
163 {
164  NS_LOG_FUNCTION(this);
165 }
166 
167 int64_t
169 {
170  NS_LOG_FUNCTION(this << stream);
171  m_uv->SetStream(stream);
172  return 1;
173 }
174 
175 void
177 {
178  // Cobalt parameters
179  NS_LOG_FUNCTION(this);
180  m_recInvSqrtCache[0] = ~0;
181  CacheInit();
182  m_count = 0;
183  m_dropping = false;
184  m_recInvSqrt = ~0U;
186  m_dropNext = 0;
187 }
188 
189 bool
190 CobaltQueueDisc::CoDelTimeAfter(int64_t a, int64_t b)
191 {
192  return ((int64_t)(a) - (int64_t)(b) > 0);
193 }
194 
195 bool
197 {
198  return ((int64_t)(a) - (int64_t)(b) >= 0);
199 }
200 
201 int64_t
203 {
204  return (t.GetNanoSeconds());
205 }
206 
207 Time
209 {
210  return m_target;
211 }
212 
213 Time
215 {
216  return m_interval;
217 }
218 
219 int64_t
221 {
222  return m_dropNext;
223 }
224 
225 void
227 {
228  NS_LOG_FUNCTION(this);
229  uint32_t invsqrt = ((uint32_t)m_recInvSqrt);
230  uint32_t invsqrt2 = ((uint64_t)invsqrt * invsqrt) >> 32;
231  uint64_t val = (3LL << 32) - ((uint64_t)m_count * invsqrt2);
232 
233  val >>= 2; /* avoid overflow */
234  val = (val * invsqrt) >> (32 - 2 + 1);
235  m_recInvSqrt = val;
236 }
237 
238 void
240 {
241  m_recInvSqrt = ~0U;
243 
244  for (m_count = 1; m_count < (uint32_t)(REC_INV_SQRT_CACHE); m_count++)
245  {
246  NewtonStep();
247  NewtonStep();
248  NewtonStep();
249  NewtonStep();
251  }
252 }
253 
254 void
256 {
257  if (m_count < (uint32_t)REC_INV_SQRT_CACHE)
258  {
260  }
261  else
262  {
263  NewtonStep();
264  }
265 }
266 
267 int64_t
269 {
270  NS_LOG_FUNCTION(this);
272 }
273 
274 void
276 {
277  NS_LOG_FUNCTION(this);
278  m_uv = nullptr;
280 }
281 
284 {
285  NS_LOG_FUNCTION(this);
286  if (GetInternalQueue(0)->IsEmpty())
287  {
288  NS_LOG_LOGIC("Queue empty");
289  return nullptr;
290  }
291 
292  Ptr<const QueueDiscItem> item = GetInternalQueue(0)->Peek();
293 
294  NS_LOG_LOGIC("Number packets " << GetInternalQueue(0)->GetNPackets());
295  NS_LOG_LOGIC("Number bytes " << GetInternalQueue(0)->GetNBytes());
296 
297  return item;
298 }
299 
300 bool
302 {
303  NS_LOG_FUNCTION(this);
304  if (GetNQueueDiscClasses() > 0)
305  {
306  NS_LOG_ERROR("CobaltQueueDisc cannot have classes");
307  return false;
308  }
309 
310  if (GetNPacketFilters() > 0)
311  {
312  NS_LOG_ERROR("CobaltQueueDisc cannot have packet filters");
313  return false;
314  }
315 
316  if (GetNInternalQueues() == 0)
317  {
320  QueueSizeValue(GetMaxSize())));
321  }
322 
323  if (GetNInternalQueues() != 1)
324  {
325  NS_LOG_ERROR("CobaltQueueDisc needs 1 internal queue");
326  return false;
327  }
328  return true;
329 }
330 
331 bool
333 {
334  NS_LOG_FUNCTION(this << item);
335  Ptr<Packet> p = item->GetPacket();
336  if (GetCurrentSize() + item > GetMaxSize())
337  {
338  NS_LOG_LOGIC("Queue full -- dropping pkt");
339  int64_t now = CoDelGetTime();
340  // Call this to update Blue's drop probability
341  CobaltQueueFull(now);
343  return false;
344  }
345 
346  bool retval = GetInternalQueue(0)->Enqueue(item);
347 
348  // If Queue::Enqueue fails, QueueDisc::Drop is called by the internal queue
349  // because QueueDisc::AddInternalQueue sets the drop callback
350 
351  NS_LOG_LOGIC("Number packets " << GetInternalQueue(0)->GetNPackets());
352  NS_LOG_LOGIC("Number bytes " << GetInternalQueue(0)->GetNBytes());
353 
354  return retval;
355 }
356 
359 {
360  NS_LOG_FUNCTION(this);
361 
362  while (true)
363  {
364  Ptr<QueueDiscItem> item = GetInternalQueue(0)->Dequeue();
365  if (!item)
366  {
367  // Leave dropping state when queue is empty (derived from Codel)
368  m_dropping = false;
369  NS_LOG_LOGIC("Queue empty");
370  int64_t now = CoDelGetTime();
371  // Call this to update Blue's drop probability
372  CobaltQueueEmpty(now);
373  return nullptr;
374  }
375 
376  int64_t now = CoDelGetTime();
377 
378  NS_LOG_LOGIC("Popped " << item);
379  NS_LOG_LOGIC("Number packets remaining " << GetInternalQueue(0)->GetNPackets());
380  NS_LOG_LOGIC("Number bytes remaining " << GetInternalQueue(0)->GetNBytes());
381 
382  // Determine if item should be dropped
383  // ECN marking happens inside this function, so it need not be done here
384  bool drop = CobaltShouldDrop(item, now);
385 
386  if (drop)
387  {
389  }
390  else
391  {
392  return item;
393  }
394  }
395 }
396 
397 // Call this when a packet had to be dropped due to queue overflow.
398 void
400 {
401  NS_LOG_FUNCTION(this);
402  NS_LOG_LOGIC("Outside IF block");
404  {
405  NS_LOG_LOGIC("inside IF block");
407  m_lastUpdateTimeBlue = now;
408  }
409  m_dropping = true;
410  m_dropNext = now;
411  if (!m_count)
412  {
413  m_count = 1;
414  }
415 }
416 
417 // Call this when the queue was serviced but turned out to be empty.
418 void
420 {
421  NS_LOG_FUNCTION(this);
423  {
425  m_lastUpdateTimeBlue = now;
426  }
427  m_dropping = false;
428 
429  if (m_count && CoDelTimeAfterEq((now - m_dropNext), 0))
430  {
431  m_count--;
432  InvSqrt();
434  }
435 }
436 
437 // Determines if Cobalt should drop the packet
438 bool
440 {
441  NS_LOG_FUNCTION(this);
442  bool drop = false;
443 
444  /* Simplified Codel implementation */
445  Time delta = Simulator::Now() - item->GetTimeStamp();
446  NS_LOG_INFO("Sojourn time " << delta.As(Time::S));
447  int64_t sojournTime = Time2CoDel(delta);
448  int64_t schedule = now - m_dropNext;
449  bool over_target = CoDelTimeAfter(sojournTime, Time2CoDel(m_target));
450  bool next_due = m_count && schedule >= 0;
451  bool isMarked = false;
452 
453  // If L4S mode is enabled then check if the packet is ECT1 or CE and
454  // if sojourn time is greater than CE threshold then the packet is marked.
455  // If packet is marked successfully then the CoDel steps can be skipped.
456  if (item && m_useL4s)
457  {
458  uint8_t tosByte = 0;
459  if (item->GetUint8Value(QueueItem::IP_DSFIELD, tosByte) &&
460  (((tosByte & 0x3) == 1) || (tosByte & 0x3) == 3))
461  {
462  if ((tosByte & 0x3) == 1)
463  {
464  NS_LOG_DEBUG("ECT1 packet " << static_cast<uint16_t>(tosByte & 0x3));
465  }
466  else
467  {
468  NS_LOG_DEBUG("CE packet " << static_cast<uint16_t>(tosByte & 0x3));
469  }
470  if (CoDelTimeAfter(sojournTime, Time2CoDel(m_ceThreshold)) &&
472  {
473  NS_LOG_LOGIC("Marking due to CeThreshold " << m_ceThreshold.GetSeconds());
474  }
475  return false;
476  }
477  }
478 
479  if (over_target)
480  {
481  if (!m_dropping)
482  {
483  m_dropping = true;
484  m_dropNext = ControlLaw(now);
485  }
486  if (!m_count)
487  {
488  m_count = 1;
489  }
490  }
491  else if (m_dropping)
492  {
493  m_dropping = false;
494  }
495 
496  if (next_due && m_dropping)
497  {
498  /* Check for marking possibility only if BLUE decides NOT to drop. */
499  /* Check if router and packet, both have ECN enabled. Only if this is true, mark the packet.
500  */
501  isMarked = (m_useEcn && Mark(item, FORCED_MARK));
502  drop = !isMarked;
503 
505 
506  InvSqrt();
508  schedule = now - m_dropNext;
509  }
510  else
511  {
512  while (next_due)
513  {
514  m_count--;
515  InvSqrt();
517  schedule = now - m_dropNext;
518  next_due = m_count && schedule >= 0;
519  }
520  }
521 
522  // If CE threshold is enabled then isMarked flag is used to determine whether
523  // packet is marked and if the packet is marked then a second attempt at marking should be
524  // suppressed. If UseL4S attribute is enabled then ECT0 packets should not be marked.
525  if (!isMarked && !m_useL4s && m_useEcn &&
526  CoDelTimeAfter(sojournTime, Time2CoDel(m_ceThreshold)) &&
528  {
529  NS_LOG_LOGIC("Marking due to CeThreshold " << m_ceThreshold.GetSeconds());
530  }
531 
532  // Enable Blue Enhancement if sojourn time is greater than blueThreshold and its been m_target
533  // time until the last time blue was updated
534  if (CoDelTimeAfter(sojournTime, Time2CoDel(m_blueThreshold)) &&
536  {
538  m_lastUpdateTimeBlue = now;
539  }
540 
541  /* Simple BLUE implementation. Lack of ECN is deliberate. */
542  if (m_pDrop)
543  {
544  double u = m_uv->GetValue();
545  drop = drop || (u < m_pDrop);
546  }
547 
548  /* Overload the drop_next field as an activity timeout */
549  if (!m_count)
550  {
552  }
553  else if (schedule > 0 && !drop)
554  {
555  m_dropNext = now;
556  }
557 
558  return drop;
559 }
560 
561 } // namespace ns3
#define min(a, b)
Definition: 80211b.c:41
#define max(a, b)
Definition: 80211b.c:42
Cobalt packet queue disc.
Time m_ceThreshold
Threshold above which to CE mark.
uint32_t m_recInvSqrtCache[REC_INV_SQRT_CACHE]
Cache to maintain some initial values of InvSqrt.
Time m_target
target queue delay
static constexpr const char * CE_THRESHOLD_EXCEEDED_MARK
Sojourn time above CE threshold.
bool CheckConfig() override
Check whether the current configuration is correct.
void NewtonStep()
Calculate the reciprocal square root of m_count by using Newton's method http://en....
Time GetTarget() const
Get the target queue delay.
int64_t GetDropNext() const
Get the time for next packet drop while in the dropping state.
void InitializeParams() override
Initialize the queue parameters.
bool CoDelTimeAfterEq(int64_t a, int64_t b)
Check if CoDel time a is successive or equal to b.
void InvSqrt()
Updates the inverse square root.
double GetPdrop() const
Get the drop probability of Blue.
int64_t AssignStreams(int64_t stream)
Assign a fixed random variable stream number to the random variables used by this model.
void DoDispose() override
Dispose of the object.
TracedValue< uint32_t > m_count
Number of packets dropped since entering drop state.
double m_increment
increment value for marking probability
void CobaltQueueFull(int64_t now)
Called when the queue becomes full to alter the drop probabilities of Blue.
Time m_interval
sliding minimum time window width
double m_decrement
decrement value for marking probability
bool CobaltShouldDrop(Ptr< QueueDiscItem > item, int64_t now)
Called to decide whether the current packet should be dropped based on decisions taken by Blue and Co...
static TypeId GetTypeId()
Get the type ID.
Ptr< QueueDiscItem > DoDequeue() override
This function actually extracts a packet from the queue disc.
void CacheInit()
There is a big difference in timing between the accurate values placed in the cache and the approxima...
~CobaltQueueDisc() override
Destructor.
static constexpr const char * TARGET_EXCEEDED_DROP
Sojourn time above target.
int64_t Time2CoDel(Time t) const
Return the unsigned 32-bit integer representation of the input Time object.
bool CoDelTimeAfter(int64_t a, int64_t b)
Check if CoDel time a is successive to b.
Time GetInterval() const
Get the interval.
double m_pDrop
Drop Probability.
CobaltQueueDisc()
CobaltQueueDisc Constructor.
int64_t ControlLaw(int64_t t)
Determine the time for next drop CoDel control law is t + m_interval/sqrt(m_count).
static constexpr const char * FORCED_MARK
forced marks by Codel on ECN-enabled
Time m_blueThreshold
Threshold to enable blue enhancement.
uint32_t m_recInvSqrt
Reciprocal inverse square root.
Ptr< UniformRandomVariable > m_uv
Rng stream.
uint32_t m_lastUpdateTimeBlue
Blue's last update time for drop probability.
static constexpr const char * OVERLIMIT_DROP
Overlimit dropped packet.
TracedValue< bool > m_dropping
True if in dropping state.
Ptr< const QueueDiscItem > DoPeek() override
Return a copy of the next packet the queue disc will extract.
bool m_useEcn
True if ECN is used (packets are marked instead of being dropped)
bool DoEnqueue(Ptr< QueueDiscItem > item) override
This function actually enqueues a packet into the queue disc.
TracedValue< int64_t > m_dropNext
Time to drop next packet.
void CobaltQueueEmpty(int64_t now)
Called when the queue becomes empty to alter the drop probabilities of Blue.
bool m_useL4s
True if L4S is used (ECT1 packets are marked at CE threshold)
This class can be used to hold variables of floating point type such as 'double' or 'float'.
Definition: double.h:42
A FIFO packet queue that drops tail-end packets on overflow.
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:77
QueueDisc is an abstract base class providing the interface and implementing the operations common to...
Definition: queue-disc.h:184
void AddInternalQueue(Ptr< InternalQueue > queue)
Add an internal queue to the tail of the list of queues.
Definition: queue-disc.cc:573
uint32_t GetNPackets() const
Get the number of packets stored by the queue disc.
Definition: queue-disc.cc:432
uint32_t GetNBytes() const
Get the amount of bytes stored by the queue disc.
Definition: queue-disc.cc:439
Ptr< InternalQueue > GetInternalQueue(std::size_t i) const
Get the i-th internal queue.
Definition: queue-disc.cc:591
QueueSize GetCurrentSize() const
Get the current size of the queue disc in bytes, if operating in bytes mode, or packets,...
Definition: queue-disc.cc:515
void DropAfterDequeue(Ptr< const QueueDiscItem > item, const char *reason)
Perform the actions required when the queue disc is notified of a packet dropped after dequeue.
Definition: queue-disc.cc:759
std::size_t GetNQueueDiscClasses() const
Get the number of queue disc classes.
Definition: queue-disc.cc:661
QueueSize GetMaxSize() const
Get the maximum size of the queue disc.
Definition: queue-disc.cc:446
std::size_t GetNPacketFilters() const
Get the number of packet filters.
Definition: queue-disc.cc:618
bool SetMaxSize(QueueSize size)
Set the maximum size of the queue disc.
Definition: queue-disc.cc:474
std::size_t GetNInternalQueues() const
Get the number of internal queues.
Definition: queue-disc.cc:598
void DoDispose() override
Dispose of the object.
Definition: queue-disc.cc:376
bool Mark(Ptr< QueueDiscItem > item, const char *reason)
Marks the given packet and, if successful, updates the counters associated with the given reason.
Definition: queue-disc.cc:809
void DropBeforeEnqueue(Ptr< const QueueDiscItem > item, const char *reason)
Perform the actions required when the queue disc is notified of a packet dropped before enqueue.
Definition: queue-disc.cc:720
Class for representing queue sizes.
Definition: queue-size.h:96
void SetStream(int64_t stream)
Specifies the stream number for the RngStream.
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:208
Hold variables of type string.
Definition: string.h:56
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
int64_t GetNanoSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:418
double GetSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:403
@ S
second
Definition: nstime.h:116
static Time Max()
Maximum representable Time Not to be confused with Max(Time,Time).
Definition: nstime.h:297
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:931
double GetValue(double min, double max)
Get the next random value drawn from the distribution.
#define DEFAULT_COBALT_LIMIT
#define REC_INV_SQRT_CACHE
#define NS_LOG_ERROR(msg)
Use NS_LOG to output a message of level LOG_ERROR.
Definition: log.h:254
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:282
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:275
Ptr< T > CreateObjectWithAttributes(Args... args)
Allocate an Object on the heap and initialize with a set of attributes.
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:46
@ BYTES
Use number of bytes for queue size.
Definition: queue-size.h:46
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1338
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition: boolean.cc:124
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition: nstime.h:1414
static int64_t CoDelGetTime()
Returns the current time translated in CoDel time representation.
Ptr< const AttributeChecker > MakeTimeChecker(const Time min, const Time max)
Helper to make a Time checker with bounded range.
Definition: time.cc:533
static uint32_t ReciprocalDivide(uint32_t A, uint32_t R)
Performs a reciprocal divide, similar to the Linux kernel reciprocal_divide function.
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition: boolean.h:86
Ptr< const AttributeAccessor > MakeDoubleAccessor(T1 a1)
Definition: double.h:43