1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2007, 2014 University of Washington
4  *               2015 Universita' degli Studi di Napoli Federico II
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation;
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 
20 #include "ns3/log.h"
21 #include "ns3/abort.h"
22 #include "ns3/uinteger.h"
23 #include "ns3/pointer.h"
24 #include "ns3/object-vector.h"
25 #include "ns3/packet.h"
26 #include "ns3/socket.h"
27 #include "ns3/unused.h"
28 #include "ns3/simulator.h"
29 #include "queue-disc.h"
30 #include "ns3/net-device-queue-interface.h"
31 #include "ns3/queue.h"
32 
33 namespace ns3 {
34 
35 NS_LOG_COMPONENT_DEFINE ("QueueDisc");
36 
37 
38 NS_OBJECT_ENSURE_REGISTERED (QueueDiscClass);
39 
GetTypeId(void)40 TypeId QueueDiscClass::GetTypeId (void)
41 {
42   static TypeId tid = TypeId ("ns3::QueueDiscClass")
43     .SetParent<Object> ()
44     .SetGroupName ("TrafficControl")
45     .AddConstructor<QueueDiscClass> ()
46     .AddAttribute ("QueueDisc", "The queue disc attached to the class",
47                    PointerValue (),
48                    MakePointerAccessor (&QueueDiscClass::m_queueDisc),
49                    MakePointerChecker<QueueDisc> ())
50   ;
51   return tid;
52 }
53 
QueueDiscClass()54 QueueDiscClass::QueueDiscClass ()
55 {
56   NS_LOG_FUNCTION (this);
57 }
58 
~QueueDiscClass()59 QueueDiscClass::~QueueDiscClass ()
60 {
61   NS_LOG_FUNCTION (this);
62 }
63 
64 void
DoDispose(void)65 QueueDiscClass::DoDispose (void)
66 {
67   NS_LOG_FUNCTION (this);
68   m_queueDisc = 0;
69   Object::DoDispose ();
70 }
71 
72 Ptr<QueueDisc>
GetQueueDisc(void) const73 QueueDiscClass::GetQueueDisc (void) const
74 {
75   NS_LOG_FUNCTION (this);
76   return m_queueDisc;
77 }
78 
79 void
SetQueueDisc(Ptr<QueueDisc> qd)80 QueueDiscClass::SetQueueDisc (Ptr<QueueDisc> qd)
81 {
82   NS_LOG_FUNCTION (this);
83   NS_ABORT_MSG_IF (m_queueDisc, "Cannot set the queue disc on a class already having an attached queue disc");
84   m_queueDisc = qd;
85 }
86 
Stats()87 QueueDisc::Stats::Stats ()
88   : nTotalReceivedPackets (0),
89     nTotalReceivedBytes (0),
90     nTotalSentPackets (0),
91     nTotalSentBytes (0),
92     nTotalEnqueuedPackets (0),
93     nTotalEnqueuedBytes (0),
94     nTotalDequeuedPackets (0),
95     nTotalDequeuedBytes (0),
96     nTotalDroppedPackets (0),
97     nTotalDroppedPacketsBeforeEnqueue (0),
98     nTotalDroppedPacketsAfterDequeue (0),
99     nTotalDroppedBytes (0),
100     nTotalDroppedBytesBeforeEnqueue (0),
101     nTotalDroppedBytesAfterDequeue (0),
102     nTotalRequeuedPackets (0),
103     nTotalRequeuedBytes (0),
104     nTotalMarkedPackets (0),
105     nTotalMarkedBytes (0)
106 {
107 }
108 
109 uint32_t
GetNDroppedPackets(std::string reason) const110 QueueDisc::Stats::GetNDroppedPackets (std::string reason) const
111 {
112   uint32_t count = 0;
113   auto it = nDroppedPacketsBeforeEnqueue.find (reason);
114 
115   if (it != nDroppedPacketsBeforeEnqueue.end ())
116     {
117       count += it->second;
118     }
119 
120   it = nDroppedPacketsAfterDequeue.find (reason);
121 
122   if (it != nDroppedPacketsAfterDequeue.end ())
123     {
124       count += it->second;
125     }
126 
127   return count;
128 }
129 
130 uint64_t
GetNDroppedBytes(std::string reason) const131 QueueDisc::Stats::GetNDroppedBytes (std::string reason) const
132 {
133   uint64_t count = 0;
134   auto it = nDroppedBytesBeforeEnqueue.find (reason);
135 
136   if (it != nDroppedBytesBeforeEnqueue.end ())
137     {
138       count += it->second;
139     }
140 
141   it = nDroppedBytesAfterDequeue.find (reason);
142 
143   if (it != nDroppedBytesAfterDequeue.end ())
144     {
145       count += it->second;
146     }
147 
148   return count;
149 }
150 
151 uint32_t
GetNMarkedPackets(std::string reason) const152 QueueDisc::Stats::GetNMarkedPackets (std::string reason) const
153 {
154   auto it = nMarkedPackets.find (reason);
155 
156   if (it != nMarkedPackets.end ())
157     {
158       return it->second;
159     }
160 
161   return 0;
162 }
163 
164 uint64_t
GetNMarkedBytes(std::string reason) const165 QueueDisc::Stats::GetNMarkedBytes (std::string reason) const
166 {
167   auto it = nMarkedBytes.find (reason);
168 
169   if (it != nMarkedBytes.end ())
170     {
171       return it->second;
172     }
173 
174   return 0;
175 }
176 
177 void
Print(std::ostream & os) const178 QueueDisc::Stats::Print (std::ostream &os) const
179 {
180   std::map<std::string, uint32_t>::const_iterator itp;
181   std::map<std::string, uint64_t>::const_iterator itb;
182 
183   os << std::endl << "Packets/Bytes received: "
184                   << nTotalReceivedPackets << " / "
185                   << nTotalReceivedBytes
186      << std::endl << "Packets/Bytes enqueued: "
187                   << nTotalEnqueuedPackets << " / "
188                   << nTotalEnqueuedBytes
189      << std::endl << "Packets/Bytes dequeued: "
190                   << nTotalDequeuedPackets << " / "
191                   << nTotalDequeuedBytes
192      << std::endl << "Packets/Bytes requeued: "
193                   << nTotalRequeuedPackets << " / "
194                   << nTotalRequeuedBytes
195      << std::endl << "Packets/Bytes dropped: "
196                   << nTotalDroppedPackets << " / "
197                   << nTotalDroppedBytes
198      << std::endl << "Packets/Bytes dropped before enqueue: "
199                   << nTotalDroppedPacketsBeforeEnqueue << " / "
200                   << nTotalDroppedBytesBeforeEnqueue;
201 
202   itp = nDroppedPacketsBeforeEnqueue.begin ();
203   itb = nDroppedBytesBeforeEnqueue.begin ();
204 
205   while (itp != nDroppedPacketsBeforeEnqueue.end () &&
206          itb != nDroppedBytesBeforeEnqueue.end ())
207     {
208       NS_ASSERT (itp->first.compare (itb->first) == 0);
209       os << std::endl << "  " << itp->first << ": "
210          << itp->second << " / " << itb->second;
211       itp++;
212       itb++;
213     }
214 
215   os << std::endl << "Packets/Bytes dropped after dequeue: "
216                   << nTotalDroppedPacketsAfterDequeue << " / "
217                   << nTotalDroppedBytesAfterDequeue;
218 
219   itp = nDroppedPacketsAfterDequeue.begin ();
220   itb = nDroppedBytesAfterDequeue.begin ();
221 
222   while (itp != nDroppedPacketsAfterDequeue.end () &&
223          itb != nDroppedBytesAfterDequeue.end ())
224     {
225       NS_ASSERT (itp->first.compare (itb->first) == 0);
226       os << std::endl << "  " << itp->first << ": "
227          << itp->second << " / " << itb->second;
228       itp++;
229       itb++;
230     }
231 
232   os << std::endl << "Packets/Bytes sent: "
233                   << nTotalSentPackets << " / "
234                   << nTotalSentBytes
235      << std::endl << "Packets/Bytes marked: "
236                   << nTotalMarkedPackets << " / "
237                   << nTotalMarkedBytes;
238 
239   itp = nMarkedPackets.begin ();
240   itb = nMarkedBytes.begin ();
241 
242   while (itp != nMarkedPackets.end () &&
243          itb != nMarkedBytes.end ())
244     {
245       NS_ASSERT (itp->first.compare (itb->first) == 0);
246       os << std::endl << "  " << itp->first << ": "
247          << itp->second << " / " << itb->second;
248       itp++;
249       itb++;
250     }
251 
252   os << std::endl;
253 }
254 
operator <<(std::ostream & os,const QueueDisc::Stats & stats)255 std::ostream & operator << (std::ostream &os, const QueueDisc::Stats &stats)
256 {
257   stats.Print (os);
258   return os;
259 }
260 
261 NS_OBJECT_ENSURE_REGISTERED (QueueDisc);
262 
GetTypeId(void)263 TypeId QueueDisc::GetTypeId (void)
264 {
265   static TypeId tid = TypeId ("ns3::QueueDisc")
266     .SetParent<Object> ()
267     .SetGroupName ("TrafficControl")
268     .AddAttribute ("Quota", "The maximum number of packets dequeued in a qdisc run",
269                    UintegerValue (DEFAULT_QUOTA),
270                    MakeUintegerAccessor (&QueueDisc::SetQuota,
271                                          &QueueDisc::GetQuota),
272                    MakeUintegerChecker<uint32_t> ())
273     .AddAttribute ("InternalQueueList", "The list of internal queues.",
274                    ObjectVectorValue (),
275                    MakeObjectVectorAccessor (&QueueDisc::m_queues),
276                    MakeObjectVectorChecker<InternalQueue> ())
277     .AddAttribute ("PacketFilterList", "The list of packet filters.",
278                    ObjectVectorValue (),
279                    MakeObjectVectorAccessor (&QueueDisc::m_filters),
280                    MakeObjectVectorChecker<PacketFilter> ())
281     .AddAttribute ("QueueDiscClassList", "The list of queue disc classes.",
282                    ObjectVectorValue (),
283                    MakeObjectVectorAccessor (&QueueDisc::m_classes),
284                    MakeObjectVectorChecker<QueueDiscClass> ())
285     .AddTraceSource ("Enqueue", "Enqueue a packet in the queue disc",
286                      MakeTraceSourceAccessor (&QueueDisc::m_traceEnqueue),
287                      "ns3::QueueDiscItem::TracedCallback")
288     .AddTraceSource ("Dequeue", "Dequeue a packet from the queue disc",
289                      MakeTraceSourceAccessor (&QueueDisc::m_traceDequeue),
290                      "ns3::QueueDiscItem::TracedCallback")
291     .AddTraceSource ("Requeue", "Requeue a packet in the queue disc",
292                      MakeTraceSourceAccessor (&QueueDisc::m_traceRequeue),
293                      "ns3::QueueDiscItem::TracedCallback")
294     .AddTraceSource ("Drop", "Drop a packet stored in the queue disc",
295                      MakeTraceSourceAccessor (&QueueDisc::m_traceDrop),
296                      "ns3::QueueDiscItem::TracedCallback")
297     .AddTraceSource ("DropBeforeEnqueue", "Drop a packet before enqueue",
298                      MakeTraceSourceAccessor (&QueueDisc::m_traceDropBeforeEnqueue),
299                      "ns3::QueueDiscItem::TracedCallback")
300     .AddTraceSource ("DropAfterDequeue", "Drop a packet after dequeue",
301                      MakeTraceSourceAccessor (&QueueDisc::m_traceDropAfterDequeue),
302                      "ns3::QueueDiscItem::TracedCallback")
303     .AddTraceSource ("Mark", "Mark a packet stored in the queue disc",
304                      MakeTraceSourceAccessor (&QueueDisc::m_traceMark),
305                      "ns3::QueueDiscItem::TracedCallback")
306     .AddTraceSource ("PacketsInQueue",
307                      "Number of packets currently stored in the queue disc",
308                      MakeTraceSourceAccessor (&QueueDisc::m_nPackets),
309                      "ns3::TracedValueCallback::Uint32")
310     .AddTraceSource ("BytesInQueue",
311                      "Number of bytes currently stored in the queue disc",
312                      MakeTraceSourceAccessor (&QueueDisc::m_nBytes),
313                      "ns3::TracedValueCallback::Uint32")
314     .AddTraceSource ("SojournTime",
315                      "Sojourn time of the last packet dequeued from the queue disc",
316                      MakeTraceSourceAccessor (&QueueDisc::m_sojourn),
317                      "ns3::Time::TracedCallback")
318   ;
319   return tid;
320 }
321 
QueueDisc(QueueDiscSizePolicy policy)322 QueueDisc::QueueDisc (QueueDiscSizePolicy policy)
323   :  m_nPackets (0),
324      m_nBytes (0),
325      m_maxSize (QueueSize ("1p")),         // to avoid that setting the mode at construction time is ignored
326      m_running (false),
327      m_peeked (false),
328      m_sizePolicy (policy),
329      m_prohibitChangeMode (false)
330 {
331   NS_LOG_FUNCTION (this << (uint16_t)policy);
332 
333   // These lambdas call the DropBeforeEnqueue or DropAfterDequeue methods of this
334   // QueueDisc object. Given that a callback to the operator() of these lambdas
335   // is connected to the DropBeforeEnqueue and DropAfterDequeue traces of the
336   // internal queues, the INTERNAL_QUEUE_DROP constant is passed as the reason
337   // why the packet is dropped.
338   m_internalQueueDbeFunctor = [this] (Ptr<const QueueDiscItem> item)
339     {
340       return DropBeforeEnqueue (item, INTERNAL_QUEUE_DROP);
341     };
342   m_internalQueueDadFunctor = [this] (Ptr<const QueueDiscItem> item)
343     {
344       return DropAfterDequeue (item, INTERNAL_QUEUE_DROP);
345     };
346 
347   // These lambdas call the DropBeforeEnqueue or DropAfterDequeue methods of this
348   // QueueDisc object. Given that a callback to the operator() of these lambdas
349   // is connected to the DropBeforeEnqueue and DropAfterDequeue traces of the
350   // child queue discs, the concatenation of the CHILD_QUEUE_DISC_DROP constant
351   // and the second argument provided by such traces is passed as the reason why
352   // the packet is dropped.
353   m_childQueueDiscDbeFunctor = [this] (Ptr<const QueueDiscItem> item, const char* r)
354     {
355       return DropBeforeEnqueue (item,
356                                 m_childQueueDiscDropMsg.assign (CHILD_QUEUE_DISC_DROP).append (r).data ());
357     };
358   m_childQueueDiscDadFunctor = [this] (Ptr<const QueueDiscItem> item, const char* r)
359     {
360       return DropAfterDequeue (item,
361                                m_childQueueDiscDropMsg.assign (CHILD_QUEUE_DISC_DROP).append (r).data ());
362     };
363   m_childQueueDiscMarkFunctor = [this] (Ptr<const QueueDiscItem> item, const char* r)
364     {
365       return Mark (const_cast<QueueDiscItem *> (PeekPointer (item)),
366                    m_childQueueDiscMarkMsg.assign (CHILD_QUEUE_DISC_MARK).append (r).data ());
367     };
368 }
369 
QueueDisc(QueueDiscSizePolicy policy,QueueSizeUnit unit)370 QueueDisc::QueueDisc (QueueDiscSizePolicy policy, QueueSizeUnit unit)
371   : QueueDisc (policy)
372 {
373   m_maxSize = QueueSize (unit, 0);
374   m_prohibitChangeMode = true;
375 }
376 
~QueueDisc()377 QueueDisc::~QueueDisc ()
378 {
379   NS_LOG_FUNCTION (this);
380 }
381 
382 void
DoDispose(void)383 QueueDisc::DoDispose (void)
384 {
385   NS_LOG_FUNCTION (this);
386   m_queues.clear ();
387   m_filters.clear ();
388   m_classes.clear ();
389   m_devQueueIface = 0;
390   m_send = nullptr;
391   m_requeued = 0;
392   m_internalQueueDbeFunctor = nullptr;
393   m_internalQueueDadFunctor = nullptr;
394   m_childQueueDiscDbeFunctor = nullptr;
395   m_childQueueDiscDadFunctor = nullptr;
396   Object::DoDispose ();
397 }
398 
399 void
DoInitialize(void)400 QueueDisc::DoInitialize (void)
401 {
402   NS_LOG_FUNCTION (this);
403 
404   // Check the configuration and initialize the parameters of this queue disc
405   bool ok = CheckConfig ();
406   NS_ASSERT_MSG (ok, "The queue disc configuration is not correct");
407   NS_UNUSED (ok); // suppress compiler warning
408   InitializeParams ();
409 
410   // Check the configuration and initialize the parameters of the child queue discs
411   for (std::vector<Ptr<QueueDiscClass> >::iterator cl = m_classes.begin ();
412        cl != m_classes.end (); cl++)
413     {
414       (*cl)->GetQueueDisc ()->Initialize ();
415     }
416 
417   Object::DoInitialize ();
418 }
419 
420 const QueueDisc::Stats&
GetStats(void)421 QueueDisc::GetStats (void)
422 {
423   NS_ASSERT (m_stats.nTotalDroppedPackets == m_stats.nTotalDroppedPacketsBeforeEnqueue
424              + m_stats.nTotalDroppedPacketsAfterDequeue);
425   NS_ASSERT (m_stats.nTotalDroppedBytes == m_stats.nTotalDroppedBytesBeforeEnqueue
426              + m_stats.nTotalDroppedBytesAfterDequeue);
427 
428   // the total number of sent packets is only updated here to avoid to increase it
429   // after a dequeue and then having to decrease it if the packet is dropped after
430   // dequeue or requeued
431   m_stats.nTotalSentPackets = m_stats.nTotalDequeuedPackets - (m_requeued ? 1 : 0)
432                               - m_stats.nTotalDroppedPacketsAfterDequeue;
433   m_stats.nTotalSentBytes = m_stats.nTotalDequeuedBytes - (m_requeued ? m_requeued->GetSize () : 0)
434                             - m_stats.nTotalDroppedBytesAfterDequeue;
435 
436   return m_stats;
437 }
438 
439 uint32_t
GetNPackets() const440 QueueDisc::GetNPackets () const
441 {
442   NS_LOG_FUNCTION (this);
443   return m_nPackets;
444 }
445 
446 uint32_t
GetNBytes(void) const447 QueueDisc::GetNBytes (void) const
448 {
449   NS_LOG_FUNCTION (this);
450   return m_nBytes;
451 }
452 
453 QueueSize
GetMaxSize(void) const454 QueueDisc::GetMaxSize (void) const
455 {
456   NS_LOG_FUNCTION (this);
457 
458   switch (m_sizePolicy)
459     {
460     case QueueDiscSizePolicy::NO_LIMITS:
461       NS_FATAL_ERROR ("The size of this queue disc is not limited");
462 
463     case QueueDiscSizePolicy::SINGLE_INTERNAL_QUEUE:
464       if (GetNInternalQueues ())
465         {
466           return GetInternalQueue (0)->GetMaxSize ();
467         }
468 
469     case QueueDiscSizePolicy::SINGLE_CHILD_QUEUE_DISC:
470       if (GetNQueueDiscClasses ())
471         {
472           return GetQueueDiscClass (0)->GetQueueDisc ()->GetMaxSize ();
473         }
474 
475     case QueueDiscSizePolicy::MULTIPLE_QUEUES:
476     default:
477       return m_maxSize;
478     }
479 }
480 
481 bool
SetMaxSize(QueueSize size)482 QueueDisc::SetMaxSize (QueueSize size)
483 {
484   NS_LOG_FUNCTION (this << size);
485 
486   // do nothing if the limit is null
487   if (!size.GetValue ())
488     {
489       return false;
490     }
491 
492   if (m_prohibitChangeMode && size.GetUnit () != m_maxSize.GetUnit ())
493     {
494       NS_LOG_DEBUG ("Changing the mode of this queue disc is prohibited");
495       return false;
496     }
497 
498   switch (m_sizePolicy)
499     {
500     case QueueDiscSizePolicy::NO_LIMITS:
501       NS_FATAL_ERROR ("The size of this queue disc is not limited");
502 
503     case QueueDiscSizePolicy::SINGLE_INTERNAL_QUEUE:
504       if (GetNInternalQueues ())
505         {
506           GetInternalQueue (0)->SetMaxSize (size);
507         }
508 
509     case QueueDiscSizePolicy::SINGLE_CHILD_QUEUE_DISC:
510       if (GetNQueueDiscClasses ())
511         {
512           GetQueueDiscClass (0)->GetQueueDisc ()->SetMaxSize (size);
513         }
514 
515     case QueueDiscSizePolicy::MULTIPLE_QUEUES:
516     default:
517       m_maxSize = size;
518     }
519   return true;
520 }
521 
522 QueueSize
GetCurrentSize(void)523 QueueDisc::GetCurrentSize (void)
524 {
525   NS_LOG_FUNCTION (this);
526 
527   if (GetMaxSize ().GetUnit () == QueueSizeUnit::PACKETS)
528     {
529       return QueueSize (QueueSizeUnit::PACKETS, m_nPackets);
530     }
531   if (GetMaxSize ().GetUnit () == QueueSizeUnit::BYTES)
532     {
533       return QueueSize (QueueSizeUnit::BYTES, m_nBytes);
534     }
535   NS_ABORT_MSG ("Unknown queue size unit");
536 }
537 
538 void
SetNetDeviceQueueInterface(Ptr<NetDeviceQueueInterface> ndqi)539 QueueDisc::SetNetDeviceQueueInterface (Ptr<NetDeviceQueueInterface> ndqi)
540 {
541   NS_LOG_FUNCTION (this << ndqi);
542   m_devQueueIface = ndqi;
543 }
544 
545 Ptr<NetDeviceQueueInterface>
GetNetDeviceQueueInterface(void) const546 QueueDisc::GetNetDeviceQueueInterface (void) const
547 {
548   NS_LOG_FUNCTION (this);
549   return m_devQueueIface;
550 }
551 
552 void
SetSendCallback(SendCallback func)553 QueueDisc::SetSendCallback (SendCallback func)
554 {
555   NS_LOG_FUNCTION (this);
556   m_send = func;
557 }
558 
559 QueueDisc::SendCallback
GetSendCallback(void) const560 QueueDisc::GetSendCallback (void) const
561 {
562   NS_LOG_FUNCTION (this);
563   return m_send;
564 }
565 
566 void
SetQuota(const uint32_t quota)567 QueueDisc::SetQuota (const uint32_t quota)
568 {
569   NS_LOG_FUNCTION (this << quota);
570   m_quota = quota;
571 }
572 
573 uint32_t
GetQuota(void) const574 QueueDisc::GetQuota (void) const
575 {
576   NS_LOG_FUNCTION (this);
577   return m_quota;
578 }
579 
580 void
AddInternalQueue(Ptr<InternalQueue> queue)581 QueueDisc::AddInternalQueue (Ptr<InternalQueue> queue)
582 {
583   NS_LOG_FUNCTION (this);
584 
585   // set various callbacks on the internal queue, so that the queue disc is
586   // notified of packets enqueued, dequeued or dropped by the internal queue
587   queue->TraceConnectWithoutContext ("Enqueue",
588                                      MakeCallback (&QueueDisc::PacketEnqueued, this));
589   queue->TraceConnectWithoutContext ("Dequeue",
590                                      MakeCallback (&QueueDisc::PacketDequeued, this));
591   queue->TraceConnectWithoutContext ("DropBeforeEnqueue",
592                                      MakeCallback (&InternalQueueDropFunctor::operator(),
593                                                    &m_internalQueueDbeFunctor));
594   queue->TraceConnectWithoutContext ("DropAfterDequeue",
595                                      MakeCallback (&InternalQueueDropFunctor::operator(),
596                                                    &m_internalQueueDadFunctor));
597   m_queues.push_back (queue);
598 }
599 
600 Ptr<QueueDisc::InternalQueue>
GetInternalQueue(std::size_t i) const601 QueueDisc::GetInternalQueue (std::size_t i) const
602 {
603   NS_ASSERT (i < m_queues.size ());
604   return m_queues[i];
605 }
606 
607 std::size_t
GetNInternalQueues(void) const608 QueueDisc::GetNInternalQueues (void) const
609 {
610   return m_queues.size ();
611 }
612 
613 void
AddPacketFilter(Ptr<PacketFilter> filter)614 QueueDisc::AddPacketFilter (Ptr<PacketFilter> filter)
615 {
616   NS_LOG_FUNCTION (this);
617   m_filters.push_back (filter);
618 }
619 
620 Ptr<PacketFilter>
GetPacketFilter(std::size_t i) const621 QueueDisc::GetPacketFilter (std::size_t i) const
622 {
623   NS_ASSERT (i < m_filters.size ());
624   return m_filters[i];
625 }
626 
627 std::size_t
GetNPacketFilters(void) const628 QueueDisc::GetNPacketFilters (void) const
629 {
630   return m_filters.size ();
631 }
632 
633 void
AddQueueDiscClass(Ptr<QueueDiscClass> qdClass)634 QueueDisc::AddQueueDiscClass (Ptr<QueueDiscClass> qdClass)
635 {
636   NS_LOG_FUNCTION (this);
637   NS_ABORT_MSG_IF (qdClass->GetQueueDisc () == 0, "Cannot add a class with no attached queue disc");
638   // the child queue disc cannot be one with wake mode equal to WAKE_CHILD because
639   // such queue discs do not implement the enqueue/dequeue methods
640   NS_ABORT_MSG_IF (qdClass->GetQueueDisc ()->GetWakeMode () == WAKE_CHILD,
641                    "A queue disc with WAKE_CHILD as wake mode can only be a root queue disc");
642 
643   // set the parent callbacks on the child queue disc, so that it can notify
644   // the parent queue disc of packets enqueued, dequeued, dropped, or marked
645   qdClass->GetQueueDisc ()->TraceConnectWithoutContext ("Enqueue",
646                                      MakeCallback (&QueueDisc::PacketEnqueued, this));
647   qdClass->GetQueueDisc ()->TraceConnectWithoutContext ("Dequeue",
648                                      MakeCallback (&QueueDisc::PacketDequeued, this));
649   qdClass->GetQueueDisc ()->TraceConnectWithoutContext ("DropBeforeEnqueue",
650                                      MakeCallback (&ChildQueueDiscDropFunctor::operator(),
651                                                    &m_childQueueDiscDbeFunctor));
652   qdClass->GetQueueDisc ()->TraceConnectWithoutContext ("DropAfterDequeue",
653                                      MakeCallback (&ChildQueueDiscDropFunctor::operator(),
654                                                    &m_childQueueDiscDadFunctor));
655   qdClass->GetQueueDisc ()->TraceConnectWithoutContext ("Mark",
656                                      MakeCallback (&ChildQueueDiscMarkFunctor::operator(),
657                                                    &m_childQueueDiscMarkFunctor));
658   m_classes.push_back (qdClass);
659 }
660 
661 Ptr<QueueDiscClass>
GetQueueDiscClass(std::size_t i) const662 QueueDisc::GetQueueDiscClass (std::size_t i) const
663 {
664   NS_ASSERT (i < m_classes.size ());
665   return m_classes[i];
666 }
667 
668 std::size_t
GetNQueueDiscClasses(void) const669 QueueDisc::GetNQueueDiscClasses (void) const
670 {
671   return m_classes.size ();
672 }
673 
674 int32_t
Classify(Ptr<QueueDiscItem> item)675 QueueDisc::Classify (Ptr<QueueDiscItem> item)
676 {
677   NS_LOG_FUNCTION (this << item);
678 
679   int32_t ret = PacketFilter::PF_NO_MATCH;
680   for (std::vector<Ptr<PacketFilter> >::iterator f = m_filters.begin ();
681        f != m_filters.end () && ret == PacketFilter::PF_NO_MATCH; f++)
682     {
683       ret = (*f)->Classify (item);
684     }
685   return ret;
686 }
687 
688 QueueDisc::WakeMode
GetWakeMode(void) const689 QueueDisc::GetWakeMode (void) const
690 {
691   return WAKE_ROOT;
692 }
693 
694 void
PacketEnqueued(Ptr<const QueueDiscItem> item)695 QueueDisc::PacketEnqueued (Ptr<const QueueDiscItem> item)
696 {
697   m_nPackets++;
698   m_nBytes += item->GetSize ();
699   m_stats.nTotalEnqueuedPackets++;
700   m_stats.nTotalEnqueuedBytes += item->GetSize ();
701 
702   NS_LOG_LOGIC ("m_traceEnqueue (p)");
703   m_traceEnqueue (item);
704 }
705 
706 void
PacketDequeued(Ptr<const QueueDiscItem> item)707 QueueDisc::PacketDequeued (Ptr<const QueueDiscItem> item)
708 {
709   // If the queue disc asked the internal queue or the child queue disc to
710   // dequeue a packet because a peek operation was requested, the packet is
711   // still held by the queue disc, hence we do not need to update statistics
712   // and fire the dequeue trace. This function will be explicitly called when
713   // the packet will be actually dequeued.
714   if (!m_peeked)
715     {
716       m_nPackets--;
717       m_nBytes -= item->GetSize ();
718       m_stats.nTotalDequeuedPackets++;
719       m_stats.nTotalDequeuedBytes += item->GetSize ();
720 
721       m_sojourn (Simulator::Now () - item->GetTimeStamp ());
722 
723       NS_LOG_LOGIC ("m_traceDequeue (p)");
724       m_traceDequeue (item);
725     }
726 }
727 
728 void
DropBeforeEnqueue(Ptr<const QueueDiscItem> item,const char * reason)729 QueueDisc::DropBeforeEnqueue (Ptr<const QueueDiscItem> item, const char* reason)
730 {
731   NS_LOG_FUNCTION (this << item << reason);
732 
733   m_stats.nTotalDroppedPackets++;
734   m_stats.nTotalDroppedBytes += item->GetSize ();
735   m_stats.nTotalDroppedPacketsBeforeEnqueue++;
736   m_stats.nTotalDroppedBytesBeforeEnqueue += item->GetSize ();
737 
738   // update the number of packets dropped for the given reason
739   std::map<std::string, uint32_t>::iterator itp = m_stats.nDroppedPacketsBeforeEnqueue.find (reason);
740   if (itp != m_stats.nDroppedPacketsBeforeEnqueue.end ())
741     {
742       itp->second++;
743     }
744   else
745     {
746       m_stats.nDroppedPacketsBeforeEnqueue[reason] = 1;
747     }
748   // update the amount of bytes dropped for the given reason
749   std::map<std::string, uint64_t>::iterator itb = m_stats.nDroppedBytesBeforeEnqueue.find (reason);
750   if (itb != m_stats.nDroppedBytesBeforeEnqueue.end ())
751     {
752       itb->second += item->GetSize ();
753     }
754   else
755     {
756       m_stats.nDroppedBytesBeforeEnqueue[reason] = item->GetSize ();
757     }
758 
759   NS_LOG_DEBUG ("Total packets/bytes dropped before enqueue: "
760                 << m_stats.nTotalDroppedPacketsBeforeEnqueue << " / "
761                 << m_stats.nTotalDroppedBytesBeforeEnqueue);
762   NS_LOG_LOGIC ("m_traceDropBeforeEnqueue (p)");
763   m_traceDrop (item);
764   m_traceDropBeforeEnqueue (item, reason);
765 }
766 
767 void
DropAfterDequeue(Ptr<const QueueDiscItem> item,const char * reason)768 QueueDisc::DropAfterDequeue (Ptr<const QueueDiscItem> item, const char* reason)
769 {
770   NS_LOG_FUNCTION (this << item << reason);
771 
772   m_stats.nTotalDroppedPackets++;
773   m_stats.nTotalDroppedBytes += item->GetSize ();
774   m_stats.nTotalDroppedPacketsAfterDequeue++;
775   m_stats.nTotalDroppedBytesAfterDequeue += item->GetSize ();
776 
777   // update the number of packets dropped for the given reason
778   std::map<std::string, uint32_t>::iterator itp = m_stats.nDroppedPacketsAfterDequeue.find (reason);
779   if (itp != m_stats.nDroppedPacketsAfterDequeue.end ())
780     {
781       itp->second++;
782     }
783   else
784     {
785       m_stats.nDroppedPacketsAfterDequeue[reason] = 1;
786     }
787   // update the amount of bytes dropped for the given reason
788   std::map<std::string, uint64_t>::iterator itb = m_stats.nDroppedBytesAfterDequeue.find (reason);
789   if (itb != m_stats.nDroppedBytesAfterDequeue.end ())
790     {
791       itb->second += item->GetSize ();
792     }
793   else
794     {
795       m_stats.nDroppedBytesAfterDequeue[reason] = item->GetSize ();
796     }
797 
798   // if in the context of a peek request a dequeued packet is dropped, we need
799   // to update the statistics and fire the dequeue trace before firing the drop
800   // after dequeue trace
801   if (m_peeked)
802     {
803       // temporarily set m_peeked to false, otherwise PacketDequeued does nothing
804       m_peeked = false;
805       PacketDequeued (item);
806       m_peeked = true;
807     }
808 
809   NS_LOG_DEBUG ("Total packets/bytes dropped after dequeue: "
810                 << m_stats.nTotalDroppedPacketsAfterDequeue << " / "
811                 << m_stats.nTotalDroppedBytesAfterDequeue);
812   NS_LOG_LOGIC ("m_traceDropAfterDequeue (p)");
813   m_traceDrop (item);
814   m_traceDropAfterDequeue (item, reason);
815 }
816 
817 bool
Mark(Ptr<QueueDiscItem> item,const char * reason)818 QueueDisc::Mark (Ptr<QueueDiscItem> item, const char* reason)
819 {
820   NS_LOG_FUNCTION (this << item << reason);
821 
822   bool retval = item->Mark ();
823 
824   if (!retval)
825     {
826       return false;
827     }
828 
829   m_stats.nTotalMarkedPackets++;
830   m_stats.nTotalMarkedBytes += item->GetSize ();
831 
832   // update the number of packets marked for the given reason
833   std::map<std::string, uint32_t>::iterator itp = m_stats.nMarkedPackets.find (reason);
834   if (itp != m_stats.nMarkedPackets.end ())
835     {
836       itp->second++;
837     }
838   else
839     {
840       m_stats.nMarkedPackets[reason] = 1;
841     }
842   // update the amount of bytes marked for the given reason
843   std::map<std::string, uint64_t>::iterator itb = m_stats.nMarkedBytes.find (reason);
844   if (itb != m_stats.nMarkedBytes.end ())
845     {
846       itb->second += item->GetSize ();
847     }
848   else
849     {
850       m_stats.nMarkedBytes[reason] = item->GetSize ();
851     }
852 
853   NS_LOG_DEBUG ("Total packets/bytes marked: "
854                 << m_stats.nTotalMarkedPackets << " / "
855                 << m_stats.nTotalMarkedBytes);
856   m_traceMark (item, reason);
857   return true;
858 }
859 
860 bool
Enqueue(Ptr<QueueDiscItem> item)861 QueueDisc::Enqueue (Ptr<QueueDiscItem> item)
862 {
863   NS_LOG_FUNCTION (this << item);
864 
865   m_stats.nTotalReceivedPackets++;
866   m_stats.nTotalReceivedBytes += item->GetSize ();
867 
868   bool retval = DoEnqueue (item);
869 
870   if (retval)
871     {
872       item->SetTimeStamp (Simulator::Now ());
873     }
874 
875   // DoEnqueue may return false because:
876   // 1) the internal queue is full
877   //    -> the DropBeforeEnqueue method of this queue disc is automatically called
878   //       because QueueDisc::AddInternalQueue sets the trace callback
879   // 2) the child queue disc dropped the packet
880   //    -> the DropBeforeEnqueue method of this queue disc is automatically called
881   //       because QueueDisc::AddQueueDiscClass sets the trace callback
882   // 3) it dropped the packet
883   //    -> DoEnqueue has to explicitly call DropBeforeEnqueue
884   // Thus, we do not have to call DropBeforeEnqueue here.
885 
886   // check that the received packet was either enqueued or dropped
887   NS_ASSERT (m_stats.nTotalReceivedPackets == m_stats.nTotalDroppedPacketsBeforeEnqueue +
888              m_stats.nTotalEnqueuedPackets);
889   NS_ASSERT (m_stats.nTotalReceivedBytes == m_stats.nTotalDroppedBytesBeforeEnqueue +
890              m_stats.nTotalEnqueuedBytes);
891 
892   return retval;
893 }
894 
895 Ptr<QueueDiscItem>
Dequeue(void)896 QueueDisc::Dequeue (void)
897 {
898   NS_LOG_FUNCTION (this);
899 
900   // The QueueDisc::DoPeek method dequeues a packet and keeps it as a requeued
901   // packet. Thus, first check whether a peeked packet exists. Otherwise, call
902   // the private DoDequeue method.
903   Ptr<QueueDiscItem> item = m_requeued;
904 
905   if (item)
906     {
907       m_requeued = 0;
908       if (m_peeked)
909         {
910           // If the packet was requeued because a peek operation was requested
911           // (which is the case here because DequeuePacket calls Dequeue only
912           // when m_requeued is null), we need to explicitly call PacketDequeued
913           // to update statistics about dequeued packets and fire the dequeue trace.
914           m_peeked = false;
915           PacketDequeued (item);
916         }
917     }
918   else
919     {
920       item = DoDequeue ();
921     }
922 
923   NS_ASSERT (m_nPackets == m_stats.nTotalEnqueuedPackets - m_stats.nTotalDequeuedPackets);
924   NS_ASSERT (m_nBytes == m_stats.nTotalEnqueuedBytes - m_stats.nTotalDequeuedBytes);
925 
926   return item;
927 }
928 
929 Ptr<const QueueDiscItem>
Peek(void)930 QueueDisc::Peek (void)
931 {
932   NS_LOG_FUNCTION (this);
933   return DoPeek ();
934 }
935 
936 Ptr<const QueueDiscItem>
DoPeek(void)937 QueueDisc::DoPeek (void)
938 {
939   NS_LOG_FUNCTION (this);
940 
941   if (!m_requeued)
942     {
943       m_peeked = true;
944       m_requeued = Dequeue ();
945       // if no packet is returned, reset the m_peeked flag
946       if (!m_requeued)
947         {
948           m_peeked = false;
949         }
950     }
951   return m_requeued;
952 }
953 
954 void
Run(void)955 QueueDisc::Run (void)
956 {
957   NS_LOG_FUNCTION (this);
958 
959   if (RunBegin ())
960     {
961       uint32_t quota = m_quota;
962       while (Restart ())
963         {
964           quota -= 1;
965           if (quota <= 0)
966             {
967               /// \todo netif_schedule (q);
968               break;
969             }
970         }
971       RunEnd ();
972     }
973 }
974 
975 bool
RunBegin(void)976 QueueDisc::RunBegin (void)
977 {
978   NS_LOG_FUNCTION (this);
979   if (m_running)
980     {
981       return false;
982     }
983 
984   m_running = true;
985   return true;
986 }
987 
988 void
RunEnd(void)989 QueueDisc::RunEnd (void)
990 {
991   NS_LOG_FUNCTION (this);
992   m_running = false;
993 }
994 
995 bool
Restart(void)996 QueueDisc::Restart (void)
997 {
998   NS_LOG_FUNCTION (this);
999   Ptr<QueueDiscItem> item = DequeuePacket();
1000   if (item == 0)
1001     {
1002       NS_LOG_LOGIC ("No packet to send");
1003       return false;
1004     }
1005 
1006   return Transmit (item);
1007 }
1008 
1009 Ptr<QueueDiscItem>
DequeuePacket()1010 QueueDisc::DequeuePacket ()
1011 {
1012   NS_LOG_FUNCTION (this);
1013 
1014   Ptr<QueueDiscItem> item;
1015 
1016   // First check if there is a requeued packet
1017   if (m_requeued != 0)
1018     {
1019         // If the queue where the requeued packet is destined to is not stopped, return
1020         // the requeued packet; otherwise, return an empty packet.
1021         // If the device does not support flow control, the device queue is never stopped
1022         if (!m_devQueueIface || !m_devQueueIface->GetTxQueue (m_requeued->GetTxQueueIndex ())->IsStopped ())
1023           {
1024             item = m_requeued;
1025             m_requeued = 0;
1026             if (m_peeked)
1027               {
1028                 // If the packet was requeued because a peek operation was requested
1029                 // we need to explicitly call PacketDequeued to update statistics
1030                 // about dequeued packets and fire the dequeue trace.
1031                 m_peeked = false;
1032                 PacketDequeued (item);
1033               }
1034           }
1035     }
1036   else
1037     {
1038       // If the device is multi-queue (actually, Linux checks if the queue disc has
1039       // multiple queues), ask the queue disc to dequeue a packet (a multi-queue aware
1040       // queue disc should try not to dequeue a packet destined to a stopped queue).
1041       // Otherwise, ask the queue disc to dequeue a packet only if the (unique) queue
1042       // is not stopped.
1043       if (!m_devQueueIface ||
1044           m_devQueueIface->GetNTxQueues ()>1 || !m_devQueueIface->GetTxQueue (0)->IsStopped ())
1045         {
1046           item = Dequeue ();
1047           // If the item is not null, add the header to the packet.
1048           if (item != 0)
1049             {
1050               item->AddHeader ();
1051             }
1052           // Here, Linux tries bulk dequeues
1053         }
1054     }
1055   return item;
1056 }
1057 
1058 void
Requeue(Ptr<QueueDiscItem> item)1059 QueueDisc::Requeue (Ptr<QueueDiscItem> item)
1060 {
1061   NS_LOG_FUNCTION (this << item);
1062   m_requeued = item;
1063   /// \todo netif_schedule (q);
1064 
1065   m_stats.nTotalRequeuedPackets++;
1066   m_stats.nTotalRequeuedBytes += item->GetSize ();
1067 
1068   NS_LOG_LOGIC ("m_traceRequeue (p)");
1069   m_traceRequeue (item);
1070 }
1071 
1072 bool
Transmit(Ptr<QueueDiscItem> item)1073 QueueDisc::Transmit (Ptr<QueueDiscItem> item)
1074 {
1075   NS_LOG_FUNCTION (this << item);
1076 
1077   // if the device queue is stopped, requeue the packet and return false.
1078   // Note that if the underlying device is tc-unaware, packets are never
1079   // requeued because the queues of tc-unaware devices are never stopped
1080   if (m_devQueueIface && m_devQueueIface->GetTxQueue (item->GetTxQueueIndex ())->IsStopped ())
1081     {
1082       Requeue (item);
1083       return false;
1084     }
1085 
1086   // a single queue device makes no use of the priority tag
1087   // a device that does not install a device queue interface likely makes no use of it as well
1088   if (!m_devQueueIface || m_devQueueIface->GetNTxQueues () == 1)
1089     {
1090       SocketPriorityTag priorityTag;
1091       item->GetPacket ()->RemovePacketTag (priorityTag);
1092     }
1093   NS_ASSERT_MSG (m_send, "Send callback not set");
1094   m_send (item);
1095 
1096   // the behavior here slightly diverges from Linux. In Linux, it is advised that
1097   // the function called when a packet needs to be transmitted (ndo_start_xmit)
1098   // should always return NETDEV_TX_OK, which means that the packet is consumed by
1099   // the device driver and thus is not requeued. However, the ndo_start_xmit function
1100   // of the device driver is allowed to return NETDEV_TX_BUSY (and hence the packet
1101   // is requeued) when there is no room for the received packet in the device queue,
1102   // despite the queue is not stopped. This case is considered as a corner case or
1103   // an hard error, and should be avoided.
1104   // Here, we do not handle such corner case and always assume that the packet is
1105   // consumed by the netdevice. Thus, we ignore the value returned by Send and a
1106   // packet sent to a netdevice is never requeued. The reason is that the semantics
1107   // of the value returned by NetDevice::Send does not match that of the value
1108   // returned by ndo_start_xmit.
1109 
1110   // if the queue disc is empty or the device queue is now stopped, return false so
1111   // that the Run method does not attempt to dequeue other packets and exits
1112   if (GetNPackets () == 0 ||
1113       (m_devQueueIface && m_devQueueIface->GetTxQueue (item->GetTxQueueIndex ())->IsStopped ()))
1114     {
1115       return false;
1116     }
1117 
1118   return true;
1119 }
1120 
1121 } // namespace ns3
1122